Skip to content

Keep font state in one place for every site, and keep every site's fonts in the 6.19 cleanup - #2004

Merged
jakejackson1 merged 1 commit into
developmentfrom
refactor/unify-multisite-fonts
Nov 9, 2026
Merged

jakejackson1 merged 1 commit into
developmentfrom
refactor/unify-multisite-fonts

Conversation

@gravitypdf-bot

@gravitypdf-bot gravitypdf-bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Every site on an install shares one fonts folder, so this PR treats all font state about that folder as install-wide and stores it the same way on single site and Multisite. That removes the "network-wide here, per site there" splits the stack added. What a site needs (its default font, locale and PDF fonts) stays per site, but the 6.19 cleanup, which deletes from the shared folder, now keeps every site's fonts rather than only those of the site whose request ran the upgrade.

  • The failed downloads record (gfpdf_core_font_failures) and the 6.19 cleanup's progress (gfpdf_core_font_cleanup) are main network options on both setups. The is_multisite() write branch and the "store it empty so it's autoloaded" seed are gone.
  • The failed fonts notice's dismissal is stored in that record (a dismissed flag per file), not in each site's action_dismissal. Dismissing it once hides it for every site and the network admin, and a font that gives up again later is listed again.
  • The installed languages' font keys are cached in a site transient shared by every site. Each site's own locale is mapped on top per request. A newly installed language pack is seen by every site at once, not a day later.
  • The 6.19 cleanup runs once per install, on single site too, and keeps the fonts any site needs: each site's saved default font (DejaVu Sans Condensed for a site with none saved, as its own upgrade will save that), its locale's font and its active PDFs' fonts, plus the installed languages'. It reads each site's tables directly, without switch_to_blog().
  • The upgrade only queues the cleanup. A gfpdf_core_font_cleanup_run cron event reads the sites in batches within the sync's 20-second budget, rescheduling itself until every site is read, then cleans the folder. A file written after the cleanup was queued is kept, as a site may have downloaded it since its settings were read.
  • Uninstall removes the font state through one method, remove_font_state(), on both setups. The single-site-only back-off sweep and the font keys in the Multisite sitemeta sweep are gone.

Try it

  1. On a Multisite with two sites, set site A's Default Font to DejaVu Serif Condensed and give a form on site B a PDF in Free Serif. Copy the full core font set into wp-content/uploads/PDF_EXTENDED_TEMPLATES/fonts/.
  2. Set gfpdf_current_version to 6.18.0 on site A and load its admin. Nothing is removed until cron runs gfpdf_core_font_cleanup_run (e.g. wp cron event run gfpdf_core_font_cleanup_run). Then the folder keeps the DejaVu Serif Condensed, Free Serif and DejaVu Sans Condensed files, and nothing else from the core set. Loading site B's admin afterwards removes nothing more.
  3. Block raw.githubusercontent.com (e.g. with pre_http_request), choose a font that isn't on disk on site A and let its sync retry until it gives up. The notice appears on both sites and in the network admin. Dismissing it on site B hides it everywhere.

Test plan

  • Full single-site PHPUnit: 2589 tests. The only failures are environment-only (the container runs as root and ignores chmod): test_an_unwritable_folder_fails_without_a_request ×2.
  • Full multisite PHPUnit: 2552 tests, the same two failures.
  • New: the cleanup keeps another site's default font and the font of a site with none saved; runs once on both setups; the all-sites required set equals a single site's own set, uses the given default when none is saved (none, empty, corrupt), and covers another site's default font, a locale without its pack, and PDF fonts, skipping a site Gravity Forms hasn't run on. The one-site reader's own tests live in List custom, core and bundled fonts together in the Font Manager #2015.
  • New: the upgrade queues the cleanup rather than running it; with the budget spent, a run reads one site and requeues itself with the sites left, and the last run cleans the folder; a done or corrupt progress option queues nothing; prune() keeps files written since the time given.
  • New: the failures live on the main network; dismissing hides the files on every site until one gives up again; only a boolean true dismissal counts; the language cache is shared and the locale isn't cached; uninstall removes the language cache and the cleanup's progress, and clears its event.
  • New bad paths: a malformed cleanup record is logged and cleans nothing; the cleanup carries on past a deleted site; a run that can't requeue keeps its progress; font state stays on the main network when written or removed from another network; corrupt language caches are rebuilt; a bad nonce or a user without the capability can't dismiss the notice; uninstall and the upgrade survive a missing class.
  • PHPCS and PHPStan are clean.
  • New multisite Playwright yarn test:e2e:multisite: the failed fonts notice on the main site, a subsite and the network admin, with an image snapshot; an editor doesn't see it; a subsite admin's dismissal hides it everywhere; only a font that fails later is listed again.
  • Playwright yarn test:e2e: 131 of 131 pass. The failed fonts notice test now keeps its dismissal in an option of its own (tools/mu-plugins/gravitypdf.php), as the fixed failures record can't hold the dismissed flag.
  • CI is green.
