โ— Technical Documentation

FlowLinks Developer Reference

Complete architecture, class reference, hook system, database schema, and extension guide for FlowLinks 2.6.10.

๐Ÿ“ฆ PHP 7.4+ ๐Ÿ”Œ WordPress 5.8+ ๐Ÿ›’ WooCommerce Compatible ๐Ÿ—๏ธ 4 Core Classes ๐ŸŽฏ 3 AJAX Endpoints ๐Ÿ”’ Nonce Protected

โŒ˜ Table of Contents

01
Architecture Overview
High-level design and responsibilities

FlowLinks follows a clean singleton-based architecture with a clear separation between admin and public concerns. All persistence goes through a single settings manager that caches results per-request for performance.

pixel-flowlinks.php
โ†’
Settings
โ†’
Renderer
โ†’
Public
โ†’
the_content

Core Principles

S
Singleton Pattern

Every core class exposes get_instance(). No global state leaks.

C
Lazy Caching

Settings are fetched once per request and cached in a static array.

R
Strict Escaping

Every echo uses esc_html, esc_attr, or esc_url.

F
Filterable Ordering

Posts/Pages and Products use independent rule sets merged through interleaving.

02
File Structure
Directory layout and file responsibilities
flowlinks/ โ”œโ”€โ”€ pixel-flowlinks.php # Bootstrap, constants, hooks โ”œโ”€โ”€ uninstall.php # Cleanup on plugin removal โ”œโ”€โ”€ includes/ โ”‚ โ”œโ”€โ”€ class-flowlinks-settings.php # Settings manager (singleton) โ”‚ โ””โ”€โ”€ class-flowlinks-renderer.php # HTML renderer + query engine โ”œโ”€โ”€ admin/ โ”‚ โ”œโ”€โ”€ class-flowlinks-admin.php # Admin menu, tabs, AJAX โ”‚ โ””โ”€โ”€ views/ โ”‚ โ”œโ”€โ”€ tab-internal.php # Internal Links tab UI โ”‚ โ””โ”€โ”€ tab-about.php # About tab UI โ”œโ”€โ”€ public/ โ”‚ โ””โ”€โ”€ class-flowlinks-public.php # Frontend injection + assets โ”œโ”€โ”€ assets/ โ”‚ โ”œโ”€โ”€ css/ โ”‚ โ”‚ โ”œโ”€โ”€ admin.css โ”‚ โ”‚ โ””โ”€โ”€ frontend.css โ”‚ โ”œโ”€โ”€ js/ โ”‚ โ”‚ โ””โ”€โ”€ admin.js โ”‚ โ”œโ”€โ”€ fonts/ โ”‚ โ”‚ โ”œโ”€โ”€ Vazirmatn.woff2 โ”‚ โ”‚ โ””โ”€โ”€ Shabnam.woff2 โ”‚ โ””โ”€โ”€ images/ โ”‚ โ””โ”€โ”€ flowlinks-logo.webp โ””โ”€โ”€ # Locale translations are delivered by WordPress.org; no .po/.mo files are bundled.

Load Order

The bootstrap file loads classes in dependency order: settings โ†’ renderer โ†’ admin โ†’ public. Admin and Public are instantiated on the plugins_loaded action.

03
Class Reference
Public API for each core class

FlowLinks_Settings

Central configuration manager. Handles defaults, fetching, sanitizing, and saving settings.

f
get_instance(): FlowLinks_Settings

Returns the singleton instance.

f
get_settings(string $group = 'internal'): array

Returns fully merged settings for a group (defaults + saved options).

f
get_defaults(string $group = 'internal'): array

Returns the raw defaults array for the requested group.

f
get_current_tab(): string

Returns the sanitized current admin tab (internal or about).

f
save(): void

Sanitizes and persists POST data. Called from the Admin layer after nonce verification.

f
clear_cache(): void

Flushes the static settings cache.

FlowLinks_Renderer

Fetches related posts and produces escaped HTML for both frontend and preview contexts.

f
render_single_block(int $post_id, string $context, string $location): string

Renders a block for the given source post on the frontend.

