Plugin Directory

Changeset 3790684


Ignore:
Timestamp:
11/08/2026 08:27:28 AM (5 hours ago)
Author:
dhanushv14
Message:

Update to version 1.10.4 from GitHub

Location:
equalfix
Files:
6 edited
1 copied

Legend:

Unmodified
Added
Removed
  • equalfix/tags/1.10.4/equalfix-accessibility.php

    r3790639 r3790684  
    44 * Plugin URI:        https://equalfix.com
    55 * Description:       Helps improve your site's accessibility by detecting specific issues and applying safe, reversible fixes to the rendered HTML. Includes a full site audit, the free fix set, and per-fix-type preview and toggle controls.
    6  * Version:           1.10.3
     6 * Version:           1.10.4
    77 * Requires at least: 6.0
    88 * Requires PHP:      7.4
    … …  
    2323}
    2424
    25 define( 'EQUALFIX_VERSION', '1.10.3' );
     25define( 'EQUALFIX_VERSION', '1.10.4' );
    2626define( 'EQUALFIX_FILE', __FILE__ );
    2727define( 'EQUALFIX_DIR', plugin_dir_path( __FILE__ ) );
  • equalfix/tags/1.10.4/includes/class-equalfix-settings.php

    r3790639 r3790684  
    60126012                        $only = '';
    60136013                }
    6014                 $oneclick_n = $this->issues_oneclick_count( $result, $model, $settings );
     6014                $oneclick_stats = $this->issues_oneclick_stats( $result, $model, $settings );
     6015                $oneclick_n     = $oneclick_stats['count'];
    60156016
    60166017                if ( 'flagged' === $view || 'aside' === $view || 'fixed' === $view ) {
    … …  
    60546055                $this->render_issues_heading( $result );
    60556056                if ( $model['d_total'] > 0 ) {
    6056                         $this->render_issues_summary( $result, $model, $settings, $oneclick_n );
     6057                        $this->render_issues_summary( $result, $model, $settings, $oneclick_stats );
    60576058                }
    60586059                if ( 'all' === $view && ! $all_on ) {
    … …  
    61696170                        // the same predicate, so the chip, the tile and
    61706171                        // this list can never disagree.
    6171                         $cards_list = array();
    6172                         $manual_map = self::manual_matches_map( $result );
    6173                         $attr_map   = self::attr_matches_map( $result );
     6172                        $cards_list   = array();
     6173                        $manual_map   = self::manual_matches_map( $result );
     6174                        $attr_map     = self::attr_matches_map( $result );
     6175                        $oneclick_ctx = $this->oneclick_proposal_ctx( $result );
    61746176                        foreach ( $list as $oneclick_finding ) {
    6175                                 if ( $this->is_oneclick_finding( $oneclick_finding, $model, $settings, $manual_map, $attr_map ) ) {
     6177                                if ( $this->is_oneclick_finding( $oneclick_finding, $model, $settings, $manual_map, $attr_map, $oneclick_ctx ) ) {
    61766178                                        $cards_list[] = $oneclick_finding;
    61776179                                }
    61786180                        }
    61796181                        ?>
    6180                         <p class="eqf-filter-note"><?php esc_html_e( 'Showing only the problems the free fix set can fix in one click.', 'equalfix' ); ?> <a href="<?php echo esc_url( $this->tab_url( 'issues', array( 'view' => $view ) ) ); ?>"><?php esc_html_e( 'Show everything', 'equalfix' ); ?></a></p>
     6182                        <p class="eqf-filter-note"><?php esc_html_e( 'Showing only the problems that can be fixed in one click.', 'equalfix' ); ?> <a href="<?php echo esc_url( $this->tab_url( 'issues', array( 'view' => $view ) ) ); ?>"><?php esc_html_e( 'Show everything', 'equalfix' ); ?></a></p>
    61816183                        <?php
    61826184                }
    … …  
    63536355         * partition already).
    63546356         *
    6355          * @param array $finding    Finding.
    6356          * @param array $model      Coverage model.
    6357          * @param array $settings   Settings.
    6358          * @param array $manual_map manual_matches_map() result.
     6357         * Counter-wording batch (2026-11-08): when the caller
     6358         * passes the proposal context, a finding ALSO qualifies
     6359         * when a computed proposal is available for it — the
     6360         * owner can fix it with one click and no typing (the
     6361         * group's own Fix action), which is what the strip and
     6362         * the chip promise. Callers that omit the context get
     6363         * exactly the shipped press-only semantics (the hero
     6364         * button's own count uses that narrower set — its press
     6365         * applies the standard set and nothing else).
     6366         *
     6367         * @param array      $finding    Finding.
     6368         * @param array      $model      Coverage model.
     6369         * @param array      $settings   Settings.
     6370         * @param array      $manual_map manual_matches_map() result.
     6371         * @param array      $attr_map   attr_matches_map() result.
     6372         * @param array|null $ctx        oneclick_proposal_ctx()
     6373         *                               result, or null for the
     6374         *                               press-only predicate.
    63596375         * @return bool
    63606376         */
    6361         private function is_oneclick_finding( $finding, $model, $settings, $manual_map, $attr_map = array() ) {
     6377        private function is_oneclick_finding( $finding, $model, $settings, $manual_map, $attr_map = array(), $ctx = null ) {
    63626378                $fp = self::finding_fingerprint( $finding );
    63636379                if ( isset( $model['fixed_fps'][ $fp ] ) || isset( $model['waiting_fps'][ $fp ] ) ) {
    … …  
    63706386                // same master switch the press turns on.
    63716387                if ( isset( $attr_map[ $fp ] ) ) {
     6388                        return true;
     6389                }
     6390                // A computed proposal (Fix-First) the owner can apply
     6391                // with one click and no typing — the same per-row
     6392                // availability the bands and row panels use.
     6393                if ( null !== $ctx && $this->finding_has_oneclick_proposal( $finding, $ctx ) ) {
    63726394                        return true;
    63736395                }
    … …  
    63876409
    63886410        /**
    6389          * The DISTINCT problems one press of Apply Standard Fixes
    6390          * would clear — the summary strip's hero number and the
    6391          * "One-click fixes" chip count. Same predicate as the
    6392          * filtered list (is_oneclick_finding()), counted in the
    6393          * model's problem unit, so tile, chip and list agree.
    6394          *
    6395          * @param array $result   Stored audit result.
    6396          * @param array $model    Coverage model.
    6397          * @param array $settings Settings.
    6398          * @return int
    6399          */
    6400         private function issues_oneclick_count( $result, $model, $settings ) {
    6401                 if ( ! is_array( $result ) || empty( $result['findings'] ) ) {
    6402                         return 0;
    6403                 }
     6411         * The Pro-side state the proposal test reads (counter-
     6412         * wording batch, 2026-11-08): licence activity, the Pro
     6413         * fix set's master/type state, and Pro coverage's
     6414         * "would fix if applied" map — the same reads the fix-b
     6415         * bands and rows render from, computed once per render
     6416         * instead of once per finding.
     6417         *
     6418         * @param array $result Stored audit result.
     6419         * @return array { licence_active: bool, pro_master_on:
     6420         *               bool, headings_type_on: bool,
     6421         *               fixed_if_on: array }
     6422         */
     6423        private function oneclick_proposal_ctx( $result ) {
     6424                $pro = self::pro_state();
     6425                $ctx = array(
     6426                        'licence_active'   => ! empty( $pro['licence_active'] ),
     6427                        'pro_master_on'    => false,
     6428                        'headings_type_on' => false,
     6429                        'fixed_if_on'      => array(),
     6430                );
     6431                if ( ! $ctx['licence_active'] ) {
     6432                        return $ctx;
     6433                }
     6434                if ( class_exists( 'EqualFix_Pro_Settings' ) && method_exists( 'EqualFix_Pro_Settings', 'fix_set_state' ) && class_exists( 'EqualFix_Pro_Fixer' ) ) {
     6435                        $fix_set                 = EqualFix_Pro_Settings::fix_set_state();
     6436                        $ctx['pro_master_on']    = ! empty( $fix_set['master_on'] );
     6437                        $ctx['headings_type_on'] = ! empty( $fix_set['types'][ EqualFix_Pro_Fixer::FIX_HEADINGS ] );
     6438                }
     6439                $coverage = $this->pro_coverage( $result );
     6440                if ( null !== $coverage ) {
     6441                        $ctx['fixed_if_on'] = $coverage['fixed_if_on'];
     6442                }
     6443                return $ctx;
     6444        }
     6445
     6446        /**
     6447         * Whether a computed proposal is available for this
     6448         * finding — one click, no typing (counter-wording batch,
     6449         * 2026-11-08). This mirrors the availability the bands
     6450         * and rows already render, group by group:
     6451         *  - attribute-rule groups: the proposal builder itself
     6452         *    (a proposal flagged needs-text is the owner's words,
     6453         *    not a computed fix, and never counts);
     6454         *  - page titles: the page-title proposal (post title or
     6455         *    site name, prefilled in the panel);
     6456         *  - embeds: the band's bulk action saves the provider-
     6457         *    derived title for every embed — computed, no typing;
     6458         *  - headings (Pro): only while the licence is active —
     6459         *    the finding is in Pro coverage's "would fix if
     6460         *    applied" map (the band's Apply press fixes exactly
     6461         *    that set), or the set and type are both on, when
     6462         *    every remaining skip's row offers the per-page
     6463         *    full-correction approval.
     6464         * Everything else — owner-words groups, media captions
     6465         * and transcripts the owner must supply, the honest
     6466         * exceptions — has no computed proposal by design.
     6467         *
     6468         * @param array $finding Finding.
     6469         * @param array $ctx     oneclick_proposal_ctx() result.
     6470         * @return bool
     6471         */
     6472        private function finding_has_oneclick_proposal( $finding, $ctx ) {
     6473                $meta  = $this->finding_job_meta( $finding );
     6474                $group = $meta['group'];
     6475                if ( in_array( $group, self::attr_groups(), true ) ) {
     6476                        $proposal = $this->attr_proposal_for_finding( $finding );
     6477                        return null !== $proposal && empty( $proposal['needs_text'] );
     6478                }
     6479                if ( 'page-title' === $group ) {
     6480                        return null !== $this->pagetitle_proposal_for_finding( $finding );
     6481                }
     6482                if ( 'embed-3p' === $group || 'embed-title' === $group ) {
     6483                        return true;
     6484                }
     6485                if ( 'headings' === $group ) {
     6486                        if ( empty( $ctx['licence_active'] ) ) {
     6487                                return false;
     6488                        }
     6489                        $fp = self::finding_fingerprint( $finding );
     6490                        return isset( $ctx['fixed_if_on'][ $fp ] ) || ( ! empty( $ctx['pro_master_on'] ) && ! empty( $ctx['headings_type_on'] ) );
     6491                }
     6492                return false;
     6493        }
     6494
     6495        /**
     6496         * The DISTINCT-problem identity set behind the one-click
     6497         * counts: every problem with at least one qualifying
     6498         * finding (is_oneclick_finding()), in the model's problem
     6499         * unit, so tile, chip and list agree. With proposals off
     6500         * this is exactly the shipped press-only set.
     6501         *
     6502         * @param array $result         Stored audit result.
     6503         * @param array $model          Coverage model.
     6504         * @param array $settings       Settings.
     6505         * @param bool  $with_proposals Include computed-proposal
     6506         *                              problems.
     6507         * @return array Identity => true.
     6508         */
     6509        private function oneclick_ids( $result, $model, $settings, $with_proposals ) {
    64046510                $manual_map = self::manual_matches_map( $result );
    64056511                $attr_map   = self::attr_matches_map( $result );
     6512                $ctx        = $with_proposals ? $this->oneclick_proposal_ctx( $result ) : null;
    64066513                $dismissed  = self::dismissed_map();
    64076514                $ids        = array();
    … …  
    64106517                                continue;
    64116518                        }
    6412                         if ( $this->is_oneclick_finding( $finding, $model, $settings, $manual_map, $attr_map ) ) {
     6519                        if ( $this->is_oneclick_finding( $finding, $model, $settings, $manual_map, $attr_map, $ctx ) ) {
    64136520                                list( , $identity ) = $this->problem_identity( $finding );
    64146521                                $ids[ $identity ] = true;
    64156522                        }
    64166523                }
    6417                 return count( $ids );
     6524                return $ids;
     6525        }
     6526
     6527        /**
     6528         * The DISTINCT problems that can be fixed in one click —
     6529         * the summary strip's hero number and the "One-click
     6530         * fixes" chip count (counter-wording batch, 2026-11-08):
     6531         * the problems one press of Apply Standard Fixes would
     6532         * clear PLUS the problems carrying a computed proposal
     6533         * (finding_has_oneclick_proposal()). Same predicate as
     6534         * the filtered list, counted in the model's problem
     6535         * unit, so tile, chip and list agree.
     6536         *
     6537         * @param array $result   Stored audit result.
     6538         * @param array $model    Coverage model.
     6539         * @param array $settings Settings.
     6540         * @return int
     6541         */
     6542        private function issues_oneclick_count( $result, $model, $settings ) {
     6543                if ( ! is_array( $result ) || empty( $result['findings'] ) ) {
     6544                        return 0;
     6545                }
     6546                return count( $this->oneclick_ids( $result, $model, $settings, true ) );
     6547        }
     6548
     6549        /**
     6550         * The one-click figures the summary strip renders
     6551         * (counter-wording batch, 2026-11-08), computed in one
     6552         * pass so the tiles can never disagree:
     6553         *  - count: the widened one-click set (issues_oneclick_
     6554         *    count());
     6555         *  - press: the narrower shipped set the hero button's
     6556         *    own press (apply_standard_fixes) would clear — the
     6557         *    button is labelled with THIS number only;
     6558         *  - review_left: review-bucket problems (the model's
     6559         *    problem_buckets) that are NOT in the widened set —
     6560         *    the problems that genuinely still need the owner.
     6561         *    Equals d_review whenever no review problem carries
     6562         *    a proposal, which is how the strip knows the shipped
     6563         *    review wording still holds.
     6564         *
     6565         * @param array $result   Stored audit result.
     6566         * @param array $model    Coverage model.
     6567         * @param array $settings Settings.
     6568         * @return array { count: int, press: int, review_left: int }
     6569         */
     6570        private function issues_oneclick_stats( $result, $model, $settings ) {
     6571                $stats = array(
     6572                        'count'       => 0,
     6573                        'press'       => 0,
     6574                        'review_left' => (int) $model['d_review'],
     6575                );
     6576                if ( ! is_array( $result ) || empty( $result['findings'] ) ) {
     6577                        return $stats;
     6578                }
     6579                $ids            = $this->oneclick_ids( $result, $model, $settings, true );
     6580                $stats['count'] = count( $ids );
     6581                $stats['press'] = count( $this->oneclick_ids( $result, $model, $settings, false ) );
     6582                $left           = 0;
     6583                foreach ( (array) $model['problem_buckets'] as $identity => $bucket ) {
     6584                        if ( 'review' === $bucket && ! isset( $ids[ $identity ] ) ) {
     6585                                $left++;
     6586                        }
     6587                }
     6588                $stats['review_left'] = $left;
     6589                return $stats;
    64186590        }
    64196591
    … …  
    64226594         * three tiles over the ONE coverage model — problems
    64236595         * still to do (d_todo), the one-click count with the real
    6424          * Apply Standard Fixes form, and the review count
    6425          * (d_review) — over a progress bar, with the fixed note
    6426          * and the problems-vs-findings footnote in plain words.
    6427          * No number here is computed anywhere else.
    6428          *
    6429          * @param array $result     Stored audit result.
    6430          * @param array $model      Coverage model.
    6431          * @param array $settings   Settings.
    6432          * @param int   $oneclick_n issues_oneclick_count().
    6433          */
    6434         private function render_issues_summary( $result, $model, $settings, $oneclick_n ) {
     6596         * Apply Standard Fixes form, and the review count — over
     6597         * a progress bar, with the fixed note and the problems-
     6598         * vs-findings footnote in plain words. No number here is
     6599         * computed anywhere else.
     6600         *
     6601         * Counter-wording batch (2026-11-08): the hero tile's
     6602         * headline is the WIDENED one-click count (press set +
     6603         * computed proposals), but its form posts
     6604         * apply_standard_fixes, so the button keeps the PRESS
     6605         * count and renders only when that count is non-zero —
     6606         * the button must never promise the proposal problems.
     6607         * The review tile counts the review problems with NO
     6608         * proposal (review_left): while that equals d_review the
     6609         * shipped wording stands exactly as written; once some
     6610         * review problems carry a fix, the tile switches to the
     6611         * neutral wording for the remainder only.
     6612         *
     6613         * @param array $result        Stored audit result.
     6614         * @param array $model         Coverage model.
     6615         * @param array $settings      Settings.
     6616         * @param array $oneclick_stats issues_oneclick_stats().
     6617         */
     6618        private function render_issues_summary( $result, $model, $settings, $oneclick_stats ) {
     6619                $oneclick_n  = (int) $oneclick_stats['count'];
     6620                $press_n     = (int) $oneclick_stats['press'];
     6621                $review_left = (int) $oneclick_stats['review_left'];
    64356622                $pct = $model['d_total'] > 0 ? (int) round( 100 * $model['d_fixed'] / $model['d_total'] ) : 0;
    64366623                ?>
    … …  
    64486635                                <div class="eqf-tile eqf-tile-hero">
    64496636                                        <?php if ( $oneclick_n > 0 ) : ?>
    6450                                                 <div class="eqf-tile-big"><?php echo esc_html( sprintf( /* translators: %d: problems one press fixes */ _n( '%d can be fixed in one click', '%d can be fixed in one click', $oneclick_n, 'equalfix' ), $oneclick_n ) ); ?></div>
     6637                                                <div class="eqf-tile-big"><?php echo esc_html( sprintf( /* translators: %d: problems fixable in one click */ _n( '%d can be fixed in one click', '%d can be fixed in one click', $oneclick_n, 'equalfix' ), $oneclick_n ) ); ?></div>
    64516638                                                <div class="eqf-tile-lbl"><?php esc_html_e( 'EqualFix already knows how to fix these safely.', 'equalfix' ); ?></div>
     6639                                                <?php if ( $press_n > 0 ) : ?>
    64526640                                                <div class="eqf-tile-act">
    64536641                                                        <form method="post" action="">
    64546642                                                                <?php wp_nonce_field( 'equalfix_settings' ); ?>
    64556643                                                                <input type="hidden" name="equalfix_action" value="apply_standard_fixes">
    6456                                                                 <button type="submit" class="button button-primary"><?php echo esc_html( sprintf( /* translators: %d: problems one press fixes */ _n( 'Fix all %d now', 'Fix all %d now', $oneclick_n, 'equalfix' ), $oneclick_n ) ); ?></button>
     6644                                                                <button type="submit" class="button button-primary"><?php echo esc_html( sprintf( /* translators: %d: problems one press fixes */ _n( 'Fix all %d now', 'Fix all %d now', $press_n, 'equalfix' ), $press_n ) ); ?></button>
    64576645                                                        </form>
    64586646                                                </div>
     6647                                                <?php endif; ?>
    64596648                                        <?php else : ?>
    64606649                                                <div class="eqf-tile-big"><?php esc_html_e( '0 can be fixed in one click', 'equalfix' ); ?></div>
    … …  
    64636652                                </div>
    64646653                                <div class="eqf-tile">
    6465                                         <div class="eqf-tile-big"><?php echo (int) $model['d_review']; ?></div>
    6466                                         <div class="eqf-tile-lbl"><?php echo esc_html( _n( 'needs a quick look from you — no automatic fix is safe here', 'need a quick look from you — no automatic fix is safe here', (int) $model['d_review'], 'equalfix' ) ); ?></div>
     6654                                        <div class="eqf-tile-big"><?php echo $review_left; ?></div>
     6655                                        <div class="eqf-tile-lbl"><?php echo esc_html( $review_left === (int) $model['d_review']
     6656                                                ? _n( 'needs a quick look from you — no automatic fix is safe here', 'need a quick look from you — no automatic fix is safe here', $review_left, 'equalfix' )
     6657                                                : _n( 'needs a quick look from you — this one needs your input', 'need a quick look from you — these need your input', $review_left, 'equalfix' ) ); ?></div>
    64676658                                        <div class="eqf-tile-act"><a class="button eqf-btn-ghost" href="<?php echo esc_url( $this->tab_url( 'issues', array( 'view' => 'todo' ) ) ); ?>"><?php esc_html_e( 'Start reviewing', 'equalfix' ); ?></a></div>
    64686659                                </div>
    … …  
    75987789                                $is_review_group = in_array( $group['meta']['group'], self::REVIEW_GROUPS, true );
    75997790                                if ( $group['d_todo'] > 0 ) {
     7791                                        // Counter-wording batch (2026-11-08): a
     7792                                        // review-bucket group whose problems carry
     7793                                        // a computed fix (has_proposals — the same
     7794                                        // gate as the band's Fix all) is not
     7795                                        // "for your review" any more; the bucket
     7796                                        // itself is unchanged — the problems stay
     7797                                        // in review until the fix is applied.
    76007798                                        $count_line = $is_review_group
    7601                                                 ? sprintf( _n( '%d problem for your review', '%d problems for your review', $group['d_todo'], 'equalfix' ), $group['d_todo'] )
     7799                                                ? ( ! empty( $group['has_proposals'] )
     7800                                                        ? sprintf( _n( '%d problem — a fix is ready', '%d problems — a fix is ready', $group['d_todo'], 'equalfix' ), $group['d_todo'] )
     7801                                                        : sprintf( _n( '%d problem for your review', '%d problems for your review', $group['d_todo'], 'equalfix' ), $group['d_todo'] ) )
    76027802                                                : sprintf( _n( '%d problem still to do', '%d problems still to do', $group['d_todo'], 'equalfix' ), $group['d_todo'] );
    76037803                                } else {
    … …  
    76517851                                ?>
    76527852                                <tr class="eqf-band" id="eqf-group-<?php echo esc_attr( $meta['group'] ); ?>"><td colspan="4"><div class="eqf-band-in">
    7653                                         <span class="eqf-band-name"><?php echo esc_html( $this->issue_display_title( $meta['group'], $meta['name'] ) ); ?></span><?php if ( 'oneclick' === $card_tag ) : ?><span class="eqf-tag eqf-tag-oneclick"><?php esc_html_e( 'One-click fix', 'equalfix' ); ?></span><?php elseif ( 'pro' === $card_tag ) : ?><span class="eqf-tag eqf-tag-pro"><?php esc_html_e( 'Pro', 'equalfix' ); ?></span><?php endif; ?><?php if ( $is_review_group ) : ?><span class="eqf-badge eqf-badge-review"><?php esc_html_e( 'Needs human review', 'equalfix' ); ?></span><?php endif; ?>
     7853                                        <span class="eqf-band-name"><?php echo esc_html( $this->issue_display_title( $meta['group'], $meta['name'] ) ); ?></span><?php if ( 'oneclick' === $card_tag ) : ?><span class="eqf-tag eqf-tag-oneclick"><?php esc_html_e( 'One-click fix', 'equalfix' ); ?></span><?php elseif ( 'pro' === $card_tag ) : ?><span class="eqf-tag eqf-tag-pro"><?php esc_html_e( 'Pro', 'equalfix' ); ?></span><?php endif; ?><?php if ( $is_review_group && empty( $group['has_proposals'] ) ) : ?><span class="eqf-badge eqf-badge-review"><?php esc_html_e( 'Needs human review', 'equalfix' ); ?></span><?php endif; ?>
    76547854                                        <span class="eqf-band-count"><?php echo esc_html( $count_line ); ?></span>
    76557855                                        <?php $this->render_card_action( $group, $meta, $ctx_group, $skip_map, $open, $pro ); ?>
  • equalfix/tags/1.10.4/readme.txt

    r3790639 r3790684  
    55Tested up to: 7.1
    66Requires PHP: 7.4
    7 Stable tag: 1.10.3
     7Stable tag: 1.10.4
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
    … …  
    9999== Changelog ==
    100100
     101= 1.10.4 =
     102* Fixed: the Issues summary now counts every problem that already has a fix ready (including the computed fixes), so the one-click count matches the Fix all buttons below it.
     103
    101104= 1.10.3 =
    102105* The Issues tab is now one compact, scrollable table.
  • equalfix/trunk/equalfix-accessibility.php

    r3790639 r3790684  
    44 * Plugin URI:        https://equalfix.com
    55 * Description:       Helps improve your site's accessibility by detecting specific issues and applying safe, reversible fixes to the rendered HTML. Includes a full site audit, the free fix set, and per-fix-type preview and toggle controls.
    6  * Version:           1.10.3
     6 * Version:           1.10.4
    77 * Requires at least: 6.0
    88 * Requires PHP:      7.4
    … …  
    2323}
    2424
    25 define( 'EQUALFIX_VERSION', '1.10.3' );
     25define( 'EQUALFIX_VERSION', '1.10.4' );
    2626define( 'EQUALFIX_FILE', __FILE__ );
    2727define( 'EQUALFIX_DIR', plugin_dir_path( __FILE__ ) );
  • equalfix/trunk/includes/class-equalfix-settings.php

    r3790639 r3790684  
    60126012                        $only = '';
    60136013                }
    6014                 $oneclick_n = $this->issues_oneclick_count( $result, $model, $settings );
     6014                $oneclick_stats = $this->issues_oneclick_stats( $result, $model, $settings );
     6015                $oneclick_n     = $oneclick_stats['count'];
    60156016
    60166017                if ( 'flagged' === $view || 'aside' === $view || 'fixed' === $view ) {
    … …  
    60546055                $this->render_issues_heading( $result );
    60556056                if ( $model['d_total'] > 0 ) {
    6056                         $this->render_issues_summary( $result, $model, $settings, $oneclick_n );
     6057                        $this->render_issues_summary( $result, $model, $settings, $oneclick_stats );
    60576058                }
    60586059                if ( 'all' === $view && ! $all_on ) {
    … …  
    61696170                        // the same predicate, so the chip, the tile and
    61706171                        // this list can never disagree.
    6171                         $cards_list = array();
    6172                         $manual_map = self::manual_matches_map( $result );
    6173                         $attr_map   = self::attr_matches_map( $result );
     6172                        $cards_list   = array();
     6173                        $manual_map   = self::manual_matches_map( $result );
     6174                        $attr_map     = self::attr_matches_map( $result );
     6175                        $oneclick_ctx = $this->oneclick_proposal_ctx( $result );
    61746176                        foreach ( $list as $oneclick_finding ) {
    6175                                 if ( $this->is_oneclick_finding( $oneclick_finding, $model, $settings, $manual_map, $attr_map ) ) {
     6177                                if ( $this->is_oneclick_finding( $oneclick_finding, $model, $settings, $manual_map, $attr_map, $oneclick_ctx ) ) {
    61766178                                        $cards_list[] = $oneclick_finding;
    61776179                                }
    61786180                        }
    61796181                        ?>
    6180                         <p class="eqf-filter-note"><?php esc_html_e( 'Showing only the problems the free fix set can fix in one click.', 'equalfix' ); ?> <a href="<?php echo esc_url( $this->tab_url( 'issues', array( 'view' => $view ) ) ); ?>"><?php esc_html_e( 'Show everything', 'equalfix' ); ?></a></p>
     6182                        <p class="eqf-filter-note"><?php esc_html_e( 'Showing only the problems that can be fixed in one click.', 'equalfix' ); ?> <a href="<?php echo esc_url( $this->tab_url( 'issues', array( 'view' => $view ) ) ); ?>"><?php esc_html_e( 'Show everything', 'equalfix' ); ?></a></p>
    61816183                        <?php
    61826184                }
    … …  
    63536355         * partition already).
    63546356         *
    6355          * @param array $finding    Finding.
    6356          * @param array $model      Coverage model.
    6357          * @param array $settings   Settings.
    6358          * @param array $manual_map manual_matches_map() result.
     6357         * Counter-wording batch (2026-11-08): when the caller
     6358         * passes the proposal context, a finding ALSO qualifies
     6359         * when a computed proposal is available for it — the
     6360         * owner can fix it with one click and no typing (the
     6361         * group's own Fix action), which is what the strip and
     6362         * the chip promise. Callers that omit the context get
     6363         * exactly the shipped press-only semantics (the hero
     6364         * button's own count uses that narrower set — its press
     6365         * applies the standard set and nothing else).
     6366         *
     6367         * @param array      $finding    Finding.
     6368         * @param array      $model      Coverage model.
     6369         * @param array      $settings   Settings.
     6370         * @param array      $manual_map manual_matches_map() result.
     6371         * @param array      $attr_map   attr_matches_map() result.
     6372         * @param array|null $ctx        oneclick_proposal_ctx()
     6373         *                               result, or null for the
     6374         *                               press-only predicate.
    63596375         * @return bool
    63606376         */
    6361         private function is_oneclick_finding( $finding, $model, $settings, $manual_map, $attr_map = array() ) {
     6377        private function is_oneclick_finding( $finding, $model, $settings, $manual_map, $attr_map = array(), $ctx = null ) {
    63626378                $fp = self::finding_fingerprint( $finding );
    63636379                if ( isset( $model['fixed_fps'][ $fp ] ) || isset( $model['waiting_fps'][ $fp ] ) ) {
    … …  
    63706386                // same master switch the press turns on.
    63716387                if ( isset( $attr_map[ $fp ] ) ) {
     6388                        return true;
     6389                }
     6390                // A computed proposal (Fix-First) the owner can apply
     6391                // with one click and no typing — the same per-row
     6392                // availability the bands and row panels use.
     6393                if ( null !== $ctx && $this->finding_has_oneclick_proposal( $finding, $ctx ) ) {
    63726394                        return true;
    63736395                }
    … …  
    63876409
    63886410        /**
    6389          * The DISTINCT problems one press of Apply Standard Fixes
    6390          * would clear — the summary strip's hero number and the
    6391          * "One-click fixes" chip count. Same predicate as the
    6392          * filtered list (is_oneclick_finding()), counted in the
    6393          * model's problem unit, so tile, chip and list agree.
    6394          *
    6395          * @param array $result   Stored audit result.
    6396          * @param array $model    Coverage model.
    6397          * @param array $settings Settings.
    6398          * @return int
    6399          */
    6400         private function issues_oneclick_count( $result, $model, $settings ) {
    6401                 if ( ! is_array( $result ) || empty( $result['findings'] ) ) {
    6402                         return 0;
    6403                 }
     6411         * The Pro-side state the proposal test reads (counter-
     6412         * wording batch, 2026-11-08): licence activity, the Pro
     6413         * fix set's master/type state, and Pro coverage's
     6414         * "would fix if applied" map — the same reads the fix-b
     6415         * bands and rows render from, computed once per render
     6416         * instead of once per finding.
     6417         *
     6418         * @param array $result Stored audit result.
     6419         * @return array { licence_active: bool, pro_master_on:
     6420         *               bool, headings_type_on: bool,
     6421         *               fixed_if_on: array }
     6422         */
     6423        private function oneclick_proposal_ctx( $result ) {
     6424                $pro = self::pro_state();
     6425                $ctx = array(
     6426                        'licence_active'   => ! empty( $pro['licence_active'] ),
     6427                        'pro_master_on'    => false,
     6428                        'headings_type_on' => false,
     6429                        'fixed_if_on'      => array(),
     6430                );
     6431                if ( ! $ctx['licence_active'] ) {
     6432                        return $ctx;
     6433                }
     6434                if ( class_exists( 'EqualFix_Pro_Settings' ) && method_exists( 'EqualFix_Pro_Settings', 'fix_set_state' ) && class_exists( 'EqualFix_Pro_Fixer' ) ) {
     6435                        $fix_set                 = EqualFix_Pro_Settings::fix_set_state();
     6436                        $ctx['pro_master_on']    = ! empty( $fix_set['master_on'] );
     6437                        $ctx['headings_type_on'] = ! empty( $fix_set['types'][ EqualFix_Pro_Fixer::FIX_HEADINGS ] );
     6438                }
     6439                $coverage = $this->pro_coverage( $result );
     6440                if ( null !== $coverage ) {
     6441                        $ctx['fixed_if_on'] = $coverage['fixed_if_on'];
     6442                }
     6443                return $ctx;
     6444        }
     6445
     6446        /**
     6447         * Whether a computed proposal is available for this
     6448         * finding — one click, no typing (counter-wording batch,
     6449         * 2026-11-08). This mirrors the availability the bands
     6450         * and rows already render, group by group:
     6451         *  - attribute-rule groups: the proposal builder itself
     6452         *    (a proposal flagged needs-text is the owner's words,
     6453         *    not a computed fix, and never counts);
     6454         *  - page titles: the page-title proposal (post title or
     6455         *    site name, prefilled in the panel);
     6456         *  - embeds: the band's bulk action saves the provider-
     6457         *    derived title for every embed — computed, no typing;
     6458         *  - headings (Pro): only while the licence is active —
     6459         *    the finding is in Pro coverage's "would fix if
     6460         *    applied" map (the band's Apply press fixes exactly
     6461         *    that set), or the set and type are both on, when
     6462         *    every remaining skip's row offers the per-page
     6463         *    full-correction approval.
     6464         * Everything else — owner-words groups, media captions
     6465         * and transcripts the owner must supply, the honest
     6466         * exceptions — has no computed proposal by design.
     6467         *
     6468         * @param array $finding Finding.
     6469         * @param array $ctx     oneclick_proposal_ctx() result.
     6470         * @return bool
     6471         */
     6472        private function finding_has_oneclick_proposal( $finding, $ctx ) {
     6473                $meta  = $this->finding_job_meta( $finding );
     6474                $group = $meta['group'];
     6475                if ( in_array( $group, self::attr_groups(), true ) ) {
     6476                        $proposal = $this->attr_proposal_for_finding( $finding );
     6477                        return null !== $proposal && empty( $proposal['needs_text'] );
     6478                }
     6479                if ( 'page-title' === $group ) {
     6480                        return null !== $this->pagetitle_proposal_for_finding( $finding );
     6481                }
     6482                if ( 'embed-3p' === $group || 'embed-title' === $group ) {
     6483                        return true;
     6484                }
     6485                if ( 'headings' === $group ) {
     6486                        if ( empty( $ctx['licence_active'] ) ) {
     6487                                return false;
     6488                        }
     6489                        $fp = self::finding_fingerprint( $finding );
     6490                        return isset( $ctx['fixed_if_on'][ $fp ] ) || ( ! empty( $ctx['pro_master_on'] ) && ! empty( $ctx['headings_type_on'] ) );
     6491                }
     6492                return false;
     6493        }
     6494
     6495        /**
     6496         * The DISTINCT-problem identity set behind the one-click
     6497         * counts: every problem with at least one qualifying
     6498         * finding (is_oneclick_finding()), in the model's problem
     6499         * unit, so tile, chip and list agree. With proposals off
     6500         * this is exactly the shipped press-only set.
     6501         *
     6502         * @param array $result         Stored audit result.
     6503         * @param array $model          Coverage model.
     6504         * @param array $settings       Settings.
     6505         * @param bool  $with_proposals Include computed-proposal
     6506         *                              problems.
     6507         * @return array Identity => true.
     6508         */
     6509        private function oneclick_ids( $result, $model, $settings, $with_proposals ) {
    64046510                $manual_map = self::manual_matches_map( $result );
    64056511                $attr_map   = self::attr_matches_map( $result );
     6512                $ctx        = $with_proposals ? $this->oneclick_proposal_ctx( $result ) : null;
    64066513                $dismissed  = self::dismissed_map();
    64076514                $ids        = array();
    … …  
    64106517                                continue;
    64116518                        }
    6412                         if ( $this->is_oneclick_finding( $finding, $model, $settings, $manual_map, $attr_map ) ) {
     6519                        if ( $this->is_oneclick_finding( $finding, $model, $settings, $manual_map, $attr_map, $ctx ) ) {
    64136520                                list( , $identity ) = $this->problem_identity( $finding );
    64146521                                $ids[ $identity ] = true;
    64156522                        }
    64166523                }
    6417                 return count( $ids );
     6524                return $ids;
     6525        }
     6526
     6527        /**
     6528         * The DISTINCT problems that can be fixed in one click —
     6529         * the summary strip's hero number and the "One-click
     6530         * fixes" chip count (counter-wording batch, 2026-11-08):
     6531         * the problems one press of Apply Standard Fixes would
     6532         * clear PLUS the problems carrying a computed proposal
     6533         * (finding_has_oneclick_proposal()). Same predicate as
     6534         * the filtered list, counted in the model's problem
     6535         * unit, so tile, chip and list agree.
     6536         *
     6537         * @param array $result   Stored audit result.
     6538         * @param array $model    Coverage model.
     6539         * @param array $settings Settings.
     6540         * @return int
     6541         */
     6542        private function issues_oneclick_count( $result, $model, $settings ) {
     6543                if ( ! is_array( $result ) || empty( $result['findings'] ) ) {
     6544                        return 0;
     6545                }
     6546                return count( $this->oneclick_ids( $result, $model, $settings, true ) );
     6547        }
     6548
     6549        /**
     6550         * The one-click figures the summary strip renders
     6551         * (counter-wording batch, 2026-11-08), computed in one
     6552         * pass so the tiles can never disagree:
     6553         *  - count: the widened one-click set (issues_oneclick_
     6554         *    count());
     6555         *  - press: the narrower shipped set the hero button's
     6556         *    own press (apply_standard_fixes) would clear — the
     6557         *    button is labelled with THIS number only;
     6558         *  - review_left: review-bucket problems (the model's
     6559         *    problem_buckets) that are NOT in the widened set —
     6560         *    the problems that genuinely still need the owner.
     6561         *    Equals d_review whenever no review problem carries
     6562         *    a proposal, which is how the strip knows the shipped
     6563         *    review wording still holds.
     6564         *
     6565         * @param array $result   Stored audit result.
     6566         * @param array $model    Coverage model.
     6567         * @param array $settings Settings.
     6568         * @return array { count: int, press: int, review_left: int }
     6569         */
     6570        private function issues_oneclick_stats( $result, $model, $settings ) {
     6571                $stats = array(
     6572                        'count'       => 0,
     6573                        'press'       => 0,
     6574                        'review_left' => (int) $model['d_review'],
     6575                );
     6576                if ( ! is_array( $result ) || empty( $result['findings'] ) ) {
     6577                        return $stats;
     6578                }
     6579                $ids            = $this->oneclick_ids( $result, $model, $settings, true );
     6580                $stats['count'] = count( $ids );
     6581                $stats['press'] = count( $this->oneclick_ids( $result, $model, $settings, false ) );
     6582                $left           = 0;
     6583                foreach ( (array) $model['problem_buckets'] as $identity => $bucket ) {
     6584                        if ( 'review' === $bucket && ! isset( $ids[ $identity ] ) ) {
     6585                                $left++;
     6586                        }
     6587                }
     6588                $stats['review_left'] = $left;
     6589                return $stats;
    64186590        }
    64196591
    … …  
    64226594         * three tiles over the ONE coverage model — problems
    64236595         * still to do (d_todo), the one-click count with the real
    6424          * Apply Standard Fixes form, and the review count
    6425          * (d_review) — over a progress bar, with the fixed note
    6426          * and the problems-vs-findings footnote in plain words.
    6427          * No number here is computed anywhere else.
    6428          *
    6429          * @param array $result     Stored audit result.
    6430          * @param array $model      Coverage model.
    6431          * @param array $settings   Settings.
    6432          * @param int   $oneclick_n issues_oneclick_count().
    6433          */
    6434         private function render_issues_summary( $result, $model, $settings, $oneclick_n ) {
     6596         * Apply Standard Fixes form, and the review count — over
     6597         * a progress bar, with the fixed note and the problems-
     6598         * vs-findings footnote in plain words. No number here is
     6599         * computed anywhere else.
     6600         *
     6601         * Counter-wording batch (2026-11-08): the hero tile's
     6602         * headline is the WIDENED one-click count (press set +
     6603         * computed proposals), but its form posts
     6604         * apply_standard_fixes, so the button keeps the PRESS
     6605         * count and renders only when that count is non-zero —
     6606         * the button must never promise the proposal problems.
     6607         * The review tile counts the review problems with NO
     6608         * proposal (review_left): while that equals d_review the
     6609         * shipped wording stands exactly as written; once some
     6610         * review problems carry a fix, the tile switches to the
     6611         * neutral wording for the remainder only.
     6612         *
     6613         * @param array $result        Stored audit result.
     6614         * @param array $model         Coverage model.
     6615         * @param array $settings      Settings.
     6616         * @param array $oneclick_stats issues_oneclick_stats().
     6617         */
     6618        private function render_issues_summary( $result, $model, $settings, $oneclick_stats ) {
     6619                $oneclick_n  = (int) $oneclick_stats['count'];
     6620                $press_n     = (int) $oneclick_stats['press'];
     6621                $review_left = (int) $oneclick_stats['review_left'];
    64356622                $pct = $model['d_total'] > 0 ? (int) round( 100 * $model['d_fixed'] / $model['d_total'] ) : 0;
    64366623                ?>
    … …  
    64486635                                <div class="eqf-tile eqf-tile-hero">
    64496636                                        <?php if ( $oneclick_n > 0 ) : ?>
    6450                                                 <div class="eqf-tile-big"><?php echo esc_html( sprintf( /* translators: %d: problems one press fixes */ _n( '%d can be fixed in one click', '%d can be fixed in one click', $oneclick_n, 'equalfix' ), $oneclick_n ) ); ?></div>
     6637                                                <div class="eqf-tile-big"><?php echo esc_html( sprintf( /* translators: %d: problems fixable in one click */ _n( '%d can be fixed in one click', '%d can be fixed in one click', $oneclick_n, 'equalfix' ), $oneclick_n ) ); ?></div>
    64516638                                                <div class="eqf-tile-lbl"><?php esc_html_e( 'EqualFix already knows how to fix these safely.', 'equalfix' ); ?></div>
     6639                                                <?php if ( $press_n > 0 ) : ?>
    64526640                                                <div class="eqf-tile-act">
    64536641                                                        <form method="post" action="">
    64546642                                                                <?php wp_nonce_field( 'equalfix_settings' ); ?>
    64556643                                                                <input type="hidden" name="equalfix_action" value="apply_standard_fixes">
    6456                                                                 <button type="submit" class="button button-primary"><?php echo esc_html( sprintf( /* translators: %d: problems one press fixes */ _n( 'Fix all %d now', 'Fix all %d now', $oneclick_n, 'equalfix' ), $oneclick_n ) ); ?></button>
     6644                                                                <button type="submit" class="button button-primary"><?php echo esc_html( sprintf( /* translators: %d: problems one press fixes */ _n( 'Fix all %d now', 'Fix all %d now', $press_n, 'equalfix' ), $press_n ) ); ?></button>
    64576645                                                        </form>
    64586646                                                </div>
     6647                                                <?php endif; ?>
    64596648                                        <?php else : ?>
    64606649                                                <div class="eqf-tile-big"><?php esc_html_e( '0 can be fixed in one click', 'equalfix' ); ?></div>
    … …  
    64636652                                </div>
    64646653                                <div class="eqf-tile">
    6465                                         <div class="eqf-tile-big"><?php echo (int) $model['d_review']; ?></div>
    6466                                         <div class="eqf-tile-lbl"><?php echo esc_html( _n( 'needs a quick look from you — no automatic fix is safe here', 'need a quick look from you — no automatic fix is safe here', (int) $model['d_review'], 'equalfix' ) ); ?></div>
     6654                                        <div class="eqf-tile-big"><?php echo $review_left; ?></div>
     6655                                        <div class="eqf-tile-lbl"><?php echo esc_html( $review_left === (int) $model['d_review']
     6656                                                ? _n( 'needs a quick look from you — no automatic fix is safe here', 'need a quick look from you — no automatic fix is safe here', $review_left, 'equalfix' )
     6657                                                : _n( 'needs a quick look from you — this one needs your input', 'need a quick look from you — these need your input', $review_left, 'equalfix' ) ); ?></div>
    64676658                                        <div class="eqf-tile-act"><a class="button eqf-btn-ghost" href="<?php echo esc_url( $this->tab_url( 'issues', array( 'view' => 'todo' ) ) ); ?>"><?php esc_html_e( 'Start reviewing', 'equalfix' ); ?></a></div>
    64686659                                </div>
    … …  
    75987789                                $is_review_group = in_array( $group['meta']['group'], self::REVIEW_GROUPS, true );
    75997790                                if ( $group['d_todo'] > 0 ) {
     7791                                        // Counter-wording batch (2026-11-08): a
     7792                                        // review-bucket group whose problems carry
     7793                                        // a computed fix (has_proposals — the same
     7794                                        // gate as the band's Fix all) is not
     7795                                        // "for your review" any more; the bucket
     7796                                        // itself is unchanged — the problems stay
     7797                                        // in review until the fix is applied.
    76007798                                        $count_line = $is_review_group
    7601                                                 ? sprintf( _n( '%d problem for your review', '%d problems for your review', $group['d_todo'], 'equalfix' ), $group['d_todo'] )
     7799                                                ? ( ! empty( $group['has_proposals'] )
     7800                                                        ? sprintf( _n( '%d problem — a fix is ready', '%d problems — a fix is ready', $group['d_todo'], 'equalfix' ), $group['d_todo'] )
     7801                                                        : sprintf( _n( '%d problem for your review', '%d problems for your review', $group['d_todo'], 'equalfix' ), $group['d_todo'] ) )
    76027802                                                : sprintf( _n( '%d problem still to do', '%d problems still to do', $group['d_todo'], 'equalfix' ), $group['d_todo'] );
    76037803                                } else {
    … …  
    76517851                                ?>
    76527852                                <tr class="eqf-band" id="eqf-group-<?php echo esc_attr( $meta['group'] ); ?>"><td colspan="4"><div class="eqf-band-in">
    7653                                         <span class="eqf-band-name"><?php echo esc_html( $this->issue_display_title( $meta['group'], $meta['name'] ) ); ?></span><?php if ( 'oneclick' === $card_tag ) : ?><span class="eqf-tag eqf-tag-oneclick"><?php esc_html_e( 'One-click fix', 'equalfix' ); ?></span><?php elseif ( 'pro' === $card_tag ) : ?><span class="eqf-tag eqf-tag-pro"><?php esc_html_e( 'Pro', 'equalfix' ); ?></span><?php endif; ?><?php if ( $is_review_group ) : ?><span class="eqf-badge eqf-badge-review"><?php esc_html_e( 'Needs human review', 'equalfix' ); ?></span><?php endif; ?>
     7853                                        <span class="eqf-band-name"><?php echo esc_html( $this->issue_display_title( $meta['group'], $meta['name'] ) ); ?></span><?php if ( 'oneclick' === $card_tag ) : ?><span class="eqf-tag eqf-tag-oneclick"><?php esc_html_e( 'One-click fix', 'equalfix' ); ?></span><?php elseif ( 'pro' === $card_tag ) : ?><span class="eqf-tag eqf-tag-pro"><?php esc_html_e( 'Pro', 'equalfix' ); ?></span><?php endif; ?><?php if ( $is_review_group && empty( $group['has_proposals'] ) ) : ?><span class="eqf-badge eqf-badge-review"><?php esc_html_e( 'Needs human review', 'equalfix' ); ?></span><?php endif; ?>
    76547854                                        <span class="eqf-band-count"><?php echo esc_html( $count_line ); ?></span>
    76557855                                        <?php $this->render_card_action( $group, $meta, $ctx_group, $skip_map, $open, $pro ); ?>
  • equalfix/trunk/readme.txt

    r3790639 r3790684  
    55Tested up to: 7.1
    66Requires PHP: 7.4
    7 Stable tag: 1.10.3
     7Stable tag: 1.10.4
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
    … …  
    9999== Changelog ==
    100100
     101= 1.10.4 =
     102* Fixed: the Issues summary now counts every problem that already has a fix ready (including the computed fixes), so the one-click count matches the Fix all buttons below it.
     103
    101104= 1.10.3 =
    102105* The Issues tab is now one compact, scrollable table.
Note: See TracChangeset for help on using the changeset viewer.