More info

The model. The fonts folder lives in the main site's uploads, so one folder serves every site on every network. State about the folder (what failed, what was cleaned, which languages are installed) is install-wide. What a site wants stays per site and is read from its own settings and forms. Anything that deletes from the folder has to consider every site; anything that only adds (the background sync, a render-time download) can act on the current site's needs alone.

Each decision and its trade-off

  • Failures record and cleanup progress: stored with get_network_option( get_main_network_id(), … ). On single site that's the options table, and on Multisite it's the main network's sitemeta, so a multi-network install also has one record and one cleanup for its one folder. Trade-off: update_network_option() stores the option with autoload off on a single site. The notice check therefore now costs one primary-key query, cached for the request, on admin pages that can show notices and only for users with the settings capability. Multisite has always paid that. Keeping autoload would have needed the is_multisite() branch this PR removes.
  • Back-offs: still site transients. On a multi-network install a second network may try a failing file once more within the 15 minutes. That's harmless, and transients have no main-network API.
  • Notice dismissal: now a dismissed flag on each record entry. Behaviour change: dismissal is shared. Any admin with the capability, including a subsite admin, hides the notice for everyone, including the network admin. The System Report row still lists the failed fonts. Previously each site kept its own dismissal, and the network admin's went into the main site's settings. Model_Actions::FAILED_CORE_FONT_DISMISSAL and get_failed_core_font_dismissal() are gone; neither was released.
  • Language font keys: the installed packs live in WP_LANG_DIR, which every site shares, so their keys are cached in a site transient. get_locale() is per site and can be set without its pack, so it's mapped on each request without being cached (one in-memory lookup). Flushing on a language pack install now clears it for every site.
  • 6.19 cleanup keep set: Model_Required_Fonts::get_site_font_keys( $site_id, $unset_default ), shared with the Font Manager's "in use" check (this PR adds the optional $unset_default), reads one site, and get_install_font_files( $keys ) turns the keys gathered into files, adding the installed languages'. For each site it runs one options query (gfpdf_settings, gf_db_version, WPLANG) and the existing paged form-meta scan against that site's table prefix, skipping the scan for sites without gf_db_version. There is no separate table check.
    • A site with no default font saved counts as DejaVu Sans Condensed (Model_Font_Downloader::PRE_6_19_DEFAULT_FONT, moved from the upgrade routines so the cleanup can read it without storing it), because a site that hasn't upgraded yet will save that.
    • Behaviour changes: other sites no longer lose their fonts to the first site's upgrade and have to download them again. A single site now also records the cleanup flag, so a downgrade and a second upgrade doesn't prune again.
    • Cost: one options query plus about one form query per 100 forms, per site, once per install. Same reader as the Font Manager, so the two can't drift.
  • Running the cleanup in the background: schedule_cleanup() stores started, every site's ID (get_sites() across all networks, or the current blog) and the keys found so far in the gfpdf_core_font_cleanup main network option, then queues gfpdf_core_font_cleanup_run on the site whose request ran the upgrade. Each run reads the sites left through the sync's budget loop (within_budget(), now shared, at least one site per run) and saves the ones it didn't reach. The last run prunes and sets the option to true, which later upgrades skip.
    • Trade-offs: the folder is cleaned a cron run or more after the upgrade rather than during it, on single site too. A site created after the cleanup was queued isn't read; its fonts are downloads made since, which prune( $keep, $since ) keeps by modification time. If the event is lost before it finishes (e.g. the hosting site is deleted), the folder is simply never cleaned.
    • schedule_by() takes the event, so both events use its "never two, never later" scheduling; its warning is now Font event could not be scheduled with the event as context.
    • Controller_Upgrade_Routines::remove_unneeded_core_fonts() is now schedule_core_font_cleanup(), and Remove the core fonts a new install wouldn't have on the upgrade to 6.19 #1998's Model_Font_Downloader::PRUNED multisite flag is replaced by Model_Font_Downloader::CLEANUP. prune() takes an optional $since after $keep.
  • Sync, System Report, SYNC_REMAINING: still per site. Each site's cron downloads what that site needs, which only ever adds files.
  • Uninstall: remove_font_state() runs on both setups. It deletes the main network's failures and cleanup flag, the language cache, and each manifest file's back-off by exact key (no table-specific LIKE). remove_font_back_offs() is gone; it was single-site only and never released. The Multisite sitemeta sweep is back to licence caches and the rich text flag. On a multi-network install, back-offs on other networks are left to expire.
  • Malformed cleanup record: cleanup() logs a warning and returns, rather than defaulting the missing fields, as a default would make prune() delete too much. The record stays as it is, so that install's folder is simply never cleaned.
  • Multisite E2E: tools/wp-env/e2e-multisite.json (port 8704) and tools/playwright/config-multisite.ts run tests/playwright/multisite/ as a fifth entry in the e2e-playwright matrix. It's a separate Playwright config because wpCli() targets one environment per run.
  • Not changed here: the is_multisite() branch in Model_Font_Sources::remove_record(), which keeps an adopted record's files on Multisite in case another site's record uses them. Removing it needs the other-sites file check from plan §10 ("Multisite-safe font CRUD"). That is planned as its own Bug PR so it can be backported to 6.18.x alone, so it's left there. Until then, a suffixed copy adopted on Multisite stays on disk as an orphan.
  • Tests:
    • Removed: the single-site autoload test, the empty-record seed test, and the "single site cleans on each upgrade" test (that split is gone).
    • Merged: "once per network" into "runs once" on both suites.
    • The classes whose new tests create subsites reset $wp_rewrite and $wp_settings_errors in tear_down().

