Plugin Directory

Changeset 3717600


Ignore:
Timestamp:
09/28/2026 04:41:30 PM (3 weeks ago)
Author:
destinoexterior
Message:

Version 1.6.0: connector box redesign, tooltips, dynamic ordering, ManageWP link, WP 7.1

Location:
destino-access-audit/trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • destino-access-audit/trunk/destino-access-audit.php

    r3636849 r3717600  
    44 * Plugin URI:        https://destinoexterior.com
    55 * Description:       Security audit dashboard: lists the site's active Application Passwords (with AJAX revoke and CSV export) and shows the connection status of the installed remote management tools (UpdraftPlus/UpdraftCentral, MainWP Child, ManageWP Worker, InfiniteWP Client, Wordfence Security, Elementor Site Management).
    6  * Version:           1.5.0
     6 * Version:           1.6.0
    77 * Requires at least: 5.6
    88 * Requires PHP:      7.4
    … …  
    1616 * Changelog
    1717 * ---------
     18 * 1.6.0
     19 * - Connector block boxes (UpdraftPlus/UpdraftCentral, MainWP Child, ManageWP Worker,
     20 *   InfiniteWP Client, Wordfence Security, Elementor Site Management) restyled with a
     21 *   postbox-style header: title and Installed/Active badges in one line, right-aligned,
     22 *   separated from the body by a divider line (render_connector_box()). Every long
     23 *   explanatory paragraph (why a connection status can't be determined, UpdraftPlus's Lock
     24 *   Settings note) is now a small "?" icon whose explanation shows in a native HTML `title`
     25 *   tooltip (help_icon()) instead of always-visible body text -- tried with the plain `title`
     26 *   attribute first, since it needs no new CSS/JS; a custom-styled tooltip is the fallback if
     27 *   this doesn't read well in practice.
     28 * - The connector boxes now sort themselves with installed plugins first (get_sorted_connectors()),
     29 *   instead of a fixed order. Sorting is a hand-rolled stable sort (decorate with the original
     30 *   index, sort, undecorate) rather than relying on usort()'s stability, which PHP only
     31 *   guarantees since 8.0 and this plugin supports 7.4+.
     32 * - "Administrator Users" now renders before "Application Passwords".
     33 * - ManageWP Worker: added a direct link to its own "Connection Management" panel
     34 *   (admin_url( 'plugins.php?showWorker=1' )), the same link its docblock already described but
     35 *   never rendered as an actual button.
     36 * - Tested up to WordPress 7.1.
    1837 * 1.5.0
    1938 * - New blocks: Wordfence Security (installed/active only -- Wordfence Central's connection
    … …  
    109128}
    110129
    111 define( 'DESTINO_ACCESS_AUDIT_VERSION', '1.5.0' );
     130define( 'DESTINO_ACCESS_AUDIT_VERSION', '1.6.0' );
    112131
    113132/**
    … …  
    284303                        .destino-access-audit-table-wrap { overflow-x:auto; }
    285304                        @media (max-width: 900px) { .destino-access-audit-connectors { grid-template-columns:1fr; } }
     305                        .destino-access-audit-box-header { display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:6px 16px; margin:-1px -20px 16px; padding:14px 20px; border-bottom:1px solid #c3c4c7; }
     306                        .destino-access-audit-box-header h2 { margin:0; }
     307                        .destino-access-audit-box-badges { display:flex; align-items:center; gap:14px; flex-wrap:wrap; margin:0; font-size:13px; }
     308                        .destino-access-audit-box-badge { display:inline-flex; align-items:center; gap:6px; white-space:nowrap; }
     309                        .destino-access-audit-box-body > *:last-child { margin-bottom:0; }
     310                        .destino-access-audit-help { display:inline-flex; align-items:center; justify-content:center; width:16px; height:16px; border-radius:50%; background:#f0f0f1; color:#50575e; font-size:11px; font-weight:700; line-height:1; cursor:help; vertical-align:middle; }
     311                        .destino-access-audit-help:focus { outline:2px solid #2271b1; outline-offset:1px; }
    286312                ';
    287313        }
    … …  
    377403
    378404                        <div class="destino-access-audit-section">
     405                                <h2><?php esc_html_e( 'Administrator Users', 'destino-access-audit' ); ?></h2>
     406                                <?php self::render_administrators(); ?>
     407                        </div>
     408
     409                        <div class="destino-access-audit-section">
    379410                                <h2><?php esc_html_e( 'Application Passwords', 'destino-access-audit' ); ?></h2>
    380411                                <?php self::render_application_passwords(); ?>
    381412                        </div>
    382413
    383                         <div class="destino-access-audit-section">
    384                                 <h2><?php esc_html_e( 'Administrator Users', 'destino-access-audit' ); ?></h2>
    385                                 <?php self::render_administrators(); ?>
    386                         </div>
    387 
    388414                        <div class="destino-access-audit-connectors">
    389                                 <div class="destino-access-audit-section">
    390                                         <h2><?php echo esc_html( self::PRODUCT_UPDRAFTPLUS . ' / ' . self::PRODUCT_UPDRAFTCENTRAL ); ?></h2>
    391                                         <?php self::render_updraftplus_status(); ?>
    392                                 </div>
    393 
    394                                 <div class="destino-access-audit-section">
    395                                         <h2><?php echo esc_html( self::PRODUCT_MAINWP_CHILD ); ?></h2>
    396                                         <?php self::render_mainwp_status(); ?>
    397                                 </div>
    398 
    399                                 <div class="destino-access-audit-section">
    400                                         <h2><?php echo esc_html( self::PRODUCT_MANAGEWP_WORKER ); ?></h2>
    401                                         <?php self::render_managewp_status(); ?>
    402                                 </div>
    403 
    404                                 <div class="destino-access-audit-section">
    405                                         <h2><?php echo esc_html( self::PRODUCT_INFINITEWP_CLIENT ); ?></h2>
    406                                         <?php self::render_infinitewp_status(); ?>
    407                                 </div>
    408 
    409                                 <div class="destino-access-audit-section">
    410                                         <h2><?php echo esc_html( self::PRODUCT_WORDFENCE ); ?></h2>
    411                                         <?php self::render_wordfence_status(); ?>
    412                                 </div>
    413 
    414                                 <div class="destino-access-audit-section">
    415                                         <h2><?php echo esc_html( self::PRODUCT_ELEMENTOR_MANAGE ); ?></h2>
    416                                         <?php self::render_elementor_status(); ?>
    417                                 </div>
     415                                <?php foreach ( self::get_sorted_connectors() as $connector ) : ?>
     416                                        <?php self::render_connector_box( $connector['file'], $connector['label'], $connector['body'] ); ?>
     417                                <?php endforeach; ?>
    418418                        </div>
    419419                </div>
    420420                <?php
     421        }
     422
     423        /**
     424         * The six connector blocks, sorted so installed plugins render first and the rest after
     425         * (stable sort: within each group, the original order below is preserved -- PHP's usort()
     426         * only guarantees stability since 8.0, and this plugin supports 7.4+, so the tie-break on
     427         * original index below is done by hand rather than relying on that).
     428         *
     429         * @return array<int, array{file: string, label: string, body: callable}>
     430         */
     431        private static function get_sorted_connectors() {
     432                self::ensure_plugin_functions();
     433
     434                $connectors = array(
     435                        array(
     436                                'file'  => 'updraftplus/updraftplus.php',
     437                                'label' => self::PRODUCT_UPDRAFTPLUS . ' / ' . self::PRODUCT_UPDRAFTCENTRAL,
     438                                'body'  => array( __CLASS__, 'render_updraftplus_body' ),
     439                        ),
     440                        array(
     441                                'file'  => 'mainwp-child/mainwp-child.php',
     442                                'label' => self::PRODUCT_MAINWP_CHILD,
     443                                'body'  => array( __CLASS__, 'render_mainwp_body' ),
     444                        ),
     445                        array(
     446                                'file'  => 'worker/init.php',
     447                                'label' => self::PRODUCT_MANAGEWP_WORKER,
     448                                'body'  => array( __CLASS__, 'render_managewp_body' ),
     449                        ),
     450                        array(
     451                                'file'  => 'iwp-client/init.php',
     452                                'label' => self::PRODUCT_INFINITEWP_CLIENT,
     453                                'body'  => array( __CLASS__, 'render_infinitewp_body' ),
     454                        ),
     455                        array(
     456                                'file'  => 'wordfence/wordfence.php',
     457                                'label' => self::PRODUCT_WORDFENCE,
     458                                'body'  => array( __CLASS__, 'render_wordfence_body' ),
     459                        ),
     460                        array(
     461                                'file'  => 'manage/manage.php',
     462                                'label' => self::PRODUCT_ELEMENTOR_MANAGE,
     463                                'body'  => array( __CLASS__, 'render_elementor_body' ),
     464                        ),
     465                );
     466
     467                $decorated = array();
     468                foreach ( $connectors as $i => $connector ) {
     469                        $installed   = file_exists( WP_PLUGIN_DIR . '/' . $connector['file'] );
     470                        $decorated[] = array( $installed ? 0 : 1, $i, $connector );
     471                }
     472
     473                usort(
     474                        $decorated,
     475                        static function ( $a, $b ) {
     476                                return ( $a[0] !== $b[0] ) ? ( $a[0] <=> $b[0] ) : ( $a[1] <=> $b[1] );
     477                        }
     478                );
     479
     480                return array_map(
     481                        static function ( $item ) {
     482                                return $item[2];
     483                        },
     484                        $decorated
     485                );
     486        }
     487
     488        /**
     489         * Shared "postbox"-style wrapper for a connector block: a header strip (product name +
     490         * Installed/Active badges, right-aligned, separated from the body by a divider line) and,
     491         * only when the plugin is active, the block-specific body.
     492         *
     493         * @param string   $file  Plugin file relative to WP_PLUGIN_DIR, used for the installed/active check.
     494         * @param string   $label Product name shown as the box title.
     495         * @param callable $body  Called with no arguments to render the active-only body.
     496         */
     497        private static function render_connector_box( $file, $label, $body ) {
     498                self::ensure_plugin_functions();
     499
     500                $installed = file_exists( WP_PLUGIN_DIR . '/' . $file );
     501                $active    = is_plugin_active( $file );
     502                ?>
     503                <div class="destino-access-audit-section">
     504                        <div class="destino-access-audit-box-header">
     505                                <h2><?php echo esc_html( $label ); ?></h2>
     506                                <p class="destino-access-audit-box-badges">
     507                                        <?php echo wp_kses_post( '<span class="destino-access-audit-box-badge">' . esc_html__( 'Installed:', 'destino-access-audit' ) . ' ' . self::badge( $installed ) . '</span>' ); ?>
     508                                        <?php echo wp_kses_post( '<span class="destino-access-audit-box-badge">' . esc_html__( 'Active:', 'destino-access-audit' ) . ' ' . self::badge( $active ) . '</span>' ); ?>
     509                                </p>
     510                        </div>
     511                        <?php if ( $active ) : ?>
     512                                <div class="destino-access-audit-box-body">
     513                                        <?php call_user_func( $body ); ?>
     514                                </div>
     515                        <?php endif; ?>
     516                </div>
     517                <?php
     518        }
     519
     520        /**
     521         * A small "?" help icon whose full explanation is a native browser tooltip (the HTML
     522         * `title` attribute) rather than always-visible body text. Tried first because it needs no
     523         * new CSS/JS at all; if it doesn't read well in practice, a custom-styled tooltip is the
     524         * fallback.
     525         *
     526         * @param string $text Already-translated explanation text (plain text, no HTML).
     527         * @return string Escaped HTML.
     528         */
     529        private static function help_icon( $text ) {
     530                return ' <span class="destino-access-audit-help" tabindex="0" title="' . esc_attr( $text ) . '">?</span>';
    421531        }
    422532
    … …  
    746856         * Application Passwords.
    747857         */
    748         private static function render_updraftplus_status() {
    749                 self::ensure_plugin_functions();
    750 
    751                 $plugin_file = 'updraftplus/updraftplus.php';
    752                 $installed   = file_exists( WP_PLUGIN_DIR . '/' . $plugin_file );
    753                 $active      = is_plugin_active( $plugin_file );
    754 
    755                 echo wp_kses_post( '<p>' . esc_html__( 'Installed:', 'destino-access-audit' ) . ' ' . self::badge( $installed ) . '</p>' );
    756                 echo wp_kses_post( '<p>' . esc_html__( 'Active:', 'destino-access-audit' ) . ' ' . self::badge( $active ) . '</p>' );
    757 
    758                 if ( ! $active ) {
    759                         return;
    760                 }
    761 
     858        private static function render_updraftplus_body() {
    762859                echo '<p><a class="button button-secondary" href="' . esc_url( admin_url( 'options-general.php?page=updraftplus' ) ) . '">' . esc_html(
    763860                        sprintf(
    … …  
    771868                $locked    = ! empty( $adminlock ) && is_array( $adminlock ) && ! empty( $adminlock['password'] );
    772869
    773                 echo wp_kses_post( '<p>' . esc_html__( 'Settings screen protected by a lock password:', 'destino-access-audit' ) . ' ' . self::badge( $locked ) . '</p>' );
    774 
    775                 if ( $locked ) {
    776                         echo '<p class="description destino-access-audit-note">' . esc_html(
    777                                 sprintf(
    778                                         /* translators: %1$s: name of the third-party plugin (repeated) whose own password lock is being described. Always a product name, never translate it. */
    779                                         __( '%1$s\'s own "Lock Settings" feature (Advanced Tools tab) is hiding its settings behind a password. To review or remove it without knowing that password: temporarily add define( \'UPDRAFTPLUS_NOADMINLOCK\', true ); to wp-config.php, reload the %1$s settings page, change or remove the lock from its Advanced Tools tab, then remove that line again. If you do know the password, you can change or remove the lock directly from that same tab.', 'destino-access-audit' ),
    780                                         self::PRODUCT_UPDRAFTPLUS
    781                                 )
    782                         ) . '</p>';
    783                 }
     870                $lock_explanation = sprintf(
     871                        /* translators: %1$s: name of the third-party plugin (repeated) whose own password lock is being described. Always a product name, never translate it. */
     872                        __( '%1$s\'s own "Lock Settings" feature (Advanced Tools tab) is hiding its settings behind a password. To review or remove it without knowing that password: temporarily add define( \'UPDRAFTPLUS_NOADMINLOCK\', true ); to wp-config.php, reload the %1$s settings page, change or remove the lock from its Advanced Tools tab, then remove that line again. If you do know the password, you can change or remove the lock directly from that same tab.', 'destino-access-audit' ),
     873                        self::PRODUCT_UPDRAFTPLUS
     874                );
     875
     876                echo wp_kses_post(
     877                        '<p>' . esc_html__( 'Settings screen protected by a lock password:', 'destino-access-audit' ) . ' ' . self::badge( $locked )
     878                        . ( $locked ? self::help_icon( $lock_explanation ) : '' )
     879                        . '</p>'
     880                );
    784881
    785882                $keys = get_option( 'updraft_central_localkeys' );
    … …  
    853950         * hook_activated_plugin() and plugin_settings_link()).
    854951         */
    855         private static function render_mainwp_status() {
    856                 self::ensure_plugin_functions();
    857 
    858                 $plugin_file = 'mainwp-child/mainwp-child.php';
    859                 $installed   = file_exists( WP_PLUGIN_DIR . '/' . $plugin_file );
    860                 $active      = is_plugin_active( $plugin_file );
    861 
    862                 echo wp_kses_post( '<p>' . esc_html__( 'Installed:', 'destino-access-audit' ) . ' ' . self::badge( $installed ) . '</p>' );
    863                 echo wp_kses_post( '<p>' . esc_html__( 'Active:', 'destino-access-audit' ) . ' ' . self::badge( $active ) . '</p>' );
    864 
    865                 if ( ! $active ) {
    866                         return;
    867                 }
    868 
     952        private static function render_mainwp_body() {
    869953                echo '<p><a class="button button-secondary" href="' . esc_url( admin_url( 'options-general.php?page=mainwp_child_tab' ) ) . '">' . esc_html(
    870954                        sprintf(
    … …  
    9291013         * shows under its name on wp-admin → Plugins, or the ManageWP dashboard.
    9301014         */
    931         private static function render_managewp_status() {
    932                 self::ensure_plugin_functions();
    933 
    934                 $plugin_file = 'worker/init.php';
    935                 $installed   = file_exists( WP_PLUGIN_DIR . '/' . $plugin_file );
    936                 $active      = is_plugin_active( $plugin_file );
    937 
    938                 echo wp_kses_post( '<p>' . esc_html__( 'Installed:', 'destino-access-audit' ) . ' ' . self::badge( $installed ) . '</p>' );
    939                 echo wp_kses_post( '<p>' . esc_html__( 'Active:', 'destino-access-audit' ) . ' ' . self::badge( $active ) . '</p>' );
    940 
    941                 if ( ! $active ) {
    942                         return;
    943                 }
     1015        private static function render_managewp_body() {
     1016                echo '<p><a class="button button-secondary" href="' . esc_url( admin_url( 'plugins.php?showWorker=1' ) ) . '">' . esc_html(
     1017                        sprintf(
     1018                                /* translators: %s: name of the third-party plugin whose connection-management panel this links to. Always a product name, never translate it. */
     1019                                __( 'View %s connection details', 'destino-access-audit' ),
     1020                                self::PRODUCT_MANAGEWP_WORKER
     1021                        )
     1022                ) . '</a></p>';
     1023
     1024                $pairing_explanation = sprintf(
     1025                        /* translators: %s: name of the third-party plugin whose remote panel this pairs with. Always a product name, never translate it. */
     1026                        __( 'This plugin pairs the site with the remote panel through an OpenSSL key exchange, without exposing a readable "connected" option/meta. Check the connection from the "Connection Management" link under the plugin\'s name on wp-admin → Plugins, or from your %s panel.', 'destino-access-audit' ),
     1027                        self::PRODUCT_MANAGEWP
     1028                );
    9441029
    9451030                echo wp_kses_post(
    … …  
    9501035                                        self::PRODUCT_MANAGEWP
    9511036                                )
    952                         ) . ' ' . self::badge( null ) . '</p>'
    953                 );
    954                 echo '<p class="description destino-access-audit-note">' . esc_html(
    955                         sprintf(
    956                                 /* translators: %s: name of the third-party plugin whose remote panel this pairs with. Always a product name, never translate it. */
    957                                 __( 'This plugin pairs the site with the remote panel through an OpenSSL key exchange, without exposing a readable "connected" option/meta. Check the connection from the "Connection Management" link under the plugin\'s name on wp-admin → Plugins, or from your %s panel.', 'destino-access-audit' ),
    958                                 self::PRODUCT_MANAGEWP
    959                         )
    960                 ) . '</p>';
     1037                        ) . ' ' . self::badge( null ) . self::help_icon( $pairing_explanation ) . '</p>'
     1038                );
    9611039        }
    9621040
    … …  
    9731051         * inventing a state.
    9741052         */
    975         private static function render_infinitewp_status() {
    976                 self::ensure_plugin_functions();
    977 
    978                 $plugin_file = 'iwp-client/init.php';
    979                 $installed   = file_exists( WP_PLUGIN_DIR . '/' . $plugin_file );
    980                 $active      = is_plugin_active( $plugin_file );
    981 
    982                 echo wp_kses_post( '<p>' . esc_html__( 'Installed:', 'destino-access-audit' ) . ' ' . self::badge( $installed ) . '</p>' );
    983                 echo wp_kses_post( '<p>' . esc_html__( 'Active:', 'destino-access-audit' ) . ' ' . self::badge( $active ) . '</p>' );
    984 
    985                 if ( ! $active ) {
    986                         return;
    987                 }
     1053        private static function render_infinitewp_body() {
     1054                $pairing_explanation = sprintf(
     1055                        /* translators: %s: name of the third-party remote-management panel this verifies requests from. Always a product name, never translate it. */
     1056                        __( 'This plugin verifies every request from the remote panel using OpenSSL signatures, without exposing a readable "connected" option/meta. Check the connection from the %s panel itself (the added-sites section).', 'destino-access-audit' ),
     1057                        self::PRODUCT_INFINITEWP
     1058                );
    9881059
    9891060                echo wp_kses_post(
    … …  
    9941065                                        self::PRODUCT_INFINITEWP
    9951066                                )
    996                         ) . ' ' . self::badge( null ) . '</p>'
    997                 );
    998                 echo '<p class="description destino-access-audit-note">' . esc_html(
    999                         sprintf(
    1000                                 /* translators: %s: name of the third-party remote-management panel this verifies requests from. Always a product name, never translate it. */
    1001                                 __( 'This plugin verifies every request from the remote panel using OpenSSL signatures, without exposing a readable "connected" option/meta. Check the connection from the %s panel itself (the added-sites section).', 'destino-access-audit' ),
    1002                                 self::PRODUCT_INFINITEWP
    1003                         )
    1004                 ) . '</p>';
     1067                        ) . ' ' . self::badge( null ) . self::help_icon( $pairing_explanation ) . '</p>'
     1068                );
    10051069        }
    10061070
    … …  
    10211085         * (lib/wfConfig.php, lib/wfCentralAPI.php, lib/menu_wordfence_central.php).
    10221086         */
    1023         private static function render_wordfence_status() {
    1024                 self::ensure_plugin_functions();
    1025 
    1026                 $plugin_file = 'wordfence/wordfence.php';
    1027                 $installed   = file_exists( WP_PLUGIN_DIR . '/' . $plugin_file );
    1028                 $active      = is_plugin_active( $plugin_file );
    1029 
    1030                 echo wp_kses_post( '<p>' . esc_html__( 'Installed:', 'destino-access-audit' ) . ' ' . self::badge( $installed ) . '</p>' );
    1031                 echo wp_kses_post( '<p>' . esc_html__( 'Active:', 'destino-access-audit' ) . ' ' . self::badge( $active ) . '</p>' );
    1032 
    1033                 if ( ! $active ) {
    1034                         return;
    1035                 }
    1036 
     1087        private static function render_wordfence_body() {
    10371088                echo '<p><a class="button button-secondary" href="' . esc_url( admin_url( 'admin.php?page=Wordfence' ) ) . '">' . esc_html(
    10381089                        sprintf(
    … …  
    10431094                ) . '</a></p>';
    10441095
     1096                $connection_explanation = sprintf(
     1097                        /* translators: %1$s: name of the remote panel/service (repeated); %2$s: name of the third-party plugin it belongs to. Both are always product names, never translate them. */
     1098                        __( 'This plugin stores its %1$s connection state in a custom database table rather than a WordPress option, so it cannot be read here without depending on %2$s\'s internal, undocumented API. Check the connection from the %1$s tab in the plugin\'s own settings page.', 'destino-access-audit' ),
     1099                        self::PRODUCT_WORDFENCE_CENTRAL,
     1100                        self::PRODUCT_WORDFENCE
     1101                );
     1102
    10451103                echo wp_kses_post(
    10461104                        '<p class="destino-access-audit-note">' . esc_html(
    … …  
    10501108                                        self::PRODUCT_WORDFENCE_CENTRAL
    10511109                                )
    1052                         ) . ' ' . self::badge( null ) . '</p>'
    1053                 );
    1054                 echo '<p class="description destino-access-audit-note">' . esc_html(
    1055                         sprintf(
    1056                                 /* translators: %1$s: name of the remote panel/service (repeated); %2$s: name of the third-party plugin it belongs to. Both are always product names, never translate them. */
    1057                                 __( 'This plugin stores its %1$s connection state in a custom database table rather than a WordPress option, so it cannot be read here without depending on %2$s\'s internal, undocumented API. Check the connection from the %1$s tab in the plugin\'s own settings page.', 'destino-access-audit' ),
    1058                                 self::PRODUCT_WORDFENCE_CENTRAL,
    1059                                 self::PRODUCT_WORDFENCE
    1060                         )
    1061                 ) . '</p>';
     1110                        ) . ' ' . self::badge( null ) . self::help_icon( $connection_explanation ) . '</p>'
     1111                );
    10621112        }
    10631113
    … …  
    10781128         * (modules/settings/components/page.php).
    10791129         */
    1080         private static function render_elementor_status() {
    1081                 self::ensure_plugin_functions();
    1082 
    1083                 $plugin_file = 'manage/manage.php';
    1084                 $installed   = file_exists( WP_PLUGIN_DIR . '/' . $plugin_file );
    1085                 $active      = is_plugin_active( $plugin_file );
    1086 
    1087                 echo wp_kses_post( '<p>' . esc_html__( 'Installed:', 'destino-access-audit' ) . ' ' . self::badge( $installed ) . '</p>' );
    1088                 echo wp_kses_post( '<p>' . esc_html__( 'Active:', 'destino-access-audit' ) . ' ' . self::badge( $active ) . '</p>' );
    1089 
    1090                 if ( ! $active ) {
    1091                         return;
    1092                 }
    1093 
     1130        private static function render_elementor_body() {
    10941131                echo '<p><a class="button button-secondary" href="' . esc_url( admin_url( 'admin.php?page=manage-settings' ) ) . '">' . esc_html(
    10951132                        sprintf(
  • destino-access-audit/trunk/readme.txt

    r3636849 r3717600  
    33Tags: security, audit, application passwords, mainwp, updraftplus
    44Requires at least: 5.6
    5 Tested up to: 7.0
     5Tested up to: 7.1
    66Requires PHP: 7.4
    7 Stable tag: 1.5.0
     7Stable tag: 1.6.0
    88License: GPLv2 or later
    99License URI: https://www.gnu.org/licenses/gpl-2.0.html
    … …  
    2323* **UpdraftPlus / UpdraftCentral** — whether UpdraftPlus is installed/active, a direct link to its settings screen, whether it is connected to an UpdraftCentral dashboard (including *which* WordPress user made that connection), and whether UpdraftPlus's own password-protected "Lock Settings" feature is currently hiding that screen.
    2424* **MainWP Child** — whether MainWP Child is installed/active, a direct link to its settings screen, whether it is paired with a MainWP Dashboard, and *which* WordPress user established that pairing.
    25 * **ManageWP Worker** — installed/active status. ManageWP Worker pairs with its dashboard through an OpenSSL key exchange with no locally readable "connected" flag, so this block documents that limitation instead of guessing.
     25* **ManageWP Worker** — installed/active status, plus a direct link to its own "Connection Management" panel. ManageWP Worker pairs with its dashboard through an OpenSSL key exchange with no locally readable "connected" flag, so this block documents that limitation instead of guessing.
    2626* **InfiniteWP Client** — installed/active status, with the same honest limitation note as ManageWP Worker.
    2727* **Wordfence Security** — installed/active status. Wordfence Central's connection state lives in Wordfence's own proprietary database table, not a readable WordPress option, so this block documents that limitation instead of guessing, the same as ManageWP Worker and InfiniteWP Client.
    2828* **Elementor Site Management** — whether the "Manage" plugin by Elementor.com is installed/active, a direct link to its settings screen, and whether it is connected to my.elementor.com.
    2929
    30 The connector blocks above lay out as a 2-column grid on desktop, so they're easier to scan at a glance; Application Passwords and Administrator Users stay full-width since they contain data tables.
     30The connector blocks above lay out as a 2-column grid on desktop, installed plugins first, so they're easier to scan at a glance; each box header shows its Installed/Active status inline, with longer explanations tucked behind a small "?" tooltip instead of always-visible paragraphs. Application Passwords and Administrator Users stay full-width since they contain data tables.
    3131
    3232**Security notes**
    … …  
    6969== Changelog ==
    7070
     71= 1.6.0 =
     72* Connector block boxes (UpdraftPlus/UpdraftCentral, MainWP Child, ManageWP Worker, InfiniteWP Client, Wordfence Security, Elementor Site Management) restyled with a postbox-style header: title and Installed/Active badges in one line, separated from the body by a divider. Every long explanatory paragraph (why a connection status can't be determined, UpdraftPlus's Lock Settings note) is now a small "?" icon with the explanation in a tooltip instead of always-visible text.
     73* The connector boxes now sort themselves with installed plugins first, instead of a fixed order.
     74* Administrator Users now appears before Application Passwords.
     75* ManageWP Worker: added a direct link to its own "Connection Management" panel (`plugins.php?showWorker=1`).
     76* Tested up to WordPress 7.1.
     77
    7178= 1.5.0 =
    7279* New blocks: Wordfence Security (installed/active only — Wordfence Central's connection state lives in a proprietary database table, not a readable WordPress option) and Elementor Site Management (the separate "Manage" plugin by Elementor.com — installed/active plus a reliable connection badge).
Note: See TracChangeset for help on using the changeset viewer.