f
render_preview_block(array $temp, string $context): string

Renders a preview block using temporary settings (no DB write).

FlowLinks_Admin

Registers the admin menu, enqueues assets, handles settings saving, and serves AJAX tab content.

f
add_admin_menu(): void

Registers the top-level FlowLinks menu page.

f
enqueue_admin_assets(string $hook): void

Loads CSS/JS only on the plugin's admin page.

f
maybe_save_settings(): void

Verifies nonce + capability, then calls Settings::save().

f
ajax_get_preview(): void

AJAX handler for live preview rendering.

f
ajax_load_tab(): void

AJAX handler for lazy loading admin tab content.

FlowLinks_Public

Hooks into the_content, injects rendered blocks, and enqueues frontend assets. Dynamic CSS is attached to the enqueued stylesheet via the WordPress style API.

f
inject_links_into_content(string $content): string

Filter callback for the_content.

f
render_effective_content(string $content, int $post_id): string

Reusable rendering logic for both singular views and custom contexts.

f
enqueue_frontend_assets(): void

Enqueues the frontend stylesheet and attaches validated dynamic CSS custom properties with wp_add_inline_style(). Font-face rules are defined once in assets/css/frontend.css.

04
Settings Schema
Complete key reference and validation rules

Settings are stored under the option key flowlinks_settings_internal. The public external-credit setting show_powered_by is controlled by an explicit, clearly labeled administrator switch and defaults to false. WordPress.org supplies locale translation files; the plugin does not bundle English self-translation files or manually load its text domain.

Key Type Default Validation
enabledbooltrueisset check
show_powered_byboolfalseisset check; explicit frontend opt-in
display_matrixarray['post'=>['post'],'product'=>['product']]sanitize_key + post_type_exists
min_wordsint0absint, 0โ€“50000
excerpt_lengthint15absint, 1โ€“100
blocks_countint1derived from positions
paragraph_positionsstring'1'comma-separated integers, max 10
position_1 โ€ฆ position_10int00 or 1
section_titlestring'Recommended Content'sanitize_text_field
items_per_blockint3absint, 1โ€“20
exclude_idsarray[]absint IDs, max 500
manual_idsarray[]absint IDs, max 500
order_bystring'date'whitelisted set
orderstring'DESC'ASC or DESC
post_order_bystring'date'whitelisted set
post_orderstring'DESC'ASC or DESC
post_manual_idsarray[]absint IDs, max 500
product_order_bystring'date'whitelisted set
product_orderstring'DESC'ASC or DESC
product_manual_idsarray[]absint IDs, max 500
display_stylestring'card'card | list
columnsint3absint, 1โ€“6
show_thumbnailbooltrueisset check
show_excerptboolfalseisset check
background_colorstring'#f9f9f9'sanitize_hex_color
border_colorstring'#dddddd'sanitize_hex_color
title_colorstring'#111111'sanitize_hex_color
text_colorstring'#555555'sanitize_hex_color
item_border_colorstring'#005a87'sanitize_hex_color
content_analysis_modestring'p'p | elementor
โš ๏ธ Backward Compatibility

Legacy keys order_by, order, and manual_ids are still accepted and used as fallbacks for post_order_by, post_order, and post_manual_ids.

05
Renderer Pipeline
From settings to final HTML
load settings
โ†’
matrix lookup
โ†’
query posts
โ†’
order + merge
โ†’
generate HTML

Query Strategy

FlowLinks uses WP_Query for all standard sorts and a raw $wpdb query for WooCommerce best-sellers (joining on _total_sales). Each target content type is queried separately, then results are interleaved to produce a balanced mix.

Interleaving Algorithm

// Simplified pseudo-code of get_related_posts_by_types()
$by_type = [];
foreach ( $post_types as $type ) {
    $rule = get_order_settings_for_type( $settings, $type );
    $by_type[$type] = get_related_posts( $source_id, $rule, [$type] );
}

// Interleave: take one from each type per round
$merged = [];
$offset = 0;
while ( count( $merged ) < $limit ) {
    foreach ( $post_types as $type ) {
        $post = $by_type[$type][$offset] ?? null;
        if ( $post && ! isset( $seen[$post->ID] ) ) {
            $merged[] = $post;
            $seen[$post->ID] = true;
        }
    }
    $offset++;
}