@github-actions

github-actions Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Coverage report for commit: d507428
File: ./tmp/jest-coverage/clover.xml

Cover ┌─────────────────────────┐ Freq.
   0% │ ░░░░░░░░░░░░░░░░░░░░░░░ │  0.0%
  10% │ ░░░░░░░░░░░░░░░░░░░░░░░ │  0.0%
  20% │ ░░░░░░░░░░░░░░░░░░░░░░░ │  0.0%
  30% │ ░░░░░░░░░░░░░░░░░░░░░░░ │  0.0%
  40% │ ░░░░░░░░░░░░░░░░░░░░░░░ │  0.0%
  50% │ ░░░░░░░░░░░░░░░░░░░░░░░ │  0.0%
  60% │ ░░░░░░░░░░░░░░░░░░░░░░░ │  0.0%
  70% │ █░░░░░░░░░░░░░░░░░░░░░░ │  3.1%
  80% │ ██░░░░░░░░░░░░░░░░░░░░░ │  4.7%
  90% │ ██████░░░░░░░░░░░░░░░░░ │ 18.8%
 100% │ ███████████████████████ │ 73.4%
      └─────────────────────────┘
 *Legend:* █ = Current Distribution 
Summary - Lines: 93.99% | Methods: 90.96% | Branches: 85.66%
FilesLinesMethodsBranches
src/assets/js/react/actions
   fontManager.js97.92%91.67%100.00%
   templates.js100.00%100.00%100.00%
src/assets/js/react/components/Alert
   Alert.js100.00%100.00%100.00%
src/assets/js/react/components
   CustomHashRouter.js100.00%100.00%100.00%
   Empty.js100.00%100.00%100.00%
   ShowMessage.js80.65%80.00%72.22%
   Spinner.js100.00%100.00%100.00%
src/assets/js/react/components/FontManager
   AddFont.js100.00%100.00%100.00%
   AddUpdateFontFooter.js100.00%100.00%91.67%
   AdvancedButton.js100.00%100.00%100.00%
   DeleteConfirm.js90.91%75.00%100.00%
   FontDeleteButton.js100.00%80.00%100.00%
   FontDetail.js100.00%100.00%100.00%
   FontInstallButton.js100.00%66.67%100.00%
   FontList.js100.00%50.00%65.22%
   FontListAlertMessage.js100.00%100.00%100.00%
   FontListItems.js96.00%83.33%88.24%
   FontListSkeleton.js100.00%100.00%100.00%
   FontManager.js73.68%50.00%50.00%
   FontManagerBody.js89.12%90.32%84.96%
   FontManagerHeader.js100.00%100.00%25.00%
   FontPreview.js100.00%100.00%100.00%
   FontStatus.js100.00%66.67%100.00%
   FontUsage.js100.00%66.67%100.00%
   FontVariant.js94.12%80.00%90.24%
   FontVariantLabel.js100.00%100.00%100.00%
   Icon.js100.00%100.00%100.00%
   InitialAddUpdateState.js100.00%100.00%100.00%
   SearchBox.js90.00%66.67%69.23%
   TemplateTooltip.js100.00%100.00%100.00%
   UpdateFont.js75.00%50.00%64.29%
src/assets/js/react/components/Modal
   CloseDialog.js93.33%66.67%70.59%
src/assets/js/react/components/Template
   TemplateActivateButton.js100.00%100.00%100.00%
   TemplateButton.js85.71%66.67%100.00%
   TemplateContainer.js71.43%66.67%25.00%
   TemplateDeleteButton.js100.00%100.00%70.00%
   TemplateDocsLink.js100.00%100.00%100.00%
   TemplateFooterActions.js100.00%100.00%100.00%
   TemplateHeaderNavigation.js82.35%85.71%70.00%
   TemplateHeaderTitle.js100.00%100.00%100.00%
   TemplateList.js100.00%100.00%100.00%
   TemplateListItem.js100.00%100.00%92.86%
   TemplateListItemComponents.js100.00%100.00%100.00%
   TemplateManagerClosed.js100.00%100.00%100.00%
   TemplateScreenshot.js100.00%100.00%100.00%
   TemplateScreenshots.js100.00%100.00%50.00%
   TemplateSearch.js93.75%88.89%50.00%
   TemplateSingle.js100.00%100.00%100.00%
   TemplateSingleComponents.js100.00%100.00%75.00%
   TemplateUploader.js100.00%100.00%95.00%
   TemplateUploaderContext.js100.00%100.00%100.00%
   TemplateUploaderTile.js100.00%100.00%100.00%
