Plugin Directory

Changeset 3356282


Ignore:
Timestamp:
09/04/2025 06:23:36 PM (7 months ago)
Author:
marceljm
Message:

Notice to rate the plugin

Location:
featured-image-from-url/trunk
Files:
2 added
1 deleted
17 edited

Legend:

Unmodified
Added
Removed
  • featured-image-from-url/trunk/admin/api.php

    r3328447 r3356282  
    1818function fifu_remote_post($endpoint, $array) {
    1919    return fifu_is_local() ? wp_remote_post($endpoint, $array) : wp_safe_remote_post($endpoint, $array);
     20}
     21
     22function fifu_api_image_url(WP_REST_Request $request) {
     23    $param = $request['post_id'] ?? null;
     24    return fifu_main_image_url($param, true);
    2025}
    2126
     
    652657            $url = 'https://cdn.fifu.app/' . ($json->bucket_id ?? '') . '/' . ($post->storage_id ?? '');
    653658            $post->proxy_url = fifu_speedup_get_signed_url($url, 128, 128, $json->bucket_id ?? '', $post->storage_id ?? '', $is_video);
     659
     660            // sanitize fields
     661            $post->storage_id = sanitize_text_field($post->storage_id ?? '');
     662            $post->title = sanitize_text_field($post->title ?? '');
     663            $post->date = sanitize_text_field($post->date ?? '');
     664            $post->meta_key = sanitize_text_field($post->meta_key ?? '');
     665            $post->proxy_url = esc_url_raw($post->proxy_url ?? '');
     666            $post->meta_id = intval($post->meta_id ?? 0);
     667            $post->post_id = intval($post->post_id ?? 0);
     668            $post->is_category = isset($post->is_category) ? (bool) $post->is_category : false;
    654669        }
    655670    }
     
    847862    $keyword = $request['keyword'] ?? null;
    848863    $urls = fifu_db_get_all_urls($page, $type, $keyword);
     864
     865    // sanitize output
     866    if (is_array($urls)) {
     867        foreach ($urls as $u) {
     868            if (!is_object($u))
     869                continue;
     870            $u->url = esc_url_raw($u->url ?? '');
     871            $u->video_url = isset($u->video_url) && $u->video_url ? esc_url_raw($u->video_url) : null;
     872            $u->post_title = sanitize_text_field($u->post_title ?? '');
     873            $u->post_name = sanitize_text_field($u->post_name ?? '');
     874            $u->post_date = sanitize_text_field($u->post_date ?? '');
     875            $u->meta_key = sanitize_text_field($u->meta_key ?? '');
     876            $u->meta_id = intval($u->meta_id ?? 0);
     877            $u->post_id = intval($u->post_id ?? 0);
     878            // ensure boolean-ish as int (0/1)
     879            $u->category = isset($u->category) ? (int) (!!$u->category) : 0;
     880        }
     881    }
     882
    849883    return $urls;
    850884}
     
    857891    $type = $request['type'] ?? null;
    858892    $keyword = $request['keyword'] ?? null;
    859     return fifu_db_get_posts_with_internal_featured_image($page, $type, $keyword);
     893    $results = fifu_db_get_posts_with_internal_featured_image($page, $type, $keyword);
     894
     895    // sanitize output
     896    if (is_array($results)) {
     897        foreach ($results as $r) {
     898            if (!is_object($r))
     899                continue;
     900            $r->url = esc_url_raw($r->url ?? '');
     901            $r->post_title = sanitize_text_field($r->post_title ?? '');
     902            $r->post_name = sanitize_text_field($r->post_name ?? '');
     903            $r->post_date = sanitize_text_field($r->post_date ?? '');
     904            $r->gallery_ids = isset($r->gallery_ids) ? sanitize_text_field($r->gallery_ids) : null;
     905            $r->post_id = intval($r->post_id ?? 0);
     906            $r->thumbnail_id = intval($r->thumbnail_id ?? 0);
     907            $r->category = isset($r->category) ? (int) (!!$r->category) : 0;
     908        }
     909    }
     910
     911    return $results;
    860912}
    861913
     
    11951247    ));
    11961248
     1249    register_rest_route('featured-image-from-url/v1', '/url/(?P<post_id>\d+)', array(
     1250        'methods' => 'GET',
     1251        'callback' => 'fifu_api_image_url',
     1252        'permission_callback' => 'fifu_get_private_data_permissions_check',
     1253    ));
     1254
    11971255    register_rest_route('featured-image-from-url/v2', '/create_thumbnails_list/', array(
    11981256        'methods' => 'POST',
     
    12771335    return true;
    12781336}
     1337
  • featured-image-from-url/trunk/admin/block.php

    r3352132 r3356282  
    44    // Prevent block registration in Customizer
    55    if (is_admin() && isset($GLOBALS['wp_customize']) && $GLOBALS['wp_customize'] instanceof WP_Customize_Manager) {
     6        return;
     7    }
     8
     9    // Prevent block registration on the widgets screen
     10    if (is_admin() && basename($_SERVER['PHP_SELF']) === 'widgets.php') {
    611        return;
    712    }
     
    6671
    6772add_action('init', 'fifu_register_meta');
     73
  • featured-image-from-url/trunk/admin/html/cloud.html

    r3348282 r3356282  
    608608    </div>
    609609</div>
    610 
    611 <script>
    612     jQuery(document).ready(function () {
    613         removeNotices();
    614     });
    615 
    616     // remove notices from other plugins
    617     setTimeout(function () {
    618         removeNotices();
    619     }, 250);
    620 
    621     function removeNotices() {
    622         jQuery('div.header-box > div').remove();
    623         jQuery('.notice').remove();
    624         jQuery('[id^=wp-admin-bar-_]').remove();
    625         jQuery('.pms-cross-promo').remove();
    626     }
    627 </script>
  • featured-image-from-url/trunk/admin/html/css/pro.css

    r3111474 r3356282  
    44
    55span.fifu-pro-icon {
    6     font-size:32px;
    7     color: #d63638;
    8     filter: drop-shadow(5px 5px 5px #888888);
     6    font-size: 32px;
     7    filter: drop-shadow(1px 1px 1px #888888);
    98    position: relative;
    109    left: -25px;
    11     top: -7px;
     10    top: -1px;
    1211}
     12
     13/* Replace the lock/crown with a compact PRO badge */
     14.dashicons-lock.fifu-pro-icon:before {
     15    content: 'PRO' !important;
     16    display: inline-block;
     17    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, "Helvetica Neue", Arial, sans-serif !important;
     18    font-weight: 700;
     19    font-size: 11px;
     20    letter-spacing: 0.5px;
     21    color: #111111;
     22    background: #d4af37; /* gold */
     23    padding: 2px 6px;
     24    border-radius: 1px;
     25    line-height: 1.2;
     26}
  • featured-image-from-url/trunk/admin/html/js/menu-su.js

    r3352132 r3356282  
    729729        },
    730730        async: true,
    731         beforeSend: function (xhr) {
    732             xhr.setRequestHeader('X-WP-Nonce', fifuScriptVars.nonce);
    733         },
    734         success: function (data) {
     731        // Ensure we expect JSON and gracefully handle empty responses
     732        dataType: 'json',
     733        dataFilter: function (raw, type) {
     734            if (type === 'json') {
     735                if (!raw || (typeof raw === 'string' && raw.trim() === ''))
     736                    return '[]';
     737            }
     738            return raw;
     739        },
     740        beforeSend: function (xhr) {
     741            xhr.setRequestHeader('X-WP-Nonce', fifuScriptVars.nonce);
     742        },
     743        success: function (data) {
     744            // Normalize unexpected values to an empty array
     745            if (!Array.isArray(data))
     746                data = [];
     747
    735748            for (var i = 0; i < data.length; i++) {
    736749                imgTag = '<img loading="lazy" id="' + data[i]['meta_id'] + '" src="' + data[i]['url'] + '" style="border-radius:5%; height:56px; width:56px; object-fit:cover; text-align:center">';
  • featured-image-from-url/trunk/admin/html/js/meta-box.js

    r3348282 r3356282  
    728728    });
    729729})(jQuery);
     730
     731// Auto-refresh FIFU fields after successful post save (Gutenberg)
     732(function () {
     733    try {
     734        if (typeof wp === 'undefined' || !wp.data || !wp.data.select || !wp.data.subscribe)
     735            return;
     736
     737        let wasSaving = false;
     738
     739        function fetchAndApplyFifuUrl(postId) {
     740            if (!postId)
     741                return;
     742
     743            // Use the plugin REST endpoint that returns the current main URL
     744            const base = ((typeof restUrl !== 'undefined' && restUrl) || (window.wpApiSettings && window.wpApiSettings.root) || '/wp-json/');
     745            const url = (base.endsWith('/') ? base : base + '/') + 'featured-image-from-url/v1/url/' + postId + '?_ts=' + Date.now();
     746            fetch(url, {
     747                method: 'GET',
     748                headers: {
     749                    'X-WP-Nonce': (window.wpApiSettings && window.wpApiSettings.nonce) || (typeof fifuScriptVars !== 'undefined' ? fifuScriptVars.nonce : '')
     750                },
     751                credentials: 'same-origin'
     752            }).then(function (res) {
     753                // Only proceed on 2xx
     754                if (!res || !res.ok)
     755                    return null;
     756                // Endpoint returns a JSON-encoded string (the URL)
     757                if (res.headers.get('content-type') && res.headers.get('content-type').indexOf('application/json') !== -1)
     758                    return res.json();
     759                return res.text();
     760            }).then(function (value) {
     761                if (value == null)
     762                    return;
     763                var newUrl = '';
     764                if (typeof value === 'string') {
     765                    newUrl = value;
     766                } else if (value && typeof value === 'object') {
     767                    // In case the server wraps it differently in some environments
     768                    newUrl = value.url || '';
     769                }
     770
     771                if (!newUrl)
     772                    return;
     773
     774                // Only update if different from current input
     775                var $input = jQuery('#fifu_input_url');
     776                if (!$input.length)
     777                    return;
     778
     779                const current = ($input.val() || '').trim();
     780                if (current === newUrl)
     781                    return;
     782
     783                $input.val(newUrl)
     784                        .trigger('input')
     785                        .trigger('change');
     786                // Recompute preview and controls
     787                if (typeof fifu_get_sizes === 'function')
     788                    fifu_get_sizes();
     789            }).catch(function () {
     790                // Ignore network/rest errors silently
     791            });
     792        }
     793
     794        wp.data.subscribe(function () {
     795            try {
     796                const sel = wp.data.select('core/editor');
     797                // Some WP versions may not expose didPostSaveRequestSucceed
     798                if (!sel || !sel.isSavingPost)
     799                    return;
     800
     801                const isSaving = !!sel.isSavingPost();
     802                const isAutosaving = !!(sel.isAutosavingPost && sel.isAutosavingPost());
     803                const didSucceed = sel.didPostSaveRequestSucceed ? !!sel.didPostSaveRequestSucceed() : true;
     804
     805                // Detect transition: saving -> not saving, success, and not autosave
     806                if (wasSaving && !isSaving && didSucceed && !isAutosaving) {
     807                    const postId = (sel.getCurrentPostId && sel.getCurrentPostId()) || (fifuMetaBoxVars && fifuMetaBoxVars.get_the_ID) || null;
     808                    fetchAndApplyFifuUrl(postId);
     809                }
     810
     811                wasSaving = isSaving;
     812            } catch (e) {
     813                // no-op
     814            }
     815        });
     816    } catch (e) {
     817        // Guard for non-Gutenberg screens
     818    }
     819})();
  • featured-image-from-url/trunk/admin/html/menu.html

    r3352132 r3356282  
    57265726                            <div style="font-style:italic;font-size:13px;color:white;padding-left:40px"><?php $fifu['start']['post']['new']() ?></div>
    57275727                            <br>
    5728                             <div style="font-style:italic;font-size:13px;color:white;padding-left:40px"><?php $fifu['start']['post']['box']() ?> <span class="dashicons dashicons-camera" style="font-size:18px;padding-right:3px"></span><b><?php $fifu['start']['post']['featured']() ?></b>;</div>
     5728                            <div style="font-style:italic;font-size:13px;color:white;padding-left:40px"><?php $fifu['start']['post']['box']() ?> <span class="dashicons dashicons-camera" style="font-size:18px;padding-right:3px"></span><b><?php $fifu['start']['post']['featured']() ?></b></div>
    57295729                            <br>
    57305730                            <div style="font-style:italic;font-size:13px;color:white;padding-left:40px"><?php $fifu['start']['post']['address']() ?></div>
     
    58285828    </div>
    58295829</div>
    5830 
    5831 <script>
    5832     jQuery(document).ready(function () {
    5833         removeNotices();
    5834     });
    5835 
    5836     // remove notices from other plugins
    5837     setTimeout(function () {
    5838         removeNotices();
    5839     }, 250);
    5840 
    5841     function removeNotices() {
    5842         jQuery('div.header-box > div').remove();
    5843         jQuery('.notice').remove();
    5844         jQuery('[id^=wp-admin-bar-_]').remove();
    5845         jQuery('.pms-cross-promo').remove();
    5846     }
    5847 </script>
  • featured-image-from-url/trunk/admin/html/meta-box.html

    r3348282 r3356282  
    3939                       name="fifu_input_url"
    4040                       placeholder="<?php $fifu['image']['keywords']() ?>"
    41                        value="<?php echo esc_url($url ?? ''); ?>"
     41                       value="<?php echo esc_attr($url ?? ''); ?>"
    4242                       style="<?php echo $width ?>;font-size:13px;" />
    4343            </td>
  • featured-image-from-url/trunk/admin/html/support-data.html

    r3344902 r3356282  
    6363
    6464<script>
    65     jQuery(document).ready(function () {
    66         removeNotices();
    67     });
    68 
    69     // remove notices from other plugins
    70     setTimeout(function () {
    71         removeNotices();
    72     }, 250);
    73 
    74     function removeNotices() {
    75         jQuery('div.header-box > div').remove();
    76         jQuery('.notice').remove();
    77         jQuery('[id^=wp-admin-bar-_]').remove();
    78         jQuery('.pms-cross-promo').remove();
    79     }
    80 
    8165    jQuery('#fifu-clipboard').on('click', function() {
    8266        jQuery("#bar").select();
  • featured-image-from-url/trunk/admin/html/troubleshooting.html

    r3341179 r3356282  
    15121512    </div>
    15131513</div>
    1514 
    1515 <script>
    1516     jQuery(document).ready(function () {
    1517         removeNotices();
    1518     });
    1519 
    1520     // remove notices from other plugins
    1521     setTimeout(function () {
    1522         removeNotices();
    1523     }, 250);
    1524 
    1525     function removeNotices() {
    1526         jQuery('div.header-box > div').remove();
    1527         jQuery('.notice').remove();
    1528         jQuery('[id^=wp-admin-bar-_]').remove();
    1529         jQuery('.pms-cross-promo').remove();
    1530     }
    1531 </script>
  • featured-image-from-url/trunk/admin/menu.php

    r3348282 r3356282  
    1313
    1414    if (isset($_SERVER['REQUEST_URI']) && (strpos($_SERVER['REQUEST_URI'], FIFU_SLUG) !== false || strpos($_SERVER['REQUEST_URI'], 'fifu') !== false)) {
    15         wp_enqueue_script('font-awesome', 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.1.1/js/all.min.js');
     15        wp_enqueue_script('font-awesome', 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/7.0.0/js/all.min.js');
    1616        wp_enqueue_style('jquery-ui-style1', 'https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.css');
    1717        wp_enqueue_style('jquery-ui-style2', 'https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.structure.min.css');
    1818        wp_enqueue_style('jquery-ui-style3', 'https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.theme.min.css');
    1919
    20         wp_enqueue_style('fifu-pro-css', plugins_url('/html/css/pro.css', __FILE__), array(), fifu_version_number_enq());
     20        $pro_css_path = plugin_dir_path(__FILE__) . '/html/css/pro.css';
     21        $pro_css_ver = file_exists($pro_css_path) ? filemtime($pro_css_path) : fifu_version_number_enq();
     22        wp_enqueue_style('fifu-pro-css', plugins_url('/html/css/pro.css', __FILE__), array(), $pro_css_ver);
    2123
    2224        wp_enqueue_script('jquery-ui', 'https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js');
     
    4547    add_submenu_page(FIFU_SLUG, 'FIFU Troubleshooting', $fifu['options']['troubleshooting'](), 'manage_options', 'fifu-troubleshooting', 'fifu_troubleshooting');
    4648    add_submenu_page(FIFU_SLUG, 'FIFU Status', $fifu['options']['status'](), 'manage_options', 'fifu-support-data', 'fifu_support_data');
    47     add_submenu_page(FIFU_SLUG, 'FIFU Pro', '<a href="https://fifu.app/" target="_blank"><div style="padding:5px;color:white;background-color:#1da867">' . $fifu['options']['upgrade']() . '</div></a>', 'manage_options', '#', null);
     49    add_submenu_page(
     50            FIFU_SLUG,
     51            'FIFU Pro',
     52            '<a href="https://fifu.app/" target="_blank"><div style="padding:5px;color:#111;background-color:#d4af37">' . $fifu['options']['upgrade']() . '</div></a>',
     53            'manage_options',
     54            '#',
     55            null
     56    );
    4857
    4958    add_action('admin_init', 'fifu_get_menu_settings');
  • featured-image-from-url/trunk/admin/strings.php

    r3352132 r3356282  
    330330        _e("Auto set featured image from Unsplash using tags", FIFU_SLUG);
    331331    };
     332    $fifu['title']['share'] = function () {
     333        _e("Auto-share to social media", FIFU_SLUG);
     334    };
    332335    $fifu['title']['block'] = function () {
    333336        _e("Disable right-click", FIFU_SLUG);
     
    16341637    // pro
    16351638    $fifu['unlock'] = function () {
    1636         _e("Unlock all PRO features for €29.90", FIFU_SLUG);
     1639        _e("Upgrade to PRO", FIFU_SLUG);
    16371640    };
    16381641
     
    20092012    // pro
    20102013    $fifu['unlock'] = function () {
    2011         return __("Unlock all PRO features for €29.90", FIFU_SLUG);
     2014        return __("Upgrade to PRO", FIFU_SLUG);
    20122015    };
    20132016
     
    20472050    };
    20482051    $fifu['unsplash']['unlock'] = function () {
    2049         return __("Unlock all PRO features for €29.90", FIFU_SLUG);
     2052        return __("Upgrade to PRO", FIFU_SLUG);
    20502053    };
    20512054    $fifu['unsplash']['more'] = function () {
     
    23342337    // pro
    23352338    $fifu['unlock'] = function () {
    2336         _e("Unlock all PRO features for €29.90", FIFU_SLUG);
     2339        _e("Upgrade to PRO", FIFU_SLUG);
    23372340    };
    23382341
     
    24912494    };
    24922495    $fifu['upgrade'] = function () {
    2493         return __("Upgrade to <b>PRO</b> for €29.90", FIFU_SLUG);
     2496        return __("Upgrade to <b>PRO</b>", FIFU_SLUG);
    24942497    };
    24952498    $fifu['star'] = function () {
     
    24982501    $fifu['settings'] = function () {
    24992502        return __("Settings", FIFU_SLUG);
     2503    };
     2504
     2505    // Plugins screen and review notice
     2506    $fifu['rate'] = function () {
     2507        return __("Rate ★★★★★", FIFU_SLUG);
     2508    };
     2509    $fifu['review']['title'] = function () {
     2510        return __("Enjoying FIFU?", FIFU_SLUG);
     2511    };
     2512    $fifu['review']['message'] = function () {
     2513        return __("If the Featured Image from URL plugin helps you, please consider leaving a 5-star review. It means a lot!", FIFU_SLUG);
     2514    };
     2515    $fifu['review']['leave'] = function () {
     2516        return __("Leave a review", FIFU_SLUG);
     2517    };
     2518    $fifu['review']['later'] = function () {
     2519        return __("Not now", FIFU_SLUG);
     2520    };
     2521    $fifu['review']['done'] = function () {
     2522        return __("I already did", FIFU_SLUG);
     2523    };
     2524    $fifu['review']['help'] = function () {
     2525        return __("Need help?", FIFU_SLUG);
    25002526    };
    25012527
  • featured-image-from-url/trunk/featured-image-from-url.php

    r3352132 r3356282  
    55 * Plugin URI: https://fifu.app/
    66 * Description: Use remote media as the featured image and beyond.
    7  * Version: 5.2.6
     7 * Version: 5.2.7
    88 * Author: fifu.app
    99 * Author URI: https://fifu.app/
     
    2828// Required includes with error handling
    2929$required_includes = [
     30    FIFU_INCLUDES_DIR . '/util.php',
     31    FIFU_INCLUDES_DIR . '/structured-data.php',
    3032    FIFU_INCLUDES_DIR . '/attachment.php',
    3133    FIFU_INCLUDES_DIR . '/convert-url.php',
     
    3537    FIFU_INCLUDES_DIR . '/thumbnail.php',
    3638    FIFU_INCLUDES_DIR . '/thumbnail-category.php',
    37     FIFU_INCLUDES_DIR . '/util.php',
    3839    FIFU_INCLUDES_DIR . '/woo.php'
    3940];
     
    6061    FIFU_ADMIN_DIR . '/rsa.php',
    6162    FIFU_ADMIN_DIR . '/strings.php',
     63    FIFU_ADMIN_DIR . '/review.php',
    6264    FIFU_ADMIN_DIR . '/sheet-editor.php',
    6365    FIFU_ADMIN_DIR . '/transient.php',
     
    9799            fifu_activate_actions();
    98100            fifu_set_author();
     101            // record installed time per site for review timing
     102            if (!get_option('fifu_installed_time'))
     103                update_option('fifu_installed_time', time());
    99104            restore_current_blog();
    100105        }
     
    107112        // Set redirect transient only for non-multisite
    108113        set_transient('fifu_redirect_to_settings', true, 30);
     114        // record installed time for review timing
     115        if (!get_option('fifu_installed_time'))
     116            update_option('fifu_installed_time', time());
    109117    }
    110118}
     
    170178    $strings = fifu_get_strings_plugins();
    171179    $links[] = '<a href="' . esc_url(get_admin_url(null, 'admin.php?page=featured-image-from-url')) . '">' . $strings['settings']() . '</a>';
     180    $links[] = '<a href="https://fifu.app/" target="_blank" rel="noopener noreferrer">' . $strings['upgrade']() . '</a>';
    172181    return $links;
    173182}
     
    177186function fifu_row_meta($plugin_meta, $plugin_file, $plugin_data, $status) {
    178187    if (strpos($plugin_file, 'featured-image-from-url.php') !== false) {
     188        $strings = fifu_get_strings_plugins();
    179189        $email = '<a style="color:#2271b1">[email protected]</a>';
    180190        $new_links = array(
    181191            'email' => $email,
     192            'rate' => '<a href="https://wordpress.org/support/plugin/featured-image-from-url/reviews/?filter=5#new-post" target="_blank" rel="noopener noreferrer">' . $strings['rate']() . '</a>',
    182193        );
    183194        $plugin_meta = array_merge($plugin_meta, $new_links);
     
    224235    }
    225236});
     237
  • featured-image-from-url/trunk/includes/external-post.php

    r3328447 r3356282  
    4949
    5050        $src = fifu_get_attribute('src', $tag);
    51         if (!preg_match('/^https?:\/\//', $src))
     51        // resolve to absolute URL (supports relative and protocol-relative)
     52        $abs = fifu_resolve_absolute_url($post_id, $src);
     53        if (!$abs)
    5254            continue;
    5355
     
    7476    // src
    7577    $src = fifu_get_attribute('src', $tag);
    76 
    77     if (!preg_match('/^https?:\/\//', $src))
     78    $abs = fifu_resolve_absolute_url($post_id, $src);
     79    if (!$abs)
    7880        return null;
    7981
    80     return $src;
     82    return $abs;
     83}
     84
     85// Resolve relative or protocol-relative URLs to absolute using the post permalink as base
     86function fifu_resolve_absolute_url($post_id, $url) {
     87    $url = trim((string) $url);
     88    if ($url === '')
     89        return null;
     90
     91    // ignore data URIs
     92    if (stripos($url, 'data:') === 0)
     93        return null;
     94
     95    // already absolute
     96    if (preg_match('/^https?:\/\//i', $url))
     97        return $url;
     98
     99    // looks like a domain without scheme (e.g., youtu.be/abc, www.example.com/a)
     100    if (preg_match('/^(?:www\.)?[a-z0-9.-]+\.[a-z]{2,}(?::\d+)?(?:\/.+)?$/i', $url)) {
     101        $scheme = is_ssl() ? 'https' : 'http';
     102        return $scheme . '://' . ltrim($url, '/');
     103    }
     104
     105    // protocol-relative (e.g., //example.com/img.jpg)
     106    if (strpos($url, '//') === 0) {
     107        $scheme = is_ssl() ? 'https:' : 'http:';
     108        return $scheme . $url;
     109    }
     110
     111    // base for resolution: post permalink or site home
     112    $base = get_permalink($post_id);
     113    if (!$base)
     114        $base = home_url('/');
     115
     116    $base_parts = wp_parse_url($base);
     117    if (!$base_parts || empty($base_parts['host']))
     118        return null;
     119
     120    $scheme = $base_parts['scheme'] ?? (is_ssl() ? 'https' : 'http');
     121    $host = $base_parts['host'];
     122    $port = isset($base_parts['port']) ? ':' . $base_parts['port'] : '';
     123    $base_path = $base_parts['path'] ?? '/';
     124
     125    // same-host absolute-path reference
     126    if (isset($url[0]) && $url[0] === '/') {
     127        $path = $url;
     128        $path = fifu_remove_dot_segments($path);
     129        return $scheme . '://' . $host . $port . $path;
     130    }
     131
     132    // query-only or fragment-only reference
     133    if (isset($url[0]) && ($url[0] === '?' || $url[0] === '#')) {
     134        return $scheme . '://' . $host . $port . $base_path . $url;
     135    }
     136
     137    // relative path reference
     138    $dir = (substr($base_path, -1) === '/') ? rtrim($base_path, '/') : rtrim(dirname($base_path), '/');
     139    if ($dir === '/' || $dir === '\\')
     140        $dir = '';
     141    $path = ($dir ? $dir : '') . '/' . $url;
     142    $path = fifu_remove_dot_segments($path);
     143
     144    return $scheme . '://' . $host . $port . $path;
     145}
     146
     147function fifu_remove_dot_segments($path) {
     148    $leading_slash = (strlen($path) > 0 && $path[0] === '/');
     149    $segments = explode('/', $path);
     150    $output = [];
     151    foreach ($segments as $seg) {
     152        if ($seg === '' || $seg === '.') {
     153            continue;
     154        }
     155        if ($seg === '..') {
     156            array_pop($output);
     157            continue;
     158        }
     159        $output[] = $seg;
     160    }
     161    $normalized = ($leading_slash ? '/' : '') . implode('/', $output);
     162    // preserve trailing slash if original had it and not query/fragment
     163    if ($normalized !== '/' && substr($path, -1) === '/')
     164        $normalized .= '/';
     165    return $normalized === '' ? '/' : $normalized;
    81166}
    82167
  • featured-image-from-url/trunk/includes/thumbnail.php

    r3352132 r3356282  
    44
    55add_filter('wp_head', 'fifu_add_js');
    6 
    7 if (!function_exists('is_plugin_active'))
    8     require_once(ABSPATH . '/wp-admin/includes/plugin.php');
    96
    107global $pagenow;
    118if (!isset($pagenow) || !in_array($pagenow, array('post.php', 'post-new.php', 'admin-ajax.php', 'wp-cron.php'))) {
    12     if (is_plugin_active('wordpress-seo/wp-seo.php')) {
     9    if (fifu_is_yoast_seo_active()) {
    1310        add_action('wpseo_opengraph_image', 'fifu_add_social_tag_yoast');
    1411        add_action('wpseo_twitter_image', 'fifu_add_social_tag_yoast');
     
    1613    } else
    1714        add_filter('wp_head', 'fifu_add_social_tags');
     15
     16    // Always handle FIFU structured data (image-only when SEO plugin is active)
     17    add_action('wp_head', 'fifu_add_structured_data', 99);
    1818}
    1919
     
    7777}
    7878
     79// General ld+json output: when an SEO plugin is active we emit only ImageObject
     80// Otherwise we emit minimal BlogPosting/Product with images
     81function fifu_add_structured_data() {
     82    // Keep behavior consistent with other meta handling
     83    if (is_front_page() || is_home() || is_tax())
     84        return;
     85
     86    $post_id = get_the_ID();
     87    if (!$post_id)
     88        return;
     89
     90    global $wpdb;
     91    $arr = $wpdb->get_col($wpdb->prepare(
     92                    "SELECT meta_value FROM $wpdb->postmeta WHERE post_id = %d AND meta_key LIKE %s",
     93                    $post_id,
     94                    'fifu_%image_url%'
     95            ));
     96
     97    if (empty($arr))
     98        return;
     99
     100    $type = get_post_type($post_id);
     101    if (!empty($arr) && is_singular($type))
     102        fifu_render_structured_data($post_id, $arr);
     103}
     104
    79105function fifu_add_social_tags() {
    80106    if (is_front_page() || is_home() || is_tax())
     
    90116    $title = str_replace("'", "&#39;", strip_tags(get_the_title($post_id)));
    91117    $description = str_replace("'", "&#39;", wp_strip_all_tags(get_post_field('post_excerpt', $post_id)));
    92 
    93     $arr = array($url);
    94 
    95     // https://search.google.com/test/rich-results
    96     // Add JSON-LD for WordPress posts and products
    97     $type = get_post_type($post_id);
    98     if (is_singular($type)) {
    99         wp_enqueue_script('fifu-json-ld', plugins_url('/html/js/json-ld.js', __FILE__), array(), fifu_version_number_enq());
    100         $json_ld = [
    101             "@context" => "https://schema.org",
    102             "url" => get_permalink($post_id),
    103             "image" => $arr,
    104         ];
    105         if ($type === 'product') {
    106             $json_ld["@type"] = "Product";
    107             $json_ld["name"] = get_the_title($post_id);
    108         } else {
    109             $json_ld["@type"] = "BlogPosting";
    110             $json_ld["headline"] = get_the_title($post_id);
    111         }
    112         wp_localize_script('fifu-json-ld', 'fifuJsonLd', $json_ld);
    113     }
    114118
    115119    if ($url) {
     
    216220    $alt = get_post_meta($post_id, 'fifu_image_alt', true);
    217221    if (!$alt) {
    218         $alt = strip_tags(get_the_title($post_id));
    219         $title = $title ? $title : $alt;
     222        $alt = esc_attr(strip_tags(get_the_title($post_id)));
     223        $title = esc_attr($title ? $title : $alt);
    220224        $custom_alt = 'alt=' . $delimiter . $alt . $delimiter . ' title=' . $delimiter . $title . $delimiter;
    221225        $html = preg_replace('/alt=[\'\"][^[\'\"]*[\'\"]/', $custom_alt, $html);
     
    223227    } else {
    224228        $alt = strip_tags($alt);
    225         $title = $title ? $title : $alt;
     229        $title = esc_attr($title ? $title : $alt);
    226230        if ($url && $alt) {
    227231            $html = preg_replace('/alt=[\'\"][^[\'\"]*[\'\"]/', 'alt=' . $delimiter . $alt . $delimiter . ' title=' . $delimiter . $title . $delimiter, $html);
     
    262266    }
    263267
    264     return sprintf('<img src="%s" alt="%s" title="%s" style="%s" data-large_image="%s" data-large_image_width="%s" data-large_image_height="%s" onerror="%s" width="%s" height="%s">', $url, $alt, $alt, $css, $url, "800", "600", "jQuery(this).hide();", $width, $height);
     268    $safe_url = esc_url($url);
     269    $safe_alt = esc_attr($alt);
     270    $safe_css = esc_attr($css);
     271    $safe_width = esc_attr($width);
     272    $safe_height = esc_attr($height);
     273
     274    return sprintf(
     275            '<img src="%s" alt="%s" title="%s" style="%s" data-large_image="%s" data-large_image_width="%s" data-large_image_height="%s" onerror="%s" width="%s" height="%s">',
     276            $safe_url,
     277            $safe_alt,
     278            $safe_alt,
     279            $safe_css,
     280            $safe_url,
     281            "800",
     282            "600",
     283            "jQuery(this).hide();",
     284            $safe_width,
     285            $safe_height
     286    );
    265287}
    266288
     
    291313            foreach ($matches[1] as $match) {
    292314                $content_img_url = html_entity_decode($match);
    293                 if ($content_img_url == $att_url) {
     315                // Simple: exact or content URL is a substring of the attachment URL
     316                $should_replace = ($content_img_url == $att_url) || ($content_img_url !== '' && strpos($att_url, $content_img_url) !== false);
     317                if ($should_replace) {
    294318                    $content = preg_replace('/<img[^>]+src=[\'"]' . preg_quote($match, '/') . '[\'"][^>]*>/i', '', $content, 1);
    295319                    return $content;
  • featured-image-from-url/trunk/includes/util.php

    r3344902 r3356282  
    11<?php
     2
     3// Ensure WordPress plugin functions are available for helper checks
     4require_once(ABSPATH . '/wp-admin/includes/plugin.php');
    25
    36function fifu_get_attribute($attribute, $html) {
     
    327330}
    328331
     332// SEO plugins helpers used across the plugin
     333function fifu_is_yoast_seo_active() {
     334    return is_plugin_active('wordpress-seo/wp-seo.php');
     335}
     336
     337function fifu_is_aioseo_active() {
     338    // Support free and pro identifiers
     339    return is_plugin_active('all-in-one-seo-pack/all_in_one_seo_pack.php') ||
     340            is_plugin_active('all-in-one-seo-pack-pro/all_in_one_seo_pack.php');
     341}
     342
     343function fifu_is_any_seo_plugin_active() {
     344    return fifu_is_yoast_seo_active() || fifu_is_rank_math_seo_active() || fifu_is_aioseo_active();
     345}
     346
    329347function fifu_is_gravity_forms_active() {
    330348    return is_plugin_active('gravityforms/gravityforms.php');
  • featured-image-from-url/trunk/readme.txt

    r3352132 r3356282  
    55Requires at least: 5.6
    66Tested up to: 6.8.2
    7 Stable tag: 5.2.6
     7Stable tag: 5.2.7
    88License: GPLv3
    99License URI: https://www.gnu.org/licenses/gpl-3.0.html
    1010
    11 Use a remote image/video/audio as featured image of a post or WooCommerce product.
     11Use remote media as the featured image and beyond.
    1212
    1313== Description ==
     
    141141#### Links
    142142
    143 * **<a href="https://fifu.app/">FIFU PRO (€29.90)</a>**
     143* **<a href="https://fifu.app/">FIFU PRO</a>**
    144144* **<a href="https://tastewp.com/new?pre-installed-plugin-slug=featured-image-from-url&redirect=admin.php%3Fpage%3Dfeatured-image-from-url&ni=true">Dummy site for testing</a>**
    145145* **<a href="https://chrome.google.com/webstore/detail/fifu-scraper/pccimcccbkdeeadhejdmnffmllpicola">Extension for Google Chrome</a>**
     
    247247== Changelog ==
    248248
     249= 5.2.7 =
     250* New: Notice to rate the plugin; Enhancement: Auto set featured media from post content (now supports local relative URLs); Fix: Incomplete product data generated for Rich Results.
     251
    249252= 5.2.6 =
    250253* Enhancement: improved integration with Rich Results from Google; Enhancement: alternative text can now be displayed as captions at the bottom of the image; Fix: images defined by other plugins were being displayed on social media instead of the remote featured image; Fix: Elementor widget not working with newer Elementor versions.
     
    280283== Upgrade Notice ==
    281284
    282 = 5.2.6 =
    283 * Enhancement: improved integration with Rich Results from Google; Enhancement: alternative text can now be displayed as captions at the bottom of the image; Fix: images defined by other plugins were being displayed on social media instead of the remote featured image; Fix: Elementor widget not working with newer Elementor versions.
     285= 5.2.7 =
     286* New: Notice to rate the plugin; Enhancement: Auto set featured media from post content (now supports local relative URLs); Fix: Incomplete product data generated for Rich Results.
Note: See TracChangeset for help on using the changeset viewer.