{"id":346282,"date":"2026-07-31T11:58:18","date_gmt":"2026-07-31T11:58:18","guid":{"rendered":"https:\/\/wordpress.org\/plugins\/cannyforge-archive-generator\/"},"modified":"2026-07-31T12:07:36","modified_gmt":"2026-07-31T12:07:36","slug":"cannyforge-archive-generator","status":"publish","type":"plugin","link":"https:\/\/wordpress.org\/plugins\/cannyforge-archive-generator\/","author":12533770,"comment_status":"closed","ping_status":"closed","template":"","meta":{"version":"0.1.1","stable_tag":"0.1.1","tested":"7.0.2","requires":"6.4","requires_php":"8.1","requires_plugins":null,"header_name":"CannyForge Archive Generator","header_author":"","header_description":"A combined HTML sitemap and JavaScript-powered archive with a compact replacement for default WordPress taxonomy pagination.","assets_banners_color":"625a7f","last_updated":"2026-07-31 12:07:36","external_support_url":"","external_repository_url":"","donate_link":"","header_plugin_uri":"","header_author_uri":"","rating":0,"author_block_rating":0,"active_installs":0,"downloads":40,"num_ratings":0,"support_threads":0,"support_threads_resolved":0,"author_block_count":0,"sections":["description","installation","faq","changelog"],"tags":{"0.1.1":{"tag":"0.1.1","author":"user256","date":"2026-07-31 12:07:36"}},"upgrade_notice":[],"ratings":[],"assets_icons":{"icon-128x128.png":{"filename":"icon-128x128.png","revision":3629950,"resolution":"128x128","location":"assets","locale":"","width":128,"height":128},"icon-256x256.png":{"filename":"icon-256x256.png","revision":3629950,"resolution":"256x256","location":"assets","locale":"","width":256,"height":256}},"assets_banners":{"banner-1544x500.png":{"filename":"banner-1544x500.png","revision":3629950,"resolution":"1544x500","location":"assets","locale":"","width":1544,"height":500},"banner-772x250.png":{"filename":"banner-772x250.png","revision":3629950,"resolution":"772x250","location":"assets","locale":"","width":772,"height":250}},"assets_blueprints":{},"all_blocks":[],"tagged_versions":["0.1.1"],"block_files":[],"assets_screenshots":{"screenshot-1.png":{"filename":"screenshot-1.png","revision":3629959,"resolution":"1","location":"assets","locale":"","width":1639,"height":743},"screenshot-2.png":{"filename":"screenshot-2.png","revision":3629959,"resolution":"2","location":"assets","locale":"","width":1641,"height":780},"screenshot-3.png":{"filename":"screenshot-3.png","revision":3629959,"resolution":"3","location":"assets","locale":"","width":1639,"height":743}},"screenshots":{"1":"Content settings: choose an archive style (latest posts, curated, or combined) and tune the news-cycle window.","2":"The generated archive \/ HTML sitemap page, with the client-side search and category \/ tag \/ month \/ author filters.","3":"Display settings: front-end theme layout, colours, and which link details the archive shows."}},"plugin_section":[],"plugin_tags":[1445,2755,900,186,1557],"plugin_category":[55],"plugin_contributors":[262026],"plugin_business_model":[],"class_list":["post-346282","plugin","type-plugin","status-publish","hentry","plugin_tags-archive","plugin_tags-filters","plugin_tags-pagination","plugin_tags-seo","plugin_tags-sitemap","plugin_category-seo-and-marketing","plugin_contributors-user256","plugin_committers-user256"],"banners":{"banner":"https:\/\/ps.w.org\/cannyforge-archive-generator\/assets\/banner-772x250.png?rev=3629950","banner_2x":"https:\/\/ps.w.org\/cannyforge-archive-generator\/assets\/banner-1544x500.png?rev=3629950","banner_rtl":false,"banner_2x_rtl":false},"icons":{"svg":false,"icon":"https:\/\/ps.w.org\/cannyforge-archive-generator\/assets\/icon-128x128.png?rev=3629950","icon_2x":"https:\/\/ps.w.org\/cannyforge-archive-generator\/assets\/icon-256x256.png?rev=3629950","generated":false},"screenshots":[{"src":"https:\/\/ps.w.org\/cannyforge-archive-generator\/assets\/screenshot-1.png?rev=3629959","caption":"Content settings: choose an archive style (latest posts, curated, or combined) and tune the news-cycle window."},{"src":"https:\/\/ps.w.org\/cannyforge-archive-generator\/assets\/screenshot-2.png?rev=3629959","caption":"The generated archive \/ HTML sitemap page, with the client-side search and category \/ tag \/ month \/ author filters."},{"src":"https:\/\/ps.w.org\/cannyforge-archive-generator\/assets\/screenshot-3.png?rev=3629959","caption":"Display settings: front-end theme layout, colours, and which link details the archive shows."}],"raw_content":"<!--section=description-->\n<p>CannyForge Archive Generator gives news and blog sites a combined HTML sitemap and\nJavaScript-powered archive, and replaces WordPress's default taxonomy pagination\nwith a configurable compact sequence that links out to that archive.<\/p>\n\n<p>The release supports PHP 8.1 through 8.5. CI runs the automated unit suite on\neach of those versions; static analysis is pinned to PHP 8.1. The \"Requires\nPHP\" value above is the minimum supported version, not a claim that every\nfuture PHP release is continuously verified.<\/p>\n\n<p>The plugin provides a compact pagination experience and a clear HTML archive\nlink, making older and newer content easier to discover through site\nnavigation. It does not promise a search-ranking or crawler outcome.<\/p>\n\n<p>Features:<\/p>\n\n<ul>\n<li>HTML-sitemap archive page at a crawlable, no-JavaScript URL.<\/li>\n<li>News mode: include content published within a configurable recent window.<\/li>\n<li>Blog mode: a curated URL list, entered manually or imported from CSV.<\/li>\n<li>Client-side search and category \/ tag \/ month \/ author filters.<\/li>\n<li>Configurable compact pagination with a \"View Archive\" link, targeted per archive type.<\/li>\n<li>Optional full archive continuation: <code>\/archive\/<\/code> stays the optimised first\npage and <code>\/archive\/page\/2\/<\/code> onward lists remaining eligible posts without\nJavaScript, newest first.<\/li>\n<li>SEO controls: archive title, meta description, robots directives, canonical.<\/li>\n<li>Content selection: include \/ exclude by term, drop noindex, pin URLs first.<\/li>\n<\/ul>\n\n<h3>External services<\/h3>\n\n<p>This plugin can connect to Google services to fetch top-content signals for the\narchive. These integrations are optional and are only used when the site owner\nconfigures Google credentials and explicitly connects an account.<\/p>\n\n<h4>Google OAuth \/ Search Console \/ Google Analytics 4<\/h4>\n\n<p>What the service is used for:<\/p>\n\n<ul>\n<li>Google OAuth authenticates the site owner's Google account and refreshes API access tokens.<\/li>\n<li>Google Search Console can supply top page URLs for the configured property.<\/li>\n<li>Google Analytics 4 can supply top page paths as the primary signal or as a Search Console fallback.<\/li>\n<\/ul>\n\n<p>What data is sent and received:<\/p>\n\n<ul>\n<li>When the owner starts Connect, the browser visits Google's OAuth\nauthorisation endpoint with the configured client ID, registered redirect\nURI, requested read-only scopes, response type, access type, and a\nshort-lived state value at\n  https:\/\/accounts.google.com\/o\/oauth2\/v2\/auth. Google returns an authorisation code to that\nredirect URI; the code, client ID, client secret, redirect URI, and grant\ntype are sent server-to-server to <code>https:\/\/oauth2.googleapis.com\/token<\/code>.\nGoogle returns access and refresh tokens plus the access-token lifetime.<\/li>\n<li>When the access token is missing or expired, the client ID, client secret,\nrefresh token, and refresh grant type are sent to the same token endpoint.\nThe returned access token and lifetime are cached encrypted locally; the\nrefresh token is retained encrypted until Disconnect or plugin deletion.<\/li>\n<li>After a successful connection that requested Search Console access, the\nplugin automatically lists the account's Search Console properties with a\nGET to <code>https:\/\/www.googleapis.com\/webmasters\/v3\/sites<\/code>. Google returns\nproperty URLs and permissions. The selected property list is cached for\nthe current WordPress user for 10 minutes.<\/li>\n<li>When the owner loads Search Console properties, the same property-list call\nruns. When the owner refreshes top content, the configured Search Console\nproperty URL, date range, dimensions (<code>page<\/code>), row limit, and access token\nare sent to <code>https:\/\/www.googleapis.com\/webmasters\/v3\/sites\/{property}\/searchAnalytics\/query<\/code>.\nGoogle returns page rows; resolved local post IDs, source URLs, and a\nrefresh timestamp are stored in the Search Console cache option until the\nnext refresh, Disconnect, or deletion.<\/li>\n<li>After a successful Analytics wizard connection, the plugin automatically\nlists GA4 accounts and properties with the access token at\n  https:\/\/analyticsadmin.googleapis.com\/v1beta\/accountSummaries. Google\nreturns account\/property names and IDs; the list is cached for the current\nWordPress user for 10 minutes. Clicking Load GA4 properties repeats that\ncall.<\/li>\n<li>When the owner refreshes GA4 top content, the configured numeric property\nID, date range, <code>pagePath<\/code> dimension, <code>screenPageViews<\/code> metric, descending\norder, row limit, and access token are sent to\n  https:\/\/analyticsdata.googleapis.com\/v1beta\/properties\/{property}:runReport.\nGoogle returns page-path rows; resolved local post IDs, source paths, and a\nrefresh timestamp are stored in the GA4 cache option until the next refresh,\nDisconnect, or deletion.<\/li>\n<\/ul>\n\n<p>What access is requested:<\/p>\n\n<ul>\n<li>The Search Console read-only scope (<code>https:\/\/www.googleapis.com\/auth\/webmasters.readonly<\/code>) is requested for Search Console-only and combined wizard paths.<\/li>\n<li>The Analytics (GA4) read-only scope (<code>https:\/\/www.googleapis.com\/auth\/analytics.readonly<\/code>) is requested for Analytics-only and combined wizard paths, or when a GA4 property ID is already configured. A Search Console-only setup never asks for Analytics access.<\/li>\n<\/ul>\n\n<p>When it happens:<\/p>\n\n<ul>\n<li>Only when the site owner clicks Connect, loads a property list, or manually\nrefreshes a Google-backed cache. The post-Connect Search Console property\nlisting is automatic only when that access was requested; the GA4 property\nlisting is automatic for an Analytics wizard connection.<\/li>\n<li>Never during wp.org installation or by default on an unconfigured site.<\/li>\n<\/ul>\n\n<p>How credentials are stored and removed:<\/p>\n\n<ul>\n<li>The client secret, refresh token, and cached access token are encrypted at rest.<\/li>\n<li>Clicking Disconnect makes a best-effort POST to\n  https:\/\/oauth2.googleapis.com\/revoke with the refresh token (or still\nvalid access token) before clearing the locally stored tokens and caches, so\nthe connection is invalidated on Google's side as well as locally. If the\nendpoint cannot be reached, local credentials are still cleared and the\nadmin is told the remote grant may need to be revoked manually.<\/li>\n<li>Deleting the plugin (not just deactivating it) makes the same best-effort revocation call, then permanently removes the plugin's stored settings and credentials, fixed archive caches, and all user-scoped Google property-list and OAuth-state transients, including the encrypted Google credentials. Search-result and rate-limit transients have bounded expirations and are allowed to expire naturally rather than being swept during uninstall. Deactivating the plugin never removes this data \u2014 only deleting it does.<\/li>\n<\/ul>\n\n<p>Service policies:<\/p>\n\n<ul>\n<li>Google APIs Terms of Service: https:\/\/developers.google.com\/terms<\/li>\n<li>Google API Services User Data Policy: https:\/\/developers.google.com\/terms\/api-services-user-data-policy<\/li>\n<li>Google Privacy Policy: https:\/\/policies.google.com\/privacy<\/li>\n<li>Google Analytics Terms of Service (where GA4 is enabled): https:\/\/marketingplatform.google.com\/about\/analytics\/terms\/us\/<\/li>\n<\/ul>\n\n<!--section=installation-->\n<ol>\n<li>Upload the <code>cannyforge-archive-generator<\/code> folder to <code>\/wp-content\/plugins\/<\/code>.<\/li>\n<li>Activate the plugin through the <em>Plugins<\/em> menu in WordPress.<\/li>\n<li>Open <em>CannyForge Archive Generator<\/em> in the admin menu and configure the archive.<\/li>\n<li>Visit the fixed <code>\/archive\/<\/code> endpoint to view the generated archive. The\noptional <code>archive_url<\/code> setting changes only where a \"View Archive\" link\npoints; it does not change the endpoint URL.<\/li>\n<\/ol>\n\n<!--section=faq-->\n<dl>\n<dt id=\"does%20the%20archive%20work%20without%20javascript%3F\"><h3>Does the archive work without JavaScript?<\/h3><\/dt>\n<dd><p>Yes. The archive list is server-rendered and crawlable; the JavaScript only\nadds client-side search and filtering on top.<\/p><\/dd>\n<dt id=\"can%20i%20publish%20a%20complete%20archive%20after%20the%20selected%20first%20page%3F\"><h3>Can I publish a complete archive after the selected first page?<\/h3><\/dt>\n<dd><p>Yes. Enable <strong>full archive pages<\/strong> under Pagination. The existing <code>\/archive\/<\/code>\npage remains the optimised first page; <code>\/archive\/page\/2\/<\/code> onward then lists\nremaining eligible published posts, newest first. Posts already rendered on\npage one are excluded by local post ID. Curated external URLs remain page-one\nonly and never suppress a local post. <code>\/archive\/page\/1\/<\/code> redirects to\n    \/archive\/; malformed and out-of-range page URLs return 404.<\/p><\/dd>\n<dt id=\"does%20it%20replace%20my%20theme%27s%20pagination%20automatically%3F\"><h3>Does it replace my theme's pagination automatically?<\/h3><\/dt>\n<dd><p>On targeted archive types it filters the theme's pagination output. Themes that\nrender pagination in a non-standard way can place the\n    [cannyforge_pagination] shortcode explicitly.<\/p><\/dd>\n<dt id=\"if%20i%20deactivate%20the%20plugin%20%28or%20turn%20off%20pagination%20targeting%29%2C%20do%20i%20get%20my%20normal%20pagination%20back%3F\"><h3>If I deactivate the plugin (or turn off pagination targeting), do I get my normal pagination back?<\/h3><\/dt>\n<dd><p>Yes, completely and immediately. The shortened pagination is applied by\nfiltering WordPress's own pagination output at render time (<code>navigation_markup_template<\/code>);\nnothing is written to your permalinks, post content, or database schema.\nDeactivating the plugin, or unchecking an archive type under Pagination\nTargeting in settings, restores the theme's default numbered pagination on\nthe very next page load \u2014 there is no residue to clean up.<\/p><\/dd>\n<dt id=\"exactly%20what%20data%20is%20sent%20to%20google%2C%20and%20when%3F\"><h3>Exactly what data is sent to Google, and when?<\/h3><\/dt>\n<dd><p>Nothing is sent to Google unless you explicitly configure Google credentials\nand click Connect, load a property list, or manually refresh a Google-backed\ncache \u2014 never on install and never by default. The full endpoint-by-endpoint\nrequest, response, trigger, retention, and policy disclosure is in the\nExternal services section above.<\/p><\/dd>\n<dt id=\"how%20does%20the%20archive%20cache%20behave%2C%20and%20when%20does%20it%20refresh%3F\"><h3>How does the archive cache behave, and when does it refresh?<\/h3><\/dt>\n<dd><p>The rendered archive HTML is cached per mode (Blog \/ News \/ Hybrid) using the\nWordPress Transients API for up to 24 hours. That cache is cleared\nimmediately \u2014 not after the TTL \u2014 whenever you save the plugin's settings, or\nwhenever a post\/page is saved or deleted, or a term or author changes, so the\narchive reflects your latest configuration and content right away in the\ncases that matter. A change written directly to the <code>cannyforge_archive_settings<\/code>\noption outside the settings UI (bypassing that save action) only takes effect\non the next invalidating event or once the 24-hour TTL expires.<\/p><\/dd>\n\n<\/dl>\n\n<!--section=changelog-->\n<h4>0.1.1<\/h4>\n\n<ul>\n<li>Optional Google connection with least-privilege OAuth, Search Console\nproperty\/report sourcing, and GA4 Analytics Admin\/Data API top-content\nsourcing, with local caching and disconnect cleanup.<\/li>\n<li>Optional full-site archive continuation after the optimised <code>\/archive\/<\/code>\npage, disabled by default and served at <code>\/archive\/page\/2\/<\/code> onward.<\/li>\n<li>Sprint 2 hardening: archive endpoint lifecycle, content-selection\nnormalisation, distributable build helper, front-end theming controls,\nhistoric-content seeding, fragment caching, extensibility hooks, and\nCSS moved to enqueued stylesheets.<\/li>\n<\/ul>\n\n<h4>0.1.0<\/h4>\n\n<ul>\n<li>Initial release: archive page, News and Blog modes, client-side filters,\npagination replacement, SEO controls, and content selection.<\/li>\n<\/ul>","raw_excerpt":"HTML sitemap and JavaScript archive, with a compact replacement for default taxonomy pagination.","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin\/346282","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin"}],"about":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/types\/plugin"}],"replies":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/comments?post=346282"}],"author":[{"embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wporg\/v1\/users\/user256"}],"wp:attachment":[{"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/media?parent=346282"}],"wp:term":[{"taxonomy":"plugin_section","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_section?post=346282"},{"taxonomy":"plugin_tags","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_tags?post=346282"},{"taxonomy":"plugin_category","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_category?post=346282"},{"taxonomy":"plugin_contributors","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_contributors?post=346282"},{"taxonomy":"plugin_business_model","embeddable":true,"href":"https:\/\/wordpress.org\/plugins\/wp-json\/wp\/v2\/plugin_business_model?post=346282"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}