src/assets/js/react/reducers
   fontManagerReducer.js91.46%100.00%81.08%
   index.js100.00%100.00%100.00%
   templateReducer.js100.00%100.00%100.00%
src/assets/js/react/sagas
   fontManager.js90.16%92.86%88.57%
   index.js100.00%100.00%100.00%
   templates.js91.30%100.00%100.00%
src/assets/js/react/selectors
   getTemplates.js91.11%100.00%83.33%
src/assets/js/react/utilities/FontManager
   associatedFontManagerSelectBox.js94.44%100.00%77.78%
   fontManagerReducer.js100.00%100.00%94.12%
   toggleUpdateFont.js100.00%100.00%100.00%
src/assets/js/react/utilities/Template
   hasPendingUpload.js100.00%100.00%100.00%
src/assets/js/react/utilities
   withRouterHooks.js100.00%100.00%100.00%

🤖 Jest coverage report

@github-actions

github-actions Bot commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Coverage report for commit: d507428
File: tmp/coverage/report-xml/merged.xml

Cover ┌─────────────────────────┐ Freq.
   0% │ ██████░░░░░░░░░░░░░░░░░ │  7.6%
  10% │ ░░░░░░░░░░░░░░░░░░░░░░░ │  0.0%
  20% │ █░░░░░░░░░░░░░░░░░░░░░░ │  0.4%
  30% │ █░░░░░░░░░░░░░░░░░░░░░░ │  1.3%
  40% │ █░░░░░░░░░░░░░░░░░░░░░░ │  0.4%
  50% │ ███████░░░░░░░░░░░░░░░░ │  9.3%
  60% │ █░░░░░░░░░░░░░░░░░░░░░░ │  0.9%
  70% │ ██░░░░░░░░░░░░░░░░░░░░░ │  2.7%
  80% │ █████████████░░░░░░░░░░ │ 16.9%
  90% │ ███████████████████████ │ 31.6%
 100% │ ██████████████████████░ │ 28.9%
      └─────────────────────────┘
 *Legend:* █ = Current Distribution 
Summary - Lines: 89.54% | Methods: 95.71%
FilesLinesMethodsBranches
/var/www/html/wp-content/plugins/gravity-pdf
   api.php98.14%100.00%100.00%
   gravity-pdf-updater.php53.97%100.00%100.00%
   pdf.php59.82%81.25%100.00%
/var/www/html/wp-content/plugins/gravity-pdf/src/Controller
   Controller_Actions.php100.00%100.00%100.00%
   Controller_Activation.php95.83%100.00%100.00%
   Controller_Custom_Fonts.php95.47%100.00%100.00%
   Controller_Debug.php100.00%100.00%100.00%
   Controller_Export_Entries.php96.77%100.00%100.00%
   Controller_Font_Downloader.php100.00%100.00%100.00%
   Controller_Font_Sources.php100.00%100.00%100.00%
   Controller_Form_Settings.php87.37%90.00%100.00%
   Controller_Install.php100.00%100.00%100.00%
   Controller_Mergetags.php100.00%100.00%100.00%
   Controller_PDF.php81.46%100.00%100.00%
   Controller_Pdf_Cache.php100.00%100.00%100.00%
   Controller_Pdf_Queue.php85.27%87.50%100.00%
   Controller_Save_Core_Fonts.php91.67%100.00%100.00%
   Controller_Settings.php86.90%100.00%100.00%
   Controller_Shortcodes.php100.00%100.00%100.00%
   Controller_System_Report.php100.00%100.00%100.00%
   Controller_Templates.php100.00%100.00%100.00%
   Controller_Uninstaller.php83.33%77.78%100.00%
   Controller_Upgrade_Routines.php95.19%100.00%100.00%
   Controller_Webhooks.php100.00%100.00%100.00%
   Controller_Zapier.php100.00%100.00%100.00%
/var/www/html/wp-content/plugins/gravity-pdf/src/Exceptions
   GravityPdfDatabaseUpdateException.php50.00%100.00%100.00%
   GravityPdfDomainException.php50.00%100.00%100.00%
   GravityPdfException.php50.00%100.00%100.00%
   GravityPdfFontNotFoundException.php50.00%100.00%100.00%
   GravityPdfIdException.php50.00%100.00%100.00%
   GravityPdfModelNotUpdatedException.php50.00%100.00%100.00%
   GravityPdfNotATemplateException.php50.00%100.00%100.00%
   GravityPdfRuntimeException.php50.00%100.00%100.00%
   GravityPdfShortcodeEntryIdException.php50.00%100.00%100.00%
   GravityPdfShortcodePdfConditionalLogicFailedException.php50.00%100.00%100.00%
   GravityPdfShortcodePdfConfigNotFoundException.php50.00%100.00%100.00%
   GravityPdfShortcodePdfInactiveException.php50.00%100.00%100.00%
