Recent Commits to wp-pluginsused:master

  • 29/07/2026 21:50

    Emit the markup kses leaves, and assert loss rather than bytes

    Emit the markup kses leaves, and assert loss rather than bytes
    
    The plugin has two ways out: the shortcodes return their markup, while
    display_pluginsused() echoes it through wp_kses(). kses normalises as
    well as filters, so the two paths were emitting different bytes for the
    same listing - viewBox lowercased to viewbox, a space added before a
    self-closing slash, ' rewritten to ' inside a title. A theme
    calling the template tag got markup the shortcode never produced.
    
    The icon now spells its attributes the way kses leaves them, so the two
    paths agree. Lowercase viewbox is safe and is what the echoing path has
    always shipped: an HTML parser applies its SVG case-fixup table, so it
    reaches the DOM as viewBox and the icon still scales.
    
    The entity rewriting cannot be pre-empted that way, so the two tests now
    assert what they mean rather than byte-identity. The allow-list test
    compares inventories of tags and attribute names, since an incomplete
    allow list drops a tag or an attribute and that is the failure worth
    catching. The template-tag test compares the echoed output against
    wp_kses( render(), allowed_html() ), which is what it should equal.
    
    Also made the settings_fields() assertion quote-agnostic: core quotes
    option_page with single quotes, which is not part of any contract.
    
    158 tests, 442 assertions, green.
  • 29/07/2026 21:28

    Match wp_enqueue_script( with its bracket, not as a substring

    Match wp_enqueue_script( with its bracket, not as a substring
    
    The no-jQuery test forbade the bare string 'wp_enqueue_script', which is
    also a substring of the ACTION name wp_enqueue_scripts - the hook a
    plugin legitimately uses to enqueue a stylesheet. Registering no scripts
    is not the same as never touching the hook scripts are registered on.
    
    wp-stats failed on exactly this: it hooks wp_enqueue_scripts to add its
    one sheet. The others here pass today only because they happen not to,
    so this is a latent false failure waiting for the first stylesheet.
    
    Found by the first PHPUnit run of this work.
  • 29/07/2026 11:33

    Re-lock composer against the PHP 8.2 floor

    Re-lock composer against the PHP 8.2 floor
    
    composer.json moved to >=8.2 with the floor raise but the lock file still
    recorded >=7.4. Composer treats that as an error rather than a warning,
    so 'composer install' would have failed inside the tests container before
    PHPUnit ran a single test - and CI does exactly that on all six matrix
    rows.
    
    bin/verify.py now compares the two, so the pair cannot drift again.
  • 29/07/2026 11:29

    Step 12: drop the last two phpcs suppressions and fix the code under …

    Step 12: drop the last two phpcs suppressions and fix the code under them
    
    Both were in includes/, both had a reason written next to them, and the target
    for this collection is zero.
    
    display_pluginsused() echoed its markup behind an EscapeOutput ignore. It now
    escapes at the sink like everything else, through wp_kses() with an allow-list
    that says exactly which tags and attributes the listings use. A test asserts
    wp_kses() leaves all three listings byte-identical, so the day the markup
    grows a tag the list has not got, the suite says so rather than the template
    tag quietly printing less than the shortcodes do.
    
    The summary sentence carried an I18n.NoHtmlWrappedStrings ignore arguing that
    rewording the msgid would orphan its translations. That is true and it is
    still the right trade: a translator has no business being handed <strong>, and
    a msgid wrapped in a tag is a translation waiting to lose it. The tags moved
    into the concatenation, the rendered string is byte-for-byte what it was, and
    the three orphaned msgids are recorded as a NOTE in the changelog.
    
    phpcs and php -l are clean across the plugin, and bin/verify.py reports zero.
  • 29/07/2026 11:27

    Step 11: put the suite on the house conventions and add the shared tests

    Step 11: put the suite on the house conventions and add the shared tests
    
    Files and classes follow 7.1: helper-fixtures.php becomes helper-testcase.php
    because what it declares is the base test case, test-stats.php becomes
    test-summary.php so nothing confuses it with the WP-Stats integration this
    plugin does not have, and every class is WP_PluginsUsed_<Area>_Test extending
    WP_PluginsUsed_TestCase. test-bootstrap.php and test-uninstall.php extended
    WP_UnitTestCase directly and now go through the shared base like the rest.
    
    Discovery is by the test- prefix now, so a misnamed file would be silently
    skipped rather than fail: 109 test methods before this step, 150 after, none
    lost in the rename.
    
    tests/test-metadata.php carries the fourteen shared assertions from 7.2, plus
    three specific to what this plugin just did: that every fired hook carries the
    prefix, that no apply_filters_deprecated shim crept back in, and that the
    Upgrade Notice names all three renamed filters in both spellings. The last one
    is the guard that matters -- the rename is only survivable for a site owner if
    the notice actually lists it.
    
    run_uninstall() is shared rather than repeated, because uninstall.php declares
    a function and so can only be included once per process. Two files assert
    against it; the second include would have been a silent no-op and the test
    would have passed on rows nothing had deleted.
  • 29/07/2026 11:27

    Step 2 (redo): re-sync the shared config files after the templates moved

    Step 2 (redo): re-sync the shared config files after the templates moved
    
    _standards/templates/phpcs.xml and phpunit-multisite.xml.dist changed while
    this plugin was being worked through, so the copies taken at 516e33a had
    drifted from the shared originals. Both are re-copied verbatim.
    
    Neither new exclusion applies here -- WP-PluginsUsed owns no database table
    and has no list table -- but the point of these files is that all nineteen
    carry the same one, so it is copied whole rather than trimmed to fit.
  • 29/07/2026 11:22

    Step 10: put the readme on the canonical section set

    Step 10: put the readme on the canonical section set
    
    The six h2s in the order the standard fixes them: Description, Usage,
    Frequently Asked Questions, Screenshots, Changelog, Upgrade Notice. Usage was
    an h3 buried in Description and the changelog sat above the screenshots.
    
    The License line gets its two trailing spaces back. Without them Markdown ran
    "License: GPLv2 or later" and the License URI together into one line on
    GitHub, which is invisible in the source and in a diff.
    
    Description gains a Features list and keeps Donations as its last h3, in the
    one wording all nineteen use, without the stray bullet it carried. Development
    and Credits are gone: the repo link belongs in the repo, and the Flaticon
    credit was for the wordpress.org listing icon rather than anything this plugin
    ships -- worth re-adding to the .org assets if it is still owed.
    
    Changelog entries are sorted into BREAKING, NEW, CHANGED, FIXED, NOTE within
    2.0.0. The security fix moves from a sixth prefix, SECURITY:, to FIXED:, the
    raised floors become their own BREAKING line, and four 2008-era bullets that
    were indented with two spaces are normalised so they parse as bullets at all.
  • 29/07/2026 11:20

    Step 9: scope the emitted class names and drop the last inline style

    Step 9: scope the emitted class names and drop the last inline style
    
    The plugin ships no stylesheet, but it does emit markup, and the two class
    names on the state icons were unscoped. They become wp-pluginsused-icon,
    wp-pluginsused-icon-active and wp-pluginsused-icon-inactive, so a theme has
    one prefix to target and no chance of colliding with anything else.
    
    The SVGs lose their style="vertical-align: middle" as well. It was the last
    inline CSS in the plugin, and the icons still inherit their colour through
    currentColor, which is the part that actually matters for a theme.
    
    There is no css/ or js/ directory to normalise -- the plugin has neither, and
    the SVG is built in PHP -- so this is the whole of steps 7 to 9. Steps 7 and 8
    are no-ops: no JavaScript, and the GIF markers were already replaced by inline
    SVG earlier in this major.
  • 29/07/2026 11:19

    Step 6: put the settings screen on the house rules

    Step 6: put the settings screen on the house rules
    
    The screen was already Settings API rather than hand-written markup, so this
    is the naming and the details around it:
    
    * CAPABILITY, SECTION_DISPLAY and SECTION_HIDDEN join PAGE and GROUP, and the
      two section ids and both field ids gain the plugin's prefix.
    * Every capability check goes through capability(), which fires
      wp_pluginsused_capability with a context, so a site hands the screen to a
      different role in one place rather than by hunting current_user_can() calls.
    * register() and render() become register_settings() and render_page(),
      matching the other settings screens in the collection.
    * The hidden-plugins list loses its inline style attribute and separates its
      labels with <br />, the way core's own checkbox lists do. That was the only
      inline CSS in the plugin.
    
    Five tests follow, including two standing guards: no inline style attribute
    reaches the screen, and no file hand-writes a form table.
  • 29/07/2026 11:17

    Step 5: prefix the three public filters and write the Upgrade Notice

    Step 5: prefix the three public filters and write the Upgrade Notice
    
        pluginsused_show_version    ->  wp_pluginsused_show_version
        pluginsused_hidden_plugins  ->  wp_pluginsused_hidden_plugins
        pluginsused_plugins_used    ->  wp_pluginsused_plugins_used
    
    All three shipped in 1.50, so this genuinely breaks third-party code. The old
    names are dropped outright -- no apply_filters_deprecated() shims, which was
    the decision taken for the whole collection: a shim keeping an unprefixed name
    alive keeps the clash the rename exists to remove.
    
    Two more unprefixed identifiers go with them, for the same reason and in the
    same release rather than one at a time: the PLUGINSUSED_SHOW_VERSION constant
    and the $pluginsused_hidden_plugins global, which were how 1.50 was configured
    by editing the plugin's own file. The settings screen added in this major does
    both jobs and survives updates, which is what those two never did.
    
    The Upgrade Notice is written for a site owner: what silently stops working,
    where in their site to look for it, what the symptom will be weeks later, and
    the reassurance that no code of their own means nothing to do. The changelog
    carries the same two BREAKING lines in developer shorthand.
    
    Tests follow: the filter call sites are renamed, and the two that asserted the
    legacy global and constant still worked now assert that they do not.
  • 29/07/2026 11:13

    Step 4: give the plugin the two canonical option rows

    Step 4: give the plugin the two canonical option rows
    
    Settings move from pluginsused_options to wp_pluginsused_options, and the
    version markers get a row of their own, wp_pluginsused_version, holding
    'plugin' and 'db' and nothing else. Keeping them apart means the settings
    screen and the upgrade routine can never overwrite each other, and the
    sanitise callback never has to rescue a value the form did not post.
    
    There was no migration here to retarget -- 1.50, the last release, stored
    nothing at all, so the unprefixed row only exists on an install that ran a
    development build of 2.0.0. maybe_upgrade() is that one migration: it folds
    the old row in, re-sanitises it, deletes it, then stamps both markers in a
    single write so a half-finished upgrade cannot record itself as complete. It
    runs on activation and on every admin load, because activation hooks do not
    fire on update, which is the usual reason a migration never runs.
    
    uninstall.php removes all three rows -- both current ones and the unprefixed
    leftover, for an install removed before it ever reached wp-admin.
    
    Seven new tests cover the fold, the re-sanitise, the refusal to overwrite a
    newer prefixed row, idempotency, and the shape of the marker row.
  • 29/07/2026 11:04

    Step 3: prefix the classes, add the six constants, raise the floors

    Step 3: prefix the classes, add the six constants, raise the floors
    
    PluginsUsed, PluginsUsed_Options, PluginsUsed_Settings and
    PluginsUsed_Template become WP_PluginsUsed_*, each in the class-wp-pluginsused-*
    file its name now implies. Nothing unprefixed is left in the global namespace
    except the template tag and the deprecated shims, which are the documented
    public API and keep the names they shipped with.
    
    The plugin file gains the four constants it was missing -- DB_VERSION, SLUG,
    DIR and URL -- and loads includes/ through DIR rather than __DIR__.
    
    Floors go to WordPress 6.8 and PHP 8.2 in the plugin header and the readme;
    phpcs.xml, .wp-env.json, composer.json and the CI matrix already carried them
    from the templates. There is no version-guarded back-compat shim to delete:
    the plugin has never branched on a PHP or WordPress version.
    
    The one test that asserted the classes must NOT be prefixed with WP_ now
    asserts the opposite, and that each one lives in the file its name implies.
  • 29/07/2026 11:01

    Step 2: adopt the shared config files from _standards/templates/

    Step 2: adopt the shared config files from _standards/templates/
    
    .editorconfig, .gitignore, composer.json, phpcs.xml, both phpunit configs,
    bin/test.sh, bin/test-multisite.sh, every index.php and ci.yml now come from
    the templates verbatim, with only the placeholders substituted. The eslint job
    goes with them: this plugin ships no JavaScript, so there is nothing to lint
    and no package.json, eslint.config.mjs or vitest.config.mjs to add.
    
    .wp-env.json gains the ports assigned in STANDARDS.md 10, 8906 and 8907, so
    this plugin's environment can no longer collide with a sibling's.
    
    The template's /tests/* exclusion list is closed, which cost four fixes here
    rather than four new exclusions:
    
    * The fixtures write their plugin files through WP_Filesystem instead of
      mkdir()/file_put_contents()/unlink()/rmdir(), with the transport pinned to
      "direct" in the bootstrap so it never negotiates credentials. The two tests
      that built a fixture inline now share the same pair of helpers.
    * The DOM inspection reads DOMAttr's lowercase name/value and describes the
      node with getNodePath(), rather than reaching for camelCase DOMNode
      properties WPCS cannot let through.
    * Five docblocks opening on a lowercase function name were reworded.

Log in