Column Auto-Fit

When display_style = 'card' and the number of rendered items is โ‰ค 6, the effective column count matches the item count. This guarantees all cards fit into a single row regardless of the configured columns setting.

06
Admin Layer
Menu, tabs, and asset management

Admin Menu

FlowLinks registers a single top-level menu with slug pixel-flowlinks and the dashicons-admin-links icon at position 60.

Lazy Tab Loading

Tab content is rendered via AJAX (flowlinks_load_tab) to keep initial page weight minimal. The active tab is determined server-side by FlowLinks_Settings::get_current_tab() and reflected in the URL ?tab= parameter.

Asset Loading Rules

AssetLoaded When
wp-color-pickerAlways on FlowLinks admin page
flowlinks-admin (CSS)Always on FlowLinks admin page
flowlinks-frontend (CSS)Only when Internal tab is active
flowlinks-admin (JS)Always, with jQuery + wp-color-picker + wp-util deps

Localized Data

flowlinks_admin_obj = {
    ajax_url: '/wp-admin/admin-ajax.php',
    preview_nonce: '<nonce>',
    tabs_nonce: '<nonce>',
    current_tab: 'internal'
};
07
AJAX Endpoints
Registered actions and payloads

flowlinks_load_tab

Actionwp_ajax_flowlinks_load_tab
Nonceflowlinks_tabs_nonce
Capabilitymanage_options
Inputtab (internal | about)
Output{ success: true, data: { content: '<html>' } }

flowlinks_get_preview

Actionwp_ajax_flowlinks_get_preview
Nonceflowlinks_preview_nonce
Capabilitymanage_options
Inputsettings (JSON string of FlowLinks[internal])
Output{ success: true, data: { html: '<html>' } }
๐Ÿ“Œ Preview Data Flow

The JS collects all form fields, serializes them into a nested object, then sends them as a JSON string. The PHP handler parses it with json_decode, normalizes booleans, and passes the array to FlowLinks_Renderer::render_preview_block().

08
Frontend Injection
How the block lands in your content

Injection Hook

FlowLinks hooks the_content at priority 20. In singular views, FlowLinks_Public::inject_links_into_content() builds the effective content.

Paragraph Detection

// The frontend scans the original HTML structure.
$paragraph_ends = $this->find_paragraph_end_offsets( $content, $analysis_mode );

// Only real <p>...</p> elements are counted.
// Comments and raw-text/code areas are ignored.
// Elementor mode recognizes text-editor/widget wrappers; without one, it falls back to all real <p> elements.

Position Matching

The paragraph positions string (e.g. "2, 5, 9") is parsed into an integer array, deduplicated, sorted, and capped at 10 values. Each matching real HTML paragraph is located by its byte offset, then a block is rendered immediately after that paragraph without reserializing the original content.

LTR Enforcement

Every text-bearing element is emitted with inline direction:ltr !important;text-align:left !important. This guarantees correct alignment even on RTL-themed sites that aggressively override styles.

09
Hooks & Filters
Extension points for developers
โš ๏ธ Version 2.6.10

The current release does not expose public filters. The internal design follows WordPress coding standards, so filters can be added in a future version with deliberate compatibility review.

Registered Actions

HookCallbackPurpose
admin_menuAdmin::add_admin_menuRegister top-level menu
admin_enqueue_scriptsAdmin::enqueue_admin_assetsLoad admin CSS/JS
admin_initAdmin::maybe_save_settingsHandle form submit
admin_noticesAdmin::show_admin_noticesSuccess notice
wp_ajax_flowlinks_get_previewAdmin::ajax_get_previewPreview render
wp_ajax_flowlinks_load_tabAdmin::ajax_load_tabTab lazy-load
wp_enqueue_scriptsPublic::enqueue_frontend_assetsEnqueue frontend CSS and attach validated CSS variables with wp_add_inline_style()
the_contentPublic::inject_links_into_contentFrontend injection