/var/www/html/wp-content/plugins/gravity-pdf/src/Helper/Fields
   Field_Address.php92.86%100.00%100.00%
   Field_Chainedselect.php66.67%75.00%100.00%
   Field_Checkbox.php94.44%100.00%100.00%
   Field_Consent.php95.00%100.00%100.00%
   Field_Coupon.php--100.00%
   Field_Creditcard.php83.33%100.00%100.00%
   Field_Date.php83.33%100.00%100.00%
   Field_Default.php83.33%100.00%100.00%
   Field_Discount.php44.00%75.00%100.00%
   Field_Email.php83.33%100.00%100.00%
   Field_Fg_Ls_Consent.php92.86%100.00%100.00%
   Field_Fg_Ls_Signature.php73.08%66.67%100.00%
   Field_Fileupload.php95.83%100.00%100.00%
   Field_Form.php90.91%100.00%100.00%
   Field_Hidden.php81.82%100.00%100.00%
   Field_Html.php89.47%100.00%100.00%
   Field_Image_Choice.php86.67%100.00%100.00%
   Field_Likert.php97.22%100.00%100.00%
   Field_List.php92.41%100.00%100.00%
   Field_Multi_Choice.php50.00%100.00%100.00%
   Field_Multiselect.php92.59%100.00%100.00%
   Field_Name.php84.62%100.00%100.00%
   Field_Number.php83.33%100.00%100.00%
   Field_Option.php96.15%100.00%100.00%
   Field_Page.php83.33%100.00%100.00%
   Field_Phone.php94.55%100.00%100.00%
   Field_Poll.php93.75%100.00%100.00%
   Field_Post_Category.php85.00%100.00%100.00%
   Field_Post_Content.php82.35%100.00%100.00%
   Field_Post_Custom_Field.php50.00%100.00%100.00%
   Field_Post_Excerpt.php81.82%100.00%100.00%
   Field_Post_Image.php94.00%100.00%100.00%
   Field_Post_Tags.php90.91%100.00%100.00%
   Field_Post_Title.php81.82%100.00%100.00%
   Field_Product.php92.31%100.00%100.00%
   Field_Products.php84.32%100.00%100.00%
   Field_Quantity.php84.62%100.00%100.00%
   Field_Quiz.php89.74%100.00%100.00%
   Field_Radio.php94.74%100.00%100.00%
   Field_Rank.php95.24%100.00%100.00%
   Field_Rating.php95.24%100.00%100.00%
   Field_Repeater.php97.39%100.00%100.00%
   Field_Section.php90.74%100.00%100.00%
   Field_Select.php94.12%100.00%100.00%
   Field_Shipping.php92.86%100.00%100.00%
   Field_Signature.php71.11%100.00%100.00%
   Field_Slim.php82.35%100.00%100.00%
   Field_Slim_Post.php91.30%100.00%100.00%
   Field_Subtotal.php65.38%75.00%100.00%
   Field_Survey.php95.00%100.00%100.00%
   Field_Tax.php32.00%50.00%100.00%
   Field_Text.php81.82%100.00%100.00%
   Field_Textarea.php91.18%100.00%100.00%
   Field_Time.php81.82%100.00%100.00%
   Field_Tos.php92.86%100.00%100.00%
   Field_Total.php88.00%100.00%100.00%
   Field_Website.php85.71%100.00%100.00%
/var/www/html/wp-content/plugins/gravity-pdf/src/Helper/Fonts
   FlushCache.php92.31%100.00%100.00%
   LocalFile.php88.89%100.00%100.00%
   LocalFilesystem.php66.67%100.00%100.00%
   SupportsOtl.php87.50%100.00%100.00%
   TtfFontValidation.php84.62%100.00%100.00%
