From 86d965dcd9681867de2fee177a325a2a31ac1621 Mon Sep 17 00:00:00 2001 From: ben Date: Tue, 29 Sep 2026 09:56:22 +0200 Subject: [PATCH 1/7] feat(wordpress): 1.2.1 lists Site Editor templates, parts, patterns and menus in the PDF links report, 20 rows per page --- .../svn/assets/blueprints/blueprint.json | 2 +- wordpress/svn/trunk/README.txt | 12 +- wordpress/svn/trunk/simplepdf-embed.php | 211 +++++++++++++++--- 3 files changed, 188 insertions(+), 37 deletions(-) diff --git a/wordpress/svn/assets/blueprints/blueprint.json b/wordpress/svn/assets/blueprints/blueprint.json index 3050e75..ff3b21b 100644 --- a/wordpress/svn/assets/blueprints/blueprint.json +++ b/wordpress/svn/assets/blueprints/blueprint.json @@ -13,7 +13,7 @@ "step": "installPlugin", "pluginZipFile": { "resource": "url", - "url": "https://downloads.wordpress.org/plugin/simplepdf-embed.1.2.0.zip" + "url": "https://downloads.wordpress.org/plugin/simplepdf-embed.1.2.1.zip" }, "options": { "activate": true diff --git a/wordpress/svn/trunk/README.txt b/wordpress/svn/trunk/README.txt index 6106a04..3c1a589 100644 --- a/wordpress/svn/trunk/README.txt +++ b/wordpress/svn/trunk/README.txt @@ -2,7 +2,7 @@ Contributors: bendersej Tags: pdf, pdf form, fill pdf, sign pdf, form submissions Tested up to: 7.1.2 -Stable tag: 1.2.0 +Stable tag: 1.2.1 License: GPLv2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html Requires at least: 5.8 @@ -37,7 +37,7 @@ Visitors fill and sign your PDFs, then download them. The document is filled in * Every PDF link opens in SimplePDF by default, whatever the extension case (`Consent.PDF`) or query string (`form.pdf?ver=2`) * Or limit it to the pages and posts you pick, and try it on a draft before going live -* The settings page lists the PDF links on your pages and posts, and where each one opens +* The settings page lists the PDF links on your pages, posts, Site Editor templates, patterns and menus, and where each one opens * Add the `exclude-simplepdf` class to a link to keep the browser's own PDF viewer * Visitors who browse with an AI assistant (ChatGPT's browser, Chrome with WebMCP) can ask it to fill the form for them, and check every answer before they submit. One checkbox turns it off. @@ -98,13 +98,13 @@ Every PDF link on your site now opens in SimplePDF. Open Settings > SimplePDF Em = A PDF link does not open in SimplePDF = -Settings > SimplePDF Embed lists the PDF links on your pages and posts, with the reason next to any link that opens in the browser. The usual causes: +Settings > SimplePDF Embed lists the PDF links on your pages, posts, Site Editor templates, patterns and menus, with the reason next to any link that opens in the browser. The usual causes: * The link does not point to a `.pdf` file * The link has the `exclude-simplepdf` class * The page is not picked in "Where it runs" -Links added by a page builder, a menu or a widget open the same way but are not listed. +Links added by a page builder, a classic menu or a widget open the same way but are not listed. = How do I keep one PDF in the browser's viewer? = @@ -138,6 +138,10 @@ This plugin connects to SimplePDF (https://simplepdf.com) in two ways. == Changelog == += 1.2.1 = +* The PDF links report now also lists the links in your Site Editor templates, template parts, synced patterns and navigation menus, like your home page, header and footer +* The report shows 20 rows at a time, with page navigation + = 1.2.0 = * New settings page: see every PDF link on your pages and posts, and where each one opens * Choose where it runs: everywhere, or only on the pages and posts you pick, drafts included diff --git a/wordpress/svn/trunk/simplepdf-embed.php b/wordpress/svn/trunk/simplepdf-embed.php index 540831a..30756e6 100644 --- a/wordpress/svn/trunk/simplepdf-embed.php +++ b/wordpress/svn/trunk/simplepdf-embed.php @@ -5,7 +5,7 @@ Author: SimplePDF Author URI: https://simplepdf.com Description: Visitors fill and sign your PDFs right on your site. With a SimplePDF account, every filled PDF comes back to you, automatically. -Version: 1.2.0 +Version: 1.2.1 License: GPL v2 or later License URI: https://www.gnu.org/licenses/gpl-2.0.html */ @@ -14,10 +14,11 @@ require_once __DIR__ . '/pdf-links.php'; -define('SIMPLEPDF_PLUGIN_VERSION', '1.2.0'); +define('SIMPLEPDF_PLUGIN_VERSION', '1.2.1'); define('SIMPLEPDF_SETTINGS_SCREEN', 'settings_page_simplepdf_settings'); define('SIMPLEPDF_POST_LIST_LIMIT', 300); define('SIMPLEPDF_PDF_PAGE_LIMIT', 100); +define('SIMPLEPDF_PDF_TABLE_PAGE_SIZE', 20); define('SIMPLEPDF_WEB_EMBED_VERSION', '1.9.0'); define('SIMPLEPDF_REVIEW_URL', 'https://wordpress.org/support/plugin/simplepdf-embed/reviews/#new-post'); define('SIMPLEPDF_PRICING_URL', 'https://simplepdf.com/pricing?ref=wordpress'); @@ -307,6 +308,7 @@ function simplepdf_admin_css() { .simplepdf-step-text { display: flex; flex-direction: column; gap: 2px; } .simplepdf-get-started .button { margin-left: auto; min-width: 11rem; text-align: center; } .simplepdf-pdf-table { margin-top: 12px; } +.simplepdf-pdf-pagination { margin: 8px 0 0; } .simplepdf-pdf-table td:first-child { width: 30%; } .simplepdf-pdf-links { margin: 0; } .simplepdf-pdf-links li { display: flex; align-items: baseline; gap: 8px; margin-bottom: 6px; } @@ -354,19 +356,74 @@ function simplepdf_render_header() { __('%s (template)', 'simplepdf-embed'), + /* translators: %s: template part title, e.g. Header */ + 'wp_template_part' => __('%s (template part)', 'simplepdf-embed'), + /* translators: %s: synced pattern title */ + 'wp_block' => __('%s (pattern)', 'simplepdf-embed'), + /* translators: %s: navigation menu title */ + 'wp_navigation' => __('%s (menu)', 'simplepdf-embed'), + ); +} + +function simplepdf_is_site_part($post) { + return array_key_exists($post->post_type, simplepdf_site_part_label_formats()); +} + +// A theme's customized templates stay in wp_posts after a theme switch; only the active theme's show on the site. +function simplepdf_is_shown_on_site($post) { + switch ( $post->post_type ) { + case 'wp_template': + case 'wp_template_part': + return has_term(get_stylesheet(), 'wp_theme', $post); + default: + return true; + } +} + +// A navigation menu saves each item's URL as a block attribute, not as an tag. +function simplepdf_get_navigation_link_html($blocks) { + $link_html = ''; + foreach ( $blocks as $block ) { + $url = isset($block['attrs']['url']) && is_string($block['attrs']['url']) ? $block['attrs']['url'] : ''; + $link_html .= $url === '' ? '' : ''; + $link_html .= simplepdf_get_navigation_link_html($block['innerBlocks']); + } + + return $link_html; +} + +function simplepdf_get_linked_html($post) { + switch ( $post->post_type ) { + case 'wp_navigation': + return simplepdf_get_navigation_link_html(parse_blocks($post->post_content)); + default: + return $post->post_content; + } +} + function simplepdf_scan_pages_with_pdf_links() { global $wpdb; + $post_types = array_merge(array('page', 'post'), array_keys(simplepdf_site_part_label_formats())); + $post_type_placeholders = implode(', ', array_fill(0, count($post_types), '%s')); + // Site parts first: a handful of rows that show on every page, never pushed out of the cap by recent posts. $post_ids = $wpdb->get_col($wpdb->prepare( "SELECT ID FROM {$wpdb->posts} - WHERE post_type IN ('page', 'post') + WHERE post_type IN ($post_type_placeholders) AND post_status IN ('publish', 'private', 'draft') AND (post_content LIKE %s OR post_content LIKE %s) - ORDER BY post_modified DESC + ORDER BY post_type IN ('page', 'post'), post_modified DESC LIMIT %d", - '%' . $wpdb->esc_like('.pdf') . '%', - '%' . $wpdb->esc_like('simplepdf') . '%', - SIMPLEPDF_PDF_PAGE_LIMIT + array_merge($post_types, array( + '%' . $wpdb->esc_like('.pdf') . '%', + '%' . $wpdb->esc_like('simplepdf') . '%', + SIMPLEPDF_PDF_PAGE_LIMIT, + )) )); if ( empty($post_ids) ) { return array('pages' => array(), 'is_capped' => false); @@ -374,18 +431,17 @@ function simplepdf_scan_pages_with_pdf_links() { $posts = get_posts(array( 'post__in' => array_map('absint', $post_ids), - 'post_type' => array('page', 'post'), + 'post_type' => $post_types, 'post_status' => array('publish', 'private', 'draft'), 'orderby' => 'post__in', 'numberposts' => -1, 'update_post_meta_cache' => false, - 'update_post_term_cache' => false, )); $pages = array(); - foreach ( $posts as $post ) { - $page_url = get_permalink($post); - $pdf_links = simplepdf_extract_pdf_links($post->post_content, function ($href) use ($page_url) { + foreach ( array_filter($posts, 'simplepdf_is_shown_on_site') as $post ) { + $page_url = simplepdf_is_site_part($post) ? home_url('/') : get_permalink($post); + $pdf_links = simplepdf_extract_pdf_links(simplepdf_get_linked_html($post), function ($href) use ($page_url) { return $href === '' ? $href : WP_Http::make_absolute_url($href, $page_url); }); if ( ! empty($pdf_links) ) { @@ -413,14 +469,29 @@ function simplepdf_runs_on_post($post_id) { || in_array($post_id, simplepdf_get_selected_post_ids(), true); } -function simplepdf_get_link_outcome($pdf_link, $runs_on_page, $has_missing_account) { +// 'runs' | 'not_picked' | 'picked_pages': whether the script loads where the link shows. +function simplepdf_get_page_scope($post) { + if ( simplepdf_is_site_part($post) && simplepdf_get_load_scope() === 'everywhere' ) { + return 'runs'; + } + if ( simplepdf_is_site_part($post) ) { + return empty(simplepdf_get_selected_post_ids()) ? 'not_picked' : 'picked_pages'; + } + + return simplepdf_runs_on_post($post->ID) ? 'runs' : 'not_picked'; +} + +function simplepdf_get_link_outcome($pdf_link, $page_scope, $has_missing_account) { switch ( $pdf_link['link_type'] ) { case 'opens_in_simplepdf': - if ( ! $runs_on_page ) { - return array('opens_in' => 'browser', 'reason' => ''); + switch ( $page_scope ) { + case 'runs': + return array('opens_in' => $has_missing_account ? 'error' : 'simplepdf', 'reason' => ''); + case 'picked_pages': + return array('opens_in' => 'picked_pages', 'reason' => ''); + default: + return array('opens_in' => 'browser', 'reason' => ''); } - - return array('opens_in' => $has_missing_account ? 'error' : 'simplepdf', 'reason' => ''); case 'excluded': return array('opens_in' => 'browser', 'reason' => __('The link has the exclude-simplepdf class', 'simplepdf-embed')); case 'not_recognised': @@ -430,14 +501,21 @@ function simplepdf_get_link_outcome($pdf_link, $runs_on_page, $has_missing_accou } } +// '' for a site part that shows on many pages rather than at one address. function simplepdf_get_view_url($post) { - return $post->post_status === 'publish' ? get_permalink($post) : get_preview_post_link($post); + if ( ! simplepdf_is_site_part($post) ) { + return $post->post_status === 'publish' ? get_permalink($post) : get_preview_post_link($post); + } + + $is_home_template = $post->post_type === 'wp_template' && in_array($post->post_name, array('home', 'front-page', 'index'), true); + + return $is_home_template ? home_url('/') : ''; } function simplepdf_render_scan_scope_note($is_capped) { ?>