Suggested Future Filters

// Filter the final rendered HTML block
$html = apply_filters( 'flowlinks/rendered_block', $html, $posts, $settings );

// Filter the related posts before rendering
$posts = apply_filters( 'flowlinks/related_posts', $posts, $source_id, $settings );

// Filter paragraph positions
$positions = apply_filters( 'flowlinks/paragraph_positions', $positions, $post_id );
10
Security Model
Defense-in-depth against common attacks

Nonce Verification

All form submissions are protected by wp_nonce_field( 'flowlinks_save_settings', 'flowlinks_save_settings_nonce' ). AJAX handlers call check_ajax_referer() with the relevant nonce action.

Capability Checks

Every mutating action requires current_user_can( 'manage_options' ). The menu itself is registered with the same capability.

Input Sanitization

Field TypeSanitizer
Integersabsint() with range clamps
Booleansisset() check
Keys / enumssanitize_key() + whitelist
Textsanitize_text_field()
URLsesc_url_raw() + url_to_postid()
Colorssanitize_hex_color()
Post typespost_type_exists()

Output Escaping

All HTML output goes through esc_html(), esc_attr(), or esc_url(). No raw user input is ever echoed.

SQL Safety

The single raw SQL query (WooCommerce best-sellers) uses $wpdb->prepare() with placeholder arrays. All other queries use WP_Query.

11
Performance
Optimization techniques and benchmarks
1
Settings Caching

Static array caches settings per request โ€” DB hit happens only once.

2
no_found_rows

All WP_Query calls set no_found_rows = true to skip COUNT queries.

3
ignore_sticky_posts

Sticky post merging is bypassed for recommendation queries.

4
posts_per_page caps

Hard limit of 20 items per query prevents runaway loads.

5
Prepared SQL

The best-sellers query is capped at 500 rows and uses a single JOIN.

6
Conditional asset loading

Frontend CSS loads only when is_singular() and plugin is enabled.

12
Extension Guide
Common customization recipes

Recipe 1 โ€” Add a Custom Post Type to the Matrix

// Register a CPT, then extend the matrix on save
add_filter( 'flowlinks/supported_post_types', function( $types ) {
    $types[] = 'recipe';
    return $types;
} );

Recipe 2 โ€” Add a Custom Sort Method

add_filter( 'flowlinks/allowed_order_by', function( $allowed ) {
    $allowed[] = 'menu_order';
    return $allowed;
} );

add_filter( 'flowlinks/related_posts', function( $posts, $source_id, $settings ) {
    if ( $settings['order_by'] === 'menu_order' ) {
        usort( $posts, fn($a,$b) => $a->menu_order <=> $b->menu_order );
    }
    return $posts;
}, 10, 3 );

Recipe 3 โ€” Add Custom CSS Variable

add_filter( 'flowlinks/style_vars', function( $vars, $settings ) {
    $vars[] = '--lf-custom-shadow: 0 4px 12px rgba(0,0,0,.08)';
    return $vars;
}, 10, 2 );

Recipe 4 โ€” Replace Default Item Template

add_filter( 'flowlinks/item_html', function( $html, $post, $settings ) {
    return '<article class="custom-item">' . esc_html( $post->post_title ) . '</article>';
}, 10, 3 );
13
Changelog
Version history and notable changes
VersionHighlights
2.6.10 Enqueued dynamic CSS and admin tab JavaScript; removed raw style/script output, manual text-domain loading, and bundled en_US translation files; tightened CSS-value validation; clarified explicit opt-in for the optional public credit link; hardened structural paragraph-position detection; reduced repeated related-content queries.
2.6.9 Equal width Style & Colors cards; About page refinement.
2.6.6 Paragraph positions field consolidates position_1 โ€ฆ position_10.
2.6.3 Authoritative UI system; Vazirmatn + Shabnam typography.
2.6.0 Independent post/product ordering profiles.
2.5.0 Card / List layout switch; color pickers.
2.0.0 Content Type Matrix; AJAX tabs.
1.0.0 Initial public release.
๐ŸŒ Stay Updated

Follow releases and documentation at pixelplugins.top.