/var/www/html/wp-content/plugins/gravity-pdf/src/Helper
   Helper_Abstract_Addon.php94.09%100.00%100.00%
   Helper_Abstract_Config_Settings.php75.00%100.00%100.00%
   Helper_Abstract_Controller.php-100.00%100.00%
   Helper_Abstract_Field_Products.php89.47%66.67%100.00%
   Helper_Abstract_Fields.php96.10%95.83%100.00%
   Helper_Abstract_Fields_Input_Type.php84.00%100.00%100.00%
   Helper_Abstract_Form.php--100.00%
   Helper_Abstract_Model.php100.00%100.00%100.00%
   Helper_Abstract_Options.php77.13%79.66%100.00%
   Helper_Abstract_Pdf_Shortcode.php94.67%95.65%100.00%
   Helper_Abstract_View.php96.77%100.00%100.00%
   Helper_Async_Notifications.php64.29%100.00%100.00%
   Helper_Data.php98.43%100.00%100.00%
   Helper_Field_Container.php92.31%100.00%100.00%
   Helper_Field_Container_Gf25.php92.00%100.00%100.00%
   Helper_Field_Container_Void.php16.67%-100.00%
   Helper_Form.php80.65%64.29%100.00%
   Helper_Interface_Actions.php-100.00%100.00%
   Helper_Interface_Config.php50.00%100.00%100.00%
   Helper_Interface_Config_Settings.php50.00%100.00%100.00%
   Helper_Interface_Deprecated_Features.php50.00%100.00%100.00%
   Helper_Interface_Extension_Settings.php-100.00%100.00%
   Helper_Interface_Extension_Uninstaller.php-100.00%100.00%
   Helper_Interface_Field_Pdf_Config.php50.00%100.00%100.00%
   Helper_Interface_Filters.php-100.00%100.00%
   Helper_Interface_Setup_TearDown.php50.00%100.00%100.00%
   Helper_Interface_Url_Signer.php-100.00%100.00%
   Helper_Logger.php-100.00%100.00%
   Helper_Misc.php89.17%100.00%100.00%
   Helper_Mpdf.php50.00%100.00%100.00%
   Helper_Notices.php92.98%100.00%100.00%
   Helper_Options_Fields.php98.10%100.00%100.00%
   Helper_PDF.php88.61%97.56%100.00%
   Helper_PDF_List_Table.php84.50%92.31%100.00%
   Helper_Pdf_Queue.php84.31%100.00%100.00%
   Helper_QueryPath.php80.00%100.00%100.00%
   Helper_Render_Health.php85.71%100.00%100.00%
   Helper_Sha256_Url_Signer.php90.91%100.00%100.00%
   Helper_Singleton.php100.00%100.00%100.00%
   Helper_Templates.php97.29%100.00%100.00%
   Helper_Trait_Logger.php25.00%-100.00%
   Helper_Trait_Removed_Methods.php100.00%100.00%100.00%
   Helper_Url_Signer.php84.82%100.00%100.00%
/var/www/html/wp-content/plugins/gravity-pdf/src/Helper/Licensing
Table truncated to fit comment

🤖 PHPUnit coverage report

