Plugin Directory

Changeset 3362936


Ignore:
Timestamp:
09/17/2025 06:49:03 AM (3 months ago)
Author:
JavierCasares
Message:

[4.1.0] - 2025-09-16

Added

  • "Never" send notifications.
  • Choose notification day and time.
  • Configurable cache expiration (1, 6, 12 or 24 hours).
  • WP-CLI command to configure cache expiration.
  • Constants to set hiding components.
  • WP-CLI command to manage hidden components.
  • WP-CLI command to configure notification email and period.
  • Add notifications for Slack and Teams.
  • Disable mail notifications from mails.
  • WordPress Playground blueprint (in test).

Changed

  • Cache is always the same eveywhere.
  • Options for notifications.
  • Schedule fields only appear for selected periods.
  • Placeholder and conditional display for Slack and Teams notification fields.
  • Added links to documentation.

Fixed

  • When a plugin is updated, hide the vulnerabilities.
  • Display of schedule fields based on period.
  • Conditional display for email, Slack, and Teams notification fields.
  • When save, there where two saving messages.
  • wp_get_update_php_url() for WordPress 5.1.0+ (fallback)
  • wp_timezone() for WordPress 5.1.0+ (fallback)
  • wp_doing_cron() for WordPress 4.8.0+ (fallback)
  • Application Passwords / REST API after WordPress 5.6.0

Compatibility

  • WordPress: 4.7 - 6.9
  • PHP: 5.6 - 8.4
  • WP-CLI: 2.3.0 - 2.11.0

Tests

  • PHP Coding Standards: 3.13.4
  • WordPress Coding Standards: 3.2.0
  • Plugin Check (PCP): 1.6.0
  • SonarCloud Code Review
  • Amplify Code Check
Location:
wpvulnerability
Files:
62 added
1 deleted
17 edited

Legend:

Unmodified
Added
Removed
  • wpvulnerability/trunk/assets/admin.css

    r3136611 r3362936  
    8686}
    8787
    88 /* Diseño móvil */
     88/* Mobile layout */
    8989@media (max-width: 1280px) {
    9090        .wpvulnerability-container {
  • wpvulnerability/trunk/changelog.txt

    r3267956 r3362936  
    11== Changelog ==
     2
     3= [4.1.0] - 2025-09-16 =
     4
     5**Added**
     6
     7* "Never" send notifications.
     8* Choose notification day and time.
     9* Configurable cache expiration (1, 6, 12 or 24 hours).
     10* WP-CLI command to configure cache expiration.
     11* Constants to set hiding components.
     12* WP-CLI command to manage hidden components.
     13* WP-CLI command to configure notification email and period.
     14* Add notifications for Slack and Teams.
     15* Disable mail notifications from mails.
     16* WordPress Playground blueprint (in test).
     17
     18**Changed**
     19
     20* Cache is always the same eveywhere.
     21* Options for notifications.
     22* Schedule fields only appear for selected periods.
     23* Placeholder and conditional display for Slack and Teams notification fields.
     24* Added links to documentation.
     25
     26**Fixed**
     27
     28* When a plugin is updated, hide the vulnerabilities.
     29* Display of schedule fields based on period.
     30* Conditional display for email, Slack, and Teams notification fields.
     31* When save, there where two saving messages.
     32* wp_get_update_php_url() for WordPress 5.1.0+ (fallback)
     33* wp_timezone() for WordPress 5.1.0+ (fallback)
     34* wp_doing_cron() for WordPress 4.8.0+ (fallback)
     35* Application Passwords / REST API after WordPress 5.6.0
     36
     37**Compatibility**
     38
     39* WordPress: 4.7 - 6.9
     40* PHP: 5.6 - 8.4
     41* WP-CLI: 2.3.0 - 2.11.0
     42
     43**Tests**
     44
     45* PHP Coding Standards: 3.13.4
     46* WordPress Coding Standards: 3.2.0
     47* Plugin Check (PCP): 1.6.0
     48* SonarCloud Code Review
     49* Amplify Code Check
    250
    351= [4.0.4] - 2025-04-07 =
  • wpvulnerability/trunk/languages/wpvulnerability.pot

    r3161036 r3362936  
    4848msgstr ""
    4949
    50 #: wpvulnerability-admin.php:152 wpvulnerability-adminms.php:337
     50#: wpvulnerability-admin.php:150 wpvulnerability-adminms.php:335
     51msgid "Read more if you want to force the cache time."
     52msgstr ""
     53
     54#: wpvulnerability-admin.php:153 wpvulnerability-adminms.php:338
    5155msgid "Reload Data"
    5256msgstr ""
    5357
    54 #: wpvulnerability-admin.php:158 wpvulnerability-adminms.php:344
     58#: wpvulnerability-admin.php:159 wpvulnerability-adminms.php:345
    5559msgid "Email test"
    5660msgstr ""
     
    230234msgstr ""
    231235
    232 #: wpvulnerability-admin.php:627 wpvulnerability-adminms.php:807
     236#: wpvulnerability-admin.php:627 wpvulnerability-adminms.php:816
    233237msgid "Daily"
    234238msgstr ""
    235239
    236 #: wpvulnerability-admin.php:632 wpvulnerability-adminms.php:812
     240#: wpvulnerability-admin.php:632 wpvulnerability-adminms.php:821
    237241msgid "Weekly"
     242msgstr ""
     243
     244#: wpvulnerability-admin.php:637 wpvulnerability-adminms.php:826
     245msgid "Never"
    238246msgstr ""
    239247
     
    378386msgstr ""
    379387
     388#: wpvulnerability-admin.php:180 wpvulnerability-adminms.php:366
     389msgid "Read more about how to change the \"From:\" of the email."
     390msgstr ""
     391
     392#: wpvulnerability-admin.php:763 wpvulnerability-adminms.php:940
     393msgid "Read more about how to force the deactivation of an item."
     394msgstr ""
     395
    380396#: wpvulnerability-adminms.php:124 wpvulnerability-adminms.php:218
    381397msgid "Settings saved."
  • wpvulnerability/trunk/readme.txt

    r3267956 r3362936  
    22Contributors: javiercasares, davidperez, lbonomo, alexclassroom
    33Tags: security, vulnerability, site-health
    4 Requires at least: 4.1
    5 Tested up to: 6.8
    6 Stable tag: 4.0.4
     4Requires at least: 4.7
     5Tested up to: 6.9
     6Stable tag: 4.1.0
    77Requires PHP: 5.6
    8 Version: 4.0.4
     8Version: 4.1.0
    99License: GPL-2.0-or-later
    1010License URI: https://spdx.org/licenses/GPL-2.0-or-later.html
     
    4242* SQLite: `wp wpvulnerability sqlite`
    4343
     44To configure the plugin you can use:
     45
     46* Hide component: `wp wpvulnerability config hide <component> [on|off]`
     47* Notification email: `wp wpvulnerability config email <emails>` (comma separatted)
     48* Notification period: `wp wpvulnerability config period <never|daily|weekly>`
     49* Cache duration: `wp wpvulnerability config cache <1|6|12|24>` (in hours)
     50
    4451All commands support the `--format` option to specify the output format:
    4552
     
    8895If the constant is active, it will be visible in the configuration screen.
    8996
     97= Force hiding checks (since: 4.1.0) =
     98
     99If you want to always hide a specific component, you can define a constant in `wp-config.php`. When set to `true`, the option will be checked automatically in the settings screen and the related analysis will be skipped.
     100
     101Example:
     102
     103`define( 'WPVULNERABILITY_HIDE_APACHE', true );`
     104
     105Available constants: `WPVULNERABILITY_HIDE_CORE`, `WPVULNERABILITY_HIDE_PLUGINS`, `WPVULNERABILITY_HIDE_THEMES`, `WPVULNERABILITY_HIDE_PHP`, `WPVULNERABILITY_HIDE_APACHE`, `WPVULNERABILITY_HIDE_NGINX`, `WPVULNERABILITY_HIDE_MARIADB`, `WPVULNERABILITY_HIDE_MYSQL`, `WPVULNERABILITY_HIDE_IMAGEMAGICK`, `WPVULNERABILITY_HIDE_CURL`, `WPVULNERABILITY_HIDE_MEMCACHED`, `WPVULNERABILITY_HIDE_REDIS`, `WPVULNERABILITY_HIDE_SQLITE`.
     106
     107= Cache duration (since: 4.1.0) =
     108
     109By default, data from the API is cached for 12 hours. To change this, define `WPVULNERABILITY_CACHE_HOURS` in `wp-config.php` with one of `1`, `6`, `12` or `24`. This value overrides the setting screen and WP-CLI command.
     110
     111`define( 'WPVULNERABILITY_CACHE_HOURS', 24 );`
     112
    90113== Installation ==
    91114
     
    124147== Compatibility ==
    125148
    126 * WordPress: 4.1 - 6.8
     149* WordPress: 4.7 - 6.9
    127150* PHP: 5.6 - 8.4
    128151* WP-CLI: 2.3.0 - 2.11.0
     
    130153== Changelog ==
    131154
    132 = [4.0.4] - 2025-04-07 =
     155= [4.1.0] - 2025-09-16 =
    133156
    134157**Added**
    135158
    136 * Extra sanitizations.
     159* "Never" send notifications.
     160* Choose notification day and time.
     161* Configurable cache expiration (1, 6, 12 or 24 hours).
     162* WP-CLI command to configure cache expiration.
     163* Constants to set hiding components.
     164* WP-CLI command to manage hidden components.
     165* WP-CLI command to configure notification email and period.
     166* Add notifications for Slack and Teams.
     167* Disable mail notifications from mails.
     168* WordPress Playground blueprint (in test).
    137169
    138170**Changed**
    139171
    140 * Translation improvements.
     172* Cache is always the same eveywhere.
     173* Options for notifications.
     174* Schedule fields only appear for selected periods.
     175* Placeholder and conditional display for Slack and Teams notification fields.
     176* Added links to documentation.
    141177
    142178**Fixed**
    143179
    144 * Plugin and translation load.
     180* When a plugin is updated, hide the vulnerabilities.
     181* Display of schedule fields based on period.
     182* Conditional display for email, Slack, and Teams notification fields.
     183* When save, there where two saving messages.
     184* wp_get_update_php_url() for WordPress 5.1.0+ (fallback)
     185* wp_timezone() for WordPress 5.1.0+ (fallback)
     186* wp_doing_cron() for WordPress 4.8.0+ (fallback)
     187* Application Passwords / REST API after WordPress 5.6.0
    145188
    146189**Compatibility**
    147190
    148 * WordPress: 4.1 - 6.8
     191* WordPress: 4.7 - 6.9
    149192* PHP: 5.6 - 8.4
    150193* WP-CLI: 2.3.0 - 2.11.0
     
    152195**Tests**
    153196
    154 * PHP Coding Standards: 3.12.1
    155 * WordPress Coding Standards: 3.1.0
    156 * Plugin Check (PCP): 1.4.0
     197* PHP Coding Standards: 3.13.4
     198* WordPress Coding Standards: 3.2.0
     199* Plugin Check (PCP): 1.6.0
    157200* SonarCloud Code Review
    158 
    159 = [4.0.3] - 2024-10-28 =
    160 
    161 * Recreation of the 4.0.2 version. Something did not created the 4.0.2 version.
    162 
    163 = [4.0.2] - 2024-10-25 =
    164 
    165 **Fixed**
    166 
    167 * ImageMagick: it crashes in some cases where the hosting does not have ImageMagick.
    168 
    169 **Compatibility**
    170 
    171 * WordPress: 4.1 - 6.7
    172 * PHP: 5.6 - 8.4
    173 * WP-CLI: 2.3.0 - 2.11.0
    174 
    175 **Tests**
    176 
    177 * PHP Coding Standards: 3.10.3
    178 * WordPress Coding Standards: 3.1.0
    179 * Plugin Check (PCP): 1.1.0
    180 * SonarCloud Code Review
    181 
    182 = [4.0.1] - 2024-10-04 =
    183 
    184 **Fixed**
    185 
    186 * API endpoints: some API endpoints were failing.
    187 * CLI endpoints: some CLI endpoints were failing.
    188 
    189 **Compatibility**
    190 
    191 * WordPress: 4.1 - 6.7
    192 * PHP: 5.6 - 8.4
    193 * WP-CLI: 2.3.0 - 2.11.0
    194 
    195 **Tests**
    196 
    197 * PHP Coding Standards: 3.10.3
    198 * WordPress Coding Standards: 3.1.0
    199 * Plugin Check (PCP): 1.1.0
    200 * SonarCloud Code Review
    201 
    202 = [4.0.0] - 2024-10-01 =
    203 
    204 **Added**
    205 
    206 * ImageMagic vulnerabilities (Site Health + WP-CLI + API + mail).
    207 * curl vulnerabilities (Site Health + WP-CLI + API + mail).
    208 * memcached vulnerabilities (Site Health + WP-CLI + API + mail).
    209 * Redis vulnerabilities (Site Health + WP-CLI + API + mail).
    210 * SQLite vulnerabilities (Site Health + WP-CLI + API + mail).
    211 
    212 **Fixed**
    213 
    214 * Test email without email.
    215 * Improved MariaDB 11.x detection.
    216 * Improved versions detection (major-minor.patch-build).
    217 * WordPress < 5.3: use of wp_date().
    218 * WordPress < 5.0: locale detection.
    219 * Dashboard widget only for users with capabilities.
    220 * WordPress < 5.2: link to Site Health
    221 
    222 **Changed**
    223 
    224 * Big refactory.
    225 * Less files, less size, improved code quality.
    226 
    227 **Compatibility**
    228 
    229 * WordPress: 4.1 - 6.7
    230 * PHP: 5.6 - 8.4
    231 * WP-CLI: 2.3.0 - 2.11.0
    232 
    233 **Tests**
    234 
    235 * Manual Testing:
    236   * WordPress 6.7 / PHP 8.4
    237   * WordPress 6.6 / PHP 8.3
    238   * WordPress 6.4 / PHP 8.2
    239   * WordPress 6.1 / PHP 8.1
    240   * WordPress 5.8 / PHP 8.0
    241   * WordPress 5.5 / PHP 7.4
    242   * WordPress 5.3 / PHP 7.3
    243   * WordPress 4.9 / PHP 7.2
    244   * WordPress 4.8 / PHP 7.1
    245   * WordPress 4.6 / PHP 7.0
    246   * WordPress 4.1 / PHP 5.6
    247 * PHP Coding Standards: 3.10.3
    248 * WordPress Coding Standards: 3.1.0
    249 * Plugin Check (PCP): 1.1.0
    250 * SonarCloud Code Review
     201* Amplify Code Check
    251202
    252203= Previous versions =
     
    271222== Vulnerabilities ==
    272223
    273 * No vulnerabilities have been published up to version 4.0.4.
     224* No vulnerabilities have been published up to version 4.1.0.
    274225
    275226Found a security vulnerability? Please report it to us privately at the [WPVulnerability GitHub repository](https://github.com/javiercasares/wpvulnerability/security/advisories/new).
  • wpvulnerability/trunk/wpvulnerability-admin.php

    r3267956 r3362936  
    2828 */
    2929function wpvulnerability_admin_enqueue_scripts() {
    30     wp_enqueue_style(
    31         'wpvulnerability-admin',
    32         WPVULNERABILITY_PLUGIN_URL . 'assets/admin.css',
    33         array(),
    34         WPVULNERABILITY_PLUGIN_VERSION
    35     );
     30        wp_enqueue_style(
     31            'wpvulnerability-admin',
     32            WPVULNERABILITY_PLUGIN_URL . 'assets/admin.css',
     33            array(),
     34            WPVULNERABILITY_PLUGIN_VERSION
     35        );
     36
     37        wp_enqueue_script(
     38            'wpvulnerability-admin-js',
     39            WPVULNERABILITY_PLUGIN_URL . 'assets/admin.js',
     40            array( 'jquery' ),
     41            WPVULNERABILITY_PLUGIN_VERSION,
     42            true
     43        );
    3644}
    3745add_action( 'admin_enqueue_scripts', 'wpvulnerability_admin_enqueue_scripts' );
     
    120128    }
    121129    ?>
    122     <?php settings_errors(); ?>
    123     <div class="wrap">
     130        <?php
     131        settings_errors( 'admin_wpvulnerability_settings' );
     132        settings_errors( 'admin_wpvulnerability_analyze' );
     133        ?>
     134        <div class="wrap">
    124135        <div class="wpvulnerability-container">
    125136            <div class="wpvulnerability-column">
     
    147158                    </header>
    148159                    <div class="section-content">
    149                         <p><?php esc_html_e( 'Reload all Core, Plugins, Themes and other components information directly from the API to have updated data.', 'wpvulnerability' ); ?></p>
    150                         <form method="post" action="options-general.php?page=wpvulnerability-options">
    151                             <?php wp_nonce_field( 'wpvulnerability_reset_action', 'wpvulnerability_reset_nonce' ); ?>
    152                             <input type="submit" name="wpvulnerability_reset" value="<?php esc_attr_e( 'Reload Data', 'wpvulnerability' ); ?>" class="button button-secondary">
    153                         </form>
     160                                                <p><?php esc_html_e( 'Reload all Core, Plugins, Themes and other components information directly from the API to have updated data.', 'wpvulnerability' ); ?></p>
     161                                                <form method="post" action="options-general.php?page=wpvulnerability-options">
     162                                                        <?php wp_nonce_field( 'wpvulnerability_reset_action', 'wpvulnerability_reset_nonce' ); ?>
     163                                                        <input type="submit" name="wpvulnerability_reset" value="<?php esc_attr_e( 'Reload Data', 'wpvulnerability' ); ?>" class="button button-secondary">
     164                                                </form>
    154165                    </div>
    155166                </section>
     
    176187                        }
    177188                        ?>
    178                         <p><?php esc_html_e( 'Send an email with the vulnerabilities (or empty).', 'wpvulnerability' ); ?></p>
    179                         <form method="post" action="options-general.php?page=wpvulnerability-options">
    180                             <?php wp_nonce_field( 'wpvulnerability_email_action', 'wpvulnerability_email_nonce' ); ?>
    181                             <input type="submit" name="wpvulnerability_email" value="<?php esc_attr_e( 'Send email', 'wpvulnerability' ); ?>" class="button button-secondary">
    182                         </form>
     189<p><?php esc_html_e( 'Send an email with the vulnerabilities (or empty).', 'wpvulnerability' ); ?></p>
     190<p><a href="https://www.wpvulnerability.com/plugin/#from-mail" target="_blank"><small><i><?php esc_html_e( 'Read more about how to change the "From:" of the email.', 'wpvulnerability' ); ?></i></small></a></p>
     191<form method="post" action="options-general.php?page=wpvulnerability-options">
     192    <?php wp_nonce_field( 'wpvulnerability_email_action', 'wpvulnerability_email_nonce' ); ?>
     193<input type="submit" name="wpvulnerability_email" value="<?php esc_attr_e( 'Send email', 'wpvulnerability' ); ?>" class="button button-secondary">
     194</form>
    183195                    </div>
    184196                </section>
     
    555567function wpvulnerability_admin_section_notifications() {
    556568
    557     // Output the header information for the notifications section.
    558     esc_html_e( 'Configure and save these settings to receive email notifications.', 'wpvulnerability' );
     569        // Output the header information for the notifications section.
     570        esc_html_e( 'Configure and save these settings to receive notifications.', 'wpvulnerability' );
    559571}
    560572
     
    604616
    605617/**
     618 * Print the cache expiration selector.
     619 *
     620 * @since 4.1.0
     621 *
     622 * @return void
     623 */
     624function wpvulnerability_admin_cache_callback() {
     625
     626    $wpvulnerability_settings = get_option( 'wpvulnerability-config', array() );
     627    $options                  = array( 1, 6, 12, 24 );
     628    $forced_cache             = null;
     629
     630    if ( defined( 'WPVULNERABILITY_CACHE_HOURS' ) ) {
     631        $forced_cache = (int) WPVULNERABILITY_CACHE_HOURS;
     632        if ( ! in_array( $forced_cache, $options, true ) ) {
     633            $options[] = $forced_cache;
     634            sort( $options, SORT_NUMERIC );
     635        }
     636    }
     637
     638    $current = isset( $wpvulnerability_settings['cache'] ) ? (int) $wpvulnerability_settings['cache'] : 12;
     639    if ( null !== $forced_cache ) {
     640        $current = $forced_cache;
     641    }
     642
     643    echo '<select name="wpvulnerability-config[cache]" id="wpvulnerability_cache"';
     644    disabled( null !== $forced_cache );
     645    echo '>';
     646    foreach ( $options as $hours ) {
     647        printf(
     648            '<option value="%1$s"%2$s>%3$s</option>',
     649            esc_attr( $hours ),
     650            selected( $current, $hours, false ),
     651            esc_html(
     652                sprintf(
     653                        /* translators: %d: number of hours */
     654                    _n( '%d hour', '%d hours', $hours, 'wpvulnerability' ),
     655                    $hours
     656                )
     657            )
     658        );
     659    }
     660        echo '</select>';
     661
     662    if ( null !== $forced_cache ) {
     663            printf(
     664                '<input type="hidden" name="wpvulnerability-config[cache]" value="%s" />',
     665                esc_attr( $current )
     666            );
     667    }
     668
     669        printf(
     670            '<p class="description"><a href="%1$s" target="_blank"><small><i>%2$s</i></small></a></p>',
     671            esc_url( 'https://www.wpvulnerability.com/plugin/#cache-duration' ),
     672            esc_html__( 'Read more if you want to force the cache time.', 'wpvulnerability' )
     673        );
     674}
     675
     676/**
    606677 * Print when to send the vulnerability scan emails.
    607678 *
     
    612683function wpvulnerability_admin_period_callback() {
    613684
    614     // Get the saved plugin settings.
    615     $wpvulnerability_settings = get_option( 'wpvulnerability-config', array() );
    616 
    617     // Set the default period to 'weekly' if not set or empty.
    618     if ( ! isset( $wpvulnerability_settings['period'] ) || empty( $wpvulnerability_settings['period'] ) ) {
    619         $wpvulnerability_settings['period'] = 'weekly';
    620     }
    621 
    622     // Output the period select box.
     685        // Get the saved plugin settings.
     686        $wpvulnerability_settings = get_option( 'wpvulnerability-config', array() );
     687        $defaults                 = array(
     688            'period' => 'weekly',
     689            'day'    => 'monday',
     690            'hour'   => 0,
     691            'minute' => 0,
     692        );
     693        $wpvulnerability_settings = wp_parse_args( $wpvulnerability_settings, $defaults );
     694
     695        ?>
     696        <div id="wpvulnerability_period">
     697        <label>
     698                <input type="radio" name="wpvulnerability-config[period]" value="never" <?php checked( $wpvulnerability_settings['period'], 'never' ); ?> />
     699                <?php esc_html_e( 'Never', 'wpvulnerability' ); ?>
     700        </label>
     701        <br/>
     702        <label>
     703                <input type="radio" name="wpvulnerability-config[period]" value="daily" <?php checked( $wpvulnerability_settings['period'], 'daily' ); ?> />
     704                <?php esc_html_e( 'Daily', 'wpvulnerability' ); ?>
     705        </label>
     706        <br/>
     707        <label>
     708                <input type="radio" name="wpvulnerability-config[period]" value="weekly" <?php checked( $wpvulnerability_settings['period'], 'weekly' ); ?> />
     709                <?php esc_html_e( 'Weekly', 'wpvulnerability' ); ?>
     710        </label>
     711        <div id="wpvulnerability_day_wrap">
     712                <br/>
     713                <label for="wpvulnerability_day"><?php esc_html_e( 'Day', 'wpvulnerability' ); ?></label>
     714                <select name="wpvulnerability-config[day]" id="wpvulnerability_day">
     715                        <option value="monday" <?php selected( $wpvulnerability_settings['day'], 'monday' ); ?>><?php esc_html_e( 'Monday', 'wpvulnerability' ); ?></option>
     716                        <option value="tuesday" <?php selected( $wpvulnerability_settings['day'], 'tuesday' ); ?>><?php esc_html_e( 'Tuesday', 'wpvulnerability' ); ?></option>
     717                        <option value="wednesday" <?php selected( $wpvulnerability_settings['day'], 'wednesday' ); ?>><?php esc_html_e( 'Wednesday', 'wpvulnerability' ); ?></option>
     718                        <option value="thursday" <?php selected( $wpvulnerability_settings['day'], 'thursday' ); ?>><?php esc_html_e( 'Thursday', 'wpvulnerability' ); ?></option>
     719                        <option value="friday" <?php selected( $wpvulnerability_settings['day'], 'friday' ); ?>><?php esc_html_e( 'Friday', 'wpvulnerability' ); ?></option>
     720                        <option value="saturday" <?php selected( $wpvulnerability_settings['day'], 'saturday' ); ?>><?php esc_html_e( 'Saturday', 'wpvulnerability' ); ?></option>
     721                        <option value="sunday" <?php selected( $wpvulnerability_settings['day'], 'sunday' ); ?>><?php esc_html_e( 'Sunday', 'wpvulnerability' ); ?></option>
     722                </select>
     723        </div>
     724        <div id="wpvulnerability_time_wrap">
     725                <br/>
     726                <label for="wpvulnerability_hour"><?php esc_html_e( 'Hour', 'wpvulnerability' ); ?></label>
     727                <input type="number" min="0" max="23" name="wpvulnerability-config[hour]" id="wpvulnerability_hour" value="<?php echo esc_attr( (string) $wpvulnerability_settings['hour'] ); ?>" />
     728                <label for="wpvulnerability_minute"><?php esc_html_e( 'Minute', 'wpvulnerability' ); ?></label>
     729                <input type="number" min="0" max="59" name="wpvulnerability-config[minute]" id="wpvulnerability_minute" value="<?php echo esc_attr( (string) $wpvulnerability_settings['minute'] ); ?>" />
     730        </div>
     731        </div>
     732        <?php
     733}
     734
     735/**
     736 * Print where to send the notifications.
     737 *
     738 * @since 3.6.0
     739 *
     740 * @return void
     741 */
     742function wpvulnerability_admin_notify_callback() {
     743
     744        // Get the saved plugin settings.
     745        $wpvulnerability_settings = get_option( 'wpvulnerability-config', array() );
     746                $defaults         = array(
     747                    'email' => 'y',
     748                    'slack' => 'n',
     749                    'teams' => 'n',
     750                );
     751
     752                if ( ! isset( $wpvulnerability_settings['notify'] ) || ! is_array( $wpvulnerability_settings['notify'] ) ) {
     753                                $wpvulnerability_settings['notify'] = $defaults;
     754                } else {
     755                                $wpvulnerability_settings['notify'] = wp_parse_args( $wpvulnerability_settings['notify'], $defaults );
     756                }
     757
     758                $wpvulnerability_settings['notify'] = wpvulnerability_normalize_notify_settings( $wpvulnerability_settings['notify'] );
     759
     760                $email_enabled = wpvulnerability_is_yes( $wpvulnerability_settings['notify']['email'] );
     761                $slack_enabled = wpvulnerability_is_yes( $wpvulnerability_settings['notify']['slack'] );
     762                $teams_enabled = wpvulnerability_is_yes( $wpvulnerability_settings['notify']['teams'] );
     763
     764                ?>
     765        <div id="wpvulnerability_notify">
     766        <label>
     767                                <input type="checkbox" name="wpvulnerability-config[notify][email]" value="y" <?php checked( $email_enabled ); ?> />
     768                <?php esc_html_e( 'Email', 'wpvulnerability' ); ?>
     769        </label>
     770        <br/>
     771        <label>
     772                                <input type="checkbox" name="wpvulnerability-config[notify][slack]" value="y" <?php checked( $slack_enabled ); ?> />
     773                <?php esc_html_e( 'Slack', 'wpvulnerability' ); ?>
     774        </label>
     775        <br/>
     776        <label>
     777                                <input type="checkbox" name="wpvulnerability-config[notify][teams]" value="y" <?php checked( $teams_enabled ); ?> />
     778                <?php esc_html_e( 'Microsoft Teams', 'wpvulnerability' ); ?>
     779        </label>
     780        </div>
     781        <?php
     782}
     783
     784/**
     785 * Print the Slack webhook input field.
     786 *
     787 * @since 3.6.0
     788 *
     789 * @return void
     790 */
     791function wpvulnerability_admin_slack_callback() {
     792
     793        $wpvulnerability_settings = get_option( 'wpvulnerability-config', array() );
     794        $slack_webhook            = isset( $wpvulnerability_settings['slack_webhook'] ) ? $wpvulnerability_settings['slack_webhook'] : '';
     795
    623796    ?>
    624     <div id="wpvulnerability_period">
    625     <label>
    626         <input type="radio" name="wpvulnerability-config[period]" value="daily" <?php checked( $wpvulnerability_settings['period'], 'daily' ); ?> />
    627         <?php esc_html_e( 'Daily', 'wpvulnerability' ); ?>
    628     </label>
    629     <br/>
    630     <label>
    631         <input type="radio" name="wpvulnerability-config[period]" value="weekly" <?php checked( $wpvulnerability_settings['period'], 'weekly' ); ?> />
    632         <?php esc_html_e( 'Weekly', 'wpvulnerability' ); ?>
    633     </label>
    634     </div>
    635     <?php
     797                <input class="regular-text" type="text" name="wpvulnerability-config[slack_webhook]" id="wpvulnerability_slack_webhook" placeholder="<?php echo esc_attr( 'https://hooks.slack.com/services/...' ); ?>" value="<?php echo esc_attr( (string) $slack_webhook ); ?>" />
     798                <?php
     799}
     800
     801/**
     802 * Print the Teams webhook input field.
     803 *
     804 * @since 3.6.0
     805 *
     806 * @return void
     807 */
     808function wpvulnerability_admin_teams_callback() {
     809
     810        $wpvulnerability_settings = get_option( 'wpvulnerability-config', array() );
     811        $teams_webhook            = isset( $wpvulnerability_settings['teams_webhook'] ) ? $wpvulnerability_settings['teams_webhook'] : '';
     812
     813    ?>
     814                <input class="regular-text" type="text" name="wpvulnerability-config[teams_webhook]" id="wpvulnerability_teams_webhook" placeholder="<?php echo esc_attr( 'https://outlook.office.com/webhook/...' ); ?>" value="<?php echo esc_attr( (string) $teams_webhook ); ?>" />
     815                <?php
    636816}
    637817
     
    652832    // Retrieve the WPVulnerability plugin settings.
    653833    $wpvulnerability_analyze = get_option( 'wpvulnerability-analyze', array() );
    654     if ( ! isset( $wpvulnerability_analyze['core'] ) ) {
    655         $wpvulnerability_analyze['core'] = 0;
    656     }
    657     if ( ! isset( $wpvulnerability_analyze['plugins'] ) ) {
    658         $wpvulnerability_analyze['plugins'] = 0;
    659     }
    660     if ( ! isset( $wpvulnerability_analyze['themes'] ) ) {
    661         $wpvulnerability_analyze['themes'] = 0;
    662     }
    663     if ( ! isset( $wpvulnerability_analyze['php'] ) ) {
    664         $wpvulnerability_analyze['php'] = 0;
    665     }
    666     if ( ! isset( $wpvulnerability_analyze['apache'] ) ) {
    667         $wpvulnerability_analyze['apache'] = 0;
    668     }
    669     if ( ! isset( $wpvulnerability_analyze['nginx'] ) ) {
    670         $wpvulnerability_analyze['nginx'] = 0;
    671     }
    672     if ( ! isset( $wpvulnerability_analyze['mariadb'] ) ) {
    673         $wpvulnerability_analyze['mariadb'] = 0;
    674     }
    675     if ( ! isset( $wpvulnerability_analyze['mysql'] ) ) {
    676         $wpvulnerability_analyze['mysql'] = 0;
    677     }
    678     if ( ! isset( $wpvulnerability_analyze['imagemagick'] ) ) {
    679         $wpvulnerability_analyze['imagemagick'] = 0;
    680     }
    681     if ( ! isset( $wpvulnerability_analyze['curl'] ) ) {
    682         $wpvulnerability_analyze['curl'] = 0;
    683     }
    684     if ( ! isset( $wpvulnerability_analyze['memcached'] ) ) {
    685         $wpvulnerability_analyze['memcached'] = 0;
    686     }
    687     if ( ! isset( $wpvulnerability_analyze['redis'] ) ) {
    688         $wpvulnerability_analyze['redis'] = 0;
    689     }
    690     if ( ! isset( $wpvulnerability_analyze['sqlite'] ) ) {
    691         $wpvulnerability_analyze['sqlite'] = 0;
     834    $components              = array( 'core', 'plugins', 'themes', 'php', 'apache', 'nginx', 'mariadb', 'mysql', 'imagemagick', 'curl', 'memcached', 'redis', 'sqlite' );
     835    $forced                  = array();
     836
     837    foreach ( $components as $component ) {
     838        if ( ! isset( $wpvulnerability_analyze[ $component ] ) ) {
     839            $wpvulnerability_analyze[ $component ] = 0;
     840        }
     841        $constant             = 'WPVULNERABILITY_HIDE_' . strtoupper( $component );
     842        $forced[ $component ] = defined( $constant ) && constant( $constant );
     843        if ( $forced[ $component ] ) {
     844            $wpvulnerability_analyze[ $component ] = 1;
     845        }
    692846    }
    693847
     
    695849    <div id="wpvulnerability_analyze">
    696850    <label>
    697         <input type="checkbox" name="wpvulnerability-analyze[core]" value="core" <?php checked( $wpvulnerability_analyze['core'] ); ?> />
     851        <input type="checkbox" name="wpvulnerability-analyze[core]" value="core" <?php checked( $wpvulnerability_analyze['core'] ); ?> <?php disabled( $forced['core'] ); ?> />
    698852        <?php esc_html_e( 'Core', 'wpvulnerability' ); ?>
    699853    </label>
    700854    <br/>
    701855    <label>
    702         <input type="checkbox" name="wpvulnerability-analyze[plugins]" value="plugins" <?php checked( $wpvulnerability_analyze['plugins'] ); ?> />
     856        <input type="checkbox" name="wpvulnerability-analyze[plugins]" value="plugins" <?php checked( $wpvulnerability_analyze['plugins'] ); ?> <?php disabled( $forced['plugins'] ); ?> />
    703857        <?php esc_html_e( 'Plugins', 'wpvulnerability' ); ?>
    704858    </label>
    705859    <br/>
    706860    <label>
    707         <input type="checkbox" name="wpvulnerability-analyze[themes]" value="themes" <?php checked( $wpvulnerability_analyze['themes'] ); ?> />
     861        <input type="checkbox" name="wpvulnerability-analyze[themes]" value="themes" <?php checked( $wpvulnerability_analyze['themes'] ); ?> <?php disabled( $forced['themes'] ); ?> />
    708862        <?php esc_html_e( 'Themes', 'wpvulnerability' ); ?>
    709863    </label>
    710864    <br/>
    711865    <label>
    712         <input type="checkbox" name="wpvulnerability-analyze[php]" value="php" <?php checked( $wpvulnerability_analyze['php'] ); ?> />
     866        <input type="checkbox" name="wpvulnerability-analyze[php]" value="php" <?php checked( $wpvulnerability_analyze['php'] ); ?> <?php disabled( $forced['php'] ); ?> />
    713867        <?php esc_html_e( 'PHP', 'wpvulnerability' ); ?>
    714868    </label>
    715869    <br/>
    716870    <label>
    717         <input type="checkbox" name="wpvulnerability-analyze[apache]" value="apache" <?php checked( $wpvulnerability_analyze['apache'] ); ?> />
     871        <input type="checkbox" name="wpvulnerability-analyze[apache]" value="apache" <?php checked( $wpvulnerability_analyze['apache'] ); ?> <?php disabled( $forced['apache'] ); ?> />
    718872        <?php esc_html_e( 'Apache HTTPD', 'wpvulnerability' ); ?>
    719873    </label>
    720874    <br/>
    721875    <label>
    722         <input type="checkbox" name="wpvulnerability-analyze[nginx]" value="nginx" <?php checked( $wpvulnerability_analyze['nginx'] ); ?> />
     876        <input type="checkbox" name="wpvulnerability-analyze[nginx]" value="nginx" <?php checked( $wpvulnerability_analyze['nginx'] ); ?> <?php disabled( $forced['nginx'] ); ?> />
    723877        <?php esc_html_e( 'nginx', 'wpvulnerability' ); ?>
    724878    </label>
    725879    <br/>
    726880    <label>
    727         <input type="checkbox" name="wpvulnerability-analyze[mariadb]" value="mariadb" <?php checked( $wpvulnerability_analyze['mariadb'] ); ?> />
     881        <input type="checkbox" name="wpvulnerability-analyze[mariadb]" value="mariadb" <?php checked( $wpvulnerability_analyze['mariadb'] ); ?> <?php disabled( $forced['mariadb'] ); ?> />
    728882        <?php esc_html_e( 'MariaDB', 'wpvulnerability' ); ?>
    729883    </label>
    730884    <br/>
    731885    <label>
    732         <input type="checkbox" name="wpvulnerability-analyze[mysql]" value="mysql" <?php checked( $wpvulnerability_analyze['mysql'] ); ?> />
     886        <input type="checkbox" name="wpvulnerability-analyze[mysql]" value="mysql" <?php checked( $wpvulnerability_analyze['mysql'] ); ?> <?php disabled( $forced['mysql'] ); ?> />
    733887        <?php esc_html_e( 'MySQL', 'wpvulnerability' ); ?>
    734888    </label>
    735889    <br/>
    736890    <label>
    737         <input type="checkbox" name="wpvulnerability-analyze[imagemagick]" value="imagemagick" <?php checked( $wpvulnerability_analyze['imagemagick'] ); ?> />
     891        <input type="checkbox" name="wpvulnerability-analyze[imagemagick]" value="imagemagick" <?php checked( $wpvulnerability_analyze['imagemagick'] ); ?> <?php disabled( $forced['imagemagick'] ); ?> />
    738892        <?php esc_html_e( 'ImageMagick', 'wpvulnerability' ); ?>
    739893    </label>
    740894    <br/>
    741895    <label>
    742         <input type="checkbox" name="wpvulnerability-analyze[curl]" value="curl" <?php checked( $wpvulnerability_analyze['curl'] ); ?> />
     896        <input type="checkbox" name="wpvulnerability-analyze[curl]" value="curl" <?php checked( $wpvulnerability_analyze['curl'] ); ?> <?php disabled( $forced['curl'] ); ?> />
    743897        <?php esc_html_e( 'curl', 'wpvulnerability' ); ?>
    744898    </label>
    745 
    746899    <br/>
    747900    <label>
    748         <input type="checkbox" name="wpvulnerability-analyze[memcached]" value="memcached" <?php checked( $wpvulnerability_analyze['memcached'] ); ?> />
     901        <input type="checkbox" name="wpvulnerability-analyze[memcached]" value="memcached" <?php checked( $wpvulnerability_analyze['memcached'] ); ?> <?php disabled( $forced['memcached'] ); ?> />
    749902        <?php esc_html_e( 'memcached', 'wpvulnerability' ); ?>
    750903    </label>
    751904    <br/>
    752905    <label>
    753         <input type="checkbox" name="wpvulnerability-analyze[redis]" value="redis" <?php checked( $wpvulnerability_analyze['redis'] ); ?> />
     906        <input type="checkbox" name="wpvulnerability-analyze[redis]" value="redis" <?php checked( $wpvulnerability_analyze['redis'] ); ?> <?php disabled( $forced['redis'] ); ?> />
    754907        <?php esc_html_e( 'Redis', 'wpvulnerability' ); ?>
    755908    </label>
    756909    <br/>
    757910    <label>
    758         <input type="checkbox" name="wpvulnerability-analyze[sqlite]" value="sqlite" <?php checked( $wpvulnerability_analyze['sqlite'] ); ?> />
     911        <input type="checkbox" name="wpvulnerability-analyze[sqlite]" value="sqlite" <?php checked( $wpvulnerability_analyze['sqlite'] ); ?> <?php disabled( $forced['sqlite'] ); ?> />
    759912        <?php esc_html_e( 'SQLite', 'wpvulnerability' ); ?>
    760913    </label>
     914    <p><a href="https://www.wpvulnerability.com/plugin/#force-hiding-checks" target="_blank"><small><i><?php esc_html_e( 'Read more about how to force the deactivation of an item.', 'wpvulnerability' ); ?></i></small></a></p>
    761915    </div>
    762916    <?php
     
    774928function wpvulnerability_admin_sanitize( $input ) {
    775929
    776     $sanitized_values = array();
    777     $input_emails     = array();
    778 
    779     if ( isset( $input['emails'] ) ) {
    780 
    781         $input_email_text = explode( ',', $input['emails'] );
    782 
    783         // Loop through each email address in the input field.
    784         foreach ( $input_email_text as $input_email ) {
    785 
    786             // Sanitize each email address.
    787             $input_email = sanitize_email( trim( $input_email ) );
    788 
    789             if ( $input_email ) {
    790                 $input_emails[] = $input_email;
    791             }
    792         }
    793     }
    794 
    795     if ( count( $input_emails ) ) {
    796         $sanitized_values['emails'] = implode( ',', $input_emails );
    797     } else {
    798         $sanitized_values['emails'] = null;
    799     }
    800 
    801     if ( isset( $input['period'] ) ) {
    802 
    803         // Check the value of the period field and sanitize it.
    804         switch ( $input['period'] ) {
    805             case 'daily':
    806                 $sanitized_values['period'] = 'daily';
    807                 break;
    808             case 'weekly':
    809             default:
    810                 $sanitized_values['period'] = 'weekly';
    811                 break;
    812         }
    813     }
    814 
    815     // Clear the scheduled hook for the notification event and set it to run at the sanitized interval.
    816     wp_clear_scheduled_hook( 'wpvulnerability_notification' );
    817     wp_schedule_event( time(), $sanitized_values['period'], 'wpvulnerability_notification' );
    818 
    819     return $sanitized_values;
     930        $sanitized_values = array();
     931        $input_emails     = array();
     932
     933        // Default notification values.
     934                $sanitized_values['notify']        = array(
     935                    'email' => 'n',
     936                    'slack' => 'n',
     937                    'teams' => 'n',
     938                );
     939                $sanitized_values['slack_webhook'] = '';
     940                $sanitized_values['teams_webhook'] = '';
     941                $sanitized_values['day']           = 'monday';
     942                $sanitized_values['hour']          = 0;
     943                $sanitized_values['minute']        = 0;
     944                $sanitized_values['cache']         = 12;
     945
     946                if ( isset( $input['emails'] ) ) {
     947
     948                    $input_email_text = explode( ',', $input['emails'] );
     949
     950                    // Loop through each email address in the input field.
     951                    foreach ( $input_email_text as $input_email ) {
     952
     953                        // Sanitize each email address.
     954                        $input_email = sanitize_email( trim( $input_email ) );
     955
     956                        if ( $input_email ) {
     957                            $input_emails[] = $input_email;
     958                        }
     959                    }
     960                }
     961
     962                if ( count( $input_emails ) ) {
     963                    $sanitized_values['emails'] = implode( ',', $input_emails );
     964                } else {
     965                    $sanitized_values['emails'] = null;
     966                }
     967
     968                if ( isset( $input['period'] ) ) {
     969
     970                    // Check the value of the period field and sanitize it.
     971                    switch ( $input['period'] ) {
     972                        case 'never':
     973                                $sanitized_values['period'] = 'never';
     974                            break;
     975                        case 'daily':
     976                            $sanitized_values['period'] = 'daily';
     977                            break;
     978                        case 'weekly':
     979                            $sanitized_values['period'] = 'weekly';
     980                            break;
     981                        default:
     982                            $sanitized_values['period'] = 'weekly';
     983                            break;
     984                    }
     985                }
     986
     987                if ( isset( $input['day'] ) ) {
     988                        $day                     = strtolower( sanitize_text_field( $input['day'] ) );
     989                        $valid_days              = array( 'sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday' );
     990                        $sanitized_values['day'] = in_array( $day, $valid_days, true ) ? $day : 'monday';
     991                }
     992
     993                if ( isset( $input['hour'] ) ) {
     994                        $hour                     = (int) $input['hour'];
     995                        $sanitized_values['hour'] = max( 0, min( 23, $hour ) );
     996                }
     997
     998                if ( isset( $input['minute'] ) ) {
     999                        $minute                     = (int) $input['minute'];
     1000                        $sanitized_values['minute'] = max( 0, min( 59, $minute ) );
     1001                }
     1002
     1003                if ( isset( $input['cache'] ) ) {
     1004                        $cache                     = (int) $input['cache'];
     1005                        $sanitized_values['cache'] = in_array( $cache, array( 1, 6, 12, 24 ), true ) ? $cache : 12;
     1006                }
     1007
     1008                if ( isset( $input['notify'] ) && is_array( $input['notify'] ) ) {
     1009                        $notify_input               = array_map( 'sanitize_text_field', (array) wp_unslash( $input['notify'] ) );
     1010                        $sanitized_values['notify'] = wpvulnerability_normalize_notify_settings( $notify_input );
     1011                }
     1012
     1013                if ( isset( $input['slack_webhook'] ) ) {
     1014                        $sanitized_values['slack_webhook'] = esc_url_raw( trim( $input['slack_webhook'] ) );
     1015                }
     1016
     1017                if ( isset( $input['teams_webhook'] ) ) {
     1018                        $sanitized_values['teams_webhook'] = esc_url_raw( trim( $input['teams_webhook'] ) );
     1019                }
     1020
     1021                // Schedule notification based on sanitized values.
     1022                wpvulnerability_schedule_notification_event( $sanitized_values );
     1023                return $sanitized_values;
    8201024}
    8211025
     
    8511055    );
    8521056
     1057    foreach ( $sanitized_values as $component => $value ) {
     1058        $constant = 'WPVULNERABILITY_HIDE_' . strtoupper( $component );
     1059        if ( defined( $constant ) && constant( $constant ) ) {
     1060            $sanitized_values[ $component ] = 1;
     1061        }
     1062    }
     1063
    8531064    return $sanitized_values;
    8541065}
     
    11891400
    11901401    // Add a section to the settings page.
    1191     add_settings_section(
    1192         'admin_wpvulnerability_settings',
    1193         __( 'Receive notifications in your email', 'wpvulnerability' ),
    1194         'wpvulnerability_admin_section_notifications',
    1195         'wpvulnerability-config'
    1196     );
    1197 
    1198     // Add a field for the email addresses.
    1199     add_settings_field(
    1200         'wpvulnerability_emails',
    1201         __( 'Email addresses to notify (separated by commas)', 'wpvulnerability' ),
    1202         'wpvulnerability_admin_emails_callback',
    1203         'wpvulnerability-config',
    1204         'admin_wpvulnerability_settings'
    1205     );
    1206 
    1207     // Add a field for the notification period.
    1208     add_settings_field(
    1209         'wpvulnerability_period',
    1210         __( 'How often you want to receive notifications', 'wpvulnerability' ),
    1211         'wpvulnerability_admin_period_callback',
    1212         'wpvulnerability-config',
    1213         'admin_wpvulnerability_settings'
    1214     );
     1402        add_settings_section(
     1403            'admin_wpvulnerability_settings',
     1404            __( 'Receive vulnerability notifications', 'wpvulnerability' ),
     1405            'wpvulnerability_admin_section_notifications',
     1406            'wpvulnerability-config'
     1407        );
     1408
     1409        // Add a field for the cache expiration time.
     1410        add_settings_field(
     1411            'wpvulnerability_cache',
     1412            __( 'Cache expiration time', 'wpvulnerability' ),
     1413            'wpvulnerability_admin_cache_callback',
     1414            'wpvulnerability-config',
     1415            'admin_wpvulnerability_settings'
     1416        );
     1417
     1418        // Add a field for the notification period.
     1419        add_settings_field(
     1420            'wpvulnerability_period',
     1421            __( 'How often you want to receive notifications', 'wpvulnerability' ),
     1422            'wpvulnerability_admin_period_callback',
     1423            'wpvulnerability-config',
     1424            'admin_wpvulnerability_settings'
     1425        );
     1426
     1427                // Add a field for notification methods.
     1428                add_settings_field(
     1429                    'wpvulnerability_notify',
     1430                    __( 'Where do you want to receive notifications?', 'wpvulnerability' ),
     1431                    'wpvulnerability_admin_notify_callback',
     1432                    'wpvulnerability-config',
     1433                    'admin_wpvulnerability_settings'
     1434                );
     1435
     1436        // Add a field for the email addresses.
     1437        add_settings_field(
     1438            'wpvulnerability_emails',
     1439            __( 'Email addresses to notify (separated by commas)', 'wpvulnerability' ),
     1440            'wpvulnerability_admin_emails_callback',
     1441            'wpvulnerability-config',
     1442            'admin_wpvulnerability_settings'
     1443        );
     1444
     1445                // Add a field for the Slack webhook.
     1446                add_settings_field(
     1447                    'wpvulnerability_slack_webhook',
     1448                    __( 'Slack webhook URL', 'wpvulnerability' ),
     1449                    'wpvulnerability_admin_slack_callback',
     1450                    'wpvulnerability-config',
     1451                    'admin_wpvulnerability_settings'
     1452                );
     1453
     1454                // Add a field for the Teams webhook.
     1455                add_settings_field(
     1456                    'wpvulnerability_teams_webhook',
     1457                    __( 'Teams webhook URL', 'wpvulnerability' ),
     1458                    'wpvulnerability_admin_teams_callback',
     1459                    'wpvulnerability-config',
     1460                    'admin_wpvulnerability_settings'
     1461                );
    12151462
    12161463    // Register the settings for analysis.
  • wpvulnerability/trunk/wpvulnerability-adminms.php

    r3267956 r3362936  
    2828 */
    2929function wpvulnerability_admin_enqueue_scripts() {
    30     // Enqueue the admin stylesheet.
    31     wp_enqueue_style(
    32         'wpvulnerability-admin',
    33         WPVULNERABILITY_PLUGIN_URL . 'assets/admin.css',
    34         array(),
    35         WPVULNERABILITY_PLUGIN_VERSION
    36     );
     30        // Enqueue the admin stylesheet.
     31        wp_enqueue_style(
     32            'wpvulnerability-admin',
     33            WPVULNERABILITY_PLUGIN_URL . 'assets/admin.css',
     34            array(),
     35            WPVULNERABILITY_PLUGIN_VERSION
     36        );
     37
     38        wp_enqueue_script(
     39            'wpvulnerability-admin-js',
     40            WPVULNERABILITY_PLUGIN_URL . 'assets/admin.js',
     41            array( 'jquery' ),
     42            WPVULNERABILITY_PLUGIN_VERSION,
     43            true
     44        );
    3745}
    3846add_action( 'admin_enqueue_scripts', 'wpvulnerability_admin_enqueue_scripts' );
     
    6573            if ( isset( $_POST['wpvulnerability_submit'] ) && isset( $_POST['wpvulnerability-config'] ) ) {
    6674
    67                 $wpvulnerability_sanitized_values['emails'] = null;
    68                 $wpvulnerability_sanitized_values['period'] = 'weekly';
    69 
    70                 $wpvulnerability_input['emails'] = array();
    71                 if ( isset( $_POST['wpvulnerability-config']['emails'] ) ) {
    72                     $wpvulnerability_config_emails    = wp_kses( wp_unslash( (string) $_POST['wpvulnerability-config']['emails'] ), 'strip' );
    73                     $wpvulnerability_input_email_text = explode( ',', $wpvulnerability_config_emails );
    74                     foreach ( $wpvulnerability_input_email_text as $wpvulnerability_input_email ) {
    75                         $wpvulnerability_input_email = sanitize_email( trim( $wpvulnerability_input_email ) );
    76                         if ( is_email( $wpvulnerability_input_email ) ) {
    77                             $wpvulnerability_input['emails'][] = $wpvulnerability_input_email;
    78                         }
    79                         unset( $wpvulnerability_input_email );
    80                     }
    81                     unset( $wpvulnerability_input_email_text, $wpvulnerability_config_emails );
    82                 }
    83                 if ( count( $wpvulnerability_input['emails'] ) ) {
    84                     $wpvulnerability_sanitized_values['emails'] = implode( ',', $wpvulnerability_input['emails'] );
    85                 } else {
    86                     $wpvulnerability_sanitized_values['emails'] = null;
    87                 }
    88 
    89                 $wpvulnerability_input['period'] = null;
    90                 if ( isset( $_POST['wpvulnerability-config']['period'] ) ) {
    91                     $wpvulnerability_input['period'] = wp_kses( wp_unslash( (string) $_POST['wpvulnerability-config']['period'] ), 'strip' );
    92                 }
    93                 switch ( $wpvulnerability_input['period'] ) {
    94                     case 'daily':
    95                         $wpvulnerability_sanitized_values['period'] = 'daily';
    96                         break;
    97                     case 'weekly':
    98                     default:
    99                         $wpvulnerability_sanitized_values['period'] = 'weekly';
    100                         break;
    101                 }
    102                 unset( $wpvulnerability_input );
    103 
    104                 update_site_option(
    105                     'wpvulnerability-config',
    106                     array(
    107                         'emails' => $wpvulnerability_sanitized_values['emails'],
    108                         'period' => $wpvulnerability_sanitized_values['period'],
    109                     )
    110                 );
    111 
    112                 $current_schedule = wp_get_schedule( 'wpvulnerability_notification' );
    113                 if ( $current_schedule !== $wpvulnerability_sanitized_values['period'] ) {
    114                     wp_clear_scheduled_hook( 'wpvulnerability_notification' );
    115                     wp_schedule_event( time(), $wpvulnerability_sanitized_values['period'], 'wpvulnerability_notification' );
    116                 }
    117                 unset( $current_schedule );
     75                                                                $wpvulnerability_sanitized_values['emails'] = null;
     76                                                                $wpvulnerability_sanitized_values['period'] = 'weekly';
     77                                                                $wpvulnerability_sanitized_values['day']    = 'monday';
     78                                                                $wpvulnerability_sanitized_values['hour']   = 0;
     79                                                                $wpvulnerability_sanitized_values['minute'] = 0;
     80                                                                $wpvulnerability_sanitized_values['cache']  = 12;
     81                                                                                                                                $wpvulnerability_sanitized_values['notify'] = array(
     82                                                                                                                                    'email' => 'n',
     83                                                                                                                                    'slack' => 'n',
     84                                                                                                                                    'teams' => 'n',
     85                                                                                                                                );
     86                                                                                                                                if ( isset( $post_config['notify'] ) && is_array( $post_config['notify'] ) ) {
     87                                                                                                                                    $notify                                     = array_map( 'sanitize_text_field', wp_unslash( $post_config['notify'] ) );
     88                                                                                                                                    $wpvulnerability_sanitized_values['notify'] = wpvulnerability_normalize_notify_settings( $notify );
     89                                                                                                                                }
     90                                                                                                                                $wpvulnerability_sanitized_values['slack_webhook'] = '';
     91                                                                                                                                $wpvulnerability_sanitized_values['teams_webhook'] = '';
     92
     93                                                                                                                                $wpvulnerability_input['emails'] = array();
     94                                                                                                                                if ( isset( $_POST['wpvulnerability-config']['emails'] ) ) {
     95                                                                                                                                    $wpvulnerability_config_emails    = wp_kses( wp_unslash( (string) $_POST['wpvulnerability-config']['emails'] ), 'strip' );
     96                                                                                                                                    $wpvulnerability_input_email_text = explode( ',', $wpvulnerability_config_emails );
     97                                                                                                                                    foreach ( $wpvulnerability_input_email_text as $wpvulnerability_input_email ) {
     98                                                                                                                                        $wpvulnerability_input_email = sanitize_email( trim( $wpvulnerability_input_email ) );
     99                                                                                                                                        if ( is_email( $wpvulnerability_input_email ) ) {
     100                                                                                                                                            $wpvulnerability_input['emails'][] = $wpvulnerability_input_email;
     101                                                                                                                                        }
     102                                                                                                                                        unset( $wpvulnerability_input_email );
     103                                                                                                                                    }
     104                                                                                                                                    unset( $wpvulnerability_input_email_text, $wpvulnerability_config_emails );
     105                                                                                                                                }
     106                                                                                                                                if ( count( $wpvulnerability_input['emails'] ) ) {
     107                                                                                                                                    $wpvulnerability_sanitized_values['emails'] = implode( ',', $wpvulnerability_input['emails'] );
     108                                                                                                                                } else {
     109                                                                                                                                    $wpvulnerability_sanitized_values['emails'] = null;
     110                                                                                                                                }
     111
     112                                                                                                                                $wpvulnerability_input['period'] = null;
     113                                                                                                                                if ( isset( $_POST['wpvulnerability-config']['period'] ) ) {
     114                                                                                                                                        $wpvulnerability_input['period'] = wp_kses( wp_unslash( (string) $_POST['wpvulnerability-config']['period'] ), 'strip' );
     115                                                                                                                                }
     116                                                                                                                                switch ( $wpvulnerability_input['period'] ) {
     117                                                                                                                                    case 'never':
     118                                                                                                                                        $wpvulnerability_sanitized_values['period'] = 'never';
     119                                                                                                                                        break;
     120                                                                                                                                    case 'daily':
     121                                                                                                                                            $wpvulnerability_sanitized_values['period'] = 'daily';
     122                                                                                                                                        break;
     123                                                                                                                                    case 'weekly':
     124                                                                                                                                            $wpvulnerability_sanitized_values['period'] = 'weekly';
     125                                                                                                                                        break;
     126                                                                                                                                    default:
     127                                                                                                                                            $wpvulnerability_sanitized_values['period'] = 'weekly';
     128                                                                                                                                        break;
     129                                                                                                                                }
     130
     131                                                                                                                                if ( isset( $_POST['wpvulnerability-config']['day'] ) ) {
     132                                                                                                                                                $day                                     = strtolower( wp_kses( wp_unslash( (string) $_POST['wpvulnerability-config']['day'] ), 'strip' ) );
     133                                                                                                                                                $valid_days                              = array( 'sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday' );
     134                                                                                                                                                $wpvulnerability_sanitized_values['day'] = in_array( $day, $valid_days, true ) ? $day : 'monday';
     135                                                                                                                                }
     136
     137                                                                                                                                if ( isset( $_POST['wpvulnerability-config']['hour'] ) ) {
     138                                                                                                                                                $hour                                     = (int) $_POST['wpvulnerability-config']['hour'];
     139                                                                                                                                                $wpvulnerability_sanitized_values['hour'] = max( 0, min( 23, $hour ) );
     140                                                                                                                                }
     141
     142                                                                                                                                if ( isset( $_POST['wpvulnerability-config']['minute'] ) ) {
     143                                                                                                                                                $minute                                     = (int) $_POST['wpvulnerability-config']['minute'];
     144                                                                                                                                                $wpvulnerability_sanitized_values['minute'] = max( 0, min( 59, $minute ) );
     145                                                                                                                                }
     146
     147                                                                                                                                if ( isset( $_POST['wpvulnerability-config']['cache'] ) ) {
     148                                                                                                                                                $cache                                     = (int) $_POST['wpvulnerability-config']['cache'];
     149                                                                                                                                                $wpvulnerability_sanitized_values['cache'] = in_array( $cache, array( 1, 6, 12, 24 ), true ) ? $cache : 12;
     150                                                                                                                                }
     151
     152                                                                                                                                $post_config = filter_input( INPUT_POST, 'wpvulnerability-config', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY );
     153                                                                                                                                if ( isset( $post_config['notify'] ) && is_array( $post_config['notify'] ) ) {
     154                                                                                                                                    $notify                                     = array_map( 'sanitize_text_field', wp_unslash( $post_config['notify'] ) );
     155                                                                                                                                    $wpvulnerability_sanitized_values['notify'] = wpvulnerability_normalize_notify_settings( $notify );
     156                                                                                                                                }
     157
     158                                                                                                                                if ( isset( $_POST['wpvulnerability-config']['slack_webhook'] ) ) {
     159                                                                                                                                        $slack = wp_kses( wp_unslash( (string) $_POST['wpvulnerability-config']['slack_webhook'] ), 'strip' );
     160                                                                                                                                        $wpvulnerability_sanitized_values['slack_webhook'] = esc_url_raw( $slack );
     161                                                                                                                                }
     162
     163                                                                                                                                if ( isset( $_POST['wpvulnerability-config']['teams_webhook'] ) ) {
     164                                                                                                                                        $teams = wp_kses( wp_unslash( (string) $_POST['wpvulnerability-config']['teams_webhook'] ), 'strip' );
     165                                                                                                                                        $wpvulnerability_sanitized_values['teams_webhook'] = esc_url_raw( $teams );
     166                                                                                                                                }
     167
     168                                                                                                                                unset( $wpvulnerability_input );
     169
     170                                                                                                                                update_site_option(
     171                                                                                                                                    'wpvulnerability-config',
     172                                                                                                                                    array(
     173                                                                                                                                        'emails'        => $wpvulnerability_sanitized_values['emails'],
     174                                                                                                                                        'period'        => $wpvulnerability_sanitized_values['period'],
     175                                                                                                                                        'day'           => $wpvulnerability_sanitized_values['day'],
     176                                                                                                                                        'hour'          => $wpvulnerability_sanitized_values['hour'],
     177                                                                                                                                        'minute'        => $wpvulnerability_sanitized_values['minute'],
     178                                                                                                                                        'notify'        => $wpvulnerability_sanitized_values['notify'],
     179                                                                                                                                        'slack_webhook' => $wpvulnerability_sanitized_values['slack_webhook'],
     180                                                                                                                                        'teams_webhook' => $wpvulnerability_sanitized_values['teams_webhook'],
     181                                                                                                                                        'cache'         => $wpvulnerability_sanitized_values['cache'],
     182                                                                                                                                    )
     183                                                                                                                                );
     184                                wpvulnerability_schedule_notification_event( $wpvulnerability_sanitized_values );
    118185
    119186                unset( $wpvulnerability_sanitized_values );
     
    332399            </header>
    333400            <div class="section-content">
    334                 <p><?php esc_html_e( 'Reload all Core, Plugins, Themes and other components information directly from the API to have updated data.', 'wpvulnerability' ); ?></p>
    335                 <form method="post" action="settings.php?page=wpvulnerability-options">
    336                     <?php wp_nonce_field( 'wpvulnerability_nonce', 'wpauto_nonce' ); ?>
    337                     <input type="submit" name="wpvulnerability_reset" value="<?php esc_attr_e( 'Reload Data', 'wpvulnerability' ); ?>" class="button button-secondary">
    338                 </form>
     401                                <p><?php esc_html_e( 'Reload all Core, Plugins, Themes and other components information directly from the API to have updated data.', 'wpvulnerability' ); ?></p>
     402                                <form method="post" action="settings.php?page=wpvulnerability-options">
     403                                        <?php wp_nonce_field( 'wpvulnerability_nonce', 'wpauto_nonce' ); ?>
     404                                        <input type="submit" name="wpvulnerability_reset" value="<?php esc_attr_e( 'Reload Data', 'wpvulnerability' ); ?>" class="button button-secondary">
     405                                </form>
    339406            </div>
    340407        </section>
     
    362429    }
    363430    ?>
    364                 <p><?php esc_html_e( 'Send an email with the vulnerabilities (or empty).', 'wpvulnerability' ); ?></p>
    365                 <form method="post" action="settings.php?page=wpvulnerability-options">
    366                     <?php wp_nonce_field( 'wpvulnerability_nonce', 'wpauto_nonce' ); ?>
    367                     <input type="submit" name="wpvulnerability_email" value="<?php esc_attr_e( 'Send email', 'wpvulnerability' ); ?>" class="button button-secondary">
    368                 </form>
     431<p><?php esc_html_e( 'Send an email with the vulnerabilities (or empty).', 'wpvulnerability' ); ?></p>
     432<p><a href="https://www.wpvulnerability.com/plugin/#from-mail" target="_blank"><small><i><?php esc_html_e( 'Read more about how to change the "From:" of the email.', 'wpvulnerability' ); ?></i></small></a></p>
     433<form method="post" action="settings.php?page=wpvulnerability-options">
     434    <?php wp_nonce_field( 'wpvulnerability_nonce', 'wpauto_nonce' ); ?>
     435<input type="submit" name="wpvulnerability_email" value="<?php esc_attr_e( 'Send email', 'wpvulnerability' ); ?>" class="button button-secondary">
     436</form>
    369437            </div>
    370438        </section>
     
    738806 */
    739807function wpvulnerability_admin_section_notifications() {
    740     // Output the header information for the notifications section.
    741     esc_html_e( 'Configure and save these settings to receive email notifications.', 'wpvulnerability' );
     808        // Output the header information for the notifications section.
     809        esc_html_e( 'Configure and save these settings to receive notifications.', 'wpvulnerability' );
    742810}
    743811
     
    771839    unset( $admin_email );
    772840}
     841
     842/**
     843 * Print the cache expiration selector.
     844 *
     845 * @since 4.1.0
     846 *
     847 * @return void
     848 */
     849function wpvulnerability_admin_cache_callback() {
     850
     851    $wpvulnerability_settings = get_site_option( 'wpvulnerability-config', array() );
     852    $options                  = array( 1, 6, 12, 24 );
     853    $forced_cache             = null;
     854
     855    if ( defined( 'WPVULNERABILITY_CACHE_HOURS' ) ) {
     856        $forced_cache = (int) WPVULNERABILITY_CACHE_HOURS;
     857        if ( ! in_array( $forced_cache, $options, true ) ) {
     858            $options[] = $forced_cache;
     859            sort( $options, SORT_NUMERIC );
     860        }
     861    }
     862
     863    $current = isset( $wpvulnerability_settings['cache'] ) ? (int) $wpvulnerability_settings['cache'] : 12;
     864    if ( null !== $forced_cache ) {
     865        $current = $forced_cache;
     866    }
     867
     868    echo '<select name="wpvulnerability-config[cache]" id="wpvulnerability_cache"';
     869    disabled( null !== $forced_cache );
     870    echo '>';
     871    foreach ( $options as $hours ) {
     872        printf(
     873            '<option value="%1$s"%2$s>%3$s</option>',
     874            esc_attr( $hours ),
     875            selected( $current, $hours, false ),
     876            esc_html(
     877                sprintf(
     878                        /* translators: %d: number of hours */
     879                    _n( '%d hour', '%d hours', $hours, 'wpvulnerability' ),
     880                    $hours
     881                )
     882            )
     883        );
     884    }
     885        echo '</select>';
     886
     887    if ( null !== $forced_cache ) {
     888            printf(
     889                '<input type="hidden" name="wpvulnerability-config[cache]" value="%s" />',
     890                esc_attr( $current )
     891            );
     892    }
     893
     894        printf(
     895            '<p class="description"><a href="%1$s" target="_blank"><small><i>%2$s</i></small></a></p>',
     896            esc_url( 'https://www.wpvulnerability.com/plugin/#cache-duration' ),
     897            esc_html__( 'Read more if you want to force the cache time.', 'wpvulnerability' )
     898        );
     899}
     900
    773901
    774902/**
     
    792920 */
    793921function wpvulnerability_admin_period_callback() {
    794     // Get the saved plugin settings.
    795     $wpvulnerability_settings = get_site_option( 'wpvulnerability-config', array() );
    796 
    797     // Set the default period to 'weekly' if not set or empty.
    798     if ( ! isset( $wpvulnerability_settings['period'] ) || empty( $wpvulnerability_settings['period'] ) ) {
    799         $wpvulnerability_settings['period'] = 'weekly';
    800     }
    801 
    802     // Output the period select box.
    803     ?>
    804     <div id="wpvulnerability_period">
    805     <label>
    806         <input type="radio" name="wpvulnerability-config[period]" value="daily" <?php checked( $wpvulnerability_settings['period'], 'daily' ); ?> />
    807         <?php esc_html_e( 'Daily', 'wpvulnerability' ); ?>
    808     </label>
    809     <br/>
    810     <label>
    811         <input type="radio" name="wpvulnerability-config[period]" value="weekly" <?php checked( $wpvulnerability_settings['period'], 'weekly' ); ?> />
    812         <?php esc_html_e( 'Weekly', 'wpvulnerability' ); ?>
    813     </label>
    814     </div>
    815     <?php
     922        // Get the saved plugin settings.
     923        $wpvulnerability_settings = get_site_option( 'wpvulnerability-config', array() );
     924        $defaults                 = array(
     925            'period' => 'weekly',
     926            'day'    => 'monday',
     927            'hour'   => 0,
     928            'minute' => 0,
     929        );
     930        $wpvulnerability_settings = wp_parse_args( $wpvulnerability_settings, $defaults );
     931
     932        ?>
     933        <div id="wpvulnerability_period">
     934        <label>
     935                <input type="radio" name="wpvulnerability-config[period]" value="never" <?php checked( $wpvulnerability_settings['period'], 'never' ); ?> />
     936                <?php esc_html_e( 'Never', 'wpvulnerability' ); ?>
     937        </label>
     938        <br/>
     939        <label>
     940                <input type="radio" name="wpvulnerability-config[period]" value="daily" <?php checked( $wpvulnerability_settings['period'], 'daily' ); ?> />
     941                <?php esc_html_e( 'Daily', 'wpvulnerability' ); ?>
     942        </label>
     943        <br/>
     944        <label>
     945                <input type="radio" name="wpvulnerability-config[period]" value="weekly" <?php checked( $wpvulnerability_settings['period'], 'weekly' ); ?> />
     946                <?php esc_html_e( 'Weekly', 'wpvulnerability' ); ?>
     947        </label>
     948        <div id="wpvulnerability_day_wrap">
     949                <br/>
     950                <label for="wpvulnerability_day"><?php esc_html_e( 'Day', 'wpvulnerability' ); ?></label>
     951                <select name="wpvulnerability-config[day]" id="wpvulnerability_day">
     952                        <option value="monday" <?php selected( $wpvulnerability_settings['day'], 'monday' ); ?>><?php esc_html_e( 'Monday', 'wpvulnerability' ); ?></option>
     953                        <option value="tuesday" <?php selected( $wpvulnerability_settings['day'], 'tuesday' ); ?>><?php esc_html_e( 'Tuesday', 'wpvulnerability' ); ?></option>
     954                        <option value="wednesday" <?php selected( $wpvulnerability_settings['day'], 'wednesday' ); ?>><?php esc_html_e( 'Wednesday', 'wpvulnerability' ); ?></option>
     955                        <option value="thursday" <?php selected( $wpvulnerability_settings['day'], 'thursday' ); ?>><?php esc_html_e( 'Thursday', 'wpvulnerability' ); ?></option>
     956                        <option value="friday" <?php selected( $wpvulnerability_settings['day'], 'friday' ); ?>><?php esc_html_e( 'Friday', 'wpvulnerability' ); ?></option>
     957                        <option value="saturday" <?php selected( $wpvulnerability_settings['day'], 'saturday' ); ?>><?php esc_html_e( 'Saturday', 'wpvulnerability' ); ?></option>
     958                        <option value="sunday" <?php selected( $wpvulnerability_settings['day'], 'sunday' ); ?>><?php esc_html_e( 'Sunday', 'wpvulnerability' ); ?></option>
     959                </select>
     960        </div>
     961        <div id="wpvulnerability_time_wrap">
     962                <br/>
     963                <label for="wpvulnerability_hour"><?php esc_html_e( 'Hour', 'wpvulnerability' ); ?></label>
     964                <input type="number" min="0" max="23" name="wpvulnerability-config[hour]" id="wpvulnerability_hour" value="<?php echo esc_attr( (string) $wpvulnerability_settings['hour'] ); ?>" />
     965                <label for="wpvulnerability_minute"><?php esc_html_e( 'Minute', 'wpvulnerability' ); ?></label>
     966                <input type="number" min="0" max="59" name="wpvulnerability-config[minute]" id="wpvulnerability_minute" value="<?php echo esc_attr( (string) $wpvulnerability_settings['minute'] ); ?>" />
     967        </div>
     968        </div>
     969        <?php
     970}
     971
     972/**
     973 * Print where to send the notifications.
     974 *
     975 * @since 3.6.0
     976 *
     977 * @return void
     978 */
     979function wpvulnerability_admin_notify_callback() {
     980        $wpvulnerability_settings = get_site_option( 'wpvulnerability-config', array() );
     981            $defaults             = array(
     982                'email' => 'y',
     983                'slack' => 'n',
     984                'teams' => 'n',
     985            );
     986
     987            if ( ! isset( $wpvulnerability_settings['notify'] ) || ! is_array( $wpvulnerability_settings['notify'] ) ) {
     988                            $wpvulnerability_settings['notify'] = $defaults;
     989            } else {
     990                            $wpvulnerability_settings['notify'] = wp_parse_args( $wpvulnerability_settings['notify'], $defaults );
     991            }
     992
     993            $wpvulnerability_settings['notify'] = wpvulnerability_normalize_notify_settings( $wpvulnerability_settings['notify'] );
     994
     995            $email_enabled = wpvulnerability_is_yes( $wpvulnerability_settings['notify']['email'] );
     996            $slack_enabled = wpvulnerability_is_yes( $wpvulnerability_settings['notify']['slack'] );
     997            $teams_enabled = wpvulnerability_is_yes( $wpvulnerability_settings['notify']['teams'] );
     998
     999            ?>
     1000        <div id="wpvulnerability_notify">
     1001        <label>
     1002                            <input type="checkbox" name="wpvulnerability-config[notify][email]" value="y" <?php checked( $email_enabled ); ?> />
     1003                <?php esc_html_e( 'Email', 'wpvulnerability' ); ?>
     1004        </label>
     1005        <br/>
     1006        <label>
     1007                            <input type="checkbox" name="wpvulnerability-config[notify][slack]" value="y" <?php checked( $slack_enabled ); ?> />
     1008                <?php esc_html_e( 'Slack', 'wpvulnerability' ); ?>
     1009        </label>
     1010        <br/>
     1011        <label>
     1012                            <input type="checkbox" name="wpvulnerability-config[notify][teams]" value="y" <?php checked( $teams_enabled ); ?> />
     1013                <?php esc_html_e( 'Microsoft Teams', 'wpvulnerability' ); ?>
     1014        </label>
     1015        </div>
     1016        <?php
     1017}
     1018
     1019/**
     1020 * Print the Slack webhook input field.
     1021 *
     1022 * @since 3.6.0
     1023 *
     1024 * @return void
     1025 */
     1026function wpvulnerability_admin_slack_callback() {
     1027
     1028        $wpvulnerability_settings = get_site_option( 'wpvulnerability-config', array() );
     1029        $slack_webhook            = isset( $wpvulnerability_settings['slack_webhook'] ) ? $wpvulnerability_settings['slack_webhook'] : '';
     1030
     1031    ?>
     1032                <input class="regular-text" type="text" name="wpvulnerability-config[slack_webhook]" id="wpvulnerability_slack_webhook" placeholder="<?php echo esc_attr( 'https://hooks.slack.com/services/...' ); ?>" value="<?php echo esc_attr( (string) $slack_webhook ); ?>" />
     1033                <?php
     1034}
     1035
     1036/**
     1037 * Print the Teams webhook input field.
     1038 *
     1039 * @since 3.6.0
     1040 *
     1041 * @return void
     1042 */
     1043function wpvulnerability_admin_teams_callback() {
     1044
     1045        $wpvulnerability_settings = get_site_option( 'wpvulnerability-config', array() );
     1046        $teams_webhook            = isset( $wpvulnerability_settings['teams_webhook'] ) ? $wpvulnerability_settings['teams_webhook'] : '';
     1047
     1048    ?>
     1049                <input class="regular-text" type="text" name="wpvulnerability-config[teams_webhook]" id="wpvulnerability_teams_webhook" placeholder="<?php echo esc_attr( 'https://outlook.office.com/webhook/...' ); ?>" value="<?php echo esc_attr( (string) $teams_webhook ); ?>" />
     1050                <?php
    8161051}
    8171052
     
    8291064 */
    8301065function wpvulnerability_admin_analyze_callback() {
     1066
    8311067    // Retrieve the WPVulnerability plugin settings.
    8321068    $wpvulnerability_analyze = get_site_option( 'wpvulnerability-analyze', array() );
    833     if ( ! isset( $wpvulnerability_analyze['core'] ) ) {
    834         $wpvulnerability_analyze['core'] = 0;
    835     }
    836     if ( ! isset( $wpvulnerability_analyze['plugins'] ) ) {
    837         $wpvulnerability_analyze['plugins'] = 0;
    838     }
    839     if ( ! isset( $wpvulnerability_analyze['themes'] ) ) {
    840         $wpvulnerability_analyze['themes'] = 0;
    841     }
    842     if ( ! isset( $wpvulnerability_analyze['php'] ) ) {
    843         $wpvulnerability_analyze['php'] = 0;
    844     }
    845     if ( ! isset( $wpvulnerability_analyze['apache'] ) ) {
    846         $wpvulnerability_analyze['apache'] = 0;
    847     }
    848     if ( ! isset( $wpvulnerability_analyze['nginx'] ) ) {
    849         $wpvulnerability_analyze['nginx'] = 0;
    850     }
    851     if ( ! isset( $wpvulnerability_analyze['mariadb'] ) ) {
    852         $wpvulnerability_analyze['mariadb'] = 0;
    853     }
    854     if ( ! isset( $wpvulnerability_analyze['mysql'] ) ) {
    855         $wpvulnerability_analyze['mysql'] = 0;
    856     }
    857     if ( ! isset( $wpvulnerability_analyze['imagemagick'] ) ) {
    858         $wpvulnerability_analyze['imagemagick'] = 0;
    859     }
    860     if ( ! isset( $wpvulnerability_analyze['curl'] ) ) {
    861         $wpvulnerability_analyze['curl'] = 0;
    862     }
    863     if ( ! isset( $wpvulnerability_analyze['memcached'] ) ) {
    864         $wpvulnerability_analyze['memcached'] = 0;
    865     }
    866     if ( ! isset( $wpvulnerability_analyze['redis'] ) ) {
    867         $wpvulnerability_analyze['redis'] = 0;
    868     }
    869     if ( ! isset( $wpvulnerability_analyze['sqlite'] ) ) {
    870         $wpvulnerability_analyze['sqlite'] = 0;
     1069    $components              = array( 'core', 'plugins', 'themes', 'php', 'apache', 'nginx', 'mariadb', 'mysql', 'imagemagick', 'curl', 'memcached', 'redis', 'sqlite' );
     1070    $forced                  = array();
     1071
     1072    foreach ( $components as $component ) {
     1073        if ( ! isset( $wpvulnerability_analyze[ $component ] ) ) {
     1074            $wpvulnerability_analyze[ $component ] = 0;
     1075        }
     1076        $constant             = 'WPVULNERABILITY_HIDE_' . strtoupper( $component );
     1077        $forced[ $component ] = defined( $constant ) && constant( $constant );
     1078        if ( $forced[ $component ] ) {
     1079            $wpvulnerability_analyze[ $component ] = 1;
     1080        }
    8711081    }
    8721082    ?>
    8731083    <div id="wpvulnerability_analyze">
    8741084    <label>
    875         <input type="checkbox" name="wpvulnerability-analyze[core]" value="core" <?php checked( $wpvulnerability_analyze['core'] ); ?> />
     1085        <input type="checkbox" name="wpvulnerability-analyze[core]" value="core" <?php checked( $wpvulnerability_analyze['core'] ); ?> <?php disabled( $forced['core'] ); ?> />
    8761086        <?php esc_html_e( 'Core', 'wpvulnerability' ); ?>
    8771087    </label>
    8781088    <br/>
    8791089    <label>
    880         <input type="checkbox" name="wpvulnerability-analyze[plugins]" value="plugins" <?php checked( $wpvulnerability_analyze['plugins'] ); ?> />
     1090        <input type="checkbox" name="wpvulnerability-analyze[plugins]" value="plugins" <?php checked( $wpvulnerability_analyze['plugins'] ); ?> <?php disabled( $forced['plugins'] ); ?> />
    8811091        <?php esc_html_e( 'Plugins', 'wpvulnerability' ); ?>
    8821092    </label>
    8831093    <br/>
    8841094    <label>
    885         <input type="checkbox" name="wpvulnerability-analyze[themes]" value="themes" <?php checked( $wpvulnerability_analyze['themes'] ); ?> />
     1095        <input type="checkbox" name="wpvulnerability-analyze[themes]" value="themes" <?php checked( $wpvulnerability_analyze['themes'] ); ?> <?php disabled( $forced['themes'] ); ?> />
    8861096        <?php esc_html_e( 'Themes', 'wpvulnerability' ); ?>
    8871097    </label>
    8881098    <br/>
    8891099    <label>
    890         <input type="checkbox" name="wpvulnerability-analyze[php]" value="php" <?php checked( $wpvulnerability_analyze['php'] ); ?> />
     1100        <input type="checkbox" name="wpvulnerability-analyze[php]" value="php" <?php checked( $wpvulnerability_analyze['php'] ); ?> <?php disabled( $forced['php'] ); ?> />
    8911101        <?php esc_html_e( 'PHP', 'wpvulnerability' ); ?>
    8921102    </label>
    8931103    <br/>
    8941104    <label>
    895         <input type="checkbox" name="wpvulnerability-analyze[apache]" value="apache" <?php checked( $wpvulnerability_analyze['apache'] ); ?> />
     1105        <input type="checkbox" name="wpvulnerability-analyze[apache]" value="apache" <?php checked( $wpvulnerability_analyze['apache'] ); ?> <?php disabled( $forced['apache'] ); ?> />
    8961106        <?php esc_html_e( 'Apache HTTPD', 'wpvulnerability' ); ?>
    8971107    </label>
    8981108    <br/>
    8991109    <label>
    900         <input type="checkbox" name="wpvulnerability-analyze[nginx]" value="nginx" <?php checked( $wpvulnerability_analyze['nginx'] ); ?> />
     1110        <input type="checkbox" name="wpvulnerability-analyze[nginx]" value="nginx" <?php checked( $wpvulnerability_analyze['nginx'] ); ?> <?php disabled( $forced['nginx'] ); ?> />
    9011111        <?php esc_html_e( 'nginx', 'wpvulnerability' ); ?>
    9021112    </label>
    9031113    <br/>
    9041114    <label>
    905         <input type="checkbox" name="wpvulnerability-analyze[mariadb]" value="mariadb" <?php checked( $wpvulnerability_analyze['mariadb'] ); ?> />
     1115        <input type="checkbox" name="wpvulnerability-analyze[mariadb]" value="mariadb" <?php checked( $wpvulnerability_analyze['mariadb'] ); ?> <?php disabled( $forced['mariadb'] ); ?> />
    9061116        <?php esc_html_e( 'MariaDB', 'wpvulnerability' ); ?>
    9071117    </label>
    9081118    <br/>
    9091119    <label>
    910         <input type="checkbox" name="wpvulnerability-analyze[mysql]" value="mysql" <?php checked( $wpvulnerability_analyze['mysql'] ); ?> />
     1120        <input type="checkbox" name="wpvulnerability-analyze[mysql]" value="mysql" <?php checked( $wpvulnerability_analyze['mysql'] ); ?> <?php disabled( $forced['mysql'] ); ?> />
    9111121        <?php esc_html_e( 'MySQL', 'wpvulnerability' ); ?>
    9121122    </label>
    9131123    <br/>
    9141124    <label>
    915         <input type="checkbox" name="wpvulnerability-analyze[imagemagick]" value="imagemagick" <?php checked( $wpvulnerability_analyze['imagemagick'] ); ?> />
     1125        <input type="checkbox" name="wpvulnerability-analyze[imagemagick]" value="imagemagick" <?php checked( $wpvulnerability_analyze['imagemagick'] ); ?> <?php disabled( $forced['imagemagick'] ); ?> />
    9161126        <?php esc_html_e( 'ImageMagick', 'wpvulnerability' ); ?>
    9171127    </label>
    9181128    <br/>
    9191129    <label>
    920         <input type="checkbox" name="wpvulnerability-analyze[curl]" value="curl" <?php checked( $wpvulnerability_analyze['curl'] ); ?> />
     1130        <input type="checkbox" name="wpvulnerability-analyze[curl]" value="curl" <?php checked( $wpvulnerability_analyze['curl'] ); ?> <?php disabled( $forced['curl'] ); ?> />
    9211131        <?php esc_html_e( 'curl', 'wpvulnerability' ); ?>
    9221132    </label>
    9231133    <br/>
    9241134    <label>
    925         <input type="checkbox" name="wpvulnerability-analyze[memcached]" value="memcached" <?php checked( $wpvulnerability_analyze['memcached'] ); ?> />
     1135        <input type="checkbox" name="wpvulnerability-analyze[memcached]" value="memcached" <?php checked( $wpvulnerability_analyze['memcached'] ); ?> <?php disabled( $forced['memcached'] ); ?> />
    9261136        <?php esc_html_e( 'memcached', 'wpvulnerability' ); ?>
    9271137    </label>
    9281138    <br/>
    9291139    <label>
    930         <input type="checkbox" name="wpvulnerability-analyze[redis]" value="redis" <?php checked( $wpvulnerability_analyze['redis'] ); ?> />
     1140        <input type="checkbox" name="wpvulnerability-analyze[redis]" value="redis" <?php checked( $wpvulnerability_analyze['redis'] ); ?> <?php disabled( $forced['redis'] ); ?> />
    9311141        <?php esc_html_e( 'Redis', 'wpvulnerability' ); ?>
    9321142    </label>
    9331143    <br/>
    9341144    <label>
    935         <input type="checkbox" name="wpvulnerability-analyze[sqlite]" value="sqlite" <?php checked( $wpvulnerability_analyze['sqlite'] ); ?> />
     1145        <input type="checkbox" name="wpvulnerability-analyze[sqlite]" value="sqlite" <?php checked( $wpvulnerability_analyze['sqlite'] ); ?> <?php disabled( $forced['sqlite'] ); ?> />
    9361146        <?php esc_html_e( 'SQLite', 'wpvulnerability' ); ?>
    9371147    </label>
     1148    <p><a href="https://www.wpvulnerability.com/plugin/#force-hiding-checks" target="_blank"><small><i><?php esc_html_e( 'Read more about how to force the deactivation of an item.', 'wpvulnerability' ); ?></i></small></a></p>
    9381149    </div>
    9391150    <?php
     
    12611472
    12621473/**
    1263  * Sanitiza estrictamente la configuración principal (emails y periodos).
    1264  *
    1265  * @param array $input Valores de entrada.
    1266  * @return array Valores sanitizados.
     1474 * Strictly sanitizes the main configuration (emails and periods).
     1475 *
     1476 * @param array $input Input values.
     1477 * @return array Sanitized values.
    12671478 */
    12681479function wpvulnerability_sanitize_config( $input ) {
     1480                $sanitized = array();
     1481
     1482                // Emails (comma-separated list).
     1483    if ( isset( $input['emails'] ) ) {
     1484            $emails_raw       = explode( ',', $input['emails'] );
     1485            $sanitized_emails = array();
     1486
     1487        foreach ( $emails_raw as $email ) {
     1488                $email = sanitize_email( trim( $email ) );
     1489            if ( is_email( $email ) ) {
     1490                    $sanitized_emails[] = $email;
     1491            }
     1492        }
     1493
     1494            $sanitized['emails'] = implode( ',', $sanitized_emails );
     1495    }
     1496
     1497                // Period (daily, weekly, monthly, never).
     1498                $allowed_periods = array( 'daily', 'weekly', 'monthly', 'never' );
     1499    if ( isset( $input['period'] ) && in_array( $input['period'], $allowed_periods, true ) ) {
     1500                    $sanitized['period'] = $input['period'];
     1501    } else {
     1502                    $sanitized['period'] = 'weekly';
     1503    }
     1504
     1505                // Day of week for weekly schedule.
     1506                $sanitized['day'] = 'monday';
     1507    if ( isset( $input['day'] ) ) {
     1508                    $day              = strtolower( $input['day'] );
     1509                    $valid            = array( 'sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday' );
     1510                    $sanitized['day'] = in_array( $day, $valid, true ) ? $day : 'monday';
     1511    }
     1512
     1513        // Time (hour and minute).
     1514        $sanitized['hour']   = 0;
     1515        $sanitized['minute'] = 0;
     1516    if ( isset( $input['hour'] ) ) {
     1517                    $hour              = (int) $input['hour'];
     1518                    $sanitized['hour'] = max( 0, min( 23, $hour ) );
     1519    }
     1520    if ( isset( $input['minute'] ) ) {
     1521                    $minute              = (int) $input['minute'];
     1522                    $sanitized['minute'] = max( 0, min( 59, $minute ) );
     1523    }
     1524
     1525        // Cache expiration.
     1526        $sanitized['cache'] = 12;
     1527    if ( isset( $input['cache'] ) ) {
     1528                    $cache              = (int) $input['cache'];
     1529                    $sanitized['cache'] = in_array( $cache, array( 1, 6, 12, 24 ), true ) ? $cache : 12;
     1530    }
     1531
     1532        // Notification methods.
     1533            $sanitized['notify'] = array(
     1534                'email' => 'n',
     1535                'slack' => 'n',
     1536                'teams' => 'n',
     1537            );
     1538            if ( isset( $input['notify'] ) && is_array( $input['notify'] ) ) {
     1539                            $notify_input        = array_map( 'sanitize_text_field', (array) wp_unslash( $input['notify'] ) );
     1540                            $sanitized['notify'] = wpvulnerability_normalize_notify_settings( $notify_input );
     1541            }
     1542
     1543            // Webhooks.
     1544            if ( isset( $input['slack_webhook'] ) ) {
     1545                $sanitized['slack_webhook'] = esc_url_raw( trim( $input['slack_webhook'] ) );
     1546            }
     1547
     1548            if ( isset( $input['teams_webhook'] ) ) {
     1549                $sanitized['teams_webhook'] = esc_url_raw( trim( $input['teams_webhook'] ) );
     1550            }
     1551
     1552            return $sanitized;
     1553}
     1554
     1555/**
     1556 * Sanitizes the messages generated by the plugin (simple messages).
     1557 *
     1558 * @param array $input Input values.
     1559 * @return array Sanitized values.
     1560 */
     1561function wpvulnerability_sanitize_messages( $input ) {
    12691562    $sanitized = array();
    12701563
    1271     // Emails (lista separada por comas).
    1272     if ( isset( $input['emails'] ) ) {
    1273         $emails_raw       = explode( ',', $input['emails'] );
    1274         $sanitized_emails = array();
    1275 
    1276         foreach ( $emails_raw as $email ) {
    1277             $email = sanitize_email( trim( $email ) );
    1278             if ( is_email( $email ) ) {
    1279                 $sanitized_emails[] = $email;
    1280             }
    1281         }
    1282 
    1283         $sanitized['emails'] = implode( ',', $sanitized_emails );
    1284     }
    1285 
    1286     // Period (daily, weekly, monthly).
    1287     $allowed_periods = array( 'daily', 'weekly', 'monthly' );
    1288     if ( isset( $input['period'] ) && in_array( $input['period'], $allowed_periods, true ) ) {
    1289         $sanitized['period'] = $input['period'];
    1290     } else {
    1291         $sanitized['period'] = 'weekly';
     1564    foreach ( $input as $key => $message ) {
     1565        $sanitized[ sanitize_key( $key ) ] = sanitize_text_field( $message );
    12921566    }
    12931567
     
    12961570
    12971571/**
    1298  * Sanitiza los mensajes generados por el plugin (mensajes simples).
    1299  *
    1300  * @param array $input Valores de entrada.
    1301  * @return array Valores sanitizados.
    1302  */
    1303 function wpvulnerability_sanitize_messages( $input ) {
    1304     $sanitized = array();
    1305 
    1306     foreach ( $input as $key => $message ) {
    1307         $sanitized[ sanitize_key( $key ) ] = sanitize_text_field( $message );
    1308     }
    1309 
    1310     return $sanitized;
    1311 }
    1312 
    1313 /**
    1314  * Sanitiza estrictamente las opciones de análisis (booleanos).
    1315  *
    1316  * @param array $input Valores de entrada.
    1317  * @return array Valores sanitizados.
     1572 * Strictly sanitizes the analysis options (booleans).
     1573 *
     1574 * @param array $input Input values.
     1575 * @return array Sanitized values.
    13181576 */
    13191577function wpvulnerability_sanitize_analyze( $input ) {
     
    13381596    foreach ( $components as $component ) {
    13391597        $sanitized[ $component ] = isset( $input[ $component ] ) ? (bool) $input[ $component ] : false;
     1598
     1599        $constant = 'WPVULNERABILITY_HIDE_' . strtoupper( $component );
     1600        if ( defined( $constant ) && constant( $constant ) ) {
     1601            $sanitized[ $component ] = true;
     1602        }
    13401603    }
    13411604
     
    13771640
    13781641    // Add a section to the settings page.
    1379     add_settings_section(
    1380         'admin_wpvulnerability_settings',
    1381         __( 'Receive notifications in your email', 'wpvulnerability' ),
    1382         'wpvulnerability_admin_section_notifications',
    1383         'wpvulnerability-config'
    1384     );
    1385 
    1386     // Add a field to the settings page for the email addresses.
    1387     add_settings_field(
    1388         'wpvulnerability_emails',
    1389         __( 'Email addresses to notify (separated by commas)', 'wpvulnerability' ),
    1390         'wpvulnerability_admin_emails_callback',
    1391         'wpvulnerability-config',
    1392         'admin_wpvulnerability_settings'
    1393     );
    1394 
    1395     // Add a field to the settings page for the notification period.
    1396     add_settings_field(
    1397         'wpvulnerability_period',
    1398         __( 'How often you want to receive notifications', 'wpvulnerability' ),
    1399         'wpvulnerability_admin_period_callback',
    1400         'wpvulnerability-config',
    1401         'admin_wpvulnerability_settings'
    1402     );
     1642        add_settings_section(
     1643            'admin_wpvulnerability_settings',
     1644            __( 'Receive vulnerability notifications', 'wpvulnerability' ),
     1645            'wpvulnerability_admin_section_notifications',
     1646            'wpvulnerability-config'
     1647        );
     1648
     1649        // Add a field to the settings page for the cache expiration time.
     1650        add_settings_field(
     1651            'wpvulnerability_cache',
     1652            __( 'Cache expiration time', 'wpvulnerability' ),
     1653            'wpvulnerability_admin_cache_callback',
     1654            'wpvulnerability-config',
     1655            'admin_wpvulnerability_settings'
     1656        );
     1657
     1658        // Add a field to the settings page for the notification period.
     1659        add_settings_field(
     1660            'wpvulnerability_period',
     1661            __( 'How often you want to receive notifications', 'wpvulnerability' ),
     1662            'wpvulnerability_admin_period_callback',
     1663            'wpvulnerability-config',
     1664            'admin_wpvulnerability_settings'
     1665        );
     1666
     1667                // Add a field for notification methods.
     1668                add_settings_field(
     1669                    'wpvulnerability_notify',
     1670                    __( 'Where do you want to receive notifications?', 'wpvulnerability' ),
     1671                    'wpvulnerability_admin_notify_callback',
     1672                    'wpvulnerability-config',
     1673                    'admin_wpvulnerability_settings'
     1674                );
     1675
     1676        // Add a field to the settings page for the email addresses.
     1677        add_settings_field(
     1678            'wpvulnerability_emails',
     1679            __( 'Email addresses to notify (separated by commas)', 'wpvulnerability' ),
     1680            'wpvulnerability_admin_emails_callback',
     1681            'wpvulnerability-config',
     1682            'admin_wpvulnerability_settings'
     1683        );
     1684
     1685                // Add a field for the Slack webhook.
     1686                add_settings_field(
     1687                    'wpvulnerability_slack_webhook',
     1688                    __( 'Slack webhook URL', 'wpvulnerability' ),
     1689                    'wpvulnerability_admin_slack_callback',
     1690                    'wpvulnerability-config',
     1691                    'admin_wpvulnerability_settings'
     1692                );
     1693
     1694                // Add a field for the Teams webhook.
     1695                add_settings_field(
     1696                    'wpvulnerability_teams_webhook',
     1697                    __( 'Teams webhook URL', 'wpvulnerability' ),
     1698                    'wpvulnerability_admin_teams_callback',
     1699                    'wpvulnerability-config',
     1700                    'admin_wpvulnerability_settings'
     1701                );
    14031702
    14041703    // Register the plugin settings to be saved in the database.
  • wpvulnerability/trunk/wpvulnerability-api.php

    r3162498 r3362936  
    449449
    450450    // Check if application passwords are available.
    451     if ( wp_is_application_passwords_available() ) {
     451    if ( function_exists( 'wp_is_application_passwords_available' ) && wp_is_application_passwords_available() ) {
    452452        $authorization_header = $request->get_header( 'authorization' );
    453453
  • wpvulnerability/trunk/wpvulnerability-core.php

    r3161036 r3362936  
    144144    // Cache the vulnerability data and the timestamp for cache expiration.
    145145    if ( is_multisite() ) {
    146         update_site_option( 'wpvulnerability-core', wp_json_encode( $core ) );
    147         update_site_option( 'wpvulnerability-core-vulnerable', wp_json_encode( number_format( $wpvulnerability_core_vulnerable, 0, '.', '' ) ) );
    148         update_site_option( 'wpvulnerability-core-cache', wp_json_encode( number_format( time() + ( 3600 * WPVULNERABILITY_CACHE_HOURS ), 0, '.', '' ) ) );
     146                update_site_option( 'wpvulnerability-core', wp_json_encode( $core ) );
     147                update_site_option( 'wpvulnerability-core-vulnerable', wp_json_encode( number_format( $wpvulnerability_core_vulnerable, 0, '.', '' ) ) );
     148                update_site_option( 'wpvulnerability-core-cache', wp_json_encode( number_format( time() + ( 3600 * wpvulnerability_cache_hours() ), 0, '.', '' ) ) );
    149149    } else {
    150         update_option( 'wpvulnerability-core', wp_json_encode( $core ) );
    151         update_option( 'wpvulnerability-core-vulnerable', wp_json_encode( number_format( $wpvulnerability_core_vulnerable, 0, '.', '' ) ) );
    152         update_option( 'wpvulnerability-core-cache', wp_json_encode( number_format( time() + ( 3600 * WPVULNERABILITY_CACHE_HOURS ), 0, '.', '' ) ) );
     150                update_option( 'wpvulnerability-core', wp_json_encode( $core ) );
     151                update_option( 'wpvulnerability-core-vulnerable', wp_json_encode( number_format( $wpvulnerability_core_vulnerable, 0, '.', '' ) ) );
     152                update_option( 'wpvulnerability-core-cache', wp_json_encode( number_format( time() + ( 3600 * wpvulnerability_cache_hours() ), 0, '.', '' ) ) );
    153153    }
    154154
  • wpvulnerability/trunk/wpvulnerability-general.php

    r3175415 r3362936  
    8080    // If the command execution failed or returned null, shell_exec is not working.
    8181    return null !== $test; // Return true if the command was successful.
     82}
     83
     84/**
     85 * Retrieve the cache expiration in hours.
     86 *
     87 * The value can be defined via the WPVULNERABILITY_CACHE_HOURS constant,
     88 * configured in the plugin settings, or falls back to 12 hours.
     89 *
     90 * @since 4.1.0
     91 *
     92 * @return int Cache duration in hours.
     93 */
     94function wpvulnerability_cache_hours() {
     95                $default = 12;
     96
     97    if ( defined( 'WPVULNERABILITY_CACHE_HOURS' ) && WPVULNERABILITY_CACHE_HOURS !== $default ) {
     98                    return (int) WPVULNERABILITY_CACHE_HOURS;
     99    }
     100
     101                $settings = is_multisite() ? get_site_option( 'wpvulnerability-config', array() ) : get_option( 'wpvulnerability-config', array() );
     102    if ( isset( $settings['cache'] ) ) {
     103                    $cache = (int) $settings['cache'];
     104        if ( in_array( $cache, array( 1, 6, 12, 24 ), true ) ) {
     105                        return $cache;
     106        }
     107    }
     108
     109                return $default;
     110}
     111
     112/**
     113 * Normalize various truthy and falsy values into the expected 'y' or 'n' format.
     114 *
     115 * This helper ensures that configuration options stored as booleans or integers
     116 * in previous plugin versions are converted into the new string-based format.
     117 *
     118 * @since 4.1.1
     119 *
     120 * @param mixed $value Value to normalize.
     121 *
     122 * @return string Returns 'y' when enabled, 'n' otherwise.
     123 */
     124function wpvulnerability_normalize_yes_no( $value ) {
     125    if ( is_string( $value ) ) {
     126            $value = strtolower( trim( $value ) );
     127    }
     128
     129        $truthy = array( 'y', 'yes', '1', 1, true, 'true', 'on' );
     130
     131        return in_array( $value, $truthy, true ) ? 'y' : 'n';
     132}
     133
     134/**
     135 * Determine if a stored yes/no value should be treated as enabled.
     136 *
     137 * @since 4.1.1
     138 *
     139 * @param mixed $value Value to evaluate.
     140 *
     141 * @return bool True when enabled, false otherwise.
     142 */
     143function wpvulnerability_is_yes( $value ) {
     144        return 'y' === wpvulnerability_normalize_yes_no( $value );
     145}
     146
     147/**
     148 * Normalize the notification configuration array.
     149 *
     150 * @since 4.1.1
     151 *
     152 * @param mixed $notify Notification configuration values.
     153 *
     154 * @return array Normalized notification configuration containing 'email', 'slack', and 'teams'.
     155 */
     156function wpvulnerability_normalize_notify_settings( $notify ) {
     157        $defaults   = array(
     158            'email' => 'n',
     159            'slack' => 'n',
     160            'teams' => 'n',
     161        );
     162        $normalized = array();
     163
     164        if ( is_array( $notify ) ) {
     165            foreach ( $notify as $channel => $value ) {
     166                    $normalized[ $channel ] = wpvulnerability_normalize_yes_no( $value );
     167            }
     168        }
     169
     170        return array_merge( $defaults, $normalized );
    82171}
    83172
     
    690779            // Cache the response data.
    691780            if ( is_multisite() ) {
    692                 set_site_transient( $key, $body, HOUR_IN_SECONDS * WPVULNERABILITY_CACHE_HOURS );
     781                    set_site_transient( $key, $body, HOUR_IN_SECONDS * wpvulnerability_cache_hours() );
    693782            } else {
    694                 set_transient( $key, $body, HOUR_IN_SECONDS * WPVULNERABILITY_CACHE_HOURS );
     783                    set_transient( $key, $body, HOUR_IN_SECONDS * wpvulnerability_cache_hours() );
    695784            }
    696785
     
    9391028            // Cache the response data.
    9401029            if ( is_multisite() ) {
    941                 set_site_transient( $key, $body, HOUR_IN_SECONDS * 24 );
     1030                            set_site_transient( $key, $body, HOUR_IN_SECONDS * wpvulnerability_cache_hours() );
    9421031            } else {
    943                 set_transient( $key, $body, HOUR_IN_SECONDS * 24 );
     1032                            set_transient( $key, $body, HOUR_IN_SECONDS * wpvulnerability_cache_hours() );
    9441033            }
    9451034            $vulnerability = $body; // Use the fresh data.
     
    10621151
    10631152    // Cache the statistics data and the timestamp for cache expiration.
    1064     $encoded_statistics = wp_json_encode( $statistics );
    1065     $cache_expiration   = number_format( time() + ( 3600 * WPVULNERABILITY_CACHE_HOURS ), 0, '.', '' );
     1153    $encoded_statistics   = wp_json_encode( $statistics );
     1154        $cache_expiration = number_format( time() + ( 3600 * wpvulnerability_cache_hours() ), 0, '.', '' );
    10661155
    10671156    if ( is_multisite() ) {
     
    11101199            if ( $cache ) {
    11111200                if ( is_multisite() ) {
    1112                     set_site_transient( $key, $body, HOUR_IN_SECONDS * WPVULNERABILITY_CACHE_HOURS );
     1201                        set_site_transient( $key, $body, HOUR_IN_SECONDS * wpvulnerability_cache_hours() );
    11131202                } else {
    1114                     set_transient( $key, $body, HOUR_IN_SECONDS * WPVULNERABILITY_CACHE_HOURS );
     1203                        set_transient( $key, $body, HOUR_IN_SECONDS * wpvulnerability_cache_hours() );
    11151204                }
    11161205            }
  • wpvulnerability/trunk/wpvulnerability-notifications.php

    r3161036 r3362936  
    6161// Hook the function to the 'cron_schedules' filter to add the custom schedule.
    6262add_filter( 'cron_schedules', 'wpvulnerability_add_every_day' );
     63
     64/**
     65 * Handles disabling email notifications via a front-end link.
     66 *
     67 * When the `wpvulnerability_disable_email` query parameter is present and the nonce is
     68 * valid, this function disables email notifications for the plugin and outputs
     69 * a confirmation message.
     70 *
     71 * @since 2.0.0
     72 */
     73function wpvulnerability_disable_email_handler() {
     74    if ( isset( $_GET['wpvulnerability_disable_email'] ) ) {
     75            $nonce = isset( $_GET['nonce'] ) ? sanitize_text_field( wp_unslash( $_GET['nonce'] ) ) : '';
     76
     77        if ( ! wp_verify_nonce( $nonce, 'wpvulnerability-disable-email' ) ) {
     78                wp_die( esc_html__( 'Invalid request.', 'wpvulnerability' ) );
     79        }
     80
     81            $settings = is_multisite() ? get_site_option( 'wpvulnerability-config' ) : get_option( 'wpvulnerability-config' );
     82
     83        if ( ! is_array( $settings ) ) {
     84                $settings = array();
     85        }
     86
     87                        $settings['notify']          = wpvulnerability_normalize_notify_settings( isset( $settings['notify'] ) ? $settings['notify'] : array() );
     88                        $settings['notify']['email'] = 'n';
     89
     90        if ( is_multisite() ) {
     91                update_site_option( 'wpvulnerability-config', $settings );
     92        } else {
     93                update_option( 'wpvulnerability-config', $settings );
     94        }
     95            wp_die(
     96                esc_html__( 'You have unsubscribed from WPVulnerability notifications.', 'wpvulnerability' ),
     97                esc_html__( 'WPVulnerability', 'wpvulnerability' ),
     98                array( 'response' => 200 )
     99            );
     100    }
     101}
    63102
    64103/**
     
    141180    $message .= '                                       </tr>' . "\n";
    142181
     182    $nonce = wp_create_nonce( 'wpvulnerability-disable-email' );
     183    if ( is_multisite() ) {
     184        $disable_url = network_home_url( '?wpvulnerability_disable_email=1&nonce=' . $nonce );
     185        $manage_url  = network_admin_url( 'settings.php?page=wpvulnerability-options' );
     186    } else {
     187        $disable_url = home_url( '?wpvulnerability_disable_email=1&nonce=' . $nonce );
     188        $manage_url  = admin_url( 'options-general.php?page=wpvulnerability-options' );
     189    }
     190    $message .= '                                     <tr style="box-sizing: border-box; margin: 0;">' . "\n";
     191    $message .= '                                         <td class="aligncenter content-block" style="box-sizing: border-box; vertical-align: top; color: #999; text-align: center; margin: 0; padding: 0 0 20px;" align="center" valign="top"><a href="' . esc_url( $disable_url ) . '">' . esc_html__( 'Disable email notifications', 'wpvulnerability' ) . '</a> | <a href="' . esc_url( $manage_url ) . '">' . esc_html__( 'Manage notifications', 'wpvulnerability' ) . '</a></td>' . "\n";
     192    $message .= '                                       </tr>' . "\n";
     193
    143194    // Add the site URL in the footer based on the multisite configuration.
    144195    if ( is_multisite() ) {
     
    179230 */
    180231function wpvulnerability_execute_notification( $forced = false ) {
    181     $email_content            = '';
    182     $wpvulnerability_settings = is_multisite() ? get_site_option( 'wpvulnerability-config' ) : get_option( 'wpvulnerability-config' );
    183 
    184     // Check if forced email sending is not required.
    185     if ( ! $forced && ( empty( $wpvulnerability_settings['emails'] ) || empty( $wpvulnerability_settings['period'] ) ) ) {
    186         return false;
     232                $email_content                      = '';
     233                $wpvulnerability_settings           = is_multisite() ? get_site_option( 'wpvulnerability-config' ) : get_option( 'wpvulnerability-config' );
     234                $wpvulnerability_settings           = is_array( $wpvulnerability_settings ) ? $wpvulnerability_settings : array();
     235                $wpvulnerability_settings['notify'] = wpvulnerability_normalize_notify_settings( isset( $wpvulnerability_settings['notify'] ) ? $wpvulnerability_settings['notify'] : array() );
     236
     237        // Check if forced sending is not required and no channels configured.
     238    if ( ! $forced ) {
     239            $available = false;
     240
     241        if ( wpvulnerability_is_yes( $wpvulnerability_settings['notify']['email'] ) && ! empty( $wpvulnerability_settings['emails'] ) ) {
     242                        $available = true;
     243        }
     244
     245        if ( wpvulnerability_is_yes( $wpvulnerability_settings['notify']['slack'] ) && ! empty( $wpvulnerability_settings['slack_webhook'] ) ) {
     246                                $available = true;
     247        }
     248
     249        if ( wpvulnerability_is_yes( $wpvulnerability_settings['notify']['teams'] ) && ! empty( $wpvulnerability_settings['teams_webhook'] ) ) {
     250                                $available = true;
     251        }
     252
     253        if ( ! $available || empty( $wpvulnerability_settings['period'] ) ) {
     254                return false;
     255        }
    187256    }
    188257
     
    326395    );
    327396
    328     $email_prepared = wpvulnerability_email_prepare( esc_html__( 'Vulnerability found', 'wpvulnerability' ), $email_content );
    329 
    330     // Prepare email headers.
    331     $email_headers   = array();
    332     $email_headers[] = 'From: WPVulnerability <' . $from_email . '>';
    333     $email_headers[] = 'Content-Type: text/html; charset=UTF-8';
    334 
    335     if ( $forced && ( empty( $wpvulnerability_settings['emails'] ) ) ) {
    336         // Determine the recipient email.
    337         $wpvulnerability_settings['emails'][] = $admin_email;
    338     }
    339 
    340     // Send the email.
    341     $wpmail = wp_mail( $wpvulnerability_settings['emails'], $email_subject, $email_prepared, $email_headers );
    342 
    343     return $wpmail;
     397        $email_prepared = wpvulnerability_email_prepare( esc_html__( 'Vulnerability found', 'wpvulnerability' ), $email_content );
     398
     399        // Prepare email headers.
     400        $email_headers   = array();
     401        $email_headers[] = 'From: WPVulnerability <' . $from_email . '>';
     402        $email_headers[] = 'Content-Type: text/html; charset=UTF-8';
     403
     404        $text_message = $email_subject . "\n\n" . wp_strip_all_tags( $email_content );
     405
     406    if ( $forced && wpvulnerability_is_yes( $wpvulnerability_settings['notify']['email'] ) && empty( $wpvulnerability_settings['emails'] ) ) {
     407                        // Determine the recipient email.
     408                        $wpvulnerability_settings['emails'][] = $admin_email;
     409    }
     410
     411        $sent = false;
     412
     413    if ( wpvulnerability_is_yes( $wpvulnerability_settings['notify']['email'] ) && ! empty( $wpvulnerability_settings['emails'] ) ) {
     414                        $wpmail = wp_mail( $wpvulnerability_settings['emails'], $email_subject, $email_prepared, $email_headers );
     415                        $sent   = $sent || $wpmail;
     416    }
     417
     418    if ( wpvulnerability_is_yes( $wpvulnerability_settings['notify']['slack'] ) && ! empty( $wpvulnerability_settings['slack_webhook'] ) ) {
     419                        wp_remote_post(
     420                            $wpvulnerability_settings['slack_webhook'],
     421                            array(
     422                                'body'    => wp_json_encode( array( 'text' => $text_message ) ),
     423                                'headers' => array( 'Content-Type' => 'application/json' ),
     424                            )
     425                        );
     426                        $sent = true;
     427    }
     428
     429    if ( wpvulnerability_is_yes( $wpvulnerability_settings['notify']['teams'] ) && ! empty( $wpvulnerability_settings['teams_webhook'] ) ) {
     430                        wp_remote_post(
     431                            $wpvulnerability_settings['teams_webhook'],
     432                            array(
     433                                'body'    => wp_json_encode( array( 'text' => $text_message ) ),
     434                                'headers' => array( 'Content-Type' => 'application/json' ),
     435                            )
     436                        );
     437                        $sent = true;
     438    }
     439
     440        return $sent;
    344441}
  • wpvulnerability/trunk/wpvulnerability-plugins.php

    r3161036 r3362936  
    99
    1010defined( 'ABSPATH' ) || die( 'No script kiddies please!' );
     11
     12/**
     13 * Enqueues the admin JavaScript for plugin update interactions.
     14 *
     15 * @since 4.1.0
     16 *
     17 * @param string $hook Current admin page hook.
     18 *
     19 * @return void
     20 */
     21function wpvulnerability_plugins_admin_enqueue_scripts( $hook ) {
     22    if ( 'plugins.php' !== $hook && 'plugins-network' !== $hook ) {
     23        return;
     24    }
     25
     26    wp_enqueue_script(
     27        'wpvulnerability-admin-js',
     28        WPVULNERABILITY_PLUGIN_URL . 'assets/admin.js',
     29        array( 'jquery' ),
     30        WPVULNERABILITY_PLUGIN_VERSION,
     31        true
     32    );
     33}
     34add_action( 'admin_enqueue_scripts', 'wpvulnerability_plugins_admin_enqueue_scripts' );
    1135
    1236/**
     
    255279    // Update site options for multisite installations.
    256280    if ( is_multisite() ) {
    257         update_site_option( 'wpvulnerability-plugins', wp_json_encode( $plugins ) );
    258         update_site_option( 'wpvulnerability-plugins-vulnerable', wp_json_encode( number_format( $wpvulnerability_plugins_vulnerable, 0, '.', '' ) ) );
    259         update_site_option( 'wpvulnerability-plugins-cache', wp_json_encode( number_format( time() + ( 3600 * WPVULNERABILITY_CACHE_HOURS ), 0, '.', '' ) ) );
     281                update_site_option( 'wpvulnerability-plugins', wp_json_encode( $plugins ) );
     282                update_site_option( 'wpvulnerability-plugins-vulnerable', wp_json_encode( number_format( $wpvulnerability_plugins_vulnerable, 0, '.', '' ) ) );
     283                update_site_option( 'wpvulnerability-plugins-cache', wp_json_encode( number_format( time() + ( 3600 * wpvulnerability_cache_hours() ), 0, '.', '' ) ) );
    260284    } else {
    261285        // Update options for single site installations.
    262         update_option( 'wpvulnerability-plugins', wp_json_encode( $plugins ) );
    263         update_option( 'wpvulnerability-plugins-vulnerable', wp_json_encode( number_format( $wpvulnerability_plugins_vulnerable, 0, '.', '' ) ) );
    264         update_option( 'wpvulnerability-plugins-cache', wp_json_encode( number_format( time() + ( 3600 * WPVULNERABILITY_CACHE_HOURS ), 0, '.', '' ) ) );
     286                update_option( 'wpvulnerability-plugins', wp_json_encode( $plugins ) );
     287                update_option( 'wpvulnerability-plugins-vulnerable', wp_json_encode( number_format( $wpvulnerability_plugins_vulnerable, 0, '.', '' ) ) );
     288                update_option( 'wpvulnerability-plugins-cache', wp_json_encode( number_format( time() + ( 3600 * wpvulnerability_cache_hours() ), 0, '.', '' ) ) );
    265289    }
    266290
     
    308332        // Update site options for multisite installations.
    309333        if ( is_multisite() ) {
    310             update_site_option( 'wpvulnerability-plugins-data', wp_json_encode( $pluginsdata ) );
    311             update_site_option( 'wpvulnerability-plugins-cache-data', wp_json_encode( number_format( time() + ( 3600 * WPVULNERABILITY_CACHE_HOURS ), 0, '.', '' ) ) );
    312             update_site_option( 'wpvulnerability-plugins-cache', wp_json_encode( number_format( time() + ( 3600 * WPVULNERABILITY_CACHE_HOURS ), 0, '.', '' ) ) );
     334                        update_site_option( 'wpvulnerability-plugins-data', wp_json_encode( $pluginsdata ) );
     335                        update_site_option( 'wpvulnerability-plugins-cache-data', wp_json_encode( number_format( time() + ( 3600 * wpvulnerability_cache_hours() ), 0, '.', '' ) ) );
     336                        update_site_option( 'wpvulnerability-plugins-cache', wp_json_encode( number_format( time() + ( 3600 * wpvulnerability_cache_hours() ), 0, '.', '' ) ) );
    313337        } else {
    314338            // Update options for single site installations.
    315             update_option( 'wpvulnerability-plugins-data', wp_json_encode( $pluginsdata ) );
    316             update_option( 'wpvulnerability-plugins-cache-data', wp_json_encode( number_format( time() + ( 3600 * WPVULNERABILITY_CACHE_HOURS ), 0, '.', '' ) ) );
    317             update_option( 'wpvulnerability-plugins-cache', wp_json_encode( number_format( time() + ( 3600 * WPVULNERABILITY_CACHE_HOURS ), 0, '.', '' ) ) );
     339                        update_option( 'wpvulnerability-plugins-data', wp_json_encode( $pluginsdata ) );
     340                        update_option( 'wpvulnerability-plugins-cache-data', wp_json_encode( number_format( time() + ( 3600 * wpvulnerability_cache_hours() ), 0, '.', '' ) ) );
     341                        update_option( 'wpvulnerability-plugins-cache', wp_json_encode( number_format( time() + ( 3600 * wpvulnerability_cache_hours() ), 0, '.', '' ) ) );
    318342        }
    319343    }
  • wpvulnerability/trunk/wpvulnerability-run.php

    r3161036 r3362936  
    158158    // Add wpvulnerability-config option if it does not exist.
    159159    if ( ! $config_key( 'wpvulnerability-config' ) ) {
    160         $default_config = array(
    161             'emails' => get_bloginfo( 'admin_email' ),
    162             'period' => 'weekly',
    163         );
    164         $add_option( 'wpvulnerability-config', $default_config );
     160                                                                $default_config = array(
     161                                                                    'emails'        => get_bloginfo( 'admin_email' ),
     162                                                                    'period'        => 'weekly',
     163                                                                    'day'           => 'monday',
     164                                                                    'hour'          => 0,
     165                                                                    'minute'        => 0,
     166                                                                    'cache'         => 12,
     167                                                                    'notify'        => array(
     168                                                                        'email' => 'y',
     169                                                                        'slack' => 'n',
     170                                                                        'teams' => 'n',
     171                                                                    ),
     172                                                                    'slack_webhook' => '',
     173                                                                    'teams_webhook' => '',
     174                                                                );
     175                                                                $add_option( 'wpvulnerability-config', $default_config );
    165176    }
    166177
     
    235246            'sqlite'      => 0,
    236247        );
    237         $current_option  = $config_key( 'wpvulnerability-analyze' );
     248
     249        foreach ( array_keys( $default_analyze ) as $component ) {
     250            $constant = 'WPVULNERABILITY_HIDE_' . strtoupper( $component );
     251            if ( defined( $constant ) && constant( $constant ) ) {
     252                $default_analyze[ $component ] = 1;
     253            }
     254        }
     255        $current_option = $config_key( 'wpvulnerability-analyze' );
    238256
    239257        if ( false === $current_option ) {
     
    484502
    485503    if ( in_array( $type, $valid_types, true ) ) {
     504        $constant = 'WPVULNERABILITY_HIDE_' . strtoupper( $type );
     505        if ( defined( $constant ) && constant( $constant ) ) {
     506            return false;
     507        }
     508
    486509        return ! ( isset( $wpvulnerability_analyze[ $type ] ) && (int) $wpvulnerability_analyze[ $type ] );
    487510    }
  • wpvulnerability/trunk/wpvulnerability-schedule.php

    r3161036 r3362936  
    2929
    3030/**
    31  * Schedule vulnerability notification and execute notification function.
     31 * Calculate the next notification timestamp based on plugin settings.
    3232 *
    33  * @since 2.0.0
     33 * @since 4.1.1
     34 *
     35 * @param array $config Plugin configuration.
     36 * @return int Timestamp for next notification.
     37 */
     38function wpvulnerability_get_next_notification_timestamp( $config ) {
     39    $hour   = isset( $config['hour'] ) ? max( 0, min( 23, (int) $config['hour'] ) ) : 0;
     40    $minute = isset( $config['minute'] ) ? max( 0, min( 59, (int) $config['minute'] ) ) : 0;
     41
     42    if ( function_exists( 'wp_timezone' ) ) {
     43        $timezone = wp_timezone();
     44    } else {
     45        $timezone_string = get_option( 'timezone_string' );
     46        if ( $timezone_string ) {
     47            $timezone = new DateTimeZone( $timezone_string );
     48        } else {
     49            $offset    = (float) get_option( 'gmt_offset' );
     50            $hours     = (int) $offset;
     51            $minutes   = $offset - $hours;
     52            $sign      = ( $offset < 0 ) ? '-' : '+';
     53            $abs_hour  = abs( $hours );
     54            $abs_mins  = (int) round( abs( $minutes ) * 60 );
     55            $tz_offset = sprintf( '%s%02d:%02d', $sign, $abs_hour, $abs_mins );
     56            $timezone  = new DateTimeZone( $tz_offset );
     57        }
     58    }
     59
     60    $current_time   = new DateTime( 'now', $timezone );
     61    $scheduled_time = new DateTime( 'now', $timezone );
     62    $scheduled_time->setTime( $hour, $minute, 0 );
     63
     64    if ( isset( $config['period'] ) && 'weekly' === $config['period'] ) {
     65        $day       = isset( $config['day'] ) ? strtolower( $config['day'] ) : 'monday';
     66        $weekdays  = array( 'sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday' );
     67        $day_index = array_search( $day, $weekdays, true );
     68        if ( false === $day_index ) {
     69            $day_index = 1; // Monday.
     70        }
     71        while ( (int) $scheduled_time->format( 'w' ) !== $day_index || $scheduled_time->getTimestamp() <= $current_time->getTimestamp() ) {
     72            $scheduled_time->modify( '+1 day' );
     73        }
     74    } elseif ( $scheduled_time->getTimestamp() <= $current_time->getTimestamp() ) {
     75        $scheduled_time->modify( '+1 day' );
     76    }
     77
     78    return (int) $scheduled_time->getTimestamp();
     79}
     80
     81/**
     82 * Schedule vulnerability notifications.
     83 *
     84 * @since 4.1.1
     85 *
     86 * @param array $config Plugin configuration.
    3487 *
    3588 * @return void
    3689 */
    37 $wpvulnerability_s = array();
    38 if ( is_multisite() ) {
    39     $wpvulnerability_s = get_site_option( 'wpvulnerability-config' );
    40 } else {
    41     $wpvulnerability_s = get_option( 'wpvulnerability-config' );
     90function wpvulnerability_schedule_notification_event( $config ) {
     91        wp_clear_scheduled_hook( 'wpvulnerability_notification' );
     92    if ( ! isset( $config['period'] ) || 'never' === $config['period'] ) {
     93            return;
     94    }
     95
     96    if ( ! is_multisite() || ( is_multisite() && is_main_site() ) ) {
     97            $timestamp = wpvulnerability_get_next_notification_timestamp( $config );
     98            wp_schedule_event( $timestamp, $config['period'], 'wpvulnerability_notification' );
     99    }
    42100}
    43101
    44 // Check if notification period is set.
    45 if ( isset( $wpvulnerability_s['period'] ) ) {
    46 
    47     // Check if notification is not already scheduled.
    48     if ( ! wp_next_scheduled( 'wpvulnerability_notification' ) ) {
    49 
    50         // Schedule notification.
    51         if ( ! is_multisite() || ( is_multisite() && is_main_site() ) ) {
    52             wp_schedule_event( time(), $wpvulnerability_s['period'], 'wpvulnerability_notification' );
    53         }
    54     }
    55 
    56     // Add action to execute notification function.
    57     add_action( 'wpvulnerability_notification', 'wpvulnerability_execute_notification' );
    58 }
    59 
    60 // Clean up.
     102$wpvulnerability_s = is_multisite() ? get_site_option( 'wpvulnerability-config' ) : get_option( 'wpvulnerability-config' );
     103wpvulnerability_schedule_notification_event( $wpvulnerability_s );
     104add_action( 'wpvulnerability_notification', 'wpvulnerability_execute_notification' );
    61105unset( $wpvulnerability_s );
  • wpvulnerability/trunk/wpvulnerability-sitehealth.php

    r3161036 r3362936  
    239239        );
    240240
    241         // Add specific action links if necessary.
    242         if ( 'php' === $software ) {
    243             $result['actions'] .= sprintf(
    244                 '<p><a href="%s">%s</a></p>',
    245                 esc_url( wp_get_update_php_url() ),
    246                 __( 'How to update PHP', 'wpvulnerability' )
    247             );
     241                // Add specific action links if necessary.
     242        if ( 'php' === $software && function_exists( 'wp_get_update_php_url' ) && class_exists( 'WP_Site_Health' ) ) {
     243                $result['actions'] .= sprintf(
     244                    '<p><a href="%s">%s</a></p>',
     245                    esc_url( wp_get_update_php_url() ),
     246                    __( 'How to update PHP', 'wpvulnerability' )
     247                );
    248248        }
    249249    }
  • wpvulnerability/trunk/wpvulnerability-software.php

    r3161036 r3362936  
    153153    // Cache the vulnerability data and the timestamp for cache expiration.
    154154    if ( is_multisite() ) {
    155         update_site_option( 'wpvulnerability-' . $software, wp_json_encode( $data ) );
    156         update_site_option( 'wpvulnerability-' . $software . '-vulnerable', wp_json_encode( number_format( $wpvulnerability_software_vulnerable, 0, '.', '' ) ) );
    157         update_site_option( 'wpvulnerability-' . $software . '-cache', wp_json_encode( number_format( time() + ( 3600 * WPVULNERABILITY_CACHE_HOURS ), 0, '.', '' ) ) );
     155                update_site_option( 'wpvulnerability-' . $software, wp_json_encode( $data ) );
     156                update_site_option( 'wpvulnerability-' . $software . '-vulnerable', wp_json_encode( number_format( $wpvulnerability_software_vulnerable, 0, '.', '' ) ) );
     157                update_site_option( 'wpvulnerability-' . $software . '-cache', wp_json_encode( number_format( time() + ( 3600 * wpvulnerability_cache_hours() ), 0, '.', '' ) ) );
    158158    } else {
    159         update_option( 'wpvulnerability-' . $software, wp_json_encode( $data ) );
    160         update_option( 'wpvulnerability-' . $software . '-vulnerable', wp_json_encode( number_format( $wpvulnerability_software_vulnerable, 0, '.', '' ) ) );
    161         update_option( 'wpvulnerability-' . $software . '-cache', wp_json_encode( number_format( time() + ( 3600 * WPVULNERABILITY_CACHE_HOURS ), 0, '.', '' ) ) );
     159                update_option( 'wpvulnerability-' . $software, wp_json_encode( $data ) );
     160                update_option( 'wpvulnerability-' . $software . '-vulnerable', wp_json_encode( number_format( $wpvulnerability_software_vulnerable, 0, '.', '' ) ) );
     161                update_option( 'wpvulnerability-' . $software . '-cache', wp_json_encode( number_format( time() + ( 3600 * wpvulnerability_cache_hours() ), 0, '.', '' ) ) );
    162162    }
    163163
  • wpvulnerability/trunk/wpvulnerability-themes.php

    r3161036 r3362936  
    190190    // Update options for multisite installations.
    191191    if ( is_multisite() ) {
    192         update_site_option( 'wpvulnerability-themes', wp_json_encode( $themes_v ) );
    193         update_site_option( 'wpvulnerability-themes-vulnerable', wp_json_encode( number_format( $wpvulnerability_themes_vulnerable, 0, '.', '' ) ) );
    194         update_site_option( 'wpvulnerability-themes-cache', wp_json_encode( number_format( time() + ( 3600 * WPVULNERABILITY_CACHE_HOURS ), 0, '.', '' ) ) );
     192                update_site_option( 'wpvulnerability-themes', wp_json_encode( $themes_v ) );
     193                update_site_option( 'wpvulnerability-themes-vulnerable', wp_json_encode( number_format( $wpvulnerability_themes_vulnerable, 0, '.', '' ) ) );
     194                update_site_option( 'wpvulnerability-themes-cache', wp_json_encode( number_format( time() + ( 3600 * wpvulnerability_cache_hours() ), 0, '.', '' ) ) );
    195195
    196196        // Update options for single site installations.
    197197    } else {
    198         update_option( 'wpvulnerability-themes', wp_json_encode( $themes_v ) );
    199         update_option( 'wpvulnerability-themes-vulnerable', wp_json_encode( number_format( $wpvulnerability_themes_vulnerable, 0, '.', '' ) ) );
    200         update_option( 'wpvulnerability-themes-cache', wp_json_encode( number_format( time() + ( 3600 * WPVULNERABILITY_CACHE_HOURS ), 0, '.', '' ) ) );
     198                update_option( 'wpvulnerability-themes', wp_json_encode( $themes_v ) );
     199                update_option( 'wpvulnerability-themes-vulnerable', wp_json_encode( number_format( $wpvulnerability_themes_vulnerable, 0, '.', '' ) ) );
     200                update_option( 'wpvulnerability-themes-cache', wp_json_encode( number_format( time() + ( 3600 * wpvulnerability_cache_hours() ), 0, '.', '' ) ) );
    201201    }
    202202
  • wpvulnerability/trunk/wpvulnerability.php

    r3267956 r3362936  
    44 * Plugin URI: https://www.wpvulnerability.com/
    55 * Description: Receive information about possible vulnerabilities in your WordPress from WordPress Vulnerability Database API.
    6  * Requires at least: 4.1
     6 * Requires at least: 4.7
    77 * Requires PHP: 5.6
    8  * Version: 4.0.4
     8 * Version: 4.1.0
    99 * Author: Javier Casares
    1010 * Author URI: https://www.javiercasares.com/
     
    2424 * Set some constants that I can change in future versions.
    2525 */
    26 define( 'WPVULNERABILITY_PLUGIN_VERSION', '4.0.4' );
     26define( 'WPVULNERABILITY_PLUGIN_VERSION', '4.1.0' );
    2727define( 'WPVULNERABILITY_API_HOST', 'https://www.wpvulnerability.net/' );
    28 define( 'WPVULNERABILITY_CACHE_HOURS', 12 );
    2928
    3029/**
     
    3635define( 'WPVULNERABILITY_PLUGIN_PATH', plugin_dir_path( __FILE__ ) );
    3736
     37// Handle front-end email opt-out requests early.
     38if ( isset( $_GET['wpvulnerability_disable_email'] ) ) { // phpcs:ignore
     39        // Load pluggable functions so wp_verify_nonce() is available.
     40    if ( ! function_exists( 'wp_verify_nonce' ) ) {
     41            require_once ABSPATH . WPINC . '/pluggable.php';
     42    }
     43
     44        require_once WPVULNERABILITY_PLUGIN_PATH . '/wpvulnerability-notifications.php';
     45        wpvulnerability_disable_email_handler();
     46}
     47
    3848/**
    3949 * Initialize the plugin.
     
    4454 */
    4555function wpvulnerability_plugin_init() {
    46     /*
    47      * Load the plugin's localization files.
    48      *
    49      * @since 2.0.0
    50      */
    51     load_plugin_textdomain( 'wpvulnerability', false, dirname( WPVULNERABILITY_PLUGIN_BASE ) . '/languages' );
    52 
    5356    wpvulnerability_activation();
    5457
     
    8992    require_once WPVULNERABILITY_PLUGIN_PATH . '/wpvulnerability-notifications.php';
    9093    require_once WPVULNERABILITY_PLUGIN_PATH . '/wpvulnerability-sitehealth.php';
    91     require_once WPVULNERABILITY_PLUGIN_PATH . '/wpvulnerability-cli.php';
     94        require_once WPVULNERABILITY_PLUGIN_PATH . '/class-wpvulnerability-cli.php';
     95        require_once WPVULNERABILITY_PLUGIN_PATH . '/class-wpvulnerability-config-cli.php';
    9296
    93     // Update non-cached data.
    94     wpvulnerability_expired_database_data();
     97        // Update non-cached data.
     98        wpvulnerability_expired_database_data();
     99}
     100
     101/*
     102 * Backward compatibility for wp_doing_cron().
     103 *
     104 * wp_doing_cron() was introduced in WordPress 4.8. This plugin supports
     105 * versions as old as WordPress 4.7, so define the function when it does not
     106 * exist in core.
     107 *
     108 * @since 4.1.1
     109 */
     110if ( ! function_exists( 'wp_doing_cron' ) ) {
     111        /**
     112         * Determines whether the current request is a WordPress cron request.
     113         *
     114         * Mirrors the core wp_doing_cron() function available since 4.8.0.
     115         *
     116         * @since 4.1.1
     117         *
     118         * @return bool Whether the current request is a WordPress cron request.
     119         */
     120    function wp_doing_cron() {
     121            return apply_filters( 'wp_doing_cron', defined( 'DOING_CRON' ) && DOING_CRON );
     122    }
    95123}
    96124
     
    99127 */
    100128if (
    101         ( ! is_multisite() && is_admin() ) ||
     129                ( ! is_multisite() && is_admin() ) ||
    102130        ( is_multisite() && ( is_network_admin() || is_main_site() ) ) ||
    103131        ( defined( 'WP_CLI' ) && WP_CLI ) ||
Note: See TracChangeset for help on using the changeset viewer.