- + ' . esc_html__('SimplePDF', 'simplepdf-embed') . ''; break; + case 'picked_pages': + echo '' . esc_html__('Picked pages', 'simplepdf-embed') . ''; + break; case 'error': echo '' . esc_html__('Error', 'simplepdf-embed') . ''; break; @@ -523,10 +604,20 @@ function simplepdf_render_pdf_table($pages_with_pdf_links) { - - - + + + + + + + + + + + + +

@@ -558,26 +649,26 @@ function simplepdf_build_pdf_link_report() { $scan = simplepdf_get_pages_with_pdf_links(); $company_identifier = simplepdf_get_company_identifier(); $has_missing_account = $company_identifier !== '' && simplepdf_get_account_status($company_identifier) === 'not_found'; - $counts = array('simplepdf' => 0, 'browser' => 0, 'error' => 0, 'not_picked' => 0); + $counts = array('simplepdf' => 0, 'picked_pages' => 0, 'browser' => 0, 'error' => 0, 'not_picked' => 0); $pages = array(); foreach ( $scan['pages'] as $page ) { - $runs_on_page = simplepdf_runs_on_post($page['post']->ID); + $page_scope = simplepdf_get_page_scope($page['post']); $pdf_links = array(); foreach ( $page['pdf_links'] as $pdf_link ) { - $outcome = simplepdf_get_link_outcome($pdf_link, $runs_on_page, $has_missing_account); + $outcome = simplepdf_get_link_outcome($pdf_link, $page_scope, $has_missing_account); $counts[$outcome['opens_in']]++; - $counts['not_picked'] += $pdf_link['link_type'] === 'opens_in_simplepdf' && ! $runs_on_page ? 1 : 0; + $counts['not_picked'] += $pdf_link['link_type'] === 'opens_in_simplepdf' && $page_scope === 'not_picked' ? 1 : 0; $pdf_links[] = array_merge($pdf_link, array('outcome' => $outcome)); } - $pages[] = array('post' => $page['post'], 'runs_on_page' => $runs_on_page, 'pdf_links' => $pdf_links); + $pages[] = array('post' => $page['post'], 'page_scope' => $page_scope, 'pdf_links' => $pdf_links); } return array( 'pages' => $pages, 'is_capped' => $scan['is_capped'], 'counts' => $counts, - 'link_count' => $counts['simplepdf'] + $counts['browser'] + $counts['error'], + 'link_count' => $counts['simplepdf'] + $counts['picked_pages'] + $counts['browser'] + $counts['error'], 'account_address' => $company_identifier . '.simplepdf.com', ); } @@ -609,14 +700,15 @@ function simplepdf_get_pdfs_card_header($report) { $counts['simplepdf'], $link_count ); - if ( $counts['simplepdf'] === 0 && $counts['not_picked'] > 0 ) { + $opens_somewhere = $counts['simplepdf'] + $counts['picked_pages'] > 0; + if ( ! $opens_somewhere && $counts['not_picked'] > 0 ) { return array( 'needs_attention' => true, 'title' => $title, 'lede' => __('Your PDF links open in the browser, not in SimplePDF. Switch "Where it runs" to Everywhere, or pick these pages.', 'simplepdf-embed'), ); } - if ( $counts['simplepdf'] === 0 ) { + if ( ! $opens_somewhere ) { return array( 'needs_attention' => true, 'title' => $title, @@ -631,6 +723,50 @@ function simplepdf_get_pdfs_card_header($report) { ); } +function simplepdf_get_pdf_table_page_url($table_page) { + return add_query_arg('pdf_links_page', $table_page, menu_page_url('simplepdf_settings', false)) . '#simplepdf-pdf-links'; +} + +function simplepdf_render_pdf_table_pagination($table_page, $table_page_count, $row_count) { + ?> +
+
+ + + + + 1 ) : ?> + + + + + + + + + + + + + +
+
+ -
+ post_type, $site_part_label_formats) ) { + return sprintf($site_part_label_formats[$post->post_type], $label); + } switch ( $post->post_status ) { case 'draft': From 958902a3b5b1011a32fb2d5bb9bfbed6535a4915 Mon Sep 17 00:00:00 2001 From: ben Date: Tue, 29 Sep 2026 10:01:39 +0200 Subject: [PATCH 2/7] fix(wordpress): review round (missing account on site parts, header with picked pages, only what the site shows, Edit link guard) --- wordpress/svn/trunk/simplepdf-embed.php | 98 +++++++++++++++++-------- 1 file changed, 67 insertions(+), 31 deletions(-) diff --git a/wordpress/svn/trunk/simplepdf-embed.php b/wordpress/svn/trunk/simplepdf-embed.php index 30756e6..c38274b 100644 --- a/wordpress/svn/trunk/simplepdf-embed.php +++ b/wordpress/svn/trunk/simplepdf-embed.php @@ -374,14 +374,20 @@ function simplepdf_is_site_part($post) { return array_key_exists($post->post_type, simplepdf_site_part_label_formats()); } -// A theme's customized templates stay in wp_posts after a theme switch; only the active theme's show on the site. +// A theme's customized templates stay in wp_posts after a theme switch, and an unsynced pattern is only ever +// copied into posts: neither shows on the site as itself. function simplepdf_is_shown_on_site($post) { switch ( $post->post_type ) { + case 'page': + case 'post': + return true; case 'wp_template': case 'wp_template_part': - return has_term(get_stylesheet(), 'wp_theme', $post); + return $post->post_status === 'publish' && has_term(get_stylesheet(), 'wp_theme', $post); + case 'wp_block': + return $post->post_status === 'publish' && get_post_meta($post->ID, 'wp_pattern_sync_status', true) !== 'unsynced'; default: - return true; + return $post->post_status === 'publish'; } } @@ -410,11 +416,10 @@ function simplepdf_scan_pages_with_pdf_links() { global $wpdb; $post_types = array_merge(array('page', 'post'), array_keys(simplepdf_site_part_label_formats())); - $post_type_placeholders = implode(', ', array_fill(0, count($post_types), '%s')); // Site parts first: a handful of rows that show on every page, never pushed out of the cap by recent posts. $post_ids = $wpdb->get_col($wpdb->prepare( "SELECT ID FROM {$wpdb->posts} - WHERE post_type IN ($post_type_placeholders) + WHERE post_type IN (" . implode(', ', array_fill(0, count($post_types), '%s')) . ") AND post_status IN ('publish', 'private', 'draft') AND (post_content LIKE %s OR post_content LIKE %s) ORDER BY post_type IN ('page', 'post'), post_modified DESC @@ -435,7 +440,6 @@ function simplepdf_scan_pages_with_pdf_links() { 'post_status' => array('publish', 'private', 'draft'), 'orderby' => 'post__in', 'numberposts' => -1, - 'update_post_meta_cache' => false, )); $pages = array(); @@ -470,15 +474,31 @@ function simplepdf_runs_on_post($post_id) { } // 'runs' | 'not_picked' | 'picked_pages': whether the script loads where the link shows. +// A site part's ID is never picked, so under picked pages it opens wherever a picked page shows it. function simplepdf_get_page_scope($post) { - if ( simplepdf_is_site_part($post) && simplepdf_get_load_scope() === 'everywhere' ) { + if ( simplepdf_runs_on_post($post->ID) ) { return 'runs'; } - if ( simplepdf_is_site_part($post) ) { - return empty(simplepdf_get_selected_post_ids()) ? 'not_picked' : 'picked_pages'; + if ( ! simplepdf_is_site_part($post) || empty(simplepdf_get_selected_post_ids()) ) { + return 'not_picked'; } - return simplepdf_runs_on_post($post->ID) ? 'runs' : 'not_picked'; + return 'picked_pages'; +} + +function simplepdf_get_page_scope_note($post, $page_scope) { + switch ( $page_scope ) { + case 'runs': + return ''; + case 'picked_pages': + return __('Shows on several pages: opens in SimplePDF on the picked pages that use it', 'simplepdf-embed'); + case 'not_picked': + return simplepdf_is_site_part($post) + ? __('Opens in SimplePDF once you pick a page that shows it, or choose Everywhere', 'simplepdf-embed') + : __('Not picked in "Where it runs"', 'simplepdf-embed'); + default: + return ''; + } } function simplepdf_get_link_outcome($pdf_link, $page_scope, $has_missing_account) { @@ -488,7 +508,9 @@ function simplepdf_get_link_outcome($pdf_link, $page_scope, $has_missing_account case 'runs': return array('opens_in' => $has_missing_account ? 'error' : 'simplepdf', 'reason' => ''); case 'picked_pages': - return array('opens_in' => 'picked_pages', 'reason' => ''); + return array('opens_in' => $has_missing_account ? 'error' : 'picked_pages', 'reason' => ''); + case 'not_picked': + return array('opens_in' => 'browser', 'reason' => ''); default: return array('opens_in' => 'browser', 'reason' => ''); } @@ -507,7 +529,13 @@ function simplepdf_get_view_url($post) { return $post->post_status === 'publish' ? get_permalink($post) : get_preview_post_link($post); } - $is_home_template = $post->post_type === 'wp_template' && in_array($post->post_name, array('home', 'front-page', 'index'), true); + $is_posts_page = $post->post_type === 'wp_template' && $post->post_name === 'home' && get_option('show_on_front') === 'page'; + if ( $is_posts_page ) { + $posts_page_url = get_permalink((int) get_option('page_for_posts')); + + return $posts_page_url === false ? '' : $posts_page_url; + } + $is_home_template = $post->post_type === 'wp_template' && in_array($post->post_name, array('home', 'front-page'), true); return $is_home_template ? home_url('/') : ''; } @@ -610,17 +638,16 @@ function simplepdf_render_pdf_table($pages_with_pdf_links) { - - - - - - - - -
- -
+ + + + + + +
+ +
+