@gravitypdf-bot
gravitypdf-bot force-pushed the refactor/unify-multisite-fonts branch from 70d21df to ca0ef98 Compare November 3, 2026 21:45
@jakejackson1
jakejackson1 force-pushed the refactor/unify-multisite-fonts branch from ca0ef98 to d1ae519 Compare November 3, 2026 23:23
@gravitypdf-bot
gravitypdf-bot force-pushed the refactor/unify-multisite-fonts branch from d1ae519 to 4be62dd Compare November 4, 2026 01:40
@gravitypdf-bot
gravitypdf-bot force-pushed the refactor/unify-multisite-fonts branch from 4be62dd to 50ddcb9 Compare November 5, 2026 22:51
@jakejackson1
jakejackson1 removed this pull request from stack #2003 November 5, 2026 23:27
@gravitypdf-bot
gravitypdf-bot changed the base branch from feature/core-font-upgrade-prune to feature/font-manager-downloaded-fonts November 5, 2026 23:27
@gravitypdf-bot
gravitypdf-bot force-pushed the refactor/unify-multisite-fonts branch from 50ddcb9 to 0388659 Compare November 5, 2026 23:59
gravitypdf-bot added a commit that referenced this pull request Nov 6, 2026
mpdf-core-fonts v1.0.0, which the Core Font installer downloaded, holds mPDF's pre-8.1.0 Garuda.ttf, whose "k" carries
broken TrueType hinting (mpdf/mpdf#1440). v1.1.1 ships mPDF's fixed file, and all four Garuda faces are dehinted with
it. prune() keeps a font the site needs, and the downloader skips a file that's present, so a Thai site's old Garuda
would stay for good.

prune() now treats Model_Font_Downloader::OUTDATED (the four Garuda files) as never kept: an unmodified v1.0.0 copy is
deleted whether or not the site needs it, under the same size, hash and downloads-off checks as any other file. When a
needed one goes, it queues a sync so the current file comes back in the background (the render-time finder fetches it
too). The version change already queues a sync, but #2004 moves this cleanup into cron batches that can run after it.
A modified Garuda, a v1.1.1 one, and one whose download is turned off stay.

The other 43 dehinted fonts differ only in hinting and are left alone when kept, though the fork's character ranges
files record v1.1.1 sizes, so mPDF ignores the ranges file (and logs a warning) for a kept v1.0.0 regular face.

Adds the v1.0.0 (blob b9fcdfd) and v1.1.1 (blob fc4379b) Garuda.ttf as test fixtures, and moves the reset of the
installer manifest stand-in into CopiesFontFixtures::use_shipped_installer_manifest().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@jakejackson1
jakejackson1 added this pull request to stack #2017 November 6, 2026 02:57
@jakejackson1
jakejackson1 force-pushed the refactor/unify-multisite-fonts branch from 0388659 to ae938cd Compare November 6, 2026 03:06
Base automatically changed from feature/font-manager-downloaded-fonts to development November 9, 2026 03:20
@gravitypdf-bot
gravitypdf-bot force-pushed the refactor/unify-multisite-fonts branch from ae938cd to 8acc891 Compare November 9, 2026 03:31
@gravitypdf-bot
gravitypdf-bot force-pushed the refactor/unify-multisite-fonts branch from 8acc891 to 1470b93 Compare November 9, 2026 04:58
@gravitypdf-bot gravitypdf-bot added update-visual-baselines Refresh Lost Pixel visual baselines from this PR's run (pair with run-tests) and removed update-visual-baselines Refresh Lost Pixel visual baselines from this PR's run (pair with run-tests) labels Nov 9, 2026
…nts in the 6.19 cleanup

Every site shares the fonts folder, so the font state about it is now stored the same way on single site and
Multisite: the failed downloads record and the 6.19 cleanup's progress are main network options, the language font
cache is a site transient, and the failed fonts notice's dismissal lives in the record rather than in each site's
settings. The 6.19 cleanup keeps the fonts any site needs, reading each site's settings, locale and PDFs directly
through the same reader the Font Manager uses for other sites' fonts. It runs in a cron event that reads the sites in
batches within the sync's time budget, so no upgrade request pays for a large network's scan.

A malformed cleanup record is now logged and skipped rather than fatal. Adds a Multisite Playwright environment on
port 8704, run as a fifth entry in the E2E matrix, and tests the failed fonts notice across sites and the network admin.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@gravitypdf-bot
gravitypdf-bot force-pushed the refactor/unify-multisite-fonts branch from 19a27c0 to d507428 Compare November 9, 2026 05:11
@jakejackson1
jakejackson1 merged commit 823d176 into development Nov 9, 2026
@jakejackson1
jakejackson1 deleted the refactor/unify-multisite-fonts branch November 9, 2026 05:21
gravitypdf-bot added a commit that referenced this pull request Nov 10, 2026
…e to 6.19

mpdf-core-fonts v1.0.0, which the Core Font installer downloaded, holds mPDF's pre-8.1.0 Garuda.ttf, whose "k" carries
broken TrueType hinting (mpdf/mpdf#1440), and Sun-ExtA and Sun-ExtB files browsers reject as web fonts
(GravityPDF/mpdf#881). prune() keeps a font the site needs, and the downloader skips a file that's present, so those
copies would stay for good.

The manifest moves to v1.1.2, which repairs Sun-ExtA and Sun-ExtB, and the Sun-ExtA preview opt-out goes with the fault
it worked around. prune() now treats Model_Font_Downloader::OUTDATED (the four Garuda files and the two Sun-Ext files)
as never kept: an unmodified v1.0.0 copy is deleted whether or not the site needs it, under the same size, hash and
downloads-off checks as any other file. When a needed one goes, it queues a sync so the current file comes back in the
background (the render-time finder fetches it too). The version change already queues a sync, but #2004 moves this
cleanup into cron batches that can run after it. A modified copy, a current one, and one whose download is turned off
stay.

The other 43 dehinted fonts differ only in hinting and are left alone when kept, though the fork's character ranges
files record v1.1.1 sizes, so mPDF ignores the ranges file (and logs a warning) for a kept v1.0.0 regular face.

Adds the v1.0.0 (blob b9fcdfd) and v1.1.1 (blob fc4379b) Garuda.ttf as test fixtures, and moves the reset of the
installer manifest stand-in into CopiesFontFixtures::use_shipped_installer_manifest().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gravitypdf-bot added a commit that referenced this pull request Nov 10, 2026
…e to 6.19

mpdf-core-fonts v1.0.0, which the Core Font installer downloaded, holds mPDF's pre-8.1.0 Garuda.ttf, whose "k" carries
broken TrueType hinting (mpdf/mpdf#1440), and Sun-ExtA and Sun-ExtB files browsers reject as web fonts
(GravityPDF/mpdf#881). prune() keeps a font the site needs, and the downloader skips a file that's present, so those
copies would stay for good.

The manifest moves to v1.1.2, which repairs Sun-ExtA and Sun-ExtB, and the Sun-ExtA preview opt-out goes with the fault
it worked around. prune() now treats Model_Font_Downloader::OUTDATED (the four Garuda files and the two Sun-Ext files)
as never kept: an unmodified v1.0.0 copy is deleted whether or not the site needs it, under the same size, hash and
downloads-off checks as any other file. When a needed one goes, it queues a sync so the current file comes back in the
background (the render-time finder fetches it too). The version change already queues a sync, but #2004 moves this
cleanup into cron batches that can run after it. A modified copy, a current one, and one whose download is turned off
stay.

The other 43 dehinted fonts differ only in hinting and are left alone when kept, though the fork's character ranges
files record v1.1.1 sizes, so mPDF ignores the ranges file (and logs a warning) for a kept v1.0.0 regular face.

Bumps the mPDF fork to 725098a for GravityPDF/mpdf#883, whose ranges files record the repaired sizes and whose
TTFontFile reads Sun-ExtA with OTL on. The fork no longer uses myclabs/deep-copy, which is now only PHPUnit's, so it
stops being scoped or stripped from vendor/.

Adds the v1.0.0 (blob b9fcdfd) and v1.1.1 (blob fc4379b) Garuda.ttf as test fixtures, and moves the reset of the
installer manifest stand-in into CopiesFontFixtures::use_shipped_installer_manifest().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gravitypdf-bot added a commit that referenced this pull request Nov 10, 2026
…e to 6.19

mpdf-core-fonts v1.0.0, which the Core Font installer downloaded, holds mPDF's pre-8.1.0 Garuda.ttf, whose "k" carries
broken TrueType hinting (mpdf/mpdf#1440), and Sun-ExtA and Sun-ExtB files browsers reject as web fonts
(GravityPDF/mpdf#881). prune() keeps a font the site needs, and the downloader skips a file that's present, so those
copies would stay for good.

The manifest moves to v1.1.2, which repairs Sun-ExtA and Sun-ExtB, and the Sun-ExtA preview opt-out goes with the fault
it worked around. prune() now treats Model_Font_Downloader::OUTDATED (the four Garuda files and the two Sun-Ext files)
as never kept: an unmodified v1.0.0 copy is deleted whether or not the site needs it, under the same size, hash and
downloads-off checks as any other file. When a needed one goes, it queues a sync so the current file comes back in the
background (the render-time finder fetches it too). The version change already queues a sync, but #2004 moves this
cleanup into cron batches that can run after it. A modified copy, a current one, and one whose download is turned off
stay.

The other 43 dehinted fonts differ only in hinting and are left alone when kept, though the fork's character ranges
files record v1.1.1 sizes, so mPDF ignores the ranges file (and logs a warning) for a kept v1.0.0 regular face.

Bumps the mPDF fork to 725098a for GravityPDF/mpdf#883, whose ranges files record the repaired sizes and whose
TTFontFile reads Sun-ExtA with OTL on. The fork no longer uses myclabs/deep-copy, so the plugin requires it directly to
keep GFPDF_Vendor\DeepCopy available to add-ons and custom templates, and a scoper patcher deprecates its constructor
ahead of its removal in 7.0.

Adds the v1.0.0 (blob b9fcdfd) and v1.1.1 (blob fc4379b) Garuda.ttf as test fixtures, and moves the reset of the
installer manifest stand-in into CopiesFontFixtures::use_shipped_installer_manifest().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
jakejackson1 pushed a commit that referenced this pull request Nov 10, 2026
…e to 6.19

mpdf-core-fonts v1.0.0, which the Core Font installer downloaded, holds mPDF's pre-8.1.0 Garuda.ttf, whose "k" carries
broken TrueType hinting (mpdf/mpdf#1440), and Sun-ExtA and Sun-ExtB files browsers reject as web fonts
(GravityPDF/mpdf#881). prune() keeps a font the site needs, and the downloader skips a file that's present, so those
copies would stay for good.

The manifest moves to v1.1.2, which repairs Sun-ExtA and Sun-ExtB, and the Sun-ExtA preview opt-out goes with the fault
it worked around. prune() now treats Model_Font_Downloader::OUTDATED (the four Garuda files and the two Sun-Ext files)
as never kept: an unmodified v1.0.0 copy is deleted whether or not the site needs it, under the same size, hash and
downloads-off checks as any other file. When a needed one goes, it queues a sync so the current file comes back in the
background (the render-time finder fetches it too). The version change already queues a sync, but #2004 moves this
cleanup into cron batches that can run after it. A modified copy, a current one, and one whose download is turned off
stay.

The other 43 dehinted fonts differ only in hinting and are left alone when kept, though the fork's character ranges
files record v1.1.1 sizes, so mPDF ignores the ranges file (and logs a warning) for a kept v1.0.0 regular face.

Bumps the mPDF fork to 725098a for GravityPDF/mpdf#883, whose ranges files record the repaired sizes and whose
TTFontFile reads Sun-ExtA with OTL on. The fork no longer uses myclabs/deep-copy, so the plugin requires it directly to
keep GFPDF_Vendor\DeepCopy available to add-ons and custom templates, and a scoper patcher deprecates its constructor
ahead of its removal in 7.0.

Adds the v1.0.0 (blob b9fcdfd) and v1.1.1 (blob fc4379b) Garuda.ttf as test fixtures, and moves the reset of the
installer manifest stand-in into CopiesFontFixtures::use_shipped_installer_manifest().

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants