Plugin Directory

Changeset 3754124


Ignore:
Timestamp:
10/19/2026 06:48:07 AM (6 days ago)
Author:
haroondev
Message:

Tag 1.0.1: Add review notice banner and update brand SVG icons

Location:
purgeforge-db-cleaner
Files:
33 added
10 edited

Legend:

Unmodified
Added
Removed
  • purgeforge-db-cleaner/trunk/admin/class-admin-page.php

    r3754016 r3754124  
    2929         */
    3030        const MENU_POSITION = 81;
    31 
    32         /**
    33          * The menu icon: a database drawn as three stacked discs, a 20 x 20 filled shape. WordPress
    34          * paints a base64 SVG menu icon in the colour of the current admin colour scheme, and it
    35          * does so by setting the fill, so the icon has to be a filled shape and not an outline.
    36          */
    37         const MENU_ICON = '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="black"><ellipse cx="10" cy="4.2" rx="7" ry="2.7"/><path d="M3 7.6c1.2 1.3 3.8 2 7 2s5.8-.7 7-2v2.7c0 1.5-3.1 2.7-7 2.7s-7-1.2-7-2.7z"/><path d="M3 13.3c1.2 1.3 3.8 2 7 2s5.8-.7 7-2v2.5c0 1.5-3.1 2.7-7 2.7s-7-1.2-7-2.7z"/></svg>';
    3831
    3932        /**
    … …  
    180173        private function get_menu_icon() {
    181174                // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode -- Not obfuscation: a data URI is how WordPress takes an SVG menu icon.
    182                 return 'data:image/svg+xml;base64,' . base64_encode( self::MENU_ICON );
     175                return 'data:image/svg+xml;base64,' . base64_encode( Icons::get_menu_icon_svg() );
    183176        }
    184177
    … …  
    257250                                        'clearErrorLog'    => 'purgeforge_clear_error_log',
    258251                                        'debugLog'         => 'purgeforge_debug_log',
     252                                        'review'           => 'purgeforge_review_action',
    259253                                ),
    260254                                'tasks'   => $this->format_tasks(),
    … …  
    409403                        <hr class="wp-header-end">
    410404
     405                        <?php
     406                        /**
     407                         * Fires above the dashboard, for banners that belong to this screen only.
     408                         */
     409                        do_action( 'purgeforge_page_notices' );
     410                        ?>
     411
    411412                        <div class="purgeforge-shell">
    412413                                <aside class="purgeforge-sidebar">
    413414                                        <div class="purgeforge-brand">
    414415                                                <span class="purgeforge-brand__version"><?php echo esc_html( PURGEFORGE_DB_CLEANER_VERSION ); ?></span>
    415                                                 <span class="purgeforge-brand__logo"><?php Icons::render( 'database', 36 ); ?></span>
     416                                                <span class="purgeforge-brand__logo"><?php Icons::render_brand_badge( 36 ); ?></span>
    416417                                                <div class="purgeforge-brand__text">
    417418                                                        <span class="purgeforge-brand__name"><?php esc_html_e( 'PurgeForge DB Cleaner', 'purgeforge-db-cleaner' ); ?></span>
  • purgeforge-db-cleaner/trunk/assets/admin.css

    r3754016 r3754124  
    5858}
    5959
     60/* Review banner ------------------------------------------------------------ */
     61
     62.purgeforge-review {
     63        position: relative;
     64        display: flex;
     65        align-items: flex-start;
     66        gap: 14px;
     67        margin: 0 0 16px;
     68        padding: 16px 48px 16px 18px;
     69        color: #fff;
     70        background: linear-gradient(135deg, #0d6efd 0%, #1668e3 100%);
     71        border-radius: 8px;
     72        box-shadow: 0 1px 3px rgba(15, 23, 42, 0.12);
     73}
     74
     75.purgeforge-review[hidden] {
     76        display: none;
     77}
     78
     79.purgeforge-review__star {
     80        display: flex;
     81        flex: none;
     82        color: #f59e0b;
     83}
     84
     85.purgeforge-review__star .purgeforge-icon {
     86        fill: currentColor;
     87}
     88
     89.purgeforge-review__body {
     90        min-width: 0;
     91}
     92
     93.purgeforge-review__title {
     94        margin: 0 0 2px;
     95        font-size: 15px;
     96        font-weight: 700;
     97        line-height: 1.4;
     98        color: #fff;
     99}
     100
     101.purgeforge-review__text {
     102        margin: 0 0 12px;
     103        font-size: 13px;
     104        line-height: 1.5;
     105        color: rgba(255, 255, 255, 0.92);
     106}
     107
     108.purgeforge-review__actions {
     109        display: flex;
     110        flex-wrap: wrap;
     111        gap: 8px;
     112}
     113
     114.purgeforge-review__btn {
     115        display: inline-flex;
     116        align-items: center;
     117        padding: 6px 14px;
     118        font: inherit;
     119        font-size: 13px;
     120        font-weight: 600;
     121        line-height: 1.4;
     122        color: #fff;
     123        text-decoration: none;
     124        cursor: pointer;
     125        background: rgba(255, 255, 255, 0.18);
     126        border: 0;
     127        border-radius: 999px;
     128        transition: background-color 0.15s ease;
     129}
     130
     131.purgeforge-review__btn:hover,
     132.purgeforge-review__btn:focus {
     133        color: #fff;
     134        background: rgba(255, 255, 255, 0.3);
     135}
     136
     137.purgeforge-review__btn--primary {
     138        color: #0d4fb3;
     139        background: #fff;
     140}
     141
     142.purgeforge-review__btn--primary:hover,
     143.purgeforge-review__btn--primary:focus {
     144        color: #0a3d8a;
     145        background: #e8f0ff;
     146}
     147
     148.purgeforge-review__btn:focus-visible,
     149.purgeforge-review__close:focus-visible {
     150        outline: 2px solid #fff;
     151        outline-offset: 2px;
     152}
     153
     154.purgeforge-review__close {
     155        position: absolute;
     156        top: 10px;
     157        right: 10px;
     158        display: flex;
     159        padding: 4px;
     160        color: #fff;
     161        cursor: pointer;
     162        background: transparent;
     163        border: 0;
     164        border-radius: 50%;
     165        opacity: 0.8;
     166}
     167
     168.purgeforge-review__close:hover {
     169        background: rgba(255, 255, 255, 0.18);
     170        opacity: 1;
     171}
     172
    60173/* Brand and navigation ------------------------------------------------------ */
    61174
    … …  
    98211        width: 36px;
    99212        height: 36px;
    100         stroke-width: 1.75;
    101213}
    102214
  • purgeforge-db-cleaner/trunk/assets/admin.js

    r3754016 r3754124  
    32133213        } );
    32143214
     3215        /**
     3216         * Review banner: hides it at once and tells the server which button was used. A link opens its
     3217         * page in a new tab as usual.
     3218         */
     3219        function initReviewNotice() {
     3220                var banner = document.getElementById( 'purgeforge-review' );
     3221
     3222                if ( ! banner ) {
     3223                        return;
     3224                }
     3225
     3226                banner.addEventListener( 'click', function ( event ) {
     3227                        var control = event.target.closest( '[data-purgeforge-review]' );
     3228
     3229                        if ( ! control ) {
     3230                                return;
     3231                        }
     3232
     3233                        request( config.actions.review, { choice: control.dataset.purgeforgeReview } ).catch( function () {
     3234                                // The banner comes back on the next load; nothing else to do.
     3235                        } );
     3236
     3237                        banner.hidden = true;
     3238                } );
     3239        }
     3240
     3241        initReviewNotice();
    32153242        initTasks();
    32163243        initAnalytics();
  • purgeforge-db-cleaner/trunk/includes/class-activator.php

    r3754016 r3754124  
    2323
    2424        /**
    25          * Runs on plugin activation. Writes a single non-autoloaded option; no tables are created.
     25         * Runs on plugin activation. Writes non-autoloaded options; no tables are created.
    2626         *
    2727         * @return void
    … …  
    3030                if ( ! current_user_can( 'activate_plugins' ) ) {
    3131                        return;
     32                }
     33
     34                // Kept from the first activation on, so the review banner can wait a week.
     35                if ( false === get_option( Review_Notice::OPTION_ACTIVATED ) ) {
     36                        add_option( Review_Notice::OPTION_ACTIVATED, time(), '', 'no' );
    3237                }
    3338
  • purgeforge-db-cleaner/trunk/includes/class-activity-log.php

    r3754016 r3754124  
    8282                }
    8383
    84                 $trigger  = self::TRIGGER_CRON === $trigger ? self::TRIGGER_CRON : self::TRIGGER_MANUAL;
     84                $trigger = self::TRIGGER_CRON === $trigger ? self::TRIGGER_CRON : self::TRIGGER_MANUAL;
     85
     86                // A cleanup a person ran is what makes the review banner appropriate.
     87                if ( self::TRIGGER_MANUAL === $trigger && 'yes' !== get_option( Review_Notice::OPTION_CLEANED ) ) {
     88                        update_option( Review_Notice::OPTION_CLEANED, 'yes', false );
     89                }
     90
    8591                $run_id   = $this->sanitize_run_id( $run_id );
    8692                $previous = $this->get_summary();
  • purgeforge-db-cleaner/trunk/includes/class-icons.php

    r3754016 r3754124  
    6262                'shopping-bag'       => '<path d="M6 2 3 6v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V6l-3-4Z"/><path d="M3 6h18"/><path d="M16 10a4 4 0 0 1-8 0"/>',
    6363                'sliders-horizontal' => '<line x1="21" x2="14" y1="4" y2="4"/><line x1="10" x2="3" y1="4" y2="4"/><line x1="21" x2="12" y1="12" y2="12"/><line x1="8" x2="3" y1="12" y2="12"/><line x1="21" x2="16" y1="20" y2="20"/><line x1="12" x2="3" y1="20" y2="20"/><line x1="14" x2="14" y1="2" y2="6"/><line x1="8" x2="8" y1="10" y2="14"/><line x1="16" x2="16" y1="18" y2="22"/>',
     64                'star'               => '<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"/>',
    6465                'table'              => '<path d="M12 3v18"/><rect width="18" height="18" x="3" y="3" rx="2"/><path d="M3 9h18"/><path d="M3 15h18"/>',
    6566                'tag'                => '<path d="M12 2H2v10l9.29 9.29c.94.94 2.48.94 3.42 0l6.58-6.58c.94-.94.94-2.48 0-3.42L12 2Z"/><path d="M7 7h.01"/>',
    … …  
    6768                'users'              => '<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M22 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/>',
    6869                'woocommerce'        => '<path fill="currentColor" stroke="none" d="M2.227 4.857A2.228 2.228 0 000 7.094v7.457c0 1.236 1.001 2.237 2.237 2.237h9.253l4.229 2.355-.962-2.355h7.006c1.236 0 2.237-1 2.237-2.237V7.094c0-1.236-1-2.237-2.237-2.237zm8.08 1.311c.194.002.372.071.535.2a.769.769 0 01.304.56.851.851 0 01-.098.47c-.382.707-.696 1.894-.951 3.542-.246 1.6-.334 2.846-.275 3.739.02.245-.02.46-.118.647a.632.632 0 01-.52.353c-.255.02-.52-.098-.775-.362-.913-.933-1.639-2.326-2.169-4.18a184.085 184.085 0 00-1.413 2.825c-.578 1.11-1.069 1.678-1.481 1.708-.265.02-.49-.206-.687-.677-.5-1.286-1.04-3.768-1.619-7.448-.03-.255.02-.48.157-.657.137-.186.344-.284.618-.304.5-.04.785.196.854.706.304 2.051.638 3.788.991 5.21L5.809 8.41c.196-.373.441-.57.736-.589.431-.03.696.245.804.824.246 1.305.56 2.414.932 3.356.255-2.492.687-4.288 1.295-5.397.148-.274.363-.412.648-.431a.866.866 0 01.084-.004zm3.734 1.063c.167 0 .343.02.53.06.687.146 1.216.52 1.57 1.137.314.53.47 1.168.47 1.933 0 1.011-.254 1.933-.765 2.777-.588.981-1.354 1.472-2.305 1.472-.167 0-.344-.02-.53-.059-.697-.147-1.217-.52-1.57-1.138-.314-.54-.471-1.187-.471-1.943 0-1.01.255-1.933.765-2.767.599-.981 1.364-1.472 2.306-1.472zm6.152 0c.167 0 .343.02.53.06.696.146 1.216.52 1.57 1.137.314.53.47 1.168.47 1.933 0 1.011-.254 1.933-.765 2.777-.588.981-1.354 1.472-2.305 1.472-.167 0-.344-.02-.53-.059-.697-.147-1.217-.52-1.57-1.138-.314-.54-.471-1.187-.471-1.943 0-1.01.255-1.933.765-2.767.599-.981 1.364-1.472 2.306-1.472zm-6.107 1.645c-.307-.002-.606.201-.889.622a3.173 3.173 0 00-.52 1.168c-.05.225-.069.47-.069.716 0 .284.06.589.177.893.147.382.343.589.579.638.245.049.51-.06.795-.315.363-.323.608-.804.745-1.452.05-.225.069-.47.069-.726a2.49 2.49 0 00-.176-.893c-.148-.382-.344-.588-.58-.637a.714.714 0 00-.131-.014zm6.152 0c-.307-.002-.606.201-.889.622a3.173 3.173 0 00-.52 1.168c-.049.225-.069.47-.069.716 0 .284.06.589.177.893.147.382.344.589.579.638.245.049.51-.06.795-.315.363-.323.608-.804.745-1.452.04-.225.07-.47.07-.726a2.49 2.49 0 00-.177-.893c-.148-.382-.344-.588-.58-.637a.714.714 0 00-.131-.014z"/>',
     70                'x'                  => '<path d="M18 6 6 18"/><path d="m6 6 12 12"/>',
    6971                'zap'                => '<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"/>',
    7072        );
     73
     74        /**
     75         * Inner markup of the brand badge: a shield around a database sitting on an anvil, with a
     76         * sparkle. The plugin's own mark, drawn to match the icon set above, so it is not in SHAPES.
     77         */
     78        const BRAND_BADGE_SHAPES = '<path d="M12 2.5 19.5 5.2V11c0 4.6-3.1 8-7.5 9.8C7.6 19 4.5 15.6 4.5 11V5.2z"/><ellipse cx="12" cy="6.6" rx="3.6" ry="1.4"/><path d="M8.4 6.6v5.2"/><path d="M15.6 6.6v5.2"/><path d="M8.4 9.2a3.6 1.4 0 0 0 7.2 0"/><path d="M8.4 11.8a3.6 1.4 0 0 0 7.2 0"/><path d="M7 14.3h9.4c0 1-.7 1.5-1.6 1.5h-.7l.6 2H9.2l.6-2C8.2 15.8 7.4 15.3 7 14.3z"/><path d="M21 1.4v3.2"/><path d="M19.4 3h3.2"/><circle cx="22" cy="7.6" r=".4"/>';
    7179
    7280        /**
    … …  
    124132        public static function render( $name, $size = self::DEFAULT_SIZE ) {
    125133                echo wp_kses( self::get( $name, $size ), self::allowed_html() );
     134        }
     135
     136        /**
     137         * The brand badge as an SVG string: white-on-accent artwork that follows the text colour. It
     138         * is decorative, so it is hidden from assistive technology.
     139         *
     140         * @param int $size Width and height in pixels, from 8 to 64.
     141         * @return string
     142         */
     143        public static function get_brand_badge_svg( $size = 36 ) {
     144                $size = max( 8, min( 64, (int) $size ) );
     145
     146                return sprintf(
     147                        '<svg class="purgeforge-icon purgeforge-icon--brand" xmlns="http://www.w3.org/2000/svg" width="%1$d" height="%1$d" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false">%2$s</svg>',
     148                        $size,
     149                        self::BRAND_BADGE_SHAPES
     150                );
     151        }
     152
     153        /**
     154         * The brand badge as an admin menu icon, for add_menu_page().
     155         *
     156         * WordPress recolours a menu SVG by setting fill on its root element and ignores stroke, so
     157         * the strokes are drawn into a mask and a rectangle that inherits the root fill is painted
     158         * through it. That keeps the icon matching the menu's normal, hover and current colours.
     159         *
     160         * WordPress rewrites every fill attribute, style attribute and fill property in the markup,
     161         * so the root's fill must be the only one: the strokes are left unfilled with fill-opacity.
     162         *
     163         * @return string The SVG markup, not yet encoded.
     164         */
     165        public static function get_menu_icon_svg() {
     166                return '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="black"><mask id="pf"><g fill-opacity="0" stroke="white" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">' . self::BRAND_BADGE_SHAPES . '</g></mask><rect width="24" height="24" mask="url(#pf)"/></svg>';
     167        }
     168
     169        /**
     170         * Prints the brand badge.
     171         *
     172         * @param int $size Width and height in pixels, from 8 to 64.
     173         * @return void
     174         */
     175        public static function render_brand_badge( $size = 36 ) {
     176                echo wp_kses( self::get_brand_badge_svg( $size ), self::allowed_html() );
    126177        }
    127178
  • purgeforge-db-cleaner/trunk/includes/class-plugin.php

    r3754016 r3754124  
    7979                        $admin_page  = new Admin_Page( $cleaner, $cron, $log, $settings, $options, $history, $errors, $system_info, new Debug_Log() );
    8080                        $admin_page->register();
     81
     82                        ( new Review_Notice() )->register();
    8183                }
    8284
  • purgeforge-db-cleaner/trunk/purgeforge-db-cleaner.php

    r3754016 r3754124  
    44 * Plugin URI:        https://wordpress.org/plugins/purgeforge-db-cleaner/
    55 * Description:       Safe, efficient database and transient cleaner for WordPress.
    6  * Version:           1.0.0
     6 * Version:           1.0.1
    77 * Requires at least: 5.8
    88 * Requires PHP:      7.4
    … …  
    2424}
    2525
    26 define( 'PURGEFORGE_DB_CLEANER_VERSION', '1.0.0' );
     26define( 'PURGEFORGE_DB_CLEANER_VERSION', '1.0.1' );
    2727define( 'PURGEFORGE_DB_CLEANER_FILE', __FILE__ );
    2828define( 'PURGEFORGE_DB_CLEANER_PATH', plugin_dir_path( __FILE__ ) );
  • purgeforge-db-cleaner/trunk/readme.txt

    r3754016 r3754124  
    55Tested up to: 7.1
    66Requires PHP: 7.4
    7 Stable tag: 1.0.0
     7Stable tag: 1.0.1
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
    … …  
    218218
    219219== Changelog ==
     220
     221= 1.0.1 =
     222* Enhancement: Added theme-matched, dismissible review request banner with secure AJAX handling.
     223* UI/UX: Replaced raster images with crisp monochrome white vector SVG brand icons in both admin sidebar menu and dashboard header badge.
     224* Performance: Optimized icon rendering and ensured 100% PHPCS compliance across all new notice components.
    220225
    221226= 1.0.0 =
  • purgeforge-db-cleaner/trunk/uninstall.php

    r3754016 r3754124  
    2626delete_option( 'purgeforge_batch_size' );
    2727delete_option( 'purgeforge_revisions_keep' );
     28delete_option( 'purgeforge_first_activated_time' );
     29delete_option( 'purgeforge_review_dismissed' );
     30delete_option( 'purgeforge_review_snooze_until' );
     31delete_option( 'purgeforge_has_cleaned' );
Note: See TracChangeset for help on using the changeset viewer.