Version Description
24/August/2022 =
FEATURE: Two-Factor Authentication (2FA) functionality & related settings.
FEATURE: Set up a mechanism to load the firewall PHP file early.
FEATURE: PHP firewall rule engine.
FEATURE: Add WHOIS lookup functionality.
FEATURE: Implement 6G firewall rules in the new PHP-based firewall.
FEATURE: Disable WordPress application passwords.
FEATURE: Remove the plugin's tables and options when uninstalling the plugin according to configuration settings.
FEATURE: Trash spam comments after n number of days as per configuration set in Admin Dashboard > WP Security > SPAM Prevention > the "Comment SPAM" tab > the "Comment Processing" section > the "Trash Comments After" settings.
FEATURE: Brute force Cookie-based Firewall Protection based on the PHP code instead of htaccess rules so that it also works with Nginx, IIS etc servers.
FEATURE: Allow multiple email addresses for the User Login > Notify By Email setting.
FEATURE: IPv6 range support in CIDR Format enabled.
FIX: The WooCommerce customer was redirected to the wp-login page after payment with an external payment gateway if forced logout configured after a specific number of minutes.
FIX: If the WordPress language was set to something other than English, then auto-update core, plugin, and theme emails sent in English instead of the configured language.
FIX: Database error for multisite when creating a new site solved.
FIX: Captcha options should not be autoloaded.
FIX: Database error for multisite cronjob column name.
FIX: The plugin clogs up the database with lots of rows. Delete old data after 90 days.
FIX: Rename Login issue with wp plugin list command solved.
FIX: Rename Login breaks logout functionality if WP_HOME is set to a different URL than the WordPress core files URL.
FIX: PHP Fatal error: Uncaught Error: Class 'AIOWPSecurity_Admin_Init' not found in html/wp-content/plugins/all-in-one-wp-security-and-firewall/wp-security-core.php:366.
FIX: The Spam comment blocked IP address remains blocked even after spammed comments are approved.
FIX: Admin Dashboard > WP Security > Security Points Breakdown Section piechart tooltips flickering.
FIX: The "Time Length of 404 Lockout" option doesn't do anything.
FIX: Search did not work for the 404 Event Logs list table.
FIX: Search did not work for Failed Logins list table.
FIX: Search did not work for the Account Activity list table.
FIX: Bulk deletions did not work for the Account Activity list table.
FIX: Warning when bots make malformed requests.
FIX: When the user had pressed the bottom bulk action button of the list table, the bulk action was confirmed by two confirm alerts.
FIX: Unblock link in 404 Event Logs list table redirected to wrong tab.
FIX: Temp Block, Blacklist IP and Delete links in 404 Event Logs list table didn't work.
FIX: Rename login page and Cookie based brute force login prevention configurations didn't work simultaneously.
FIX: Fatal error when activating using older PHP versions
FIX: If auto_prepend_file is already pointed to the firewall bootstrap file from php.ini manually, the bootstrap file try to include itself.
FIX: The custom logo wasn't displayed on the login lockdown unlock request form.
TWEAK: Allow taking database backups via the UpdraftPlus backup plugin.
TWEAK: Make lockout reasons more specific.
TWEAK: Update notice class.
TWEAK: If the user has not performed the cookie test, the brute force attack prevention configuration fields remain disabled in the Admin Dashboard > WP Security > Brute Force > Cookie Based Brute Force Prevention.
TWEAK: Display locked IP addresses lockout date and release date in WordPress settings format.
TWEAK: Improve success or messages when performing bulk actions on the table list.
TWEAK: 404 events date is displayed in WordPress settings format.
TWEAK: Account activity login date and logout date are displayed in WordPress settings format.
TWEAK: Add a label for each setting field.
TWEAK: JQMIGRATE: jQuery.fn.click() event shorthand is deprecated.
TWEAK: Fix typos at Admin Dashboard > WP Security > Firewall > Basic Firewall Rules > Block Access to Debug Log File.
Release Info
| Developer | DavidAnderson |
| Plugin | |
| Version | 5.0.0 |
| Comparing to | |
| See all releases | |
Code changes from version 4.4.12 to 5.0.0
- admin/general/wp-security-list-table.php +22 -1
- admin/wp-security-admin-init.php +46 -28
- admin/wp-security-admin-menu.php +20 -19
- admin/wp-security-blacklist-menu.php +7 -17
- admin/wp-security-brute-force-menu.php +153 -205
- admin/wp-security-dashboard-menu.php +25 -20
- admin/wp-security-database-menu.php +123 -189
- admin/wp-security-filescan-menu.php +16 -12
- admin/wp-security-filesystem-menu.php +6 -6
- admin/wp-security-firewall-menu.php +218 -78
- admin/wp-security-firewall-setup-notice.php +590 -0
- admin/wp-security-list-404.php +111 -79
- admin/wp-security-list-acct-activity.php +99 -86
- admin/wp-security-list-comment-spammer-ip.php +2 -2
- admin/wp-security-list-locked-ip.php +117 -94
- admin/wp-security-list-logged-in-users.php +1 -1
- admin/wp-security-list-login-fails.php +82 -85
- admin/wp-security-list-permanent-blocked-ip.php +22 -21
- admin/wp-security-maintenance-menu.php +3 -3
- admin/wp-security-misc-options-menu.php +15 -15
- admin/wp-security-settings-menu.php +136 -58
- admin/wp-security-spam-menu.php +79 -24
- admin/wp-security-tools-menu.php +239 -0
- admin/wp-security-user-accounts-menu.php +5 -5
- admin/wp-security-user-login-menu.php +232 -104
- admin/wp-security-user-registration-menu.php +8 -8
- classes/firewall/family/wp-security-firewall-families.php +9 -0
- classes/firewall/family/wp-security-firewall-family-builder.php +33 -0
- classes/firewall/family/wp-security-firewall-family-collection.php +49 -0
- classes/firewall/family/wp-security-firewall-family.php +86 -0
- classes/firewall/libs/wp-security-firewall-config.php +121 -0
- classes/firewall/rule/actions/action-exit-trait.php +17 -0
- classes/firewall/rule/actions/action-forbid-and-exit-trait.php +23 -0
- classes/firewall/rule/actions/action-forbid-trait.php +17 -0
- classes/firewall/rule/rules/rule-block-query-strings-6g.php +61 -0
- classes/firewall/rule/rules/rule-block-refs-6g.php +52 -0
- classes/firewall/rule/rules/rule-block-request-strings-6g.php +61 -0
- classes/firewall/rule/rules/rule-block-user-agents-6g.php +52 -0
- classes/firewall/rule/rules/rule-request-method-6g.php +53 -0
- classes/firewall/rule/wp-security-firewall-rule-builder.php +48 -0
- classes/firewall/rule/wp-security-firewall-rule-utils.php +29 -0
- classes/firewall/rule/wp-security-firewall-rule.php +90 -0
- classes/firewall/wp-security-firewall-loader.php +157 -0
- classes/firewall/wp-security-firewall.php +20 -0
- classes/grade-system/wp-security-feature-item-manager.php +27 -4
- classes/wp-security-abstract-ids.php +20 -0
- classes/wp-security-backup.php +0 -339
- classes/wp-security-base-tasks.php +34 -0
- classes/wp-security-block-bootstrap.php +169 -0
- classes/wp-security-block-file.php +193 -0
- classes/wp-security-block-htaccess.php +89 -0
- classes/wp-security-block-litespeed.php +29 -0
- classes/wp-security-block-muplugin.php +93 -0
- classes/wp-security-block-userini.php +96 -0
- classes/wp-security-block-wpconfig.php +94 -0
- classes/wp-security-blocking.php +5 -0
- classes/wp-security-captcha.php +4 -4
- classes/wp-security-cleanup.php +51 -0
- classes/wp-security-comment.php +45 -0
- classes/wp-security-configure-settings.php +31 -7
- classes/wp-security-cronjob-handler.php +64 -4
- classes/wp-security-deactivation-tasks.php +16 -43
- classes/wp-security-debug-logger.php +0 -41
- classes/wp-security-general-init-tasks.php +97 -11
- classes/wp-security-installer.php +36 -9
- classes/wp-security-notices.php +98 -2
- classes/wp-security-process-renamed-login-page.php +22 -11
- classes/wp-security-two-factor-login.php +104 -0
- classes/wp-security-uninstallation-tasks.php +83 -0
- classes/wp-security-user-login.php +184 -55
- classes/wp-security-utility-file.php +53 -4
- classes/wp-security-utility-firewall.php +186 -0
- classes/wp-security-utility-htaccess.php +12 -162
- classes/wp-security-utility-ip-address.php +47 -27
- classes/wp-security-utility.php +177 -54
- classes/wp-security-wp-footer-content.php +1 -1
- css/wp-security-admin-styles.css +8 -0
- includes/simba-tfa/includes/frontend-settings.js +126 -0
- includes/simba-tfa/includes/jquery-qrcode/README.md +40 -0
- includes/simba-tfa/includes/jquery-qrcode/jquery-qrcode.js +2332 -0
- includes/simba-tfa/includes/jquery-qrcode/jquery-qrcode.min.js +2 -0
- includes/simba-tfa/includes/jquery.blockUI.js +619 -0
- includes/simba-tfa/includes/jquery.blockUI.min.js +14 -0
- includes/simba-tfa/includes/login-form-integrations.php +129 -0
- includes/simba-tfa/includes/select2.css +482 -0
- includes/simba-tfa/includes/select2.js +5580 -0
- includes/simba-tfa/includes/select2.min.js +2 -0
- includes/simba-tfa/includes/tfa.js +218 -0
- includes/simba-tfa/includes/tfa_admin_icon_16x16.png +0 -0
- includes/simba-tfa/includes/tfa_admin_icon_32x32.png +0 -0
- includes/simba-tfa/includes/tfa_frontend.php +204 -0
- includes/simba-tfa/includes/users.css +13 -0
- includes/simba-tfa/providers/totp-hotp/Base32/Base32.php +225 -0
- includes/simba-tfa/providers/totp-hotp/hotp-php-master/LICENSE +24 -0
- includes/simba-tfa/providers/totp-hotp/hotp-php-master/README.markdown +27 -0
- includes/simba-tfa/providers/totp-hotp/hotp-php-master/example.php +160 -0
- includes/simba-tfa/providers/totp-hotp/hotp-php-master/hotp.php +174 -0
- includes/simba-tfa/providers/totp-hotp/loader.php +1015 -0
- includes/simba-tfa/simba-tfa.php +1250 -0
- includes/simba-tfa/templates/admin-settings.php +185 -0
- includes/simba-tfa/templates/settings-intro-notices.php +28 -0
- includes/simba-tfa/templates/shortcode-tfa-user-settings.php +26 -0
- includes/simba-tfa/templates/trusted-devices-inner-box.php +29 -0
- includes/simba-tfa/templates/user-settings.php +61 -0
- js/wp-security-admin-script.js +43 -3
- languages/all-in-one-wp-security-and-firewall-de_DE.po +11 -11
- languages/all-in-one-wp-security-and-firewall-fr_FR.po +9 -9
- languages/all-in-one-wp-security-and-firewall-hu_HU.po +9 -9
- languages/all-in-one-wp-security-and-firewall-ko_KR.po +9 -9
- languages/all-in-one-wp-security-and-firewall-nl_NL.po +11 -11
- languages/all-in-one-wp-security-and-firewall-pl_PL.po +11 -11
- languages/all-in-one-wp-security-and-firewall-pt_BR.po +11 -11
- languages/all-in-one-wp-security-and-firewall-ru_RU.po +9 -9
- languages/all-in-one-wp-security-and-firewall-sv_SE.po +5 -5
|
@@ -477,7 +477,13 @@ class AIOWPSecurity_List_Table {
|
|
| 477 |
|
| 478 |
echo "</select>\n";
|
| 479 |
|
| 480 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 481 |
echo "\n";
|
| 482 |
}
|
| 483 |
|
|
@@ -1401,4 +1407,19 @@ class AIOWPSecurity_List_Table {
|
|
| 1401 |
|
| 1402 |
printf( "<script>list_args = %s;</script>\n", wp_json_encode( $args ) );
|
| 1403 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1404 |
}
|
| 477 |
|
| 478 |
echo "</select>\n";
|
| 479 |
|
| 480 |
+
$submit_attributes = array('id' => "doaction$two");
|
| 481 |
+
|
| 482 |
+
if ('top' == $which) {
|
| 483 |
+
$submit_attributes['onclick'] = "return confirm('".esc_js(__('Are you sure you want to perform this bulk action?', 'all-in-one-wp-security-and-firewall'))."')";
|
| 484 |
+
}
|
| 485 |
+
|
| 486 |
+
submit_button(__('Apply'), 'action', '', false, $submit_attributes);
|
| 487 |
echo "\n";
|
| 488 |
}
|
| 489 |
|
| 1407 |
|
| 1408 |
printf( "<script>list_args = %s;</script>\n", wp_json_encode( $args ) );
|
| 1409 |
}
|
| 1410 |
+
|
| 1411 |
+
/**
|
| 1412 |
+
* Retrieves and returns current WP general settings date time format.
|
| 1413 |
+
*
|
| 1414 |
+
* @return String
|
| 1415 |
+
*/
|
| 1416 |
+
protected function get_wp_date_time_format() {
|
| 1417 |
+
static $wp_date_time_format;
|
| 1418 |
+
|
| 1419 |
+
if (!isset($wp_date_time_format)) {
|
| 1420 |
+
$wp_date_time_format = get_option('date_format').' '.get_option('time_format');
|
| 1421 |
+
}
|
| 1422 |
+
|
| 1423 |
+
return $wp_date_time_format;
|
| 1424 |
+
}
|
| 1425 |
}
|
|
@@ -44,10 +44,13 @@ class AIOWPSecurity_Admin_Init {
|
|
| 44 |
*/
|
| 45 |
public function __construct() {
|
| 46 |
//This class is only initialized if is_admin() is true
|
| 47 |
-
|
| 48 |
-
add_action('admin_menu', array($this, 'create_admin_menus'));
|
| 49 |
//handle CSV download
|
| 50 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
|
| 52 |
add_action('admin_init', array($this, 'hook_admin_notices'));
|
| 53 |
|
|
@@ -83,7 +86,7 @@ class AIOWPSecurity_Admin_Init {
|
|
| 83 |
}
|
| 84 |
}
|
| 85 |
|
| 86 |
-
function aiowps_csv_download() {
|
| 87 |
global $aio_wp_security;
|
| 88 |
if (isset($_POST['aiowpsec_export_acct_activity_logs_to_csv'])) { //Export account activity logs
|
| 89 |
$nonce = $_REQUEST['_wpnonce'];
|
|
@@ -156,7 +159,7 @@ class AIOWPSecurity_Admin_Init {
|
|
| 156 |
return $this->is_aiowps_admin_page;
|
| 157 |
}
|
| 158 |
global $pagenow;
|
| 159 |
-
$this->is_aiowps_admin_page = ('admin.php' == $pagenow && isset($_GET['page']) && false !== strpos($_GET['page'], AIOWPSEC_MENU_SLUG_PREFIX));
|
| 160 |
return $this->is_aiowps_admin_page;
|
| 161 |
}
|
| 162 |
|
|
@@ -200,6 +203,8 @@ class AIOWPSecurity_Admin_Init {
|
|
| 200 |
public function render_admin_notices() {
|
| 201 |
global $aio_wp_security;
|
| 202 |
|
|
|
|
|
|
|
| 203 |
$installed_at = $aio_wp_security->notices->get_aiowps_plugin_installed_timestamp();
|
| 204 |
$time_now = $aio_wp_security->notices->get_time_now();
|
| 205 |
$installed_for = $time_now - $installed_at;
|
|
@@ -218,17 +223,25 @@ class AIOWPSecurity_Admin_Init {
|
|
| 218 |
include_once('wp-security-admin-menu.php');
|
| 219 |
}
|
| 220 |
|
| 221 |
-
|
| 222 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 223 |
wp_enqueue_script('jquery');
|
| 224 |
wp_enqueue_script('postbox');
|
| 225 |
wp_enqueue_script('dashboard');
|
| 226 |
wp_enqueue_script('thickbox');
|
| 227 |
wp_enqueue_script('media-upload');
|
| 228 |
-
wp_register_script('aiowpsec-admin-js', AIO_WP_SECURITY_URL. '/js/wp-security-admin-script.js', array('jquery'));
|
| 229 |
wp_enqueue_script('aiowpsec-admin-js');
|
| 230 |
wp_register_script('aiowpsec-pw-tool-js', AIO_WP_SECURITY_URL. '/js/password-strength-tool.js', array('jquery')); // We will enqueue this in the user acct menu class
|
| 231 |
-
|
| 232 |
|
| 233 |
function admin_menu_page_styles()
|
| 234 |
{
|
|
@@ -236,7 +249,8 @@ class AIOWPSecurity_Admin_Init {
|
|
| 236 |
wp_enqueue_style('thickbox');
|
| 237 |
wp_enqueue_style('global');
|
| 238 |
wp_enqueue_style('wp-admin');
|
| 239 |
-
|
|
|
|
| 240 |
}
|
| 241 |
|
| 242 |
function init_hook_handler_for_admin_side()
|
|
@@ -302,22 +316,15 @@ class AIOWPSecurity_Admin_Init {
|
|
| 302 |
//The old "test cookie" used to be too easy to guess because someone could just read the code and get the value.
|
| 303 |
//So now we will drop a more secure test cookie using a 10 digit random string
|
| 304 |
|
| 305 |
-
if($aio_wp_security->configs->get_value('aiowps_enable_brute_force_attack_prevention')
|
| 306 |
-
// This code is for users who had this feature saved using an older release. This will drop the new more secure test cookie to the browser
|
| 307 |
-
$
|
| 308 |
-
if(empty($
|
| 309 |
$random_suffix = AIOWPSecurity_Utility::generate_alpha_numeric_random_string(10);
|
| 310 |
$test_cookie_name = 'aiowps_cookie_test_'.$random_suffix;
|
| 311 |
$aio_wp_security->configs->set_value('aiowps_cookie_brute_test',$test_cookie_name);
|
| 312 |
$aio_wp_security->configs->save_config();//save the value
|
| 313 |
-
AIOWPSecurity_Utility::set_cookie_value($test_cookie_name,
|
| 314 |
-
|
| 315 |
-
//Write this new cookie to the .htaccess file
|
| 316 |
-
$res = AIOWPSecurity_Utility_Htaccess::write_to_htaccess();
|
| 317 |
-
if( !$res ){
|
| 318 |
-
$aio_wp_security->debug_logger->log_debug("Error writing new test cookie with random suffix to .htaccess file!",4);
|
| 319 |
-
}
|
| 320 |
-
|
| 321 |
}
|
| 322 |
}
|
| 323 |
//For cookie test form submission case
|
|
@@ -329,7 +336,7 @@ class AIOWPSecurity_Admin_Init {
|
|
| 329 |
$test_cookie_name = 'aiowps_cookie_test_'.$random_suffix;
|
| 330 |
$aio_wp_security->configs->set_value('aiowps_cookie_brute_test',$test_cookie_name);
|
| 331 |
$aio_wp_security->configs->save_config();//save the value
|
| 332 |
-
AIOWPSecurity_Utility::set_cookie_value($test_cookie_name,
|
| 333 |
$cur_url = "admin.php?page=".AIOWPSEC_BRUTE_FORCE_MENU_SLUG."&tab=tab2";
|
| 334 |
$redirect_url = AIOWPSecurity_Utility::add_query_data_to_url($cur_url, 'aiowps_cookie_test', "1");
|
| 335 |
AIOWPSecurity_Utility::redirect_to_url($redirect_url);
|
|
@@ -399,30 +406,31 @@ class AIOWPSecurity_Admin_Init {
|
|
| 399 |
add_submenu_page(AIOWPSEC_MAIN_MENU_SLUG, __('User Login', 'all-in-one-wp-security-and-firewall'), __('User Login', 'all-in-one-wp-security-and-firewall') , AIOWPSEC_MANAGEMENT_PERMISSION, AIOWPSEC_USER_LOGIN_MENU_SLUG, array($this, 'handle_user_login_menu_rendering'));
|
| 400 |
add_submenu_page(AIOWPSEC_MAIN_MENU_SLUG, __('User Registration', 'all-in-one-wp-security-and-firewall'), __('User Registration', 'all-in-one-wp-security-and-firewall') , AIOWPSEC_MANAGEMENT_PERMISSION, AIOWPSEC_USER_REGISTRATION_MENU_SLUG, array($this, 'handle_user_registration_menu_rendering'));
|
| 401 |
add_submenu_page(AIOWPSEC_MAIN_MENU_SLUG, __('Database Security', 'all-in-one-wp-security-and-firewall'), __('Database Security', 'all-in-one-wp-security-and-firewall') , AIOWPSEC_MANAGEMENT_PERMISSION, AIOWPSEC_DB_SEC_MENU_SLUG, array($this, 'handle_database_menu_rendering'));
|
| 402 |
-
if (
|
| 403 |
//Suppress the Filesystem Security menu if site is a multi site AND not the main site
|
| 404 |
}else{
|
| 405 |
add_submenu_page(AIOWPSEC_MAIN_MENU_SLUG, __('Filesystem Security', 'all-in-one-wp-security-and-firewall'), __('Filesystem Security', 'all-in-one-wp-security-and-firewall') , AIOWPSEC_MANAGEMENT_PERMISSION, AIOWPSEC_FILESYSTEM_MENU_SLUG, array($this, 'handle_filesystem_menu_rendering'));
|
| 406 |
}
|
| 407 |
-
if (
|
| 408 |
//Suppress the Blacklist Manager menu if site is a multi site AND not the main site
|
| 409 |
}else{
|
| 410 |
add_submenu_page(AIOWPSEC_MAIN_MENU_SLUG, __('Blacklist Manager', 'all-in-one-wp-security-and-firewall'), __('Blacklist Manager', 'all-in-one-wp-security-and-firewall') , AIOWPSEC_MANAGEMENT_PERMISSION, AIOWPSEC_BLACKLIST_MENU_SLUG, array($this, 'handle_blacklist_menu_rendering'));
|
| 411 |
}
|
| 412 |
-
if (
|
| 413 |
//Suppress the firewall menu if site is a multi site AND not the main site
|
| 414 |
}else{
|
| 415 |
add_submenu_page(AIOWPSEC_MAIN_MENU_SLUG, __('Firewall', 'all-in-one-wp-security-and-firewall'), __('Firewall', 'all-in-one-wp-security-and-firewall') , AIOWPSEC_MANAGEMENT_PERMISSION, AIOWPSEC_FIREWALL_MENU_SLUG, array($this, 'handle_firewall_menu_rendering'));
|
| 416 |
}
|
| 417 |
add_submenu_page(AIOWPSEC_MAIN_MENU_SLUG, __('Brute Force', 'all-in-one-wp-security-and-firewall'), __('Brute Force', 'all-in-one-wp-security-and-firewall') , AIOWPSEC_MANAGEMENT_PERMISSION, AIOWPSEC_BRUTE_FORCE_MENU_SLUG, array($this, 'handle_brute_force_menu_rendering'));
|
| 418 |
add_submenu_page(AIOWPSEC_MAIN_MENU_SLUG, __('SPAM Prevention', 'all-in-one-wp-security-and-firewall'), __('SPAM Prevention', 'all-in-one-wp-security-and-firewall') , AIOWPSEC_MANAGEMENT_PERMISSION, AIOWPSEC_SPAM_MENU_SLUG, array($this, 'handle_spam_menu_rendering'));
|
| 419 |
-
if (
|
| 420 |
//Suppress the filescan menu if site is a multi site AND not the main site
|
| 421 |
}else{
|
| 422 |
add_submenu_page(AIOWPSEC_MAIN_MENU_SLUG, __('Scanner', 'all-in-one-wp-security-and-firewall'), __('Scanner', 'all-in-one-wp-security-and-firewall') , AIOWPSEC_MANAGEMENT_PERMISSION, AIOWPSEC_FILESCAN_MENU_SLUG, array($this, 'handle_filescan_menu_rendering'));
|
| 423 |
}
|
| 424 |
add_submenu_page(AIOWPSEC_MAIN_MENU_SLUG, __('Maintenance', 'all-in-one-wp-security-and-firewall'), __('Maintenance', 'all-in-one-wp-security-and-firewall') , AIOWPSEC_MANAGEMENT_PERMISSION, AIOWPSEC_MAINTENANCE_MENU_SLUG, array($this, 'handle_maintenance_menu_rendering'));
|
| 425 |
add_submenu_page(AIOWPSEC_MAIN_MENU_SLUG, __('Miscellaneous', 'all-in-one-wp-security-and-firewall'), __('Miscellaneous', 'all-in-one-wp-security-and-firewall') , AIOWPSEC_MANAGEMENT_PERMISSION, AIOWPSEC_MISC_MENU_SLUG, array($this, 'handle_misc_menu_rendering'));
|
|
|
|
| 426 |
do_action('aiowpsecurity_admin_menu_created');
|
| 427 |
}
|
| 428 |
|
|
@@ -510,6 +518,16 @@ class AIOWPSecurity_Admin_Init {
|
|
| 510 |
include_once('wp-security-misc-options-menu.php');
|
| 511 |
$this->misc_menu = new AIOWPSecurity_Misc_Options_Menu();
|
| 512 |
}
|
| 513 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 514 |
}//End of class
|
| 515 |
|
| 44 |
*/
|
| 45 |
public function __construct() {
|
| 46 |
//This class is only initialized if is_admin() is true
|
| 47 |
+
|
|
|
|
| 48 |
//handle CSV download
|
| 49 |
+
if (current_user_can(AIOWPSEC_MANAGEMENT_PERMISSION)) {
|
| 50 |
+
$this->admin_includes();
|
| 51 |
+
add_action('admin_menu', array($this, 'create_admin_menus'));
|
| 52 |
+
add_action('admin_init', array($this, 'aiowps_csv_download'));
|
| 53 |
+
}
|
| 54 |
|
| 55 |
add_action('admin_init', array($this, 'hook_admin_notices'));
|
| 56 |
|
| 86 |
}
|
| 87 |
}
|
| 88 |
|
| 89 |
+
public function aiowps_csv_download() {
|
| 90 |
global $aio_wp_security;
|
| 91 |
if (isset($_POST['aiowpsec_export_acct_activity_logs_to_csv'])) { //Export account activity logs
|
| 92 |
$nonce = $_REQUEST['_wpnonce'];
|
| 159 |
return $this->is_aiowps_admin_page;
|
| 160 |
}
|
| 161 |
global $pagenow;
|
| 162 |
+
$this->is_aiowps_admin_page = (current_user_can(AIOWPSEC_MANAGEMENT_PERMISSION) && 'admin.php' == $pagenow && isset($_GET['page']) && false !== strpos($_GET['page'], AIOWPSEC_MENU_SLUG_PREFIX));
|
| 163 |
return $this->is_aiowps_admin_page;
|
| 164 |
}
|
| 165 |
|
| 203 |
public function render_admin_notices() {
|
| 204 |
global $aio_wp_security;
|
| 205 |
|
| 206 |
+
$aio_wp_security->notices->do_notice('automated-database-backup', 'automated-database-backup');
|
| 207 |
+
|
| 208 |
$installed_at = $aio_wp_security->notices->get_aiowps_plugin_installed_timestamp();
|
| 209 |
$time_now = $aio_wp_security->notices->get_time_now();
|
| 210 |
$installed_for = $time_now - $installed_at;
|
| 223 |
include_once('wp-security-admin-menu.php');
|
| 224 |
}
|
| 225 |
|
| 226 |
+
/**
|
| 227 |
+
* Enqueue admin JavaScripts.
|
| 228 |
+
*
|
| 229 |
+
* @return Void
|
| 230 |
+
*/
|
| 231 |
+
public function admin_menu_page_scripts() {
|
| 232 |
+
if (!AIOWPSecurity_Utility::has_manage_cap()) {
|
| 233 |
+
return;
|
| 234 |
+
}
|
| 235 |
+
|
| 236 |
wp_enqueue_script('jquery');
|
| 237 |
wp_enqueue_script('postbox');
|
| 238 |
wp_enqueue_script('dashboard');
|
| 239 |
wp_enqueue_script('thickbox');
|
| 240 |
wp_enqueue_script('media-upload');
|
| 241 |
+
wp_register_script('aiowpsec-admin-js', AIO_WP_SECURITY_URL. '/js/wp-security-admin-script.js', array('jquery'), AIO_WP_SECURITY_VERSION, true);
|
| 242 |
wp_enqueue_script('aiowpsec-admin-js');
|
| 243 |
wp_register_script('aiowpsec-pw-tool-js', AIO_WP_SECURITY_URL. '/js/password-strength-tool.js', array('jquery')); // We will enqueue this in the user acct menu class
|
| 244 |
+
}
|
| 245 |
|
| 246 |
function admin_menu_page_styles()
|
| 247 |
{
|
| 249 |
wp_enqueue_style('thickbox');
|
| 250 |
wp_enqueue_style('global');
|
| 251 |
wp_enqueue_style('wp-admin');
|
| 252 |
+
$admin_css_version = (defined('WP_DEBUG') && WP_DEBUG) ? time() : filemtime(AIO_WP_SECURITY_PATH. '/css/wp-security-admin-styles.css');
|
| 253 |
+
wp_enqueue_style('aiowpsec-admin-css', AIO_WP_SECURITY_URL. '/css/wp-security-admin-styles.css', array(), $admin_css_version);
|
| 254 |
}
|
| 255 |
|
| 256 |
function init_hook_handler_for_admin_side()
|
| 316 |
//The old "test cookie" used to be too easy to guess because someone could just read the code and get the value.
|
| 317 |
//So now we will drop a more secure test cookie using a 10 digit random string
|
| 318 |
|
| 319 |
+
if ('1' == $aio_wp_security->configs->get_value('aiowps_enable_brute_force_attack_prevention')) {
|
| 320 |
+
// This code is for users who had this feature saved using an older release. This will drop the new more secure test cookie to the browser
|
| 321 |
+
$test_cookie_name_saved = $aio_wp_security->configs->get_value('aiowps_cookie_brute_test');
|
| 322 |
+
if (empty($test_cookie_name_saved)) {
|
| 323 |
$random_suffix = AIOWPSecurity_Utility::generate_alpha_numeric_random_string(10);
|
| 324 |
$test_cookie_name = 'aiowps_cookie_test_'.$random_suffix;
|
| 325 |
$aio_wp_security->configs->set_value('aiowps_cookie_brute_test',$test_cookie_name);
|
| 326 |
$aio_wp_security->configs->save_config();//save the value
|
| 327 |
+
AIOWPSecurity_Utility::set_cookie_value($test_cookie_name, '1');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 328 |
}
|
| 329 |
}
|
| 330 |
//For cookie test form submission case
|
| 336 |
$test_cookie_name = 'aiowps_cookie_test_'.$random_suffix;
|
| 337 |
$aio_wp_security->configs->set_value('aiowps_cookie_brute_test',$test_cookie_name);
|
| 338 |
$aio_wp_security->configs->save_config();//save the value
|
| 339 |
+
AIOWPSecurity_Utility::set_cookie_value($test_cookie_name, '1');
|
| 340 |
$cur_url = "admin.php?page=".AIOWPSEC_BRUTE_FORCE_MENU_SLUG."&tab=tab2";
|
| 341 |
$redirect_url = AIOWPSecurity_Utility::add_query_data_to_url($cur_url, 'aiowps_cookie_test', "1");
|
| 342 |
AIOWPSecurity_Utility::redirect_to_url($redirect_url);
|
| 406 |
add_submenu_page(AIOWPSEC_MAIN_MENU_SLUG, __('User Login', 'all-in-one-wp-security-and-firewall'), __('User Login', 'all-in-one-wp-security-and-firewall') , AIOWPSEC_MANAGEMENT_PERMISSION, AIOWPSEC_USER_LOGIN_MENU_SLUG, array($this, 'handle_user_login_menu_rendering'));
|
| 407 |
add_submenu_page(AIOWPSEC_MAIN_MENU_SLUG, __('User Registration', 'all-in-one-wp-security-and-firewall'), __('User Registration', 'all-in-one-wp-security-and-firewall') , AIOWPSEC_MANAGEMENT_PERMISSION, AIOWPSEC_USER_REGISTRATION_MENU_SLUG, array($this, 'handle_user_registration_menu_rendering'));
|
| 408 |
add_submenu_page(AIOWPSEC_MAIN_MENU_SLUG, __('Database Security', 'all-in-one-wp-security-and-firewall'), __('Database Security', 'all-in-one-wp-security-and-firewall') , AIOWPSEC_MANAGEMENT_PERMISSION, AIOWPSEC_DB_SEC_MENU_SLUG, array($this, 'handle_database_menu_rendering'));
|
| 409 |
+
if (is_multisite() && get_current_blog_id() != 1){
|
| 410 |
//Suppress the Filesystem Security menu if site is a multi site AND not the main site
|
| 411 |
}else{
|
| 412 |
add_submenu_page(AIOWPSEC_MAIN_MENU_SLUG, __('Filesystem Security', 'all-in-one-wp-security-and-firewall'), __('Filesystem Security', 'all-in-one-wp-security-and-firewall') , AIOWPSEC_MANAGEMENT_PERMISSION, AIOWPSEC_FILESYSTEM_MENU_SLUG, array($this, 'handle_filesystem_menu_rendering'));
|
| 413 |
}
|
| 414 |
+
if (is_multisite() && get_current_blog_id() != 1){
|
| 415 |
//Suppress the Blacklist Manager menu if site is a multi site AND not the main site
|
| 416 |
}else{
|
| 417 |
add_submenu_page(AIOWPSEC_MAIN_MENU_SLUG, __('Blacklist Manager', 'all-in-one-wp-security-and-firewall'), __('Blacklist Manager', 'all-in-one-wp-security-and-firewall') , AIOWPSEC_MANAGEMENT_PERMISSION, AIOWPSEC_BLACKLIST_MENU_SLUG, array($this, 'handle_blacklist_menu_rendering'));
|
| 418 |
}
|
| 419 |
+
if (is_multisite() && get_current_blog_id() != 1){
|
| 420 |
//Suppress the firewall menu if site is a multi site AND not the main site
|
| 421 |
}else{
|
| 422 |
add_submenu_page(AIOWPSEC_MAIN_MENU_SLUG, __('Firewall', 'all-in-one-wp-security-and-firewall'), __('Firewall', 'all-in-one-wp-security-and-firewall') , AIOWPSEC_MANAGEMENT_PERMISSION, AIOWPSEC_FIREWALL_MENU_SLUG, array($this, 'handle_firewall_menu_rendering'));
|
| 423 |
}
|
| 424 |
add_submenu_page(AIOWPSEC_MAIN_MENU_SLUG, __('Brute Force', 'all-in-one-wp-security-and-firewall'), __('Brute Force', 'all-in-one-wp-security-and-firewall') , AIOWPSEC_MANAGEMENT_PERMISSION, AIOWPSEC_BRUTE_FORCE_MENU_SLUG, array($this, 'handle_brute_force_menu_rendering'));
|
| 425 |
add_submenu_page(AIOWPSEC_MAIN_MENU_SLUG, __('SPAM Prevention', 'all-in-one-wp-security-and-firewall'), __('SPAM Prevention', 'all-in-one-wp-security-and-firewall') , AIOWPSEC_MANAGEMENT_PERMISSION, AIOWPSEC_SPAM_MENU_SLUG, array($this, 'handle_spam_menu_rendering'));
|
| 426 |
+
if (is_multisite() && get_current_blog_id() != 1){
|
| 427 |
//Suppress the filescan menu if site is a multi site AND not the main site
|
| 428 |
}else{
|
| 429 |
add_submenu_page(AIOWPSEC_MAIN_MENU_SLUG, __('Scanner', 'all-in-one-wp-security-and-firewall'), __('Scanner', 'all-in-one-wp-security-and-firewall') , AIOWPSEC_MANAGEMENT_PERMISSION, AIOWPSEC_FILESCAN_MENU_SLUG, array($this, 'handle_filescan_menu_rendering'));
|
| 430 |
}
|
| 431 |
add_submenu_page(AIOWPSEC_MAIN_MENU_SLUG, __('Maintenance', 'all-in-one-wp-security-and-firewall'), __('Maintenance', 'all-in-one-wp-security-and-firewall') , AIOWPSEC_MANAGEMENT_PERMISSION, AIOWPSEC_MAINTENANCE_MENU_SLUG, array($this, 'handle_maintenance_menu_rendering'));
|
| 432 |
add_submenu_page(AIOWPSEC_MAIN_MENU_SLUG, __('Miscellaneous', 'all-in-one-wp-security-and-firewall'), __('Miscellaneous', 'all-in-one-wp-security-and-firewall') , AIOWPSEC_MANAGEMENT_PERMISSION, AIOWPSEC_MISC_MENU_SLUG, array($this, 'handle_misc_menu_rendering'));
|
| 433 |
+
add_submenu_page(AIOWPSEC_MAIN_MENU_SLUG, __('Tools', 'all-in-one-wp-security-and-firewall'), __('Tools', 'all-in-one-wp-security-and-firewall'), AIOWPSEC_MANAGEMENT_PERMISSION, AIOWPSEC_TOOLS_MENU_SLUG, array($this, 'handle_tools_menu_rendering'));
|
| 434 |
do_action('aiowpsecurity_admin_menu_created');
|
| 435 |
}
|
| 436 |
|
| 518 |
include_once('wp-security-misc-options-menu.php');
|
| 519 |
$this->misc_menu = new AIOWPSecurity_Misc_Options_Menu();
|
| 520 |
}
|
| 521 |
+
|
| 522 |
+
/**
|
| 523 |
+
* Renders 'Tools' submenu first tab page.
|
| 524 |
+
*
|
| 525 |
+
* @return Void
|
| 526 |
+
*/
|
| 527 |
+
public function handle_tools_menu_rendering() {
|
| 528 |
+
include_once(AIO_WP_SECURITY_PATH.'/admin/wp-security-tools-menu.php');
|
| 529 |
+
new AIOWPSecurity_Tools_Menu();
|
| 530 |
+
}
|
| 531 |
+
|
| 532 |
}//End of class
|
| 533 |
|
|
@@ -67,14 +67,25 @@ abstract class AIOWPSecurity_Admin_Menu
|
|
| 67 |
_e('Settings successfully updated.','all-in-one-wp-security-and-firewall');
|
| 68 |
echo '</strong></p></div>';
|
| 69 |
}
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
function show_msg_updated($msg)
|
| 79 |
{
|
| 80 |
echo '<div id="message" class="updated fade"><p><strong>';
|
|
@@ -114,15 +125,5 @@ abstract class AIOWPSecurity_Admin_Menu
|
|
| 114 |
ob_end_clean();
|
| 115 |
return $output;
|
| 116 |
}
|
| 117 |
-
|
| 118 |
-
static function display_bulk_result_message()
|
| 119 |
-
{
|
| 120 |
-
if(isset($_GET['bulk_count'])) {
|
| 121 |
-
AIOWPSecurity_Admin_Menu::show_msg_updated_st(__('The bulk action was successful', 'all-in-one-wp-security-and-firewall'));
|
| 122 |
-
}
|
| 123 |
-
|
| 124 |
-
if(isset($_GET['bulk_error'])) {
|
| 125 |
-
AIOWPSecurity_Admin_Menu::show_msg_error_st(__('The bulk action failed', 'all-in-one-wp-security-and-firewall'));
|
| 126 |
-
}
|
| 127 |
-
}
|
| 128 |
}
|
| 67 |
_e('Settings successfully updated.','all-in-one-wp-security-and-firewall');
|
| 68 |
echo '</strong></p></div>';
|
| 69 |
}
|
| 70 |
+
|
| 71 |
+
/**
|
| 72 |
+
* Renders record(s) successfully deleted message at top of page.
|
| 73 |
+
*
|
| 74 |
+
* @return Void
|
| 75 |
+
*/
|
| 76 |
+
public static function show_msg_record_deleted_st() {
|
| 77 |
+
AIOWPSecurity_Admin_Menu::show_msg_updated_st(__('Successfully deleted the selected record(s).', 'all-in-one-wp-security-and-firewall'));
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
/**
|
| 81 |
+
* Renders record(s) unsuccessfully deleted message at top of page.
|
| 82 |
+
*
|
| 83 |
+
* @return Void
|
| 84 |
+
*/
|
| 85 |
+
public static function show_msg_record_not_deleted_st() {
|
| 86 |
+
AIOWPSecurity_Admin_Menu::show_msg_error_st(__('Failed to delete the selected record(s).', 'all-in-one-wp-security-and-firewall'));
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
function show_msg_updated($msg)
|
| 90 |
{
|
| 91 |
echo '<div id="message" class="updated fade"><p><strong>';
|
| 125 |
ob_end_clean();
|
| 126 |
return $output;
|
| 127 |
}
|
| 128 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 129 |
}
|
|
@@ -185,33 +185,23 @@ class AIOWPSecurity_Blacklist_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 185 |
<tr valign="top">
|
| 186 |
<th scope="row"><?php _e('Enable IP or User Agent Blacklisting', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 187 |
<td>
|
| 188 |
-
<input name="aiowps_enable_blacklisting" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_blacklisting')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 189 |
-
<
|
| 190 |
</td>
|
| 191 |
</tr>
|
| 192 |
<tr valign="top">
|
| 193 |
-
<th scope="row"><?php _e('Enter IP Addresses:', 'all-in-one-wp-security-and-firewall')?></th>
|
| 194 |
<td>
|
| 195 |
-
<textarea name="aiowps_banned_ip_addresses" rows="5" cols="50"><?php echo ($result == -1)?htmlspecialchars($_POST['aiowps_banned_ip_addresses']):htmlspecialchars($aio_wp_security->configs->get_value('aiowps_banned_ip_addresses')); ?></textarea>
|
| 196 |
<br />
|
| 197 |
<span class="description"><?php _e('Enter one or more IP addresses or IP ranges.','all-in-one-wp-security-and-firewall');?></span>
|
| 198 |
-
|
| 199 |
-
<div class="aiowps_more_info_body">
|
| 200 |
-
<?php
|
| 201 |
-
echo '<p class="description">'.__('Each IP address must be on a new line.', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 202 |
-
echo '<p class="description">'.__('To specify an IP range use a wildcard "*" character. Acceptable ways to use wildcards is shown in the examples below:', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 203 |
-
echo '<p class="description">'.__('Example 1: 195.47.89.*', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 204 |
-
echo '<p class="description">'.__('Example 2: 195.47.*.*', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 205 |
-
echo '<p class="description">'.__('Example 3: 195.*.*.*', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 206 |
-
?>
|
| 207 |
-
</div>
|
| 208 |
-
|
| 209 |
</td>
|
| 210 |
</tr>
|
| 211 |
<tr valign="top">
|
| 212 |
-
<th scope="row"><?php _e('Enter User Agents:', 'all-in-one-wp-security-and-firewall')?></th>
|
| 213 |
<td>
|
| 214 |
-
<textarea name="aiowps_banned_user_agents" rows="5" cols="50"><?php echo ($result == -1)?htmlspecialchars($_POST['aiowps_banned_user_agents']):htmlspecialchars($aio_wp_security->configs->get_value('aiowps_banned_user_agents')); ?></textarea>
|
| 215 |
<br />
|
| 216 |
<span class="description">
|
| 217 |
<?php _e('Enter one or more user agent strings.','all-in-one-wp-security-and-firewall');?></span>
|
| 185 |
<tr valign="top">
|
| 186 |
<th scope="row"><?php _e('Enable IP or User Agent Blacklisting', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 187 |
<td>
|
| 188 |
+
<input id="aiowps_enable_blacklisting" name="aiowps_enable_blacklisting" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_blacklisting')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 189 |
+
<label for="aiowps_enable_blacklisting" class="description"><?php _e('Check this if you want to enable the banning (or blacklisting) of selected IP addresses and/or user agents specified in the settings below', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 190 |
</td>
|
| 191 |
</tr>
|
| 192 |
<tr valign="top">
|
| 193 |
+
<th scope="row"><label for="aiowps_banned_ip_addresses"><?php _e('Enter IP Addresses:', 'all-in-one-wp-security-and-firewall')?></label></th>
|
| 194 |
<td>
|
| 195 |
+
<textarea id="aiowps_banned_ip_addresses" name="aiowps_banned_ip_addresses" rows="5" cols="50"><?php echo ($result == -1)?htmlspecialchars($_POST['aiowps_banned_ip_addresses']):htmlspecialchars($aio_wp_security->configs->get_value('aiowps_banned_ip_addresses')); ?></textarea>
|
| 196 |
<br />
|
| 197 |
<span class="description"><?php _e('Enter one or more IP addresses or IP ranges.','all-in-one-wp-security-and-firewall');?></span>
|
| 198 |
+
<?php $aio_wp_security->include_template('info/ip-address-ip-range-info.php');?>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 199 |
</td>
|
| 200 |
</tr>
|
| 201 |
<tr valign="top">
|
| 202 |
+
<th scope="row"><label for="aiowps_banned_user_agents"><?php _e('Enter User Agents:', 'all-in-one-wp-security-and-firewall')?></label></th>
|
| 203 |
<td>
|
| 204 |
+
<textarea id="aiowps_banned_user_agents" name="aiowps_banned_user_agents" rows="5" cols="50"><?php echo ($result == -1)?htmlspecialchars($_POST['aiowps_banned_user_agents']):htmlspecialchars($aio_wp_security->configs->get_value('aiowps_banned_user_agents')); ?></textarea>
|
| 205 |
<br />
|
| 206 |
<span class="description">
|
| 207 |
<?php _e('Enter one or more user agent strings.','all-in-one-wp-security-and-firewall');?></span>
|
|
@@ -10,16 +10,20 @@ class AIOWPSecurity_Brute_Force_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 10 |
/* Specify all the tabs of this menu in the following array */
|
| 11 |
var $menu_tabs;
|
| 12 |
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
'tab1' => 'render_tab1',
|
| 15 |
'tab2' => 'render_tab2',
|
| 16 |
'tab3' => 'render_tab3',
|
| 17 |
'tab4' => 'render_tab4',
|
| 18 |
'tab5' => 'render_tab5',
|
| 19 |
-
|
| 20 |
|
| 21 |
-
function __construct()
|
| 22 |
-
{
|
| 23 |
$this->render_menu_page();
|
| 24 |
}
|
| 25 |
|
|
@@ -31,7 +35,6 @@ class AIOWPSecurity_Brute_Force_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 31 |
'tab3' => __('Login Captcha', 'all-in-one-wp-security-and-firewall'),
|
| 32 |
'tab4' => __('Login Whitelist', 'all-in-one-wp-security-and-firewall'),
|
| 33 |
'tab5' => __('Honeypot', 'all-in-one-wp-security-and-firewall'),
|
| 34 |
-
|
| 35 |
);
|
| 36 |
}
|
| 37 |
|
|
@@ -45,7 +48,7 @@ class AIOWPSecurity_Brute_Force_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 45 |
echo '<h2 class="nav-tab-wrapper">';
|
| 46 |
foreach ( $this->menu_tabs as $tab_key => $tab_caption )
|
| 47 |
{
|
| 48 |
-
if (
|
| 49 |
&& stristr($tab_caption, "Rename Login Page") === false && stristr($tab_caption, "Login Captcha") === false){
|
| 50 |
//Suppress the all Brute Force menu tabs if site is a multi site AND not the main site except "rename login" and "captcha"
|
| 51 |
}else{
|
|
@@ -84,9 +87,9 @@ class AIOWPSecurity_Brute_Force_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 84 |
$aiowps_login_page_slug = '';
|
| 85 |
|
| 86 |
if (get_option('permalink_structure')){
|
| 87 |
-
$
|
| 88 |
}else{
|
| 89 |
-
$
|
| 90 |
}
|
| 91 |
|
| 92 |
if(isset($_POST['aiowps_save_rename_login_page_settings']))//Do form submission tasks
|
|
@@ -95,8 +98,8 @@ class AIOWPSecurity_Brute_Force_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 95 |
$nonce=$_REQUEST['_wpnonce'];
|
| 96 |
if (!wp_verify_nonce($nonce, 'aiowpsec-rename-login-page-nonce'))
|
| 97 |
{
|
| 98 |
-
$aio_wp_security->debug_logger->log_debug("Nonce check failed for rename login page save
|
| 99 |
-
die("Nonce check failed for rename login page save
|
| 100 |
}
|
| 101 |
|
| 102 |
if (empty($_POST['aiowps_login_page_slug']) && isset($_POST["aiowps_enable_rename_login_page"])){
|
|
@@ -118,21 +121,12 @@ class AIOWPSecurity_Brute_Force_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 118 |
//Save all the form values to the options
|
| 119 |
if (isset($_POST["aiowps_enable_rename_login_page"])){
|
| 120 |
$aio_wp_security->configs->set_value('aiowps_enable_rename_login_page', '1');
|
| 121 |
-
// check if the cookie based feature was active and deactivate it and delete the directives in .htaccess
|
| 122 |
-
if($aio_wp_security->configs->get_value('aiowps_enable_brute_force_attack_prevention')){
|
| 123 |
-
$cookie_feature_active = true;
|
| 124 |
-
$aio_wp_security->configs->set_value('aiowps_enable_brute_force_attack_prevention', '');//deactivate cookie based feature
|
| 125 |
-
}
|
| 126 |
}else{
|
| 127 |
$aio_wp_security->configs->set_value('aiowps_enable_rename_login_page', '');
|
| 128 |
}
|
| 129 |
$aio_wp_security->configs->set_value('aiowps_login_page_slug',$aiowps_login_page_slug);
|
| 130 |
$aio_wp_security->configs->save_config();
|
| 131 |
|
| 132 |
-
// if cookie based feature was active previously need to clear those rules out of .htaccess
|
| 133 |
-
if($cookie_feature_active){
|
| 134 |
-
$htaccess_res = AIOWPSecurity_Utility_Htaccess::write_to_htaccess(); //Delete the cookie based directives
|
| 135 |
-
}
|
| 136 |
|
| 137 |
//Recalculate points after the feature status/options have been altered
|
| 138 |
$aiowps_feature_mgr->check_feature_status_and_recalculate_points();
|
|
@@ -178,8 +172,7 @@ class AIOWPSecurity_Brute_Force_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 178 |
<div class="aio_yellow_box">
|
| 179 |
<p><?php _e('Your WordPress login page URL has been renamed.', 'all-in-one-wp-security-and-firewall'); ?></p>
|
| 180 |
<p><?php _e('Your current login URL is:', 'all-in-one-wp-security-and-firewall'); ?></p>
|
| 181 |
-
<p><strong><?php echo $
|
| 182 |
-
<p><strong><?php _e('NOTE: If you already had the Cookie-Based Brute Force Prevention feature active, the plugin has automatically deactivated it because only one of these features can be active at any one time.', 'all-in-one-wp-security-and-firewall'); ?></strong></p>
|
| 183 |
</div>
|
| 184 |
|
| 185 |
<?php
|
|
@@ -207,13 +200,13 @@ class AIOWPSecurity_Brute_Force_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 207 |
<tr valign="top">
|
| 208 |
<th scope="row"><?php _e('Enable Rename Login Page Feature', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 209 |
<td>
|
| 210 |
-
<input name="aiowps_enable_rename_login_page" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_rename_login_page')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 211 |
-
<
|
| 212 |
</td>
|
| 213 |
</tr>
|
| 214 |
<tr valign="top">
|
| 215 |
-
<th scope="row"><?php _e('Login Page URL', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 216 |
-
<td><code><?php echo $
|
| 217 |
<span class="description"><?php _e('Enter a string which will represent your secure login page slug. You are encouraged to choose something which is hard to guess and only you will remember.', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 218 |
</td>
|
| 219 |
</tr>
|
|
@@ -225,101 +218,79 @@ class AIOWPSecurity_Brute_Force_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 225 |
<?php
|
| 226 |
}
|
| 227 |
|
| 228 |
-
|
| 229 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 230 |
global $aio_wp_security;
|
| 231 |
global $aiowps_feature_mgr;
|
| 232 |
$error = false;
|
|
|
|
| 233 |
|
| 234 |
//Save settings for brute force cookie method
|
| 235 |
-
if(isset($_POST['aiowps_apply_cookie_based_bruteforce_firewall']))
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
{
|
| 240 |
-
$aio_wp_security->debug_logger->log_debug("Nonce check failed on enable cookie based brute force prevention feature!",4);
|
| 241 |
-
die("Nonce check failed on enable cookie based brute force prevention feature!");
|
| 242 |
}
|
| 243 |
|
| 244 |
-
if(isset($_POST['aiowps_enable_brute_force_attack_prevention']))
|
| 245 |
-
{
|
| 246 |
$brute_force_feature_secret_word = sanitize_text_field($_POST['aiowps_brute_force_secret_word']);
|
| 247 |
-
if(empty($brute_force_feature_secret_word)){
|
| 248 |
-
$brute_force_feature_secret_word = "
|
| 249 |
-
}
|
| 250 |
$msg = '<p>'.__('Settings have not been saved - your secret word must consist only of alphanumeric characters, ie, letters and/or numbers only!', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 251 |
$error = true;
|
| 252 |
}
|
| 253 |
|
| 254 |
-
if(filter_var($_POST['aiowps_cookie_based_brute_force_redirect_url'], FILTER_VALIDATE_URL))
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
else
|
| 259 |
-
{
|
| 260 |
-
$aio_wp_security->configs->set_value('aiowps_cookie_based_brute_force_redirect_url','http://127.0.0.1');
|
| 261 |
}
|
|
|
|
| 262 |
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
if (!$error)
|
| 267 |
-
{
|
| 268 |
-
$aio_wp_security->configs->set_value('aiowps_brute_force_secret_word',$brute_force_feature_secret_word);
|
| 269 |
$msg = '<p>'.__('You have successfully enabled the cookie based brute force prevention feature', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 270 |
$msg .= '<p>'.__('From now on you will need to log into your WP Admin using the following URL:', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 271 |
$msg .= '<p><strong>'.AIOWPSEC_WP_URL.'/?'.$brute_force_feature_secret_word.'=1</strong></p>';
|
| 272 |
$msg .= '<p>'.__('It is important that you save this URL value somewhere in case you forget it, OR,', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 273 |
$msg .= '<p>'.sprintf( __('simply remember to add a "?%s=1" to your current site URL address.', 'all-in-one-wp-security-and-firewall'), $brute_force_feature_secret_word).'</p>';
|
| 274 |
}
|
| 275 |
-
}
|
| 276 |
-
|
| 277 |
-
{
|
| 278 |
-
$aio_wp_security->configs->set_value('aiowps_enable_brute_force_attack_prevention','');
|
| 279 |
$msg = __('You have successfully saved cookie based brute force prevention feature settings.', 'all-in-one-wp-security-and-firewall');
|
| 280 |
}
|
| 281 |
|
| 282 |
-
if(isset($_POST['aiowps_brute_force_attack_prevention_pw_protected_exception']))
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
else
|
| 287 |
-
{
|
| 288 |
-
$aio_wp_security->configs->set_value('aiowps_brute_force_attack_prevention_pw_protected_exception','');
|
| 289 |
}
|
| 290 |
|
| 291 |
-
if(isset($_POST['aiowps_brute_force_attack_prevention_ajax_exception']))
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
else
|
| 296 |
-
{
|
| 297 |
-
$aio_wp_security->configs->set_value('aiowps_brute_force_attack_prevention_ajax_exception','');
|
| 298 |
}
|
| 299 |
|
| 300 |
-
if (!$error)
|
| 301 |
-
{
|
| 302 |
$aio_wp_security->configs->save_config();//save the value
|
| 303 |
|
| 304 |
//Recalculate points after the feature status/options have been altered
|
| 305 |
$aiowps_feature_mgr->check_feature_status_and_recalculate_points();
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
echo $msg;
|
| 311 |
-
echo '</p></div>';
|
| 312 |
-
}
|
| 313 |
-
else {
|
| 314 |
-
$this->show_msg_error(__('Could not write to the .htaccess file. Please check the file permissions.', 'all-in-one-wp-security-and-firewall'));
|
| 315 |
}
|
| 316 |
-
}
|
| 317 |
-
else
|
| 318 |
-
{
|
| 319 |
$this->show_msg_error($msg);
|
| 320 |
}
|
| 321 |
}
|
| 322 |
-
|
| 323 |
?>
|
| 324 |
<h2><?php _e('Brute Force Prevention Firewall Settings', 'all-in-one-wp-security-and-firewall')?></h2>
|
| 325 |
|
|
@@ -328,35 +299,18 @@ class AIOWPSecurity_Brute_Force_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 328 |
//TODO - need to fix the following message
|
| 329 |
echo '<p>'.__('A Brute Force Attack is when a hacker tries many combinations of usernames and passwords until they succeed in guessing the right combination.', 'all-in-one-wp-security-and-firewall').
|
| 330 |
'<br />'.__('Due to the fact that at any one time there may be many concurrent login attempts occurring on your site via malicious automated robots, this also has a negative impact on your server\'s memory and performance.', 'all-in-one-wp-security-and-firewall').
|
| 331 |
-
'<br />'.__('The features in this tab will stop the majority of
|
| 332 |
?>
|
| 333 |
</div>
|
| 334 |
<div class="aio_yellow_box">
|
| 335 |
<?php
|
| 336 |
$backup_tab_link = '<a href="admin.php?page='.AIOWPSEC_SETTINGS_MENU_SLUG.'&tab=tab2" target="_blank">'.__('backup', 'all-in-one-wp-security-and-firewall').'</a>';
|
| 337 |
$video_link = '<a href="https://www.tipsandtricks-hq.com/all-in-one-wp-security-plugin-cookie-based-brute-force-login-attack-prevention-feature-5994" target="_blank">'.__('video tutorial', 'all-in-one-wp-security-and-firewall').'</a>';
|
| 338 |
-
$info_msg = sprintf( __('
|
| 339 |
-
$info_msg1 = __('If this feature is not used correctly, you can get locked out of your site. A backed up .htaccess file will come in handy if that happens.', 'all-in-one-wp-security-and-firewall');
|
| 340 |
-
$info_msg2 = sprintf( __('To learn more about how to use this feature please watch the following %s.', 'all-in-one-wp-security-and-firewall'), $video_link);
|
| 341 |
$brute_force_login_feature_link = '<a href="admin.php?page='.AIOWPSEC_FIREWALL_MENU_SLUG.'&tab=tab4" target="_blank">'.__('Cookie-Based Brute Force Login Prevention', 'all-in-one-wp-security-and-firewall').'</a>';
|
| 342 |
-
echo '<p>'
|
| 343 |
-
'<br />'.$info_msg1.
|
| 344 |
-
'<br />'.$info_msg2.'</p>';
|
| 345 |
?>
|
| 346 |
</div>
|
| 347 |
-
<?php
|
| 348 |
-
//Show the user the new login URL if this feature is active
|
| 349 |
-
if ($aio_wp_security->configs->get_value('aiowps_enable_brute_force_attack_prevention')=='1')
|
| 350 |
-
{
|
| 351 |
-
?>
|
| 352 |
-
<div class="aio_yellow_box">
|
| 353 |
-
<p><strong><?php _e('NOTE: If you already had the Rename Login Page feature active, the plugin has automatically deactivated it because only one of these features can be active at any one time.', 'all-in-one-wp-security-and-firewall'); ?></strong></p>
|
| 354 |
-
</div>
|
| 355 |
-
|
| 356 |
-
<?php
|
| 357 |
-
}
|
| 358 |
-
?>
|
| 359 |
-
|
| 360 |
<div class="postbox">
|
| 361 |
<h3 class="hndle"><label for="title"><?php _e('Cookie Based Brute Force Login Prevention', 'all-in-one-wp-security-and-firewall'); ?></label></h3>
|
| 362 |
<div class="inside">
|
|
@@ -372,12 +326,46 @@ class AIOWPSecurity_Brute_Force_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 372 |
<?php _e('This feature can lock you out of admin if it doesn\'t work correctly on your site. You <a href="https://www.tipsandtricks-hq.com/wordpress-security-and-firewall-plugin#advanced_features_note" target="_blank">'.__('must read this message', 'all-in-one-wp-security-and-firewall').'</a> before activating this feature.', 'all-in-one-wp-security-and-firewall'); ?>
|
| 373 |
</p>
|
| 374 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 375 |
<table class="form-table">
|
| 376 |
<tr valign="top">
|
| 377 |
-
<th scope="row"><?php _e('Enable
|
| 378 |
<td>
|
| 379 |
-
<input name="aiowps_enable_brute_force_attack_prevention" type="checkbox"<?php checked($aio_wp_security->configs->get_value('aiowps_enable_brute_force_attack_prevention'))
|
| 380 |
-
|
| 381 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 382 |
<div class="aiowps_more_info_body">
|
| 383 |
<p class="description">
|
|
@@ -399,14 +387,14 @@ class AIOWPSecurity_Brute_Force_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 399 |
</td>
|
| 400 |
</tr>
|
| 401 |
<tr valign="top">
|
| 402 |
-
<th scope="row"><?php _e('Secret Word', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 403 |
-
<td><input type="text" size="40" name="aiowps_brute_force_secret_word" value="<?php echo $aio_wp_security->configs->get_value('aiowps_brute_force_secret_word'); ?>"<?php
|
| 404 |
<span class="description"><?php _e('Choose a secret word consisting of alphanumeric characters which you can use to access your special URL. Your are highly encouraged to choose a word which will be difficult to guess.', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 405 |
</td>
|
| 406 |
</tr>
|
| 407 |
<tr valign="top">
|
| 408 |
-
<th scope="row"><?php _e('Re-direct URL', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 409 |
-
<td><input type="text" size="40" name="aiowps_cookie_based_brute_force_redirect_url" value="<?php echo $aio_wp_security->configs->get_value('aiowps_cookie_based_brute_force_redirect_url'); ?>"<?php
|
| 410 |
<span class="description">
|
| 411 |
<?php
|
| 412 |
_e('Specify a URL to redirect a hacker to when they try to access your WordPress login page.', 'all-in-one-wp-security-and-firewall');
|
|
@@ -431,17 +419,17 @@ class AIOWPSecurity_Brute_Force_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 431 |
</td>
|
| 432 |
</tr>
|
| 433 |
<tr valign="top">
|
| 434 |
-
<th scope="row"><?php _e('My
|
| 435 |
<td>
|
| 436 |
-
<input name="aiowps_brute_force_attack_prevention_pw_protected_exception" type="checkbox"<?php
|
| 437 |
-
<
|
| 438 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 439 |
<div class="aiowps_more_info_body">
|
| 440 |
<p class="description">
|
| 441 |
<?php
|
| 442 |
-
_e('In the cases where you are protecting some of your posts or pages using the in-built WordPress password protection feature, a few extra lines of directives and exceptions need to be added
|
| 443 |
echo '<br />';
|
| 444 |
-
_e('By enabling this checkbox the plugin will add the necessary rules and exceptions
|
| 445 |
echo '<br />';
|
| 446 |
echo "<strong>".__('Helpful Tip:', 'all-in-one-wp-security-and-firewall')."</strong>";
|
| 447 |
echo '<br />';
|
|
@@ -452,17 +440,17 @@ class AIOWPSecurity_Brute_Force_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 452 |
</td>
|
| 453 |
</tr>
|
| 454 |
<tr valign="top">
|
| 455 |
-
<th scope="row"><?php _e('My
|
| 456 |
<td>
|
| 457 |
-
<input name="aiowps_brute_force_attack_prevention_ajax_exception" type="checkbox"<?php
|
| 458 |
-
<
|
| 459 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 460 |
<div class="aiowps_more_info_body">
|
| 461 |
<p class="description">
|
| 462 |
<?php
|
| 463 |
-
_e('In the cases where your WordPress installation has a theme or
|
| 464 |
echo '<br />';
|
| 465 |
-
_e('By enabling this checkbox the plugin will add the necessary rules and exceptions
|
| 466 |
?>
|
| 467 |
</p>
|
| 468 |
</div>
|
|
@@ -470,33 +458,9 @@ class AIOWPSecurity_Brute_Force_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 470 |
</tr>
|
| 471 |
</table>
|
| 472 |
<?php
|
| 473 |
-
$
|
| 474 |
-
|
| 475 |
-
|
| 476 |
-
{
|
| 477 |
-
if (isset($_REQUEST['aiowps_cookie_test']))
|
| 478 |
-
{//Cookie test was just performed and the test succeded
|
| 479 |
-
echo '<div class="aio_green_box"><p>';
|
| 480 |
-
_e('The cookie test was successful. You can now enable this feature.', 'all-in-one-wp-security-and-firewall');
|
| 481 |
-
echo '</p></div>';
|
| 482 |
-
}
|
| 483 |
-
echo '<input type="submit" name="aiowps_apply_cookie_based_bruteforce_firewall" value="'.__('Save Feature Settings', 'all-in-one-wp-security-and-firewall').'" class="button-primary" />';
|
| 484 |
-
}
|
| 485 |
-
else
|
| 486 |
-
{
|
| 487 |
-
//Cookie test needs to be performed
|
| 488 |
-
if(isset($_REQUEST['aiowps_cookie_test']) && $cookie_test_value != '1'){//Test failed
|
| 489 |
-
echo '<div class="aio_red_box"><p>';
|
| 490 |
-
_e('The cookie test failed on this server. So this feature cannot be used on this site.', 'all-in-one-wp-security-and-firewall');
|
| 491 |
-
echo '</p></div>';
|
| 492 |
-
}
|
| 493 |
-
|
| 494 |
-
echo '<div class="aio_yellow_box"><p>';
|
| 495 |
-
_e("Before using this feature you are required to perform a cookie test first. This is to make sure that your browser cookie is working correctly and that you won't lock yourself out.", 'all-in-one-wp-security-and-firewall');
|
| 496 |
-
echo '</p></div>';
|
| 497 |
-
echo '<input type="submit" name="aiowps_do_cookie_test_for_bfla" value="'.__('Perform Cookie Test', 'all-in-one-wp-security-and-firewall').'" class="button-primary" />';
|
| 498 |
-
}
|
| 499 |
-
?>
|
| 500 |
</form>
|
| 501 |
</div></div>
|
| 502 |
<?php
|
|
@@ -507,14 +471,11 @@ class AIOWPSecurity_Brute_Force_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 507 |
global $aio_wp_security;
|
| 508 |
global $aiowps_feature_mgr;
|
| 509 |
|
| 510 |
-
if(isset($_POST['aiowpsec_save_captcha_settings']))//Do form submission tasks
|
| 511 |
-
|
| 512 |
-
|
| 513 |
-
|
| 514 |
-
|
| 515 |
-
{
|
| 516 |
-
$aio_wp_security->debug_logger->log_debug("Nonce check failed on captcha settings save!",4);
|
| 517 |
-
die("Nonce check failed on captcha settings save!");
|
| 518 |
}
|
| 519 |
|
| 520 |
|
|
@@ -539,7 +500,7 @@ class AIOWPSecurity_Brute_Force_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 539 |
}
|
| 540 |
}
|
| 541 |
|
| 542 |
-
$aio_wp_security->configs->set_value('aiowps_default_recaptcha',isset($_POST["aiowps_default_recaptcha"])?'1':'');//Checkbox
|
| 543 |
$aio_wp_security->configs->save_config();
|
| 544 |
|
| 545 |
//Recalculate points after the feature status/options have been altered
|
|
@@ -592,18 +553,18 @@ class AIOWPSecurity_Brute_Force_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 592 |
<tr valign="top">
|
| 593 |
<th scope="row"><?php _e('Use Google reCAPTCHA as default', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 594 |
<td>
|
| 595 |
-
<input name="aiowps_default_recaptcha" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_default_recaptcha')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 596 |
-
<
|
| 597 |
</td>
|
| 598 |
</tr>
|
| 599 |
<tr valign="top">
|
| 600 |
-
<th scope="row"><?php _e('Site Key', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 601 |
-
<td><input type="text" size="50" name="aiowps_recaptcha_site_key" value="<?php echo esc_html( $aio_wp_security->configs->get_value('aiowps_recaptcha_site_key') ); ?>" />
|
| 602 |
</td>
|
| 603 |
</tr>
|
| 604 |
<tr valign="top">
|
| 605 |
-
<th scope="row"><?php _e('Secret Key', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 606 |
-
<td><input type="text" size="50" name="aiowps_recaptcha_secret_key" value="<?php echo esc_html(
|
| 607 |
</td>
|
| 608 |
</tr>
|
| 609 |
</table>
|
|
@@ -620,8 +581,8 @@ class AIOWPSecurity_Brute_Force_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 620 |
<tr valign="top">
|
| 621 |
<th scope="row"><?php _e('Enable Captcha On Login Page', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 622 |
<td>
|
| 623 |
-
<input name="aiowps_enable_login_captcha" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_login_captcha')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 624 |
-
<
|
| 625 |
</td>
|
| 626 |
</tr>
|
| 627 |
</table>
|
|
@@ -639,8 +600,8 @@ class AIOWPSecurity_Brute_Force_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 639 |
<tr valign="top">
|
| 640 |
<th scope="row"><?php _e('Enable Captcha On Lost Password Page', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 641 |
<td>
|
| 642 |
-
<input name="aiowps_enable_lost_password_captcha" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_lost_password_captcha')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 643 |
-
<
|
| 644 |
</td>
|
| 645 |
</tr>
|
| 646 |
</table>
|
|
@@ -657,15 +618,15 @@ class AIOWPSecurity_Brute_Force_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 657 |
<tr valign="top">
|
| 658 |
<th scope="row"><?php _e('Enable Captcha On Custom Login Form', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 659 |
<td>
|
| 660 |
-
<input name="aiowps_enable_custom_login_captcha" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_custom_login_captcha')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 661 |
-
<
|
| 662 |
</td>
|
| 663 |
</tr>
|
| 664 |
</table>
|
| 665 |
</div></div>
|
| 666 |
<?php
|
| 667 |
// Only display woocommerce captcha settings if woo is active
|
| 668 |
-
if (
|
| 669 |
?>
|
| 670 |
<div class="postbox">
|
| 671 |
<h3 class="hndle"><label for="title"><?php _e('Woocommerce Forms Captcha Settings', 'all-in-one-wp-security-and-firewall'); ?></label></h3>
|
|
@@ -679,8 +640,8 @@ class AIOWPSecurity_Brute_Force_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 679 |
<tr valign="top">
|
| 680 |
<th scope="row"><?php _e('Enable Captcha On Woocommerce Login Form', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 681 |
<td>
|
| 682 |
-
<input name="aiowps_enable_woo_login_captcha" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_woo_login_captcha')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 683 |
-
<
|
| 684 |
</td>
|
| 685 |
</tr>
|
| 686 |
</table>
|
|
@@ -692,8 +653,8 @@ class AIOWPSecurity_Brute_Force_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 692 |
<tr valign="top">
|
| 693 |
<th scope="row"><?php _e('Enable Captcha On Woocommerce Lost Password Form', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 694 |
<td>
|
| 695 |
-
<input name="aiowps_enable_woo_lostpassword_captcha" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_woo_lostpassword_captcha')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 696 |
-
<
|
| 697 |
</td>
|
| 698 |
</tr>
|
| 699 |
</table>
|
|
@@ -705,8 +666,8 @@ class AIOWPSecurity_Brute_Force_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 705 |
<tr valign="top">
|
| 706 |
<th scope="row"><?php _e('Enable Captcha On Woocommerce Registration Form', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 707 |
<td>
|
| 708 |
-
<input name="aiowps_enable_woo_register_captcha" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_woo_register_captcha')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 709 |
-
<
|
| 710 |
</td>
|
| 711 |
</tr>
|
| 712 |
</table>
|
|
@@ -731,8 +692,8 @@ class AIOWPSecurity_Brute_Force_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 731 |
$nonce=$_REQUEST['_wpnonce'];
|
| 732 |
if (!wp_verify_nonce($nonce, 'aiowpsec-whitelist-settings-nonce'))
|
| 733 |
{
|
| 734 |
-
$aio_wp_security->debug_logger->log_debug("Nonce check failed for save whitelist settings
|
| 735 |
-
die(__('Nonce check failed for save whitelist settings
|
| 736 |
}
|
| 737 |
|
| 738 |
if (isset($_POST["aiowps_enable_whitelisting"]) && empty($_POST['aiowps_allowed_ip_addresses']))
|
|
@@ -819,36 +780,24 @@ class AIOWPSecurity_Brute_Force_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 819 |
<tr valign="top">
|
| 820 |
<th scope="row"><?php _e('Enable IP Whitelisting', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 821 |
<td>
|
| 822 |
-
<input name="aiowps_enable_whitelisting" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_whitelisting')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 823 |
-
<
|
| 824 |
</td>
|
| 825 |
</tr>
|
| 826 |
<tr valign="top">
|
| 827 |
-
<th scope="row"><?php _e('Your Current IP Address', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 828 |
<td>
|
| 829 |
-
<input size="20" name="aiowps_user_ip" type="text" value="<?php echo $your_ip_address; ?>" readonly="readonly"/>
|
| 830 |
<span class="description"><?php _e('You can copy and paste this address in the text box below if you want to include it in your login whitelist.', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 831 |
</td>
|
| 832 |
</tr>
|
| 833 |
<tr valign="top">
|
| 834 |
-
<th scope="row"><?php _e('Enter Whitelisted IP Addresses:', 'all-in-one-wp-security-and-firewall')?></th>
|
| 835 |
<td>
|
| 836 |
-
<textarea name="aiowps_allowed_ip_addresses" rows="5" cols="50"><?php echo ($result
|
| 837 |
<br />
|
| 838 |
-
<span class="description"><?php _e('Enter one or more IP addresses or IP ranges you wish to include in your whitelist. Only the addresses specified here will have access to the WordPress login page.','all-in-one-wp-security-and-firewall');?></span>
|
| 839 |
-
|
| 840 |
-
<div class="aiowps_more_info_body">
|
| 841 |
-
<?php
|
| 842 |
-
echo '<p class="description"><strong>'.__('Each IP address must be on a new line.', 'all-in-one-wp-security-and-firewall').'</strong></p>';
|
| 843 |
-
echo '<p class="description">'.__('To specify an IPv4 range use a wildcard "*" character. Acceptable ways to use wildcards is shown in the examples below:', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 844 |
-
echo '<p class="description">'.__('Example 1: 195.47.89.*', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 845 |
-
echo '<p class="description">'.__('Example 2: 195.47.*.*', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 846 |
-
echo '<p class="description">'.__('Example 3: 195.*.*.*', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 847 |
-
echo '<p class="description">'.__('Or you can enter an IPv6 address (NOTE: ranges/wildcards are currently not supported for ipv6)', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 848 |
-
echo '<p class="description">'.__('Example 4: 4102:0:3ea6:79fd:b:46f8:230f:bb05', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 849 |
-
echo '<p class="description">'.__('Example 5: 2205:0:1ca2:810d::', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 850 |
-
?>
|
| 851 |
-
</div>
|
| 852 |
|
| 853 |
</td>
|
| 854 |
</tr>
|
|
@@ -870,8 +819,8 @@ class AIOWPSecurity_Brute_Force_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 870 |
$nonce=$_REQUEST['_wpnonce'];
|
| 871 |
if (!wp_verify_nonce($nonce, 'aiowpsec-honeypot-settings-nonce'))
|
| 872 |
{
|
| 873 |
-
$aio_wp_security->debug_logger->log_debug("Nonce check failed on honeypot settings save
|
| 874 |
-
die("Nonce check failed on honeypot settings save
|
| 875 |
}
|
| 876 |
|
| 877 |
//Save all the form values to the options
|
|
@@ -908,8 +857,8 @@ class AIOWPSecurity_Brute_Force_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 908 |
<tr valign="top">
|
| 909 |
<th scope="row"><?php _e('Enable Honeypot On Login Page', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 910 |
<td>
|
| 911 |
-
<input name="aiowps_enable_login_honeypot" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_login_honeypot')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 912 |
-
<
|
| 913 |
</td>
|
| 914 |
</tr>
|
| 915 |
</table>
|
|
@@ -919,6 +868,5 @@ class AIOWPSecurity_Brute_Force_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 919 |
</form>
|
| 920 |
<?php
|
| 921 |
}
|
| 922 |
-
|
| 923 |
-
|
| 924 |
} //end class
|
| 10 |
/* Specify all the tabs of this menu in the following array */
|
| 11 |
var $menu_tabs;
|
| 12 |
|
| 13 |
+
/**
|
| 14 |
+
* Tab slugs and respective render functions.
|
| 15 |
+
*
|
| 16 |
+
* @var string[]
|
| 17 |
+
*/
|
| 18 |
+
private $menu_tabs_handler = array(
|
| 19 |
'tab1' => 'render_tab1',
|
| 20 |
'tab2' => 'render_tab2',
|
| 21 |
'tab3' => 'render_tab3',
|
| 22 |
'tab4' => 'render_tab4',
|
| 23 |
'tab5' => 'render_tab5',
|
| 24 |
+
);
|
| 25 |
|
| 26 |
+
public function __construct() {
|
|
|
|
| 27 |
$this->render_menu_page();
|
| 28 |
}
|
| 29 |
|
| 35 |
'tab3' => __('Login Captcha', 'all-in-one-wp-security-and-firewall'),
|
| 36 |
'tab4' => __('Login Whitelist', 'all-in-one-wp-security-and-firewall'),
|
| 37 |
'tab5' => __('Honeypot', 'all-in-one-wp-security-and-firewall'),
|
|
|
|
| 38 |
);
|
| 39 |
}
|
| 40 |
|
| 48 |
echo '<h2 class="nav-tab-wrapper">';
|
| 49 |
foreach ( $this->menu_tabs as $tab_key => $tab_caption )
|
| 50 |
{
|
| 51 |
+
if (is_multisite() && get_current_blog_id() != 1
|
| 52 |
&& stristr($tab_caption, "Rename Login Page") === false && stristr($tab_caption, "Login Captcha") === false){
|
| 53 |
//Suppress the all Brute Force menu tabs if site is a multi site AND not the main site except "rename login" and "captcha"
|
| 54 |
}else{
|
| 87 |
$aiowps_login_page_slug = '';
|
| 88 |
|
| 89 |
if (get_option('permalink_structure')){
|
| 90 |
+
$site_url = trailingslashit(site_url());
|
| 91 |
}else{
|
| 92 |
+
$site_url = trailingslashit(site_url()) . '?';
|
| 93 |
}
|
| 94 |
|
| 95 |
if(isset($_POST['aiowps_save_rename_login_page_settings']))//Do form submission tasks
|
| 98 |
$nonce=$_REQUEST['_wpnonce'];
|
| 99 |
if (!wp_verify_nonce($nonce, 'aiowpsec-rename-login-page-nonce'))
|
| 100 |
{
|
| 101 |
+
$aio_wp_security->debug_logger->log_debug("Nonce check failed for rename login page save.",4);
|
| 102 |
+
die("Nonce check failed for rename login page save.");
|
| 103 |
}
|
| 104 |
|
| 105 |
if (empty($_POST['aiowps_login_page_slug']) && isset($_POST["aiowps_enable_rename_login_page"])){
|
| 121 |
//Save all the form values to the options
|
| 122 |
if (isset($_POST["aiowps_enable_rename_login_page"])){
|
| 123 |
$aio_wp_security->configs->set_value('aiowps_enable_rename_login_page', '1');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 124 |
}else{
|
| 125 |
$aio_wp_security->configs->set_value('aiowps_enable_rename_login_page', '');
|
| 126 |
}
|
| 127 |
$aio_wp_security->configs->set_value('aiowps_login_page_slug',$aiowps_login_page_slug);
|
| 128 |
$aio_wp_security->configs->save_config();
|
| 129 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
|
| 131 |
//Recalculate points after the feature status/options have been altered
|
| 132 |
$aiowps_feature_mgr->check_feature_status_and_recalculate_points();
|
| 172 |
<div class="aio_yellow_box">
|
| 173 |
<p><?php _e('Your WordPress login page URL has been renamed.', 'all-in-one-wp-security-and-firewall'); ?></p>
|
| 174 |
<p><?php _e('Your current login URL is:', 'all-in-one-wp-security-and-firewall'); ?></p>
|
| 175 |
+
<p><strong><?php echo $site_url.$aio_wp_security->configs->get_value('aiowps_login_page_slug'); ?></strong></p>
|
|
|
|
| 176 |
</div>
|
| 177 |
|
| 178 |
<?php
|
| 200 |
<tr valign="top">
|
| 201 |
<th scope="row"><?php _e('Enable Rename Login Page Feature', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 202 |
<td>
|
| 203 |
+
<input id="aiowps_enable_rename_login_page" name="aiowps_enable_rename_login_page" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_rename_login_page')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 204 |
+
<label for="aiowps_enable_rename_login_page" class="description"><?php _e('Check this if you want to enable the rename login page feature', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 205 |
</td>
|
| 206 |
</tr>
|
| 207 |
<tr valign="top">
|
| 208 |
+
<th scope="row"><label for="aiowps_login_page_slug"><?php _e('Login Page URL', 'all-in-one-wp-security-and-firewall')?>:</label></th>
|
| 209 |
+
<td><code><?php echo $site_url; ?></code><input id="aiowps_login_page_slug" type="text" size="15" name="aiowps_login_page_slug" value="<?php echo $aio_wp_security->configs->get_value('aiowps_login_page_slug'); ?>" />
|
| 210 |
<span class="description"><?php _e('Enter a string which will represent your secure login page slug. You are encouraged to choose something which is hard to guess and only you will remember.', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 211 |
</td>
|
| 212 |
</tr>
|
| 218 |
<?php
|
| 219 |
}
|
| 220 |
|
| 221 |
+
/**
|
| 222 |
+
* Render content of the cookie based brute force prevention tab.
|
| 223 |
+
*
|
| 224 |
+
* @return Void
|
| 225 |
+
*/
|
| 226 |
+
private function render_tab2() {
|
| 227 |
global $aio_wp_security;
|
| 228 |
global $aiowps_feature_mgr;
|
| 229 |
$error = false;
|
| 230 |
+
$msg = '';
|
| 231 |
|
| 232 |
//Save settings for brute force cookie method
|
| 233 |
+
if (isset($_POST['aiowps_apply_cookie_based_bruteforce_firewall'])) {
|
| 234 |
+
if (!wp_verify_nonce($_POST['_wpnonce'], 'aiowpsec-enable-cookie-based-brute-force-prevention')) {
|
| 235 |
+
$aio_wp_security->debug_logger->log_debug('Nonce check failed on enable cookie based brute force prevention feature.',4);
|
| 236 |
+
die('Nonce check failed on enable cookie based brute force prevention feature.');
|
|
|
|
|
|
|
|
|
|
| 237 |
}
|
| 238 |
|
| 239 |
+
if (isset($_POST['aiowps_enable_brute_force_attack_prevention'])) {
|
|
|
|
| 240 |
$brute_force_feature_secret_word = sanitize_text_field($_POST['aiowps_brute_force_secret_word']);
|
| 241 |
+
if (empty($brute_force_feature_secret_word)) {
|
| 242 |
+
$brute_force_feature_secret_word = "aiossecret";
|
| 243 |
+
} elseif (!ctype_alnum($brute_force_feature_secret_word)) {
|
| 244 |
$msg = '<p>'.__('Settings have not been saved - your secret word must consist only of alphanumeric characters, ie, letters and/or numbers only!', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 245 |
$error = true;
|
| 246 |
}
|
| 247 |
|
| 248 |
+
if (filter_var($_POST['aiowps_cookie_based_brute_force_redirect_url'], FILTER_VALIDATE_URL)) {
|
| 249 |
+
$aio_wp_security->configs->set_value('aiowps_cookie_based_brute_force_redirect_url', esc_url_raw($_POST['aiowps_cookie_based_brute_force_redirect_url']));
|
| 250 |
+
} else {
|
| 251 |
+
$aio_wp_security->configs->set_value('aiowps_cookie_based_brute_force_redirect_url', 'http://127.0.0.1');
|
|
|
|
|
|
|
|
|
|
| 252 |
}
|
| 253 |
+
$aio_wp_security->configs->set_value('aiowps_enable_brute_force_attack_prevention', '1');
|
| 254 |
|
| 255 |
+
if (!$error) {
|
| 256 |
+
$aio_wp_security->configs->set_value('aiowps_brute_force_secret_word', $brute_force_feature_secret_word);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 257 |
$msg = '<p>'.__('You have successfully enabled the cookie based brute force prevention feature', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 258 |
$msg .= '<p>'.__('From now on you will need to log into your WP Admin using the following URL:', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 259 |
$msg .= '<p><strong>'.AIOWPSEC_WP_URL.'/?'.$brute_force_feature_secret_word.'=1</strong></p>';
|
| 260 |
$msg .= '<p>'.__('It is important that you save this URL value somewhere in case you forget it, OR,', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 261 |
$msg .= '<p>'.sprintf( __('simply remember to add a "?%s=1" to your current site URL address.', 'all-in-one-wp-security-and-firewall'), $brute_force_feature_secret_word).'</p>';
|
| 262 |
}
|
| 263 |
+
} else {
|
| 264 |
+
$aio_wp_security->configs->set_value('aiowps_enable_brute_force_attack_prevention', '');
|
|
|
|
|
|
|
| 265 |
$msg = __('You have successfully saved cookie based brute force prevention feature settings.', 'all-in-one-wp-security-and-firewall');
|
| 266 |
}
|
| 267 |
|
| 268 |
+
if (isset($_POST['aiowps_brute_force_attack_prevention_pw_protected_exception'])) {
|
| 269 |
+
$aio_wp_security->configs->set_value('aiowps_brute_force_attack_prevention_pw_protected_exception', '1');
|
| 270 |
+
} else {
|
| 271 |
+
$aio_wp_security->configs->set_value('aiowps_brute_force_attack_prevention_pw_protected_exception', '');
|
|
|
|
|
|
|
|
|
|
| 272 |
}
|
| 273 |
|
| 274 |
+
if (isset($_POST['aiowps_brute_force_attack_prevention_ajax_exception'])) {
|
| 275 |
+
$aio_wp_security->configs->set_value('aiowps_brute_force_attack_prevention_ajax_exception', '1');
|
| 276 |
+
} else {
|
| 277 |
+
$aio_wp_security->configs->set_value('aiowps_brute_force_attack_prevention_ajax_exception', '');
|
|
|
|
|
|
|
|
|
|
| 278 |
}
|
| 279 |
|
| 280 |
+
if (!$error) {
|
|
|
|
| 281 |
$aio_wp_security->configs->save_config();//save the value
|
| 282 |
|
| 283 |
//Recalculate points after the feature status/options have been altered
|
| 284 |
$aiowps_feature_mgr->check_feature_status_and_recalculate_points();
|
| 285 |
+
if ('' != $msg) {
|
| 286 |
+
echo '<div id="message" class="updated fade"><p>';
|
| 287 |
+
echo $msg;
|
| 288 |
+
echo '</p></div>';
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 289 |
}
|
| 290 |
+
} else {
|
|
|
|
|
|
|
| 291 |
$this->show_msg_error($msg);
|
| 292 |
}
|
| 293 |
}
|
|
|
|
| 294 |
?>
|
| 295 |
<h2><?php _e('Brute Force Prevention Firewall Settings', 'all-in-one-wp-security-and-firewall')?></h2>
|
| 296 |
|
| 299 |
//TODO - need to fix the following message
|
| 300 |
echo '<p>'.__('A Brute Force Attack is when a hacker tries many combinations of usernames and passwords until they succeed in guessing the right combination.', 'all-in-one-wp-security-and-firewall').
|
| 301 |
'<br />'.__('Due to the fact that at any one time there may be many concurrent login attempts occurring on your site via malicious automated robots, this also has a negative impact on your server\'s memory and performance.', 'all-in-one-wp-security-and-firewall').
|
| 302 |
+
'<br />'.__('The features in this tab will stop the majority of brute force login attacks thus providing even better protection for your WP login page.', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 303 |
?>
|
| 304 |
</div>
|
| 305 |
<div class="aio_yellow_box">
|
| 306 |
<?php
|
| 307 |
$backup_tab_link = '<a href="admin.php?page='.AIOWPSEC_SETTINGS_MENU_SLUG.'&tab=tab2" target="_blank">'.__('backup', 'all-in-one-wp-security-and-firewall').'</a>';
|
| 308 |
$video_link = '<a href="https://www.tipsandtricks-hq.com/all-in-one-wp-security-plugin-cookie-based-brute-force-login-attack-prevention-feature-5994" target="_blank">'.__('video tutorial', 'all-in-one-wp-security-and-firewall').'</a>';
|
| 309 |
+
$info_msg = sprintf( __('To learn more about how to use this feature, please watch the following %s.', 'all-in-one-wp-security-and-firewall'), $video_link);
|
|
|
|
|
|
|
| 310 |
$brute_force_login_feature_link = '<a href="admin.php?page='.AIOWPSEC_FIREWALL_MENU_SLUG.'&tab=tab4" target="_blank">'.__('Cookie-Based Brute Force Login Prevention', 'all-in-one-wp-security-and-firewall').'</a>';
|
| 311 |
+
echo '<p>' . $info_msg . '</p>';
|
|
|
|
|
|
|
| 312 |
?>
|
| 313 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 314 |
<div class="postbox">
|
| 315 |
<h3 class="hndle"><label for="title"><?php _e('Cookie Based Brute Force Login Prevention', 'all-in-one-wp-security-and-firewall'); ?></label></h3>
|
| 316 |
<div class="inside">
|
| 326 |
<?php _e('This feature can lock you out of admin if it doesn\'t work correctly on your site. You <a href="https://www.tipsandtricks-hq.com/wordpress-security-and-firewall-plugin#advanced_features_note" target="_blank">'.__('must read this message', 'all-in-one-wp-security-and-firewall').'</a> before activating this feature.', 'all-in-one-wp-security-and-firewall'); ?>
|
| 327 |
</p>
|
| 328 |
</div>
|
| 329 |
+
<?php
|
| 330 |
+
$cookie_test_value = $aio_wp_security->configs->get_value('aiowps_cookie_test_success');
|
| 331 |
+
|
| 332 |
+
$disable_brute_force_fetaure_input = true;
|
| 333 |
+
// If the cookie test is successful or if the feature is already enabled then go ahead as normal
|
| 334 |
+
if ('1' == $cookie_test_value || '1' == $aio_wp_security->configs->get_value('aiowps_enable_brute_force_attack_prevention')) {
|
| 335 |
+
if (isset($_POST['aiowps_cookie_test'])) {//Cookie test was just performed and the test succeded
|
| 336 |
+
echo '<div class="aio_green_box"><p>';
|
| 337 |
+
_e('The cookie test was successful. You can now enable this feature.', 'all-in-one-wp-security-and-firewall');
|
| 338 |
+
echo '</p></div>';
|
| 339 |
+
}
|
| 340 |
+
$disable_brute_force_fetaure_input = false;
|
| 341 |
+
} else {
|
| 342 |
+
//Cookie test needs to be performed
|
| 343 |
+
if (isset($_POST['aiowps_cookie_test']) && '1' != $cookie_test_value) {//Test failed
|
| 344 |
+
echo '<div class="aio_red_box"><p>';
|
| 345 |
+
_e('The cookie test failed on this server. Consequently, this feature cannot be used on this site.', 'all-in-one-wp-security-and-firewall');
|
| 346 |
+
echo '</p></div>';
|
| 347 |
+
}
|
| 348 |
+
?>
|
| 349 |
+
<div class="aio_yellow_box">
|
| 350 |
+
<p>
|
| 351 |
+
<?php
|
| 352 |
+
_e('Before using this feature, you must perform a cookie test first.', 'all-in-one-wp-security-and-firewall');
|
| 353 |
+
echo ' ';
|
| 354 |
+
echo htmlspecialchars(__("This ensures that your browser cookie is working correctly and that you won't lock yourself out.", 'all-in-one-wp-security-and-firewall'));
|
| 355 |
+
?>
|
| 356 |
+
</p>
|
| 357 |
+
</div>
|
| 358 |
+
<?php
|
| 359 |
+
submit_button(__('Perform cookie test', 'all-in-one-wp-security-and-firewall'), 'primary' , 'aiowps_do_cookie_test_for_bfla');
|
| 360 |
+
}
|
| 361 |
+
$disable_brute_force_sub_fields = !$aio_wp_security->configs->get_value('aiowps_enable_brute_force_attack_prevention');
|
| 362 |
+
?>
|
| 363 |
<table class="form-table">
|
| 364 |
<tr valign="top">
|
| 365 |
+
<th scope="row"><?php _e('Enable brute force attack prevention', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 366 |
<td>
|
| 367 |
+
<input id="aiowps_enable_brute_force_attack_prevention" name="aiowps_enable_brute_force_attack_prevention" type="checkbox"<?php checked($aio_wp_security->configs->get_value('aiowps_enable_brute_force_attack_prevention'));?> value="1"<?php disabled($disable_brute_force_fetaure_input); ?>/>
|
| 368 |
+
<label for="aiowps_enable_brute_force_attack_prevention" class="description"><?php _e('Check this if you want to protect your login page from Brute Force Attack.', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 369 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 370 |
<div class="aiowps_more_info_body">
|
| 371 |
<p class="description">
|
| 387 |
</td>
|
| 388 |
</tr>
|
| 389 |
<tr valign="top">
|
| 390 |
+
<th scope="row"><label for="aiowps_brute_force_secret_word"><?php _e('Secret Word', 'all-in-one-wp-security-and-firewall')?>:</label></th>
|
| 391 |
+
<td><input id="aiowps_brute_force_secret_word" type="text" size="40" name="aiowps_brute_force_secret_word" value="<?php echo $aio_wp_security->configs->get_value('aiowps_brute_force_secret_word'); ?>"<?php disabled($disable_brute_force_sub_fields); ?>/>
|
| 392 |
<span class="description"><?php _e('Choose a secret word consisting of alphanumeric characters which you can use to access your special URL. Your are highly encouraged to choose a word which will be difficult to guess.', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 393 |
</td>
|
| 394 |
</tr>
|
| 395 |
<tr valign="top">
|
| 396 |
+
<th scope="row"><label for="aiowps_cookie_based_brute_force_redirect_url"><?php _e('Re-direct URL', 'all-in-one-wp-security-and-firewall')?>:</label></th>
|
| 397 |
+
<td><input id="aiowps_cookie_based_brute_force_redirect_url" type="text" size="40" name="aiowps_cookie_based_brute_force_redirect_url" value="<?php echo $aio_wp_security->configs->get_value('aiowps_cookie_based_brute_force_redirect_url'); ?>" <?php disabled($disable_brute_force_sub_fields); ?> />
|
| 398 |
<span class="description">
|
| 399 |
<?php
|
| 400 |
_e('Specify a URL to redirect a hacker to when they try to access your WordPress login page.', 'all-in-one-wp-security-and-firewall');
|
| 419 |
</td>
|
| 420 |
</tr>
|
| 421 |
<tr valign="top">
|
| 422 |
+
<th scope="row"><?php _e('My site has posts or pages which are password protected', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 423 |
<td>
|
| 424 |
+
<input id="aiowps_brute_force_attack_prevention_pw_protected_exception" name="aiowps_brute_force_attack_prevention_pw_protected_exception" type="checkbox"<?php checked($aio_wp_security->configs->get_value('aiowps_brute_force_attack_prevention_pw_protected_exception')); ?> value="1"<?php disabled($disable_brute_force_sub_fields); ?> />
|
| 425 |
+
<label for="aiowps_brute_force_attack_prevention_pw_protected_exception" class="description"><?php _e('Check this if you are using the native WordPress password protection feature for some or all of your blog posts or pages.', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 426 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 427 |
<div class="aiowps_more_info_body">
|
| 428 |
<p class="description">
|
| 429 |
<?php
|
| 430 |
+
_e('In the cases where you are protecting some of your posts or pages using the in-built WordPress password protection feature, a few extra lines of directives and exceptions need to be added so that people trying to access pages are not automatically blocked.', 'all-in-one-wp-security-and-firewall');
|
| 431 |
echo '<br />';
|
| 432 |
+
_e('By enabling this checkbox, the plugin will add the necessary rules and exceptions so that people trying to access these pages are not automatically blocked.', 'all-in-one-wp-security-and-firewall');
|
| 433 |
echo '<br />';
|
| 434 |
echo "<strong>".__('Helpful Tip:', 'all-in-one-wp-security-and-firewall')."</strong>";
|
| 435 |
echo '<br />';
|
| 440 |
</td>
|
| 441 |
</tr>
|
| 442 |
<tr valign="top">
|
| 443 |
+
<th scope="row"><?php _e('My site has a theme or plugins which use AJAX', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 444 |
<td>
|
| 445 |
+
<input id="aiowps_brute_force_attack_prevention_ajax_exception" name="aiowps_brute_force_attack_prevention_ajax_exception" type="checkbox"<?php checked($aio_wp_security->configs->get_value('aiowps_brute_force_attack_prevention_ajax_exception')); ?> value="1"<?php disabled($disable_brute_force_sub_fields); ?>/>
|
| 446 |
+
<label for="aiowps_brute_force_attack_prevention_ajax_exception" class="description"><?php _e('Check this if your site uses AJAX functionality.', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 447 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 448 |
<div class="aiowps_more_info_body">
|
| 449 |
<p class="description">
|
| 450 |
<?php
|
| 451 |
+
_e('In the cases where your WordPress installation has a theme or plugin that uses AJAX, a few extra lines of directives and exceptions need to be added to prevent AJAX requests from being automatically blocked by the brute force prevention feature.', 'all-in-one-wp-security-and-firewall');
|
| 452 |
echo '<br />';
|
| 453 |
+
_e('By enabling this checkbox, the plugin will add the necessary rules and exceptions so that AJAX operations will work as expected.', 'all-in-one-wp-security-and-firewall');
|
| 454 |
?>
|
| 455 |
</p>
|
| 456 |
</div>
|
| 458 |
</tr>
|
| 459 |
</table>
|
| 460 |
<?php
|
| 461 |
+
$other_attributes = $disable_brute_force_fetaure_input ? array('disabled' => 'disabled') : array();
|
| 462 |
+
submit_button(__('Save feature settings', 'all-in-one-wp-security-and-firewall'), 'primary', 'aiowps_apply_cookie_based_bruteforce_firewall', false, $other_attributes);
|
| 463 |
+
?>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 464 |
</form>
|
| 465 |
</div></div>
|
| 466 |
<?php
|
| 471 |
global $aio_wp_security;
|
| 472 |
global $aiowps_feature_mgr;
|
| 473 |
|
| 474 |
+
if (isset($_POST['aiowpsec_save_captcha_settings'])) { //Do form submission tasks
|
| 475 |
+
$error = '';
|
| 476 |
+
if (!wp_verify_nonce($_POST['_wpnonce'], 'aiowpsec-captcha-settings-nonce')) {
|
| 477 |
+
$aio_wp_security->debug_logger->log_debug('Nonce check failed on captcha settings save.', 4);
|
| 478 |
+
die('Nonce check failed on captcha settings save.');
|
|
|
|
|
|
|
|
|
|
| 479 |
}
|
| 480 |
|
| 481 |
|
| 500 |
}
|
| 501 |
}
|
| 502 |
|
| 503 |
+
$aio_wp_security->configs->set_value('aiowps_default_recaptcha', isset($_POST["aiowps_default_recaptcha"])? '1' : '');//Checkbox
|
| 504 |
$aio_wp_security->configs->save_config();
|
| 505 |
|
| 506 |
//Recalculate points after the feature status/options have been altered
|
| 553 |
<tr valign="top">
|
| 554 |
<th scope="row"><?php _e('Use Google reCAPTCHA as default', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 555 |
<td>
|
| 556 |
+
<input id="aiowps_default_recaptcha" name="aiowps_default_recaptcha" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_default_recaptcha')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 557 |
+
<label for="aiowps_default_recaptcha" class="description"><?php _e('Check this if you want to default to Google reCAPTCHA for all settings below. (If this is left unchecked, all captcha forms will revert to the plain maths captcha)', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 558 |
</td>
|
| 559 |
</tr>
|
| 560 |
<tr valign="top">
|
| 561 |
+
<th scope="row"><label for="aiowps_recaptcha_site_key"><?php _e('Site Key', 'all-in-one-wp-security-and-firewall')?>:</label></th>
|
| 562 |
+
<td><input id="aiowps_recaptcha_site_key" type="text" size="50" name="aiowps_recaptcha_site_key" value="<?php echo esc_html( $aio_wp_security->configs->get_value('aiowps_recaptcha_site_key') ); ?>" />
|
| 563 |
</td>
|
| 564 |
</tr>
|
| 565 |
<tr valign="top">
|
| 566 |
+
<th scope="row"><label for="aiowps_recaptcha_secret_key"><?php _e('Secret Key', 'all-in-one-wp-security-and-firewall')?>:</label></th>
|
| 567 |
+
<td><input id="aiowps_recaptcha_secret_key" type="text" size="50" name="aiowps_recaptcha_secret_key" value="<?php echo esc_html($secret_key_masked); ?>" />
|
| 568 |
</td>
|
| 569 |
</tr>
|
| 570 |
</table>
|
| 581 |
<tr valign="top">
|
| 582 |
<th scope="row"><?php _e('Enable Captcha On Login Page', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 583 |
<td>
|
| 584 |
+
<input id="aiowps_enable_login_captcha" name="aiowps_enable_login_captcha" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_login_captcha')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 585 |
+
<label for="aiowps_enable_login_captcha" class="description"><?php _e('Check this if you want to insert a captcha form on the login page', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 586 |
</td>
|
| 587 |
</tr>
|
| 588 |
</table>
|
| 600 |
<tr valign="top">
|
| 601 |
<th scope="row"><?php _e('Enable Captcha On Lost Password Page', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 602 |
<td>
|
| 603 |
+
<input id="aiowps_enable_lost_password_captcha" name="aiowps_enable_lost_password_captcha" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_lost_password_captcha')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 604 |
+
<label for="aiowps_enable_lost_password_captcha" class="description"><?php _e('Check this if you want to insert a captcha form on the lost password page', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 605 |
</td>
|
| 606 |
</tr>
|
| 607 |
</table>
|
| 618 |
<tr valign="top">
|
| 619 |
<th scope="row"><?php _e('Enable Captcha On Custom Login Form', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 620 |
<td>
|
| 621 |
+
<input id="aiowps_enable_custom_login_captcha" name="aiowps_enable_custom_login_captcha" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_custom_login_captcha')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 622 |
+
<label for="aiowps_enable_custom_login_captcha" class="description"><?php _e('Check this if you want to insert captcha on a custom login form generated by the following WP function: wp_login_form()', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 623 |
</td>
|
| 624 |
</tr>
|
| 625 |
</table>
|
| 626 |
</div></div>
|
| 627 |
<?php
|
| 628 |
// Only display woocommerce captcha settings if woo is active
|
| 629 |
+
if (AIOWPSecurity_Utility::is_woocommerce_plugin_active()) {
|
| 630 |
?>
|
| 631 |
<div class="postbox">
|
| 632 |
<h3 class="hndle"><label for="title"><?php _e('Woocommerce Forms Captcha Settings', 'all-in-one-wp-security-and-firewall'); ?></label></h3>
|
| 640 |
<tr valign="top">
|
| 641 |
<th scope="row"><?php _e('Enable Captcha On Woocommerce Login Form', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 642 |
<td>
|
| 643 |
+
<input id="aiowps_enable_woo_login_captcha" name="aiowps_enable_woo_login_captcha" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_woo_login_captcha')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 644 |
+
<label for="aiowps_enable_woo_login_captcha" class="description"><?php _e('Check this if you want to insert captcha on a Woocommerce login form', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 645 |
</td>
|
| 646 |
</tr>
|
| 647 |
</table>
|
| 653 |
<tr valign="top">
|
| 654 |
<th scope="row"><?php _e('Enable Captcha On Woocommerce Lost Password Form', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 655 |
<td>
|
| 656 |
+
<input id="aiowps_enable_woo_lostpassword_captcha" name="aiowps_enable_woo_lostpassword_captcha" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_woo_lostpassword_captcha')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 657 |
+
<label for="aiowps_enable_woo_lostpassword_captcha" class="description"><?php _e('Check this if you want to insert captcha on a Woocommerce lost password form', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 658 |
</td>
|
| 659 |
</tr>
|
| 660 |
</table>
|
| 666 |
<tr valign="top">
|
| 667 |
<th scope="row"><?php _e('Enable Captcha On Woocommerce Registration Form', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 668 |
<td>
|
| 669 |
+
<input id="aiowps_enable_woo_register_captcha" name="aiowps_enable_woo_register_captcha" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_woo_register_captcha')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 670 |
+
<label for="aiowps_enable_woo_register_captcha" class="description"><?php _e('Check this if you want to insert captcha on a Woocommerce registration form', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 671 |
</td>
|
| 672 |
</tr>
|
| 673 |
</table>
|
| 692 |
$nonce=$_REQUEST['_wpnonce'];
|
| 693 |
if (!wp_verify_nonce($nonce, 'aiowpsec-whitelist-settings-nonce'))
|
| 694 |
{
|
| 695 |
+
$aio_wp_security->debug_logger->log_debug("Nonce check failed for save whitelist settings.",4);
|
| 696 |
+
die(__('Nonce check failed for save whitelist settings.','all-in-one-wp-security-and-firewall'));
|
| 697 |
}
|
| 698 |
|
| 699 |
if (isset($_POST["aiowps_enable_whitelisting"]) && empty($_POST['aiowps_allowed_ip_addresses']))
|
| 780 |
<tr valign="top">
|
| 781 |
<th scope="row"><?php _e('Enable IP Whitelisting', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 782 |
<td>
|
| 783 |
+
<input id="aiowps_enable_whitelisting" name="aiowps_enable_whitelisting" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_whitelisting')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 784 |
+
<label for="aiowps_enable_whitelisting" class="description"><?php _e('Check this if you want to enable the whitelisting of selected IP addresses specified in the settings below', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 785 |
</td>
|
| 786 |
</tr>
|
| 787 |
<tr valign="top">
|
| 788 |
+
<th scope="row"><label for="aiowps_user_ip"><?php _e('Your Current IP Address', 'all-in-one-wp-security-and-firewall')?>:</label></th>
|
| 789 |
<td>
|
| 790 |
+
<input id="aiowps_user_ip" size="20" name="aiowps_user_ip" type="text" value="<?php echo $your_ip_address; ?>" readonly="readonly"/>
|
| 791 |
<span class="description"><?php _e('You can copy and paste this address in the text box below if you want to include it in your login whitelist.', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 792 |
</td>
|
| 793 |
</tr>
|
| 794 |
<tr valign="top">
|
| 795 |
+
<th scope="row"><label for="aiowps_allowed_ip_addresses"><?php _e('Enter Whitelisted IP Addresses:', 'all-in-one-wp-security-and-firewall')?></label></th>
|
| 796 |
<td>
|
| 797 |
+
<textarea id="aiowps_allowed_ip_addresses" name="aiowps_allowed_ip_addresses" rows="5" cols="50"><?php echo esc_textarea(wp_unslash(-1 == $result ? $_POST['aiowps_allowed_ip_addresses'] : $aio_wp_security->configs->get_value('aiowps_allowed_ip_addresses'))); ?></textarea>
|
| 798 |
<br />
|
| 799 |
+
<span class="description"><?php _e('Enter one or more IP addresses or IP ranges you wish to include in your whitelist.', 'all-in-one-wp-security-and-firewall') . ' ' . _e('Only the addresses specified here will have access to the WordPress login page.', 'all-in-one-wp-security-and-firewall');?></span>
|
| 800 |
+
<?php $aio_wp_security->include_template('info/ip-address-ip-range-info.php');?>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 801 |
|
| 802 |
</td>
|
| 803 |
</tr>
|
| 819 |
$nonce=$_REQUEST['_wpnonce'];
|
| 820 |
if (!wp_verify_nonce($nonce, 'aiowpsec-honeypot-settings-nonce'))
|
| 821 |
{
|
| 822 |
+
$aio_wp_security->debug_logger->log_debug("Nonce check failed on honeypot settings save.",4);
|
| 823 |
+
die("Nonce check failed on honeypot settings save.");
|
| 824 |
}
|
| 825 |
|
| 826 |
//Save all the form values to the options
|
| 857 |
<tr valign="top">
|
| 858 |
<th scope="row"><?php _e('Enable Honeypot On Login Page', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 859 |
<td>
|
| 860 |
+
<input id="aiowps_enable_login_honeypot" name="aiowps_enable_login_honeypot" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_login_honeypot')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 861 |
+
<label for="aiowps_enable_login_honeypot" class="description"><?php _e('Check this if you want to enable the honeypot feature for the login page', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 862 |
</td>
|
| 863 |
</tr>
|
| 864 |
</table>
|
| 868 |
</form>
|
| 869 |
<?php
|
| 870 |
}
|
| 871 |
+
|
|
|
|
| 872 |
} //end class
|
|
@@ -83,8 +83,12 @@ class AIOWPSecurity_Dashboard_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 83 |
<?php
|
| 84 |
}
|
| 85 |
|
| 86 |
-
|
| 87 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
global $wpdb;
|
| 89 |
include_once 'wp-security-list-locked-ip.php'; //For rendering the AIOWPSecurity_List_Table in tab1
|
| 90 |
$locked_ip_list = new AIOWPSecurity_List_Locked_IP(); //For rendering the AIOWPSecurity_List_Table in tab1
|
|
@@ -120,8 +124,7 @@ class AIOWPSecurity_Dashboard_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 120 |
$locked_ip_list->prepare_items();
|
| 121 |
//echo "put table of locked entries here";
|
| 122 |
?>
|
| 123 |
-
|
| 124 |
-
onSubmit="return confirm('Are you sure you want to perform this bulk operation on the selected entries?');">
|
| 125 |
<!-- For plugins, we also need to ensure that the form posts back to our current page -->
|
| 126 |
<input type="hidden" name="page" value="<?php echo esc_attr($_REQUEST['page']); ?>"/>
|
| 127 |
<?php
|
|
@@ -138,8 +141,12 @@ class AIOWPSecurity_Dashboard_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 138 |
<?php
|
| 139 |
}
|
| 140 |
|
| 141 |
-
|
| 142 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 143 |
global $wpdb;
|
| 144 |
include_once 'wp-security-list-permanent-blocked-ip.php'; //For rendering the AIOWPSecurity_List_Table
|
| 145 |
$blocked_ip_list = new AIOWPSecurity_List_Blocked_IP(); //For rendering the AIOWPSecurity_List_Table
|
|
@@ -150,7 +157,6 @@ class AIOWPSecurity_Dashboard_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 150 |
$blocked_ip_list->unblock_ip_address(strip_tags($_REQUEST['blocked_id']));
|
| 151 |
}
|
| 152 |
}
|
| 153 |
-
AIOWPSecurity_Admin_Menu::display_bulk_result_message();
|
| 154 |
|
| 155 |
?>
|
| 156 |
<div class="aio_blue_box">
|
|
@@ -170,7 +176,7 @@ class AIOWPSecurity_Dashboard_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 170 |
//Fetch, prepare, sort, and filter our data...
|
| 171 |
$blocked_ip_list->prepare_items();
|
| 172 |
?>
|
| 173 |
-
<form id="tables-filter" method="
|
| 174 |
<!-- For plugins, we also need to ensure that the form posts back to our current page -->
|
| 175 |
<input type="hidden" name="page" value="<?php echo esc_attr($_REQUEST['page']); ?>"/>
|
| 176 |
<?php
|
|
@@ -188,13 +194,12 @@ class AIOWPSecurity_Dashboard_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 188 |
<?php
|
| 189 |
}
|
| 190 |
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
{
|
| 198 |
//Needed for rendering the debug log table
|
| 199 |
include_once 'wp-security-list-debug.php';
|
| 200 |
$debug_log_list = new AIOWPSecurity_List_Debug_Log();
|
|
@@ -551,7 +556,7 @@ class AIOWPSecurity_Dashboard_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 551 |
foreach ($data as $entry) {
|
| 552 |
$login_summary_table .= '<tr>';
|
| 553 |
$login_summary_table .= '<td>' . $entry['user_login'] . '</td>';
|
| 554 |
-
$login_summary_table .= '<td>' . $entry['login_date'] . '</td>';
|
| 555 |
$login_summary_table .= '<td>' . $entry['login_ip'] . '</td>';
|
| 556 |
$login_summary_table .= '</tr>';
|
| 557 |
}
|
|
@@ -604,9 +609,9 @@ class AIOWPSecurity_Dashboard_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 604 |
//Insert Rename Login Page feature box if this feature is active
|
| 605 |
if ($aio_wp_security->configs->get_value('aiowps_enable_rename_login_page') == '1') {
|
| 606 |
if (get_option('permalink_structure')) {
|
| 607 |
-
$
|
| 608 |
} else {
|
| 609 |
-
$
|
| 610 |
}
|
| 611 |
|
| 612 |
$rename_login_feature_link = '<a href="admin.php?page=' . AIOWPSEC_BRUTE_FORCE_MENU_SLUG . '&tab=tab1" target="_blank">' . __('Rename Login Page', 'all-in-one-wp-security-and-firewall') . '</a>';
|
|
@@ -614,7 +619,7 @@ class AIOWPSecurity_Dashboard_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 614 |
|
| 615 |
echo '<p>' . sprintf(__('The %s feature is currently active.', 'all-in-one-wp-security-and-firewall'), $rename_login_feature_link) . '</p>';
|
| 616 |
echo '<p>' . __('Your new WordPress login URL is now:', 'all-in-one-wp-security-and-firewall') . '</p>';
|
| 617 |
-
echo '<p><strong>' . $
|
| 618 |
echo '</div>'; //yellow box div
|
| 619 |
echo '<div class="aio_clear_float"></div>';
|
| 620 |
}//End if statement for Rename Login box
|
|
@@ -626,7 +631,7 @@ class AIOWPSecurity_Dashboard_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 626 |
// default display messages
|
| 627 |
$multiple_users_info_msg = __('Number of users currently logged into your site (including you) is:', 'all-in-one-wp-security-and-firewall');
|
| 628 |
$single_user_info_msg = __('There are no other users currently logged in.', 'all-in-one-wp-security-and-firewall');
|
| 629 |
-
if (
|
| 630 |
$current_blog_id = get_current_blog_id();
|
| 631 |
$is_main = is_main_site($current_blog_id);
|
| 632 |
|
| 83 |
<?php
|
| 84 |
}
|
| 85 |
|
| 86 |
+
/**
|
| 87 |
+
* Renders the submenu's tab2 tab body.
|
| 88 |
+
*
|
| 89 |
+
* @return Void
|
| 90 |
+
*/
|
| 91 |
+
public function render_tab2() {
|
| 92 |
global $wpdb;
|
| 93 |
include_once 'wp-security-list-locked-ip.php'; //For rendering the AIOWPSecurity_List_Table in tab1
|
| 94 |
$locked_ip_list = new AIOWPSecurity_List_Locked_IP(); //For rendering the AIOWPSecurity_List_Table in tab1
|
| 124 |
$locked_ip_list->prepare_items();
|
| 125 |
//echo "put table of locked entries here";
|
| 126 |
?>
|
| 127 |
+
<form id="tables-filter" method="post">
|
|
|
|
| 128 |
<!-- For plugins, we also need to ensure that the form posts back to our current page -->
|
| 129 |
<input type="hidden" name="page" value="<?php echo esc_attr($_REQUEST['page']); ?>"/>
|
| 130 |
<?php
|
| 141 |
<?php
|
| 142 |
}
|
| 143 |
|
| 144 |
+
/**
|
| 145 |
+
* Renders the submenu's tab3 tab body.
|
| 146 |
+
*
|
| 147 |
+
* @return Void
|
| 148 |
+
*/
|
| 149 |
+
public function render_tab3() {
|
| 150 |
global $wpdb;
|
| 151 |
include_once 'wp-security-list-permanent-blocked-ip.php'; //For rendering the AIOWPSecurity_List_Table
|
| 152 |
$blocked_ip_list = new AIOWPSecurity_List_Blocked_IP(); //For rendering the AIOWPSecurity_List_Table
|
| 157 |
$blocked_ip_list->unblock_ip_address(strip_tags($_REQUEST['blocked_id']));
|
| 158 |
}
|
| 159 |
}
|
|
|
|
| 160 |
|
| 161 |
?>
|
| 162 |
<div class="aio_blue_box">
|
| 176 |
//Fetch, prepare, sort, and filter our data...
|
| 177 |
$blocked_ip_list->prepare_items();
|
| 178 |
?>
|
| 179 |
+
<form id="tables-filter" method="post">
|
| 180 |
<!-- For plugins, we also need to ensure that the form posts back to our current page -->
|
| 181 |
<input type="hidden" name="page" value="<?php echo esc_attr($_REQUEST['page']); ?>"/>
|
| 182 |
<?php
|
| 194 |
<?php
|
| 195 |
}
|
| 196 |
|
| 197 |
+
/**
|
| 198 |
+
* Renders tab 4 which is the AIOWPS Logs tab. Responsible for displaying the logs
|
| 199 |
+
*
|
| 200 |
+
* @return void
|
| 201 |
+
*/
|
| 202 |
+
public function render_tab4() {
|
|
|
|
| 203 |
//Needed for rendering the debug log table
|
| 204 |
include_once 'wp-security-list-debug.php';
|
| 205 |
$debug_log_list = new AIOWPSecurity_List_Debug_Log();
|
| 556 |
foreach ($data as $entry) {
|
| 557 |
$login_summary_table .= '<tr>';
|
| 558 |
$login_summary_table .= '<td>' . $entry['user_login'] . '</td>';
|
| 559 |
+
$login_summary_table .= '<td>' . get_date_from_gmt(mysql2date('Y-m-d H:i:s', $entry['login_date']), get_option('date_format').' '.get_option('time_format')) . '</td>';
|
| 560 |
$login_summary_table .= '<td>' . $entry['login_ip'] . '</td>';
|
| 561 |
$login_summary_table .= '</tr>';
|
| 562 |
}
|
| 609 |
//Insert Rename Login Page feature box if this feature is active
|
| 610 |
if ($aio_wp_security->configs->get_value('aiowps_enable_rename_login_page') == '1') {
|
| 611 |
if (get_option('permalink_structure')) {
|
| 612 |
+
$site_url = trailingslashit(site_url());
|
| 613 |
} else {
|
| 614 |
+
$site_url = trailingslashit(site_url()) . '?';
|
| 615 |
}
|
| 616 |
|
| 617 |
$rename_login_feature_link = '<a href="admin.php?page=' . AIOWPSEC_BRUTE_FORCE_MENU_SLUG . '&tab=tab1" target="_blank">' . __('Rename Login Page', 'all-in-one-wp-security-and-firewall') . '</a>';
|
| 619 |
|
| 620 |
echo '<p>' . sprintf(__('The %s feature is currently active.', 'all-in-one-wp-security-and-firewall'), $rename_login_feature_link) . '</p>';
|
| 621 |
echo '<p>' . __('Your new WordPress login URL is now:', 'all-in-one-wp-security-and-firewall') . '</p>';
|
| 622 |
+
echo '<p><strong>' . $site_url . $aio_wp_security->configs->get_value('aiowps_login_page_slug') . '</strong></p>';
|
| 623 |
echo '</div>'; //yellow box div
|
| 624 |
echo '<div class="aio_clear_float"></div>';
|
| 625 |
}//End if statement for Rename Login box
|
| 631 |
// default display messages
|
| 632 |
$multiple_users_info_msg = __('Number of users currently logged into your site (including you) is:', 'all-in-one-wp-security-and-firewall');
|
| 633 |
$single_user_info_msg = __('There are no other users currently logged in.', 'all-in-one-wp-security-and-firewall');
|
| 634 |
+
if (is_multisite()) {
|
| 635 |
$current_blog_id = get_current_blog_id();
|
| 636 |
$is_main = is_main_site($current_blog_id);
|
| 637 |
|
|
@@ -6,7 +6,7 @@ if(!defined('ABSPATH')){
|
|
| 6 |
class AIOWPSecurity_Database_Menu extends AIOWPSecurity_Admin_Menu
|
| 7 |
{
|
| 8 |
var $menu_page_slug = AIOWPSEC_DB_SEC_MENU_SLUG;
|
| 9 |
-
|
| 10 |
/* Specify all the tabs of this menu in the following array */
|
| 11 |
var $menu_tabs;
|
| 12 |
|
|
@@ -15,26 +15,83 @@ class AIOWPSecurity_Database_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 15 |
'tab2' => 'render_tab2',
|
| 16 |
);
|
| 17 |
|
| 18 |
-
|
| 19 |
-
|
|
|
|
|
|
|
| 20 |
$this->render_menu_page();
|
| 21 |
}
|
| 22 |
-
|
| 23 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
{
|
| 25 |
-
if (
|
| 26 |
//Suppress the DB prefix change tab if site is a multi site AND not the main site
|
| 27 |
$this->menu_tabs = array(
|
| 28 |
-
//'tab1' => __('
|
| 29 |
-
'tab2' => __('
|
| 30 |
);
|
| 31 |
-
}else{
|
| 32 |
$this->menu_tabs = array(
|
| 33 |
-
'tab1' => __('
|
| 34 |
-
'tab2' => __('
|
| 35 |
);
|
| 36 |
}
|
| 37 |
-
|
| 38 |
}
|
| 39 |
|
| 40 |
/*
|
|
@@ -126,11 +183,11 @@ class AIOWPSecurity_Database_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 126 |
}
|
| 127 |
}
|
| 128 |
?>
|
| 129 |
-
<h2><?php _e('Change
|
| 130 |
<div class="aio_blue_box">
|
| 131 |
<?php
|
| 132 |
-
echo '<p>'.__('Your WordPress
|
| 133 |
-
<br />'.__('The
|
| 134 |
<br />'.__('One way to add a layer of protection for your DB is to change the default WordPress table prefix from "wp_" to something else which will be difficult for hackers to guess.', 'all-in-one-wp-security-and-firewall').'
|
| 135 |
<br />'.__('This feature allows you to easily change the prefix to a value of your choice or to a random value set by this plugin.', 'all-in-one-wp-security-and-firewall').'
|
| 136 |
</p>';
|
|
@@ -138,7 +195,7 @@ class AIOWPSecurity_Database_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 138 |
</div>
|
| 139 |
|
| 140 |
<div class="postbox">
|
| 141 |
-
<h3 class="hndle"><label for="title"><?php _e('
|
| 142 |
<div class="inside">
|
| 143 |
<?php
|
| 144 |
//Display security info badge
|
|
@@ -147,18 +204,21 @@ class AIOWPSecurity_Database_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 147 |
?>
|
| 148 |
|
| 149 |
<div class="aio_red_box">
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
|
|
|
|
|
|
|
|
|
| 155 |
</div>
|
| 156 |
|
| 157 |
<form action="" method="POST">
|
| 158 |
<?php wp_nonce_field('aiowpsec-db-prefix-change-nonce'); ?>
|
| 159 |
<table class="form-table">
|
| 160 |
<tr valign="top">
|
| 161 |
-
<th scope="row"><?php _e('Current
|
| 162 |
<td>
|
| 163 |
<span class="aiowpsec_field_value"><strong><?php echo $wpdb->prefix; ?></strong></span>
|
| 164 |
<?php
|
|
@@ -171,17 +231,22 @@ class AIOWPSecurity_Database_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 171 |
</td>
|
| 172 |
</tr>
|
| 173 |
<tr valign="top">
|
| 174 |
-
<th scope="row"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 175 |
<td>
|
| 176 |
-
<input name="aiowps_enable_random_prefix" type="checkbox" <?php if($aio_wp_security->configs->get_value('aiowps_enable_random_prefix')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 177 |
-
<
|
| 178 |
<br /><?php _e('OR', 'all-in-one-wp-security-and-firewall'); ?>
|
| 179 |
-
<br /><input type="text" size="10" name="aiowps_new_manual_db_prefix" value="<?php //echo $aio_wp_security->configs->get_value('aiowps_new_manual_db_prefix'); ?>" />
|
| 180 |
-
<
|
| 181 |
</td>
|
| 182 |
</tr>
|
| 183 |
</table>
|
| 184 |
-
<input type="submit" name="aiowps_db_prefix_change" value="<?php _e('Change
|
| 185 |
</form>
|
| 186 |
</div></div>
|
| 187 |
<?php
|
|
@@ -191,168 +256,37 @@ class AIOWPSecurity_Database_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 191 |
$this->change_db_prefix($old_db_prefix,$new_db_prefix);
|
| 192 |
}
|
| 193 |
}
|
| 194 |
-
|
| 195 |
-
function render_tab2()
|
| 196 |
-
{
|
| 197 |
-
global $aio_wp_security;
|
| 198 |
-
global $aiowps_feature_mgr;
|
| 199 |
-
if (isset($_POST['aiowps_manual_db_backup']))
|
| 200 |
-
{
|
| 201 |
-
$nonce=$_REQUEST['_wpnonce'];
|
| 202 |
-
if (!wp_verify_nonce($nonce, 'aiowpsec-db-manual-change-nonce'))
|
| 203 |
-
{
|
| 204 |
-
$aio_wp_security->debug_logger->log_debug("Nonce check failed for manual DB backup operation!",4);
|
| 205 |
-
die(__('Nonce check failed for manual DB backup operation!','all-in-one-wp-security-and-firewall'));
|
| 206 |
-
}
|
| 207 |
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
$aiowps_backup_dir = WP_CONTENT_DIR.'/'.AIO_WP_SECURITY_BACKUPS_DIR_NAME;
|
| 219 |
-
$aiowps_backup_file_path = $aiowps_backup_dir. '/' . $backup_file_name;
|
| 220 |
-
}
|
| 221 |
-
echo '<div id="message" class="updated fade"><p>';
|
| 222 |
-
_e('DB Backup was successfully completed! You will receive the backup file via email if you have enabled "Send Backup File Via Email", otherwise you can retrieve it via FTP from the following directory:','all-in-one-wp-security-and-firewall');
|
| 223 |
-
echo '</p><p>';
|
| 224 |
-
_e('Your DB Backup File location: ');
|
| 225 |
-
echo '<strong>'.$aiowps_backup_file_path.'</strong>';
|
| 226 |
-
echo '</p></div>';
|
| 227 |
-
}
|
| 228 |
-
else
|
| 229 |
-
{
|
| 230 |
-
$aio_wp_security->debug_logger->log_debug("DB Backup - Backup operation failed!",4);
|
| 231 |
-
$this->show_msg_error(__('DB Backup failed. Please check the permissions of the backup directory.','all-in-one-wp-security-and-firewall'));
|
| 232 |
-
}
|
| 233 |
}
|
| 234 |
-
|
| 235 |
-
if(isset($_POST['aiowps_schedule_backups']))//Do form submission tasks
|
| 236 |
-
{
|
| 237 |
-
$error = '';
|
| 238 |
-
$nonce=$_REQUEST['_wpnonce'];
|
| 239 |
-
if (!wp_verify_nonce($nonce, 'aiowpsec-scheduled-backup-nonce'))
|
| 240 |
-
{
|
| 241 |
-
$aio_wp_security->debug_logger->log_debug("Nonce check failed on scheduled DB backup options save!",4);
|
| 242 |
-
die("Nonce check failed on scheduled DB backup options save!");
|
| 243 |
-
}
|
| 244 |
-
|
| 245 |
-
$backup_frequency = sanitize_text_field($_POST['aiowps_db_backup_frequency']);
|
| 246 |
-
if(!is_numeric($backup_frequency))
|
| 247 |
-
{
|
| 248 |
-
$error .= '<br />'.__('You entered a non numeric value for the "backup time interval" field. It has been set to the default value.','all-in-one-wp-security-and-firewall');
|
| 249 |
-
$backup_frequency = '4';//Set it to the default value for this field
|
| 250 |
-
}
|
| 251 |
-
|
| 252 |
-
$files_to_keep = sanitize_text_field($_POST['aiowps_backup_files_stored']);
|
| 253 |
-
if(!is_numeric($files_to_keep))
|
| 254 |
-
{
|
| 255 |
-
$error .= '<br />'.__('You entered a non numeric value for the "number of backup files to keep" field. It has been set to the default value.','all-in-one-wp-security-and-firewall');
|
| 256 |
-
$files_to_keep = '2';//Set it to the default value for this field
|
| 257 |
-
}
|
| 258 |
-
|
| 259 |
-
$email_address = sanitize_email($_POST['aiowps_backup_email_address']);
|
| 260 |
-
if(!is_email($email_address))
|
| 261 |
-
{
|
| 262 |
-
$error .= '<br />'.__('You have entered an incorrect email address format. It has been set to your WordPress admin email as default.','all-in-one-wp-security-and-firewall');
|
| 263 |
-
$email_address = get_bloginfo('admin_email'); //Set the default value to the blog admin email
|
| 264 |
-
}
|
| 265 |
-
|
| 266 |
-
if($error)
|
| 267 |
-
{
|
| 268 |
-
$this->show_msg_error(__('Attention!','all-in-one-wp-security-and-firewall').$error);
|
| 269 |
-
}
|
| 270 |
-
|
| 271 |
-
//Save all the form values to the options
|
| 272 |
-
$aio_wp_security->configs->set_value('aiowps_enable_automated_backups',isset($_POST["aiowps_enable_automated_backups"])?'1':'');
|
| 273 |
-
$aio_wp_security->configs->set_value('aiowps_db_backup_frequency',absint($backup_frequency));
|
| 274 |
-
$aio_wp_security->configs->set_value('aiowps_db_backup_interval',$_POST["aiowps_db_backup_interval"]);
|
| 275 |
-
$aio_wp_security->configs->set_value('aiowps_backup_files_stored',absint($files_to_keep));
|
| 276 |
-
$aio_wp_security->configs->set_value('aiowps_send_backup_email_address',isset($_POST["aiowps_send_backup_email_address"])?'1':'');
|
| 277 |
-
$aio_wp_security->configs->set_value('aiowps_backup_email_address',$email_address);
|
| 278 |
-
$aio_wp_security->configs->save_config();
|
| 279 |
-
|
| 280 |
-
//Recalculate points after the feature status/options have been altered
|
| 281 |
-
$aiowps_feature_mgr->check_feature_status_and_recalculate_points();
|
| 282 |
-
$this->show_msg_settings_updated();
|
| 283 |
-
|
| 284 |
-
//Let's check if backup interval was set to less than 24 hours
|
| 285 |
-
if (isset($_POST["aiowps_enable_automated_backups"]) && ($backup_frequency < 24) && $_POST["aiowps_db_backup_interval"]==0)
|
| 286 |
-
{
|
| 287 |
-
$alert_user_msg = 'ATTENTION: You have configured your backups to occur at least once daily. For most websites we recommended that you choose a less frequent backup
|
| 288 |
-
schedule such as once every few days, once a week or once a month. Choosing a less frequent schedule will also help reduce your server load.';
|
| 289 |
-
$this->show_msg_updated_st(__($alert_user_msg, 'all-in-one-wp-security-and-firewall'));
|
| 290 |
-
}
|
| 291 |
-
}
|
| 292 |
-
|
| 293 |
?>
|
| 294 |
<div class="postbox">
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
//Display security info badge
|
| 310 |
-
global $aiowps_feature_mgr;
|
| 311 |
-
$aiowps_feature_mgr->output_feature_details_badge("db-security-db-backup");
|
| 312 |
-
?>
|
| 313 |
-
|
| 314 |
-
<form action="" method="POST">
|
| 315 |
-
<?php wp_nonce_field('aiowpsec-scheduled-backup-nonce'); ?>
|
| 316 |
-
<table class="form-table">
|
| 317 |
-
<tr valign="top">
|
| 318 |
-
<th scope="row"><?php _e('Enable Automated Scheduled Backups', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 319 |
-
<td>
|
| 320 |
-
<input name="aiowps_enable_automated_backups" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_automated_backups')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 321 |
-
<span class="description"><?php _e('Check this if you want the system to automatically generate backups periodically based on the settings below', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 322 |
-
</td>
|
| 323 |
-
</tr>
|
| 324 |
-
<tr valign="top">
|
| 325 |
-
<th scope="row"><?php _e('Backup Time Interval', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 326 |
-
<td><input type="text" size="5" name="aiowps_db_backup_frequency" value="<?php echo $aio_wp_security->configs->get_value('aiowps_db_backup_frequency'); ?>" />
|
| 327 |
-
<select id="backup_interval" name="aiowps_db_backup_interval">
|
| 328 |
-
<option value="0" <?php selected( $aio_wp_security->configs->get_value('aiowps_db_backup_interval'), '0' ); ?>><?php _e( 'Hours', 'all-in-one-wp-security-and-firewall' ); ?></option>
|
| 329 |
-
<option value="1" <?php selected( $aio_wp_security->configs->get_value('aiowps_db_backup_interval'), '1' ); ?>><?php _e( 'Days', 'all-in-one-wp-security-and-firewall' ); ?></option>
|
| 330 |
-
<option value="2" <?php selected( $aio_wp_security->configs->get_value('aiowps_db_backup_interval'), '2' ); ?>><?php _e( 'Weeks', 'all-in-one-wp-security-and-firewall' ); ?></option>
|
| 331 |
-
</select>
|
| 332 |
-
<span class="description"><?php _e('Set the value for how often you would like an automated backup to occur', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 333 |
-
</td>
|
| 334 |
-
</tr>
|
| 335 |
-
<tr valign="top">
|
| 336 |
-
<th scope="row"><?php _e('Number of Backup Files To Keep', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 337 |
-
<td><input type="text" size="5" name="aiowps_backup_files_stored" value="<?php echo $aio_wp_security->configs->get_value('aiowps_backup_files_stored'); ?>" />
|
| 338 |
-
<span class="description"><?php _e('Thie field allows you to choose the number of backup files you would like to keep in the backup directory', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 339 |
-
</td>
|
| 340 |
-
</tr>
|
| 341 |
-
<tr valign="top">
|
| 342 |
-
<th scope="row"><?php _e('Send Backup File Via Email', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 343 |
-
<td>
|
| 344 |
-
<input name="aiowps_send_backup_email_address" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_send_backup_email_address')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 345 |
-
<span class="description"><?php _e('Check this if you want the system to email you the backup file after a DB backup has been performed', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 346 |
-
<br /><input type="text" size="30" name="aiowps_backup_email_address" value="<?php echo $aio_wp_security->configs->get_value('aiowps_backup_email_address'); ?>" />
|
| 347 |
-
<span class="description"><?php _e('Enter an email address', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 348 |
-
</td>
|
| 349 |
-
</tr>
|
| 350 |
-
</table>
|
| 351 |
-
<input type="submit" name="aiowps_schedule_backups" value="<?php _e('Save Settings', 'all-in-one-wp-security-and-firewall')?>" class="button-primary" />
|
| 352 |
-
</form>
|
| 353 |
-
</div></div>
|
| 354 |
-
|
| 355 |
<?php
|
|
|
|
| 356 |
}
|
| 357 |
|
| 358 |
/*
|
|
@@ -395,7 +329,7 @@ class AIOWPSecurity_Database_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 395 |
}
|
| 396 |
|
| 397 |
//Get multisite blog_ids if applicable
|
| 398 |
-
if (
|
| 399 |
$blog_ids = AIOWPSecurity_Utility::get_blog_ids();
|
| 400 |
}
|
| 401 |
|
|
@@ -477,7 +411,7 @@ class AIOWPSecurity_Database_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 477 |
}
|
| 478 |
|
| 479 |
//Now let's update the options tables for the multisite subsites if applicable
|
| 480 |
-
if (
|
| 481 |
if(!empty($blog_ids)){
|
| 482 |
foreach ($blog_ids as $blog_id) {
|
| 483 |
if ($blog_id == 1){continue;} //skip main site
|
|
@@ -527,7 +461,7 @@ class AIOWPSecurity_Database_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 527 |
}
|
| 528 |
echo '<p class="aio_success_with_icon">'.__('The usermeta table records which had references to the old DB prefix were updated successfully!', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 529 |
//Display tasks finished message
|
| 530 |
-
$tasks_finished_msg_string = '<p class="aio_info_with_icon">'. __('
|
| 531 |
echo ($tasks_finished_msg_string);
|
| 532 |
}
|
| 533 |
|
|
@@ -601,4 +535,4 @@ class AIOWPSecurity_Database_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 601 |
return;
|
| 602 |
}
|
| 603 |
|
| 604 |
-
} //end class
|
| 6 |
class AIOWPSecurity_Database_Menu extends AIOWPSecurity_Admin_Menu
|
| 7 |
{
|
| 8 |
var $menu_page_slug = AIOWPSEC_DB_SEC_MENU_SLUG;
|
| 9 |
+
|
| 10 |
/* Specify all the tabs of this menu in the following array */
|
| 11 |
var $menu_tabs;
|
| 12 |
|
| 15 |
'tab2' => 'render_tab2',
|
| 16 |
);
|
| 17 |
|
| 18 |
+
/**
|
| 19 |
+
* Class constructor
|
| 20 |
+
*/
|
| 21 |
+
public function __construct() {
|
| 22 |
$this->render_menu_page();
|
| 23 |
}
|
| 24 |
+
|
| 25 |
+
/**
|
| 26 |
+
* Return installation or activation link of UpdraftPlus plugin
|
| 27 |
+
*
|
| 28 |
+
* @return String
|
| 29 |
+
*/
|
| 30 |
+
private function get_install_activate_link_of_updraft_plugin() {
|
| 31 |
+
// If UpdraftPlus is activated, then return empty.
|
| 32 |
+
if (class_exists('UpdraftPlus')) return '';
|
| 33 |
+
|
| 34 |
+
// Generally it is 'updraftplus/updraftplus.php',
|
| 35 |
+
// but we can't assume that the user hasn't renamed the plugin folder - with 3 million UDP users and 1 million AIOWPS, there will be some who have.
|
| 36 |
+
$updraftplus_plugin_file_rel_to_plugins_dir = $this->get_updraftplus_plugin_file_rel_to_plugins_dir();
|
| 37 |
+
|
| 38 |
+
// If UpdraftPlus is installed but not activated, then return activate link.
|
| 39 |
+
if ($updraftplus_plugin_file_rel_to_plugins_dir) {
|
| 40 |
+
$activate_url = add_query_arg(array(
|
| 41 |
+
'_wpnonce' => wp_create_nonce('activate-plugin_'.$updraftplus_plugin_file_rel_to_plugins_dir),
|
| 42 |
+
'action' => 'activate',
|
| 43 |
+
'plugin' => $updraftplus_plugin_file_rel_to_plugins_dir,
|
| 44 |
+
), network_admin_url('plugins.php'));
|
| 45 |
+
|
| 46 |
+
// If is network admin then add to link newtwork activation.
|
| 47 |
+
if (is_network_admin()) {
|
| 48 |
+
$activate_url = add_query_arg(array('networkwide' => 1), $activate_url);
|
| 49 |
+
}
|
| 50 |
+
return sprintf('<a href="%s">%s</a>',
|
| 51 |
+
$activate_url,
|
| 52 |
+
__('UpdraftPlus is installed but currently not active.', 'all-in-one-wp-security-and-firewall') .' '. __('Follow this link to activate UpdraftPlus, to take a backup.', 'all-in-one-wp-security-and-firewall')
|
| 53 |
+
);
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
// If UpdraftPlus is not activated or installed, then return the installation link
|
| 57 |
+
return '<a href="'.wp_nonce_url(self_admin_url('update.php?action=install-plugin&plugin=updraftplus'), 'install-plugin_updraftplus').'">'.__('Follow this link to install UpdraftPlus, to take a database backup.', 'all-in-one-wp-security-and-firewall').'</a>';
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
/**
|
| 61 |
+
* Get path to the UpdraftPlus plugin file relative to the plugins directory.
|
| 62 |
+
*
|
| 63 |
+
* @return String|false path to the UpdraftPlus plugin file relative to the plugins directory
|
| 64 |
+
*/
|
| 65 |
+
private function get_updraftplus_plugin_file_rel_to_plugins_dir() {
|
| 66 |
+
if (!function_exists('get_plugins')) {
|
| 67 |
+
include_once ABSPATH . '/wp-admin/includes/plugin.php';
|
| 68 |
+
}
|
| 69 |
+
|
| 70 |
+
$installed_plugins = get_plugins();
|
| 71 |
+
$installed_plugins_keys = array_keys($installed_plugins);
|
| 72 |
+
foreach ($installed_plugins_keys as $plugin_file_rel_to_plugins_dir) {
|
| 73 |
+
$temp_plugin_file_name = substr($plugin_file_rel_to_plugins_dir, strpos($plugin_file_rel_to_plugins_dir, '/') + 1);
|
| 74 |
+
if ('updraftplus.php' == $temp_plugin_file_name) {
|
| 75 |
+
return $plugin_file_rel_to_plugins_dir;
|
| 76 |
+
}
|
| 77 |
+
}
|
| 78 |
+
return false;
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
public function set_menu_tabs()
|
| 82 |
{
|
| 83 |
+
if (is_multisite() && get_current_blog_id() != 1){
|
| 84 |
//Suppress the DB prefix change tab if site is a multi site AND not the main site
|
| 85 |
$this->menu_tabs = array(
|
| 86 |
+
//'tab1' => __('Database prefix', 'all-in-one-wp-security-and-firewall'),
|
| 87 |
+
'tab2' => __('Database backup', 'all-in-one-wp-security-and-firewall'),
|
| 88 |
);
|
| 89 |
+
} else {
|
| 90 |
$this->menu_tabs = array(
|
| 91 |
+
'tab1' => __('Database prefix', 'all-in-one-wp-security-and-firewall'),
|
| 92 |
+
'tab2' => __('Database backup', 'all-in-one-wp-security-and-firewall'),
|
| 93 |
);
|
| 94 |
}
|
|
|
|
| 95 |
}
|
| 96 |
|
| 97 |
/*
|
| 183 |
}
|
| 184 |
}
|
| 185 |
?>
|
| 186 |
+
<h2><?php _e('Change database prefix', 'all-in-one-wp-security-and-firewall')?></h2>
|
| 187 |
<div class="aio_blue_box">
|
| 188 |
<?php
|
| 189 |
+
echo '<p>'.__('Your WordPress database is the most important asset of your website because it contains a lot of your site\'s precious information.', 'all-in-one-wp-security-and-firewall').'
|
| 190 |
+
<br />'.__('The database is also a target for hackers via methods such as SQL injections and malicious and automated code which targets certain tables.', 'all-in-one-wp-security-and-firewall').'
|
| 191 |
<br />'.__('One way to add a layer of protection for your DB is to change the default WordPress table prefix from "wp_" to something else which will be difficult for hackers to guess.', 'all-in-one-wp-security-and-firewall').'
|
| 192 |
<br />'.__('This feature allows you to easily change the prefix to a value of your choice or to a random value set by this plugin.', 'all-in-one-wp-security-and-firewall').'
|
| 193 |
</p>';
|
| 195 |
</div>
|
| 196 |
|
| 197 |
<div class="postbox">
|
| 198 |
+
<h3 class="hndle"><label for="title"><?php _e('Database prefix options', 'all-in-one-wp-security-and-firewall'); ?></label></h3>
|
| 199 |
<div class="inside">
|
| 200 |
<?php
|
| 201 |
//Display security info badge
|
| 204 |
?>
|
| 205 |
|
| 206 |
<div class="aio_red_box">
|
| 207 |
+
<p>
|
| 208 |
+
<strong>
|
| 209 |
+
<?php
|
| 210 |
+
$backup_tab_link = '<a href="admin.php?page='.AIOWPSEC_DB_SEC_MENU_SLUG.'&tab=tab2">'.__('database backup', 'all-in-one-wp-security-and-firewall').'</a>';
|
| 211 |
+
printf(__('It is recommended that you perform a %s before using this feature', 'all-in-one-wp-security-and-firewall'), $backup_tab_link);
|
| 212 |
+
?>
|
| 213 |
+
</strong>
|
| 214 |
+
</p>
|
| 215 |
</div>
|
| 216 |
|
| 217 |
<form action="" method="POST">
|
| 218 |
<?php wp_nonce_field('aiowpsec-db-prefix-change-nonce'); ?>
|
| 219 |
<table class="form-table">
|
| 220 |
<tr valign="top">
|
| 221 |
+
<th scope="row"><?php _e('Current database table prefix', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 222 |
<td>
|
| 223 |
<span class="aiowpsec_field_value"><strong><?php echo $wpdb->prefix; ?></strong></span>
|
| 224 |
<?php
|
| 231 |
</td>
|
| 232 |
</tr>
|
| 233 |
<tr valign="top">
|
| 234 |
+
<th scope="row">
|
| 235 |
+
<label>
|
| 236 |
+
<label for="aiowps_new_manual_db_prefix">
|
| 237 |
+
<?php _e('Generate new database table prefix', 'all-in-one-wp-security-and-firewall'); ?>:
|
| 238 |
+
</label>
|
| 239 |
+
</th>
|
| 240 |
<td>
|
| 241 |
+
<input id="aiowps_enable_random_prefix" name="aiowps_enable_random_prefix" type="checkbox" <?php if($aio_wp_security->configs->get_value('aiowps_enable_random_prefix')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 242 |
+
<label for="aiowps_enable_random_prefix" class="description"><?php _e('Check this if you want the plugin to generate a random 6 character string for the table prefix', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 243 |
<br /><?php _e('OR', 'all-in-one-wp-security-and-firewall'); ?>
|
| 244 |
+
<br /><input type="text" size="10" id="aiowps_new_manual_db_prefix" name="aiowps_new_manual_db_prefix" value="<?php //echo $aio_wp_security->configs->get_value('aiowps_new_manual_db_prefix'); ?>" />
|
| 245 |
+
<label for="aiowps_new_manual_db_prefix" class="description"><?php _e('Choose your own DB prefix by specifying a string which contains letters and/or numbers and/or underscores. Example: xyz_', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 246 |
</td>
|
| 247 |
</tr>
|
| 248 |
</table>
|
| 249 |
+
<input type="submit" name="aiowps_db_prefix_change" value="<?php _e('Change database prefix', 'all-in-one-wp-security-and-firewall')?>" class="button-primary" />
|
| 250 |
</form>
|
| 251 |
</div></div>
|
| 252 |
<?php
|
| 256 |
$this->change_db_prefix($old_db_prefix,$new_db_prefix);
|
| 257 |
}
|
| 258 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 259 |
|
| 260 |
+
/**
|
| 261 |
+
* Render tab2 contents.
|
| 262 |
+
*
|
| 263 |
+
* @return void
|
| 264 |
+
*/
|
| 265 |
+
private function render_tab2() {
|
| 266 |
+
global $aio_wp_security;
|
| 267 |
+
$updraftplus_admin = !empty($GLOBALS['updraftplus_admin']) ? $GLOBALS['updraftplus_admin'] : null;
|
| 268 |
+
if ($updraftplus_admin) {
|
| 269 |
+
$updraftplus_admin->add_backup_scaffolding(__('Take a database backup using UpdraftPlus', 'all-in-one-wp-security-and-firewall'), array($updraftplus_admin, 'backupnow_modal_contents'));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 270 |
}
|
| 271 |
+
$install_activate_link = $this->get_install_activate_link_of_updraft_plugin();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 272 |
?>
|
| 273 |
<div class="postbox">
|
| 274 |
+
<h3 class="hndle"><label for="title"><?php _e('Manual Backup', 'all-in-one-wp-security-and-firewall'); ?></label></h3>
|
| 275 |
+
<div class="inside">
|
| 276 |
+
<?php if (empty($install_activate_link)) { ?>
|
| 277 |
+
<p>
|
| 278 |
+
<a href="<?php echo admin_url('options-general.php?page=updraftplus#updraft-existing-backups-heading'); ?>" title="<?php _e('UpdraftPlus Backup/Restore', 'all-in-one-wp-security-and-firewall'); ?>" alt="<?php _e('UpdraftPlus Backup/Restore', 'all-in-one-wp-security-and-firewall'); ?>"><?php echo __('Your backups are on the UpdraftPlus Backup/Restore admin page.', 'all-in-one-wp-security-and-firewall'); ?></a>
|
| 279 |
+
</p>
|
| 280 |
+
<button type="button" id="aios-manual-db-backup-now" class="button-primary"><?php _e('Create database backup now', 'all-in-one-wp-security-and-firewall'); ?></button>
|
| 281 |
+
<?php } else { ?>
|
| 282 |
+
<p>
|
| 283 |
+
<?php echo wp_kses($install_activate_link, array('a' => array('title' => array(), 'href' => array()))); ?>
|
| 284 |
+
</p>
|
| 285 |
+
<?php } ?>
|
| 286 |
+
</div>
|
| 287 |
+
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 288 |
<?php
|
| 289 |
+
$aio_wp_security->include_template('automated-database-backup.php', false, array('install_activate_link' => $install_activate_link));
|
| 290 |
}
|
| 291 |
|
| 292 |
/*
|
| 329 |
}
|
| 330 |
|
| 331 |
//Get multisite blog_ids if applicable
|
| 332 |
+
if (is_multisite()) {
|
| 333 |
$blog_ids = AIOWPSecurity_Utility::get_blog_ids();
|
| 334 |
}
|
| 335 |
|
| 411 |
}
|
| 412 |
|
| 413 |
//Now let's update the options tables for the multisite subsites if applicable
|
| 414 |
+
if (is_multisite()) {
|
| 415 |
if(!empty($blog_ids)){
|
| 416 |
foreach ($blog_ids as $blog_id) {
|
| 417 |
if ($blog_id == 1){continue;} //skip main site
|
| 461 |
}
|
| 462 |
echo '<p class="aio_success_with_icon">'.__('The usermeta table records which had references to the old DB prefix were updated successfully!', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 463 |
//Display tasks finished message
|
| 464 |
+
$tasks_finished_msg_string = '<p class="aio_info_with_icon">'. __('The database prefix change tasks have been completed.', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 465 |
echo ($tasks_finished_msg_string);
|
| 466 |
}
|
| 467 |
|
| 535 |
return;
|
| 536 |
}
|
| 537 |
|
| 538 |
+
} //end class
|
|
@@ -288,13 +288,13 @@ class AIOWPSecurity_Filescan_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 288 |
<tr valign="top">
|
| 289 |
<th scope="row"><?php _e('Enable Automated File Change Detection Scan', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 290 |
<td>
|
| 291 |
-
<input name="aiowps_enable_automated_fcd_scan" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_automated_fcd_scan')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 292 |
-
<
|
| 293 |
</td>
|
| 294 |
</tr>
|
| 295 |
<tr valign="top">
|
| 296 |
-
<th scope="row"><?php _e('Scan Time Interval', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 297 |
-
<td><input type="text" size="5" name="aiowps_fcd_scan_frequency" value="<?php echo $aio_wp_security->configs->get_value('aiowps_fcd_scan_frequency'); ?>" />
|
| 298 |
<select id="backup_interval" name="aiowps_fcd_scan_interval">
|
| 299 |
<option value="0" <?php selected( $aio_wp_security->configs->get_value('aiowps_fcd_scan_interval'), '0' ); ?>><?php _e( 'Hours', 'all-in-one-wp-security-and-firewall' ); ?></option>
|
| 300 |
<option value="1" <?php selected( $aio_wp_security->configs->get_value('aiowps_fcd_scan_interval'), '1' ); ?>><?php _e( 'Days', 'all-in-one-wp-security-and-firewall' ); ?></option>
|
|
@@ -304,8 +304,8 @@ class AIOWPSecurity_Filescan_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 304 |
</td>
|
| 305 |
</tr>
|
| 306 |
<tr valign="top">
|
| 307 |
-
<th scope="row"><?php _e('File Types To Ignore', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 308 |
-
<td><textarea name="aiowps_fcd_exclude_filetypes" rows="5" cols="50"><?php echo htmlspecialchars($aio_wp_security->configs->get_value('aiowps_fcd_exclude_filetypes')); ?></textarea>
|
| 309 |
<br />
|
| 310 |
<span class="description"><?php _e('Enter each file type or extension on a new line which you wish to exclude from the file change detection scan.', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 311 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
|
@@ -321,8 +321,8 @@ class AIOWPSecurity_Filescan_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 321 |
</td>
|
| 322 |
</tr>
|
| 323 |
<tr valign="top">
|
| 324 |
-
<th scope="row"><?php _e('Files/Directories To Ignore', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 325 |
-
<td><textarea name="aiowps_fcd_exclude_files" rows="5" cols="50"><?php echo htmlspecialchars($aio_wp_security->configs->get_value('aiowps_fcd_exclude_files')); ?></textarea>
|
| 326 |
<br />
|
| 327 |
<span class="description"><?php _e('Enter each file or directory on a new line which you wish to exclude from the file change detection scan.', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 328 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
|
@@ -337,12 +337,16 @@ class AIOWPSecurity_Filescan_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 337 |
</td>
|
| 338 |
</tr>
|
| 339 |
<tr valign="top">
|
| 340 |
-
<th scope="row"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 341 |
<td>
|
| 342 |
-
<input name="aiowps_send_fcd_scan_email" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_send_fcd_scan_email')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 343 |
-
<
|
| 344 |
<br />
|
| 345 |
-
<textarea name="aiowps_fcd_scan_email_address" rows="5" cols="50"><?php echo htmlspecialchars($aio_wp_security->configs->get_value('aiowps_fcd_scan_email_address')); ?></textarea>
|
| 346 |
<br />
|
| 347 |
<span class="description"><?php _e('Enter one or more email addresses on a new line.', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 348 |
</td>
|
| 288 |
<tr valign="top">
|
| 289 |
<th scope="row"><?php _e('Enable Automated File Change Detection Scan', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 290 |
<td>
|
| 291 |
+
<input id="aiowps_enable_automated_fcd_scan" name="aiowps_enable_automated_fcd_scan" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_automated_fcd_scan')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 292 |
+
<label for="aiowps_enable_automated_fcd_scan" class="description"><?php _e('Check this if you want the system to automatically/periodically scan your files to check for file changes based on the settings below', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 293 |
</td>
|
| 294 |
</tr>
|
| 295 |
<tr valign="top">
|
| 296 |
+
<th scope="row"><label for="aiowps_fcd_scan_frequency"><?php _e('Scan Time Interval', 'all-in-one-wp-security-and-firewall')?>:</label></th>
|
| 297 |
+
<td><input id="aiowps_fcd_scan_frequency" type="text" size="5" name="aiowps_fcd_scan_frequency" value="<?php echo $aio_wp_security->configs->get_value('aiowps_fcd_scan_frequency'); ?>" />
|
| 298 |
<select id="backup_interval" name="aiowps_fcd_scan_interval">
|
| 299 |
<option value="0" <?php selected( $aio_wp_security->configs->get_value('aiowps_fcd_scan_interval'), '0' ); ?>><?php _e( 'Hours', 'all-in-one-wp-security-and-firewall' ); ?></option>
|
| 300 |
<option value="1" <?php selected( $aio_wp_security->configs->get_value('aiowps_fcd_scan_interval'), '1' ); ?>><?php _e( 'Days', 'all-in-one-wp-security-and-firewall' ); ?></option>
|
| 304 |
</td>
|
| 305 |
</tr>
|
| 306 |
<tr valign="top">
|
| 307 |
+
<th scope="row"><label for="aiowps_fcd_exclude_filetypes"><?php _e('File Types To Ignore', 'all-in-one-wp-security-and-firewall')?>:</label></th>
|
| 308 |
+
<td><textarea id="aiowps_fcd_exclude_filetypes" name="aiowps_fcd_exclude_filetypes" rows="5" cols="50"><?php echo htmlspecialchars($aio_wp_security->configs->get_value('aiowps_fcd_exclude_filetypes')); ?></textarea>
|
| 309 |
<br />
|
| 310 |
<span class="description"><?php _e('Enter each file type or extension on a new line which you wish to exclude from the file change detection scan.', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 311 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 321 |
</td>
|
| 322 |
</tr>
|
| 323 |
<tr valign="top">
|
| 324 |
+
<th scope="row"><label for="aiowps_fcd_exclude_files"><?php _e('Files/Directories To Ignore', 'all-in-one-wp-security-and-firewall')?>:</label></th>
|
| 325 |
+
<td><textarea id="aiowps_fcd_exclude_files" name="aiowps_fcd_exclude_files" rows="5" cols="50"><?php echo htmlspecialchars($aio_wp_security->configs->get_value('aiowps_fcd_exclude_files')); ?></textarea>
|
| 326 |
<br />
|
| 327 |
<span class="description"><?php _e('Enter each file or directory on a new line which you wish to exclude from the file change detection scan.', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 328 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 337 |
</td>
|
| 338 |
</tr>
|
| 339 |
<tr valign="top">
|
| 340 |
+
<th scope="row">
|
| 341 |
+
<label for="aiowps_fcd_scan_email_address">
|
| 342 |
+
<?php _e('Send Email When Change Detected', 'all-in-one-wp-security-and-firewall'); ?>:
|
| 343 |
+
</label>
|
| 344 |
+
</th>
|
| 345 |
<td>
|
| 346 |
+
<input id="aiowps_send_fcd_scan_email" name="aiowps_send_fcd_scan_email" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_send_fcd_scan_email')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 347 |
+
<label for="aiowps_send_fcd_scan_email" class="description"><?php _e('Check this if you want the system to email you if a file change was detected', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 348 |
<br />
|
| 349 |
+
<textarea name="aiowps_fcd_scan_email_address" id="aiowps_fcd_scan_email_address" rows="5" cols="50"><?php echo htmlspecialchars($aio_wp_security->configs->get_value('aiowps_fcd_scan_email_address')); ?></textarea>
|
| 350 |
<br />
|
| 351 |
<span class="description"><?php _e('Enter one or more email addresses on a new line.', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 352 |
</td>
|
|
@@ -234,8 +234,8 @@ class AIOWPSecurity_Filesystem_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 234 |
<tr valign="top">
|
| 235 |
<th scope="row"><?php _e('Disable Ability To Edit PHP Files', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 236 |
<td>
|
| 237 |
-
<input name="aiowps_disable_file_editing" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_disable_file_editing')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 238 |
-
<
|
| 239 |
</td>
|
| 240 |
</tr>
|
| 241 |
</table>
|
|
@@ -310,8 +310,8 @@ class AIOWPSecurity_Filesystem_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 310 |
<tr valign="top">
|
| 311 |
<th scope="row"><?php _e('Prevent Access to WP Default Install Files', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 312 |
<td>
|
| 313 |
-
<input name="aiowps_prevent_default_wp_file_access" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_prevent_default_wp_file_access')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 314 |
-
<
|
| 315 |
</td>
|
| 316 |
</tr>
|
| 317 |
</table>
|
|
@@ -355,8 +355,8 @@ class AIOWPSecurity_Filesystem_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 355 |
<p><?php _e('Please click the button below to view the latest system logs', 'all-in-one-wp-security-and-firewall'); ?>:</p>
|
| 356 |
<form action="" method="POST">
|
| 357 |
<?php wp_nonce_field('aiowpsec-view-system-logs-nonce'); ?>
|
| 358 |
-
<div><?php _e('Enter System Log File Name', 'all-in-one-wp-security-and-firewall')
|
| 359 |
-
<input type="text" size="25" name="aiowps_system_log_file" value="<?php echo esc_html($sys_log_file); ?>" />
|
| 360 |
<span class="description"><?php _e('Enter your system log file name. (Defaults to error_log)', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 361 |
</div>
|
| 362 |
<div class="aio_spacer_15"></div>
|
| 234 |
<tr valign="top">
|
| 235 |
<th scope="row"><?php _e('Disable Ability To Edit PHP Files', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 236 |
<td>
|
| 237 |
+
<input id="aiowps_disable_file_editing" name="aiowps_disable_file_editing" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_disable_file_editing')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 238 |
+
<label for="aiowps_disable_file_editing" class="description"><?php _e('Check this if you want to remove the ability for people to edit PHP files via the WP dashboard', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 239 |
</td>
|
| 240 |
</tr>
|
| 241 |
</table>
|
| 310 |
<tr valign="top">
|
| 311 |
<th scope="row"><?php _e('Prevent Access to WP Default Install Files', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 312 |
<td>
|
| 313 |
+
<input id="aiowps_prevent_default_wp_file_access" name="aiowps_prevent_default_wp_file_access" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_prevent_default_wp_file_access')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 314 |
+
<label for="aiowps_prevent_default_wp_file_access" class="description"><?php _e('Check this if you want to prevent access to readme.html, license.txt and wp-config-sample.php.', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 315 |
</td>
|
| 316 |
</tr>
|
| 317 |
</table>
|
| 355 |
<p><?php _e('Please click the button below to view the latest system logs', 'all-in-one-wp-security-and-firewall'); ?>:</p>
|
| 356 |
<form action="" method="POST">
|
| 357 |
<?php wp_nonce_field('aiowpsec-view-system-logs-nonce'); ?>
|
| 358 |
+
<div><label for="aiowps_system_log_file"><?php _e('Enter System Log File Name', 'all-in-one-wp-security-and-firewall')?>:</label>
|
| 359 |
+
<input id="aiowps_system_log_file" type="text" size="25" name="aiowps_system_log_file" value="<?php echo esc_html($sys_log_file); ?>" />
|
| 360 |
<span class="description"><?php _e('Enter your system log file name. (Defaults to error_log)', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 361 |
</div>
|
| 362 |
<div class="aio_spacer_15"></div>
|
|
@@ -72,8 +72,12 @@ class AIOWPSecurity_Firewall_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 72 |
<?php
|
| 73 |
}
|
| 74 |
|
| 75 |
-
|
| 76 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
global $aiowps_feature_mgr;
|
| 78 |
global $aio_wp_security;
|
| 79 |
if(isset($_POST['aiowps_apply_basic_firewall_settings']))//Do form submission tasks
|
|
@@ -166,8 +170,8 @@ class AIOWPSecurity_Firewall_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 166 |
<tr valign="top">
|
| 167 |
<th scope="row"><?php _e('Enable Basic Firewall Protection', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 168 |
<td>
|
| 169 |
-
<input name="aiowps_enable_basic_firewall" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_basic_firewall')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 170 |
-
<
|
| 171 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 172 |
<div class="aiowps_more_info_body">
|
| 173 |
<?php
|
|
@@ -183,8 +187,8 @@ class AIOWPSecurity_Firewall_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 183 |
</td>
|
| 184 |
</tr>
|
| 185 |
<tr valign="top">
|
| 186 |
-
<th scope="row"><?php _e('Max File Upload Size (MB)', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 187 |
-
<td><input type="number" min="0" step="1" name="aiowps_max_file_upload_size" value="<?php echo esc_html($aio_wp_security->configs->get_value('aiowps_max_file_upload_size')); ?>" />
|
| 188 |
<span class="description"><?php _e('The value for the maximum file upload size used in the .htaccess file. (Defaults to 10MB if left blank)', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 189 |
</td>
|
| 190 |
</tr>
|
|
@@ -203,8 +207,8 @@ class AIOWPSecurity_Firewall_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 203 |
<tr valign="top">
|
| 204 |
<th scope="row"><?php _e('Completely Block Access To XMLRPC', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 205 |
<td>
|
| 206 |
-
<input name="aiowps_enable_pingback_firewall" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_pingback_firewall')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 207 |
-
<
|
| 208 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 209 |
<div class="aiowps_more_info_body">
|
| 210 |
<?php
|
|
@@ -223,8 +227,8 @@ class AIOWPSecurity_Firewall_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 223 |
<tr valign="top">
|
| 224 |
<th scope="row"><?php _e('Disable Pingback Functionality From XMLRPC', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 225 |
<td>
|
| 226 |
-
<input name="aiowps_disable_xmlrpc_pingback_methods" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_disable_xmlrpc_pingback_methods')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 227 |
-
<
|
| 228 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 229 |
<div class="aiowps_more_info_body">
|
| 230 |
<?php
|
|
@@ -249,13 +253,13 @@ class AIOWPSecurity_Firewall_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 249 |
<tr valign="top">
|
| 250 |
<th scope="row"><?php _e('Block Access to debug.log File', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 251 |
<td>
|
| 252 |
-
<input name="aiowps_block_debug_log_file_access" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_block_debug_log_file_access')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 253 |
-
<
|
| 254 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 255 |
<div class="aiowps_more_info_body">
|
| 256 |
<?php
|
| 257 |
echo '<p class="description">'.__('WordPress has an option to turn on the debug logging to a file located in wp-content/debug.log. This file may contain sensitive information.', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 258 |
-
echo '<p class="description">'.__('Using this
|
| 259 |
?>
|
| 260 |
</div>
|
| 261 |
</td>
|
|
@@ -376,8 +380,8 @@ class AIOWPSecurity_Firewall_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 376 |
<tr valign="top">
|
| 377 |
<th scope="row"><?php _e('Disable Index Views', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 378 |
<td>
|
| 379 |
-
<input name="aiowps_disable_index_views" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_disable_index_views')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 380 |
-
<
|
| 381 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 382 |
<div class="aiowps_more_info_body">
|
| 383 |
<p class="description">
|
|
@@ -406,8 +410,8 @@ class AIOWPSecurity_Firewall_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 406 |
<tr valign="top">
|
| 407 |
<th scope="row"><?php _e('Disable Trace and Track', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 408 |
<td>
|
| 409 |
-
<input name="aiowps_disable_trace_and_track" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_disable_trace_and_track')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 410 |
-
<
|
| 411 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 412 |
<div class="aiowps_more_info_body">
|
| 413 |
<p class="description">
|
|
@@ -437,8 +441,8 @@ class AIOWPSecurity_Firewall_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 437 |
<tr valign="top">
|
| 438 |
<th scope="row"><?php _e('Forbid Proxy Comment Posting', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 439 |
<td>
|
| 440 |
-
<input name="aiowps_forbid_proxy_comments" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_forbid_proxy_comments')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 441 |
-
<
|
| 442 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 443 |
<div class="aiowps_more_info_body">
|
| 444 |
<p class="description">
|
|
@@ -465,8 +469,8 @@ class AIOWPSecurity_Firewall_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 465 |
<tr valign="top">
|
| 466 |
<th scope="row"><?php _e('Deny Bad Query Strings', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 467 |
<td>
|
| 468 |
-
<input name="aiowps_deny_bad_query_strings" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_deny_bad_query_strings')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 469 |
-
<
|
| 470 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 471 |
<div class="aiowps_more_info_body">
|
| 472 |
<p class="description">
|
|
@@ -494,8 +498,8 @@ class AIOWPSecurity_Firewall_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 494 |
<tr valign="top">
|
| 495 |
<th scope="row"><?php _e('Enable Advanced Character String Filter', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 496 |
<td>
|
| 497 |
-
<input name="aiowps_advanced_char_string_filter" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_advanced_char_string_filter')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 498 |
-
<
|
| 499 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 500 |
<div class="aiowps_more_info_body">
|
| 501 |
<p class="description">
|
|
@@ -516,60 +520,133 @@ class AIOWPSecurity_Firewall_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 516 |
<?php
|
| 517 |
}
|
| 518 |
|
| 519 |
-
|
| 520 |
-
|
| 521 |
-
|
| 522 |
-
|
| 523 |
-
|
| 524 |
-
|
| 525 |
-
|
| 526 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 527 |
$aio_wp_security->debug_logger->log_debug("Nonce check failed on enable 5G/6G firewall settings!",4);
|
| 528 |
die("Nonce check failed on enable 5G/6G firewall settings!");
|
| 529 |
}
|
| 530 |
|
| 531 |
//Save settings
|
| 532 |
-
if(isset($_POST['aiowps_enable_5g_firewall']))
|
| 533 |
-
|
| 534 |
-
$
|
| 535 |
-
}
|
| 536 |
-
|
| 537 |
-
{
|
| 538 |
-
$aio_wp_security->configs->set_value('aiowps_enable_5g_firewall','');
|
| 539 |
-
}
|
| 540 |
-
if(isset($_POST['aiowps_enable_6g_firewall']))
|
| 541 |
-
{
|
| 542 |
-
$aio_wp_security->configs->set_value('aiowps_enable_6g_firewall','1');
|
| 543 |
}
|
| 544 |
-
|
| 545 |
-
{
|
| 546 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 547 |
}
|
| 548 |
|
| 549 |
//Commit the config settings
|
| 550 |
$aio_wp_security->configs->save_config();
|
| 551 |
|
| 552 |
-
|
| 553 |
-
$res = AIOWPSecurity_Utility_Htaccess::write_to_htaccess();
|
| 554 |
-
|
| 555 |
-
if ($res)
|
| 556 |
-
{
|
| 557 |
$this->show_msg_updated(__('You have successfully saved the 5G/6G Firewall Protection configuration', 'all-in-one-wp-security-and-firewall'));
|
| 558 |
// Recalculate points after the feature status/options have been altered
|
| 559 |
$aiowps_feature_mgr->check_feature_status_and_recalculate_points();
|
| 560 |
-
}
|
| 561 |
-
else
|
| 562 |
-
{
|
| 563 |
$this->show_msg_error(__('Could not write to the .htaccess file. Please check the file permissions.', 'all-in-one-wp-security-and-firewall'));
|
| 564 |
}
|
| 565 |
}
|
| 566 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 567 |
?>
|
| 568 |
<h2><?php _e('Firewall Settings', 'all-in-one-wp-security-and-firewall')?></h2>
|
| 569 |
<div class="aio_blue_box">
|
| 570 |
<?php
|
| 571 |
-
$backup_tab_link = '<a href="admin.php?page='.AIOWPSEC_SETTINGS_MENU_SLUG.'&tab=tab2" target="_blank">backup</a>';
|
| 572 |
-
$info_msg = '<p>'.sprintf(
|
| 573 |
$info_msg .= '<p>'.__('The 6G Blacklist is updated and improved version of 5G Blacklist. If you have 5G Blacklist active, you might consider activating 6G Blacklist instead.', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 574 |
$info_msg .= '<p>'.__('The 6G Blacklist is a simple, flexible blacklist that helps reduce the number of malicious URL requests that hit your website.', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 575 |
$info_msg .= '<p>'.__('The added advantage of applying the 6G firewall to your site is that it has been tested and confirmed by the people at PerishablePress.com to be an optimal and least disruptive set of .htaccess security rules for general WP sites running on an Apache server or similar.', 'all-in-one-wp-security-and-firewall').'</p>';
|
|
@@ -593,8 +670,8 @@ class AIOWPSecurity_Firewall_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 593 |
<tr valign="top">
|
| 594 |
<th scope="row"><?php _e('Enable 6G Firewall Protection', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 595 |
<td>
|
| 596 |
-
<input name="aiowps_enable_6g_firewall" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_6g_firewall')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 597 |
-
<
|
| 598 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 599 |
<div class="aiowps_more_info_body">
|
| 600 |
<?php
|
|
@@ -611,8 +688,8 @@ class AIOWPSecurity_Firewall_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 611 |
<tr valign="top">
|
| 612 |
<th scope="row"><?php _e('Enable legacy 5G Firewall Protection', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 613 |
<td>
|
| 614 |
-
<input name="aiowps_enable_5g_firewall" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_5g_firewall')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 615 |
-
<
|
| 616 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 617 |
<div class="aiowps_more_info_body">
|
| 618 |
<?php
|
|
@@ -630,6 +707,70 @@ class AIOWPSecurity_Firewall_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 630 |
<input type="submit" name="aiowps_apply_5g_6g_firewall_settings" value="<?php _e('Save 5G/6G Firewall Settings', 'all-in-one-wp-security-and-firewall')?>" class="button-primary" />
|
| 631 |
</form>
|
| 632 |
</div></div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 633 |
<?php
|
| 634 |
}
|
| 635 |
|
|
@@ -701,8 +842,8 @@ class AIOWPSecurity_Firewall_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 701 |
<tr valign="top">
|
| 702 |
<th scope="row"><?php _e('Block Fake Googlebots', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 703 |
<td>
|
| 704 |
-
<input name="aiowps_block_fake_googlebots" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_block_fake_googlebots')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 705 |
-
<
|
| 706 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 707 |
<div class="aiowps_more_info_body">
|
| 708 |
<?php
|
|
@@ -720,8 +861,7 @@ class AIOWPSecurity_Firewall_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 720 |
<?php
|
| 721 |
}
|
| 722 |
|
| 723 |
-
function render_tab5()
|
| 724 |
-
{
|
| 725 |
global $aio_wp_security;
|
| 726 |
global $aiowps_feature_mgr;
|
| 727 |
|
|
@@ -776,8 +916,8 @@ class AIOWPSecurity_Firewall_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 776 |
<tr valign="top">
|
| 777 |
<th scope="row"><?php _e('Prevent Image Hotlinking', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 778 |
<td>
|
| 779 |
-
<input name="aiowps_prevent_hotlinking" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_prevent_hotlinking')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 780 |
-
<
|
| 781 |
</td>
|
| 782 |
</tr>
|
| 783 |
</table>
|
|
@@ -920,8 +1060,8 @@ class AIOWPSecurity_Firewall_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 920 |
<tr valign="top">
|
| 921 |
<th scope="row"><?php _e('Enable 404 IP Detection and Lockout', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 922 |
<td>
|
| 923 |
-
<input name="aiowps_enable_404_IP_lockout" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_404_IP_lockout')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 924 |
-
<
|
| 925 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 926 |
<div class="aiowps_more_info_body">
|
| 927 |
<p class="description">
|
|
@@ -942,8 +1082,8 @@ class AIOWPSecurity_Firewall_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 942 |
</tr>
|
| 943 |
-->
|
| 944 |
<tr valign="top">
|
| 945 |
-
<th scope="row"><?php _e('Time Length of 404 Lockout (min)', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 946 |
-
<td><input type="text" size="5" name="aiowps_404_lockout_time_length" value="<?php echo $aio_wp_security->configs->get_value('aiowps_404_lockout_time_length'); ?>" />
|
| 947 |
<span class="description"><?php _e('Set the length of time for which a blocked IP address will be prevented from visiting your site', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 948 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 949 |
<div class="aiowps_more_info_body">
|
|
@@ -958,8 +1098,8 @@ class AIOWPSecurity_Firewall_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 958 |
</td>
|
| 959 |
</tr>
|
| 960 |
<tr valign="top">
|
| 961 |
-
<th scope="row"><?php _e('404 Lockout Redirect URL', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 962 |
-
<td><input type="text" size="50" name="aiowps_404_lock_redirect_url" value="<?php echo esc_url_raw( $aio_wp_security->configs->get_value('aiowps_404_lock_redirect_url'), array( 'http', 'https' ) ); ?>" />
|
| 963 |
<span class="description"><?php _e('A blocked visitor will be automatically redirected to this URL.', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 964 |
</td>
|
| 965 |
</tr>
|
|
@@ -1099,21 +1239,21 @@ class AIOWPSecurity_Firewall_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 1099 |
<tr valign="top">
|
| 1100 |
<th scope="row"><?php _e('Enable Custom .htaccess Rules', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 1101 |
<td>
|
| 1102 |
-
<input name="aiowps_enable_custom_rules" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_custom_rules')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 1103 |
-
<
|
| 1104 |
</td>
|
| 1105 |
</tr>
|
| 1106 |
<tr valign="top">
|
| 1107 |
<th scope="row"><?php _e('Place custom rules at the top', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 1108 |
<td>
|
| 1109 |
-
<input name="aiowps_place_custom_rules_at_top" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_place_custom_rules_at_top')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 1110 |
-
<
|
| 1111 |
</td>
|
| 1112 |
</tr>
|
| 1113 |
<tr valign="top">
|
| 1114 |
-
<th scope="row"><?php _e('Enter Custom .htaccess Rules:', 'all-in-one-wp-security-and-firewall')?></th>
|
| 1115 |
<td>
|
| 1116 |
-
<textarea name="aiowps_custom_rules" rows="35" cols="50"><?php echo htmlspecialchars($aio_wp_security->configs->get_value('aiowps_custom_rules')); ?></textarea>
|
| 1117 |
<br />
|
| 1118 |
<span class="description"><?php _e('Enter your custom .htaccess rules/directives.','all-in-one-wp-security-and-firewall');?></span>
|
| 1119 |
</td>
|
|
@@ -1125,4 +1265,4 @@ class AIOWPSecurity_Firewall_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 1125 |
<?php
|
| 1126 |
}
|
| 1127 |
|
| 1128 |
-
} //end class
|
| 72 |
<?php
|
| 73 |
}
|
| 74 |
|
| 75 |
+
/**
|
| 76 |
+
* Renders the submenu's tab1 tab body.
|
| 77 |
+
*
|
| 78 |
+
* @return Void
|
| 79 |
+
*/
|
| 80 |
+
public function render_tab1() {
|
| 81 |
global $aiowps_feature_mgr;
|
| 82 |
global $aio_wp_security;
|
| 83 |
if(isset($_POST['aiowps_apply_basic_firewall_settings']))//Do form submission tasks
|
| 170 |
<tr valign="top">
|
| 171 |
<th scope="row"><?php _e('Enable Basic Firewall Protection', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 172 |
<td>
|
| 173 |
+
<input id="aiowps_enable_basic_firewall" name="aiowps_enable_basic_firewall" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_basic_firewall')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 174 |
+
<label for="aiowps_enable_basic_firewall" class="description"><?php _e('Check this if you want to apply basic firewall protection to your site.', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 175 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 176 |
<div class="aiowps_more_info_body">
|
| 177 |
<?php
|
| 187 |
</td>
|
| 188 |
</tr>
|
| 189 |
<tr valign="top">
|
| 190 |
+
<th scope="row"><label for="aiowps_max_file_upload_size"><?php _e('Max File Upload Size (MB)', 'all-in-one-wp-security-and-firewall')?>:</label></th>
|
| 191 |
+
<td><input id="aiowps_max_file_upload_size" type="number" min="0" step="1" name="aiowps_max_file_upload_size" value="<?php echo esc_html($aio_wp_security->configs->get_value('aiowps_max_file_upload_size')); ?>" />
|
| 192 |
<span class="description"><?php _e('The value for the maximum file upload size used in the .htaccess file. (Defaults to 10MB if left blank)', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 193 |
</td>
|
| 194 |
</tr>
|
| 207 |
<tr valign="top">
|
| 208 |
<th scope="row"><?php _e('Completely Block Access To XMLRPC', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 209 |
<td>
|
| 210 |
+
<input id="aiowps_enable_pingback_firewall" name="aiowps_enable_pingback_firewall" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_pingback_firewall')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 211 |
+
<label for="aiowps_enable_pingback_firewall" class="description"><?php _e('Check this if you are not using the WP XML-RPC functionality and you want to completely block external access to XMLRPC.', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 212 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 213 |
<div class="aiowps_more_info_body">
|
| 214 |
<?php
|
| 227 |
<tr valign="top">
|
| 228 |
<th scope="row"><?php _e('Disable Pingback Functionality From XMLRPC', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 229 |
<td>
|
| 230 |
+
<input id="aiowps_disable_xmlrpc_pingback_methods" name="aiowps_disable_xmlrpc_pingback_methods" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_disable_xmlrpc_pingback_methods')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 231 |
+
<label for="aiowps_disable_xmlrpc_pingback_methods" class="description"><?php _e('If you use Jetpack or WP iOS or other apps which need WP XML-RPC functionality then check this. This will enable protection against WordPress pingback vulnerabilities.', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 232 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 233 |
<div class="aiowps_more_info_body">
|
| 234 |
<?php
|
| 253 |
<tr valign="top">
|
| 254 |
<th scope="row"><?php _e('Block Access to debug.log File', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 255 |
<td>
|
| 256 |
+
<input id="aiowps_block_debug_log_file_access" name="aiowps_block_debug_log_file_access" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_block_debug_log_file_access')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 257 |
+
<label for="aiowps_block_debug_log_file_access" class="description"><?php _e('Check this if you want to block access to the debug.log file that WordPress creates when debug logging is enabled.', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 258 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 259 |
<div class="aiowps_more_info_body">
|
| 260 |
<?php
|
| 261 |
echo '<p class="description">'.__('WordPress has an option to turn on the debug logging to a file located in wp-content/debug.log. This file may contain sensitive information.', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 262 |
+
echo '<p class="description">'.__('Using this option will block external access to this file.', 'all-in-one-wp-security-and-firewall').' '.__('You can still access this file by logging into your site via FTP.', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 263 |
?>
|
| 264 |
</div>
|
| 265 |
</td>
|
| 380 |
<tr valign="top">
|
| 381 |
<th scope="row"><?php _e('Disable Index Views', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 382 |
<td>
|
| 383 |
+
<input id="aiowps_disable_index_views" name="aiowps_disable_index_views" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_disable_index_views')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 384 |
+
<label for="aiowps_disable_index_views" class="description"><?php _e('Check this if you want to disable directory and file listing.', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 385 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 386 |
<div class="aiowps_more_info_body">
|
| 387 |
<p class="description">
|
| 410 |
<tr valign="top">
|
| 411 |
<th scope="row"><?php _e('Disable Trace and Track', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 412 |
<td>
|
| 413 |
+
<input id="aiowps_disable_trace_and_track" name="aiowps_disable_trace_and_track" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_disable_trace_and_track')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 414 |
+
<label for="aiowps_disable_trace_and_track" class="description"><?php _e('Check this if you want to disable trace and track.', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 415 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 416 |
<div class="aiowps_more_info_body">
|
| 417 |
<p class="description">
|
| 441 |
<tr valign="top">
|
| 442 |
<th scope="row"><?php _e('Forbid Proxy Comment Posting', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 443 |
<td>
|
| 444 |
+
<input id="aiowps_forbid_proxy_comments" name="aiowps_forbid_proxy_comments" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_forbid_proxy_comments')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 445 |
+
<label for="aiowps_forbid_proxy_comments" class="description"><?php _e('Check this if you want to forbid proxy comment posting.', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 446 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 447 |
<div class="aiowps_more_info_body">
|
| 448 |
<p class="description">
|
| 469 |
<tr valign="top">
|
| 470 |
<th scope="row"><?php _e('Deny Bad Query Strings', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 471 |
<td>
|
| 472 |
+
<input id="aiowps_deny_bad_query_strings" name="aiowps_deny_bad_query_strings" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_deny_bad_query_strings')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 473 |
+
<label for="aiowps_deny_bad_query_strings" class="description"><?php _e('This will help protect you against malicious queries via XSS.', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 474 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 475 |
<div class="aiowps_more_info_body">
|
| 476 |
<p class="description">
|
| 498 |
<tr valign="top">
|
| 499 |
<th scope="row"><?php _e('Enable Advanced Character String Filter', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 500 |
<td>
|
| 501 |
+
<input id="aiowps_advanced_char_string_filter" name="aiowps_advanced_char_string_filter" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_advanced_char_string_filter')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 502 |
+
<label for="aiowps_advanced_char_string_filter" class="description"><?php _e('This will block bad character matches from XSS.', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 503 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 504 |
<div class="aiowps_more_info_body">
|
| 505 |
<p class="description">
|
| 520 |
<?php
|
| 521 |
}
|
| 522 |
|
| 523 |
+
/**
|
| 524 |
+
* Renders the 6G Blacklist Firewall Rules tab
|
| 525 |
+
*
|
| 526 |
+
* @return void
|
| 527 |
+
*/
|
| 528 |
+
private function render_tab3() {
|
| 529 |
+
global $aio_wp_security, $aiowps_feature_mgr, $aiowps_firewall_config;
|
| 530 |
+
|
| 531 |
+
$block_request_methods = array_map('strtolower', AIOS_Abstracted_Ids::get_firewall_block_request_methods());
|
| 532 |
+
|
| 533 |
+
//Other 6G settings form submission
|
| 534 |
+
if (isset($_POST['aiowps_apply_6g_other_settings'])) {
|
| 535 |
+
|
| 536 |
+
if (!wp_verify_nonce($_POST['_wpnonce'], 'aiowpsec-other-6g-settings-nonce')) {
|
| 537 |
+
$aio_wp_security->debug_logger->log_debug('Nonce check failed for other 6G settings.');
|
| 538 |
+
die("Nonce check failed");
|
| 539 |
+
}
|
| 540 |
+
|
| 541 |
+
$aiowps_firewall_config->set_value('aiowps_6g_block_query', (bool) isset($_POST['aiowps_block_query']));
|
| 542 |
+
$aiowps_firewall_config->set_value('aiowps_6g_block_request', (bool) isset($_POST['aiowps_block_request']));
|
| 543 |
+
$aiowps_firewall_config->set_value('aiowps_6g_block_referrers', (bool) isset($_POST['aiowps_block_refs']));
|
| 544 |
+
$aiowps_firewall_config->set_value('aiowps_6g_block_agents', (bool) isset($_POST['aiowps_block_agents']));
|
| 545 |
+
}
|
| 546 |
+
|
| 547 |
+
//Block request methods form
|
| 548 |
+
if (isset($_POST['aiowps_apply_6g_block_request_methods_settings'])) {
|
| 549 |
+
|
| 550 |
+
if (!wp_verify_nonce($_POST['_wpnonce'], 'aiowpsec-6g-block-request-methods-nonce')) {
|
| 551 |
+
$aio_wp_security->debug_logger->log_debug('Nonce check failed for blocking HTTP request methods');
|
| 552 |
+
die("Nonce check failed");
|
| 553 |
+
}
|
| 554 |
+
|
| 555 |
+
$methods = array();
|
| 556 |
+
|
| 557 |
+
foreach ($block_request_methods as $block_request_method) {
|
| 558 |
+
if (isset($_POST['aiowps_block_request_method_'.$block_request_method])) {
|
| 559 |
+
$methods[] = strtoupper($block_request_method);
|
| 560 |
+
}
|
| 561 |
+
}
|
| 562 |
+
|
| 563 |
+
$aiowps_firewall_config->set_value('aiowps_6g_block_request_methods', $methods);
|
| 564 |
+
}
|
| 565 |
+
|
| 566 |
+
//Save 6G/5G
|
| 567 |
+
if (isset($_POST['aiowps_apply_5g_6g_firewall_settings'])) {
|
| 568 |
+
if (!wp_verify_nonce($_POST['_wpnonce'], 'aiowpsec-enable-5g-6g-firewall-nonce')) {
|
| 569 |
$aio_wp_security->debug_logger->log_debug("Nonce check failed on enable 5G/6G firewall settings!",4);
|
| 570 |
die("Nonce check failed on enable 5G/6G firewall settings!");
|
| 571 |
}
|
| 572 |
|
| 573 |
//Save settings
|
| 574 |
+
if (isset($_POST['aiowps_enable_5g_firewall'])) {
|
| 575 |
+
$aio_wp_security->configs->set_value('aiowps_enable_5g_firewall', '1');
|
| 576 |
+
$res = AIOWPSecurity_Utility_Htaccess::write_to_htaccess(); //Now let's write the applicable rules to the .htaccess file
|
| 577 |
+
} else {
|
| 578 |
+
$aio_wp_security->configs->set_value('aiowps_enable_5g_firewall', '');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 579 |
}
|
| 580 |
+
|
| 581 |
+
if (isset($_POST['aiowps_enable_6g_firewall'])) {
|
| 582 |
+
$aiowps_firewall_config->set_value('aiowps_6g_block_request_methods', AIOS_Abstracted_Ids::get_firewall_block_request_methods());
|
| 583 |
+
$aiowps_firewall_config->set_value('aiowps_6g_block_query', true);
|
| 584 |
+
$aiowps_firewall_config->set_value('aiowps_6g_block_request', true);
|
| 585 |
+
$aiowps_firewall_config->set_value('aiowps_6g_block_referrers', true);
|
| 586 |
+
$aiowps_firewall_config->set_value('aiowps_6g_block_agents', true);
|
| 587 |
+
$aio_wp_security->configs->set_value('aiowps_enable_6g_firewall', '1');
|
| 588 |
+
$res = true; //shows the success notice
|
| 589 |
+
} else {
|
| 590 |
+
$aiowps_firewall_config->set_value('aiowps_6g_block_request_methods', array());
|
| 591 |
+
$aiowps_firewall_config->set_value('aiowps_6g_block_query', false);
|
| 592 |
+
$aiowps_firewall_config->set_value('aiowps_6g_block_request', false);
|
| 593 |
+
$aiowps_firewall_config->set_value('aiowps_6g_block_referrers', false);
|
| 594 |
+
$aiowps_firewall_config->set_value('aiowps_6g_block_agents', false);
|
| 595 |
+
$aio_wp_security->configs->set_value('aiowps_enable_6g_firewall', '');
|
| 596 |
+
$res = true;
|
| 597 |
}
|
| 598 |
|
| 599 |
//Commit the config settings
|
| 600 |
$aio_wp_security->configs->save_config();
|
| 601 |
|
| 602 |
+
if ($res) {
|
|
|
|
|
|
|
|
|
|
|
|
|
| 603 |
$this->show_msg_updated(__('You have successfully saved the 5G/6G Firewall Protection configuration', 'all-in-one-wp-security-and-firewall'));
|
| 604 |
// Recalculate points after the feature status/options have been altered
|
| 605 |
$aiowps_feature_mgr->check_feature_status_and_recalculate_points();
|
| 606 |
+
} else {
|
|
|
|
|
|
|
| 607 |
$this->show_msg_error(__('Could not write to the .htaccess file. Please check the file permissions.', 'all-in-one-wp-security-and-firewall'));
|
| 608 |
}
|
| 609 |
}
|
| 610 |
|
| 611 |
+
//Load required data from config
|
| 612 |
+
if (!empty($aiowps_firewall_config)) {
|
| 613 |
+
// firewall config is available
|
| 614 |
+
$methods = $aiowps_firewall_config->get_value('aiowps_6g_block_request_methods');
|
| 615 |
+
if (empty($methods)) {
|
| 616 |
+
$methods = array();
|
| 617 |
+
}
|
| 618 |
+
|
| 619 |
+
$blocked_query = (bool) $aiowps_firewall_config->get_value('aiowps_6g_block_query');
|
| 620 |
+
$blocked_request = (bool) $aiowps_firewall_config->get_value('aiowps_6g_block_request');
|
| 621 |
+
$blocked_referrers = (bool) $aiowps_firewall_config->get_value('aiowps_6g_block_referrers');
|
| 622 |
+
$blocked_agents = (bool) $aiowps_firewall_config->get_value('aiowps_6g_block_agents');
|
| 623 |
+
} else {
|
| 624 |
+
// firewall config is unavailable
|
| 625 |
+
?>
|
| 626 |
+
<div class="notice notice-error">
|
| 627 |
+
<p><strong><?php _e('All in One WP Security and Firewall', 'all-in-one-wp-security-and-firewall'); ?></strong></p>
|
| 628 |
+
<p><?php _e('We were unable to access the firewall\'s configuration file:', 'all-in-one-wp-security-and-firewall');?></p>
|
| 629 |
+
<pre style='max-width: 100%;background-color: #f0f0f0;border:#ccc solid 1px;padding: 10px;white-space:pre-wrap;'><?php echo esc_html(AIOWPSecurity_Utility_Firewall::get_firewall_rules_path() . 'settings');?></pre>
|
| 630 |
+
<p><?php _e('As a result, the firewall will be unavailable.', 'all-in-one-wp-security-and-firewall');?></p>
|
| 631 |
+
<p><?php _e('Please check your PHP error log for further information.', 'all-in-one-wp-security-and-firewall');?></p>
|
| 632 |
+
<p><?php _e('If you\'re unable to locate your PHP log file, please contact your web hosting company to ask them where it can be found on their setup.', 'all-in-one-wp-security-and-firewall');?></p>
|
| 633 |
+
</div>
|
| 634 |
+
<?php
|
| 635 |
+
|
| 636 |
+
//set default variables
|
| 637 |
+
$methods = array();
|
| 638 |
+
$blocked_query = false;
|
| 639 |
+
$blocked_request = false;
|
| 640 |
+
$blocked_referrers = false;
|
| 641 |
+
$blocked_agents = false;
|
| 642 |
+
}
|
| 643 |
+
|
| 644 |
?>
|
| 645 |
<h2><?php _e('Firewall Settings', 'all-in-one-wp-security-and-firewall')?></h2>
|
| 646 |
<div class="aio_blue_box">
|
| 647 |
<?php
|
| 648 |
+
$backup_tab_link = '<a href="admin.php?page='.AIOWPSEC_SETTINGS_MENU_SLUG.'&tab=tab2" target="_blank">'.__('backup', 'all-in-one-wp-security-and-firewall').'</a>';
|
| 649 |
+
$info_msg = '<p>'.sprintf(__('This feature allows you to activate the %s (or legacy %s) firewall security protection rules designed and produced by %s.', 'all-in-one-wp-security-and-firewall'), '<a href="http://perishablepress.com/6g/" target="_blank">6G</a>', '<a href="http://perishablepress.com/5g-blacklist-2013/" target="_blank">5G</a>', '<a href="http://perishablepress.com/" target="_blank">Perishable Press</a>').'</p>';
|
| 650 |
$info_msg .= '<p>'.__('The 6G Blacklist is updated and improved version of 5G Blacklist. If you have 5G Blacklist active, you might consider activating 6G Blacklist instead.', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 651 |
$info_msg .= '<p>'.__('The 6G Blacklist is a simple, flexible blacklist that helps reduce the number of malicious URL requests that hit your website.', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 652 |
$info_msg .= '<p>'.__('The added advantage of applying the 6G firewall to your site is that it has been tested and confirmed by the people at PerishablePress.com to be an optimal and least disruptive set of .htaccess security rules for general WP sites running on an Apache server or similar.', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 670 |
<tr valign="top">
|
| 671 |
<th scope="row"><?php _e('Enable 6G Firewall Protection', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 672 |
<td>
|
| 673 |
+
<input id="aiowps_enable_6g_firewall" name="aiowps_enable_6g_firewall" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_6g_firewall')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 674 |
+
<label for="aiowps_enable_6g_firewall" class="description"><?php _e('Check this if you want to apply the 6G Blacklist firewall protection from perishablepress.com to your site.', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 675 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 676 |
<div class="aiowps_more_info_body">
|
| 677 |
<?php
|
| 688 |
<tr valign="top">
|
| 689 |
<th scope="row"><?php _e('Enable legacy 5G Firewall Protection', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 690 |
<td>
|
| 691 |
+
<input id="aiowps_enable_5g_firewall" name="aiowps_enable_5g_firewall" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_5g_firewall')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 692 |
+
<label for="aiowps_enable_5g_firewall" class="description"><?php _e('Check this if you want to apply the 5G Blacklist firewall protection from perishablepress.com to your site.', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 693 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 694 |
<div class="aiowps_more_info_body">
|
| 695 |
<?php
|
| 707 |
<input type="submit" name="aiowps_apply_5g_6g_firewall_settings" value="<?php _e('Save 5G/6G Firewall Settings', 'all-in-one-wp-security-and-firewall')?>" class="button-primary" />
|
| 708 |
</form>
|
| 709 |
</div></div>
|
| 710 |
+
|
| 711 |
+
<?php /** Block 6G request methods form */?>
|
| 712 |
+
<form action="" method="POST">
|
| 713 |
+
<?php wp_nonce_field('aiowpsec-6g-block-request-methods-nonce'); ?>
|
| 714 |
+
<div class="postbox">
|
| 715 |
+
<h3 class="hndle"><label for="title"><?php _e('6G block request methods', 'all-in-one-wp-security-and-firewall'); ?></label></h3>
|
| 716 |
+
<div class="inside">
|
| 717 |
+
<table class="form-table">
|
| 718 |
+
<?php foreach ($block_request_methods as $block_request_method) {?>
|
| 719 |
+
<tr>
|
| 720 |
+
<th><?php printf(__('Block %s method', 'all-in-one-wp-security-and-firewall'), strtoupper($block_request_method));?>:</th>
|
| 721 |
+
<td>
|
| 722 |
+
<input id="<?php echo esc_attr("aiowps_block_request_method_{$block_request_method}");?>" name="<?php echo esc_attr("aiowps_block_request_method_{$block_request_method}");?>" type="checkbox"<?php checked(in_array(strtoupper($block_request_method), $methods));?>>
|
| 723 |
+
<label for="<?php echo esc_attr("aiowps_block_request_method_{$block_request_method}");?>" class="description"><?php printf(__('Check this to block the %s request method', 'all-in-one-wp-security-and-firewall'), strtoupper($block_request_method));?></label>
|
| 724 |
+
</td>
|
| 725 |
+
</tr>
|
| 726 |
+
<?php } ?>
|
| 727 |
+
</table>
|
| 728 |
+
<input type="submit" name="aiowps_apply_6g_block_request_methods_settings" value="<?php esc_attr_e('Save request methods settings', 'all-in-one-wp-security-and-firewall');?>" class="button-primary"<?php disabled(empty($aiowps_firewall_config)); ?>/>
|
| 729 |
+
</div></div>
|
| 730 |
+
</form>
|
| 731 |
+
|
| 732 |
+
<?php /** Other 6G settings form */?>
|
| 733 |
+
<form action="" method="POST">
|
| 734 |
+
<?php wp_nonce_field('aiowpsec-other-6g-settings-nonce'); ?>
|
| 735 |
+
<div class="postbox">
|
| 736 |
+
<h3 class="hndle"><label for="title"><?php _e('6G other settings', 'all-in-one-wp-security-and-firewall'); ?></label></h3>
|
| 737 |
+
<div class="inside">
|
| 738 |
+
<table class="form-table">
|
| 739 |
+
<tr>
|
| 740 |
+
<th><?php _e('Block query strings', 'all-in-one-wp-security-and-firewall');?>:</th>
|
| 741 |
+
<td>
|
| 742 |
+
<input id="aiowps_block_query" name="aiowps_block_query" type="checkbox"<?php checked($blocked_query);?>>
|
| 743 |
+
<label for="aiowps_block_query" class="description"><?php _e('Check this to block all query strings recommended by 6G', 'all-in-one-wp-security-and-firewall');?></label>
|
| 744 |
+
</td>
|
| 745 |
+
</tr>
|
| 746 |
+
<tr>
|
| 747 |
+
<th><?php _e('Block request strings', 'all-in-one-wp-security-and-firewall');?>:</th>
|
| 748 |
+
<td>
|
| 749 |
+
<input id="aiowps_block_request" name="aiowps_block_request" type="checkbox"<?php checked($blocked_request);?>>
|
| 750 |
+
<label for="aiowps_block_request" class="description"><?php _e('Check this to block all request strings recommended by 6G', 'all-in-one-wp-security-and-firewall');?></label>
|
| 751 |
+
</td>
|
| 752 |
+
</tr>
|
| 753 |
+
<tr>
|
| 754 |
+
<th><?php _e('Block referrers', 'all-in-one-wp-security-and-firewall');?>:</th>
|
| 755 |
+
<td>
|
| 756 |
+
<input id="aiowps_block_refs" name="aiowps_block_refs" type="checkbox"<?php checked($blocked_referrers);?>>
|
| 757 |
+
<label for="aiowps_block_refs" class="description"><?php _e('Check this to block all referrers recommended by 6G', 'all-in-one-wp-security-and-firewall');?></label>
|
| 758 |
+
</td>
|
| 759 |
+
</tr>
|
| 760 |
+
<tr>
|
| 761 |
+
<th><?php _e('Block user-agents', 'all-in-one-wp-security-and-firewall');?>:</th>
|
| 762 |
+
<td>
|
| 763 |
+
<input id="aiowps_block_agents" name="aiowps_block_agents" type="checkbox"<?php checked($blocked_agents);?>>
|
| 764 |
+
<label for="aiowps_block_agents" class="description"><?php _e('Check this to block all user-agents recommended by 6G', 'all-in-one-wp-security-and-firewall');?></label>
|
| 765 |
+
</td>
|
| 766 |
+
</tr>
|
| 767 |
+
</table>
|
| 768 |
+
<input type="submit" name="aiowps_apply_6g_other_settings"<?php disabled(empty($aiowps_firewall_config));?> value="<?php _e('Save other settings', 'all-in-one-wp-security-and-firewall')?>" class="button-primary" />
|
| 769 |
+
</div>
|
| 770 |
+
</div>
|
| 771 |
+
</form>
|
| 772 |
+
|
| 773 |
+
|
| 774 |
<?php
|
| 775 |
}
|
| 776 |
|
| 842 |
<tr valign="top">
|
| 843 |
<th scope="row"><?php _e('Block Fake Googlebots', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 844 |
<td>
|
| 845 |
+
<input id="aiowps_block_fake_googlebots" name="aiowps_block_fake_googlebots" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_block_fake_googlebots')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 846 |
+
<label for="aiowps_block_fake_googlebots" class="description"><?php _e('Check this if you want to block all fake Googlebots.', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 847 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 848 |
<div class="aiowps_more_info_body">
|
| 849 |
<?php
|
| 861 |
<?php
|
| 862 |
}
|
| 863 |
|
| 864 |
+
public function render_tab5() {
|
|
|
|
| 865 |
global $aio_wp_security;
|
| 866 |
global $aiowps_feature_mgr;
|
| 867 |
|
| 916 |
<tr valign="top">
|
| 917 |
<th scope="row"><?php _e('Prevent Image Hotlinking', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 918 |
<td>
|
| 919 |
+
<input id="aiowps_prevent_hotlinking" name="aiowps_prevent_hotlinking" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_prevent_hotlinking')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 920 |
+
<label for="aiowps_prevent_hotlinking" class="description"><?php _e('Check this if you want to prevent hotlinking to images on your site.', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 921 |
</td>
|
| 922 |
</tr>
|
| 923 |
</table>
|
| 1060 |
<tr valign="top">
|
| 1061 |
<th scope="row"><?php _e('Enable 404 IP Detection and Lockout', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 1062 |
<td>
|
| 1063 |
+
<input id="aiowps_enable_404_IP_lockout" name="aiowps_enable_404_IP_lockout" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_404_IP_lockout')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 1064 |
+
<label for="aiowps_enable_404_IP_lockout" class="description"><?php _e('Check this if you want to enable the lockout of selected IP addresses.', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 1065 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 1066 |
<div class="aiowps_more_info_body">
|
| 1067 |
<p class="description">
|
| 1082 |
</tr>
|
| 1083 |
-->
|
| 1084 |
<tr valign="top">
|
| 1085 |
+
<th scope="row"><label for="aiowps_404_lockout_time_length"><?php _e('Time Length of 404 Lockout (min)', 'all-in-one-wp-security-and-firewall')?>:</label></th>
|
| 1086 |
+
<td><input id="aiowps_404_lockout_time_length" type="text" size="5" name="aiowps_404_lockout_time_length" value="<?php echo $aio_wp_security->configs->get_value('aiowps_404_lockout_time_length'); ?>" />
|
| 1087 |
<span class="description"><?php _e('Set the length of time for which a blocked IP address will be prevented from visiting your site', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 1088 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 1089 |
<div class="aiowps_more_info_body">
|
| 1098 |
</td>
|
| 1099 |
</tr>
|
| 1100 |
<tr valign="top">
|
| 1101 |
+
<th scope="row"><label for="aiowps_404_lock_redirect_url"><?php _e('404 Lockout Redirect URL', 'all-in-one-wp-security-and-firewall')?>:</label></th>
|
| 1102 |
+
<td><input id="aiowps_404_lock_redirect_url" type="text" size="50" name="aiowps_404_lock_redirect_url" value="<?php echo esc_url_raw( $aio_wp_security->configs->get_value('aiowps_404_lock_redirect_url'), array( 'http', 'https' ) ); ?>" />
|
| 1103 |
<span class="description"><?php _e('A blocked visitor will be automatically redirected to this URL.', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 1104 |
</td>
|
| 1105 |
</tr>
|
| 1239 |
<tr valign="top">
|
| 1240 |
<th scope="row"><?php _e('Enable Custom .htaccess Rules', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 1241 |
<td>
|
| 1242 |
+
<input id="aiowps_enable_custom_rules" name="aiowps_enable_custom_rules" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_custom_rules')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 1243 |
+
<label for="aiowps_enable_custom_rules" class="description"><?php _e('Check this if you want to enable custom rules entered in the text box below', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 1244 |
</td>
|
| 1245 |
</tr>
|
| 1246 |
<tr valign="top">
|
| 1247 |
<th scope="row"><?php _e('Place custom rules at the top', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 1248 |
<td>
|
| 1249 |
+
<input id="aiowps_place_custom_rules_at_top" name="aiowps_place_custom_rules_at_top" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_place_custom_rules_at_top')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 1250 |
+
<label for="aiowps_place_custom_rules_at_top" class="description"><?php _e('Check this if you want to place your custom rules at the beginning of all the rules applied by this plugin', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 1251 |
</td>
|
| 1252 |
</tr>
|
| 1253 |
<tr valign="top">
|
| 1254 |
+
<th scope="row"><label for="aiowps_custom_rules"><?php _e('Enter Custom .htaccess Rules:', 'all-in-one-wp-security-and-firewall')?></label></th>
|
| 1255 |
<td>
|
| 1256 |
+
<textarea id="aiowps_custom_rules" name="aiowps_custom_rules" rows="35" cols="50"><?php echo htmlspecialchars($aio_wp_security->configs->get_value('aiowps_custom_rules')); ?></textarea>
|
| 1257 |
<br />
|
| 1258 |
<span class="description"><?php _e('Enter your custom .htaccess rules/directives.','all-in-one-wp-security-and-firewall');?></span>
|
| 1259 |
</td>
|
| 1265 |
<?php
|
| 1266 |
}
|
| 1267 |
|
| 1268 |
+
} //end class
|
|
@@ -0,0 +1,590 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
if (!defined('ABSPATH')) {
|
| 3 |
+
exit; //Exit if accessed directly
|
| 4 |
+
}
|
| 5 |
+
|
| 6 |
+
class AIOWPSecurity_Firewall_Setup_Notice {
|
| 7 |
+
|
| 8 |
+
/**
|
| 9 |
+
* Holds reference to an instance of itself
|
| 10 |
+
*
|
| 11 |
+
* @var AIOWPSecurity_Firewall_Setup_Notice
|
| 12 |
+
*/
|
| 13 |
+
private static $instance = null;
|
| 14 |
+
|
| 15 |
+
/**
|
| 16 |
+
* Holds our wp-config file wrapped in our manager class
|
| 17 |
+
*
|
| 18 |
+
* @var AIOWPSecurity_Block_WpConfig
|
| 19 |
+
*/
|
| 20 |
+
private $wpconfig;
|
| 21 |
+
|
| 22 |
+
/**
|
| 23 |
+
* Holds our mu-plugin file wrapped in our manager class
|
| 24 |
+
*
|
| 25 |
+
* @var AIOWPSecurity_Block_Muplugin
|
| 26 |
+
*/
|
| 27 |
+
private $muplugin;
|
| 28 |
+
|
| 29 |
+
/**
|
| 30 |
+
* Holds our bootstrap file wrapped in our manager class
|
| 31 |
+
*
|
| 32 |
+
* @var AIOWPSecurity_Block_Bootstrap
|
| 33 |
+
*/
|
| 34 |
+
private $bootstrap;
|
| 35 |
+
|
| 36 |
+
/**
|
| 37 |
+
* Constants for the different notice types
|
| 38 |
+
*
|
| 39 |
+
* @var string
|
| 40 |
+
*/
|
| 41 |
+
const NOTICE_BOOTSTRAP = 'manual_bootstrap';
|
| 42 |
+
const NOTICE_MANUAL = 'manual';
|
| 43 |
+
const NOTICE_INSTALLED = 'success';
|
| 44 |
+
const NOTICE_DIRECTIVE_SET = 'userini_directive';
|
| 45 |
+
|
| 46 |
+
/**
|
| 47 |
+
* Constructs our object by setting up our essential files
|
| 48 |
+
*/
|
| 49 |
+
private function __construct() {
|
| 50 |
+
$this->bootstrap = AIOWPSecurity_Utility_Firewall::get_bootstrap_file();
|
| 51 |
+
$this->wpconfig = AIOWPSecurity_Utility_Firewall::get_wpconfig_file();
|
| 52 |
+
$this->muplugin = AIOWPSecurity_Utility_Firewall::get_muplugin_file();
|
| 53 |
+
AIOWPSecurity_Utility_Firewall::get_firewall_rules_path(); //creates the needed directories for the first time
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
/**
|
| 57 |
+
* Entry point for the dashboard notice
|
| 58 |
+
*
|
| 59 |
+
* @return void
|
| 60 |
+
*/
|
| 61 |
+
public function start_firewall_setup() {
|
| 62 |
+
|
| 63 |
+
global $aio_wp_security;
|
| 64 |
+
|
| 65 |
+
$firewall_files = array(
|
| 66 |
+
'server' => AIOWPSecurity_Utility_Firewall::get_server_file(),
|
| 67 |
+
'bootstrap' => $this->bootstrap,
|
| 68 |
+
'wpconfig' => $this->wpconfig,
|
| 69 |
+
'muplugin' => $this->muplugin,
|
| 70 |
+
);
|
| 71 |
+
|
| 72 |
+
//Check each file and update the contents if necessary
|
| 73 |
+
foreach ($firewall_files as $name => $file) {
|
| 74 |
+
${'is_firewall_in_'.$name} = false;
|
| 75 |
+
|
| 76 |
+
if (AIOWPSecurity_Utility_Firewall::MANUAL_SETUP === $file) {
|
| 77 |
+
continue;
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
${'is_firewall_in_'.$name} = $file->contains_contents();
|
| 81 |
+
|
| 82 |
+
if (true === ${'is_firewall_in_'.$name}) {
|
| 83 |
+
$file->update_contents();
|
| 84 |
+
}
|
| 85 |
+
}
|
| 86 |
+
|
| 87 |
+
if (!$aio_wp_security->is_aiowps_admin_page()) {
|
| 88 |
+
return;
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
if (true !== $is_firewall_in_server || true !== $is_firewall_in_bootstrap) {
|
| 92 |
+
|
| 93 |
+
if (true !== $is_firewall_in_wpconfig) {
|
| 94 |
+
$this->render_automatic_setup_notice(); //Show notice to setup firewall, if the firewall is not present in our required files
|
| 95 |
+
}elseif (true === $is_firewall_in_wpconfig) {
|
| 96 |
+
$this->render_upgrade_protection_notice();
|
| 97 |
+
}
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
$this->render_notices();
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
/**
|
| 104 |
+
* Will execute when the user presses 'Set up now' button
|
| 105 |
+
*
|
| 106 |
+
* @return void
|
| 107 |
+
*/
|
| 108 |
+
private function do_setup() {
|
| 109 |
+
|
| 110 |
+
$is_inserted_firewall_file = false;
|
| 111 |
+
|
| 112 |
+
$is_inserted_bootstrap_file = $this->bootstrap->contains_contents();
|
| 113 |
+
|
| 114 |
+
if (true !== $is_inserted_bootstrap_file) {
|
| 115 |
+
|
| 116 |
+
$is_inserted_bootstrap_file = $this->bootstrap->insert_contents();
|
| 117 |
+
|
| 118 |
+
if (true !== $is_inserted_bootstrap_file) {
|
| 119 |
+
$this->log_wp_error($is_inserted_bootstrap_file);
|
| 120 |
+
$this->show_notice(self::NOTICE_BOOTSTRAP);
|
| 121 |
+
return;
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
}
|
| 125 |
+
|
| 126 |
+
$firewall_file = AIOWPSecurity_Utility_Firewall::get_server_file();
|
| 127 |
+
|
| 128 |
+
if ($firewall_file instanceof AIOWPSecurity_Block_Userini) {
|
| 129 |
+
|
| 130 |
+
$directive = AIOWPSecurity_Utility_Firewall::get_already_set_directive($firewall_file);
|
| 131 |
+
|
| 132 |
+
if (!empty($directive)) {
|
| 133 |
+
|
| 134 |
+
if (AIOWPSecurity_Utility_Firewall::get_bootstrap_path() === $directive) {
|
| 135 |
+
$is_inserted_firewall_file = true;
|
| 136 |
+
} else {
|
| 137 |
+
$this->show_notice(self::NOTICE_DIRECTIVE_SET, array('directive'=>$directive));
|
| 138 |
+
}
|
| 139 |
+
|
| 140 |
+
} else {
|
| 141 |
+
$is_inserted_firewall_file = $firewall_file->insert_contents();
|
| 142 |
+
}
|
| 143 |
+
|
| 144 |
+
} else {
|
| 145 |
+
|
| 146 |
+
if (AIOWPSecurity_Utility_Firewall::MANUAL_SETUP !== $firewall_file) {
|
| 147 |
+
$is_inserted_firewall_file = $firewall_file->insert_contents(); // attempts to insert firewall into required file
|
| 148 |
+
}
|
| 149 |
+
}
|
| 150 |
+
|
| 151 |
+
$is_inserted_wpconfig = $this->wpconfig->contains_contents();
|
| 152 |
+
if (true !== $is_inserted_wpconfig) {
|
| 153 |
+
$is_inserted_wpconfig = $this->wpconfig->insert_contents();
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
+
if (true === $is_inserted_firewall_file) {
|
| 157 |
+
$this->show_notice(self::NOTICE_INSTALLED);
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
if (true !== $is_inserted_firewall_file) {
|
| 161 |
+
|
| 162 |
+
if (true !== $is_inserted_wpconfig) {
|
| 163 |
+
$is_inserted_muplugin = $this->muplugin->insert_contents();
|
| 164 |
+
$this->log_wp_error($is_inserted_muplugin);
|
| 165 |
+
$this->log_wp_error($is_inserted_wpconfig);
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
$this->log_wp_error($is_inserted_firewall_file);
|
| 169 |
+
$this->show_notice(self::NOTICE_MANUAL);
|
| 170 |
+
|
| 171 |
+
}
|
| 172 |
+
|
| 173 |
+
}
|
| 174 |
+
|
| 175 |
+
/**
|
| 176 |
+
* Handles the form submission for the 'Set up now' notice
|
| 177 |
+
*
|
| 178 |
+
* @return void
|
| 179 |
+
*/
|
| 180 |
+
public function handle_setup_form() {
|
| 181 |
+
if (isset($_POST['_wpnonce']) && wp_verify_nonce($_POST['_wpnonce'], 'aiowpsec-firewall-setup')) {
|
| 182 |
+
$this->do_setup();
|
| 183 |
+
AIOWPSecurity_Utility::redirect_to_url(admin_url('admin.php?page=aiowpsec'));
|
| 184 |
+
}
|
| 185 |
+
}
|
| 186 |
+
|
| 187 |
+
/**
|
| 188 |
+
* Wrapper function to log WP_Errors to debug log
|
| 189 |
+
*
|
| 190 |
+
* @param WP_Error $wp_error - Our error which gets logged
|
| 191 |
+
* @return void
|
| 192 |
+
*/
|
| 193 |
+
private function log_wp_error($wp_error) {
|
| 194 |
+
|
| 195 |
+
if (is_wp_error($wp_error)) {
|
| 196 |
+
global $aio_wp_security;
|
| 197 |
+
|
| 198 |
+
$error_message = $wp_error->get_error_message();
|
| 199 |
+
$error_message .= ' - ';
|
| 200 |
+
$error_message .= $wp_error->get_error_data();
|
| 201 |
+
$aio_wp_security->debug_logger->log_debug($error_message, 4);
|
| 202 |
+
}
|
| 203 |
+
}
|
| 204 |
+
|
| 205 |
+
/**
|
| 206 |
+
* Sets the flags to show notices
|
| 207 |
+
*
|
| 208 |
+
* @param string $type - the type of notice we want to set
|
| 209 |
+
* @param array $values - any values that need to be passed
|
| 210 |
+
* @return void
|
| 211 |
+
*/
|
| 212 |
+
private function show_notice($type, $values = array()) {
|
| 213 |
+
global $aio_wp_security;
|
| 214 |
+
|
| 215 |
+
$aio_wp_security->configs->set_value('firewall_notice_'.$type, true);
|
| 216 |
+
|
| 217 |
+
if (!empty($values)) {
|
| 218 |
+
$aio_wp_security->configs->set_value('firewall_notice_values', $values);
|
| 219 |
+
}
|
| 220 |
+
|
| 221 |
+
$aio_wp_security->configs->save_config();
|
| 222 |
+
}
|
| 223 |
+
|
| 224 |
+
/**
|
| 225 |
+
* Renders any necessary notices
|
| 226 |
+
*
|
| 227 |
+
* @return void
|
| 228 |
+
*/
|
| 229 |
+
private function render_notices() {
|
| 230 |
+
global $aio_wp_security;
|
| 231 |
+
|
| 232 |
+
$notices = array(
|
| 233 |
+
self::NOTICE_BOOTSTRAP,
|
| 234 |
+
self::NOTICE_MANUAL,
|
| 235 |
+
self::NOTICE_INSTALLED,
|
| 236 |
+
self::NOTICE_DIRECTIVE_SET,
|
| 237 |
+
);
|
| 238 |
+
|
| 239 |
+
foreach($notices as $notice) {
|
| 240 |
+
if ($aio_wp_security->configs->get_value('firewall_notice_'.$notice)) {
|
| 241 |
+
|
| 242 |
+
switch($notice) {
|
| 243 |
+
case self::NOTICE_BOOTSTRAP:
|
| 244 |
+
$this->render_bootstrap_notice(); break;
|
| 245 |
+
|
| 246 |
+
case self::NOTICE_MANUAL:
|
| 247 |
+
if (!$this->any_pending_notices(self::NOTICE_MANUAL)) {
|
| 248 |
+
$this->render_manual_setup_notice();
|
| 249 |
+
}
|
| 250 |
+
break;
|
| 251 |
+
case self::NOTICE_INSTALLED:
|
| 252 |
+
$this->render_firewall_installed_notice(); break;
|
| 253 |
+
|
| 254 |
+
case self::NOTICE_DIRECTIVE_SET:
|
| 255 |
+
$values = $aio_wp_security->configs->get_value('firewall_notice_values');
|
| 256 |
+
$this->render_userini_directive_set_notice($values['directive']);
|
| 257 |
+
$aio_wp_security->configs->delete_value('firewall_notice_values');
|
| 258 |
+
break;
|
| 259 |
+
}
|
| 260 |
+
|
| 261 |
+
$aio_wp_security->configs->delete_value('firewall_notice_'.$notice);
|
| 262 |
+
}
|
| 263 |
+
}
|
| 264 |
+
|
| 265 |
+
$aio_wp_security->configs->save_config();
|
| 266 |
+
}
|
| 267 |
+
|
| 268 |
+
/**
|
| 269 |
+
* Detects if we have any notices pending to display
|
| 270 |
+
*
|
| 271 |
+
* @param string $exclude - do not check the status of these notices
|
| 272 |
+
* @return boolean
|
| 273 |
+
*/
|
| 274 |
+
private function any_pending_notices(...$exclude) {
|
| 275 |
+
global $aio_wp_security;
|
| 276 |
+
|
| 277 |
+
$notices = array(
|
| 278 |
+
self::NOTICE_BOOTSTRAP,
|
| 279 |
+
self::NOTICE_MANUAL,
|
| 280 |
+
self::NOTICE_INSTALLED,
|
| 281 |
+
self::NOTICE_DIRECTIVE_SET,
|
| 282 |
+
);
|
| 283 |
+
$notices = array_diff($notices, $exclude);
|
| 284 |
+
|
| 285 |
+
foreach($notices as $notice) {
|
| 286 |
+
if (true === $aio_wp_security->configs->get_value('firewall_notice_'.$notice)) {
|
| 287 |
+
return true;
|
| 288 |
+
}
|
| 289 |
+
}
|
| 290 |
+
|
| 291 |
+
return false;
|
| 292 |
+
}
|
| 293 |
+
|
| 294 |
+
/**
|
| 295 |
+
* Notice is shown if we are unable to write to the bootstrap file
|
| 296 |
+
*
|
| 297 |
+
* @return void
|
| 298 |
+
*/
|
| 299 |
+
private function render_bootstrap_notice() {
|
| 300 |
+
?>
|
| 301 |
+
<div class="notice notice-error is-dismissible">
|
| 302 |
+
<p>
|
| 303 |
+
<strong><?php _e('All In One WP Security and Firewall', 'all-in-one-wp-security-and-firewall'); ?></strong>
|
| 304 |
+
</p>
|
| 305 |
+
<p><?php _e('We were unable to create the file necessary to give you the highest level of protection.', 'all-in-one-wp-security-and-firewall');?></p>
|
| 306 |
+
<p><?php _e('Your firewall will have reduced protection which means some of your firewall\'s functionality will be unavailable.', 'all-in-one-wp-security-and-firewall');?></p>
|
| 307 |
+
<p><?php _e('If you would like to manually set up the necessary file, please follow these steps:', 'all-in-one-wp-security-and-firewall');?></p>
|
| 308 |
+
<p>
|
| 309 |
+
<?php
|
| 310 |
+
/* translators: %s Boostrap file name. */
|
| 311 |
+
printf(__('1. Create a file with the name %s in the same directory as your WordPress install is in, i.e.:', 'all-in-one-wp-security-and-firewall'), pathinfo($this->bootstrap, PATHINFO_BASENAME));
|
| 312 |
+
?>
|
| 313 |
+
</p>
|
| 314 |
+
<pre style='max-width: 100%;background-color: #f0f0f0;border:#ccc solid 1px;padding: 10px;white-space:pre-wrap;'><?php echo esc_html($this->bootstrap); ?></pre>
|
| 315 |
+
<p><?php _e('2. Paste in the following code:', 'all-in-one-wp-security-and-firewall');?></p>
|
| 316 |
+
<pre style='max-width: 100%;background-color: #f0f0f0;border:#ccc solid 1px;padding: 10px;white-space:pre-wrap;'><?php echo htmlentities($this->bootstrap->get_contents()); ?></pre>
|
| 317 |
+
<p><?php _e('3. Save the file and press the \'Try again\' button below:', 'all-in-one-wp-security-and-firewall');?></p>
|
| 318 |
+
<?php
|
| 319 |
+
$this->render_try_again_button();
|
| 320 |
+
$this->render_manual_notice_footer();
|
| 321 |
+
}
|
| 322 |
+
|
| 323 |
+
/**
|
| 324 |
+
* Notice is shown if auto_prepend_file directive is already set in user.ini
|
| 325 |
+
*
|
| 326 |
+
* @param string $directive_value
|
| 327 |
+
* @return void
|
| 328 |
+
*/
|
| 329 |
+
private function render_userini_directive_set_notice($directive_value) {
|
| 330 |
+
|
| 331 |
+
$bootstrap_path = AIOWPSecurity_Utility_Firewall::get_bootstrap_path();
|
| 332 |
+
$firewall_file = AIOWPSecurity_Utility_Firewall::get_server_file();
|
| 333 |
+
|
| 334 |
+
$this->render_manual_notice_header();
|
| 335 |
+
?>
|
| 336 |
+
<p>
|
| 337 |
+
<?php _e('1. Open the following file:', 'all-in-one-wp-security-and-firewall'); ?>
|
| 338 |
+
</p>
|
| 339 |
+
<p><code><?php echo esc_html($firewall_file); ?></code></p>
|
| 340 |
+
|
| 341 |
+
<?php if (empty($directive_value)) {?>
|
| 342 |
+
<p>
|
| 343 |
+
<?php _e('2. Look for the auto_prepend_file directive.', 'all-in-one-wp-security-and-firewall'); ?>
|
| 344 |
+
</p>
|
| 345 |
+
<?php } else {?>
|
| 346 |
+
<p>
|
| 347 |
+
<?php _e('2. Look for the following:', 'all-in-one-wp-security-and-firewall'); ?>
|
| 348 |
+
</p>
|
| 349 |
+
<pre style='max-width: 100%;background-color: #f0f0f0;border:#ccc solid 1px;padding: 10px;white-space:pre-wrap;'><?php echo "auto_prepend_file='".esc_html($directive_value)."'";?></pre>
|
| 350 |
+
<?php } ?>
|
| 351 |
+
|
| 352 |
+
<p>
|
| 353 |
+
<?php _e('3. Change it to the following:', 'all-in-one-wp-security-and-firewall'); ?>
|
| 354 |
+
</p>
|
| 355 |
+
<pre style='max-width: 100%;background-color: #f0f0f0;border:#ccc solid 1px;padding: 10px;white-space:pre-wrap;'><?php echo "auto_prepend_file='".esc_html($bootstrap_path)."'";?></pre>
|
| 356 |
+
<p>
|
| 357 |
+
<?php echo __('4. Save the file and press the \'Try again\' button below:', 'all-in-one-wp-security-and-firewall').' '.__('You may have to wait up to 5 minutes before the settings take effect.', 'all-in-one-wp-security-and-firewall'); ?>
|
| 358 |
+
</p>
|
| 359 |
+
<?php
|
| 360 |
+
$this->render_try_again_button();
|
| 361 |
+
$this->render_manual_notice_footer();
|
| 362 |
+
}
|
| 363 |
+
|
| 364 |
+
/**
|
| 365 |
+
* Shows when the firewall has successfully installed
|
| 366 |
+
*
|
| 367 |
+
* @return void
|
| 368 |
+
*/
|
| 369 |
+
private function render_firewall_installed_notice() {
|
| 370 |
+
?>
|
| 371 |
+
<div class='notice notice-success is-dismissible'>
|
| 372 |
+
<p><strong><?php _e('All In One WP Security and Firewall', 'all-in-one-wp-security-and-firewall'); ?></strong></p>
|
| 373 |
+
<p>
|
| 374 |
+
<?php
|
| 375 |
+
echo __('Your firewall has been installed with the highest level of protection.', 'all-in-one-wp-security-and-firewall').' '.
|
| 376 |
+
__('You may have to wait 5 minutes for the changes to take effect.', 'all-in-one-wp-security-and-firewall');
|
| 377 |
+
?>
|
| 378 |
+
</p>
|
| 379 |
+
</div>
|
| 380 |
+
<?php
|
| 381 |
+
}
|
| 382 |
+
|
| 383 |
+
/**
|
| 384 |
+
* Renders the 'manual setup' dashboard notice
|
| 385 |
+
*
|
| 386 |
+
* @return void
|
| 387 |
+
*/
|
| 388 |
+
private function render_manual_setup_notice() {
|
| 389 |
+
|
| 390 |
+
$firewall_file = AIOWPSecurity_Utility_Firewall::get_server_file();
|
| 391 |
+
|
| 392 |
+
if (AIOWPSecurity_Utility_Firewall::MANUAL_SETUP === $firewall_file) {
|
| 393 |
+
//Show users how to manually add the firewall via php.ini if we can't detect their server
|
| 394 |
+
$bootstrap_path = AIOWPSecurity_Utility_Firewall::get_bootstrap_path();
|
| 395 |
+
|
| 396 |
+
$this->render_manual_notice_header();
|
| 397 |
+
?>
|
| 398 |
+
<p>
|
| 399 |
+
<?php _e('1. Open your php.ini file.', 'all-in-one-wp-security-and-firewall'); ?>
|
| 400 |
+
</p>
|
| 401 |
+
<p>
|
| 402 |
+
<?php _e('2. Set the auto_prepend_file directive like below:', 'all-in-one-wp-security-and-firewall'); ?>
|
| 403 |
+
</p>
|
| 404 |
+
<pre style='max-width: 100%;background-color: #f0f0f0;border:#ccc solid 1px;padding: 10px;white-space:pre-wrap;'><?php echo "auto_prepend_file='".esc_html($bootstrap_path)."'";?></pre>
|
| 405 |
+
<p>
|
| 406 |
+
<?php echo __('3. Restart the webserver and refresh the page', 'all-in-one-wp-security-and-firewall').' '.__('You may have to wait up to 5 minutes before the settings take effect.', 'all-in-one-wp-security-and-firewall'); ?>
|
| 407 |
+
</p>
|
| 408 |
+
<?php
|
| 409 |
+
$this->render_manual_notice_footer();
|
| 410 |
+
} else {
|
| 411 |
+
//Show users how to manually add the firewall via their own server file
|
| 412 |
+
$this->render_manual_notice_header();
|
| 413 |
+
$firewall_file_name = pathinfo($firewall_file, PATHINFO_BASENAME);
|
| 414 |
+
?>
|
| 415 |
+
<p>
|
| 416 |
+
<?php
|
| 417 |
+
/* translators: %s Firewall file name. */
|
| 418 |
+
printf(__('1. Create a file with the name %s in the same directory as your WordPress install is in, i.e.:', 'all-in-one-wp-security-and-firewall'), $firewall_file_name);
|
| 419 |
+
?>
|
| 420 |
+
<p><code><?php echo esc_html($firewall_file); ?></code></p>
|
| 421 |
+
</p>
|
| 422 |
+
<p>
|
| 423 |
+
<?php _e('2. Paste in the following directives:', 'all-in-one-wp-security-and-firewall'); ?>
|
| 424 |
+
</p>
|
| 425 |
+
<pre style='max-width: 100%;background-color: #f0f0f0;border:#ccc solid 1px;padding: 10px;white-space:pre-wrap;'><?php echo htmlentities($firewall_file->get_contents()); ?></pre>
|
| 426 |
+
<p>
|
| 427 |
+
<?php echo __('3. Save the file and press the \'Try again\' button below:', 'all-in-one-wp-security-and-firewall'); ?>
|
| 428 |
+
</p>
|
| 429 |
+
<?php
|
| 430 |
+
$this->render_try_again_button();
|
| 431 |
+
$this->render_manual_notice_footer();
|
| 432 |
+
}
|
| 433 |
+
}
|
| 434 |
+
|
| 435 |
+
/**
|
| 436 |
+
* The header for notices that require manual intervention
|
| 437 |
+
*
|
| 438 |
+
* @return void
|
| 439 |
+
*/
|
| 440 |
+
private function render_manual_notice_header() {
|
| 441 |
+
?>
|
| 442 |
+
<div class="notice notice-warning is-dismissible">
|
| 443 |
+
<p>
|
| 444 |
+
<strong><?php _e('All In One WP Security and Firewall', 'all-in-one-wp-security-and-firewall'); ?></strong>
|
| 445 |
+
</p>
|
| 446 |
+
<p>
|
| 447 |
+
<?php echo __('We were unable to set up your firewall with the highest level of protection.', 'all-in-one-wp-security-and-firewall').' '.
|
| 448 |
+
__('Your firewall will have reduced functionality.', 'all-in-one-wp-security-and-firewall');
|
| 449 |
+
?>
|
| 450 |
+
</p>
|
| 451 |
+
<p>
|
| 452 |
+
<?php _e('To give your site the highest level of protection, please follow these steps:', 'all-in-one-wp-security-and-firewall'); ?>
|
| 453 |
+
</p>
|
| 454 |
+
<?php
|
| 455 |
+
}
|
| 456 |
+
|
| 457 |
+
/**
|
| 458 |
+
* The footer for notices that require manual intervention
|
| 459 |
+
*
|
| 460 |
+
* @return void
|
| 461 |
+
*/
|
| 462 |
+
private function render_manual_notice_footer() {
|
| 463 |
+
?>
|
| 464 |
+
<p>
|
| 465 |
+
<strong><?php _e('Note: if you\'re unable to perform any of the aforementioned steps, please ask your web hosting provider for further assistance.', 'all-in-one-wp-security-and-firewall'); ?></strong>
|
| 466 |
+
</p>
|
| 467 |
+
</div>
|
| 468 |
+
<?php
|
| 469 |
+
}
|
| 470 |
+
|
| 471 |
+
/**
|
| 472 |
+
* Render Try again button.
|
| 473 |
+
*
|
| 474 |
+
* @return void
|
| 475 |
+
*/
|
| 476 |
+
private function render_try_again_button() {
|
| 477 |
+
?>
|
| 478 |
+
<form action="<?php echo esc_url(admin_url('admin-post.php')); ?>" method="POST">
|
| 479 |
+
<?php wp_nonce_field('aiowpsec-firewall-setup'); ?>
|
| 480 |
+
<input type="hidden" name="action" value="aiowps_firewall_setup">
|
| 481 |
+
<div style="padding-top: 10px; padding-bottom: 10px;">
|
| 482 |
+
<input class="button button-primary" type="submit" name="btn_try_again" value="<?php _e('Try again', 'all-in-one-wp-security-and-firewall'); ?>">
|
| 483 |
+
</div>
|
| 484 |
+
</form>
|
| 485 |
+
<?php
|
| 486 |
+
}
|
| 487 |
+
|
| 488 |
+
/**
|
| 489 |
+
* Renders the warning that users do not have the highest level of protection
|
| 490 |
+
*
|
| 491 |
+
* @return void
|
| 492 |
+
*/
|
| 493 |
+
private function render_upgrade_protection_notice() {
|
| 494 |
+
|
| 495 |
+
if ($this->should_not_show_notice()) {
|
| 496 |
+
return;
|
| 497 |
+
}
|
| 498 |
+
?>
|
| 499 |
+
<div class="notice notice-warning">
|
| 500 |
+
<form action="<?php echo esc_url(admin_url('admin-post.php')); ?>" method="POST">
|
| 501 |
+
<?php wp_nonce_field('aiowpsec-firewall-setup'); ?>
|
| 502 |
+
<input type="hidden" name="action" value="aiowps_firewall_setup">
|
| 503 |
+
<p>
|
| 504 |
+
<?php _e('We have detected that your AIOWPS firewall is not fully installed, and therefore does not have the highest level of protection. ', 'all-in-one-wp-security-and-firewall');?>
|
| 505 |
+
<?php _e('Your firewall will have reduced functionality until it has been upgraded. ', 'all-in-one-wp-security-and-firewall');?>
|
| 506 |
+
<div style="padding-top: 10px;">
|
| 507 |
+
<input class="button button-primary" type="submit" name="btn_upgrade_now" value="<?php _e('Upgrade your protection now', 'all-in-one-wp-security-and-firewall'); ?>">
|
| 508 |
+
</div>
|
| 509 |
+
</p>
|
| 510 |
+
</form>
|
| 511 |
+
|
| 512 |
+
</div>
|
| 513 |
+
|
| 514 |
+
<?php
|
| 515 |
+
}
|
| 516 |
+
|
| 517 |
+
/**
|
| 518 |
+
* Whether the firewall notice should not be shown.
|
| 519 |
+
*
|
| 520 |
+
* return boolean True if the firewall notice should not be shown otherwise false.
|
| 521 |
+
*/
|
| 522 |
+
private function should_not_show_notice() {
|
| 523 |
+
if (!is_main_site()) {
|
| 524 |
+
return true;
|
| 525 |
+
}
|
| 526 |
+
|
| 527 |
+
if (!current_user_can(AIOWPSEC_MANAGEMENT_PERMISSION)) {
|
| 528 |
+
return true;
|
| 529 |
+
}
|
| 530 |
+
|
| 531 |
+
if ($this->any_pending_notices()) {
|
| 532 |
+
return true; //only display if there are no other notices waiting to be displayed
|
| 533 |
+
}
|
| 534 |
+
|
| 535 |
+
return false;
|
| 536 |
+
}
|
| 537 |
+
|
| 538 |
+
/**
|
| 539 |
+
* Renders the 'Set up now' dashboard notice
|
| 540 |
+
*
|
| 541 |
+
* @return void
|
| 542 |
+
*/
|
| 543 |
+
private function render_automatic_setup_notice() {
|
| 544 |
+
|
| 545 |
+
if ($this->should_not_show_notice()) {
|
| 546 |
+
return;
|
| 547 |
+
}
|
| 548 |
+
?>
|
| 549 |
+
<div class="notice notice-information">
|
| 550 |
+
|
| 551 |
+
<form action="<?php echo esc_url(admin_url('admin-post.php')); ?>" method="POST">
|
| 552 |
+
<?php wp_nonce_field('aiowpsec-firewall-setup'); ?>
|
| 553 |
+
<input type="hidden" name="action" value="aiowps_firewall_setup">
|
| 554 |
+
<p>
|
| 555 |
+
<strong><?php _e('All In One WP Security and Firewall', 'all-in-one-wp-security-and-firewall'); ?></strong>
|
| 556 |
+
</p>
|
| 557 |
+
<p>
|
| 558 |
+
<?php echo __('Our PHP-based firewall has been created to give you even greater protection.', 'all-in-one-wp-security-and-firewall').' '.
|
| 559 |
+
__('To ensure the PHP-based firewall runs before any potentially vulnerable code in your WordPress site can be reached, it will need to be set up.');?>
|
| 560 |
+
</p>
|
| 561 |
+
<p>
|
| 562 |
+
<?php _e('If you already have our .htaccess-based firewall enabled, you will still need to set up the PHP-based firewall to benefit from its protection.', 'all-in-one-wp-security-and-firewall'); ?>
|
| 563 |
+
</p>
|
| 564 |
+
<p>
|
| 565 |
+
<?php _e('To set up the PHP-based firewall, press the \'Set up now\' button below:', 'all-in-one-wp-security-and-firewall'); ?>
|
| 566 |
+
</p>
|
| 567 |
+
<div style="padding-top: 10px; padding-bottom: 10px;">
|
| 568 |
+
<input class="button button-primary" type="submit" name="btn_setup_now" value="<?php _e('Set up now', 'all-in-one-wp-security-and-firewall'); ?>">
|
| 569 |
+
</div>
|
| 570 |
+
</form>
|
| 571 |
+
|
| 572 |
+
</div>
|
| 573 |
+
|
| 574 |
+
<?php
|
| 575 |
+
}
|
| 576 |
+
|
| 577 |
+
/**
|
| 578 |
+
* Ensures only one instance of the class can be created (singleton)
|
| 579 |
+
*
|
| 580 |
+
* @return void
|
| 581 |
+
*/
|
| 582 |
+
public static function get_instance() {
|
| 583 |
+
|
| 584 |
+
if (null === self::$instance) {
|
| 585 |
+
self::$instance = new self();
|
| 586 |
+
}
|
| 587 |
+
|
| 588 |
+
return self::$instance;
|
| 589 |
+
}
|
| 590 |
+
}
|
|
@@ -5,7 +5,7 @@ if(!defined('ABSPATH')){
|
|
| 5 |
|
| 6 |
class AIOWPSecurity_List_404 extends AIOWPSecurity_List_Table {
|
| 7 |
|
| 8 |
-
function __construct() {
|
| 9 |
global $status, $page;
|
| 10 |
|
| 11 |
//Set parent defaults
|
|
@@ -16,15 +16,22 @@ class AIOWPSecurity_List_404 extends AIOWPSecurity_List_Table {
|
|
| 16 |
));
|
| 17 |
}
|
| 18 |
|
| 19 |
-
function column_default($item, $column_name) {
|
| 20 |
return $item[$column_name];
|
| 21 |
}
|
| 22 |
|
| 23 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
$tab = strip_tags($_REQUEST['tab']);
|
| 25 |
$ip = $item['ip_or_host'];
|
| 26 |
|
| 27 |
-
|
| 28 |
//Check if this IP address is locked
|
| 29 |
$is_locked = AIOWPSecurity_Utility::check_locked_ip($ip);
|
| 30 |
$delete_url = sprintf('admin.php?page=%s&tab=%s&action=%s&id=%s', AIOWPSEC_FIREWALL_MENU_SLUG, $tab, 'delete_event_log', $item['id']);
|
|
@@ -52,7 +59,7 @@ class AIOWPSecurity_List_404 extends AIOWPSecurity_List_Table {
|
|
| 52 |
);
|
| 53 |
}
|
| 54 |
|
| 55 |
-
function column_status($item) {
|
| 56 |
global $aio_wp_security;
|
| 57 |
$ip = $item['ip_or_host'];
|
| 58 |
//Check if this IP address is locked
|
|
@@ -69,7 +76,7 @@ class AIOWPSecurity_List_404 extends AIOWPSecurity_List_Table {
|
|
| 69 |
}
|
| 70 |
}
|
| 71 |
|
| 72 |
-
function column_cb($item) {
|
| 73 |
return sprintf(
|
| 74 |
'<input type="checkbox" name="%1$s[]" value="%2$s" />',
|
| 75 |
/* $1%s */ $this->_args['singular'], //Let's simply repurpose the table's singular label
|
|
@@ -77,7 +84,7 @@ class AIOWPSecurity_List_404 extends AIOWPSecurity_List_Table {
|
|
| 77 |
);
|
| 78 |
}
|
| 79 |
|
| 80 |
-
function get_columns() {
|
| 81 |
$columns = array(
|
| 82 |
'cb' => '<input type="checkbox" />', //Render a checkbox
|
| 83 |
'id' => 'ID',
|
|
@@ -92,7 +99,7 @@ class AIOWPSecurity_List_404 extends AIOWPSecurity_List_Table {
|
|
| 92 |
return $columns;
|
| 93 |
}
|
| 94 |
|
| 95 |
-
function get_sortable_columns() {
|
| 96 |
$sortable_columns = array(
|
| 97 |
'id' => array('id', false),
|
| 98 |
'event_type' => array('event_type', false),
|
|
@@ -105,7 +112,7 @@ class AIOWPSecurity_List_404 extends AIOWPSecurity_List_Table {
|
|
| 105 |
return $sortable_columns;
|
| 106 |
}
|
| 107 |
|
| 108 |
-
function get_bulk_actions() {
|
| 109 |
$actions = array(
|
| 110 |
//'unlock' => 'Unlock',
|
| 111 |
'bulk_block_ip' => 'Temp Block IP',
|
|
@@ -115,7 +122,7 @@ class AIOWPSecurity_List_404 extends AIOWPSecurity_List_Table {
|
|
| 115 |
return $actions;
|
| 116 |
}
|
| 117 |
|
| 118 |
-
function process_bulk_action() {
|
| 119 |
if ('bulk_block_ip' === $this->current_action()) {//Process delete bulk actions
|
| 120 |
if (!isset($_REQUEST['item'])) {
|
| 121 |
AIOWPSecurity_Admin_Menu::show_msg_error_st(__('Please select some records using the checkboxes', 'all-in-one-wp-security-and-firewall'));
|
|
@@ -140,13 +147,16 @@ class AIOWPSecurity_List_404 extends AIOWPSecurity_List_Table {
|
|
| 140 |
}
|
| 141 |
}
|
| 142 |
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 148 |
global $wpdb;
|
| 149 |
-
$events_table = AIOWPSEC_TBL_LOGIN_LOCKDOWN;
|
| 150 |
if (is_array($entries)) {
|
| 151 |
//lock multiple records
|
| 152 |
$entries = array_filter($entries, 'is_numeric'); //discard non-numeric ID values
|
|
@@ -176,11 +186,14 @@ class AIOWPSecurity_List_404 extends AIOWPSecurity_List_Table {
|
|
| 176 |
}
|
| 177 |
}
|
| 178 |
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
|
|
|
|
|
|
|
|
|
| 184 |
global $wpdb, $aio_wp_security;
|
| 185 |
$bl_ip_addresses = $aio_wp_security->configs->get_value('aiowps_banned_ip_addresses'); //get the currently saved blacklisted IPs
|
| 186 |
$ip_list_array = AIOWPSecurity_Utility_IP::create_ip_list_array_from_string_with_newline($bl_ip_addresses);
|
|
@@ -229,12 +242,14 @@ class AIOWPSecurity_List_404 extends AIOWPSecurity_List_Table {
|
|
| 229 |
}
|
| 230 |
}
|
| 231 |
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
|
|
|
|
|
|
| 238 |
global $wpdb, $aio_wp_security;
|
| 239 |
$events_table = AIOWPSEC_TBL_EVENTS;
|
| 240 |
if (is_array($entries)) {
|
|
@@ -246,9 +261,13 @@ class AIOWPSecurity_List_404 extends AIOWPSecurity_List_Table {
|
|
| 246 |
$id_list = "(" . implode(",", $entries) . ")"; //Create comma separate list for DB operation
|
| 247 |
$delete_command = "DELETE FROM " . $events_table . " WHERE id IN " . $id_list;
|
| 248 |
$result = $wpdb->query($delete_command);
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 252 |
}
|
| 253 |
|
| 254 |
} elseif ($entries != NULL) {
|
|
@@ -263,62 +282,75 @@ class AIOWPSecurity_List_404 extends AIOWPSecurity_List_Table {
|
|
| 263 |
$delete_command = "DELETE FROM " . $events_table . " WHERE id = '" . absint($entries) . "'";
|
| 264 |
//$delete_command = $wpdb->prepare("DELETE FROM $events_table WHERE id = %s", absint($entries));
|
| 265 |
$result = $wpdb->query($delete_command);
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 269 |
}
|
| 270 |
}
|
| 271 |
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 280 |
|
| 281 |
-
|
| 282 |
|
| 283 |
-
|
| 284 |
|
| 285 |
-
|
| 286 |
-
|
| 287 |
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
$orderby = AIOWPSecurity_Utility::sanitize_value_by_array($orderby, $sortable);
|
| 297 |
-
$order = AIOWPSecurity_Utility::sanitize_value_by_array($order, array('DESC' => '1', 'ASC' => '1'));
|
| 298 |
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
//lets insert an empty "status" column - we will use later
|
| 308 |
-
$row['status'] = '';
|
| 309 |
-
$new_data[] = $row;
|
| 310 |
-
}
|
| 311 |
-
if (!$ignore_pagination) {
|
| 312 |
-
$current_page = $this->get_pagenum();
|
| 313 |
-
$total_items = count($new_data);
|
| 314 |
-
$new_data = array_slice($new_data, (($current_page - 1) * $per_page), $per_page);
|
| 315 |
-
$this->set_pagination_args(array(
|
| 316 |
-
'total_items' => $total_items, //WE have to calculate the total number of items
|
| 317 |
-
'per_page' => $per_page, //WE have to determine how many items to show on a page
|
| 318 |
-
'total_pages' => ceil($total_items / $per_page) //WE have to calculate the total number of pages
|
| 319 |
-
));
|
| 320 |
-
}
|
| 321 |
-
$this->items = $new_data;
|
| 322 |
-
}
|
| 323 |
|
| 324 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
|
| 6 |
class AIOWPSecurity_List_404 extends AIOWPSecurity_List_Table {
|
| 7 |
|
| 8 |
+
public function __construct() {
|
| 9 |
global $status, $page;
|
| 10 |
|
| 11 |
//Set parent defaults
|
| 16 |
));
|
| 17 |
}
|
| 18 |
|
| 19 |
+
public function column_default($item, $column_name) {
|
| 20 |
return $item[$column_name];
|
| 21 |
}
|
| 22 |
|
| 23 |
+
/**
|
| 24 |
+
* Returns id column html to be rendered.
|
| 25 |
+
*
|
| 26 |
+
* @param Array - data for the columns on the current row
|
| 27 |
+
*
|
| 28 |
+
* @return String
|
| 29 |
+
*/
|
| 30 |
+
public function column_id($item) {
|
| 31 |
$tab = strip_tags($_REQUEST['tab']);
|
| 32 |
$ip = $item['ip_or_host'];
|
| 33 |
|
| 34 |
+
$blocked_ips_tab = 'tab2';
|
| 35 |
//Check if this IP address is locked
|
| 36 |
$is_locked = AIOWPSecurity_Utility::check_locked_ip($ip);
|
| 37 |
$delete_url = sprintf('admin.php?page=%s&tab=%s&action=%s&id=%s', AIOWPSEC_FIREWALL_MENU_SLUG, $tab, 'delete_event_log', $item['id']);
|
| 59 |
);
|
| 60 |
}
|
| 61 |
|
| 62 |
+
public function column_status($item) {
|
| 63 |
global $aio_wp_security;
|
| 64 |
$ip = $item['ip_or_host'];
|
| 65 |
//Check if this IP address is locked
|
| 76 |
}
|
| 77 |
}
|
| 78 |
|
| 79 |
+
public function column_cb($item) {
|
| 80 |
return sprintf(
|
| 81 |
'<input type="checkbox" name="%1$s[]" value="%2$s" />',
|
| 82 |
/* $1%s */ $this->_args['singular'], //Let's simply repurpose the table's singular label
|
| 84 |
);
|
| 85 |
}
|
| 86 |
|
| 87 |
+
public function get_columns() {
|
| 88 |
$columns = array(
|
| 89 |
'cb' => '<input type="checkbox" />', //Render a checkbox
|
| 90 |
'id' => 'ID',
|
| 99 |
return $columns;
|
| 100 |
}
|
| 101 |
|
| 102 |
+
public function get_sortable_columns() {
|
| 103 |
$sortable_columns = array(
|
| 104 |
'id' => array('id', false),
|
| 105 |
'event_type' => array('event_type', false),
|
| 112 |
return $sortable_columns;
|
| 113 |
}
|
| 114 |
|
| 115 |
+
public function get_bulk_actions() {
|
| 116 |
$actions = array(
|
| 117 |
//'unlock' => 'Unlock',
|
| 118 |
'bulk_block_ip' => 'Temp Block IP',
|
| 122 |
return $actions;
|
| 123 |
}
|
| 124 |
|
| 125 |
+
public function process_bulk_action() {
|
| 126 |
if ('bulk_block_ip' === $this->current_action()) {//Process delete bulk actions
|
| 127 |
if (!isset($_REQUEST['item'])) {
|
| 128 |
AIOWPSecurity_Admin_Menu::show_msg_error_st(__('Please select some records using the checkboxes', 'all-in-one-wp-security-and-firewall'));
|
| 147 |
}
|
| 148 |
}
|
| 149 |
|
| 150 |
+
/**
|
| 151 |
+
* Locks an IP address by adding it to the AIOWPSEC_TBL_LOGIN_LOCKDOWN table.
|
| 152 |
+
*
|
| 153 |
+
* @param Array|String - ids that correspond to ip addresses in the AIOWPSEC_TBL_EVENTS table or a single ip address
|
| 154 |
+
* @param String - (optional)username of user being locked
|
| 155 |
+
*
|
| 156 |
+
* @return Boolean|Void
|
| 157 |
+
*/
|
| 158 |
+
public function block_ip($entries, $username = '') {
|
| 159 |
global $wpdb;
|
|
|
|
| 160 |
if (is_array($entries)) {
|
| 161 |
//lock multiple records
|
| 162 |
$entries = array_filter($entries, 'is_numeric'); //discard non-numeric ID values
|
| 186 |
}
|
| 187 |
}
|
| 188 |
|
| 189 |
+
/**
|
| 190 |
+
* Permanently blocks an IP address by adding it to the blacklist and writing rules to the htaccess file.
|
| 191 |
+
*
|
| 192 |
+
* @param Array|String - ids that correspond to ip addresses in the AIOWPSEC_TBL_EVENTS table or a single ip address
|
| 193 |
+
*
|
| 194 |
+
* @return Boolean|Void
|
| 195 |
+
*/
|
| 196 |
+
public function blacklist_ip_address($entries) {
|
| 197 |
global $wpdb, $aio_wp_security;
|
| 198 |
$bl_ip_addresses = $aio_wp_security->configs->get_value('aiowps_banned_ip_addresses'); //get the currently saved blacklisted IPs
|
| 199 |
$ip_list_array = AIOWPSecurity_Utility_IP::create_ip_list_array_from_string_with_newline($bl_ip_addresses);
|
| 242 |
}
|
| 243 |
}
|
| 244 |
|
| 245 |
+
/**
|
| 246 |
+
* Deletes one or more records from the AIOWPSEC_TBL_EVENTS table.
|
| 247 |
+
*
|
| 248 |
+
* @param Array|String|Integer - ids or a single id
|
| 249 |
+
*
|
| 250 |
+
* @return Void
|
| 251 |
+
*/
|
| 252 |
+
public function delete_404_event_records($entries) {
|
| 253 |
global $wpdb, $aio_wp_security;
|
| 254 |
$events_table = AIOWPSEC_TBL_EVENTS;
|
| 255 |
if (is_array($entries)) {
|
| 261 |
$id_list = "(" . implode(",", $entries) . ")"; //Create comma separate list for DB operation
|
| 262 |
$delete_command = "DELETE FROM " . $events_table . " WHERE id IN " . $id_list;
|
| 263 |
$result = $wpdb->query($delete_command);
|
| 264 |
+
if ($result) {
|
| 265 |
+
AIOWPSecurity_Admin_Menu::show_msg_record_deleted_st();
|
| 266 |
+
} else {
|
| 267 |
+
// Error on bulk delete
|
| 268 |
+
$aio_wp_security->debug_logger->log_debug('Database error occurred when deleting rows from Events table. Database error: '.$wpdb->last_error, 4);
|
| 269 |
+
AIOWPSecurity_Admin_Menu::show_msg_record_not_deleted_st();
|
| 270 |
+
}
|
| 271 |
}
|
| 272 |
|
| 273 |
} elseif ($entries != NULL) {
|
| 282 |
$delete_command = "DELETE FROM " . $events_table . " WHERE id = '" . absint($entries) . "'";
|
| 283 |
//$delete_command = $wpdb->prepare("DELETE FROM $events_table WHERE id = %s", absint($entries));
|
| 284 |
$result = $wpdb->query($delete_command);
|
| 285 |
+
if ($result) {
|
| 286 |
+
AIOWPSecurity_Admin_Menu::show_msg_record_deleted_st();
|
| 287 |
+
} elseif ($result === false) {
|
| 288 |
+
// Error on single delete
|
| 289 |
+
$aio_wp_security->debug_logger->log_debug('Database error occurred when deleting rows from Events table. Database error: '.$wpdb->last_error, 4);
|
| 290 |
+
AIOWPSecurity_Admin_Menu::show_msg_record_not_deleted_st();
|
| 291 |
+
}
|
| 292 |
}
|
| 293 |
}
|
| 294 |
|
| 295 |
+
/**
|
| 296 |
+
* Retrieves all items from AIOWPSEC_TBL_EVENTS according to a search term inside $_REQUEST['s'] and only '404' events if there is no search term. It then assigns to $this->items.
|
| 297 |
+
*
|
| 298 |
+
* @param Boolean $ignore_pagination - whether to not paginate
|
| 299 |
+
*
|
| 300 |
+
* @return Void
|
| 301 |
+
*/
|
| 302 |
+
public function prepare_items($ignore_pagination = false) {
|
| 303 |
+
/**
|
| 304 |
+
* First, lets decide how many records per page to show
|
| 305 |
+
*/
|
| 306 |
+
$per_page = 100;
|
| 307 |
+
$columns = $this->get_columns();
|
| 308 |
+
$hidden = array();
|
| 309 |
+
$sortable = $this->get_sortable_columns();
|
| 310 |
+
$search_term = isset($_REQUEST['s']) ? sanitize_text_field(stripslashes($_REQUEST['s'])) : '';
|
| 311 |
|
| 312 |
+
$this->_column_headers = array($columns, $hidden, $sortable);
|
| 313 |
|
| 314 |
+
$this->process_bulk_action();
|
| 315 |
|
| 316 |
+
global $wpdb;
|
| 317 |
+
$events_table_name = AIOWPSEC_TBL_EVENTS;
|
| 318 |
|
| 319 |
+
/* -- Ordering parameters -- */
|
| 320 |
+
//Parameters that are going to be used to order the result
|
| 321 |
+
isset($_GET['orderby']) ? $orderby = strip_tags($_GET['orderby']): $orderby = '';
|
| 322 |
+
isset($_GET['order']) ? $order = strip_tags($_GET['order']): $order = '';
|
| 323 |
|
| 324 |
+
$orderby = !empty($orderby) ? esc_sql($orderby) : 'id';
|
| 325 |
+
$order = !empty($order) ? esc_sql($order) : 'DESC';
|
|
|
|
|
|
|
|
|
|
| 326 |
|
| 327 |
+
$orderby = AIOWPSecurity_Utility::sanitize_value_by_array($orderby, $sortable);
|
| 328 |
+
$order = AIOWPSecurity_Utility::sanitize_value_by_array($order, array('DESC' => '1', 'ASC' => '1'));
|
| 329 |
+
|
| 330 |
+
if (empty($search_term)) {
|
| 331 |
+
$data = $wpdb->get_results("SELECT * FROM $events_table_name WHERE `event_type` = '404' ORDER BY $orderby $order", ARRAY_A);
|
| 332 |
+
} else {
|
| 333 |
+
$data = $wpdb->get_results($wpdb->prepare("SELECT * FROM $events_table_name WHERE `ip_or_host` LIKE '%%%s%%' OR `url` LIKE '%%%s%%' OR `referer_info` LIKE '%%%s%%' ORDER BY $orderby $order", $search_term, $search_term, $search_term), ARRAY_A);
|
| 334 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 335 |
|
| 336 |
+
if (!$ignore_pagination) {
|
| 337 |
+
$current_page = $this->get_pagenum();
|
| 338 |
+
$total_items = count($data);
|
| 339 |
+
$data = array_slice($data, (($current_page - 1) * $per_page), $per_page);
|
| 340 |
+
$this->set_pagination_args(array(
|
| 341 |
+
'total_items' => $total_items, //WE have to calculate the total number of items
|
| 342 |
+
'per_page' => $per_page, //WE have to determine how many items to show on a page
|
| 343 |
+
'total_pages' => ceil($total_items / $per_page) //WE have to calculate the total number of pages
|
| 344 |
+
));
|
| 345 |
+
}
|
| 346 |
+
|
| 347 |
+
foreach ($data as $index => $row) {
|
| 348 |
+
// Insert an empty status column - we will use later
|
| 349 |
+
$data[$index]['status'] = '';
|
| 350 |
+
$data[$index]['event_date'] = get_date_from_gmt(mysql2date('Y-m-d H:i:s', $row['event_date']), $this->get_wp_date_time_format());
|
| 351 |
+
}
|
| 352 |
+
|
| 353 |
+
$this->items = $data;
|
| 354 |
+
}
|
| 355 |
+
|
| 356 |
+
}
|
|
@@ -5,7 +5,10 @@ if(!defined('ABSPATH')){
|
|
| 5 |
|
| 6 |
class AIOWPSecurity_List_Account_Activity extends AIOWPSecurity_List_Table {
|
| 7 |
|
| 8 |
-
|
|
|
|
|
|
|
|
|
|
| 9 |
global $status, $page;
|
| 10 |
|
| 11 |
//Set parent defaults
|
|
@@ -17,12 +20,12 @@ class AIOWPSecurity_List_Account_Activity extends AIOWPSecurity_List_Table {
|
|
| 17 |
|
| 18 |
}
|
| 19 |
|
| 20 |
-
function column_default($item, $column_name){
|
| 21 |
return $item[$column_name];
|
| 22 |
}
|
| 23 |
|
| 24 |
-
function column_user_id($item){
|
| 25 |
-
$tab = strip_tags($_REQUEST['tab']);
|
| 26 |
$delete_url = sprintf('admin.php?page=%s&tab=%s&action=%s&activity_login_rec=%s', AIOWPSEC_USER_LOGIN_MENU_SLUG, $tab, 'delete_acct_activity_rec', $item['id']);
|
| 27 |
//Add nonce to delete URL
|
| 28 |
$delete_url_nonce = wp_nonce_url($delete_url, "delete_acct_activity_log", "aiowps_nonce");
|
|
@@ -43,7 +46,7 @@ class AIOWPSecurity_List_Account_Activity extends AIOWPSecurity_List_Table {
|
|
| 43 |
return '1000-10-10 10:00:00' == $item['logout_date'] ? __('Login session still active', 'all-in-one-wp-security-and-firewall') : $item['logout_date'];
|
| 44 |
}
|
| 45 |
|
| 46 |
-
function column_cb($item){
|
| 47 |
return sprintf(
|
| 48 |
'<input type="checkbox" name="%1$s[]" value="%2$s" />',
|
| 49 |
/*$1%s*/ $this->_args['singular'], //Let's simply repurpose the table's singular label
|
|
@@ -51,7 +54,7 @@ class AIOWPSecurity_List_Account_Activity extends AIOWPSecurity_List_Table {
|
|
| 51 |
);
|
| 52 |
}
|
| 53 |
|
| 54 |
-
function get_columns(){
|
| 55 |
$columns = array(
|
| 56 |
'cb' => '<input type="checkbox" />', //Render a checkbox
|
| 57 |
'user_id' => __('User ID', 'all-in-one-wp-security-and-firewall'),
|
|
@@ -63,7 +66,7 @@ class AIOWPSecurity_List_Account_Activity extends AIOWPSecurity_List_Table {
|
|
| 63 |
return $columns;
|
| 64 |
}
|
| 65 |
|
| 66 |
-
function get_sortable_columns() {
|
| 67 |
$sortable_columns = array(
|
| 68 |
'user_id' => array('user_id',false),
|
| 69 |
'user_login' => array('user_login',false),
|
|
@@ -74,36 +77,36 @@ class AIOWPSecurity_List_Account_Activity extends AIOWPSecurity_List_Table {
|
|
| 74 |
return $sortable_columns;
|
| 75 |
}
|
| 76 |
|
| 77 |
-
function get_bulk_actions() {
|
| 78 |
$actions = array(
|
| 79 |
'delete' => 'Delete'
|
| 80 |
);
|
| 81 |
return $actions;
|
| 82 |
}
|
| 83 |
|
| 84 |
-
function process_bulk_action() {
|
| 85 |
-
if('delete'===$this->current_action())
|
| 86 |
{//Process delete bulk actions
|
| 87 |
-
if(!isset($_REQUEST['item']))
|
| 88 |
-
{
|
| 89 |
$error_msg = '<div id="message" class="error"><p><strong>';
|
| 90 |
$error_msg .= __('Please select some records using the checkboxes','all-in-one-wp-security-and-firewall');
|
| 91 |
$error_msg .= '</strong></p></div>';
|
| 92 |
-
|
| 93 |
} else{
|
| 94 |
-
|
|
|
|
| 95 |
}
|
| 96 |
}
|
| 97 |
}
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
global $wpdb, $aio_wp_security;
|
| 108 |
$login_activity_table = AIOWPSEC_TBL_USER_LOGIN_ACTIVITY;
|
| 109 |
if (is_array($entries))
|
|
@@ -111,28 +114,22 @@ class AIOWPSecurity_List_Account_Activity extends AIOWPSecurity_List_Table {
|
|
| 111 |
if (isset($_REQUEST['_wp_http_referer']))
|
| 112 |
{
|
| 113 |
//Delete multiple records
|
| 114 |
-
$tab = strip_tags($_REQUEST['tab']);
|
| 115 |
|
| 116 |
$entries = array_filter($entries, 'is_numeric'); //discard non-numeric ID values
|
| 117 |
$id_list = "(" .implode(",",$entries) .")"; //Create comma separate list for DB operation
|
| 118 |
$delete_command = "DELETE FROM ".$login_activity_table." WHERE id IN ".$id_list;
|
| 119 |
$result = $wpdb->query($delete_command);
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
$redir_url = sprintf('admin.php?page=%s&tab=%s&bulk_error=%s', AIOWPSEC_USER_LOGIN_MENU_SLUG, $tab, 1);
|
| 128 |
-
AIOWPSecurity_Utility::redirect_to_url($redir_url);
|
| 129 |
-
|
| 130 |
-
}
|
| 131 |
}
|
| 132 |
-
}
|
| 133 |
-
|
| 134 |
-
{
|
| 135 |
-
$nonce=isset($_GET['aiowps_nonce'])?$_GET['aiowps_nonce']:'';
|
| 136 |
if (!isset($nonce) ||!wp_verify_nonce($nonce, 'delete_acct_activity_log'))
|
| 137 |
{
|
| 138 |
$aio_wp_security->debug_logger->log_debug("Nonce check failed for delete selected account activity logs operation!",4);
|
|
@@ -141,61 +138,77 @@ class AIOWPSecurity_List_Account_Activity extends AIOWPSecurity_List_Table {
|
|
| 141 |
//Delete single record
|
| 142 |
$delete_command = "DELETE FROM ".$login_activity_table." WHERE id = '".absint($entries)."'";
|
| 143 |
$result = $wpdb->query($delete_command);
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
}
|
| 152 |
}
|
| 153 |
-
|
| 154 |
-
function prepare_items($ignore_pagination = false) {
|
| 155 |
-
/**
|
| 156 |
-
* First, lets decide how many records per page to show
|
| 157 |
-
*/
|
| 158 |
-
$per_page = 100;
|
| 159 |
-
$columns = $this->get_columns();
|
| 160 |
-
$hidden = array();
|
| 161 |
-
$sortable = $this->get_sortable_columns();
|
| 162 |
-
$search = isset( $_REQUEST['s'] ) ? sanitize_text_field( $_REQUEST['s'] ) : '';
|
| 163 |
|
| 164 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 165 |
|
| 166 |
-
|
| 167 |
|
| 168 |
-
|
| 169 |
-
$login_activity_table = AIOWPSEC_TBL_USER_LOGIN_ACTIVITY;
|
| 170 |
|
| 171 |
-
|
| 172 |
-
|
| 173 |
|
| 174 |
-
|
| 175 |
-
|
| 176 |
|
| 177 |
-
|
| 178 |
-
|
| 179 |
|
| 180 |
-
|
| 181 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 182 |
|
| 183 |
-
if(empty($search)) {
|
| 184 |
-
$data = $wpdb->get_results("SELECT * FROM $login_activity_table ORDER BY $orderby $order", ARRAY_A);
|
| 185 |
-
} else {
|
| 186 |
-
$data = $wpdb->get_results($wpdb->prepare("SELECT * FROM $login_activity_table WHERE `user_login` LIKE '%%%s%%' OR `login_ip` LIKE '%%%s%%' ORDER BY $orderby $order LIMIT %d", $search, $search, 100), ARRAY_A);
|
| 187 |
-
}
|
| 188 |
-
|
| 189 |
-
if (!$ignore_pagination) {
|
| 190 |
-
$current_page = $this->get_pagenum();
|
| 191 |
-
$total_items = count($data);
|
| 192 |
-
$data = array_slice($data, (($current_page - 1) * $per_page), $per_page);
|
| 193 |
-
$this->set_pagination_args(array(
|
| 194 |
-
'total_items' => $total_items, //WE have to calculate the total number of items
|
| 195 |
-
'per_page' => $per_page, //WE have to determine how many items to show on a page
|
| 196 |
-
'total_pages' => ceil($total_items / $per_page) //WE have to calculate the total number of pages
|
| 197 |
-
));
|
| 198 |
-
}
|
| 199 |
-
$this->items = $data;
|
| 200 |
-
}
|
| 201 |
}
|
| 5 |
|
| 6 |
class AIOWPSecurity_List_Account_Activity extends AIOWPSecurity_List_Table {
|
| 7 |
|
| 8 |
+
/**
|
| 9 |
+
* Class constructor
|
| 10 |
+
*/
|
| 11 |
+
public function __construct(){
|
| 12 |
global $status, $page;
|
| 13 |
|
| 14 |
//Set parent defaults
|
| 20 |
|
| 21 |
}
|
| 22 |
|
| 23 |
+
public function column_default($item, $column_name){
|
| 24 |
return $item[$column_name];
|
| 25 |
}
|
| 26 |
|
| 27 |
+
public function column_user_id($item){
|
| 28 |
+
$tab = strip_tags(stripslashes($_REQUEST['tab']));
|
| 29 |
$delete_url = sprintf('admin.php?page=%s&tab=%s&action=%s&activity_login_rec=%s', AIOWPSEC_USER_LOGIN_MENU_SLUG, $tab, 'delete_acct_activity_rec', $item['id']);
|
| 30 |
//Add nonce to delete URL
|
| 31 |
$delete_url_nonce = wp_nonce_url($delete_url, "delete_acct_activity_log", "aiowps_nonce");
|
| 46 |
return '1000-10-10 10:00:00' == $item['logout_date'] ? __('Login session still active', 'all-in-one-wp-security-and-firewall') : $item['logout_date'];
|
| 47 |
}
|
| 48 |
|
| 49 |
+
public function column_cb($item){
|
| 50 |
return sprintf(
|
| 51 |
'<input type="checkbox" name="%1$s[]" value="%2$s" />',
|
| 52 |
/*$1%s*/ $this->_args['singular'], //Let's simply repurpose the table's singular label
|
| 54 |
);
|
| 55 |
}
|
| 56 |
|
| 57 |
+
public function get_columns(){
|
| 58 |
$columns = array(
|
| 59 |
'cb' => '<input type="checkbox" />', //Render a checkbox
|
| 60 |
'user_id' => __('User ID', 'all-in-one-wp-security-and-firewall'),
|
| 66 |
return $columns;
|
| 67 |
}
|
| 68 |
|
| 69 |
+
public function get_sortable_columns() {
|
| 70 |
$sortable_columns = array(
|
| 71 |
'user_id' => array('user_id',false),
|
| 72 |
'user_login' => array('user_login',false),
|
| 77 |
return $sortable_columns;
|
| 78 |
}
|
| 79 |
|
| 80 |
+
public function get_bulk_actions() {
|
| 81 |
$actions = array(
|
| 82 |
'delete' => 'Delete'
|
| 83 |
);
|
| 84 |
return $actions;
|
| 85 |
}
|
| 86 |
|
| 87 |
+
public function process_bulk_action() {
|
| 88 |
+
if ('delete'===$this->current_action())
|
| 89 |
{//Process delete bulk actions
|
| 90 |
+
if (!isset($_REQUEST['item'])) {
|
|
|
|
| 91 |
$error_msg = '<div id="message" class="error"><p><strong>';
|
| 92 |
$error_msg .= __('Please select some records using the checkboxes','all-in-one-wp-security-and-firewall');
|
| 93 |
$error_msg .= '</strong></p></div>';
|
| 94 |
+
echo $error_msg;
|
| 95 |
} else{
|
| 96 |
+
$delete_login_activity_ids = array_filter(array_map('intval', $_REQUEST['item']));
|
| 97 |
+
$this->delete_login_activity_records($delete_login_activity_ids);
|
| 98 |
}
|
| 99 |
}
|
| 100 |
}
|
| 101 |
+
|
| 102 |
+
/**
|
| 103 |
+
* Deletes one or more records from the AIOWPSEC_TBL_USER_LOGIN_ACTIVITY table.
|
| 104 |
+
*
|
| 105 |
+
* @param Array|String|Integer - ids or a single id
|
| 106 |
+
*
|
| 107 |
+
* @return Void
|
| 108 |
+
*/
|
| 109 |
+
public function delete_login_activity_records($entries) {
|
| 110 |
global $wpdb, $aio_wp_security;
|
| 111 |
$login_activity_table = AIOWPSEC_TBL_USER_LOGIN_ACTIVITY;
|
| 112 |
if (is_array($entries))
|
| 114 |
if (isset($_REQUEST['_wp_http_referer']))
|
| 115 |
{
|
| 116 |
//Delete multiple records
|
| 117 |
+
$tab = strip_tags(stripslashes($_REQUEST['tab']));
|
| 118 |
|
| 119 |
$entries = array_filter($entries, 'is_numeric'); //discard non-numeric ID values
|
| 120 |
$id_list = "(" .implode(",",$entries) .")"; //Create comma separate list for DB operation
|
| 121 |
$delete_command = "DELETE FROM ".$login_activity_table." WHERE id IN ".$id_list;
|
| 122 |
$result = $wpdb->query($delete_command);
|
| 123 |
+
if ($result) {
|
| 124 |
+
AIOWPSecurity_Admin_Menu::show_msg_record_deleted_st();
|
| 125 |
+
} else {
|
| 126 |
+
// Error on bulk delete
|
| 127 |
+
$aio_wp_security->debug_logger->log_debug('Database error occurred when deleting rows from User Login Activity table. Database error: '.$wpdb->last_error, 4);
|
| 128 |
+
AIOWPSecurity_Admin_Menu::show_msg_record_not_deleted_st();
|
| 129 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
}
|
| 131 |
+
} elseif ($entries != NULL) {
|
| 132 |
+
$nonce=isset($_GET['aiowps_nonce']) ? stripslashes($_GET['aiowps_nonce']) : '';
|
|
|
|
|
|
|
| 133 |
if (!isset($nonce) ||!wp_verify_nonce($nonce, 'delete_acct_activity_log'))
|
| 134 |
{
|
| 135 |
$aio_wp_security->debug_logger->log_debug("Nonce check failed for delete selected account activity logs operation!",4);
|
| 138 |
//Delete single record
|
| 139 |
$delete_command = "DELETE FROM ".$login_activity_table." WHERE id = '".absint($entries)."'";
|
| 140 |
$result = $wpdb->query($delete_command);
|
| 141 |
+
if ($result) {
|
| 142 |
+
AIOWPSecurity_Admin_Menu::show_msg_record_deleted_st();
|
| 143 |
+
} elseif ($result === false) {
|
| 144 |
+
// Error on single delete
|
| 145 |
+
$aio_wp_security->debug_logger->log_debug('Database error occurred when deleting rows from User Login Activity table. Database error: '.$wpdb->last_error, 4);
|
| 146 |
+
AIOWPSecurity_Admin_Menu::show_msg_record_not_deleted_st();
|
| 147 |
+
}
|
| 148 |
}
|
| 149 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 150 |
|
| 151 |
+
/**
|
| 152 |
+
* Retrieves all items from AIOWPSEC_TBL_USER_LOGIN_ACTIVITY according to a search term inside $_REQUEST['s']. It then assigns to $this->items.
|
| 153 |
+
*
|
| 154 |
+
* @param Boolean $ignore_pagination - whether to not paginate
|
| 155 |
+
*
|
| 156 |
+
* @return Void
|
| 157 |
+
*/
|
| 158 |
+
public function prepare_items($ignore_pagination = false) {
|
| 159 |
+
/**
|
| 160 |
+
* First, lets decide how many records per page to show
|
| 161 |
+
*/
|
| 162 |
+
$per_page = 100;
|
| 163 |
+
$columns = $this->get_columns();
|
| 164 |
+
$hidden = array();
|
| 165 |
+
$sortable = $this->get_sortable_columns();
|
| 166 |
+
$search_term = isset($_REQUEST['s']) ? sanitize_text_field(stripslashes($_REQUEST['s'])) : '';
|
| 167 |
|
| 168 |
+
$this->_column_headers = array($columns, $hidden, $sortable);
|
| 169 |
|
| 170 |
+
$this->process_bulk_action();
|
|
|
|
| 171 |
|
| 172 |
+
global $wpdb;
|
| 173 |
+
$login_activity_table = AIOWPSEC_TBL_USER_LOGIN_ACTIVITY;
|
| 174 |
|
| 175 |
+
/* -- Ordering parameters -- */
|
| 176 |
+
//Parameters that are going to be used to order the result
|
| 177 |
|
| 178 |
+
$orderby = isset($_GET['orderby']) ? strip_tags(stripslashes($_GET['orderby'])) : $orderby = '';
|
| 179 |
+
$order = isset($_GET['order']) ? strip_tags(stripslashes($_GET['order'])) : $order = '';
|
| 180 |
|
| 181 |
+
$orderby = !empty($orderby) ? esc_sql($orderby) : 'login_date';
|
| 182 |
+
$order = !empty($order) ? esc_sql($order) : 'DESC';
|
| 183 |
+
|
| 184 |
+
$orderby = AIOWPSecurity_Utility::sanitize_value_by_array($orderby, $sortable);
|
| 185 |
+
$order = AIOWPSecurity_Utility::sanitize_value_by_array($order, array('DESC' => '1', 'ASC' => '1'));
|
| 186 |
+
|
| 187 |
+
if (empty($search_term)) {
|
| 188 |
+
$data = $wpdb->get_results("SELECT * FROM $login_activity_table ORDER BY $orderby $order", ARRAY_A);
|
| 189 |
+
} else {
|
| 190 |
+
$data = $wpdb->get_results($wpdb->prepare("SELECT * FROM $login_activity_table WHERE `user_login` LIKE '%%%s%%' OR `login_ip` LIKE '%%%s%%' ORDER BY $orderby $order LIMIT 100", $search_term, $search_term), ARRAY_A);
|
| 191 |
+
}
|
| 192 |
+
|
| 193 |
+
if (!$ignore_pagination) {
|
| 194 |
+
$current_page = $this->get_pagenum();
|
| 195 |
+
$total_items = count($data);
|
| 196 |
+
$data = array_slice($data, (($current_page - 1) * $per_page), $per_page);
|
| 197 |
+
$this->set_pagination_args(array(
|
| 198 |
+
'total_items' => $total_items, //WE have to calculate the total number of items
|
| 199 |
+
'per_page' => $per_page, //WE have to determine how many items to show on a page
|
| 200 |
+
'total_pages' => ceil($total_items / $per_page) //WE have to calculate the total number of pages
|
| 201 |
+
));
|
| 202 |
+
}
|
| 203 |
+
|
| 204 |
+
foreach ($data as $index => $row) {
|
| 205 |
+
$data[$index]['login_date'] = get_date_from_gmt(mysql2date('Y-m-d H:i:s', $row['login_date']), $this->get_wp_date_time_format());
|
| 206 |
+
if ('1000-10-10 10:00:00' != $row['logout_date']) {
|
| 207 |
+
$data[$index]['logout_date'] = get_date_from_gmt(mysql2date('Y-m-d H:i:s', $row['logout_date']), $this->get_wp_date_time_format());
|
| 208 |
+
}
|
| 209 |
+
}
|
| 210 |
+
|
| 211 |
+
$this->items = $data;
|
| 212 |
+
}
|
| 213 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 214 |
}
|
|
@@ -24,7 +24,7 @@ class AIOWPSecurity_List_Comment_Spammer_IP extends AIOWPSecurity_List_Table {
|
|
| 24 |
function column_comment_author_IP($item){
|
| 25 |
$tab = strip_tags($_REQUEST['tab']);
|
| 26 |
//Build row actions
|
| 27 |
-
if (
|
| 28 |
//Suppress the block link if site is a multi site AND not the main site
|
| 29 |
$actions = array(); //blank array
|
| 30 |
}else{
|
|
@@ -73,7 +73,7 @@ class AIOWPSecurity_List_Comment_Spammer_IP extends AIOWPSecurity_List_Table {
|
|
| 73 |
}
|
| 74 |
|
| 75 |
function get_bulk_actions() {
|
| 76 |
-
if (
|
| 77 |
//Suppress the block link if site is a multi site AND not the main site
|
| 78 |
$actions = array(); //blank array
|
| 79 |
}else{
|
| 24 |
function column_comment_author_IP($item){
|
| 25 |
$tab = strip_tags($_REQUEST['tab']);
|
| 26 |
//Build row actions
|
| 27 |
+
if (is_multisite() && get_current_blog_id() != 1){
|
| 28 |
//Suppress the block link if site is a multi site AND not the main site
|
| 29 |
$actions = array(); //blank array
|
| 30 |
}else{
|
| 73 |
}
|
| 74 |
|
| 75 |
function get_bulk_actions() {
|
| 76 |
+
if (is_multisite() && get_current_blog_id() != 1){
|
| 77 |
//Suppress the block link if site is a multi site AND not the main site
|
| 78 |
$actions = array(); //blank array
|
| 79 |
}else{
|
|
@@ -109,54 +109,55 @@ class AIOWPSecurity_List_Locked_IP extends AIOWPSecurity_List_Table {
|
|
| 109 |
}
|
| 110 |
}
|
| 111 |
}
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
|
|
|
| 160 |
global $wpdb, $aio_wp_security;
|
| 161 |
$lockdown_table = AIOWPSEC_TBL_LOGIN_LOCKDOWN;
|
| 162 |
if (is_array($entries))
|
|
@@ -168,10 +169,13 @@ class AIOWPSecurity_List_Locked_IP extends AIOWPSecurity_List_Table {
|
|
| 168 |
$id_list = "(" .implode(",",$entries) .")"; //Create comma separate list for DB operation
|
| 169 |
$delete_command = "DELETE FROM ".$lockdown_table." WHERE id IN ".$id_list;
|
| 170 |
$result = $wpdb->query($delete_command);
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
|
|
|
|
|
|
|
|
|
| 175 |
}
|
| 176 |
}
|
| 177 |
elseif ($entries != NULL)
|
|
@@ -185,50 +189,69 @@ class AIOWPSecurity_List_Locked_IP extends AIOWPSecurity_List_Table {
|
|
| 185 |
//Delete single record
|
| 186 |
$delete_command = "DELETE FROM ".$lockdown_table." WHERE id = '".absint($entries)."'";
|
| 187 |
$result = $wpdb->query($delete_command);
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
|
|
|
|
|
|
|
|
|
| 192 |
}
|
| 193 |
}
|
| 194 |
-
|
| 195 |
-
function prepare_items() {
|
| 196 |
-
/**
|
| 197 |
-
* First, lets decide how many records per page to show
|
| 198 |
-
*/
|
| 199 |
-
$per_page = 100;
|
| 200 |
-
$columns = $this->get_columns();
|
| 201 |
-
$hidden = array();
|
| 202 |
-
$sortable = $this->get_sortable_columns();
|
| 203 |
-
|
| 204 |
-
$this->_column_headers = array($columns, $hidden, $sortable);
|
| 205 |
-
|
| 206 |
-
$this->process_bulk_action();
|
| 207 |
-
|
| 208 |
-
global $wpdb;
|
| 209 |
-
$lockdown_table_name = AIOWPSEC_TBL_LOGIN_LOCKDOWN;
|
| 210 |
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 215 |
|
| 216 |
-
|
| 217 |
-
$order = !empty($order) ? esc_sql($order) : 'DESC';
|
| 218 |
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
}
|
| 110 |
}
|
| 111 |
}
|
| 112 |
+
|
| 113 |
+
/**
|
| 114 |
+
* Unlocks an IP range by modifying the release_date column of a record in the AIOWPSEC_TBL_LOGIN_LOCKDOWN table.
|
| 115 |
+
*
|
| 116 |
+
* @param Array|Integer - ids or a single id
|
| 117 |
+
*
|
| 118 |
+
* @return Void
|
| 119 |
+
*/
|
| 120 |
+
public function unlock_ip_range($entries) {
|
| 121 |
+
global $wpdb, $aio_wp_security;
|
| 122 |
+
|
| 123 |
+
$lockdown_table = AIOWPSEC_TBL_LOGIN_LOCKDOWN;
|
| 124 |
+
|
| 125 |
+
$now = current_time('mysql', true);
|
| 126 |
+
|
| 127 |
+
if (is_array($entries)) {
|
| 128 |
+
if (isset($_REQUEST['_wp_http_referer'])) {
|
| 129 |
+
// Unlock multiple records
|
| 130 |
+
$entries = array_filter($entries, 'is_numeric'); // Discard non-numeric ID values
|
| 131 |
+
$id_list = '(' .implode(',', $entries) .')'; // Create comma separate list for DB operation
|
| 132 |
+
$result = $wpdb->query($wpdb->prepare("UPDATE $lockdown_table SET `release_date` = %s WHERE `id` IN $id_list", $now));
|
| 133 |
+
|
| 134 |
+
if (NULL != $result) {
|
| 135 |
+
AIOWPSecurity_Admin_Menu::show_msg_updated_st(__('The selected IP entries were unlocked successfully!', 'all-in-one-wp-security-and-firewall'));
|
| 136 |
+
}
|
| 137 |
+
}
|
| 138 |
+
} elseif (NULL != $entries) {
|
| 139 |
+
if (!isset($_GET['aiowps_nonce']) || !wp_verify_nonce($_GET['aiowps_nonce'], 'unlock_ip')) {
|
| 140 |
+
$aio_wp_security->debug_logger->log_debug('Nonce check failed for unlock IP operation.', 4);
|
| 141 |
+
die(__('Nonce check failed for unlock IP operation!', 'all-in-one-wp-security-and-firewall'));
|
| 142 |
+
}
|
| 143 |
+
|
| 144 |
+
// Unlock single record
|
| 145 |
+
$result = $wpdb->query($wpdb->prepare("UPDATE $lockdown_table SET `release_date` = %s WHERE `id` = %d", $now, absint($entries)));
|
| 146 |
+
|
| 147 |
+
if (NULL != $result) {
|
| 148 |
+
AIOWPSecurity_Admin_Menu::show_msg_updated_st(__('The selected IP entry was unlocked successfully.', 'all-in-one-wp-security-and-firewall'));
|
| 149 |
+
}
|
| 150 |
+
}
|
| 151 |
+
}
|
| 152 |
+
|
| 153 |
+
/**
|
| 154 |
+
* Deletes one or more records from the AIOWPSEC_TBL_LOGIN_LOCKDOWN table.
|
| 155 |
+
*
|
| 156 |
+
* @param Array|String|Integer - ids or a single id
|
| 157 |
+
*
|
| 158 |
+
* @return Void
|
| 159 |
+
*/
|
| 160 |
+
public function delete_lockdown_records($entries) {
|
| 161 |
global $wpdb, $aio_wp_security;
|
| 162 |
$lockdown_table = AIOWPSEC_TBL_LOGIN_LOCKDOWN;
|
| 163 |
if (is_array($entries))
|
| 169 |
$id_list = "(" .implode(",",$entries) .")"; //Create comma separate list for DB operation
|
| 170 |
$delete_command = "DELETE FROM ".$lockdown_table." WHERE id IN ".$id_list;
|
| 171 |
$result = $wpdb->query($delete_command);
|
| 172 |
+
if ($result) {
|
| 173 |
+
AIOWPSecurity_Admin_Menu::show_msg_record_deleted_st();
|
| 174 |
+
} else {
|
| 175 |
+
// Error on bulk delete
|
| 176 |
+
$aio_wp_security->debug_logger->log_debug('Database error occurred when deleting rows from Login Lockdown table. Database error: '.$wpdb->last_error, 4);
|
| 177 |
+
AIOWPSecurity_Admin_Menu::show_msg_record_not_deleted_st();
|
| 178 |
+
}
|
| 179 |
}
|
| 180 |
}
|
| 181 |
elseif ($entries != NULL)
|
| 189 |
//Delete single record
|
| 190 |
$delete_command = "DELETE FROM ".$lockdown_table." WHERE id = '".absint($entries)."'";
|
| 191 |
$result = $wpdb->query($delete_command);
|
| 192 |
+
if ($result) {
|
| 193 |
+
AIOWPSecurity_Admin_Menu::show_msg_record_deleted_st();
|
| 194 |
+
} elseif ($result === false) {
|
| 195 |
+
// Error on single delete
|
| 196 |
+
$aio_wp_security->debug_logger->log_debug('Database error occurred when deleting rows from Login Lockdown table. Database error: '.$wpdb->last_error, 4);
|
| 197 |
+
AIOWPSecurity_Admin_Menu::show_msg_record_not_deleted_st();
|
| 198 |
+
}
|
| 199 |
}
|
| 200 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 201 |
|
| 202 |
+
/**
|
| 203 |
+
* Retrieves all items from AIOWPSEC_TBL_LOGIN_LOCKDOWN. It may paginate and then assigns to $this->items.
|
| 204 |
+
*
|
| 205 |
+
* @param Boolean $ignore_pagination - whether to not paginate
|
| 206 |
+
*
|
| 207 |
+
* @return Void
|
| 208 |
+
*/
|
| 209 |
+
public function prepare_items($ignore_pagination = false) {
|
| 210 |
+
global $wpdb;
|
| 211 |
|
| 212 |
+
$lockdown_table = AIOWPSEC_TBL_LOGIN_LOCKDOWN;
|
|
|
|
| 213 |
|
| 214 |
+
$this->process_bulk_action();
|
| 215 |
+
|
| 216 |
+
// How many records per page to show
|
| 217 |
+
$per_page = 100;
|
| 218 |
+
$columns = $this->get_columns();
|
| 219 |
+
$hidden = array();
|
| 220 |
+
$sortable = $this->get_sortable_columns();
|
| 221 |
+
|
| 222 |
+
$this->_column_headers = array($columns, $hidden, $sortable);
|
| 223 |
+
|
| 224 |
+
// Parameters that are going to be used to order the result
|
| 225 |
+
$orderby = isset($_GET['orderby']) ? sanitize_text_field(wp_unslash($_GET['orderby'])) : '';
|
| 226 |
+
$order = isset($_GET['order']) ? sanitize_text_field(wp_unslash($_GET['order'])) : '';
|
| 227 |
+
|
| 228 |
+
$orderby = !empty($orderby) ? esc_sql($orderby) : 'lockdown_date';
|
| 229 |
+
$order = !empty($order) ? esc_sql($order) : 'DESC';
|
| 230 |
+
|
| 231 |
+
$orderby = AIOWPSecurity_Utility::sanitize_value_by_array($orderby, $sortable);
|
| 232 |
+
$order = AIOWPSecurity_Utility::sanitize_value_by_array($order, array('DESC' => '1', 'ASC' => '1'));
|
| 233 |
+
|
| 234 |
+
$now = current_time('mysql', true);
|
| 235 |
+
|
| 236 |
+
$data = $wpdb->get_results($wpdb->prepare("SELECT * FROM $lockdown_table WHERE `release_date` > %s ORDER BY $orderby $order", $now), ARRAY_A);
|
| 237 |
+
|
| 238 |
+
if (!$ignore_pagination) {
|
| 239 |
+
$current_page = $this->get_pagenum();
|
| 240 |
+
$total_items = count($data);
|
| 241 |
+
$data = array_slice($data, (($current_page - 1) * $per_page), $per_page);
|
| 242 |
+
$this->set_pagination_args(array(
|
| 243 |
+
'total_items' => $total_items, // WE have to calculate the total number of items
|
| 244 |
+
'per_page' => $per_page, // WE have to determine how many items to show on a page
|
| 245 |
+
'total_pages' => ceil($total_items / $per_page) // WE have to calculate the total number of pages
|
| 246 |
+
));
|
| 247 |
+
}
|
| 248 |
+
|
| 249 |
+
foreach ($data as $index => $row) {
|
| 250 |
+
$data[$index]['lockdown_date'] = get_date_from_gmt(mysql2date('Y-m-d H:i:s', $row['lockdown_date']), $this->get_wp_date_time_format());
|
| 251 |
+
$data[$index]['release_date'] = get_date_from_gmt(mysql2date('Y-m-d H:i:s', $row['release_date']), $this->get_wp_date_time_format());
|
| 252 |
+
}
|
| 253 |
+
|
| 254 |
+
$this->items = $data;
|
| 255 |
+
}
|
| 256 |
+
|
| 257 |
+
}
|
|
@@ -112,7 +112,7 @@ class AIOWPSecurity_List_Logged_In_Users extends AIOWPSecurity_List_Table {
|
|
| 112 |
global $wpdb;
|
| 113 |
global $aio_wp_security;
|
| 114 |
|
| 115 |
-
if (
|
| 116 |
$current_blog_id = get_current_blog_id();
|
| 117 |
$logged_in_users = AIOWPSecurity_User_Login::get_subsite_logged_in_users($current_blog_id);
|
| 118 |
} else {
|
| 112 |
global $wpdb;
|
| 113 |
global $aio_wp_security;
|
| 114 |
|
| 115 |
+
if (is_multisite()) {
|
| 116 |
$current_blog_id = get_current_blog_id();
|
| 117 |
$logged_in_users = AIOWPSecurity_User_Login::get_subsite_logged_in_users($current_blog_id);
|
| 118 |
} else {
|
|
@@ -21,17 +21,6 @@ class AIOWPSecurity_List_Login_Failed_Attempts extends AIOWPSecurity_List_Table
|
|
| 21 |
return $item[$column_name];
|
| 22 |
}
|
| 23 |
|
| 24 |
-
/**
|
| 25 |
-
* This method returns failed login date after being formatted according to the date_format and time_format options.
|
| 26 |
-
*
|
| 27 |
-
* @param Array $item - an array containing data for a single row of the failed logins table
|
| 28 |
-
*
|
| 29 |
-
* @return String returns formatted date-time string
|
| 30 |
-
*/
|
| 31 |
-
protected function column_failed_login_date($item) {
|
| 32 |
-
return get_date_from_gmt(mysql2date('Y-m-d H:i:s', $item['failed_login_date']), get_option('date_format').' '.get_option('time_format'));
|
| 33 |
-
}
|
| 34 |
-
|
| 35 |
function column_login_attempt_ip($item){
|
| 36 |
$tab = strip_tags($_REQUEST['tab']);
|
| 37 |
$delete_url = sprintf('admin.php?page=%s&tab=%s&action=%s&failed_login_id=%s', AIOWPSEC_USER_LOGIN_MENU_SLUG, $tab, 'delete_failed_login_rec', $item['id']);
|
|
@@ -50,7 +39,6 @@ class AIOWPSecurity_List_Login_Failed_Attempts extends AIOWPSecurity_List_Table
|
|
| 50 |
);
|
| 51 |
}
|
| 52 |
|
| 53 |
-
|
| 54 |
function column_cb($item){
|
| 55 |
return sprintf(
|
| 56 |
'<input type="checkbox" name="%1$s[]" value="%2$s" />',
|
|
@@ -58,7 +46,7 @@ class AIOWPSecurity_List_Login_Failed_Attempts extends AIOWPSecurity_List_Table
|
|
| 58 |
/*$2%s*/ $item['id'] //The value of the checkbox should be the record's id
|
| 59 |
);
|
| 60 |
}
|
| 61 |
-
|
| 62 |
function get_columns(){
|
| 63 |
$columns = array(
|
| 64 |
'cb' => '<input type="checkbox" />', //Render a checkbox
|
|
@@ -103,15 +91,15 @@ class AIOWPSecurity_List_Login_Failed_Attempts extends AIOWPSecurity_List_Table
|
|
| 103 |
}
|
| 104 |
}
|
| 105 |
}
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
global $wpdb, $aio_wp_security;
|
| 116 |
$failed_login_table = AIOWPSEC_TBL_FAILED_LOGINS;
|
| 117 |
if (is_array($entries))
|
|
@@ -124,17 +112,13 @@ class AIOWPSecurity_List_Login_Failed_Attempts extends AIOWPSecurity_List_Table
|
|
| 124 |
$id_list = "(" .implode(",",$entries) .")"; //Create comma separate list for DB operation
|
| 125 |
$delete_command = "DELETE FROM ".$failed_login_table." WHERE ID IN ".$id_list;
|
| 126 |
$result = $wpdb->query($delete_command);
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
$redir_url = sprintf('admin.php?page=%s&tab=%s&bulk_error=%s', AIOWPSEC_USER_LOGIN_MENU_SLUG, $tab, 1);
|
| 135 |
-
AIOWPSecurity_Utility::redirect_to_url($redir_url);
|
| 136 |
-
|
| 137 |
-
}
|
| 138 |
}
|
| 139 |
|
| 140 |
} elseif ($entries != NULL)
|
|
@@ -148,59 +132,72 @@ class AIOWPSecurity_List_Login_Failed_Attempts extends AIOWPSecurity_List_Table
|
|
| 148 |
//Delete single record
|
| 149 |
$delete_command = "DELETE FROM ".$failed_login_table." WHERE ID = '".absint($entries)."'";
|
| 150 |
$result = $wpdb->query($delete_command);
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
}
|
| 159 |
}
|
| 160 |
-
|
| 161 |
-
function prepare_items($ignore_pagination = false) {
|
| 162 |
-
/**
|
| 163 |
-
* First, lets decide how many records per page to show
|
| 164 |
-
*/
|
| 165 |
-
$per_page = 100;
|
| 166 |
-
$columns = $this->get_columns();
|
| 167 |
-
$hidden = array();
|
| 168 |
-
$sortable = $this->get_sortable_columns();
|
| 169 |
-
$search = isset( $_REQUEST['s'] ) ? sanitize_text_field( $_REQUEST['s'] ) : '';
|
| 170 |
-
|
| 171 |
-
$this->_column_headers = array($columns, $hidden, $sortable);
|
| 172 |
-
|
| 173 |
-
$this->process_bulk_action();
|
| 174 |
-
|
| 175 |
-
global $wpdb;
|
| 176 |
-
$failed_logins_table_name = AIOWPSEC_TBL_FAILED_LOGINS;
|
| 177 |
-
|
| 178 |
-
/* -- Ordering parameters -- */
|
| 179 |
-
//Parameters that are going to be used to order the result
|
| 180 |
-
isset($_GET["orderby"]) ? $orderby = strip_tags($_GET["orderby"]) : $orderby = '';
|
| 181 |
-
isset($_GET["order"]) ? $order = strip_tags($_GET["order"]) : $order = '';
|
| 182 |
-
|
| 183 |
-
$orderby = !empty($orderby) ? esc_sql($orderby) : 'failed_login_date';
|
| 184 |
-
$order = !empty($order) ? esc_sql($order) : 'DESC';
|
| 185 |
-
|
| 186 |
-
$orderby = AIOWPSecurity_Utility::sanitize_value_by_array($orderby, $sortable);
|
| 187 |
-
$order = AIOWPSecurity_Utility::sanitize_value_by_array($order, array('DESC' => '1', 'ASC' => '1'));
|
| 188 |
-
if(empty($search)) {
|
| 189 |
-
$data = $wpdb->get_results("SELECT * FROM " . $failed_logins_table_name . " ORDER BY $orderby $order", ARRAY_A);
|
| 190 |
-
} else {
|
| 191 |
-
$data = $wpdb->get_results($wpdb->prepare("SELECT * FROM $failed_logins_table_name WHERE `user_login` LIKE '%%%s%%' OR `login_attempt_ip` LIKE '%%%s%%' ORDER BY $orderby $order", $search, $search), ARRAY_A);
|
| 192 |
-
}
|
| 193 |
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 206 |
}
|
| 21 |
return $item[$column_name];
|
| 22 |
}
|
| 23 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 24 |
function column_login_attempt_ip($item){
|
| 25 |
$tab = strip_tags($_REQUEST['tab']);
|
| 26 |
$delete_url = sprintf('admin.php?page=%s&tab=%s&action=%s&failed_login_id=%s', AIOWPSEC_USER_LOGIN_MENU_SLUG, $tab, 'delete_failed_login_rec', $item['id']);
|
| 39 |
);
|
| 40 |
}
|
| 41 |
|
|
|
|
| 42 |
function column_cb($item){
|
| 43 |
return sprintf(
|
| 44 |
'<input type="checkbox" name="%1$s[]" value="%2$s" />',
|
| 46 |
/*$2%s*/ $item['id'] //The value of the checkbox should be the record's id
|
| 47 |
);
|
| 48 |
}
|
| 49 |
+
|
| 50 |
function get_columns(){
|
| 51 |
$columns = array(
|
| 52 |
'cb' => '<input type="checkbox" />', //Render a checkbox
|
| 91 |
}
|
| 92 |
}
|
| 93 |
}
|
| 94 |
+
|
| 95 |
+
/**
|
| 96 |
+
* Deletes one or more records from the AIOWPSEC_TBL_FAILED_LOGINS table.
|
| 97 |
+
*
|
| 98 |
+
* @param Array|String|Integer - ids or a single id
|
| 99 |
+
*
|
| 100 |
+
* @return Void
|
| 101 |
+
*/
|
| 102 |
+
public function delete_login_failed_records($entries) {
|
| 103 |
global $wpdb, $aio_wp_security;
|
| 104 |
$failed_login_table = AIOWPSEC_TBL_FAILED_LOGINS;
|
| 105 |
if (is_array($entries))
|
| 112 |
$id_list = "(" .implode(",",$entries) .")"; //Create comma separate list for DB operation
|
| 113 |
$delete_command = "DELETE FROM ".$failed_login_table." WHERE ID IN ".$id_list;
|
| 114 |
$result = $wpdb->query($delete_command);
|
| 115 |
+
if ($result) {
|
| 116 |
+
AIOWPSecurity_Admin_Menu::show_msg_record_deleted_st();
|
| 117 |
+
} else {
|
| 118 |
+
// Error on bulk delete
|
| 119 |
+
$aio_wp_security->debug_logger->log_debug('Database error occurred when deleting rows from Failed Logins table. Database error: '.$wpdb->last_error, 4);
|
| 120 |
+
AIOWPSecurity_Admin_Menu::show_msg_record_not_deleted_st();
|
| 121 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 122 |
}
|
| 123 |
|
| 124 |
} elseif ($entries != NULL)
|
| 132 |
//Delete single record
|
| 133 |
$delete_command = "DELETE FROM ".$failed_login_table." WHERE ID = '".absint($entries)."'";
|
| 134 |
$result = $wpdb->query($delete_command);
|
| 135 |
+
if ($result) {
|
| 136 |
+
AIOWPSecurity_Admin_Menu::show_msg_record_deleted_st();
|
| 137 |
+
} elseif ($result === false) {
|
| 138 |
+
// Error on single delete
|
| 139 |
+
$aio_wp_security->debug_logger->log_debug('Database error occurred when deleting rows from Failed Logins table. Database error: '.$wpdb->last_error, 4);
|
| 140 |
+
AIOWPSecurity_Admin_Menu::show_msg_record_not_deleted_st();
|
| 141 |
+
}
|
| 142 |
}
|
| 143 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 144 |
|
| 145 |
+
/**
|
| 146 |
+
* Retrieves all items from AIOWPSEC_TBL_FAILED_LOGINS according to a search term inside $_REQUEST['s']. It then assigns to $this->items.
|
| 147 |
+
*
|
| 148 |
+
* @param Boolean $ignore_pagination - whether to not paginate
|
| 149 |
+
*
|
| 150 |
+
* @return Void
|
| 151 |
+
*/
|
| 152 |
+
public function prepare_items($ignore_pagination = false) {
|
| 153 |
+
/**
|
| 154 |
+
* First, lets decide how many records per page to show
|
| 155 |
+
*/
|
| 156 |
+
$per_page = 100;
|
| 157 |
+
$columns = $this->get_columns();
|
| 158 |
+
$hidden = array();
|
| 159 |
+
$sortable = $this->get_sortable_columns();
|
| 160 |
+
$search_term = isset($_REQUEST['s']) ? sanitize_text_field(stripslashes($_REQUEST['s'])) : '';
|
| 161 |
+
|
| 162 |
+
$this->_column_headers = array($columns, $hidden, $sortable);
|
| 163 |
+
|
| 164 |
+
$this->process_bulk_action();
|
| 165 |
+
|
| 166 |
+
global $wpdb;
|
| 167 |
+
$failed_logins_table_name = AIOWPSEC_TBL_FAILED_LOGINS;
|
| 168 |
+
|
| 169 |
+
/* -- Ordering parameters -- */
|
| 170 |
+
//Parameters that are going to be used to order the result
|
| 171 |
+
isset($_GET['orderby']) ? $orderby = strip_tags($_GET['orderby']) : $orderby = '';
|
| 172 |
+
isset($_GET['order']) ? $order = strip_tags($_GET['order']) : $order = '';
|
| 173 |
+
|
| 174 |
+
$orderby = !empty($orderby) ? esc_sql($orderby) : 'failed_login_date';
|
| 175 |
+
$order = !empty($order) ? esc_sql($order) : 'DESC';
|
| 176 |
+
|
| 177 |
+
$orderby = AIOWPSecurity_Utility::sanitize_value_by_array($orderby, $sortable);
|
| 178 |
+
$order = AIOWPSecurity_Utility::sanitize_value_by_array($order, array('DESC' => '1', 'ASC' => '1'));
|
| 179 |
+
if(empty($search_term)) {
|
| 180 |
+
$data = $wpdb->get_results("SELECT * FROM $failed_logins_table_name ORDER BY $orderby $order", ARRAY_A);
|
| 181 |
+
} else {
|
| 182 |
+
$data = $wpdb->get_results($wpdb->prepare("SELECT * FROM $failed_logins_table_name WHERE `user_login` LIKE '%%%s%%' OR `login_attempt_ip` LIKE '%%%s%%' ORDER BY $orderby $order", $search_term, $search_term), ARRAY_A);
|
| 183 |
+
}
|
| 184 |
+
|
| 185 |
+
if (!$ignore_pagination) {
|
| 186 |
+
$current_page = $this->get_pagenum();
|
| 187 |
+
$total_items = count($data);
|
| 188 |
+
$data = array_slice($data, (($current_page - 1) * $per_page), $per_page);
|
| 189 |
+
$this->set_pagination_args(array(
|
| 190 |
+
'total_items' => $total_items, //WE have to calculate the total number of items
|
| 191 |
+
'per_page' => $per_page, //WE have to determine how many items to show on a page
|
| 192 |
+
'total_pages' => ceil($total_items / $per_page) //WE have to calculate the total number of pages
|
| 193 |
+
));
|
| 194 |
+
}
|
| 195 |
+
|
| 196 |
+
foreach ($data as $index => $row) {
|
| 197 |
+
$data[$index]['failed_login_date'] = get_date_from_gmt(mysql2date('Y-m-d H:i:s', $row['failed_login_date']), $this->get_wp_date_time_format());
|
| 198 |
+
}
|
| 199 |
+
|
| 200 |
+
$this->items = $data;
|
| 201 |
+
}
|
| 202 |
+
|
| 203 |
}
|
|
@@ -100,13 +100,14 @@ class AIOWPSecurity_List_Blocked_IP extends AIOWPSecurity_List_Table
|
|
| 100 |
}
|
| 101 |
}
|
| 102 |
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
|
|
|
| 110 |
global $wpdb, $aio_wp_security;
|
| 111 |
if (is_array($entries)) {
|
| 112 |
if (isset($_REQUEST['_wp_http_referer'])) {
|
|
@@ -117,17 +118,13 @@ class AIOWPSecurity_List_Blocked_IP extends AIOWPSecurity_List_Table
|
|
| 117 |
$id_list = "(" . implode(",", $entries) . ")"; //Create comma separate list for DB operation
|
| 118 |
$delete_command = "DELETE FROM " . AIOWPSEC_TBL_PERM_BLOCK . " WHERE id IN " . $id_list;
|
| 119 |
$result = $wpdb->query($delete_command);
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
$redir_url = sprintf('admin.php?page=%s&tab=%s&bulk_error=%s', AIOWPSEC_MAIN_MENU_SLUG, $tab, 1);
|
| 128 |
-
AIOWPSecurity_Utility::redirect_to_url($redir_url);
|
| 129 |
-
|
| 130 |
-
}
|
| 131 |
}
|
| 132 |
} elseif ($entries != NULL) {
|
| 133 |
$nonce = isset($_GET['aiowps_nonce']) ? $_GET['aiowps_nonce'] : '';
|
|
@@ -138,9 +135,13 @@ class AIOWPSecurity_List_Blocked_IP extends AIOWPSecurity_List_Table
|
|
| 138 |
//Delete single record
|
| 139 |
$delete_command = "DELETE FROM " . AIOWPSEC_TBL_PERM_BLOCK . " WHERE id = '" . absint($entries) . "'";
|
| 140 |
$result = $wpdb->query($delete_command);
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 144 |
}
|
| 145 |
}
|
| 146 |
|
| 100 |
}
|
| 101 |
}
|
| 102 |
|
| 103 |
+
/**
|
| 104 |
+
* Deletes one or more records from the AIOWPSEC_TBL_PERM_BLOCK table.
|
| 105 |
+
*
|
| 106 |
+
* @param Array|String|Integer - ids or a single id
|
| 107 |
+
*
|
| 108 |
+
* @return Void
|
| 109 |
+
*/
|
| 110 |
+
public function unblock_ip_address($entries) {
|
| 111 |
global $wpdb, $aio_wp_security;
|
| 112 |
if (is_array($entries)) {
|
| 113 |
if (isset($_REQUEST['_wp_http_referer'])) {
|
| 118 |
$id_list = "(" . implode(",", $entries) . ")"; //Create comma separate list for DB operation
|
| 119 |
$delete_command = "DELETE FROM " . AIOWPSEC_TBL_PERM_BLOCK . " WHERE id IN " . $id_list;
|
| 120 |
$result = $wpdb->query($delete_command);
|
| 121 |
+
if ($result) {
|
| 122 |
+
AIOWPSecurity_Admin_Menu::show_msg_updated_st(__('Successfully unblocked and deleted the selected record(s).', 'all-in-one-wp-security-and-firewall'));
|
| 123 |
+
} else {
|
| 124 |
+
// Error on bulk delete
|
| 125 |
+
$aio_wp_security->debug_logger->log_debug('Database error occurred when deleting rows from Perm Block table. Database error: '.$wpdb->last_error, 4);
|
| 126 |
+
AIOWPSecurity_Admin_Menu::show_msg_error_st(__('Failed to unblock and delete the selected record(s).', 'all-in-one-wp-security-and-firewall'));
|
| 127 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 128 |
}
|
| 129 |
} elseif ($entries != NULL) {
|
| 130 |
$nonce = isset($_GET['aiowps_nonce']) ? $_GET['aiowps_nonce'] : '';
|
| 135 |
//Delete single record
|
| 136 |
$delete_command = "DELETE FROM " . AIOWPSEC_TBL_PERM_BLOCK . " WHERE id = '" . absint($entries) . "'";
|
| 137 |
$result = $wpdb->query($delete_command);
|
| 138 |
+
if ($result) {
|
| 139 |
+
AIOWPSecurity_Admin_Menu::show_msg_updated_st(__('Successfully unblocked and deleted the selected record(s).', 'all-in-one-wp-security-and-firewall'));
|
| 140 |
+
} elseif ($result === false) {
|
| 141 |
+
// Error on single delete
|
| 142 |
+
$aio_wp_security->debug_logger->log_debug('Database error occurred when deleting rows from Perm Block table. Database error: '.$wpdb->last_error, 4);
|
| 143 |
+
AIOWPSecurity_Admin_Menu::show_msg_error_st(__('Failed to unblock and delete the selected record(s).', 'all-in-one-wp-security-and-firewall'));
|
| 144 |
+
}
|
| 145 |
}
|
| 146 |
}
|
| 147 |
|
|
@@ -103,12 +103,12 @@ class AIOWPSecurity_Maintenance_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 103 |
<tr valign="top">
|
| 104 |
<th scope="row"><?php _e('Enable Front-end Lockout', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 105 |
<td>
|
| 106 |
-
<input name="aiowps_site_lockout" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_site_lockout')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 107 |
-
<
|
| 108 |
</td>
|
| 109 |
</tr>
|
| 110 |
<tr valign="top">
|
| 111 |
-
<th scope="row"><?php _e('Enter a Message:', 'all-in-one-wp-security-and-firewall')?></th>
|
| 112 |
<td>
|
| 113 |
<?php
|
| 114 |
$aiowps_site_lockout_msg_raw = $aio_wp_security->configs->get_value('aiowps_site_lockout_msg');
|
| 103 |
<tr valign="top">
|
| 104 |
<th scope="row"><?php _e('Enable Front-end Lockout', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 105 |
<td>
|
| 106 |
+
<input id="aiowps_site_lockout" name="aiowps_site_lockout" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_site_lockout')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 107 |
+
<label for="aiowps_site_lockout" class="description"><?php _e('Check this if you want all visitors except those who are logged in as administrator to be locked out of the front-end of your site.', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 108 |
</td>
|
| 109 |
</tr>
|
| 110 |
<tr valign="top">
|
| 111 |
+
<th scope="row"><label for="aiowps_site_lockout_msg_editor_content"><?php _e('Enter a Message:', 'all-in-one-wp-security-and-firewall')?></label></th>
|
| 112 |
<td>
|
| 113 |
<?php
|
| 114 |
$aiowps_site_lockout_msg_raw = $aio_wp_security->configs->get_value('aiowps_site_lockout_msg');
|
|
@@ -24,12 +24,12 @@ class AIOWPSecurity_Misc_Options_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 24 |
|
| 25 |
function set_menu_tabs()
|
| 26 |
{
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
}
|
| 34 |
|
| 35 |
/*
|
|
@@ -105,8 +105,8 @@ class AIOWPSecurity_Misc_Options_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 105 |
<tr valign="top">
|
| 106 |
<th scope="row"><?php _e('Enable Copy Protection', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 107 |
<td>
|
| 108 |
-
<input name="aiowps_copy_protection" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_copy_protection')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 109 |
-
<
|
| 110 |
</td>
|
| 111 |
</tr>
|
| 112 |
|
|
@@ -156,8 +156,8 @@ class AIOWPSecurity_Misc_Options_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 156 |
<tr valign="top">
|
| 157 |
<th scope="row"><?php _e('Enable iFrame Protection', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 158 |
<td>
|
| 159 |
-
<input name="aiowps_prevent_site_display_inside_frame" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_prevent_site_display_inside_frame')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 160 |
-
<
|
| 161 |
</td>
|
| 162 |
</tr>
|
| 163 |
|
|
@@ -207,8 +207,8 @@ class AIOWPSecurity_Misc_Options_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 207 |
<tr valign="top">
|
| 208 |
<th scope="row"><?php _e('Disable Users Enumeration', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 209 |
<td>
|
| 210 |
-
<input name="aiowps_prevent_users_enumeration" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_prevent_users_enumeration')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 211 |
-
<
|
| 212 |
</td>
|
| 213 |
</tr>
|
| 214 |
|
|
@@ -267,8 +267,8 @@ class AIOWPSecurity_Misc_Options_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 267 |
<tr valign="top">
|
| 268 |
<th scope="row"><?php _e('Disallow Unauthorized REST Requests', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 269 |
<td>
|
| 270 |
-
<input name="aiowps_disallow_unauthorized_rest_requests" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_disallow_unauthorized_rest_requests')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 271 |
-
<
|
| 272 |
</td>
|
| 273 |
</tr>
|
| 274 |
|
|
@@ -281,5 +281,5 @@ class AIOWPSecurity_Misc_Options_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 281 |
</div></div>
|
| 282 |
<?php
|
| 283 |
}
|
| 284 |
-
|
| 285 |
} //end class
|
| 24 |
|
| 25 |
function set_menu_tabs()
|
| 26 |
{
|
| 27 |
+
$this->menu_tabs = array(
|
| 28 |
+
'tab1' => __('Copy Protection', 'all-in-one-wp-security-and-firewall'),
|
| 29 |
+
'tab2' => __('Frames', 'all-in-one-wp-security-and-firewall'),
|
| 30 |
+
'tab3' => __('Users Enumeration', 'all-in-one-wp-security-and-firewall'),
|
| 31 |
+
'tab4' => __('WP REST API', 'all-in-one-wp-security-and-firewall'),
|
| 32 |
+
);
|
| 33 |
}
|
| 34 |
|
| 35 |
/*
|
| 105 |
<tr valign="top">
|
| 106 |
<th scope="row"><?php _e('Enable Copy Protection', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 107 |
<td>
|
| 108 |
+
<input id="aiowps_copy_protection" name="aiowps_copy_protection" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_copy_protection')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 109 |
+
<label for="aiowps_copy_protection" class="description"><?php _e('Check this if you want to disable the "Right Click", "Text Selection" and "Copy" option on the front end of your site.', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 110 |
</td>
|
| 111 |
</tr>
|
| 112 |
|
| 156 |
<tr valign="top">
|
| 157 |
<th scope="row"><?php _e('Enable iFrame Protection', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 158 |
<td>
|
| 159 |
+
<input id="aiowps_prevent_site_display_inside_frame" name="aiowps_prevent_site_display_inside_frame" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_prevent_site_display_inside_frame')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 160 |
+
<label for="aiowps_prevent_site_display_inside_frame" class="description"><?php _e('Check this if you want to stop other sites from displaying your content in a frame or iframe.', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 161 |
</td>
|
| 162 |
</tr>
|
| 163 |
|
| 207 |
<tr valign="top">
|
| 208 |
<th scope="row"><?php _e('Disable Users Enumeration', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 209 |
<td>
|
| 210 |
+
<input id="aiowps_prevent_users_enumeration" name="aiowps_prevent_users_enumeration" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_prevent_users_enumeration')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 211 |
+
<label for="aiowps_prevent_users_enumeration" class="description"><?php _e('Check this if you want to stop users enumeration.', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 212 |
</td>
|
| 213 |
</tr>
|
| 214 |
|
| 267 |
<tr valign="top">
|
| 268 |
<th scope="row"><?php _e('Disallow Unauthorized REST Requests', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 269 |
<td>
|
| 270 |
+
<input id="aiowps_disallow_unauthorized_rest_requests" name="aiowps_disallow_unauthorized_rest_requests" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_disallow_unauthorized_rest_requests')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 271 |
+
<label for="aiowps_disallow_unauthorized_rest_requests" class="description"><?php _e('Check this if you want to stop REST API access for non-logged in requests.', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 272 |
</td>
|
| 273 |
</tr>
|
| 274 |
|
| 281 |
</div></div>
|
| 282 |
<?php
|
| 283 |
}
|
| 284 |
+
|
| 285 |
} //end class
|
|
@@ -15,7 +15,7 @@ class AIOWPSecurity_Settings_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 15 |
}
|
| 16 |
|
| 17 |
public function set_menu_tabs() {
|
| 18 |
-
$
|
| 19 |
array(
|
| 20 |
'tab1' => array(
|
| 21 |
'title' => __('General Settings', 'all-in-one-wp-security-and-firewall'),
|
|
@@ -28,6 +28,10 @@ class AIOWPSecurity_Settings_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 28 |
'tab3' => array(
|
| 29 |
'title' => 'wp-config.php '.__('File', 'all-in-one-wp-security-and-firewall'),
|
| 30 |
'render_callback' => array($this, 'render_tab3'),
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
),
|
| 32 |
'tab4' => array(
|
| 33 |
'title' => __('WP Version Info', 'all-in-one-wp-security-and-firewall'),
|
|
@@ -39,6 +43,7 @@ class AIOWPSecurity_Settings_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 39 |
),
|
| 40 |
)
|
| 41 |
);
|
|
|
|
| 42 |
}
|
| 43 |
|
| 44 |
/*
|
|
@@ -55,6 +60,20 @@ class AIOWPSecurity_Settings_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 55 |
echo '</h2>';
|
| 56 |
}
|
| 57 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 58 |
/*
|
| 59 |
* The menu rendering goes here
|
| 60 |
*/
|
|
@@ -276,8 +295,8 @@ class AIOWPSecurity_Settings_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 276 |
<tr valign="top">
|
| 277 |
<th scope="row"><?php _e('Enable Debug', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 278 |
<td>
|
| 279 |
-
<input name="aiowps_enable_debug" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_debug')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 280 |
-
<
|
| 281 |
</td>
|
| 282 |
</tr>
|
| 283 |
</table>
|
|
@@ -288,12 +307,15 @@ class AIOWPSecurity_Settings_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 288 |
<?php
|
| 289 |
}
|
| 290 |
|
| 291 |
-
|
| 292 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 293 |
global $aio_wp_security;
|
| 294 |
|
| 295 |
-
|
| 296 |
-
$home_path = get_home_path();
|
| 297 |
$htaccess_path = $home_path . '.htaccess';
|
| 298 |
|
| 299 |
if(isset($_POST['aiowps_save_htaccess']))//Do form submission tasks
|
|
@@ -383,7 +405,7 @@ class AIOWPSecurity_Settings_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 383 |
</div>
|
| 384 |
<?php
|
| 385 |
$blog_id = get_current_blog_id();
|
| 386 |
-
if (
|
| 387 |
{
|
| 388 |
//Hide config settings if MS and not main site
|
| 389 |
AIOWPSecurity_Utility::display_multisite_message();
|
|
@@ -407,7 +429,7 @@ class AIOWPSecurity_Settings_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 407 |
<?php wp_nonce_field('aiowpsec-restore-htaccess-nonce'); ?>
|
| 408 |
<table class="form-table">
|
| 409 |
<tr valign="top">
|
| 410 |
-
<th scope="row"><?php _e('.htaccess file to restore from', 'all-in-one-wp-security-and-firewall')
|
| 411 |
<td>
|
| 412 |
<input type="button" id="aiowps_htaccess_file_button" name="aiowps_htaccess_file_button" class="button rbutton" value="<?php _e('Select Your htaccess File', 'all-in-one-wp-security-and-firewall'); ?>" />
|
| 413 |
<input name="aiowps_htaccess_file" type="text" id="aiowps_htaccess_file" value="" size="80" />
|
|
@@ -484,7 +506,7 @@ class AIOWPSecurity_Settings_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 484 |
</div>
|
| 485 |
<?php
|
| 486 |
$blog_id = get_current_blog_id();
|
| 487 |
-
if (
|
| 488 |
{
|
| 489 |
//Hide config settings if MS and not main site
|
| 490 |
AIOWPSecurity_Utility::display_multisite_message();
|
|
@@ -509,7 +531,7 @@ class AIOWPSecurity_Settings_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 509 |
<?php wp_nonce_field('aiowpsec-restore-wp-config-nonce'); ?>
|
| 510 |
<table class="form-table">
|
| 511 |
<tr valign="top">
|
| 512 |
-
<th scope="row"><?php _e('wp-config file to restore from', 'all-in-one-wp-security-and-firewall')
|
| 513 |
<td>
|
| 514 |
<input type="button" id="aiowps_wp_config_file_button" name="aiowps_wp_config_file_button" class="button rbutton" value="<?php _e('Select Your wp-config File', 'all-in-one-wp-security-and-firewall'); ?>" />
|
| 515 |
<input name="aiowps_wp_config_file" type="text" id="aiowps_wp_config_file" value="" size="80" />
|
|
@@ -538,8 +560,59 @@ class AIOWPSecurity_Settings_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 538 |
} //End if statement
|
| 539 |
}
|
| 540 |
|
| 541 |
-
|
| 542 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 543 |
global $aio_wp_security;
|
| 544 |
global $aiowps_feature_mgr;
|
| 545 |
|
|
@@ -558,7 +631,7 @@ class AIOWPSecurity_Settings_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 558 |
$aiowps_feature_mgr->check_feature_status_and_recalculate_points();
|
| 559 |
|
| 560 |
$this->show_msg_settings_updated();
|
| 561 |
-
|
| 562 |
?>
|
| 563 |
<h2><?php _e('WP Generator Meta Tag & Version Info', 'all-in-one-wp-security-and-firewall')?></h2>
|
| 564 |
<div class="aio_blue_box">
|
|
@@ -588,8 +661,8 @@ class AIOWPSecurity_Settings_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 588 |
<tr valign="top">
|
| 589 |
<th scope="row"><?php _e('Remove WP Generator Meta Info', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 590 |
<td>
|
| 591 |
-
<input name="aiowps_remove_wp_generator_meta_info" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_remove_wp_generator_meta_info')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 592 |
-
<
|
| 593 |
</td>
|
| 594 |
</tr>
|
| 595 |
</table>
|
|
@@ -599,7 +672,6 @@ class AIOWPSecurity_Settings_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 599 |
<?php
|
| 600 |
}
|
| 601 |
|
| 602 |
-
|
| 603 |
public function render_tab5() {
|
| 604 |
global $aio_wp_security;
|
| 605 |
|
|
@@ -723,48 +795,54 @@ class AIOWPSecurity_Settings_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 723 |
</div>
|
| 724 |
|
| 725 |
<div class="postbox">
|
| 726 |
-
|
| 727 |
-
|
| 728 |
-
|
| 729 |
-
|
| 730 |
-
|
| 731 |
-
|
| 732 |
-
|
| 733 |
-
|
| 734 |
-
|
| 735 |
-
|
| 736 |
-
|
| 737 |
-
|
| 738 |
<div class="postbox">
|
| 739 |
-
|
| 740 |
-
|
| 741 |
-
|
| 742 |
-
|
| 743 |
-
|
| 744 |
-
|
| 745 |
-
|
| 746 |
-
|
| 747 |
-
|
| 748 |
-
|
| 749 |
-
|
| 750 |
-
|
| 751 |
-
|
| 752 |
-
|
| 753 |
-
|
| 754 |
-
|
| 755 |
-
|
| 756 |
-
|
| 757 |
-
|
| 758 |
-
|
| 759 |
-
|
| 760 |
-
|
| 761 |
-
|
| 762 |
-
|
| 763 |
-
|
| 764 |
-
|
| 765 |
-
|
| 766 |
-
|
| 767 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 768 |
}
|
| 769 |
|
| 770 |
function check_if_wp_config_contents($wp_file)
|
| 15 |
}
|
| 16 |
|
| 17 |
public function set_menu_tabs() {
|
| 18 |
+
$menu_tabs = apply_filters('aiowpsecurity_setting_tabs',
|
| 19 |
array(
|
| 20 |
'tab1' => array(
|
| 21 |
'title' => __('General Settings', 'all-in-one-wp-security-and-firewall'),
|
| 28 |
'tab3' => array(
|
| 29 |
'title' => 'wp-config.php '.__('File', 'all-in-one-wp-security-and-firewall'),
|
| 30 |
'render_callback' => array($this, 'render_tab3'),
|
| 31 |
+
),
|
| 32 |
+
'delete-plugin-settings' => array(
|
| 33 |
+
'title' => __('Delete Plugin Settings', 'all-in-one-wp-security-and-firewall'),
|
| 34 |
+
'render_callback' => array($this, 'render_delete_plugin_settings_tab'),
|
| 35 |
),
|
| 36 |
'tab4' => array(
|
| 37 |
'title' => __('WP Version Info', 'all-in-one-wp-security-and-firewall'),
|
| 43 |
),
|
| 44 |
)
|
| 45 |
);
|
| 46 |
+
$this->menu_tabs = array_filter($menu_tabs, array($this, 'should_display_tab'));
|
| 47 |
}
|
| 48 |
|
| 49 |
/*
|
| 60 |
echo '</h2>';
|
| 61 |
}
|
| 62 |
|
| 63 |
+
/**
|
| 64 |
+
* Decide whether to display the tab for the given tab information.
|
| 65 |
+
*
|
| 66 |
+
* @param array $tab_info tab information array cotaining element keys like title, render_callback and display_condition_callback etc..
|
| 67 |
+
* @return boolean The tab information array contains element keys such as title, render_callback, and display_condition_callback, among others.
|
| 68 |
+
*/
|
| 69 |
+
private function should_display_tab($tab_info) {
|
| 70 |
+
if (!empty($tab_info['display_condition_callback']) && is_callable($tab_info['display_condition_callback'])) {
|
| 71 |
+
return call_user_func($tab_info['display_condition_callback']);
|
| 72 |
+
} else {
|
| 73 |
+
return true;
|
| 74 |
+
}
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
/*
|
| 78 |
* The menu rendering goes here
|
| 79 |
*/
|
| 295 |
<tr valign="top">
|
| 296 |
<th scope="row"><?php _e('Enable Debug', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 297 |
<td>
|
| 298 |
+
<input id="aiowps_enable_debug" name="aiowps_enable_debug" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_debug')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 299 |
+
<label for="aiowps_enable_debug" class="description"><?php _e('Check this if you want to enable debug. You should keep this option disabled after you have finished debugging the issue.', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 300 |
</td>
|
| 301 |
</tr>
|
| 302 |
</table>
|
| 307 |
<?php
|
| 308 |
}
|
| 309 |
|
| 310 |
+
/**
|
| 311 |
+
* Render tab 2 content.
|
| 312 |
+
*
|
| 313 |
+
* @return void
|
| 314 |
+
*/
|
| 315 |
+
private function render_tab2() {
|
| 316 |
global $aio_wp_security;
|
| 317 |
|
| 318 |
+
$home_path = AIOWPSecurity_Utility_File::get_home_path();
|
|
|
|
| 319 |
$htaccess_path = $home_path . '.htaccess';
|
| 320 |
|
| 321 |
if(isset($_POST['aiowps_save_htaccess']))//Do form submission tasks
|
| 405 |
</div>
|
| 406 |
<?php
|
| 407 |
$blog_id = get_current_blog_id();
|
| 408 |
+
if (is_multisite() && !is_main_site( $blog_id ))
|
| 409 |
{
|
| 410 |
//Hide config settings if MS and not main site
|
| 411 |
AIOWPSecurity_Utility::display_multisite_message();
|
| 429 |
<?php wp_nonce_field('aiowpsec-restore-htaccess-nonce'); ?>
|
| 430 |
<table class="form-table">
|
| 431 |
<tr valign="top">
|
| 432 |
+
<th scope="row"><label for="aiowps_htaccess_file_button"><?php _e('.htaccess file to restore from', 'all-in-one-wp-security-and-firewall')?></label>:</th>
|
| 433 |
<td>
|
| 434 |
<input type="button" id="aiowps_htaccess_file_button" name="aiowps_htaccess_file_button" class="button rbutton" value="<?php _e('Select Your htaccess File', 'all-in-one-wp-security-and-firewall'); ?>" />
|
| 435 |
<input name="aiowps_htaccess_file" type="text" id="aiowps_htaccess_file" value="" size="80" />
|
| 506 |
</div>
|
| 507 |
<?php
|
| 508 |
$blog_id = get_current_blog_id();
|
| 509 |
+
if (is_multisite() && !is_main_site( $blog_id ))
|
| 510 |
{
|
| 511 |
//Hide config settings if MS and not main site
|
| 512 |
AIOWPSecurity_Utility::display_multisite_message();
|
| 531 |
<?php wp_nonce_field('aiowpsec-restore-wp-config-nonce'); ?>
|
| 532 |
<table class="form-table">
|
| 533 |
<tr valign="top">
|
| 534 |
+
<th scope="row"><label for="aiowps_wp_config_file_button"><?php _e('wp-config file to restore from', 'all-in-one-wp-security-and-firewall')?></label>:</th>
|
| 535 |
<td>
|
| 536 |
<input type="button" id="aiowps_wp_config_file_button" name="aiowps_wp_config_file_button" class="button rbutton" value="<?php _e('Select Your wp-config File', 'all-in-one-wp-security-and-firewall'); ?>" />
|
| 537 |
<input name="aiowps_wp_config_file" type="text" id="aiowps_wp_config_file" value="" size="80" />
|
| 560 |
} //End if statement
|
| 561 |
}
|
| 562 |
|
| 563 |
+
public function render_delete_plugin_settings_tab() {
|
| 564 |
+
global $aio_wp_security;
|
| 565 |
+
|
| 566 |
+
if (isset($_POST['aiowpsec_save_delete_plugin_settings']))
|
| 567 |
+
{
|
| 568 |
+
$nonce=$_REQUEST['_wpnonce'];
|
| 569 |
+
if (!wp_verify_nonce($nonce, 'aiowpsec-delete-plugin-settings'))
|
| 570 |
+
{
|
| 571 |
+
$aio_wp_security->debug_logger->log_debug("Nonce check failed on manage delete plugin settings save.",4);
|
| 572 |
+
die("Nonce check failed on manage delete plugin settings save.");
|
| 573 |
+
}
|
| 574 |
+
|
| 575 |
+
//Save settings
|
| 576 |
+
$aio_wp_security->configs->set_value('aiowps_on_uninstall_delete_db_tables', isset($_POST['aiowps_on_uninstall_delete_db_tables']) ? '1' : '');
|
| 577 |
+
$aio_wp_security->configs->set_value('aiowps_on_uninstall_delete_configs', isset($_POST['aiowps_on_uninstall_delete_configs']) ? '1' : '');
|
| 578 |
+
$aio_wp_security->configs->save_config();
|
| 579 |
+
|
| 580 |
+
$this->show_msg_updated(__('Manage delete plugin settings saved.', 'all-in-one-wp-security-and-firewall'));
|
| 581 |
+
|
| 582 |
+
}
|
| 583 |
+
?>
|
| 584 |
+
<div class="postbox">
|
| 585 |
+
<h3 class="hndle"><label for="title"><?php _e('Manage delete plugin tasks', 'all-in-one-wp-security-and-firewall'); ?></label></h3>
|
| 586 |
+
<div class="inside">
|
| 587 |
+
<form action="" method="POST">
|
| 588 |
+
<?php wp_nonce_field('aiowpsec-delete-plugin-settings'); ?>
|
| 589 |
+
|
| 590 |
+
<table class="form-table">
|
| 591 |
+
<tr valign="top">
|
| 592 |
+
<th scope="row"><?php _e('Delete database tables', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 593 |
+
<td>
|
| 594 |
+
<input id="aiowps_on_uninstall_delete_db_tables" name="aiowps_on_uninstall_delete_db_tables" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_on_uninstall_delete_db_tables')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 595 |
+
<label for="aiowps_on_uninstall_delete_db_tables" class="description"><?php _e('Check this if you want to remove database tables when the plugin is deactivated.', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 596 |
+
</td>
|
| 597 |
+
</tr>
|
| 598 |
+
<tr valign="top">
|
| 599 |
+
<th scope="row"><?php _e('Delete settings', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 600 |
+
<td>
|
| 601 |
+
<input id="aiowps_on_uninstall_delete_configs" name="aiowps_on_uninstall_delete_configs" type="checkbox"<?php checked($aio_wp_security->configs->get_value('aiowps_on_uninstall_delete_configs'), '1'); ?> value="1"/>
|
| 602 |
+
<label for="aiowps_on_uninstall_delete_configs" class="description"><?php echo __('Check this if you want to remove all plugin settings when uninstalling the plugin.', 'all-in-one-wp-security-and-firewall').' '.__('It will also remove all custom htaccess rules that were added by this plugin.', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 603 |
+
</td>
|
| 604 |
+
</tr>
|
| 605 |
+
</table>
|
| 606 |
+
|
| 607 |
+
<div class="submit">
|
| 608 |
+
<input type="submit" class="button-primary" name="aiowpsec_save_delete_plugin_settings" value="<?php _e('Save Settings', 'all-in-one-wp-security-and-firewall'); ?>" />
|
| 609 |
+
</div>
|
| 610 |
+
</form>
|
| 611 |
+
</div></div>
|
| 612 |
+
<?php
|
| 613 |
+
}
|
| 614 |
+
|
| 615 |
+
public function render_tab4() {
|
| 616 |
global $aio_wp_security;
|
| 617 |
global $aiowps_feature_mgr;
|
| 618 |
|
| 631 |
$aiowps_feature_mgr->check_feature_status_and_recalculate_points();
|
| 632 |
|
| 633 |
$this->show_msg_settings_updated();
|
| 634 |
+
}
|
| 635 |
?>
|
| 636 |
<h2><?php _e('WP Generator Meta Tag & Version Info', 'all-in-one-wp-security-and-firewall')?></h2>
|
| 637 |
<div class="aio_blue_box">
|
| 661 |
<tr valign="top">
|
| 662 |
<th scope="row"><?php _e('Remove WP Generator Meta Info', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 663 |
<td>
|
| 664 |
+
<input id="aiowps_remove_wp_generator_meta_info" name="aiowps_remove_wp_generator_meta_info" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_remove_wp_generator_meta_info')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 665 |
+
<label for="aiowps_remove_wp_generator_meta_info" class="description"><?php _e('Check this if you want to remove the version and meta info produced by WP from all pages', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 666 |
</td>
|
| 667 |
</tr>
|
| 668 |
</table>
|
| 672 |
<?php
|
| 673 |
}
|
| 674 |
|
|
|
|
| 675 |
public function render_tab5() {
|
| 676 |
global $aio_wp_security;
|
| 677 |
|
| 795 |
</div>
|
| 796 |
|
| 797 |
<div class="postbox">
|
| 798 |
+
<h3 class="hndle"><label for="title"><?php _e('Export AIOWPS Settings', 'all-in-one-wp-security-and-firewall'); ?></label></h3>
|
| 799 |
+
<div class="inside">
|
| 800 |
+
<form action="" method="POST">
|
| 801 |
+
<?php wp_nonce_field('aiowpsec-export-settings-nonce'); ?>
|
| 802 |
+
<table class="form-table">
|
| 803 |
+
<tr valign="top">
|
| 804 |
+
<span class="description"><?php _e('To export your All In One WP Security & Firewall settings click the button below.', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 805 |
+
</tr>
|
| 806 |
+
</table>
|
| 807 |
+
<input type="submit" name="aiowps_export_settings" value="<?php _e('Export AIOWPS Settings', 'all-in-one-wp-security-and-firewall')?>" class="button-primary" />
|
| 808 |
+
</form>
|
| 809 |
+
</div></div>
|
| 810 |
<div class="postbox">
|
| 811 |
+
<h3 class="hndle"><label for="title"><?php _e('Import AIOWPS Settings', 'all-in-one-wp-security-and-firewall'); ?></label></h3>
|
| 812 |
+
<div class="inside">
|
| 813 |
+
<form action="" method="POST">
|
| 814 |
+
<?php wp_nonce_field('aiowpsec-import-settings-nonce'); ?>
|
| 815 |
+
<table class="form-table">
|
| 816 |
+
<tr valign="top">
|
| 817 |
+
<span class="description"><?php _e('Use this section to import your All In One WP Security & Firewall settings from a file. Alternatively, copy/paste the contents of your import file into the textarea below.', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 818 |
+
<th scope="row">
|
| 819 |
+
<label for="aiowps_import_settings_file_button">
|
| 820 |
+
<?php _e('Import File', 'all-in-one-wp-security-and-firewall'); ?>:
|
| 821 |
+
</label>
|
| 822 |
+
</th>
|
| 823 |
+
<td>
|
| 824 |
+
<input type="button" id="aiowps_import_settings_file_button" name="aiowps_import_settings_file_button" class="button rbutton" value="<?php _e('Select Your Import Settings File', 'all-in-one-wp-security-and-firewall'); ?>" />
|
| 825 |
+
<input name="aiowps_import_settings_file" type="text" id="aiowps_import_settings_file" value="" size="80" />
|
| 826 |
+
<p class="description">
|
| 827 |
+
<?php
|
| 828 |
+
_e('After selecting your file, click the button below to apply the settings to your site.', 'all-in-one-wp-security-and-firewall');
|
| 829 |
+
?>
|
| 830 |
+
</p>
|
| 831 |
+
</td>
|
| 832 |
+
</tr>
|
| 833 |
+
<tr valign="top">
|
| 834 |
+
<th scope="row">
|
| 835 |
+
<label for="aiowps_import_settings_text"><?php _e('Copy/Paste Import Data', 'all-in-one-wp-security-and-firewall'); ?>:</label>
|
| 836 |
+
</th>
|
| 837 |
+
<td>
|
| 838 |
+
<textarea name="aiowps_import_settings_text" id="aiowps_import_settings_text" style="width:80%;height:140px;"></textarea>
|
| 839 |
+
</td>
|
| 840 |
+
</tr>
|
| 841 |
+
</table>
|
| 842 |
+
<input type="submit" name="aiowps_import_settings" value="<?php _e('Import AIOWPS Settings', 'all-in-one-wp-security-and-firewall')?>" class="button-primary" />
|
| 843 |
+
</form>
|
| 844 |
+
</div></div>
|
| 845 |
+
<?php
|
| 846 |
}
|
| 847 |
|
| 848 |
function check_if_wp_config_contents($wp_file)
|
|
@@ -89,9 +89,26 @@ class AIOWPSecurity_Spam_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 89 |
$aio_wp_security->configs->set_value('aiowps_enable_comment_captcha',isset($_POST["aiowps_enable_comment_captcha"])?'1':'');
|
| 90 |
$aio_wp_security->configs->set_value('aiowps_enable_spambot_blocking',isset($_POST["aiowps_enable_spambot_blocking"])?'1':'');
|
| 91 |
|
| 92 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 93 |
$aio_wp_security->configs->save_config();
|
| 94 |
|
|
|
|
|
|
|
| 95 |
//Recalculate points after the feature status/options have been altered
|
| 96 |
$aiowps_feature_mgr->check_feature_status_and_recalculate_points();
|
| 97 |
|
|
@@ -130,8 +147,8 @@ class AIOWPSecurity_Spam_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 130 |
<tr valign="top">
|
| 131 |
<th scope="row"><?php _e('Enable Captcha On Comment Forms', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 132 |
<td>
|
| 133 |
-
<input name="aiowps_enable_comment_captcha" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_comment_captcha')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 134 |
-
<
|
| 135 |
</td>
|
| 136 |
</tr>
|
| 137 |
</table>
|
|
@@ -151,7 +168,7 @@ class AIOWPSecurity_Spam_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 151 |
//Display security info badge
|
| 152 |
$aiowps_feature_mgr->output_feature_details_badge("block-spambots");
|
| 153 |
$blog_id = get_current_blog_id();
|
| 154 |
-
if (
|
| 155 |
{
|
| 156 |
//Hide config settings if MS and not main site
|
| 157 |
AIOWPSecurity_Utility::display_multisite_message();
|
|
@@ -163,8 +180,8 @@ class AIOWPSecurity_Spam_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 163 |
<tr valign="top">
|
| 164 |
<th scope="row"><?php _e('Block Spambots From Posting Comments', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 165 |
<td>
|
| 166 |
-
<input name="aiowps_enable_spambot_blocking" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_spambot_blocking')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 167 |
-
<
|
| 168 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 169 |
<div class="aiowps_more_info_body">
|
| 170 |
<?php
|
|
@@ -180,13 +197,51 @@ class AIOWPSecurity_Spam_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 180 |
<?php } //End if statement ?>
|
| 181 |
</div></div>
|
| 182 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 183 |
<input type="submit" name="aiowps_apply_comment_spam_prevention_settings" value="<?php _e('Save Settings', 'all-in-one-wp-security-and-firewall')?>" class="button-primary" />
|
| 184 |
</form>
|
| 185 |
<?php
|
| 186 |
}
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 190 |
global $aio_wp_security;
|
| 191 |
global $aiowps_feature_mgr;
|
| 192 |
include_once 'wp-security-list-comment-spammer-ip.php'; //For rendering the AIOWPSecurity_List_Table in tab2
|
|
@@ -330,13 +385,13 @@ class AIOWPSecurity_Spam_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 330 |
<tr valign="top">
|
| 331 |
<th scope="row"><?php _e('Enable Auto Block of SPAM Comment IPs', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 332 |
<td>
|
| 333 |
-
<input name="aiowps_enable_autoblock_spam_ip" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_autoblock_spam_ip')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 334 |
-
<
|
| 335 |
</td>
|
| 336 |
</tr>
|
| 337 |
<tr valign="top">
|
| 338 |
-
<th scope="row"><?php _e('Minimum number of SPAM comments', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 339 |
-
<td><input type="text" size="5" name="aiowps_spam_ip_min_comments_block" value="<?php echo $aio_wp_security->configs->get_value('aiowps_spam_ip_min_comments_block'); ?>" />
|
| 340 |
<span class="description"><?php _e('Specify the minimum number of SPAM comments for an IP address before it is permanently blocked.', 'all-in-one-wp-security-and-firewall');?></span>
|
| 341 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 342 |
<div class="aiowps_more_info_body">
|
|
@@ -377,8 +432,8 @@ class AIOWPSecurity_Spam_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 377 |
<?php wp_nonce_field('aiowpsec-spammer-ip-list-nonce'); ?>
|
| 378 |
<table class="form-table">
|
| 379 |
<tr valign="top">
|
| 380 |
-
<th scope="row"><?php _e('Minimum number of SPAM comments per IP', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 381 |
-
<td><input type="text" size="5" name="aiowps_spam_ip_min_comments" value="<?php echo $aio_wp_security->configs->get_value('aiowps_spam_ip_min_comments'); ?>" />
|
| 382 |
<span class="description"><?php _e('This field allows you to list only those IP addresses which have been used to post X or more SPAM comments.', 'all-in-one-wp-security-and-firewall');?></span>
|
| 383 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 384 |
<div class="aiowps_more_info_body">
|
|
@@ -398,7 +453,7 @@ class AIOWPSecurity_Spam_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 398 |
<h3 class="hndle"><label for="title"><?php _e('SPAMMER IP Address Results', 'all-in-one-wp-security-and-firewall'); ?></label></h3>
|
| 399 |
<div class="inside">
|
| 400 |
<?php
|
| 401 |
-
if (
|
| 402 |
{
|
| 403 |
echo '<div class="aio_yellow_box">';
|
| 404 |
echo '<p>'.__('The plugin has detected that you are using a Multi-Site WordPress installation.', 'all-in-one-wp-security-and-firewall').'</p>
|
|
@@ -410,7 +465,7 @@ class AIOWPSecurity_Spam_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 410 |
$spammer_ip_list->prepare_items();
|
| 411 |
//echo "put table of locked entries here";
|
| 412 |
?>
|
| 413 |
-
|
| 414 |
<!-- For plugins, we also need to ensure that the form posts back to our current page -->
|
| 415 |
<input type="hidden" name="page" value="<?php echo esc_attr($_REQUEST['page']); ?>" />
|
| 416 |
<input type="hidden" name="tab" value="<?php echo esc_attr($_REQUEST['tab']); ?>" />
|
|
@@ -468,10 +523,10 @@ class AIOWPSecurity_Spam_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 468 |
?>
|
| 469 |
<table class="form-table">
|
| 470 |
<tr valign="top">
|
| 471 |
-
<th scope="row"><?php _e('Enable Captcha On BuddyPress Registration Form', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 472 |
<td>
|
| 473 |
-
<input name="aiowps_enable_bp_register_captcha" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_bp_register_captcha')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 474 |
-
<
|
| 475 |
</td>
|
| 476 |
</tr>
|
| 477 |
</table>
|
|
@@ -530,10 +585,10 @@ class AIOWPSecurity_Spam_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 530 |
?>
|
| 531 |
<table class="form-table">
|
| 532 |
<tr valign="top">
|
| 533 |
-
<th scope="row"><?php _e('Enable Captcha On BBPress New Topic Form', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 534 |
<td>
|
| 535 |
-
<input name="aiowps_enable_bbp_new_topic_captcha" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_bbp_new_topic_captcha')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 536 |
-
<
|
| 537 |
</td>
|
| 538 |
</tr>
|
| 539 |
</table>
|
|
@@ -546,4 +601,4 @@ class AIOWPSecurity_Spam_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 546 |
}
|
| 547 |
}
|
| 548 |
|
| 549 |
-
} //end class
|
| 89 |
$aio_wp_security->configs->set_value('aiowps_enable_comment_captcha',isset($_POST["aiowps_enable_comment_captcha"])?'1':'');
|
| 90 |
$aio_wp_security->configs->set_value('aiowps_enable_spambot_blocking',isset($_POST["aiowps_enable_spambot_blocking"])?'1':'');
|
| 91 |
|
| 92 |
+
$aio_wp_security->configs->set_value('aiowps_enable_trash_spam_comments', isset($_POST['aiowps_enable_trash_spam_comments']) ? '1' : '');
|
| 93 |
+
$aiowps_trash_spam_comments_after_days = '';
|
| 94 |
+
if (isset($_POST['aiowps_trash_spam_comments_after_days'])) {
|
| 95 |
+
if (!empty($_POST['aiowps_trash_spam_comments_after_days'])) {
|
| 96 |
+
$aiowps_trash_spam_comments_after_days = sanitize_text_field($_POST['aiowps_trash_spam_comments_after_days']);
|
| 97 |
+
}
|
| 98 |
+
if (isset($_POST['aiowps_enable_trash_spam_comments']) && !is_numeric($aiowps_trash_spam_comments_after_days)) {
|
| 99 |
+
$error = __('You entered a non numeric value for the "move spam comments to trash after number of days" field.','all-in-one-wp-security-and-firewall').' '.__('It has been set to the default value.','all-in-one-wp-security-and-firewall');
|
| 100 |
+
$aiowps_trash_spam_comments_after_days = '14';//Set it to the default value for this field
|
| 101 |
+
$this->show_msg_error(__('Attention!','all-in-one-wp-security-and-firewall').' '.htmlspecialchars($error));
|
| 102 |
+
}
|
| 103 |
+
$aiowps_trash_spam_comments_after_days = absint($aiowps_trash_spam_comments_after_days);
|
| 104 |
+
$aio_wp_security->configs->set_value('aiowps_trash_spam_comments_after_days', $aiowps_trash_spam_comments_after_days);
|
| 105 |
+
}
|
| 106 |
+
|
| 107 |
+
//Commit the config settings
|
| 108 |
$aio_wp_security->configs->save_config();
|
| 109 |
|
| 110 |
+
AIOWPSecurity_Comment::trash_spam_comments();
|
| 111 |
+
|
| 112 |
//Recalculate points after the feature status/options have been altered
|
| 113 |
$aiowps_feature_mgr->check_feature_status_and_recalculate_points();
|
| 114 |
|
| 147 |
<tr valign="top">
|
| 148 |
<th scope="row"><?php _e('Enable Captcha On Comment Forms', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 149 |
<td>
|
| 150 |
+
<input id="aiowps_enable_comment_captcha" name="aiowps_enable_comment_captcha" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_comment_captcha')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 151 |
+
<label for="aiowps_enable_comment_captcha" class="description"><?php _e('Check this if you want to insert a captcha field on the comment forms', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 152 |
</td>
|
| 153 |
</tr>
|
| 154 |
</table>
|
| 168 |
//Display security info badge
|
| 169 |
$aiowps_feature_mgr->output_feature_details_badge("block-spambots");
|
| 170 |
$blog_id = get_current_blog_id();
|
| 171 |
+
if (is_multisite() && !is_main_site( $blog_id ))
|
| 172 |
{
|
| 173 |
//Hide config settings if MS and not main site
|
| 174 |
AIOWPSecurity_Utility::display_multisite_message();
|
| 180 |
<tr valign="top">
|
| 181 |
<th scope="row"><?php _e('Block Spambots From Posting Comments', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 182 |
<td>
|
| 183 |
+
<input id="aiowps_enable_spambot_blocking" name="aiowps_enable_spambot_blocking" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_spambot_blocking')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 184 |
+
<label for="aiowps_enable_spambot_blocking" class="description"><?php _e('Check this if you want to apply a firewall rule which will block comments originating from spambots.', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 185 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 186 |
<div class="aiowps_more_info_body">
|
| 187 |
<?php
|
| 197 |
<?php } //End if statement ?>
|
| 198 |
</div></div>
|
| 199 |
|
| 200 |
+
<div class="postbox">
|
| 201 |
+
<h3 class="hndle"><label for="title"><?php _e('Comment Processing', 'all-in-one-wp-security-and-firewall'); ?></label></h3>
|
| 202 |
+
<div class="inside">
|
| 203 |
+
<table class="form-table">
|
| 204 |
+
<tr valign="top">
|
| 205 |
+
<th scope="row">
|
| 206 |
+
<label for="aiowps_trash_spam_comments_after_days">
|
| 207 |
+
<?php _e('Trash spam comments', 'all-in-one-wp-security-and-firewall'); ?>:
|
| 208 |
+
</label>
|
| 209 |
+
</th>
|
| 210 |
+
<td>
|
| 211 |
+
<input name="aiowps_enable_trash_spam_comments" id="aiowps_enable_trash_spam_comments" type="checkbox" <?php checked($aio_wp_security->configs->get_value('aiowps_enable_trash_spam_comments'), 1); ?> value="1"/>
|
| 212 |
+
<?php
|
| 213 |
+
$disbled = '';
|
| 214 |
+
if(!$aio_wp_security->configs->get_value('aiowps_enable_trash_spam_comments')) $disbled = "disabled";
|
| 215 |
+
echo '<label for="aiowps_enable_trash_spam_comments" class="description">';
|
| 216 |
+
printf(
|
| 217 |
+
__('Move spam comments to trash after %s days.', 'all-in-one-wp-security-and-firewall'),
|
| 218 |
+
'</label><input type="number" min="1" max="99" name="aiowps_trash_spam_comments_after_days" value="'.$aio_wp_security->configs->get_value('aiowps_trash_spam_comments_after_days').'" '.$disbled.'><label for="aiowps_enable_trash_spam_comments">'
|
| 219 |
+
);
|
| 220 |
+
echo '</label>';
|
| 221 |
+
?>
|
| 222 |
+
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 223 |
+
<div class="aiowps_more_info_body">
|
| 224 |
+
<?php
|
| 225 |
+
echo '<p class="description">'.__('Enble this feature in order to move the spam comments to trash after given number of days.', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 226 |
+
?>
|
| 227 |
+
</div>
|
| 228 |
+
</td>
|
| 229 |
+
</tr>
|
| 230 |
+
</table>
|
| 231 |
+
</div>
|
| 232 |
+
</div>
|
| 233 |
+
|
| 234 |
<input type="submit" name="aiowps_apply_comment_spam_prevention_settings" value="<?php _e('Save Settings', 'all-in-one-wp-security-and-firewall')?>" class="button-primary" />
|
| 235 |
</form>
|
| 236 |
<?php
|
| 237 |
}
|
| 238 |
+
|
| 239 |
+
/**
|
| 240 |
+
* Renders the submenu's tab2 tab body.
|
| 241 |
+
*
|
| 242 |
+
* @return Void
|
| 243 |
+
*/
|
| 244 |
+
public function render_tab2() {
|
| 245 |
global $aio_wp_security;
|
| 246 |
global $aiowps_feature_mgr;
|
| 247 |
include_once 'wp-security-list-comment-spammer-ip.php'; //For rendering the AIOWPSecurity_List_Table in tab2
|
| 385 |
<tr valign="top">
|
| 386 |
<th scope="row"><?php _e('Enable Auto Block of SPAM Comment IPs', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 387 |
<td>
|
| 388 |
+
<input id="aiowps_enable_autoblock_spam_ip" name="aiowps_enable_autoblock_spam_ip" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_autoblock_spam_ip')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 389 |
+
<label for="aiowps_enable_autoblock_spam_ip" class="description"><?php _e('Check this box if you want this plugin to automatically block IP addresses which submit SPAM comments.', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 390 |
</td>
|
| 391 |
</tr>
|
| 392 |
<tr valign="top">
|
| 393 |
+
<th scope="row"><label for="aiowps_spam_ip_min_comments_block"><?php _e('Minimum number of SPAM comments', 'all-in-one-wp-security-and-firewall')?>:</label></th>
|
| 394 |
+
<td><input id="aiowps_spam_ip_min_comments_block" type="text" size="5" name="aiowps_spam_ip_min_comments_block" value="<?php echo $aio_wp_security->configs->get_value('aiowps_spam_ip_min_comments_block'); ?>" />
|
| 395 |
<span class="description"><?php _e('Specify the minimum number of SPAM comments for an IP address before it is permanently blocked.', 'all-in-one-wp-security-and-firewall');?></span>
|
| 396 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 397 |
<div class="aiowps_more_info_body">
|
| 432 |
<?php wp_nonce_field('aiowpsec-spammer-ip-list-nonce'); ?>
|
| 433 |
<table class="form-table">
|
| 434 |
<tr valign="top">
|
| 435 |
+
<th scope="row"><label for="aiowps_spam_ip_min_comments"><?php _e('Minimum number of SPAM comments per IP', 'all-in-one-wp-security-and-firewall')?>:</label></th>
|
| 436 |
+
<td><input id="aiowps_spam_ip_min_comments" type="text" size="5" name="aiowps_spam_ip_min_comments" value="<?php echo $aio_wp_security->configs->get_value('aiowps_spam_ip_min_comments'); ?>" />
|
| 437 |
<span class="description"><?php _e('This field allows you to list only those IP addresses which have been used to post X or more SPAM comments.', 'all-in-one-wp-security-and-firewall');?></span>
|
| 438 |
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 439 |
<div class="aiowps_more_info_body">
|
| 453 |
<h3 class="hndle"><label for="title"><?php _e('SPAMMER IP Address Results', 'all-in-one-wp-security-and-firewall'); ?></label></h3>
|
| 454 |
<div class="inside">
|
| 455 |
<?php
|
| 456 |
+
if (is_multisite() && get_current_blog_id() != 1)
|
| 457 |
{
|
| 458 |
echo '<div class="aio_yellow_box">';
|
| 459 |
echo '<p>'.__('The plugin has detected that you are using a Multi-Site WordPress installation.', 'all-in-one-wp-security-and-firewall').'</p>
|
| 465 |
$spammer_ip_list->prepare_items();
|
| 466 |
//echo "put table of locked entries here";
|
| 467 |
?>
|
| 468 |
+
<form id="tables-filter" method="get">
|
| 469 |
<!-- For plugins, we also need to ensure that the form posts back to our current page -->
|
| 470 |
<input type="hidden" name="page" value="<?php echo esc_attr($_REQUEST['page']); ?>" />
|
| 471 |
<input type="hidden" name="tab" value="<?php echo esc_attr($_REQUEST['tab']); ?>" />
|
| 523 |
?>
|
| 524 |
<table class="form-table">
|
| 525 |
<tr valign="top">
|
| 526 |
+
<th scope="row"><label for="aiowps_enable_bp_register_captcha"><?php _e('Enable Captcha On BuddyPress Registration Form', 'all-in-one-wp-security-and-firewall')?>:</label></th>
|
| 527 |
<td>
|
| 528 |
+
<input id="aiowps_enable_bp_register_captcha" name="aiowps_enable_bp_register_captcha" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_bp_register_captcha')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 529 |
+
<label for="aiowps_enable_bp_register_captcha"><?php _e('Check this if you want to insert a captcha field on the BuddyPress registration forms', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 530 |
</td>
|
| 531 |
</tr>
|
| 532 |
</table>
|
| 585 |
?>
|
| 586 |
<table class="form-table">
|
| 587 |
<tr valign="top">
|
| 588 |
+
<th scope="row"><label for="aiowps_enable_bbp_new_topic_captcha"><?php _e('Enable Captcha On BBPress New Topic Form', 'all-in-one-wp-security-and-firewall')?>:</label></th>
|
| 589 |
<td>
|
| 590 |
+
<input id="aiowps_enable_bbp_new_topic_captcha" name="aiowps_enable_bbp_new_topic_captcha" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_bbp_new_topic_captcha')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 591 |
+
<label for="aiowps_enable_bbp_new_topic_captcha"><?php _e('Check this if you want to insert a captcha field on the BBPress new topic forms', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 592 |
</td>
|
| 593 |
</tr>
|
| 594 |
</table>
|
| 601 |
}
|
| 602 |
}
|
| 603 |
|
| 604 |
+
} //end class
|
|
@@ -0,0 +1,239 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
|
| 3 |
+
if (!defined('ABSPATH')) {
|
| 4 |
+
exit; // Exit if accessed directly
|
| 5 |
+
}
|
| 6 |
+
|
| 7 |
+
class AIOWPSecurity_Tools_Menu extends AIOWPSecurity_Admin_Menu {
|
| 8 |
+
|
| 9 |
+
/**
|
| 10 |
+
* All tab keys, titles and render callbacks.
|
| 11 |
+
*
|
| 12 |
+
* @var Array
|
| 13 |
+
*/
|
| 14 |
+
protected $menu_tabs;
|
| 15 |
+
|
| 16 |
+
/**
|
| 17 |
+
* Renders the submenu's current tab page.
|
| 18 |
+
*
|
| 19 |
+
* @return Void
|
| 20 |
+
*/
|
| 21 |
+
public function __construct() {
|
| 22 |
+
$this->render_menu_page();
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
/**
|
| 26 |
+
* Populates $menu_tabs array.
|
| 27 |
+
*
|
| 28 |
+
* @return Void
|
| 29 |
+
*/
|
| 30 |
+
private function set_menu_tabs() {
|
| 31 |
+
$this->menu_tabs = apply_filters('aiowpsecurity_tools_tabs',
|
| 32 |
+
array(
|
| 33 |
+
'whois-lookup' => array(
|
| 34 |
+
'title' => __('WHOIS Lookup', 'all-in-one-wp-security-and-firewall'),
|
| 35 |
+
'render_callback' => array($this, 'render_whois_lookup_tab'),
|
| 36 |
+
)
|
| 37 |
+
)
|
| 38 |
+
);
|
| 39 |
+
}
|
| 40 |
+
|
| 41 |
+
/**
|
| 42 |
+
* Renders the submenu's tabs as nav items.
|
| 43 |
+
*
|
| 44 |
+
* @return Void
|
| 45 |
+
*/
|
| 46 |
+
private function render_menu_tabs() {
|
| 47 |
+
$current_tab = $this->get_current_tab();
|
| 48 |
+
|
| 49 |
+
echo '<h2 class="nav-tab-wrapper">';
|
| 50 |
+
foreach ($this->menu_tabs as $tab_key => $tab_info) {
|
| 51 |
+
$active = $current_tab == $tab_key ? 'nav-tab-active' : '';
|
| 52 |
+
echo '<a class="nav-tab '.$active.'" href="?page='.AIOWPSEC_TOOLS_MENU_SLUG.'&tab='.$tab_key.'">'.esc_html($tab_info['title']).'</a>';
|
| 53 |
+
}
|
| 54 |
+
echo '</h2>';
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
/**
|
| 58 |
+
* Renders the submenu's current tab page.
|
| 59 |
+
*
|
| 60 |
+
* @return Void
|
| 61 |
+
*/
|
| 62 |
+
private function render_menu_page() {
|
| 63 |
+
echo '<div class="wrap">'; // Start of wrap
|
| 64 |
+
echo '<h2>'.__('Tools', 'all-in-one-wp-security-and-firewall').'</h2>'; // Interface title
|
| 65 |
+
$this->set_menu_tabs();
|
| 66 |
+
$tab = $this->get_current_tab();
|
| 67 |
+
$this->render_menu_tabs();
|
| 68 |
+
|
| 69 |
+
?>
|
| 70 |
+
<div id="poststuff">
|
| 71 |
+
<div id="post-body">
|
| 72 |
+
<?php call_user_func($this->menu_tabs[$tab]['render_callback']); ?>
|
| 73 |
+
</div>
|
| 74 |
+
</div>
|
| 75 |
+
<?php
|
| 76 |
+
|
| 77 |
+
echo '</div>'; // End of wrap
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
/**
|
| 81 |
+
* Does a WHOIS lookup on an IP address or domain name and then returns the result.
|
| 82 |
+
*
|
| 83 |
+
* @param String $search - IP address or domain name to do a WHOIS lookup on
|
| 84 |
+
* @param Integer $timeout - connection timeout for fsockopen
|
| 85 |
+
*
|
| 86 |
+
* @return String|WP_Error - returns preformatted WHOIS lookup result or WP_Error
|
| 87 |
+
*/
|
| 88 |
+
private function whois_lookup($search, $timeout = 10) {
|
| 89 |
+
$fp = @fsockopen('whois.iana.org', 43, $errno, $errstr, $timeout);
|
| 90 |
+
|
| 91 |
+
if (!$fp) {
|
| 92 |
+
return new WP_Error('whois_lookup_failed', 'whois.iana.org: Socket Error '.$errno.' - '.$errstr);
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
$queries = sprintf(__('Querying %s: %s', 'all-in-one-wp-security-and-firewall'), 'whois.iana.org', $search)."\n";
|
| 96 |
+
|
| 97 |
+
fputs($fp, $search."\r\n");
|
| 98 |
+
$out = '';
|
| 99 |
+
while (!feof($fp)) {
|
| 100 |
+
$line = fgets($fp);
|
| 101 |
+
if (preg_match('/refer: +(\S+)/', $line, $matches)) {
|
| 102 |
+
$referral_server = $matches[1];
|
| 103 |
+
$queries .= sprintf(__('Redirected to %s', 'all-in-one-wp-security-and-firewall'), $referral_server)."\n";
|
| 104 |
+
break;
|
| 105 |
+
}
|
| 106 |
+
$out .= $line;
|
| 107 |
+
}
|
| 108 |
+
fclose($fp);
|
| 109 |
+
|
| 110 |
+
if (!isset($referral_server) && filter_var($search, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6) && preg_match('/whois: +(\S+)/', $out, $matches)) {
|
| 111 |
+
$referral_server = $matches[1];
|
| 112 |
+
$queries .= sprintf(__('Redirected to %s', 'all-in-one-wp-security-and-firewall'), $referral_server)."\n";
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
while (isset($referral_server)) {
|
| 116 |
+
$referrals[] = $referral_server;
|
| 117 |
+
|
| 118 |
+
$fp = @fsockopen($referral_server, 43, $errno, $errstr, $timeout);
|
| 119 |
+
|
| 120 |
+
if (!$fp) {
|
| 121 |
+
return new WP_Error('whois_lookup_failed', $referral_server.': Socket Error '.$errno.' - '.$errstr);
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
if ('whois.arin.net' == $referral_server) {
|
| 125 |
+
$formatted_search = 'n + '.$search;
|
| 126 |
+
} elseif ('whois.denic.de' == $referral_server) {
|
| 127 |
+
$formatted_search = '-T dn,ace '.$search;
|
| 128 |
+
} elseif ('whois.dk-hostmaster.dk' == $referral_server) {
|
| 129 |
+
$formatted_search = '--charset=utf-8 --show-handles '.$search;
|
| 130 |
+
} elseif ('whois.nic.ad.jp' == $referral_server || 'whois.jprs.jp' == $referral_server) {
|
| 131 |
+
$formatted_search = $search.'/e';
|
| 132 |
+
} else {
|
| 133 |
+
$formatted_search = $search;
|
| 134 |
+
}
|
| 135 |
+
|
| 136 |
+
$queries .= sprintf(__('Querying %s: %s', 'all-in-one-wp-security-and-firewall'), $referral_server, $formatted_search)."\n";
|
| 137 |
+
|
| 138 |
+
$referral_server = null;
|
| 139 |
+
|
| 140 |
+
fputs($fp, $formatted_search."\r\n");
|
| 141 |
+
$out = '';
|
| 142 |
+
while (!feof($fp)) {
|
| 143 |
+
$line = fgets($fp);
|
| 144 |
+
if (preg_match('/Registrar WHOIS Server: +(\S+)/', $line, $matches) ||
|
| 145 |
+
preg_match('/% referto: +whois -h (\S+)/', $line, $matches) ||
|
| 146 |
+
preg_match('/% referto: +(\S+)/', $line, $matches) ||
|
| 147 |
+
preg_match('/ReferralServer: +rwhois:\/\/(\S+)/', $line, $matches) ||
|
| 148 |
+
preg_match('/ReferralServer: +whois:\/\/(\S+)/', $line, $matches)) {
|
| 149 |
+
if (!in_array($matches[1], $referrals)) {
|
| 150 |
+
$referral_server = $matches[1];
|
| 151 |
+
$queries .= sprintf(__('Redirected to %s', 'all-in-one-wp-security-and-firewall'), $referral_server)."\n";
|
| 152 |
+
break;
|
| 153 |
+
}
|
| 154 |
+
}
|
| 155 |
+
$out .= $line;
|
| 156 |
+
}
|
| 157 |
+
fclose($fp);
|
| 158 |
+
}
|
| 159 |
+
|
| 160 |
+
return $queries."\n".$out;
|
| 161 |
+
}
|
| 162 |
+
|
| 163 |
+
/**
|
| 164 |
+
* Renders the submenu's whois-lookup tab body.
|
| 165 |
+
*
|
| 166 |
+
* @return Void
|
| 167 |
+
*/
|
| 168 |
+
private function render_whois_lookup_tab() {
|
| 169 |
+
global $aio_wp_security;
|
| 170 |
+
|
| 171 |
+
?>
|
| 172 |
+
<div class="aio_blue_box">
|
| 173 |
+
<p><?php echo __('The WHOIS lookup feature gives you a way to look up who owns an IP address or domain name.', 'all-in-one-wp-security-and-firewall').' '.__('You can use this to investigate users engaging in malicious activity on your site.', 'all-in-one-wp-security-and-firewall'); ?></p>
|
| 174 |
+
</div>
|
| 175 |
+
<div class="postbox">
|
| 176 |
+
<h3 class="hndle"><?php _e('WHOIS Lookup On IP Or Domain', 'all-in-one-wp-security-and-firewall'); ?></h3>
|
| 177 |
+
<div class="inside">
|
| 178 |
+
<form method="post" action="">
|
| 179 |
+
<?php wp_nonce_field('aiowpsec-whois-lookup'); ?>
|
| 180 |
+
<table class="form-table">
|
| 181 |
+
<tr valign="top">
|
| 182 |
+
<th scope="row">
|
| 183 |
+
<label for="aiowps_whois_ip_or_domain"><?php _e('IP address or domain name:', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 184 |
+
</th>
|
| 185 |
+
<td>
|
| 186 |
+
<input id="aiowps_whois_ip_or_domain" type="text" name="aiowps_whois_ip_or_domain" value="" size="80"/>
|
| 187 |
+
</td>
|
| 188 |
+
</tr>
|
| 189 |
+
</table>
|
| 190 |
+
<input class="button-primary" type="submit" value="<?php _e('Look up IP or domain', 'all-in-one-wp-security-and-firewall'); ?>"/>
|
| 191 |
+
</form>
|
| 192 |
+
</div>
|
| 193 |
+
</div>
|
| 194 |
+
<?php
|
| 195 |
+
|
| 196 |
+
if (isset($_POST['aiowps_whois_ip_or_domain'])) {
|
| 197 |
+
$nonce = $_POST['_wpnonce'];
|
| 198 |
+
|
| 199 |
+
if (!wp_verify_nonce($nonce, 'aiowpsec-whois-lookup')) {
|
| 200 |
+
$aio_wp_security->debug_logger->log_debug('Nonce check failed on WHOIS lookup.', 4);
|
| 201 |
+
die('Nonce check failed on WHOIS lookup.');
|
| 202 |
+
}
|
| 203 |
+
|
| 204 |
+
$ip_or_domain = stripslashes($_POST['aiowps_whois_ip_or_domain']);
|
| 205 |
+
|
| 206 |
+
?>
|
| 207 |
+
<div class="postbox">
|
| 208 |
+
<h3 class="hndle">
|
| 209 |
+
<table>
|
| 210 |
+
<tr valign="top">
|
| 211 |
+
<th scope="row">WHOIS: </th>
|
| 212 |
+
<td><?php echo htmlspecialchars($ip_or_domain); ?></td>
|
| 213 |
+
</tr>
|
| 214 |
+
</table>
|
| 215 |
+
</h3>
|
| 216 |
+
<div class="inside">
|
| 217 |
+
<pre><?php
|
| 218 |
+
if (empty($ip_or_domain) || !(filter_var($ip_or_domain, FILTER_VALIDATE_IP) || filter_var($ip_or_domain, FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME))) {
|
| 219 |
+
$this->show_msg_error(__('Please enter a valid IP address or domain name to look up.', 'all-in-one-wp-security-and-firewall'));
|
| 220 |
+
_e('Nothing to show.', 'all-in-one-wp-security-and-firewall');
|
| 221 |
+
} else {
|
| 222 |
+
$result = $this->whois_lookup($ip_or_domain);
|
| 223 |
+
|
| 224 |
+
if (is_wp_error($result)) {
|
| 225 |
+
$this->show_msg_error(htmlspecialchars($result->get_error_message()));
|
| 226 |
+
_e('Nothing to show.', 'all-in-one-wp-security-and-firewall');
|
| 227 |
+
} else {
|
| 228 |
+
echo htmlspecialchars($result);
|
| 229 |
+
}
|
| 230 |
+
}
|
| 231 |
+
?></pre>
|
| 232 |
+
</div>
|
| 233 |
+
</div>
|
| 234 |
+
<?php
|
| 235 |
+
|
| 236 |
+
}
|
| 237 |
+
}
|
| 238 |
+
|
| 239 |
+
} // End of class
|
|
@@ -93,7 +93,7 @@ class AIOWPSecurity_User_Accounts_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 93 |
<?php
|
| 94 |
//display a list of all administrator accounts for this site
|
| 95 |
$postbox_title = __('List of Administrator Accounts', 'all-in-one-wp-security-and-firewall');
|
| 96 |
-
if (
|
| 97 |
$blog_id = get_current_blog_id();
|
| 98 |
$this->postbox($postbox_title, $this->get_all_admin_accounts($blog_id));
|
| 99 |
} else {
|
|
@@ -115,8 +115,8 @@ class AIOWPSecurity_User_Accounts_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 115 |
<?php wp_nonce_field('aiowpsec-change-admin-nonce'); ?>
|
| 116 |
<table class="form-table">
|
| 117 |
<tr valign="top">
|
| 118 |
-
<th scope="row"><label for="
|
| 119 |
-
<td><input type="text" size="16" name="aiowps_new_user_name" />
|
| 120 |
<p class="description"><?php _e('Choose a new username for admin.', 'all-in-one-wp-security-and-firewall'); ?></p>
|
| 121 |
</td>
|
| 122 |
</tr>
|
|
@@ -266,7 +266,7 @@ class AIOWPSecurity_User_Accounts_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 266 |
}
|
| 267 |
|
| 268 |
//multisite considerations
|
| 269 |
-
if (
|
| 270 |
$oldAdmins = $wpdb->get_var( "SELECT meta_value FROM `" . $wpdb->sitemeta . "` WHERE meta_key = 'site_admins'" );
|
| 271 |
$newAdmins = str_replace( '5:"admin"', strlen( $new_username ) . ':"' . esc_sql( $new_username ) . '"', $oldAdmins );
|
| 272 |
$wpdb->query( "UPDATE `" . $wpdb->sitemeta . "` SET meta_value = '" . esc_sql( $newAdmins ) . "' WHERE meta_key = 'site_admins'" );
|
|
@@ -279,7 +279,7 @@ class AIOWPSecurity_User_Accounts_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 279 |
$after_logout_url = AIOWPSecurity_Utility::get_current_page_url();
|
| 280 |
$after_logout_payload = array('redirect_to'=>$after_logout_url, 'msg'=>$aio_wp_security->user_login_obj->key_login_msg.'=admin_user_changed', );
|
| 281 |
//Save some of the logout redirect data to a transient
|
| 282 |
-
|
| 283 |
|
| 284 |
$logout_url = AIOWPSEC_WP_URL.'?aiowpsec_do_log_out=1';
|
| 285 |
$logout_url = AIOWPSecurity_Utility::add_query_data_to_url($logout_url, 'al_additional_data', '1');
|
| 93 |
<?php
|
| 94 |
//display a list of all administrator accounts for this site
|
| 95 |
$postbox_title = __('List of Administrator Accounts', 'all-in-one-wp-security-and-firewall');
|
| 96 |
+
if (is_multisite()) { //Multi-site: get admin accounts for current site
|
| 97 |
$blog_id = get_current_blog_id();
|
| 98 |
$this->postbox($postbox_title, $this->get_all_admin_accounts($blog_id));
|
| 99 |
} else {
|
| 115 |
<?php wp_nonce_field('aiowpsec-change-admin-nonce'); ?>
|
| 116 |
<table class="form-table">
|
| 117 |
<tr valign="top">
|
| 118 |
+
<th scope="row"><label for="aiowps_new_user_name"><?php _e('New Admin Username', 'all-in-one-wp-security-and-firewall')?>:</label></th>
|
| 119 |
+
<td><input type="text" size="16" id="aiowps_new_user_name" name="aiowps_new_user_name" />
|
| 120 |
<p class="description"><?php _e('Choose a new username for admin.', 'all-in-one-wp-security-and-firewall'); ?></p>
|
| 121 |
</td>
|
| 122 |
</tr>
|
| 266 |
}
|
| 267 |
|
| 268 |
//multisite considerations
|
| 269 |
+
if ( is_multisite() ) { //process sitemeta if we're in a multi-site situation
|
| 270 |
$oldAdmins = $wpdb->get_var( "SELECT meta_value FROM `" . $wpdb->sitemeta . "` WHERE meta_key = 'site_admins'" );
|
| 271 |
$newAdmins = str_replace( '5:"admin"', strlen( $new_username ) . ':"' . esc_sql( $new_username ) . '"', $oldAdmins );
|
| 272 |
$wpdb->query( "UPDATE `" . $wpdb->sitemeta . "` SET meta_value = '" . esc_sql( $newAdmins ) . "' WHERE meta_key = 'site_admins'" );
|
| 279 |
$after_logout_url = AIOWPSecurity_Utility::get_current_page_url();
|
| 280 |
$after_logout_payload = array('redirect_to'=>$after_logout_url, 'msg'=>$aio_wp_security->user_login_obj->key_login_msg.'=admin_user_changed', );
|
| 281 |
//Save some of the logout redirect data to a transient
|
| 282 |
+
is_multisite() ? set_site_transient('aiowps_logout_payload', $after_logout_payload, 30 * 60) : set_transient('aiowps_logout_payload', $after_logout_payload, 30 * 60);
|
| 283 |
|
| 284 |
$logout_url = AIOWPSEC_WP_URL.'?aiowpsec_do_log_out=1';
|
| 285 |
$logout_url = AIOWPSecurity_Utility::add_query_data_to_url($logout_url, 'al_additional_data', '1');
|
|
@@ -3,8 +3,7 @@ if(!defined('ABSPATH')){
|
|
| 3 |
exit;//Exit if accessed directly
|
| 4 |
}
|
| 5 |
|
| 6 |
-
class AIOWPSecurity_User_Login_Menu extends AIOWPSecurity_Admin_Menu
|
| 7 |
-
{
|
| 8 |
var $menu_page_slug = AIOWPSEC_USER_LOGIN_MENU_SLUG;
|
| 9 |
|
| 10 |
/* Specify all the tabs of this menu in the following array */
|
|
@@ -15,29 +14,32 @@ class AIOWPSecurity_User_Login_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 15 |
'tab3' => 'render_tab3',
|
| 16 |
'tab4' => 'render_tab4',
|
| 17 |
'tab5' => 'render_tab5',
|
|
|
|
| 18 |
);
|
| 19 |
|
| 20 |
-
|
|
|
|
|
|
|
|
|
|
| 21 |
{
|
| 22 |
$this->render_menu_page();
|
| 23 |
}
|
| 24 |
|
| 25 |
-
function set_menu_tabs()
|
| 26 |
-
{
|
| 27 |
$this->menu_tabs = array(
|
| 28 |
'tab1' => __('Login Lockdown', 'all-in-one-wp-security-and-firewall'),
|
| 29 |
'tab2' => __('Failed Login Records', 'all-in-one-wp-security-and-firewall'),
|
| 30 |
'tab3' => __('Force Logout', 'all-in-one-wp-security-and-firewall'),
|
| 31 |
'tab4' => __('Account Activity Logs', 'all-in-one-wp-security-and-firewall'),
|
| 32 |
'tab5' => __('Logged In Users', 'all-in-one-wp-security-and-firewall'),
|
|
|
|
| 33 |
);
|
| 34 |
}
|
| 35 |
|
| 36 |
/*
|
| 37 |
* Renders our tabs of this menu as nav items
|
| 38 |
*/
|
| 39 |
-
function render_menu_tabs()
|
| 40 |
-
{
|
| 41 |
$current_tab = $this->get_current_tab();
|
| 42 |
|
| 43 |
echo '<h2 class="nav-tab-wrapper">';
|
|
@@ -52,8 +54,7 @@ class AIOWPSecurity_User_Login_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 52 |
/*
|
| 53 |
* The menu rendering goes here
|
| 54 |
*/
|
| 55 |
-
function render_menu_page()
|
| 56 |
-
{
|
| 57 |
echo '<div class="wrap">';
|
| 58 |
echo '<h2>'.__('User Login','all-in-one-wp-security-and-firewall').'</h2>';//Interface title
|
| 59 |
$this->set_menu_tabs();
|
|
@@ -69,9 +70,13 @@ class AIOWPSecurity_User_Login_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 69 |
</div><!-- end of wrap -->
|
| 70 |
<?php
|
| 71 |
}
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 75 |
global $aio_wp_security;
|
| 76 |
global $aiowps_feature_mgr;
|
| 77 |
include_once 'wp-security-list-locked-ip.php'; //For rendering the AIOWPSecurity_List_Table in tab1
|
|
@@ -88,9 +93,8 @@ class AIOWPSecurity_User_Login_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 88 |
}
|
| 89 |
|
| 90 |
$max_login_attempt_val = sanitize_text_field($_POST['aiowps_max_login_attempts']);
|
| 91 |
-
if(!is_numeric($max_login_attempt_val))
|
| 92 |
-
|
| 93 |
-
$error .= '<br />'.__('You entered a non numeric value for the max login attempts field. It has been set to the default value.','all-in-one-wp-security-and-firewall');
|
| 94 |
$max_login_attempt_val = '3';//Set it to the default value for this field
|
| 95 |
}
|
| 96 |
|
|
@@ -105,25 +109,47 @@ class AIOWPSecurity_User_Login_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 105 |
if(!is_numeric($lockout_time_length))
|
| 106 |
{
|
| 107 |
$error .= '<br />'.__('You entered a non numeric value for the lockout time length field. It has been set to the default value.','all-in-one-wp-security-and-firewall');
|
| 108 |
-
$lockout_time_length = '
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
}
|
| 110 |
|
| 111 |
-
$
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 117 |
|
| 118 |
// Instantly lockout specific usernames
|
| 119 |
-
$
|
| 120 |
// Read into array, sanitize, filter empty and keep only unique usernames.
|
| 121 |
$instantly_lockout_specific_usernames
|
| 122 |
= array_unique(
|
| 123 |
array_filter(
|
| 124 |
array_map(
|
| 125 |
'sanitize_user',
|
| 126 |
-
AIOWPSecurity_Utility::explode_trim_filter_empty($
|
| 127 |
),
|
| 128 |
'strlen'
|
| 129 |
)
|
|
@@ -144,11 +170,13 @@ class AIOWPSecurity_User_Login_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 144 |
$aio_wp_security->configs->set_value('aiowps_max_login_attempts',absint($max_login_attempt_val));
|
| 145 |
$aio_wp_security->configs->set_value('aiowps_retry_time_period',absint($login_retry_time_period));
|
| 146 |
$aio_wp_security->configs->set_value('aiowps_lockout_time_length',absint($lockout_time_length));
|
|
|
|
| 147 |
$aio_wp_security->configs->set_value('aiowps_set_generic_login_msg',isset($_POST["aiowps_set_generic_login_msg"])?'1':'');
|
| 148 |
$aio_wp_security->configs->set_value('aiowps_enable_invalid_username_lockdown',isset($_POST["aiowps_enable_invalid_username_lockdown"])?'1':'');
|
| 149 |
$aio_wp_security->configs->set_value('aiowps_instantly_lockout_specific_usernames', $instantly_lockout_specific_usernames);
|
| 150 |
$aio_wp_security->configs->set_value('aiowps_enable_email_notify',isset($_POST["aiowps_enable_email_notify"])?'1':'');
|
| 151 |
-
$aio_wp_security->configs->set_value('
|
|
|
|
| 152 |
$aio_wp_security->configs->save_config();
|
| 153 |
|
| 154 |
//Recalculate points after the feature status/options have been altered
|
|
@@ -156,19 +184,7 @@ class AIOWPSecurity_User_Login_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 156 |
|
| 157 |
$this->show_msg_settings_updated();
|
| 158 |
}
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
if(isset($_REQUEST['action'])) //Do list table form row action tasks
|
| 162 |
-
{
|
| 163 |
-
if($_REQUEST['action'] == 'delete_blocked_ip'){ //Delete link was clicked for a row in list table
|
| 164 |
-
$locked_ip_list->delete_lockdown_records(strip_tags($_REQUEST['lockdown_id']));
|
| 165 |
-
}
|
| 166 |
-
|
| 167 |
-
if($_REQUEST['action'] == 'unlock_ip'){ //Unlock link was clicked for a row in list table
|
| 168 |
-
$locked_ip_list->unlock_ip_range(strip_tags($_REQUEST['lockdown_id']));
|
| 169 |
-
}
|
| 170 |
-
}
|
| 171 |
-
|
| 172 |
//login lockdown whitelist settings
|
| 173 |
$result = 1;
|
| 174 |
if (isset($_POST['aiowps_save_lockdown_whitelist_settings']))
|
|
@@ -188,7 +204,7 @@ class AIOWPSecurity_User_Login_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 188 |
{
|
| 189 |
if (!empty($_POST['aiowps_lockdown_allowed_ip_addresses']))
|
| 190 |
{
|
| 191 |
-
$ip_addresses = $_POST['aiowps_lockdown_allowed_ip_addresses'];
|
| 192 |
$ip_list_array = AIOWPSecurity_Utility_IP::create_ip_list_array_from_string_with_newline($ip_addresses);
|
| 193 |
$payload = AIOWPSecurity_Utility_IP::validate_ip_list($ip_list_array, 'whitelist');
|
| 194 |
if($payload[0] == 1){
|
|
@@ -244,70 +260,123 @@ class AIOWPSecurity_User_Login_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 244 |
<tr valign="top">
|
| 245 |
<th scope="row"><?php _e('Enable Login Lockdown Feature', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 246 |
<td>
|
| 247 |
-
<input name="aiowps_enable_login_lockdown" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_login_lockdown')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 248 |
-
<
|
| 249 |
</td>
|
| 250 |
</tr>
|
| 251 |
<tr valign="top">
|
| 252 |
<th scope="row"><?php _e('Allow Unlock Requests', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 253 |
<td>
|
| 254 |
-
<input name="aiowps_allow_unlock_requests" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_allow_unlock_requests')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 255 |
-
<
|
| 256 |
</td>
|
| 257 |
</tr>
|
| 258 |
<tr valign="top">
|
| 259 |
-
<th scope="row"><?php _e('Max Login Attempts', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 260 |
-
<td><input type="text" size="5" name="aiowps_max_login_attempts" value="<?php echo esc_html($aio_wp_security->configs->get_value('aiowps_max_login_attempts')); ?>" />
|
| 261 |
<span class="description"><?php _e('Set the value for the maximum login retries before IP address is locked out', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 262 |
</td>
|
| 263 |
</tr>
|
| 264 |
<tr valign="top">
|
| 265 |
-
<th scope="row"><?php _e('Login Retry Time Period (min)', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 266 |
-
<td><input type="text" size="5" name="aiowps_retry_time_period" value="<?php echo esc_html($aio_wp_security->configs->get_value('aiowps_retry_time_period')); ?>" />
|
| 267 |
<span class="description"><?php _e('If the maximum number of failed login attempts for a particular IP address occur within this time period the plugin will lock out that address', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 268 |
</td>
|
| 269 |
</tr>
|
| 270 |
<tr valign="top">
|
| 271 |
-
<th scope="row"
|
| 272 |
-
|
| 273 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 274 |
</td>
|
| 275 |
</tr>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 276 |
<tr valign="top">
|
| 277 |
<th scope="row"><?php _e('Display Generic Error Message', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 278 |
<td>
|
| 279 |
-
<input name="aiowps_set_generic_login_msg" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_set_generic_login_msg')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 280 |
-
<
|
| 281 |
</td>
|
| 282 |
</tr>
|
| 283 |
<tr valign="top">
|
| 284 |
<th scope="row"><?php _e('Instantly Lockout Invalid Usernames', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 285 |
<td>
|
| 286 |
-
<input name="aiowps_enable_invalid_username_lockdown" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_invalid_username_lockdown')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 287 |
-
<
|
| 288 |
</td>
|
| 289 |
</tr>
|
| 290 |
<tr valign="top">
|
| 291 |
-
<th scope="row"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 292 |
<td>
|
| 293 |
-
<?php
|
| 294 |
$instant_lockout_users_list = $aio_wp_security->configs->get_value('aiowps_instantly_lockout_specific_usernames');
|
| 295 |
if(empty($instant_lockout_users_list)){
|
| 296 |
$instant_lockout_users_list = array();
|
| 297 |
}
|
| 298 |
?>
|
| 299 |
-
<textarea name="aiowps_instantly_lockout_specific_usernames" cols="50" rows="5"><?php echo esc_textarea(implode(PHP_EOL, $instant_lockout_users_list)); ?></textarea><br>
|
| 300 |
<span class="description"><?php _e('Insert one username per line. Existing usernames are not blocked even if present in the list.', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 301 |
</td>
|
| 302 |
</tr>
|
| 303 |
<tr valign="top">
|
| 304 |
-
<th scope="row"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 305 |
<td>
|
| 306 |
-
<input name="aiowps_enable_email_notify" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_email_notify')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 307 |
-
<
|
| 308 |
-
<br
|
| 309 |
-
|
| 310 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 311 |
</tr>
|
| 312 |
</table>
|
| 313 |
<input type="submit" name="aiowps_login_lockdown" value="<?php _e('Save Settings', 'all-in-one-wp-security-and-firewall')?>" class="button-primary" />
|
|
@@ -330,29 +399,19 @@ class AIOWPSecurity_User_Login_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 330 |
<?php wp_nonce_field('aiowpsec-lockdown-whitelist-settings-nonce'); ?>
|
| 331 |
<table class="form-table">
|
| 332 |
<tr valign="top">
|
| 333 |
-
<th scope="row"><?php _e('Enable Login Lockdown IP Whitelist', 'all-in-one-wp-security-and-firewall')
|
| 334 |
<td>
|
| 335 |
-
<input name="aiowps_lockdown_enable_whitelisting" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_lockdown_enable_whitelisting')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 336 |
<span class="description"><?php _e('Check this if you want to enable the whitelisting of selected IP addresses specified in the settings below', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 337 |
</td>
|
| 338 |
</tr>
|
| 339 |
<tr valign="top">
|
| 340 |
-
<th scope="row"><?php _e('Enter Whitelisted IP Addresses:', 'all-in-one-wp-security-and-firewall')?></th>
|
| 341 |
<td>
|
| 342 |
-
<textarea name="aiowps_lockdown_allowed_ip_addresses" rows="5" cols="50"><?php echo ($result
|
| 343 |
<br />
|
| 344 |
-
<span class="description"><?php _e('Enter one or more IP addresses or IP ranges you wish to include in your whitelist. The addresses specified here will never be blocked by the login lockdown feature.','all-in-one-wp-security-and-firewall');?></span>
|
| 345 |
-
|
| 346 |
-
<div class="aiowps_more_info_body">
|
| 347 |
-
<?php
|
| 348 |
-
echo '<p class="description">'.__('Each IP address must be on a new line.', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 349 |
-
echo '<p class="description">'.__('To specify an IP range use a wildcard "*" character. Acceptable ways to use wildcards is shown in the examples below:', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 350 |
-
echo '<p class="description">'.__('Example 1: 195.47.89.*', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 351 |
-
echo '<p class="description">'.__('Example 2: 195.47.*.*', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 352 |
-
echo '<p class="description">'.__('Example 3: 195.*.*.*', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 353 |
-
?>
|
| 354 |
-
</div>
|
| 355 |
-
|
| 356 |
</td>
|
| 357 |
</tr>
|
| 358 |
</table>
|
|
@@ -363,8 +422,12 @@ class AIOWPSecurity_User_Login_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 363 |
<?php
|
| 364 |
}
|
| 365 |
|
| 366 |
-
|
| 367 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 368 |
global $aio_wp_security, $wpdb;
|
| 369 |
if (isset($_POST['aiowps_delete_failed_login_records']))
|
| 370 |
{
|
|
@@ -385,7 +448,7 @@ class AIOWPSecurity_User_Login_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 385 |
}
|
| 386 |
else
|
| 387 |
{
|
| 388 |
-
$this->show_msg_updated(__('All records from the Failed Logins table were deleted successfully
|
| 389 |
}
|
| 390 |
}
|
| 391 |
|
|
@@ -397,8 +460,7 @@ class AIOWPSecurity_User_Login_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 397 |
$failed_login_list->delete_login_failed_records(strip_tags($_REQUEST['failed_login_id']));
|
| 398 |
}
|
| 399 |
}
|
| 400 |
-
|
| 401 |
-
AIOWPSecurity_Admin_Menu::display_bulk_result_message();
|
| 402 |
?>
|
| 403 |
<div class="aio_blue_box">
|
| 404 |
<?php
|
|
@@ -416,7 +478,7 @@ class AIOWPSecurity_User_Login_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 416 |
$failed_login_list->prepare_items();
|
| 417 |
//echo "put table of locked entries here";
|
| 418 |
?>
|
| 419 |
-
<form id="tables-filter" method="
|
| 420 |
<!-- For plugins, we also need to ensure that the form posts back to our current page -->
|
| 421 |
<input type="hidden" name="page" value="<?php echo esc_attr($_REQUEST['page']); ?>" />
|
| 422 |
<?php
|
|
@@ -525,13 +587,13 @@ class AIOWPSecurity_User_Login_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 525 |
<tr valign="top">
|
| 526 |
<th scope="row"><?php _e('Enable Force WP User Logout', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 527 |
<td>
|
| 528 |
-
<input name="aiowps_enable_forced_logout" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_forced_logout')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 529 |
-
<
|
| 530 |
</td>
|
| 531 |
</tr>
|
| 532 |
<tr valign="top">
|
| 533 |
-
<th scope="row"><?php _e('Logout the WP User After XX Minutes', 'all-in-one-wp-security-and-firewall')
|
| 534 |
-
<td><input type="text" size="5" name="aiowps_logout_time_period" value="<?php echo $aio_wp_security->configs->get_value('aiowps_logout_time_period'); ?>" />
|
| 535 |
<span class="description"><?php _e('(Minutes) The user will be forced to log back in after this time period has elapased.', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 536 |
</td>
|
| 537 |
</tr>
|
|
@@ -541,9 +603,13 @@ class AIOWPSecurity_User_Login_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 541 |
</div></div>
|
| 542 |
<?php
|
| 543 |
}
|
| 544 |
-
|
| 545 |
-
|
| 546 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 547 |
include_once 'wp-security-list-acct-activity.php'; //For rendering the AIOWPSecurity_List_Table in tab4
|
| 548 |
$acct_activity_list = new AIOWPSecurity_List_Account_Activity(); //For rendering the AIOWPSecurity_List_Table in tab2
|
| 549 |
if(isset($_REQUEST['action'])) //Do row action tasks for list table form for login activity display
|
|
@@ -552,8 +618,7 @@ class AIOWPSecurity_User_Login_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 552 |
$acct_activity_list->delete_login_activity_records(strip_tags($_REQUEST['activity_login_rec']));
|
| 553 |
}
|
| 554 |
}
|
| 555 |
-
|
| 556 |
-
AIOWPSecurity_Admin_Menu::display_bulk_result_message();
|
| 557 |
?>
|
| 558 |
<div class="aio_blue_box">
|
| 559 |
<?php
|
|
@@ -570,7 +635,7 @@ class AIOWPSecurity_User_Login_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 570 |
$acct_activity_list->prepare_items();
|
| 571 |
//echo "put table of locked entries here";
|
| 572 |
?>
|
| 573 |
-
<form id="tables-filter" method="
|
| 574 |
<!-- For plugins, we also need to ensure that the form posts back to our current page -->
|
| 575 |
<input type="hidden" name="page" value="<?php echo esc_attr($_REQUEST['page']); ?>" />
|
| 576 |
<?php
|
|
@@ -598,11 +663,15 @@ class AIOWPSecurity_User_Login_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 598 |
</div></div>
|
| 599 |
<?php
|
| 600 |
}
|
| 601 |
-
|
| 602 |
-
|
| 603 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 604 |
global $aio_wp_security;
|
| 605 |
-
$logged_in_users = (
|
| 606 |
|
| 607 |
include_once 'wp-security-list-logged-in-users.php'; //For rendering the AIOWPSecurity_List_Table
|
| 608 |
$user_list = new AIOWPSecurity_List_Logged_In_Users();
|
|
@@ -613,8 +682,7 @@ class AIOWPSecurity_User_Login_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 613 |
}
|
| 614 |
}
|
| 615 |
|
| 616 |
-
if (isset($_POST['aiowps_refresh_logged_in_user_list']))
|
| 617 |
-
{
|
| 618 |
$nonce=$_REQUEST['_wpnonce'];
|
| 619 |
if (!wp_verify_nonce($nonce, 'aiowpsec-logged-in-users-nonce'))
|
| 620 |
{
|
|
@@ -634,8 +702,7 @@ class AIOWPSecurity_User_Login_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 634 |
<input type="submit" name="aiowps_refresh_logged_in_user_list" value="<?php _e('Refresh Data', 'all-in-one-wp-security-and-firewall')?>" class="button-primary" />
|
| 635 |
</form>
|
| 636 |
</div></div>
|
| 637 |
-
|
| 638 |
-
<div class="aio_blue_box">
|
| 639 |
<?php
|
| 640 |
echo '<p>'.__('This tab displays all users who are currently logged into your site.', 'all-in-one-wp-security-and-firewall').'
|
| 641 |
<br />'.__('If you suspect there is a user or users who are logged in which should not be, you can block them by inspecting the IP addresses from the data below and adding them to your blacklist.', 'all-in-one-wp-security-and-firewall').'
|
|
@@ -651,7 +718,7 @@ class AIOWPSecurity_User_Login_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 651 |
$user_list->prepare_items();
|
| 652 |
//echo "put table of locked entries here";
|
| 653 |
?>
|
| 654 |
-
|
| 655 |
<!-- For plugins, we also need to ensure that the form posts back to our current page -->
|
| 656 |
<input type="hidden" name="page" value="<?php echo esc_attr($_REQUEST['page']); ?>" />
|
| 657 |
<input type="hidden" name="tab" value="<?php echo esc_attr($_REQUEST['tab']); ?>" />
|
|
@@ -662,5 +729,66 @@ class AIOWPSecurity_User_Login_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 662 |
<?php
|
| 663 |
|
| 664 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 665 |
|
| 666 |
-
} //end class
|
| 3 |
exit;//Exit if accessed directly
|
| 4 |
}
|
| 5 |
|
| 6 |
+
class AIOWPSecurity_User_Login_Menu extends AIOWPSecurity_Admin_Menu {
|
|
|
|
| 7 |
var $menu_page_slug = AIOWPSEC_USER_LOGIN_MENU_SLUG;
|
| 8 |
|
| 9 |
/* Specify all the tabs of this menu in the following array */
|
| 14 |
'tab3' => 'render_tab3',
|
| 15 |
'tab4' => 'render_tab4',
|
| 16 |
'tab5' => 'render_tab5',
|
| 17 |
+
'additional' => 'render_additional_tab',
|
| 18 |
);
|
| 19 |
|
| 20 |
+
/**
|
| 21 |
+
* Class constructor
|
| 22 |
+
*/
|
| 23 |
+
public function __construct()
|
| 24 |
{
|
| 25 |
$this->render_menu_page();
|
| 26 |
}
|
| 27 |
|
| 28 |
+
function set_menu_tabs() {
|
|
|
|
| 29 |
$this->menu_tabs = array(
|
| 30 |
'tab1' => __('Login Lockdown', 'all-in-one-wp-security-and-firewall'),
|
| 31 |
'tab2' => __('Failed Login Records', 'all-in-one-wp-security-and-firewall'),
|
| 32 |
'tab3' => __('Force Logout', 'all-in-one-wp-security-and-firewall'),
|
| 33 |
'tab4' => __('Account Activity Logs', 'all-in-one-wp-security-and-firewall'),
|
| 34 |
'tab5' => __('Logged In Users', 'all-in-one-wp-security-and-firewall'),
|
| 35 |
+
'additional' => __('Additional Settings', 'all-in-one-wp-security-and-firewall'),
|
| 36 |
);
|
| 37 |
}
|
| 38 |
|
| 39 |
/*
|
| 40 |
* Renders our tabs of this menu as nav items
|
| 41 |
*/
|
| 42 |
+
function render_menu_tabs() {
|
|
|
|
| 43 |
$current_tab = $this->get_current_tab();
|
| 44 |
|
| 45 |
echo '<h2 class="nav-tab-wrapper">';
|
| 54 |
/*
|
| 55 |
* The menu rendering goes here
|
| 56 |
*/
|
| 57 |
+
function render_menu_page() {
|
|
|
|
| 58 |
echo '<div class="wrap">';
|
| 59 |
echo '<h2>'.__('User Login','all-in-one-wp-security-and-firewall').'</h2>';//Interface title
|
| 60 |
$this->set_menu_tabs();
|
| 70 |
</div><!-- end of wrap -->
|
| 71 |
<?php
|
| 72 |
}
|
| 73 |
+
|
| 74 |
+
/**
|
| 75 |
+
* Displays the Login Lockdown tab.
|
| 76 |
+
*
|
| 77 |
+
* @return Void
|
| 78 |
+
*/
|
| 79 |
+
private function render_tab1() {
|
| 80 |
global $aio_wp_security;
|
| 81 |
global $aiowps_feature_mgr;
|
| 82 |
include_once 'wp-security-list-locked-ip.php'; //For rendering the AIOWPSecurity_List_Table in tab1
|
| 93 |
}
|
| 94 |
|
| 95 |
$max_login_attempt_val = sanitize_text_field($_POST['aiowps_max_login_attempts']);
|
| 96 |
+
if (!is_numeric($max_login_attempt_val)) {
|
| 97 |
+
$error .= '<br />'.__('You entered a non-numeric value for the max login attempts field. It has been set to the default value.','all-in-one-wp-security-and-firewall');
|
|
|
|
| 98 |
$max_login_attempt_val = '3';//Set it to the default value for this field
|
| 99 |
}
|
| 100 |
|
| 109 |
if(!is_numeric($lockout_time_length))
|
| 110 |
{
|
| 111 |
$error .= '<br />'.__('You entered a non numeric value for the lockout time length field. It has been set to the default value.','all-in-one-wp-security-and-firewall');
|
| 112 |
+
$lockout_time_length = '5'; //Set it to the default value for this field
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
$max_lockout_time_length = sanitize_text_field($_POST['aiowps_max_lockout_time_length']);
|
| 116 |
+
if (!is_numeric($max_lockout_time_length)) {
|
| 117 |
+
$error .= '<br />'.__('You entered a non numeric value for the maximim lockout time length field. It has been set to the default value.','all-in-one-wp-security-and-firewall');
|
| 118 |
+
$max_lockout_time_length = '60'; //Set it to the default value for this field
|
| 119 |
}
|
| 120 |
|
| 121 |
+
$email_addresses = isset($_POST['aiowps_email_address']) ? stripslashes($_POST['aiowps_email_address']) : get_bloginfo('admin_email');
|
| 122 |
+
$email_addresses_trimmed = AIOWPSecurity_Utility::explode_trim_filter_empty($email_addresses, "\n");
|
| 123 |
+
// Read into array, sanitize, filter empty and keep only unique usernames.
|
| 124 |
+
$email_address_list
|
| 125 |
+
= array_unique(
|
| 126 |
+
array_filter(
|
| 127 |
+
array_map(
|
| 128 |
+
'sanitize_email',
|
| 129 |
+
$email_addresses_trimmed
|
| 130 |
+
),
|
| 131 |
+
'is_email'
|
| 132 |
+
)
|
| 133 |
+
);
|
| 134 |
+
if (isset($_POST['aiowps_enable_email_notify']) && 1 == $_POST['aiowps_enable_email_notify'] && 0 == count($email_addresses_trimmed)) {
|
| 135 |
+
$error .= '<br />' . __('Please fill in one or more email addresses to notify.', 'all-in-one-wp-security-and-firewall');
|
| 136 |
+
} else if (isset($_POST['aiowps_enable_email_notify']) && 1 == $_POST['aiowps_enable_email_notify'] && (0 == count($email_address_list) || count($email_address_list) != count($email_addresses_trimmed))) {
|
| 137 |
+
$error .= '<br />' . __('You have entered one or more invalid email addresses.', 'all-in-one-wp-security-and-firewall');
|
| 138 |
}
|
| 139 |
+
if (0 == count($email_address_list)) {
|
| 140 |
+
$error .= ' ' . __('It has been set to your WordPress admin email as default.', 'all-in-one-wp-security-and-firewall');
|
| 141 |
+
$email_address_list[] = get_bloginfo('admin_email');
|
| 142 |
+
}
|
| 143 |
|
| 144 |
// Instantly lockout specific usernames
|
| 145 |
+
$instantly_lockout_specific_usernames = isset($_POST['aiowps_instantly_lockout_specific_usernames']) ? $_POST['aiowps_instantly_lockout_specific_usernames'] : '';
|
| 146 |
// Read into array, sanitize, filter empty and keep only unique usernames.
|
| 147 |
$instantly_lockout_specific_usernames
|
| 148 |
= array_unique(
|
| 149 |
array_filter(
|
| 150 |
array_map(
|
| 151 |
'sanitize_user',
|
| 152 |
+
AIOWPSecurity_Utility::explode_trim_filter_empty($instantly_lockout_specific_usernames)
|
| 153 |
),
|
| 154 |
'strlen'
|
| 155 |
)
|
| 170 |
$aio_wp_security->configs->set_value('aiowps_max_login_attempts',absint($max_login_attempt_val));
|
| 171 |
$aio_wp_security->configs->set_value('aiowps_retry_time_period',absint($login_retry_time_period));
|
| 172 |
$aio_wp_security->configs->set_value('aiowps_lockout_time_length',absint($lockout_time_length));
|
| 173 |
+
$aio_wp_security->configs->set_value('aiowps_max_lockout_time_length', absint($max_lockout_time_length));
|
| 174 |
$aio_wp_security->configs->set_value('aiowps_set_generic_login_msg',isset($_POST["aiowps_set_generic_login_msg"])?'1':'');
|
| 175 |
$aio_wp_security->configs->set_value('aiowps_enable_invalid_username_lockdown',isset($_POST["aiowps_enable_invalid_username_lockdown"])?'1':'');
|
| 176 |
$aio_wp_security->configs->set_value('aiowps_instantly_lockout_specific_usernames', $instantly_lockout_specific_usernames);
|
| 177 |
$aio_wp_security->configs->set_value('aiowps_enable_email_notify',isset($_POST["aiowps_enable_email_notify"])?'1':'');
|
| 178 |
+
$aio_wp_security->configs->set_value('aiowps_enable_php_backtrace_in_email', isset($_POST['aiowps_enable_php_backtrace_in_email']) ? '1' : '');
|
| 179 |
+
$aio_wp_security->configs->set_value('aiowps_email_address', $email_address_list);
|
| 180 |
$aio_wp_security->configs->save_config();
|
| 181 |
|
| 182 |
//Recalculate points after the feature status/options have been altered
|
| 184 |
|
| 185 |
$this->show_msg_settings_updated();
|
| 186 |
}
|
| 187 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 188 |
//login lockdown whitelist settings
|
| 189 |
$result = 1;
|
| 190 |
if (isset($_POST['aiowps_save_lockdown_whitelist_settings']))
|
| 204 |
{
|
| 205 |
if (!empty($_POST['aiowps_lockdown_allowed_ip_addresses']))
|
| 206 |
{
|
| 207 |
+
$ip_addresses = stripslashes($_POST['aiowps_lockdown_allowed_ip_addresses']);
|
| 208 |
$ip_list_array = AIOWPSecurity_Utility_IP::create_ip_list_array_from_string_with_newline($ip_addresses);
|
| 209 |
$payload = AIOWPSecurity_Utility_IP::validate_ip_list($ip_list_array, 'whitelist');
|
| 210 |
if($payload[0] == 1){
|
| 260 |
<tr valign="top">
|
| 261 |
<th scope="row"><?php _e('Enable Login Lockdown Feature', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 262 |
<td>
|
| 263 |
+
<input id="aiowps_enable_login_lockdown" name="aiowps_enable_login_lockdown" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_login_lockdown')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 264 |
+
<label for="aiowps_enable_login_lockdown" class="description"><?php _e('Check this if you want to enable the login lockdown feature and apply the settings below', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 265 |
</td>
|
| 266 |
</tr>
|
| 267 |
<tr valign="top">
|
| 268 |
<th scope="row"><?php _e('Allow Unlock Requests', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 269 |
<td>
|
| 270 |
+
<input id="aiowps_allow_unlock_requests" name="aiowps_allow_unlock_requests" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_allow_unlock_requests')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 271 |
+
<label for="aiowps_allow_unlock_requests" class="description"><?php _e('Check this if you want to allow users to generate an automated unlock request link which will unlock their account', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 272 |
</td>
|
| 273 |
</tr>
|
| 274 |
<tr valign="top">
|
| 275 |
+
<th scope="row"><label for="aiowps_max_login_attempts"><?php _e('Max Login Attempts', 'all-in-one-wp-security-and-firewall'); ?>:</label></th>
|
| 276 |
+
<td><input id="aiowps_max_login_attempts" type="text" size="5" name="aiowps_max_login_attempts" value="<?php echo esc_html($aio_wp_security->configs->get_value('aiowps_max_login_attempts')); ?>" />
|
| 277 |
<span class="description"><?php _e('Set the value for the maximum login retries before IP address is locked out', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 278 |
</td>
|
| 279 |
</tr>
|
| 280 |
<tr valign="top">
|
| 281 |
+
<th scope="row"><label for="aiowps_retry_time_period"><?php _e('Login Retry Time Period (min)', 'all-in-one-wp-security-and-firewall'); ?>:</label></th>
|
| 282 |
+
<td><input id="aiowps_retry_time_period" type="text" size="5" name="aiowps_retry_time_period" value="<?php echo esc_html($aio_wp_security->configs->get_value('aiowps_retry_time_period')); ?>" />
|
| 283 |
<span class="description"><?php _e('If the maximum number of failed login attempts for a particular IP address occur within this time period the plugin will lock out that address', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 284 |
</td>
|
| 285 |
</tr>
|
| 286 |
<tr valign="top">
|
| 287 |
+
<th scope="row">
|
| 288 |
+
<label for="aiowps_lockout_time_length">
|
| 289 |
+
<?php _e('Minimum lockout time length', 'all-in-one-wp-security-and-firewall'); ?>:
|
| 290 |
+
</label>
|
| 291 |
+
</th>
|
| 292 |
+
<td><input type="text" size="5" name="aiowps_lockout_time_length" id="aiowps_lockout_time_length" value="<?php echo esc_attr($aio_wp_security->configs->get_value('aiowps_lockout_time_length')); ?>" />
|
| 293 |
+
<span class="description">
|
| 294 |
+
<?php
|
| 295 |
+
echo __('Set the minimum time period in minutes of lockout.', 'all-in-one-wp-security-and-firewall').' '.
|
| 296 |
+
__('This failed login lockout time will be tripled on each failed login.', 'all-in-one-wp-security-and-firewall');
|
| 297 |
+
?>
|
| 298 |
+
</span>
|
| 299 |
</td>
|
| 300 |
</tr>
|
| 301 |
+
<tr valign="top">
|
| 302 |
+
<th scope="row">
|
| 303 |
+
<label for="aiowps_max_lockout_time_length">
|
| 304 |
+
<?php _e('Maximum lockout time length', 'all-in-one-wp-security-and-firewall')?>:
|
| 305 |
+
</label>
|
| 306 |
+
</th>
|
| 307 |
+
<td><input type="text" size="5" name="aiowps_max_lockout_time_length" id="aiowps_max_lockout_time_length" value="<?php echo esc_attr($aio_wp_security->configs->get_value('aiowps_max_lockout_time_length')); ?>" />
|
| 308 |
+
<span class="description">
|
| 309 |
+
<?php
|
| 310 |
+
echo __('Set the maximum time period in minutes of lockout.', 'all-in-one-wp-security-and-firewall').' '.
|
| 311 |
+
__('No IP address will be blocked for more than this time period after making a failed login attempt.', 'all-in-one-wp-security-and-firewall')
|
| 312 |
+
?>
|
| 313 |
+
</span>
|
| 314 |
+
</tr>
|
| 315 |
<tr valign="top">
|
| 316 |
<th scope="row"><?php _e('Display Generic Error Message', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 317 |
<td>
|
| 318 |
+
<input id="aiowps_set_generic_login_msg" name="aiowps_set_generic_login_msg" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_set_generic_login_msg')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 319 |
+
<label for="aiowps_set_generic_login_msg" class="description"><?php _e('Check this if you want to show a generic error message when a login attempt fails', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 320 |
</td>
|
| 321 |
</tr>
|
| 322 |
<tr valign="top">
|
| 323 |
<th scope="row"><?php _e('Instantly Lockout Invalid Usernames', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 324 |
<td>
|
| 325 |
+
<input id="aiowps_enable_invalid_username_lockdown" name="aiowps_enable_invalid_username_lockdown" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_invalid_username_lockdown')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 326 |
+
<label for="aiowps_enable_invalid_username_lockdown" class="description"><?php _e('Check this if you want to instantly lockout login attempts with usernames which do not exist on your system', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 327 |
</td>
|
| 328 |
</tr>
|
| 329 |
<tr valign="top">
|
| 330 |
+
<th scope="row">
|
| 331 |
+
<label for="aiowps_instantly_lockout_specific_usernames">
|
| 332 |
+
<?php _e('Instantly Lockout Specific Usernames', 'all-in-one-wp-security-and-firewall')?>:
|
| 333 |
+
</label>
|
| 334 |
+
</th>
|
| 335 |
<td>
|
| 336 |
+
<?php
|
| 337 |
$instant_lockout_users_list = $aio_wp_security->configs->get_value('aiowps_instantly_lockout_specific_usernames');
|
| 338 |
if(empty($instant_lockout_users_list)){
|
| 339 |
$instant_lockout_users_list = array();
|
| 340 |
}
|
| 341 |
?>
|
| 342 |
+
<textarea id="aiowps_instantly_lockout_specific_usernames" name="aiowps_instantly_lockout_specific_usernames" cols="50" rows="5"><?php echo esc_textarea(implode(PHP_EOL, $instant_lockout_users_list)); ?></textarea><br>
|
| 343 |
<span class="description"><?php _e('Insert one username per line. Existing usernames are not blocked even if present in the list.', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 344 |
</td>
|
| 345 |
</tr>
|
| 346 |
<tr valign="top">
|
| 347 |
+
<th scope="row">
|
| 348 |
+
<label for="aiowps_email_address">
|
| 349 |
+
<?php _e('Notify By Email', 'all-in-one-wp-security-and-firewall')?>:
|
| 350 |
+
</label>
|
| 351 |
+
</th>
|
| 352 |
<td>
|
| 353 |
+
<input id="aiowps_enable_email_notify" name="aiowps_enable_email_notify" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_email_notify')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 354 |
+
<label for="aiowps_enable_email_notify" class="description"><?php _e('Check this if you want to receive an email when someone has been locked out due to maximum failed login attempts', 'all-in-one-wp-security-and-firewall'); ?></span></label>
|
| 355 |
+
<br />
|
| 356 |
+
<?php
|
| 357 |
+
$aiowps_email_address_list = (array)$aio_wp_security->configs->get_value('aiowps_email_address');
|
| 358 |
+
?>
|
| 359 |
+
<textarea id="aiowps_email_address" name="aiowps_email_address" cols="50" rows="5"><?php echo esc_textarea(implode("\n", $aiowps_email_address_list)); ?></textarea><br>
|
| 360 |
+
<span class="description"><?php _e('Fill in one email address per line.', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 361 |
+
<span class="aiowps_more_info_anchor"><span class="aiowps_more_info_toggle_char">+</span><span class="aiowps_more_info_toggle_text"><?php _e('More Info', 'all-in-one-wp-security-and-firewall'); ?></span></span>
|
| 362 |
+
<div class="aiowps_more_info_body">
|
| 363 |
+
<?php
|
| 364 |
+
echo '<p class="description">'.__('Each email address must be on a new line.', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 365 |
+
echo '<p class="description">'.__('If a valid email address has not been filled in, it will not be saved.', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 366 |
+
echo '<p class="description">'.__('The valid email address format is userid@example.com', 'all-in-one-wp-security-and-firewall').'</p>';
|
| 367 |
+
echo '<p class="description">'.sprintf(__('Example: %s', 'all-in-one-wp-security-and-firewall'), 'rick@wordpress.org').'</p>';
|
| 368 |
+
?>
|
| 369 |
+
</div>
|
| 370 |
+
</td>
|
| 371 |
+
</tr>
|
| 372 |
+
<tr valign="top">
|
| 373 |
+
<th scope="row">
|
| 374 |
+
<?php _e('Enable PHP Backtrace In Email', 'all-in-one-wp-security-and-firewall')?>:
|
| 375 |
+
</th>
|
| 376 |
+
<td>
|
| 377 |
+
<input name="aiowps_enable_php_backtrace_in_email" id="aiowps_enable_php_backtrace_in_email" type="checkbox"<?php checked($aio_wp_security->configs->get_value('aiowps_enable_php_backtrace_in_email'), '1'); ?> value="1"/>
|
| 378 |
+
<label for="aiowps_enable_php_backtrace_in_email"><?php _e('Check this if you want to include the PHP backtrace in notification emails.', 'all-in-one-wp-security-and-firewall'); ?> <?php _e('This is internal coding information which makes it easier to investigate where an issued occurred.', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 379 |
+
</td>
|
| 380 |
</tr>
|
| 381 |
</table>
|
| 382 |
<input type="submit" name="aiowps_login_lockdown" value="<?php _e('Save Settings', 'all-in-one-wp-security-and-firewall')?>" class="button-primary" />
|
| 399 |
<?php wp_nonce_field('aiowpsec-lockdown-whitelist-settings-nonce'); ?>
|
| 400 |
<table class="form-table">
|
| 401 |
<tr valign="top">
|
| 402 |
+
<th scope="row"><label for="aiowps_lockdown_enable_whitelisting"><?php _e('Enable Login Lockdown IP Whitelist', 'all-in-one-wp-security-and-firewall')?></label>:</th>
|
| 403 |
<td>
|
| 404 |
+
<input id="aiowps_lockdown_enable_whitelisting" name="aiowps_lockdown_enable_whitelisting" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_lockdown_enable_whitelisting')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 405 |
<span class="description"><?php _e('Check this if you want to enable the whitelisting of selected IP addresses specified in the settings below', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 406 |
</td>
|
| 407 |
</tr>
|
| 408 |
<tr valign="top">
|
| 409 |
+
<th scope="row"><label for="aiowps_lockdown_allowed_ip_addresses"><?php _e('Enter Whitelisted IP Addresses:', 'all-in-one-wp-security-and-firewall')?></label></th>
|
| 410 |
<td>
|
| 411 |
+
<textarea id="aiowps_lockdown_allowed_ip_addresses" name="aiowps_lockdown_allowed_ip_addresses" rows="5" cols="50"><?php echo esc_textarea(wp_unslash(-1 == $result ? $_POST['aiowps_lockdown_allowed_ip_addresses'] : $aio_wp_security->configs->get_value('aiowps_lockdown_allowed_ip_addresses'))); ?></textarea>
|
| 412 |
<br />
|
| 413 |
+
<span class="description"><?php _e('Enter one or more IP addresses or IP ranges you wish to include in your whitelist.', 'all-in-one-wp-security-and-firewall') . ' ' . _e('The addresses specified here will never be blocked by the login lockdown feature.', 'all-in-one-wp-security-and-firewall');?></span>
|
| 414 |
+
<?php $aio_wp_security->include_template('info/ip-address-ip-range-info.php');?>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 415 |
</td>
|
| 416 |
</tr>
|
| 417 |
</table>
|
| 422 |
<?php
|
| 423 |
}
|
| 424 |
|
| 425 |
+
/**
|
| 426 |
+
* Renders the submenu's tab2 tab body.
|
| 427 |
+
*
|
| 428 |
+
* @return Void
|
| 429 |
+
*/
|
| 430 |
+
public function render_tab2() {
|
| 431 |
global $aio_wp_security, $wpdb;
|
| 432 |
if (isset($_POST['aiowps_delete_failed_login_records']))
|
| 433 |
{
|
| 448 |
}
|
| 449 |
else
|
| 450 |
{
|
| 451 |
+
$this->show_msg_updated(__('All records from the Failed Logins table were deleted successfully.','all-in-one-wp-security-and-firewall'));
|
| 452 |
}
|
| 453 |
}
|
| 454 |
|
| 460 |
$failed_login_list->delete_login_failed_records(strip_tags($_REQUEST['failed_login_id']));
|
| 461 |
}
|
| 462 |
}
|
| 463 |
+
|
|
|
|
| 464 |
?>
|
| 465 |
<div class="aio_blue_box">
|
| 466 |
<?php
|
| 478 |
$failed_login_list->prepare_items();
|
| 479 |
//echo "put table of locked entries here";
|
| 480 |
?>
|
| 481 |
+
<form id="tables-filter" method="post">
|
| 482 |
<!-- For plugins, we also need to ensure that the form posts back to our current page -->
|
| 483 |
<input type="hidden" name="page" value="<?php echo esc_attr($_REQUEST['page']); ?>" />
|
| 484 |
<?php
|
| 587 |
<tr valign="top">
|
| 588 |
<th scope="row"><?php _e('Enable Force WP User Logout', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 589 |
<td>
|
| 590 |
+
<input id="aiowps_enable_forced_logout" name="aiowps_enable_forced_logout" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_forced_logout')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 591 |
+
<label for="aiowps_enable_forced_logout" class="description"><?php _e('Check this if you want to force a wp user to be logged out after a configured amount of time', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 592 |
</td>
|
| 593 |
</tr>
|
| 594 |
<tr valign="top">
|
| 595 |
+
<th scope="row"><label for="aiowps_logout_time_period"><?php _e('Logout the WP User After XX Minutes', 'all-in-one-wp-security-and-firewall')?></label>:</th>
|
| 596 |
+
<td><input id="aiowps_logout_time_period" type="text" size="5" name="aiowps_logout_time_period" value="<?php echo $aio_wp_security->configs->get_value('aiowps_logout_time_period'); ?>" />
|
| 597 |
<span class="description"><?php _e('(Minutes) The user will be forced to log back in after this time period has elapased.', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 598 |
</td>
|
| 599 |
</tr>
|
| 603 |
</div></div>
|
| 604 |
<?php
|
| 605 |
}
|
| 606 |
+
|
| 607 |
+
/**
|
| 608 |
+
* Renders the submenu's tab4 tab body.
|
| 609 |
+
*
|
| 610 |
+
* @return Void
|
| 611 |
+
*/
|
| 612 |
+
public function render_tab4() {
|
| 613 |
include_once 'wp-security-list-acct-activity.php'; //For rendering the AIOWPSecurity_List_Table in tab4
|
| 614 |
$acct_activity_list = new AIOWPSecurity_List_Account_Activity(); //For rendering the AIOWPSecurity_List_Table in tab2
|
| 615 |
if(isset($_REQUEST['action'])) //Do row action tasks for list table form for login activity display
|
| 618 |
$acct_activity_list->delete_login_activity_records(strip_tags($_REQUEST['activity_login_rec']));
|
| 619 |
}
|
| 620 |
}
|
| 621 |
+
|
|
|
|
| 622 |
?>
|
| 623 |
<div class="aio_blue_box">
|
| 624 |
<?php
|
| 635 |
$acct_activity_list->prepare_items();
|
| 636 |
//echo "put table of locked entries here";
|
| 637 |
?>
|
| 638 |
+
<form id="tables-filter" method="post">
|
| 639 |
<!-- For plugins, we also need to ensure that the form posts back to our current page -->
|
| 640 |
<input type="hidden" name="page" value="<?php echo esc_attr($_REQUEST['page']); ?>" />
|
| 641 |
<?php
|
| 663 |
</div></div>
|
| 664 |
<?php
|
| 665 |
}
|
| 666 |
+
|
| 667 |
+
/**
|
| 668 |
+
* Renders the submenu's tab5 tab body.
|
| 669 |
+
*
|
| 670 |
+
* @return Void
|
| 671 |
+
*/
|
| 672 |
+
public function render_tab5() {
|
| 673 |
global $aio_wp_security;
|
| 674 |
+
$logged_in_users = (is_multisite() ? get_site_transient('users_online') : get_transient('users_online'));
|
| 675 |
|
| 676 |
include_once 'wp-security-list-logged-in-users.php'; //For rendering the AIOWPSecurity_List_Table
|
| 677 |
$user_list = new AIOWPSecurity_List_Logged_In_Users();
|
| 682 |
}
|
| 683 |
}
|
| 684 |
|
| 685 |
+
if (isset($_POST['aiowps_refresh_logged_in_user_list'])) {
|
|
|
|
| 686 |
$nonce=$_REQUEST['_wpnonce'];
|
| 687 |
if (!wp_verify_nonce($nonce, 'aiowpsec-logged-in-users-nonce'))
|
| 688 |
{
|
| 702 |
<input type="submit" name="aiowps_refresh_logged_in_user_list" value="<?php _e('Refresh Data', 'all-in-one-wp-security-and-firewall')?>" class="button-primary" />
|
| 703 |
</form>
|
| 704 |
</div></div>
|
| 705 |
+
<div class="aio_blue_box">
|
|
|
|
| 706 |
<?php
|
| 707 |
echo '<p>'.__('This tab displays all users who are currently logged into your site.', 'all-in-one-wp-security-and-firewall').'
|
| 708 |
<br />'.__('If you suspect there is a user or users who are logged in which should not be, you can block them by inspecting the IP addresses from the data below and adding them to your blacklist.', 'all-in-one-wp-security-and-firewall').'
|
| 718 |
$user_list->prepare_items();
|
| 719 |
//echo "put table of locked entries here";
|
| 720 |
?>
|
| 721 |
+
<form id="tables-filter" method="get">
|
| 722 |
<!-- For plugins, we also need to ensure that the form posts back to our current page -->
|
| 723 |
<input type="hidden" name="page" value="<?php echo esc_attr($_REQUEST['page']); ?>" />
|
| 724 |
<input type="hidden" name="tab" value="<?php echo esc_attr($_REQUEST['tab']); ?>" />
|
| 729 |
<?php
|
| 730 |
|
| 731 |
}
|
| 732 |
+
|
| 733 |
+
/**
|
| 734 |
+
* Shows additional tab and field for the disable application password and saves on submit.
|
| 735 |
+
*
|
| 736 |
+
* @global AIO_WP_Security $aio_wp_security
|
| 737 |
+
* @global AIOWPSecurity_Feature_Item_Manager $aiowps_feature_mgr
|
| 738 |
+
* @return void
|
| 739 |
+
*/
|
| 740 |
+
public function render_additional_tab() {
|
| 741 |
+
global $aio_wp_security;
|
| 742 |
+
global $aiowps_feature_mgr;
|
| 743 |
+
|
| 744 |
+
if(isset($_POST['aiowpsec_save_additonal_settings'])) {
|
| 745 |
+
if (!wp_verify_nonce($_POST['_wpnonce'], 'aiowpsec-additonal-settings-nonce')) {
|
| 746 |
+
$aio_wp_security->debug_logger->log_debug("Nonce check failed on additonal settings save.", 4);
|
| 747 |
+
die("Nonce check failed on additonal settings save.");
|
| 748 |
+
}
|
| 749 |
+
|
| 750 |
+
//Save all the form values to the options
|
| 751 |
+
$aio_wp_security->configs->set_value('aiowps_disable_application_password', isset($_POST['aiowps_disable_application_password']) ? '1' : '');
|
| 752 |
+
$aio_wp_security->configs->save_config();
|
| 753 |
+
|
| 754 |
+
//Recalculate points after the feature status/options have been altered
|
| 755 |
+
$aiowps_feature_mgr->check_feature_status_and_recalculate_points();
|
| 756 |
+
|
| 757 |
+
$this->show_msg_settings_updated();
|
| 758 |
+
}
|
| 759 |
+
?>
|
| 760 |
+
<div class="aio_blue_box">
|
| 761 |
+
<?php
|
| 762 |
+
echo '<p>'.__('WordPress 5.6 introduced a new feature called "Application Passwords".', 'all-in-one-wp-security-and-firewall').'
|
| 763 |
+
<br />'.__('This allows you to create a token from the WordPress dashboard which then can be used in the authorization header.', 'all-in-one-wp-security-and-firewall').'
|
| 764 |
+
<br /><br />'.__('This feature allows you to disable Application Passwords as they can leave your site vulnerable to social engineering and phishing scams.', 'all-in-one-wp-security-and-firewall').'
|
| 765 |
+
</p>';
|
| 766 |
+
?>
|
| 767 |
+
</div>
|
| 768 |
+
<form action="" method="POST">
|
| 769 |
+
<div class="postbox">
|
| 770 |
+
<h3 class="hndle"><label for="title"><?php _e('Additonal Settings', 'all-in-one-wp-security-and-firewall'); ?></label></h3>
|
| 771 |
+
<div class="inside">
|
| 772 |
+
<?php
|
| 773 |
+
//Display security info badge
|
| 774 |
+
global $aiowps_feature_mgr;
|
| 775 |
+
$aiowps_feature_mgr->output_feature_details_badge("disable-application-password");
|
| 776 |
+
?>
|
| 777 |
+
|
| 778 |
+
<?php wp_nonce_field('aiowpsec-additonal-settings-nonce'); ?>
|
| 779 |
+
<table class="form-table">
|
| 780 |
+
<tr valign="top">
|
| 781 |
+
<th scope="row"><?php _e('Disable Application Password', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 782 |
+
<td>
|
| 783 |
+
<input name="aiowps_disable_application_password" id="aiowps_disable_application_password" type="checkbox" <?php checked($aio_wp_security->configs->get_value('aiowps_disable_application_password'), '1'); ?> value="1"/>
|
| 784 |
+
<label for="aiowps_disable_application_password"><?php _e('Check this if you want to disable the application password.', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 785 |
+
</td>
|
| 786 |
+
</tr>
|
| 787 |
+
</table>
|
| 788 |
+
</div></div>
|
| 789 |
+
<input type="submit" name="aiowpsec_save_additonal_settings" value="<?php _e('Save Settings', 'all-in-one-wp-security-and-firewall')?>" class="button-primary" />
|
| 790 |
+
</form>
|
| 791 |
+
<?php
|
| 792 |
+
}
|
| 793 |
|
| 794 |
+
} //end class
|
|
@@ -129,7 +129,7 @@ class AIOWPSecurity_User_Registration_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 129 |
//Display security info badge
|
| 130 |
$aiowps_feature_mgr->output_feature_details_badge("manually-approve-registrations");
|
| 131 |
$blog_id = get_current_blog_id();
|
| 132 |
-
if (
|
| 133 |
{
|
| 134 |
//Hide config settings if MS and not main site
|
| 135 |
AIOWPSecurity_Utility::display_multisite_message();
|
|
@@ -141,8 +141,8 @@ class AIOWPSecurity_User_Registration_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 141 |
<tr valign="top">
|
| 142 |
<th scope="row"><?php _e('Enable manual approval of new registrations', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 143 |
<td>
|
| 144 |
-
<input name="aiowps_enable_manual_registration_approval" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_manual_registration_approval')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 145 |
-
<
|
| 146 |
</td>
|
| 147 |
</tr>
|
| 148 |
</table>
|
|
@@ -213,7 +213,7 @@ class AIOWPSecurity_User_Registration_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 213 |
<h3 class="hndle"><label for="title"><?php _e('Registration Page Captcha Settings', 'all-in-one-wp-security-and-firewall'); ?></label></h3>
|
| 214 |
<div class="inside">
|
| 215 |
<?php
|
| 216 |
-
if (
|
| 217 |
{
|
| 218 |
//Hide config settings if MS and not main site
|
| 219 |
$special_msg = '<div class="aio_yellow_box">';
|
|
@@ -235,8 +235,8 @@ class AIOWPSecurity_User_Registration_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 235 |
<tr valign="top">
|
| 236 |
<th scope="row"><?php _e('Enable Captcha On Registration Page', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 237 |
<td>
|
| 238 |
-
<input name="aiowps_enable_registration_page_captcha" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_registration_page_captcha')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 239 |
-
<
|
| 240 |
</td>
|
| 241 |
</tr>
|
| 242 |
</table>
|
|
@@ -296,8 +296,8 @@ class AIOWPSecurity_User_Registration_Menu extends AIOWPSecurity_Admin_Menu
|
|
| 296 |
<tr valign="top">
|
| 297 |
<th scope="row"><?php _e('Enable Honeypot On Registration Page', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 298 |
<td>
|
| 299 |
-
<input name="aiowps_enable_registration_honeypot" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_registration_honeypot')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 300 |
-
<
|
| 301 |
</td>
|
| 302 |
</tr>
|
| 303 |
</table>
|
| 129 |
//Display security info badge
|
| 130 |
$aiowps_feature_mgr->output_feature_details_badge("manually-approve-registrations");
|
| 131 |
$blog_id = get_current_blog_id();
|
| 132 |
+
if (is_multisite() && !is_main_site( $blog_id ))
|
| 133 |
{
|
| 134 |
//Hide config settings if MS and not main site
|
| 135 |
AIOWPSecurity_Utility::display_multisite_message();
|
| 141 |
<tr valign="top">
|
| 142 |
<th scope="row"><?php _e('Enable manual approval of new registrations', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 143 |
<td>
|
| 144 |
+
<input id="aiowps_enable_manual_registration_approval" name="aiowps_enable_manual_registration_approval" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_manual_registration_approval')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 145 |
+
<label for="aiowps_enable_manual_registration_approval" class="description"><?php _e('Check this if you want to automatically disable all newly registered accounts so that you can approve them manually.', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 146 |
</td>
|
| 147 |
</tr>
|
| 148 |
</table>
|
| 213 |
<h3 class="hndle"><label for="title"><?php _e('Registration Page Captcha Settings', 'all-in-one-wp-security-and-firewall'); ?></label></h3>
|
| 214 |
<div class="inside">
|
| 215 |
<?php
|
| 216 |
+
if (is_multisite() && get_current_blog_id() != 1)
|
| 217 |
{
|
| 218 |
//Hide config settings if MS and not main site
|
| 219 |
$special_msg = '<div class="aio_yellow_box">';
|
| 235 |
<tr valign="top">
|
| 236 |
<th scope="row"><?php _e('Enable Captcha On Registration Page', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 237 |
<td>
|
| 238 |
+
<input id="aiowps_enable_registration_page_captcha" name="aiowps_enable_registration_page_captcha" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_registration_page_captcha')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 239 |
+
<label for="aiowps_enable_registration_page_captcha" class="description"><?php _e('Check this if you want to insert a captcha form on the WordPress user registration page (if you allow user registration).', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 240 |
</td>
|
| 241 |
</tr>
|
| 242 |
</table>
|
| 296 |
<tr valign="top">
|
| 297 |
<th scope="row"><?php _e('Enable Honeypot On Registration Page', 'all-in-one-wp-security-and-firewall')?>:</th>
|
| 298 |
<td>
|
| 299 |
+
<input id="aiowps_enable_registration_honeypot" name="aiowps_enable_registration_honeypot" type="checkbox"<?php if($aio_wp_security->configs->get_value('aiowps_enable_registration_honeypot')=='1') echo ' checked="checked"'; ?> value="1"/>
|
| 300 |
+
<label for="aiowps_enable_registration_honeypot" class="description"><?php _e('Check this if you want to enable the honeypot feature for the registration page', 'all-in-one-wp-security-and-firewall'); ?></label>
|
| 301 |
</td>
|
| 302 |
</tr>
|
| 303 |
</table>
|
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
namespace AIOWPS\Firewall;
|
| 3 |
+
|
| 4 |
+
/**
|
| 5 |
+
* Our list of families
|
| 6 |
+
*/
|
| 7 |
+
return array(
|
| 8 |
+
array('name' => '6G', 'priority' => 0),
|
| 9 |
+
);
|
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
namespace AIOWPS\Firewall;
|
| 3 |
+
|
| 4 |
+
/**
|
| 5 |
+
* Builds all our families
|
| 6 |
+
*/
|
| 7 |
+
class Family_Builder {
|
| 8 |
+
|
| 9 |
+
/**
|
| 10 |
+
* Get our families sorted by priority
|
| 11 |
+
*
|
| 12 |
+
* @return array
|
| 13 |
+
*/
|
| 14 |
+
public static function get_families() {
|
| 15 |
+
|
| 16 |
+
$family_list = include(AIOWPS_FIREWALL_DIR.'/family/wp-security-firewall-families.php');
|
| 17 |
+
|
| 18 |
+
//Prioritise the families
|
| 19 |
+
usort($family_list, function($member, $member2) {
|
| 20 |
+
if ($member['priority'] == $member2['priority']) {
|
| 21 |
+
return 0;
|
| 22 |
+
}
|
| 23 |
+
return ($member['priority'] > $member2['priority']) ? 1 : -1;
|
| 24 |
+
});
|
| 25 |
+
|
| 26 |
+
$families = array();
|
| 27 |
+
foreach ($family_list as $member) {
|
| 28 |
+
$families[strtolower($member['name'])] = new Family($member['name'], $member['priority']);
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
return $families;
|
| 32 |
+
}
|
| 33 |
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
namespace AIOWPS\Firewall;
|
| 3 |
+
|
| 4 |
+
/**
|
| 5 |
+
* Holds all our families
|
| 6 |
+
*/
|
| 7 |
+
class Family_Collection {
|
| 8 |
+
|
| 9 |
+
/**
|
| 10 |
+
* Holds our families
|
| 11 |
+
*
|
| 12 |
+
* @var array
|
| 13 |
+
*/
|
| 14 |
+
protected $families;
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
/**
|
| 18 |
+
* Constructs our family collection object
|
| 19 |
+
*
|
| 20 |
+
* @param array $families - The sorted families to contain
|
| 21 |
+
*/
|
| 22 |
+
public function __construct($families = array()) {
|
| 23 |
+
$this->families = $families;
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
/**
|
| 27 |
+
* Generator method to iterate over the familes
|
| 28 |
+
*
|
| 29 |
+
* @return iterable
|
| 30 |
+
*/
|
| 31 |
+
public function get_family() {
|
| 32 |
+
foreach ($this->families as $family) {
|
| 33 |
+
yield $family;
|
| 34 |
+
}
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
/**
|
| 38 |
+
* Adds a new rule to a family member
|
| 39 |
+
*
|
| 40 |
+
* @param Rule $rule - an active rule to add to its family
|
| 41 |
+
* @return void
|
| 42 |
+
*/
|
| 43 |
+
public function add_rule_to_member(Rule $rule) {
|
| 44 |
+
$key = strtolower($rule->family);
|
| 45 |
+
if (array_key_exists($key, $this->families)) {
|
| 46 |
+
$this->families[$key]->add_rule($rule);
|
| 47 |
+
}
|
| 48 |
+
}
|
| 49 |
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
namespace AIOWPS\Firewall;
|
| 3 |
+
|
| 4 |
+
/**
|
| 5 |
+
* Represents a family (a grouping of rules)
|
| 6 |
+
*/
|
| 7 |
+
class Family {
|
| 8 |
+
|
| 9 |
+
/**
|
| 10 |
+
* Name of the family
|
| 11 |
+
*
|
| 12 |
+
* @var string
|
| 13 |
+
*/
|
| 14 |
+
public $name;
|
| 15 |
+
|
| 16 |
+
/**
|
| 17 |
+
* Priority of the family (0 is the highest)
|
| 18 |
+
*
|
| 19 |
+
* @var int
|
| 20 |
+
*/
|
| 21 |
+
public $priority;
|
| 22 |
+
|
| 23 |
+
/**
|
| 24 |
+
* List of rules to apply
|
| 25 |
+
*
|
| 26 |
+
* @var array
|
| 27 |
+
*/
|
| 28 |
+
protected $rules;
|
| 29 |
+
|
| 30 |
+
/**
|
| 31 |
+
* Builds our family object
|
| 32 |
+
*
|
| 33 |
+
* @param string $name
|
| 34 |
+
* @param integer $priority
|
| 35 |
+
*/
|
| 36 |
+
public function __construct($name, $priority = 999999) {
|
| 37 |
+
$this->name = $name;
|
| 38 |
+
$this->priority = $priority;
|
| 39 |
+
$this->rules = array();
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
/**
|
| 43 |
+
* Adds a rule to the family
|
| 44 |
+
*
|
| 45 |
+
* @param Rule $rule
|
| 46 |
+
* @return void
|
| 47 |
+
*/
|
| 48 |
+
public function add_rule(Rule $rule) {
|
| 49 |
+
$this->rules[] = $rule;
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
/**
|
| 53 |
+
* Applies all the rules in the family
|
| 54 |
+
*
|
| 55 |
+
* @return void
|
| 56 |
+
*/
|
| 57 |
+
public function apply_all() {
|
| 58 |
+
|
| 59 |
+
if (empty($this->rules)) {
|
| 60 |
+
return;
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
//ensure the rules are ordered by priority
|
| 64 |
+
usort($this->rules, function(Rule $rule, Rule $rule2) {
|
| 65 |
+
if ($rule->priority == $rule2->priority) {
|
| 66 |
+
return 0;
|
| 67 |
+
}
|
| 68 |
+
return ($rule->priority > $rule2->priority) ? 1 : -1;
|
| 69 |
+
});
|
| 70 |
+
|
| 71 |
+
foreach ($this->rules as $rule) {
|
| 72 |
+
$rule->apply();
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
/**
|
| 78 |
+
* Returns the family name if used as a string
|
| 79 |
+
*
|
| 80 |
+
* @return string
|
| 81 |
+
*/
|
| 82 |
+
public function __toString() {
|
| 83 |
+
return $this->name;
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
namespace AIOWPS\Firewall;
|
| 3 |
+
|
| 4 |
+
/**
|
| 5 |
+
* Gives us access to our firewall's config
|
| 6 |
+
*/
|
| 7 |
+
class Config {
|
| 8 |
+
|
| 9 |
+
/**
|
| 10 |
+
* The path to our config file
|
| 11 |
+
*
|
| 12 |
+
* @var string
|
| 13 |
+
*/
|
| 14 |
+
protected $path;
|
| 15 |
+
|
| 16 |
+
/**
|
| 17 |
+
* Constructs object
|
| 18 |
+
*
|
| 19 |
+
* @param string $path
|
| 20 |
+
*/
|
| 21 |
+
public function __construct($path) {
|
| 22 |
+
$this->path = $path;
|
| 23 |
+
$this->init_file();
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
/**
|
| 27 |
+
* Initialise the file if it doesn't exist
|
| 28 |
+
*
|
| 29 |
+
* @return void
|
| 30 |
+
*/
|
| 31 |
+
private function init_file() {
|
| 32 |
+
clearstatcache();
|
| 33 |
+
if (!file_exists($this->path)) {
|
| 34 |
+
@file_put_contents($this->path, $this->get_file_content_prefix() . json_encode(array())); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- ignore this
|
| 35 |
+
}
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
/**
|
| 39 |
+
* Get the config file's prefix content. N.B. Some code assumes that this doesn't change, so review all consumers of this method before changing its output.
|
| 40 |
+
*
|
| 41 |
+
* @return string
|
| 42 |
+
*/
|
| 43 |
+
private function get_file_content_prefix() {
|
| 44 |
+
return "<?php __halt_compiler();\n";
|
| 45 |
+
}
|
| 46 |
+
|
| 47 |
+
/**
|
| 48 |
+
* Gets the value from the config array
|
| 49 |
+
*
|
| 50 |
+
* @param string $key
|
| 51 |
+
* @return mixed|null
|
| 52 |
+
*/
|
| 53 |
+
public function get_value($key) {
|
| 54 |
+
|
| 55 |
+
$contents = $this->get_contents();
|
| 56 |
+
|
| 57 |
+
if (null === $contents) {
|
| 58 |
+
return null;
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
if (!isset($contents[$key])) {
|
| 62 |
+
return null;
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
return $contents[$key];
|
| 66 |
+
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
/**
|
| 70 |
+
* Sets a value in our config array
|
| 71 |
+
*
|
| 72 |
+
* @param string $key
|
| 73 |
+
* @param mixed $value
|
| 74 |
+
* @return boolean
|
| 75 |
+
*/
|
| 76 |
+
public function set_value($key, $value) {
|
| 77 |
+
|
| 78 |
+
$contents = $this->get_contents();
|
| 79 |
+
|
| 80 |
+
if (null === $contents) {
|
| 81 |
+
return false;
|
| 82 |
+
}
|
| 83 |
+
|
| 84 |
+
$contents[$key] = $value;
|
| 85 |
+
|
| 86 |
+
return (false !== @file_put_contents($this->path, $this->get_file_content_prefix() . json_encode($contents), LOCK_EX)); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- ignore this
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
/**
|
| 92 |
+
* Loads the config array from file
|
| 93 |
+
*
|
| 94 |
+
* @return string
|
| 95 |
+
*/
|
| 96 |
+
private function get_contents() {
|
| 97 |
+
// __COMPILER_HALT_OFFSET__ doesn't define in a few PHP versions. It's a PHP bug.
|
| 98 |
+
// https://bugs.php.net/bug.php?id=70164
|
| 99 |
+
$contents = @file_get_contents($this->path, false, null, strlen($this->get_file_content_prefix())); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- ignore this
|
| 100 |
+
|
| 101 |
+
if (false === $contents) {
|
| 102 |
+
return null;
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
if (empty($contents)) {
|
| 106 |
+
return array();
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
return json_decode($contents, true);
|
| 110 |
+
}
|
| 111 |
+
|
| 112 |
+
/**
|
| 113 |
+
* Returns the path
|
| 114 |
+
*
|
| 115 |
+
* @return string
|
| 116 |
+
*/
|
| 117 |
+
public function __toString() {
|
| 118 |
+
return $this->path;
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
namespace AIOWPS\Firewall;
|
| 3 |
+
|
| 4 |
+
/**
|
| 5 |
+
* Trait which exits the current request
|
| 6 |
+
*/
|
| 7 |
+
trait Action_Exit_Trait {
|
| 8 |
+
|
| 9 |
+
/**
|
| 10 |
+
* Exit when the rule condition is satisfied.
|
| 11 |
+
*
|
| 12 |
+
* @return void
|
| 13 |
+
*/
|
| 14 |
+
public function do_action() {
|
| 15 |
+
exit();
|
| 16 |
+
}
|
| 17 |
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
namespace AIOWPS\Firewall;
|
| 3 |
+
|
| 4 |
+
/**
|
| 5 |
+
* Combines the forbid and exit trait
|
| 6 |
+
*/
|
| 7 |
+
trait Action_Forbid_and_Exit_Trait {
|
| 8 |
+
|
| 9 |
+
use Action_Forbid_Trait, Action_Exit_Trait {
|
| 10 |
+
Action_Forbid_Trait::do_action as protected do_action_forbid;
|
| 11 |
+
Action_Exit_Trait::do_action as protected do_action_exit;
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
/**
|
| 15 |
+
* Forbid 403 and Exit when the rule condition is satisfied.
|
| 16 |
+
*
|
| 17 |
+
* @return void
|
| 18 |
+
*/
|
| 19 |
+
public function do_action() {
|
| 20 |
+
$this->do_action_forbid();
|
| 21 |
+
$this->do_action_exit();
|
| 22 |
+
}
|
| 23 |
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
namespace AIOWPS\Firewall;
|
| 3 |
+
|
| 4 |
+
/**
|
| 5 |
+
* Trait to set the header to forbidden
|
| 6 |
+
*/
|
| 7 |
+
trait Action_Forbid_Trait {
|
| 8 |
+
|
| 9 |
+
/**
|
| 10 |
+
* Forbid 403 when the rule condition is satisfied.
|
| 11 |
+
*
|
| 12 |
+
* @return void
|
| 13 |
+
*/
|
| 14 |
+
public function do_action() {
|
| 15 |
+
header('HTTP/1.1 403 Forbidden');
|
| 16 |
+
}
|
| 17 |
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
namespace AIOWPS\Firewall;
|
| 3 |
+
|
| 4 |
+
/**
|
| 5 |
+
* Rule that blocks certain kinds of data from the query string
|
| 6 |
+
*/
|
| 7 |
+
class Rule_Block_Query_Strings_6g extends Rule {
|
| 8 |
+
|
| 9 |
+
/**
|
| 10 |
+
* Implements the action to be taken
|
| 11 |
+
*/
|
| 12 |
+
use Action_Forbid_and_Exit_Trait;
|
| 13 |
+
|
| 14 |
+
/**
|
| 15 |
+
* Construct our rule
|
| 16 |
+
*/
|
| 17 |
+
public function __construct() {
|
| 18 |
+
// Set the rule's metadata
|
| 19 |
+
$this->name = 'Block query strings';
|
| 20 |
+
$this->family = '6G';
|
| 21 |
+
$this->priority = 0;
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
/**
|
| 25 |
+
* Determines whether the rule is active
|
| 26 |
+
*
|
| 27 |
+
* @return boolean
|
| 28 |
+
*/
|
| 29 |
+
public function is_active() {
|
| 30 |
+
global $aiowps_firewall_config;
|
| 31 |
+
return (bool) $aiowps_firewall_config->get_value('aiowps_6g_block_query');
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
/**
|
| 35 |
+
* The condition to be satisfied for the rule to apply
|
| 36 |
+
*
|
| 37 |
+
* @return boolean
|
| 38 |
+
*/
|
| 39 |
+
public function is_satisfied() {
|
| 40 |
+
|
| 41 |
+
if (empty($_SERVER['QUERY_STRING'])) return !Rule::SATISFIED;
|
| 42 |
+
|
| 43 |
+
//Patterns to match against
|
| 44 |
+
$patterns = array(
|
| 45 |
+
'/[a-z0-9]{2000,}/i',
|
| 46 |
+
'/(eval\()/i',
|
| 47 |
+
'/(127\.0\.0\.1)/i',
|
| 48 |
+
'/(javascript:)(.*)(;)/i',
|
| 49 |
+
'/(base64_encode)(.*)(\()/i',
|
| 50 |
+
'/(GLOBALS|REQUEST)(=|\[|%)/i',
|
| 51 |
+
'/(<|%3C)(.*)script(.*)(>|%3)/i',
|
| 52 |
+
'#(\|\.\.\.|\.\./|~|`|<|>|\|)#i',
|
| 53 |
+
'#(boot\.ini|etc/passwd|self/environ)#i',
|
| 54 |
+
'/(thumbs?(_editor|open)?|tim(thumb)?)\.php/i',
|
| 55 |
+
'/(\'|\")(.*)(drop|insert|md5|select|union)/i',
|
| 56 |
+
);
|
| 57 |
+
|
| 58 |
+
return Rule_Utils::contains_pattern(rawurldecode($_SERVER['QUERY_STRING']), $patterns);
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
namespace AIOWPS\Firewall;
|
| 3 |
+
|
| 4 |
+
/**
|
| 5 |
+
* Rule that blocks certain referrers recommended by 6G
|
| 6 |
+
*/
|
| 7 |
+
class Rule_Block_Refs_6g extends Rule {
|
| 8 |
+
|
| 9 |
+
/**
|
| 10 |
+
* Implements the action to be taken
|
| 11 |
+
*/
|
| 12 |
+
use Action_Forbid_and_Exit_Trait;
|
| 13 |
+
|
| 14 |
+
/**
|
| 15 |
+
* Construct our rule
|
| 16 |
+
*/
|
| 17 |
+
public function __construct() {
|
| 18 |
+
// Set the rule's metadata
|
| 19 |
+
$this->name = 'Block referrer strings';
|
| 20 |
+
$this->family = '6G';
|
| 21 |
+
$this->priority = 0;
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
/**
|
| 25 |
+
* Determines whether the rule is active
|
| 26 |
+
*
|
| 27 |
+
* @return boolean
|
| 28 |
+
*/
|
| 29 |
+
public function is_active() {
|
| 30 |
+
global $aiowps_firewall_config;
|
| 31 |
+
return (bool) $aiowps_firewall_config->get_value('aiowps_6g_block_referrers');
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
/**
|
| 35 |
+
* The condition to be satisfied for the rule to apply
|
| 36 |
+
*
|
| 37 |
+
* @return boolean
|
| 38 |
+
*/
|
| 39 |
+
public function is_satisfied() {
|
| 40 |
+
|
| 41 |
+
if (empty($_SERVER['HTTP_REFERER'])) return !Rule::SATISFIED;
|
| 42 |
+
|
| 43 |
+
//Patterns to match against
|
| 44 |
+
$patterns = array(
|
| 45 |
+
'/[a-z0-9]{2000,}/i',
|
| 46 |
+
'/(semalt.com|todaperfeita)/i',
|
| 47 |
+
);
|
| 48 |
+
|
| 49 |
+
return Rule_Utils::contains_pattern($_SERVER['HTTP_REFERER'], $patterns);
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
namespace AIOWPS\Firewall;
|
| 3 |
+
|
| 4 |
+
/**
|
| 5 |
+
* Rule that blocks certain kinds of data from the request string
|
| 6 |
+
*/
|
| 7 |
+
class Rule_Block_Request_Strings_6g extends Rule {
|
| 8 |
+
|
| 9 |
+
/**
|
| 10 |
+
* Implements the action to be taken
|
| 11 |
+
*/
|
| 12 |
+
use Action_Forbid_and_Exit_Trait;
|
| 13 |
+
|
| 14 |
+
/**
|
| 15 |
+
* Construct our rule
|
| 16 |
+
*/
|
| 17 |
+
public function __construct() {
|
| 18 |
+
// Set the rule's metadata
|
| 19 |
+
$this->name = 'Block request strings';
|
| 20 |
+
$this->family = '6G';
|
| 21 |
+
$this->priority = 0;
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
/**
|
| 25 |
+
* Determines whether the rule is active
|
| 26 |
+
*
|
| 27 |
+
* @return boolean
|
| 28 |
+
*/
|
| 29 |
+
public function is_active() {
|
| 30 |
+
global $aiowps_firewall_config;
|
| 31 |
+
return (bool) $aiowps_firewall_config->get_value('aiowps_6g_block_request');
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
/**
|
| 35 |
+
* The condition to be satisfied for the rule to apply
|
| 36 |
+
*
|
| 37 |
+
* @return boolean
|
| 38 |
+
*/
|
| 39 |
+
public function is_satisfied() {
|
| 40 |
+
|
| 41 |
+
if (empty($_SERVER['PHP_SELF'])) return !Rule::SATISFIED;
|
| 42 |
+
|
| 43 |
+
//Patterns to match against
|
| 44 |
+
$patterns = array(
|
| 45 |
+
'/[a-z0-9]{2000,}/i',
|
| 46 |
+
'#(https?|ftp|php):/#i',
|
| 47 |
+
'#(base64_encode)(.*)(\()#i',
|
| 48 |
+
'#(=\'|=\%27|/\'/?)\.#i',
|
| 49 |
+
'#/(\$(\&)?|\*|\"|\.|,|&|&?)/?$#i',
|
| 50 |
+
'#(\{0\}|\(/\(|\.\.\.|\+\+\+|\\"\\")#i',
|
| 51 |
+
'#(~|`|<|>|:|;|,|%|\|\s|\{|\}|\[|\]|\|)#i',
|
| 52 |
+
'#/(=|\$&|_mm|cgi-|etc/passwd|muieblack)#i',
|
| 53 |
+
'#(&pws=0|_vti_|\(null\)|\{\$itemURL\}|echo(.*)kae|etc/passwd|eval\(|self/environ)#i',
|
| 54 |
+
'#\.(aspx?|bash|bak?|cfg|cgi|dll|exe|git|hg|ini|jsp|log|mdb|out|sql|svn|swp|tar|rar|rdf)$#i',
|
| 55 |
+
'#/(^$|(wp-)?config|mobiquo|phpinfo|shell|sqlpatch|thumb|thumb_editor|thumbopen|timthumb|webshell)\.php#i',
|
| 56 |
+
);
|
| 57 |
+
|
| 58 |
+
return Rule_Utils::contains_pattern(rawurldecode($_SERVER['PHP_SELF']), $patterns);
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
namespace AIOWPS\Firewall;
|
| 3 |
+
|
| 4 |
+
/**
|
| 5 |
+
* Rule that blocks certain user-agents recommended by 6G
|
| 6 |
+
*/
|
| 7 |
+
class Rule_Block_User_Agents_6g extends Rule {
|
| 8 |
+
|
| 9 |
+
/**
|
| 10 |
+
* Implements the action to be taken
|
| 11 |
+
*/
|
| 12 |
+
use Action_Forbid_and_Exit_Trait;
|
| 13 |
+
|
| 14 |
+
/**
|
| 15 |
+
* Construct our rule
|
| 16 |
+
*/
|
| 17 |
+
public function __construct() {
|
| 18 |
+
// Set the rule's metadata
|
| 19 |
+
$this->name = 'Block user-agents';
|
| 20 |
+
$this->family = '6G';
|
| 21 |
+
$this->priority = 0;
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
/**
|
| 25 |
+
* Determines whether the rule is active
|
| 26 |
+
*
|
| 27 |
+
* @return boolean
|
| 28 |
+
*/
|
| 29 |
+
public function is_active() {
|
| 30 |
+
global $aiowps_firewall_config;
|
| 31 |
+
return (bool) $aiowps_firewall_config->get_value('aiowps_6g_block_agents');
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
/**
|
| 35 |
+
* The condition to be satisfied for the rule to apply
|
| 36 |
+
*
|
| 37 |
+
* @return boolean
|
| 38 |
+
*/
|
| 39 |
+
public function is_satisfied() {
|
| 40 |
+
|
| 41 |
+
if (empty($_SERVER['HTTP_USER_AGENT'])) return !Rule::SATISFIED;
|
| 42 |
+
|
| 43 |
+
//Patterns to match against
|
| 44 |
+
$patterns = array(
|
| 45 |
+
'/[a-z0-9]{2000,}/i',
|
| 46 |
+
'/(archive.org|binlar|casper|checkpriv|choppy|clshttp|cmsworld|diavol|dotbot|extract|feedfinder|flicky|g00g1e|harvest|heritrix|httrack|kmccrew|loader|miner|nikto|nutch|planetwork|postrank|purebot|pycurl|python|seekerspider|siclab|skygrid|sqlmap|sucker|turnit|vikspider|winhttp|xxxyy|youda|zmeu|zune)/i',
|
| 47 |
+
);
|
| 48 |
+
|
| 49 |
+
return Rule_Utils::contains_pattern($_SERVER['HTTP_USER_AGENT'], $patterns);
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
namespace AIOWPS\Firewall;
|
| 3 |
+
|
| 4 |
+
/**
|
| 5 |
+
* Rule that blocks certain kinds of HTTP request methods (e.g DEBUG or PUT)
|
| 6 |
+
*/
|
| 7 |
+
class Rule_Request_Method_6g extends Rule {
|
| 8 |
+
|
| 9 |
+
/**
|
| 10 |
+
* Implements the action to be taken
|
| 11 |
+
*/
|
| 12 |
+
use Action_Forbid_and_Exit_Trait;
|
| 13 |
+
|
| 14 |
+
/**
|
| 15 |
+
* List of request methods to block
|
| 16 |
+
*
|
| 17 |
+
* @var array
|
| 18 |
+
*/
|
| 19 |
+
private $blocked_methods;
|
| 20 |
+
|
| 21 |
+
/**
|
| 22 |
+
* Construct our rule
|
| 23 |
+
*/
|
| 24 |
+
public function __construct() {
|
| 25 |
+
global $aiowps_firewall_config;
|
| 26 |
+
|
| 27 |
+
// Set the rule's metadata
|
| 28 |
+
$this->name = 'Block request methods';
|
| 29 |
+
$this->family = '6G';
|
| 30 |
+
$this->priority = 0;
|
| 31 |
+
|
| 32 |
+
$this->blocked_methods = $aiowps_firewall_config->get_value('aiowps_6g_block_request_methods');
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
/**
|
| 36 |
+
* Determines whether the rule is active
|
| 37 |
+
*
|
| 38 |
+
* @return boolean
|
| 39 |
+
*/
|
| 40 |
+
public function is_active() {
|
| 41 |
+
return !empty($this->blocked_methods);
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
/**
|
| 45 |
+
* The condition to be satisfied for the rule to apply
|
| 46 |
+
*
|
| 47 |
+
* @return boolean
|
| 48 |
+
*/
|
| 49 |
+
public function is_satisfied() {
|
| 50 |
+
return in_array(strtoupper($_SERVER['REQUEST_METHOD']), $this->blocked_methods);
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
namespace AIOWPS\Firewall;
|
| 3 |
+
|
| 4 |
+
/**
|
| 5 |
+
* Builds our rules
|
| 6 |
+
*/
|
| 7 |
+
class Rule_Builder {
|
| 8 |
+
|
| 9 |
+
/**
|
| 10 |
+
* Gets our rule if it's active
|
| 11 |
+
*
|
| 12 |
+
* @return iterable
|
| 13 |
+
*/
|
| 14 |
+
public static function get_active_rule() {
|
| 15 |
+
|
| 16 |
+
foreach (self::get_rule_classname() as $classname) {
|
| 17 |
+
|
| 18 |
+
$rule = new $classname();
|
| 19 |
+
|
| 20 |
+
if (!$rule->is_active()) {
|
| 21 |
+
continue;
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
yield $rule;
|
| 25 |
+
}
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
/**
|
| 29 |
+
* Generates the classname for each rule
|
| 30 |
+
*
|
| 31 |
+
* @return iterable
|
| 32 |
+
*/
|
| 33 |
+
private static function get_rule_classname() {
|
| 34 |
+
|
| 35 |
+
$handle = opendir(AIOWPS_FIREWALL_DIR.'/rule/rules/');
|
| 36 |
+
if ($handle) {
|
| 37 |
+
while (false !== ($entry = readdir($handle))) {
|
| 38 |
+
$matches = array();
|
| 39 |
+
if (preg_match('/^rule-(.*)\.php$/', $entry, $matches)) {
|
| 40 |
+
yield "AIOWPS\Firewall\Rule_".ucwords(str_replace('-', '_', $matches[1]), '_');
|
| 41 |
+
}
|
| 42 |
+
}
|
| 43 |
+
@closedir($handle); //phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
}
|
| 47 |
+
|
| 48 |
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
namespace AIOWPS\Firewall;
|
| 3 |
+
|
| 4 |
+
/**
|
| 5 |
+
* Utility methods to help with the rules
|
| 6 |
+
*/
|
| 7 |
+
class Rule_Utils {
|
| 8 |
+
|
| 9 |
+
/**
|
| 10 |
+
* Check if the subject contains the given pattern or patterns
|
| 11 |
+
*
|
| 12 |
+
* @param string $subject - The subject we wish to check the pattern or patterns against.
|
| 13 |
+
* @param string|array $pattern - Regex pattern. An array for multiple patterns; a string otherwise.
|
| 14 |
+
* @return boolean
|
| 15 |
+
*/
|
| 16 |
+
public static function contains_pattern($subject, $pattern) {
|
| 17 |
+
|
| 18 |
+
if (is_string($pattern)) return (1 === preg_match($pattern, $subject));
|
| 19 |
+
|
| 20 |
+
if (!is_array($pattern)) return false;
|
| 21 |
+
|
| 22 |
+
foreach ($pattern as $patt) {
|
| 23 |
+
if (preg_match($patt, $subject)) return true;
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
return false;
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
namespace AIOWPS\Firewall;
|
| 3 |
+
|
| 4 |
+
/**
|
| 5 |
+
* Base class for our firewall rules
|
| 6 |
+
*/
|
| 7 |
+
abstract class Rule {
|
| 8 |
+
|
| 9 |
+
/**
|
| 10 |
+
* Name of the rule
|
| 11 |
+
*
|
| 12 |
+
* @var string
|
| 13 |
+
*/
|
| 14 |
+
public $name;
|
| 15 |
+
|
| 16 |
+
/**
|
| 17 |
+
* Name of the family the rule belongs to
|
| 18 |
+
*
|
| 19 |
+
* @var string
|
| 20 |
+
*/
|
| 21 |
+
public $family;
|
| 22 |
+
|
| 23 |
+
/**
|
| 24 |
+
* Rule's priority (0 is the highest)
|
| 25 |
+
*
|
| 26 |
+
* @var int
|
| 27 |
+
*/
|
| 28 |
+
public $priority;
|
| 29 |
+
|
| 30 |
+
/**
|
| 31 |
+
* An abstraction for when the rule is satisfied
|
| 32 |
+
*
|
| 33 |
+
* @var boolean
|
| 34 |
+
*/
|
| 35 |
+
const SATISFIED = true;
|
| 36 |
+
|
| 37 |
+
/**
|
| 38 |
+
* Executes the rule's action
|
| 39 |
+
*
|
| 40 |
+
* @return void
|
| 41 |
+
*/
|
| 42 |
+
abstract public function do_action();
|
| 43 |
+
|
| 44 |
+
/**
|
| 45 |
+
* Check if the rule is active
|
| 46 |
+
*
|
| 47 |
+
* @return boolean
|
| 48 |
+
*/
|
| 49 |
+
abstract public function is_active();
|
| 50 |
+
|
| 51 |
+
/**
|
| 52 |
+
* Check if the rule has been satisfied
|
| 53 |
+
*
|
| 54 |
+
* @return boolean
|
| 55 |
+
*/
|
| 56 |
+
abstract public function is_satisfied();
|
| 57 |
+
|
| 58 |
+
/**
|
| 59 |
+
* Apply the rule and execute the action if satisfied
|
| 60 |
+
*
|
| 61 |
+
* @return void
|
| 62 |
+
*/
|
| 63 |
+
public function apply() {
|
| 64 |
+
|
| 65 |
+
if ($this->is_satisfied()) {
|
| 66 |
+
|
| 67 |
+
if (defined('AIOS_FIREWALL_DEBUG') && AIOS_FIREWALL_DEBUG) {
|
| 68 |
+
error_log("AIOS firewall rule triggered: {$this->name}");
|
| 69 |
+
|
| 70 |
+
if (defined('AIOS_FIREWALL_SERVER_DUMP') && AIOS_FIREWALL_SERVER_DUMP) {
|
| 71 |
+
error_log(print_r($_SERVER, true));
|
| 72 |
+
}
|
| 73 |
+
}
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
$this->do_action();
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
/**
|
| 83 |
+
* Show the rule's name
|
| 84 |
+
*
|
| 85 |
+
* @return string
|
| 86 |
+
*/
|
| 87 |
+
public function __toString() {
|
| 88 |
+
return $this->name;
|
| 89 |
+
}
|
| 90 |
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
namespace AIOWPS\Firewall;
|
| 3 |
+
|
| 4 |
+
if (!defined('AIOWPS_FIREWALL_DIR')) {
|
| 5 |
+
header('HTTP/1.1 403 Forbidden');
|
| 6 |
+
exit();
|
| 7 |
+
}
|
| 8 |
+
|
| 9 |
+
/**
|
| 10 |
+
* Loads and executes our firewall
|
| 11 |
+
*/
|
| 12 |
+
class Loader {
|
| 13 |
+
|
| 14 |
+
/**
|
| 15 |
+
* Reference to itself
|
| 16 |
+
*
|
| 17 |
+
* @var Loader
|
| 18 |
+
*/
|
| 19 |
+
protected static $instance = null;
|
| 20 |
+
|
| 21 |
+
/**
|
| 22 |
+
* Loads and builds all the necessary files
|
| 23 |
+
*
|
| 24 |
+
* @return void
|
| 25 |
+
*/
|
| 26 |
+
public function load_firewall() {
|
| 27 |
+
|
| 28 |
+
try {
|
| 29 |
+
|
| 30 |
+
$this->init_includes();
|
| 31 |
+
$this->init_services();
|
| 32 |
+
|
| 33 |
+
$families = new Family_Collection(Family_Builder::get_families());
|
| 34 |
+
|
| 35 |
+
foreach (Rule_Builder::get_active_rule() as $rule) {
|
| 36 |
+
$families->add_rule_to_member($rule);
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
foreach ($families->get_family() as $member) {
|
| 40 |
+
$member->apply_all();
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
} catch (\Exception $e) {
|
| 44 |
+
$this->log_message($e->getMessage());
|
| 45 |
+
} catch (\Error $e) {
|
| 46 |
+
$this->log_message($e->getMessage());
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
/**
|
| 52 |
+
* Log our error messages
|
| 53 |
+
*
|
| 54 |
+
* @param string $message
|
| 55 |
+
* @return void
|
| 56 |
+
*/
|
| 57 |
+
private function log_message($message) {
|
| 58 |
+
if (function_exists('do_action')) {
|
| 59 |
+
do_action('aios_firewall_loader_log_error', $message, $this);
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
error_log('AIOS firewall error: ' . $message);
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
/**
|
| 66 |
+
* Initialises our services
|
| 67 |
+
*
|
| 68 |
+
* @return void
|
| 69 |
+
*/
|
| 70 |
+
private function init_services() {
|
| 71 |
+
|
| 72 |
+
$workspace = $this->get_firewall_workspace();
|
| 73 |
+
|
| 74 |
+
if (empty($workspace)) {
|
| 75 |
+
throw new \Exception('unable to locate workspace.');
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
$GLOBALS['aiowps_firewall_config'] = new Config($workspace . 'settings.php');
|
| 79 |
+
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
/**
|
| 83 |
+
* Get our workspace directory (i.e: where we save and load data to and from)
|
| 84 |
+
*
|
| 85 |
+
* @return string
|
| 86 |
+
*/
|
| 87 |
+
private function get_firewall_workspace() {
|
| 88 |
+
global $aiowps_firewall_rules_path;
|
| 89 |
+
$workspace = '';
|
| 90 |
+
|
| 91 |
+
if (!empty($aiowps_firewall_rules_path)) {
|
| 92 |
+
$workspace = $aiowps_firewall_rules_path;
|
| 93 |
+
} else {
|
| 94 |
+
//Are we in a WordPress context?
|
| 95 |
+
if (function_exists('wp_get_upload_dir')) {
|
| 96 |
+
$upload_dir_info = wp_get_upload_dir();
|
| 97 |
+
$firewall_rules_path = trailingslashit($upload_dir_info['basedir'].'/aios/firewall-rules');
|
| 98 |
+
$workspace = wp_normalize_path($firewall_rules_path);
|
| 99 |
+
}
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
return $workspace;
|
| 103 |
+
}
|
| 104 |
+
|
| 105 |
+
/**
|
| 106 |
+
* Registers the autoloader
|
| 107 |
+
*
|
| 108 |
+
* @return void
|
| 109 |
+
*/
|
| 110 |
+
private function init_includes() {
|
| 111 |
+
|
| 112 |
+
spl_autoload_register(function($class) {
|
| 113 |
+
if (0 === strpos($class, "AIOWPS\\Firewall\\")) { //only autoload the firewall's files
|
| 114 |
+
|
| 115 |
+
$relative_classname = substr($class, strlen("AIOWPS\\Firewall\\"), strlen($class)-1);
|
| 116 |
+
|
| 117 |
+
$classname = str_replace('_', '-', strtolower($relative_classname));
|
| 118 |
+
|
| 119 |
+
$file = "wp-security-firewall-{$classname}.php";
|
| 120 |
+
$rule = "{$classname}.php";
|
| 121 |
+
|
| 122 |
+
$paths = array(
|
| 123 |
+
AIOWPS_FIREWALL_DIR."/{$file}",
|
| 124 |
+
AIOWPS_FIREWALL_DIR."/family/{$file}",
|
| 125 |
+
AIOWPS_FIREWALL_DIR."/rule/{$file}",
|
| 126 |
+
AIOWPS_FIREWALL_DIR."/rule/actions/{$classname}.php",
|
| 127 |
+
AIOWPS_FIREWALL_DIR."/rule/rules/{$rule}",
|
| 128 |
+
AIOWPS_FIREWALL_DIR."/libs/{$file}",
|
| 129 |
+
);
|
| 130 |
+
|
| 131 |
+
clearstatcache();
|
| 132 |
+
foreach ($paths as $path) {
|
| 133 |
+
if (file_exists($path)) {
|
| 134 |
+
include_once($path);
|
| 135 |
+
break;
|
| 136 |
+
}
|
| 137 |
+
}
|
| 138 |
+
}
|
| 139 |
+
});
|
| 140 |
+
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
/**
|
| 144 |
+
* Gets or creates an instance of this object
|
| 145 |
+
*
|
| 146 |
+
* @return Loader
|
| 147 |
+
*/
|
| 148 |
+
public static function get_instance() {
|
| 149 |
+
|
| 150 |
+
if (null === self::$instance) {
|
| 151 |
+
return new self();
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
return self::$instance;
|
| 155 |
+
}
|
| 156 |
+
|
| 157 |
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
/**
|
| 3 |
+
* This is the file that will be loaded using auto_prepend_file directive
|
| 4 |
+
*/
|
| 5 |
+
|
| 6 |
+
if (!defined('AIOWPS_FIREWALL_DIR')) {
|
| 7 |
+
define('AIOWPS_FIREWALL_DIR', dirname(__FILE__));
|
| 8 |
+
}
|
| 9 |
+
|
| 10 |
+
if (!defined('AIOWPSEC_FIREWALL_DONE')) {
|
| 11 |
+
|
| 12 |
+
//Gracefully handle if the file is unable to be included. (i.e: ensure the user's site does not crash)
|
| 13 |
+
if (!(@include_once AIOWPS_FIREWALL_DIR . '/wp-security-firewall-loader.php')) {
|
| 14 |
+
error_log('AIOS firewall error: unable to load the firewall.');
|
| 15 |
+
return;
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
\AIOWPS\Firewall\Loader::get_instance()->load_firewall();
|
| 19 |
+
define('AIOWPSEC_FIREWALL_DONE', true);
|
| 20 |
+
}
|
|
@@ -72,10 +72,10 @@ class AIOWPSecurity_Feature_Item_Manager {
|
|
| 72 |
$this->feature_items[] = new AIOWPSecurity_Feature_Item("registration-honeypot", __("Enable Registration Honeypot", "all-in-one-wp-security-and-firewall"), $this->feature_point_2, $this->sec_level_inter);
|
| 73 |
|
| 74 |
//Database Security Menu Features
|
| 75 |
-
//
|
| 76 |
-
$this->feature_items[] = new AIOWPSecurity_Feature_Item("db-security-db-prefix", __("
|
| 77 |
-
//
|
| 78 |
-
$this->feature_items[] = new AIOWPSecurity_Feature_Item("db-security-db-backup", __("
|
| 79 |
|
| 80 |
//File System Security Menu Features
|
| 81 |
//File Permissions
|
|
@@ -102,6 +102,9 @@ class AIOWPSecurity_Feature_Item_Manager {
|
|
| 102 |
//Login Honeypot
|
| 103 |
$this->feature_items[] = new AIOWPSecurity_Feature_Item("login-honeypot", __("Enable Login Honeypot", "all-in-one-wp-security-and-firewall"), $this->feature_point_2, $this->sec_level_inter);
|
| 104 |
|
|
|
|
|
|
|
|
|
|
| 105 |
//Additional and Advanced firewall
|
| 106 |
$this->feature_items[] = new AIOWPSecurity_Feature_Item("firewall-enable-brute-force-attack-prevention", __("Enable Brute Force Attack Prevention", "all-in-one-wp-security-and-firewall"), $this->feature_point_4, $this->sec_level_advanced);
|
| 107 |
$this->feature_items[] = new AIOWPSecurity_Feature_Item("firewall-disable-index-views", __("Disable Index Views", "all-in-one-wp-security-and-firewall"), $this->feature_point_1, $this->sec_level_inter);
|
|
@@ -311,6 +314,10 @@ class AIOWPSecurity_Feature_Item_Manager {
|
|
| 311 |
if ("login-honeypot" == $item->feature_id) {
|
| 312 |
$this->check_enable_login_honeypot_feature($item);
|
| 313 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
| 314 |
|
| 315 |
if ("block-spambots" == $item->feature_id) {
|
| 316 |
$this->check_enable_block_spambots_feature($item);
|
|
@@ -702,6 +709,22 @@ class AIOWPSecurity_Feature_Item_Manager {
|
|
| 702 |
$item->set_feature_status($this->feature_inactive);
|
| 703 |
}
|
| 704 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 705 |
|
| 706 |
public function check_enable_block_spambots_feature($item) {
|
| 707 |
global $aio_wp_security;
|
| 72 |
$this->feature_items[] = new AIOWPSecurity_Feature_Item("registration-honeypot", __("Enable Registration Honeypot", "all-in-one-wp-security-and-firewall"), $this->feature_point_2, $this->sec_level_inter);
|
| 73 |
|
| 74 |
//Database Security Menu Features
|
| 75 |
+
//Database prefix
|
| 76 |
+
$this->feature_items[] = new AIOWPSecurity_Feature_Item("db-security-db-prefix", __("Database prefix", "all-in-one-wp-security-and-firewall"), $this->feature_point_2, $this->sec_level_inter);
|
| 77 |
+
//Database backup
|
| 78 |
+
$this->feature_items[] = new AIOWPSecurity_Feature_Item("db-security-db-backup", __("Database backup", "all-in-one-wp-security-and-firewall"), $this->feature_point_4, $this->sec_level_basic);
|
| 79 |
|
| 80 |
//File System Security Menu Features
|
| 81 |
//File Permissions
|
| 102 |
//Login Honeypot
|
| 103 |
$this->feature_items[] = new AIOWPSecurity_Feature_Item("login-honeypot", __("Enable Login Honeypot", "all-in-one-wp-security-and-firewall"), $this->feature_point_2, $this->sec_level_inter);
|
| 104 |
|
| 105 |
+
//Disabled application password feture set points and level
|
| 106 |
+
$this->feature_items[] = new AIOWPSecurity_Feature_Item('disable-application-password', __('Disable Application Password', 'all-in-one-wp-security-and-firewall'), $this->feature_point_2, $this->sec_level_inter);
|
| 107 |
+
|
| 108 |
//Additional and Advanced firewall
|
| 109 |
$this->feature_items[] = new AIOWPSecurity_Feature_Item("firewall-enable-brute-force-attack-prevention", __("Enable Brute Force Attack Prevention", "all-in-one-wp-security-and-firewall"), $this->feature_point_4, $this->sec_level_advanced);
|
| 110 |
$this->feature_items[] = new AIOWPSecurity_Feature_Item("firewall-disable-index-views", __("Disable Index Views", "all-in-one-wp-security-and-firewall"), $this->feature_point_1, $this->sec_level_inter);
|
| 314 |
if ("login-honeypot" == $item->feature_id) {
|
| 315 |
$this->check_enable_login_honeypot_feature($item);
|
| 316 |
}
|
| 317 |
+
|
| 318 |
+
if ("disable-application-password" == $item->feature_id) {
|
| 319 |
+
$this->check_disable_application_password_feature($item);
|
| 320 |
+
}
|
| 321 |
|
| 322 |
if ("block-spambots" == $item->feature_id) {
|
| 323 |
$this->check_enable_block_spambots_feature($item);
|
| 709 |
$item->set_feature_status($this->feature_inactive);
|
| 710 |
}
|
| 711 |
}
|
| 712 |
+
|
| 713 |
+
/**
|
| 714 |
+
* Featurs list updated based on the disabled appliction password on or off
|
| 715 |
+
*
|
| 716 |
+
* @param object $item
|
| 717 |
+
* @global AIO_WP_Security $aio_wp_security
|
| 718 |
+
* @return void
|
| 719 |
+
*/
|
| 720 |
+
public function check_disable_application_password_feature($item) {
|
| 721 |
+
global $aio_wp_security;
|
| 722 |
+
if ('1' == $aio_wp_security->configs->get_value('aiowps_disable_application_password')) {
|
| 723 |
+
$item->set_feature_status($this->feature_active);
|
| 724 |
+
} else {
|
| 725 |
+
$item->set_feature_status($this->feature_inactive);
|
| 726 |
+
}
|
| 727 |
+
}
|
| 728 |
|
| 729 |
public function check_enable_block_spambots_feature($item) {
|
| 730 |
global $aio_wp_security;
|
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
if (!defined('ABSPATH')) {
|
| 3 |
+
exit; //Exit if accessed directly
|
| 4 |
+
}
|
| 5 |
+
|
| 6 |
+
/**
|
| 7 |
+
* All ids and static names, array.
|
| 8 |
+
*/
|
| 9 |
+
class AIOS_Abstracted_Ids {
|
| 10 |
+
|
| 11 |
+
/**
|
| 12 |
+
* Get firewall block request methods.
|
| 13 |
+
*
|
| 14 |
+
* @return array
|
| 15 |
+
*/
|
| 16 |
+
public static function get_firewall_block_request_methods() {
|
| 17 |
+
return array('DEBUG','MOVE', 'PUT', 'TRACK');
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
}
|
|
@@ -1,339 +0,0 @@
|
|
| 1 |
-
<?php
|
| 2 |
-
if (!defined('ABSPATH')) {
|
| 3 |
-
exit;//Exit if accessed directly
|
| 4 |
-
}
|
| 5 |
-
|
| 6 |
-
class AIOWPSecurity_Backup {
|
| 7 |
-
|
| 8 |
-
public $last_backup_file_name;//Stores the name of the last backup file when execute_backup function is called
|
| 9 |
-
|
| 10 |
-
public $last_backup_file_path;
|
| 11 |
-
|
| 12 |
-
public $last_backup_file_dir_multisite;
|
| 13 |
-
|
| 14 |
-
public function __construct() {
|
| 15 |
-
add_action('aiowps_perform_scheduled_backup_tasks', array($this, 'aiowps_scheduled_backup_handler'));
|
| 16 |
-
add_action('aiowps_perform_db_cleanup_tasks', array($this, 'aiowps_scheduled_db_cleanup_handler'));
|
| 17 |
-
}
|
| 18 |
-
|
| 19 |
-
/**
|
| 20 |
-
* Add slashes, sanitize end-of-line characters (?), wrap $value in quotation marks.
|
| 21 |
-
*
|
| 22 |
-
* @param string $value
|
| 23 |
-
* @return string
|
| 24 |
-
*/
|
| 25 |
-
public function sanitize_db_field($value) {
|
| 26 |
-
return '"' . preg_replace("/".PHP_EOL."/", "\n", addslashes($value)) . '"';
|
| 27 |
-
}
|
| 28 |
-
|
| 29 |
-
/**
|
| 30 |
-
* Write sql dump of $tables to backup file identified by $handle.
|
| 31 |
-
*
|
| 32 |
-
* @global wpdb $wpdb WordPress database abstraction object.
|
| 33 |
-
* @global AIO_WP_Security $aio_wp_security
|
| 34 |
-
* @param resource $handle
|
| 35 |
-
* @param array $tables
|
| 36 |
-
* @return boolean True, if database tables dump have been successfully written to the backup file, false otherwise.
|
| 37 |
-
*/
|
| 38 |
-
public function write_db_backup_file($handle, $tables) {
|
| 39 |
-
global $wpdb, $aio_wp_security;
|
| 40 |
-
|
| 41 |
-
// When importing the backup, tell database server that our data is in UTF-8...
|
| 42 |
-
// ...and that foreign key checks should be ignored.
|
| 43 |
-
$preamble
|
| 44 |
-
= "-- All In One WP Security & Firewall {$aio_wp_security->version}" . PHP_EOL . '-- MySQL dump' . PHP_EOL . '-- ' . date('Y-m-d H:i:s') . PHP_EOL . PHP_EOL . "SET NAMES utf8;" . PHP_EOL . "SET foreign_key_checks = 0;" . PHP_EOL . PHP_EOL;
|
| 45 |
-
if (!@fwrite($handle, $preamble)) {
|
| 46 |
-
return false;
|
| 47 |
-
}
|
| 48 |
-
|
| 49 |
-
// Loop through each table
|
| 50 |
-
foreach ($tables as $table) {
|
| 51 |
-
$table_name = $table[0];
|
| 52 |
-
|
| 53 |
-
$result_create_table = $wpdb->get_row('SHOW CREATE TABLE `' . $table_name . '`;', ARRAY_N);
|
| 54 |
-
if (empty($result_create_table)) {
|
| 55 |
-
$aio_wp_security->debug_logger->log_debug(__METHOD__ . " - get_row returned null for table: ".$table_name, 4);
|
| 56 |
-
return false; // Avoid incomplete backups
|
| 57 |
-
}
|
| 58 |
-
|
| 59 |
-
// Drop/create table preamble
|
| 60 |
-
$drop_and_create = 'DROP TABLE IF EXISTS `' . $table_name . '`;' . PHP_EOL . PHP_EOL . $result_create_table[1] . ";" . PHP_EOL . PHP_EOL;
|
| 61 |
-
if (!@fwrite($handle, $drop_and_create)) {
|
| 62 |
-
return false;
|
| 63 |
-
}
|
| 64 |
-
|
| 65 |
-
// Dump table contents
|
| 66 |
-
// Fetch results as row of objects to spare memory.
|
| 67 |
-
$result = $wpdb->get_results('SELECT * FROM `' . $table_name . '`;', OBJECT);
|
| 68 |
-
foreach ($result as $object_row) {
|
| 69 |
-
// Convert object row to array row: this is what $wpdb->get_results()
|
| 70 |
-
// internally does when invoked with ARRAY_N param, but in the process
|
| 71 |
-
// it creates new copy of entire results array that eats a lot of memory.
|
| 72 |
-
$row = array_values(get_object_vars($object_row));
|
| 73 |
-
// Start INSERT statement
|
| 74 |
-
if (!@fwrite($handle, 'INSERT INTO `' . $table_name . '` VALUES(')) {
|
| 75 |
-
return false;
|
| 76 |
-
}
|
| 77 |
-
// Loop through all fields and echo them out
|
| 78 |
-
foreach ($row as $idx => $field) {
|
| 79 |
-
// Echo fields separator (except for first loop)
|
| 80 |
-
if (($idx > 0) && !@fwrite($handle, ',')) {
|
| 81 |
-
return false;
|
| 82 |
-
}
|
| 83 |
-
// Echo field content (sanitized)
|
| 84 |
-
if (!@fwrite($handle, $this->sanitize_db_field($field))) {
|
| 85 |
-
return false;
|
| 86 |
-
}
|
| 87 |
-
}
|
| 88 |
-
// Finish INSERT statement
|
| 89 |
-
if (!@fwrite($handle, ");" . PHP_EOL)) {
|
| 90 |
-
return false;
|
| 91 |
-
}
|
| 92 |
-
}
|
| 93 |
-
// Place two-empty lines after table data
|
| 94 |
-
if (!@fwrite($handle, PHP_EOL . PHP_EOL)) {
|
| 95 |
-
return false;
|
| 96 |
-
}
|
| 97 |
-
}
|
| 98 |
-
|
| 99 |
-
return true;
|
| 100 |
-
}
|
| 101 |
-
|
| 102 |
-
/**
|
| 103 |
-
* This function will perform a database backup
|
| 104 |
-
*/
|
| 105 |
-
public function execute_backup() {
|
| 106 |
-
global $wpdb, $aio_wp_security;
|
| 107 |
-
$is_multi_site = function_exists('is_multisite') && is_multisite();
|
| 108 |
-
|
| 109 |
-
$execute_backup_memory_limit = apply_filters('aiowps_execute_backup_set_memory_limit', '512M');
|
| 110 |
-
@ini_set('memory_limit', $execute_backup_memory_limit);
|
| 111 |
-
|
| 112 |
-
if ($is_multi_site) {
|
| 113 |
-
//Let's get the current site's table prefix
|
| 114 |
-
$site_pref = esc_sql($wpdb->prefix);
|
| 115 |
-
$db_query = "SHOW TABLES LIKE '".$site_pref."%'";
|
| 116 |
-
$tables = $wpdb->get_results($db_query, ARRAY_N);
|
| 117 |
-
} else {
|
| 118 |
-
//get all of the tables
|
| 119 |
-
$tables = $wpdb->get_results('SHOW TABLES', ARRAY_N);
|
| 120 |
-
}
|
| 121 |
-
|
| 122 |
-
if (empty($tables)) {
|
| 123 |
-
$aio_wp_security->debug_logger->log_debug(__METHOD__ . " - no tables found!", 4);
|
| 124 |
-
return false;
|
| 125 |
-
}
|
| 126 |
-
|
| 127 |
-
//Check to see if the main "backups" directory exists - create it otherwise
|
| 128 |
-
|
| 129 |
-
$aiowps_backup_dir = WP_CONTENT_DIR.'/'.AIO_WP_SECURITY_BACKUPS_DIR_NAME;
|
| 130 |
-
if (!AIOWPSecurity_Utility_File::create_dir($aiowps_backup_dir)) {
|
| 131 |
-
$aio_wp_security->debug_logger->log_debug(__METHOD__ . " - Creation of DB backup directory failed!", 4);
|
| 132 |
-
return false;
|
| 133 |
-
}
|
| 134 |
-
|
| 135 |
-
//Generate a random prefix for more secure filenames
|
| 136 |
-
$random_suffix = AIOWPSecurity_Utility::generate_alpha_numeric_random_string(10);
|
| 137 |
-
|
| 138 |
-
if ($is_multi_site) {
|
| 139 |
-
global $current_blog;
|
| 140 |
-
$blog_id = $current_blog->blog_id;
|
| 141 |
-
//Get the current site name string for use later
|
| 142 |
-
$site_name = get_bloginfo('name');
|
| 143 |
-
|
| 144 |
-
$site_name = strtolower($site_name);
|
| 145 |
-
|
| 146 |
-
//make alphanumeric
|
| 147 |
-
$site_name = preg_replace("/[^a-z0-9_\s-]/", "", $site_name);
|
| 148 |
-
|
| 149 |
-
//Cleanup multiple instances of dashes or whitespaces
|
| 150 |
-
$site_name = preg_replace("/[\s-]+/", " ", $site_name);
|
| 151 |
-
|
| 152 |
-
//Convert whitespaces and underscore to dash
|
| 153 |
-
$site_name = preg_replace("/[\s_]/", "-", $site_name);
|
| 154 |
-
|
| 155 |
-
$file = 'database-backup-site-name-' . $site_name . '-' . current_time('Ymd-His') . '-' . $random_suffix;
|
| 156 |
-
|
| 157 |
-
//We will create a sub dir for the blog using its blog id
|
| 158 |
-
$dirpath = $aiowps_backup_dir . '/blogid_' . $blog_id;
|
| 159 |
-
|
| 160 |
-
//Create a subdirectory for this blog_id
|
| 161 |
-
if (!AIOWPSecurity_Utility_File::create_dir($dirpath)) {
|
| 162 |
-
$aio_wp_security->debug_logger->log_debug("Creation failed of DB backup directory for the following multisite blog ID: ".$blog_id, 4);
|
| 163 |
-
return false;
|
| 164 |
-
}
|
| 165 |
-
} else {
|
| 166 |
-
$dirpath = $aiowps_backup_dir;
|
| 167 |
-
$file = 'database-backup-' . current_time('Ymd-His') . '-' . $random_suffix;
|
| 168 |
-
}
|
| 169 |
-
|
| 170 |
-
$handle = @fopen($dirpath . '/' . $file . '.sql', 'w+');
|
| 171 |
-
|
| 172 |
-
if (false === $handle) {
|
| 173 |
-
$aio_wp_security->debug_logger->log_debug("Cannot create DB backup file: {$dirpath}/{$file}.sql", 4);
|
| 174 |
-
return false;
|
| 175 |
-
}
|
| 176 |
-
|
| 177 |
-
// Delete old backup files now to avoid polluting backups directory
|
| 178 |
-
// with incomplete backups on websites where max execution time is too
|
| 179 |
-
// low for database content to be written to a file:
|
| 180 |
-
// https://github.com/Arsenal21/all-in-one-wordpress-security/issues/62
|
| 181 |
-
$this->aiowps_delete_backup_files($dirpath);
|
| 182 |
-
|
| 183 |
-
$fw_res = $this->write_db_backup_file($handle, $tables);
|
| 184 |
-
@fclose($handle);
|
| 185 |
-
|
| 186 |
-
if (!$fw_res) {
|
| 187 |
-
@unlink($dirpath . '/' . $file . '.sql');
|
| 188 |
-
$aio_wp_security->debug_logger->log_debug(__METHOD__ . " - Write to DB backup file failed", 4);
|
| 189 |
-
return false;
|
| 190 |
-
}
|
| 191 |
-
|
| 192 |
-
//zip the file
|
| 193 |
-
if (class_exists('ZipArchive')) {
|
| 194 |
-
$zip = new ZipArchive();
|
| 195 |
-
$archive = $zip->open($dirpath . '/' . $file . '.zip', ZipArchive::CREATE);// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
| 196 |
-
$zip->addFile($dirpath . '/' . $file . '.sql', $file . '.sql');
|
| 197 |
-
$zip->close();
|
| 198 |
-
|
| 199 |
-
//delete .sql and keep zip
|
| 200 |
-
@unlink($dirpath . '/' . $file . '.sql');
|
| 201 |
-
$fileext = '.zip';
|
| 202 |
-
} else {
|
| 203 |
-
$fileext = '.sql';
|
| 204 |
-
}
|
| 205 |
-
$this->last_backup_file_name = $file . $fileext;//database-backup-YYYYMMDD-HHIISS-<random-string>.zip or database-backup-YYYYMMDD-HHIISS-<random-string>.sql
|
| 206 |
-
$this->last_backup_file_path = $dirpath . '/' . $file . $fileext;
|
| 207 |
-
if ($is_multi_site) {
|
| 208 |
-
$this->last_backup_file_dir_multisite = $aiowps_backup_dir . '/blogid_' . $blog_id;
|
| 209 |
-
}
|
| 210 |
-
|
| 211 |
-
$this->aiowps_send_backup_email(); //Send backup file via email if applicable
|
| 212 |
-
return true;
|
| 213 |
-
}
|
| 214 |
-
|
| 215 |
-
public function aiowps_send_backup_email() {
|
| 216 |
-
global $aio_wp_security;
|
| 217 |
-
if ($aio_wp_security->configs->get_value('aiowps_send_backup_email_address') == '1') {
|
| 218 |
-
//Get the right email address.
|
| 219 |
-
if (is_email($aio_wp_security->configs->get_value('aiowps_backup_email_address'))) {
|
| 220 |
-
$toaddress = $aio_wp_security->configs->get_value('aiowps_backup_email_address');
|
| 221 |
-
} else {
|
| 222 |
-
$toaddress = get_site_option('admin_email');
|
| 223 |
-
}
|
| 224 |
-
|
| 225 |
-
$to = $toaddress;
|
| 226 |
-
$site_title = get_bloginfo('name');
|
| 227 |
-
$from_name = empty($site_title) ? 'WordPress' : $site_title;
|
| 228 |
-
|
| 229 |
-
$headers = 'From: ' . $from_name . ' <' . get_option('admin_email') . '>' . PHP_EOL;
|
| 230 |
-
$subject = __('All In One WP Security - Site Database Backup', 'all-in-one-wp-security-and-firewall') . ' ' . date('l, F jS, Y \a\\t g:i a', current_time('timestamp'));
|
| 231 |
-
$attachment = array($this->last_backup_file_path);
|
| 232 |
-
$message = __('Attached is your latest DB backup file for site URL', 'all-in-one-wp-security-and-firewall') . ' ' . get_option('siteurl') . __(' generated on', 'all-in-one-wp-security-and-firewall') . ' ' . date('l, F jS, Y \a\\t g:i a', current_time('timestamp'));
|
| 233 |
-
|
| 234 |
-
$sendMail = wp_mail($to, $subject, $message, $headers, $attachment);
|
| 235 |
-
if (false === $sendMail) {
|
| 236 |
-
$aio_wp_security->debug_logger->log_debug("Backup notification email failed to send to ".$to, 4);
|
| 237 |
-
}
|
| 238 |
-
}
|
| 239 |
-
}
|
| 240 |
-
|
| 241 |
-
public function aiowps_delete_backup_files($backups_dir) {
|
| 242 |
-
global $aio_wp_security;
|
| 243 |
-
$files_to_keep = absint($aio_wp_security->configs->get_value('aiowps_backup_files_stored'));
|
| 244 |
-
if ($files_to_keep > 0) {
|
| 245 |
-
$aio_wp_security->debug_logger->log_debug(sprintf('DB Backup - Deleting all but %d latest backup file(s) in %s directory.', $files_to_keep, $backups_dir));
|
| 246 |
-
$files = AIOWPSecurity_Utility_File::scan_dir_sort_date($backups_dir);
|
| 247 |
-
$count = 0;
|
| 248 |
-
|
| 249 |
-
foreach ($files as $file) {
|
| 250 |
-
if (strpos($file, 'database-backup') !== false) {
|
| 251 |
-
if ($count >= $files_to_keep) {
|
| 252 |
-
@unlink($backups_dir . '/' . $file);
|
| 253 |
-
}
|
| 254 |
-
$count++;
|
| 255 |
-
}
|
| 256 |
-
}
|
| 257 |
-
} else {
|
| 258 |
-
$aio_wp_security->debug_logger->log_debug('DB Backup - Backup configuration prevents removal of old backup files!', 3);
|
| 259 |
-
}
|
| 260 |
-
}
|
| 261 |
-
|
| 262 |
-
public function aiowps_scheduled_backup_handler() {
|
| 263 |
-
global $aio_wp_security;
|
| 264 |
-
if ($aio_wp_security->configs->get_value('aiowps_enable_automated_backups')=='1') {
|
| 265 |
-
$aio_wp_security->debug_logger->log_debug_cron("DB Backup - Scheduled backup is enabled. Checking if a backup needs to be done now...");
|
| 266 |
-
$time_now = current_time('mysql');
|
| 267 |
-
$current_time = strtotime($time_now);
|
| 268 |
-
$backup_frequency = $aio_wp_security->configs->get_value('aiowps_db_backup_frequency'); //Number of hours or days or months interval per backup
|
| 269 |
-
$interval_setting = $aio_wp_security->configs->get_value('aiowps_db_backup_interval'); //Hours/Days/Months
|
| 270 |
-
switch ($interval_setting) {
|
| 271 |
-
case '0':
|
| 272 |
-
$interval = 'hours';
|
| 273 |
-
break;
|
| 274 |
-
case '1':
|
| 275 |
-
$interval = 'days';
|
| 276 |
-
break;
|
| 277 |
-
case '2':
|
| 278 |
-
$interval = 'weeks';
|
| 279 |
-
break;
|
| 280 |
-
default:
|
| 281 |
-
// Fall back to default value, if config is corrupted for some reason.
|
| 282 |
-
$interval = 'weeks';
|
| 283 |
-
break;
|
| 284 |
-
}
|
| 285 |
-
$last_backup_time = $aio_wp_security->configs->get_value('aiowps_last_backup_time');
|
| 286 |
-
if (null != $last_backup_time) {
|
| 287 |
-
$last_backup_time = strtotime($aio_wp_security->configs->get_value('aiowps_last_backup_time'));
|
| 288 |
-
$next_backup_time = strtotime("+".abs($backup_frequency).$interval, $last_backup_time);
|
| 289 |
-
if ($next_backup_time <= $current_time) {
|
| 290 |
-
//It's time to do a backup
|
| 291 |
-
$result = $this->execute_backup();
|
| 292 |
-
if ($result) {
|
| 293 |
-
$aio_wp_security->configs->set_value('aiowps_last_backup_time', $time_now);
|
| 294 |
-
$aio_wp_security->configs->save_config();
|
| 295 |
-
$aio_wp_security->debug_logger->log_debug_cron("DB Backup - Scheduled backup was successfully completed.");
|
| 296 |
-
} else {
|
| 297 |
-
$aio_wp_security->debug_logger->log_debug_cron("DB Backup - Scheduled backup operation failed!", 4);
|
| 298 |
-
}
|
| 299 |
-
}
|
| 300 |
-
} else {
|
| 301 |
-
//Set the last backup time to now so it can trigger for the next scheduled period
|
| 302 |
-
$aio_wp_security->configs->set_value('aiowps_last_backup_time', $time_now);
|
| 303 |
-
$aio_wp_security->configs->save_config();
|
| 304 |
-
}
|
| 305 |
-
}
|
| 306 |
-
}
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
public function aiowps_scheduled_db_cleanup_handler() {
|
| 310 |
-
//Check the events table because this can grow quite large especially when 404 events are being logged
|
| 311 |
-
$events_table_name = AIOWPSEC_TBL_EVENTS;
|
| 312 |
-
$max_rows_event_table = '5000'; //Keep a max of 5000 rows in the events table
|
| 313 |
-
$max_rows_event_table = apply_filters('aiowps_max_rows_event_table', $max_rows_event_table);
|
| 314 |
-
AIOWPSecurity_Utility::cleanup_table($events_table_name, $max_rows_event_table);
|
| 315 |
-
|
| 316 |
-
//Check the failed logins table
|
| 317 |
-
$failed_logins_table_name = AIOWPSEC_TBL_FAILED_LOGINS;
|
| 318 |
-
$max_rows_failed_logins_table = '5000'; //Keep a max of 5000 rows in the events table
|
| 319 |
-
$max_rows_failed_logins_table = apply_filters('aiowps_max_rows_failed_logins_table', $max_rows_failed_logins_table);
|
| 320 |
-
AIOWPSecurity_Utility::cleanup_table($failed_logins_table_name, $max_rows_failed_logins_table);
|
| 321 |
-
|
| 322 |
-
//Check the login activity table
|
| 323 |
-
$login_activity_table_name = AIOWPSEC_TBL_USER_LOGIN_ACTIVITY;
|
| 324 |
-
$max_rows_login_activity_table = '5000'; //Keep a max of 5000 rows in the events table
|
| 325 |
-
$max_rows_login_activity_table = apply_filters('aiowps_max_rows_login_attempts_table', $max_rows_login_activity_table);
|
| 326 |
-
AIOWPSecurity_Utility::cleanup_table($login_activity_table_name, $max_rows_login_activity_table);
|
| 327 |
-
|
| 328 |
-
//Check the global meta table
|
| 329 |
-
$global_meta_table_name = AIOWPSEC_TBL_GLOBAL_META_DATA;
|
| 330 |
-
$max_rows_global_meta_table = '5000'; //Keep a max of 5000 rows in this table
|
| 331 |
-
$max_rows_global_meta_table = apply_filters('aiowps_max_rows_global_meta_table', $max_rows_global_meta_table);
|
| 332 |
-
AIOWPSecurity_Utility::cleanup_table($global_meta_table_name, $max_rows_global_meta_table);
|
| 333 |
-
|
| 334 |
-
//Delete any expired _aiowps_captcha_string_info_xxxx option
|
| 335 |
-
AIOWPSecurity_Utility::delete_expired_captcha_options();
|
| 336 |
-
|
| 337 |
-
//Keep adding other DB cleanup tasks as they arise...
|
| 338 |
-
}
|
| 339 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
if (!defined('ABSPATH')) {
|
| 3 |
+
exit;//Exit if accessed directly
|
| 4 |
+
}
|
| 5 |
+
|
| 6 |
+
abstract class AIOWPSecurity_Base_Tasks {
|
| 7 |
+
/**
|
| 8 |
+
* Runs intended various tasks
|
| 9 |
+
* Handles single and multi-site (NW activation) cases
|
| 10 |
+
*
|
| 11 |
+
* @global type $wpdb
|
| 12 |
+
*/
|
| 13 |
+
public static function run() {
|
| 14 |
+
if (is_multisite()) {
|
| 15 |
+
global $wpdb;
|
| 16 |
+
// check if it is a network activation
|
| 17 |
+
$blog_ids = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs");
|
| 18 |
+
foreach ($blog_ids as $blog_id) {
|
| 19 |
+
switch_to_blog($blog_id);
|
| 20 |
+
static::run_for_a_site();
|
| 21 |
+
restore_current_blog();
|
| 22 |
+
}
|
| 23 |
+
} else {
|
| 24 |
+
static::run_for_a_site();
|
| 25 |
+
}
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
/**
|
| 29 |
+
* Run uninstallation task for a single site.
|
| 30 |
+
*
|
| 31 |
+
* @return void
|
| 32 |
+
*/
|
| 33 |
+
abstract protected static function run_for_a_site();
|
| 34 |
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
if (!defined('ABSPATH')) {
|
| 3 |
+
exit; //Exit if accessed directly
|
| 4 |
+
}
|
| 5 |
+
|
| 6 |
+
/**
|
| 7 |
+
* Firewall bootstrap file content block.
|
| 8 |
+
*/
|
| 9 |
+
class AIOWPSecurity_Block_Bootstrap extends AIOWPSecurity_Block_File {
|
| 10 |
+
|
| 11 |
+
/**
|
| 12 |
+
* Keeps track of our bootstrap file version
|
| 13 |
+
*
|
| 14 |
+
* @var string
|
| 15 |
+
*/
|
| 16 |
+
protected $version = '1.0.0';
|
| 17 |
+
|
| 18 |
+
/**
|
| 19 |
+
* Inserts our code into our bootstrap file.
|
| 20 |
+
*
|
| 21 |
+
* @return boolean|WP_Error
|
| 22 |
+
*/
|
| 23 |
+
public function insert_contents() {
|
| 24 |
+
$info = pathinfo($this->file_path);
|
| 25 |
+
|
| 26 |
+
if (!isset($info['dirname'])) {
|
| 27 |
+
return new WP_Error(
|
| 28 |
+
'file_no_directory',
|
| 29 |
+
'No directory has been set',
|
| 30 |
+
$this->file_path
|
| 31 |
+
);
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
if (!is_writable($info['dirname'])) {
|
| 35 |
+
return new WP_Error(
|
| 36 |
+
'file_directory_not_writable',
|
| 37 |
+
'The directory has incorrect write permissions. Please double check its permissions and try again.',
|
| 38 |
+
$info['dirname']
|
| 39 |
+
);
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
return (false !== @file_put_contents($this->file_path, $this->get_contents())); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- ignore this
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
/**
|
| 46 |
+
* Checks whether the bootstrap contents are valid
|
| 47 |
+
*
|
| 48 |
+
* @param string $contents
|
| 49 |
+
* @return boolean
|
| 50 |
+
*/
|
| 51 |
+
protected function is_content_valid($contents) {
|
| 52 |
+
|
| 53 |
+
//The regexes we extract the paths from
|
| 54 |
+
$regexes = array('/file_exists\(\'?(.*)\'?\)/isU', '/include_once\(\'?(.*)\'?\)/isU');
|
| 55 |
+
$firewall_path_str = $this->get_firewall_path_str();
|
| 56 |
+
|
| 57 |
+
foreach ($regexes as $regex) {
|
| 58 |
+
$matches = array();
|
| 59 |
+
$result = preg_match($regex, $contents, $matches);
|
| 60 |
+
|
| 61 |
+
if (empty($matches[1]) || false === $result) {
|
| 62 |
+
continue;
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
if ($firewall_path_str !== $matches[1]) {
|
| 66 |
+
return false;
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
return true;
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
/**
|
| 75 |
+
* Get the firewall path string that contains "__DIR__" for home dir, if plugin dir isn't a symbolic link..
|
| 76 |
+
*
|
| 77 |
+
* @return string The firewall path string.
|
| 78 |
+
*/
|
| 79 |
+
private function get_firewall_path_str() {
|
| 80 |
+
$firewall_path = AIOWPSecurity_Utility_Firewall::get_firewall_path();
|
| 81 |
+
$firewall_path_str = $this->get_path_str_for_given_absolute_path($firewall_path);
|
| 82 |
+
return $firewall_path_str;
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
/**
|
| 86 |
+
* Get path string to write bootstrap file from given path.
|
| 87 |
+
*
|
| 88 |
+
* @param string $path a path that we want to write to the bootstrap file.
|
| 89 |
+
* @return string The path that can be written in the bootstrap file.
|
| 90 |
+
*/
|
| 91 |
+
private function get_path_str_for_given_absolute_path($path) {
|
| 92 |
+
$home_path = AIOWPSecurity_Utility_File::get_home_path();
|
| 93 |
+
// If the plugin is symbolic linked, then the plugin's firewall path is not started with home_path.
|
| 94 |
+
$path_str = (0 === strpos($path, $home_path)) ? "__DIR__.'/".substr($path, strlen($home_path))."'" : "'".$path."'";
|
| 95 |
+
return $path_str;
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
/**
|
| 99 |
+
* Get the firewall rules path string that contains "__DIR__" for home dir, if plugin dir isn't a symbolic link.
|
| 100 |
+
*
|
| 101 |
+
* @return string The firewall rule path string.
|
| 102 |
+
*/
|
| 103 |
+
private function get_firewall_rules_path_str() {
|
| 104 |
+
$firewall_rules_path = AIOWPSecurity_Utility_Firewall::get_firewall_rules_path();
|
| 105 |
+
$firewall_rules_path_str = $this->get_path_str_for_given_absolute_path($firewall_rules_path);
|
| 106 |
+
return $firewall_rules_path_str;
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
/**
|
| 110 |
+
* The regex pattern that demarcates our contents
|
| 111 |
+
*
|
| 112 |
+
* @return string
|
| 113 |
+
*/
|
| 114 |
+
protected function get_regex_pattern() {
|
| 115 |
+
return '#// Begin AIOWPSEC Firewall(.*)// End AIOWPSEC Firewall#isU';
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
/**
|
| 119 |
+
* Bootstrap file contents to insert
|
| 120 |
+
*
|
| 121 |
+
* @return string
|
| 122 |
+
*/
|
| 123 |
+
public function get_contents() {
|
| 124 |
+
$firewall_path_str = $this->get_firewall_path_str();
|
| 125 |
+
$firewall_rules_path_str = $this->get_firewall_rules_path_str();
|
| 126 |
+
|
| 127 |
+
$code = "<?php\n";
|
| 128 |
+
$code .= $this->get_warning_message();
|
| 129 |
+
|
| 130 |
+
$directive = AIOWPSecurity_Utility_Firewall::get_already_set_directive();
|
| 131 |
+
|
| 132 |
+
if (!empty($directive) && $directive !== $this->file_path) {
|
| 133 |
+
$code .= "// Previously set auto_prepend_file\n";
|
| 134 |
+
$code .= "if (file_exists('{$directive}')) {\n";
|
| 135 |
+
$code .= "\tinclude_once('{$directive}');\n";
|
| 136 |
+
$code .= "}\n";
|
| 137 |
+
}
|
| 138 |
+
|
| 139 |
+
$code .= "\$aiowps_firewall_rules_path = {$firewall_rules_path_str};\n\n";
|
| 140 |
+
$code .= "// Begin AIOWPSEC Firewall\n";
|
| 141 |
+
$code .= "if (file_exists({$firewall_path_str})) {\n";
|
| 142 |
+
$code .= "\tinclude_once({$firewall_path_str});\n";
|
| 143 |
+
$code .= "}\n";
|
| 144 |
+
$code .= "// End AIOWPSEC Firewall\n";
|
| 145 |
+
|
| 146 |
+
return $code;
|
| 147 |
+
}
|
| 148 |
+
|
| 149 |
+
/**
|
| 150 |
+
* Gets our warning message for users
|
| 151 |
+
*
|
| 152 |
+
* @return string
|
| 153 |
+
*/
|
| 154 |
+
protected function get_warning_message() {
|
| 155 |
+
|
| 156 |
+
$warning = "/** \n";
|
| 157 |
+
$warning .= " * @version {$this->version}\n";
|
| 158 |
+
$warning .= " * WARNING: Please do not delete this file.\n";
|
| 159 |
+
$warning .= " * \n";
|
| 160 |
+
$warning .= " * This will cause PHP to throw a fatal error and render your site unusable.\n";
|
| 161 |
+
$warning .= " * \n";
|
| 162 |
+
$warning .= " * To safely delete this file, please check both your .user.ini file and your php.ini file and ensure this file is not set in the auto_prepend_file directive.\n";
|
| 163 |
+
$warning .= " * \n";
|
| 164 |
+
$warning .= " * Please ask your web hosting provider if you need guidance with executing the aforementioned steps.\n";
|
| 165 |
+
$warning .= " */\n";
|
| 166 |
+
|
| 167 |
+
return $warning;
|
| 168 |
+
}
|
| 169 |
+
}
|
|
@@ -0,0 +1,193 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
if (!defined('ABSPATH')) {
|
| 3 |
+
exit; //Exit if accessed directly
|
| 4 |
+
}
|
| 5 |
+
|
| 6 |
+
abstract class AIOWPSecurity_Block_File {
|
| 7 |
+
|
| 8 |
+
/**
|
| 9 |
+
* Full path to the file we're managing
|
| 10 |
+
*
|
| 11 |
+
* @var string
|
| 12 |
+
*/
|
| 13 |
+
protected $file_path;
|
| 14 |
+
|
| 15 |
+
/**
|
| 16 |
+
* Receives the full file path
|
| 17 |
+
*
|
| 18 |
+
* @param string $file_path
|
| 19 |
+
*/
|
| 20 |
+
public function __construct($file_path) {
|
| 21 |
+
$this->file_path = $file_path;
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
/**
|
| 25 |
+
* Insert the contents to the managed file
|
| 26 |
+
*
|
| 27 |
+
* @return boolean|WP_Error true if success; false if failed
|
| 28 |
+
*/
|
| 29 |
+
abstract public function insert_contents();
|
| 30 |
+
|
| 31 |
+
/**
|
| 32 |
+
* Returns the contents to be inserted into the managed file
|
| 33 |
+
*
|
| 34 |
+
* @return string
|
| 35 |
+
*/
|
| 36 |
+
abstract public function get_contents();
|
| 37 |
+
|
| 38 |
+
/**
|
| 39 |
+
* Returns the regex pattern that separates our contents from others the file may contain
|
| 40 |
+
*
|
| 41 |
+
* @return string
|
| 42 |
+
*/
|
| 43 |
+
abstract protected function get_regex_pattern();
|
| 44 |
+
|
| 45 |
+
/**
|
| 46 |
+
* Checks whether the file's contents are valid
|
| 47 |
+
*
|
| 48 |
+
* @param string $contents
|
| 49 |
+
* @return boolean
|
| 50 |
+
*/
|
| 51 |
+
abstract protected function is_content_valid($contents);
|
| 52 |
+
|
| 53 |
+
/**
|
| 54 |
+
* Updates the contents of the managed file
|
| 55 |
+
*
|
| 56 |
+
* @return boolean|WP_Error true if updated; false if not updated
|
| 57 |
+
*/
|
| 58 |
+
public function update_contents() {
|
| 59 |
+
|
| 60 |
+
if (!is_readable($this->file_path) || !is_writable($this->file_path)) {
|
| 61 |
+
return new WP_Error(
|
| 62 |
+
'file_wrong_permissions',
|
| 63 |
+
'The file has incorrect read or write permissions. Please double check its permissions and try again.',
|
| 64 |
+
$this->file_path
|
| 65 |
+
);
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
$contents = @file_get_contents($this->file_path); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- ignore this
|
| 69 |
+
|
| 70 |
+
if (false === $contents) {
|
| 71 |
+
return new WP_Error(
|
| 72 |
+
'file_unable_to_read',
|
| 73 |
+
'Unable to read the file. Please double check its permissions and try again.',
|
| 74 |
+
$this->file_path
|
| 75 |
+
);
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
$matches = array();
|
| 79 |
+
$match_count = preg_match_all($this->get_regex_pattern(), $contents, $matches);
|
| 80 |
+
|
| 81 |
+
if (empty($matches[1]) || false === $match_count) {
|
| 82 |
+
return false;
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
//checks whether an update is required
|
| 86 |
+
$requires_update = false;
|
| 87 |
+
$match = '';
|
| 88 |
+
foreach ($matches[1] as $match) {
|
| 89 |
+
|
| 90 |
+
$requires_update = !$this->is_content_valid($match);
|
| 91 |
+
|
| 92 |
+
if (true === $requires_update) {
|
| 93 |
+
break;
|
| 94 |
+
}
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
//perform the update
|
| 98 |
+
if ($requires_update) {
|
| 99 |
+
|
| 100 |
+
$block_removed = $this->remove_contents();
|
| 101 |
+
$block_inserted = $this->insert_contents();
|
| 102 |
+
|
| 103 |
+
return (true === $block_removed && true === $block_inserted);
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
return false;
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
/**
|
| 110 |
+
* Checks whether the file contains our contents
|
| 111 |
+
*
|
| 112 |
+
* @return boolean|WP_Error true if found; false if not found
|
| 113 |
+
*/
|
| 114 |
+
public function contains_contents() {
|
| 115 |
+
|
| 116 |
+
if (!is_readable($this->file_path)) {
|
| 117 |
+
return new WP_Error(
|
| 118 |
+
'file_wrong_permissions',
|
| 119 |
+
'The file has incorrect read permissions. Please double check its permissions and try again.',
|
| 120 |
+
$this->file_path
|
| 121 |
+
);
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
$contents = @file_get_contents($this->file_path); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- ignore this
|
| 125 |
+
|
| 126 |
+
if (false === $contents) {
|
| 127 |
+
return new WP_Error(
|
| 128 |
+
'file_unable_to_read',
|
| 129 |
+
'Unable to read the file. Please double check its permissions and try again.',
|
| 130 |
+
$this->file_path
|
| 131 |
+
);
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
return (1 === preg_match($this->get_regex_pattern(), $contents));
|
| 135 |
+
}
|
| 136 |
+
|
| 137 |
+
/**
|
| 138 |
+
* Removes our contents from the file
|
| 139 |
+
*
|
| 140 |
+
* @return boolean|WP_Error
|
| 141 |
+
*/
|
| 142 |
+
public function remove_contents() {
|
| 143 |
+
|
| 144 |
+
if (!is_readable($this->file_path) || !is_writable($this->file_path)) {
|
| 145 |
+
return new WP_Error(
|
| 146 |
+
'file_wrong_permissions',
|
| 147 |
+
'The file has incorrect read or write permissions. Please double check its permissions and try again.',
|
| 148 |
+
$this->file_path
|
| 149 |
+
);
|
| 150 |
+
}
|
| 151 |
+
|
| 152 |
+
$contents = @file_get_contents($this->file_path); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- ignore this
|
| 153 |
+
|
| 154 |
+
if (false === $contents) {
|
| 155 |
+
return new WP_Error(
|
| 156 |
+
'file_unable_to_read',
|
| 157 |
+
'Unable to read the file. Please double check its permissions and try again.',
|
| 158 |
+
$this->file_path
|
| 159 |
+
);
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
$removed = 0;
|
| 163 |
+
$contents = preg_replace($this->get_regex_pattern(), "", $contents, -1, $removed);
|
| 164 |
+
|
| 165 |
+
if (null === $contents) {
|
| 166 |
+
return new WP_Error(
|
| 167 |
+
'file_unable_to_alter',
|
| 168 |
+
'Unable to alter the file.',
|
| 169 |
+
$this->file_path
|
| 170 |
+
);
|
| 171 |
+
}
|
| 172 |
+
|
| 173 |
+
if (false === @file_put_contents($this->file_path, $contents, LOCK_EX)) { // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- ignore this
|
| 174 |
+
return new WP_Error(
|
| 175 |
+
'file_unable_to_write',
|
| 176 |
+
'Unable to write to the file. Please double check its permissions and try again.',
|
| 177 |
+
$this->file_path
|
| 178 |
+
);
|
| 179 |
+
}
|
| 180 |
+
|
| 181 |
+
return $removed > 0;
|
| 182 |
+
}
|
| 183 |
+
|
| 184 |
+
/**
|
| 185 |
+
* By default returns the full path to the file being managed
|
| 186 |
+
*
|
| 187 |
+
* @return string
|
| 188 |
+
*/
|
| 189 |
+
public function __toString() {
|
| 190 |
+
return $this->file_path;
|
| 191 |
+
}
|
| 192 |
+
|
| 193 |
+
} //end of class
|
|
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
if (!defined('ABSPATH')) {
|
| 3 |
+
exit; //Exit if accessed directly
|
| 4 |
+
}
|
| 5 |
+
|
| 6 |
+
class AIOWPSecurity_Block_Htaccess extends AIOWPSecurity_Block_File {
|
| 7 |
+
|
| 8 |
+
/**
|
| 9 |
+
* Attempts to insert our apache directives into the htaccess file
|
| 10 |
+
*
|
| 11 |
+
* @return boolean|WP_Error true if success; false if failed
|
| 12 |
+
*/
|
| 13 |
+
public function insert_contents() {
|
| 14 |
+
$home_path = AIOWPSecurity_Utility_File::get_home_path();
|
| 15 |
+
|
| 16 |
+
if (!is_writable($home_path)) {
|
| 17 |
+
return new WP_Error(
|
| 18 |
+
'file_wrong_permissions',
|
| 19 |
+
'The file has incorrect write permissions. Please double check its permissions and try again.',
|
| 20 |
+
$this->file_path
|
| 21 |
+
);
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
return (false !== @file_put_contents($this->file_path, $this->get_contents(), FILE_APPEND)); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- ignore this
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
/**
|
| 28 |
+
* Checks whether the file's contents are valid
|
| 29 |
+
*
|
| 30 |
+
* @param string $contents
|
| 31 |
+
* @return boolean
|
| 32 |
+
*/
|
| 33 |
+
protected function is_content_valid($contents) {
|
| 34 |
+
|
| 35 |
+
$regex = '/php_value auto_prepend_file \'(.*)\'/isU';
|
| 36 |
+
$bootstrap_path = AIOWPSecurity_Utility_Firewall::get_bootstrap_path();
|
| 37 |
+
|
| 38 |
+
$matches = array();
|
| 39 |
+
|
| 40 |
+
if (preg_match_all($regex, $contents, $matches)) {
|
| 41 |
+
$match = '';
|
| 42 |
+
foreach ($matches[1] as $match) {
|
| 43 |
+
|
| 44 |
+
if ($bootstrap_path !== $match) {
|
| 45 |
+
return false;
|
| 46 |
+
}
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
} else {
|
| 50 |
+
return false;
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
return true;
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
/**
|
| 57 |
+
* The regex pattern that demarcates our contents
|
| 58 |
+
*
|
| 59 |
+
* @return string
|
| 60 |
+
*/
|
| 61 |
+
protected function get_regex_pattern() {
|
| 62 |
+
return '/\r?\n# Begin AIOWPSEC Firewall(.*?)# End AIOWPSEC Firewall/is';
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
/**
|
| 66 |
+
* Our contents; the required apache directives for auto prepending a file
|
| 67 |
+
*
|
| 68 |
+
* @return string
|
| 69 |
+
*/
|
| 70 |
+
public function get_contents() {
|
| 71 |
+
$bootstrap_path = AIOWPSecurity_Utility_Firewall::get_bootstrap_path();
|
| 72 |
+
|
| 73 |
+
$directives = "\n# Begin AIOWPSEC Firewall\n";
|
| 74 |
+
$directives .= "\t<IfModule mod_php5.c>\n";
|
| 75 |
+
$directives .= "\t\tphp_value auto_prepend_file '{$bootstrap_path}'\n";
|
| 76 |
+
$directives .= "\t</IfModule>\n";
|
| 77 |
+
$directives .= "\t<IfModule mod_php7.c>\n";
|
| 78 |
+
$directives .= "\t\tphp_value auto_prepend_file '{$bootstrap_path}'\n";
|
| 79 |
+
$directives .= "\t</IfModule>\n";
|
| 80 |
+
$directives .= "\t<IfModule mod_php.c>\n";
|
| 81 |
+
$directives .= "\t\tphp_value auto_prepend_file '{$bootstrap_path}'\n";
|
| 82 |
+
$directives .= "\t</IfModule>\n";
|
| 83 |
+
$directives .= "# End AIOWPSEC Firewall";
|
| 84 |
+
|
| 85 |
+
return $directives;
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
} //end of class
|
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
if (!defined('ABSPATH')) {
|
| 3 |
+
exit; //Exit if accessed directly
|
| 4 |
+
}
|
| 5 |
+
|
| 6 |
+
class AIOWPSecurity_Block_Litespeed extends AIOWPSecurity_Block_Htaccess {
|
| 7 |
+
|
| 8 |
+
/**
|
| 9 |
+
* Get the directives needed for litespeed server
|
| 10 |
+
*
|
| 11 |
+
* @return string
|
| 12 |
+
*/
|
| 13 |
+
public function get_contents() {
|
| 14 |
+
$bootstrap_path = AIOWPSecurity_Utility_Firewall::get_bootstrap_path();
|
| 15 |
+
|
| 16 |
+
$directives = "\n# Begin AIOWPSEC Firewall\n";
|
| 17 |
+
$directives .= "\t<IfModule LiteSpeed>\n";
|
| 18 |
+
$directives .= "\t\tphp_value auto_prepend_file '{$bootstrap_path}'\n";
|
| 19 |
+
$directives .= "\t</IfModule>\n";
|
| 20 |
+
$directives .= "\t<IfModule lsapi_module>\n";
|
| 21 |
+
$directives .= "\t\tphp_value auto_prepend_file '{$bootstrap_path}'\n";
|
| 22 |
+
$directives .= "\t</IfModule>\n";
|
| 23 |
+
$directives .= "# End AIOWPSEC Firewall";
|
| 24 |
+
|
| 25 |
+
return $directives;
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
if (!defined('ABSPATH')) {
|
| 3 |
+
exit; //Exit if accessed directly
|
| 4 |
+
}
|
| 5 |
+
|
| 6 |
+
class AIOWPSecurity_Block_Muplugin extends AIOWPSecurity_Block_File {
|
| 7 |
+
|
| 8 |
+
/**
|
| 9 |
+
* Inserts our code into our mu-plugin.
|
| 10 |
+
*
|
| 11 |
+
* The mu-plugin and the mu-plugin directory will be created if they don't already exists
|
| 12 |
+
*
|
| 13 |
+
* @return boolean|WP_Error
|
| 14 |
+
*/
|
| 15 |
+
public function insert_contents() {
|
| 16 |
+
$info = pathinfo($this->file_path);
|
| 17 |
+
|
| 18 |
+
if (!isset($info['dirname'])) {
|
| 19 |
+
return new WP_Error(
|
| 20 |
+
'file_no_directory',
|
| 21 |
+
'No directory has been set',
|
| 22 |
+
$this->file_path
|
| 23 |
+
);
|
| 24 |
+
}
|
| 25 |
+
|
| 26 |
+
if (false === wp_mkdir_p($info['dirname'])) {
|
| 27 |
+
return new WP_Error(
|
| 28 |
+
'file_no_directory_created',
|
| 29 |
+
'Unable to create the directory',
|
| 30 |
+
$info['dirname']
|
| 31 |
+
);
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
return (false !== @file_put_contents($this->file_path, $this->get_contents())); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- ignore this
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
/**
|
| 38 |
+
* Checks whether the mu-plugin contents are valid
|
| 39 |
+
*
|
| 40 |
+
* @param string $contents
|
| 41 |
+
* @return boolean
|
| 42 |
+
*/
|
| 43 |
+
protected function is_content_valid($contents) {
|
| 44 |
+
|
| 45 |
+
//The regexes we extract the paths from
|
| 46 |
+
$regexes = array('/file_exists\(\'(.*)\'\)/isU', '/include_once\(\'(.*)\'\)/isU');
|
| 47 |
+
$regex = '';
|
| 48 |
+
$bootstrap_path = AIOWPSecurity_Utility_Firewall::get_bootstrap_path();
|
| 49 |
+
|
| 50 |
+
foreach ($regexes as $regex) {
|
| 51 |
+
$matches = array();
|
| 52 |
+
$result = preg_match($regex, $contents, $matches);
|
| 53 |
+
|
| 54 |
+
if (empty($matches[1]) || false === $result) {
|
| 55 |
+
continue;
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
if ($bootstrap_path !== $matches[1]) {
|
| 59 |
+
return false;
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
return true;
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
/**
|
| 68 |
+
* The regex pattern that demarcates our contents
|
| 69 |
+
*
|
| 70 |
+
* @return string
|
| 71 |
+
*/
|
| 72 |
+
protected function get_regex_pattern() {
|
| 73 |
+
return '#// Begin AIOWPSEC Firewall(.*)// End AIOWPSEC Firewall#isU';
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
/**
|
| 77 |
+
* Our firewall code to insert
|
| 78 |
+
*
|
| 79 |
+
* @return string
|
| 80 |
+
*/
|
| 81 |
+
public function get_contents() {
|
| 82 |
+
$bootstrap_path = AIOWPSecurity_Utility_Firewall::get_bootstrap_path();
|
| 83 |
+
|
| 84 |
+
$code = "<?php\n";
|
| 85 |
+
$code .= "// Begin AIOWPSEC Firewall\n";
|
| 86 |
+
$code .= "if (file_exists('{$bootstrap_path}')) {\n";
|
| 87 |
+
$code .= "\tinclude_once('{$bootstrap_path}');\n";
|
| 88 |
+
$code .= "}\n";
|
| 89 |
+
$code .= "// End AIOWPSEC Firewall\n";
|
| 90 |
+
|
| 91 |
+
return $code;
|
| 92 |
+
}
|
| 93 |
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
if (!defined('ABSPATH')) {
|
| 3 |
+
exit; //Exit if accessed directly
|
| 4 |
+
}
|
| 5 |
+
|
| 6 |
+
class AIOWPSecurity_Block_Userini extends AIOWPSecurity_Block_File {
|
| 7 |
+
|
| 8 |
+
/**
|
| 9 |
+
* Inserts our directive into the user.ini file
|
| 10 |
+
*
|
| 11 |
+
* @return boolean|WP_Error true if inserted; false if failed
|
| 12 |
+
*/
|
| 13 |
+
public function insert_contents() {
|
| 14 |
+
$home_path = AIOWPSecurity_Utility_File::get_home_path();
|
| 15 |
+
|
| 16 |
+
if (!is_writable($home_path)) {
|
| 17 |
+
return new WP_Error(
|
| 18 |
+
'file_directory_not_writable',
|
| 19 |
+
'The directory has incorrect write permissions. Please double check its permissions and try again.',
|
| 20 |
+
$home_path
|
| 21 |
+
);
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
return (false !== @file_put_contents($this->file_path, $this->get_contents(), FILE_APPEND)); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- ignore this
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
/**
|
| 28 |
+
* Checks whether the user.ini file contents are valid
|
| 29 |
+
*
|
| 30 |
+
* @param string $contents
|
| 31 |
+
* @return boolean
|
| 32 |
+
*/
|
| 33 |
+
protected function is_content_valid($contents) {
|
| 34 |
+
|
| 35 |
+
$regex = '/auto_prepend_file=\'(.*)\'/isU';
|
| 36 |
+
$bootstrap_path = AIOWPSecurity_Utility_Firewall::get_bootstrap_path();
|
| 37 |
+
|
| 38 |
+
$match = array();
|
| 39 |
+
$result = preg_match($regex, $contents, $match);
|
| 40 |
+
|
| 41 |
+
if (empty($match[1]) || false === $result) {
|
| 42 |
+
return false;
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
if ($bootstrap_path !== $match[1]) {
|
| 46 |
+
return false;
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
return true;
|
| 50 |
+
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
/**
|
| 54 |
+
* Our regex pattern that demarcates our contents
|
| 55 |
+
*
|
| 56 |
+
* @return string
|
| 57 |
+
*/
|
| 58 |
+
protected function get_regex_pattern() {
|
| 59 |
+
return '/\r?\n# Begin AIOWPSEC Firewall(.*?)# End AIOWPSEC Firewall/is';
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
/**
|
| 63 |
+
* Directives inserted into user.ini
|
| 64 |
+
*
|
| 65 |
+
* @return string
|
| 66 |
+
*/
|
| 67 |
+
public function get_contents() {
|
| 68 |
+
$bootstrap_path = AIOWPSecurity_Utility_Firewall::get_bootstrap_path();
|
| 69 |
+
|
| 70 |
+
$directive = "\n# Begin AIOWPSEC Firewall\n";
|
| 71 |
+
$directive .= "auto_prepend_file='{$bootstrap_path}'\n";
|
| 72 |
+
$directive .= "# End AIOWPSEC Firewall";
|
| 73 |
+
|
| 74 |
+
return $directive;
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
/**
|
| 78 |
+
* Extends the contains_contents function to check for already set directives
|
| 79 |
+
*
|
| 80 |
+
* @return boolean|WP_Error
|
| 81 |
+
*/
|
| 82 |
+
public function contains_contents() {
|
| 83 |
+
$contains = parent::contains_contents();
|
| 84 |
+
|
| 85 |
+
if (false === $contains) {
|
| 86 |
+
$directive_userini = AIOWPSecurity_Utility_Firewall::get_already_set_directive($this->file_path);
|
| 87 |
+
$directive = AIOWPSecurity_Utility_Firewall::get_already_set_directive();
|
| 88 |
+
|
| 89 |
+
if ((AIOWPSecurity_Utility_Firewall::get_bootstrap_path() === $directive) || (AIOWPSecurity_Utility_Firewall::get_bootstrap_path() === $directive_userini)) {
|
| 90 |
+
return true;
|
| 91 |
+
}
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
return $contains;
|
| 95 |
+
}
|
| 96 |
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
if (!defined('ABSPATH')) {
|
| 3 |
+
exit; //Exit if accessed directly
|
| 4 |
+
}
|
| 5 |
+
|
| 6 |
+
class AIOWPSecurity_Block_WpConfig extends AIOWPSecurity_Block_File {
|
| 7 |
+
|
| 8 |
+
/**
|
| 9 |
+
* Attempts to insert our code contents into wp-config file
|
| 10 |
+
*
|
| 11 |
+
* @return boolean|WP_Error true if success; false if unsuccessful
|
| 12 |
+
*/
|
| 13 |
+
public function insert_contents() {
|
| 14 |
+
|
| 15 |
+
if (!is_readable($this->file_path) || !is_writable($this->file_path)) {
|
| 16 |
+
return new WP_Error(
|
| 17 |
+
'file_wrong_permissions',
|
| 18 |
+
'The file has incorrect read or write permissions. Please double check its permissions and try again.',
|
| 19 |
+
$this->file_path
|
| 20 |
+
);
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
$wp_config = file($this->file_path, FILE_IGNORE_NEW_LINES);
|
| 24 |
+
|
| 25 |
+
if (false === $wp_config) {
|
| 26 |
+
return new WP_Error(
|
| 27 |
+
'file_no_contents',
|
| 28 |
+
'Unable to access the file\'s contents',
|
| 29 |
+
$this->file_path
|
| 30 |
+
);
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
array_shift($wp_config);
|
| 34 |
+
array_unshift($wp_config, $this->get_contents());
|
| 35 |
+
|
| 36 |
+
return (false !== @file_put_contents($this->file_path, implode(PHP_EOL, $wp_config), LOCK_EX)); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- ignore this
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
/**
|
| 40 |
+
* Checks the validity of the content
|
| 41 |
+
*
|
| 42 |
+
* @param string $contents - contents we're checking
|
| 43 |
+
* @return boolean true if content is valid; false if invalid
|
| 44 |
+
*/
|
| 45 |
+
protected function is_content_valid($contents) {
|
| 46 |
+
//The regexes we extract the paths from
|
| 47 |
+
$regexes = array('/file_exists\(\'(.*)\'\)/isU', '/include_once\(\'(.*)\'\)/isU');
|
| 48 |
+
$regex = '';
|
| 49 |
+
$bootstrap_path = AIOWPSecurity_Utility_Firewall::get_bootstrap_path();
|
| 50 |
+
|
| 51 |
+
foreach ($regexes as $regex) {
|
| 52 |
+
$matches = array();
|
| 53 |
+
$result = preg_match($regex, $contents, $matches);
|
| 54 |
+
|
| 55 |
+
if (empty($matches[1]) || false === $result) {
|
| 56 |
+
continue;
|
| 57 |
+
}
|
| 58 |
+
|
| 59 |
+
if ($bootstrap_path !== $matches[1]) {
|
| 60 |
+
return false;
|
| 61 |
+
}
|
| 62 |
+
}
|
| 63 |
+
|
| 64 |
+
return true;
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
/**
|
| 68 |
+
* The particular regex that demarcates our contents
|
| 69 |
+
*
|
| 70 |
+
* @return string
|
| 71 |
+
*/
|
| 72 |
+
protected function get_regex_pattern() {
|
| 73 |
+
return '#\r?\n// Begin AIOWPSEC Firewall(.*?)// End AIOWPSEC Firewall#is';
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
/**
|
| 77 |
+
* Our firewall code to insert
|
| 78 |
+
*
|
| 79 |
+
* @return string
|
| 80 |
+
*/
|
| 81 |
+
public function get_contents() {
|
| 82 |
+
$bootstrap_path = AIOWPSecurity_Utility_Firewall::get_bootstrap_path();
|
| 83 |
+
|
| 84 |
+
$code = "<?php\n";
|
| 85 |
+
$code .= "// Begin AIOWPSEC Firewall\n";
|
| 86 |
+
$code .= "if (file_exists('{$bootstrap_path}')) {\n";
|
| 87 |
+
$code .= "\tinclude_once('{$bootstrap_path}');\n";
|
| 88 |
+
$code .= "}\n";
|
| 89 |
+
$code .= "// End AIOWPSEC Firewall";
|
| 90 |
+
|
| 91 |
+
return $code;
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
}
|
|
@@ -61,6 +61,11 @@ class AIOWPSecurity_Blocking {
|
|
| 61 |
*/
|
| 62 |
public static function add_ip_to_block_list($ip_address, $reason = '') {
|
| 63 |
global $wpdb, $aio_wp_security;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 64 |
//Check if this IP address is already in the block list
|
| 65 |
$blocked = AIOWPSecurity_Blocking::is_ip_blocked($ip_address);
|
| 66 |
$time_now = current_time('mysql');
|
| 61 |
*/
|
| 62 |
public static function add_ip_to_block_list($ip_address, $reason = '') {
|
| 63 |
global $wpdb, $aio_wp_security;
|
| 64 |
+
$user = wp_get_current_user();
|
| 65 |
+
if (array_intersect(array('administrator', 'editor', 'author'), $user->roles) && AIOWPSecurity_Utility_IP::get_user_ip_address() == $ip_address) {
|
| 66 |
+
return;
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
//Check if this IP address is already in the block list
|
| 70 |
$blocked = AIOWPSecurity_Blocking::is_ip_blocked($ip_address);
|
| 71 |
$time_now = current_time('mysql');
|
|
@@ -109,12 +109,12 @@ class AIOWPSecurity_Captcha {
|
|
| 109 |
$current_time = time();
|
| 110 |
$enc_result = base64_encode($current_time.$captcha_secret_string.$result);
|
| 111 |
$random_str = AIOWPSecurity_Utility::generate_alpha_numeric_random_string(10);
|
| 112 |
-
if (
|
| 113 |
update_site_option('aiowps_captcha_string_info_'.$random_str, $enc_result);
|
| 114 |
update_site_option('aiowps_captcha_string_info_time_'.$random_str, $current_time);
|
| 115 |
} else {
|
| 116 |
-
update_option('aiowps_captcha_string_info_'.$random_str, $enc_result);
|
| 117 |
-
update_option('aiowps_captcha_string_info_time_'.$random_str, $current_time);
|
| 118 |
}
|
| 119 |
$equation_string .= '<input type="hidden" name="aiowps-captcha-string-info" id="aiowps-captcha-string-info" value="'.$random_str.'" />';
|
| 120 |
$equation_string .= '<input type="hidden" name="aiowps-captcha-temp-string" id="aiowps-captcha-temp-string" value="'.$current_time.'" />';
|
|
@@ -199,7 +199,7 @@ class AIOWPSecurity_Captcha {
|
|
| 199 |
$captcha_temp_string = sanitize_text_field($_POST['aiowps-captcha-temp-string']);
|
| 200 |
$submitted_encoded_string = base64_encode($captcha_temp_string.$captcha_secret_string.$captcha_answer);
|
| 201 |
$trans_handle = sanitize_text_field($_POST['aiowps-captcha-string-info']);
|
| 202 |
-
if (
|
| 203 |
$captcha_string_info_option = get_site_option('aiowps_captcha_string_info_'.$trans_handle);
|
| 204 |
delete_site_option('aiowps_captcha_string_info_'.$trans_handle);
|
| 205 |
delete_site_option('aiowps_captcha_string_info_time_'.$trans_handle);
|
| 109 |
$current_time = time();
|
| 110 |
$enc_result = base64_encode($current_time.$captcha_secret_string.$result);
|
| 111 |
$random_str = AIOWPSecurity_Utility::generate_alpha_numeric_random_string(10);
|
| 112 |
+
if (is_multisite()) {
|
| 113 |
update_site_option('aiowps_captcha_string_info_'.$random_str, $enc_result);
|
| 114 |
update_site_option('aiowps_captcha_string_info_time_'.$random_str, $current_time);
|
| 115 |
} else {
|
| 116 |
+
update_option('aiowps_captcha_string_info_'.$random_str, $enc_result, false);
|
| 117 |
+
update_option('aiowps_captcha_string_info_time_'.$random_str, $current_time, false);
|
| 118 |
}
|
| 119 |
$equation_string .= '<input type="hidden" name="aiowps-captcha-string-info" id="aiowps-captcha-string-info" value="'.$random_str.'" />';
|
| 120 |
$equation_string .= '<input type="hidden" name="aiowps-captcha-temp-string" id="aiowps-captcha-temp-string" value="'.$current_time.'" />';
|
| 199 |
$captcha_temp_string = sanitize_text_field($_POST['aiowps-captcha-temp-string']);
|
| 200 |
$submitted_encoded_string = base64_encode($captcha_temp_string.$captcha_secret_string.$captcha_answer);
|
| 201 |
$trans_handle = sanitize_text_field($_POST['aiowps-captcha-string-info']);
|
| 202 |
+
if (is_multisite()) {
|
| 203 |
$captcha_string_info_option = get_site_option('aiowps_captcha_string_info_'.$trans_handle);
|
| 204 |
delete_site_option('aiowps_captcha_string_info_'.$trans_handle);
|
| 205 |
delete_site_option('aiowps_captcha_string_info_time_'.$trans_handle);
|
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
if (!defined('ABSPATH')) {
|
| 3 |
+
exit;//Exit if accessed directly
|
| 4 |
+
}
|
| 5 |
+
|
| 6 |
+
/**
|
| 7 |
+
* AIOWPSecurity_Cleanup class for clean up database etc.
|
| 8 |
+
*
|
| 9 |
+
* @access public
|
| 10 |
+
*/
|
| 11 |
+
class AIOWPSecurity_Cleanup {
|
| 12 |
+
|
| 13 |
+
/**
|
| 14 |
+
* Class constructor added action
|
| 15 |
+
*/
|
| 16 |
+
public function __construct() {
|
| 17 |
+
add_action('aiowps_perform_db_cleanup_tasks', array($this, 'aiowps_scheduled_db_cleanup_handler'));
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
/**
|
| 21 |
+
* Clean up unnecessary old data from aiowps tables.
|
| 22 |
+
*
|
| 23 |
+
* @return void
|
| 24 |
+
*/
|
| 25 |
+
public function aiowps_scheduled_db_cleanup_handler() {
|
| 26 |
+
//Check the events table because this can grow quite large especially when 404 events are being logged
|
| 27 |
+
$events_table_name = AIOWPSEC_TBL_EVENTS;
|
| 28 |
+
$purge_events_records_after_days = AIOS_PURGE_EVENTS_RECORDS_AFTER_DAYS; //purge older records in the events table
|
| 29 |
+
$purge_events_records_after_days = apply_filters('aios_purge_events_records_after_days', $purge_events_records_after_days);
|
| 30 |
+
AIOWPSecurity_Utility::purge_table_records($events_table_name, $purge_events_records_after_days, 'event_date');
|
| 31 |
+
|
| 32 |
+
//Check the failed logins table
|
| 33 |
+
// aiowps_perform_failed_login_cleanup_task already does it.
|
| 34 |
+
|
| 35 |
+
//Check the login activity table
|
| 36 |
+
$login_activity_table_name = AIOWPSEC_TBL_USER_LOGIN_ACTIVITY;
|
| 37 |
+
$purge_login_activity_records_after_days = AIOS_PURGE_LOGIN_ACTIVITY_RECORDS_AFTER_DAYS; //purge older records in the login activity table
|
| 38 |
+
$purge_login_activity_records_after_days = apply_filters('aios_purge_login_activity_records_after_days', $purge_login_activity_records_after_days);
|
| 39 |
+
AIOWPSecurity_Utility::purge_table_records($login_activity_table_name, $purge_login_activity_records_after_days, 'login_date');
|
| 40 |
+
|
| 41 |
+
//Check the global meta table
|
| 42 |
+
$global_meta_table_name = AIOWPSEC_TBL_GLOBAL_META_DATA;
|
| 43 |
+
$purge_global_meta_records_after_days = AIOS_PURGE_GLOBAL_META_DATA_RECORDS_AFTER_DAYS; //purge older records in global meta table
|
| 44 |
+
$purge_global_meta_records_after_days = apply_filters('aios_purge_global_meta_records_after_days', $purge_global_meta_records_after_days);
|
| 45 |
+
AIOWPSecurity_Utility::purge_table_records($global_meta_table_name, $purge_global_meta_records_after_days, 'date_time');
|
| 46 |
+
|
| 47 |
+
//Delete any expired _aiowps_captcha_string_info_xxxx option
|
| 48 |
+
AIOWPSecurity_Utility::delete_expired_captcha_options();
|
| 49 |
+
//Keep adding other DB cleanup tasks as they arise...
|
| 50 |
+
}
|
| 51 |
+
}
|
|
@@ -15,6 +15,8 @@ class AIOWPSecurity_Comment {
|
|
| 15 |
*/
|
| 16 |
public function __construct() {
|
| 17 |
add_filter('pre_comment_user_ip', array($this, 'pre_comment_user_ip'));
|
|
|
|
|
|
|
| 18 |
}
|
| 19 |
|
| 20 |
/**
|
|
@@ -29,4 +31,47 @@ class AIOWPSecurity_Comment {
|
|
| 29 |
}
|
| 30 |
return $comment_user_ip;
|
| 31 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 32 |
}
|
| 15 |
*/
|
| 16 |
public function __construct() {
|
| 17 |
add_filter('pre_comment_user_ip', array($this, 'pre_comment_user_ip'));
|
| 18 |
+
add_action('comment_spam_to_approved', array($this, 'comment_spam_status_change'));
|
| 19 |
+
add_action('comment_spam_to_unapproved', array($this, 'comment_spam_status_change'));
|
| 20 |
}
|
| 21 |
|
| 22 |
/**
|
| 31 |
}
|
| 32 |
return $comment_user_ip;
|
| 33 |
}
|
| 34 |
+
|
| 35 |
+
/**
|
| 36 |
+
* Move spam comments to trash.
|
| 37 |
+
*/
|
| 38 |
+
public static function trash_spam_comments() {
|
| 39 |
+
global $aio_wp_security;
|
| 40 |
+
if ('1' == $aio_wp_security->configs->get_value('aiowps_enable_trash_spam_comments') && absint($aio_wp_security->configs->get_value('aiowps_trash_spam_comments_after_days'))) {
|
| 41 |
+
$date_before = absint($aio_wp_security->configs->get_value('aiowps_trash_spam_comments_after_days')).' days ago';
|
| 42 |
+
$comment_ids = get_comments(array(
|
| 43 |
+
'fields' => 'ids',
|
| 44 |
+
'status' => 'spam',
|
| 45 |
+
'date_query' => array(
|
| 46 |
+
array(
|
| 47 |
+
'before' => $date_before,
|
| 48 |
+
'inclusive' => true,
|
| 49 |
+
),
|
| 50 |
+
)
|
| 51 |
+
));
|
| 52 |
+
|
| 53 |
+
if (!empty($comment_ids)) {
|
| 54 |
+
foreach ($comment_ids as $comment_id) {
|
| 55 |
+
wp_trash_comment($comment_id);
|
| 56 |
+
}
|
| 57 |
+
}
|
| 58 |
+
}
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
/**
|
| 62 |
+
* Delete ip from aiowps_permanent_block table once the comment's spam status changed.
|
| 63 |
+
*
|
| 64 |
+
* @param object $comment_data comment object.
|
| 65 |
+
*/
|
| 66 |
+
public function comment_spam_status_change($comment_data) {
|
| 67 |
+
global $wpdb, $aio_wp_security;
|
| 68 |
+
$comment_ip = $comment_data->comment_author_IP;
|
| 69 |
+
$sql = $wpdb->prepare("SELECT COUNT(*) FROM {$wpdb->comments} WHERE comment_author_IP = %s AND comment_approved = 'spam'", $comment_ip);
|
| 70 |
+
$total_spam_comment = $wpdb->get_var($sql);
|
| 71 |
+
$min_comment_before_block = $aio_wp_security->configs->get_value('aiowps_spam_ip_min_comments_block');
|
| 72 |
+
if ($total_spam_comment < $min_comment_before_block) {
|
| 73 |
+
$where = array('blocked_ip' => $comment_ip, 'block_reason' => 'spam');
|
| 74 |
+
$wpdb->delete(AIOWPSEC_TBL_PERM_BLOCK, $where, array('%s'));
|
| 75 |
+
}
|
| 76 |
+
}
|
| 77 |
}
|
|
@@ -12,11 +12,15 @@ class AIOWPSecurity_Configure_Settings {
|
|
| 12 |
*/
|
| 13 |
public static function set_default_settings() {
|
| 14 |
global $aio_wp_security;
|
| 15 |
-
$blog_email_address =
|
| 16 |
-
|
|
|
|
| 17 |
//Debug
|
| 18 |
$aio_wp_security->configs->set_value('aiowps_enable_debug', '');//Checkbox
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
| 20 |
//WP Generator Meta Tag feature
|
| 21 |
$aio_wp_security->configs->set_value('aiowps_remove_wp_generator_meta_info', '');//Checkbox
|
| 22 |
|
|
@@ -31,7 +35,8 @@ class AIOWPSecurity_Configure_Settings {
|
|
| 31 |
$aio_wp_security->configs->set_value('aiowps_allow_unlock_requests', '1'); // Checkbox
|
| 32 |
$aio_wp_security->configs->set_value('aiowps_max_login_attempts', '3');
|
| 33 |
$aio_wp_security->configs->set_value('aiowps_retry_time_period', '5');
|
| 34 |
-
$aio_wp_security->configs->set_value('aiowps_lockout_time_length', '
|
|
|
|
| 35 |
$aio_wp_security->configs->set_value('aiowps_set_generic_login_msg', '');//Checkbox
|
| 36 |
$aio_wp_security->configs->set_value('aiowps_enable_email_notify', '');//Checkbox
|
| 37 |
$aio_wp_security->configs->set_value('aiowps_email_address', $blog_email_address);//text field
|
|
@@ -109,6 +114,7 @@ class AIOWPSecurity_Configure_Settings {
|
|
| 109 |
//Brute Force features
|
| 110 |
$aio_wp_security->configs->set_value('aiowps_enable_rename_login_page', '');//Checkbox
|
| 111 |
$aio_wp_security->configs->set_value('aiowps_enable_login_honeypot', '');//Checkbox
|
|
|
|
| 112 |
|
| 113 |
$aio_wp_security->configs->set_value('aiowps_enable_brute_force_attack_prevention', '');//Checkbox
|
| 114 |
$aio_wp_security->configs->set_value('aiowps_brute_force_secret_word', '');
|
|
@@ -128,6 +134,8 @@ class AIOWPSecurity_Configure_Settings {
|
|
| 128 |
$aio_wp_security->configs->set_value('aiowps_spam_ip_min_comments_block', '');
|
| 129 |
$aio_wp_security->configs->set_value('aiowps_enable_bp_register_captcha', '');
|
| 130 |
$aio_wp_security->configs->set_value('aiowps_enable_bbp_new_topic_captcha', '');//Checkbox
|
|
|
|
|
|
|
| 131 |
|
| 132 |
//Filescan features
|
| 133 |
//File change detection feature
|
|
@@ -156,6 +164,10 @@ class AIOWPSecurity_Configure_Settings {
|
|
| 156 |
$aio_wp_security->configs->set_value('aiowps_recaptcha_secret_key', '');
|
| 157 |
$aio_wp_security->configs->set_value('aiowps_default_recaptcha', '');//Checkbox
|
| 158 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 159 |
//TODO - keep adding default options for any fields that require it
|
| 160 |
|
| 161 |
//Save it
|
|
@@ -164,11 +176,15 @@ class AIOWPSecurity_Configure_Settings {
|
|
| 164 |
|
| 165 |
public static function add_option_values() {
|
| 166 |
global $aio_wp_security;
|
| 167 |
-
$blog_email_address =
|
|
|
|
| 168 |
|
| 169 |
//Debug
|
| 170 |
$aio_wp_security->configs->add_value('aiowps_enable_debug', '');//Checkbox
|
| 171 |
-
|
|
|
|
|
|
|
|
|
|
| 172 |
//WP Generator Meta Tag feature
|
| 173 |
$aio_wp_security->configs->add_value('aiowps_remove_wp_generator_meta_info', '');//Checkbox
|
| 174 |
|
|
@@ -184,7 +200,8 @@ class AIOWPSecurity_Configure_Settings {
|
|
| 184 |
$aio_wp_security->configs->add_value('aiowps_allow_unlock_requests', '1'); // Checkbox
|
| 185 |
$aio_wp_security->configs->add_value('aiowps_max_login_attempts', '3');
|
| 186 |
$aio_wp_security->configs->add_value('aiowps_retry_time_period', '5');
|
| 187 |
-
$aio_wp_security->configs->add_value('aiowps_lockout_time_length', '
|
|
|
|
| 188 |
$aio_wp_security->configs->add_value('aiowps_set_generic_login_msg', '');//Checkbox
|
| 189 |
$aio_wp_security->configs->add_value('aiowps_enable_email_notify', '');//Checkbox
|
| 190 |
$aio_wp_security->configs->add_value('aiowps_email_address', $blog_email_address);//text field
|
|
@@ -258,6 +275,7 @@ class AIOWPSecurity_Configure_Settings {
|
|
| 258 |
//Brute Force features
|
| 259 |
$aio_wp_security->configs->add_value('aiowps_enable_rename_login_page', '');//Checkbox
|
| 260 |
$aio_wp_security->configs->add_value('aiowps_enable_login_honeypot', '');//Checkbox
|
|
|
|
| 261 |
|
| 262 |
$aio_wp_security->configs->add_value('aiowps_enable_brute_force_attack_prevention', '');//Checkbox
|
| 263 |
$aio_wp_security->configs->add_value('aiowps_brute_force_secret_word', '');
|
|
@@ -277,6 +295,8 @@ class AIOWPSecurity_Configure_Settings {
|
|
| 277 |
$aio_wp_security->configs->add_value('aiowps_spam_ip_min_comments_block', '');
|
| 278 |
$aio_wp_security->configs->add_value('aiowps_enable_bp_register_captcha', '');
|
| 279 |
$aio_wp_security->configs->add_value('aiowps_enable_bbp_new_topic_captcha', '');//Checkbox
|
|
|
|
|
|
|
| 280 |
|
| 281 |
|
| 282 |
//Filescan features
|
|
@@ -306,6 +326,10 @@ class AIOWPSecurity_Configure_Settings {
|
|
| 306 |
$aio_wp_security->configs->add_value('aiowps_recaptcha_secret_key', '');
|
| 307 |
$aio_wp_security->configs->add_value('aiowps_default_recaptcha', '');//Checkbox
|
| 308 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 309 |
//TODO - keep adding default options for any fields that require it
|
| 310 |
|
| 311 |
//Save it
|
| 12 |
*/
|
| 13 |
public static function set_default_settings() {
|
| 14 |
global $aio_wp_security;
|
| 15 |
+
$blog_email_address = array();
|
| 16 |
+
$blog_email_address[] = get_bloginfo('admin_email'); //Get the blog admin email address - we will use as the default value
|
| 17 |
+
|
| 18 |
//Debug
|
| 19 |
$aio_wp_security->configs->set_value('aiowps_enable_debug', '');//Checkbox
|
| 20 |
+
|
| 21 |
+
//PHP backtrace
|
| 22 |
+
$aio_wp_security->configs->set_value('aiowps_enable_php_backtrace_in_email', '');//Checkbox
|
| 23 |
+
|
| 24 |
//WP Generator Meta Tag feature
|
| 25 |
$aio_wp_security->configs->set_value('aiowps_remove_wp_generator_meta_info', '');//Checkbox
|
| 26 |
|
| 35 |
$aio_wp_security->configs->set_value('aiowps_allow_unlock_requests', '1'); // Checkbox
|
| 36 |
$aio_wp_security->configs->set_value('aiowps_max_login_attempts', '3');
|
| 37 |
$aio_wp_security->configs->set_value('aiowps_retry_time_period', '5');
|
| 38 |
+
$aio_wp_security->configs->set_value('aiowps_lockout_time_length', '5');
|
| 39 |
+
$aio_wp_security->configs->set_value('aiowps_max_lockout_time_length', '60');
|
| 40 |
$aio_wp_security->configs->set_value('aiowps_set_generic_login_msg', '');//Checkbox
|
| 41 |
$aio_wp_security->configs->set_value('aiowps_enable_email_notify', '');//Checkbox
|
| 42 |
$aio_wp_security->configs->set_value('aiowps_email_address', $blog_email_address);//text field
|
| 114 |
//Brute Force features
|
| 115 |
$aio_wp_security->configs->set_value('aiowps_enable_rename_login_page', '');//Checkbox
|
| 116 |
$aio_wp_security->configs->set_value('aiowps_enable_login_honeypot', '');//Checkbox
|
| 117 |
+
$aio_wp_security->configs->set_value('aiowps_disable_application_password', '');//Checkbox
|
| 118 |
|
| 119 |
$aio_wp_security->configs->set_value('aiowps_enable_brute_force_attack_prevention', '');//Checkbox
|
| 120 |
$aio_wp_security->configs->set_value('aiowps_brute_force_secret_word', '');
|
| 134 |
$aio_wp_security->configs->set_value('aiowps_spam_ip_min_comments_block', '');
|
| 135 |
$aio_wp_security->configs->set_value('aiowps_enable_bp_register_captcha', '');
|
| 136 |
$aio_wp_security->configs->set_value('aiowps_enable_bbp_new_topic_captcha', '');//Checkbox
|
| 137 |
+
$aio_wp_security->configs->set_value('aiowps_enable_trash_spam_comments', '');
|
| 138 |
+
$aio_wp_security->configs->set_value('aiowps_trash_spam_comments_after_days', '14');
|
| 139 |
|
| 140 |
//Filescan features
|
| 141 |
//File change detection feature
|
| 164 |
$aio_wp_security->configs->set_value('aiowps_recaptcha_secret_key', '');
|
| 165 |
$aio_wp_security->configs->set_value('aiowps_default_recaptcha', '');//Checkbox
|
| 166 |
|
| 167 |
+
// Deactivation Handler
|
| 168 |
+
$aio_wp_security->configs->set_value('aiowps_on_uninstall_delete_db_tables', '1'); //Checkbox
|
| 169 |
+
$aio_wp_security->configs->set_value('aiowps_on_uninstall_delete_configs', '1'); //Checkbox
|
| 170 |
+
|
| 171 |
//TODO - keep adding default options for any fields that require it
|
| 172 |
|
| 173 |
//Save it
|
| 176 |
|
| 177 |
public static function add_option_values() {
|
| 178 |
global $aio_wp_security;
|
| 179 |
+
$blog_email_address = array();
|
| 180 |
+
$blog_email_address[] = get_bloginfo('admin_email'); //Get the blog admin email address - we will use as the default value
|
| 181 |
|
| 182 |
//Debug
|
| 183 |
$aio_wp_security->configs->add_value('aiowps_enable_debug', '');//Checkbox
|
| 184 |
+
|
| 185 |
+
//PHP backtrace
|
| 186 |
+
$aio_wp_security->configs->add_value('aiowps_enable_php_backtrace_in_email', '');//Checkbox
|
| 187 |
+
|
| 188 |
//WP Generator Meta Tag feature
|
| 189 |
$aio_wp_security->configs->add_value('aiowps_remove_wp_generator_meta_info', '');//Checkbox
|
| 190 |
|
| 200 |
$aio_wp_security->configs->add_value('aiowps_allow_unlock_requests', '1'); // Checkbox
|
| 201 |
$aio_wp_security->configs->add_value('aiowps_max_login_attempts', '3');
|
| 202 |
$aio_wp_security->configs->add_value('aiowps_retry_time_period', '5');
|
| 203 |
+
$aio_wp_security->configs->add_value('aiowps_lockout_time_length', '5');
|
| 204 |
+
$aio_wp_security->configs->add_value('aiowps_max_lockout_time_length', '60');
|
| 205 |
$aio_wp_security->configs->add_value('aiowps_set_generic_login_msg', '');//Checkbox
|
| 206 |
$aio_wp_security->configs->add_value('aiowps_enable_email_notify', '');//Checkbox
|
| 207 |
$aio_wp_security->configs->add_value('aiowps_email_address', $blog_email_address);//text field
|
| 275 |
//Brute Force features
|
| 276 |
$aio_wp_security->configs->add_value('aiowps_enable_rename_login_page', '');//Checkbox
|
| 277 |
$aio_wp_security->configs->add_value('aiowps_enable_login_honeypot', '');//Checkbox
|
| 278 |
+
$aio_wp_security->configs->set_value('aiowps_disable_application_password', '1');//Checkbox
|
| 279 |
|
| 280 |
$aio_wp_security->configs->add_value('aiowps_enable_brute_force_attack_prevention', '');//Checkbox
|
| 281 |
$aio_wp_security->configs->add_value('aiowps_brute_force_secret_word', '');
|
| 295 |
$aio_wp_security->configs->add_value('aiowps_spam_ip_min_comments_block', '');
|
| 296 |
$aio_wp_security->configs->add_value('aiowps_enable_bp_register_captcha', '');
|
| 297 |
$aio_wp_security->configs->add_value('aiowps_enable_bbp_new_topic_captcha', '');//Checkbox
|
| 298 |
+
$aio_wp_security->configs->set_value('aiowps_enable_trash_spam_comments', '');
|
| 299 |
+
$aio_wp_security->configs->set_value('aiowps_trash_spam_comments_after_days', '14');
|
| 300 |
|
| 301 |
|
| 302 |
//Filescan features
|
| 326 |
$aio_wp_security->configs->add_value('aiowps_recaptcha_secret_key', '');
|
| 327 |
$aio_wp_security->configs->add_value('aiowps_default_recaptcha', '');//Checkbox
|
| 328 |
|
| 329 |
+
// Deactivation Handler
|
| 330 |
+
$aio_wp_security->configs->set_value('aiowps_on_uninstall_delete_db_tables', '1'); //Checkbox
|
| 331 |
+
$aio_wp_security->configs->set_value('aiowps_on_uninstall_delete_configs', '1'); //Checkbox
|
| 332 |
+
|
| 333 |
//TODO - keep adding default options for any fields that require it
|
| 334 |
|
| 335 |
//Save it
|
|
@@ -12,17 +12,66 @@ class AIOWPSecurity_Cronjob_Handler {
|
|
| 12 |
* Class constructor
|
| 13 |
*/
|
| 14 |
public function __construct() {
|
|
|
|
|
|
|
|
|
|
| 15 |
add_action('aiowps_hourly_cron_event', array($this, 'aiowps_hourly_cron_event_handler'));
|
| 16 |
add_action('aiowps_daily_cron_event', array($this, 'aiowps_daily_cron_event_handler'));
|
| 17 |
add_action('aiowps_perform_failed_login_cleanup_task', array($this, 'failed_login_cleanup'));
|
| 18 |
add_action('aiowps_purge_old_debug_logs', array($this, 'purge_old_debug_logs'));
|
|
|
|
| 19 |
}
|
| 20 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
public function aiowps_hourly_cron_event_handler() {
|
| 22 |
//Do stuff that needs checking hourly
|
| 23 |
-
|
| 24 |
do_action('aiowps_perform_fcd_scan_tasks');
|
| 25 |
-
do_action('aiowps_perform_db_cleanup_tasks');
|
| 26 |
}
|
| 27 |
|
| 28 |
/**
|
|
@@ -31,8 +80,10 @@ class AIOWPSecurity_Cronjob_Handler {
|
|
| 31 |
* @return void
|
| 32 |
*/
|
| 33 |
public function aiowps_daily_cron_event_handler() {
|
|
|
|
| 34 |
do_action('aiowps_perform_failed_login_cleanup_task');
|
| 35 |
do_action('aiowps_purge_old_debug_logs');
|
|
|
|
| 36 |
}
|
| 37 |
|
| 38 |
/**
|
|
@@ -44,7 +95,7 @@ class AIOWPSecurity_Cronjob_Handler {
|
|
| 44 |
global $wpdb, $aio_wp_security;
|
| 45 |
|
| 46 |
$purge_records_after_days = apply_filters('aiowps_purge_failed_login_records_after_days', AIOWPSEC_PURGE_FAILED_LOGIN_RECORDS_AFTER_DAYS);
|
| 47 |
-
$older_than_date_time = date('Y-m-d H:m:s', strtotime('-' . $purge_records_after_days . ' days',
|
| 48 |
$sql = $wpdb->prepare('DELETE FROM ' . AIOWPSEC_TBL_FAILED_LOGINS . ' WHERE failed_login_date<%s', $older_than_date_time);
|
| 49 |
$ret_deleted = $wpdb->query($sql);
|
| 50 |
if (false === $ret_deleted) {
|
|
@@ -83,4 +134,13 @@ class AIOWPSecurity_Cronjob_Handler {
|
|
| 83 |
$aio_wp_security->debug_logger->log_debug_cron("Failed to purge older debug logs : {$error_msg}", 4);
|
| 84 |
}
|
| 85 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
}
|
| 12 |
* Class constructor
|
| 13 |
*/
|
| 14 |
public function __construct() {
|
| 15 |
+
add_filter('cron_schedules', array('AIOWPSecurity_Cronjob_Handler', 'cron_schedules'));
|
| 16 |
+
|
| 17 |
+
add_action('aios_15_minutes_cron_event', array($this, 'aios_15_minutes_cron_event'));
|
| 18 |
add_action('aiowps_hourly_cron_event', array($this, 'aiowps_hourly_cron_event_handler'));
|
| 19 |
add_action('aiowps_daily_cron_event', array($this, 'aiowps_daily_cron_event_handler'));
|
| 20 |
add_action('aiowps_perform_failed_login_cleanup_task', array($this, 'failed_login_cleanup'));
|
| 21 |
add_action('aiowps_purge_old_debug_logs', array($this, 'purge_old_debug_logs'));
|
| 22 |
+
add_action('aiowps_send_lockout_email', array($this, 'send_lockout_email'));
|
| 23 |
}
|
| 24 |
+
|
| 25 |
+
/**
|
| 26 |
+
* Adds a custom cron schedule for every 5 minutes.
|
| 27 |
+
*
|
| 28 |
+
* @param array $schedules An array of cron schedules.
|
| 29 |
+
* @return array Filtered array of cron schedules.
|
| 30 |
+
*/
|
| 31 |
+
public static function cron_schedules($schedules) {
|
| 32 |
+
$schedules['aios-every-15-minutes'] = array(
|
| 33 |
+
'interval' => 900, // 15 * 60
|
| 34 |
+
'display' => __('Every 15 minutes', 'all-in-one-wp-security-and-firewall')
|
| 35 |
+
);
|
| 36 |
+
return $schedules;
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
/**
|
| 40 |
+
* Run cron event every 5 minute.
|
| 41 |
+
*
|
| 42 |
+
* @return void
|
| 43 |
+
*/
|
| 44 |
+
public function aios_15_minutes_cron_event() {
|
| 45 |
+
global $aio_wp_security;
|
| 46 |
+
|
| 47 |
+
if (!class_exists('Updraft_Semaphore_3_0')) {
|
| 48 |
+
require_once(AIO_WP_SECURITY_PATH.'/vendor/team-updraft/common-libs/src/updraft-semaphore/class-updraft-semaphore.php');
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
$fifteen_minutes_cron_semaphore = new Updraft_Semaphore_3_0('aios_15_minutes_cron_event', 60);
|
| 52 |
+
|
| 53 |
+
if ($fifteen_minutes_cron_semaphore->lock(2)) {
|
| 54 |
+
try {
|
| 55 |
+
$aio_wp_security->user_login_obj->send_login_lockout_emails();
|
| 56 |
+
} catch (Exception $e) {
|
| 57 |
+
$log_message = 'Exception ('.get_class($e).') occurred during the 5 minutes cron event action call: '.$e->getMessage().' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().')';
|
| 58 |
+
$aio_wp_security->debug_logger->log_debug($log_message, 4);
|
| 59 |
+
// @codingStandardsIgnoreLine
|
| 60 |
+
} catch (Error $e) {
|
| 61 |
+
$log_message = 'PHP Fatal error ('.get_class($e).') occurred during the the 5 minutes cron event action call. Error Message: '.$e->getMessage().' (Code: '.$e->getCode().', line '.$e->getLine().' in '.$e->getFile().')';
|
| 62 |
+
$aio_wp_security->debug_logger->log_debug($log_message, 4);
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
$fifteen_minutes_cron_semaphore->release();
|
| 66 |
+
} else {
|
| 67 |
+
$aio_wp_security->debug_logger->log_debug('The 15 minutes cron event lock could not get the lock.');
|
| 68 |
+
}
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
public function aiowps_hourly_cron_event_handler() {
|
| 72 |
//Do stuff that needs checking hourly
|
| 73 |
+
AIOWPSecurity_Comment::trash_spam_comments();
|
| 74 |
do_action('aiowps_perform_fcd_scan_tasks');
|
|
|
|
| 75 |
}
|
| 76 |
|
| 77 |
/**
|
| 80 |
* @return void
|
| 81 |
*/
|
| 82 |
public function aiowps_daily_cron_event_handler() {
|
| 83 |
+
do_action('aiowps_perform_db_cleanup_tasks');
|
| 84 |
do_action('aiowps_perform_failed_login_cleanup_task');
|
| 85 |
do_action('aiowps_purge_old_debug_logs');
|
| 86 |
+
do_action('aiowps_send_lockout_email');
|
| 87 |
}
|
| 88 |
|
| 89 |
/**
|
| 95 |
global $wpdb, $aio_wp_security;
|
| 96 |
|
| 97 |
$purge_records_after_days = apply_filters('aiowps_purge_failed_login_records_after_days', AIOWPSEC_PURGE_FAILED_LOGIN_RECORDS_AFTER_DAYS);
|
| 98 |
+
$older_than_date_time = date('Y-m-d H:m:s', strtotime('-' . $purge_records_after_days . ' days', current_time('timestamp', true)));
|
| 99 |
$sql = $wpdb->prepare('DELETE FROM ' . AIOWPSEC_TBL_FAILED_LOGINS . ' WHERE failed_login_date<%s', $older_than_date_time);
|
| 100 |
$ret_deleted = $wpdb->query($sql);
|
| 101 |
if (false === $ret_deleted) {
|
| 134 |
$aio_wp_security->debug_logger->log_debug_cron("Failed to purge older debug logs : {$error_msg}", 4);
|
| 135 |
}
|
| 136 |
}
|
| 137 |
+
/**
|
| 138 |
+
* Send email notification to an user who has flag is_lockout_email_sent is 0.
|
| 139 |
+
*
|
| 140 |
+
* @return Void
|
| 141 |
+
*/
|
| 142 |
+
public function send_lockout_email() {
|
| 143 |
+
global $aio_wp_security;
|
| 144 |
+
$aio_wp_security->user_login_obj->send_login_lockout_emails();
|
| 145 |
+
}
|
| 146 |
}
|
|
@@ -3,54 +3,27 @@ if (!defined('ABSPATH')) {
|
|
| 3 |
exit;//Exit if accessed directly
|
| 4 |
}
|
| 5 |
|
| 6 |
-
|
| 7 |
-
require_once(dirname(__FILE__) . '/wp-security-configure-settings.php');
|
| 8 |
|
| 9 |
-
class
|
| 10 |
/**
|
| 11 |
-
*
|
| 12 |
-
* Handles single and multi-site (NW activation) cases
|
| 13 |
*
|
| 14 |
-
* @
|
| 15 |
-
* @global type $aio_wp_security
|
| 16 |
-
* @param type $networkwide
|
| 17 |
*/
|
| 18 |
-
|
| 19 |
-
global $wpdb;
|
| 20 |
global $aio_wp_security;
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
switch_to_blog($blog_id);
|
| 33 |
-
//Let's first save the current aio_wp_security_configs options in a temp option
|
| 34 |
-
update_option('aiowps_temp_configs', $aio_wp_security->configs->configs);
|
| 35 |
-
|
| 36 |
-
AIOWPSecurity_Deactivation::clear_cron_events();
|
| 37 |
-
restore_current_blog();
|
| 38 |
-
}
|
| 39 |
-
} else {
|
| 40 |
-
//Let's first save the current aio_wp_security_configs options in a temp option
|
| 41 |
-
update_option('aiowps_temp_configs', $aio_wp_security->configs->configs);
|
| 42 |
-
|
| 43 |
-
AIOWPSecurity_Deactivation::clear_cron_events();
|
| 44 |
}
|
| 45 |
-
//Deactivate all firewall and other .htaccess rules
|
| 46 |
-
AIOWPSecurity_Configure_Settings::turn_off_all_firewall_rules();
|
| 47 |
-
}
|
| 48 |
-
|
| 49 |
-
/**
|
| 50 |
-
* Helper function which clears aiowps cron events
|
| 51 |
-
*/
|
| 52 |
-
public static function clear_cron_events() {
|
| 53 |
-
wp_clear_scheduled_hook('aiowps_hourly_cron_event');
|
| 54 |
-
wp_clear_scheduled_hook('aiowps_daily_cron_event');
|
| 55 |
}
|
| 56 |
}
|
| 3 |
exit;//Exit if accessed directly
|
| 4 |
}
|
| 5 |
|
| 6 |
+
require_once(AIO_WP_SECURITY_PATH.'/classes/wp-security-base-tasks.php');
|
|
|
|
| 7 |
|
| 8 |
+
class AIOWPSecurity_Deactivation_Tasks extends AIOWPSecurity_Base_Tasks {
|
| 9 |
/**
|
| 10 |
+
* Run deactivation task for a single site.
|
|
|
|
| 11 |
*
|
| 12 |
+
* @return void
|
|
|
|
|
|
|
| 13 |
*/
|
| 14 |
+
protected static function run_for_a_site() {
|
|
|
|
| 15 |
global $aio_wp_security;
|
| 16 |
+
//Let's first save the current aio_wp_security_configs options in a temp option
|
| 17 |
+
update_option('aiowps_temp_configs', $aio_wp_security->configs->configs);
|
| 18 |
+
|
| 19 |
+
delete_option('aio_wp_security_configs');
|
| 20 |
+
|
| 21 |
+
if (is_main_site()) {
|
| 22 |
+
// Remove all firewall and other .htaccess rules and remove all settings from .htaccess file that were added by this plugin
|
| 23 |
+
AIOWPSecurity_Configure_Settings::turn_off_all_firewall_rules();
|
| 24 |
+
|
| 25 |
+
//Deactivates PHP-based firewall
|
| 26 |
+
AIOWPSecurity_Utility_Firewall::remove_firewall();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 27 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
}
|
| 29 |
}
|
|
@@ -16,7 +16,6 @@ class AIOWPSecurity_Logger {
|
|
| 16 |
|
| 17 |
public function __construct($debug_enabled) {
|
| 18 |
$this->debug_enabled = $debug_enabled;
|
| 19 |
-
$this->maybe_create_debug_log_table();
|
| 20 |
}
|
| 21 |
|
| 22 |
/**
|
|
@@ -29,46 +28,6 @@ class AIOWPSecurity_Logger {
|
|
| 29 |
return isset($this->debug_readable_level[$level_code]) ? $this->debug_readable_level[$level_code] : 'UNKNOWN';
|
| 30 |
}
|
| 31 |
|
| 32 |
-
/**
|
| 33 |
-
* Creates the debug log table if it doesn't already exist
|
| 34 |
-
*
|
| 35 |
-
* @return void
|
| 36 |
-
*/
|
| 37 |
-
private function maybe_create_debug_log_table() {
|
| 38 |
-
|
| 39 |
-
global $wpdb;
|
| 40 |
-
|
| 41 |
-
if (!function_exists('maybe_create_table')) {
|
| 42 |
-
//needed for the maybe_create_table function
|
| 43 |
-
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
|
| 44 |
-
}
|
| 45 |
-
|
| 46 |
-
$charset_collate = '';
|
| 47 |
-
if (!empty($wpdb->charset)) {
|
| 48 |
-
$charset_collate = "DEFAULT CHARACTER SET $wpdb->charset";
|
| 49 |
-
} else {
|
| 50 |
-
$charset_collate = "DEFAULT CHARSET=utf8";
|
| 51 |
-
}
|
| 52 |
-
if (!empty($wpdb->collate)) {
|
| 53 |
-
$charset_collate .= " COLLATE $wpdb->collate";
|
| 54 |
-
}
|
| 55 |
-
|
| 56 |
-
//This exists as a constant, but multisite will need to refresh $wpdb->prefix
|
| 57 |
-
$debug_log_tbl_name = $wpdb->prefix.'aiowps_debug_log';
|
| 58 |
-
|
| 59 |
-
$debug_log_tbl_sql = "CREATE TABLE " . $debug_log_tbl_name . " (
|
| 60 |
-
id bigint(20) NOT NULL AUTO_INCREMENT,
|
| 61 |
-
level varchar(25) NOT NULL DEFAULT '',
|
| 62 |
-
message text NOT NULL DEFAULT '',
|
| 63 |
-
type varchar(25) NOT NULL DEFAULT '',
|
| 64 |
-
created datetime NOT NULL DEFAULT '1000-10-10 10:00:00',
|
| 65 |
-
PRIMARY KEY (id)
|
| 66 |
-
)" . $charset_collate . ";";
|
| 67 |
-
|
| 68 |
-
maybe_create_table($debug_log_tbl_name, $debug_log_tbl_sql);
|
| 69 |
-
|
| 70 |
-
}
|
| 71 |
-
|
| 72 |
/**
|
| 73 |
* Clears the debug logs
|
| 74 |
*
|
| 16 |
|
| 17 |
public function __construct($debug_enabled) {
|
| 18 |
$this->debug_enabled = $debug_enabled;
|
|
|
|
| 19 |
}
|
| 20 |
|
| 21 |
/**
|
| 28 |
return isset($this->debug_readable_level[$level_code]) ? $this->debug_readable_level[$level_code] : 'UNKNOWN';
|
| 29 |
}
|
| 30 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
/**
|
| 32 |
* Clears the debug logs
|
| 33 |
*
|
|
@@ -28,16 +28,18 @@ class AIOWPSecurity_General_Init_Tasks {
|
|
| 28 |
add_filter('retrieve_password_message', array($this, 'decode_reset_pw_msg'), 10, 4); //Fix for non decoded html entities in password reset link
|
| 29 |
}
|
| 30 |
|
| 31 |
-
if (current_user_can(AIOWPSEC_MANAGEMENT_PERMISSION) &&
|
| 32 |
-
|
| 33 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
|
| 35 |
-
if (current_user_can(AIOWPSEC_MANAGEMENT_PERMISSION)) {
|
| 36 |
$this->reapply_htaccess_rules();
|
| 37 |
add_action('admin_notices', array($this,'reapply_htaccess_rules_notice'));
|
| 38 |
}
|
| 39 |
|
| 40 |
-
|
| 41 |
/**
|
| 42 |
* Send X-Frame-Options: SAMEORIGIN in HTTP header
|
| 43 |
*/
|
|
@@ -54,13 +56,38 @@ class AIOWPSecurity_General_Init_Tasks {
|
|
| 54 |
// For the cookie based brute force prevention feature
|
| 55 |
if ($aio_wp_security->configs->get_value('aiowps_enable_brute_force_attack_prevention') == 1) {
|
| 56 |
$bfcf_secret_word = $aio_wp_security->configs->get_value('aiowps_brute_force_secret_word');
|
|
|
|
| 57 |
if (isset($_GET[$bfcf_secret_word])) {
|
| 58 |
// If URL contains secret word in query param then set cookie and then redirect to the login page
|
| 59 |
-
AIOWPSecurity_Utility::set_cookie_value($bfcf_secret_word,
|
| 60 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 61 |
}
|
| 62 |
}
|
| 63 |
-
|
| 64 |
// Stop users enumeration feature
|
| 65 |
if ($aio_wp_security->configs->get_value('aiowps_prevent_users_enumeration') == 1) {
|
| 66 |
include_once(AIO_WP_SECURITY_PATH.'/other-includes/wp-security-stop-users-enumeration.php');
|
|
@@ -167,6 +194,25 @@ class AIOWPSecurity_General_Init_Tasks {
|
|
| 167 |
}
|
| 168 |
}
|
| 169 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 170 |
// For lost password captcha feature
|
| 171 |
if ($aio_wp_security->configs->get_value('aiowps_enable_lost_password_captcha') == '1') {
|
| 172 |
if (!is_user_logged_in()) {
|
|
@@ -181,7 +227,7 @@ class AIOWPSecurity_General_Init_Tasks {
|
|
| 181 |
}
|
| 182 |
|
| 183 |
// For registration page captcha feature
|
| 184 |
-
if (
|
| 185 |
$blog_id = get_current_blog_id();
|
| 186 |
switch_to_blog($blog_id);
|
| 187 |
if ($aio_wp_security->configs->get_value('aiowps_enable_registration_page_captcha') == '1') {
|
|
@@ -201,7 +247,7 @@ class AIOWPSecurity_General_Init_Tasks {
|
|
| 201 |
}
|
| 202 |
|
| 203 |
// For comment captcha feature or custom login form captcha
|
| 204 |
-
if (
|
| 205 |
$blog_id = get_current_blog_id();
|
| 206 |
switch_to_blog($blog_id);
|
| 207 |
if ($aio_wp_security->configs->get_value('aiowps_enable_comment_captcha') == '1') {
|
|
@@ -248,7 +294,6 @@ class AIOWPSecurity_General_Init_Tasks {
|
|
| 248 |
if ($aio_wp_security->configs->get_value('aiowps_enable_404_logging') == '1') {
|
| 249 |
add_action('wp_head', array($this, 'check_404_event'));
|
| 250 |
}
|
| 251 |
-
|
| 252 |
// Add more tasks that need to be executed at init time
|
| 253 |
|
| 254 |
} // end _construct()
|
|
@@ -435,6 +480,35 @@ class AIOWPSecurity_General_Init_Tasks {
|
|
| 435 |
echo $honey_input;
|
| 436 |
}
|
| 437 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 438 |
public function process_comment_post($comment) {
|
| 439 |
global $aio_wp_security;
|
| 440 |
if (is_user_logged_in()) {
|
|
@@ -657,4 +731,16 @@ class AIOWPSecurity_General_Init_Tasks {
|
|
| 657 |
wp_enqueue_script('google-recaptcha', 'https://www.google.com/recaptcha/api.js', false);
|
| 658 |
}
|
| 659 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 660 |
}
|
| 28 |
add_filter('retrieve_password_message', array($this, 'decode_reset_pw_msg'), 10, 4); //Fix for non decoded html entities in password reset link
|
| 29 |
}
|
| 30 |
|
| 31 |
+
if (current_user_can(AIOWPSEC_MANAGEMENT_PERMISSION) && is_admin()) {
|
| 32 |
+
if ($aio_wp_security->configs->get_value('aios_is_google_recaptcha_wrong_site_key')) {
|
| 33 |
+
add_action('all_admin_notices', array($this, 'google_recaptcha_notice'));
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
add_action('all_admin_notices', array($this, 'do_firewall_notice'));
|
| 37 |
+
add_action('admin_post_aiowps_firewall_setup', array(AIOWPSecurity_Firewall_Setup_Notice::get_instance(), 'handle_setup_form'));
|
| 38 |
|
|
|
|
| 39 |
$this->reapply_htaccess_rules();
|
| 40 |
add_action('admin_notices', array($this,'reapply_htaccess_rules_notice'));
|
| 41 |
}
|
| 42 |
|
|
|
|
| 43 |
/**
|
| 44 |
* Send X-Frame-Options: SAMEORIGIN in HTTP header
|
| 45 |
*/
|
| 56 |
// For the cookie based brute force prevention feature
|
| 57 |
if ($aio_wp_security->configs->get_value('aiowps_enable_brute_force_attack_prevention') == 1) {
|
| 58 |
$bfcf_secret_word = $aio_wp_security->configs->get_value('aiowps_brute_force_secret_word');
|
| 59 |
+
$login_page_slug = $aio_wp_security->configs->get_value('aiowps_login_page_slug');
|
| 60 |
if (isset($_GET[$bfcf_secret_word])) {
|
| 61 |
// If URL contains secret word in query param then set cookie and then redirect to the login page
|
| 62 |
+
AIOWPSecurity_Utility::set_cookie_value($bfcf_secret_word, '1');
|
| 63 |
+
if ('1' == $aio_wp_security->configs->get_value('aiowps_enable_rename_login_page') && !is_user_logged_in()) {
|
| 64 |
+
$login_url = site_url($login_page_slug);
|
| 65 |
+
AIOWPSecurity_Utility::redirect_to_url($login_url);
|
| 66 |
+
} else {
|
| 67 |
+
AIOWPSecurity_Utility::redirect_to_url(AIOWPSEC_WP_URL.'/wp-admin');
|
| 68 |
+
}
|
| 69 |
+
} else {
|
| 70 |
+
$secret_word_cookie_val = AIOWPSecurity_Utility::get_cookie_value($bfcf_secret_word);
|
| 71 |
+
$pw_protected_exception = $aio_wp_security->configs->get_value('aiowps_brute_force_attack_prevention_pw_protected_exception');
|
| 72 |
+
$prevent_ajax_exception = $aio_wp_security->configs->get_value('aiowps_brute_force_attack_prevention_ajax_exception');
|
| 73 |
+
|
| 74 |
+
if ('' != $_SERVER['REQUEST_URI'] && 1 != $secret_word_cookie_val) {
|
| 75 |
+
// admin section or login page or login custom slug called
|
| 76 |
+
$is_admin_or_login = (false != strpos($_SERVER['REQUEST_URI'], 'wp-admin') || false != strpos($_SERVER['REQUEST_URI'], 'wp-login') || ('' != $login_page_slug && false != strpos($_SERVER['REQUEST_URI'], $login_page_slug))) ? 1 : 0;
|
| 77 |
+
|
| 78 |
+
// admin side ajax called
|
| 79 |
+
$is_admin_ajax_request = ('1' == $prevent_ajax_exception && false != strpos($_SERVER['REQUEST_URI'], 'wp-admin/admin-ajax.php')) ? 1 : 0;
|
| 80 |
+
|
| 81 |
+
// password protected page called
|
| 82 |
+
$is_password_protected_access = ('1' == $pw_protected_exception && isset($_GET['action']) && 'postpass' == $_GET['action']) ? 1 : 0;
|
| 83 |
+
// cookie based brute force on and accessing admin without ajax and password protected then redirect
|
| 84 |
+
if ($is_admin_or_login && !$is_admin_ajax_request && !$is_password_protected_access) {
|
| 85 |
+
$redirect_url = $aio_wp_security->configs->get_value('aiowps_cookie_based_brute_force_redirect_url');
|
| 86 |
+
AIOWPSecurity_Utility::redirect_to_url($redirect_url);
|
| 87 |
+
}
|
| 88 |
+
}
|
| 89 |
}
|
| 90 |
}
|
|
|
|
| 91 |
// Stop users enumeration feature
|
| 92 |
if ($aio_wp_security->configs->get_value('aiowps_prevent_users_enumeration') == 1) {
|
| 93 |
include_once(AIO_WP_SECURITY_PATH.'/other-includes/wp-security-stop-users-enumeration.php');
|
| 194 |
}
|
| 195 |
}
|
| 196 |
|
| 197 |
+
// For disable application password feature hide generate password
|
| 198 |
+
if ('1' == $aio_wp_security->configs->get_value('aiowps_disable_application_password')) {
|
| 199 |
+
add_filter('wp_is_application_passwords_available', '__return_false');
|
| 200 |
+
add_action('edit_user_profile', array($this, 'show_disabled_application_password_message'));
|
| 201 |
+
add_action('show_user_profile', array($this, 'show_disabled_application_password_message'));
|
| 202 |
+
|
| 203 |
+
// Override the wp_die handler for app passwords were disabled.
|
| 204 |
+
if (!empty($_SERVER['SCRIPT_FILENAME']) && ABSPATH . 'wp-admin/authorize-application.php' == $_SERVER['SCRIPT_FILENAME']) {
|
| 205 |
+
add_filter('wp_die_handler', function () {
|
| 206 |
+
return function ($message, $title, $args) {
|
| 207 |
+
if ('Application passwords are not available.' == $message) {
|
| 208 |
+
$message = htmlspecialchars(__('Application passwords have been disabled by All In One WP Security & Firewall plugin.', 'all-in-one-wp-security-and-firewall'));
|
| 209 |
+
}
|
| 210 |
+
_default_wp_die_handler($message, $title, $args);
|
| 211 |
+
};
|
| 212 |
+
}, 10, 1);
|
| 213 |
+
}
|
| 214 |
+
}
|
| 215 |
+
|
| 216 |
// For lost password captcha feature
|
| 217 |
if ($aio_wp_security->configs->get_value('aiowps_enable_lost_password_captcha') == '1') {
|
| 218 |
if (!is_user_logged_in()) {
|
| 227 |
}
|
| 228 |
|
| 229 |
// For registration page captcha feature
|
| 230 |
+
if (is_multisite()) {
|
| 231 |
$blog_id = get_current_blog_id();
|
| 232 |
switch_to_blog($blog_id);
|
| 233 |
if ($aio_wp_security->configs->get_value('aiowps_enable_registration_page_captcha') == '1') {
|
| 247 |
}
|
| 248 |
|
| 249 |
// For comment captcha feature or custom login form captcha
|
| 250 |
+
if (is_multisite()) {
|
| 251 |
$blog_id = get_current_blog_id();
|
| 252 |
switch_to_blog($blog_id);
|
| 253 |
if ($aio_wp_security->configs->get_value('aiowps_enable_comment_captcha') == '1') {
|
| 294 |
if ($aio_wp_security->configs->get_value('aiowps_enable_404_logging') == '1') {
|
| 295 |
add_action('wp_head', array($this, 'check_404_event'));
|
| 296 |
}
|
|
|
|
| 297 |
// Add more tasks that need to be executed at init time
|
| 298 |
|
| 299 |
} // end _construct()
|
| 480 |
echo $honey_input;
|
| 481 |
}
|
| 482 |
|
| 483 |
+
/**
|
| 484 |
+
* Shows application password disabed message on user edit profile page.
|
| 485 |
+
* If logged user is admin showing the Change Setting option.
|
| 486 |
+
*
|
| 487 |
+
* @return void
|
| 488 |
+
*/
|
| 489 |
+
public function show_disabled_application_password_message() {
|
| 490 |
+
if (is_user_logged_in() && is_admin()) {
|
| 491 |
+
$disabled_message = '<h2>'.__('Application Passwords', 'all-in-one-wp-security-and-firewall').'</h2>';
|
| 492 |
+
$disabled_message .= '<table class="form-table" role="presentation">';
|
| 493 |
+
$disabled_message .= '<tbody>';
|
| 494 |
+
$disabled_message .= '<tr id="disable-password">';
|
| 495 |
+
$disabled_message .= '<th>'.__('Disabled').'</th>';
|
| 496 |
+
$disabled_message .= '<td>'.htmlspecialchars(__('Application passwords have been disabled by All In One WP Security & Firewall plugin.', 'all-in-one-wp-security-and-firewall'));
|
| 497 |
+
if (current_user_can(AIOWPSEC_MANAGEMENT_PERMISSION)) {
|
| 498 |
+
$aiowps_addtional_setting_url = 'admin.php?page=aiowpsec_userlogin&tab=additional';
|
| 499 |
+
$change_setting_url = is_multisite() ? network_admin_url($aiowps_addtional_setting_url) : admin_url($aiowps_addtional_setting_url);
|
| 500 |
+
$disabled_message .= '<p><a href="'.$change_setting_url.'" class="button">'.__('Change Setting', 'all-in-one-wp-security-and-firewall').'</a></p>';
|
| 501 |
+
} else {
|
| 502 |
+
$disabled_message .= ' '.__('Site admin can only change this setting.', 'all-in-one-wp-security-and-firewall');
|
| 503 |
+
}
|
| 504 |
+
$disabled_message .= '</td>';
|
| 505 |
+
$disabled_message .= '</tr>';
|
| 506 |
+
$disabled_message .= '<tbody>';
|
| 507 |
+
$disabled_message .= '</table>';
|
| 508 |
+
echo $disabled_message;
|
| 509 |
+
}
|
| 510 |
+
}
|
| 511 |
+
|
| 512 |
public function process_comment_post($comment) {
|
| 513 |
global $aio_wp_security;
|
| 514 |
if (is_user_logged_in()) {
|
| 731 |
wp_enqueue_script('google-recaptcha', 'https://www.google.com/recaptcha/api.js', false);
|
| 732 |
}
|
| 733 |
}
|
| 734 |
+
|
| 735 |
+
/**
|
| 736 |
+
* Shows the firewall notice
|
| 737 |
+
*
|
| 738 |
+
* @return void
|
| 739 |
+
*/
|
| 740 |
+
public function do_firewall_notice() {
|
| 741 |
+
|
| 742 |
+
$firewall_setup = AIOWPSecurity_Firewall_Setup_Notice::get_instance();
|
| 743 |
+
$firewall_setup->start_firewall_setup();
|
| 744 |
+
|
| 745 |
+
}
|
| 746 |
}
|
|
@@ -3,10 +3,14 @@ if (!defined('ABSPATH')) {
|
|
| 3 |
exit;//Exit if accessed directly
|
| 4 |
}
|
| 5 |
|
| 6 |
-
//Allows activating via wp-cli
|
| 7 |
-
require_once(dirname(__FILE__) . '/wp-security-configure-settings.php');
|
| 8 |
-
|
| 9 |
class AIOWPSecurity_Installer {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
public static function run_installer($networkwide = '') {
|
| 11 |
global $wpdb;
|
| 12 |
if (function_exists('is_multisite') && is_multisite() && $networkwide) {
|
|
@@ -57,6 +61,8 @@ class AIOWPSecurity_Installer {
|
|
| 57 |
$perm_block_tbl_name = AIOWPSEC_TBL_PERM_BLOCK;
|
| 58 |
}
|
| 59 |
|
|
|
|
|
|
|
| 60 |
$charset_collate = '';
|
| 61 |
if (!empty($wpdb->charset)) {
|
| 62 |
$charset_collate = "DEFAULT CHARACTER SET $wpdb->charset";
|
|
@@ -76,6 +82,8 @@ class AIOWPSecurity_Installer {
|
|
| 76 |
failed_login_ip varchar(100) NOT NULL DEFAULT '',
|
| 77 |
lock_reason varchar(128) NOT NULL DEFAULT '',
|
| 78 |
unlock_key varchar(128) NOT NULL DEFAULT '',
|
|
|
|
|
|
|
| 79 |
PRIMARY KEY (id)
|
| 80 |
)" . $charset_collate . ";";
|
| 81 |
dbDelta($ld_tbl_sql);
|
|
@@ -146,6 +154,16 @@ class AIOWPSecurity_Installer {
|
|
| 146 |
)" . $charset_collate . ";";
|
| 147 |
dbDelta($pb_tbl_sql);
|
| 148 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 149 |
update_option("aiowpsec_db_version", AIO_WP_SECURITY_DB_VERSION);
|
| 150 |
}
|
| 151 |
|
|
@@ -153,7 +171,7 @@ class AIOWPSecurity_Installer {
|
|
| 153 |
global $aio_wp_security;
|
| 154 |
//Create our folder in the "wp-content" directory
|
| 155 |
$aiowps_dir = WP_CONTENT_DIR . '/' . AIO_WP_SECURITY_BACKUPS_DIR_NAME;
|
| 156 |
-
if (!is_dir($aiowps_dir)) {
|
| 157 |
mkdir($aiowps_dir, 0755, true);
|
| 158 |
//Let's also create an empty index.html file in this folder
|
| 159 |
$index_file = $aiowps_dir . '/index.html';
|
|
@@ -209,11 +227,15 @@ class AIOWPSecurity_Installer {
|
|
| 209 |
* Handles both single and multi-site (NW activation) cases
|
| 210 |
*
|
| 211 |
* @global type $wpdb
|
| 212 |
-
* @param
|
|
|
|
| 213 |
*/
|
| 214 |
-
public static function set_cron_tasks_upon_activation($networkwide) {
|
| 215 |
-
|
| 216 |
-
|
|
|
|
|
|
|
|
|
|
| 217 |
// check if it is a network activation
|
| 218 |
$blogids = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs");
|
| 219 |
foreach ($blogids as $blog_id) {
|
|
@@ -229,9 +251,14 @@ class AIOWPSecurity_Installer {
|
|
| 229 |
}
|
| 230 |
|
| 231 |
/**
|
| 232 |
-
* Helper function for scheduling aiowps cron events
|
|
|
|
|
|
|
| 233 |
*/
|
| 234 |
public static function schedule_cron_events() {
|
|
|
|
|
|
|
|
|
|
| 235 |
if (!wp_next_scheduled('aiowps_hourly_cron_event')) {
|
| 236 |
wp_schedule_event(time(), 'hourly', 'aiowps_hourly_cron_event'); //schedule an hourly cron event
|
| 237 |
}
|
| 3 |
exit;//Exit if accessed directly
|
| 4 |
}
|
| 5 |
|
|
|
|
|
|
|
|
|
|
| 6 |
class AIOWPSecurity_Installer {
|
| 7 |
+
|
| 8 |
+
/**
|
| 9 |
+
* Run installer function.
|
| 10 |
+
*
|
| 11 |
+
* @param boolean $networkwide
|
| 12 |
+
* @return void
|
| 13 |
+
*/
|
| 14 |
public static function run_installer($networkwide = '') {
|
| 15 |
global $wpdb;
|
| 16 |
if (function_exists('is_multisite') && is_multisite() && $networkwide) {
|
| 61 |
$perm_block_tbl_name = AIOWPSEC_TBL_PERM_BLOCK;
|
| 62 |
}
|
| 63 |
|
| 64 |
+
$debug_log_tbl_name = AIOWPSEC_TBL_DEBUG_LOG;
|
| 65 |
+
|
| 66 |
$charset_collate = '';
|
| 67 |
if (!empty($wpdb->charset)) {
|
| 68 |
$charset_collate = "DEFAULT CHARACTER SET $wpdb->charset";
|
| 82 |
failed_login_ip varchar(100) NOT NULL DEFAULT '',
|
| 83 |
lock_reason varchar(128) NOT NULL DEFAULT '',
|
| 84 |
unlock_key varchar(128) NOT NULL DEFAULT '',
|
| 85 |
+
is_lockout_email_sent tinyint(1) NOT NULL DEFAULT '0',
|
| 86 |
+
backtrace_log text NOT NULL DEFAULT '',
|
| 87 |
PRIMARY KEY (id)
|
| 88 |
)" . $charset_collate . ";";
|
| 89 |
dbDelta($ld_tbl_sql);
|
| 154 |
)" . $charset_collate . ";";
|
| 155 |
dbDelta($pb_tbl_sql);
|
| 156 |
|
| 157 |
+
$debug_log_tbl_sql = "CREATE TABLE " . $debug_log_tbl_name . " (
|
| 158 |
+
id bigint(20) NOT NULL AUTO_INCREMENT,
|
| 159 |
+
level varchar(25) NOT NULL DEFAULT '',
|
| 160 |
+
message text NOT NULL DEFAULT '',
|
| 161 |
+
type varchar(25) NOT NULL DEFAULT '',
|
| 162 |
+
created datetime NOT NULL DEFAULT '1000-10-10 10:00:00',
|
| 163 |
+
PRIMARY KEY (id)
|
| 164 |
+
)" . $charset_collate . ";";
|
| 165 |
+
dbDelta($debug_log_tbl_sql);
|
| 166 |
+
|
| 167 |
update_option("aiowpsec_db_version", AIO_WP_SECURITY_DB_VERSION);
|
| 168 |
}
|
| 169 |
|
| 171 |
global $aio_wp_security;
|
| 172 |
//Create our folder in the "wp-content" directory
|
| 173 |
$aiowps_dir = WP_CONTENT_DIR . '/' . AIO_WP_SECURITY_BACKUPS_DIR_NAME;
|
| 174 |
+
if (!is_dir($aiowps_dir) && is_writable(WP_CONTENT_DIR)) {
|
| 175 |
mkdir($aiowps_dir, 0755, true);
|
| 176 |
//Let's also create an empty index.html file in this folder
|
| 177 |
$index_file = $aiowps_dir . '/index.html';
|
| 227 |
* Handles both single and multi-site (NW activation) cases
|
| 228 |
*
|
| 229 |
* @global type $wpdb
|
| 230 |
+
* @param Boolean $networkwide Whether set cronjob networkwide or normal site.
|
| 231 |
+
* @return Void
|
| 232 |
*/
|
| 233 |
+
public static function set_cron_tasks_upon_activation($networkwide = false) {
|
| 234 |
+
require_once(__DIR__.'/wp-security-cronjob-handler.php');
|
| 235 |
+
// It is required because we are going to schedule a 15-minute cron event upon activation.
|
| 236 |
+
add_filter('cron_schedules', array('AIOWPSecurity_Cronjob_Handler', 'cron_schedules'));
|
| 237 |
+
if (is_multisite() && $networkwide) {
|
| 238 |
+
global $wpdb;
|
| 239 |
// check if it is a network activation
|
| 240 |
$blogids = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs");
|
| 241 |
foreach ($blogids as $blog_id) {
|
| 251 |
}
|
| 252 |
|
| 253 |
/**
|
| 254 |
+
* Helper function for scheduling aiowps cron events.
|
| 255 |
+
*
|
| 256 |
+
* @return Void
|
| 257 |
*/
|
| 258 |
public static function schedule_cron_events() {
|
| 259 |
+
if (!wp_next_scheduled('aios_15_minutes_cron_event')) {
|
| 260 |
+
wp_schedule_event(time(), 'aios-every-15-minutes', 'aios_15_minutes_cron_event'); //schedule a 15 minutes cron event
|
| 261 |
+
}
|
| 262 |
if (!wp_next_scheduled('aiowps_hourly_cron_event')) {
|
| 263 |
wp_schedule_event(time(), 'hourly', 'aiowps_hourly_cron_event'); //schedule an hourly cron event
|
| 264 |
}
|
|
@@ -2,9 +2,9 @@
|
|
| 2 |
|
| 3 |
if (!defined('AIO_WP_SECURITY_PATH')) die('No direct access allowed');
|
| 4 |
|
| 5 |
-
if (!class_exists('
|
| 6 |
|
| 7 |
-
class AIOWPSecurity_Notices extends
|
| 8 |
|
| 9 |
private $initialized = false;
|
| 10 |
|
|
@@ -22,6 +22,26 @@ class AIOWPSecurity_Notices extends Updraft_Notices {
|
|
| 22 |
$parent_notice_content = parent::populate_notices_content();
|
| 23 |
|
| 24 |
$child_notice_content = array(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
'rate_plugin' => array(
|
| 26 |
'text' => sprintf(htmlspecialchars(__('Hey - We noticed All In One WP Security & Firewall has kept your site safe for a while. If you like us, please consider leaving a positive review to spread the word. Or if you have any issues or questions please leave us a support message %s.', 'all-in-one-wp-security-and-firewall')), '<a href="https://wordpress.org/support/plugin/all-in-one-wp-security-and-firewall/" target="_blank">'.__('here', 'all-in-one-wp-security-and-firewall').'</a>').'<br>'.__('Thank you so much!', 'all-in-one-wp-security-and-firewall').'<br><br>- <b>'.__('Team All In One WP Security & Firewall', 'all-in-one-wp-security-and-firewall').'</b>',
|
| 27 |
'image' => 'notices/aiowps-logo.png',
|
|
@@ -57,6 +77,78 @@ class AIOWPSecurity_Notices extends Updraft_Notices {
|
|
| 57 |
|
| 58 |
return array_merge($parent_notice_content, $child_notice_content);
|
| 59 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
|
| 61 |
/**
|
| 62 |
* Call this method to setup the notices
|
|
@@ -170,6 +262,8 @@ class AIOWPSecurity_Notices extends Updraft_Notices {
|
|
| 170 |
* Checks whether a notice is dismissed(returns true) or not(returns false).
|
| 171 |
*
|
| 172 |
* @param String $dismiss_time - dismiss time id for the notice
|
|
|
|
|
|
|
| 173 |
*/
|
| 174 |
protected function check_notice_dismissed($dismiss_time) {
|
| 175 |
$time_now = $this->get_time_now();
|
|
@@ -197,6 +291,8 @@ class AIOWPSecurity_Notices extends Updraft_Notices {
|
|
| 197 |
$template_file = 'report.php';
|
| 198 |
} elseif ('report-plain' == $position) {
|
| 199 |
$template_file = 'report-plain.php';
|
|
|
|
|
|
|
| 200 |
} else {
|
| 201 |
$template_file = 'horizontal-notice.php';
|
| 202 |
}
|
| 2 |
|
| 3 |
if (!defined('AIO_WP_SECURITY_PATH')) die('No direct access allowed');
|
| 4 |
|
| 5 |
+
if (!class_exists('Updraft_Notices_1_2')) require_once(AIO_WP_SECURITY_PATH.'/vendor/team-updraft/common-libs/src/updraft-notices/updraft-notices.php');
|
| 6 |
|
| 7 |
+
class AIOWPSecurity_Notices extends Updraft_Notices_1_2 {
|
| 8 |
|
| 9 |
private $initialized = false;
|
| 10 |
|
| 22 |
$parent_notice_content = parent::populate_notices_content();
|
| 23 |
|
| 24 |
$child_notice_content = array(
|
| 25 |
+
// Upgrade AIOS backup to UDP backup in the 5.0.0 version
|
| 26 |
+
'automated-database-backup' => array(
|
| 27 |
+
'title' => htmlspecialchars(__('Removed database backup feature from the All In One WP Security & Firewall plugin', 'all-in-one-wp-security-and-firewall')),
|
| 28 |
+
'text' => '<p>' .
|
| 29 |
+
__('Beginning with version 5.0.0, AIOS has replaced the AIOS backup method with the superior UpdraftPlus method.', 'all-in-one-wp-security-and-firewall') . ' '.
|
| 30 |
+
__('It remains free and is fully supported by the UpdraftPlus team.', 'all-in-one-wp-security-and-firewall') .
|
| 31 |
+
'</p>' .
|
| 32 |
+
'<p>' .
|
| 33 |
+
__('You are seeing this notice because you have previously set up automated database backups in AIOS.', 'all-in-one-wp-security-and-firewall') . ' ' .
|
| 34 |
+
__('Would you like to set up scheduled backups with UpdraftPlus?', 'all-in-one-wp-security-and-firewall') .
|
| 35 |
+
'</p>',
|
| 36 |
+
'button_link' => add_query_arg(array(
|
| 37 |
+
'page' => 'aiowpsec_database',
|
| 38 |
+
'tab' => 'tab2',
|
| 39 |
+
), admin_url('admin.php')) . '#automated-scheduled-backups-heading',
|
| 40 |
+
'button_meta' => __('Setup UpdraftPlus backup plugin', 'all-in-one-wp-security-and-firewall'),
|
| 41 |
+
'dismiss_time' => 'dismiss_automated_database_backup_notice',
|
| 42 |
+
'supported_positions' => array('automated-database-backup'),
|
| 43 |
+
'validity_function' => 'should_show_automated_database_backup_notice',
|
| 44 |
+
),
|
| 45 |
'rate_plugin' => array(
|
| 46 |
'text' => sprintf(htmlspecialchars(__('Hey - We noticed All In One WP Security & Firewall has kept your site safe for a while. If you like us, please consider leaving a positive review to spread the word. Or if you have any issues or questions please leave us a support message %s.', 'all-in-one-wp-security-and-firewall')), '<a href="https://wordpress.org/support/plugin/all-in-one-wp-security-and-firewall/" target="_blank">'.__('here', 'all-in-one-wp-security-and-firewall').'</a>').'<br>'.__('Thank you so much!', 'all-in-one-wp-security-and-firewall').'<br><br>- <b>'.__('Team All In One WP Security & Firewall', 'all-in-one-wp-security-and-firewall').'</b>',
|
| 47 |
'image' => 'notices/aiowps-logo.png',
|
| 77 |
|
| 78 |
return array_merge($parent_notice_content, $child_notice_content);
|
| 79 |
}
|
| 80 |
+
|
| 81 |
+
/**
|
| 82 |
+
* Decides whether to show an automated database backup notice.
|
| 83 |
+
*
|
| 84 |
+
* @return Boolean True if an automated database notice should be shown, otherwise false.
|
| 85 |
+
*/
|
| 86 |
+
protected function should_show_automated_database_backup_notice() {
|
| 87 |
+
if ($this->is_database_backup_admin_page_tab()) {
|
| 88 |
+
return false;
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
if ($this->is_updraftplus_plugin_active() && $this->is_schedule_database_backup_set_in_updraftplus()) {
|
| 92 |
+
return false;
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
global $aio_wp_security;
|
| 96 |
+
if ('1' == $aio_wp_security->configs->get_value('aiowps_enable_automated_backups')) {
|
| 97 |
+
return true;
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
return false;
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
/**
|
| 104 |
+
* Whether the current page is the AIOS database backup admin page
|
| 105 |
+
*
|
| 106 |
+
* @return Boolean True if the current page is the AIOS database backup admin page, otherwise false.
|
| 107 |
+
*/
|
| 108 |
+
private function is_database_backup_admin_page_tab() {
|
| 109 |
+
return $this->is_database_security_admin_page() && $this->is_database_backup_tab();
|
| 110 |
+
}
|
| 111 |
+
|
| 112 |
+
/**
|
| 113 |
+
* Whether the current page is the database security admin page.
|
| 114 |
+
*
|
| 115 |
+
* @return Boolean True if the current page is the database security admin page, otherwise false.
|
| 116 |
+
*/
|
| 117 |
+
private function is_database_security_admin_page() {
|
| 118 |
+
return ('admin.php' == $GLOBALS['pagenow'] && isset($_GET['page']) && 'aiowpsec_database' == $_GET['page']);
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
/**
|
| 122 |
+
* Whether the current tab is the database backup tab.
|
| 123 |
+
*
|
| 124 |
+
* @return Boolean True if the current tab is the database backup tab, otherwise false.
|
| 125 |
+
*/
|
| 126 |
+
private function is_database_backup_tab() {
|
| 127 |
+
return (isset($_GET['tab']) && 'tab2' == $_GET['tab']);
|
| 128 |
+
}
|
| 129 |
+
|
| 130 |
+
/**
|
| 131 |
+
* Check whether the UpdraftPlus plugin is active or not.
|
| 132 |
+
*
|
| 133 |
+
* @return bool True if the UpdraftPlus plugin is active, otherwise false.
|
| 134 |
+
*/
|
| 135 |
+
private function is_updraftplus_plugin_active() {
|
| 136 |
+
return class_exists('UpdraftPlus');
|
| 137 |
+
}
|
| 138 |
+
|
| 139 |
+
/**
|
| 140 |
+
* Check whether the database backup scheduled in the UpdraftPlus plugin.
|
| 141 |
+
*
|
| 142 |
+
* @return bool
|
| 143 |
+
*/
|
| 144 |
+
private function is_schedule_database_backup_set_in_updraftplus() {
|
| 145 |
+
$updraft_interval_database_option_val = get_option('updraft_interval_database', '');
|
| 146 |
+
if (empty($updraft_interval_database_option_val) || 'manual' == $updraft_interval_database_option_val) {
|
| 147 |
+
return false;
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
return true;
|
| 151 |
+
}
|
| 152 |
|
| 153 |
/**
|
| 154 |
* Call this method to setup the notices
|
| 262 |
* Checks whether a notice is dismissed(returns true) or not(returns false).
|
| 263 |
*
|
| 264 |
* @param String $dismiss_time - dismiss time id for the notice
|
| 265 |
+
*
|
| 266 |
+
* @return boolean
|
| 267 |
*/
|
| 268 |
protected function check_notice_dismissed($dismiss_time) {
|
| 269 |
$time_now = $this->get_time_now();
|
| 291 |
$template_file = 'report.php';
|
| 292 |
} elseif ('report-plain' == $position) {
|
| 293 |
$template_file = 'report-plain.php';
|
| 294 |
+
} elseif ('automated-database-backup' == $position) {
|
| 295 |
+
$template_file = 'automated-database-backup-notice.php';
|
| 296 |
} else {
|
| 297 |
$template_file = 'horizontal-notice.php';
|
| 298 |
}
|
|
@@ -106,6 +106,11 @@ class AIOWPSecurity_Process_Renamed_Login_Page {
|
|
| 106 |
return $url;
|
| 107 |
}
|
| 108 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 109 |
public static function renamed_login_init_tasks() {
|
| 110 |
global $aio_wp_security;
|
| 111 |
|
|
@@ -183,32 +188,38 @@ class AIOWPSecurity_Process_Renamed_Login_Page {
|
|
| 183 |
}
|
| 184 |
}
|
| 185 |
|
| 186 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 187 |
|
| 188 |
$login_slug = $aio_wp_security->configs->get_value('aiowps_login_page_slug');
|
| 189 |
-
$
|
| 190 |
|
| 191 |
/*
|
| 192 |
* Compatibility fix for WPML plugin
|
| 193 |
*/
|
| 194 |
-
if (function_exists('wpml_object_id') && strpos($
|
| 195 |
-
$
|
| 196 |
function qtranxf_init_language() {}// phpcs:ignore Squiz.WhiteSpace.ScopeClosingBrace.ContentBefore,PEAR.WhiteSpace.ScopeClosingBrace.Line,Squiz.PHP.InnerFunctions.NotAllowed
|
| 197 |
}
|
| 198 |
|
| 199 |
/*
|
| 200 |
* *** Compatibility fix for qTranslate-X plugin ***
|
| 201 |
* qTranslate-X plugin modifies the result for the following command by adding the protocol and host to the url path:
|
| 202 |
-
*
|
| 203 |
* Therefore we will remove the protocol and host for the following cases:
|
| 204 |
* qTranslate-X is active AND the URL being accessed contains the secret slug
|
| 205 |
*/
|
| 206 |
-
if (function_exists('qtranxf_init_language') && strpos($
|
| 207 |
-
$
|
| 208 |
-
$
|
| 209 |
}
|
| 210 |
|
| 211 |
-
if (untrailingslashit($
|
| 212 |
if (empty($action) && is_user_logged_in()) {
|
| 213 |
//if user is already logged in but tries to access the renamed login page, send them to the dashboard
|
| 214 |
// or to requested redirect-page, filterd in 'login_redirect'.
|
|
@@ -243,9 +254,9 @@ class AIOWPSecurity_Process_Renamed_Login_Page {
|
|
| 243 |
global $aio_wp_security;
|
| 244 |
$login_slug = $aio_wp_security->configs->get_value('aiowps_login_page_slug');
|
| 245 |
if (get_option('permalink_structure')) {
|
| 246 |
-
return trailingslashit(trailingslashit(
|
| 247 |
} else {
|
| 248 |
-
return trailingslashit(
|
| 249 |
}
|
| 250 |
}
|
| 251 |
|
| 106 |
return $url;
|
| 107 |
}
|
| 108 |
|
| 109 |
+
/**
|
| 110 |
+
* Login page renamed related tasks, do not allow access if not logged with rename login page.
|
| 111 |
+
*
|
| 112 |
+
* @return void
|
| 113 |
+
*/
|
| 114 |
public static function renamed_login_init_tasks() {
|
| 115 |
global $aio_wp_security;
|
| 116 |
|
| 188 |
}
|
| 189 |
}
|
| 190 |
|
| 191 |
+
$parsed_url_path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
|
| 192 |
+
|
| 193 |
+
// for `wp plugin list` it will be empty so showing Not available isntead plugin list.
|
| 194 |
+
if (empty($parsed_url_path) && !defined('WP_CLI')) {
|
| 195 |
+
do_action('aiowps_before_wp_die_renamed_login');
|
| 196 |
+
wp_die(__('Not available.', 'all-in-one-wp-security-and-firewall'), 403);
|
| 197 |
+
}
|
| 198 |
|
| 199 |
$login_slug = $aio_wp_security->configs->get_value('aiowps_login_page_slug');
|
| 200 |
+
$site_url_with_slug = site_url($login_slug, 'relative');
|
| 201 |
|
| 202 |
/*
|
| 203 |
* Compatibility fix for WPML plugin
|
| 204 |
*/
|
| 205 |
+
if (function_exists('wpml_object_id') && strpos($site_url_with_slug, $login_slug)) {
|
| 206 |
+
$site_url_with_slug = site_url($login_slug);
|
| 207 |
function qtranxf_init_language() {}// phpcs:ignore Squiz.WhiteSpace.ScopeClosingBrace.ContentBefore,PEAR.WhiteSpace.ScopeClosingBrace.Line,Squiz.PHP.InnerFunctions.NotAllowed
|
| 208 |
}
|
| 209 |
|
| 210 |
/*
|
| 211 |
* *** Compatibility fix for qTranslate-X plugin ***
|
| 212 |
* qTranslate-X plugin modifies the result for the following command by adding the protocol and host to the url path:
|
| 213 |
+
* site_url($login_slug, 'relative');
|
| 214 |
* Therefore we will remove the protocol and host for the following cases:
|
| 215 |
* qTranslate-X is active AND the URL being accessed contains the secret slug
|
| 216 |
*/
|
| 217 |
+
if (function_exists('qtranxf_init_language') && strpos($site_url_with_slug, $login_slug)) {
|
| 218 |
+
$parsed_site_url_with_slug = parse_url($site_url_with_slug);
|
| 219 |
+
$site_url_with_slug = $parsed_site_url_with_slug['path']; //this will return just the path minus the protocol and host
|
| 220 |
}
|
| 221 |
|
| 222 |
+
if (untrailingslashit($parsed_url_path) === $site_url_with_slug || (!get_option('permalink_structure') && isset($_GET[$login_slug]))) {
|
| 223 |
if (empty($action) && is_user_logged_in()) {
|
| 224 |
//if user is already logged in but tries to access the renamed login page, send them to the dashboard
|
| 225 |
// or to requested redirect-page, filterd in 'login_redirect'.
|
| 254 |
global $aio_wp_security;
|
| 255 |
$login_slug = $aio_wp_security->configs->get_value('aiowps_login_page_slug');
|
| 256 |
if (get_option('permalink_structure')) {
|
| 257 |
+
return trailingslashit(trailingslashit(site_url()) . $login_slug);
|
| 258 |
} else {
|
| 259 |
+
return trailingslashit(site_url()) . '?' . $login_slug;
|
| 260 |
}
|
| 261 |
}
|
| 262 |
|
|
@@ -0,0 +1,104 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
if (!defined('ABSPATH')) die('No direct access allowed');
|
| 3 |
+
|
| 4 |
+
if (!class_exists('Simba_Two_Factor_Authentication')) require AIO_WP_SECURITY_PATH.'/includes/simba-tfa/simba-tfa.php';
|
| 5 |
+
|
| 6 |
+
/**
|
| 7 |
+
* This parent-child relationship enables the two to be split without affecting backwards compatibility for developers making direct calls
|
| 8 |
+
*
|
| 9 |
+
* This class is for the plugin encapsulation.
|
| 10 |
+
*/
|
| 11 |
+
class AIO_WP_Security_Simba_Two_Factor_Authentication_Plugin extends Simba_Two_Factor_Authentication {
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
/**
|
| 15 |
+
* Simba_Two_Factor_Authentication_Plugin Constructor
|
| 16 |
+
*
|
| 17 |
+
* @uses __FILE__
|
| 18 |
+
* @return Void
|
| 19 |
+
*/
|
| 20 |
+
public function __construct() {
|
| 21 |
+
|
| 22 |
+
if (!function_exists('mcrypt_get_iv_size') && !function_exists('openssl_cipher_iv_length')) {
|
| 23 |
+
add_action('all_admin_notices', array($this, 'admin_notice_missing_mcrypt_and_openssl'));
|
| 24 |
+
return;
|
| 25 |
+
}
|
| 26 |
+
add_filter('aiowpsecurity_setting_tabs', array($this, 'add_two_factor_setting_tab'));
|
| 27 |
+
add_action('admin_menu', array($this, 'menu_entry_for_user'), 30);
|
| 28 |
+
$this->version = AIO_WP_SECURITY_VERSION;
|
| 29 |
+
$this->set_user_settings_page_slug(AIOWPSEC_TWO_FACTOR_AUTH_MENU_SLUG);
|
| 30 |
+
$settings_page_heading = __('Two Factor Authentication - Admin Settings', 'all-in-one-wp-security-and-firewall');
|
| 31 |
+
$this->set_settings_page_heading($settings_page_heading);
|
| 32 |
+
$this->set_plugin_translate_url('https://translate.wordpress.org/projects/wp-plugins/all-in-one-wp-security-and-firewall/');
|
| 33 |
+
$this->set_site_wide_administration_url(admin_url('admin.php?page=aiowpsec_settings&tab=two-factor-authentication'));
|
| 34 |
+
$this->set_premium_version_url('https://aiowpsecurity.com');
|
| 35 |
+
$this->set_faq_url('https://wordpress.org/plugins/all-in-one-wp-security-and-firewall/#faq');
|
| 36 |
+
parent::__construct();
|
| 37 |
+
}
|
| 38 |
+
|
| 39 |
+
/**
|
| 40 |
+
* Runs upon the WP actions admin_menu and network_admin_menu
|
| 41 |
+
*/
|
| 42 |
+
public function menu_entry_for_user() {
|
| 43 |
+
|
| 44 |
+
$this->get_totp_controller()->potentially_port_private_keys();
|
| 45 |
+
|
| 46 |
+
global $current_user;
|
| 47 |
+
if ($this->is_activated_for_user($current_user->ID)) {
|
| 48 |
+
if (!current_user_can(AIOWPSEC_MANAGEMENT_PERMISSION)) {
|
| 49 |
+
$menu_icon_url = AIO_WP_SECURITY_URL . '/images/plugin-icon.png';
|
| 50 |
+
add_menu_page(__('WP Security', 'all-in-one-wp-security-and-firewall'), __('WP Security', 'all-in-one-wp-security-and-firewall'), AIOWPSEC_MANAGEMENT_PERMISSION, AIOWPSEC_MAIN_MENU_SLUG, '', $menu_icon_url);
|
| 51 |
+
}
|
| 52 |
+
add_submenu_page(AIOWPSEC_MAIN_MENU_SLUG, __('Two Factor Auth', 'all-in-one-wp-security-and-firewall'), __('Two Factor Auth', 'all-in-one-wp-security-and-firewall'), 'read', AIOWPSEC_TWO_FACTOR_AUTH_MENU_SLUG, array($this, 'show_dashboard_user_settings_page'));
|
| 53 |
+
}
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
/**
|
| 57 |
+
* Builds Two Factor Authentication tab
|
| 58 |
+
*
|
| 59 |
+
* @param array $tabs array that contain tab name and call back function
|
| 60 |
+
* @return array Returns all tabs with callback function name
|
| 61 |
+
*/
|
| 62 |
+
public function add_two_factor_setting_tab($tabs = array()) {
|
| 63 |
+
if (!current_user_can(AIOWPSEC_MANAGEMENT_PERMISSION)) return;
|
| 64 |
+
$tabs['two-factor-authentication'] = array(
|
| 65 |
+
'title' => __('Two Factor Authentication', 'all-in-one-wp-security-and-firewall-premium'),
|
| 66 |
+
'render_callback' => array($this, 'render_two_factor_authentication'),
|
| 67 |
+
'display_condition_callback' => 'is_main_site',
|
| 68 |
+
);
|
| 69 |
+
return $tabs;
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
/**
|
| 73 |
+
* Display the Two Factor Authentication tab & handle the operations
|
| 74 |
+
*/
|
| 75 |
+
public function render_two_factor_authentication() {
|
| 76 |
+
$this->get_totp_controller()->potentially_port_private_keys();
|
| 77 |
+
$this->show_admin_settings_page();
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
/**
|
| 81 |
+
* Include the admin settings page code.
|
| 82 |
+
*/
|
| 83 |
+
public function show_admin_settings_page() {
|
| 84 |
+
$totp_controller = $this->get_totp_controller();
|
| 85 |
+
$totp_controller->setUserHMACTypes();
|
| 86 |
+
if (!is_admin() || !current_user_can(AIOWPSEC_MANAGEMENT_PERMISSION)) return;
|
| 87 |
+
$this->include_template('admin-settings.php', array(
|
| 88 |
+
'totp_controller' => $totp_controller,
|
| 89 |
+
'settings_page_heading' => $this->get_settings_page_heading(),
|
| 90 |
+
'admin_settings_links' => array(),
|
| 91 |
+
));
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
/**
|
| 95 |
+
* Runs conditionally on the WP action all_admin_notices.
|
| 96 |
+
*/
|
| 97 |
+
public function admin_notice_missing_mcrypt_and_openssl() {
|
| 98 |
+
$this->show_admin_warning('<strong>'.__('PHP OpenSSL or mcrypt module required', 'all-in-one-wp-security-and-firewall').'</strong><br> '.__('The All In One WP Security plugin\'s Two Factor Authentication module requires either the PHP openssl (preferred) or mcrypt module to be installed. Please ask your web hosting company to install one of them.', 'all-in-one-wp-security-and-firewall'), 'error');
|
| 99 |
+
}
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
if (false === AIOWPSecurity_Utility::is_incopatible_tfa_premium_version_active() && false === AIOWPSecurity_Utility::is_tfa_or_self_plugin_activating()) {
|
| 103 |
+
$GLOBALS['simba_two_factor_authentication'] = new AIO_WP_Security_Simba_Two_Factor_Authentication_Plugin();
|
| 104 |
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
if (!defined('ABSPATH')) {
|
| 3 |
+
exit;//Exit if accessed directly
|
| 4 |
+
}
|
| 5 |
+
|
| 6 |
+
require_once(AIO_WP_SECURITY_PATH.'/classes/wp-security-base-tasks.php');
|
| 7 |
+
|
| 8 |
+
class AIOWPSecurity_Uninstallation_Tasks extends AIOWPSecurity_Base_Tasks {
|
| 9 |
+
/**
|
| 10 |
+
* Runs various uninstallation tasks
|
| 11 |
+
* Handles single and multi-site (NW activation) cases
|
| 12 |
+
*
|
| 13 |
+
* @global type $wpdb
|
| 14 |
+
* @global type $aio_wp_security
|
| 15 |
+
*/
|
| 16 |
+
public static function run() {
|
| 17 |
+
if (is_multisite()) {
|
| 18 |
+
delete_site_transient('users_online');
|
| 19 |
+
} else {
|
| 20 |
+
delete_transient('users_online');
|
| 21 |
+
}
|
| 22 |
+
parent::run();
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
/**
|
| 26 |
+
* Run uninstallation task for a single site.
|
| 27 |
+
*
|
| 28 |
+
* @return void
|
| 29 |
+
*/
|
| 30 |
+
protected static function run_for_a_site() {
|
| 31 |
+
self::clear_cron_events();
|
| 32 |
+
// Drop db tables and configs
|
| 33 |
+
self::drop_database_tables_and_configs();
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
/**
|
| 37 |
+
* Function to drop database tables and remove configuration settings
|
| 38 |
+
*
|
| 39 |
+
* @return void
|
| 40 |
+
*/
|
| 41 |
+
public static function drop_database_tables_and_configs() {
|
| 42 |
+
|
| 43 |
+
global $wpdb, $aio_wp_security;
|
| 44 |
+
|
| 45 |
+
$database_tables = array(
|
| 46 |
+
$wpdb->prefix.'aiowps_login_lockdown',
|
| 47 |
+
$wpdb->prefix.'aiowps_failed_logins',
|
| 48 |
+
$wpdb->prefix.'aiowps_login_activity',
|
| 49 |
+
$wpdb->prefix.'aiowps_global_meta',
|
| 50 |
+
$wpdb->prefix.'aiowps_events',
|
| 51 |
+
$wpdb->prefix.'aiowps_permanent_block',
|
| 52 |
+
$wpdb->prefix.'aiowps_debug_log',
|
| 53 |
+
);
|
| 54 |
+
|
| 55 |
+
// check and drop database tables
|
| 56 |
+
if ('1' == $aio_wp_security->configs->get_value('aiowps_on_uninstall_delete_db_tables')) {
|
| 57 |
+
foreach ($database_tables as $table_name) {
|
| 58 |
+
$wpdb->query("DROP TABLE IF EXISTS `$table_name`");
|
| 59 |
+
}
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
// check and delete configurations
|
| 63 |
+
if ('1' == $aio_wp_security->configs->get_value('aiowps_on_uninstall_delete_configs')) {
|
| 64 |
+
|
| 65 |
+
delete_option('aio_wp_security_configs');
|
| 66 |
+
delete_option('aiowps_temp_configs');
|
| 67 |
+
delete_option('aiowpsec_db_version');
|
| 68 |
+
|
| 69 |
+
if (is_main_site()) {
|
| 70 |
+
// Remove all settings from .htaccess file that were added by this plugin
|
| 71 |
+
AIOWPSecurity_Utility_Htaccess::write_to_htaccess();
|
| 72 |
+
}
|
| 73 |
+
}
|
| 74 |
+
}
|
| 75 |
+
|
| 76 |
+
/**
|
| 77 |
+
* Helper function which clears aiowps cron events
|
| 78 |
+
*/
|
| 79 |
+
private static function clear_cron_events() {
|
| 80 |
+
wp_clear_scheduled_hook('aiowps_hourly_cron_event');
|
| 81 |
+
wp_clear_scheduled_hook('aiowps_daily_cron_event');
|
| 82 |
+
}
|
| 83 |
+
}
|
|
@@ -4,7 +4,7 @@ if (!defined('ABSPATH')) {
|
|
| 4 |
}
|
| 5 |
|
| 6 |
class AIOWPSecurity_User_Login {
|
| 7 |
-
|
| 8 |
public $key_login_msg;// This will store a URI query string key for passing messages to the login form
|
| 9 |
|
| 10 |
public function __construct() {
|
|
@@ -56,16 +56,22 @@ class AIOWPSecurity_User_Login {
|
|
| 56 |
}
|
| 57 |
|
| 58 |
/**
|
| 59 |
-
*
|
|
|
|
|
|
|
| 60 |
*/
|
| 61 |
public function disable_login_lockdown_by_const_notice() {
|
| 62 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 63 |
echo '<div class="notice notice-error">
|
| 64 |
<p>'.
|
| 65 |
__('You have disabled login lockdown by defining the AIOWPS_DISABLE_LOGIN_LOCKDOWN constant value as true, and the login lockdown setting has enabled it.', 'all-in-one-wp-security-and-firewall') . ' ' .
|
| 66 |
/* translators: 1: Locked IP Addresses admin page link */
|
| 67 |
sprintf(__('Delete your login lockdown IP from %s and define the AIOWPS_DISABLE_LOGIN_LOCKDOWN constant value as false.', 'all-in-one-wp-security-and-firewall'),
|
| 68 |
-
'<a href="'.admin_url('admin.php?page=aiowpsec&tab=
|
| 69 |
).
|
| 70 |
'</p>
|
| 71 |
</div>';
|
|
@@ -135,6 +141,7 @@ class AIOWPSecurity_User_Login {
|
|
| 135 |
}
|
| 136 |
return $user;
|
| 137 |
}
|
|
|
|
| 138 |
/**
|
| 139 |
* Check, whether $user needs to be manually approved by site admin yet.
|
| 140 |
*
|
|
@@ -158,6 +165,7 @@ class AIOWPSecurity_User_Login {
|
|
| 158 |
}
|
| 159 |
return $user;
|
| 160 |
}
|
|
|
|
| 161 |
/**
|
| 162 |
* Handle post authentication steps (in case of failed login):
|
| 163 |
* - increment number of failed logins for $username
|
|
@@ -199,19 +207,32 @@ class AIOWPSecurity_User_Login {
|
|
| 199 |
// Too many failed logins from user's IP?
|
| 200 |
$login_attempts_permitted = absint($aio_wp_security->configs->get_value('aiowps_max_login_attempts'));
|
| 201 |
$too_many_failed_logins = $login_attempts_permitted <= $this->get_login_fail_count();
|
|
|
|
| 202 |
// Is an invalid username or email the reason for login error?
|
| 203 |
$invalid_username = ($user->get_error_code() === 'invalid_username' || $user->get_error_code() == 'invalid_email');
|
| 204 |
// Should an invalid username be immediately locked?
|
| 205 |
$invalid_username_lockdown = $aio_wp_security->configs->get_value('aiowps_enable_invalid_username_lockdown') == '1';
|
| 206 |
$lock_invalid_username = $invalid_username && $invalid_username_lockdown;
|
|
|
|
| 207 |
// Should an invalid username be blocked as per blacklist?
|
| 208 |
$instant_lockout_users_list = $aio_wp_security->configs->get_value('aiowps_instantly_lockout_specific_usernames');
|
| 209 |
if (!is_array($instant_lockout_users_list)) {
|
| 210 |
$instant_lockout_users_list = array();
|
| 211 |
}
|
| 212 |
$username_blacklisted = $invalid_username && in_array($username, $instant_lockout_users_list);
|
| 213 |
-
|
| 214 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 215 |
}
|
| 216 |
}
|
| 217 |
}
|
|
@@ -232,33 +253,61 @@ class AIOWPSecurity_User_Login {
|
|
| 232 |
$login_lockdown_table = AIOWPSEC_TBL_LOGIN_LOCKDOWN;
|
| 233 |
$ip = AIOWPSecurity_Utility_IP::get_user_ip_address(); //Get the IP address of user
|
| 234 |
if (empty($ip)) return false;
|
| 235 |
-
$now = current_time('mysql');
|
| 236 |
-
$locked_user = $wpdb->get_row("SELECT * FROM $login_lockdown_table
|
| 237 |
return $locked_user;
|
| 238 |
}
|
| 239 |
/**
|
| 240 |
* This function queries the aiowps_failed_logins table and returns the number of failures for current IP range within allowed failure period
|
| 241 |
*/
|
| 242 |
public function get_login_fail_count() {
|
|
|
|
| 243 |
global $wpdb, $aio_wp_security;
|
|
|
|
| 244 |
$failed_logins_table = AIOWPSEC_TBL_FAILED_LOGINS;
|
| 245 |
$login_retry_interval = $aio_wp_security->configs->get_value('aiowps_retry_time_period');
|
| 246 |
$now = current_time('mysql', true);
|
| 247 |
$ip = AIOWPSecurity_Utility_IP::get_user_ip_address(); //Get the IP address of user
|
|
|
|
| 248 |
if (empty($ip)) return false;
|
|
|
|
| 249 |
$login_failures = $wpdb->get_var("SELECT COUNT(ID) FROM $failed_logins_table " . "WHERE failed_login_date + INTERVAL " . esc_sql($login_retry_interval) . " MINUTE > '" . esc_sql($now) . "' AND " . "login_attempt_ip = '" . esc_sql($ip) . "'");
|
| 250 |
return $login_failures;
|
| 251 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 252 |
/**
|
| 253 |
* Adds an entry to the `aiowps_login_lockdown` table.
|
| 254 |
*
|
| 255 |
-
* @param string $username
|
| 256 |
* @param string $lock_reason
|
|
|
|
| 257 |
*/
|
| 258 |
-
public function lock_the_user($username, $lock_reason = 'login_fail') {
|
| 259 |
global $wpdb, $aio_wp_security;
|
| 260 |
$login_lockdown_table = AIOWPSEC_TBL_LOGIN_LOCKDOWN;
|
| 261 |
-
$lock_minutes = $
|
| 262 |
$ip = AIOWPSecurity_Utility_IP::get_user_ip_address(); //Get the IP address of user
|
| 263 |
if (empty($ip)) return;
|
| 264 |
$ip_range = AIOWPSecurity_Utility_IP::get_sanitized_ip_range($ip); //Get the IP range of the current user
|
|
@@ -273,21 +322,24 @@ class AIOWPSecurity_User_Login {
|
|
| 273 |
}
|
| 274 |
$ip_range_str = esc_sql($ip_range).'.*';// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
| 275 |
|
| 276 |
-
$lock_time = current_time('mysql');
|
| 277 |
$date = new DateTime($lock_time);
|
| 278 |
$add_interval = 'PT'.absint($lock_minutes).'M';
|
| 279 |
$date->add(new DateInterval($add_interval));
|
| 280 |
$release_time = $date->format('Y-m-d H:i:s');
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 284 |
$result = $wpdb->insert($login_lockdown_table, $data, $format);
|
| 285 |
|
| 286 |
if (false === $result) {
|
| 287 |
$aio_wp_security->debug_logger->log_debug("Error inserting record into ".$login_lockdown_table, 4);//Log the highly unlikely event of DB error
|
| 288 |
} else {
|
| 289 |
do_action('aiowps_lockdown_event', $ip_range, $username);
|
| 290 |
-
$this->send_ip_lock_notification_email($username, $ip_range, $ip);
|
| 291 |
$aio_wp_security->debug_logger->log_debug("The following IP address range has been locked out for exceeding the maximum login attempts: ".$ip_range, 2);//Log the lockdown event
|
| 292 |
}
|
| 293 |
}
|
|
@@ -320,32 +372,46 @@ class AIOWPSecurity_User_Login {
|
|
| 320 |
}
|
| 321 |
|
| 322 |
/**
|
| 323 |
-
* Send IP Lock notification
|
| 324 |
*
|
| 325 |
-
* @param
|
| 326 |
-
* @param
|
| 327 |
-
*
|
| 328 |
-
* @return
|
| 329 |
*/
|
| 330 |
-
|
| 331 |
global $aio_wp_security;
|
| 332 |
-
$
|
| 333 |
-
if (
|
| 334 |
-
$
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
|
| 338 |
-
|
| 339 |
-
|
| 340 |
-
|
| 341 |
-
|
| 342 |
-
|
| 343 |
-
|
| 344 |
-
|
| 345 |
-
|
| 346 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 347 |
}
|
| 348 |
}
|
|
|
|
| 349 |
}
|
| 350 |
|
| 351 |
/**
|
|
@@ -362,13 +428,13 @@ class AIOWPSecurity_User_Login {
|
|
| 362 |
$unlock_link = '';
|
| 363 |
$lockdown_table_name = AIOWPSEC_TBL_LOGIN_LOCKDOWN;
|
| 364 |
$secret_rand_key = (md5(uniqid(rand(), true)));
|
| 365 |
-
$unlock_request_date_time =
|
| 366 |
$res = $wpdb->query($wpdb->prepare("UPDATE $lockdown_table_name SET unlock_key = %s WHERE release_date > %s AND failed_login_ip LIKE %s", $secret_rand_key, $unlock_request_date_time, "%" . esc_sql($ip_range) . "%"));
|
| 367 |
if (null == $res) {
|
| 368 |
$aio_wp_security->debug_logger->log_debug("No locked user found with IP range ".$ip_range, 4);
|
| 369 |
return false;
|
| 370 |
} else {
|
| 371 |
-
//Check if unlock
|
| 372 |
if (isset($_POST['aiowps-woo-login'])) {
|
| 373 |
$date_time = current_time('mysql');
|
| 374 |
$data = array('date_time' => $date_time, 'meta_key1' => 'woo_unlock_request_key', 'meta_value1' => $secret_rand_key);
|
|
@@ -395,7 +461,7 @@ class AIOWPSecurity_User_Login {
|
|
| 395 |
public static function process_unlock_request($unlock_key) {
|
| 396 |
global $wpdb, $aio_wp_security;
|
| 397 |
$lockdown_table_name = AIOWPSEC_TBL_LOGIN_LOCKDOWN;
|
| 398 |
-
$unlock_request_date_time =
|
| 399 |
$unlock_command = $wpdb->prepare("UPDATE ".$lockdown_table_name." SET release_date = %s WHERE unlock_key = %s", $unlock_request_date_time, $unlock_key);
|
| 400 |
$result = $wpdb->query($unlock_command);
|
| 401 |
if (false === $result) {
|
|
@@ -412,9 +478,9 @@ class AIOWPSecurity_User_Login {
|
|
| 412 |
}
|
| 413 |
if ($aio_wp_security->configs->get_value('aiowps_enable_rename_login_page')=='1') {
|
| 414 |
if (get_option('permalink_structure')) {
|
| 415 |
-
$
|
| 416 |
} else {
|
| 417 |
-
$
|
| 418 |
}
|
| 419 |
if ($woo_unlock) {
|
| 420 |
$login_url = wc_get_page_permalink('myaccount'); //redirect to woo login page if applicable
|
|
@@ -424,7 +490,7 @@ class AIOWPSecurity_User_Login {
|
|
| 424 |
$aio_wp_security->debug_logger->log_debug("process_unlock_request(): Error deleting row from AIOWPSEC_TBL_GLOBAL_META_DATA for meta_key1=woo_unlock_request_key and meta_value1=".$unlock_key, 4);
|
| 425 |
}
|
| 426 |
} else {
|
| 427 |
-
$login_url = $
|
| 428 |
}
|
| 429 |
|
| 430 |
AIOWPSecurity_Utility::redirect_to_url($login_url);
|
|
@@ -467,8 +533,11 @@ class AIOWPSecurity_User_Login {
|
|
| 467 |
if (is_user_logged_in()) {
|
| 468 |
$current_user = wp_get_current_user();
|
| 469 |
$user_id = $current_user->ID;
|
| 470 |
-
$current_time = current_time('mysql');
|
| 471 |
-
$login_time = $this->
|
|
|
|
|
|
|
|
|
|
| 472 |
$diff = strtotime($current_time) - strtotime($login_time);
|
| 473 |
$logout_time_interval_value = $aio_wp_security->configs->get_value('aiowps_logout_time_period');
|
| 474 |
$logout_time_interval_val_seconds = $logout_time_interval_value * 60;
|
|
@@ -479,7 +548,7 @@ class AIOWPSecurity_User_Login {
|
|
| 479 |
$curr_page_url = AIOWPSecurity_Utility::get_current_page_url();
|
| 480 |
$after_logout_payload = array('redirect_to' => $curr_page_url, 'msg' => $this->key_login_msg.'=session_expired');
|
| 481 |
//Save some of the logout redirect data to a transient
|
| 482 |
-
|
| 483 |
$logout_url = AIOWPSEC_WP_URL.'?aiowpsec_do_log_out=1';
|
| 484 |
$logout_url = AIOWPSecurity_Utility::add_query_data_to_url($logout_url, 'al_additional_data', '1');
|
| 485 |
$logout_url_with_nonce = html_entity_decode(wp_nonce_url($logout_url, 'aio_logout'));
|
|
@@ -489,10 +558,17 @@ class AIOWPSecurity_User_Login {
|
|
| 489 |
}
|
| 490 |
}
|
| 491 |
|
| 492 |
-
|
| 493 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 494 |
return $last_login;
|
| 495 |
}
|
|
|
|
| 496 |
public static function wp_login_action_handler($user_login, $user = '') {
|
| 497 |
global $wpdb, $aio_wp_security;
|
| 498 |
$login_activity_table = AIOWPSEC_TBL_USER_LOGIN_ACTIVITY;
|
|
@@ -505,8 +581,8 @@ class AIOWPSecurity_User_Login {
|
|
| 505 |
return;
|
| 506 |
}
|
| 507 |
}
|
| 508 |
-
$login_date_time = current_time('mysql');
|
| 509 |
-
update_user_meta($user->ID, '
|
| 510 |
$curr_ip_address = AIOWPSecurity_Utility_IP::get_user_ip_address();
|
| 511 |
$data = array('user_id' => $user->ID, 'user_login' => $user_login, 'login_date' => $login_date_time, 'login_ip' => $curr_ip_address);
|
| 512 |
$format = array('%d', '%s', '%s', '%s');
|
|
@@ -531,7 +607,7 @@ class AIOWPSecurity_User_Login {
|
|
| 531 |
//Clean up transients table
|
| 532 |
$this->cleanup_users_online_transient($user_id, $ip_addr);
|
| 533 |
$login_activity_table = AIOWPSEC_TBL_USER_LOGIN_ACTIVITY;
|
| 534 |
-
$logout_date_time = current_time('mysql');
|
| 535 |
$data = array('logout_date' => $logout_date_time);
|
| 536 |
$where = array('user_id' => $user_id, 'login_ip' => $ip_addr, 'logout_date' => '1000-10-10 10:00:00');
|
| 537 |
$result = $wpdb->update($login_activity_table, $data, $where);
|
|
@@ -547,7 +623,7 @@ class AIOWPSecurity_User_Login {
|
|
| 547 |
*/
|
| 548 |
public function update_users_online_transient() {
|
| 549 |
if (is_user_logged_in()) {
|
| 550 |
-
$is_multi_site =
|
| 551 |
$current_user_ip = AIOWPSecurity_Utility_IP::get_user_ip_address();
|
| 552 |
// get the logged in users list from transients entry
|
| 553 |
$logged_in_users = ($is_multi_site ? get_site_transient('users_online') : get_transient('users_online'));
|
|
@@ -595,10 +671,10 @@ class AIOWPSecurity_User_Login {
|
|
| 595 |
if ($update_existing) {
|
| 596 |
// Update transient if the last activity was over 15 min ago for this user
|
| 597 |
$logged_in_users[$item_index] = $current_user_info;
|
| 598 |
-
|
| 599 |
} else {
|
| 600 |
$logged_in_users[] = $current_user_info;
|
| 601 |
-
|
| 602 |
}
|
| 603 |
}
|
| 604 |
}
|
|
@@ -612,7 +688,7 @@ class AIOWPSecurity_User_Login {
|
|
| 612 |
* @return void
|
| 613 |
*/
|
| 614 |
public function cleanup_users_online_transient($user_id, $ip_addr) {
|
| 615 |
-
$is_multi_site =
|
| 616 |
if ($is_multi_site) {
|
| 617 |
$current_blog_id = get_current_blog_id();
|
| 618 |
$logged_in_users = AIOWPSecurity_User_Login::get_subsite_logged_in_users($current_blog_id);
|
|
@@ -709,7 +785,7 @@ class AIOWPSecurity_User_Login {
|
|
| 709 |
if (empty($blog_id)) return false;
|
| 710 |
|
| 711 |
$subsite_logged_in_users = array();
|
| 712 |
-
if (
|
| 713 |
// this contains all logged in users sitewide across subsites
|
| 714 |
$users_online = get_site_transient('users_online');
|
| 715 |
if (empty($users_online)) {
|
|
@@ -725,4 +801,57 @@ class AIOWPSecurity_User_Login {
|
|
| 725 |
return $subsite_logged_in_users;
|
| 726 |
}
|
| 727 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 728 |
}
|
| 4 |
}
|
| 5 |
|
| 6 |
class AIOWPSecurity_User_Login {
|
| 7 |
+
|
| 8 |
public $key_login_msg;// This will store a URI query string key for passing messages to the login form
|
| 9 |
|
| 10 |
public function __construct() {
|
| 56 |
}
|
| 57 |
|
| 58 |
/**
|
| 59 |
+
* Displays admin to disable lockdown message.
|
| 60 |
+
*
|
| 61 |
+
* @return Void
|
| 62 |
*/
|
| 63 |
public function disable_login_lockdown_by_const_notice() {
|
| 64 |
|
| 65 |
+
if (!AIOWPSecurity_Utility::has_manage_cap()) {
|
| 66 |
+
return;
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
echo '<div class="notice notice-error">
|
| 70 |
<p>'.
|
| 71 |
__('You have disabled login lockdown by defining the AIOWPS_DISABLE_LOGIN_LOCKDOWN constant value as true, and the login lockdown setting has enabled it.', 'all-in-one-wp-security-and-firewall') . ' ' .
|
| 72 |
/* translators: 1: Locked IP Addresses admin page link */
|
| 73 |
sprintf(__('Delete your login lockdown IP from %s and define the AIOWPS_DISABLE_LOGIN_LOCKDOWN constant value as false.', 'all-in-one-wp-security-and-firewall'),
|
| 74 |
+
'<a href="'.admin_url('admin.php?page=aiowpsec&tab=tab2').'">' . __('Locked IP Addresses', 'all-in-one-wp-security-and-firewall') . '</a>'
|
| 75 |
).
|
| 76 |
'</p>
|
| 77 |
</div>';
|
| 141 |
}
|
| 142 |
return $user;
|
| 143 |
}
|
| 144 |
+
|
| 145 |
/**
|
| 146 |
* Check, whether $user needs to be manually approved by site admin yet.
|
| 147 |
*
|
| 165 |
}
|
| 166 |
return $user;
|
| 167 |
}
|
| 168 |
+
|
| 169 |
/**
|
| 170 |
* Handle post authentication steps (in case of failed login):
|
| 171 |
* - increment number of failed logins for $username
|
| 207 |
// Too many failed logins from user's IP?
|
| 208 |
$login_attempts_permitted = absint($aio_wp_security->configs->get_value('aiowps_max_login_attempts'));
|
| 209 |
$too_many_failed_logins = $login_attempts_permitted <= $this->get_login_fail_count();
|
| 210 |
+
|
| 211 |
// Is an invalid username or email the reason for login error?
|
| 212 |
$invalid_username = ($user->get_error_code() === 'invalid_username' || $user->get_error_code() == 'invalid_email');
|
| 213 |
// Should an invalid username be immediately locked?
|
| 214 |
$invalid_username_lockdown = $aio_wp_security->configs->get_value('aiowps_enable_invalid_username_lockdown') == '1';
|
| 215 |
$lock_invalid_username = $invalid_username && $invalid_username_lockdown;
|
| 216 |
+
|
| 217 |
// Should an invalid username be blocked as per blacklist?
|
| 218 |
$instant_lockout_users_list = $aio_wp_security->configs->get_value('aiowps_instantly_lockout_specific_usernames');
|
| 219 |
if (!is_array($instant_lockout_users_list)) {
|
| 220 |
$instant_lockout_users_list = array();
|
| 221 |
}
|
| 222 |
$username_blacklisted = $invalid_username && in_array($username, $instant_lockout_users_list);
|
| 223 |
+
|
| 224 |
+
$lock_reasons = array();
|
| 225 |
+
if ($too_many_failed_logins) {
|
| 226 |
+
$lock_reasons[] = 'too_many_failed_logins';
|
| 227 |
+
}
|
| 228 |
+
if ($lock_invalid_username) {
|
| 229 |
+
$lock_reasons[] = 'invalid_username';
|
| 230 |
+
}
|
| 231 |
+
if ($username_blacklisted) {
|
| 232 |
+
$lock_reasons[] = 'username_blacklisted';
|
| 233 |
+
}
|
| 234 |
+
if ($lock_reasons) {
|
| 235 |
+
$this->lock_the_user($username, implode(',', $lock_reasons));
|
| 236 |
}
|
| 237 |
}
|
| 238 |
}
|
| 253 |
$login_lockdown_table = AIOWPSEC_TBL_LOGIN_LOCKDOWN;
|
| 254 |
$ip = AIOWPSecurity_Utility_IP::get_user_ip_address(); //Get the IP address of user
|
| 255 |
if (empty($ip)) return false;
|
| 256 |
+
$now = current_time('mysql', true);
|
| 257 |
+
$locked_user = $wpdb->get_row($wpdb->prepare("SELECT * FROM $login_lockdown_table WHERE `release_date` > %s AND `failed_login_ip` = %s", $now, $ip), ARRAY_A);
|
| 258 |
return $locked_user;
|
| 259 |
}
|
| 260 |
/**
|
| 261 |
* This function queries the aiowps_failed_logins table and returns the number of failures for current IP range within allowed failure period
|
| 262 |
*/
|
| 263 |
public function get_login_fail_count() {
|
| 264 |
+
|
| 265 |
global $wpdb, $aio_wp_security;
|
| 266 |
+
|
| 267 |
$failed_logins_table = AIOWPSEC_TBL_FAILED_LOGINS;
|
| 268 |
$login_retry_interval = $aio_wp_security->configs->get_value('aiowps_retry_time_period');
|
| 269 |
$now = current_time('mysql', true);
|
| 270 |
$ip = AIOWPSecurity_Utility_IP::get_user_ip_address(); //Get the IP address of user
|
| 271 |
+
|
| 272 |
if (empty($ip)) return false;
|
| 273 |
+
|
| 274 |
$login_failures = $wpdb->get_var("SELECT COUNT(ID) FROM $failed_logins_table " . "WHERE failed_login_date + INTERVAL " . esc_sql($login_retry_interval) . " MINUTE > '" . esc_sql($now) . "' AND " . "login_attempt_ip = '" . esc_sql($ip) . "'");
|
| 275 |
return $login_failures;
|
| 276 |
}
|
| 277 |
+
|
| 278 |
+
/**
|
| 279 |
+
* Get lockout time dynamically multiplied with default lockout time
|
| 280 |
+
*
|
| 281 |
+
* @return Integer get lockout time length.
|
| 282 |
+
*/
|
| 283 |
+
private function get_dynamic_lockout_time_length() {
|
| 284 |
+
global $aio_wp_security;
|
| 285 |
+
|
| 286 |
+
$login_fail_count = $this->get_login_fail_count();
|
| 287 |
+
$lockout_time_default = $aio_wp_security->configs->get_value('aiowps_lockout_time_length');
|
| 288 |
+
if (!is_numeric($lockout_time_default)) {
|
| 289 |
+
$lockout_time_default = 5;
|
| 290 |
+
}
|
| 291 |
+
$lockout_time_max = $aio_wp_security->configs->get_value('aiowps_max_lockout_time_length');
|
| 292 |
+
if (!is_numeric($lockout_time_max)) {
|
| 293 |
+
$lockout_time_max = 60;
|
| 294 |
+
}
|
| 295 |
+
$lockout_time_length = (int) ($login_fail_count > 0 ? (3 * $lockout_time_default * ($login_fail_count + 1)) : $lockout_time_default);
|
| 296 |
+
|
| 297 |
+
return $lockout_time_length >= $lockout_time_max ? $lockout_time_max : $lockout_time_length;
|
| 298 |
+
}
|
| 299 |
+
|
| 300 |
/**
|
| 301 |
* Adds an entry to the `aiowps_login_lockdown` table.
|
| 302 |
*
|
| 303 |
+
* @param string $username User's username or email
|
| 304 |
* @param string $lock_reason
|
| 305 |
+
* @param bool $is_lockout_email_sent flag for lockout email send
|
| 306 |
*/
|
| 307 |
+
public function lock_the_user($username, $lock_reason = 'login_fail', $is_lockout_email_sent = 0) {
|
| 308 |
global $wpdb, $aio_wp_security;
|
| 309 |
$login_lockdown_table = AIOWPSEC_TBL_LOGIN_LOCKDOWN;
|
| 310 |
+
$lock_minutes = $this->get_dynamic_lockout_time_length();
|
| 311 |
$ip = AIOWPSecurity_Utility_IP::get_user_ip_address(); //Get the IP address of user
|
| 312 |
if (empty($ip)) return;
|
| 313 |
$ip_range = AIOWPSecurity_Utility_IP::get_sanitized_ip_range($ip); //Get the IP range of the current user
|
| 322 |
}
|
| 323 |
$ip_range_str = esc_sql($ip_range).'.*';// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
|
| 324 |
|
| 325 |
+
$lock_time = current_time('mysql', true);
|
| 326 |
$date = new DateTime($lock_time);
|
| 327 |
$add_interval = 'PT'.absint($lock_minutes).'M';
|
| 328 |
$date->add(new DateInterval($add_interval));
|
| 329 |
$release_time = $date->format('Y-m-d H:i:s');
|
| 330 |
+
$backtrace_log = '';
|
| 331 |
+
if ('1' == $aio_wp_security->configs->get_value('aiowps_enable_php_backtrace_in_email')) {
|
| 332 |
+
$backtrace_log = print_r(debug_backtrace(), true);
|
| 333 |
+
}
|
| 334 |
+
$is_lockout_email_sent = (1 == $aio_wp_security->configs->get_value('aiowps_enable_email_notify') ? 0 : -1);
|
| 335 |
+
$data = array('user_id' => $user_id, 'user_login' => $username, 'lockdown_date' => $lock_time, 'release_date' => $release_time, 'failed_login_IP' => $ip, 'lock_reason' => $lock_reason, 'is_lockout_email_sent' => $is_lockout_email_sent, 'backtrace_log' => $backtrace_log);
|
| 336 |
+
$format = array('%d', '%s', '%s', '%s', '%s', '%s', '%d', '%s');
|
| 337 |
$result = $wpdb->insert($login_lockdown_table, $data, $format);
|
| 338 |
|
| 339 |
if (false === $result) {
|
| 340 |
$aio_wp_security->debug_logger->log_debug("Error inserting record into ".$login_lockdown_table, 4);//Log the highly unlikely event of DB error
|
| 341 |
} else {
|
| 342 |
do_action('aiowps_lockdown_event', $ip_range, $username);
|
|
|
|
| 343 |
$aio_wp_security->debug_logger->log_debug("The following IP address range has been locked out for exceeding the maximum login attempts: ".$ip_range, 2);//Log the lockdown event
|
| 344 |
}
|
| 345 |
}
|
| 372 |
}
|
| 373 |
|
| 374 |
/**
|
| 375 |
+
* Send IP Lock notification.
|
| 376 |
*
|
| 377 |
+
* @param Array $lockout_ips_list have username, ip_range, ip
|
| 378 |
+
* @param String $backtrace_filepath
|
| 379 |
+
*
|
| 380 |
+
* @return Boolean True if mail sent otherwise false.
|
| 381 |
*/
|
| 382 |
+
private function send_ip_lock_notification_email($lockout_ips_list = array(), $backtrace_filepath = '') {
|
| 383 |
global $aio_wp_security;
|
| 384 |
+
$send_mail = false;
|
| 385 |
+
if (0 != count($lockout_ips_list)) {
|
| 386 |
+
$email_notification_enabled = $aio_wp_security->configs->get_value('aiowps_enable_email_notify');
|
| 387 |
+
if (1 == $email_notification_enabled) {
|
| 388 |
+
$to_email_address = $aio_wp_security->configs->get_value('aiowps_email_address');
|
| 389 |
+
$subject = '['.get_option('home').'] '. __('Site Lockout Notification', 'all-in-one-wp-security-and-firewall');
|
| 390 |
+
$email_msg = __('Lockdown events had occurred due to too many failed login attempts or invalid username:', 'all-in-one-wp-security-and-firewall')."\n\n";
|
| 391 |
+
|
| 392 |
+
foreach ($lockout_ips_list as $lockout_ip) {
|
| 393 |
+
$email_msg .= __('Username:', 'all-in-one-wp-security-and-firewall') . ' ' . $lockout_ip['username'] . "\n";
|
| 394 |
+
$email_msg .= __('IP Address:', 'all-in-one-wp-security-and-firewall') . ' ' . $lockout_ip['ip'] . "\n";
|
| 395 |
+
if ('' != $lockout_ip['ip_range']) {
|
| 396 |
+
$email_msg .= __('IP Range:', 'all-in-one-wp-security-and-firewall') . ' ' . $lockout_ip['ip_range'] . '.*' . "\n";
|
| 397 |
+
}
|
| 398 |
+
$email_msg .= "\n";
|
| 399 |
+
}
|
| 400 |
+
|
| 401 |
+
$email_msg .= __("Log into your site WordPress administration panel to see the duration of the lockout or to unlock the user.", 'all-in-one-wp-security-and-firewall') . "\n";
|
| 402 |
+
|
| 403 |
+
$site_title = get_bloginfo('name');
|
| 404 |
+
$from_name = empty($site_title) ? 'WordPress' : $site_title;
|
| 405 |
+
$email_header = 'From: '.$from_name.' <'.get_bloginfo('admin_email').'>' . "\r\n\\";
|
| 406 |
+
$send_mail = wp_mail($to_email_address, $subject, $email_msg, $email_header, $backtrace_filepath);
|
| 407 |
+
|
| 408 |
+
if (false === $send_mail) {
|
| 409 |
+
$ips_list = implode(', ', wp_list_pluck($lockout_ips_list, 'ip'));
|
| 410 |
+
$aio_wp_security->debug_logger->log_debug("Lockout notification email failed to send to " . implode(', ', $to_email_address) . " for IPs ".$ips_list, 4);
|
| 411 |
+
}
|
| 412 |
}
|
| 413 |
}
|
| 414 |
+
return $send_mail;
|
| 415 |
}
|
| 416 |
|
| 417 |
/**
|
| 428 |
$unlock_link = '';
|
| 429 |
$lockdown_table_name = AIOWPSEC_TBL_LOGIN_LOCKDOWN;
|
| 430 |
$secret_rand_key = (md5(uniqid(rand(), true)));
|
| 431 |
+
$unlock_request_date_time = current_time('mysql', true);
|
| 432 |
$res = $wpdb->query($wpdb->prepare("UPDATE $lockdown_table_name SET unlock_key = %s WHERE release_date > %s AND failed_login_ip LIKE %s", $secret_rand_key, $unlock_request_date_time, "%" . esc_sql($ip_range) . "%"));
|
| 433 |
if (null == $res) {
|
| 434 |
$aio_wp_security->debug_logger->log_debug("No locked user found with IP range ".$ip_range, 4);
|
| 435 |
return false;
|
| 436 |
} else {
|
| 437 |
+
//Check if unlock request or submitted from a woocommerce account login page
|
| 438 |
if (isset($_POST['aiowps-woo-login'])) {
|
| 439 |
$date_time = current_time('mysql');
|
| 440 |
$data = array('date_time' => $date_time, 'meta_key1' => 'woo_unlock_request_key', 'meta_value1' => $secret_rand_key);
|
| 461 |
public static function process_unlock_request($unlock_key) {
|
| 462 |
global $wpdb, $aio_wp_security;
|
| 463 |
$lockdown_table_name = AIOWPSEC_TBL_LOGIN_LOCKDOWN;
|
| 464 |
+
$unlock_request_date_time = current_time('mysql', true);
|
| 465 |
$unlock_command = $wpdb->prepare("UPDATE ".$lockdown_table_name." SET release_date = %s WHERE unlock_key = %s", $unlock_request_date_time, $unlock_key);
|
| 466 |
$result = $wpdb->query($unlock_command);
|
| 467 |
if (false === $result) {
|
| 478 |
}
|
| 479 |
if ($aio_wp_security->configs->get_value('aiowps_enable_rename_login_page')=='1') {
|
| 480 |
if (get_option('permalink_structure')) {
|
| 481 |
+
$site_url = trailingslashit(site_url());
|
| 482 |
} else {
|
| 483 |
+
$site_url = trailingslashit(site_url()) . '?';
|
| 484 |
}
|
| 485 |
if ($woo_unlock) {
|
| 486 |
$login_url = wc_get_page_permalink('myaccount'); //redirect to woo login page if applicable
|
| 490 |
$aio_wp_security->debug_logger->log_debug("process_unlock_request(): Error deleting row from AIOWPSEC_TBL_GLOBAL_META_DATA for meta_key1=woo_unlock_request_key and meta_value1=".$unlock_key, 4);
|
| 491 |
}
|
| 492 |
} else {
|
| 493 |
+
$login_url = $site_url.$aio_wp_security->configs->get_value('aiowps_login_page_slug');
|
| 494 |
}
|
| 495 |
|
| 496 |
AIOWPSecurity_Utility::redirect_to_url($login_url);
|
| 533 |
if (is_user_logged_in()) {
|
| 534 |
$current_user = wp_get_current_user();
|
| 535 |
$user_id = $current_user->ID;
|
| 536 |
+
$current_time = current_time('mysql', true);
|
| 537 |
+
$login_time = $this->get_wp_user_aiowps_last_login_time($user_id);
|
| 538 |
+
if (empty($login_time)) {
|
| 539 |
+
return;
|
| 540 |
+
}
|
| 541 |
$diff = strtotime($current_time) - strtotime($login_time);
|
| 542 |
$logout_time_interval_value = $aio_wp_security->configs->get_value('aiowps_logout_time_period');
|
| 543 |
$logout_time_interval_val_seconds = $logout_time_interval_value * 60;
|
| 548 |
$curr_page_url = AIOWPSecurity_Utility::get_current_page_url();
|
| 549 |
$after_logout_payload = array('redirect_to' => $curr_page_url, 'msg' => $this->key_login_msg.'=session_expired');
|
| 550 |
//Save some of the logout redirect data to a transient
|
| 551 |
+
is_multisite() ? set_site_transient('aiowps_logout_payload', $after_logout_payload, 30 * 60) : set_transient('aiowps_logout_payload', $after_logout_payload, 30 * 60);
|
| 552 |
$logout_url = AIOWPSEC_WP_URL.'?aiowpsec_do_log_out=1';
|
| 553 |
$logout_url = AIOWPSecurity_Utility::add_query_data_to_url($logout_url, 'al_additional_data', '1');
|
| 554 |
$logout_url_with_nonce = html_entity_decode(wp_nonce_url($logout_url, 'aio_logout'));
|
| 558 |
}
|
| 559 |
}
|
| 560 |
|
| 561 |
+
/**
|
| 562 |
+
* Get last logged in time of given user id.
|
| 563 |
+
*
|
| 564 |
+
* @param integer $user_id
|
| 565 |
+
* @return mixed Last login time. False for an invalid $user_id (non-numeric, zero, or negative value). An empty string if a valid but non-existing user ID is passed.
|
| 566 |
+
*/
|
| 567 |
+
public function get_wp_user_aiowps_last_login_time($user_id) {
|
| 568 |
+
$last_login = apply_filters('aiowps_get_last_login_time', get_user_meta($user_id, 'aiowps_last_login_time', true), $user_id);
|
| 569 |
return $last_login;
|
| 570 |
}
|
| 571 |
+
|
| 572 |
public static function wp_login_action_handler($user_login, $user = '') {
|
| 573 |
global $wpdb, $aio_wp_security;
|
| 574 |
$login_activity_table = AIOWPSEC_TBL_USER_LOGIN_ACTIVITY;
|
| 581 |
return;
|
| 582 |
}
|
| 583 |
}
|
| 584 |
+
$login_date_time = current_time('mysql', true);
|
| 585 |
+
update_user_meta($user->ID, 'aiowps_last_login_time', $login_date_time); //store last login time in meta table
|
| 586 |
$curr_ip_address = AIOWPSecurity_Utility_IP::get_user_ip_address();
|
| 587 |
$data = array('user_id' => $user->ID, 'user_login' => $user_login, 'login_date' => $login_date_time, 'login_ip' => $curr_ip_address);
|
| 588 |
$format = array('%d', '%s', '%s', '%s');
|
| 607 |
//Clean up transients table
|
| 608 |
$this->cleanup_users_online_transient($user_id, $ip_addr);
|
| 609 |
$login_activity_table = AIOWPSEC_TBL_USER_LOGIN_ACTIVITY;
|
| 610 |
+
$logout_date_time = current_time('mysql', true);
|
| 611 |
$data = array('logout_date' => $logout_date_time);
|
| 612 |
$where = array('user_id' => $user_id, 'login_ip' => $ip_addr, 'logout_date' => '1000-10-10 10:00:00');
|
| 613 |
$result = $wpdb->update($login_activity_table, $data, $where);
|
| 623 |
*/
|
| 624 |
public function update_users_online_transient() {
|
| 625 |
if (is_user_logged_in()) {
|
| 626 |
+
$is_multi_site = is_multisite();
|
| 627 |
$current_user_ip = AIOWPSecurity_Utility_IP::get_user_ip_address();
|
| 628 |
// get the logged in users list from transients entry
|
| 629 |
$logged_in_users = ($is_multi_site ? get_site_transient('users_online') : get_transient('users_online'));
|
| 671 |
if ($update_existing) {
|
| 672 |
// Update transient if the last activity was over 15 min ago for this user
|
| 673 |
$logged_in_users[$item_index] = $current_user_info;
|
| 674 |
+
is_multisite() ? set_site_transient('users_online', $logged_in_users, 30 * 60) : set_transient('users_online', $logged_in_users, 30 * 60);
|
| 675 |
} else {
|
| 676 |
$logged_in_users[] = $current_user_info;
|
| 677 |
+
is_multisite() ? set_site_transient('users_online', $logged_in_users, 30 * 60) : set_transient('users_online', $logged_in_users, 30 * 60);
|
| 678 |
}
|
| 679 |
}
|
| 680 |
}
|
| 688 |
* @return void
|
| 689 |
*/
|
| 690 |
public function cleanup_users_online_transient($user_id, $ip_addr) {
|
| 691 |
+
$is_multi_site = is_multisite();
|
| 692 |
if ($is_multi_site) {
|
| 693 |
$current_blog_id = get_current_blog_id();
|
| 694 |
$logged_in_users = AIOWPSecurity_User_Login::get_subsite_logged_in_users($current_blog_id);
|
| 785 |
if (empty($blog_id)) return false;
|
| 786 |
|
| 787 |
$subsite_logged_in_users = array();
|
| 788 |
+
if (is_multisite()) {
|
| 789 |
// this contains all logged in users sitewide across subsites
|
| 790 |
$users_online = get_site_transient('users_online');
|
| 791 |
if (empty($users_online)) {
|
| 801 |
return $subsite_logged_in_users;
|
| 802 |
}
|
| 803 |
|
| 804 |
+
/**
|
| 805 |
+
* Send email notification to an user that has flag is_lockout_email_sent is 0.
|
| 806 |
+
*
|
| 807 |
+
* @return Void
|
| 808 |
+
*/
|
| 809 |
+
public function send_login_lockout_emails() {
|
| 810 |
+
global $wpdb, $aio_wp_security;
|
| 811 |
+
// if user enabled notification email then only have to send
|
| 812 |
+
$email_notification_enabled = $aio_wp_security->configs->get_value('aiowps_enable_email_notify');
|
| 813 |
+
if (0 == $email_notification_enabled) {
|
| 814 |
+
return;
|
| 815 |
+
}
|
| 816 |
+
// get recent lockout records on top to notify
|
| 817 |
+
$sql = $wpdb->prepare('SELECT id, user_login, failed_login_ip, backtrace_log FROM ' .AIOWPSEC_TBL_LOGIN_LOCKDOWN. ' WHERE is_lockout_email_sent = %d ORDER BY id DESC', 0);
|
| 818 |
+
$result = $wpdb->get_results($sql);
|
| 819 |
+
if (empty($result)) {
|
| 820 |
+
return;
|
| 821 |
+
}
|
| 822 |
+
$login_lockout_ids_send_emails = array();
|
| 823 |
+
$lockout_ips_backtrace_log = array();
|
| 824 |
+
$lockout_ips_list = array();
|
| 825 |
+
$backtrace_filepath = '';
|
| 826 |
+
foreach ($result as $row) {
|
| 827 |
+
$ip_range = AIOWPSecurity_Utility_IP::get_sanitized_ip_range($row->failed_login_ip);
|
| 828 |
+
$lockout_ips_list[] = array('username' => $row->user_login, 'ip' => $row->failed_login_ip, 'ip_range' => $ip_range);
|
| 829 |
+
$login_lockout_ids_send_emails[] = $row->id;
|
| 830 |
+
if ('1' == $aio_wp_security->configs->get_value('aiowps_enable_php_backtrace_in_email') && '' != $row->backtrace_log) {
|
| 831 |
+
$lockout_ips_backtrace_log[] = array('backtrace_log' => $row->backtrace_log);
|
| 832 |
+
}
|
| 833 |
+
}
|
| 834 |
+
|
| 835 |
+
if (0 != count($lockout_ips_backtrace_log)) {
|
| 836 |
+
$backtrace_filepath = AIOWPSecurity_Utility::login_lockdown_email_backtrace_log_file($lockout_ips_backtrace_log);
|
| 837 |
+
}
|
| 838 |
+
|
| 839 |
+
$this->send_ip_lock_notification_email($lockout_ips_list, $backtrace_filepath);
|
| 840 |
+
|
| 841 |
+
if ('' != $backtrace_filepath) {
|
| 842 |
+
unlink($backtrace_filepath);
|
| 843 |
+
}
|
| 844 |
+
|
| 845 |
+
if (!empty($login_lockout_ids_send_emails)) {
|
| 846 |
+
$aio_wp_security->debug_logger->log_debug(sprintf('The IP lock notification emails of login lockout ids [%s] are sent.', implode(', ', $login_lockout_ids_send_emails)), 4);
|
| 847 |
+
// update all email to as sent.
|
| 848 |
+
$sql = $wpdb->prepare('UPDATE '.AIOWPSEC_TBL_LOGIN_LOCKDOWN.' SET is_lockout_email_sent = %d WHERE is_lockout_email_sent = %d', 1, 0);
|
| 849 |
+
//$sql = $wpdb->prepare('UPDATE '.AIOWPSEC_TBL_LOGIN_LOCKDOWN.' SET is_lockout_email_sent = %d WHERE id IN (%1s)', 1, implode(', ', $login_lockout_ids_send_emails));
|
| 850 |
+
$update_result = $wpdb->query($sql);
|
| 851 |
+
if (false === $update_result) {
|
| 852 |
+
$error_msg = empty($wpdb->last_error) ? 'Could not receive the reason for the failure' : $wpdb->last_error;
|
| 853 |
+
$aio_wp_security->debug_logger->log_debug_cron("Lockout email flag is not updated in database due to error: {$error_msg}", 4);
|
| 854 |
+
}
|
| 855 |
+
}
|
| 856 |
+
}
|
| 857 |
}
|
|
@@ -13,9 +13,8 @@ class AIOWPSecurity_Utility_File {
|
|
| 13 |
// NOTE: we can add to this list in future if we wish
|
| 14 |
|
| 15 |
//Get wp-config.php file path
|
| 16 |
-
if (!function_exists('get_home_path')) require_once(ABSPATH. '/wp-admin/includes/file.php');
|
| 17 |
$wp_config_path = AIOWPSecurity_Utility_File::get_wp_config_file_path();
|
| 18 |
-
$home_path = get_home_path();
|
| 19 |
|
| 20 |
$this->files_and_dirs_to_check = array(
|
| 21 |
array('name' => 'root directory', 'path' => ABSPATH, 'permissions' => '0755'),
|
|
@@ -33,6 +32,20 @@ class AIOWPSecurity_Utility_File {
|
|
| 33 |
|
| 34 |
}
|
| 35 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 36 |
public static function get_wp_config_file_path() {
|
| 37 |
$wp_config_file = ABSPATH . 'wp-config.php';
|
| 38 |
if (file_exists($wp_config_file)) {
|
|
@@ -126,7 +139,7 @@ class AIOWPSecurity_Utility_File {
|
|
| 126 |
|
| 127 |
//First check if a backup entry already exists in the global_meta table
|
| 128 |
$aiowps_global_meta_tbl_name = AIOWPSEC_TBL_GLOBAL_META_DATA;
|
| 129 |
-
$resultset = $wpdb->get_row($wpdb->prepare("SELECT * FROM $aiowps_global_meta_tbl_name WHERE meta_key1 =
|
| 130 |
if ($resultset) {
|
| 131 |
$where = array('meta_key1' => $key_description);
|
| 132 |
$res = $wpdb->update($aiowps_global_meta_tbl_name, $data, $where);
|
|
@@ -396,7 +409,7 @@ class AIOWPSecurity_Utility_File {
|
|
| 396 |
// Remove the upload path base directory from the attachment URL
|
| 397 |
$attachment_url = str_replace($upload_dir_paths['baseurl'] . '/', '', $attachment_url);
|
| 398 |
// Now run custom database query to get attachment ID from attachment URL
|
| 399 |
-
$attachment_id = $wpdb->get_var($wpdb->prepare("SELECT wposts.ID FROM $wpdb->posts wposts, $wpdb->postmeta wpostmeta WHERE wposts.ID = wpostmeta.post_id AND wpostmeta.meta_key = '_wp_attached_file' AND wpostmeta.meta_value =
|
| 400 |
}
|
| 401 |
return $attachment_id;
|
| 402 |
}
|
|
@@ -424,4 +437,40 @@ class AIOWPSecurity_Utility_File {
|
|
| 424 |
return array_keys($files);
|
| 425 |
}
|
| 426 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 427 |
}
|
| 13 |
// NOTE: we can add to this list in future if we wish
|
| 14 |
|
| 15 |
//Get wp-config.php file path
|
|
|
|
| 16 |
$wp_config_path = AIOWPSecurity_Utility_File::get_wp_config_file_path();
|
| 17 |
+
$home_path = self::get_home_path();
|
| 18 |
|
| 19 |
$this->files_and_dirs_to_check = array(
|
| 20 |
array('name' => 'root directory', 'path' => ABSPATH, 'permissions' => '0755'),
|
| 32 |
|
| 33 |
}
|
| 34 |
|
| 35 |
+
/**
|
| 36 |
+
* Returns full path to mu-plugin directory
|
| 37 |
+
*
|
| 38 |
+
* @return string
|
| 39 |
+
*/
|
| 40 |
+
public static function get_mu_plugin_dir() {
|
| 41 |
+
return WPMU_PLUGIN_DIR;
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
/**
|
| 45 |
+
* Returns path to wp-config
|
| 46 |
+
*
|
| 47 |
+
* @return string
|
| 48 |
+
*/
|
| 49 |
public static function get_wp_config_file_path() {
|
| 50 |
$wp_config_file = ABSPATH . 'wp-config.php';
|
| 51 |
if (file_exists($wp_config_file)) {
|
| 139 |
|
| 140 |
//First check if a backup entry already exists in the global_meta table
|
| 141 |
$aiowps_global_meta_tbl_name = AIOWPSEC_TBL_GLOBAL_META_DATA;
|
| 142 |
+
$resultset = $wpdb->get_row($wpdb->prepare("SELECT * FROM $aiowps_global_meta_tbl_name WHERE meta_key1 = %s", $key_description));
|
| 143 |
if ($resultset) {
|
| 144 |
$where = array('meta_key1' => $key_description);
|
| 145 |
$res = $wpdb->update($aiowps_global_meta_tbl_name, $data, $where);
|
| 409 |
// Remove the upload path base directory from the attachment URL
|
| 410 |
$attachment_url = str_replace($upload_dir_paths['baseurl'] . '/', '', $attachment_url);
|
| 411 |
// Now run custom database query to get attachment ID from attachment URL
|
| 412 |
+
$attachment_id = $wpdb->get_var($wpdb->prepare("SELECT wposts.ID FROM $wpdb->posts wposts, $wpdb->postmeta wpostmeta WHERE wposts.ID = wpostmeta.post_id AND wpostmeta.meta_key = '_wp_attached_file' AND wpostmeta.meta_value = %s AND wposts.post_type = 'attachment'", $attachment_url));
|
| 413 |
}
|
| 414 |
return $attachment_id;
|
| 415 |
}
|
| 437 |
return array_keys($files);
|
| 438 |
}
|
| 439 |
|
| 440 |
+
/**
|
| 441 |
+
* Remove a directory from the local filesystem
|
| 442 |
+
*
|
| 443 |
+
* @param string $dir - the directory
|
| 444 |
+
* @param boolean $contents_only - if set to true, then do not remove the directory, but only empty it of contents
|
| 445 |
+
*
|
| 446 |
+
* @return boolean - success/failure
|
| 447 |
+
*/
|
| 448 |
+
public static function remove_local_directory($dir, $contents_only = false) {
|
| 449 |
+
|
| 450 |
+
$handle = opendir($dir);
|
| 451 |
+
if ($handle) {
|
| 452 |
+
while (false !== ($entry = readdir($handle))) {
|
| 453 |
+
if ('.' !== $entry && '..' !== $entry) {
|
| 454 |
+
if (is_dir($dir.'/'.$entry)) {
|
| 455 |
+
self::remove_local_directory($dir.'/'.$entry, false);
|
| 456 |
+
} else {
|
| 457 |
+
@unlink($dir.'/'.$entry);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
|
| 458 |
+
}
|
| 459 |
+
}
|
| 460 |
+
}
|
| 461 |
+
@closedir($handle);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged
|
| 462 |
+
}
|
| 463 |
+
|
| 464 |
+
return $contents_only ? true : rmdir($dir);
|
| 465 |
+
}
|
| 466 |
+
|
| 467 |
+
/**
|
| 468 |
+
* Get home path.
|
| 469 |
+
*
|
| 470 |
+
* @return string
|
| 471 |
+
*/
|
| 472 |
+
public static function get_home_path() {
|
| 473 |
+
if (!function_exists('get_home_path')) require_once(ABSPATH. '/wp-admin/includes/file.php');
|
| 474 |
+
return wp_normalize_path(get_home_path());
|
| 475 |
+
}
|
| 476 |
}
|
|
@@ -0,0 +1,186 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
if (!defined('ABSPATH')) {
|
| 3 |
+
exit; //Exit if accessed directly
|
| 4 |
+
}
|
| 5 |
+
|
| 6 |
+
class AIOWPSecurity_Utility_Firewall {
|
| 7 |
+
|
| 8 |
+
/**
|
| 9 |
+
* Returned if the user is required to setup auto_prepend_file manually
|
| 10 |
+
*
|
| 11 |
+
* @var null
|
| 12 |
+
*/
|
| 13 |
+
const MANUAL_SETUP = null;
|
| 14 |
+
|
| 15 |
+
/**
|
| 16 |
+
* Returns the current path to our firewall file
|
| 17 |
+
*
|
| 18 |
+
* @return string
|
| 19 |
+
*/
|
| 20 |
+
public static function get_firewall_path() {
|
| 21 |
+
return wp_normalize_path(AIO_WP_SECURITY_PATH.'/classes/firewall/wp-security-firewall.php');
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
/**
|
| 25 |
+
* Returns the firewall rules path.
|
| 26 |
+
*
|
| 27 |
+
* @return string
|
| 28 |
+
*/
|
| 29 |
+
public static function get_firewall_rules_path() {
|
| 30 |
+
$upload_dir_info = wp_get_upload_dir();
|
| 31 |
+
$firewall_rules_path = trailingslashit($upload_dir_info['basedir'].'/aios/firewall-rules');
|
| 32 |
+
wp_mkdir_p($firewall_rules_path);
|
| 33 |
+
return wp_normalize_path($firewall_rules_path);
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
/**
|
| 37 |
+
* Returns the current path to our bootstrap file
|
| 38 |
+
*
|
| 39 |
+
* @return string
|
| 40 |
+
*/
|
| 41 |
+
public static function get_bootstrap_path() {
|
| 42 |
+
return path_join(AIOWPSecurity_Utility_File::get_home_path(), 'aios-bootstrap.php');
|
| 43 |
+
}
|
| 44 |
+
|
| 45 |
+
/**
|
| 46 |
+
* Returns the full path to our mu-plugin
|
| 47 |
+
*
|
| 48 |
+
* @return string
|
| 49 |
+
*/
|
| 50 |
+
public static function get_muplugin_path() {
|
| 51 |
+
return path_join(AIOWPSecurity_Utility_File::get_mu_plugin_dir(), 'aiowpsec-firewall-loader.php');
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
/**
|
| 55 |
+
* Returns our managed mu-plugin file
|
| 56 |
+
*
|
| 57 |
+
* @return AIOWPSecurity_Block_Muplugin
|
| 58 |
+
*/
|
| 59 |
+
public static function get_muplugin_file() {
|
| 60 |
+
return new AIOWPSecurity_Block_Muplugin(AIOWPSecurity_Utility_Firewall::get_muplugin_path());
|
| 61 |
+
}
|
| 62 |
+
|
| 63 |
+
/**
|
| 64 |
+
* Returns our managed wp-config file
|
| 65 |
+
*
|
| 66 |
+
* @return AIOWPSecurity_Block_WpConfig
|
| 67 |
+
*/
|
| 68 |
+
public static function get_wpconfig_file() {
|
| 69 |
+
return new AIOWPSecurity_Block_WpConfig(AIOWPSecurity_Utility_File::get_wp_config_file_path());
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
/**
|
| 73 |
+
* Returns our managed bootstrap file
|
| 74 |
+
*
|
| 75 |
+
* @return AIOWPSecurity_Block_Bootstrap
|
| 76 |
+
*/
|
| 77 |
+
public static function get_bootstrap_file() {
|
| 78 |
+
return new AIOWPSecurity_Block_Bootstrap(AIOWPSecurity_Utility_Firewall::get_bootstrap_path());
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
/**
|
| 82 |
+
* Gets the auto_prepend_file directive, if already set
|
| 83 |
+
*
|
| 84 |
+
* @param string $source - where to check for the directive
|
| 85 |
+
* @return string - returns the directive if set, or empty string if not set
|
| 86 |
+
*/
|
| 87 |
+
public static function get_already_set_directive($source = '') {
|
| 88 |
+
|
| 89 |
+
if (!empty($source)) {
|
| 90 |
+
clearstatcache();
|
| 91 |
+
if (file_exists($source) && is_readable($source)) {
|
| 92 |
+
|
| 93 |
+
$vals = @parse_ini_file($source); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- ignore this
|
| 94 |
+
|
| 95 |
+
if (false !== $vals && isset($vals['auto_prepend_file'])) {
|
| 96 |
+
return $vals['auto_prepend_file'];
|
| 97 |
+
}
|
| 98 |
+
}
|
| 99 |
+
} else {
|
| 100 |
+
$directive = ini_get('auto_prepend_file');
|
| 101 |
+
if (false !== $directive) {
|
| 102 |
+
return $directive;
|
| 103 |
+
}
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
return '';
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
/**
|
| 110 |
+
* Returns the file that's necessary to load our firewall
|
| 111 |
+
*
|
| 112 |
+
* @return AIOWPSecurity_Block_File|null file needed to load the firewall
|
| 113 |
+
*/
|
| 114 |
+
public static function get_server_file() {
|
| 115 |
+
$server_type = AIOWPSecurity_Utility::get_server_type();
|
| 116 |
+
$is_cgi = false;
|
| 117 |
+
$sapi = PHP_SAPI;
|
| 118 |
+
|
| 119 |
+
if (false !== stripos($sapi, 'cgi')) {
|
| 120 |
+
$is_cgi = true;
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
if (AIOWPSecurity_Utility::UNSUPPORTED_SERVER_TYPE === $server_type) {
|
| 124 |
+
return self::MANUAL_SETUP;
|
| 125 |
+
|
| 126 |
+
} elseif (false === $is_cgi && 'apache' === $server_type) {
|
| 127 |
+
|
| 128 |
+
$htpath = path_join(get_home_path(), '.htaccess');
|
| 129 |
+
return new AIOWPSecurity_Block_Htaccess($htpath);
|
| 130 |
+
|
| 131 |
+
} elseif ('litespeed' === $server_type || 'litespeed' === $sapi) {
|
| 132 |
+
|
| 133 |
+
$htpath = path_join(get_home_path(), '.htaccess');
|
| 134 |
+
return new AIOWPSecurity_Block_Litespeed($htpath);
|
| 135 |
+
|
| 136 |
+
} else {
|
| 137 |
+
$userini = path_join(get_home_path(), '.user.ini');
|
| 138 |
+
return new AIOWPSecurity_Block_Userini($userini);
|
| 139 |
+
}
|
| 140 |
+
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
/**
|
| 144 |
+
* Attempts to remove our firewall.
|
| 145 |
+
*
|
| 146 |
+
* @return void
|
| 147 |
+
*/
|
| 148 |
+
public static function remove_firewall() {
|
| 149 |
+
|
| 150 |
+
$firewall_files = array(
|
| 151 |
+
'server' => AIOWPSecurity_Utility_Firewall::get_server_file(),
|
| 152 |
+
'bootstrap' => AIOWPSecurity_Utility_Firewall::get_bootstrap_file(),
|
| 153 |
+
'wpconfig' => AIOWPSecurity_Utility_Firewall::get_wpconfig_file(),
|
| 154 |
+
'muplugin' => AIOWPSecurity_Utility_Firewall::get_muplugin_file(),
|
| 155 |
+
);
|
| 156 |
+
|
| 157 |
+
foreach ($firewall_files as $file) {
|
| 158 |
+
if (AIOWPSecurity_Utility_Firewall::MANUAL_SETUP === $file) {
|
| 159 |
+
continue;
|
| 160 |
+
}
|
| 161 |
+
|
| 162 |
+
if (true === $file->contains_contents()) {
|
| 163 |
+
|
| 164 |
+
$removed = $file->remove_contents();
|
| 165 |
+
|
| 166 |
+
if (is_wp_error($removed)) {
|
| 167 |
+
global $aio_wp_security;
|
| 168 |
+
|
| 169 |
+
$error_message = $removed->get_error_message();
|
| 170 |
+
$error_message .= ' - ';
|
| 171 |
+
$error_message .= $removed->get_error_data();
|
| 172 |
+
$aio_wp_security->debug_logger->log_debug($error_message, 4);
|
| 173 |
+
}
|
| 174 |
+
}
|
| 175 |
+
|
| 176 |
+
}
|
| 177 |
+
|
| 178 |
+
//Delete our mu-plugin, if it's created
|
| 179 |
+
clearstatcache();
|
| 180 |
+
$muplugin_path = $firewall_files['muplugin'];
|
| 181 |
+
if (file_exists($muplugin_path)) {
|
| 182 |
+
@unlink($muplugin_path); // phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- ignore this
|
| 183 |
+
}
|
| 184 |
+
|
| 185 |
+
}
|
| 186 |
+
}
|
|
@@ -106,8 +106,7 @@ class AIOWPSecurity_Utility_Htaccess {
|
|
| 106 |
return false; //unable to write to the file
|
| 107 |
}
|
| 108 |
|
| 109 |
-
|
| 110 |
-
$home_path = get_home_path();
|
| 111 |
$htaccess = $home_path . '.htaccess';
|
| 112 |
|
| 113 |
if (!$f = @fopen($htaccess, 'a+')) {// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged,Squiz.PHP.DisallowMultipleAssignments.FoundInControlStructure
|
|
@@ -157,8 +156,7 @@ class AIOWPSecurity_Utility_Htaccess {
|
|
| 157 |
* @return boolean
|
| 158 |
*/
|
| 159 |
public static function delete_from_htaccess($section = 'All In One WP Security') {
|
| 160 |
-
|
| 161 |
-
$home_path = get_home_path();
|
| 162 |
$htaccess = $home_path . '.htaccess';
|
| 163 |
|
| 164 |
if (!file_exists($htaccess)) {
|
|
@@ -223,9 +221,7 @@ class AIOWPSecurity_Utility_Htaccess {
|
|
| 223 |
$rules .= AIOWPSecurity_Utility_Htaccess::getrules_forbid_proxy_comment_posting();
|
| 224 |
$rules .= AIOWPSecurity_Utility_Htaccess::getrules_deny_bad_query_strings();
|
| 225 |
$rules .= AIOWPSecurity_Utility_Htaccess::getrules_advanced_character_string_filter();
|
| 226 |
-
$rules .= AIOWPSecurity_Utility_Htaccess::getrules_6g_blacklist();
|
| 227 |
$rules .= AIOWPSecurity_Utility_Htaccess::getrules_5g_blacklist();
|
| 228 |
-
$rules .= AIOWPSecurity_Utility_Htaccess::getrules_enable_brute_force_prevention();
|
| 229 |
$rules .= AIOWPSecurity_Utility_Htaccess::getrules_block_spambots();
|
| 230 |
$rules .= AIOWPSecurity_Utility_Htaccess::getrules_enable_login_whitelist_v2();
|
| 231 |
$rules .= AIOWPSecurity_Utility_Htaccess::prevent_image_hotlinks();
|
|
@@ -278,7 +274,7 @@ class AIOWPSecurity_Utility_Htaccess {
|
|
| 278 |
$hosts = AIOWPSecurity_Utility::explode_trim_filter_empty($aio_wp_security->configs->get_value('aiowps_banned_ip_addresses'));
|
| 279 |
// Filter out duplicate lines, add netmask to IP addresses
|
| 280 |
$ips_with_netmask = self::add_netmask(array_unique($hosts));
|
| 281 |
-
if (!empty($ips_with_netmask)
|
| 282 |
$rules .= AIOWPSecurity_Utility_Htaccess::$ip_blacklist_marker_start . PHP_EOL; //Add feature marker start
|
| 283 |
|
| 284 |
if ($apache_or_litespeed) {
|
|
@@ -410,36 +406,6 @@ class AIOWPSecurity_Utility_Htaccess {
|
|
| 410 |
return $rules;
|
| 411 |
}
|
| 412 |
|
| 413 |
-
/**
|
| 414 |
-
* This function will write some drectives to block all people who do not have a cookie
|
| 415 |
-
* when trying to access the WP login page
|
| 416 |
-
*/
|
| 417 |
-
public static function getrules_enable_brute_force_prevention() {
|
| 418 |
-
global $aio_wp_security;
|
| 419 |
-
$rules = '';
|
| 420 |
-
if ($aio_wp_security->configs->get_value('aiowps_enable_brute_force_attack_prevention') == '1') {
|
| 421 |
-
$cookie_name = $aio_wp_security->configs->get_value('aiowps_brute_force_secret_word');
|
| 422 |
-
$test_cookie_name = $aio_wp_security->configs->get_value('aiowps_cookie_brute_test');
|
| 423 |
-
$redirect_url = $aio_wp_security->configs->get_value('aiowps_cookie_based_brute_force_redirect_url');
|
| 424 |
-
$rules .= AIOWPSecurity_Utility_Htaccess::$enable_brute_force_attack_prevention_marker_start . PHP_EOL; //Add feature marker start
|
| 425 |
-
$rules .= 'RewriteEngine On' . PHP_EOL;
|
| 426 |
-
$rules .= 'RewriteCond %{REQUEST_URI} (wp-admin|wp-login)' . PHP_EOL;// If URI contains wp-admin or wp-login
|
| 427 |
-
if ($aio_wp_security->configs->get_value('aiowps_brute_force_attack_prevention_ajax_exception') == '1') {
|
| 428 |
-
$rules .= 'RewriteCond %{REQUEST_URI} !(wp-admin/admin-ajax.php)' . PHP_EOL; // To allow ajax requests through
|
| 429 |
-
}
|
| 430 |
-
if ($aio_wp_security->configs->get_value('aiowps_brute_force_attack_prevention_pw_protected_exception') == '1') {
|
| 431 |
-
$rules .= 'RewriteCond %{QUERY_STRING} !(action\=postpass)' . PHP_EOL; // Possible workaround for people usign the password protected page/post feature
|
| 432 |
-
}
|
| 433 |
-
$rules .= 'RewriteCond %{HTTP_COOKIE} !' . $cookie_name . '= [NC]' . PHP_EOL;
|
| 434 |
-
$rules .= 'RewriteCond %{HTTP_COOKIE} !' . $test_cookie_name . '= [NC]' . PHP_EOL;
|
| 435 |
-
$rules .= 'RewriteRule .* ' . $redirect_url . ' [L]' . PHP_EOL;
|
| 436 |
-
$rules .= AIOWPSecurity_Utility_Htaccess::$enable_brute_force_attack_prevention_marker_end . PHP_EOL; //Add feature marker end
|
| 437 |
-
}
|
| 438 |
-
|
| 439 |
-
return $rules;
|
| 440 |
-
}
|
| 441 |
-
|
| 442 |
-
|
| 443 |
/**
|
| 444 |
* This function will write some directives to allow IPs in the whitelist to access wp-login.php or wp-admin
|
| 445 |
* The function also handles the following special cases:
|
|
@@ -628,12 +594,11 @@ class AIOWPSecurity_Utility_Htaccess {
|
|
| 628 |
if (!empty($ips_with_netmask)) {
|
| 629 |
foreach ($ips_with_netmask as $xhost) {
|
| 630 |
$ipv6 = false;
|
| 631 |
-
if (strpos($xhost, ':')
|
| 632 |
-
//possible ipv6 addr
|
| 633 |
-
|
| 634 |
-
|
| 635 |
-
|
| 636 |
-
continue;
|
| 637 |
}
|
| 638 |
}
|
| 639 |
$ip_range = substr($xhost, 0, strpos($xhost, "/")); //check if address range
|
|
@@ -964,119 +929,6 @@ class AIOWPSecurity_Utility_Htaccess {
|
|
| 964 |
return $rules;
|
| 965 |
}
|
| 966 |
|
| 967 |
-
/**
|
| 968 |
-
* This function contains the rules for the 6G blacklist produced by Jeff Starr:
|
| 969 |
-
* https://perishablepress.com/6g/
|
| 970 |
-
*/
|
| 971 |
-
public static function getrules_6g_blacklist() {
|
| 972 |
-
global $aio_wp_security;
|
| 973 |
-
$rules = '';
|
| 974 |
-
$ip_blacklist_23 = '';
|
| 975 |
-
$ip_blacklist_24 = '';
|
| 976 |
-
if ($aio_wp_security->configs->get_value('aiowps_enable_6g_firewall') == '1') {
|
| 977 |
-
//if ip blacklist is enabled we will merge that code with the 6G rules to prevent clashes
|
| 978 |
-
if ($aio_wp_security->configs->get_value('aiowps_enable_blacklisting') == '1') {
|
| 979 |
-
// Let's do the list of blacklisted IPs first
|
| 980 |
-
$hosts = AIOWPSecurity_Utility::explode_trim_filter_empty($aio_wp_security->configs->get_value('aiowps_banned_ip_addresses'));
|
| 981 |
-
// Filter out duplicate lines, add netmask to IP addresses
|
| 982 |
-
$ips_with_netmask = self::add_netmask(array_unique($hosts));
|
| 983 |
-
|
| 984 |
-
if (!empty($ips_with_netmask)) {
|
| 985 |
-
// Apache or LiteSpeed webserver
|
| 986 |
-
// Apache 2.2 and older
|
| 987 |
-
$ip_blacklist_23 .= '#AIOWPS_IP_BLACKLIST_2_3_START' . PHP_EOL;
|
| 988 |
-
$ip_blacklist_24 .= '#AIOWPS_IP_BLACKLIST_2_4_START' . PHP_EOL;
|
| 989 |
-
foreach ($ips_with_netmask as $ip_with_netmask) {
|
| 990 |
-
$ip_blacklist_23 .= "Deny from " . $ip_with_netmask . PHP_EOL;
|
| 991 |
-
$ip_blacklist_24 .= "Require not ip " . $ip_with_netmask . PHP_EOL;
|
| 992 |
-
}
|
| 993 |
-
$ip_blacklist_23 .= '#AIOWPS_IP_BLACKLIST_2_3_END' . PHP_EOL;
|
| 994 |
-
$ip_blacklist_24 .= '#AIOWPS_IP_BLACKLIST_2_4_END' . PHP_EOL;
|
| 995 |
-
}
|
| 996 |
-
}
|
| 997 |
-
|
| 998 |
-
$rules .= AIOWPSecurity_Utility_Htaccess::$six_g_blacklist_marker_start . PHP_EOL; //Add feature marker start
|
| 999 |
-
|
| 1000 |
-
$rules .= '# 6G FIREWALL/BLACKLIST
|
| 1001 |
-
# @ https://perishablepress.com/6g/
|
| 1002 |
-
|
| 1003 |
-
# 6G:[QUERY STRINGS]
|
| 1004 |
-
<IfModule mod_rewrite.c>
|
| 1005 |
-
RewriteEngine On
|
| 1006 |
-
RewriteCond %{QUERY_STRING} (eval\() [NC,OR]
|
| 1007 |
-
RewriteCond %{QUERY_STRING} (127\.0\.0\.1) [NC,OR]
|
| 1008 |
-
RewriteCond %{QUERY_STRING} ([a-z0-9]{2000,}) [NC,OR]
|
| 1009 |
-
RewriteCond %{QUERY_STRING} (javascript:)(.*)(;) [NC,OR]
|
| 1010 |
-
RewriteCond %{QUERY_STRING} (base64_encode)(.*)(\() [NC,OR]
|
| 1011 |
-
RewriteCond %{QUERY_STRING} (GLOBALS|REQUEST)(=|\[|%) [NC,OR]
|
| 1012 |
-
RewriteCond %{QUERY_STRING} (<|%3C)(.*)script(.*)(>|%3) [NC,OR]
|
| 1013 |
-
RewriteCond %{QUERY_STRING} (\\|\.\.\.|\.\./|~|`|<|>|\|) [NC,OR]
|
| 1014 |
-
RewriteCond %{QUERY_STRING} (boot\.ini|etc/passwd|self/environ) [NC,OR]
|
| 1015 |
-
RewriteCond %{QUERY_STRING} (thumbs?(_editor|open)?|tim(thumb)?)\.php [NC,OR]
|
| 1016 |
-
RewriteCond %{QUERY_STRING} (\'|\")(.*)(drop|insert|md5|select|union) [NC]
|
| 1017 |
-
RewriteRule .* - [F]
|
| 1018 |
-
</IfModule>
|
| 1019 |
-
|
| 1020 |
-
# 6G:[REQUEST METHOD]
|
| 1021 |
-
<IfModule mod_rewrite.c>
|
| 1022 |
-
RewriteCond %{REQUEST_METHOD} ^(connect|debug|move|put|trace|track) [NC]
|
| 1023 |
-
RewriteRule .* - [F]
|
| 1024 |
-
</IfModule>
|
| 1025 |
-
|
| 1026 |
-
# 6G:[REFERRERS]
|
| 1027 |
-
<IfModule mod_rewrite.c>
|
| 1028 |
-
RewriteCond %{HTTP_REFERER} ([a-z0-9]{2000,}) [NC,OR]
|
| 1029 |
-
RewriteCond %{HTTP_REFERER} (semalt.com|todaperfeita) [NC]
|
| 1030 |
-
RewriteRule .* - [F]
|
| 1031 |
-
</IfModule>
|
| 1032 |
-
|
| 1033 |
-
# 6G:[REQUEST STRINGS]
|
| 1034 |
-
<IfModule mod_alias.c>
|
| 1035 |
-
RedirectMatch 403 (?i)([a-z0-9]{2000,})
|
| 1036 |
-
RedirectMatch 403 (?i)(https?|ftp|php):/
|
| 1037 |
-
RedirectMatch 403 (?i)(base64_encode)(.*)(\()
|
| 1038 |
-
RedirectMatch 403 (?i)(=\\\'|=\\%27|/\\\'/?)\.
|
| 1039 |
-
RedirectMatch 403 (?i)/(\$(\&)?|\*|\"|\.|,|&|&?)/?$
|
| 1040 |
-
RedirectMatch 403 (?i)(\{0\}|\(/\(|\.\.\.|\+\+\+|\\\"\\\")
|
| 1041 |
-
RedirectMatch 403 (?i)(~|`|<|>|:|;|,|%|\\|\s|\{|\}|\[|\]|\|)
|
| 1042 |
-
RedirectMatch 403 (?i)/(=|\$&|_mm|cgi-|etc/passwd|muieblack)
|
| 1043 |
-
RedirectMatch 403 (?i)(&pws=0|_vti_|\(null\)|\{\$itemURL\}|echo(.*)kae|etc/passwd|eval\(|self/environ)
|
| 1044 |
-
RedirectMatch 403 (?i)\.(aspx?|bash|bak?|cfg|cgi|dll|exe|git|hg|ini|jsp|log|mdb|out|sql|svn|swp|tar|rar|rdf)$
|
| 1045 |
-
RedirectMatch 403 (?i)/(^$|(wp-)?config|mobiquo|phpinfo|shell|sqlpatch|thumb|thumb_editor|thumbopen|timthumb|webshell)\.php
|
| 1046 |
-
</IfModule>
|
| 1047 |
-
|
| 1048 |
-
# 6G:[USER AGENTS]
|
| 1049 |
-
<IfModule mod_setenvif.c>
|
| 1050 |
-
SetEnvIfNoCase User-Agent ([a-z0-9]{2000,}) bad_bot
|
| 1051 |
-
SetEnvIfNoCase User-Agent (archive.org|binlar|casper|checkpriv|choppy|clshttp|cmsworld|diavol|dotbot|extract|feedfinder|flicky|g00g1e|harvest|heritrix|httrack|kmccrew|loader|miner|nikto|nutch|planetwork|postrank|purebot|pycurl|python|seekerspider|siclab|skygrid|sqlmap|sucker|turnit|vikspider|winhttp|xxxyy|youda|zmeu|zune) bad_bot
|
| 1052 |
-
|
| 1053 |
-
# Apache < 2.3
|
| 1054 |
-
<IfModule !mod_authz_core.c>
|
| 1055 |
-
Order Allow,Deny
|
| 1056 |
-
Allow from all
|
| 1057 |
-
Deny from env=bad_bot';
|
| 1058 |
-
if (!empty($ip_blacklist_23))
|
| 1059 |
-
$rules .= PHP_EOL.$ip_blacklist_23; //add ip blacklist if applicable
|
| 1060 |
-
$rules .= '
|
| 1061 |
-
</IfModule>
|
| 1062 |
-
|
| 1063 |
-
# Apache >= 2.3
|
| 1064 |
-
<IfModule mod_authz_core.c>
|
| 1065 |
-
<RequireAll>
|
| 1066 |
-
Require all Granted
|
| 1067 |
-
Require not env bad_bot';
|
| 1068 |
-
if (!empty($ip_blacklist_24))
|
| 1069 |
-
$rules .= PHP_EOL.$ip_blacklist_24; //add ip blacklist if applicable
|
| 1070 |
-
$rules .= '
|
| 1071 |
-
</RequireAll>
|
| 1072 |
-
</IfModule>
|
| 1073 |
-
</IfModule>' . PHP_EOL;
|
| 1074 |
-
$rules .= AIOWPSecurity_Utility_Htaccess::$six_g_blacklist_marker_end . PHP_EOL; //Add feature marker end
|
| 1075 |
-
}
|
| 1076 |
-
|
| 1077 |
-
return $rules;
|
| 1078 |
-
}
|
| 1079 |
-
|
| 1080 |
/**
|
| 1081 |
* This function will write some directives to block all comments which do not originate from the blog's domain
|
| 1082 |
* OR if the user agent is empty. All blocked requests will be redirected to 127.0.0.1
|
|
@@ -1272,15 +1124,13 @@ END;
|
|
| 1272 |
foreach ($ips as $ip) {
|
| 1273 |
//Check if ipv6
|
| 1274 |
if (strpos($ip, ':') !== false) {
|
| 1275 |
-
//for now
|
| 1276 |
-
$
|
| 1277 |
-
if (false
|
| 1278 |
-
|
| 1279 |
}
|
| 1280 |
-
$output[] = $ip;
|
| 1281 |
}
|
| 1282 |
|
| 1283 |
-
|
| 1284 |
$parts = explode('.', $ip);
|
| 1285 |
|
| 1286 |
// Skip any IP that is empty, has more parts than expected or has
|
| 106 |
return false; //unable to write to the file
|
| 107 |
}
|
| 108 |
|
| 109 |
+
$home_path = AIOWPSecurity_Utility_File::get_home_path();
|
|
|
|
| 110 |
$htaccess = $home_path . '.htaccess';
|
| 111 |
|
| 112 |
if (!$f = @fopen($htaccess, 'a+')) {// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged,Squiz.PHP.DisallowMultipleAssignments.FoundInControlStructure
|
| 156 |
* @return boolean
|
| 157 |
*/
|
| 158 |
public static function delete_from_htaccess($section = 'All In One WP Security') {
|
| 159 |
+
$home_path = AIOWPSecurity_Utility_File::get_home_path();
|
|
|
|
| 160 |
$htaccess = $home_path . '.htaccess';
|
| 161 |
|
| 162 |
if (!file_exists($htaccess)) {
|
| 221 |
$rules .= AIOWPSecurity_Utility_Htaccess::getrules_forbid_proxy_comment_posting();
|
| 222 |
$rules .= AIOWPSecurity_Utility_Htaccess::getrules_deny_bad_query_strings();
|
| 223 |
$rules .= AIOWPSecurity_Utility_Htaccess::getrules_advanced_character_string_filter();
|
|
|
|
| 224 |
$rules .= AIOWPSecurity_Utility_Htaccess::getrules_5g_blacklist();
|
|
|
|
| 225 |
$rules .= AIOWPSecurity_Utility_Htaccess::getrules_block_spambots();
|
| 226 |
$rules .= AIOWPSecurity_Utility_Htaccess::getrules_enable_login_whitelist_v2();
|
| 227 |
$rules .= AIOWPSecurity_Utility_Htaccess::prevent_image_hotlinks();
|
| 274 |
$hosts = AIOWPSecurity_Utility::explode_trim_filter_empty($aio_wp_security->configs->get_value('aiowps_banned_ip_addresses'));
|
| 275 |
// Filter out duplicate lines, add netmask to IP addresses
|
| 276 |
$ips_with_netmask = self::add_netmask(array_unique($hosts));
|
| 277 |
+
if (!empty($ips_with_netmask)) {
|
| 278 |
$rules .= AIOWPSecurity_Utility_Htaccess::$ip_blacklist_marker_start . PHP_EOL; //Add feature marker start
|
| 279 |
|
| 280 |
if ($apache_or_litespeed) {
|
| 406 |
return $rules;
|
| 407 |
}
|
| 408 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 409 |
/**
|
| 410 |
* This function will write some directives to allow IPs in the whitelist to access wp-login.php or wp-admin
|
| 411 |
* The function also handles the following special cases:
|
| 594 |
if (!empty($ips_with_netmask)) {
|
| 595 |
foreach ($ips_with_netmask as $xhost) {
|
| 596 |
$ipv6 = false;
|
| 597 |
+
if (false !== strpos($xhost, ':')) {
|
| 598 |
+
//possible ipv6 addr or range
|
| 599 |
+
$checked_ip = AIOWPSecurity_Utility_IP::is_ipv6_address_or_ipv6_range($xhost);
|
| 600 |
+
if (false == $checked_ip) {
|
| 601 |
+
continue;
|
|
|
|
| 602 |
}
|
| 603 |
}
|
| 604 |
$ip_range = substr($xhost, 0, strpos($xhost, "/")); //check if address range
|
| 929 |
return $rules;
|
| 930 |
}
|
| 931 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 932 |
/**
|
| 933 |
* This function will write some directives to block all comments which do not originate from the blog's domain
|
| 934 |
* OR if the user agent is empty. All blocked requests will be redirected to 127.0.0.1
|
| 1124 |
foreach ($ips as $ip) {
|
| 1125 |
//Check if ipv6
|
| 1126 |
if (strpos($ip, ':') !== false) {
|
| 1127 |
+
//for now support whole ipv6 and CIDR range.
|
| 1128 |
+
$checked_ip = AIOWPSecurity_Utility_IP::is_ipv6_address_or_ipv6_range($ip);
|
| 1129 |
+
if (false != $checked_ip) {
|
| 1130 |
+
$output[] = $ip;
|
| 1131 |
}
|
|
|
|
| 1132 |
}
|
| 1133 |
|
|
|
|
| 1134 |
$parts = explode('.', $ip);
|
| 1135 |
|
| 1136 |
// Skip any IP that is empty, has more parts than expected or has
|
|
@@ -90,6 +90,36 @@ class AIOWPSecurity_Utility_IP {
|
|
| 90 |
return $ip_list_array;
|
| 91 |
}
|
| 92 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 93 |
public static function validate_ip_list($ip_list_array, $list_type = '') {
|
| 94 |
$errors = '';
|
| 95 |
|
|
@@ -101,18 +131,18 @@ class AIOWPSecurity_Utility_IP {
|
|
| 101 |
foreach ($submitted_ips as $item) {
|
| 102 |
$item = sanitize_text_field($item);
|
| 103 |
if (strlen($item) > 0) {
|
| 104 |
-
//ipv6 -
|
| 105 |
if (strpos($item, ':') !== false) {
|
| 106 |
//possible ipv6 addr
|
| 107 |
-
$
|
| 108 |
-
if (false === $
|
| 109 |
$errors .= "\n".$item.__(' is not a valid ip address format.', 'all-in-one-wp-security-and-firewall');
|
| 110 |
-
}
|
| 111 |
$list[] = trim($item);
|
| 112 |
}
|
| 113 |
continue;
|
| 114 |
}
|
| 115 |
-
|
| 116 |
$ipParts = explode('.', $item);
|
| 117 |
$isIP = 0;
|
| 118 |
$partcount = 1;
|
|
@@ -132,18 +162,18 @@ class AIOWPSecurity_Utility_IP {
|
|
| 132 |
|
| 133 |
switch ($partcount) {
|
| 134 |
case 1:
|
| 135 |
-
if (trim($part)
|
| 136 |
$goodip = false;
|
| 137 |
$errors .= "\n".$item.__(' is not a valid ip address format.', 'all-in-one-wp-security-and-firewall');
|
| 138 |
}
|
| 139 |
break;
|
| 140 |
case 2:
|
| 141 |
-
if (trim($part)
|
| 142 |
$foundwild = true;
|
| 143 |
}
|
| 144 |
break;
|
| 145 |
default:
|
| 146 |
-
if (trim($part)
|
| 147 |
if (true == $foundwild) {
|
| 148 |
$goodip = false;
|
| 149 |
$errors .= "\n".$item.__(' is not a valid ip address format.', 'all-in-one-wp-security-and-firewall');
|
|
@@ -203,30 +233,20 @@ class AIOWPSecurity_Utility_IP {
|
|
| 203 |
if (empty($ip_address) || empty($whitelisted_ips)) return false;
|
| 204 |
|
| 205 |
$ip_list_array = AIOWPSecurity_Utility_IP::create_ip_list_array_from_string_with_newline($whitelisted_ips);
|
| 206 |
-
|
| 207 |
if (empty($ip_list_array)) return false;
|
| 208 |
|
| 209 |
-
|
|
|
|
| 210 |
foreach ($ip_list_array as $white_ip) {
|
| 211 |
$ipParts = explode('.', $white_ip);
|
| 212 |
$found = array_search('*', $ipParts);
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
|
| 220 |
-
} elseif (2 == $found) {
|
| 221 |
-
//means last 2 octets are wildcards - check if visitor IP falls inside this range
|
| 222 |
-
if ($visitor_ipParts[0] == $ipParts[0] && $visitor_ipParts[1] == $ipParts[1]) {
|
| 223 |
-
return true;
|
| 224 |
-
}
|
| 225 |
-
} elseif (3 == $found) {
|
| 226 |
-
//means last octet is wildcard - check if visitor IP falls inside this range
|
| 227 |
-
if ($visitor_ipParts[0] == $ipParts[0] && $visitor_ipParts[1] == $ipParts[1] && $visitor_ipParts[2] == $ipParts[2]) {
|
| 228 |
-
return true;
|
| 229 |
-
}
|
| 230 |
}
|
| 231 |
} elseif ($white_ip == $ip_address) {
|
| 232 |
return true;
|
| 90 |
return $ip_list_array;
|
| 91 |
}
|
| 92 |
|
| 93 |
+
/**
|
| 94 |
+
* Returns IPv6 ip address or IPv6 range if valid
|
| 95 |
+
*
|
| 96 |
+
* @param string $item possible IPv6 ip address or IPv6 range
|
| 97 |
+
* @return string|boolean $checked_ip trimmed IPv6 ip address or IPv6 range if given input is valid otherwise false.
|
| 98 |
+
*/
|
| 99 |
+
public static function is_ipv6_address_or_ipv6_range($item) {
|
| 100 |
+
$checked_ip = false;
|
| 101 |
+
$res = WP_Http::is_ip_address($item);
|
| 102 |
+
if ('6' == $res && Requests_IPv6::check_ipv6($item)) {
|
| 103 |
+
$checked_ip = trim($item);
|
| 104 |
+
} else {
|
| 105 |
+
//ipv6 - range check for valid CIDR range
|
| 106 |
+
$item_ip_range = explode('/', $item);
|
| 107 |
+
$ip_part_valid = filter_var($item_ip_range[0], FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);
|
| 108 |
+
//1 - 164 range of the IPv6 subnect masking as per CISCO propersed change from 128.
|
| 109 |
+
if (2 == count($item_ip_range) && $ip_part_valid && $item_ip_range[1] >= 1 && $item_ip_range[1] <= 164) {
|
| 110 |
+
$checked_ip = trim($item);
|
| 111 |
+
}
|
| 112 |
+
}
|
| 113 |
+
return $checked_ip;
|
| 114 |
+
}
|
| 115 |
+
|
| 116 |
+
/**
|
| 117 |
+
* Validates IP or IP range
|
| 118 |
+
*
|
| 119 |
+
* @param array $ip_list_array
|
| 120 |
+
* @param string $list_type
|
| 121 |
+
* @return array $return_payload
|
| 122 |
+
*/
|
| 123 |
public static function validate_ip_list($ip_list_array, $list_type = '') {
|
| 124 |
$errors = '';
|
| 125 |
|
| 131 |
foreach ($submitted_ips as $item) {
|
| 132 |
$item = sanitize_text_field($item);
|
| 133 |
if (strlen($item) > 0) {
|
| 134 |
+
//ipv6 - check
|
| 135 |
if (strpos($item, ':') !== false) {
|
| 136 |
//possible ipv6 addr
|
| 137 |
+
$checked_ip = AIOWPSecurity_Utility_IP::is_ipv6_address_or_ipv6_range($item);
|
| 138 |
+
if (false === $checked_ip) {
|
| 139 |
$errors .= "\n".$item.__(' is not a valid ip address format.', 'all-in-one-wp-security-and-firewall');
|
| 140 |
+
} else {
|
| 141 |
$list[] = trim($item);
|
| 142 |
}
|
| 143 |
continue;
|
| 144 |
}
|
| 145 |
+
|
| 146 |
$ipParts = explode('.', $item);
|
| 147 |
$isIP = 0;
|
| 148 |
$partcount = 1;
|
| 162 |
|
| 163 |
switch ($partcount) {
|
| 164 |
case 1:
|
| 165 |
+
if ('*' == trim($part)) {
|
| 166 |
$goodip = false;
|
| 167 |
$errors .= "\n".$item.__(' is not a valid ip address format.', 'all-in-one-wp-security-and-firewall');
|
| 168 |
}
|
| 169 |
break;
|
| 170 |
case 2:
|
| 171 |
+
if ('*' == trim($part)) {
|
| 172 |
$foundwild = true;
|
| 173 |
}
|
| 174 |
break;
|
| 175 |
default:
|
| 176 |
+
if ('*' != trim($part)) {
|
| 177 |
if (true == $foundwild) {
|
| 178 |
$goodip = false;
|
| 179 |
$errors .= "\n".$item.__(' is not a valid ip address format.', 'all-in-one-wp-security-and-firewall');
|
| 233 |
if (empty($ip_address) || empty($whitelisted_ips)) return false;
|
| 234 |
|
| 235 |
$ip_list_array = AIOWPSecurity_Utility_IP::create_ip_list_array_from_string_with_newline($whitelisted_ips);
|
|
|
|
| 236 |
if (empty($ip_list_array)) return false;
|
| 237 |
|
| 238 |
+
require_once(AIO_WP_SECURITY_PATH.'/vendor/mlocati/ip-lib/ip-lib.php');
|
| 239 |
+
$ip_address_parsed = \IPLib\Factory::parseAddressString($ip_address);
|
| 240 |
foreach ($ip_list_array as $white_ip) {
|
| 241 |
$ipParts = explode('.', $white_ip);
|
| 242 |
$found = array_search('*', $ipParts);
|
| 243 |
+
$found_white_ipv6 = strpos($white_ip, ':');
|
| 244 |
+
// ipv4 range check starts
|
| 245 |
+
if (false !== $found || false != $found_white_ipv6) {
|
| 246 |
+
$range = \IPLib\Factory::parseRangeString($white_ip);
|
| 247 |
+
$with_in_range = $range->contains($ip_address_parsed);
|
| 248 |
+
if (true == $with_in_range) {
|
| 249 |
+
return true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 250 |
}
|
| 251 |
} elseif ($white_ip == $ip_address) {
|
| 252 |
return true;
|
|
@@ -4,10 +4,32 @@ if (!defined('ABSPATH')) {
|
|
| 4 |
}
|
| 5 |
|
| 6 |
class AIOWPSecurity_Utility {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
public function __construct() {
|
| 8 |
//NOP
|
| 9 |
}
|
| 10 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
/**
|
| 12 |
* Explode $string with $delimiter, trim all lines and filter out empty ones.
|
| 13 |
*
|
|
@@ -86,7 +108,7 @@ class AIOWPSecurity_Utility {
|
|
| 86 |
}
|
| 87 |
|
| 88 |
//If multisite
|
| 89 |
-
if (
|
| 90 |
$blog_id = get_current_blog_id();
|
| 91 |
$admin_users = get_users('blog_id=' . $blog_id . '&orderby=login&role=administrator');
|
| 92 |
foreach ($admin_users as $user) {
|
|
@@ -205,15 +227,6 @@ class AIOWPSecurity_Utility {
|
|
| 205 |
return "";
|
| 206 |
}
|
| 207 |
|
| 208 |
-
/**
|
| 209 |
-
* Checks if installation is multisite
|
| 210 |
-
*
|
| 211 |
-
* @return type
|
| 212 |
-
*/
|
| 213 |
-
public static function is_multisite_install() {
|
| 214 |
-
return function_exists('is_multisite') && is_multisite();
|
| 215 |
-
}
|
| 216 |
-
|
| 217 |
/**
|
| 218 |
* This is a general yellow box message for when we want to suppress a feature's config items because site is subsite of multi-site
|
| 219 |
*/
|
|
@@ -370,7 +383,7 @@ class AIOWPSecurity_Utility {
|
|
| 370 |
$referer_info = isset($_SERVER['HTTP_REFERER']) ? esc_attr($_SERVER['HTTP_REFERER']) : '';
|
| 371 |
}
|
| 372 |
|
| 373 |
-
$current_time = current_time('mysql');
|
| 374 |
$data = array(
|
| 375 |
'event_type' => $event_type,
|
| 376 |
'username' => $username,
|
|
@@ -401,7 +414,8 @@ class AIOWPSecurity_Utility {
|
|
| 401 |
public static function check_locked_ip($ip) {
|
| 402 |
global $wpdb;
|
| 403 |
$login_lockdown_table = AIOWPSEC_TBL_LOGIN_LOCKDOWN;
|
| 404 |
-
$
|
|
|
|
| 405 |
if (null != $locked_ip) {
|
| 406 |
return true;
|
| 407 |
} else {
|
|
@@ -418,7 +432,7 @@ class AIOWPSecurity_Utility {
|
|
| 418 |
public static function get_locked_ips() {
|
| 419 |
global $wpdb;
|
| 420 |
$login_lockdown_table = AIOWPSEC_TBL_LOGIN_LOCKDOWN;
|
| 421 |
-
$now = current_time('mysql');
|
| 422 |
$locked_ips = $wpdb->get_results($wpdb->prepare("SELECT * FROM $login_lockdown_table WHERE release_date > %s", $now), ARRAY_A);
|
| 423 |
|
| 424 |
if (empty($locked_ips)) {
|
|
@@ -430,18 +444,27 @@ class AIOWPSecurity_Utility {
|
|
| 430 |
|
| 431 |
|
| 432 |
/**
|
| 433 |
-
* Locks an IP address - Adds an entry to the
|
| 434 |
*
|
| 435 |
-
* @global
|
| 436 |
-
* @global
|
| 437 |
-
*
|
| 438 |
-
* @param
|
| 439 |
-
* @param
|
|
|
|
|
|
|
|
|
|
| 440 |
*/
|
| 441 |
-
public static function lock_IP($ip, $lock_reason
|
| 442 |
global $wpdb, $aio_wp_security;
|
| 443 |
$login_lockdown_table = AIOWPSEC_TBL_LOGIN_LOCKDOWN;
|
| 444 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 445 |
$username = sanitize_user($username);
|
| 446 |
$user = get_user_by('login', $username); //Returns WP_User object if exists
|
| 447 |
|
|
@@ -455,11 +478,15 @@ class AIOWPSecurity_Utility {
|
|
| 455 |
$user_id = $user->ID;
|
| 456 |
}
|
| 457 |
|
| 458 |
-
$
|
| 459 |
-
|
| 460 |
-
|
| 461 |
-
|
| 462 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 463 |
if ($result > 0) {
|
| 464 |
} elseif (false === $result) {
|
| 465 |
$aio_wp_security->debug_logger->log_debug("lock_IP: Error inserting record into " . $login_lockdown_table, 4);//Log the highly unlikely event of DB error
|
|
@@ -475,7 +502,7 @@ class AIOWPSecurity_Utility {
|
|
| 475 |
*/
|
| 476 |
public static function get_blog_ids() {
|
| 477 |
global $wpdb;
|
| 478 |
-
if (
|
| 479 |
global $wpdb;
|
| 480 |
$blog_ids = $wpdb->get_col("SELECT blog_id FROM " . $wpdb->prefix . "blogs");
|
| 481 |
} else {
|
|
@@ -484,31 +511,56 @@ class AIOWPSecurity_Utility {
|
|
| 484 |
return $blog_ids;
|
| 485 |
}
|
| 486 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 487 |
|
| 488 |
/**
|
| 489 |
* This function will delete the oldest rows from a table which are over the max amount of rows specified
|
| 490 |
*
|
| 491 |
-
* @global type $wpdb
|
| 492 |
-
* @global type $aio_wp_security
|
| 493 |
-
* @param type $table_name
|
| 494 |
-
* @param type $max_rows
|
|
|
|
| 495 |
* @return bool
|
| 496 |
*/
|
| 497 |
-
public static function cleanup_table($table_name, $max_rows = '10000') {
|
| 498 |
global $wpdb, $aio_wp_security;
|
| 499 |
|
| 500 |
$num_rows = $wpdb->get_var("select count(*) from $table_name");
|
| 501 |
$result = true;
|
| 502 |
if ($num_rows > $max_rows) {
|
| 503 |
//if the table has more than max entries delete oldest rows
|
| 504 |
-
|
| 505 |
$del_sql = "DELETE FROM $table_name
|
| 506 |
-
WHERE
|
| 507 |
-
SELECT
|
| 508 |
FROM (
|
| 509 |
-
SELECT
|
| 510 |
FROM $table_name
|
| 511 |
-
ORDER BY
|
| 512 |
LIMIT 1 OFFSET $max_rows
|
| 513 |
) foo_tmp
|
| 514 |
)";
|
|
@@ -529,26 +581,28 @@ class AIOWPSecurity_Utility {
|
|
| 529 |
*
|
| 530 |
* @global wpdb $wpdb
|
| 531 |
*/
|
| 532 |
-
|
| 533 |
-
|
| 534 |
-
|
| 535 |
-
|
| 536 |
-
|
| 537 |
-
|
| 538 |
-
|
| 539 |
-
|
| 540 |
-
|
| 541 |
-
|
| 542 |
-
|
| 543 |
-
|
| 544 |
-
|
| 545 |
-
|
| 546 |
-
|
| 547 |
-
|
| 548 |
-
|
|
|
|
| 549 |
}
|
| 550 |
}
|
| 551 |
}
|
|
|
|
| 552 |
|
| 553 |
/**
|
| 554 |
* Get server type.
|
|
@@ -619,5 +673,74 @@ class AIOWPSecurity_Utility {
|
|
| 619 |
if ($chars_unmasked >= $str_length) return $str;
|
| 620 |
return preg_replace("/(.{".$chars_unmasked."}$)(*SKIP)(*F)|(.)/u", "*", $str);
|
| 621 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 622 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 623 |
}
|
| 4 |
}
|
| 5 |
|
| 6 |
class AIOWPSecurity_Utility {
|
| 7 |
+
|
| 8 |
+
/**
|
| 9 |
+
* Returned when we can't detect the user's server software
|
| 10 |
+
*
|
| 11 |
+
* @var int
|
| 12 |
+
*/
|
| 13 |
+
const UNSUPPORTED_SERVER_TYPE = -1;
|
| 14 |
+
|
| 15 |
+
/**
|
| 16 |
+
* Class constructor
|
| 17 |
+
*/
|
| 18 |
public function __construct() {
|
| 19 |
//NOP
|
| 20 |
}
|
| 21 |
|
| 22 |
+
/**
|
| 23 |
+
* Check whether the current logged in user has the capability to manage the AIOWPS plugin
|
| 24 |
+
*
|
| 25 |
+
* @return Boolean True if the logged in user has capability to manage the AIOWPS plugin, otherwise false
|
| 26 |
+
*/
|
| 27 |
+
public static function has_manage_cap() {
|
| 28 |
+
// This filter will useful when the administrator would like to give permission to access AIOWPS to Security Analyst.
|
| 29 |
+
$cap = apply_filters('aiowps_management_capability', AIOWPSEC_MANAGEMENT_PERMISSION);
|
| 30 |
+
return current_user_can($cap);
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
/**
|
| 34 |
* Explode $string with $delimiter, trim all lines and filter out empty ones.
|
| 35 |
*
|
| 108 |
}
|
| 109 |
|
| 110 |
//If multisite
|
| 111 |
+
if (is_multisite()) {
|
| 112 |
$blog_id = get_current_blog_id();
|
| 113 |
$admin_users = get_users('blog_id=' . $blog_id . '&orderby=login&role=administrator');
|
| 114 |
foreach ($admin_users as $user) {
|
| 227 |
return "";
|
| 228 |
}
|
| 229 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 230 |
/**
|
| 231 |
* This is a general yellow box message for when we want to suppress a feature's config items because site is subsite of multi-site
|
| 232 |
*/
|
| 383 |
$referer_info = isset($_SERVER['HTTP_REFERER']) ? esc_attr($_SERVER['HTTP_REFERER']) : '';
|
| 384 |
}
|
| 385 |
|
| 386 |
+
$current_time = current_time('mysql', true);
|
| 387 |
$data = array(
|
| 388 |
'event_type' => $event_type,
|
| 389 |
'username' => $username,
|
| 414 |
public static function check_locked_ip($ip) {
|
| 415 |
global $wpdb;
|
| 416 |
$login_lockdown_table = AIOWPSEC_TBL_LOGIN_LOCKDOWN;
|
| 417 |
+
$now = current_time('mysql', true);
|
| 418 |
+
$locked_ip = $wpdb->get_row($wpdb->prepare("SELECT * FROM $login_lockdown_table WHERE release_date > %s AND failed_login_ip = %s", $now, $ip), ARRAY_A);
|
| 419 |
if (null != $locked_ip) {
|
| 420 |
return true;
|
| 421 |
} else {
|
| 432 |
public static function get_locked_ips() {
|
| 433 |
global $wpdb;
|
| 434 |
$login_lockdown_table = AIOWPSEC_TBL_LOGIN_LOCKDOWN;
|
| 435 |
+
$now = current_time('mysql', true);
|
| 436 |
$locked_ips = $wpdb->get_results($wpdb->prepare("SELECT * FROM $login_lockdown_table WHERE release_date > %s", $now), ARRAY_A);
|
| 437 |
|
| 438 |
if (empty($locked_ips)) {
|
| 444 |
|
| 445 |
|
| 446 |
/**
|
| 447 |
+
* Locks an IP address - Adds an entry to the AIOWPSEC_TBL_LOGIN_LOCKDOWN table.
|
| 448 |
*
|
| 449 |
+
* @global wpdb $wpdb
|
| 450 |
+
* @global AIO_WP_Security $aio_wp_security
|
| 451 |
+
*
|
| 452 |
+
* @param String $ip
|
| 453 |
+
* @param String $lock_reason
|
| 454 |
+
* @param String $username
|
| 455 |
+
*
|
| 456 |
+
* @return Void
|
| 457 |
*/
|
| 458 |
+
public static function lock_IP($ip, $lock_reason, $username = '') {
|
| 459 |
global $wpdb, $aio_wp_security;
|
| 460 |
$login_lockdown_table = AIOWPSEC_TBL_LOGIN_LOCKDOWN;
|
| 461 |
+
|
| 462 |
+
if ('404' == $lock_reason) {
|
| 463 |
+
$lock_minutes = $aio_wp_security->configs->get_value('aiowps_404_lockout_time_length');
|
| 464 |
+
} else {
|
| 465 |
+
$lock_minutes = $aio_wp_security->user_login_obj->get_dynamic_lockout_time_length();
|
| 466 |
+
}
|
| 467 |
+
|
| 468 |
$username = sanitize_user($username);
|
| 469 |
$user = get_user_by('login', $username); //Returns WP_User object if exists
|
| 470 |
|
| 478 |
$user_id = $user->ID;
|
| 479 |
}
|
| 480 |
|
| 481 |
+
$ip = esc_sql($ip);
|
| 482 |
+
|
| 483 |
+
$lock_time = current_time('mysql', true);
|
| 484 |
+
$release_time = date('Y-m-d H:i:s', time() + ($lock_minutes * MINUTE_IN_SECONDS));
|
| 485 |
+
|
| 486 |
+
$data = array('user_id' => $user_id, 'user_login' => $username, 'lockdown_date' => $lock_time, 'release_date' => $release_time, 'failed_login_IP' => $ip, 'lock_reason' => $lock_reason);
|
| 487 |
+
$format = array('%d', '%s', '%s', '%s', '%s', '%s');
|
| 488 |
+
$result = $wpdb->insert($login_lockdown_table, $data, $format);
|
| 489 |
+
|
| 490 |
if ($result > 0) {
|
| 491 |
} elseif (false === $result) {
|
| 492 |
$aio_wp_security->debug_logger->log_debug("lock_IP: Error inserting record into " . $login_lockdown_table, 4);//Log the highly unlikely event of DB error
|
| 502 |
*/
|
| 503 |
public static function get_blog_ids() {
|
| 504 |
global $wpdb;
|
| 505 |
+
if (is_multisite()) {
|
| 506 |
global $wpdb;
|
| 507 |
$blog_ids = $wpdb->get_col("SELECT blog_id FROM " . $wpdb->prefix . "blogs");
|
| 508 |
} else {
|
| 511 |
return $blog_ids;
|
| 512 |
}
|
| 513 |
|
| 514 |
+
/**
|
| 515 |
+
* Purges old records of table
|
| 516 |
+
*
|
| 517 |
+
* @global type $wpdb WP Database object
|
| 518 |
+
* @global type $aio_wp_security AIO WP Security object
|
| 519 |
+
* @param type $table_name Table name
|
| 520 |
+
* @param type $purge_records_after_days Records after days to be deleted
|
| 521 |
+
* @param type $date_field Date field of table
|
| 522 |
+
* @return void
|
| 523 |
+
*/
|
| 524 |
+
public static function purge_table_records($table_name, $purge_records_after_days, $date_field) {
|
| 525 |
+
global $wpdb, $aio_wp_security;
|
| 526 |
+
|
| 527 |
+
$older_than_date_time = date('Y-m-d H:m:s', strtotime('-' . $purge_records_after_days . ' days', current_time('timestamp', true)));
|
| 528 |
+
$sql = $wpdb->prepare('DELETE FROM ' . $table_name . ' WHERE '.$date_field.' < %s', $older_than_date_time);
|
| 529 |
+
$ret_deleted = $wpdb->query($sql);
|
| 530 |
+
if (false === $ret_deleted) {
|
| 531 |
+
$err_db = !empty($wpdb->last_error) ? ' ('.$wpdb->last_error.' - '.$wpdb->last_query.')' : '';
|
| 532 |
+
// Status level 4 indicates failure status.
|
| 533 |
+
$aio_wp_security->debug_logger->log_debug_cron('Purge records error - failed to purge older records for ' . $table_name . '.' . $err_db, 4);
|
| 534 |
+
} else {
|
| 535 |
+
$aio_wp_security->debug_logger->log_debug_cron(sprintf('Purge records - %d records were deleted for ' . $table_name . '.', $ret_deleted));
|
| 536 |
+
}
|
| 537 |
+
}
|
| 538 |
|
| 539 |
/**
|
| 540 |
* This function will delete the oldest rows from a table which are over the max amount of rows specified
|
| 541 |
*
|
| 542 |
+
* @global type $wpdb WP Database object
|
| 543 |
+
* @global type $aio_wp_security AIO WP Security object
|
| 544 |
+
* @param type $table_name Table name
|
| 545 |
+
* @param type $max_rows More than max to be deleted
|
| 546 |
+
* @param type $id_field Primary field of table
|
| 547 |
* @return bool
|
| 548 |
*/
|
| 549 |
+
public static function cleanup_table($table_name, $max_rows = '10000', $id_field = 'id') {
|
| 550 |
global $wpdb, $aio_wp_security;
|
| 551 |
|
| 552 |
$num_rows = $wpdb->get_var("select count(*) from $table_name");
|
| 553 |
$result = true;
|
| 554 |
if ($num_rows > $max_rows) {
|
| 555 |
//if the table has more than max entries delete oldest rows
|
| 556 |
+
|
| 557 |
$del_sql = "DELETE FROM $table_name
|
| 558 |
+
WHERE ".$id_field." <= (
|
| 559 |
+
SELECT ".$id_field."
|
| 560 |
FROM (
|
| 561 |
+
SELECT ".$id_field."
|
| 562 |
FROM $table_name
|
| 563 |
+
ORDER BY ".$id_field." DESC
|
| 564 |
LIMIT 1 OFFSET $max_rows
|
| 565 |
) foo_tmp
|
| 566 |
)";
|
| 581 |
*
|
| 582 |
* @global wpdb $wpdb
|
| 583 |
*/
|
| 584 |
+
public static function delete_expired_captcha_options() {
|
| 585 |
+
global $wpdb;
|
| 586 |
+
$current_unix_time = current_time('timestamp', true);
|
| 587 |
+
$previous_hour = $current_unix_time - 3600;
|
| 588 |
+
$tbl = is_multisite() ? $wpdb->sitemeta : $wpdb->prefix . 'options';
|
| 589 |
+
$key_name = is_multisite() ? 'meta_key' : 'option_name';
|
| 590 |
+
$key_val = is_multisite() ? 'meta_value' : 'option_value';
|
| 591 |
+
$query = $wpdb->prepare("SELECT * FROM {$tbl} WHERE {$key_name} LIKE 'aiowps_captcha_string_info_time_%' AND {$key_val} < %s", $previous_hour);
|
| 592 |
+
$res = $wpdb->get_results($query, ARRAY_A);
|
| 593 |
+
if (!empty($res)) {
|
| 594 |
+
foreach ($res as $item) {
|
| 595 |
+
$option_name = $item[$key_name];
|
| 596 |
+
if (is_multisite()) {
|
| 597 |
+
delete_site_option($option_name);
|
| 598 |
+
delete_site_option(str_replace('time_', '', $option_name));
|
| 599 |
+
} else {
|
| 600 |
+
delete_option($option_name);
|
| 601 |
+
delete_option(str_replace('time_', '', $option_name));
|
| 602 |
}
|
| 603 |
}
|
| 604 |
}
|
| 605 |
+
}
|
| 606 |
|
| 607 |
/**
|
| 608 |
* Get server type.
|
| 673 |
if ($chars_unmasked >= $str_length) return $str;
|
| 674 |
return preg_replace("/(.{".$chars_unmasked."}$)(*SKIP)(*F)|(.)/u", "*", $str);
|
| 675 |
}
|
| 676 |
+
|
| 677 |
+
/**
|
| 678 |
+
* Create a php backtrace log file for login lockdown email
|
| 679 |
+
*
|
| 680 |
+
* @param Array $logs
|
| 681 |
+
* @global AIO_WP_Security $aio_wp_security
|
| 682 |
+
* @return string
|
| 683 |
+
*/
|
| 684 |
+
public static function login_lockdown_email_backtrace_log_file($logs = array()) {
|
| 685 |
+
global $aio_wp_security;
|
| 686 |
+
$temp_dir = get_temp_dir();
|
| 687 |
+
$backtrace_filename = wp_unique_filename($temp_dir, 'log_backtrace_' . time() . '.txt');
|
| 688 |
+
$backtrace_filepath = $temp_dir.$backtrace_filename;
|
| 689 |
+
if (count($logs) > 0) {
|
| 690 |
+
$dbg = "";
|
| 691 |
+
foreach ($logs as $log) {
|
| 692 |
+
$dbg.= "############ BACKTRACE STARTS ########\n";
|
| 693 |
+
$dbg.= $log['backtrace_log'];
|
| 694 |
+
$dbg.= "############ BACKTRACE ENDS ########\n\n";
|
| 695 |
+
}
|
| 696 |
+
} else {
|
| 697 |
+
$dbg = debug_backtrace();
|
| 698 |
+
}
|
| 699 |
+
$is_log_file_written = file_put_contents($backtrace_filepath, print_r($dbg, true));
|
| 700 |
+
if ($is_log_file_written) {
|
| 701 |
+
return $backtrace_filepath;
|
| 702 |
+
} else {
|
| 703 |
+
$aio_wp_security->debug_logger->log_debug("Error in writing php backtrace file " . $backtrace_filepath . " to attach in email.", 4);
|
| 704 |
+
return '';
|
| 705 |
+
}
|
| 706 |
+
}
|
| 707 |
|
| 708 |
+
/**
|
| 709 |
+
* Check whether the WooCommerce plugin is active.
|
| 710 |
+
*
|
| 711 |
+
* @return Boolean True if the WooCommerce plugin is active, otherwise false.
|
| 712 |
+
*/
|
| 713 |
+
public static function is_woocommerce_plugin_active() {
|
| 714 |
+
return class_exists('WooCommerce');
|
| 715 |
+
}
|
| 716 |
+
|
| 717 |
+
/**
|
| 718 |
+
* Check whether incompatible TFA premium plugin version active.
|
| 719 |
+
*
|
| 720 |
+
* @return boolean True if the incompatible TFA premium plugin version active, otherwise false.
|
| 721 |
+
*/
|
| 722 |
+
public static function is_incopatible_tfa_premium_version_active() {
|
| 723 |
+
if (!function_exists('get_plugins')) {
|
| 724 |
+
require_once(ABSPATH.'/wp-admin/includes/plugin.php');
|
| 725 |
+
}
|
| 726 |
+
foreach (get_plugins() as $plugin_slug => $plugin_info) {
|
| 727 |
+
if (is_plugin_active($plugin_slug) && strpos($plugin_slug, '/') && is_dir(WP_PLUGIN_DIR.'/'.explode('/', $plugin_slug)[0].'/simba-tfa/premium') && version_compare($plugin_info['Version'], AIOS_TFA_PREMIUM_LATEST_INCOMPATIBLE_VERSION, '<=')) {
|
| 728 |
+
return true;
|
| 729 |
+
}
|
| 730 |
+
}
|
| 731 |
+
|
| 732 |
+
return false;
|
| 733 |
+
}
|
| 734 |
+
|
| 735 |
+
/**
|
| 736 |
+
* Check whether TFA plugin activating.
|
| 737 |
+
*
|
| 738 |
+
* @return boolean True if the TFA plugin activating, otherwise false.
|
| 739 |
+
*/
|
| 740 |
+
public static function is_tfa_or_self_plugin_activating() {
|
| 741 |
+
// The $GLOBALS['pagenow'] doesn't set in the network admin plugins page and it throws the warning "Notice: Undefined index: pagenow in ..." so we can't use it.
|
| 742 |
+
// https://core.trac.wordpress.org/ticket/42656
|
| 743 |
+
return is_admin() &&
|
| 744 |
+
preg_match('#/wp-admin/plugins.php$#i', $_SERVER['PHP_SELF']) && isset($_GET['plugin']) && (preg_match("/\/two-factor-login.php/", $_GET['plugin']) || preg_match("/all-in-one-wp-security-and-firewall/", $_GET['plugin']));
|
| 745 |
+
}
|
| 746 |
}
|
|
@@ -14,7 +14,7 @@ class AIOWPSecurity_WP_Footer_Content {
|
|
| 14 |
if ($aio_wp_security->configs->get_value('aiowps_default_recaptcha')) {
|
| 15 |
// For Woocommerce forms.
|
| 16 |
// Only proceed if woocommerce installed and active
|
| 17 |
-
if (
|
| 18 |
if ($aio_wp_security->configs->get_value('aiowps_enable_woo_login_captcha') == '1' || $aio_wp_security->configs->get_value('aiowps_enable_woo_register_captcha') == '1' || $aio_wp_security->configs->get_value('aiowps_enable_woo_lostpassword_captcha') == '1') {
|
| 19 |
$this->print_recaptcha_api_woo();
|
| 20 |
}
|
| 14 |
if ($aio_wp_security->configs->get_value('aiowps_default_recaptcha')) {
|
| 15 |
// For Woocommerce forms.
|
| 16 |
// Only proceed if woocommerce installed and active
|
| 17 |
+
if (AIOWPSecurity_Utility::is_woocommerce_plugin_active()) {
|
| 18 |
if ($aio_wp_security->configs->get_value('aiowps_enable_woo_login_captcha') == '1' || $aio_wp_security->configs->get_value('aiowps_enable_woo_register_captcha') == '1' || $aio_wp_security->configs->get_value('aiowps_enable_woo_lostpassword_captcha') == '1') {
|
| 19 |
$this->print_recaptcha_api_woo();
|
| 20 |
}
|
|
@@ -412,4 +412,12 @@
|
|
| 412 |
min-width: 17px;
|
| 413 |
border: 1px solid #ccc;
|
| 414 |
background: #f7f7f7;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 415 |
}
|
| 412 |
min-width: 17px;
|
| 413 |
border: 1px solid #ccc;
|
| 414 |
background: #f7f7f7;
|
| 415 |
+
}
|
| 416 |
+
|
| 417 |
+
svg > g > g.google-visualization-tooltip {
|
| 418 |
+
pointer-events: none;
|
| 419 |
+
}
|
| 420 |
+
|
| 421 |
+
.wp-security_page_aiowpsec_settings h2, .wp-security_page_aiowpsec_settings #poststuff h2 {
|
| 422 |
+
padding-left: 0;
|
| 423 |
}
|
|
@@ -0,0 +1,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
var tfa_query_leaving = false;
|
| 2 |
+
|
| 3 |
+
// Prevent accidental leaving if there are unsaved settings
|
| 4 |
+
window.onbeforeunload = function(e) {
|
| 5 |
+
if (tfa_query_leaving) {
|
| 6 |
+
e.returnValue = simba_tfa_frontend.ask;
|
| 7 |
+
return simba_tfa_frontend.ask;
|
| 8 |
+
}
|
| 9 |
+
}
|
| 10 |
+
|
| 11 |
+
jQuery(function($) {
|
| 12 |
+
|
| 13 |
+
$(".tfa_settings_form input, .tfa_settings_form textarea, .tfa_settings_form select" ).on('change', function() {
|
| 14 |
+
tfa_query_leaving = true;
|
| 15 |
+
});
|
| 16 |
+
|
| 17 |
+
$(".tfa_settings_form input[name='simbatfa_delivery_type']").on('change', function() {
|
| 18 |
+
$(".tfa_third_party_holder").slideToggle();
|
| 19 |
+
});
|
| 20 |
+
|
| 21 |
+
$(".simbatfa_settings_save").on('click', function() {
|
| 22 |
+
|
| 23 |
+
$.blockUI({ message: '<div style="margin: 8px;font-size:150%;">'+simba_tfa_frontend.saving+'</div>' });
|
| 24 |
+
|
| 25 |
+
// https://stackoverflow.com/questions/10147149/how-can-i-override-jquerys-serialize-to-include-unchecked-checkboxes
|
| 26 |
+
var form_data = $('.tfa_settings_form input, .tfa_settings_form textarea, .tfa_settings_form select').serialize();
|
| 27 |
+
|
| 28 |
+
// Include unchecked checkboxes. Use filter to only include unchecked boxes.
|
| 29 |
+
$.each($('.tfa_settings_form input[type=checkbox]')
|
| 30 |
+
.filter(function(idx) {
|
| 31 |
+
return $(this).prop('checked') === false
|
| 32 |
+
}),
|
| 33 |
+
function(idx, el){
|
| 34 |
+
// attach matched element names to the form_data with a chosen value.
|
| 35 |
+
var emptyVal = '0';
|
| 36 |
+
form_data += '&' + $(el).attr('name') + '=' + emptyVal;
|
| 37 |
+
}
|
| 38 |
+
);
|
| 39 |
+
|
| 40 |
+
$.post(simba_tfa_frontend.ajax_url, {
|
| 41 |
+
action: 'tfa_frontend',
|
| 42 |
+
subaction: 'savesettings',
|
| 43 |
+
settings: form_data,
|
| 44 |
+
nonce: simba_tfa_frontend.nonce
|
| 45 |
+
}, function(response) {
|
| 46 |
+
var settings_saved = false;
|
| 47 |
+
try {
|
| 48 |
+
var resp = JSON.parse(response);
|
| 49 |
+
if (resp.hasOwnProperty('result')) {
|
| 50 |
+
settings_saved = true;
|
| 51 |
+
tfa_query_leaving = false;
|
| 52 |
+
// Allow user code to respond
|
| 53 |
+
$(document).trigger('tfa_settings_saved', resp);
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
if (resp.hasOwnProperty('message')) {
|
| 57 |
+
alert(resp.message);
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
if (resp.hasOwnProperty('qr')) {
|
| 61 |
+
$('.simbaotp_qr_container').data('qrcode', resp['qr']).empty().qrcode({
|
| 62 |
+
"render": "image",
|
| 63 |
+
"text": resp['qr'],
|
| 64 |
+
});
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
if (resp.hasOwnProperty('al_type_disp')) {
|
| 68 |
+
$("#al_type_name").html(resp['al_type_disp']['disp']);
|
| 69 |
+
$("#al_type_desc").html(resp['al_type_disp']['desc']);
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
} catch(err) {
|
| 73 |
+
console.log(err);
|
| 74 |
+
console.log(response);
|
| 75 |
+
if ('' === simba_tfa_frontend.also_try) {
|
| 76 |
+
alert(simba_tfa_frontend.response+response);
|
| 77 |
+
}
|
| 78 |
+
}
|
| 79 |
+
if ('' != simba_tfa_frontend.also_try) {
|
| 80 |
+
if (!settings_saved) {
|
| 81 |
+
$.post(simba_tfa_frontend.also_try, {
|
| 82 |
+
action: 'tfa_frontend',
|
| 83 |
+
subaction: 'savesettings',
|
| 84 |
+
settings: form_data,
|
| 85 |
+
nonce: simba_tfa_frontend.nonce
|
| 86 |
+
}, function(response) {
|
| 87 |
+
|
| 88 |
+
try {
|
| 89 |
+
var resp = JSON.parse(response);
|
| 90 |
+
if (resp.hasOwnProperty('result')) {
|
| 91 |
+
settings_saved = true;
|
| 92 |
+
tfa_query_leaving = false;
|
| 93 |
+
// Allow user code to respond
|
| 94 |
+
$(document).trigger('tfa_settings_saved', resp);
|
| 95 |
+
}
|
| 96 |
+
if (resp.hasOwnProperty('message')) {
|
| 97 |
+
alert(resp.message);
|
| 98 |
+
}
|
| 99 |
+
if (resp.hasOwnProperty('qr')) {
|
| 100 |
+
$('.simbaotp_qr_container').data('qrcode', resp['qr']).empty().qrcode({
|
| 101 |
+
"render": "image",
|
| 102 |
+
"text": resp['qr'],
|
| 103 |
+
});
|
| 104 |
+
}
|
| 105 |
+
if (resp.hasOwnProperty('al_type_disp')) {
|
| 106 |
+
$("#al_type_name").html(resp['al_type_disp']['disp']);
|
| 107 |
+
$("#al_type_desc").html(resp['al_type_disp']['desc']);
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
} catch(err) {
|
| 111 |
+
console.log(err);
|
| 112 |
+
console.log(response);
|
| 113 |
+
alert(simba_tfa_frontend.response+response);
|
| 114 |
+
}
|
| 115 |
+
$.unblockUI();
|
| 116 |
+
});
|
| 117 |
+
} else {
|
| 118 |
+
$.unblockUI();
|
| 119 |
+
}
|
| 120 |
+
} else {
|
| 121 |
+
$.unblockUI();
|
| 122 |
+
}
|
| 123 |
+
});
|
| 124 |
+
|
| 125 |
+
});
|
| 126 |
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# jQuery.qrcode
|
| 2 |
+
|
| 3 |
+
[![license][license-img]][github] [![web][web-img]][web] [![github][github-img]][github] [![bower][bower-img]][github]
|
| 4 |
+
|
| 5 |
+
jQuery plugin to dynamically generate QR codes. Uses [QR Code Generator][qrcode] (MIT).
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
## License
|
| 9 |
+
The MIT License (MIT)
|
| 10 |
+
|
| 11 |
+
Copyright (c) 2016 Lars Jung (https://larsjung.de)
|
| 12 |
+
|
| 13 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 14 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 15 |
+
in the Software without restriction, including without limitation the rights
|
| 16 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 17 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 18 |
+
furnished to do so, subject to the following conditions:
|
| 19 |
+
|
| 20 |
+
The above copyright notice and this permission notice shall be included in
|
| 21 |
+
all copies or substantial portions of the Software.
|
| 22 |
+
|
| 23 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 24 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 25 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 26 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 27 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 28 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
| 29 |
+
THE SOFTWARE.
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
[web]: https://larsjung.de/qrcode/
|
| 33 |
+
[github]: https://github.com/lrsjng/jquery-qrcode
|
| 34 |
+
|
| 35 |
+
[license-img]: https://img.shields.io/badge/license-MIT-a0a060.svg?style=flat-square
|
| 36 |
+
[web-img]: https://img.shields.io/badge/web-larsjung.de/qrcode-a0a060.svg?style=flat-square
|
| 37 |
+
[github-img]: https://img.shields.io/badge/github-lrsjng/jquery--qrcode-a0a060.svg?style=flat-square
|
| 38 |
+
[bower-img]: https://img.shields.io/badge/bower-lrsjng/jquery--qrcode-a0a060.svg?style=flat-square
|
| 39 |
+
|
| 40 |
+
[qrcode]: https://github.com/kazuhikoarase/qrcode-generator
|
|
@@ -0,0 +1,2332 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*! jquery-qrcode v0.14.0 - https://larsjung.de/jquery-qrcode/ */
|
| 2 |
+
(function (vendor_qrcode) {
|
| 3 |
+
'use strict';
|
| 4 |
+
|
| 5 |
+
var jq = window.jQuery;
|
| 6 |
+
|
| 7 |
+
// Check if canvas is available in the browser (as Modernizr does)
|
| 8 |
+
var hasCanvas = (function () {
|
| 9 |
+
var elem = document.createElement('canvas');
|
| 10 |
+
return !!(elem.getContext && elem.getContext('2d'));
|
| 11 |
+
}());
|
| 12 |
+
|
| 13 |
+
// Wrapper for the original QR code generator.
|
| 14 |
+
function createQRCode(text, level, version, quiet) {
|
| 15 |
+
var qr = {};
|
| 16 |
+
|
| 17 |
+
var vqr = vendor_qrcode(version, level);
|
| 18 |
+
vqr.addData(text);
|
| 19 |
+
vqr.make();
|
| 20 |
+
|
| 21 |
+
quiet = quiet || 0;
|
| 22 |
+
|
| 23 |
+
var qrModuleCount = vqr.getModuleCount();
|
| 24 |
+
var quietModuleCount = vqr.getModuleCount() + 2 * quiet;
|
| 25 |
+
|
| 26 |
+
function isDark(row, col) {
|
| 27 |
+
row -= quiet;
|
| 28 |
+
col -= quiet;
|
| 29 |
+
|
| 30 |
+
if (row < 0 || row >= qrModuleCount || col < 0 || col >= qrModuleCount) {
|
| 31 |
+
return false;
|
| 32 |
+
}
|
| 33 |
+
return vqr.isDark(row, col);
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
function addBlank(l, t, r, b) {
|
| 37 |
+
var prevIsDark = qr.isDark;
|
| 38 |
+
var moduleSize = 1 / quietModuleCount;
|
| 39 |
+
|
| 40 |
+
qr.isDark = function (row, col) {
|
| 41 |
+
var ml = col * moduleSize;
|
| 42 |
+
var mt = row * moduleSize;
|
| 43 |
+
var mr = ml + moduleSize;
|
| 44 |
+
var mb = mt + moduleSize;
|
| 45 |
+
|
| 46 |
+
return prevIsDark(row, col) && (l > mr || ml > r || t > mb || mt > b);
|
| 47 |
+
};
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
qr.text = text;
|
| 51 |
+
qr.level = level;
|
| 52 |
+
qr.version = version;
|
| 53 |
+
qr.moduleCount = quietModuleCount;
|
| 54 |
+
qr.isDark = isDark;
|
| 55 |
+
qr.addBlank = addBlank;
|
| 56 |
+
|
| 57 |
+
return qr;
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
// Returns a minimal QR code for the given text starting with version `minVersion`.
|
| 61 |
+
// Returns `undefined` if `text` is too long to be encoded in `maxVersion`.
|
| 62 |
+
function createMinQRCode(text, level, minVersion, maxVersion, quiet) {
|
| 63 |
+
minVersion = Math.max(1, minVersion || 1);
|
| 64 |
+
maxVersion = Math.min(40, maxVersion || 40);
|
| 65 |
+
for (var version = minVersion; version <= maxVersion; version += 1) {
|
| 66 |
+
try {
|
| 67 |
+
return createQRCode(text, level, version, quiet);
|
| 68 |
+
} catch (err) {/* empty */}
|
| 69 |
+
}
|
| 70 |
+
return undefined;
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
function drawBackgroundLabel(qr, context, settings) {
|
| 74 |
+
var size = settings.size;
|
| 75 |
+
var font = 'bold ' + settings.mSize * size + 'px ' + settings.fontname;
|
| 76 |
+
var ctx = jq('<canvas/>')[0].getContext('2d');
|
| 77 |
+
|
| 78 |
+
ctx.font = font;
|
| 79 |
+
|
| 80 |
+
var w = ctx.measureText(settings.label).width;
|
| 81 |
+
var sh = settings.mSize;
|
| 82 |
+
var sw = w / size;
|
| 83 |
+
var sl = (1 - sw) * settings.mPosX;
|
| 84 |
+
var st = (1 - sh) * settings.mPosY;
|
| 85 |
+
var sr = sl + sw;
|
| 86 |
+
var sb = st + sh;
|
| 87 |
+
var pad = 0.01;
|
| 88 |
+
|
| 89 |
+
if (settings.mode === 1) {
|
| 90 |
+
// Strip
|
| 91 |
+
qr.addBlank(0, st - pad, size, sb + pad);
|
| 92 |
+
} else {
|
| 93 |
+
// Box
|
| 94 |
+
qr.addBlank(sl - pad, st - pad, sr + pad, sb + pad);
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
context.fillStyle = settings.fontcolor;
|
| 98 |
+
context.font = font;
|
| 99 |
+
context.fillText(settings.label, sl * size, st * size + 0.75 * settings.mSize * size);
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
function drawBackgroundImage(qr, context, settings) {
|
| 103 |
+
var size = settings.size;
|
| 104 |
+
var w = settings.image.naturalWidth || 1;
|
| 105 |
+
var h = settings.image.naturalHeight || 1;
|
| 106 |
+
var sh = settings.mSize;
|
| 107 |
+
var sw = sh * w / h;
|
| 108 |
+
var sl = (1 - sw) * settings.mPosX;
|
| 109 |
+
var st = (1 - sh) * settings.mPosY;
|
| 110 |
+
var sr = sl + sw;
|
| 111 |
+
var sb = st + sh;
|
| 112 |
+
var pad = 0.01;
|
| 113 |
+
|
| 114 |
+
if (settings.mode === 3) {
|
| 115 |
+
// Strip
|
| 116 |
+
qr.addBlank(0, st - pad, size, sb + pad);
|
| 117 |
+
} else {
|
| 118 |
+
// Box
|
| 119 |
+
qr.addBlank(sl - pad, st - pad, sr + pad, sb + pad);
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
context.drawImage(settings.image, sl * size, st * size, sw * size, sh * size);
|
| 123 |
+
}
|
| 124 |
+
|
| 125 |
+
function drawBackground(qr, context, settings) {
|
| 126 |
+
if (jq(settings.background).is('img')) {
|
| 127 |
+
context.drawImage(settings.background, 0, 0, settings.size, settings.size);
|
| 128 |
+
} else if (settings.background) {
|
| 129 |
+
context.fillStyle = settings.background;
|
| 130 |
+
context.fillRect(settings.left, settings.top, settings.size, settings.size);
|
| 131 |
+
}
|
| 132 |
+
|
| 133 |
+
var mode = settings.mode;
|
| 134 |
+
if (mode === 1 || mode === 2) {
|
| 135 |
+
drawBackgroundLabel(qr, context, settings);
|
| 136 |
+
} else if (mode === 3 || mode === 4) {
|
| 137 |
+
drawBackgroundImage(qr, context, settings);
|
| 138 |
+
}
|
| 139 |
+
}
|
| 140 |
+
|
| 141 |
+
function drawModuleDefault(qr, context, settings, left, top, width, row, col) {
|
| 142 |
+
if (qr.isDark(row, col)) {
|
| 143 |
+
context.rect(left, top, width, width);
|
| 144 |
+
}
|
| 145 |
+
}
|
| 146 |
+
|
| 147 |
+
function drawModuleRoundedDark(ctx, l, t, r, b, rad, nw, ne, se, sw) {
|
| 148 |
+
if (nw) {
|
| 149 |
+
ctx.moveTo(l + rad, t);
|
| 150 |
+
} else {
|
| 151 |
+
ctx.moveTo(l, t);
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
if (ne) {
|
| 155 |
+
ctx.lineTo(r - rad, t);
|
| 156 |
+
ctx.arcTo(r, t, r, b, rad);
|
| 157 |
+
} else {
|
| 158 |
+
ctx.lineTo(r, t);
|
| 159 |
+
}
|
| 160 |
+
|
| 161 |
+
if (se) {
|
| 162 |
+
ctx.lineTo(r, b - rad);
|
| 163 |
+
ctx.arcTo(r, b, l, b, rad);
|
| 164 |
+
} else {
|
| 165 |
+
ctx.lineTo(r, b);
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
if (sw) {
|
| 169 |
+
ctx.lineTo(l + rad, b);
|
| 170 |
+
ctx.arcTo(l, b, l, t, rad);
|
| 171 |
+
} else {
|
| 172 |
+
ctx.lineTo(l, b);
|
| 173 |
+
}
|
| 174 |
+
|
| 175 |
+
if (nw) {
|
| 176 |
+
ctx.lineTo(l, t + rad);
|
| 177 |
+
ctx.arcTo(l, t, r, t, rad);
|
| 178 |
+
} else {
|
| 179 |
+
ctx.lineTo(l, t);
|
| 180 |
+
}
|
| 181 |
+
}
|
| 182 |
+
|
| 183 |
+
function drawModuleRoundendLight(ctx, l, t, r, b, rad, nw, ne, se, sw) {
|
| 184 |
+
if (nw) {
|
| 185 |
+
ctx.moveTo(l + rad, t);
|
| 186 |
+
ctx.lineTo(l, t);
|
| 187 |
+
ctx.lineTo(l, t + rad);
|
| 188 |
+
ctx.arcTo(l, t, l + rad, t, rad);
|
| 189 |
+
}
|
| 190 |
+
|
| 191 |
+
if (ne) {
|
| 192 |
+
ctx.moveTo(r - rad, t);
|
| 193 |
+
ctx.lineTo(r, t);
|
| 194 |
+
ctx.lineTo(r, t + rad);
|
| 195 |
+
ctx.arcTo(r, t, r - rad, t, rad);
|
| 196 |
+
}
|
| 197 |
+
|
| 198 |
+
if (se) {
|
| 199 |
+
ctx.moveTo(r - rad, b);
|
| 200 |
+
ctx.lineTo(r, b);
|
| 201 |
+
ctx.lineTo(r, b - rad);
|
| 202 |
+
ctx.arcTo(r, b, r - rad, b, rad);
|
| 203 |
+
}
|
| 204 |
+
|
| 205 |
+
if (sw) {
|
| 206 |
+
ctx.moveTo(l + rad, b);
|
| 207 |
+
ctx.lineTo(l, b);
|
| 208 |
+
ctx.lineTo(l, b - rad);
|
| 209 |
+
ctx.arcTo(l, b, l + rad, b, rad);
|
| 210 |
+
}
|
| 211 |
+
}
|
| 212 |
+
|
| 213 |
+
function drawModuleRounded(qr, context, settings, left, top, width, row, col) {
|
| 214 |
+
var isDark = qr.isDark;
|
| 215 |
+
var right = left + width;
|
| 216 |
+
var bottom = top + width;
|
| 217 |
+
var radius = settings.radius * width;
|
| 218 |
+
var rowT = row - 1;
|
| 219 |
+
var rowB = row + 1;
|
| 220 |
+
var colL = col - 1;
|
| 221 |
+
var colR = col + 1;
|
| 222 |
+
var center = isDark(row, col);
|
| 223 |
+
var northwest = isDark(rowT, colL);
|
| 224 |
+
var north = isDark(rowT, col);
|
| 225 |
+
var northeast = isDark(rowT, colR);
|
| 226 |
+
var east = isDark(row, colR);
|
| 227 |
+
var southeast = isDark(rowB, colR);
|
| 228 |
+
var south = isDark(rowB, col);
|
| 229 |
+
var southwest = isDark(rowB, colL);
|
| 230 |
+
var west = isDark(row, colL);
|
| 231 |
+
|
| 232 |
+
if (center) {
|
| 233 |
+
drawModuleRoundedDark(context, left, top, right, bottom, radius, !north && !west, !north && !east, !south && !east, !south && !west);
|
| 234 |
+
} else {
|
| 235 |
+
drawModuleRoundendLight(context, left, top, right, bottom, radius, north && west && northwest, north && east && northeast, south && east && southeast, south && west && southwest);
|
| 236 |
+
}
|
| 237 |
+
}
|
| 238 |
+
|
| 239 |
+
function drawModules(qr, context, settings) {
|
| 240 |
+
var moduleCount = qr.moduleCount;
|
| 241 |
+
var moduleSize = settings.size / moduleCount;
|
| 242 |
+
var fn = drawModuleDefault;
|
| 243 |
+
var row;
|
| 244 |
+
var col;
|
| 245 |
+
|
| 246 |
+
if (settings.radius > 0 && settings.radius <= 0.5) {
|
| 247 |
+
fn = drawModuleRounded;
|
| 248 |
+
}
|
| 249 |
+
|
| 250 |
+
context.beginPath();
|
| 251 |
+
for (row = 0; row < moduleCount; row += 1) {
|
| 252 |
+
for (col = 0; col < moduleCount; col += 1) {
|
| 253 |
+
var l = settings.left + col * moduleSize;
|
| 254 |
+
var t = settings.top + row * moduleSize;
|
| 255 |
+
var w = moduleSize;
|
| 256 |
+
|
| 257 |
+
fn(qr, context, settings, l, t, w, row, col);
|
| 258 |
+
}
|
| 259 |
+
}
|
| 260 |
+
if (jq(settings.fill).is('img')) {
|
| 261 |
+
context.strokeStyle = 'rgba(0,0,0,0.5)';
|
| 262 |
+
context.lineWidth = 2;
|
| 263 |
+
context.stroke();
|
| 264 |
+
var prev = context.globalCompositeOperation;
|
| 265 |
+
context.globalCompositeOperation = 'destination-out';
|
| 266 |
+
context.fill();
|
| 267 |
+
context.globalCompositeOperation = prev;
|
| 268 |
+
|
| 269 |
+
context.clip();
|
| 270 |
+
context.drawImage(settings.fill, 0, 0, settings.size, settings.size);
|
| 271 |
+
context.restore();
|
| 272 |
+
} else {
|
| 273 |
+
context.fillStyle = settings.fill;
|
| 274 |
+
context.fill();
|
| 275 |
+
}
|
| 276 |
+
}
|
| 277 |
+
|
| 278 |
+
// Draws QR code to the given `canvas` and returns it.
|
| 279 |
+
function drawOnCanvas(canvas, settings) {
|
| 280 |
+
var qr = createMinQRCode(settings.text, settings.ecLevel, settings.minVersion, settings.maxVersion, settings.quiet);
|
| 281 |
+
if (!qr) {
|
| 282 |
+
return null;
|
| 283 |
+
}
|
| 284 |
+
|
| 285 |
+
var $canvas = jq(canvas).data('qrcode', qr);
|
| 286 |
+
var context = $canvas[0].getContext('2d');
|
| 287 |
+
|
| 288 |
+
drawBackground(qr, context, settings);
|
| 289 |
+
drawModules(qr, context, settings);
|
| 290 |
+
|
| 291 |
+
return $canvas;
|
| 292 |
+
}
|
| 293 |
+
|
| 294 |
+
// Returns a `canvas` element representing the QR code for the given settings.
|
| 295 |
+
function createCanvas(settings) {
|
| 296 |
+
var $canvas = jq('<canvas/>').attr('width', settings.size).attr('height', settings.size);
|
| 297 |
+
return drawOnCanvas($canvas, settings);
|
| 298 |
+
}
|
| 299 |
+
|
| 300 |
+
// Returns an `image` element representing the QR code for the given settings.
|
| 301 |
+
function createImage(settings) {
|
| 302 |
+
return jq('<img/>').attr('src', createCanvas(settings)[0].toDataURL('image/png'));
|
| 303 |
+
}
|
| 304 |
+
|
| 305 |
+
// Returns a `div` element representing the QR code for the given settings.
|
| 306 |
+
function createDiv(settings) {
|
| 307 |
+
var qr = createMinQRCode(settings.text, settings.ecLevel, settings.minVersion, settings.maxVersion, settings.quiet);
|
| 308 |
+
if (!qr) {
|
| 309 |
+
return null;
|
| 310 |
+
}
|
| 311 |
+
|
| 312 |
+
// some shortcuts to improve compression
|
| 313 |
+
var settings_size = settings.size;
|
| 314 |
+
var settings_bgColor = settings.background;
|
| 315 |
+
var math_floor = Math.floor;
|
| 316 |
+
|
| 317 |
+
var moduleCount = qr.moduleCount;
|
| 318 |
+
var moduleSize = math_floor(settings_size / moduleCount);
|
| 319 |
+
var offset = math_floor(0.5 * (settings_size - moduleSize * moduleCount));
|
| 320 |
+
|
| 321 |
+
var row;
|
| 322 |
+
var col;
|
| 323 |
+
|
| 324 |
+
var containerCSS = {
|
| 325 |
+
position: 'relative',
|
| 326 |
+
left: 0,
|
| 327 |
+
top: 0,
|
| 328 |
+
padding: 0,
|
| 329 |
+
margin: 0,
|
| 330 |
+
width: settings_size,
|
| 331 |
+
height: settings_size
|
| 332 |
+
};
|
| 333 |
+
var darkCSS = {
|
| 334 |
+
position: 'absolute',
|
| 335 |
+
padding: 0,
|
| 336 |
+
margin: 0,
|
| 337 |
+
width: moduleSize,
|
| 338 |
+
height: moduleSize,
|
| 339 |
+
'background-color': settings.fill
|
| 340 |
+
};
|
| 341 |
+
|
| 342 |
+
var $div = jq('<div/>').data('qrcode', qr).css(containerCSS);
|
| 343 |
+
|
| 344 |
+
if (settings_bgColor) {
|
| 345 |
+
$div.css('background-color', settings_bgColor);
|
| 346 |
+
}
|
| 347 |
+
|
| 348 |
+
for (row = 0; row < moduleCount; row += 1) {
|
| 349 |
+
for (col = 0; col < moduleCount; col += 1) {
|
| 350 |
+
if (qr.isDark(row, col)) {
|
| 351 |
+
jq('<div/>')
|
| 352 |
+
.css(darkCSS)
|
| 353 |
+
.css({
|
| 354 |
+
left: offset + col * moduleSize,
|
| 355 |
+
top: offset + row * moduleSize
|
| 356 |
+
})
|
| 357 |
+
.appendTo($div);
|
| 358 |
+
}
|
| 359 |
+
}
|
| 360 |
+
}
|
| 361 |
+
|
| 362 |
+
return $div;
|
| 363 |
+
}
|
| 364 |
+
|
| 365 |
+
function createHTML(settings) {
|
| 366 |
+
if (hasCanvas && settings.render === 'canvas') {
|
| 367 |
+
return createCanvas(settings);
|
| 368 |
+
} else if (hasCanvas && settings.render === 'image') {
|
| 369 |
+
return createImage(settings);
|
| 370 |
+
}
|
| 371 |
+
|
| 372 |
+
return createDiv(settings);
|
| 373 |
+
}
|
| 374 |
+
|
| 375 |
+
// Plugin
|
| 376 |
+
// ======
|
| 377 |
+
|
| 378 |
+
// Default settings
|
| 379 |
+
// ----------------
|
| 380 |
+
var defaults = {
|
| 381 |
+
// render method: `'canvas'`, `'image'` or `'div'`
|
| 382 |
+
render: 'canvas',
|
| 383 |
+
|
| 384 |
+
// version range somewhere in 1 .. 40
|
| 385 |
+
minVersion: 1,
|
| 386 |
+
maxVersion: 40,
|
| 387 |
+
|
| 388 |
+
// error correction level: `'L'`, `'M'`, `'Q'` or `'H'`
|
| 389 |
+
ecLevel: 'L',
|
| 390 |
+
|
| 391 |
+
// offset in pixel if drawn onto existing canvas
|
| 392 |
+
left: 0,
|
| 393 |
+
top: 0,
|
| 394 |
+
|
| 395 |
+
// size in pixel
|
| 396 |
+
size: 200,
|
| 397 |
+
|
| 398 |
+
// code color or image element
|
| 399 |
+
fill: '#000',
|
| 400 |
+
|
| 401 |
+
// background color or image element, `null` for transparent background
|
| 402 |
+
background: null,
|
| 403 |
+
|
| 404 |
+
// content
|
| 405 |
+
text: 'no text',
|
| 406 |
+
|
| 407 |
+
// corner radius relative to module width: 0.0 .. 0.5
|
| 408 |
+
radius: 0,
|
| 409 |
+
|
| 410 |
+
// quiet zone in modules
|
| 411 |
+
quiet: 0,
|
| 412 |
+
|
| 413 |
+
// modes
|
| 414 |
+
// 0: normal
|
| 415 |
+
// 1: label strip
|
| 416 |
+
// 2: label box
|
| 417 |
+
// 3: image strip
|
| 418 |
+
// 4: image box
|
| 419 |
+
mode: 0,
|
| 420 |
+
|
| 421 |
+
mSize: 0.1,
|
| 422 |
+
mPosX: 0.5,
|
| 423 |
+
mPosY: 0.5,
|
| 424 |
+
|
| 425 |
+
label: 'no label',
|
| 426 |
+
fontname: 'sans',
|
| 427 |
+
fontcolor: '#000',
|
| 428 |
+
|
| 429 |
+
image: null
|
| 430 |
+
};
|
| 431 |
+
|
| 432 |
+
// Register the plugin
|
| 433 |
+
// -------------------
|
| 434 |
+
jq.fn.qrcode = function (options) {
|
| 435 |
+
var settings = jq.extend({}, defaults, options);
|
| 436 |
+
|
| 437 |
+
return this.each(function (idx, el) {
|
| 438 |
+
if (el.nodeName.toLowerCase() === 'canvas') {
|
| 439 |
+
drawOnCanvas(el, settings);
|
| 440 |
+
} else {
|
| 441 |
+
jq(el).append(createHTML(settings));
|
| 442 |
+
}
|
| 443 |
+
});
|
| 444 |
+
};
|
| 445 |
+
}(function () {
|
| 446 |
+
// `qrcode` is the single public function defined by the `QR Code Generator`
|
| 447 |
+
//---------------------------------------------------------------------
|
| 448 |
+
//
|
| 449 |
+
// QR Code Generator for JavaScript
|
| 450 |
+
//
|
| 451 |
+
// Copyright (c) 2009 Kazuhiko Arase
|
| 452 |
+
//
|
| 453 |
+
// URL: http://www.d-project.com/
|
| 454 |
+
//
|
| 455 |
+
// Licensed under the MIT license:
|
| 456 |
+
// http://www.opensource.org/licenses/mit-license.php
|
| 457 |
+
//
|
| 458 |
+
// The word 'QR Code' is registered trademark of
|
| 459 |
+
// DENSO WAVE INCORPORATED
|
| 460 |
+
// http://www.denso-wave.com/qrcode/faqpatent-e.html
|
| 461 |
+
//
|
| 462 |
+
//---------------------------------------------------------------------
|
| 463 |
+
|
| 464 |
+
var qrcode = function() {
|
| 465 |
+
|
| 466 |
+
//---------------------------------------------------------------------
|
| 467 |
+
// qrcode
|
| 468 |
+
//---------------------------------------------------------------------
|
| 469 |
+
|
| 470 |
+
/**
|
| 471 |
+
* qrcode
|
| 472 |
+
* @param typeNumber 1 to 40
|
| 473 |
+
* @param errorCorrectLevel 'L','M','Q','H'
|
| 474 |
+
*/
|
| 475 |
+
var qrcode = function(typeNumber, errorCorrectLevel) {
|
| 476 |
+
|
| 477 |
+
var PAD0 = 0xEC;
|
| 478 |
+
var PAD1 = 0x11;
|
| 479 |
+
|
| 480 |
+
var _typeNumber = typeNumber;
|
| 481 |
+
var _errorCorrectLevel = QRErrorCorrectLevel[errorCorrectLevel];
|
| 482 |
+
var _modules = null;
|
| 483 |
+
var _moduleCount = 0;
|
| 484 |
+
var _dataCache = null;
|
| 485 |
+
var _dataList = new Array();
|
| 486 |
+
|
| 487 |
+
var _this = {};
|
| 488 |
+
|
| 489 |
+
var makeImpl = function(test, maskPattern) {
|
| 490 |
+
|
| 491 |
+
_moduleCount = _typeNumber * 4 + 17;
|
| 492 |
+
_modules = function(moduleCount) {
|
| 493 |
+
var modules = new Array(moduleCount);
|
| 494 |
+
for (var row = 0; row < moduleCount; row += 1) {
|
| 495 |
+
modules[row] = new Array(moduleCount);
|
| 496 |
+
for (var col = 0; col < moduleCount; col += 1) {
|
| 497 |
+
modules[row][col] = null;
|
| 498 |
+
}
|
| 499 |
+
}
|
| 500 |
+
return modules;
|
| 501 |
+
}(_moduleCount);
|
| 502 |
+
|
| 503 |
+
setupPositionProbePattern(0, 0);
|
| 504 |
+
setupPositionProbePattern(_moduleCount - 7, 0);
|
| 505 |
+
setupPositionProbePattern(0, _moduleCount - 7);
|
| 506 |
+
setupPositionAdjustPattern();
|
| 507 |
+
setupTimingPattern();
|
| 508 |
+
setupTypeInfo(test, maskPattern);
|
| 509 |
+
|
| 510 |
+
if (_typeNumber >= 7) {
|
| 511 |
+
setupTypeNumber(test);
|
| 512 |
+
}
|
| 513 |
+
|
| 514 |
+
if (_dataCache == null) {
|
| 515 |
+
_dataCache = createData(_typeNumber, _errorCorrectLevel, _dataList);
|
| 516 |
+
}
|
| 517 |
+
|
| 518 |
+
mapData(_dataCache, maskPattern);
|
| 519 |
+
};
|
| 520 |
+
|
| 521 |
+
var setupPositionProbePattern = function(row, col) {
|
| 522 |
+
|
| 523 |
+
for (var r = -1; r <= 7; r += 1) {
|
| 524 |
+
|
| 525 |
+
if (row + r <= -1 || _moduleCount <= row + r) continue;
|
| 526 |
+
|
| 527 |
+
for (var c = -1; c <= 7; c += 1) {
|
| 528 |
+
|
| 529 |
+
if (col + c <= -1 || _moduleCount <= col + c) continue;
|
| 530 |
+
|
| 531 |
+
if ( (0 <= r && r <= 6 && (c == 0 || c == 6) )
|
| 532 |
+
|| (0 <= c && c <= 6 && (r == 0 || r == 6) )
|
| 533 |
+
|| (2 <= r && r <= 4 && 2 <= c && c <= 4) ) {
|
| 534 |
+
_modules[row + r][col + c] = true;
|
| 535 |
+
} else {
|
| 536 |
+
_modules[row + r][col + c] = false;
|
| 537 |
+
}
|
| 538 |
+
}
|
| 539 |
+
}
|
| 540 |
+
};
|
| 541 |
+
|
| 542 |
+
var getBestMaskPattern = function() {
|
| 543 |
+
|
| 544 |
+
var minLostPoint = 0;
|
| 545 |
+
var pattern = 0;
|
| 546 |
+
|
| 547 |
+
for (var i = 0; i < 8; i += 1) {
|
| 548 |
+
|
| 549 |
+
makeImpl(true, i);
|
| 550 |
+
|
| 551 |
+
var lostPoint = QRUtil.getLostPoint(_this);
|
| 552 |
+
|
| 553 |
+
if (i == 0 || minLostPoint > lostPoint) {
|
| 554 |
+
minLostPoint = lostPoint;
|
| 555 |
+
pattern = i;
|
| 556 |
+
}
|
| 557 |
+
}
|
| 558 |
+
|
| 559 |
+
return pattern;
|
| 560 |
+
};
|
| 561 |
+
|
| 562 |
+
var setupTimingPattern = function() {
|
| 563 |
+
|
| 564 |
+
for (var r = 8; r < _moduleCount - 8; r += 1) {
|
| 565 |
+
if (_modules[r][6] != null) {
|
| 566 |
+
continue;
|
| 567 |
+
}
|
| 568 |
+
_modules[r][6] = (r % 2 == 0);
|
| 569 |
+
}
|
| 570 |
+
|
| 571 |
+
for (var c = 8; c < _moduleCount - 8; c += 1) {
|
| 572 |
+
if (_modules[6][c] != null) {
|
| 573 |
+
continue;
|
| 574 |
+
}
|
| 575 |
+
_modules[6][c] = (c % 2 == 0);
|
| 576 |
+
}
|
| 577 |
+
};
|
| 578 |
+
|
| 579 |
+
var setupPositionAdjustPattern = function() {
|
| 580 |
+
|
| 581 |
+
var pos = QRUtil.getPatternPosition(_typeNumber);
|
| 582 |
+
|
| 583 |
+
for (var i = 0; i < pos.length; i += 1) {
|
| 584 |
+
|
| 585 |
+
for (var j = 0; j < pos.length; j += 1) {
|
| 586 |
+
|
| 587 |
+
var row = pos[i];
|
| 588 |
+
var col = pos[j];
|
| 589 |
+
|
| 590 |
+
if (_modules[row][col] != null) {
|
| 591 |
+
continue;
|
| 592 |
+
}
|
| 593 |
+
|
| 594 |
+
for (var r = -2; r <= 2; r += 1) {
|
| 595 |
+
|
| 596 |
+
for (var c = -2; c <= 2; c += 1) {
|
| 597 |
+
|
| 598 |
+
if (r == -2 || r == 2 || c == -2 || c == 2
|
| 599 |
+
|| (r == 0 && c == 0) ) {
|
| 600 |
+
_modules[row + r][col + c] = true;
|
| 601 |
+
} else {
|
| 602 |
+
_modules[row + r][col + c] = false;
|
| 603 |
+
}
|
| 604 |
+
}
|
| 605 |
+
}
|
| 606 |
+
}
|
| 607 |
+
}
|
| 608 |
+
};
|
| 609 |
+
|
| 610 |
+
var setupTypeNumber = function(test) {
|
| 611 |
+
|
| 612 |
+
var bits = QRUtil.getBCHTypeNumber(_typeNumber);
|
| 613 |
+
|
| 614 |
+
for (var i = 0; i < 18; i += 1) {
|
| 615 |
+
var mod = (!test && ( (bits >> i) & 1) == 1);
|
| 616 |
+
_modules[Math.floor(i / 3)][i % 3 + _moduleCount - 8 - 3] = mod;
|
| 617 |
+
}
|
| 618 |
+
|
| 619 |
+
for (var i = 0; i < 18; i += 1) {
|
| 620 |
+
var mod = (!test && ( (bits >> i) & 1) == 1);
|
| 621 |
+
_modules[i % 3 + _moduleCount - 8 - 3][Math.floor(i / 3)] = mod;
|
| 622 |
+
}
|
| 623 |
+
};
|
| 624 |
+
|
| 625 |
+
var setupTypeInfo = function(test, maskPattern) {
|
| 626 |
+
|
| 627 |
+
var data = (_errorCorrectLevel << 3) | maskPattern;
|
| 628 |
+
var bits = QRUtil.getBCHTypeInfo(data);
|
| 629 |
+
|
| 630 |
+
// vertical
|
| 631 |
+
for (var i = 0; i < 15; i += 1) {
|
| 632 |
+
|
| 633 |
+
var mod = (!test && ( (bits >> i) & 1) == 1);
|
| 634 |
+
|
| 635 |
+
if (i < 6) {
|
| 636 |
+
_modules[i][8] = mod;
|
| 637 |
+
} else if (i < 8) {
|
| 638 |
+
_modules[i + 1][8] = mod;
|
| 639 |
+
} else {
|
| 640 |
+
_modules[_moduleCount - 15 + i][8] = mod;
|
| 641 |
+
}
|
| 642 |
+
}
|
| 643 |
+
|
| 644 |
+
// horizontal
|
| 645 |
+
for (var i = 0; i < 15; i += 1) {
|
| 646 |
+
|
| 647 |
+
var mod = (!test && ( (bits >> i) & 1) == 1);
|
| 648 |
+
|
| 649 |
+
if (i < 8) {
|
| 650 |
+
_modules[8][_moduleCount - i - 1] = mod;
|
| 651 |
+
} else if (i < 9) {
|
| 652 |
+
_modules[8][15 - i - 1 + 1] = mod;
|
| 653 |
+
} else {
|
| 654 |
+
_modules[8][15 - i - 1] = mod;
|
| 655 |
+
}
|
| 656 |
+
}
|
| 657 |
+
|
| 658 |
+
// fixed module
|
| 659 |
+
_modules[_moduleCount - 8][8] = (!test);
|
| 660 |
+
};
|
| 661 |
+
|
| 662 |
+
var mapData = function(data, maskPattern) {
|
| 663 |
+
|
| 664 |
+
var inc = -1;
|
| 665 |
+
var row = _moduleCount - 1;
|
| 666 |
+
var bitIndex = 7;
|
| 667 |
+
var byteIndex = 0;
|
| 668 |
+
var maskFunc = QRUtil.getMaskFunction(maskPattern);
|
| 669 |
+
|
| 670 |
+
for (var col = _moduleCount - 1; col > 0; col -= 2) {
|
| 671 |
+
|
| 672 |
+
if (col == 6) col -= 1;
|
| 673 |
+
|
| 674 |
+
while (true) {
|
| 675 |
+
|
| 676 |
+
for (var c = 0; c < 2; c += 1) {
|
| 677 |
+
|
| 678 |
+
if (_modules[row][col - c] == null) {
|
| 679 |
+
|
| 680 |
+
var dark = false;
|
| 681 |
+
|
| 682 |
+
if (byteIndex < data.length) {
|
| 683 |
+
dark = ( ( (data[byteIndex] >>> bitIndex) & 1) == 1);
|
| 684 |
+
}
|
| 685 |
+
|
| 686 |
+
var mask = maskFunc(row, col - c);
|
| 687 |
+
|
| 688 |
+
if (mask) {
|
| 689 |
+
dark = !dark;
|
| 690 |
+
}
|
| 691 |
+
|
| 692 |
+
_modules[row][col - c] = dark;
|
| 693 |
+
bitIndex -= 1;
|
| 694 |
+
|
| 695 |
+
if (bitIndex == -1) {
|
| 696 |
+
byteIndex += 1;
|
| 697 |
+
bitIndex = 7;
|
| 698 |
+
}
|
| 699 |
+
}
|
| 700 |
+
}
|
| 701 |
+
|
| 702 |
+
row += inc;
|
| 703 |
+
|
| 704 |
+
if (row < 0 || _moduleCount <= row) {
|
| 705 |
+
row -= inc;
|
| 706 |
+
inc = -inc;
|
| 707 |
+
break;
|
| 708 |
+
}
|
| 709 |
+
}
|
| 710 |
+
}
|
| 711 |
+
};
|
| 712 |
+
|
| 713 |
+
var createBytes = function(buffer, rsBlocks) {
|
| 714 |
+
|
| 715 |
+
var offset = 0;
|
| 716 |
+
|
| 717 |
+
var maxDcCount = 0;
|
| 718 |
+
var maxEcCount = 0;
|
| 719 |
+
|
| 720 |
+
var dcdata = new Array(rsBlocks.length);
|
| 721 |
+
var ecdata = new Array(rsBlocks.length);
|
| 722 |
+
|
| 723 |
+
for (var r = 0; r < rsBlocks.length; r += 1) {
|
| 724 |
+
|
| 725 |
+
var dcCount = rsBlocks[r].dataCount;
|
| 726 |
+
var ecCount = rsBlocks[r].totalCount - dcCount;
|
| 727 |
+
|
| 728 |
+
maxDcCount = Math.max(maxDcCount, dcCount);
|
| 729 |
+
maxEcCount = Math.max(maxEcCount, ecCount);
|
| 730 |
+
|
| 731 |
+
dcdata[r] = new Array(dcCount);
|
| 732 |
+
|
| 733 |
+
for (var i = 0; i < dcdata[r].length; i += 1) {
|
| 734 |
+
dcdata[r][i] = 0xff & buffer.getBuffer()[i + offset];
|
| 735 |
+
}
|
| 736 |
+
offset += dcCount;
|
| 737 |
+
|
| 738 |
+
var rsPoly = QRUtil.getErrorCorrectPolynomial(ecCount);
|
| 739 |
+
var rawPoly = qrPolynomial(dcdata[r], rsPoly.getLength() - 1);
|
| 740 |
+
|
| 741 |
+
var modPoly = rawPoly.mod(rsPoly);
|
| 742 |
+
ecdata[r] = new Array(rsPoly.getLength() - 1);
|
| 743 |
+
for (var i = 0; i < ecdata[r].length; i += 1) {
|
| 744 |
+
var modIndex = i + modPoly.getLength() - ecdata[r].length;
|
| 745 |
+
ecdata[r][i] = (modIndex >= 0)? modPoly.getAt(modIndex) : 0;
|
| 746 |
+
}
|
| 747 |
+
}
|
| 748 |
+
|
| 749 |
+
var totalCodeCount = 0;
|
| 750 |
+
for (var i = 0; i < rsBlocks.length; i += 1) {
|
| 751 |
+
totalCodeCount += rsBlocks[i].totalCount;
|
| 752 |
+
}
|
| 753 |
+
|
| 754 |
+
var data = new Array(totalCodeCount);
|
| 755 |
+
var index = 0;
|
| 756 |
+
|
| 757 |
+
for (var i = 0; i < maxDcCount; i += 1) {
|
| 758 |
+
for (var r = 0; r < rsBlocks.length; r += 1) {
|
| 759 |
+
if (i < dcdata[r].length) {
|
| 760 |
+
data[index] = dcdata[r][i];
|
| 761 |
+
index += 1;
|
| 762 |
+
}
|
| 763 |
+
}
|
| 764 |
+
}
|
| 765 |
+
|
| 766 |
+
for (var i = 0; i < maxEcCount; i += 1) {
|
| 767 |
+
for (var r = 0; r < rsBlocks.length; r += 1) {
|
| 768 |
+
if (i < ecdata[r].length) {
|
| 769 |
+
data[index] = ecdata[r][i];
|
| 770 |
+
index += 1;
|
| 771 |
+
}
|
| 772 |
+
}
|
| 773 |
+
}
|
| 774 |
+
|
| 775 |
+
return data;
|
| 776 |
+
};
|
| 777 |
+
|
| 778 |
+
var createData = function(typeNumber, errorCorrectLevel, dataList) {
|
| 779 |
+
|
| 780 |
+
var rsBlocks = QRRSBlock.getRSBlocks(typeNumber, errorCorrectLevel);
|
| 781 |
+
|
| 782 |
+
var buffer = qrBitBuffer();
|
| 783 |
+
|
| 784 |
+
for (var i = 0; i < dataList.length; i += 1) {
|
| 785 |
+
var data = dataList[i];
|
| 786 |
+
buffer.put(data.getMode(), 4);
|
| 787 |
+
buffer.put(data.getLength(), QRUtil.getLengthInBits(data.getMode(), typeNumber) );
|
| 788 |
+
data.write(buffer);
|
| 789 |
+
}
|
| 790 |
+
|
| 791 |
+
// calc num max data.
|
| 792 |
+
var totalDataCount = 0;
|
| 793 |
+
for (var i = 0; i < rsBlocks.length; i += 1) {
|
| 794 |
+
totalDataCount += rsBlocks[i].dataCount;
|
| 795 |
+
}
|
| 796 |
+
|
| 797 |
+
if (buffer.getLengthInBits() > totalDataCount * 8) {
|
| 798 |
+
throw new Error('code length overflow. ('
|
| 799 |
+
+ buffer.getLengthInBits()
|
| 800 |
+
+ '>'
|
| 801 |
+
+ totalDataCount * 8
|
| 802 |
+
+ ')');
|
| 803 |
+
}
|
| 804 |
+
|
| 805 |
+
// end code
|
| 806 |
+
if (buffer.getLengthInBits() + 4 <= totalDataCount * 8) {
|
| 807 |
+
buffer.put(0, 4);
|
| 808 |
+
}
|
| 809 |
+
|
| 810 |
+
// padding
|
| 811 |
+
while (buffer.getLengthInBits() % 8 != 0) {
|
| 812 |
+
buffer.putBit(false);
|
| 813 |
+
}
|
| 814 |
+
|
| 815 |
+
// padding
|
| 816 |
+
while (true) {
|
| 817 |
+
|
| 818 |
+
if (buffer.getLengthInBits() >= totalDataCount * 8) {
|
| 819 |
+
break;
|
| 820 |
+
}
|
| 821 |
+
buffer.put(PAD0, 8);
|
| 822 |
+
|
| 823 |
+
if (buffer.getLengthInBits() >= totalDataCount * 8) {
|
| 824 |
+
break;
|
| 825 |
+
}
|
| 826 |
+
buffer.put(PAD1, 8);
|
| 827 |
+
}
|
| 828 |
+
|
| 829 |
+
return createBytes(buffer, rsBlocks);
|
| 830 |
+
};
|
| 831 |
+
|
| 832 |
+
_this.addData = function(data) {
|
| 833 |
+
var newData = qr8BitByte(data);
|
| 834 |
+
_dataList.push(newData);
|
| 835 |
+
_dataCache = null;
|
| 836 |
+
};
|
| 837 |
+
|
| 838 |
+
_this.isDark = function(row, col) {
|
| 839 |
+
if (row < 0 || _moduleCount <= row || col < 0 || _moduleCount <= col) {
|
| 840 |
+
throw new Error(row + ',' + col);
|
| 841 |
+
}
|
| 842 |
+
return _modules[row][col];
|
| 843 |
+
};
|
| 844 |
+
|
| 845 |
+
_this.getModuleCount = function() {
|
| 846 |
+
return _moduleCount;
|
| 847 |
+
};
|
| 848 |
+
|
| 849 |
+
_this.make = function() {
|
| 850 |
+
makeImpl(false, getBestMaskPattern() );
|
| 851 |
+
};
|
| 852 |
+
|
| 853 |
+
_this.createTableTag = function(cellSize, margin) {
|
| 854 |
+
|
| 855 |
+
cellSize = cellSize || 2;
|
| 856 |
+
margin = (typeof margin == 'undefined')? cellSize * 4 : margin;
|
| 857 |
+
|
| 858 |
+
var qrHtml = '';
|
| 859 |
+
|
| 860 |
+
qrHtml += '<table style="';
|
| 861 |
+
qrHtml += ' border-width: 0px; border-style: none;';
|
| 862 |
+
qrHtml += ' border-collapse: collapse;';
|
| 863 |
+
qrHtml += ' padding: 0px; margin: ' + margin + 'px;';
|
| 864 |
+
qrHtml += '">';
|
| 865 |
+
qrHtml += '<tbody>';
|
| 866 |
+
|
| 867 |
+
for (var r = 0; r < _this.getModuleCount(); r += 1) {
|
| 868 |
+
|
| 869 |
+
qrHtml += '<tr>';
|
| 870 |
+
|
| 871 |
+
for (var c = 0; c < _this.getModuleCount(); c += 1) {
|
| 872 |
+
qrHtml += '<td style="';
|
| 873 |
+
qrHtml += ' border-width: 0px; border-style: none;';
|
| 874 |
+
qrHtml += ' border-collapse: collapse;';
|
| 875 |
+
qrHtml += ' padding: 0px; margin: 0px;';
|
| 876 |
+
qrHtml += ' width: ' + cellSize + 'px;';
|
| 877 |
+
qrHtml += ' height: ' + cellSize + 'px;';
|
| 878 |
+
qrHtml += ' background-color: ';
|
| 879 |
+
qrHtml += _this.isDark(r, c)? '#000000' : '#ffffff';
|
| 880 |
+
qrHtml += ';';
|
| 881 |
+
qrHtml += '"/>';
|
| 882 |
+
}
|
| 883 |
+
|
| 884 |
+
qrHtml += '</tr>';
|
| 885 |
+
}
|
| 886 |
+
|
| 887 |
+
qrHtml += '</tbody>';
|
| 888 |
+
qrHtml += '</table>';
|
| 889 |
+
|
| 890 |
+
return qrHtml;
|
| 891 |
+
};
|
| 892 |
+
|
| 893 |
+
_this.createImgTag = function(cellSize, margin) {
|
| 894 |
+
|
| 895 |
+
cellSize = cellSize || 2;
|
| 896 |
+
margin = (typeof margin == 'undefined')? cellSize * 4 : margin;
|
| 897 |
+
|
| 898 |
+
var size = _this.getModuleCount() * cellSize + margin * 2;
|
| 899 |
+
var min = margin;
|
| 900 |
+
var max = size - margin;
|
| 901 |
+
|
| 902 |
+
return createImgTag(size, size, function(x, y) {
|
| 903 |
+
if (min <= x && x < max && min <= y && y < max) {
|
| 904 |
+
var c = Math.floor( (x - min) / cellSize);
|
| 905 |
+
var r = Math.floor( (y - min) / cellSize);
|
| 906 |
+
return _this.isDark(r, c)? 0 : 1;
|
| 907 |
+
} else {
|
| 908 |
+
return 1;
|
| 909 |
+
}
|
| 910 |
+
} );
|
| 911 |
+
};
|
| 912 |
+
|
| 913 |
+
return _this;
|
| 914 |
+
};
|
| 915 |
+
|
| 916 |
+
//---------------------------------------------------------------------
|
| 917 |
+
// qrcode.stringToBytes
|
| 918 |
+
//---------------------------------------------------------------------
|
| 919 |
+
|
| 920 |
+
qrcode.stringToBytes = function(s) {
|
| 921 |
+
var bytes = new Array();
|
| 922 |
+
for (var i = 0; i < s.length; i += 1) {
|
| 923 |
+
var c = s.charCodeAt(i);
|
| 924 |
+
bytes.push(c & 0xff);
|
| 925 |
+
}
|
| 926 |
+
return bytes;
|
| 927 |
+
};
|
| 928 |
+
|
| 929 |
+
//---------------------------------------------------------------------
|
| 930 |
+
// qrcode.createStringToBytes
|
| 931 |
+
//---------------------------------------------------------------------
|
| 932 |
+
|
| 933 |
+
/**
|
| 934 |
+
* @param unicodeData base64 string of byte array.
|
| 935 |
+
* [16bit Unicode],[16bit Bytes], ...
|
| 936 |
+
* @param numChars
|
| 937 |
+
*/
|
| 938 |
+
qrcode.createStringToBytes = function(unicodeData, numChars) {
|
| 939 |
+
|
| 940 |
+
// create conversion map.
|
| 941 |
+
|
| 942 |
+
var unicodeMap = function() {
|
| 943 |
+
|
| 944 |
+
var bin = base64DecodeInputStream(unicodeData);
|
| 945 |
+
var read = function() {
|
| 946 |
+
var b = bin.read();
|
| 947 |
+
if (b == -1) throw new Error();
|
| 948 |
+
return b;
|
| 949 |
+
};
|
| 950 |
+
|
| 951 |
+
var count = 0;
|
| 952 |
+
var unicodeMap = {};
|
| 953 |
+
while (true) {
|
| 954 |
+
var b0 = bin.read();
|
| 955 |
+
if (b0 == -1) break;
|
| 956 |
+
var b1 = read();
|
| 957 |
+
var b2 = read();
|
| 958 |
+
var b3 = read();
|
| 959 |
+
var k = String.fromCharCode( (b0 << 8) | b1);
|
| 960 |
+
var v = (b2 << 8) | b3;
|
| 961 |
+
unicodeMap[k] = v;
|
| 962 |
+
count += 1;
|
| 963 |
+
}
|
| 964 |
+
if (count != numChars) {
|
| 965 |
+
throw new Error(count + ' != ' + numChars);
|
| 966 |
+
}
|
| 967 |
+
|
| 968 |
+
return unicodeMap;
|
| 969 |
+
}();
|
| 970 |
+
|
| 971 |
+
var unknownChar = '?'.charCodeAt(0);
|
| 972 |
+
|
| 973 |
+
return function(s) {
|
| 974 |
+
var bytes = new Array();
|
| 975 |
+
for (var i = 0; i < s.length; i += 1) {
|
| 976 |
+
var c = s.charCodeAt(i);
|
| 977 |
+
if (c < 128) {
|
| 978 |
+
bytes.push(c);
|
| 979 |
+
} else {
|
| 980 |
+
var b = unicodeMap[s.charAt(i)];
|
| 981 |
+
if (typeof b == 'number') {
|
| 982 |
+
if ( (b & 0xff) == b) {
|
| 983 |
+
// 1byte
|
| 984 |
+
bytes.push(b);
|
| 985 |
+
} else {
|
| 986 |
+
// 2bytes
|
| 987 |
+
bytes.push(b >>> 8);
|
| 988 |
+
bytes.push(b & 0xff);
|
| 989 |
+
}
|
| 990 |
+
} else {
|
| 991 |
+
bytes.push(unknownChar);
|
| 992 |
+
}
|
| 993 |
+
}
|
| 994 |
+
}
|
| 995 |
+
return bytes;
|
| 996 |
+
};
|
| 997 |
+
};
|
| 998 |
+
|
| 999 |
+
//---------------------------------------------------------------------
|
| 1000 |
+
// QRMode
|
| 1001 |
+
//---------------------------------------------------------------------
|
| 1002 |
+
|
| 1003 |
+
var QRMode = {
|
| 1004 |
+
MODE_NUMBER : 1 << 0,
|
| 1005 |
+
MODE_ALPHA_NUM : 1 << 1,
|
| 1006 |
+
MODE_8BIT_BYTE : 1 << 2,
|
| 1007 |
+
MODE_KANJI : 1 << 3
|
| 1008 |
+
};
|
| 1009 |
+
|
| 1010 |
+
//---------------------------------------------------------------------
|
| 1011 |
+
// QRErrorCorrectLevel
|
| 1012 |
+
//---------------------------------------------------------------------
|
| 1013 |
+
|
| 1014 |
+
var QRErrorCorrectLevel = {
|
| 1015 |
+
L : 1,
|
| 1016 |
+
M : 0,
|
| 1017 |
+
Q : 3,
|
| 1018 |
+
H : 2
|
| 1019 |
+
};
|
| 1020 |
+
|
| 1021 |
+
//---------------------------------------------------------------------
|
| 1022 |
+
// QRMaskPattern
|
| 1023 |
+
//---------------------------------------------------------------------
|
| 1024 |
+
|
| 1025 |
+
var QRMaskPattern = {
|
| 1026 |
+
PATTERN000 : 0,
|
| 1027 |
+
PATTERN001 : 1,
|
| 1028 |
+
PATTERN010 : 2,
|
| 1029 |
+
PATTERN011 : 3,
|
| 1030 |
+
PATTERN100 : 4,
|
| 1031 |
+
PATTERN101 : 5,
|
| 1032 |
+
PATTERN110 : 6,
|
| 1033 |
+
PATTERN111 : 7
|
| 1034 |
+
};
|
| 1035 |
+
|
| 1036 |
+
//---------------------------------------------------------------------
|
| 1037 |
+
// QRUtil
|
| 1038 |
+
//---------------------------------------------------------------------
|
| 1039 |
+
|
| 1040 |
+
var QRUtil = function() {
|
| 1041 |
+
|
| 1042 |
+
var PATTERN_POSITION_TABLE = [
|
| 1043 |
+
[],
|
| 1044 |
+
[6, 18],
|
| 1045 |
+
[6, 22],
|
| 1046 |
+
[6, 26],
|
| 1047 |
+
[6, 30],
|
| 1048 |
+
[6, 34],
|
| 1049 |
+
[6, 22, 38],
|
| 1050 |
+
[6, 24, 42],
|
| 1051 |
+
[6, 26, 46],
|
| 1052 |
+
[6, 28, 50],
|
| 1053 |
+
[6, 30, 54],
|
| 1054 |
+
[6, 32, 58],
|
| 1055 |
+
[6, 34, 62],
|
| 1056 |
+
[6, 26, 46, 66],
|
| 1057 |
+
[6, 26, 48, 70],
|
| 1058 |
+
[6, 26, 50, 74],
|
| 1059 |
+
[6, 30, 54, 78],
|
| 1060 |
+
[6, 30, 56, 82],
|
| 1061 |
+
[6, 30, 58, 86],
|
| 1062 |
+
[6, 34, 62, 90],
|
| 1063 |
+
[6, 28, 50, 72, 94],
|
| 1064 |
+
[6, 26, 50, 74, 98],
|
| 1065 |
+
[6, 30, 54, 78, 102],
|
| 1066 |
+
[6, 28, 54, 80, 106],
|
| 1067 |
+
[6, 32, 58, 84, 110],
|
| 1068 |
+
[6, 30, 58, 86, 114],
|
| 1069 |
+
[6, 34, 62, 90, 118],
|
| 1070 |
+
[6, 26, 50, 74, 98, 122],
|
| 1071 |
+
[6, 30, 54, 78, 102, 126],
|
| 1072 |
+
[6, 26, 52, 78, 104, 130],
|
| 1073 |
+
[6, 30, 56, 82, 108, 134],
|
| 1074 |
+
[6, 34, 60, 86, 112, 138],
|
| 1075 |
+
[6, 30, 58, 86, 114, 142],
|
| 1076 |
+
[6, 34, 62, 90, 118, 146],
|
| 1077 |
+
[6, 30, 54, 78, 102, 126, 150],
|
| 1078 |
+
[6, 24, 50, 76, 102, 128, 154],
|
| 1079 |
+
[6, 28, 54, 80, 106, 132, 158],
|
| 1080 |
+
[6, 32, 58, 84, 110, 136, 162],
|
| 1081 |
+
[6, 26, 54, 82, 110, 138, 166],
|
| 1082 |
+
[6, 30, 58, 86, 114, 142, 170]
|
| 1083 |
+
];
|
| 1084 |
+
var G15 = (1 << 10) | (1 << 8) | (1 << 5) | (1 << 4) | (1 << 2) | (1 << 1) | (1 << 0);
|
| 1085 |
+
var G18 = (1 << 12) | (1 << 11) | (1 << 10) | (1 << 9) | (1 << 8) | (1 << 5) | (1 << 2) | (1 << 0);
|
| 1086 |
+
var G15_MASK = (1 << 14) | (1 << 12) | (1 << 10) | (1 << 4) | (1 << 1);
|
| 1087 |
+
|
| 1088 |
+
var _this = {};
|
| 1089 |
+
|
| 1090 |
+
var getBCHDigit = function(data) {
|
| 1091 |
+
var digit = 0;
|
| 1092 |
+
while (data != 0) {
|
| 1093 |
+
digit += 1;
|
| 1094 |
+
data >>>= 1;
|
| 1095 |
+
}
|
| 1096 |
+
return digit;
|
| 1097 |
+
};
|
| 1098 |
+
|
| 1099 |
+
_this.getBCHTypeInfo = function(data) {
|
| 1100 |
+
var d = data << 10;
|
| 1101 |
+
while (getBCHDigit(d) - getBCHDigit(G15) >= 0) {
|
| 1102 |
+
d ^= (G15 << (getBCHDigit(d) - getBCHDigit(G15) ) );
|
| 1103 |
+
}
|
| 1104 |
+
return ( (data << 10) | d) ^ G15_MASK;
|
| 1105 |
+
};
|
| 1106 |
+
|
| 1107 |
+
_this.getBCHTypeNumber = function(data) {
|
| 1108 |
+
var d = data << 12;
|
| 1109 |
+
while (getBCHDigit(d) - getBCHDigit(G18) >= 0) {
|
| 1110 |
+
d ^= (G18 << (getBCHDigit(d) - getBCHDigit(G18) ) );
|
| 1111 |
+
}
|
| 1112 |
+
return (data << 12) | d;
|
| 1113 |
+
};
|
| 1114 |
+
|
| 1115 |
+
_this.getPatternPosition = function(typeNumber) {
|
| 1116 |
+
return PATTERN_POSITION_TABLE[typeNumber - 1];
|
| 1117 |
+
};
|
| 1118 |
+
|
| 1119 |
+
_this.getMaskFunction = function(maskPattern) {
|
| 1120 |
+
|
| 1121 |
+
switch (maskPattern) {
|
| 1122 |
+
|
| 1123 |
+
case QRMaskPattern.PATTERN000 :
|
| 1124 |
+
return function(i, j) { return (i + j) % 2 == 0; };
|
| 1125 |
+
case QRMaskPattern.PATTERN001 :
|
| 1126 |
+
return function(i, j) { return i % 2 == 0; };
|
| 1127 |
+
case QRMaskPattern.PATTERN010 :
|
| 1128 |
+
return function(i, j) { return j % 3 == 0; };
|
| 1129 |
+
case QRMaskPattern.PATTERN011 :
|
| 1130 |
+
return function(i, j) { return (i + j) % 3 == 0; };
|
| 1131 |
+
case QRMaskPattern.PATTERN100 :
|
| 1132 |
+
return function(i, j) { return (Math.floor(i / 2) + Math.floor(j / 3) ) % 2 == 0; };
|
| 1133 |
+
case QRMaskPattern.PATTERN101 :
|
| 1134 |
+
return function(i, j) { return (i * j) % 2 + (i * j) % 3 == 0; };
|
| 1135 |
+
case QRMaskPattern.PATTERN110 :
|
| 1136 |
+
return function(i, j) { return ( (i * j) % 2 + (i * j) % 3) % 2 == 0; };
|
| 1137 |
+
case QRMaskPattern.PATTERN111 :
|
| 1138 |
+
return function(i, j) { return ( (i * j) % 3 + (i + j) % 2) % 2 == 0; };
|
| 1139 |
+
|
| 1140 |
+
default :
|
| 1141 |
+
throw new Error('bad maskPattern:' + maskPattern);
|
| 1142 |
+
}
|
| 1143 |
+
};
|
| 1144 |
+
|
| 1145 |
+
_this.getErrorCorrectPolynomial = function(errorCorrectLength) {
|
| 1146 |
+
var a = qrPolynomial([1], 0);
|
| 1147 |
+
for (var i = 0; i < errorCorrectLength; i += 1) {
|
| 1148 |
+
a = a.multiply(qrPolynomial([1, QRMath.gexp(i)], 0) );
|
| 1149 |
+
}
|
| 1150 |
+
return a;
|
| 1151 |
+
};
|
| 1152 |
+
|
| 1153 |
+
_this.getLengthInBits = function(mode, type) {
|
| 1154 |
+
|
| 1155 |
+
if (1 <= type && type < 10) {
|
| 1156 |
+
|
| 1157 |
+
// 1 - 9
|
| 1158 |
+
|
| 1159 |
+
switch(mode) {
|
| 1160 |
+
case QRMode.MODE_NUMBER : return 10;
|
| 1161 |
+
case QRMode.MODE_ALPHA_NUM : return 9;
|
| 1162 |
+
case QRMode.MODE_8BIT_BYTE : return 8;
|
| 1163 |
+
case QRMode.MODE_KANJI : return 8;
|
| 1164 |
+
default :
|
| 1165 |
+
throw new Error('mode:' + mode);
|
| 1166 |
+
}
|
| 1167 |
+
|
| 1168 |
+
} else if (type < 27) {
|
| 1169 |
+
|
| 1170 |
+
// 10 - 26
|
| 1171 |
+
|
| 1172 |
+
switch(mode) {
|
| 1173 |
+
case QRMode.MODE_NUMBER : return 12;
|
| 1174 |
+
case QRMode.MODE_ALPHA_NUM : return 11;
|
| 1175 |
+
case QRMode.MODE_8BIT_BYTE : return 16;
|
| 1176 |
+
case QRMode.MODE_KANJI : return 10;
|
| 1177 |
+
default :
|
| 1178 |
+
throw new Error('mode:' + mode);
|
| 1179 |
+
}
|
| 1180 |
+
|
| 1181 |
+
} else if (type < 41) {
|
| 1182 |
+
|
| 1183 |
+
// 27 - 40
|
| 1184 |
+
|
| 1185 |
+
switch(mode) {
|
| 1186 |
+
case QRMode.MODE_NUMBER : return 14;
|
| 1187 |
+
case QRMode.MODE_ALPHA_NUM : return 13;
|
| 1188 |
+
case QRMode.MODE_8BIT_BYTE : return 16;
|
| 1189 |
+
case QRMode.MODE_KANJI : return 12;
|
| 1190 |
+
default :
|
| 1191 |
+
throw new Error('mode:' + mode);
|
| 1192 |
+
}
|
| 1193 |
+
|
| 1194 |
+
} else {
|
| 1195 |
+
throw new Error('type:' + type);
|
| 1196 |
+
}
|
| 1197 |
+
};
|
| 1198 |
+
|
| 1199 |
+
_this.getLostPoint = function(qrcode) {
|
| 1200 |
+
|
| 1201 |
+
var moduleCount = qrcode.getModuleCount();
|
| 1202 |
+
|
| 1203 |
+
var lostPoint = 0;
|
| 1204 |
+
|
| 1205 |
+
// LEVEL1
|
| 1206 |
+
|
| 1207 |
+
for (var row = 0; row < moduleCount; row += 1) {
|
| 1208 |
+
for (var col = 0; col < moduleCount; col += 1) {
|
| 1209 |
+
|
| 1210 |
+
var sameCount = 0;
|
| 1211 |
+
var dark = qrcode.isDark(row, col);
|
| 1212 |
+
|
| 1213 |
+
for (var r = -1; r <= 1; r += 1) {
|
| 1214 |
+
|
| 1215 |
+
if (row + r < 0 || moduleCount <= row + r) {
|
| 1216 |
+
continue;
|
| 1217 |
+
}
|
| 1218 |
+
|
| 1219 |
+
for (var c = -1; c <= 1; c += 1) {
|
| 1220 |
+
|
| 1221 |
+
if (col + c < 0 || moduleCount <= col + c) {
|
| 1222 |
+
continue;
|
| 1223 |
+
}
|
| 1224 |
+
|
| 1225 |
+
if (r == 0 && c == 0) {
|
| 1226 |
+
continue;
|
| 1227 |
+
}
|
| 1228 |
+
|
| 1229 |
+
if (dark == qrcode.isDark(row + r, col + c) ) {
|
| 1230 |
+
sameCount += 1;
|
| 1231 |
+
}
|
| 1232 |
+
}
|
| 1233 |
+
}
|
| 1234 |
+
|
| 1235 |
+
if (sameCount > 5) {
|
| 1236 |
+
lostPoint += (3 + sameCount - 5);
|
| 1237 |
+
}
|
| 1238 |
+
}
|
| 1239 |
+
};
|
| 1240 |
+
|
| 1241 |
+
// LEVEL2
|
| 1242 |
+
|
| 1243 |
+
for (var row = 0; row < moduleCount - 1; row += 1) {
|
| 1244 |
+
for (var col = 0; col < moduleCount - 1; col += 1) {
|
| 1245 |
+
var count = 0;
|
| 1246 |
+
if (qrcode.isDark(row, col) ) count += 1;
|
| 1247 |
+
if (qrcode.isDark(row + 1, col) ) count += 1;
|
| 1248 |
+
if (qrcode.isDark(row, col + 1) ) count += 1;
|
| 1249 |
+
if (qrcode.isDark(row + 1, col + 1) ) count += 1;
|
| 1250 |
+
if (count == 0 || count == 4) {
|
| 1251 |
+
lostPoint += 3;
|
| 1252 |
+
}
|
| 1253 |
+
}
|
| 1254 |
+
}
|
| 1255 |
+
|
| 1256 |
+
// LEVEL3
|
| 1257 |
+
|
| 1258 |
+
for (var row = 0; row < moduleCount; row += 1) {
|
| 1259 |
+
for (var col = 0; col < moduleCount - 6; col += 1) {
|
| 1260 |
+
if (qrcode.isDark(row, col)
|
| 1261 |
+
&& !qrcode.isDark(row, col + 1)
|
| 1262 |
+
&& qrcode.isDark(row, col + 2)
|
| 1263 |
+
&& qrcode.isDark(row, col + 3)
|
| 1264 |
+
&& qrcode.isDark(row, col + 4)
|
| 1265 |
+
&& !qrcode.isDark(row, col + 5)
|
| 1266 |
+
&& qrcode.isDark(row, col + 6) ) {
|
| 1267 |
+
lostPoint += 40;
|
| 1268 |
+
}
|
| 1269 |
+
}
|
| 1270 |
+
}
|
| 1271 |
+
|
| 1272 |
+
for (var col = 0; col < moduleCount; col += 1) {
|
| 1273 |
+
for (var row = 0; row < moduleCount - 6; row += 1) {
|
| 1274 |
+
if (qrcode.isDark(row, col)
|
| 1275 |
+
&& !qrcode.isDark(row + 1, col)
|
| 1276 |
+
&& qrcode.isDark(row + 2, col)
|
| 1277 |
+
&& qrcode.isDark(row + 3, col)
|
| 1278 |
+
&& qrcode.isDark(row + 4, col)
|
| 1279 |
+
&& !qrcode.isDark(row + 5, col)
|
| 1280 |
+
&& qrcode.isDark(row + 6, col) ) {
|
| 1281 |
+
lostPoint += 40;
|
| 1282 |
+
}
|
| 1283 |
+
}
|
| 1284 |
+
}
|
| 1285 |
+
|
| 1286 |
+
// LEVEL4
|
| 1287 |
+
|
| 1288 |
+
var darkCount = 0;
|
| 1289 |
+
|
| 1290 |
+
for (var col = 0; col < moduleCount; col += 1) {
|
| 1291 |
+
for (var row = 0; row < moduleCount; row += 1) {
|
| 1292 |
+
if (qrcode.isDark(row, col) ) {
|
| 1293 |
+
darkCount += 1;
|
| 1294 |
+
}
|
| 1295 |
+
}
|
| 1296 |
+
}
|
| 1297 |
+
|
| 1298 |
+
var ratio = Math.abs(100 * darkCount / moduleCount / moduleCount - 50) / 5;
|
| 1299 |
+
lostPoint += ratio * 10;
|
| 1300 |
+
|
| 1301 |
+
return lostPoint;
|
| 1302 |
+
};
|
| 1303 |
+
|
| 1304 |
+
return _this;
|
| 1305 |
+
}();
|
| 1306 |
+
|
| 1307 |
+
//---------------------------------------------------------------------
|
| 1308 |
+
// QRMath
|
| 1309 |
+
//---------------------------------------------------------------------
|
| 1310 |
+
|
| 1311 |
+
var QRMath = function() {
|
| 1312 |
+
|
| 1313 |
+
var EXP_TABLE = new Array(256);
|
| 1314 |
+
var LOG_TABLE = new Array(256);
|
| 1315 |
+
|
| 1316 |
+
// initialize tables
|
| 1317 |
+
for (var i = 0; i < 8; i += 1) {
|
| 1318 |
+
EXP_TABLE[i] = 1 << i;
|
| 1319 |
+
}
|
| 1320 |
+
for (var i = 8; i < 256; i += 1) {
|
| 1321 |
+
EXP_TABLE[i] = EXP_TABLE[i - 4]
|
| 1322 |
+
^ EXP_TABLE[i - 5]
|
| 1323 |
+
^ EXP_TABLE[i - 6]
|
| 1324 |
+
^ EXP_TABLE[i - 8];
|
| 1325 |
+
}
|
| 1326 |
+
for (var i = 0; i < 255; i += 1) {
|
| 1327 |
+
LOG_TABLE[EXP_TABLE[i] ] = i;
|
| 1328 |
+
}
|
| 1329 |
+
|
| 1330 |
+
var _this = {};
|
| 1331 |
+
|
| 1332 |
+
_this.glog = function(n) {
|
| 1333 |
+
|
| 1334 |
+
if (n < 1) {
|
| 1335 |
+
throw new Error('glog(' + n + ')');
|
| 1336 |
+
}
|
| 1337 |
+
|
| 1338 |
+
return LOG_TABLE[n];
|
| 1339 |
+
};
|
| 1340 |
+
|
| 1341 |
+
_this.gexp = function(n) {
|
| 1342 |
+
|
| 1343 |
+
while (n < 0) {
|
| 1344 |
+
n += 255;
|
| 1345 |
+
}
|
| 1346 |
+
|
| 1347 |
+
while (n >= 256) {
|
| 1348 |
+
n -= 255;
|
| 1349 |
+
}
|
| 1350 |
+
|
| 1351 |
+
return EXP_TABLE[n];
|
| 1352 |
+
};
|
| 1353 |
+
|
| 1354 |
+
return _this;
|
| 1355 |
+
}();
|
| 1356 |
+
|
| 1357 |
+
//---------------------------------------------------------------------
|
| 1358 |
+
// qrPolynomial
|
| 1359 |
+
//---------------------------------------------------------------------
|
| 1360 |
+
|
| 1361 |
+
function qrPolynomial(num, shift) {
|
| 1362 |
+
|
| 1363 |
+
if (typeof num.length == 'undefined') {
|
| 1364 |
+
throw new Error(num.length + '/' + shift);
|
| 1365 |
+
}
|
| 1366 |
+
|
| 1367 |
+
var _num = function() {
|
| 1368 |
+
var offset = 0;
|
| 1369 |
+
while (offset < num.length && num[offset] == 0) {
|
| 1370 |
+
offset += 1;
|
| 1371 |
+
}
|
| 1372 |
+
var _num = new Array(num.length - offset + shift);
|
| 1373 |
+
for (var i = 0; i < num.length - offset; i += 1) {
|
| 1374 |
+
_num[i] = num[i + offset];
|
| 1375 |
+
}
|
| 1376 |
+
return _num;
|
| 1377 |
+
}();
|
| 1378 |
+
|
| 1379 |
+
var _this = {};
|
| 1380 |
+
|
| 1381 |
+
_this.getAt = function(index) {
|
| 1382 |
+
return _num[index];
|
| 1383 |
+
};
|
| 1384 |
+
|
| 1385 |
+
_this.getLength = function() {
|
| 1386 |
+
return _num.length;
|
| 1387 |
+
};
|
| 1388 |
+
|
| 1389 |
+
_this.multiply = function(e) {
|
| 1390 |
+
|
| 1391 |
+
var num = new Array(_this.getLength() + e.getLength() - 1);
|
| 1392 |
+
|
| 1393 |
+
for (var i = 0; i < _this.getLength(); i += 1) {
|
| 1394 |
+
for (var j = 0; j < e.getLength(); j += 1) {
|
| 1395 |
+
num[i + j] ^= QRMath.gexp(QRMath.glog(_this.getAt(i) ) + QRMath.glog(e.getAt(j) ) );
|
| 1396 |
+
}
|
| 1397 |
+
}
|
| 1398 |
+
|
| 1399 |
+
return qrPolynomial(num, 0);
|
| 1400 |
+
};
|
| 1401 |
+
|
| 1402 |
+
_this.mod = function(e) {
|
| 1403 |
+
|
| 1404 |
+
if (_this.getLength() - e.getLength() < 0) {
|
| 1405 |
+
return _this;
|
| 1406 |
+
}
|
| 1407 |
+
|
| 1408 |
+
var ratio = QRMath.glog(_this.getAt(0) ) - QRMath.glog(e.getAt(0) );
|
| 1409 |
+
|
| 1410 |
+
var num = new Array(_this.getLength() );
|
| 1411 |
+
for (var i = 0; i < _this.getLength(); i += 1) {
|
| 1412 |
+
num[i] = _this.getAt(i);
|
| 1413 |
+
}
|
| 1414 |
+
|
| 1415 |
+
for (var i = 0; i < e.getLength(); i += 1) {
|
| 1416 |
+
num[i] ^= QRMath.gexp(QRMath.glog(e.getAt(i) ) + ratio);
|
| 1417 |
+
}
|
| 1418 |
+
|
| 1419 |
+
// recursive call
|
| 1420 |
+
return qrPolynomial(num, 0).mod(e);
|
| 1421 |
+
};
|
| 1422 |
+
|
| 1423 |
+
return _this;
|
| 1424 |
+
};
|
| 1425 |
+
|
| 1426 |
+
//---------------------------------------------------------------------
|
| 1427 |
+
// QRRSBlock
|
| 1428 |
+
//---------------------------------------------------------------------
|
| 1429 |
+
|
| 1430 |
+
var QRRSBlock = function() {
|
| 1431 |
+
|
| 1432 |
+
var RS_BLOCK_TABLE = [
|
| 1433 |
+
|
| 1434 |
+
// L
|
| 1435 |
+
// M
|
| 1436 |
+
// Q
|
| 1437 |
+
// H
|
| 1438 |
+
|
| 1439 |
+
// 1
|
| 1440 |
+
[1, 26, 19],
|
| 1441 |
+
[1, 26, 16],
|
| 1442 |
+
[1, 26, 13],
|
| 1443 |
+
[1, 26, 9],
|
| 1444 |
+
|
| 1445 |
+
// 2
|
| 1446 |
+
[1, 44, 34],
|
| 1447 |
+
[1, 44, 28],
|
| 1448 |
+
[1, 44, 22],
|
| 1449 |
+
[1, 44, 16],
|
| 1450 |
+
|
| 1451 |
+
// 3
|
| 1452 |
+
[1, 70, 55],
|
| 1453 |
+
[1, 70, 44],
|
| 1454 |
+
[2, 35, 17],
|
| 1455 |
+
[2, 35, 13],
|
| 1456 |
+
|
| 1457 |
+
// 4
|
| 1458 |
+
[1, 100, 80],
|
| 1459 |
+
[2, 50, 32],
|
| 1460 |
+
[2, 50, 24],
|
| 1461 |
+
[4, 25, 9],
|
| 1462 |
+
|
| 1463 |
+
// 5
|
| 1464 |
+
[1, 134, 108],
|
| 1465 |
+
[2, 67, 43],
|
| 1466 |
+
[2, 33, 15, 2, 34, 16],
|
| 1467 |
+
[2, 33, 11, 2, 34, 12],
|
| 1468 |
+
|
| 1469 |
+
// 6
|
| 1470 |
+
[2, 86, 68],
|
| 1471 |
+
[4, 43, 27],
|
| 1472 |
+
[4, 43, 19],
|
| 1473 |
+
[4, 43, 15],
|
| 1474 |
+
|
| 1475 |
+
// 7
|
| 1476 |
+
[2, 98, 78],
|
| 1477 |
+
[4, 49, 31],
|
| 1478 |
+
[2, 32, 14, 4, 33, 15],
|
| 1479 |
+
[4, 39, 13, 1, 40, 14],
|
| 1480 |
+
|
| 1481 |
+
// 8
|
| 1482 |
+
[2, 121, 97],
|
| 1483 |
+
[2, 60, 38, 2, 61, 39],
|
| 1484 |
+
[4, 40, 18, 2, 41, 19],
|
| 1485 |
+
[4, 40, 14, 2, 41, 15],
|
| 1486 |
+
|
| 1487 |
+
// 9
|
| 1488 |
+
[2, 146, 116],
|
| 1489 |
+
[3, 58, 36, 2, 59, 37],
|
| 1490 |
+
[4, 36, 16, 4, 37, 17],
|
| 1491 |
+
[4, 36, 12, 4, 37, 13],
|
| 1492 |
+
|
| 1493 |
+
// 10
|
| 1494 |
+
[2, 86, 68, 2, 87, 69],
|
| 1495 |
+
[4, 69, 43, 1, 70, 44],
|
| 1496 |
+
[6, 43, 19, 2, 44, 20],
|
| 1497 |
+
[6, 43, 15, 2, 44, 16],
|
| 1498 |
+
|
| 1499 |
+
// 11
|
| 1500 |
+
[4, 101, 81],
|
| 1501 |
+
[1, 80, 50, 4, 81, 51],
|
| 1502 |
+
[4, 50, 22, 4, 51, 23],
|
| 1503 |
+
[3, 36, 12, 8, 37, 13],
|
| 1504 |
+
|
| 1505 |
+
// 12
|
| 1506 |
+
[2, 116, 92, 2, 117, 93],
|
| 1507 |
+
[6, 58, 36, 2, 59, 37],
|
| 1508 |
+
[4, 46, 20, 6, 47, 21],
|
| 1509 |
+
[7, 42, 14, 4, 43, 15],
|
| 1510 |
+
|
| 1511 |
+
// 13
|
| 1512 |
+
[4, 133, 107],
|
| 1513 |
+
[8, 59, 37, 1, 60, 38],
|
| 1514 |
+
[8, 44, 20, 4, 45, 21],
|
| 1515 |
+
[12, 33, 11, 4, 34, 12],
|
| 1516 |
+
|
| 1517 |
+
// 14
|
| 1518 |
+
[3, 145, 115, 1, 146, 116],
|
| 1519 |
+
[4, 64, 40, 5, 65, 41],
|
| 1520 |
+
[11, 36, 16, 5, 37, 17],
|
| 1521 |
+
[11, 36, 12, 5, 37, 13],
|
| 1522 |
+
|
| 1523 |
+
// 15
|
| 1524 |
+
[5, 109, 87, 1, 110, 88],
|
| 1525 |
+
[5, 65, 41, 5, 66, 42],
|
| 1526 |
+
[5, 54, 24, 7, 55, 25],
|
| 1527 |
+
[11, 36, 12, 7, 37, 13],
|
| 1528 |
+
|
| 1529 |
+
// 16
|
| 1530 |
+
[5, 122, 98, 1, 123, 99],
|
| 1531 |
+
[7, 73, 45, 3, 74, 46],
|
| 1532 |
+
[15, 43, 19, 2, 44, 20],
|
| 1533 |
+
[3, 45, 15, 13, 46, 16],
|
| 1534 |
+
|
| 1535 |
+
// 17
|
| 1536 |
+
[1, 135, 107, 5, 136, 108],
|
| 1537 |
+
[10, 74, 46, 1, 75, 47],
|
| 1538 |
+
[1, 50, 22, 15, 51, 23],
|
| 1539 |
+
[2, 42, 14, 17, 43, 15],
|
| 1540 |
+
|
| 1541 |
+
// 18
|
| 1542 |
+
[5, 150, 120, 1, 151, 121],
|
| 1543 |
+
[9, 69, 43, 4, 70, 44],
|
| 1544 |
+
[17, 50, 22, 1, 51, 23],
|
| 1545 |
+
[2, 42, 14, 19, 43, 15],
|
| 1546 |
+
|
| 1547 |
+
// 19
|
| 1548 |
+
[3, 141, 113, 4, 142, 114],
|
| 1549 |
+
[3, 70, 44, 11, 71, 45],
|
| 1550 |
+
[17, 47, 21, 4, 48, 22],
|
| 1551 |
+
[9, 39, 13, 16, 40, 14],
|
| 1552 |
+
|
| 1553 |
+
// 20
|
| 1554 |
+
[3, 135, 107, 5, 136, 108],
|
| 1555 |
+
[3, 67, 41, 13, 68, 42],
|
| 1556 |
+
[15, 54, 24, 5, 55, 25],
|
| 1557 |
+
[15, 43, 15, 10, 44, 16],
|
| 1558 |
+
|
| 1559 |
+
// 21
|
| 1560 |
+
[4, 144, 116, 4, 145, 117],
|
| 1561 |
+
[17, 68, 42],
|
| 1562 |
+
[17, 50, 22, 6, 51, 23],
|
| 1563 |
+
[19, 46, 16, 6, 47, 17],
|
| 1564 |
+
|
| 1565 |
+
// 22
|
| 1566 |
+
[2, 139, 111, 7, 140, 112],
|
| 1567 |
+
[17, 74, 46],
|
| 1568 |
+
[7, 54, 24, 16, 55, 25],
|
| 1569 |
+
[34, 37, 13],
|
| 1570 |
+
|
| 1571 |
+
// 23
|
| 1572 |
+
[4, 151, 121, 5, 152, 122],
|
| 1573 |
+
[4, 75, 47, 14, 76, 48],
|
| 1574 |
+
[11, 54, 24, 14, 55, 25],
|
| 1575 |
+
[16, 45, 15, 14, 46, 16],
|
| 1576 |
+
|
| 1577 |
+
// 24
|
| 1578 |
+
[6, 147, 117, 4, 148, 118],
|
| 1579 |
+
[6, 73, 45, 14, 74, 46],
|
| 1580 |
+
[11, 54, 24, 16, 55, 25],
|
| 1581 |
+
[30, 46, 16, 2, 47, 17],
|
| 1582 |
+
|
| 1583 |
+
// 25
|
| 1584 |
+
[8, 132, 106, 4, 133, 107],
|
| 1585 |
+
[8, 75, 47, 13, 76, 48],
|
| 1586 |
+
[7, 54, 24, 22, 55, 25],
|
| 1587 |
+
[22, 45, 15, 13, 46, 16],
|
| 1588 |
+
|
| 1589 |
+
// 26
|
| 1590 |
+
[10, 142, 114, 2, 143, 115],
|
| 1591 |
+
[19, 74, 46, 4, 75, 47],
|
| 1592 |
+
[28, 50, 22, 6, 51, 23],
|
| 1593 |
+
[33, 46, 16, 4, 47, 17],
|
| 1594 |
+
|
| 1595 |
+
// 27
|
| 1596 |
+
[8, 152, 122, 4, 153, 123],
|
| 1597 |
+
[22, 73, 45, 3, 74, 46],
|
| 1598 |
+
[8, 53, 23, 26, 54, 24],
|
| 1599 |
+
[12, 45, 15, 28, 46, 16],
|
| 1600 |
+
|
| 1601 |
+
// 28
|
| 1602 |
+
[3, 147, 117, 10, 148, 118],
|
| 1603 |
+
[3, 73, 45, 23, 74, 46],
|
| 1604 |
+
[4, 54, 24, 31, 55, 25],
|
| 1605 |
+
[11, 45, 15, 31, 46, 16],
|
| 1606 |
+
|
| 1607 |
+
// 29
|
| 1608 |
+
[7, 146, 116, 7, 147, 117],
|
| 1609 |
+
[21, 73, 45, 7, 74, 46],
|
| 1610 |
+
[1, 53, 23, 37, 54, 24],
|
| 1611 |
+
[19, 45, 15, 26, 46, 16],
|
| 1612 |
+
|
| 1613 |
+
// 30
|
| 1614 |
+
[5, 145, 115, 10, 146, 116],
|
| 1615 |
+
[19, 75, 47, 10, 76, 48],
|
| 1616 |
+
[15, 54, 24, 25, 55, 25],
|
| 1617 |
+
[23, 45, 15, 25, 46, 16],
|
| 1618 |
+
|
| 1619 |
+
// 31
|
| 1620 |
+
[13, 145, 115, 3, 146, 116],
|
| 1621 |
+
[2, 74, 46, 29, 75, 47],
|
| 1622 |
+
[42, 54, 24, 1, 55, 25],
|
| 1623 |
+
[23, 45, 15, 28, 46, 16],
|
| 1624 |
+
|
| 1625 |
+
// 32
|
| 1626 |
+
[17, 145, 115],
|
| 1627 |
+
[10, 74, 46, 23, 75, 47],
|
| 1628 |
+
[10, 54, 24, 35, 55, 25],
|
| 1629 |
+
[19, 45, 15, 35, 46, 16],
|
| 1630 |
+
|
| 1631 |
+
// 33
|
| 1632 |
+
[17, 145, 115, 1, 146, 116],
|
| 1633 |
+
[14, 74, 46, 21, 75, 47],
|
| 1634 |
+
[29, 54, 24, 19, 55, 25],
|
| 1635 |
+
[11, 45, 15, 46, 46, 16],
|
| 1636 |
+
|
| 1637 |
+
// 34
|
| 1638 |
+
[13, 145, 115, 6, 146, 116],
|
| 1639 |
+
[14, 74, 46, 23, 75, 47],
|
| 1640 |
+
[44, 54, 24, 7, 55, 25],
|
| 1641 |
+
[59, 46, 16, 1, 47, 17],
|
| 1642 |
+
|
| 1643 |
+
// 35
|
| 1644 |
+
[12, 151, 121, 7, 152, 122],
|
| 1645 |
+
[12, 75, 47, 26, 76, 48],
|
| 1646 |
+
[39, 54, 24, 14, 55, 25],
|
| 1647 |
+
[22, 45, 15, 41, 46, 16],
|
| 1648 |
+
|
| 1649 |
+
// 36
|
| 1650 |
+
[6, 151, 121, 14, 152, 122],
|
| 1651 |
+
[6, 75, 47, 34, 76, 48],
|
| 1652 |
+
[46, 54, 24, 10, 55, 25],
|
| 1653 |
+
[2, 45, 15, 64, 46, 16],
|
| 1654 |
+
|
| 1655 |
+
// 37
|
| 1656 |
+
[17, 152, 122, 4, 153, 123],
|
| 1657 |
+
[29, 74, 46, 14, 75, 47],
|
| 1658 |
+
[49, 54, 24, 10, 55, 25],
|
| 1659 |
+
[24, 45, 15, 46, 46, 16],
|
| 1660 |
+
|
| 1661 |
+
// 38
|
| 1662 |
+
[4, 152, 122, 18, 153, 123],
|
| 1663 |
+
[13, 74, 46, 32, 75, 47],
|
| 1664 |
+
[48, 54, 24, 14, 55, 25],
|
| 1665 |
+
[42, 45, 15, 32, 46, 16],
|
| 1666 |
+
|
| 1667 |
+
// 39
|
| 1668 |
+
[20, 147, 117, 4, 148, 118],
|
| 1669 |
+
[40, 75, 47, 7, 76, 48],
|
| 1670 |
+
[43, 54, 24, 22, 55, 25],
|
| 1671 |
+
[10, 45, 15, 67, 46, 16],
|
| 1672 |
+
|
| 1673 |
+
// 40
|
| 1674 |
+
[19, 148, 118, 6, 149, 119],
|
| 1675 |
+
[18, 75, 47, 31, 76, 48],
|
| 1676 |
+
[34, 54, 24, 34, 55, 25],
|
| 1677 |
+
[20, 45, 15, 61, 46, 16]
|
| 1678 |
+
];
|
| 1679 |
+
|
| 1680 |
+
var qrRSBlock = function(totalCount, dataCount) {
|
| 1681 |
+
var _this = {};
|
| 1682 |
+
_this.totalCount = totalCount;
|
| 1683 |
+
_this.dataCount = dataCount;
|
| 1684 |
+
return _this;
|
| 1685 |
+
};
|
| 1686 |
+
|
| 1687 |
+
var _this = {};
|
| 1688 |
+
|
| 1689 |
+
var getRsBlockTable = function(typeNumber, errorCorrectLevel) {
|
| 1690 |
+
|
| 1691 |
+
switch(errorCorrectLevel) {
|
| 1692 |
+
case QRErrorCorrectLevel.L :
|
| 1693 |
+
return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 0];
|
| 1694 |
+
case QRErrorCorrectLevel.M :
|
| 1695 |
+
return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 1];
|
| 1696 |
+
case QRErrorCorrectLevel.Q :
|
| 1697 |
+
return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 2];
|
| 1698 |
+
case QRErrorCorrectLevel.H :
|
| 1699 |
+
return RS_BLOCK_TABLE[(typeNumber - 1) * 4 + 3];
|
| 1700 |
+
default :
|
| 1701 |
+
return undefined;
|
| 1702 |
+
}
|
| 1703 |
+
};
|
| 1704 |
+
|
| 1705 |
+
_this.getRSBlocks = function(typeNumber, errorCorrectLevel) {
|
| 1706 |
+
|
| 1707 |
+
var rsBlock = getRsBlockTable(typeNumber, errorCorrectLevel);
|
| 1708 |
+
|
| 1709 |
+
if (typeof rsBlock == 'undefined') {
|
| 1710 |
+
throw new Error('bad rs block @ typeNumber:' + typeNumber +
|
| 1711 |
+
'/errorCorrectLevel:' + errorCorrectLevel);
|
| 1712 |
+
}
|
| 1713 |
+
|
| 1714 |
+
var length = rsBlock.length / 3;
|
| 1715 |
+
|
| 1716 |
+
var list = new Array();
|
| 1717 |
+
|
| 1718 |
+
for (var i = 0; i < length; i += 1) {
|
| 1719 |
+
|
| 1720 |
+
var count = rsBlock[i * 3 + 0];
|
| 1721 |
+
var totalCount = rsBlock[i * 3 + 1];
|
| 1722 |
+
var dataCount = rsBlock[i * 3 + 2];
|
| 1723 |
+
|
| 1724 |
+
for (var j = 0; j < count; j += 1) {
|
| 1725 |
+
list.push(qrRSBlock(totalCount, dataCount) );
|
| 1726 |
+
}
|
| 1727 |
+
}
|
| 1728 |
+
|
| 1729 |
+
return list;
|
| 1730 |
+
};
|
| 1731 |
+
|
| 1732 |
+
return _this;
|
| 1733 |
+
}();
|
| 1734 |
+
|
| 1735 |
+
//---------------------------------------------------------------------
|
| 1736 |
+
// qrBitBuffer
|
| 1737 |
+
//---------------------------------------------------------------------
|
| 1738 |
+
|
| 1739 |
+
var qrBitBuffer = function() {
|
| 1740 |
+
|
| 1741 |
+
var _buffer = new Array();
|
| 1742 |
+
var _length = 0;
|
| 1743 |
+
|
| 1744 |
+
var _this = {};
|
| 1745 |
+
|
| 1746 |
+
_this.getBuffer = function() {
|
| 1747 |
+
return _buffer;
|
| 1748 |
+
};
|
| 1749 |
+
|
| 1750 |
+
_this.getAt = function(index) {
|
| 1751 |
+
var bufIndex = Math.floor(index / 8);
|
| 1752 |
+
return ( (_buffer[bufIndex] >>> (7 - index % 8) ) & 1) == 1;
|
| 1753 |
+
};
|
| 1754 |
+
|
| 1755 |
+
_this.put = function(num, length) {
|
| 1756 |
+
for (var i = 0; i < length; i += 1) {
|
| 1757 |
+
_this.putBit( ( (num >>> (length - i - 1) ) & 1) == 1);
|
| 1758 |
+
}
|
| 1759 |
+
};
|
| 1760 |
+
|
| 1761 |
+
_this.getLengthInBits = function() {
|
| 1762 |
+
return _length;
|
| 1763 |
+
};
|
| 1764 |
+
|
| 1765 |
+
_this.putBit = function(bit) {
|
| 1766 |
+
|
| 1767 |
+
var bufIndex = Math.floor(_length / 8);
|
| 1768 |
+
if (_buffer.length <= bufIndex) {
|
| 1769 |
+
_buffer.push(0);
|
| 1770 |
+
}
|
| 1771 |
+
|
| 1772 |
+
if (bit) {
|
| 1773 |
+
_buffer[bufIndex] |= (0x80 >>> (_length % 8) );
|
| 1774 |
+
}
|
| 1775 |
+
|
| 1776 |
+
_length += 1;
|
| 1777 |
+
};
|
| 1778 |
+
|
| 1779 |
+
return _this;
|
| 1780 |
+
};
|
| 1781 |
+
|
| 1782 |
+
//---------------------------------------------------------------------
|
| 1783 |
+
// qr8BitByte
|
| 1784 |
+
//---------------------------------------------------------------------
|
| 1785 |
+
|
| 1786 |
+
var qr8BitByte = function(data) {
|
| 1787 |
+
|
| 1788 |
+
var _mode = QRMode.MODE_8BIT_BYTE;
|
| 1789 |
+
var _data = data;
|
| 1790 |
+
var _bytes = qrcode.stringToBytes(data);
|
| 1791 |
+
|
| 1792 |
+
var _this = {};
|
| 1793 |
+
|
| 1794 |
+
_this.getMode = function() {
|
| 1795 |
+
return _mode;
|
| 1796 |
+
};
|
| 1797 |
+
|
| 1798 |
+
_this.getLength = function(buffer) {
|
| 1799 |
+
return _bytes.length;
|
| 1800 |
+
};
|
| 1801 |
+
|
| 1802 |
+
_this.write = function(buffer) {
|
| 1803 |
+
for (var i = 0; i < _bytes.length; i += 1) {
|
| 1804 |
+
buffer.put(_bytes[i], 8);
|
| 1805 |
+
}
|
| 1806 |
+
};
|
| 1807 |
+
|
| 1808 |
+
return _this;
|
| 1809 |
+
};
|
| 1810 |
+
|
| 1811 |
+
//=====================================================================
|
| 1812 |
+
// GIF Support etc.
|
| 1813 |
+
//
|
| 1814 |
+
|
| 1815 |
+
//---------------------------------------------------------------------
|
| 1816 |
+
// byteArrayOutputStream
|
| 1817 |
+
//---------------------------------------------------------------------
|
| 1818 |
+
|
| 1819 |
+
var byteArrayOutputStream = function() {
|
| 1820 |
+
|
| 1821 |
+
var _bytes = new Array();
|
| 1822 |
+
|
| 1823 |
+
var _this = {};
|
| 1824 |
+
|
| 1825 |
+
_this.writeByte = function(b) {
|
| 1826 |
+
_bytes.push(b & 0xff);
|
| 1827 |
+
};
|
| 1828 |
+
|
| 1829 |
+
_this.writeShort = function(i) {
|
| 1830 |
+
_this.writeByte(i);
|
| 1831 |
+
_this.writeByte(i >>> 8);
|
| 1832 |
+
};
|
| 1833 |
+
|
| 1834 |
+
_this.writeBytes = function(b, off, len) {
|
| 1835 |
+
off = off || 0;
|
| 1836 |
+
len = len || b.length;
|
| 1837 |
+
for (var i = 0; i < len; i += 1) {
|
| 1838 |
+
_this.writeByte(b[i + off]);
|
| 1839 |
+
}
|
| 1840 |
+
};
|
| 1841 |
+
|
| 1842 |
+
_this.writeString = function(s) {
|
| 1843 |
+
for (var i = 0; i < s.length; i += 1) {
|
| 1844 |
+
_this.writeByte(s.charCodeAt(i) );
|
| 1845 |
+
}
|
| 1846 |
+
};
|
| 1847 |
+
|
| 1848 |
+
_this.toByteArray = function() {
|
| 1849 |
+
return _bytes;
|
| 1850 |
+
};
|
| 1851 |
+
|
| 1852 |
+
_this.toString = function() {
|
| 1853 |
+
var s = '';
|
| 1854 |
+
s += '[';
|
| 1855 |
+
for (var i = 0; i < _bytes.length; i += 1) {
|
| 1856 |
+
if (i > 0) {
|
| 1857 |
+
s += ',';
|
| 1858 |
+
}
|
| 1859 |
+
s += _bytes[i];
|
| 1860 |
+
}
|
| 1861 |
+
s += ']';
|
| 1862 |
+
return s;
|
| 1863 |
+
};
|
| 1864 |
+
|
| 1865 |
+
return _this;
|
| 1866 |
+
};
|
| 1867 |
+
|
| 1868 |
+
//---------------------------------------------------------------------
|
| 1869 |
+
// base64EncodeOutputStream
|
| 1870 |
+
//---------------------------------------------------------------------
|
| 1871 |
+
|
| 1872 |
+
var base64EncodeOutputStream = function() {
|
| 1873 |
+
|
| 1874 |
+
var _buffer = 0;
|
| 1875 |
+
var _buflen = 0;
|
| 1876 |
+
var _length = 0;
|
| 1877 |
+
var _base64 = '';
|
| 1878 |
+
|
| 1879 |
+
var _this = {};
|
| 1880 |
+
|
| 1881 |
+
var writeEncoded = function(b) {
|
| 1882 |
+
_base64 += String.fromCharCode(encode(b & 0x3f) );
|
| 1883 |
+
};
|
| 1884 |
+
|
| 1885 |
+
var encode = function(n) {
|
| 1886 |
+
if (n < 0) {
|
| 1887 |
+
// error.
|
| 1888 |
+
} else if (n < 26) {
|
| 1889 |
+
return 0x41 + n;
|
| 1890 |
+
} else if (n < 52) {
|
| 1891 |
+
return 0x61 + (n - 26);
|
| 1892 |
+
} else if (n < 62) {
|
| 1893 |
+
return 0x30 + (n - 52);
|
| 1894 |
+
} else if (n == 62) {
|
| 1895 |
+
return 0x2b;
|
| 1896 |
+
} else if (n == 63) {
|
| 1897 |
+
return 0x2f;
|
| 1898 |
+
}
|
| 1899 |
+
throw new Error('n:' + n);
|
| 1900 |
+
};
|
| 1901 |
+
|
| 1902 |
+
_this.writeByte = function(n) {
|
| 1903 |
+
|
| 1904 |
+
_buffer = (_buffer << 8) | (n & 0xff);
|
| 1905 |
+
_buflen += 8;
|
| 1906 |
+
_length += 1;
|
| 1907 |
+
|
| 1908 |
+
while (_buflen >= 6) {
|
| 1909 |
+
writeEncoded(_buffer >>> (_buflen - 6) );
|
| 1910 |
+
_buflen -= 6;
|
| 1911 |
+
}
|
| 1912 |
+
};
|
| 1913 |
+
|
| 1914 |
+
_this.flush = function() {
|
| 1915 |
+
|
| 1916 |
+
if (_buflen > 0) {
|
| 1917 |
+
writeEncoded(_buffer << (6 - _buflen) );
|
| 1918 |
+
_buffer = 0;
|
| 1919 |
+
_buflen = 0;
|
| 1920 |
+
}
|
| 1921 |
+
|
| 1922 |
+
if (_length % 3 != 0) {
|
| 1923 |
+
// padding
|
| 1924 |
+
var padlen = 3 - _length % 3;
|
| 1925 |
+
for (var i = 0; i < padlen; i += 1) {
|
| 1926 |
+
_base64 += '=';
|
| 1927 |
+
}
|
| 1928 |
+
}
|
| 1929 |
+
};
|
| 1930 |
+
|
| 1931 |
+
_this.toString = function() {
|
| 1932 |
+
return _base64;
|
| 1933 |
+
};
|
| 1934 |
+
|
| 1935 |
+
return _this;
|
| 1936 |
+
};
|
| 1937 |
+
|
| 1938 |
+
//---------------------------------------------------------------------
|
| 1939 |
+
// base64DecodeInputStream
|
| 1940 |
+
//---------------------------------------------------------------------
|
| 1941 |
+
|
| 1942 |
+
var base64DecodeInputStream = function(str) {
|
| 1943 |
+
|
| 1944 |
+
var _str = str;
|
| 1945 |
+
var _pos = 0;
|
| 1946 |
+
var _buffer = 0;
|
| 1947 |
+
var _buflen = 0;
|
| 1948 |
+
|
| 1949 |
+
var _this = {};
|
| 1950 |
+
|
| 1951 |
+
_this.read = function() {
|
| 1952 |
+
|
| 1953 |
+
while (_buflen < 8) {
|
| 1954 |
+
|
| 1955 |
+
if (_pos >= _str.length) {
|
| 1956 |
+
if (_buflen == 0) {
|
| 1957 |
+
return -1;
|
| 1958 |
+
}
|
| 1959 |
+
throw new Error('unexpected end of file./' + _buflen);
|
| 1960 |
+
}
|
| 1961 |
+
|
| 1962 |
+
var c = _str.charAt(_pos);
|
| 1963 |
+
_pos += 1;
|
| 1964 |
+
|
| 1965 |
+
if (c == '=') {
|
| 1966 |
+
_buflen = 0;
|
| 1967 |
+
return -1;
|
| 1968 |
+
} else if (c.match(/^\s$/) ) {
|
| 1969 |
+
// ignore if whitespace.
|
| 1970 |
+
continue;
|
| 1971 |
+
}
|
| 1972 |
+
|
| 1973 |
+
_buffer = (_buffer << 6) | decode(c.charCodeAt(0) );
|
| 1974 |
+
_buflen += 6;
|
| 1975 |
+
}
|
| 1976 |
+
|
| 1977 |
+
var n = (_buffer >>> (_buflen - 8) ) & 0xff;
|
| 1978 |
+
_buflen -= 8;
|
| 1979 |
+
return n;
|
| 1980 |
+
};
|
| 1981 |
+
|
| 1982 |
+
var decode = function(c) {
|
| 1983 |
+
if (0x41 <= c && c <= 0x5a) {
|
| 1984 |
+
return c - 0x41;
|
| 1985 |
+
} else if (0x61 <= c && c <= 0x7a) {
|
| 1986 |
+
return c - 0x61 + 26;
|
| 1987 |
+
} else if (0x30 <= c && c <= 0x39) {
|
| 1988 |
+
return c - 0x30 + 52;
|
| 1989 |
+
} else if (c == 0x2b) {
|
| 1990 |
+
return 62;
|
| 1991 |
+
} else if (c == 0x2f) {
|
| 1992 |
+
return 63;
|
| 1993 |
+
} else {
|
| 1994 |
+
throw new Error('c:' + c);
|
| 1995 |
+
}
|
| 1996 |
+
};
|
| 1997 |
+
|
| 1998 |
+
return _this;
|
| 1999 |
+
};
|
| 2000 |
+
|
| 2001 |
+
//---------------------------------------------------------------------
|
| 2002 |
+
// gifImage (B/W)
|
| 2003 |
+
//---------------------------------------------------------------------
|
| 2004 |
+
|
| 2005 |
+
var gifImage = function(width, height) {
|
| 2006 |
+
|
| 2007 |
+
var _width = width;
|
| 2008 |
+
var _height = height;
|
| 2009 |
+
var _data = new Array(width * height);
|
| 2010 |
+
|
| 2011 |
+
var _this = {};
|
| 2012 |
+
|
| 2013 |
+
_this.setPixel = function(x, y, pixel) {
|
| 2014 |
+
_data[y * _width + x] = pixel;
|
| 2015 |
+
};
|
| 2016 |
+
|
| 2017 |
+
_this.write = function(out) {
|
| 2018 |
+
|
| 2019 |
+
//---------------------------------
|
| 2020 |
+
// GIF Signature
|
| 2021 |
+
|
| 2022 |
+
out.writeString('GIF87a');
|
| 2023 |
+
|
| 2024 |
+
//---------------------------------
|
| 2025 |
+
// Screen Descriptor
|
| 2026 |
+
|
| 2027 |
+
out.writeShort(_width);
|
| 2028 |
+
out.writeShort(_height);
|
| 2029 |
+
|
| 2030 |
+
out.writeByte(0x80); // 2bit
|
| 2031 |
+
out.writeByte(0);
|
| 2032 |
+
out.writeByte(0);
|
| 2033 |
+
|
| 2034 |
+
//---------------------------------
|
| 2035 |
+
// Global Color Map
|
| 2036 |
+
|
| 2037 |
+
// black
|
| 2038 |
+
out.writeByte(0x00);
|
| 2039 |
+
out.writeByte(0x00);
|
| 2040 |
+
out.writeByte(0x00);
|
| 2041 |
+
|
| 2042 |
+
// white
|
| 2043 |
+
out.writeByte(0xff);
|
| 2044 |
+
out.writeByte(0xff);
|
| 2045 |
+
out.writeByte(0xff);
|
| 2046 |
+
|
| 2047 |
+
//---------------------------------
|
| 2048 |
+
// Image Descriptor
|
| 2049 |
+
|
| 2050 |
+
out.writeString(',');
|
| 2051 |
+
out.writeShort(0);
|
| 2052 |
+
out.writeShort(0);
|
| 2053 |
+
out.writeShort(_width);
|
| 2054 |
+
out.writeShort(_height);
|
| 2055 |
+
out.writeByte(0);
|
| 2056 |
+
|
| 2057 |
+
//---------------------------------
|
| 2058 |
+
// Local Color Map
|
| 2059 |
+
|
| 2060 |
+
//---------------------------------
|
| 2061 |
+
// Raster Data
|
| 2062 |
+
|
| 2063 |
+
var lzwMinCodeSize = 2;
|
| 2064 |
+
var raster = getLZWRaster(lzwMinCodeSize);
|
| 2065 |
+
|
| 2066 |
+
out.writeByte(lzwMinCodeSize);
|
| 2067 |
+
|
| 2068 |
+
var offset = 0;
|
| 2069 |
+
|
| 2070 |
+
while (raster.length - offset > 255) {
|
| 2071 |
+
out.writeByte(255);
|
| 2072 |
+
out.writeBytes(raster, offset, 255);
|
| 2073 |
+
offset += 255;
|
| 2074 |
+
}
|
| 2075 |
+
|
| 2076 |
+
out.writeByte(raster.length - offset);
|
| 2077 |
+
out.writeBytes(raster, offset, raster.length - offset);
|
| 2078 |
+
out.writeByte(0x00);
|
| 2079 |
+
|
| 2080 |
+
//---------------------------------
|
| 2081 |
+
// GIF Terminator
|
| 2082 |
+
out.writeString(';');
|
| 2083 |
+
};
|
| 2084 |
+
|
| 2085 |
+
var bitOutputStream = function(out) {
|
| 2086 |
+
|
| 2087 |
+
var _out = out;
|
| 2088 |
+
var _bitLength = 0;
|
| 2089 |
+
var _bitBuffer = 0;
|
| 2090 |
+
|
| 2091 |
+
var _this = {};
|
| 2092 |
+
|
| 2093 |
+
_this.write = function(data, length) {
|
| 2094 |
+
|
| 2095 |
+
if ( (data >>> length) != 0) {
|
| 2096 |
+
throw new Error('length over');
|
| 2097 |
+
}
|
| 2098 |
+
|
| 2099 |
+
while (_bitLength + length >= 8) {
|
| 2100 |
+
_out.writeByte(0xff & ( (data << _bitLength) | _bitBuffer) );
|
| 2101 |
+
length -= (8 - _bitLength);
|
| 2102 |
+
data >>>= (8 - _bitLength);
|
| 2103 |
+
_bitBuffer = 0;
|
| 2104 |
+
_bitLength = 0;
|
| 2105 |
+
}
|
| 2106 |
+
|
| 2107 |
+
_bitBuffer = (data << _bitLength) | _bitBuffer;
|
| 2108 |
+
_bitLength = _bitLength + length;
|
| 2109 |
+
};
|
| 2110 |
+
|
| 2111 |
+
_this.flush = function() {
|
| 2112 |
+
if (_bitLength > 0) {
|
| 2113 |
+
_out.writeByte(_bitBuffer);
|
| 2114 |
+
}
|
| 2115 |
+
};
|
| 2116 |
+
|
| 2117 |
+
return _this;
|
| 2118 |
+
};
|
| 2119 |
+
|
| 2120 |
+
var getLZWRaster = function(lzwMinCodeSize) {
|
| 2121 |
+
|
| 2122 |
+
var clearCode = 1 << lzwMinCodeSize;
|
| 2123 |
+
var endCode = (1 << lzwMinCodeSize) + 1;
|
| 2124 |
+
var bitLength = lzwMinCodeSize + 1;
|
| 2125 |
+
|
| 2126 |
+
// Setup LZWTable
|
| 2127 |
+
var table = lzwTable();
|
| 2128 |
+
|
| 2129 |
+
for (var i = 0; i < clearCode; i += 1) {
|
| 2130 |
+
table.add(String.fromCharCode(i) );
|
| 2131 |
+
}
|
| 2132 |
+
table.add(String.fromCharCode(clearCode) );
|
| 2133 |
+
table.add(String.fromCharCode(endCode) );
|
| 2134 |
+
|
| 2135 |
+
var byteOut = byteArrayOutputStream();
|
| 2136 |
+
var bitOut = bitOutputStream(byteOut);
|
| 2137 |
+
|
| 2138 |
+
// clear code
|
| 2139 |
+
bitOut.write(clearCode, bitLength);
|
| 2140 |
+
|
| 2141 |
+
var dataIndex = 0;
|
| 2142 |
+
|
| 2143 |
+
var s = String.fromCharCode(_data[dataIndex]);
|
| 2144 |
+
dataIndex += 1;
|
| 2145 |
+
|
| 2146 |
+
while (dataIndex < _data.length) {
|
| 2147 |
+
|
| 2148 |
+
var c = String.fromCharCode(_data[dataIndex]);
|
| 2149 |
+
dataIndex += 1;
|
| 2150 |
+
|
| 2151 |
+
if (table.contains(s + c) ) {
|
| 2152 |
+
|
| 2153 |
+
s = s + c;
|
| 2154 |
+
|
| 2155 |
+
} else {
|
| 2156 |
+
|
| 2157 |
+
bitOut.write(table.indexOf(s), bitLength);
|
| 2158 |
+
|
| 2159 |
+
if (table.size() < 0xfff) {
|
| 2160 |
+
|
| 2161 |
+
if (table.size() == (1 << bitLength) ) {
|
| 2162 |
+
bitLength += 1;
|
| 2163 |
+
}
|
| 2164 |
+
|
| 2165 |
+
table.add(s + c);
|
| 2166 |
+
}
|
| 2167 |
+
|
| 2168 |
+
s = c;
|
| 2169 |
+
}
|
| 2170 |
+
}
|
| 2171 |
+
|
| 2172 |
+
bitOut.write(table.indexOf(s), bitLength);
|
| 2173 |
+
|
| 2174 |
+
// end code
|
| 2175 |
+
bitOut.write(endCode, bitLength);
|
| 2176 |
+
|
| 2177 |
+
bitOut.flush();
|
| 2178 |
+
|
| 2179 |
+
return byteOut.toByteArray();
|
| 2180 |
+
};
|
| 2181 |
+
|
| 2182 |
+
var lzwTable = function() {
|
| 2183 |
+
|
| 2184 |
+
var _map = {};
|
| 2185 |
+
var _size = 0;
|
| 2186 |
+
|
| 2187 |
+
var _this = {};
|
| 2188 |
+
|
| 2189 |
+
_this.add = function(key) {
|
| 2190 |
+
if (_this.contains(key) ) {
|
| 2191 |
+
throw new Error('dup key:' + key);
|
| 2192 |
+
}
|
| 2193 |
+
_map[key] = _size;
|
| 2194 |
+
_size += 1;
|
| 2195 |
+
};
|
| 2196 |
+
|
| 2197 |
+
_this.size = function() {
|
| 2198 |
+
return _size;
|
| 2199 |
+
};
|
| 2200 |
+
|
| 2201 |
+
_this.indexOf = function(key) {
|
| 2202 |
+
return _map[key];
|
| 2203 |
+
};
|
| 2204 |
+
|
| 2205 |
+
_this.contains = function(key) {
|
| 2206 |
+
return typeof _map[key] != 'undefined';
|
| 2207 |
+
};
|
| 2208 |
+
|
| 2209 |
+
return _this;
|
| 2210 |
+
};
|
| 2211 |
+
|
| 2212 |
+
return _this;
|
| 2213 |
+
};
|
| 2214 |
+
|
| 2215 |
+
var createImgTag = function(width, height, getPixel, alt) {
|
| 2216 |
+
|
| 2217 |
+
var gif = gifImage(width, height);
|
| 2218 |
+
for (var y = 0; y < height; y += 1) {
|
| 2219 |
+
for (var x = 0; x < width; x += 1) {
|
| 2220 |
+
gif.setPixel(x, y, getPixel(x, y) );
|
| 2221 |
+
}
|
| 2222 |
+
}
|
| 2223 |
+
|
| 2224 |
+
var b = byteArrayOutputStream();
|
| 2225 |
+
gif.write(b);
|
| 2226 |
+
|
| 2227 |
+
var base64 = base64EncodeOutputStream();
|
| 2228 |
+
var bytes = b.toByteArray();
|
| 2229 |
+
for (var i = 0; i < bytes.length; i += 1) {
|
| 2230 |
+
base64.writeByte(bytes[i]);
|
| 2231 |
+
}
|
| 2232 |
+
base64.flush();
|
| 2233 |
+
|
| 2234 |
+
var img = '';
|
| 2235 |
+
img += '<img';
|
| 2236 |
+
img += '\u0020src="';
|
| 2237 |
+
img += 'data:image/gif;base64,';
|
| 2238 |
+
img += base64;
|
| 2239 |
+
img += '"';
|
| 2240 |
+
img += '\u0020width="';
|
| 2241 |
+
img += width;
|
| 2242 |
+
img += '"';
|
| 2243 |
+
img += '\u0020height="';
|
| 2244 |
+
img += height;
|
| 2245 |
+
img += '"';
|
| 2246 |
+
if (alt) {
|
| 2247 |
+
img += '\u0020alt="';
|
| 2248 |
+
img += alt;
|
| 2249 |
+
img += '"';
|
| 2250 |
+
}
|
| 2251 |
+
img += '/>';
|
| 2252 |
+
|
| 2253 |
+
return img;
|
| 2254 |
+
};
|
| 2255 |
+
|
| 2256 |
+
//---------------------------------------------------------------------
|
| 2257 |
+
// returns qrcode function.
|
| 2258 |
+
|
| 2259 |
+
return qrcode;
|
| 2260 |
+
}();
|
| 2261 |
+
|
| 2262 |
+
(function (factory) {
|
| 2263 |
+
if (typeof define === 'function' && define.amd) {
|
| 2264 |
+
define([], factory);
|
| 2265 |
+
} else if (typeof exports === 'object') {
|
| 2266 |
+
module.exports = factory();
|
| 2267 |
+
}
|
| 2268 |
+
}(function () {
|
| 2269 |
+
return qrcode;
|
| 2270 |
+
}));
|
| 2271 |
+
//---------------------------------------------------------------------
|
| 2272 |
+
//
|
| 2273 |
+
// QR Code Generator for JavaScript UTF8 Support (optional)
|
| 2274 |
+
//
|
| 2275 |
+
// Copyright (c) 2011 Kazuhiko Arase
|
| 2276 |
+
//
|
| 2277 |
+
// URL: http://www.d-project.com/
|
| 2278 |
+
//
|
| 2279 |
+
// Licensed under the MIT license:
|
| 2280 |
+
// http://www.opensource.org/licenses/mit-license.php
|
| 2281 |
+
//
|
| 2282 |
+
// The word 'QR Code' is registered trademark of
|
| 2283 |
+
// DENSO WAVE INCORPORATED
|
| 2284 |
+
// http://www.denso-wave.com/qrcode/faqpatent-e.html
|
| 2285 |
+
//
|
| 2286 |
+
//---------------------------------------------------------------------
|
| 2287 |
+
|
| 2288 |
+
!function(qrcode) {
|
| 2289 |
+
|
| 2290 |
+
//---------------------------------------------------------------------
|
| 2291 |
+
// overwrite qrcode.stringToBytes
|
| 2292 |
+
//---------------------------------------------------------------------
|
| 2293 |
+
|
| 2294 |
+
qrcode.stringToBytes = function(s) {
|
| 2295 |
+
// http://stackoverflow.com/questions/18729405/how-to-convert-utf8-string-to-byte-array
|
| 2296 |
+
function toUTF8Array(str) {
|
| 2297 |
+
var utf8 = [];
|
| 2298 |
+
for (var i=0; i < str.length; i++) {
|
| 2299 |
+
var charcode = str.charCodeAt(i);
|
| 2300 |
+
if (charcode < 0x80) utf8.push(charcode);
|
| 2301 |
+
else if (charcode < 0x800) {
|
| 2302 |
+
utf8.push(0xc0 | (charcode >> 6),
|
| 2303 |
+
0x80 | (charcode & 0x3f));
|
| 2304 |
+
}
|
| 2305 |
+
else if (charcode < 0xd800 || charcode >= 0xe000) {
|
| 2306 |
+
utf8.push(0xe0 | (charcode >> 12),
|
| 2307 |
+
0x80 | ((charcode>>6) & 0x3f),
|
| 2308 |
+
0x80 | (charcode & 0x3f));
|
| 2309 |
+
}
|
| 2310 |
+
// surrogate pair
|
| 2311 |
+
else {
|
| 2312 |
+
i++;
|
| 2313 |
+
// UTF-16 encodes 0x10000-0x10FFFF by
|
| 2314 |
+
// subtracting 0x10000 and splitting the
|
| 2315 |
+
// 20 bits of 0x0-0xFFFFF into two halves
|
| 2316 |
+
charcode = 0x10000 + (((charcode & 0x3ff)<<10)
|
| 2317 |
+
| (str.charCodeAt(i) & 0x3ff));
|
| 2318 |
+
utf8.push(0xf0 | (charcode >>18),
|
| 2319 |
+
0x80 | ((charcode>>12) & 0x3f),
|
| 2320 |
+
0x80 | ((charcode>>6) & 0x3f),
|
| 2321 |
+
0x80 | (charcode & 0x3f));
|
| 2322 |
+
}
|
| 2323 |
+
}
|
| 2324 |
+
return utf8;
|
| 2325 |
+
}
|
| 2326 |
+
return toUTF8Array(s);
|
| 2327 |
+
};
|
| 2328 |
+
|
| 2329 |
+
}(qrcode);
|
| 2330 |
+
|
| 2331 |
+
return qrcode; // eslint-disable-line no-undef
|
| 2332 |
+
}()));
|
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
| 1 |
+
/*! jquery-qrcode v0.14.0 - https://larsjung.de/jquery-qrcode/ */
|
| 2 |
+
!function(r){"use strict";function t(t,e,n,o){function a(r,t){return r-=o,t-=o,0>r||r>=c||0>t||t>=c?!1:f.isDark(r,t)}function i(r,t,e,n){var o=u.isDark,a=1/l;u.isDark=function(i,u){var f=u*a,c=i*a,l=f+a,g=c+a;return o(i,u)&&(r>l||f>e||t>g||c>n)}}var u={},f=r(n,e);f.addData(t),f.make(),o=o||0;var c=f.getModuleCount(),l=f.getModuleCount()+2*o;return u.text=t,u.level=e,u.version=n,u.moduleCount=l,u.isDark=a,u.addBlank=i,u}function e(r,e,n,o,a){n=Math.max(1,n||1),o=Math.min(40,o||40);for(var i=n;o>=i;i+=1)try{return t(r,e,i,a)}catch(u){}}function n(r,t,e){var n=e.size,o="bold "+e.mSize*n+"px "+e.fontname,a=w("<canvas/>")[0].getContext("2d");a.font=o;var i=a.measureText(e.label).width,u=e.mSize,f=i/n,c=(1-f)*e.mPosX,l=(1-u)*e.mPosY,g=c+f,s=l+u,v=.01;1===e.mode?r.addBlank(0,l-v,n,s+v):r.addBlank(c-v,l-v,g+v,s+v),t.fillStyle=e.fontcolor,t.font=o,t.fillText(e.label,c*n,l*n+.75*e.mSize*n)}function o(r,t,e){var n=e.size,o=e.image.naturalWidth||1,a=e.image.naturalHeight||1,i=e.mSize,u=i*o/a,f=(1-u)*e.mPosX,c=(1-i)*e.mPosY,l=f+u,g=c+i,s=.01;3===e.mode?r.addBlank(0,c-s,n,g+s):r.addBlank(f-s,c-s,l+s,g+s),t.drawImage(e.image,f*n,c*n,u*n,i*n)}function a(r,t,e){w(e.background).is("img")?t.drawImage(e.background,0,0,e.size,e.size):e.background&&(t.fillStyle=e.background,t.fillRect(e.left,e.top,e.size,e.size));var a=e.mode;1===a||2===a?n(r,t,e):(3===a||4===a)&&o(r,t,e)}function i(r,t,e,n,o,a,i,u){r.isDark(i,u)&&t.rect(n,o,a,a)}function u(r,t,e,n,o,a,i,u,f,c){i?r.moveTo(t+a,e):r.moveTo(t,e),u?(r.lineTo(n-a,e),r.arcTo(n,e,n,o,a)):r.lineTo(n,e),f?(r.lineTo(n,o-a),r.arcTo(n,o,t,o,a)):r.lineTo(n,o),c?(r.lineTo(t+a,o),r.arcTo(t,o,t,e,a)):r.lineTo(t,o),i?(r.lineTo(t,e+a),r.arcTo(t,e,n,e,a)):r.lineTo(t,e)}function f(r,t,e,n,o,a,i,u,f,c){i&&(r.moveTo(t+a,e),r.lineTo(t,e),r.lineTo(t,e+a),r.arcTo(t,e,t+a,e,a)),u&&(r.moveTo(n-a,e),r.lineTo(n,e),r.lineTo(n,e+a),r.arcTo(n,e,n-a,e,a)),f&&(r.moveTo(n-a,o),r.lineTo(n,o),r.lineTo(n,o-a),r.arcTo(n,o,n-a,o,a)),c&&(r.moveTo(t+a,o),r.lineTo(t,o),r.lineTo(t,o-a),r.arcTo(t,o,t+a,o,a))}function c(r,t,e,n,o,a,i,c){var l=r.isDark,g=n+a,s=o+a,v=e.radius*a,h=i-1,d=i+1,w=c-1,m=c+1,y=l(i,c),T=l(h,w),p=l(h,c),B=l(h,m),A=l(i,m),E=l(d,m),k=l(d,c),M=l(d,w),C=l(i,w);y?u(t,n,o,g,s,v,!p&&!C,!p&&!A,!k&&!A,!k&&!C):f(t,n,o,g,s,v,p&&C&&T,p&&A&&B,k&&A&&E,k&&C&&M)}function l(r,t,e){var n,o,a=r.moduleCount,u=e.size/a,f=i;for(e.radius>0&&e.radius<=.5&&(f=c),t.beginPath(),n=0;a>n;n+=1)for(o=0;a>o;o+=1){var l=e.left+o*u,g=e.top+n*u,s=u;f(r,t,e,l,g,s,n,o)}if(w(e.fill).is("img")){t.strokeStyle="rgba(0,0,0,0.5)",t.lineWidth=2,t.stroke();var v=t.globalCompositeOperation;t.globalCompositeOperation="destination-out",t.fill(),t.globalCompositeOperation=v,t.clip(),t.drawImage(e.fill,0,0,e.size,e.size),t.restore()}else t.fillStyle=e.fill,t.fill()}function g(r,t){var n=e(t.text,t.ecLevel,t.minVersion,t.maxVersion,t.quiet);if(!n)return null;var o=w(r).data("qrcode",n),i=o[0].getContext("2d");return a(n,i,t),l(n,i,t),o}function s(r){var t=w("<canvas/>").attr("width",r.size).attr("height",r.size);return g(t,r)}function v(r){return w("<img/>").attr("src",s(r)[0].toDataURL("image/png"))}function h(r){var t=e(r.text,r.ecLevel,r.minVersion,r.maxVersion,r.quiet);if(!t)return null;var n,o,a=r.size,i=r.background,u=Math.floor,f=t.moduleCount,c=u(a/f),l=u(.5*(a-c*f)),g={position:"relative",left:0,top:0,padding:0,margin:0,width:a,height:a},s={position:"absolute",padding:0,margin:0,width:c,height:c,"background-color":r.fill},v=w("<div/>").data("qrcode",t).css(g);for(i&&v.css("background-color",i),n=0;f>n;n+=1)for(o=0;f>o;o+=1)t.isDark(n,o)&&w("<div/>").css(s).css({left:l+o*c,top:l+n*c}).appendTo(v);return v}function d(r){return m&&"canvas"===r.render?s(r):m&&"image"===r.render?v(r):h(r)}var w=window.jQuery,m=function(){var r=document.createElement("canvas");return!(!r.getContext||!r.getContext("2d"))}(),y={render:"canvas",minVersion:1,maxVersion:40,ecLevel:"L",left:0,top:0,size:200,fill:"#000",background:null,text:"no text",radius:0,quiet:0,mode:0,mSize:.1,mPosX:.5,mPosY:.5,label:"no label",fontname:"sans",fontcolor:"#000",image:null};w.fn.qrcode=function(r){var t=w.extend({},y,r);return this.each(function(r,e){"canvas"===e.nodeName.toLowerCase()?g(e,t):w(e).append(d(t))})}}(function(){var r=function(){function r(t,e){if("undefined"==typeof t.length)throw new Error(t.length+"/"+e);var n=function(){for(var r=0;r<t.length&&0==t[r];)r+=1;for(var n=new Array(t.length-r+e),o=0;o<t.length-r;o+=1)n[o]=t[o+r];return n}(),o={};return o.getAt=function(r){return n[r]},o.getLength=function(){return n.length},o.multiply=function(t){for(var e=new Array(o.getLength()+t.getLength()-1),n=0;n<o.getLength();n+=1)for(var a=0;a<t.getLength();a+=1)e[n+a]^=i.gexp(i.glog(o.getAt(n))+i.glog(t.getAt(a)));return r(e,0)},o.mod=function(t){if(o.getLength()-t.getLength()<0)return o;for(var e=i.glog(o.getAt(0))-i.glog(t.getAt(0)),n=new Array(o.getLength()),a=0;a<o.getLength();a+=1)n[a]=o.getAt(a);for(var a=0;a<t.getLength();a+=1)n[a]^=i.gexp(i.glog(t.getAt(a))+e);return r(n,0).mod(t)},o}var t=function(t,e){var o=236,i=17,l=t,g=n[e],s=null,v=0,d=null,w=new Array,m={},y=function(r,t){v=4*l+17,s=function(r){for(var t=new Array(r),e=0;r>e;e+=1){t[e]=new Array(r);for(var n=0;r>n;n+=1)t[e][n]=null}return t}(v),T(0,0),T(v-7,0),T(0,v-7),A(),B(),k(r,t),l>=7&&E(r),null==d&&(d=D(l,g,w)),M(d,t)},T=function(r,t){for(var e=-1;7>=e;e+=1)if(!(-1>=r+e||r+e>=v))for(var n=-1;7>=n;n+=1)-1>=t+n||t+n>=v||(e>=0&&6>=e&&(0==n||6==n)||n>=0&&6>=n&&(0==e||6==e)||e>=2&&4>=e&&n>=2&&4>=n?s[r+e][t+n]=!0:s[r+e][t+n]=!1)},p=function(){for(var r=0,t=0,e=0;8>e;e+=1){y(!0,e);var n=a.getLostPoint(m);(0==e||r>n)&&(r=n,t=e)}return t},B=function(){for(var r=8;v-8>r;r+=1)null==s[r][6]&&(s[r][6]=r%2==0);for(var t=8;v-8>t;t+=1)null==s[6][t]&&(s[6][t]=t%2==0)},A=function(){for(var r=a.getPatternPosition(l),t=0;t<r.length;t+=1)for(var e=0;e<r.length;e+=1){var n=r[t],o=r[e];if(null==s[n][o])for(var i=-2;2>=i;i+=1)for(var u=-2;2>=u;u+=1)-2==i||2==i||-2==u||2==u||0==i&&0==u?s[n+i][o+u]=!0:s[n+i][o+u]=!1}},E=function(r){for(var t=a.getBCHTypeNumber(l),e=0;18>e;e+=1){var n=!r&&1==(t>>e&1);s[Math.floor(e/3)][e%3+v-8-3]=n}for(var e=0;18>e;e+=1){var n=!r&&1==(t>>e&1);s[e%3+v-8-3][Math.floor(e/3)]=n}},k=function(r,t){for(var e=g<<3|t,n=a.getBCHTypeInfo(e),o=0;15>o;o+=1){var i=!r&&1==(n>>o&1);6>o?s[o][8]=i:8>o?s[o+1][8]=i:s[v-15+o][8]=i}for(var o=0;15>o;o+=1){var i=!r&&1==(n>>o&1);8>o?s[8][v-o-1]=i:9>o?s[8][15-o-1+1]=i:s[8][15-o-1]=i}s[v-8][8]=!r},M=function(r,t){for(var e=-1,n=v-1,o=7,i=0,u=a.getMaskFunction(t),f=v-1;f>0;f-=2)for(6==f&&(f-=1);;){for(var c=0;2>c;c+=1)if(null==s[n][f-c]){var l=!1;i<r.length&&(l=1==(r[i]>>>o&1));var g=u(n,f-c);g&&(l=!l),s[n][f-c]=l,o-=1,-1==o&&(i+=1,o=7)}if(n+=e,0>n||n>=v){n-=e,e=-e;break}}},C=function(t,e){for(var n=0,o=0,i=0,u=new Array(e.length),f=new Array(e.length),c=0;c<e.length;c+=1){var l=e[c].dataCount,g=e[c].totalCount-l;o=Math.max(o,l),i=Math.max(i,g),u[c]=new Array(l);for(var s=0;s<u[c].length;s+=1)u[c][s]=255&t.getBuffer()[s+n];n+=l;var v=a.getErrorCorrectPolynomial(g),h=r(u[c],v.getLength()-1),d=h.mod(v);f[c]=new Array(v.getLength()-1);for(var s=0;s<f[c].length;s+=1){var w=s+d.getLength()-f[c].length;f[c][s]=w>=0?d.getAt(w):0}}for(var m=0,s=0;s<e.length;s+=1)m+=e[s].totalCount;for(var y=new Array(m),T=0,s=0;o>s;s+=1)for(var c=0;c<e.length;c+=1)s<u[c].length&&(y[T]=u[c][s],T+=1);for(var s=0;i>s;s+=1)for(var c=0;c<e.length;c+=1)s<f[c].length&&(y[T]=f[c][s],T+=1);return y},D=function(r,t,e){for(var n=u.getRSBlocks(r,t),c=f(),l=0;l<e.length;l+=1){var g=e[l];c.put(g.getMode(),4),c.put(g.getLength(),a.getLengthInBits(g.getMode(),r)),g.write(c)}for(var s=0,l=0;l<n.length;l+=1)s+=n[l].dataCount;if(c.getLengthInBits()>8*s)throw new Error("code length overflow. ("+c.getLengthInBits()+">"+8*s+")");for(c.getLengthInBits()+4<=8*s&&c.put(0,4);c.getLengthInBits()%8!=0;)c.putBit(!1);for(;;){if(c.getLengthInBits()>=8*s)break;if(c.put(o,8),c.getLengthInBits()>=8*s)break;c.put(i,8)}return C(c,n)};return m.addData=function(r){var t=c(r);w.push(t),d=null},m.isDark=function(r,t){if(0>r||r>=v||0>t||t>=v)throw new Error(r+","+t);return s[r][t]},m.getModuleCount=function(){return v},m.make=function(){y(!1,p())},m.createTableTag=function(r,t){r=r||2,t="undefined"==typeof t?4*r:t;var e="";e+='<table style="',e+=" border-width: 0px; border-style: none;",e+=" border-collapse: collapse;",e+=" padding: 0px; margin: "+t+"px;",e+='">',e+="<tbody>";for(var n=0;n<m.getModuleCount();n+=1){e+="<tr>";for(var o=0;o<m.getModuleCount();o+=1)e+='<td style="',e+=" border-width: 0px; border-style: none;",e+=" border-collapse: collapse;",e+=" padding: 0px; margin: 0px;",e+=" width: "+r+"px;",e+=" height: "+r+"px;",e+=" background-color: ",e+=m.isDark(n,o)?"#000000":"#ffffff",e+=";",e+='"/>';e+="</tr>"}return e+="</tbody>",e+="</table>"},m.createImgTag=function(r,t){r=r||2,t="undefined"==typeof t?4*r:t;var e=m.getModuleCount()*r+2*t,n=t,o=e-t;return h(e,e,function(t,e){if(t>=n&&o>t&&e>=n&&o>e){var a=Math.floor((t-n)/r),i=Math.floor((e-n)/r);return m.isDark(i,a)?0:1}return 1})},m};t.stringToBytes=function(r){for(var t=new Array,e=0;e<r.length;e+=1){var n=r.charCodeAt(e);t.push(255&n)}return t},t.createStringToBytes=function(r,t){var e=function(){for(var e=s(r),n=function(){var r=e.read();if(-1==r)throw new Error;return r},o=0,a={};;){var i=e.read();if(-1==i)break;var u=n(),f=n(),c=n(),l=String.fromCharCode(i<<8|u),g=f<<8|c;a[l]=g,o+=1}if(o!=t)throw new Error(o+" != "+t);return a}(),n="?".charCodeAt(0);return function(r){for(var t=new Array,o=0;o<r.length;o+=1){var a=r.charCodeAt(o);if(128>a)t.push(a);else{var i=e[r.charAt(o)];"number"==typeof i?(255&i)==i?t.push(i):(t.push(i>>>8),t.push(255&i)):t.push(n)}}return t}};var e={MODE_NUMBER:1,MODE_ALPHA_NUM:2,MODE_8BIT_BYTE:4,MODE_KANJI:8},n={L:1,M:0,Q:3,H:2},o={PATTERN000:0,PATTERN001:1,PATTERN010:2,PATTERN011:3,PATTERN100:4,PATTERN101:5,PATTERN110:6,PATTERN111:7},a=function(){var t=[[],[6,18],[6,22],[6,26],[6,30],[6,34],[6,22,38],[6,24,42],[6,26,46],[6,28,50],[6,30,54],[6,32,58],[6,34,62],[6,26,46,66],[6,26,48,70],[6,26,50,74],[6,30,54,78],[6,30,56,82],[6,30,58,86],[6,34,62,90],[6,28,50,72,94],[6,26,50,74,98],[6,30,54,78,102],[6,28,54,80,106],[6,32,58,84,110],[6,30,58,86,114],[6,34,62,90,118],[6,26,50,74,98,122],[6,30,54,78,102,126],[6,26,52,78,104,130],[6,30,56,82,108,134],[6,34,60,86,112,138],[6,30,58,86,114,142],[6,34,62,90,118,146],[6,30,54,78,102,126,150],[6,24,50,76,102,128,154],[6,28,54,80,106,132,158],[6,32,58,84,110,136,162],[6,26,54,82,110,138,166],[6,30,58,86,114,142,170]],n=1335,a=7973,u=21522,f={},c=function(r){for(var t=0;0!=r;)t+=1,r>>>=1;return t};return f.getBCHTypeInfo=function(r){for(var t=r<<10;c(t)-c(n)>=0;)t^=n<<c(t)-c(n);return(r<<10|t)^u},f.getBCHTypeNumber=function(r){for(var t=r<<12;c(t)-c(a)>=0;)t^=a<<c(t)-c(a);return r<<12|t},f.getPatternPosition=function(r){return t[r-1]},f.getMaskFunction=function(r){switch(r){case o.PATTERN000:return function(r,t){return(r+t)%2==0};case o.PATTERN001:return function(r,t){return r%2==0};case o.PATTERN010:return function(r,t){return t%3==0};case o.PATTERN011:return function(r,t){return(r+t)%3==0};case o.PATTERN100:return function(r,t){return(Math.floor(r/2)+Math.floor(t/3))%2==0};case o.PATTERN101:return function(r,t){return r*t%2+r*t%3==0};case o.PATTERN110:return function(r,t){return(r*t%2+r*t%3)%2==0};case o.PATTERN111:return function(r,t){return(r*t%3+(r+t)%2)%2==0};default:throw new Error("bad maskPattern:"+r)}},f.getErrorCorrectPolynomial=function(t){for(var e=r([1],0),n=0;t>n;n+=1)e=e.multiply(r([1,i.gexp(n)],0));return e},f.getLengthInBits=function(r,t){if(t>=1&&10>t)switch(r){case e.MODE_NUMBER:return 10;case e.MODE_ALPHA_NUM:return 9;case e.MODE_8BIT_BYTE:return 8;case e.MODE_KANJI:return 8;default:throw new Error("mode:"+r)}else if(27>t)switch(r){case e.MODE_NUMBER:return 12;case e.MODE_ALPHA_NUM:return 11;case e.MODE_8BIT_BYTE:return 16;case e.MODE_KANJI:return 10;default:throw new Error("mode:"+r)}else{if(!(41>t))throw new Error("type:"+t);switch(r){case e.MODE_NUMBER:return 14;case e.MODE_ALPHA_NUM:return 13;case e.MODE_8BIT_BYTE:return 16;case e.MODE_KANJI:return 12;default:throw new Error("mode:"+r)}}},f.getLostPoint=function(r){for(var t=r.getModuleCount(),e=0,n=0;t>n;n+=1)for(var o=0;t>o;o+=1){for(var a=0,i=r.isDark(n,o),u=-1;1>=u;u+=1)if(!(0>n+u||n+u>=t))for(var f=-1;1>=f;f+=1)0>o+f||o+f>=t||(0!=u||0!=f)&&i==r.isDark(n+u,o+f)&&(a+=1);a>5&&(e+=3+a-5)}for(var n=0;t-1>n;n+=1)for(var o=0;t-1>o;o+=1){var c=0;r.isDark(n,o)&&(c+=1),r.isDark(n+1,o)&&(c+=1),r.isDark(n,o+1)&&(c+=1),r.isDark(n+1,o+1)&&(c+=1),(0==c||4==c)&&(e+=3)}for(var n=0;t>n;n+=1)for(var o=0;t-6>o;o+=1)r.isDark(n,o)&&!r.isDark(n,o+1)&&r.isDark(n,o+2)&&r.isDark(n,o+3)&&r.isDark(n,o+4)&&!r.isDark(n,o+5)&&r.isDark(n,o+6)&&(e+=40);for(var o=0;t>o;o+=1)for(var n=0;t-6>n;n+=1)r.isDark(n,o)&&!r.isDark(n+1,o)&&r.isDark(n+2,o)&&r.isDark(n+3,o)&&r.isDark(n+4,o)&&!r.isDark(n+5,o)&&r.isDark(n+6,o)&&(e+=40);for(var l=0,o=0;t>o;o+=1)for(var n=0;t>n;n+=1)r.isDark(n,o)&&(l+=1);var g=Math.abs(100*l/t/t-50)/5;return e+=10*g},f}(),i=function(){for(var r=new Array(256),t=new Array(256),e=0;8>e;e+=1)r[e]=1<<e;for(var e=8;256>e;e+=1)r[e]=r[e-4]^r[e-5]^r[e-6]^r[e-8];for(var e=0;255>e;e+=1)t[r[e]]=e;var n={};return n.glog=function(r){if(1>r)throw new Error("glog("+r+")");return t[r]},n.gexp=function(t){for(;0>t;)t+=255;for(;t>=256;)t-=255;return r[t]},n}(),u=function(){var r=[[1,26,19],[1,26,16],[1,26,13],[1,26,9],[1,44,34],[1,44,28],[1,44,22],[1,44,16],[1,70,55],[1,70,44],[2,35,17],[2,35,13],[1,100,80],[2,50,32],[2,50,24],[4,25,9],[1,134,108],[2,67,43],[2,33,15,2,34,16],[2,33,11,2,34,12],[2,86,68],[4,43,27],[4,43,19],[4,43,15],[2,98,78],[4,49,31],[2,32,14,4,33,15],[4,39,13,1,40,14],[2,121,97],[2,60,38,2,61,39],[4,40,18,2,41,19],[4,40,14,2,41,15],[2,146,116],[3,58,36,2,59,37],[4,36,16,4,37,17],[4,36,12,4,37,13],[2,86,68,2,87,69],[4,69,43,1,70,44],[6,43,19,2,44,20],[6,43,15,2,44,16],[4,101,81],[1,80,50,4,81,51],[4,50,22,4,51,23],[3,36,12,8,37,13],[2,116,92,2,117,93],[6,58,36,2,59,37],[4,46,20,6,47,21],[7,42,14,4,43,15],[4,133,107],[8,59,37,1,60,38],[8,44,20,4,45,21],[12,33,11,4,34,12],[3,145,115,1,146,116],[4,64,40,5,65,41],[11,36,16,5,37,17],[11,36,12,5,37,13],[5,109,87,1,110,88],[5,65,41,5,66,42],[5,54,24,7,55,25],[11,36,12,7,37,13],[5,122,98,1,123,99],[7,73,45,3,74,46],[15,43,19,2,44,20],[3,45,15,13,46,16],[1,135,107,5,136,108],[10,74,46,1,75,47],[1,50,22,15,51,23],[2,42,14,17,43,15],[5,150,120,1,151,121],[9,69,43,4,70,44],[17,50,22,1,51,23],[2,42,14,19,43,15],[3,141,113,4,142,114],[3,70,44,11,71,45],[17,47,21,4,48,22],[9,39,13,16,40,14],[3,135,107,5,136,108],[3,67,41,13,68,42],[15,54,24,5,55,25],[15,43,15,10,44,16],[4,144,116,4,145,117],[17,68,42],[17,50,22,6,51,23],[19,46,16,6,47,17],[2,139,111,7,140,112],[17,74,46],[7,54,24,16,55,25],[34,37,13],[4,151,121,5,152,122],[4,75,47,14,76,48],[11,54,24,14,55,25],[16,45,15,14,46,16],[6,147,117,4,148,118],[6,73,45,14,74,46],[11,54,24,16,55,25],[30,46,16,2,47,17],[8,132,106,4,133,107],[8,75,47,13,76,48],[7,54,24,22,55,25],[22,45,15,13,46,16],[10,142,114,2,143,115],[19,74,46,4,75,47],[28,50,22,6,51,23],[33,46,16,4,47,17],[8,152,122,4,153,123],[22,73,45,3,74,46],[8,53,23,26,54,24],[12,45,15,28,46,16],[3,147,117,10,148,118],[3,73,45,23,74,46],[4,54,24,31,55,25],[11,45,15,31,46,16],[7,146,116,7,147,117],[21,73,45,7,74,46],[1,53,23,37,54,24],[19,45,15,26,46,16],[5,145,115,10,146,116],[19,75,47,10,76,48],[15,54,24,25,55,25],[23,45,15,25,46,16],[13,145,115,3,146,116],[2,74,46,29,75,47],[42,54,24,1,55,25],[23,45,15,28,46,16],[17,145,115],[10,74,46,23,75,47],[10,54,24,35,55,25],[19,45,15,35,46,16],[17,145,115,1,146,116],[14,74,46,21,75,47],[29,54,24,19,55,25],[11,45,15,46,46,16],[13,145,115,6,146,116],[14,74,46,23,75,47],[44,54,24,7,55,25],[59,46,16,1,47,17],[12,151,121,7,152,122],[12,75,47,26,76,48],[39,54,24,14,55,25],[22,45,15,41,46,16],[6,151,121,14,152,122],[6,75,47,34,76,48],[46,54,24,10,55,25],[2,45,15,64,46,16],[17,152,122,4,153,123],[29,74,46,14,75,47],[49,54,24,10,55,25],[24,45,15,46,46,16],[4,152,122,18,153,123],[13,74,46,32,75,47],[48,54,24,14,55,25],[42,45,15,32,46,16],[20,147,117,4,148,118],[40,75,47,7,76,48],[43,54,24,22,55,25],[10,45,15,67,46,16],[19,148,118,6,149,119],[18,75,47,31,76,48],[34,54,24,34,55,25],[20,45,15,61,46,16]],t=function(r,t){var e={};return e.totalCount=r,e.dataCount=t,e},e={},o=function(t,e){switch(e){case n.L:return r[4*(t-1)+0];case n.M:return r[4*(t-1)+1];case n.Q:return r[4*(t-1)+2];case n.H:return r[4*(t-1)+3];default:return}};return e.getRSBlocks=function(r,e){var n=o(r,e);if("undefined"==typeof n)throw new Error("bad rs block @ typeNumber:"+r+"/errorCorrectLevel:"+e);for(var a=n.length/3,i=new Array,u=0;a>u;u+=1)for(var f=n[3*u+0],c=n[3*u+1],l=n[3*u+2],g=0;f>g;g+=1)i.push(t(c,l));return i},e}(),f=function(){var r=new Array,t=0,e={};return e.getBuffer=function(){return r},e.getAt=function(t){var e=Math.floor(t/8);return 1==(r[e]>>>7-t%8&1)},e.put=function(r,t){for(var n=0;t>n;n+=1)e.putBit(1==(r>>>t-n-1&1))},e.getLengthInBits=function(){return t},e.putBit=function(e){var n=Math.floor(t/8);r.length<=n&&r.push(0),e&&(r[n]|=128>>>t%8),t+=1},e},c=function(r){var n=e.MODE_8BIT_BYTE,o=t.stringToBytes(r),a={};return a.getMode=function(){return n},a.getLength=function(r){return o.length},a.write=function(r){for(var t=0;t<o.length;t+=1)r.put(o[t],8)},a},l=function(){var r=new Array,t={};return t.writeByte=function(t){r.push(255&t)},t.writeShort=function(r){t.writeByte(r),t.writeByte(r>>>8)},t.writeBytes=function(r,e,n){e=e||0,n=n||r.length;for(var o=0;n>o;o+=1)t.writeByte(r[o+e])},t.writeString=function(r){for(var e=0;e<r.length;e+=1)t.writeByte(r.charCodeAt(e))},t.toByteArray=function(){return r},t.toString=function(){var t="";t+="[";for(var e=0;e<r.length;e+=1)e>0&&(t+=","),t+=r[e];return t+="]"},t},g=function(){var r=0,t=0,e=0,n="",o={},a=function(r){n+=String.fromCharCode(i(63&r))},i=function(r){if(0>r);else{if(26>r)return 65+r;if(52>r)return 97+(r-26);if(62>r)return 48+(r-52);if(62==r)return 43;if(63==r)return 47}throw new Error("n:"+r)};return o.writeByte=function(n){for(r=r<<8|255&n,t+=8,e+=1;t>=6;)a(r>>>t-6),t-=6},o.flush=function(){if(t>0&&(a(r<<6-t),r=0,t=0),e%3!=0)for(var o=3-e%3,i=0;o>i;i+=1)n+="="},o.toString=function(){return n},o},s=function(r){var t=r,e=0,n=0,o=0,a={};a.read=function(){for(;8>o;){if(e>=t.length){if(0==o)return-1;throw new Error("unexpected end of file./"+o)}var r=t.charAt(e);if(e+=1,"="==r)return o=0,-1;r.match(/^\s$/)||(n=n<<6|i(r.charCodeAt(0)),o+=6)}var a=n>>>o-8&255;return o-=8,a};var i=function(r){if(r>=65&&90>=r)return r-65;if(r>=97&&122>=r)return r-97+26;if(r>=48&&57>=r)return r-48+52;if(43==r)return 62;if(47==r)return 63;throw new Error("c:"+r)};return a},v=function(r,t){var e=r,n=t,o=new Array(r*t),a={};a.setPixel=function(r,t,n){o[t*e+r]=n},a.write=function(r){r.writeString("GIF87a"),r.writeShort(e),r.writeShort(n),r.writeByte(128),r.writeByte(0),r.writeByte(0),r.writeByte(0),r.writeByte(0),r.writeByte(0),r.writeByte(255),r.writeByte(255),r.writeByte(255),r.writeString(","),r.writeShort(0),r.writeShort(0),r.writeShort(e),r.writeShort(n),r.writeByte(0);var t=2,o=u(t);r.writeByte(t);for(var a=0;o.length-a>255;)r.writeByte(255),r.writeBytes(o,a,255),a+=255;r.writeByte(o.length-a),r.writeBytes(o,a,o.length-a),r.writeByte(0),r.writeString(";")};var i=function(r){var t=r,e=0,n=0,o={};return o.write=function(r,o){if(r>>>o!=0)throw new Error("length over");for(;e+o>=8;)t.writeByte(255&(r<<e|n)),o-=8-e,r>>>=8-e,n=0,e=0;n=r<<e|n,e+=o},o.flush=function(){e>0&&t.writeByte(n)},o},u=function(r){for(var t=1<<r,e=(1<<r)+1,n=r+1,a=f(),u=0;t>u;u+=1)a.add(String.fromCharCode(u));a.add(String.fromCharCode(t)),a.add(String.fromCharCode(e));var c=l(),g=i(c);g.write(t,n);var s=0,v=String.fromCharCode(o[s]);for(s+=1;s<o.length;){var h=String.fromCharCode(o[s]);s+=1,a.contains(v+h)?v+=h:(g.write(a.indexOf(v),n),a.size()<4095&&(a.size()==1<<n&&(n+=1),a.add(v+h)),v=h)}return g.write(a.indexOf(v),n),g.write(e,n),g.flush(),c.toByteArray()},f=function(){var r={},t=0,e={};return e.add=function(n){if(e.contains(n))throw new Error("dup key:"+n);r[n]=t,t+=1},e.size=function(){return t},e.indexOf=function(t){return r[t]},e.contains=function(t){return"undefined"!=typeof r[t]},e};return a},h=function(r,t,e,n){for(var o=v(r,t),a=0;t>a;a+=1)for(var i=0;r>i;i+=1)o.setPixel(i,a,e(i,a));var u=l();o.write(u);for(var f=g(),c=u.toByteArray(),s=0;s<c.length;s+=1)f.writeByte(c[s]);f.flush();var h="";return h+="<img",h+=' src="',h+="data:image/gif;base64,",h+=f,h+='"',h+=' width="',h+=r,h+='"',h+=' height="',h+=t,h+='"',n&&(h+=' alt="',h+=n,h+='"'),h+="/>"};return t}();return function(r){"function"==typeof define&&define.amd?define([],r):"object"==typeof exports&&(module.exports=r())}(function(){return r}),!function(r){r.stringToBytes=function(r){function t(r){for(var t=[],e=0;e<r.length;e++){var n=r.charCodeAt(e);128>n?t.push(n):2048>n?t.push(192|n>>6,128|63&n):55296>n||n>=57344?t.push(224|n>>12,128|n>>6&63,128|63&n):(e++,n=65536+((1023&n)<<10|1023&r.charCodeAt(e)),t.push(240|n>>18,128|n>>12&63,128|n>>6&63,128|63&n))}return t}return t(r)}}(r),r}());
|
|
@@ -0,0 +1,619 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*!
|
| 2 |
+
* jQuery blockUI plugin
|
| 3 |
+
* Version 2.70.0-2014.11.23
|
| 4 |
+
* Requires jQuery v1.7 or later
|
| 5 |
+
*
|
| 6 |
+
* Examples at: http://malsup.com/jquery/block/
|
| 7 |
+
* Copyright (c) 2007-2013 M. Alsup
|
| 8 |
+
* Dual licensed under the MIT and GPL licenses:
|
| 9 |
+
* http://www.opensource.org/licenses/mit-license.php
|
| 10 |
+
* http://www.gnu.org/licenses/gpl.html
|
| 11 |
+
*
|
| 12 |
+
* Thanks to Amir-Hossein Sobhi for some excellent contributions!
|
| 13 |
+
*/
|
| 14 |
+
;(function() {
|
| 15 |
+
/*jshint eqeqeq:false curly:false latedef:false */
|
| 16 |
+
"use strict";
|
| 17 |
+
|
| 18 |
+
function setup($) {
|
| 19 |
+
$.fn._fadeIn = $.fn.fadeIn;
|
| 20 |
+
|
| 21 |
+
var noOp = $.noop || function() {};
|
| 22 |
+
|
| 23 |
+
// this bit is to ensure we don't call setExpression when we shouldn't (with extra muscle to handle
|
| 24 |
+
// confusing userAgent strings on Vista)
|
| 25 |
+
var msie = /MSIE/.test(navigator.userAgent);
|
| 26 |
+
var ie6 = /MSIE 6.0/.test(navigator.userAgent) && ! /MSIE 8.0/.test(navigator.userAgent);
|
| 27 |
+
var mode = document.documentMode || 0;
|
| 28 |
+
var setExpr = $.isFunction( document.createElement('div').style.setExpression );
|
| 29 |
+
|
| 30 |
+
// global $ methods for blocking/unblocking the entire page
|
| 31 |
+
$.blockUI = function(opts) { install(window, opts); };
|
| 32 |
+
$.unblockUI = function(opts) { remove(window, opts); };
|
| 33 |
+
|
| 34 |
+
// convenience method for quick growl-like notifications (http://www.google.com/search?q=growl)
|
| 35 |
+
$.growlUI = function(title, message, timeout, onClose) {
|
| 36 |
+
var $m = $('<div class="growlUI"></div>');
|
| 37 |
+
if (title) $m.append('<h1>'+title+'</h1>');
|
| 38 |
+
if (message) $m.append('<h2>'+message+'</h2>');
|
| 39 |
+
if (timeout === undefined) timeout = 3000;
|
| 40 |
+
|
| 41 |
+
// Added by konapun: Set timeout to 30 seconds if this growl is moused over, like normal toast notifications
|
| 42 |
+
var callBlock = function(opts) {
|
| 43 |
+
opts = opts || {};
|
| 44 |
+
|
| 45 |
+
$.blockUI({
|
| 46 |
+
message: $m,
|
| 47 |
+
fadeIn : typeof opts.fadeIn !== 'undefined' ? opts.fadeIn : 700,
|
| 48 |
+
fadeOut: typeof opts.fadeOut !== 'undefined' ? opts.fadeOut : 1000,
|
| 49 |
+
timeout: typeof opts.timeout !== 'undefined' ? opts.timeout : timeout,
|
| 50 |
+
centerY: false,
|
| 51 |
+
showOverlay: false,
|
| 52 |
+
onUnblock: onClose,
|
| 53 |
+
css: $.blockUI.defaults.growlCSS
|
| 54 |
+
});
|
| 55 |
+
};
|
| 56 |
+
|
| 57 |
+
callBlock();
|
| 58 |
+
var nonmousedOpacity = $m.css('opacity');
|
| 59 |
+
$m.mouseover(function() {
|
| 60 |
+
callBlock({
|
| 61 |
+
fadeIn: 0,
|
| 62 |
+
timeout: 30000
|
| 63 |
+
});
|
| 64 |
+
|
| 65 |
+
var displayBlock = $('.blockMsg');
|
| 66 |
+
displayBlock.stop(); // cancel fadeout if it has started
|
| 67 |
+
displayBlock.fadeTo(300, 1); // make it easier to read the message by removing transparency
|
| 68 |
+
}).mouseout(function() {
|
| 69 |
+
$('.blockMsg').fadeOut(1000);
|
| 70 |
+
});
|
| 71 |
+
// End konapun additions
|
| 72 |
+
};
|
| 73 |
+
|
| 74 |
+
// plugin method for blocking element content
|
| 75 |
+
$.fn.block = function(opts) {
|
| 76 |
+
if ( this[0] === window ) {
|
| 77 |
+
$.blockUI( opts );
|
| 78 |
+
return this;
|
| 79 |
+
}
|
| 80 |
+
var fullOpts = $.extend({}, $.blockUI.defaults, opts || {});
|
| 81 |
+
this.each(function() {
|
| 82 |
+
var $el = $(this);
|
| 83 |
+
if (fullOpts.ignoreIfBlocked && $el.data('blockUI.isBlocked'))
|
| 84 |
+
return;
|
| 85 |
+
$el.unblock({ fadeOut: 0 });
|
| 86 |
+
});
|
| 87 |
+
|
| 88 |
+
return this.each(function() {
|
| 89 |
+
if ($.css(this,'position') == 'static') {
|
| 90 |
+
this.style.position = 'relative';
|
| 91 |
+
$(this).data('blockUI.static', true);
|
| 92 |
+
}
|
| 93 |
+
this.style.zoom = 1; // force 'hasLayout' in ie
|
| 94 |
+
install(this, opts);
|
| 95 |
+
});
|
| 96 |
+
};
|
| 97 |
+
|
| 98 |
+
// plugin method for unblocking element content
|
| 99 |
+
$.fn.unblock = function(opts) {
|
| 100 |
+
if ( this[0] === window ) {
|
| 101 |
+
$.unblockUI( opts );
|
| 102 |
+
return this;
|
| 103 |
+
}
|
| 104 |
+
return this.each(function() {
|
| 105 |
+
remove(this, opts);
|
| 106 |
+
});
|
| 107 |
+
};
|
| 108 |
+
|
| 109 |
+
$.blockUI.version = 2.70; // 2nd generation blocking at no extra cost!
|
| 110 |
+
|
| 111 |
+
// override these in your code to change the default behavior and style
|
| 112 |
+
$.blockUI.defaults = {
|
| 113 |
+
// message displayed when blocking (use null for no message)
|
| 114 |
+
message: '<h1>Please wait...</h1>',
|
| 115 |
+
|
| 116 |
+
title: null, // title string; only used when theme == true
|
| 117 |
+
draggable: true, // only used when theme == true (requires jquery-ui.js to be loaded)
|
| 118 |
+
|
| 119 |
+
theme: false, // set to true to use with jQuery UI themes
|
| 120 |
+
|
| 121 |
+
// styles for the message when blocking; if you wish to disable
|
| 122 |
+
// these and use an external stylesheet then do this in your code:
|
| 123 |
+
// $.blockUI.defaults.css = {};
|
| 124 |
+
css: {
|
| 125 |
+
padding: 0,
|
| 126 |
+
margin: 0,
|
| 127 |
+
width: '30%',
|
| 128 |
+
top: '40%',
|
| 129 |
+
left: '35%',
|
| 130 |
+
textAlign: 'center',
|
| 131 |
+
color: '#000',
|
| 132 |
+
border: '3px solid #aaa',
|
| 133 |
+
backgroundColor:'#fff',
|
| 134 |
+
cursor: 'wait'
|
| 135 |
+
},
|
| 136 |
+
|
| 137 |
+
// minimal style set used when themes are used
|
| 138 |
+
themedCSS: {
|
| 139 |
+
width: '30%',
|
| 140 |
+
top: '40%',
|
| 141 |
+
left: '35%'
|
| 142 |
+
},
|
| 143 |
+
|
| 144 |
+
// styles for the overlay
|
| 145 |
+
overlayCSS: {
|
| 146 |
+
backgroundColor: '#000',
|
| 147 |
+
opacity: 0.6,
|
| 148 |
+
cursor: 'wait'
|
| 149 |
+
},
|
| 150 |
+
|
| 151 |
+
// style to replace wait cursor before unblocking to correct issue
|
| 152 |
+
// of lingering wait cursor
|
| 153 |
+
cursorReset: 'default',
|
| 154 |
+
|
| 155 |
+
// styles applied when using $.growlUI
|
| 156 |
+
growlCSS: {
|
| 157 |
+
width: '350px',
|
| 158 |
+
top: '10px',
|
| 159 |
+
left: '',
|
| 160 |
+
right: '10px',
|
| 161 |
+
border: 'none',
|
| 162 |
+
padding: '5px',
|
| 163 |
+
opacity: 0.6,
|
| 164 |
+
cursor: 'default',
|
| 165 |
+
color: '#fff',
|
| 166 |
+
backgroundColor: '#000',
|
| 167 |
+
'-webkit-border-radius':'10px',
|
| 168 |
+
'-moz-border-radius': '10px',
|
| 169 |
+
'border-radius': '10px'
|
| 170 |
+
},
|
| 171 |
+
|
| 172 |
+
// IE issues: 'about:blank' fails on HTTPS and javascript:false is s-l-o-w
|
| 173 |
+
// (hat tip to Jorge H. N. de Vasconcelos)
|
| 174 |
+
/*jshint scripturl:true */
|
| 175 |
+
iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank',
|
| 176 |
+
|
| 177 |
+
// force usage of iframe in non-IE browsers (handy for blocking applets)
|
| 178 |
+
forceIframe: false,
|
| 179 |
+
|
| 180 |
+
// z-index for the blocking overlay
|
| 181 |
+
baseZ: 1000,
|
| 182 |
+
|
| 183 |
+
// set these to true to have the message automatically centered
|
| 184 |
+
centerX: true, // <-- only effects element blocking (page block controlled via css above)
|
| 185 |
+
centerY: true,
|
| 186 |
+
|
| 187 |
+
// allow body element to be stetched in ie6; this makes blocking look better
|
| 188 |
+
// on "short" pages. disable if you wish to prevent changes to the body height
|
| 189 |
+
allowBodyStretch: true,
|
| 190 |
+
|
| 191 |
+
// enable if you want key and mouse events to be disabled for content that is blocked
|
| 192 |
+
bindEvents: true,
|
| 193 |
+
|
| 194 |
+
// be default blockUI will supress tab navigation from leaving blocking content
|
| 195 |
+
// (if bindEvents is true)
|
| 196 |
+
constrainTabKey: true,
|
| 197 |
+
|
| 198 |
+
// fadeIn time in millis; set to 0 to disable fadeIn on block
|
| 199 |
+
fadeIn: 200,
|
| 200 |
+
|
| 201 |
+
// fadeOut time in millis; set to 0 to disable fadeOut on unblock
|
| 202 |
+
fadeOut: 400,
|
| 203 |
+
|
| 204 |
+
// time in millis to wait before auto-unblocking; set to 0 to disable auto-unblock
|
| 205 |
+
timeout: 0,
|
| 206 |
+
|
| 207 |
+
// disable if you don't want to show the overlay
|
| 208 |
+
showOverlay: true,
|
| 209 |
+
|
| 210 |
+
// if true, focus will be placed in the first available input field when
|
| 211 |
+
// page blocking
|
| 212 |
+
focusInput: true,
|
| 213 |
+
|
| 214 |
+
// elements that can receive focus
|
| 215 |
+
focusableElements: ':input:enabled:visible',
|
| 216 |
+
|
| 217 |
+
// suppresses the use of overlay styles on FF/Linux (due to performance issues with opacity)
|
| 218 |
+
// no longer needed in 2012
|
| 219 |
+
// applyPlatformOpacityRules: true,
|
| 220 |
+
|
| 221 |
+
// callback method invoked when fadeIn has completed and blocking message is visible
|
| 222 |
+
onBlock: null,
|
| 223 |
+
|
| 224 |
+
// callback method invoked when unblocking has completed; the callback is
|
| 225 |
+
// passed the element that has been unblocked (which is the window object for page
|
| 226 |
+
// blocks) and the options that were passed to the unblock call:
|
| 227 |
+
// onUnblock(element, options)
|
| 228 |
+
onUnblock: null,
|
| 229 |
+
|
| 230 |
+
// callback method invoked when the overlay area is clicked.
|
| 231 |
+
// setting this will turn the cursor to a pointer, otherwise cursor defined in overlayCss will be used.
|
| 232 |
+
onOverlayClick: null,
|
| 233 |
+
|
| 234 |
+
// don't ask; if you really must know: http://groups.google.com/group/jquery-en/browse_thread/thread/36640a8730503595/2f6a79a77a78e493#2f6a79a77a78e493
|
| 235 |
+
quirksmodeOffsetHack: 4,
|
| 236 |
+
|
| 237 |
+
// class name of the message block
|
| 238 |
+
blockMsgClass: 'blockMsg',
|
| 239 |
+
|
| 240 |
+
// if it is already blocked, then ignore it (don't unblock and reblock)
|
| 241 |
+
ignoreIfBlocked: false
|
| 242 |
+
};
|
| 243 |
+
|
| 244 |
+
// private data and functions follow...
|
| 245 |
+
|
| 246 |
+
var pageBlock = null;
|
| 247 |
+
var pageBlockEls = [];
|
| 248 |
+
|
| 249 |
+
function install(el, opts) {
|
| 250 |
+
var css, themedCSS;
|
| 251 |
+
var full = (el == window);
|
| 252 |
+
var msg = (opts && opts.message !== undefined ? opts.message : undefined);
|
| 253 |
+
opts = $.extend({}, $.blockUI.defaults, opts || {});
|
| 254 |
+
|
| 255 |
+
if (opts.ignoreIfBlocked && $(el).data('blockUI.isBlocked'))
|
| 256 |
+
return;
|
| 257 |
+
|
| 258 |
+
opts.overlayCSS = $.extend({}, $.blockUI.defaults.overlayCSS, opts.overlayCSS || {});
|
| 259 |
+
css = $.extend({}, $.blockUI.defaults.css, opts.css || {});
|
| 260 |
+
if (opts.onOverlayClick)
|
| 261 |
+
opts.overlayCSS.cursor = 'pointer';
|
| 262 |
+
|
| 263 |
+
themedCSS = $.extend({}, $.blockUI.defaults.themedCSS, opts.themedCSS || {});
|
| 264 |
+
msg = msg === undefined ? opts.message : msg;
|
| 265 |
+
|
| 266 |
+
// remove the current block (if there is one)
|
| 267 |
+
if (full && pageBlock)
|
| 268 |
+
remove(window, {fadeOut:0});
|
| 269 |
+
|
| 270 |
+
// if an existing element is being used as the blocking content then we capture
|
| 271 |
+
// its current place in the DOM (and current display style) so we can restore
|
| 272 |
+
// it when we unblock
|
| 273 |
+
if (msg && typeof msg != 'string' && (msg.parentNode || msg.jquery)) {
|
| 274 |
+
var node = msg.jquery ? msg[0] : msg;
|
| 275 |
+
var data = {};
|
| 276 |
+
$(el).data('blockUI.history', data);
|
| 277 |
+
data.el = node;
|
| 278 |
+
data.parent = node.parentNode;
|
| 279 |
+
data.display = node.style.display;
|
| 280 |
+
data.position = node.style.position;
|
| 281 |
+
if (data.parent)
|
| 282 |
+
data.parent.removeChild(node);
|
| 283 |
+
}
|
| 284 |
+
|
| 285 |
+
$(el).data('blockUI.onUnblock', opts.onUnblock);
|
| 286 |
+
var z = opts.baseZ;
|
| 287 |
+
|
| 288 |
+
// blockUI uses 3 layers for blocking, for simplicity they are all used on every platform;
|
| 289 |
+
// layer1 is the iframe layer which is used to supress bleed through of underlying content
|
| 290 |
+
// layer2 is the overlay layer which has opacity and a wait cursor (by default)
|
| 291 |
+
// layer3 is the message content that is displayed while blocking
|
| 292 |
+
var lyr1, lyr2, lyr3, s;
|
| 293 |
+
if (msie || opts.forceIframe)
|
| 294 |
+
lyr1 = $('<iframe class="blockUI" style="z-index:'+ (z++) +';display:none;border:none;margin:0;padding:0;position:absolute;width:100%;height:100%;top:0;left:0" src="'+opts.iframeSrc+'"></iframe>');
|
| 295 |
+
else
|
| 296 |
+
lyr1 = $('<div class="blockUI" style="display:none"></div>');
|
| 297 |
+
|
| 298 |
+
if (opts.theme)
|
| 299 |
+
lyr2 = $('<div class="blockUI blockOverlay ui-widget-overlay" style="z-index:'+ (z++) +';display:none"></div>');
|
| 300 |
+
else
|
| 301 |
+
lyr2 = $('<div class="blockUI blockOverlay" style="z-index:'+ (z++) +';display:none;border:none;margin:0;padding:0;width:100%;height:100%;top:0;left:0"></div>');
|
| 302 |
+
|
| 303 |
+
if (opts.theme && full) {
|
| 304 |
+
s = '<div class="blockUI ' + opts.blockMsgClass + ' blockPage ui-dialog ui-widget ui-corner-all" style="z-index:'+(z+10)+';display:none;position:fixed">';
|
| 305 |
+
if ( opts.title ) {
|
| 306 |
+
s += '<div class="ui-widget-header ui-dialog-titlebar ui-corner-all blockTitle">'+(opts.title || ' ')+'</div>';
|
| 307 |
+
}
|
| 308 |
+
s += '<div class="ui-widget-content ui-dialog-content"></div>';
|
| 309 |
+
s += '</div>';
|
| 310 |
+
}
|
| 311 |
+
else if (opts.theme) {
|
| 312 |
+
s = '<div class="blockUI ' + opts.blockMsgClass + ' blockElement ui-dialog ui-widget ui-corner-all" style="z-index:'+(z+10)+';display:none;position:absolute">';
|
| 313 |
+
if ( opts.title ) {
|
| 314 |
+
s += '<div class="ui-widget-header ui-dialog-titlebar ui-corner-all blockTitle">'+(opts.title || ' ')+'</div>';
|
| 315 |
+
}
|
| 316 |
+
s += '<div class="ui-widget-content ui-dialog-content"></div>';
|
| 317 |
+
s += '</div>';
|
| 318 |
+
}
|
| 319 |
+
else if (full) {
|
| 320 |
+
s = '<div class="blockUI ' + opts.blockMsgClass + ' blockPage" style="z-index:'+(z+10)+';display:none;position:fixed"></div>';
|
| 321 |
+
}
|
| 322 |
+
else {
|
| 323 |
+
s = '<div class="blockUI ' + opts.blockMsgClass + ' blockElement" style="z-index:'+(z+10)+';display:none;position:absolute"></div>';
|
| 324 |
+
}
|
| 325 |
+
lyr3 = $(s);
|
| 326 |
+
|
| 327 |
+
// if we have a message, style it
|
| 328 |
+
if (msg) {
|
| 329 |
+
if (opts.theme) {
|
| 330 |
+
lyr3.css(themedCSS);
|
| 331 |
+
lyr3.addClass('ui-widget-content');
|
| 332 |
+
}
|
| 333 |
+
else
|
| 334 |
+
lyr3.css(css);
|
| 335 |
+
}
|
| 336 |
+
|
| 337 |
+
// style the overlay
|
| 338 |
+
if (!opts.theme /*&& (!opts.applyPlatformOpacityRules)*/)
|
| 339 |
+
lyr2.css(opts.overlayCSS);
|
| 340 |
+
lyr2.css('position', full ? 'fixed' : 'absolute');
|
| 341 |
+
|
| 342 |
+
// make iframe layer transparent in IE
|
| 343 |
+
if (msie || opts.forceIframe)
|
| 344 |
+
lyr1.css('opacity',0.0);
|
| 345 |
+
|
| 346 |
+
//$([lyr1[0],lyr2[0],lyr3[0]]).appendTo(full ? 'body' : el);
|
| 347 |
+
var layers = [lyr1,lyr2,lyr3], $par = full ? $('body') : $(el);
|
| 348 |
+
$.each(layers, function() {
|
| 349 |
+
this.appendTo($par);
|
| 350 |
+
});
|
| 351 |
+
|
| 352 |
+
if (opts.theme && opts.draggable && $.fn.draggable) {
|
| 353 |
+
lyr3.draggable({
|
| 354 |
+
handle: '.ui-dialog-titlebar',
|
| 355 |
+
cancel: 'li'
|
| 356 |
+
});
|
| 357 |
+
}
|
| 358 |
+
|
| 359 |
+
// ie7 must use absolute positioning in quirks mode and to account for activex issues (when scrolling)
|
| 360 |
+
var expr = setExpr && (!$.support.boxModel || $('object,embed', full ? null : el).length > 0);
|
| 361 |
+
if (ie6 || expr) {
|
| 362 |
+
// give body 100% height
|
| 363 |
+
if (full && opts.allowBodyStretch && $.support.boxModel)
|
| 364 |
+
$('html,body').css('height','100%');
|
| 365 |
+
|
| 366 |
+
// fix ie6 issue when blocked element has a border width
|
| 367 |
+
if ((ie6 || !$.support.boxModel) && !full) {
|
| 368 |
+
var t = sz(el,'borderTopWidth'), l = sz(el,'borderLeftWidth');
|
| 369 |
+
var fixT = t ? '(0 - '+t+')' : 0;
|
| 370 |
+
var fixL = l ? '(0 - '+l+')' : 0;
|
| 371 |
+
}
|
| 372 |
+
|
| 373 |
+
// simulate fixed position
|
| 374 |
+
$.each(layers, function(i,o) {
|
| 375 |
+
var s = o[0].style;
|
| 376 |
+
s.position = 'absolute';
|
| 377 |
+
if (i < 2) {
|
| 378 |
+
if (full)
|
| 379 |
+
s.setExpression('height','Math.max(document.body.scrollHeight, document.body.offsetHeight) - (jQuery.support.boxModel?0:'+opts.quirksmodeOffsetHack+') + "px"');
|
| 380 |
+
else
|
| 381 |
+
s.setExpression('height','this.parentNode.offsetHeight + "px"');
|
| 382 |
+
if (full)
|
| 383 |
+
s.setExpression('width','jQuery.support.boxModel && document.documentElement.clientWidth || document.body.clientWidth + "px"');
|
| 384 |
+
else
|
| 385 |
+
s.setExpression('width','this.parentNode.offsetWidth + "px"');
|
| 386 |
+
if (fixL) s.setExpression('left', fixL);
|
| 387 |
+
if (fixT) s.setExpression('top', fixT);
|
| 388 |
+
}
|
| 389 |
+
else if (opts.centerY) {
|
| 390 |
+
if (full) s.setExpression('top','(document.documentElement.clientHeight || document.body.clientHeight) / 2 - (this.offsetHeight / 2) + (blah = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"');
|
| 391 |
+
s.marginTop = 0;
|
| 392 |
+
}
|
| 393 |
+
else if (!opts.centerY && full) {
|
| 394 |
+
var top = (opts.css && opts.css.top) ? parseInt(opts.css.top, 10) : 0;
|
| 395 |
+
var expression = '((document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + '+top+') + "px"';
|
| 396 |
+
s.setExpression('top',expression);
|
| 397 |
+
}
|
| 398 |
+
});
|
| 399 |
+
}
|
| 400 |
+
|
| 401 |
+
// show the message
|
| 402 |
+
if (msg) {
|
| 403 |
+
if (opts.theme)
|
| 404 |
+
lyr3.find('.ui-widget-content').append(msg);
|
| 405 |
+
else
|
| 406 |
+
lyr3.append(msg);
|
| 407 |
+
if (msg.jquery || msg.nodeType)
|
| 408 |
+
$(msg).show();
|
| 409 |
+
}
|
| 410 |
+
|
| 411 |
+
if ((msie || opts.forceIframe) && opts.showOverlay)
|
| 412 |
+
lyr1.show(); // opacity is zero
|
| 413 |
+
if (opts.fadeIn) {
|
| 414 |
+
var cb = opts.onBlock ? opts.onBlock : noOp;
|
| 415 |
+
var cb1 = (opts.showOverlay && !msg) ? cb : noOp;
|
| 416 |
+
var cb2 = msg ? cb : noOp;
|
| 417 |
+
if (opts.showOverlay)
|
| 418 |
+
lyr2._fadeIn(opts.fadeIn, cb1);
|
| 419 |
+
if (msg)
|
| 420 |
+
lyr3._fadeIn(opts.fadeIn, cb2);
|
| 421 |
+
}
|
| 422 |
+
else {
|
| 423 |
+
if (opts.showOverlay)
|
| 424 |
+
lyr2.show();
|
| 425 |
+
if (msg)
|
| 426 |
+
lyr3.show();
|
| 427 |
+
if (opts.onBlock)
|
| 428 |
+
opts.onBlock.bind(lyr3)();
|
| 429 |
+
}
|
| 430 |
+
|
| 431 |
+
// bind key and mouse events
|
| 432 |
+
bind(1, el, opts);
|
| 433 |
+
|
| 434 |
+
if (full) {
|
| 435 |
+
pageBlock = lyr3[0];
|
| 436 |
+
pageBlockEls = $(opts.focusableElements,pageBlock);
|
| 437 |
+
if (opts.focusInput)
|
| 438 |
+
setTimeout(focus, 20);
|
| 439 |
+
}
|
| 440 |
+
else
|
| 441 |
+
center(lyr3[0], opts.centerX, opts.centerY);
|
| 442 |
+
|
| 443 |
+
if (opts.timeout) {
|
| 444 |
+
// auto-unblock
|
| 445 |
+
var to = setTimeout(function() {
|
| 446 |
+
if (full)
|
| 447 |
+
$.unblockUI(opts);
|
| 448 |
+
else
|
| 449 |
+
$(el).unblock(opts);
|
| 450 |
+
}, opts.timeout);
|
| 451 |
+
$(el).data('blockUI.timeout', to);
|
| 452 |
+
}
|
| 453 |
+
}
|
| 454 |
+
|
| 455 |
+
// remove the block
|
| 456 |
+
function remove(el, opts) {
|
| 457 |
+
var count;
|
| 458 |
+
var full = (el == window);
|
| 459 |
+
var $el = $(el);
|
| 460 |
+
var data = $el.data('blockUI.history');
|
| 461 |
+
var to = $el.data('blockUI.timeout');
|
| 462 |
+
if (to) {
|
| 463 |
+
clearTimeout(to);
|
| 464 |
+
$el.removeData('blockUI.timeout');
|
| 465 |
+
}
|
| 466 |
+
opts = $.extend({}, $.blockUI.defaults, opts || {});
|
| 467 |
+
bind(0, el, opts); // unbind events
|
| 468 |
+
|
| 469 |
+
if (opts.onUnblock === null) {
|
| 470 |
+
opts.onUnblock = $el.data('blockUI.onUnblock');
|
| 471 |
+
$el.removeData('blockUI.onUnblock');
|
| 472 |
+
}
|
| 473 |
+
|
| 474 |
+
var els;
|
| 475 |
+
if (full) // crazy selector to handle odd field errors in ie6/7
|
| 476 |
+
els = $('body').children().filter('.blockUI').add('body > .blockUI');
|
| 477 |
+
else
|
| 478 |
+
els = $el.find('>.blockUI');
|
| 479 |
+
|
| 480 |
+
// fix cursor issue
|
| 481 |
+
if ( opts.cursorReset ) {
|
| 482 |
+
if ( els.length > 1 )
|
| 483 |
+
els[1].style.cursor = opts.cursorReset;
|
| 484 |
+
if ( els.length > 2 )
|
| 485 |
+
els[2].style.cursor = opts.cursorReset;
|
| 486 |
+
}
|
| 487 |
+
|
| 488 |
+
if (full)
|
| 489 |
+
pageBlock = pageBlockEls = null;
|
| 490 |
+
|
| 491 |
+
if (opts.fadeOut) {
|
| 492 |
+
count = els.length;
|
| 493 |
+
els.stop().fadeOut(opts.fadeOut, function() {
|
| 494 |
+
if ( --count === 0)
|
| 495 |
+
reset(els,data,opts,el);
|
| 496 |
+
});
|
| 497 |
+
}
|
| 498 |
+
else
|
| 499 |
+
reset(els, data, opts, el);
|
| 500 |
+
}
|
| 501 |
+
|
| 502 |
+
// move blocking element back into the DOM where it started
|
| 503 |
+
function reset(els,data,opts,el) {
|
| 504 |
+
var $el = $(el);
|
| 505 |
+
if ( $el.data('blockUI.isBlocked') )
|
| 506 |
+
return;
|
| 507 |
+
|
| 508 |
+
els.each(function(i,o) {
|
| 509 |
+
// remove via DOM calls so we don't lose event handlers
|
| 510 |
+
if (this.parentNode)
|
| 511 |
+
this.parentNode.removeChild(this);
|
| 512 |
+
});
|
| 513 |
+
|
| 514 |
+
if (data && data.el) {
|
| 515 |
+
data.el.style.display = data.display;
|
| 516 |
+
data.el.style.position = data.position;
|
| 517 |
+
data.el.style.cursor = 'default'; // #59
|
| 518 |
+
if (data.parent)
|
| 519 |
+
data.parent.appendChild(data.el);
|
| 520 |
+
$el.removeData('blockUI.history');
|
| 521 |
+
}
|
| 522 |
+
|
| 523 |
+
if ($el.data('blockUI.static')) {
|
| 524 |
+
$el.css('position', 'static'); // #22
|
| 525 |
+
}
|
| 526 |
+
|
| 527 |
+
if (typeof opts.onUnblock == 'function')
|
| 528 |
+
opts.onUnblock(el,opts);
|
| 529 |
+
|
| 530 |
+
// fix issue in Safari 6 where block artifacts remain until reflow
|
| 531 |
+
var body = $(document.body), w = body.width(), cssW = body[0].style.width;
|
| 532 |
+
body.width(w-1).width(w);
|
| 533 |
+
body[0].style.width = cssW;
|
| 534 |
+
}
|
| 535 |
+
|
| 536 |
+
// bind/unbind the handler
|
| 537 |
+
function bind(b, el, opts) {
|
| 538 |
+
var full = el == window, $el = $(el);
|
| 539 |
+
|
| 540 |
+
// don't bother unbinding if there is nothing to unbind
|
| 541 |
+
if (!b && (full && !pageBlock || !full && !$el.data('blockUI.isBlocked')))
|
| 542 |
+
return;
|
| 543 |
+
|
| 544 |
+
$el.data('blockUI.isBlocked', b);
|
| 545 |
+
|
| 546 |
+
// don't bind events when overlay is not in use or if bindEvents is false
|
| 547 |
+
if (!full || !opts.bindEvents || (b && !opts.showOverlay))
|
| 548 |
+
return;
|
| 549 |
+
|
| 550 |
+
// bind anchors and inputs for mouse and key events
|
| 551 |
+
var events = 'mousedown mouseup keydown keypress keyup touchstart touchend touchmove';
|
| 552 |
+
if (b)
|
| 553 |
+
$(document).bind(events, opts, handler);
|
| 554 |
+
else
|
| 555 |
+
$(document).unbind(events, handler);
|
| 556 |
+
|
| 557 |
+
// former impl...
|
| 558 |
+
// var $e = $('a,:input');
|
| 559 |
+
// b ? $e.bind(events, opts, handler) : $e.unbind(events, handler);
|
| 560 |
+
}
|
| 561 |
+
|
| 562 |
+
// event handler to suppress keyboard/mouse events when blocking
|
| 563 |
+
function handler(e) {
|
| 564 |
+
// allow tab navigation (conditionally)
|
| 565 |
+
if (e.type === 'keydown' && e.keyCode && e.keyCode == 9) {
|
| 566 |
+
if (pageBlock && e.data.constrainTabKey) {
|
| 567 |
+
var els = pageBlockEls;
|
| 568 |
+
var fwd = !e.shiftKey && e.target === els[els.length-1];
|
| 569 |
+
var back = e.shiftKey && e.target === els[0];
|
| 570 |
+
if (fwd || back) {
|
| 571 |
+
setTimeout(function(){focus(back);},10);
|
| 572 |
+
return false;
|
| 573 |
+
}
|
| 574 |
+
}
|
| 575 |
+
}
|
| 576 |
+
var opts = e.data;
|
| 577 |
+
var target = $(e.target);
|
| 578 |
+
if (target.hasClass('blockOverlay') && opts.onOverlayClick)
|
| 579 |
+
opts.onOverlayClick(e);
|
| 580 |
+
|
| 581 |
+
// allow events within the message content
|
| 582 |
+
if (target.parents('div.' + opts.blockMsgClass).length > 0)
|
| 583 |
+
return true;
|
| 584 |
+
|
| 585 |
+
// allow events for content that is not being blocked
|
| 586 |
+
return target.parents().children().filter('div.blockUI').length === 0;
|
| 587 |
+
}
|
| 588 |
+
|
| 589 |
+
function focus(back) {
|
| 590 |
+
if (!pageBlockEls)
|
| 591 |
+
return;
|
| 592 |
+
var e = pageBlockEls[back===true ? pageBlockEls.length-1 : 0];
|
| 593 |
+
if (e)
|
| 594 |
+
e.focus();
|
| 595 |
+
}
|
| 596 |
+
|
| 597 |
+
function center(el, x, y) {
|
| 598 |
+
var p = el.parentNode, s = el.style;
|
| 599 |
+
var l = ((p.offsetWidth - el.offsetWidth)/2) - sz(p,'borderLeftWidth');
|
| 600 |
+
var t = ((p.offsetHeight - el.offsetHeight)/2) - sz(p,'borderTopWidth');
|
| 601 |
+
if (x) s.left = l > 0 ? (l+'px') : '0';
|
| 602 |
+
if (y) s.top = t > 0 ? (t+'px') : '0';
|
| 603 |
+
}
|
| 604 |
+
|
| 605 |
+
function sz(el, p) {
|
| 606 |
+
return parseInt($.css(el,p),10)||0;
|
| 607 |
+
}
|
| 608 |
+
|
| 609 |
+
}
|
| 610 |
+
|
| 611 |
+
|
| 612 |
+
/*global define:true */
|
| 613 |
+
if (typeof define === 'function' && define.amd && define.amd.jQuery) {
|
| 614 |
+
define(['jquery'], setup);
|
| 615 |
+
} else {
|
| 616 |
+
setup(jQuery);
|
| 617 |
+
}
|
| 618 |
+
|
| 619 |
+
})();
|
|
@@ -0,0 +1,14 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*!
|
| 2 |
+
* jQuery blockUI plugin
|
| 3 |
+
* Version 2.70.0-2014.11.23
|
| 4 |
+
* Requires jQuery v1.7 or later
|
| 5 |
+
*
|
| 6 |
+
* Examples at: http://malsup.com/jquery/block/
|
| 7 |
+
* Copyright (c) 2007-2013 M. Alsup
|
| 8 |
+
* Dual licensed under the MIT and GPL licenses:
|
| 9 |
+
* http://www.opensource.org/licenses/mit-license.php
|
| 10 |
+
* http://www.gnu.org/licenses/gpl.html
|
| 11 |
+
*
|
| 12 |
+
* Thanks to Amir-Hossein Sobhi for some excellent contributions!
|
| 13 |
+
*/
|
| 14 |
+
!function(){"use strict";function a(a){function b(b,d){var f,p,q=b==window,r=d&&void 0!==d.message?d.message:void 0;if(d=a.extend({},a.blockUI.defaults,d||{}),!d.ignoreIfBlocked||!a(b).data("blockUI.isBlocked")){if(d.overlayCSS=a.extend({},a.blockUI.defaults.overlayCSS,d.overlayCSS||{}),f=a.extend({},a.blockUI.defaults.css,d.css||{}),d.onOverlayClick&&(d.overlayCSS.cursor="pointer"),p=a.extend({},a.blockUI.defaults.themedCSS,d.themedCSS||{}),r=void 0===r?d.message:r,q&&n&&c(window,{fadeOut:0}),r&&"string"!=typeof r&&(r.parentNode||r.jquery)){var s=r.jquery?r[0]:r,t={};a(b).data("blockUI.history",t),t.el=s,t.parent=s.parentNode,t.display=s.style.display,t.position=s.style.position,t.parent&&t.parent.removeChild(s)}a(b).data("blockUI.onUnblock",d.onUnblock);var u,v,w,x,y=d.baseZ;u=a(k||d.forceIframe?'<iframe class="blockUI" style="z-index:'+y++ +';display:none;border:none;margin:0;padding:0;position:absolute;width:100%;height:100%;top:0;left:0" src="'+d.iframeSrc+'"></iframe>':'<div class="blockUI" style="display:none"></div>'),v=a(d.theme?'<div class="blockUI blockOverlay ui-widget-overlay" style="z-index:'+y++ +';display:none"></div>':'<div class="blockUI blockOverlay" style="z-index:'+y++ +';display:none;border:none;margin:0;padding:0;width:100%;height:100%;top:0;left:0"></div>'),d.theme&&q?(x='<div class="blockUI '+d.blockMsgClass+' blockPage ui-dialog ui-widget ui-corner-all" style="z-index:'+(y+10)+';display:none;position:fixed">',d.title&&(x+='<div class="ui-widget-header ui-dialog-titlebar ui-corner-all blockTitle">'+(d.title||" ")+"</div>"),x+='<div class="ui-widget-content ui-dialog-content"></div>',x+="</div>"):d.theme?(x='<div class="blockUI '+d.blockMsgClass+' blockElement ui-dialog ui-widget ui-corner-all" style="z-index:'+(y+10)+';display:none;position:absolute">',d.title&&(x+='<div class="ui-widget-header ui-dialog-titlebar ui-corner-all blockTitle">'+(d.title||" ")+"</div>"),x+='<div class="ui-widget-content ui-dialog-content"></div>',x+="</div>"):x=q?'<div class="blockUI '+d.blockMsgClass+' blockPage" style="z-index:'+(y+10)+';display:none;position:fixed"></div>':'<div class="blockUI '+d.blockMsgClass+' blockElement" style="z-index:'+(y+10)+';display:none;position:absolute"></div>',w=a(x),r&&(d.theme?(w.css(p),w.addClass("ui-widget-content")):w.css(f)),d.theme||v.css(d.overlayCSS),v.css("position",q?"fixed":"absolute"),(k||d.forceIframe)&&u.css("opacity",0);var z=[u,v,w],A=a(q?"body":b);a.each(z,function(){this.appendTo(A)}),d.theme&&d.draggable&&a.fn.draggable&&w.draggable({handle:".ui-dialog-titlebar",cancel:"li"});var B=m&&(!a.support.boxModel||a("object,embed",q?null:b).length>0);if(l||B){if(q&&d.allowBodyStretch&&a.support.boxModel&&a("html,body").css("height","100%"),(l||!a.support.boxModel)&&!q)var C=i(b,"borderTopWidth"),D=i(b,"borderLeftWidth"),E=C?"(0 - "+C+")":0,F=D?"(0 - "+D+")":0;a.each(z,function(a,b){var c=b[0].style;if(c.position="absolute",2>a)q?c.setExpression("height","Math.max(document.body.scrollHeight, document.body.offsetHeight) - (jQuery.support.boxModel?0:"+d.quirksmodeOffsetHack+') + "px"'):c.setExpression("height",'this.parentNode.offsetHeight + "px"'),q?c.setExpression("width",'jQuery.support.boxModel && document.documentElement.clientWidth || document.body.clientWidth + "px"'):c.setExpression("width",'this.parentNode.offsetWidth + "px"'),F&&c.setExpression("left",F),E&&c.setExpression("top",E);else if(d.centerY)q&&c.setExpression("top",'(document.documentElement.clientHeight || document.body.clientHeight) / 2 - (this.offsetHeight / 2) + (blah = document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "px"'),c.marginTop=0;else if(!d.centerY&&q){var e=d.css&&d.css.top?parseInt(d.css.top,10):0,f="((document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop) + "+e+') + "px"';c.setExpression("top",f)}})}if(r&&(d.theme?w.find(".ui-widget-content").append(r):w.append(r),(r.jquery||r.nodeType)&&a(r).show()),(k||d.forceIframe)&&d.showOverlay&&u.show(),d.fadeIn){var G=d.onBlock?d.onBlock:j,H=d.showOverlay&&!r?G:j,I=r?G:j;d.showOverlay&&v._fadeIn(d.fadeIn,H),r&&w._fadeIn(d.fadeIn,I)}else d.showOverlay&&v.show(),r&&w.show(),d.onBlock&&d.onBlock.bind(w)();if(e(1,b,d),q?(n=w[0],o=a(d.focusableElements,n),d.focusInput&&setTimeout(g,20)):h(w[0],d.centerX,d.centerY),d.timeout){var J=setTimeout(function(){q?a.unblockUI(d):a(b).unblock(d)},d.timeout);a(b).data("blockUI.timeout",J)}}}function c(b,c){var f,g=b==window,h=a(b),i=h.data("blockUI.history"),j=h.data("blockUI.timeout");j&&(clearTimeout(j),h.removeData("blockUI.timeout")),c=a.extend({},a.blockUI.defaults,c||{}),e(0,b,c),null===c.onUnblock&&(c.onUnblock=h.data("blockUI.onUnblock"),h.removeData("blockUI.onUnblock"));var k;k=g?a("body").children().filter(".blockUI").add("body > .blockUI"):h.find(">.blockUI"),c.cursorReset&&(k.length>1&&(k[1].style.cursor=c.cursorReset),k.length>2&&(k[2].style.cursor=c.cursorReset)),g&&(n=o=null),c.fadeOut?(f=k.length,k.stop().fadeOut(c.fadeOut,function(){0===--f&&d(k,i,c,b)})):d(k,i,c,b)}function d(b,c,d,e){var f=a(e);if(!f.data("blockUI.isBlocked")){b.each(function(){this.parentNode&&this.parentNode.removeChild(this)}),c&&c.el&&(c.el.style.display=c.display,c.el.style.position=c.position,c.el.style.cursor="default",c.parent&&c.parent.appendChild(c.el),f.removeData("blockUI.history")),f.data("blockUI.static")&&f.css("position","static"),"function"==typeof d.onUnblock&&d.onUnblock(e,d);var g=a(document.body),h=g.width(),i=g[0].style.width;g.width(h-1).width(h),g[0].style.width=i}}function e(b,c,d){var e=c==window,g=a(c);if((b||(!e||n)&&(e||g.data("blockUI.isBlocked")))&&(g.data("blockUI.isBlocked",b),e&&d.bindEvents&&(!b||d.showOverlay))){var h="mousedown mouseup keydown keypress keyup touchstart touchend touchmove";b?a(document).bind(h,d,f):a(document).unbind(h,f)}}function f(b){if("keydown"===b.type&&b.keyCode&&9==b.keyCode&&n&&b.data.constrainTabKey){var c=o,d=!b.shiftKey&&b.target===c[c.length-1],e=b.shiftKey&&b.target===c[0];if(d||e)return setTimeout(function(){g(e)},10),!1}var f=b.data,h=a(b.target);return h.hasClass("blockOverlay")&&f.onOverlayClick&&f.onOverlayClick(b),h.parents("div."+f.blockMsgClass).length>0?!0:0===h.parents().children().filter("div.blockUI").length}function g(a){if(o){var b=o[a===!0?o.length-1:0];b&&b.focus()}}function h(a,b,c){var d=a.parentNode,e=a.style,f=(d.offsetWidth-a.offsetWidth)/2-i(d,"borderLeftWidth"),g=(d.offsetHeight-a.offsetHeight)/2-i(d,"borderTopWidth");b&&(e.left=f>0?f+"px":"0"),c&&(e.top=g>0?g+"px":"0")}function i(b,c){return parseInt(a.css(b,c),10)||0}a.fn._fadeIn=a.fn.fadeIn;var j=a.noop||function(){},k=/MSIE/.test(navigator.userAgent),l=/MSIE 6.0/.test(navigator.userAgent)&&!/MSIE 8.0/.test(navigator.userAgent),m=(document.documentMode||0,a.isFunction(document.createElement("div").style.setExpression));a.blockUI=function(a){b(window,a)},a.unblockUI=function(a){c(window,a)},a.growlUI=function(b,c,d,e){var f=a('<div class="growlUI"></div>');b&&f.append("<h1>"+b+"</h1>"),c&&f.append("<h2>"+c+"</h2>"),void 0===d&&(d=3e3);var g=function(b){b=b||{},a.blockUI({message:f,fadeIn:"undefined"!=typeof b.fadeIn?b.fadeIn:700,fadeOut:"undefined"!=typeof b.fadeOut?b.fadeOut:1e3,timeout:"undefined"!=typeof b.timeout?b.timeout:d,centerY:!1,showOverlay:!1,onUnblock:e,css:a.blockUI.defaults.growlCSS})};g();f.css("opacity");f.mouseover(function(){g({fadeIn:0,timeout:3e4});var b=a(".blockMsg");b.stop(),b.fadeTo(300,1)}).mouseout(function(){a(".blockMsg").fadeOut(1e3)})},a.fn.block=function(c){if(this[0]===window)return a.blockUI(c),this;var d=a.extend({},a.blockUI.defaults,c||{});return this.each(function(){var b=a(this);d.ignoreIfBlocked&&b.data("blockUI.isBlocked")||b.unblock({fadeOut:0})}),this.each(function(){"static"==a.css(this,"position")&&(this.style.position="relative",a(this).data("blockUI.static",!0)),this.style.zoom=1,b(this,c)})},a.fn.unblock=function(b){return this[0]===window?(a.unblockUI(b),this):this.each(function(){c(this,b)})},a.blockUI.version=2.7,a.blockUI.defaults={message:"<h1>Please wait...</h1>",title:null,draggable:!0,theme:!1,css:{padding:0,margin:0,width:"30%",top:"40%",left:"35%",textAlign:"center",color:"#000",border:"3px solid #aaa",backgroundColor:"#fff",cursor:"wait"},themedCSS:{width:"30%",top:"40%",left:"35%"},overlayCSS:{backgroundColor:"#000",opacity:.6,cursor:"wait"},cursorReset:"default",growlCSS:{width:"350px",top:"10px",left:"",right:"10px",border:"none",padding:"5px",opacity:.6,cursor:"default",color:"#fff",backgroundColor:"#000","-webkit-border-radius":"10px","-moz-border-radius":"10px","border-radius":"10px"},iframeSrc:/^https/i.test(window.location.href||"")?"javascript:false":"about:blank",forceIframe:!1,baseZ:1e3,centerX:!0,centerY:!0,allowBodyStretch:!0,bindEvents:!0,constrainTabKey:!0,fadeIn:200,fadeOut:400,timeout:0,showOverlay:!0,focusInput:!0,focusableElements:":input:enabled:visible",onBlock:null,onUnblock:null,onOverlayClick:null,quirksmodeOffsetHack:4,blockMsgClass:"blockMsg",ignoreIfBlocked:!1};var n=null,o=[]}"function"==typeof define&&define.amd&&define.amd.jQuery?define(["jquery"],a):a(jQuery)}();
|
|
@@ -0,0 +1,129 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
|
| 3 |
+
if (!defined('ABSPATH')) die('No direct access.');
|
| 4 |
+
|
| 5 |
+
/**
|
| 6 |
+
* Purpose of this class: abstract out code handling integrations with login forms
|
| 7 |
+
*/
|
| 8 |
+
|
| 9 |
+
class Simba_TFA_Login_Form_Integrations {
|
| 10 |
+
|
| 11 |
+
// Main class
|
| 12 |
+
private $tfa;
|
| 13 |
+
|
| 14 |
+
/**
|
| 15 |
+
* Plugin constructor
|
| 16 |
+
*
|
| 17 |
+
* @param Object $tfa
|
| 18 |
+
*/
|
| 19 |
+
public function __construct($tfa) {
|
| 20 |
+
|
| 21 |
+
$this->tfa = $tfa;
|
| 22 |
+
|
| 23 |
+
$enqueue_upon_actions = array(
|
| 24 |
+
// This is needed for the login form on the dedicated payment page (e.g. /checkout/order-pay/123456/?pay_for_order=true&key=wc_order_blahblahblah)
|
| 25 |
+
'woocommerce_login_form_start',
|
| 26 |
+
'woocommerce_before_customer_login_form',
|
| 27 |
+
// The login form on the checkout doesn't call the woocommerce_before_customer_login_form action
|
| 28 |
+
'woocommerce_before_checkout_form',
|
| 29 |
+
'affwp_login_fields_before',
|
| 30 |
+
);
|
| 31 |
+
|
| 32 |
+
foreach ($enqueue_upon_actions as $action) {
|
| 33 |
+
add_action($action, array($this->tfa, 'login_enqueue_scripts'));
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
if (!defined('TWO_FACTOR_DISABLE') || !TWO_FACTOR_DISABLE) {
|
| 37 |
+
add_action('affwp_process_login_form', array($this, 'affwp_process_login_form'));
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
add_filter('tml_display', array($this, 'tml_display'));
|
| 41 |
+
|
| 42 |
+
// We want to run first if possible, so that we're not aborted by JavaScript exceptions in other components (our code is critical to the login process for TFA users)
|
| 43 |
+
// Unfortunately, though, people start enqueuing from init onwards (before that is buggy - https://core.trac.wordpress.org/ticket/11526), so, we try to detect the login page and go earlier there.
|
| 44 |
+
if (isset($GLOBALS['pagenow']) && 'wp-login.php' === $GLOBALS['pagenow']) {
|
| 45 |
+
add_action('init', array($this->tfa, 'login_enqueue_scripts'), -99999999999);
|
| 46 |
+
} else {
|
| 47 |
+
add_action('login_enqueue_scripts', array($this->tfa, 'login_enqueue_scripts'), -99999999999);
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
add_filter('do_shortcode_tag', array($this, 'do_shortcode_tag'), 10, 2);
|
| 51 |
+
|
| 52 |
+
add_filter('simba_tfa_login_enqueue_localize', array($this, 'simba_tfa_login_enqueue_localize'), 9);
|
| 53 |
+
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
/**
|
| 57 |
+
* Catch TML login widgets (other TML login forms already trigger)
|
| 58 |
+
*
|
| 59 |
+
* @param Mixed $whatever
|
| 60 |
+
*
|
| 61 |
+
* @return Mixed
|
| 62 |
+
*/
|
| 63 |
+
public function tml_display($whatever) {
|
| 64 |
+
$this->tfa->login_enqueue_scripts();
|
| 65 |
+
return $whatever;
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
/**
|
| 69 |
+
* Runs upon the WP filter simba_tfa_login_enqueue_localize.
|
| 70 |
+
*
|
| 71 |
+
* @param Array $localize
|
| 72 |
+
*
|
| 73 |
+
* @return Array
|
| 74 |
+
*/
|
| 75 |
+
public function simba_tfa_login_enqueue_localize($localize) {
|
| 76 |
+
// WP login form is #loginform
|
| 77 |
+
// Ultimate Membership Pro - April 2018
|
| 78 |
+
// Theme My Login 6.x - .tml-login form[name="loginform"]
|
| 79 |
+
// Theme My Login 7.x - .tml-login form[name="login"] (July 2018)
|
| 80 |
+
// WP Members - March 2018
|
| 81 |
+
// bbPress - June 2021
|
| 82 |
+
// WooCommerce - ported over from the separate wooextend.js code, June 2021
|
| 83 |
+
// Affiliates WP - ported over from the separate wooextend.js code, June 2021
|
| 84 |
+
$localize['login_form_selectors'] .= '.tml-login form[name="loginform"], .tml-login form[name="login"], #loginform, #wpmem_login form, form#ihc_login_form, .bbp-login-form, .woocommerce form.login, #affwp-login-form';
|
| 85 |
+
$localize['login_form_off_selectors'] .= '#ihc_login_form';
|
| 86 |
+
return $localize;
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
/**
|
| 90 |
+
* Runs upon the WP action affwp_process_login_form
|
| 91 |
+
*/
|
| 92 |
+
public function affwp_process_login_form() {
|
| 93 |
+
|
| 94 |
+
if (!function_exists('affiliate_wp')) return;
|
| 95 |
+
|
| 96 |
+
$affiliate_wp = affiliate_wp();
|
| 97 |
+
$login = $affiliate_wp->login;
|
| 98 |
+
|
| 99 |
+
$params = array(
|
| 100 |
+
'log' => stripslashes($_POST['affwp_user_login']),
|
| 101 |
+
'caller'=> $_SERVER['PHP_SELF'] ? $_SERVER['PHP_SELF'] : $_SERVER['REQUEST_URI'],
|
| 102 |
+
'two_factor_code' => stripslashes((string) $_POST['two_factor_code'])
|
| 103 |
+
);
|
| 104 |
+
$code_ok = $this->tfa->authorise_user_from_login($params, true);
|
| 105 |
+
|
| 106 |
+
$code_ok = apply_filters('simbatfa_affwp_process_login_form_auth_result', $code_ok, $params);
|
| 107 |
+
|
| 108 |
+
if (is_wp_error($code_ok)) {
|
| 109 |
+
$login->add_error($code_ok->get_error_code, $code_ok->get_error_message());
|
| 110 |
+
} elseif (!$code_ok) {
|
| 111 |
+
$login->add_error('authentication_failed', __('Error:', 'all-in-one-wp-security-and-firewall').' '.__('The one-time password (TFA code) you entered was incorrect.', 'all-in-one-wp-security-and-firewall'));
|
| 112 |
+
}
|
| 113 |
+
|
| 114 |
+
}
|
| 115 |
+
|
| 116 |
+
/**
|
| 117 |
+
* Ultimate Membership Pro support
|
| 118 |
+
*
|
| 119 |
+
* @param String $output
|
| 120 |
+
* @param String $tag
|
| 121 |
+
*
|
| 122 |
+
* @return String
|
| 123 |
+
*/
|
| 124 |
+
public function do_shortcode_tag($output, $tag) {
|
| 125 |
+
if ('ihc-login-form' == $tag) $this->tfa->login_enqueue_scripts();
|
| 126 |
+
return $output;
|
| 127 |
+
}
|
| 128 |
+
|
| 129 |
+
}
|
|
@@ -0,0 +1,482 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
.select2-container {
|
| 2 |
+
box-sizing: border-box;
|
| 3 |
+
display: inline-block;
|
| 4 |
+
margin: 0;
|
| 5 |
+
position: relative;
|
| 6 |
+
vertical-align: middle; }
|
| 7 |
+
.select2-container .select2-selection--single {
|
| 8 |
+
box-sizing: border-box;
|
| 9 |
+
cursor: pointer;
|
| 10 |
+
display: block;
|
| 11 |
+
height: 28px;
|
| 12 |
+
user-select: none;
|
| 13 |
+
-webkit-user-select: none; }
|
| 14 |
+
.select2-container .select2-selection--single .select2-selection__rendered {
|
| 15 |
+
display: block;
|
| 16 |
+
padding-left: 8px;
|
| 17 |
+
padding-right: 20px;
|
| 18 |
+
overflow: hidden;
|
| 19 |
+
text-overflow: ellipsis;
|
| 20 |
+
white-space: nowrap; }
|
| 21 |
+
.select2-container .select2-selection--single .select2-selection__clear {
|
| 22 |
+
position: relative; }
|
| 23 |
+
.select2-container[dir="rtl"] .select2-selection--single .select2-selection__rendered {
|
| 24 |
+
padding-right: 8px;
|
| 25 |
+
padding-left: 20px; }
|
| 26 |
+
.select2-container .select2-selection--multiple {
|
| 27 |
+
box-sizing: border-box;
|
| 28 |
+
cursor: pointer;
|
| 29 |
+
display: block;
|
| 30 |
+
min-height: 32px;
|
| 31 |
+
user-select: none;
|
| 32 |
+
-webkit-user-select: none; }
|
| 33 |
+
.select2-container .select2-selection--multiple .select2-selection__rendered {
|
| 34 |
+
display: inline-block;
|
| 35 |
+
overflow: hidden;
|
| 36 |
+
padding-left: 8px;
|
| 37 |
+
text-overflow: ellipsis;
|
| 38 |
+
white-space: nowrap; }
|
| 39 |
+
.select2-container .select2-search--inline {
|
| 40 |
+
float: left; }
|
| 41 |
+
.select2-container .select2-search--inline .select2-search__field {
|
| 42 |
+
box-sizing: border-box;
|
| 43 |
+
border: none;
|
| 44 |
+
font-size: 100%;
|
| 45 |
+
margin-top: 5px;
|
| 46 |
+
padding: 0; }
|
| 47 |
+
.select2-container .select2-search--inline .select2-search__field::-webkit-search-cancel-button {
|
| 48 |
+
-webkit-appearance: none; }
|
| 49 |
+
|
| 50 |
+
.select2-dropdown {
|
| 51 |
+
background-color: white;
|
| 52 |
+
border: 1px solid #aaa;
|
| 53 |
+
border-radius: 4px;
|
| 54 |
+
box-sizing: border-box;
|
| 55 |
+
display: block;
|
| 56 |
+
position: absolute;
|
| 57 |
+
left: -100000px;
|
| 58 |
+
width: 100%;
|
| 59 |
+
z-index: 1051; }
|
| 60 |
+
|
| 61 |
+
.select2-results {
|
| 62 |
+
display: block; }
|
| 63 |
+
|
| 64 |
+
.select2-results__options {
|
| 65 |
+
list-style: none;
|
| 66 |
+
margin: 0;
|
| 67 |
+
padding: 0; }
|
| 68 |
+
|
| 69 |
+
.select2-results__option {
|
| 70 |
+
padding: 6px;
|
| 71 |
+
user-select: none;
|
| 72 |
+
-webkit-user-select: none; }
|
| 73 |
+
.select2-results__option[aria-selected] {
|
| 74 |
+
cursor: pointer; }
|
| 75 |
+
|
| 76 |
+
.select2-container--open .select2-dropdown {
|
| 77 |
+
left: 0; }
|
| 78 |
+
|
| 79 |
+
.select2-container--open .select2-dropdown--above {
|
| 80 |
+
border-bottom: none;
|
| 81 |
+
border-bottom-left-radius: 0;
|
| 82 |
+
border-bottom-right-radius: 0; }
|
| 83 |
+
|
| 84 |
+
.select2-container--open .select2-dropdown--below {
|
| 85 |
+
border-top: none;
|
| 86 |
+
border-top-left-radius: 0;
|
| 87 |
+
border-top-right-radius: 0; }
|
| 88 |
+
|
| 89 |
+
.select2-search--dropdown {
|
| 90 |
+
display: block;
|
| 91 |
+
padding: 4px; }
|
| 92 |
+
.select2-search--dropdown .select2-search__field {
|
| 93 |
+
padding: 4px;
|
| 94 |
+
width: 100%;
|
| 95 |
+
box-sizing: border-box; }
|
| 96 |
+
.select2-search--dropdown .select2-search__field::-webkit-search-cancel-button {
|
| 97 |
+
-webkit-appearance: none; }
|
| 98 |
+
.select2-search--dropdown.select2-search--hide {
|
| 99 |
+
display: none; }
|
| 100 |
+
|
| 101 |
+
.select2-close-mask {
|
| 102 |
+
border: 0;
|
| 103 |
+
margin: 0;
|
| 104 |
+
padding: 0;
|
| 105 |
+
display: block;
|
| 106 |
+
position: fixed;
|
| 107 |
+
left: 0;
|
| 108 |
+
top: 0;
|
| 109 |
+
min-height: 100%;
|
| 110 |
+
min-width: 100%;
|
| 111 |
+
height: auto;
|
| 112 |
+
width: auto;
|
| 113 |
+
opacity: 0;
|
| 114 |
+
z-index: 99;
|
| 115 |
+
background-color: #fff;
|
| 116 |
+
filter: alpha(opacity=0); }
|
| 117 |
+
|
| 118 |
+
.select2-hidden-accessible {
|
| 119 |
+
border: 0 !important;
|
| 120 |
+
clip: rect(0 0 0 0) !important;
|
| 121 |
+
height: 1px !important;
|
| 122 |
+
margin: -1px !important;
|
| 123 |
+
overflow: hidden !important;
|
| 124 |
+
padding: 0 !important;
|
| 125 |
+
position: absolute !important;
|
| 126 |
+
width: 1px !important; }
|
| 127 |
+
|
| 128 |
+
.select2-container--default .select2-selection--single {
|
| 129 |
+
background-color: #fff;
|
| 130 |
+
border: 1px solid #aaa;
|
| 131 |
+
border-radius: 4px; }
|
| 132 |
+
.select2-container--default .select2-selection--single .select2-selection__rendered {
|
| 133 |
+
color: #444;
|
| 134 |
+
line-height: 28px; }
|
| 135 |
+
.select2-container--default .select2-selection--single .select2-selection__clear {
|
| 136 |
+
cursor: pointer;
|
| 137 |
+
float: right;
|
| 138 |
+
font-weight: bold; }
|
| 139 |
+
.select2-container--default .select2-selection--single .select2-selection__placeholder {
|
| 140 |
+
color: #999; }
|
| 141 |
+
.select2-container--default .select2-selection--single .select2-selection__arrow {
|
| 142 |
+
height: 26px;
|
| 143 |
+
position: absolute;
|
| 144 |
+
top: 1px;
|
| 145 |
+
right: 1px;
|
| 146 |
+
width: 20px; }
|
| 147 |
+
.select2-container--default .select2-selection--single .select2-selection__arrow b {
|
| 148 |
+
border-color: #888 transparent transparent transparent;
|
| 149 |
+
border-style: solid;
|
| 150 |
+
border-width: 5px 4px 0 4px;
|
| 151 |
+
height: 0;
|
| 152 |
+
left: 50%;
|
| 153 |
+
margin-left: -4px;
|
| 154 |
+
margin-top: -2px;
|
| 155 |
+
position: absolute;
|
| 156 |
+
top: 50%;
|
| 157 |
+
width: 0; }
|
| 158 |
+
|
| 159 |
+
.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__clear {
|
| 160 |
+
float: left; }
|
| 161 |
+
|
| 162 |
+
.select2-container--default[dir="rtl"] .select2-selection--single .select2-selection__arrow {
|
| 163 |
+
left: 1px;
|
| 164 |
+
right: auto; }
|
| 165 |
+
|
| 166 |
+
.select2-container--default.select2-container--disabled .select2-selection--single {
|
| 167 |
+
background-color: #eee;
|
| 168 |
+
cursor: default; }
|
| 169 |
+
.select2-container--default.select2-container--disabled .select2-selection--single .select2-selection__clear {
|
| 170 |
+
display: none; }
|
| 171 |
+
|
| 172 |
+
.select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b {
|
| 173 |
+
border-color: transparent transparent #888 transparent;
|
| 174 |
+
border-width: 0 4px 5px 4px; }
|
| 175 |
+
|
| 176 |
+
.select2-container--default .select2-selection--multiple {
|
| 177 |
+
background-color: white;
|
| 178 |
+
border: 1px solid #aaa;
|
| 179 |
+
border-radius: 4px;
|
| 180 |
+
cursor: text; }
|
| 181 |
+
.select2-container--default .select2-selection--multiple .select2-selection__rendered {
|
| 182 |
+
box-sizing: border-box;
|
| 183 |
+
list-style: none;
|
| 184 |
+
margin: 0;
|
| 185 |
+
padding: 0 5px;
|
| 186 |
+
width: 100%; }
|
| 187 |
+
.select2-container--default .select2-selection--multiple .select2-selection__placeholder {
|
| 188 |
+
color: #999;
|
| 189 |
+
margin-top: 5px;
|
| 190 |
+
float: left; }
|
| 191 |
+
.select2-container--default .select2-selection--multiple .select2-selection__clear {
|
| 192 |
+
cursor: pointer;
|
| 193 |
+
float: right;
|
| 194 |
+
font-weight: bold;
|
| 195 |
+
margin-top: 5px;
|
| 196 |
+
margin-right: 10px; }
|
| 197 |
+
.select2-container--default .select2-selection--multiple .select2-selection__choice {
|
| 198 |
+
background-color: #e4e4e4;
|
| 199 |
+
border: 1px solid #aaa;
|
| 200 |
+
border-radius: 4px;
|
| 201 |
+
cursor: default;
|
| 202 |
+
float: left;
|
| 203 |
+
margin-right: 5px;
|
| 204 |
+
margin-top: 5px;
|
| 205 |
+
padding: 0 5px; }
|
| 206 |
+
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove {
|
| 207 |
+
color: #999;
|
| 208 |
+
cursor: pointer;
|
| 209 |
+
display: inline-block;
|
| 210 |
+
font-weight: bold;
|
| 211 |
+
margin-right: 2px; }
|
| 212 |
+
.select2-container--default .select2-selection--multiple .select2-selection__choice__remove:hover {
|
| 213 |
+
color: #333; }
|
| 214 |
+
|
| 215 |
+
.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice, .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__placeholder, .select2-container--default[dir="rtl"] .select2-selection--multiple .select2-search--inline {
|
| 216 |
+
float: right; }
|
| 217 |
+
|
| 218 |
+
.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
|
| 219 |
+
margin-left: 5px;
|
| 220 |
+
margin-right: auto; }
|
| 221 |
+
|
| 222 |
+
.select2-container--default[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove {
|
| 223 |
+
margin-left: 2px;
|
| 224 |
+
margin-right: auto; }
|
| 225 |
+
|
| 226 |
+
.select2-container--default.select2-container--focus .select2-selection--multiple {
|
| 227 |
+
border: solid black 1px;
|
| 228 |
+
outline: 0; }
|
| 229 |
+
|
| 230 |
+
.select2-container--default.select2-container--disabled .select2-selection--multiple {
|
| 231 |
+
background-color: #eee;
|
| 232 |
+
cursor: default; }
|
| 233 |
+
|
| 234 |
+
.select2-container--default.select2-container--disabled .select2-selection__choice__remove {
|
| 235 |
+
display: none; }
|
| 236 |
+
|
| 237 |
+
.select2-container--default.select2-container--open.select2-container--above .select2-selection--single, .select2-container--default.select2-container--open.select2-container--above .select2-selection--multiple {
|
| 238 |
+
border-top-left-radius: 0;
|
| 239 |
+
border-top-right-radius: 0; }
|
| 240 |
+
|
| 241 |
+
.select2-container--default.select2-container--open.select2-container--below .select2-selection--single, .select2-container--default.select2-container--open.select2-container--below .select2-selection--multiple {
|
| 242 |
+
border-bottom-left-radius: 0;
|
| 243 |
+
border-bottom-right-radius: 0; }
|
| 244 |
+
|
| 245 |
+
.select2-container--default .select2-search--dropdown .select2-search__field {
|
| 246 |
+
border: 1px solid #aaa; }
|
| 247 |
+
|
| 248 |
+
.select2-container--default .select2-search--inline .select2-search__field {
|
| 249 |
+
background: transparent;
|
| 250 |
+
border: none;
|
| 251 |
+
outline: 0;
|
| 252 |
+
box-shadow: none;
|
| 253 |
+
-webkit-appearance: textfield; }
|
| 254 |
+
|
| 255 |
+
.select2-container--default .select2-results > .select2-results__options {
|
| 256 |
+
max-height: 200px;
|
| 257 |
+
overflow-y: auto; }
|
| 258 |
+
|
| 259 |
+
.select2-container--default .select2-results__option[role=group] {
|
| 260 |
+
padding: 0; }
|
| 261 |
+
|
| 262 |
+
.select2-container--default .select2-results__option[aria-disabled=true] {
|
| 263 |
+
color: #999; }
|
| 264 |
+
|
| 265 |
+
.select2-container--default .select2-results__option[aria-selected=true] {
|
| 266 |
+
background-color: #ddd; }
|
| 267 |
+
|
| 268 |
+
.select2-container--default .select2-results__option .select2-results__option {
|
| 269 |
+
padding-left: 1em; }
|
| 270 |
+
.select2-container--default .select2-results__option .select2-results__option .select2-results__group {
|
| 271 |
+
padding-left: 0; }
|
| 272 |
+
.select2-container--default .select2-results__option .select2-results__option .select2-results__option {
|
| 273 |
+
margin-left: -1em;
|
| 274 |
+
padding-left: 2em; }
|
| 275 |
+
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
|
| 276 |
+
margin-left: -2em;
|
| 277 |
+
padding-left: 3em; }
|
| 278 |
+
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
|
| 279 |
+
margin-left: -3em;
|
| 280 |
+
padding-left: 4em; }
|
| 281 |
+
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
|
| 282 |
+
margin-left: -4em;
|
| 283 |
+
padding-left: 5em; }
|
| 284 |
+
.select2-container--default .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option .select2-results__option {
|
| 285 |
+
margin-left: -5em;
|
| 286 |
+
padding-left: 6em; }
|
| 287 |
+
|
| 288 |
+
.select2-container--default .select2-results__option--highlighted[aria-selected] {
|
| 289 |
+
background-color: #5897fb;
|
| 290 |
+
color: white; }
|
| 291 |
+
|
| 292 |
+
.select2-container--default .select2-results__group {
|
| 293 |
+
cursor: default;
|
| 294 |
+
display: block;
|
| 295 |
+
padding: 6px; }
|
| 296 |
+
|
| 297 |
+
.select2-container--classic .select2-selection--single {
|
| 298 |
+
background-color: #f7f7f7;
|
| 299 |
+
border: 1px solid #aaa;
|
| 300 |
+
border-radius: 4px;
|
| 301 |
+
outline: 0;
|
| 302 |
+
background-image: -webkit-linear-gradient(top, white 50%, #eeeeee 100%);
|
| 303 |
+
background-image: -o-linear-gradient(top, white 50%, #eeeeee 100%);
|
| 304 |
+
background-image: linear-gradient(to bottom, white 50%, #eeeeee 100%);
|
| 305 |
+
background-repeat: repeat-x;
|
| 306 |
+
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0); }
|
| 307 |
+
.select2-container--classic .select2-selection--single:focus {
|
| 308 |
+
border: 1px solid #5897fb; }
|
| 309 |
+
.select2-container--classic .select2-selection--single .select2-selection__rendered {
|
| 310 |
+
color: #444;
|
| 311 |
+
line-height: 28px; }
|
| 312 |
+
.select2-container--classic .select2-selection--single .select2-selection__clear {
|
| 313 |
+
cursor: pointer;
|
| 314 |
+
float: right;
|
| 315 |
+
font-weight: bold;
|
| 316 |
+
margin-right: 10px; }
|
| 317 |
+
.select2-container--classic .select2-selection--single .select2-selection__placeholder {
|
| 318 |
+
color: #999; }
|
| 319 |
+
.select2-container--classic .select2-selection--single .select2-selection__arrow {
|
| 320 |
+
background-color: #ddd;
|
| 321 |
+
border: none;
|
| 322 |
+
border-left: 1px solid #aaa;
|
| 323 |
+
border-top-right-radius: 4px;
|
| 324 |
+
border-bottom-right-radius: 4px;
|
| 325 |
+
height: 26px;
|
| 326 |
+
position: absolute;
|
| 327 |
+
top: 1px;
|
| 328 |
+
right: 1px;
|
| 329 |
+
width: 20px;
|
| 330 |
+
background-image: -webkit-linear-gradient(top, #eeeeee 50%, #cccccc 100%);
|
| 331 |
+
background-image: -o-linear-gradient(top, #eeeeee 50%, #cccccc 100%);
|
| 332 |
+
background-image: linear-gradient(to bottom, #eeeeee 50%, #cccccc 100%);
|
| 333 |
+
background-repeat: repeat-x;
|
| 334 |
+
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFCCCCCC', GradientType=0); }
|
| 335 |
+
.select2-container--classic .select2-selection--single .select2-selection__arrow b {
|
| 336 |
+
border-color: #888 transparent transparent transparent;
|
| 337 |
+
border-style: solid;
|
| 338 |
+
border-width: 5px 4px 0 4px;
|
| 339 |
+
height: 0;
|
| 340 |
+
left: 50%;
|
| 341 |
+
margin-left: -4px;
|
| 342 |
+
margin-top: -2px;
|
| 343 |
+
position: absolute;
|
| 344 |
+
top: 50%;
|
| 345 |
+
width: 0; }
|
| 346 |
+
|
| 347 |
+
.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__clear {
|
| 348 |
+
float: left; }
|
| 349 |
+
|
| 350 |
+
.select2-container--classic[dir="rtl"] .select2-selection--single .select2-selection__arrow {
|
| 351 |
+
border: none;
|
| 352 |
+
border-right: 1px solid #aaa;
|
| 353 |
+
border-radius: 0;
|
| 354 |
+
border-top-left-radius: 4px;
|
| 355 |
+
border-bottom-left-radius: 4px;
|
| 356 |
+
left: 1px;
|
| 357 |
+
right: auto; }
|
| 358 |
+
|
| 359 |
+
.select2-container--classic.select2-container--open .select2-selection--single {
|
| 360 |
+
border: 1px solid #5897fb; }
|
| 361 |
+
.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow {
|
| 362 |
+
background: transparent;
|
| 363 |
+
border: none; }
|
| 364 |
+
.select2-container--classic.select2-container--open .select2-selection--single .select2-selection__arrow b {
|
| 365 |
+
border-color: transparent transparent #888 transparent;
|
| 366 |
+
border-width: 0 4px 5px 4px; }
|
| 367 |
+
|
| 368 |
+
.select2-container--classic.select2-container--open.select2-container--above .select2-selection--single {
|
| 369 |
+
border-top: none;
|
| 370 |
+
border-top-left-radius: 0;
|
| 371 |
+
border-top-right-radius: 0;
|
| 372 |
+
background-image: -webkit-linear-gradient(top, white 0%, #eeeeee 50%);
|
| 373 |
+
background-image: -o-linear-gradient(top, white 0%, #eeeeee 50%);
|
| 374 |
+
background-image: linear-gradient(to bottom, white 0%, #eeeeee 50%);
|
| 375 |
+
background-repeat: repeat-x;
|
| 376 |
+
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF', endColorstr='#FFEEEEEE', GradientType=0); }
|
| 377 |
+
|
| 378 |
+
.select2-container--classic.select2-container--open.select2-container--below .select2-selection--single {
|
| 379 |
+
border-bottom: none;
|
| 380 |
+
border-bottom-left-radius: 0;
|
| 381 |
+
border-bottom-right-radius: 0;
|
| 382 |
+
background-image: -webkit-linear-gradient(top, #eeeeee 50%, white 100%);
|
| 383 |
+
background-image: -o-linear-gradient(top, #eeeeee 50%, white 100%);
|
| 384 |
+
background-image: linear-gradient(to bottom, #eeeeee 50%, white 100%);
|
| 385 |
+
background-repeat: repeat-x;
|
| 386 |
+
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFEEEEEE', endColorstr='#FFFFFFFF', GradientType=0); }
|
| 387 |
+
|
| 388 |
+
.select2-container--classic .select2-selection--multiple {
|
| 389 |
+
background-color: white;
|
| 390 |
+
border: 1px solid #aaa;
|
| 391 |
+
border-radius: 4px;
|
| 392 |
+
cursor: text;
|
| 393 |
+
outline: 0; }
|
| 394 |
+
.select2-container--classic .select2-selection--multiple:focus {
|
| 395 |
+
border: 1px solid #5897fb; }
|
| 396 |
+
.select2-container--classic .select2-selection--multiple .select2-selection__rendered {
|
| 397 |
+
list-style: none;
|
| 398 |
+
margin: 0;
|
| 399 |
+
padding: 0 5px; }
|
| 400 |
+
.select2-container--classic .select2-selection--multiple .select2-selection__clear {
|
| 401 |
+
display: none; }
|
| 402 |
+
.select2-container--classic .select2-selection--multiple .select2-selection__choice {
|
| 403 |
+
background-color: #e4e4e4;
|
| 404 |
+
border: 1px solid #aaa;
|
| 405 |
+
border-radius: 4px;
|
| 406 |
+
cursor: default;
|
| 407 |
+
float: left;
|
| 408 |
+
margin-right: 5px;
|
| 409 |
+
margin-top: 5px;
|
| 410 |
+
padding: 0 5px; }
|
| 411 |
+
.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove {
|
| 412 |
+
color: #888;
|
| 413 |
+
cursor: pointer;
|
| 414 |
+
display: inline-block;
|
| 415 |
+
font-weight: bold;
|
| 416 |
+
margin-right: 2px; }
|
| 417 |
+
.select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover {
|
| 418 |
+
color: #555; }
|
| 419 |
+
|
| 420 |
+
.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
|
| 421 |
+
float: right; }
|
| 422 |
+
|
| 423 |
+
.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice {
|
| 424 |
+
margin-left: 5px;
|
| 425 |
+
margin-right: auto; }
|
| 426 |
+
|
| 427 |
+
.select2-container--classic[dir="rtl"] .select2-selection--multiple .select2-selection__choice__remove {
|
| 428 |
+
margin-left: 2px;
|
| 429 |
+
margin-right: auto; }
|
| 430 |
+
|
| 431 |
+
.select2-container--classic.select2-container--open .select2-selection--multiple {
|
| 432 |
+
border: 1px solid #5897fb; }
|
| 433 |
+
|
| 434 |
+
.select2-container--classic.select2-container--open.select2-container--above .select2-selection--multiple {
|
| 435 |
+
border-top: none;
|
| 436 |
+
border-top-left-radius: 0;
|
| 437 |
+
border-top-right-radius: 0; }
|
| 438 |
+
|
| 439 |
+
.select2-container--classic.select2-container--open.select2-container--below .select2-selection--multiple {
|
| 440 |
+
border-bottom: none;
|
| 441 |
+
border-bottom-left-radius: 0;
|
| 442 |
+
border-bottom-right-radius: 0; }
|
| 443 |
+
|
| 444 |
+
.select2-container--classic .select2-search--dropdown .select2-search__field {
|
| 445 |
+
border: 1px solid #aaa;
|
| 446 |
+
outline: 0; }
|
| 447 |
+
|
| 448 |
+
.select2-container--classic .select2-search--inline .select2-search__field {
|
| 449 |
+
outline: 0;
|
| 450 |
+
box-shadow: none; }
|
| 451 |
+
|
| 452 |
+
.select2-container--classic .select2-dropdown {
|
| 453 |
+
background-color: white;
|
| 454 |
+
border: 1px solid transparent; }
|
| 455 |
+
|
| 456 |
+
.select2-container--classic .select2-dropdown--above {
|
| 457 |
+
border-bottom: none; }
|
| 458 |
+
|
| 459 |
+
.select2-container--classic .select2-dropdown--below {
|
| 460 |
+
border-top: none; }
|
| 461 |
+
|
| 462 |
+
.select2-container--classic .select2-results > .select2-results__options {
|
| 463 |
+
max-height: 200px;
|
| 464 |
+
overflow-y: auto; }
|
| 465 |
+
|
| 466 |
+
.select2-container--classic .select2-results__option[role=group] {
|
| 467 |
+
padding: 0; }
|
| 468 |
+
|
| 469 |
+
.select2-container--classic .select2-results__option[aria-disabled=true] {
|
| 470 |
+
color: grey; }
|
| 471 |
+
|
| 472 |
+
.select2-container--classic .select2-results__option--highlighted[aria-selected] {
|
| 473 |
+
background-color: #3875d7;
|
| 474 |
+
color: white; }
|
| 475 |
+
|
| 476 |
+
.select2-container--classic .select2-results__group {
|
| 477 |
+
cursor: default;
|
| 478 |
+
display: block;
|
| 479 |
+
padding: 6px; }
|
| 480 |
+
|
| 481 |
+
.select2-container--classic.select2-container--open .select2-dropdown {
|
| 482 |
+
border-color: #5897fb; }
|
|
@@ -0,0 +1,5580 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/*!
|
| 2 |
+
* Select2 4.0.2
|
| 3 |
+
* https://select2.github.io
|
| 4 |
+
*
|
| 5 |
+
* Released under the MIT license
|
| 6 |
+
* https://github.com/select2/select2/blob/master/LICENSE.md
|
| 7 |
+
*/
|
| 8 |
+
(function (factory) {
|
| 9 |
+
if (typeof define === 'function' && define.amd) {
|
| 10 |
+
// AMD. Register as an anonymous module.
|
| 11 |
+
define(['jquery'], factory);
|
| 12 |
+
} else if (typeof exports === 'object') {
|
| 13 |
+
// Node/CommonJS
|
| 14 |
+
factory(require('jquery'));
|
| 15 |
+
} else {
|
| 16 |
+
// Browser globals
|
| 17 |
+
factory(jQuery);
|
| 18 |
+
}
|
| 19 |
+
}(function (jQuery) {
|
| 20 |
+
// This is needed so we can catch the AMD loader configuration and use it
|
| 21 |
+
// The inner file should be wrapped (by `banner.start.js`) in a function that
|
| 22 |
+
// returns the AMD loader references.
|
| 23 |
+
var S2 =
|
| 24 |
+
(function () {
|
| 25 |
+
// Restore the Select2 AMD loader so it can be used
|
| 26 |
+
// Needed mostly in the language files, where the loader is not inserted
|
| 27 |
+
if (jQuery && jQuery.fn && jQuery.fn.select2 && jQuery.fn.select2.amd) {
|
| 28 |
+
var S2 = jQuery.fn.select2.amd;
|
| 29 |
+
}
|
| 30 |
+
var S2;(function () { if (!S2 || !S2.requirejs) {
|
| 31 |
+
if (!S2) { S2 = {}; } else { require = S2; }
|
| 32 |
+
/**
|
| 33 |
+
* @license almond 0.3.1 Copyright (c) 2011-2014, The Dojo Foundation All Rights Reserved.
|
| 34 |
+
* Available via the MIT or new BSD license.
|
| 35 |
+
* see: http://github.com/jrburke/almond for details
|
| 36 |
+
*/
|
| 37 |
+
//Going sloppy to avoid 'use strict' string cost, but strict practices should
|
| 38 |
+
//be followed.
|
| 39 |
+
/*jslint sloppy: true */
|
| 40 |
+
/*global setTimeout: false */
|
| 41 |
+
|
| 42 |
+
var requirejs, require, define;
|
| 43 |
+
(function (undef) {
|
| 44 |
+
var main, req, makeMap, handlers,
|
| 45 |
+
defined = {},
|
| 46 |
+
waiting = {},
|
| 47 |
+
config = {},
|
| 48 |
+
defining = {},
|
| 49 |
+
hasOwn = Object.prototype.hasOwnProperty,
|
| 50 |
+
aps = [].slice,
|
| 51 |
+
jsSuffixRegExp = /\.js$/;
|
| 52 |
+
|
| 53 |
+
function hasProp(obj, prop) {
|
| 54 |
+
return hasOwn.call(obj, prop);
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
/**
|
| 58 |
+
* Given a relative module name, like ./something, normalize it to
|
| 59 |
+
* a real name that can be mapped to a path.
|
| 60 |
+
* @param {String} name the relative name
|
| 61 |
+
* @param {String} baseName a real name that the name arg is relative
|
| 62 |
+
* to.
|
| 63 |
+
* @returns {String} normalized name
|
| 64 |
+
*/
|
| 65 |
+
function normalize(name, baseName) {
|
| 66 |
+
var nameParts, nameSegment, mapValue, foundMap, lastIndex,
|
| 67 |
+
foundI, foundStarMap, starI, i, j, part,
|
| 68 |
+
baseParts = baseName && baseName.split("/"),
|
| 69 |
+
map = config.map,
|
| 70 |
+
starMap = (map && map['*']) || {};
|
| 71 |
+
|
| 72 |
+
//Adjust any relative paths.
|
| 73 |
+
if (name && name.charAt(0) === ".") {
|
| 74 |
+
//If have a base name, try to normalize against it,
|
| 75 |
+
//otherwise, assume it is a top-level require that will
|
| 76 |
+
//be relative to baseUrl in the end.
|
| 77 |
+
if (baseName) {
|
| 78 |
+
name = name.split('/');
|
| 79 |
+
lastIndex = name.length - 1;
|
| 80 |
+
|
| 81 |
+
// Node .js allowance:
|
| 82 |
+
if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) {
|
| 83 |
+
name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, '');
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
//Lop off the last part of baseParts, so that . matches the
|
| 87 |
+
//"directory" and not name of the baseName's module. For instance,
|
| 88 |
+
//baseName of "one/two/three", maps to "one/two/three.js", but we
|
| 89 |
+
//want the directory, "one/two" for this normalization.
|
| 90 |
+
name = baseParts.slice(0, baseParts.length - 1).concat(name);
|
| 91 |
+
|
| 92 |
+
//start trimDots
|
| 93 |
+
for (i = 0; i < name.length; i += 1) {
|
| 94 |
+
part = name[i];
|
| 95 |
+
if (part === ".") {
|
| 96 |
+
name.splice(i, 1);
|
| 97 |
+
i -= 1;
|
| 98 |
+
} else if (part === "..") {
|
| 99 |
+
if (i === 1 && (name[2] === '..' || name[0] === '..')) {
|
| 100 |
+
//End of the line. Keep at least one non-dot
|
| 101 |
+
//path segment at the front so it can be mapped
|
| 102 |
+
//correctly to disk. Otherwise, there is likely
|
| 103 |
+
//no path mapping for a path starting with '..'.
|
| 104 |
+
//This can still fail, but catches the most reasonable
|
| 105 |
+
//uses of ..
|
| 106 |
+
break;
|
| 107 |
+
} else if (i > 0) {
|
| 108 |
+
name.splice(i - 1, 2);
|
| 109 |
+
i -= 2;
|
| 110 |
+
}
|
| 111 |
+
}
|
| 112 |
+
}
|
| 113 |
+
//end trimDots
|
| 114 |
+
|
| 115 |
+
name = name.join("/");
|
| 116 |
+
} else if (name.indexOf('./') === 0) {
|
| 117 |
+
// No baseName, so this is ID is resolved relative
|
| 118 |
+
// to baseUrl, pull off the leading dot.
|
| 119 |
+
name = name.substring(2);
|
| 120 |
+
}
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
//Apply map config if available.
|
| 124 |
+
if ((baseParts || starMap) && map) {
|
| 125 |
+
nameParts = name.split('/');
|
| 126 |
+
|
| 127 |
+
for (i = nameParts.length; i > 0; i -= 1) {
|
| 128 |
+
nameSegment = nameParts.slice(0, i).join("/");
|
| 129 |
+
|
| 130 |
+
if (baseParts) {
|
| 131 |
+
//Find the longest baseName segment match in the config.
|
| 132 |
+
//So, do joins on the biggest to smallest lengths of baseParts.
|
| 133 |
+
for (j = baseParts.length; j > 0; j -= 1) {
|
| 134 |
+
mapValue = map[baseParts.slice(0, j).join('/')];
|
| 135 |
+
|
| 136 |
+
//baseName segment has config, find if it has one for
|
| 137 |
+
//this name.
|
| 138 |
+
if (mapValue) {
|
| 139 |
+
mapValue = mapValue[nameSegment];
|
| 140 |
+
if (mapValue) {
|
| 141 |
+
//Match, update name to the new value.
|
| 142 |
+
foundMap = mapValue;
|
| 143 |
+
foundI = i;
|
| 144 |
+
break;
|
| 145 |
+
}
|
| 146 |
+
}
|
| 147 |
+
}
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
if (foundMap) {
|
| 151 |
+
break;
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
//Check for a star map match, but just hold on to it,
|
| 155 |
+
//if there is a shorter segment match later in a matching
|
| 156 |
+
//config, then favor over this star map.
|
| 157 |
+
if (!foundStarMap && starMap && starMap[nameSegment]) {
|
| 158 |
+
foundStarMap = starMap[nameSegment];
|
| 159 |
+
starI = i;
|
| 160 |
+
}
|
| 161 |
+
}
|
| 162 |
+
|
| 163 |
+
if (!foundMap && foundStarMap) {
|
| 164 |
+
foundMap = foundStarMap;
|
| 165 |
+
foundI = starI;
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
if (foundMap) {
|
| 169 |
+
nameParts.splice(0, foundI, foundMap);
|
| 170 |
+
name = nameParts.join('/');
|
| 171 |
+
}
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
return name;
|
| 175 |
+
}
|
| 176 |
+
|
| 177 |
+
function makeRequire(relName, forceSync) {
|
| 178 |
+
return function () {
|
| 179 |
+
//A version of a require function that passes a moduleName
|
| 180 |
+
//value for items that may need to
|
| 181 |
+
//look up paths relative to the moduleName
|
| 182 |
+
var args = aps.call(arguments, 0);
|
| 183 |
+
|
| 184 |
+
//If first arg is not require('string'), and there is only
|
| 185 |
+
//one arg, it is the array form without a callback. Insert
|
| 186 |
+
//a null so that the following concat is correct.
|
| 187 |
+
if (typeof args[0] !== 'string' && args.length === 1) {
|
| 188 |
+
args.push(null);
|
| 189 |
+
}
|
| 190 |
+
return req.apply(undef, args.concat([relName, forceSync]));
|
| 191 |
+
};
|
| 192 |
+
}
|
| 193 |
+
|
| 194 |
+
function makeNormalize(relName) {
|
| 195 |
+
return function (name) {
|
| 196 |
+
return normalize(name, relName);
|
| 197 |
+
};
|
| 198 |
+
}
|
| 199 |
+
|
| 200 |
+
function makeLoad(depName) {
|
| 201 |
+
return function (value) {
|
| 202 |
+
defined[depName] = value;
|
| 203 |
+
};
|
| 204 |
+
}
|
| 205 |
+
|
| 206 |
+
function callDep(name) {
|
| 207 |
+
if (hasProp(waiting, name)) {
|
| 208 |
+
var args = waiting[name];
|
| 209 |
+
delete waiting[name];
|
| 210 |
+
defining[name] = true;
|
| 211 |
+
main.apply(undef, args);
|
| 212 |
+
}
|
| 213 |
+
|
| 214 |
+
if (!hasProp(defined, name) && !hasProp(defining, name)) {
|
| 215 |
+
throw new Error('No ' + name);
|
| 216 |
+
}
|
| 217 |
+
return defined[name];
|
| 218 |
+
}
|
| 219 |
+
|
| 220 |
+
//Turns a plugin!resource to [plugin, resource]
|
| 221 |
+
//with the plugin being undefined if the name
|
| 222 |
+
//did not have a plugin prefix.
|
| 223 |
+
function splitPrefix(name) {
|
| 224 |
+
var prefix,
|
| 225 |
+
index = name ? name.indexOf('!') : -1;
|
| 226 |
+
if (index > -1) {
|
| 227 |
+
prefix = name.substring(0, index);
|
| 228 |
+
name = name.substring(index + 1, name.length);
|
| 229 |
+
}
|
| 230 |
+
return [prefix, name];
|
| 231 |
+
}
|
| 232 |
+
|
| 233 |
+
/**
|
| 234 |
+
* Makes a name map, normalizing the name, and using a plugin
|
| 235 |
+
* for normalization if necessary. Grabs a ref to plugin
|
| 236 |
+
* too, as an optimization.
|
| 237 |
+
*/
|
| 238 |
+
makeMap = function (name, relName) {
|
| 239 |
+
var plugin,
|
| 240 |
+
parts = splitPrefix(name),
|
| 241 |
+
prefix = parts[0];
|
| 242 |
+
|
| 243 |
+
name = parts[1];
|
| 244 |
+
|
| 245 |
+
if (prefix) {
|
| 246 |
+
prefix = normalize(prefix, relName);
|
| 247 |
+
plugin = callDep(prefix);
|
| 248 |
+
}
|
| 249 |
+
|
| 250 |
+
//Normalize according
|
| 251 |
+
if (prefix) {
|
| 252 |
+
if (plugin && plugin.normalize) {
|
| 253 |
+
name = plugin.normalize(name, makeNormalize(relName));
|
| 254 |
+
} else {
|
| 255 |
+
name = normalize(name, relName);
|
| 256 |
+
}
|
| 257 |
+
} else {
|
| 258 |
+
name = normalize(name, relName);
|
| 259 |
+
parts = splitPrefix(name);
|
| 260 |
+
prefix = parts[0];
|
| 261 |
+
name = parts[1];
|
| 262 |
+
if (prefix) {
|
| 263 |
+
plugin = callDep(prefix);
|
| 264 |
+
}
|
| 265 |
+
}
|
| 266 |
+
|
| 267 |
+
//Using ridiculous property names for space reasons
|
| 268 |
+
return {
|
| 269 |
+
f: prefix ? prefix + '!' + name : name, //fullName
|
| 270 |
+
n: name,
|
| 271 |
+
pr: prefix,
|
| 272 |
+
p: plugin
|
| 273 |
+
};
|
| 274 |
+
};
|
| 275 |
+
|
| 276 |
+
function makeConfig(name) {
|
| 277 |
+
return function () {
|
| 278 |
+
return (config && config.config && config.config[name]) || {};
|
| 279 |
+
};
|
| 280 |
+
}
|
| 281 |
+
|
| 282 |
+
handlers = {
|
| 283 |
+
require: function (name) {
|
| 284 |
+
return makeRequire(name);
|
| 285 |
+
},
|
| 286 |
+
exports: function (name) {
|
| 287 |
+
var e = defined[name];
|
| 288 |
+
if (typeof e !== 'undefined') {
|
| 289 |
+
return e;
|
| 290 |
+
} else {
|
| 291 |
+
return (defined[name] = {});
|
| 292 |
+
}
|
| 293 |
+
},
|
| 294 |
+
module: function (name) {
|
| 295 |
+
return {
|
| 296 |
+
id: name,
|
| 297 |
+
uri: '',
|
| 298 |
+
exports: defined[name],
|
| 299 |
+
config: makeConfig(name)
|
| 300 |
+
};
|
| 301 |
+
}
|
| 302 |
+
};
|
| 303 |
+
|
| 304 |
+
main = function (name, deps, callback, relName) {
|
| 305 |
+
var cjsModule, depName, ret, map, i,
|
| 306 |
+
args = [],
|
| 307 |
+
callbackType = typeof callback,
|
| 308 |
+
usingExports;
|
| 309 |
+
|
| 310 |
+
//Use name if no relName
|
| 311 |
+
relName = relName || name;
|
| 312 |
+
|
| 313 |
+
//Call the callback to define the module, if necessary.
|
| 314 |
+
if (callbackType === 'undefined' || callbackType === 'function') {
|
| 315 |
+
//Pull out the defined dependencies and pass the ordered
|
| 316 |
+
//values to the callback.
|
| 317 |
+
//Default to [require, exports, module] if no deps
|
| 318 |
+
deps = !deps.length && callback.length ? ['require', 'exports', 'module'] : deps;
|
| 319 |
+
for (i = 0; i < deps.length; i += 1) {
|
| 320 |
+
map = makeMap(deps[i], relName);
|
| 321 |
+
depName = map.f;
|
| 322 |
+
|
| 323 |
+
//Fast path CommonJS standard dependencies.
|
| 324 |
+
if (depName === "require") {
|
| 325 |
+
args[i] = handlers.require(name);
|
| 326 |
+
} else if (depName === "exports") {
|
| 327 |
+
//CommonJS module spec 1.1
|
| 328 |
+
args[i] = handlers.exports(name);
|
| 329 |
+
usingExports = true;
|
| 330 |
+
} else if (depName === "module") {
|
| 331 |
+
//CommonJS module spec 1.1
|
| 332 |
+
cjsModule = args[i] = handlers.module(name);
|
| 333 |
+
} else if (hasProp(defined, depName) ||
|
| 334 |
+
hasProp(waiting, depName) ||
|
| 335 |
+
hasProp(defining, depName)) {
|
| 336 |
+
args[i] = callDep(depName);
|
| 337 |
+
} else if (map.p) {
|
| 338 |
+
map.p.load(map.n, makeRequire(relName, true), makeLoad(depName), {});
|
| 339 |
+
args[i] = defined[depName];
|
| 340 |
+
} else {
|
| 341 |
+
throw new Error(name + ' missing ' + depName);
|
| 342 |
+
}
|
| 343 |
+
}
|
| 344 |
+
|
| 345 |
+
ret = callback ? callback.apply(defined[name], args) : undefined;
|
| 346 |
+
|
| 347 |
+
if (name) {
|
| 348 |
+
//If setting exports via "module" is in play,
|
| 349 |
+
//favor that over return value and exports. After that,
|
| 350 |
+
//favor a non-undefined return value over exports use.
|
| 351 |
+
if (cjsModule && cjsModule.exports !== undef &&
|
| 352 |
+
cjsModule.exports !== defined[name]) {
|
| 353 |
+
defined[name] = cjsModule.exports;
|
| 354 |
+
} else if (ret !== undef || !usingExports) {
|
| 355 |
+
//Use the return value from the function.
|
| 356 |
+
defined[name] = ret;
|
| 357 |
+
}
|
| 358 |
+
}
|
| 359 |
+
} else if (name) {
|
| 360 |
+
//May just be an object definition for the module. Only
|
| 361 |
+
//worry about defining if have a module name.
|
| 362 |
+
defined[name] = callback;
|
| 363 |
+
}
|
| 364 |
+
};
|
| 365 |
+
|
| 366 |
+
requirejs = require = req = function (deps, callback, relName, forceSync, alt) {
|
| 367 |
+
if (typeof deps === "string") {
|
| 368 |
+
if (handlers[deps]) {
|
| 369 |
+
//callback in this case is really relName
|
| 370 |
+
return handlers[deps](callback);
|
| 371 |
+
}
|
| 372 |
+
//Just return the module wanted. In this scenario, the
|
| 373 |
+
//deps arg is the module name, and second arg (if passed)
|
| 374 |
+
//is just the relName.
|
| 375 |
+
//Normalize module name, if it contains . or ..
|
| 376 |
+
return callDep(makeMap(deps, callback).f);
|
| 377 |
+
} else if (!deps.splice) {
|
| 378 |
+
//deps is a config object, not an array.
|
| 379 |
+
config = deps;
|
| 380 |
+
if (config.deps) {
|
| 381 |
+
req(config.deps, config.callback);
|
| 382 |
+
}
|
| 383 |
+
if (!callback) {
|
| 384 |
+
return;
|
| 385 |
+
}
|
| 386 |
+
|
| 387 |
+
if (callback.splice) {
|
| 388 |
+
//callback is an array, which means it is a dependency list.
|
| 389 |
+
//Adjust args if there are dependencies
|
| 390 |
+
deps = callback;
|
| 391 |
+
callback = relName;
|
| 392 |
+
relName = null;
|
| 393 |
+
} else {
|
| 394 |
+
deps = undef;
|
| 395 |
+
}
|
| 396 |
+
}
|
| 397 |
+
|
| 398 |
+
//Support require(['a'])
|
| 399 |
+
callback = callback || function () {};
|
| 400 |
+
|
| 401 |
+
//If relName is a function, it is an errback handler,
|
| 402 |
+
//so remove it.
|
| 403 |
+
if (typeof relName === 'function') {
|
| 404 |
+
relName = forceSync;
|
| 405 |
+
forceSync = alt;
|
| 406 |
+
}
|
| 407 |
+
|
| 408 |
+
//Simulate async callback;
|
| 409 |
+
if (forceSync) {
|
| 410 |
+
main(undef, deps, callback, relName);
|
| 411 |
+
} else {
|
| 412 |
+
//Using a non-zero value because of concern for what old browsers
|
| 413 |
+
//do, and latest browsers "upgrade" to 4 if lower value is used:
|
| 414 |
+
//http://www.whatwg.org/specs/web-apps/current-work/multipage/timers.html#dom-windowtimers-settimeout:
|
| 415 |
+
//If want a value immediately, use require('id') instead -- something
|
| 416 |
+
//that works in almond on the global level, but not guaranteed and
|
| 417 |
+
//unlikely to work in other AMD implementations.
|
| 418 |
+
setTimeout(function () {
|
| 419 |
+
main(undef, deps, callback, relName);
|
| 420 |
+
}, 4);
|
| 421 |
+
}
|
| 422 |
+
|
| 423 |
+
return req;
|
| 424 |
+
};
|
| 425 |
+
|
| 426 |
+
/**
|
| 427 |
+
* Just drops the config on the floor, but returns req in case
|
| 428 |
+
* the config return value is used.
|
| 429 |
+
*/
|
| 430 |
+
req.config = function (cfg) {
|
| 431 |
+
return req(cfg);
|
| 432 |
+
};
|
| 433 |
+
|
| 434 |
+
/**
|
| 435 |
+
* Expose module registry for debugging and tooling
|
| 436 |
+
*/
|
| 437 |
+
requirejs._defined = defined;
|
| 438 |
+
|
| 439 |
+
define = function (name, deps, callback) {
|
| 440 |
+
if (typeof name !== 'string') {
|
| 441 |
+
throw new Error('See almond README: incorrect module build, no module name');
|
| 442 |
+
}
|
| 443 |
+
|
| 444 |
+
//This module may not have dependencies
|
| 445 |
+
if (!deps.splice) {
|
| 446 |
+
//deps is not an array, so probably means
|
| 447 |
+
//an object literal or factory function for
|
| 448 |
+
//the value. Adjust args.
|
| 449 |
+
callback = deps;
|
| 450 |
+
deps = [];
|
| 451 |
+
}
|
| 452 |
+
|
| 453 |
+
if (!hasProp(defined, name) && !hasProp(waiting, name)) {
|
| 454 |
+
waiting[name] = [name, deps, callback];
|
| 455 |
+
}
|
| 456 |
+
};
|
| 457 |
+
|
| 458 |
+
define.amd = {
|
| 459 |
+
jQuery: true
|
| 460 |
+
};
|
| 461 |
+
}());
|
| 462 |
+
|
| 463 |
+
S2.requirejs = requirejs;S2.require = require;S2.define = define;
|
| 464 |
+
}
|
| 465 |
+
}());
|
| 466 |
+
S2.define("almond", function(){});
|
| 467 |
+
|
| 468 |
+
/* global jQuery:false, $:false */
|
| 469 |
+
S2.define('jquery',[],function () {
|
| 470 |
+
var _$ = jQuery || $;
|
| 471 |
+
|
| 472 |
+
if (_$ == null && console && console.error) {
|
| 473 |
+
console.error(
|
| 474 |
+
'Select2: An instance of jQuery or a jQuery-compatible library was not ' +
|
| 475 |
+
'found. Make sure that you are including jQuery before Select2 on your ' +
|
| 476 |
+
'web page.'
|
| 477 |
+
);
|
| 478 |
+
}
|
| 479 |
+
|
| 480 |
+
return _$;
|
| 481 |
+
});
|
| 482 |
+
|
| 483 |
+
S2.define('select2/utils',[
|
| 484 |
+
'jquery'
|
| 485 |
+
], function ($) {
|
| 486 |
+
var Utils = {};
|
| 487 |
+
|
| 488 |
+
Utils.Extend = function (ChildClass, SuperClass) {
|
| 489 |
+
var __hasProp = {}.hasOwnProperty;
|
| 490 |
+
|
| 491 |
+
function BaseConstructor () {
|
| 492 |
+
this.constructor = ChildClass;
|
| 493 |
+
}
|
| 494 |
+
|
| 495 |
+
for (var key in SuperClass) {
|
| 496 |
+
if (__hasProp.call(SuperClass, key)) {
|
| 497 |
+
ChildClass[key] = SuperClass[key];
|
| 498 |
+
}
|
| 499 |
+
}
|
| 500 |
+
|
| 501 |
+
BaseConstructor.prototype = SuperClass.prototype;
|
| 502 |
+
ChildClass.prototype = new BaseConstructor();
|
| 503 |
+
ChildClass.__super__ = SuperClass.prototype;
|
| 504 |
+
|
| 505 |
+
return ChildClass;
|
| 506 |
+
};
|
| 507 |
+
|
| 508 |
+
function getMethods (theClass) {
|
| 509 |
+
var proto = theClass.prototype;
|
| 510 |
+
|
| 511 |
+
var methods = [];
|
| 512 |
+
|
| 513 |
+
for (var methodName in proto) {
|
| 514 |
+
var m = proto[methodName];
|
| 515 |
+
|
| 516 |
+
if (typeof m !== 'function') {
|
| 517 |
+
continue;
|
| 518 |
+
}
|
| 519 |
+
|
| 520 |
+
if (methodName === 'constructor') {
|
| 521 |
+
continue;
|
| 522 |
+
}
|
| 523 |
+
|
| 524 |
+
methods.push(methodName);
|
| 525 |
+
}
|
| 526 |
+
|
| 527 |
+
return methods;
|
| 528 |
+
}
|
| 529 |
+
|
| 530 |
+
Utils.Decorate = function (SuperClass, DecoratorClass) {
|
| 531 |
+
var decoratedMethods = getMethods(DecoratorClass);
|
| 532 |
+
var superMethods = getMethods(SuperClass);
|
| 533 |
+
|
| 534 |
+
function DecoratedClass () {
|
| 535 |
+
var unshift = Array.prototype.unshift;
|
| 536 |
+
|
| 537 |
+
var argCount = DecoratorClass.prototype.constructor.length;
|
| 538 |
+
|
| 539 |
+
var calledConstructor = SuperClass.prototype.constructor;
|
| 540 |
+
|
| 541 |
+
if (argCount > 0) {
|
| 542 |
+
unshift.call(arguments, SuperClass.prototype.constructor);
|
| 543 |
+
|
| 544 |
+
calledConstructor = DecoratorClass.prototype.constructor;
|
| 545 |
+
}
|
| 546 |
+
|
| 547 |
+
calledConstructor.apply(this, arguments);
|
| 548 |
+
}
|
| 549 |
+
|
| 550 |
+
DecoratorClass.displayName = SuperClass.displayName;
|
| 551 |
+
|
| 552 |
+
function ctr () {
|
| 553 |
+
this.constructor = DecoratedClass;
|
| 554 |
+
}
|
| 555 |
+
|
| 556 |
+
DecoratedClass.prototype = new ctr();
|
| 557 |
+
|
| 558 |
+
for (var m = 0; m < superMethods.length; m++) {
|
| 559 |
+
var superMethod = superMethods[m];
|
| 560 |
+
|
| 561 |
+
DecoratedClass.prototype[superMethod] =
|
| 562 |
+
SuperClass.prototype[superMethod];
|
| 563 |
+
}
|
| 564 |
+
|
| 565 |
+
var calledMethod = function (methodName) {
|
| 566 |
+
// Stub out the original method if it's not decorating an actual method
|
| 567 |
+
var originalMethod = function () {};
|
| 568 |
+
|
| 569 |
+
if (methodName in DecoratedClass.prototype) {
|
| 570 |
+
originalMethod = DecoratedClass.prototype[methodName];
|
| 571 |
+
}
|
| 572 |
+
|
| 573 |
+
var decoratedMethod = DecoratorClass.prototype[methodName];
|
| 574 |
+
|
| 575 |
+
return function () {
|
| 576 |
+
var unshift = Array.prototype.unshift;
|
| 577 |
+
|
| 578 |
+
unshift.call(arguments, originalMethod);
|
| 579 |
+
|
| 580 |
+
return decoratedMethod.apply(this, arguments);
|
| 581 |
+
};
|
| 582 |
+
};
|
| 583 |
+
|
| 584 |
+
for (var d = 0; d < decoratedMethods.length; d++) {
|
| 585 |
+
var decoratedMethod = decoratedMethods[d];
|
| 586 |
+
|
| 587 |
+
DecoratedClass.prototype[decoratedMethod] = calledMethod(decoratedMethod);
|
| 588 |
+
}
|
| 589 |
+
|
| 590 |
+
return DecoratedClass;
|
| 591 |
+
};
|
| 592 |
+
|
| 593 |
+
var Observable = function () {
|
| 594 |
+
this.listeners = {};
|
| 595 |
+
};
|
| 596 |
+
|
| 597 |
+
Observable.prototype.on = function (event, callback) {
|
| 598 |
+
this.listeners = this.listeners || {};
|
| 599 |
+
|
| 600 |
+
if (event in this.listeners) {
|
| 601 |
+
this.listeners[event].push(callback);
|
| 602 |
+
} else {
|
| 603 |
+
this.listeners[event] = [callback];
|
| 604 |
+
}
|
| 605 |
+
};
|
| 606 |
+
|
| 607 |
+
Observable.prototype.trigger = function (event) {
|
| 608 |
+
var slice = Array.prototype.slice;
|
| 609 |
+
|
| 610 |
+
this.listeners = this.listeners || {};
|
| 611 |
+
|
| 612 |
+
if (event in this.listeners) {
|
| 613 |
+
this.invoke(this.listeners[event], slice.call(arguments, 1));
|
| 614 |
+
}
|
| 615 |
+
|
| 616 |
+
if ('*' in this.listeners) {
|
| 617 |
+
this.invoke(this.listeners['*'], arguments);
|
| 618 |
+
}
|
| 619 |
+
};
|
| 620 |
+
|
| 621 |
+
Observable.prototype.invoke = function (listeners, params) {
|
| 622 |
+
for (var i = 0, len = listeners.length; i < len; i++) {
|
| 623 |
+
listeners[i].apply(this, params);
|
| 624 |
+
}
|
| 625 |
+
};
|
| 626 |
+
|
| 627 |
+
Utils.Observable = Observable;
|
| 628 |
+
|
| 629 |
+
Utils.generateChars = function (length) {
|
| 630 |
+
var chars = '';
|
| 631 |
+
|
| 632 |
+
for (var i = 0; i < length; i++) {
|
| 633 |
+
var randomChar = Math.floor(Math.random() * 36);
|
| 634 |
+
chars += randomChar.toString(36);
|
| 635 |
+
}
|
| 636 |
+
|
| 637 |
+
return chars;
|
| 638 |
+
};
|
| 639 |
+
|
| 640 |
+
Utils.bind = function (func, context) {
|
| 641 |
+
return function () {
|
| 642 |
+
func.apply(context, arguments);
|
| 643 |
+
};
|
| 644 |
+
};
|
| 645 |
+
|
| 646 |
+
Utils._convertData = function (data) {
|
| 647 |
+
for (var originalKey in data) {
|
| 648 |
+
var keys = originalKey.split('-');
|
| 649 |
+
|
| 650 |
+
var dataLevel = data;
|
| 651 |
+
|
| 652 |
+
if (keys.length === 1) {
|
| 653 |
+
continue;
|
| 654 |
+
}
|
| 655 |
+
|
| 656 |
+
for (var k = 0; k < keys.length; k++) {
|
| 657 |
+
var key = keys[k];
|
| 658 |
+
|
| 659 |
+
// Lowercase the first letter
|
| 660 |
+
// By default, dash-separated becomes camelCase
|
| 661 |
+
key = key.substring(0, 1).toLowerCase() + key.substring(1);
|
| 662 |
+
|
| 663 |
+
if (!(key in dataLevel)) {
|
| 664 |
+
dataLevel[key] = {};
|
| 665 |
+
}
|
| 666 |
+
|
| 667 |
+
if (k == keys.length - 1) {
|
| 668 |
+
dataLevel[key] = data[originalKey];
|
| 669 |
+
}
|
| 670 |
+
|
| 671 |
+
dataLevel = dataLevel[key];
|
| 672 |
+
}
|
| 673 |
+
|
| 674 |
+
delete data[originalKey];
|
| 675 |
+
}
|
| 676 |
+
|
| 677 |
+
return data;
|
| 678 |
+
};
|
| 679 |
+
|
| 680 |
+
Utils.hasScroll = function (index, el) {
|
| 681 |
+
// Adapted from the function created by @ShadowScripter
|
| 682 |
+
// and adapted by @BillBarry on the Stack Exchange Code Review website.
|
| 683 |
+
// The original code can be found at
|
| 684 |
+
// http://codereview.stackexchange.com/q/13338
|
| 685 |
+
// and was designed to be used with the Sizzle selector engine.
|
| 686 |
+
|
| 687 |
+
var $el = $(el);
|
| 688 |
+
var overflowX = el.style.overflowX;
|
| 689 |
+
var overflowY = el.style.overflowY;
|
| 690 |
+
|
| 691 |
+
//Check both x and y declarations
|
| 692 |
+
if (overflowX === overflowY &&
|
| 693 |
+
(overflowY === 'hidden' || overflowY === 'visible')) {
|
| 694 |
+
return false;
|
| 695 |
+
}
|
| 696 |
+
|
| 697 |
+
if (overflowX === 'scroll' || overflowY === 'scroll') {
|
| 698 |
+
return true;
|
| 699 |
+
}
|
| 700 |
+
|
| 701 |
+
return ($el.innerHeight() < el.scrollHeight ||
|
| 702 |
+
$el.innerWidth() < el.scrollWidth);
|
| 703 |
+
};
|
| 704 |
+
|
| 705 |
+
Utils.escapeMarkup = function (markup) {
|
| 706 |
+
var replaceMap = {
|
| 707 |
+
'\\': '\',
|
| 708 |
+
'&': '&',
|
| 709 |
+
'<': '<',
|
| 710 |
+
'>': '>',
|
| 711 |
+
'"': '"',
|
| 712 |
+
'\'': ''',
|
| 713 |
+
'/': '/'
|
| 714 |
+
};
|
| 715 |
+
|
| 716 |
+
// Do not try to escape the markup if it's not a string
|
| 717 |
+
if (typeof markup !== 'string') {
|
| 718 |
+
return markup;
|
| 719 |
+
}
|
| 720 |
+
|
| 721 |
+
return String(markup).replace(/[&<>"'\/\\]/g, function (match) {
|
| 722 |
+
return replaceMap[match];
|
| 723 |
+
});
|
| 724 |
+
};
|
| 725 |
+
|
| 726 |
+
// Append an array of jQuery nodes to a given element.
|
| 727 |
+
Utils.appendMany = function ($element, $nodes) {
|
| 728 |
+
// jQuery 1.7.x does not support $.fn.append() with an array
|
| 729 |
+
// Fall back to a jQuery object collection using $.fn.add()
|
| 730 |
+
if ($.fn.jquery.substr(0, 3) === '1.7') {
|
| 731 |
+
var $jqNodes = $();
|
| 732 |
+
|
| 733 |
+
$.map($nodes, function (node) {
|
| 734 |
+
$jqNodes = $jqNodes.add(node);
|
| 735 |
+
});
|
| 736 |
+
|
| 737 |
+
$nodes = $jqNodes;
|
| 738 |
+
}
|
| 739 |
+
|
| 740 |
+
$element.append($nodes);
|
| 741 |
+
};
|
| 742 |
+
|
| 743 |
+
return Utils;
|
| 744 |
+
});
|
| 745 |
+
|
| 746 |
+
S2.define('select2/results',[
|
| 747 |
+
'jquery',
|
| 748 |
+
'./utils'
|
| 749 |
+
], function ($, Utils) {
|
| 750 |
+
function Results ($element, options, dataAdapter) {
|
| 751 |
+
this.$element = $element;
|
| 752 |
+
this.data = dataAdapter;
|
| 753 |
+
this.options = options;
|
| 754 |
+
|
| 755 |
+
Results.__super__.constructor.call(this);
|
| 756 |
+
}
|
| 757 |
+
|
| 758 |
+
Utils.Extend(Results, Utils.Observable);
|
| 759 |
+
|
| 760 |
+
Results.prototype.render = function () {
|
| 761 |
+
var $results = $(
|
| 762 |
+
'<ul class="select2-results__options" role="tree"></ul>'
|
| 763 |
+
);
|
| 764 |
+
|
| 765 |
+
if (this.options.get('multiple')) {
|
| 766 |
+
$results.attr('aria-multiselectable', 'true');
|
| 767 |
+
}
|
| 768 |
+
|
| 769 |
+
this.$results = $results;
|
| 770 |
+
|
| 771 |
+
return $results;
|
| 772 |
+
};
|
| 773 |
+
|
| 774 |
+
Results.prototype.clear = function () {
|
| 775 |
+
this.$results.empty();
|
| 776 |
+
};
|
| 777 |
+
|
| 778 |
+
Results.prototype.displayMessage = function (params) {
|
| 779 |
+
var escapeMarkup = this.options.get('escapeMarkup');
|
| 780 |
+
|
| 781 |
+
this.clear();
|
| 782 |
+
this.hideLoading();
|
| 783 |
+
|
| 784 |
+
var $message = $(
|
| 785 |
+
'<li role="treeitem" aria-live="assertive"' +
|
| 786 |
+
' class="select2-results__option"></li>'
|
| 787 |
+
);
|
| 788 |
+
|
| 789 |
+
var message = this.options.get('translations').get(params.message);
|
| 790 |
+
|
| 791 |
+
$message.append(
|
| 792 |
+
escapeMarkup(
|
| 793 |
+
message(params.args)
|
| 794 |
+
)
|
| 795 |
+
);
|
| 796 |
+
|
| 797 |
+
$message[0].className += ' select2-results__message';
|
| 798 |
+
|
| 799 |
+
this.$results.append($message);
|
| 800 |
+
};
|
| 801 |
+
|
| 802 |
+
Results.prototype.hideMessages = function () {
|
| 803 |
+
this.$results.find('.select2-results__message').remove();
|
| 804 |
+
};
|
| 805 |
+
|
| 806 |
+
Results.prototype.append = function (data) {
|
| 807 |
+
this.hideLoading();
|
| 808 |
+
|
| 809 |
+
var $options = [];
|
| 810 |
+
|
| 811 |
+
if (data.results == null || data.results.length === 0) {
|
| 812 |
+
if (this.$results.children().length === 0) {
|
| 813 |
+
this.trigger('results:message', {
|
| 814 |
+
message: 'noResults'
|
| 815 |
+
});
|
| 816 |
+
}
|
| 817 |
+
|
| 818 |
+
return;
|
| 819 |
+
}
|
| 820 |
+
|
| 821 |
+
data.results = this.sort(data.results);
|
| 822 |
+
|
| 823 |
+
for (var d = 0; d < data.results.length; d++) {
|
| 824 |
+
var item = data.results[d];
|
| 825 |
+
|
| 826 |
+
var $option = this.option(item);
|
| 827 |
+
|
| 828 |
+
$options.push($option);
|
| 829 |
+
}
|
| 830 |
+
|
| 831 |
+
this.$results.append($options);
|
| 832 |
+
};
|
| 833 |
+
|
| 834 |
+
Results.prototype.position = function ($results, $dropdown) {
|
| 835 |
+
var $resultsContainer = $dropdown.find('.select2-results');
|
| 836 |
+
$resultsContainer.append($results);
|
| 837 |
+
};
|
| 838 |
+
|
| 839 |
+
Results.prototype.sort = function (data) {
|
| 840 |
+
var sorter = this.options.get('sorter');
|
| 841 |
+
|
| 842 |
+
return sorter(data);
|
| 843 |
+
};
|
| 844 |
+
|
| 845 |
+
Results.prototype.setClasses = function () {
|
| 846 |
+
var self = this;
|
| 847 |
+
|
| 848 |
+
this.data.current(function (selected) {
|
| 849 |
+
var selectedIds = $.map(selected, function (s) {
|
| 850 |
+
return s.id.toString();
|
| 851 |
+
});
|
| 852 |
+
|
| 853 |
+
var $options = self.$results
|
| 854 |
+
.find('.select2-results__option[aria-selected]');
|
| 855 |
+
|
| 856 |
+
$options.each(function () {
|
| 857 |
+
var $option = $(this);
|
| 858 |
+
|
| 859 |
+
var item = $.data(this, 'data');
|
| 860 |
+
|
| 861 |
+
// id needs to be converted to a string when comparing
|
| 862 |
+
var id = '' + item.id;
|
| 863 |
+
|
| 864 |
+
if ((item.element != null && item.element.selected) ||
|
| 865 |
+
(item.element == null && $.inArray(id, selectedIds) > -1)) {
|
| 866 |
+
$option.attr('aria-selected', 'true');
|
| 867 |
+
} else {
|
| 868 |
+
$option.attr('aria-selected', 'false');
|
| 869 |
+
}
|
| 870 |
+
});
|
| 871 |
+
|
| 872 |
+
var $selected = $options.filter('[aria-selected=true]');
|
| 873 |
+
|
| 874 |
+
// Check if there are any selected options
|
| 875 |
+
if ($selected.length > 0) {
|
| 876 |
+
// If there are selected options, highlight the first
|
| 877 |
+
$selected.first().trigger('mouseenter');
|
| 878 |
+
} else {
|
| 879 |
+
// If there are no selected options, highlight the first option
|
| 880 |
+
// in the dropdown
|
| 881 |
+
$options.first().trigger('mouseenter');
|
| 882 |
+
}
|
| 883 |
+
});
|
| 884 |
+
};
|
| 885 |
+
|
| 886 |
+
Results.prototype.showLoading = function (params) {
|
| 887 |
+
this.hideLoading();
|
| 888 |
+
|
| 889 |
+
var loadingMore = this.options.get('translations').get('searching');
|
| 890 |
+
|
| 891 |
+
var loading = {
|
| 892 |
+
disabled: true,
|
| 893 |
+
loading: true,
|
| 894 |
+
text: loadingMore(params)
|
| 895 |
+
};
|
| 896 |
+
var $loading = this.option(loading);
|
| 897 |
+
$loading.className += ' loading-results';
|
| 898 |
+
|
| 899 |
+
this.$results.prepend($loading);
|
| 900 |
+
};
|
| 901 |
+
|
| 902 |
+
Results.prototype.hideLoading = function () {
|
| 903 |
+
this.$results.find('.loading-results').remove();
|
| 904 |
+
};
|
| 905 |
+
|
| 906 |
+
Results.prototype.option = function (data) {
|
| 907 |
+
var option = document.createElement('li');
|
| 908 |
+
option.className = 'select2-results__option';
|
| 909 |
+
|
| 910 |
+
var attrs = {
|
| 911 |
+
'role': 'treeitem',
|
| 912 |
+
'aria-selected': 'false'
|
| 913 |
+
};
|
| 914 |
+
|
| 915 |
+
if (data.disabled) {
|
| 916 |
+
delete attrs['aria-selected'];
|
| 917 |
+
attrs['aria-disabled'] = 'true';
|
| 918 |
+
}
|
| 919 |
+
|
| 920 |
+
if (data.id == null) {
|
| 921 |
+
delete attrs['aria-selected'];
|
| 922 |
+
}
|
| 923 |
+
|
| 924 |
+
if (data._resultId != null) {
|
| 925 |
+
option.id = data._resultId;
|
| 926 |
+
}
|
| 927 |
+
|
| 928 |
+
if (data.title) {
|
| 929 |
+
option.title = data.title;
|
| 930 |
+
}
|
| 931 |
+
|
| 932 |
+
if (data.children) {
|
| 933 |
+
attrs.role = 'group';
|
| 934 |
+
attrs['aria-label'] = data.text;
|
| 935 |
+
delete attrs['aria-selected'];
|
| 936 |
+
}
|
| 937 |
+
|
| 938 |
+
for (var attr in attrs) {
|
| 939 |
+
var val = attrs[attr];
|
| 940 |
+
|
| 941 |
+
option.setAttribute(attr, val);
|
| 942 |
+
}
|
| 943 |
+
|
| 944 |
+
if (data.children) {
|
| 945 |
+
var $option = $(option);
|
| 946 |
+
|
| 947 |
+
var label = document.createElement('strong');
|
| 948 |
+
label.className = 'select2-results__group';
|
| 949 |
+
|
| 950 |
+
var $label = $(label);
|
| 951 |
+
this.template(data, label);
|
| 952 |
+
|
| 953 |
+
var $children = [];
|
| 954 |
+
|
| 955 |
+
for (var c = 0; c < data.children.length; c++) {
|
| 956 |
+
var child = data.children[c];
|
| 957 |
+
|
| 958 |
+
var $child = this.option(child);
|
| 959 |
+
|
| 960 |
+
$children.push($child);
|
| 961 |
+
}
|
| 962 |
+
|
| 963 |
+
var $childrenContainer = $('<ul></ul>', {
|
| 964 |
+
'class': 'select2-results__options select2-results__options--nested'
|
| 965 |
+
});
|
| 966 |
+
|
| 967 |
+
$childrenContainer.append($children);
|
| 968 |
+
|
| 969 |
+
$option.append(label);
|
| 970 |
+
$option.append($childrenContainer);
|
| 971 |
+
} else {
|
| 972 |
+
this.template(data, option);
|
| 973 |
+
}
|
| 974 |
+
|
| 975 |
+
$.data(option, 'data', data);
|
| 976 |
+
|
| 977 |
+
return option;
|
| 978 |
+
};
|
| 979 |
+
|
| 980 |
+
Results.prototype.bind = function (container, $container) {
|
| 981 |
+
var self = this;
|
| 982 |
+
|
| 983 |
+
var id = container.id + '-results';
|
| 984 |
+
|
| 985 |
+
this.$results.attr('id', id);
|
| 986 |
+
|
| 987 |
+
container.on('results:all', function (params) {
|
| 988 |
+
self.clear();
|
| 989 |
+
self.append(params.data);
|
| 990 |
+
|
| 991 |
+
if (container.isOpen()) {
|
| 992 |
+
self.setClasses();
|
| 993 |
+
}
|
| 994 |
+
});
|
| 995 |
+
|
| 996 |
+
container.on('results:append', function (params) {
|
| 997 |
+
self.append(params.data);
|
| 998 |
+
|
| 999 |
+
if (container.isOpen()) {
|
| 1000 |
+
self.setClasses();
|
| 1001 |
+
}
|
| 1002 |
+
});
|
| 1003 |
+
|
| 1004 |
+
container.on('query', function (params) {
|
| 1005 |
+
self.hideMessages();
|
| 1006 |
+
self.showLoading(params);
|
| 1007 |
+
});
|
| 1008 |
+
|
| 1009 |
+
container.on('select', function () {
|
| 1010 |
+
if (!container.isOpen()) {
|
| 1011 |
+
return;
|
| 1012 |
+
}
|
| 1013 |
+
|
| 1014 |
+
self.setClasses();
|
| 1015 |
+
});
|
| 1016 |
+
|
| 1017 |
+
container.on('unselect', function () {
|
| 1018 |
+
if (!container.isOpen()) {
|
| 1019 |
+
return;
|
| 1020 |
+
}
|
| 1021 |
+
|
| 1022 |
+
self.setClasses();
|
| 1023 |
+
});
|
| 1024 |
+
|
| 1025 |
+
container.on('open', function () {
|
| 1026 |
+
// When the dropdown is open, aria-expended="true"
|
| 1027 |
+
self.$results.attr('aria-expanded', 'true');
|
| 1028 |
+
self.$results.attr('aria-hidden', 'false');
|
| 1029 |
+
|
| 1030 |
+
self.setClasses();
|
| 1031 |
+
self.ensureHighlightVisible();
|
| 1032 |
+
});
|
| 1033 |
+
|
| 1034 |
+
container.on('close', function () {
|
| 1035 |
+
// When the dropdown is closed, aria-expended="false"
|
| 1036 |
+
self.$results.attr('aria-expanded', 'false');
|
| 1037 |
+
self.$results.attr('aria-hidden', 'true');
|
| 1038 |
+
self.$results.removeAttr('aria-activedescendant');
|
| 1039 |
+
});
|
| 1040 |
+
|
| 1041 |
+
container.on('results:toggle', function () {
|
| 1042 |
+
var $highlighted = self.getHighlightedResults();
|
| 1043 |
+
|
| 1044 |
+
if ($highlighted.length === 0) {
|
| 1045 |
+
return;
|
| 1046 |
+
}
|
| 1047 |
+
|
| 1048 |
+
$highlighted.trigger('mouseup');
|
| 1049 |
+
});
|
| 1050 |
+
|
| 1051 |
+
container.on('results:select', function () {
|
| 1052 |
+
var $highlighted = self.getHighlightedResults();
|
| 1053 |
+
|
| 1054 |
+
if ($highlighted.length === 0) {
|
| 1055 |
+
return;
|
| 1056 |
+
}
|
| 1057 |
+
|
| 1058 |
+
var data = $highlighted.data('data');
|
| 1059 |
+
|
| 1060 |
+
if ($highlighted.attr('aria-selected') == 'true') {
|
| 1061 |
+
self.trigger('close', {});
|
| 1062 |
+
} else {
|
| 1063 |
+
self.trigger('select', {
|
| 1064 |
+
data: data
|
| 1065 |
+
});
|
| 1066 |
+
}
|
| 1067 |
+
});
|
| 1068 |
+
|
| 1069 |
+
container.on('results:previous', function () {
|
| 1070 |
+
var $highlighted = self.getHighlightedResults();
|
| 1071 |
+
|
| 1072 |
+
var $options = self.$results.find('[aria-selected]');
|
| 1073 |
+
|
| 1074 |
+
var currentIndex = $options.index($highlighted);
|
| 1075 |
+
|
| 1076 |
+
// If we are already at te top, don't move further
|
| 1077 |
+
if (currentIndex === 0) {
|
| 1078 |
+
return;
|
| 1079 |
+
}
|
| 1080 |
+
|
| 1081 |
+
var nextIndex = currentIndex - 1;
|
| 1082 |
+
|
| 1083 |
+
// If none are highlighted, highlight the first
|
| 1084 |
+
if ($highlighted.length === 0) {
|
| 1085 |
+
nextIndex = 0;
|
| 1086 |
+
}
|
| 1087 |
+
|
| 1088 |
+
var $next = $options.eq(nextIndex);
|
| 1089 |
+
|
| 1090 |
+
$next.trigger('mouseenter');
|
| 1091 |
+
|
| 1092 |
+
var currentOffset = self.$results.offset().top;
|
| 1093 |
+
var nextTop = $next.offset().top;
|
| 1094 |
+
var nextOffset = self.$results.scrollTop() + (nextTop - currentOffset);
|
| 1095 |
+
|
| 1096 |
+
if (nextIndex === 0) {
|
| 1097 |
+
self.$results.scrollTop(0);
|
| 1098 |
+
} else if (nextTop - currentOffset < 0) {
|
| 1099 |
+
self.$results.scrollTop(nextOffset);
|
| 1100 |
+
}
|
| 1101 |
+
});
|
| 1102 |
+
|
| 1103 |
+
container.on('results:next', function () {
|
| 1104 |
+
var $highlighted = self.getHighlightedResults();
|
| 1105 |
+
|
| 1106 |
+
var $options = self.$results.find('[aria-selected]');
|
| 1107 |
+
|
| 1108 |
+
var currentIndex = $options.index($highlighted);
|
| 1109 |
+
|
| 1110 |
+
var nextIndex = currentIndex + 1;
|
| 1111 |
+
|
| 1112 |
+
// If we are at the last option, stay there
|
| 1113 |
+
if (nextIndex >= $options.length) {
|
| 1114 |
+
return;
|
| 1115 |
+
}
|
| 1116 |
+
|
| 1117 |
+
var $next = $options.eq(nextIndex);
|
| 1118 |
+
|
| 1119 |
+
$next.trigger('mouseenter');
|
| 1120 |
+
|
| 1121 |
+
var currentOffset = self.$results.offset().top +
|
| 1122 |
+
self.$results.outerHeight(false);
|
| 1123 |
+
var nextBottom = $next.offset().top + $next.outerHeight(false);
|
| 1124 |
+
var nextOffset = self.$results.scrollTop() + nextBottom - currentOffset;
|
| 1125 |
+
|
| 1126 |
+
if (nextIndex === 0) {
|
| 1127 |
+
self.$results.scrollTop(0);
|
| 1128 |
+
} else if (nextBottom > currentOffset) {
|
| 1129 |
+
self.$results.scrollTop(nextOffset);
|
| 1130 |
+
}
|
| 1131 |
+
});
|
| 1132 |
+
|
| 1133 |
+
container.on('results:focus', function (params) {
|
| 1134 |
+
params.element.addClass('select2-results__option--highlighted');
|
| 1135 |
+
});
|
| 1136 |
+
|
| 1137 |
+
container.on('results:message', function (params) {
|
| 1138 |
+
self.displayMessage(params);
|
| 1139 |
+
});
|
| 1140 |
+
|
| 1141 |
+
if ($.fn.mousewheel) {
|
| 1142 |
+
this.$results.on('mousewheel', function (e) {
|
| 1143 |
+
var top = self.$results.scrollTop();
|
| 1144 |
+
|
| 1145 |
+
var bottom = self.$results.get(0).scrollHeight - top + e.deltaY;
|
| 1146 |
+
|
| 1147 |
+
var isAtTop = e.deltaY > 0 && top - e.deltaY <= 0;
|
| 1148 |
+
var isAtBottom = e.deltaY < 0 && bottom <= self.$results.height();
|
| 1149 |
+
|
| 1150 |
+
if (isAtTop) {
|
| 1151 |
+
self.$results.scrollTop(0);
|
| 1152 |
+
|
| 1153 |
+
e.preventDefault();
|
| 1154 |
+
e.stopPropagation();
|
| 1155 |
+
} else if (isAtBottom) {
|
| 1156 |
+
self.$results.scrollTop(
|
| 1157 |
+
self.$results.get(0).scrollHeight - self.$results.height()
|
| 1158 |
+
);
|
| 1159 |
+
|
| 1160 |
+
e.preventDefault();
|
| 1161 |
+
e.stopPropagation();
|
| 1162 |
+
}
|
| 1163 |
+
});
|
| 1164 |
+
}
|
| 1165 |
+
|
| 1166 |
+
this.$results.on('mouseup', '.select2-results__option[aria-selected]',
|
| 1167 |
+
function (evt) {
|
| 1168 |
+
var $this = $(this);
|
| 1169 |
+
|
| 1170 |
+
var data = $this.data('data');
|
| 1171 |
+
|
| 1172 |
+
if ($this.attr('aria-selected') === 'true') {
|
| 1173 |
+
if (self.options.get('multiple')) {
|
| 1174 |
+
self.trigger('unselect', {
|
| 1175 |
+
originalEvent: evt,
|
| 1176 |
+
data: data
|
| 1177 |
+
});
|
| 1178 |
+
} else {
|
| 1179 |
+
self.trigger('close', {});
|
| 1180 |
+
}
|
| 1181 |
+
|
| 1182 |
+
return;
|
| 1183 |
+
}
|
| 1184 |
+
|
| 1185 |
+
self.trigger('select', {
|
| 1186 |
+
originalEvent: evt,
|
| 1187 |
+
data: data
|
| 1188 |
+
});
|
| 1189 |
+
});
|
| 1190 |
+
|
| 1191 |
+
this.$results.on('mouseenter', '.select2-results__option[aria-selected]',
|
| 1192 |
+
function (evt) {
|
| 1193 |
+
var data = $(this).data('data');
|
| 1194 |
+
|
| 1195 |
+
self.getHighlightedResults()
|
| 1196 |
+
.removeClass('select2-results__option--highlighted');
|
| 1197 |
+
|
| 1198 |
+
self.trigger('results:focus', {
|
| 1199 |
+
data: data,
|
| 1200 |
+
element: $(this)
|
| 1201 |
+
});
|
| 1202 |
+
});
|
| 1203 |
+
};
|
| 1204 |
+
|
| 1205 |
+
Results.prototype.getHighlightedResults = function () {
|
| 1206 |
+
var $highlighted = this.$results
|
| 1207 |
+
.find('.select2-results__option--highlighted');
|
| 1208 |
+
|
| 1209 |
+
return $highlighted;
|
| 1210 |
+
};
|
| 1211 |
+
|
| 1212 |
+
Results.prototype.destroy = function () {
|
| 1213 |
+
this.$results.remove();
|
| 1214 |
+
};
|
| 1215 |
+
|
| 1216 |
+
Results.prototype.ensureHighlightVisible = function () {
|
| 1217 |
+
var $highlighted = this.getHighlightedResults();
|
| 1218 |
+
|
| 1219 |
+
if ($highlighted.length === 0) {
|
| 1220 |
+
return;
|
| 1221 |
+
}
|
| 1222 |
+
|
| 1223 |
+
var $options = this.$results.find('[aria-selected]');
|
| 1224 |
+
|
| 1225 |
+
var currentIndex = $options.index($highlighted);
|
| 1226 |
+
|
| 1227 |
+
var currentOffset = this.$results.offset().top;
|
| 1228 |
+
var nextTop = $highlighted.offset().top;
|
| 1229 |
+
var nextOffset = this.$results.scrollTop() + (nextTop - currentOffset);
|
| 1230 |
+
|
| 1231 |
+
var offsetDelta = nextTop - currentOffset;
|
| 1232 |
+
nextOffset -= $highlighted.outerHeight(false) * 2;
|
| 1233 |
+
|
| 1234 |
+
if (currentIndex <= 2) {
|
| 1235 |
+
this.$results.scrollTop(0);
|
| 1236 |
+
} else if (offsetDelta > this.$results.outerHeight() || offsetDelta < 0) {
|
| 1237 |
+
this.$results.scrollTop(nextOffset);
|
| 1238 |
+
}
|
| 1239 |
+
};
|
| 1240 |
+
|
| 1241 |
+
Results.prototype.template = function (result, container) {
|
| 1242 |
+
var template = this.options.get('templateResult');
|
| 1243 |
+
var escapeMarkup = this.options.get('escapeMarkup');
|
| 1244 |
+
|
| 1245 |
+
var content = template(result, container);
|
| 1246 |
+
|
| 1247 |
+
if (content == null) {
|
| 1248 |
+
container.style.display = 'none';
|
| 1249 |
+
} else if (typeof content === 'string') {
|
| 1250 |
+
container.innerHTML = escapeMarkup(content);
|
| 1251 |
+
} else {
|
| 1252 |
+
$(container).append(content);
|
| 1253 |
+
}
|
| 1254 |
+
};
|
| 1255 |
+
|
| 1256 |
+
return Results;
|
| 1257 |
+
});
|
| 1258 |
+
|
| 1259 |
+
S2.define('select2/keys',[
|
| 1260 |
+
|
| 1261 |
+
], function () {
|
| 1262 |
+
var KEYS = {
|
| 1263 |
+
BACKSPACE: 8,
|
| 1264 |
+
TAB: 9,
|
| 1265 |
+
ENTER: 13,
|
| 1266 |
+
SHIFT: 16,
|
| 1267 |
+
CTRL: 17,
|
| 1268 |
+
ALT: 18,
|
| 1269 |
+
ESC: 27,
|
| 1270 |
+
SPACE: 32,
|
| 1271 |
+
PAGE_UP: 33,
|
| 1272 |
+
PAGE_DOWN: 34,
|
| 1273 |
+
END: 35,
|
| 1274 |
+
HOME: 36,
|
| 1275 |
+
LEFT: 37,
|
| 1276 |
+
UP: 38,
|
| 1277 |
+
RIGHT: 39,
|
| 1278 |
+
DOWN: 40,
|
| 1279 |
+
DELETE: 46
|
| 1280 |
+
};
|
| 1281 |
+
|
| 1282 |
+
return KEYS;
|
| 1283 |
+
});
|
| 1284 |
+
|
| 1285 |
+
S2.define('select2/selection/base',[
|
| 1286 |
+
'jquery',
|
| 1287 |
+
'../utils',
|
| 1288 |
+
'../keys'
|
| 1289 |
+
], function ($, Utils, KEYS) {
|
| 1290 |
+
function BaseSelection ($element, options) {
|
| 1291 |
+
this.$element = $element;
|
| 1292 |
+
this.options = options;
|
| 1293 |
+
|
| 1294 |
+
BaseSelection.__super__.constructor.call(this);
|
| 1295 |
+
}
|
| 1296 |
+
|
| 1297 |
+
Utils.Extend(BaseSelection, Utils.Observable);
|
| 1298 |
+
|
| 1299 |
+
BaseSelection.prototype.render = function () {
|
| 1300 |
+
var $selection = $(
|
| 1301 |
+
'<span class="select2-selection" role="combobox" ' +
|
| 1302 |
+
' aria-haspopup="true" aria-expanded="false">' +
|
| 1303 |
+
'</span>'
|
| 1304 |
+
);
|
| 1305 |
+
|
| 1306 |
+
this._tabindex = 0;
|
| 1307 |
+
|
| 1308 |
+
if (this.$element.data('old-tabindex') != null) {
|
| 1309 |
+
this._tabindex = this.$element.data('old-tabindex');
|
| 1310 |
+
} else if (this.$element.attr('tabindex') != null) {
|
| 1311 |
+
this._tabindex = this.$element.attr('tabindex');
|
| 1312 |
+
}
|
| 1313 |
+
|
| 1314 |
+
$selection.attr('title', this.$element.attr('title'));
|
| 1315 |
+
$selection.attr('tabindex', this._tabindex);
|
| 1316 |
+
|
| 1317 |
+
this.$selection = $selection;
|
| 1318 |
+
|
| 1319 |
+
return $selection;
|
| 1320 |
+
};
|
| 1321 |
+
|
| 1322 |
+
BaseSelection.prototype.bind = function (container, $container) {
|
| 1323 |
+
var self = this;
|
| 1324 |
+
|
| 1325 |
+
var id = container.id + '-container';
|
| 1326 |
+
var resultsId = container.id + '-results';
|
| 1327 |
+
|
| 1328 |
+
this.container = container;
|
| 1329 |
+
|
| 1330 |
+
this.$selection.on('focus', function (evt) {
|
| 1331 |
+
self.trigger('focus', evt);
|
| 1332 |
+
});
|
| 1333 |
+
|
| 1334 |
+
this.$selection.on('blur', function (evt) {
|
| 1335 |
+
self._handleBlur(evt);
|
| 1336 |
+
});
|
| 1337 |
+
|
| 1338 |
+
this.$selection.on('keydown', function (evt) {
|
| 1339 |
+
self.trigger('keypress', evt);
|
| 1340 |
+
|
| 1341 |
+
if (evt.which === KEYS.SPACE) {
|
| 1342 |
+
evt.preventDefault();
|
| 1343 |
+
}
|
| 1344 |
+
});
|
| 1345 |
+
|
| 1346 |
+
container.on('results:focus', function (params) {
|
| 1347 |
+
self.$selection.attr('aria-activedescendant', params.data._resultId);
|
| 1348 |
+
});
|
| 1349 |
+
|
| 1350 |
+
container.on('selection:update', function (params) {
|
| 1351 |
+
self.update(params.data);
|
| 1352 |
+
});
|
| 1353 |
+
|
| 1354 |
+
container.on('open', function () {
|
| 1355 |
+
// When the dropdown is open, aria-expanded="true"
|
| 1356 |
+
self.$selection.attr('aria-expanded', 'true');
|
| 1357 |
+
self.$selection.attr('aria-owns', resultsId);
|
| 1358 |
+
|
| 1359 |
+
self._attachCloseHandler(container);
|
| 1360 |
+
});
|
| 1361 |
+
|
| 1362 |
+
container.on('close', function () {
|
| 1363 |
+
// When the dropdown is closed, aria-expanded="false"
|
| 1364 |
+
self.$selection.attr('aria-expanded', 'false');
|
| 1365 |
+
self.$selection.removeAttr('aria-activedescendant');
|
| 1366 |
+
self.$selection.removeAttr('aria-owns');
|
| 1367 |
+
|
| 1368 |
+
self.$selection.focus();
|
| 1369 |
+
|
| 1370 |
+
self._detachCloseHandler(container);
|
| 1371 |
+
});
|
| 1372 |
+
|
| 1373 |
+
container.on('enable', function () {
|
| 1374 |
+
self.$selection.attr('tabindex', self._tabindex);
|
| 1375 |
+
});
|
| 1376 |
+
|
| 1377 |
+
container.on('disable', function () {
|
| 1378 |
+
self.$selection.attr('tabindex', '-1');
|
| 1379 |
+
});
|
| 1380 |
+
};
|
| 1381 |
+
|
| 1382 |
+
BaseSelection.prototype._handleBlur = function (evt) {
|
| 1383 |
+
var self = this;
|
| 1384 |
+
|
| 1385 |
+
// This needs to be delayed as the active element is the body when the tab
|
| 1386 |
+
// key is pressed, possibly along with others.
|
| 1387 |
+
window.setTimeout(function () {
|
| 1388 |
+
// Don't trigger `blur` if the focus is still in the selection
|
| 1389 |
+
if (
|
| 1390 |
+
(document.activeElement == self.$selection[0]) ||
|
| 1391 |
+
($.contains(self.$selection[0], document.activeElement))
|
| 1392 |
+
) {
|
| 1393 |
+
return;
|
| 1394 |
+
}
|
| 1395 |
+
|
| 1396 |
+
self.trigger('blur', evt);
|
| 1397 |
+
}, 1);
|
| 1398 |
+
};
|
| 1399 |
+
|
| 1400 |
+
BaseSelection.prototype._attachCloseHandler = function (container) {
|
| 1401 |
+
var self = this;
|
| 1402 |
+
|
| 1403 |
+
$(document.body).on('mousedown.select2.' + container.id, function (e) {
|
| 1404 |
+
var $target = $(e.target);
|
| 1405 |
+
|
| 1406 |
+
var $select = $target.closest('.select2');
|
| 1407 |
+
|
| 1408 |
+
var $all = $('.select2.select2-container--open');
|
| 1409 |
+
|
| 1410 |
+
$all.each(function () {
|
| 1411 |
+
var $this = $(this);
|
| 1412 |
+
|
| 1413 |
+
if (this == $select[0]) {
|
| 1414 |
+
return;
|
| 1415 |
+
}
|
| 1416 |
+
|
| 1417 |
+
var $element = $this.data('element');
|
| 1418 |
+
|
| 1419 |
+
$element.select2('close');
|
| 1420 |
+
});
|
| 1421 |
+
});
|
| 1422 |
+
};
|
| 1423 |
+
|
| 1424 |
+
BaseSelection.prototype._detachCloseHandler = function (container) {
|
| 1425 |
+
$(document.body).off('mousedown.select2.' + container.id);
|
| 1426 |
+
};
|
| 1427 |
+
|
| 1428 |
+
BaseSelection.prototype.position = function ($selection, $container) {
|
| 1429 |
+
var $selectionContainer = $container.find('.selection');
|
| 1430 |
+
$selectionContainer.append($selection);
|
| 1431 |
+
};
|
| 1432 |
+
|
| 1433 |
+
BaseSelection.prototype.destroy = function () {
|
| 1434 |
+
this._detachCloseHandler(this.container);
|
| 1435 |
+
};
|
| 1436 |
+
|
| 1437 |
+
BaseSelection.prototype.update = function (data) {
|
| 1438 |
+
throw new Error('The `update` method must be defined in child classes.');
|
| 1439 |
+
};
|
| 1440 |
+
|
| 1441 |
+
return BaseSelection;
|
| 1442 |
+
});
|
| 1443 |
+
|
| 1444 |
+
S2.define('select2/selection/single',[
|
| 1445 |
+
'jquery',
|
| 1446 |
+
'./base',
|
| 1447 |
+
'../utils',
|
| 1448 |
+
'../keys'
|
| 1449 |
+
], function ($, BaseSelection, Utils, KEYS) {
|
| 1450 |
+
function SingleSelection () {
|
| 1451 |
+
SingleSelection.__super__.constructor.apply(this, arguments);
|
| 1452 |
+
}
|
| 1453 |
+
|
| 1454 |
+
Utils.Extend(SingleSelection, BaseSelection);
|
| 1455 |
+
|
| 1456 |
+
SingleSelection.prototype.render = function () {
|
| 1457 |
+
var $selection = SingleSelection.__super__.render.call(this);
|
| 1458 |
+
|
| 1459 |
+
$selection.addClass('select2-selection--single');
|
| 1460 |
+
|
| 1461 |
+
$selection.html(
|
| 1462 |
+
'<span class="select2-selection__rendered"></span>' +
|
| 1463 |
+
'<span class="select2-selection__arrow" role="presentation">' +
|
| 1464 |
+
'<b role="presentation"></b>' +
|
| 1465 |
+
'</span>'
|
| 1466 |
+
);
|
| 1467 |
+
|
| 1468 |
+
return $selection;
|
| 1469 |
+
};
|
| 1470 |
+
|
| 1471 |
+
SingleSelection.prototype.bind = function (container, $container) {
|
| 1472 |
+
var self = this;
|
| 1473 |
+
|
| 1474 |
+
SingleSelection.__super__.bind.apply(this, arguments);
|
| 1475 |
+
|
| 1476 |
+
var id = container.id + '-container';
|
| 1477 |
+
|
| 1478 |
+
this.$selection.find('.select2-selection__rendered').attr('id', id);
|
| 1479 |
+
this.$selection.attr('aria-labelledby', id);
|
| 1480 |
+
|
| 1481 |
+
this.$selection.on('mousedown', function (evt) {
|
| 1482 |
+
// Only respond to left clicks
|
| 1483 |
+
if (evt.which !== 1) {
|
| 1484 |
+
return;
|
| 1485 |
+
}
|
| 1486 |
+
|
| 1487 |
+
self.trigger('toggle', {
|
| 1488 |
+
originalEvent: evt
|
| 1489 |
+
});
|
| 1490 |
+
});
|
| 1491 |
+
|
| 1492 |
+
this.$selection.on('focus', function (evt) {
|
| 1493 |
+
// User focuses on the container
|
| 1494 |
+
});
|
| 1495 |
+
|
| 1496 |
+
this.$selection.on('blur', function (evt) {
|
| 1497 |
+
// User exits the container
|
| 1498 |
+
});
|
| 1499 |
+
|
| 1500 |
+
container.on('selection:update', function (params) {
|
| 1501 |
+
self.update(params.data);
|
| 1502 |
+
});
|
| 1503 |
+
};
|
| 1504 |
+
|
| 1505 |
+
SingleSelection.prototype.clear = function () {
|
| 1506 |
+
this.$selection.find('.select2-selection__rendered').empty();
|
| 1507 |
+
};
|
| 1508 |
+
|
| 1509 |
+
SingleSelection.prototype.display = function (data, container) {
|
| 1510 |
+
var template = this.options.get('templateSelection');
|
| 1511 |
+
var escapeMarkup = this.options.get('escapeMarkup');
|
| 1512 |
+
|
| 1513 |
+
return escapeMarkup(template(data, container));
|
| 1514 |
+
};
|
| 1515 |
+
|
| 1516 |
+
SingleSelection.prototype.selectionContainer = function () {
|
| 1517 |
+
return $('<span></span>');
|
| 1518 |
+
};
|
| 1519 |
+
|
| 1520 |
+
SingleSelection.prototype.update = function (data) {
|
| 1521 |
+
if (data.length === 0) {
|
| 1522 |
+
this.clear();
|
| 1523 |
+
return;
|
| 1524 |
+
}
|
| 1525 |
+
|
| 1526 |
+
var selection = data[0];
|
| 1527 |
+
|
| 1528 |
+
var $rendered = this.$selection.find('.select2-selection__rendered');
|
| 1529 |
+
var formatted = this.display(selection, $rendered);
|
| 1530 |
+
|
| 1531 |
+
$rendered.empty().append(formatted);
|
| 1532 |
+
$rendered.prop('title', selection.title || selection.text);
|
| 1533 |
+
};
|
| 1534 |
+
|
| 1535 |
+
return SingleSelection;
|
| 1536 |
+
});
|
| 1537 |
+
|
| 1538 |
+
S2.define('select2/selection/multiple',[
|
| 1539 |
+
'jquery',
|
| 1540 |
+
'./base',
|
| 1541 |
+
'../utils'
|
| 1542 |
+
], function ($, BaseSelection, Utils) {
|
| 1543 |
+
function MultipleSelection ($element, options) {
|
| 1544 |
+
MultipleSelection.__super__.constructor.apply(this, arguments);
|
| 1545 |
+
}
|
| 1546 |
+
|
| 1547 |
+
Utils.Extend(MultipleSelection, BaseSelection);
|
| 1548 |
+
|
| 1549 |
+
MultipleSelection.prototype.render = function () {
|
| 1550 |
+
var $selection = MultipleSelection.__super__.render.call(this);
|
| 1551 |
+
|
| 1552 |
+
$selection.addClass('select2-selection--multiple');
|
| 1553 |
+
|
| 1554 |
+
$selection.html(
|
| 1555 |
+
'<ul class="select2-selection__rendered"></ul>'
|
| 1556 |
+
);
|
| 1557 |
+
|
| 1558 |
+
return $selection;
|
| 1559 |
+
};
|
| 1560 |
+
|
| 1561 |
+
MultipleSelection.prototype.bind = function (container, $container) {
|
| 1562 |
+
var self = this;
|
| 1563 |
+
|
| 1564 |
+
MultipleSelection.__super__.bind.apply(this, arguments);
|
| 1565 |
+
|
| 1566 |
+
this.$selection.on('click', function (evt) {
|
| 1567 |
+
self.trigger('toggle', {
|
| 1568 |
+
originalEvent: evt
|
| 1569 |
+
});
|
| 1570 |
+
});
|
| 1571 |
+
|
| 1572 |
+
this.$selection.on(
|
| 1573 |
+
'click',
|
| 1574 |
+
'.select2-selection__choice__remove',
|
| 1575 |
+
function (evt) {
|
| 1576 |
+
// Ignore the event if it is disabled
|
| 1577 |
+
if (self.options.get('disabled')) {
|
| 1578 |
+
return;
|
| 1579 |
+
}
|
| 1580 |
+
|
| 1581 |
+
var $remove = $(this);
|
| 1582 |
+
var $selection = $remove.parent();
|
| 1583 |
+
|
| 1584 |
+
var data = $selection.data('data');
|
| 1585 |
+
|
| 1586 |
+
self.trigger('unselect', {
|
| 1587 |
+
originalEvent: evt,
|
| 1588 |
+
data: data
|
| 1589 |
+
});
|
| 1590 |
+
}
|
| 1591 |
+
);
|
| 1592 |
+
};
|
| 1593 |
+
|
| 1594 |
+
MultipleSelection.prototype.clear = function () {
|
| 1595 |
+
this.$selection.find('.select2-selection__rendered').empty();
|
| 1596 |
+
};
|
| 1597 |
+
|
| 1598 |
+
MultipleSelection.prototype.display = function (data, container) {
|
| 1599 |
+
var template = this.options.get('templateSelection');
|
| 1600 |
+
var escapeMarkup = this.options.get('escapeMarkup');
|
| 1601 |
+
|
| 1602 |
+
return escapeMarkup(template(data, container));
|
| 1603 |
+
};
|
| 1604 |
+
|
| 1605 |
+
MultipleSelection.prototype.selectionContainer = function () {
|
| 1606 |
+
var $container = $(
|
| 1607 |
+
'<li class="select2-selection__choice">' +
|
| 1608 |
+
'<span class="select2-selection__choice__remove" role="presentation">' +
|
| 1609 |
+
'×' +
|
| 1610 |
+
'</span>' +
|
| 1611 |
+
'</li>'
|
| 1612 |
+
);
|
| 1613 |
+
|
| 1614 |
+
return $container;
|
| 1615 |
+
};
|
| 1616 |
+
|
| 1617 |
+
MultipleSelection.prototype.update = function (data) {
|
| 1618 |
+
this.clear();
|
| 1619 |
+
|
| 1620 |
+
if (data.length === 0) {
|
| 1621 |
+
return;
|
| 1622 |
+
}
|
| 1623 |
+
|
| 1624 |
+
var $selections = [];
|
| 1625 |
+
|
| 1626 |
+
for (var d = 0; d < data.length; d++) {
|
| 1627 |
+
var selection = data[d];
|
| 1628 |
+
|
| 1629 |
+
var $selection = this.selectionContainer();
|
| 1630 |
+
var formatted = this.display(selection, $selection);
|
| 1631 |
+
|
| 1632 |
+
$selection.append(formatted);
|
| 1633 |
+
$selection.prop('title', selection.title || selection.text);
|
| 1634 |
+
|
| 1635 |
+
$selection.data('data', selection);
|
| 1636 |
+
|
| 1637 |
+
$selections.push($selection);
|
| 1638 |
+
}
|
| 1639 |
+
|
| 1640 |
+
var $rendered = this.$selection.find('.select2-selection__rendered');
|
| 1641 |
+
|
| 1642 |
+
Utils.appendMany($rendered, $selections);
|
| 1643 |
+
};
|
| 1644 |
+
|
| 1645 |
+
return MultipleSelection;
|
| 1646 |
+
});
|
| 1647 |
+
|
| 1648 |
+
S2.define('select2/selection/placeholder',[
|
| 1649 |
+
'../utils'
|
| 1650 |
+
], function (Utils) {
|
| 1651 |
+
function Placeholder (decorated, $element, options) {
|
| 1652 |
+
this.placeholder = this.normalizePlaceholder(options.get('placeholder'));
|
| 1653 |
+
|
| 1654 |
+
decorated.call(this, $element, options);
|
| 1655 |
+
}
|
| 1656 |
+
|
| 1657 |
+
Placeholder.prototype.normalizePlaceholder = function (_, placeholder) {
|
| 1658 |
+
if (typeof placeholder === 'string') {
|
| 1659 |
+
placeholder = {
|
| 1660 |
+
id: '',
|
| 1661 |
+
text: placeholder
|
| 1662 |
+
};
|
| 1663 |
+
}
|
| 1664 |
+
|
| 1665 |
+
return placeholder;
|
| 1666 |
+
};
|
| 1667 |
+
|
| 1668 |
+
Placeholder.prototype.createPlaceholder = function (decorated, placeholder) {
|
| 1669 |
+
var $placeholder = this.selectionContainer();
|
| 1670 |
+
|
| 1671 |
+
$placeholder.html(this.display(placeholder));
|
| 1672 |
+
$placeholder.addClass('select2-selection__placeholder')
|
| 1673 |
+
.removeClass('select2-selection__choice');
|
| 1674 |
+
|
| 1675 |
+
return $placeholder;
|
| 1676 |
+
};
|
| 1677 |
+
|
| 1678 |
+
Placeholder.prototype.update = function (decorated, data) {
|
| 1679 |
+
var singlePlaceholder = (
|
| 1680 |
+
data.length == 1 && data[0].id != this.placeholder.id
|
| 1681 |
+
);
|
| 1682 |
+
var multipleSelections = data.length > 1;
|
| 1683 |
+
|
| 1684 |
+
if (multipleSelections || singlePlaceholder) {
|
| 1685 |
+
return decorated.call(this, data);
|
| 1686 |
+
}
|
| 1687 |
+
|
| 1688 |
+
this.clear();
|
| 1689 |
+
|
| 1690 |
+
var $placeholder = this.createPlaceholder(this.placeholder);
|
| 1691 |
+
|
| 1692 |
+
this.$selection.find('.select2-selection__rendered').append($placeholder);
|
| 1693 |
+
};
|
| 1694 |
+
|
| 1695 |
+
return Placeholder;
|
| 1696 |
+
});
|
| 1697 |
+
|
| 1698 |
+
S2.define('select2/selection/allowClear',[
|
| 1699 |
+
'jquery',
|
| 1700 |
+
'../keys'
|
| 1701 |
+
], function ($, KEYS) {
|
| 1702 |
+
function AllowClear () { }
|
| 1703 |
+
|
| 1704 |
+
AllowClear.prototype.bind = function (decorated, container, $container) {
|
| 1705 |
+
var self = this;
|
| 1706 |
+
|
| 1707 |
+
decorated.call(this, container, $container);
|
| 1708 |
+
|
| 1709 |
+
if (this.placeholder == null) {
|
| 1710 |
+
if (this.options.get('debug') && window.console && console.error) {
|
| 1711 |
+
console.error(
|
| 1712 |
+
'Select2: The `allowClear` option should be used in combination ' +
|
| 1713 |
+
'with the `placeholder` option.'
|
| 1714 |
+
);
|
| 1715 |
+
}
|
| 1716 |
+
}
|
| 1717 |
+
|
| 1718 |
+
this.$selection.on('mousedown', '.select2-selection__clear',
|
| 1719 |
+
function (evt) {
|
| 1720 |
+
self._handleClear(evt);
|
| 1721 |
+
});
|
| 1722 |
+
|
| 1723 |
+
container.on('keypress', function (evt) {
|
| 1724 |
+
self._handleKeyboardClear(evt, container);
|
| 1725 |
+
});
|
| 1726 |
+
};
|
| 1727 |
+
|
| 1728 |
+
AllowClear.prototype._handleClear = function (_, evt) {
|
| 1729 |
+
// Ignore the event if it is disabled
|
| 1730 |
+
if (this.options.get('disabled')) {
|
| 1731 |
+
return;
|
| 1732 |
+
}
|
| 1733 |
+
|
| 1734 |
+
var $clear = this.$selection.find('.select2-selection__clear');
|
| 1735 |
+
|
| 1736 |
+
// Ignore the event if nothing has been selected
|
| 1737 |
+
if ($clear.length === 0) {
|
| 1738 |
+
return;
|
| 1739 |
+
}
|
| 1740 |
+
|
| 1741 |
+
evt.stopPropagation();
|
| 1742 |
+
|
| 1743 |
+
var data = $clear.data('data');
|
| 1744 |
+
|
| 1745 |
+
for (var d = 0; d < data.length; d++) {
|
| 1746 |
+
var unselectData = {
|
| 1747 |
+
data: data[d]
|
| 1748 |
+
};
|
| 1749 |
+
|
| 1750 |
+
// Trigger the `unselect` event, so people can prevent it from being
|
| 1751 |
+
// cleared.
|
| 1752 |
+
this.trigger('unselect', unselectData);
|
| 1753 |
+
|
| 1754 |
+
// If the event was prevented, don't clear it out.
|
| 1755 |
+
if (unselectData.prevented) {
|
| 1756 |
+
return;
|
| 1757 |
+
}
|
| 1758 |
+
}
|
| 1759 |
+
|
| 1760 |
+
this.$element.val(this.placeholder.id).trigger('change');
|
| 1761 |
+
|
| 1762 |
+
this.trigger('toggle', {});
|
| 1763 |
+
};
|
| 1764 |
+
|
| 1765 |
+
AllowClear.prototype._handleKeyboardClear = function (_, evt, container) {
|
| 1766 |
+
if (container.isOpen()) {
|
| 1767 |
+
return;
|
| 1768 |
+
}
|
| 1769 |
+
|
| 1770 |
+
if (evt.which == KEYS.DELETE || evt.which == KEYS.BACKSPACE) {
|
| 1771 |
+
this._handleClear(evt);
|
| 1772 |
+
}
|
| 1773 |
+
};
|
| 1774 |
+
|
| 1775 |
+
AllowClear.prototype.update = function (decorated, data) {
|
| 1776 |
+
decorated.call(this, data);
|
| 1777 |
+
|
| 1778 |
+
if (this.$selection.find('.select2-selection__placeholder').length > 0 ||
|
| 1779 |
+
data.length === 0) {
|
| 1780 |
+
return;
|
| 1781 |
+
}
|
| 1782 |
+
|
| 1783 |
+
var $remove = $(
|
| 1784 |
+
'<span class="select2-selection__clear">' +
|
| 1785 |
+
'×' +
|
| 1786 |
+
'</span>'
|
| 1787 |
+
);
|
| 1788 |
+
$remove.data('data', data);
|
| 1789 |
+
|
| 1790 |
+
this.$selection.find('.select2-selection__rendered').prepend($remove);
|
| 1791 |
+
};
|
| 1792 |
+
|
| 1793 |
+
return AllowClear;
|
| 1794 |
+
});
|
| 1795 |
+
|
| 1796 |
+
S2.define('select2/selection/search',[
|
| 1797 |
+
'jquery',
|
| 1798 |
+
'../utils',
|
| 1799 |
+
'../keys'
|
| 1800 |
+
], function ($, Utils, KEYS) {
|
| 1801 |
+
function Search (decorated, $element, options) {
|
| 1802 |
+
decorated.call(this, $element, options);
|
| 1803 |
+
}
|
| 1804 |
+
|
| 1805 |
+
Search.prototype.render = function (decorated) {
|
| 1806 |
+
var $search = $(
|
| 1807 |
+
'<li class="select2-search select2-search--inline">' +
|
| 1808 |
+
'<input class="select2-search__field" type="search" tabindex="-1"' +
|
| 1809 |
+
' autocomplete="off" autocorrect="off" autocapitalize="off"' +
|
| 1810 |
+
' spellcheck="false" role="textbox" aria-autocomplete="list" />' +
|
| 1811 |
+
'</li>'
|
| 1812 |
+
);
|
| 1813 |
+
|
| 1814 |
+
this.$searchContainer = $search;
|
| 1815 |
+
this.$search = $search.find('input');
|
| 1816 |
+
|
| 1817 |
+
var $rendered = decorated.call(this);
|
| 1818 |
+
|
| 1819 |
+
this._transferTabIndex();
|
| 1820 |
+
|
| 1821 |
+
return $rendered;
|
| 1822 |
+
};
|
| 1823 |
+
|
| 1824 |
+
Search.prototype.bind = function (decorated, container, $container) {
|
| 1825 |
+
var self = this;
|
| 1826 |
+
|
| 1827 |
+
decorated.call(this, container, $container);
|
| 1828 |
+
|
| 1829 |
+
container.on('open', function () {
|
| 1830 |
+
self.$search.trigger('focus');
|
| 1831 |
+
});
|
| 1832 |
+
|
| 1833 |
+
container.on('close', function () {
|
| 1834 |
+
self.$search.val('');
|
| 1835 |
+
self.$search.removeAttr('aria-activedescendant');
|
| 1836 |
+
self.$search.trigger('focus');
|
| 1837 |
+
});
|
| 1838 |
+
|
| 1839 |
+
container.on('enable', function () {
|
| 1840 |
+
self.$search.prop('disabled', false);
|
| 1841 |
+
|
| 1842 |
+
self._transferTabIndex();
|
| 1843 |
+
});
|
| 1844 |
+
|
| 1845 |
+
container.on('disable', function () {
|
| 1846 |
+
self.$search.prop('disabled', true);
|
| 1847 |
+
});
|
| 1848 |
+
|
| 1849 |
+
container.on('focus', function (evt) {
|
| 1850 |
+
self.$search.trigger('focus');
|
| 1851 |
+
});
|
| 1852 |
+
|
| 1853 |
+
container.on('results:focus', function (params) {
|
| 1854 |
+
self.$search.attr('aria-activedescendant', params.id);
|
| 1855 |
+
});
|
| 1856 |
+
|
| 1857 |
+
this.$selection.on('focusin', '.select2-search--inline', function (evt) {
|
| 1858 |
+
self.trigger('focus', evt);
|
| 1859 |
+
});
|
| 1860 |
+
|
| 1861 |
+
this.$selection.on('focusout', '.select2-search--inline', function (evt) {
|
| 1862 |
+
self._handleBlur(evt);
|
| 1863 |
+
});
|
| 1864 |
+
|
| 1865 |
+
this.$selection.on('keydown', '.select2-search--inline', function (evt) {
|
| 1866 |
+
evt.stopPropagation();
|
| 1867 |
+
|
| 1868 |
+
self.trigger('keypress', evt);
|
| 1869 |
+
|
| 1870 |
+
self._keyUpPrevented = evt.isDefaultPrevented();
|
| 1871 |
+
|
| 1872 |
+
var key = evt.which;
|
| 1873 |
+
|
| 1874 |
+
if (key === KEYS.BACKSPACE && self.$search.val() === '') {
|
| 1875 |
+
var $previousChoice = self.$searchContainer
|
| 1876 |
+
.prev('.select2-selection__choice');
|
| 1877 |
+
|
| 1878 |
+
if ($previousChoice.length > 0) {
|
| 1879 |
+
var item = $previousChoice.data('data');
|
| 1880 |
+
|
| 1881 |
+
self.searchRemoveChoice(item);
|
| 1882 |
+
|
| 1883 |
+
evt.preventDefault();
|
| 1884 |
+
}
|
| 1885 |
+
}
|
| 1886 |
+
});
|
| 1887 |
+
|
| 1888 |
+
// Try to detect the IE version should the `documentMode` property that
|
| 1889 |
+
// is stored on the document. This is only implemented in IE and is
|
| 1890 |
+
// slightly cleaner than doing a user agent check.
|
| 1891 |
+
// This property is not available in Edge, but Edge also doesn't have
|
| 1892 |
+
// this bug.
|
| 1893 |
+
var msie = document.documentMode;
|
| 1894 |
+
var disableInputEvents = msie && msie <= 11;
|
| 1895 |
+
|
| 1896 |
+
// Workaround for browsers which do not support the `input` event
|
| 1897 |
+
// This will prevent double-triggering of events for browsers which support
|
| 1898 |
+
// both the `keyup` and `input` events.
|
| 1899 |
+
this.$selection.on(
|
| 1900 |
+
'input.searchcheck',
|
| 1901 |
+
'.select2-search--inline',
|
| 1902 |
+
function (evt) {
|
| 1903 |
+
// IE will trigger the `input` event when a placeholder is used on a
|
| 1904 |
+
// search box. To get around this issue, we are forced to ignore all
|
| 1905 |
+
// `input` events in IE and keep using `keyup`.
|
| 1906 |
+
if (disableInputEvents) {
|
| 1907 |
+
self.$selection.off('input.search input.searchcheck');
|
| 1908 |
+
return;
|
| 1909 |
+
}
|
| 1910 |
+
|
| 1911 |
+
// Unbind the duplicated `keyup` event
|
| 1912 |
+
self.$selection.off('keyup.search');
|
| 1913 |
+
}
|
| 1914 |
+
);
|
| 1915 |
+
|
| 1916 |
+
this.$selection.on(
|
| 1917 |
+
'keyup.search input.search',
|
| 1918 |
+
'.select2-search--inline',
|
| 1919 |
+
function (evt) {
|
| 1920 |
+
// IE will trigger the `input` event when a placeholder is used on a
|
| 1921 |
+
// search box. To get around this issue, we are forced to ignore all
|
| 1922 |
+
// `input` events in IE and keep using `keyup`.
|
| 1923 |
+
if (disableInputEvents && evt.type === 'input') {
|
| 1924 |
+
self.$selection.off('input.search input.searchcheck');
|
| 1925 |
+
return;
|
| 1926 |
+
}
|
| 1927 |
+
|
| 1928 |
+
var key = evt.which;
|
| 1929 |
+
|
| 1930 |
+
// We can freely ignore events from modifier keys
|
| 1931 |
+
if (key == KEYS.SHIFT || key == KEYS.CTRL || key == KEYS.ALT) {
|
| 1932 |
+
return;
|
| 1933 |
+
}
|
| 1934 |
+
|
| 1935 |
+
// Tabbing will be handled during the `keydown` phase
|
| 1936 |
+
if (key == KEYS.TAB) {
|
| 1937 |
+
return;
|
| 1938 |
+
}
|
| 1939 |
+
|
| 1940 |
+
self.handleSearch(evt);
|
| 1941 |
+
}
|
| 1942 |
+
);
|
| 1943 |
+
};
|
| 1944 |
+
|
| 1945 |
+
/**
|
| 1946 |
+
* This method will transfer the tabindex attribute from the rendered
|
| 1947 |
+
* selection to the search box. This allows for the search box to be used as
|
| 1948 |
+
* the primary focus instead of the selection container.
|
| 1949 |
+
*
|
| 1950 |
+
* @private
|
| 1951 |
+
*/
|
| 1952 |
+
Search.prototype._transferTabIndex = function (decorated) {
|
| 1953 |
+
this.$search.attr('tabindex', this.$selection.attr('tabindex'));
|
| 1954 |
+
this.$selection.attr('tabindex', '-1');
|
| 1955 |
+
};
|
| 1956 |
+
|
| 1957 |
+
Search.prototype.createPlaceholder = function (decorated, placeholder) {
|
| 1958 |
+
this.$search.attr('placeholder', placeholder.text);
|
| 1959 |
+
};
|
| 1960 |
+
|
| 1961 |
+
Search.prototype.update = function (decorated, data) {
|
| 1962 |
+
var searchHadFocus = this.$search[0] == document.activeElement;
|
| 1963 |
+
|
| 1964 |
+
this.$search.attr('placeholder', '');
|
| 1965 |
+
|
| 1966 |
+
decorated.call(this, data);
|
| 1967 |
+
|
| 1968 |
+
this.$selection.find('.select2-selection__rendered')
|
| 1969 |
+
.append(this.$searchContainer);
|
| 1970 |
+
|
| 1971 |
+
this.resizeSearch();
|
| 1972 |
+
if (searchHadFocus) {
|
| 1973 |
+
this.$search.focus();
|
| 1974 |
+
}
|
| 1975 |
+
};
|
| 1976 |
+
|
| 1977 |
+
Search.prototype.handleSearch = function () {
|
| 1978 |
+
this.resizeSearch();
|
| 1979 |
+
|
| 1980 |
+
if (!this._keyUpPrevented) {
|
| 1981 |
+
var input = this.$search.val();
|
| 1982 |
+
|
| 1983 |
+
this.trigger('query', {
|
| 1984 |
+
term: input
|
| 1985 |
+
});
|
| 1986 |
+
}
|
| 1987 |
+
|
| 1988 |
+
this._keyUpPrevented = false;
|
| 1989 |
+
};
|
| 1990 |
+
|
| 1991 |
+
Search.prototype.searchRemoveChoice = function (decorated, item) {
|
| 1992 |
+
this.trigger('unselect', {
|
| 1993 |
+
data: item
|
| 1994 |
+
});
|
| 1995 |
+
|
| 1996 |
+
this.$search.val(item.text);
|
| 1997 |
+
this.handleSearch();
|
| 1998 |
+
};
|
| 1999 |
+
|
| 2000 |
+
Search.prototype.resizeSearch = function () {
|
| 2001 |
+
this.$search.css('width', '25px');
|
| 2002 |
+
|
| 2003 |
+
var width = '';
|
| 2004 |
+
|
| 2005 |
+
if (this.$search.attr('placeholder') !== '') {
|
| 2006 |
+
width = this.$selection.find('.select2-selection__rendered').innerWidth();
|
| 2007 |
+
} else {
|
| 2008 |
+
var minimumWidth = this.$search.val().length + 1;
|
| 2009 |
+
|
| 2010 |
+
width = (minimumWidth * 0.75) + 'em';
|
| 2011 |
+
}
|
| 2012 |
+
|
| 2013 |
+
this.$search.css('width', width);
|
| 2014 |
+
};
|
| 2015 |
+
|
| 2016 |
+
return Search;
|
| 2017 |
+
});
|
| 2018 |
+
|
| 2019 |
+
S2.define('select2/selection/eventRelay',[
|
| 2020 |
+
'jquery'
|
| 2021 |
+
], function ($) {
|
| 2022 |
+
function EventRelay () { }
|
| 2023 |
+
|
| 2024 |
+
EventRelay.prototype.bind = function (decorated, container, $container) {
|
| 2025 |
+
var self = this;
|
| 2026 |
+
var relayEvents = [
|
| 2027 |
+
'open', 'opening',
|
| 2028 |
+
'close', 'closing',
|
| 2029 |
+
'select', 'selecting',
|
| 2030 |
+
'unselect', 'unselecting'
|
| 2031 |
+
];
|
| 2032 |
+
|
| 2033 |
+
var preventableEvents = ['opening', 'closing', 'selecting', 'unselecting'];
|
| 2034 |
+
|
| 2035 |
+
decorated.call(this, container, $container);
|
| 2036 |
+
|
| 2037 |
+
container.on('*', function (name, params) {
|
| 2038 |
+
// Ignore events that should not be relayed
|
| 2039 |
+
if ($.inArray(name, relayEvents) === -1) {
|
| 2040 |
+
return;
|
| 2041 |
+
}
|
| 2042 |
+
|
| 2043 |
+
// The parameters should always be an object
|
| 2044 |
+
params = params || {};
|
| 2045 |
+
|
| 2046 |
+
// Generate the jQuery event for the Select2 event
|
| 2047 |
+
var evt = $.Event('select2:' + name, {
|
| 2048 |
+
params: params
|
| 2049 |
+
});
|
| 2050 |
+
|
| 2051 |
+
self.$element.trigger(evt);
|
| 2052 |
+
|
| 2053 |
+
// Only handle preventable events if it was one
|
| 2054 |
+
if ($.inArray(name, preventableEvents) === -1) {
|
| 2055 |
+
return;
|
| 2056 |
+
}
|
| 2057 |
+
|
| 2058 |
+
params.prevented = evt.isDefaultPrevented();
|
| 2059 |
+
});
|
| 2060 |
+
};
|
| 2061 |
+
|
| 2062 |
+
return EventRelay;
|
| 2063 |
+
});
|
| 2064 |
+
|
| 2065 |
+
S2.define('select2/translation',[
|
| 2066 |
+
'jquery',
|
| 2067 |
+
'require'
|
| 2068 |
+
], function ($, require) {
|
| 2069 |
+
function Translation (dict) {
|
| 2070 |
+
this.dict = dict || {};
|
| 2071 |
+
}
|
| 2072 |
+
|
| 2073 |
+
Translation.prototype.all = function () {
|
| 2074 |
+
return this.dict;
|
| 2075 |
+
};
|
| 2076 |
+
|
| 2077 |
+
Translation.prototype.get = function (key) {
|
| 2078 |
+
return this.dict[key];
|
| 2079 |
+
};
|
| 2080 |
+
|
| 2081 |
+
Translation.prototype.extend = function (translation) {
|
| 2082 |
+
this.dict = $.extend({}, translation.all(), this.dict);
|
| 2083 |
+
};
|
| 2084 |
+
|
| 2085 |
+
// Static functions
|
| 2086 |
+
|
| 2087 |
+
Translation._cache = {};
|
| 2088 |
+
|
| 2089 |
+
Translation.loadPath = function (path) {
|
| 2090 |
+
if (!(path in Translation._cache)) {
|
| 2091 |
+
var translations = require(path);
|
| 2092 |
+
|
| 2093 |
+
Translation._cache[path] = translations;
|
| 2094 |
+
}
|
| 2095 |
+
|
| 2096 |
+
return new Translation(Translation._cache[path]);
|
| 2097 |
+
};
|
| 2098 |
+
|
| 2099 |
+
return Translation;
|
| 2100 |
+
});
|
| 2101 |
+
|
| 2102 |
+
S2.define('select2/diacritics',[
|
| 2103 |
+
|
| 2104 |
+
], function () {
|
| 2105 |
+
var diacritics = {
|
| 2106 |
+
'\u24B6': 'A',
|
| 2107 |
+
'\uFF21': 'A',
|
| 2108 |
+
'\u00C0': 'A',
|
| 2109 |
+
'\u00C1': 'A',
|
| 2110 |
+
'\u00C2': 'A',
|
| 2111 |
+
'\u1EA6': 'A',
|
| 2112 |
+
'\u1EA4': 'A',
|
| 2113 |
+
'\u1EAA': 'A',
|
| 2114 |
+
'\u1EA8': 'A',
|
| 2115 |
+
'\u00C3': 'A',
|
| 2116 |
+
'\u0100': 'A',
|
| 2117 |
+
'\u0102': 'A',
|
| 2118 |
+
'\u1EB0': 'A',
|
| 2119 |
+
'\u1EAE': 'A',
|
| 2120 |
+
'\u1EB4': 'A',
|
| 2121 |
+
'\u1EB2': 'A',
|
| 2122 |
+
'\u0226': 'A',
|
| 2123 |
+
'\u01E0': 'A',
|
| 2124 |
+
'\u00C4': 'A',
|
| 2125 |
+
'\u01DE': 'A',
|
| 2126 |
+
'\u1EA2': 'A',
|
| 2127 |
+
'\u00C5': 'A',
|
| 2128 |
+
'\u01FA': 'A',
|
| 2129 |
+
'\u01CD': 'A',
|
| 2130 |
+
'\u0200': 'A',
|
| 2131 |
+
'\u0202': 'A',
|
| 2132 |
+
'\u1EA0': 'A',
|
| 2133 |
+
'\u1EAC': 'A',
|
| 2134 |
+
'\u1EB6': 'A',
|
| 2135 |
+
'\u1E00': 'A',
|
| 2136 |
+
'\u0104': 'A',
|
| 2137 |
+
'\u023A': 'A',
|
| 2138 |
+
'\u2C6F': 'A',
|
| 2139 |
+
'\uA732': 'AA',
|
| 2140 |
+
'\u00C6': 'AE',
|
| 2141 |
+
'\u01FC': 'AE',
|
| 2142 |
+
'\u01E2': 'AE',
|
| 2143 |
+
'\uA734': 'AO',
|
| 2144 |
+
'\uA736': 'AU',
|
| 2145 |
+
'\uA738': 'AV',
|
| 2146 |
+
'\uA73A': 'AV',
|
| 2147 |
+
'\uA73C': 'AY',
|
| 2148 |
+
'\u24B7': 'B',
|
| 2149 |
+
'\uFF22': 'B',
|
| 2150 |
+
'\u1E02': 'B',
|
| 2151 |
+
'\u1E04': 'B',
|
| 2152 |
+
'\u1E06': 'B',
|
| 2153 |
+
'\u0243': 'B',
|
| 2154 |
+
'\u0182': 'B',
|
| 2155 |
+
'\u0181': 'B',
|
| 2156 |
+
'\u24B8': 'C',
|
| 2157 |
+
'\uFF23': 'C',
|
| 2158 |
+
'\u0106': 'C',
|
| 2159 |
+
'\u0108': 'C',
|
| 2160 |
+
'\u010A': 'C',
|
| 2161 |
+
'\u010C': 'C',
|
| 2162 |
+
'\u00C7': 'C',
|
| 2163 |
+
'\u1E08': 'C',
|
| 2164 |
+
'\u0187': 'C',
|
| 2165 |
+
'\u023B': 'C',
|
| 2166 |
+
'\uA73E': 'C',
|
| 2167 |
+
'\u24B9': 'D',
|
| 2168 |
+
'\uFF24': 'D',
|
| 2169 |
+
'\u1E0A': 'D',
|
| 2170 |
+
'\u010E': 'D',
|
| 2171 |
+
'\u1E0C': 'D',
|
| 2172 |
+
'\u1E10': 'D',
|
| 2173 |
+
'\u1E12': 'D',
|
| 2174 |
+
'\u1E0E': 'D',
|
| 2175 |
+
'\u0110': 'D',
|
| 2176 |
+
'\u018B': 'D',
|
| 2177 |
+
'\u018A': 'D',
|
| 2178 |
+
'\u0189': 'D',
|
| 2179 |
+
'\uA779': 'D',
|
| 2180 |
+
'\u01F1': 'DZ',
|
| 2181 |
+
'\u01C4': 'DZ',
|
| 2182 |
+
'\u01F2': 'Dz',
|
| 2183 |
+
'\u01C5': 'Dz',
|
| 2184 |
+
'\u24BA': 'E',
|
| 2185 |
+
'\uFF25': 'E',
|
| 2186 |
+
'\u00C8': 'E',
|
| 2187 |
+
'\u00C9': 'E',
|
| 2188 |
+
'\u00CA': 'E',
|
| 2189 |
+
'\u1EC0': 'E',
|
| 2190 |
+
'\u1EBE': 'E',
|
| 2191 |
+
'\u1EC4': 'E',
|
| 2192 |
+
'\u1EC2': 'E',
|
| 2193 |
+
'\u1EBC': 'E',
|
| 2194 |
+
'\u0112': 'E',
|
| 2195 |
+
'\u1E14': 'E',
|
| 2196 |
+
'\u1E16': 'E',
|
| 2197 |
+
'\u0114': 'E',
|
| 2198 |
+
'\u0116': 'E',
|
| 2199 |
+
'\u00CB': 'E',
|
| 2200 |
+
'\u1EBA': 'E',
|
| 2201 |
+
'\u011A': 'E',
|
| 2202 |
+
'\u0204': 'E',
|
| 2203 |
+
'\u0206': 'E',
|
| 2204 |
+
'\u1EB8': 'E',
|
| 2205 |
+
'\u1EC6': 'E',
|
| 2206 |
+
'\u0228': 'E',
|
| 2207 |
+
'\u1E1C': 'E',
|
| 2208 |
+
'\u0118': 'E',
|
| 2209 |
+
'\u1E18': 'E',
|
| 2210 |
+
'\u1E1A': 'E',
|
| 2211 |
+
'\u0190': 'E',
|
| 2212 |
+
'\u018E': 'E',
|
| 2213 |
+
'\u24BB': 'F',
|
| 2214 |
+
'\uFF26': 'F',
|
| 2215 |
+
'\u1E1E': 'F',
|
| 2216 |
+
'\u0191': 'F',
|
| 2217 |
+
'\uA77B': 'F',
|
| 2218 |
+
'\u24BC': 'G',
|
| 2219 |
+
'\uFF27': 'G',
|
| 2220 |
+
'\u01F4': 'G',
|
| 2221 |
+
'\u011C': 'G',
|
| 2222 |
+
'\u1E20': 'G',
|
| 2223 |
+
'\u011E': 'G',
|
| 2224 |
+
'\u0120': 'G',
|
| 2225 |
+
'\u01E6': 'G',
|
| 2226 |
+
'\u0122': 'G',
|
| 2227 |
+
'\u01E4': 'G',
|
| 2228 |
+
'\u0193': 'G',
|
| 2229 |
+
'\uA7A0': 'G',
|
| 2230 |
+
'\uA77D': 'G',
|
| 2231 |
+
'\uA77E': 'G',
|
| 2232 |
+
'\u24BD': 'H',
|
| 2233 |
+
'\uFF28': 'H',
|
| 2234 |
+
'\u0124': 'H',
|
| 2235 |
+
'\u1E22': 'H',
|
| 2236 |
+
'\u1E26': 'H',
|
| 2237 |
+
'\u021E': 'H',
|
| 2238 |
+
'\u1E24': 'H',
|
| 2239 |
+
'\u1E28': 'H',
|
| 2240 |
+
'\u1E2A': 'H',
|
| 2241 |
+
'\u0126': 'H',
|
| 2242 |
+
'\u2C67': 'H',
|
| 2243 |
+
'\u2C75': 'H',
|
| 2244 |
+
'\uA78D': 'H',
|
| 2245 |
+
'\u24BE': 'I',
|
| 2246 |
+
'\uFF29': 'I',
|
| 2247 |
+
'\u00CC': 'I',
|
| 2248 |
+
'\u00CD': 'I',
|
| 2249 |
+
'\u00CE': 'I',
|
| 2250 |
+
'\u0128': 'I',
|
| 2251 |
+
'\u012A': 'I',
|
| 2252 |
+
'\u012C': 'I',
|
| 2253 |
+
'\u0130': 'I',
|
| 2254 |
+
'\u00CF': 'I',
|
| 2255 |
+
'\u1E2E': 'I',
|
| 2256 |
+
'\u1EC8': 'I',
|
| 2257 |
+
'\u01CF': 'I',
|
| 2258 |
+
'\u0208': 'I',
|
| 2259 |
+
'\u020A': 'I',
|
| 2260 |
+
'\u1ECA': 'I',
|
| 2261 |
+
'\u012E': 'I',
|
| 2262 |
+
'\u1E2C': 'I',
|
| 2263 |
+
'\u0197': 'I',
|
| 2264 |
+
'\u24BF': 'J',
|
| 2265 |
+
'\uFF2A': 'J',
|
| 2266 |
+
'\u0134': 'J',
|
| 2267 |
+
'\u0248': 'J',
|
| 2268 |
+
'\u24C0': 'K',
|
| 2269 |
+
'\uFF2B': 'K',
|
| 2270 |
+
'\u1E30': 'K',
|
| 2271 |
+
'\u01E8': 'K',
|
| 2272 |
+
'\u1E32': 'K',
|
| 2273 |
+
'\u0136': 'K',
|
| 2274 |
+
'\u1E34': 'K',
|
| 2275 |
+
'\u0198': 'K',
|
| 2276 |
+
'\u2C69': 'K',
|
| 2277 |
+
'\uA740': 'K',
|
| 2278 |
+
'\uA742': 'K',
|
| 2279 |
+
'\uA744': 'K',
|
| 2280 |
+
'\uA7A2': 'K',
|
| 2281 |
+
'\u24C1': 'L',
|
| 2282 |
+
'\uFF2C': 'L',
|
| 2283 |
+
'\u013F': 'L',
|
| 2284 |
+
'\u0139': 'L',
|
| 2285 |
+
'\u013D': 'L',
|
| 2286 |
+
'\u1E36': 'L',
|
| 2287 |
+
'\u1E38': 'L',
|
| 2288 |
+
'\u013B': 'L',
|
| 2289 |
+
'\u1E3C': 'L',
|
| 2290 |
+
'\u1E3A': 'L',
|
| 2291 |
+
'\u0141': 'L',
|
| 2292 |
+
'\u023D': 'L',
|
| 2293 |
+
'\u2C62': 'L',
|
| 2294 |
+
'\u2C60': 'L',
|
| 2295 |
+
'\uA748': 'L',
|
| 2296 |
+
'\uA746': 'L',
|
| 2297 |
+
'\uA780': 'L',
|
| 2298 |
+
'\u01C7': 'LJ',
|
| 2299 |
+
'\u01C8': 'Lj',
|
| 2300 |
+
'\u24C2': 'M',
|
| 2301 |
+
'\uFF2D': 'M',
|
| 2302 |
+
'\u1E3E': 'M',
|
| 2303 |
+
'\u1E40': 'M',
|
| 2304 |
+
'\u1E42': 'M',
|
| 2305 |
+
'\u2C6E': 'M',
|
| 2306 |
+
'\u019C': 'M',
|
| 2307 |
+
'\u24C3': 'N',
|
| 2308 |
+
'\uFF2E': 'N',
|
| 2309 |
+
'\u01F8': 'N',
|
| 2310 |
+
'\u0143': 'N',
|
| 2311 |
+
'\u00D1': 'N',
|
| 2312 |
+
'\u1E44': 'N',
|
| 2313 |
+
'\u0147': 'N',
|
| 2314 |
+
'\u1E46': 'N',
|
| 2315 |
+
'\u0145': 'N',
|
| 2316 |
+
'\u1E4A': 'N',
|
| 2317 |
+
'\u1E48': 'N',
|
| 2318 |
+
'\u0220': 'N',
|
| 2319 |
+
'\u019D': 'N',
|
| 2320 |
+
'\uA790': 'N',
|
| 2321 |
+
'\uA7A4': 'N',
|
| 2322 |
+
'\u01CA': 'NJ',
|
| 2323 |
+
'\u01CB': 'Nj',
|
| 2324 |
+
'\u24C4': 'O',
|
| 2325 |
+
'\uFF2F': 'O',
|
| 2326 |
+
'\u00D2': 'O',
|
| 2327 |
+
'\u00D3': 'O',
|
| 2328 |
+
'\u00D4': 'O',
|
| 2329 |
+
'\u1ED2': 'O',
|
| 2330 |
+
'\u1ED0': 'O',
|
| 2331 |
+
'\u1ED6': 'O',
|
| 2332 |
+
'\u1ED4': 'O',
|
| 2333 |
+
'\u00D5': 'O',
|
| 2334 |
+
'\u1E4C': 'O',
|
| 2335 |
+
'\u022C': 'O',
|
| 2336 |
+
'\u1E4E': 'O',
|
| 2337 |
+
'\u014C': 'O',
|
| 2338 |
+
'\u1E50': 'O',
|
| 2339 |
+
'\u1E52': 'O',
|
| 2340 |
+
'\u014E': 'O',
|
| 2341 |
+
'\u022E': 'O',
|
| 2342 |
+
'\u0230': 'O',
|
| 2343 |
+
'\u00D6': 'O',
|
| 2344 |
+
'\u022A': 'O',
|
| 2345 |
+
'\u1ECE': 'O',
|
| 2346 |
+
'\u0150': 'O',
|
| 2347 |
+
'\u01D1': 'O',
|
| 2348 |
+
'\u020C': 'O',
|
| 2349 |
+
'\u020E': 'O',
|
| 2350 |
+
'\u01A0': 'O',
|
| 2351 |
+
'\u1EDC': 'O',
|
| 2352 |
+
'\u1EDA': 'O',
|
| 2353 |
+
'\u1EE0': 'O',
|
| 2354 |
+
'\u1EDE': 'O',
|
| 2355 |
+
'\u1EE2': 'O',
|
| 2356 |
+
'\u1ECC': 'O',
|
| 2357 |
+
'\u1ED8': 'O',
|
| 2358 |
+
'\u01EA': 'O',
|
| 2359 |
+
'\u01EC': 'O',
|
| 2360 |
+
'\u00D8': 'O',
|
| 2361 |
+
'\u01FE': 'O',
|
| 2362 |
+
'\u0186': 'O',
|
| 2363 |
+
'\u019F': 'O',
|
| 2364 |
+
'\uA74A': 'O',
|
| 2365 |
+
'\uA74C': 'O',
|
| 2366 |
+
'\u01A2': 'OI',
|
| 2367 |
+
'\uA74E': 'OO',
|
| 2368 |
+
'\u0222': 'OU',
|
| 2369 |
+
'\u24C5': 'P',
|
| 2370 |
+
'\uFF30': 'P',
|
| 2371 |
+
'\u1E54': 'P',
|
| 2372 |
+
'\u1E56': 'P',
|
| 2373 |
+
'\u01A4': 'P',
|
| 2374 |
+
'\u2C63': 'P',
|
| 2375 |
+
'\uA750': 'P',
|
| 2376 |
+
'\uA752': 'P',
|
| 2377 |
+
'\uA754': 'P',
|
| 2378 |
+
'\u24C6': 'Q',
|
| 2379 |
+
'\uFF31': 'Q',
|
| 2380 |
+
'\uA756': 'Q',
|
| 2381 |
+
'\uA758': 'Q',
|
| 2382 |
+
'\u024A': 'Q',
|
| 2383 |
+
'\u24C7': 'R',
|
| 2384 |
+
'\uFF32': 'R',
|
| 2385 |
+
'\u0154': 'R',
|
| 2386 |
+
'\u1E58': 'R',
|
| 2387 |
+
'\u0158': 'R',
|
| 2388 |
+
'\u0210': 'R',
|
| 2389 |
+
'\u0212': 'R',
|
| 2390 |
+
'\u1E5A': 'R',
|
| 2391 |
+
'\u1E5C': 'R',
|
| 2392 |
+
'\u0156': 'R',
|
| 2393 |
+
'\u1E5E': 'R',
|
| 2394 |
+
'\u024C': 'R',
|
| 2395 |
+
'\u2C64': 'R',
|
| 2396 |
+
'\uA75A': 'R',
|
| 2397 |
+
'\uA7A6': 'R',
|
| 2398 |
+
'\uA782': 'R',
|
| 2399 |
+
'\u24C8': 'S',
|
| 2400 |
+
'\uFF33': 'S',
|
| 2401 |
+
'\u1E9E': 'S',
|
| 2402 |
+
'\u015A': 'S',
|
| 2403 |
+
'\u1E64': 'S',
|
| 2404 |
+
'\u015C': 'S',
|
| 2405 |
+
'\u1E60': 'S',
|
| 2406 |
+
'\u0160': 'S',
|
| 2407 |
+
'\u1E66': 'S',
|
| 2408 |
+
'\u1E62': 'S',
|
| 2409 |
+
'\u1E68': 'S',
|
| 2410 |
+
'\u0218': 'S',
|
| 2411 |
+
'\u015E': 'S',
|
| 2412 |
+
'\u2C7E': 'S',
|
| 2413 |
+
'\uA7A8': 'S',
|
| 2414 |
+
'\uA784': 'S',
|
| 2415 |
+
'\u24C9': 'T',
|
| 2416 |
+
'\uFF34': 'T',
|
| 2417 |
+
'\u1E6A': 'T',
|
| 2418 |
+
'\u0164': 'T',
|
| 2419 |
+
'\u1E6C': 'T',
|
| 2420 |
+
'\u021A': 'T',
|
| 2421 |
+
'\u0162': 'T',
|
| 2422 |
+
'\u1E70': 'T',
|
| 2423 |
+
'\u1E6E': 'T',
|
| 2424 |
+
'\u0166': 'T',
|
| 2425 |
+
'\u01AC': 'T',
|
| 2426 |
+
'\u01AE': 'T',
|
| 2427 |
+
'\u023E': 'T',
|
| 2428 |
+
'\uA786': 'T',
|
| 2429 |
+
'\uA728': 'TZ',
|
| 2430 |
+
'\u24CA': 'U',
|
| 2431 |
+
'\uFF35': 'U',
|
| 2432 |
+
'\u00D9': 'U',
|
| 2433 |
+
'\u00DA': 'U',
|
| 2434 |
+
'\u00DB': 'U',
|
| 2435 |
+
'\u0168': 'U',
|
| 2436 |
+
'\u1E78': 'U',
|
| 2437 |
+
'\u016A': 'U',
|
| 2438 |
+
'\u1E7A': 'U',
|
| 2439 |
+
'\u016C': 'U',
|
| 2440 |
+
'\u00DC': 'U',
|
| 2441 |
+
'\u01DB': 'U',
|
| 2442 |
+
'\u01D7': 'U',
|
| 2443 |
+
'\u01D5': 'U',
|
| 2444 |
+
'\u01D9': 'U',
|
| 2445 |
+
'\u1EE6': 'U',
|
| 2446 |
+
'\u016E': 'U',
|
| 2447 |
+
'\u0170': 'U',
|
| 2448 |
+
'\u01D3': 'U',
|
| 2449 |
+
'\u0214': 'U',
|
| 2450 |
+
'\u0216': 'U',
|
| 2451 |
+
'\u01AF': 'U',
|
| 2452 |
+
'\u1EEA': 'U',
|
| 2453 |
+
'\u1EE8': 'U',
|
| 2454 |
+
'\u1EEE': 'U',
|
| 2455 |
+
'\u1EEC': 'U',
|
| 2456 |
+
'\u1EF0': 'U',
|
| 2457 |
+
'\u1EE4': 'U',
|
| 2458 |
+
'\u1E72': 'U',
|
| 2459 |
+
'\u0172': 'U',
|
| 2460 |
+
'\u1E76': 'U',
|
| 2461 |
+
'\u1E74': 'U',
|
| 2462 |
+
'\u0244': 'U',
|
| 2463 |
+
'\u24CB': 'V',
|
| 2464 |
+
'\uFF36': 'V',
|
| 2465 |
+
'\u1E7C': 'V',
|
| 2466 |
+
'\u1E7E': 'V',
|
| 2467 |
+
'\u01B2': 'V',
|
| 2468 |
+
'\uA75E': 'V',
|
| 2469 |
+
'\u0245': 'V',
|
| 2470 |
+
'\uA760': 'VY',
|
| 2471 |
+
'\u24CC': 'W',
|
| 2472 |
+
'\uFF37': 'W',
|
| 2473 |
+
'\u1E80': 'W',
|
| 2474 |
+
'\u1E82': 'W',
|
| 2475 |
+
'\u0174': 'W',
|
| 2476 |
+
'\u1E86': 'W',
|
| 2477 |
+
'\u1E84': 'W',
|
| 2478 |
+
'\u1E88': 'W',
|
| 2479 |
+
'\u2C72': 'W',
|
| 2480 |
+
'\u24CD': 'X',
|
| 2481 |
+
'\uFF38': 'X',
|
| 2482 |
+
'\u1E8A': 'X',
|
| 2483 |
+
'\u1E8C': 'X',
|
| 2484 |
+
'\u24CE': 'Y',
|
| 2485 |
+
'\uFF39': 'Y',
|
| 2486 |
+
'\u1EF2': 'Y',
|
| 2487 |
+
'\u00DD': 'Y',
|
| 2488 |
+
'\u0176': 'Y',
|
| 2489 |
+
'\u1EF8': 'Y',
|
| 2490 |
+
'\u0232': 'Y',
|
| 2491 |
+
'\u1E8E': 'Y',
|
| 2492 |
+
'\u0178': 'Y',
|
| 2493 |
+
'\u1EF6': 'Y',
|
| 2494 |
+
'\u1EF4': 'Y',
|
| 2495 |
+
'\u01B3': 'Y',
|
| 2496 |
+
'\u024E': 'Y',
|
| 2497 |
+
'\u1EFE': 'Y',
|
| 2498 |
+
'\u24CF': 'Z',
|
| 2499 |
+
'\uFF3A': 'Z',
|
| 2500 |
+
'\u0179': 'Z',
|
| 2501 |
+
'\u1E90': 'Z',
|
| 2502 |
+
'\u017B': 'Z',
|
| 2503 |
+
'\u017D': 'Z',
|
| 2504 |
+
'\u1E92': 'Z',
|
| 2505 |
+
'\u1E94': 'Z',
|
| 2506 |
+
'\u01B5': 'Z',
|
| 2507 |
+
'\u0224': 'Z',
|
| 2508 |
+
'\u2C7F': 'Z',
|
| 2509 |
+
'\u2C6B': 'Z',
|
| 2510 |
+
'\uA762': 'Z',
|
| 2511 |
+
'\u24D0': 'a',
|
| 2512 |
+
'\uFF41': 'a',
|
| 2513 |
+
'\u1E9A': 'a',
|
| 2514 |
+
'\u00E0': 'a',
|
| 2515 |
+
'\u00E1': 'a',
|
| 2516 |
+
'\u00E2': 'a',
|
| 2517 |
+
'\u1EA7': 'a',
|
| 2518 |
+
'\u1EA5': 'a',
|
| 2519 |
+
'\u1EAB': 'a',
|
| 2520 |
+
'\u1EA9': 'a',
|
| 2521 |
+
'\u00E3': 'a',
|
| 2522 |
+
'\u0101': 'a',
|
| 2523 |
+
'\u0103': 'a',
|
| 2524 |
+
'\u1EB1': 'a',
|
| 2525 |
+
'\u1EAF': 'a',
|
| 2526 |
+
'\u1EB5': 'a',
|
| 2527 |
+
'\u1EB3': 'a',
|
| 2528 |
+
'\u0227': 'a',
|
| 2529 |
+
'\u01E1': 'a',
|
| 2530 |
+
'\u00E4': 'a',
|
| 2531 |
+
'\u01DF': 'a',
|
| 2532 |
+
'\u1EA3': 'a',
|
| 2533 |
+
'\u00E5': 'a',
|
| 2534 |
+
'\u01FB': 'a',
|
| 2535 |
+
'\u01CE': 'a',
|
| 2536 |
+
'\u0201': 'a',
|
| 2537 |
+
'\u0203': 'a',
|
| 2538 |
+
'\u1EA1': 'a',
|
| 2539 |
+
'\u1EAD': 'a',
|
| 2540 |
+
'\u1EB7': 'a',
|
| 2541 |
+
'\u1E01': 'a',
|
| 2542 |
+
'\u0105': 'a',
|
| 2543 |
+
'\u2C65': 'a',
|
| 2544 |
+
'\u0250': 'a',
|
| 2545 |
+
'\uA733': 'aa',
|
| 2546 |
+
'\u00E6': 'ae',
|
| 2547 |
+
'\u01FD': 'ae',
|
| 2548 |
+
'\u01E3': 'ae',
|
| 2549 |
+
'\uA735': 'ao',
|
| 2550 |
+
'\uA737': 'au',
|
| 2551 |
+
'\uA739': 'av',
|
| 2552 |
+
'\uA73B': 'av',
|
| 2553 |
+
'\uA73D': 'ay',
|
| 2554 |
+
'\u24D1': 'b',
|
| 2555 |
+
'\uFF42': 'b',
|
| 2556 |
+
'\u1E03': 'b',
|
| 2557 |
+
'\u1E05': 'b',
|
| 2558 |
+
'\u1E07': 'b',
|
| 2559 |
+
'\u0180': 'b',
|
| 2560 |
+
'\u0183': 'b',
|
| 2561 |
+
'\u0253': 'b',
|
| 2562 |
+
'\u24D2': 'c',
|
| 2563 |
+
'\uFF43': 'c',
|
| 2564 |
+
'\u0107': 'c',
|
| 2565 |
+
'\u0109': 'c',
|
| 2566 |
+
'\u010B': 'c',
|
| 2567 |
+
'\u010D': 'c',
|
| 2568 |
+
'\u00E7': 'c',
|
| 2569 |
+
'\u1E09': 'c',
|
| 2570 |
+
'\u0188': 'c',
|
| 2571 |
+
'\u023C': 'c',
|
| 2572 |
+
'\uA73F': 'c',
|
| 2573 |
+
'\u2184': 'c',
|
| 2574 |
+
'\u24D3': 'd',
|
| 2575 |
+
'\uFF44': 'd',
|
| 2576 |
+
'\u1E0B': 'd',
|
| 2577 |
+
'\u010F': 'd',
|
| 2578 |
+
'\u1E0D': 'd',
|
| 2579 |
+
'\u1E11': 'd',
|
| 2580 |
+
'\u1E13': 'd',
|
| 2581 |
+
'\u1E0F': 'd',
|
| 2582 |
+
'\u0111': 'd',
|
| 2583 |
+
'\u018C': 'd',
|
| 2584 |
+
'\u0256': 'd',
|
| 2585 |
+
'\u0257': 'd',
|
| 2586 |
+
'\uA77A': 'd',
|
| 2587 |
+
'\u01F3': 'dz',
|
| 2588 |
+
'\u01C6': 'dz',
|
| 2589 |
+
'\u24D4': 'e',
|
| 2590 |
+
'\uFF45': 'e',
|
| 2591 |
+
'\u00E8': 'e',
|
| 2592 |
+
'\u00E9': 'e',
|
| 2593 |
+
'\u00EA': 'e',
|
| 2594 |
+
'\u1EC1': 'e',
|
| 2595 |
+
'\u1EBF': 'e',
|
| 2596 |
+
'\u1EC5': 'e',
|
| 2597 |
+
'\u1EC3': 'e',
|
| 2598 |
+
'\u1EBD': 'e',
|
| 2599 |
+
'\u0113': 'e',
|
| 2600 |
+
'\u1E15': 'e',
|
| 2601 |
+
'\u1E17': 'e',
|
| 2602 |
+
'\u0115': 'e',
|
| 2603 |
+
'\u0117': 'e',
|
| 2604 |
+
'\u00EB': 'e',
|
| 2605 |
+
'\u1EBB': 'e',
|
| 2606 |
+
'\u011B': 'e',
|
| 2607 |
+
'\u0205': 'e',
|
| 2608 |
+
'\u0207': 'e',
|
| 2609 |
+
'\u1EB9': 'e',
|
| 2610 |
+
'\u1EC7': 'e',
|
| 2611 |
+
'\u0229': 'e',
|
| 2612 |
+
'\u1E1D': 'e',
|
| 2613 |
+
'\u0119': 'e',
|
| 2614 |
+
'\u1E19': 'e',
|
| 2615 |
+
'\u1E1B': 'e',
|
| 2616 |
+
'\u0247': 'e',
|
| 2617 |
+
'\u025B': 'e',
|
| 2618 |
+
'\u01DD': 'e',
|
| 2619 |
+
'\u24D5': 'f',
|
| 2620 |
+
'\uFF46': 'f',
|
| 2621 |
+
'\u1E1F': 'f',
|
| 2622 |
+
'\u0192': 'f',
|
| 2623 |
+
'\uA77C': 'f',
|
| 2624 |
+
'\u24D6': 'g',
|
| 2625 |
+
'\uFF47': 'g',
|
| 2626 |
+
'\u01F5': 'g',
|
| 2627 |
+
'\u011D': 'g',
|
| 2628 |
+
'\u1E21': 'g',
|
| 2629 |
+
'\u011F': 'g',
|
| 2630 |
+
'\u0121': 'g',
|
| 2631 |
+
'\u01E7': 'g',
|
| 2632 |
+
'\u0123': 'g',
|
| 2633 |
+
'\u01E5': 'g',
|
| 2634 |
+
'\u0260': 'g',
|
| 2635 |
+
'\uA7A1': 'g',
|
| 2636 |
+
'\u1D79': 'g',
|
| 2637 |
+
'\uA77F': 'g',
|
| 2638 |
+
'\u24D7': 'h',
|
| 2639 |
+
'\uFF48': 'h',
|
| 2640 |
+
'\u0125': 'h',
|
| 2641 |
+
'\u1E23': 'h',
|
| 2642 |
+
'\u1E27': 'h',
|
| 2643 |
+
'\u021F': 'h',
|
| 2644 |
+
'\u1E25': 'h',
|
| 2645 |
+
'\u1E29': 'h',
|
| 2646 |
+
'\u1E2B': 'h',
|
| 2647 |
+
'\u1E96': 'h',
|
| 2648 |
+
'\u0127': 'h',
|
| 2649 |
+
'\u2C68': 'h',
|
| 2650 |
+
'\u2C76': 'h',
|
| 2651 |
+
'\u0265': 'h',
|
| 2652 |
+
'\u0195': 'hv',
|
| 2653 |
+
'\u24D8': 'i',
|
| 2654 |
+
'\uFF49': 'i',
|
| 2655 |
+
'\u00EC': 'i',
|
| 2656 |
+
'\u00ED': 'i',
|
| 2657 |
+
'\u00EE': 'i',
|
| 2658 |
+
'\u0129': 'i',
|
| 2659 |
+
'\u012B': 'i',
|
| 2660 |
+
'\u012D': 'i',
|
| 2661 |
+
'\u00EF': 'i',
|
| 2662 |
+
'\u1E2F': 'i',
|
| 2663 |
+
'\u1EC9': 'i',
|
| 2664 |
+
'\u01D0': 'i',
|
| 2665 |
+
'\u0209': 'i',
|
| 2666 |
+
'\u020B': 'i',
|
| 2667 |
+
'\u1ECB': 'i',
|
| 2668 |
+
'\u012F': 'i',
|
| 2669 |
+
'\u1E2D': 'i',
|
| 2670 |
+
'\u0268': 'i',
|
| 2671 |
+
'\u0131': 'i',
|
| 2672 |
+
'\u24D9': 'j',
|
| 2673 |
+
'\uFF4A': 'j',
|
| 2674 |
+
'\u0135': 'j',
|
| 2675 |
+
'\u01F0': 'j',
|
| 2676 |
+
'\u0249': 'j',
|
| 2677 |
+
'\u24DA': 'k',
|
| 2678 |
+
'\uFF4B': 'k',
|
| 2679 |
+
'\u1E31': 'k',
|
| 2680 |
+
'\u01E9': 'k',
|
| 2681 |
+
'\u1E33': 'k',
|
| 2682 |
+
'\u0137': 'k',
|
| 2683 |
+
'\u1E35': 'k',
|
| 2684 |
+
'\u0199': 'k',
|
| 2685 |
+
'\u2C6A': 'k',
|
| 2686 |
+
'\uA741': 'k',
|
| 2687 |
+
'\uA743': 'k',
|
| 2688 |
+
'\uA745': 'k',
|
| 2689 |
+
'\uA7A3': 'k',
|
| 2690 |
+
'\u24DB': 'l',
|
| 2691 |
+
'\uFF4C': 'l',
|
| 2692 |
+
'\u0140': 'l',
|
| 2693 |
+
'\u013A': 'l',
|
| 2694 |
+
'\u013E': 'l',
|
| 2695 |
+
'\u1E37': 'l',
|
| 2696 |
+
'\u1E39': 'l',
|
| 2697 |
+
'\u013C': 'l',
|
| 2698 |
+
'\u1E3D': 'l',
|
| 2699 |
+
'\u1E3B': 'l',
|
| 2700 |
+
'\u017F': 'l',
|
| 2701 |
+
'\u0142': 'l',
|
| 2702 |
+
'\u019A': 'l',
|
| 2703 |
+
'\u026B': 'l',
|
| 2704 |
+
'\u2C61': 'l',
|
| 2705 |
+
'\uA749': 'l',
|
| 2706 |
+
'\uA781': 'l',
|
| 2707 |
+
'\uA747': 'l',
|
| 2708 |
+
'\u01C9': 'lj',
|
| 2709 |
+
'\u24DC': 'm',
|
| 2710 |
+
'\uFF4D': 'm',
|
| 2711 |
+
'\u1E3F': 'm',
|
| 2712 |
+
'\u1E41': 'm',
|
| 2713 |
+
'\u1E43': 'm',
|
| 2714 |
+
'\u0271': 'm',
|
| 2715 |
+
'\u026F': 'm',
|
| 2716 |
+
'\u24DD': 'n',
|
| 2717 |
+
'\uFF4E': 'n',
|
| 2718 |
+
'\u01F9': 'n',
|
| 2719 |
+
'\u0144': 'n',
|
| 2720 |
+
'\u00F1': 'n',
|
| 2721 |
+
'\u1E45': 'n',
|
| 2722 |
+
'\u0148': 'n',
|
| 2723 |
+
'\u1E47': 'n',
|
| 2724 |
+
'\u0146': 'n',
|
| 2725 |
+
'\u1E4B': 'n',
|
| 2726 |
+
'\u1E49': 'n',
|
| 2727 |
+
'\u019E': 'n',
|
| 2728 |
+
'\u0272': 'n',
|
| 2729 |
+
'\u0149': 'n',
|
| 2730 |
+
'\uA791': 'n',
|
| 2731 |
+
'\uA7A5': 'n',
|
| 2732 |
+
'\u01CC': 'nj',
|
| 2733 |
+
'\u24DE': 'o',
|
| 2734 |
+
'\uFF4F': 'o',
|
| 2735 |
+
'\u00F2': 'o',
|
| 2736 |
+
'\u00F3': 'o',
|
| 2737 |
+
'\u00F4': 'o',
|
| 2738 |
+
'\u1ED3': 'o',
|
| 2739 |
+
'\u1ED1': 'o',
|
| 2740 |
+
'\u1ED7': 'o',
|
| 2741 |
+
'\u1ED5': 'o',
|
| 2742 |
+
'\u00F5': 'o',
|
| 2743 |
+
'\u1E4D': 'o',
|
| 2744 |
+
'\u022D': 'o',
|
| 2745 |
+
'\u1E4F': 'o',
|
| 2746 |
+
'\u014D': 'o',
|
| 2747 |
+
'\u1E51': 'o',
|
| 2748 |
+
'\u1E53': 'o',
|
| 2749 |
+
'\u014F': 'o',
|
| 2750 |
+
'\u022F': 'o',
|
| 2751 |
+
'\u0231': 'o',
|
| 2752 |
+
'\u00F6': 'o',
|
| 2753 |
+
'\u022B': 'o',
|
| 2754 |
+
'\u1ECF': 'o',
|
| 2755 |
+
'\u0151': 'o',
|
| 2756 |
+
'\u01D2': 'o',
|
| 2757 |
+
'\u020D': 'o',
|
| 2758 |
+
'\u020F': 'o',
|
| 2759 |
+
'\u01A1': 'o',
|
| 2760 |
+
'\u1EDD': 'o',
|
| 2761 |
+
'\u1EDB': 'o',
|
| 2762 |
+
'\u1EE1': 'o',
|
| 2763 |
+
'\u1EDF': 'o',
|
| 2764 |
+
'\u1EE3': 'o',
|
| 2765 |
+
'\u1ECD': 'o',
|
| 2766 |
+
'\u1ED9': 'o',
|
| 2767 |
+
'\u01EB': 'o',
|
| 2768 |
+
'\u01ED': 'o',
|
| 2769 |
+
'\u00F8': 'o',
|
| 2770 |
+
'\u01FF': 'o',
|
| 2771 |
+
'\u0254': 'o',
|
| 2772 |
+
'\uA74B': 'o',
|
| 2773 |
+
'\uA74D': 'o',
|
| 2774 |
+
'\u0275': 'o',
|
| 2775 |
+
'\u01A3': 'oi',
|
| 2776 |
+
'\u0223': 'ou',
|
| 2777 |
+
'\uA74F': 'oo',
|
| 2778 |
+
'\u24DF': 'p',
|
| 2779 |
+
'\uFF50': 'p',
|
| 2780 |
+
'\u1E55': 'p',
|
| 2781 |
+
'\u1E57': 'p',
|
| 2782 |
+
'\u01A5': 'p',
|
| 2783 |
+
'\u1D7D': 'p',
|
| 2784 |
+
'\uA751': 'p',
|
| 2785 |
+
'\uA753': 'p',
|
| 2786 |
+
'\uA755': 'p',
|
| 2787 |
+
'\u24E0': 'q',
|
| 2788 |
+
'\uFF51': 'q',
|
| 2789 |
+
'\u024B': 'q',
|
| 2790 |
+
'\uA757': 'q',
|
| 2791 |
+
'\uA759': 'q',
|
| 2792 |
+
'\u24E1': 'r',
|
| 2793 |
+
'\uFF52': 'r',
|
| 2794 |
+
'\u0155': 'r',
|
| 2795 |
+
'\u1E59': 'r',
|
| 2796 |
+
'\u0159': 'r',
|
| 2797 |
+
'\u0211': 'r',
|
| 2798 |
+
'\u0213': 'r',
|
| 2799 |
+
'\u1E5B': 'r',
|
| 2800 |
+
'\u1E5D': 'r',
|
| 2801 |
+
'\u0157': 'r',
|
| 2802 |
+
'\u1E5F': 'r',
|
| 2803 |
+
'\u024D': 'r',
|
| 2804 |
+
'\u027D': 'r',
|
| 2805 |
+
'\uA75B': 'r',
|
| 2806 |
+
'\uA7A7': 'r',
|
| 2807 |
+
'\uA783': 'r',
|
| 2808 |
+
'\u24E2': 's',
|
| 2809 |
+
'\uFF53': 's',
|
| 2810 |
+
'\u00DF': 's',
|
| 2811 |
+
'\u015B': 's',
|
| 2812 |
+
'\u1E65': 's',
|
| 2813 |
+
'\u015D': 's',
|
| 2814 |
+
'\u1E61': 's',
|
| 2815 |
+
'\u0161': 's',
|
| 2816 |
+
'\u1E67': 's',
|
| 2817 |
+
'\u1E63': 's',
|
| 2818 |
+
'\u1E69': 's',
|
| 2819 |
+
'\u0219': 's',
|
| 2820 |
+
'\u015F': 's',
|
| 2821 |
+
'\u023F': 's',
|
| 2822 |
+
'\uA7A9': 's',
|
| 2823 |
+
'\uA785': 's',
|
| 2824 |
+
'\u1E9B': 's',
|
| 2825 |
+
'\u24E3': 't',
|
| 2826 |
+
'\uFF54': 't',
|
| 2827 |
+
'\u1E6B': 't',
|
| 2828 |
+
'\u1E97': 't',
|
| 2829 |
+
'\u0165': 't',
|
| 2830 |
+
'\u1E6D': 't',
|
| 2831 |
+
'\u021B': 't',
|
| 2832 |
+
'\u0163': 't',
|
| 2833 |
+
'\u1E71': 't',
|
| 2834 |
+
'\u1E6F': 't',
|
| 2835 |
+
'\u0167': 't',
|
| 2836 |
+
'\u01AD': 't',
|
| 2837 |
+
'\u0288': 't',
|
| 2838 |
+
'\u2C66': 't',
|
| 2839 |
+
'\uA787': 't',
|
| 2840 |
+
'\uA729': 'tz',
|
| 2841 |
+
'\u24E4': 'u',
|
| 2842 |
+
'\uFF55': 'u',
|
| 2843 |
+
'\u00F9': 'u',
|
| 2844 |
+
'\u00FA': 'u',
|
| 2845 |
+
'\u00FB': 'u',
|
| 2846 |
+
'\u0169': 'u',
|
| 2847 |
+
'\u1E79': 'u',
|
| 2848 |
+
'\u016B': 'u',
|
| 2849 |
+
'\u1E7B': 'u',
|
| 2850 |
+
'\u016D': 'u',
|
| 2851 |
+
'\u00FC': 'u',
|
| 2852 |
+
'\u01DC': 'u',
|
| 2853 |
+
'\u01D8': 'u',
|
| 2854 |
+
'\u01D6': 'u',
|
| 2855 |
+
'\u01DA': 'u',
|
| 2856 |
+
'\u1EE7': 'u',
|
| 2857 |
+
'\u016F': 'u',
|
| 2858 |
+
'\u0171': 'u',
|
| 2859 |
+
'\u01D4': 'u',
|
| 2860 |
+
'\u0215': 'u',
|
| 2861 |
+
'\u0217': 'u',
|
| 2862 |
+
'\u01B0': 'u',
|
| 2863 |
+
'\u1EEB': 'u',
|
| 2864 |
+
'\u1EE9': 'u',
|
| 2865 |
+
'\u1EEF': 'u',
|
| 2866 |
+
'\u1EED': 'u',
|
| 2867 |
+
'\u1EF1': 'u',
|
| 2868 |
+
'\u1EE5': 'u',
|
| 2869 |
+
'\u1E73': 'u',
|
| 2870 |
+
'\u0173': 'u',
|
| 2871 |
+
'\u1E77': 'u',
|
| 2872 |
+
'\u1E75': 'u',
|
| 2873 |
+
'\u0289': 'u',
|
| 2874 |
+
'\u24E5': 'v',
|
| 2875 |
+
'\uFF56': 'v',
|
| 2876 |
+
'\u1E7D': 'v',
|
| 2877 |
+
'\u1E7F': 'v',
|
| 2878 |
+
'\u028B': 'v',
|
| 2879 |
+
'\uA75F': 'v',
|
| 2880 |
+
'\u028C': 'v',
|
| 2881 |
+
'\uA761': 'vy',
|
| 2882 |
+
'\u24E6': 'w',
|
| 2883 |
+
'\uFF57': 'w',
|
| 2884 |
+
'\u1E81': 'w',
|
| 2885 |
+
'\u1E83': 'w',
|
| 2886 |
+
'\u0175': 'w',
|
| 2887 |
+
'\u1E87': 'w',
|
| 2888 |
+
'\u1E85': 'w',
|
| 2889 |
+
'\u1E98': 'w',
|
| 2890 |
+
'\u1E89': 'w',
|
| 2891 |
+
'\u2C73': 'w',
|
| 2892 |
+
'\u24E7': 'x',
|
| 2893 |
+
'\uFF58': 'x',
|
| 2894 |
+
'\u1E8B': 'x',
|
| 2895 |
+
'\u1E8D': 'x',
|
| 2896 |
+
'\u24E8': 'y',
|
| 2897 |
+
'\uFF59': 'y',
|
| 2898 |
+
'\u1EF3': 'y',
|
| 2899 |
+
'\u00FD': 'y',
|
| 2900 |
+
'\u0177': 'y',
|
| 2901 |
+
'\u1EF9': 'y',
|
| 2902 |
+
'\u0233': 'y',
|
| 2903 |
+
'\u1E8F': 'y',
|
| 2904 |
+
'\u00FF': 'y',
|
| 2905 |
+
'\u1EF7': 'y',
|
| 2906 |
+
'\u1E99': 'y',
|
| 2907 |
+
'\u1EF5': 'y',
|
| 2908 |
+
'\u01B4': 'y',
|
| 2909 |
+
'\u024F': 'y',
|
| 2910 |
+
'\u1EFF': 'y',
|
| 2911 |
+
'\u24E9': 'z',
|
| 2912 |
+
'\uFF5A': 'z',
|
| 2913 |
+
'\u017A': 'z',
|
| 2914 |
+
'\u1E91': 'z',
|
| 2915 |
+
'\u017C': 'z',
|
| 2916 |
+
'\u017E': 'z',
|
| 2917 |
+
'\u1E93': 'z',
|
| 2918 |
+
'\u1E95': 'z',
|
| 2919 |
+
'\u01B6': 'z',
|
| 2920 |
+
'\u0225': 'z',
|
| 2921 |
+
'\u0240': 'z',
|
| 2922 |
+
'\u2C6C': 'z',
|
| 2923 |
+
'\uA763': 'z',
|
| 2924 |
+
'\u0386': '\u0391',
|
| 2925 |
+
'\u0388': '\u0395',
|
| 2926 |
+
'\u0389': '\u0397',
|
| 2927 |
+
'\u038A': '\u0399',
|
| 2928 |
+
'\u03AA': '\u0399',
|
| 2929 |
+
'\u038C': '\u039F',
|
| 2930 |
+
'\u038E': '\u03A5',
|
| 2931 |
+
'\u03AB': '\u03A5',
|
| 2932 |
+
'\u038F': '\u03A9',
|
| 2933 |
+
'\u03AC': '\u03B1',
|
| 2934 |
+
'\u03AD': '\u03B5',
|
| 2935 |
+
'\u03AE': '\u03B7',
|
| 2936 |
+
'\u03AF': '\u03B9',
|
| 2937 |
+
'\u03CA': '\u03B9',
|
| 2938 |
+
'\u0390': '\u03B9',
|
| 2939 |
+
'\u03CC': '\u03BF',
|
| 2940 |
+
'\u03CD': '\u03C5',
|
| 2941 |
+
'\u03CB': '\u03C5',
|
| 2942 |
+
'\u03B0': '\u03C5',
|
| 2943 |
+
'\u03C9': '\u03C9',
|
| 2944 |
+
'\u03C2': '\u03C3'
|
| 2945 |
+
};
|
| 2946 |
+
|
| 2947 |
+
return diacritics;
|
| 2948 |
+
});
|
| 2949 |
+
|
| 2950 |
+
S2.define('select2/data/base',[
|
| 2951 |
+
'../utils'
|
| 2952 |
+
], function (Utils) {
|
| 2953 |
+
function BaseAdapter ($element, options) {
|
| 2954 |
+
BaseAdapter.__super__.constructor.call(this);
|
| 2955 |
+
}
|
| 2956 |
+
|
| 2957 |
+
Utils.Extend(BaseAdapter, Utils.Observable);
|
| 2958 |
+
|
| 2959 |
+
BaseAdapter.prototype.current = function (callback) {
|
| 2960 |
+
throw new Error('The `current` method must be defined in child classes.');
|
| 2961 |
+
};
|
| 2962 |
+
|
| 2963 |
+
BaseAdapter.prototype.query = function (params, callback) {
|
| 2964 |
+
throw new Error('The `query` method must be defined in child classes.');
|
| 2965 |
+
};
|
| 2966 |
+
|
| 2967 |
+
BaseAdapter.prototype.bind = function (container, $container) {
|
| 2968 |
+
// Can be implemented in subclasses
|
| 2969 |
+
};
|
| 2970 |
+
|
| 2971 |
+
BaseAdapter.prototype.destroy = function () {
|
| 2972 |
+
// Can be implemented in subclasses
|
| 2973 |
+
};
|
| 2974 |
+
|
| 2975 |
+
BaseAdapter.prototype.generateResultId = function (container, data) {
|
| 2976 |
+
var id = container.id + '-result-';
|
| 2977 |
+
|
| 2978 |
+
id += Utils.generateChars(4);
|
| 2979 |
+
|
| 2980 |
+
if (data.id != null) {
|
| 2981 |
+
id += '-' + data.id.toString();
|
| 2982 |
+
} else {
|
| 2983 |
+
id += '-' + Utils.generateChars(4);
|
| 2984 |
+
}
|
| 2985 |
+
return id;
|
| 2986 |
+
};
|
| 2987 |
+
|
| 2988 |
+
return BaseAdapter;
|
| 2989 |
+
});
|
| 2990 |
+
|
| 2991 |
+
S2.define('select2/data/select',[
|
| 2992 |
+
'./base',
|
| 2993 |
+
'../utils',
|
| 2994 |
+
'jquery'
|
| 2995 |
+
], function (BaseAdapter, Utils, $) {
|
| 2996 |
+
function SelectAdapter ($element, options) {
|
| 2997 |
+
this.$element = $element;
|
| 2998 |
+
this.options = options;
|
| 2999 |
+
|
| 3000 |
+
SelectAdapter.__super__.constructor.call(this);
|
| 3001 |
+
}
|
| 3002 |
+
|
| 3003 |
+
Utils.Extend(SelectAdapter, BaseAdapter);
|
| 3004 |
+
|
| 3005 |
+
SelectAdapter.prototype.current = function (callback) {
|
| 3006 |
+
var data = [];
|
| 3007 |
+
var self = this;
|
| 3008 |
+
|
| 3009 |
+
this.$element.find(':selected').each(function () {
|
| 3010 |
+
var $option = $(this);
|
| 3011 |
+
|
| 3012 |
+
var option = self.item($option);
|
| 3013 |
+
|
| 3014 |
+
data.push(option);
|
| 3015 |
+
});
|
| 3016 |
+
|
| 3017 |
+
callback(data);
|
| 3018 |
+
};
|
| 3019 |
+
|
| 3020 |
+
SelectAdapter.prototype.select = function (data) {
|
| 3021 |
+
var self = this;
|
| 3022 |
+
|
| 3023 |
+
data.selected = true;
|
| 3024 |
+
|
| 3025 |
+
// If data.element is a DOM node, use it instead
|
| 3026 |
+
if ($(data.element).is('option')) {
|
| 3027 |
+
data.element.selected = true;
|
| 3028 |
+
|
| 3029 |
+
this.$element.trigger('change');
|
| 3030 |
+
|
| 3031 |
+
return;
|
| 3032 |
+
}
|
| 3033 |
+
|
| 3034 |
+
if (this.$element.prop('multiple')) {
|
| 3035 |
+
this.current(function (currentData) {
|
| 3036 |
+
var val = [];
|
| 3037 |
+
|
| 3038 |
+
data = [data];
|
| 3039 |
+
data.push.apply(data, currentData);
|
| 3040 |
+
|
| 3041 |
+
for (var d = 0; d < data.length; d++) {
|
| 3042 |
+
var id = data[d].id;
|
| 3043 |
+
|
| 3044 |
+
if ($.inArray(id, val) === -1) {
|
| 3045 |
+
val.push(id);
|
| 3046 |
+
}
|
| 3047 |
+
}
|
| 3048 |
+
|
| 3049 |
+
self.$element.val(val);
|
| 3050 |
+
self.$element.trigger('change');
|
| 3051 |
+
});
|
| 3052 |
+
} else {
|
| 3053 |
+
var val = data.id;
|
| 3054 |
+
|
| 3055 |
+
this.$element.val(val);
|
| 3056 |
+
this.$element.trigger('change');
|
| 3057 |
+
}
|
| 3058 |
+
};
|
| 3059 |
+
|
| 3060 |
+
SelectAdapter.prototype.unselect = function (data) {
|
| 3061 |
+
var self = this;
|
| 3062 |
+
|
| 3063 |
+
if (!this.$element.prop('multiple')) {
|
| 3064 |
+
return;
|
| 3065 |
+
}
|
| 3066 |
+
|
| 3067 |
+
data.selected = false;
|
| 3068 |
+
|
| 3069 |
+
if ($(data.element).is('option')) {
|
| 3070 |
+
data.element.selected = false;
|
| 3071 |
+
|
| 3072 |
+
this.$element.trigger('change');
|
| 3073 |
+
|
| 3074 |
+
return;
|
| 3075 |
+
}
|
| 3076 |
+
|
| 3077 |
+
this.current(function (currentData) {
|
| 3078 |
+
var val = [];
|
| 3079 |
+
|
| 3080 |
+
for (var d = 0; d < currentData.length; d++) {
|
| 3081 |
+
var id = currentData[d].id;
|
| 3082 |
+
|
| 3083 |
+
if (id !== data.id && $.inArray(id, val) === -1) {
|
| 3084 |
+
val.push(id);
|
| 3085 |
+
}
|
| 3086 |
+
}
|
| 3087 |
+
|
| 3088 |
+
self.$element.val(val);
|
| 3089 |
+
|
| 3090 |
+
self.$element.trigger('change');
|
| 3091 |
+
});
|
| 3092 |
+
};
|
| 3093 |
+
|
| 3094 |
+
SelectAdapter.prototype.bind = function (container, $container) {
|
| 3095 |
+
var self = this;
|
| 3096 |
+
|
| 3097 |
+
this.container = container;
|
| 3098 |
+
|
| 3099 |
+
container.on('select', function (params) {
|
| 3100 |
+
self.select(params.data);
|
| 3101 |
+
});
|
| 3102 |
+
|
| 3103 |
+
container.on('unselect', function (params) {
|
| 3104 |
+
self.unselect(params.data);
|
| 3105 |
+
});
|
| 3106 |
+
};
|
| 3107 |
+
|
| 3108 |
+
SelectAdapter.prototype.destroy = function () {
|
| 3109 |
+
// Remove anything added to child elements
|
| 3110 |
+
this.$element.find('*').each(function () {
|
| 3111 |
+
// Remove any custom data set by Select2
|
| 3112 |
+
$.removeData(this, 'data');
|
| 3113 |
+
});
|
| 3114 |
+
};
|
| 3115 |
+
|
| 3116 |
+
SelectAdapter.prototype.query = function (params, callback) {
|
| 3117 |
+
var data = [];
|
| 3118 |
+
var self = this;
|
| 3119 |
+
|
| 3120 |
+
var $options = this.$element.children();
|
| 3121 |
+
|
| 3122 |
+
$options.each(function () {
|
| 3123 |
+
var $option = $(this);
|
| 3124 |
+
|
| 3125 |
+
if (!$option.is('option') && !$option.is('optgroup')) {
|
| 3126 |
+
return;
|
| 3127 |
+
}
|
| 3128 |
+
|
| 3129 |
+
var option = self.item($option);
|
| 3130 |
+
|
| 3131 |
+
var matches = self.matches(params, option);
|
| 3132 |
+
|
| 3133 |
+
if (matches !== null) {
|
| 3134 |
+
data.push(matches);
|
| 3135 |
+
}
|
| 3136 |
+
});
|
| 3137 |
+
|
| 3138 |
+
callback({
|
| 3139 |
+
results: data
|
| 3140 |
+
});
|
| 3141 |
+
};
|
| 3142 |
+
|
| 3143 |
+
SelectAdapter.prototype.addOptions = function ($options) {
|
| 3144 |
+
Utils.appendMany(this.$element, $options);
|
| 3145 |
+
};
|
| 3146 |
+
|
| 3147 |
+
SelectAdapter.prototype.option = function (data) {
|
| 3148 |
+
var option;
|
| 3149 |
+
|
| 3150 |
+
if (data.children) {
|
| 3151 |
+
option = document.createElement('optgroup');
|
| 3152 |
+
option.label = data.text;
|
| 3153 |
+
} else {
|
| 3154 |
+
option = document.createElement('option');
|
| 3155 |
+
|
| 3156 |
+
if (option.textContent !== undefined) {
|
| 3157 |
+
option.textContent = data.text;
|
| 3158 |
+
} else {
|
| 3159 |
+
option.innerText = data.text;
|
| 3160 |
+
}
|
| 3161 |
+
}
|
| 3162 |
+
|
| 3163 |
+
if (data.id) {
|
| 3164 |
+
option.value = data.id;
|
| 3165 |
+
}
|
| 3166 |
+
|
| 3167 |
+
if (data.disabled) {
|
| 3168 |
+
option.disabled = true;
|
| 3169 |
+
}
|
| 3170 |
+
|
| 3171 |
+
if (data.selected) {
|
| 3172 |
+
option.selected = true;
|
| 3173 |
+
}
|
| 3174 |
+
|
| 3175 |
+
if (data.title) {
|
| 3176 |
+
option.title = data.title;
|
| 3177 |
+
}
|
| 3178 |
+
|
| 3179 |
+
var $option = $(option);
|
| 3180 |
+
|
| 3181 |
+
var normalizedData = this._normalizeItem(data);
|
| 3182 |
+
normalizedData.element = option;
|
| 3183 |
+
|
| 3184 |
+
// Override the option's data with the combined data
|
| 3185 |
+
$.data(option, 'data', normalizedData);
|
| 3186 |
+
|
| 3187 |
+
return $option;
|
| 3188 |
+
};
|
| 3189 |
+
|
| 3190 |
+
SelectAdapter.prototype.item = function ($option) {
|
| 3191 |
+
var data = {};
|
| 3192 |
+
|
| 3193 |
+
data = $.data($option[0], 'data');
|
| 3194 |
+
|
| 3195 |
+
if (data != null) {
|
| 3196 |
+
return data;
|
| 3197 |
+
}
|
| 3198 |
+
|
| 3199 |
+
if ($option.is('option')) {
|
| 3200 |
+
data = {
|
| 3201 |
+
id: $option.val(),
|
| 3202 |
+
text: $option.text(),
|
| 3203 |
+
disabled: $option.prop('disabled'),
|
| 3204 |
+
selected: $option.prop('selected'),
|
| 3205 |
+
title: $option.prop('title')
|
| 3206 |
+
};
|
| 3207 |
+
} else if ($option.is('optgroup')) {
|
| 3208 |
+
data = {
|
| 3209 |
+
text: $option.prop('label'),
|
| 3210 |
+
children: [],
|
| 3211 |
+
title: $option.prop('title')
|
| 3212 |
+
};
|
| 3213 |
+
|
| 3214 |
+
var $children = $option.children('option');
|
| 3215 |
+
var children = [];
|
| 3216 |
+
|
| 3217 |
+
for (var c = 0; c < $children.length; c++) {
|
| 3218 |
+
var $child = $($children[c]);
|
| 3219 |
+
|
| 3220 |
+
var child = this.item($child);
|
| 3221 |
+
|
| 3222 |
+
children.push(child);
|
| 3223 |
+
}
|
| 3224 |
+
|
| 3225 |
+
data.children = children;
|
| 3226 |
+
}
|
| 3227 |
+
|
| 3228 |
+
data = this._normalizeItem(data);
|
| 3229 |
+
data.element = $option[0];
|
| 3230 |
+
|
| 3231 |
+
$.data($option[0], 'data', data);
|
| 3232 |
+
|
| 3233 |
+
return data;
|
| 3234 |
+
};
|
| 3235 |
+
|
| 3236 |
+
SelectAdapter.prototype._normalizeItem = function (item) {
|
| 3237 |
+
if (!$.isPlainObject(item)) {
|
| 3238 |
+
item = {
|
| 3239 |
+
id: item,
|
| 3240 |
+
text: item
|
| 3241 |
+
};
|
| 3242 |
+
}
|
| 3243 |
+
|
| 3244 |
+
item = $.extend({}, {
|
| 3245 |
+
text: ''
|
| 3246 |
+
}, item);
|
| 3247 |
+
|
| 3248 |
+
var defaults = {
|
| 3249 |
+
selected: false,
|
| 3250 |
+
disabled: false
|
| 3251 |
+
};
|
| 3252 |
+
|
| 3253 |
+
if (item.id != null) {
|
| 3254 |
+
item.id = item.id.toString();
|
| 3255 |
+
}
|
| 3256 |
+
|
| 3257 |
+
if (item.text != null) {
|
| 3258 |
+
item.text = item.text.toString();
|
| 3259 |
+
}
|
| 3260 |
+
|
| 3261 |
+
if (item._resultId == null && item.id && this.container != null) {
|
| 3262 |
+
item._resultId = this.generateResultId(this.container, item);
|
| 3263 |
+
}
|
| 3264 |
+
|
| 3265 |
+
return $.extend({}, defaults, item);
|
| 3266 |
+
};
|
| 3267 |
+
|
| 3268 |
+
SelectAdapter.prototype.matches = function (params, data) {
|
| 3269 |
+
var matcher = this.options.get('matcher');
|
| 3270 |
+
|
| 3271 |
+
return matcher(params, data);
|
| 3272 |
+
};
|
| 3273 |
+
|
| 3274 |
+
return SelectAdapter;
|
| 3275 |
+
});
|
| 3276 |
+
|
| 3277 |
+
S2.define('select2/data/array',[
|
| 3278 |
+
'./select',
|
| 3279 |
+
'../utils',
|
| 3280 |
+
'jquery'
|
| 3281 |
+
], function (SelectAdapter, Utils, $) {
|
| 3282 |
+
function ArrayAdapter ($element, options) {
|
| 3283 |
+
var data = options.get('data') || [];
|
| 3284 |
+
|
| 3285 |
+
ArrayAdapter.__super__.constructor.call(this, $element, options);
|
| 3286 |
+
|
| 3287 |
+
this.addOptions(this.convertToOptions(data));
|
| 3288 |
+
}
|
| 3289 |
+
|
| 3290 |
+
Utils.Extend(ArrayAdapter, SelectAdapter);
|
| 3291 |
+
|
| 3292 |
+
ArrayAdapter.prototype.select = function (data) {
|
| 3293 |
+
var $option = this.$element.find('option').filter(function (i, elm) {
|
| 3294 |
+
return elm.value == data.id.toString();
|
| 3295 |
+
});
|
| 3296 |
+
|
| 3297 |
+
if ($option.length === 0) {
|
| 3298 |
+
$option = this.option(data);
|
| 3299 |
+
|
| 3300 |
+
this.addOptions($option);
|
| 3301 |
+
}
|
| 3302 |
+
|
| 3303 |
+
ArrayAdapter.__super__.select.call(this, data);
|
| 3304 |
+
};
|
| 3305 |
+
|
| 3306 |
+
ArrayAdapter.prototype.convertToOptions = function (data) {
|
| 3307 |
+
var self = this;
|
| 3308 |
+
|
| 3309 |
+
var $existing = this.$element.find('option');
|
| 3310 |
+
var existingIds = $existing.map(function () {
|
| 3311 |
+
return self.item($(this)).id;
|
| 3312 |
+
}).get();
|
| 3313 |
+
|
| 3314 |
+
var $options = [];
|
| 3315 |
+
|
| 3316 |
+
// Filter out all items except for the one passed in the argument
|
| 3317 |
+
function onlyItem (item) {
|
| 3318 |
+
return function () {
|
| 3319 |
+
return $(this).val() == item.id;
|
| 3320 |
+
};
|
| 3321 |
+
}
|
| 3322 |
+
|
| 3323 |
+
for (var d = 0; d < data.length; d++) {
|
| 3324 |
+
var item = this._normalizeItem(data[d]);
|
| 3325 |
+
|
| 3326 |
+
// Skip items which were pre-loaded, only merge the data
|
| 3327 |
+
if ($.inArray(item.id, existingIds) >= 0) {
|
| 3328 |
+
var $existingOption = $existing.filter(onlyItem(item));
|
| 3329 |
+
|
| 3330 |
+
var existingData = this.item($existingOption);
|
| 3331 |
+
var newData = $.extend(true, {}, item, existingData);
|
| 3332 |
+
|
| 3333 |
+
var $newOption = this.option(newData);
|
| 3334 |
+
|
| 3335 |
+
$existingOption.replaceWith($newOption);
|
| 3336 |
+
|
| 3337 |
+
continue;
|
| 3338 |
+
}
|
| 3339 |
+
|
| 3340 |
+
var $option = this.option(item);
|
| 3341 |
+
|
| 3342 |
+
if (item.children) {
|
| 3343 |
+
var $children = this.convertToOptions(item.children);
|
| 3344 |
+
|
| 3345 |
+
Utils.appendMany($option, $children);
|
| 3346 |
+
}
|
| 3347 |
+
|
| 3348 |
+
$options.push($option);
|
| 3349 |
+
}
|
| 3350 |
+
|
| 3351 |
+
return $options;
|
| 3352 |
+
};
|
| 3353 |
+
|
| 3354 |
+
return ArrayAdapter;
|
| 3355 |
+
});
|
| 3356 |
+
|
| 3357 |
+
S2.define('select2/data/ajax',[
|
| 3358 |
+
'./array',
|
| 3359 |
+
'../utils',
|
| 3360 |
+
'jquery'
|
| 3361 |
+
], function (ArrayAdapter, Utils, $) {
|
| 3362 |
+
function AjaxAdapter ($element, options) {
|
| 3363 |
+
this.ajaxOptions = this._applyDefaults(options.get('ajax'));
|
| 3364 |
+
|
| 3365 |
+
if (this.ajaxOptions.processResults != null) {
|
| 3366 |
+
this.processResults = this.ajaxOptions.processResults;
|
| 3367 |
+
}
|
| 3368 |
+
|
| 3369 |
+
AjaxAdapter.__super__.constructor.call(this, $element, options);
|
| 3370 |
+
}
|
| 3371 |
+
|
| 3372 |
+
Utils.Extend(AjaxAdapter, ArrayAdapter);
|
| 3373 |
+
|
| 3374 |
+
AjaxAdapter.prototype._applyDefaults = function (options) {
|
| 3375 |
+
var defaults = {
|
| 3376 |
+
data: function (params) {
|
| 3377 |
+
return $.extend({}, params, {
|
| 3378 |
+
q: params.term
|
| 3379 |
+
});
|
| 3380 |
+
},
|
| 3381 |
+
transport: function (params, success, failure) {
|
| 3382 |
+
var $request = $.ajax(params);
|
| 3383 |
+
|
| 3384 |
+
$request.then(success);
|
| 3385 |
+
$request.fail(failure);
|
| 3386 |
+
|
| 3387 |
+
return $request;
|
| 3388 |
+
}
|
| 3389 |
+
};
|
| 3390 |
+
|
| 3391 |
+
return $.extend({}, defaults, options, true);
|
| 3392 |
+
};
|
| 3393 |
+
|
| 3394 |
+
AjaxAdapter.prototype.processResults = function (results) {
|
| 3395 |
+
return results;
|
| 3396 |
+
};
|
| 3397 |
+
|
| 3398 |
+
AjaxAdapter.prototype.query = function (params, callback) {
|
| 3399 |
+
var matches = [];
|
| 3400 |
+
var self = this;
|
| 3401 |
+
|
| 3402 |
+
if (this._request != null) {
|
| 3403 |
+
// JSONP requests cannot always be aborted
|
| 3404 |
+
if ($.isFunction(this._request.abort)) {
|
| 3405 |
+
this._request.abort();
|
| 3406 |
+
}
|
| 3407 |
+
|
| 3408 |
+
this._request = null;
|
| 3409 |
+
}
|
| 3410 |
+
|
| 3411 |
+
var options = $.extend({
|
| 3412 |
+
type: 'GET'
|
| 3413 |
+
}, this.ajaxOptions);
|
| 3414 |
+
|
| 3415 |
+
if (typeof options.url === 'function') {
|
| 3416 |
+
options.url = options.url.call(this.$element, params);
|
| 3417 |
+
}
|
| 3418 |
+
|
| 3419 |
+
if (typeof options.data === 'function') {
|
| 3420 |
+
options.data = options.data.call(this.$element, params);
|
| 3421 |
+
}
|
| 3422 |
+
|
| 3423 |
+
function request () {
|
| 3424 |
+
var $request = options.transport(options, function (data) {
|
| 3425 |
+
var results = self.processResults(data, params);
|
| 3426 |
+
|
| 3427 |
+
if (self.options.get('debug') && window.console && console.error) {
|
| 3428 |
+
// Check to make sure that the response included a `results` key.
|
| 3429 |
+
if (!results || !results.results || !$.isArray(results.results)) {
|
| 3430 |
+
console.error(
|
| 3431 |
+
'Select2: The AJAX results did not return an array in the ' +
|
| 3432 |
+
'`results` key of the response.'
|
| 3433 |
+
);
|
| 3434 |
+
}
|
| 3435 |
+
}
|
| 3436 |
+
|
| 3437 |
+
callback(results);
|
| 3438 |
+
}, function () {
|
| 3439 |
+
self.trigger('results:message', {
|
| 3440 |
+
message: 'errorLoading'
|
| 3441 |
+
});
|
| 3442 |
+
});
|
| 3443 |
+
|
| 3444 |
+
self._request = $request;
|
| 3445 |
+
}
|
| 3446 |
+
|
| 3447 |
+
if (this.ajaxOptions.delay && params.term !== '') {
|
| 3448 |
+
if (this._queryTimeout) {
|
| 3449 |
+
window.clearTimeout(this._queryTimeout);
|
| 3450 |
+
}
|
| 3451 |
+
|
| 3452 |
+
this._queryTimeout = window.setTimeout(request, this.ajaxOptions.delay);
|
| 3453 |
+
} else {
|
| 3454 |
+
request();
|
| 3455 |
+
}
|
| 3456 |
+
};
|
| 3457 |
+
|
| 3458 |
+
return AjaxAdapter;
|
| 3459 |
+
});
|
| 3460 |
+
|
| 3461 |
+
S2.define('select2/data/tags',[
|
| 3462 |
+
'jquery'
|
| 3463 |
+
], function ($) {
|
| 3464 |
+
function Tags (decorated, $element, options) {
|
| 3465 |
+
var tags = options.get('tags');
|
| 3466 |
+
|
| 3467 |
+
var createTag = options.get('createTag');
|
| 3468 |
+
|
| 3469 |
+
if (createTag !== undefined) {
|
| 3470 |
+
this.createTag = createTag;
|
| 3471 |
+
}
|
| 3472 |
+
|
| 3473 |
+
var insertTag = options.get('insertTag');
|
| 3474 |
+
|
| 3475 |
+
if (insertTag !== undefined) {
|
| 3476 |
+
this.insertTag = insertTag;
|
| 3477 |
+
}
|
| 3478 |
+
|
| 3479 |
+
decorated.call(this, $element, options);
|
| 3480 |
+
|
| 3481 |
+
if ($.isArray(tags)) {
|
| 3482 |
+
for (var t = 0; t < tags.length; t++) {
|
| 3483 |
+
var tag = tags[t];
|
| 3484 |
+
var item = this._normalizeItem(tag);
|
| 3485 |
+
|
| 3486 |
+
var $option = this.option(item);
|
| 3487 |
+
|
| 3488 |
+
this.$element.append($option);
|
| 3489 |
+
}
|
| 3490 |
+
}
|
| 3491 |
+
}
|
| 3492 |
+
|
| 3493 |
+
Tags.prototype.query = function (decorated, params, callback) {
|
| 3494 |
+
var self = this;
|
| 3495 |
+
|
| 3496 |
+
this._removeOldTags();
|
| 3497 |
+
|
| 3498 |
+
if (params.term == null || params.page != null) {
|
| 3499 |
+
decorated.call(this, params, callback);
|
| 3500 |
+
return;
|
| 3501 |
+
}
|
| 3502 |
+
|
| 3503 |
+
function wrapper (obj, child) {
|
| 3504 |
+
var data = obj.results;
|
| 3505 |
+
|
| 3506 |
+
for (var i = 0; i < data.length; i++) {
|
| 3507 |
+
var option = data[i];
|
| 3508 |
+
|
| 3509 |
+
var checkChildren = (
|
| 3510 |
+
option.children != null &&
|
| 3511 |
+
!wrapper({
|
| 3512 |
+
results: option.children
|
| 3513 |
+
}, true)
|
| 3514 |
+
);
|
| 3515 |
+
|
| 3516 |
+
var checkText = option.text === params.term;
|
| 3517 |
+
|
| 3518 |
+
if (checkText || checkChildren) {
|
| 3519 |
+
if (child) {
|
| 3520 |
+
return false;
|
| 3521 |
+
}
|
| 3522 |
+
|
| 3523 |
+
obj.data = data;
|
| 3524 |
+
callback(obj);
|
| 3525 |
+
|
| 3526 |
+
return;
|
| 3527 |
+
}
|
| 3528 |
+
}
|
| 3529 |
+
|
| 3530 |
+
if (child) {
|
| 3531 |
+
return true;
|
| 3532 |
+
}
|
| 3533 |
+
|
| 3534 |
+
var tag = self.createTag(params);
|
| 3535 |
+
|
| 3536 |
+
if (tag != null) {
|
| 3537 |
+
var $option = self.option(tag);
|
| 3538 |
+
$option.attr('data-select2-tag', true);
|
| 3539 |
+
|
| 3540 |
+
self.addOptions([$option]);
|
| 3541 |
+
|
| 3542 |
+
self.insertTag(data, tag);
|
| 3543 |
+
}
|
| 3544 |
+
|
| 3545 |
+
obj.results = data;
|
| 3546 |
+
|
| 3547 |
+
callback(obj);
|
| 3548 |
+
}
|
| 3549 |
+
|
| 3550 |
+
decorated.call(this, params, wrapper);
|
| 3551 |
+
};
|
| 3552 |
+
|
| 3553 |
+
Tags.prototype.createTag = function (decorated, params) {
|
| 3554 |
+
var term = $.trim(params.term);
|
| 3555 |
+
|
| 3556 |
+
if (term === '') {
|
| 3557 |
+
return null;
|
| 3558 |
+
}
|
| 3559 |
+
|
| 3560 |
+
return {
|
| 3561 |
+
id: term,
|
| 3562 |
+
text: term
|
| 3563 |
+
};
|
| 3564 |
+
};
|
| 3565 |
+
|
| 3566 |
+
Tags.prototype.insertTag = function (_, data, tag) {
|
| 3567 |
+
data.unshift(tag);
|
| 3568 |
+
};
|
| 3569 |
+
|
| 3570 |
+
Tags.prototype._removeOldTags = function (_) {
|
| 3571 |
+
var tag = this._lastTag;
|
| 3572 |
+
|
| 3573 |
+
var $options = this.$element.find('option[data-select2-tag]');
|
| 3574 |
+
|
| 3575 |
+
$options.each(function () {
|
| 3576 |
+
if (this.selected) {
|
| 3577 |
+
return;
|
| 3578 |
+
}
|
| 3579 |
+
|
| 3580 |
+
$(this).remove();
|
| 3581 |
+
});
|
| 3582 |
+
};
|
| 3583 |
+
|
| 3584 |
+
return Tags;
|
| 3585 |
+
});
|
| 3586 |
+
|
| 3587 |
+
S2.define('select2/data/tokenizer',[
|
| 3588 |
+
'jquery'
|
| 3589 |
+
], function ($) {
|
| 3590 |
+
function Tokenizer (decorated, $element, options) {
|
| 3591 |
+
var tokenizer = options.get('tokenizer');
|
| 3592 |
+
|
| 3593 |
+
if (tokenizer !== undefined) {
|
| 3594 |
+
this.tokenizer = tokenizer;
|
| 3595 |
+
}
|
| 3596 |
+
|
| 3597 |
+
decorated.call(this, $element, options);
|
| 3598 |
+
}
|
| 3599 |
+
|
| 3600 |
+
Tokenizer.prototype.bind = function (decorated, container, $container) {
|
| 3601 |
+
decorated.call(this, container, $container);
|
| 3602 |
+
|
| 3603 |
+
this.$search = container.dropdown.$search || container.selection.$search ||
|
| 3604 |
+
$container.find('.select2-search__field');
|
| 3605 |
+
};
|
| 3606 |
+
|
| 3607 |
+
Tokenizer.prototype.query = function (decorated, params, callback) {
|
| 3608 |
+
var self = this;
|
| 3609 |
+
|
| 3610 |
+
function select (data) {
|
| 3611 |
+
self.trigger('select', {
|
| 3612 |
+
data: data
|
| 3613 |
+
});
|
| 3614 |
+
}
|
| 3615 |
+
|
| 3616 |
+
params.term = params.term || '';
|
| 3617 |
+
|
| 3618 |
+
var tokenData = this.tokenizer(params, this.options, select);
|
| 3619 |
+
|
| 3620 |
+
if (tokenData.term !== params.term) {
|
| 3621 |
+
// Replace the search term if we have the search box
|
| 3622 |
+
if (this.$search.length) {
|
| 3623 |
+
this.$search.val(tokenData.term);
|
| 3624 |
+
this.$search.focus();
|
| 3625 |
+
}
|
| 3626 |
+
|
| 3627 |
+
params.term = tokenData.term;
|
| 3628 |
+
}
|
| 3629 |
+
|
| 3630 |
+
decorated.call(this, params, callback);
|
| 3631 |
+
};
|
| 3632 |
+
|
| 3633 |
+
Tokenizer.prototype.tokenizer = function (_, params, options, callback) {
|
| 3634 |
+
var separators = options.get('tokenSeparators') || [];
|
| 3635 |
+
var term = params.term;
|
| 3636 |
+
var i = 0;
|
| 3637 |
+
|
| 3638 |
+
var createTag = this.createTag || function (params) {
|
| 3639 |
+
return {
|
| 3640 |
+
id: params.term,
|
| 3641 |
+
text: params.term
|
| 3642 |
+
};
|
| 3643 |
+
};
|
| 3644 |
+
|
| 3645 |
+
while (i < term.length) {
|
| 3646 |
+
var termChar = term[i];
|
| 3647 |
+
|
| 3648 |
+
if ($.inArray(termChar, separators) === -1) {
|
| 3649 |
+
i++;
|
| 3650 |
+
|
| 3651 |
+
continue;
|
| 3652 |
+
}
|
| 3653 |
+
|
| 3654 |
+
var part = term.substr(0, i);
|
| 3655 |
+
var partParams = $.extend({}, params, {
|
| 3656 |
+
term: part
|
| 3657 |
+
});
|
| 3658 |
+
|
| 3659 |
+
var data = createTag(partParams);
|
| 3660 |
+
|
| 3661 |
+
if (data == null) {
|
| 3662 |
+
i++;
|
| 3663 |
+
continue;
|
| 3664 |
+
}
|
| 3665 |
+
|
| 3666 |
+
callback(data);
|
| 3667 |
+
|
| 3668 |
+
// Reset the term to not include the tokenized portion
|
| 3669 |
+
term = term.substr(i + 1) || '';
|
| 3670 |
+
i = 0;
|
| 3671 |
+
}
|
| 3672 |
+
|
| 3673 |
+
return {
|
| 3674 |
+
term: term
|
| 3675 |
+
};
|
| 3676 |
+
};
|
| 3677 |
+
|
| 3678 |
+
return Tokenizer;
|
| 3679 |
+
});
|
| 3680 |
+
|
| 3681 |
+
S2.define('select2/data/minimumInputLength',[
|
| 3682 |
+
|
| 3683 |
+
], function () {
|
| 3684 |
+
function MinimumInputLength (decorated, $e, options) {
|
| 3685 |
+
this.minimumInputLength = options.get('minimumInputLength');
|
| 3686 |
+
|
| 3687 |
+
decorated.call(this, $e, options);
|
| 3688 |
+
}
|
| 3689 |
+
|
| 3690 |
+
MinimumInputLength.prototype.query = function (decorated, params, callback) {
|
| 3691 |
+
params.term = params.term || '';
|
| 3692 |
+
|
| 3693 |
+
if (params.term.length < this.minimumInputLength) {
|
| 3694 |
+
this.trigger('results:message', {
|
| 3695 |
+
message: 'inputTooShort',
|
| 3696 |
+
args: {
|
| 3697 |
+
minimum: this.minimumInputLength,
|
| 3698 |
+
input: params.term,
|
| 3699 |
+
params: params
|
| 3700 |
+
}
|
| 3701 |
+
});
|
| 3702 |
+
|
| 3703 |
+
return;
|
| 3704 |
+
}
|
| 3705 |
+
|
| 3706 |
+
decorated.call(this, params, callback);
|
| 3707 |
+
};
|
| 3708 |
+
|
| 3709 |
+
return MinimumInputLength;
|
| 3710 |
+
});
|
| 3711 |
+
|
| 3712 |
+
S2.define('select2/data/maximumInputLength',[
|
| 3713 |
+
|
| 3714 |
+
], function () {
|
| 3715 |
+
function MaximumInputLength (decorated, $e, options) {
|
| 3716 |
+
this.maximumInputLength = options.get('maximumInputLength');
|
| 3717 |
+
|
| 3718 |
+
decorated.call(this, $e, options);
|
| 3719 |
+
}
|
| 3720 |
+
|
| 3721 |
+
MaximumInputLength.prototype.query = function (decorated, params, callback) {
|
| 3722 |
+
params.term = params.term || '';
|
| 3723 |
+
|
| 3724 |
+
if (this.maximumInputLength > 0 &&
|
| 3725 |
+
params.term.length > this.maximumInputLength) {
|
| 3726 |
+
this.trigger('results:message', {
|
| 3727 |
+
message: 'inputTooLong',
|
| 3728 |
+
args: {
|
| 3729 |
+
maximum: this.maximumInputLength,
|
| 3730 |
+
input: params.term,
|
| 3731 |
+
params: params
|
| 3732 |
+
}
|
| 3733 |
+
});
|
| 3734 |
+
|
| 3735 |
+
return;
|
| 3736 |
+
}
|
| 3737 |
+
|
| 3738 |
+
decorated.call(this, params, callback);
|
| 3739 |
+
};
|
| 3740 |
+
|
| 3741 |
+
return MaximumInputLength;
|
| 3742 |
+
});
|
| 3743 |
+
|
| 3744 |
+
S2.define('select2/data/maximumSelectionLength',[
|
| 3745 |
+
|
| 3746 |
+
], function (){
|
| 3747 |
+
function MaximumSelectionLength (decorated, $e, options) {
|
| 3748 |
+
this.maximumSelectionLength = options.get('maximumSelectionLength');
|
| 3749 |
+
|
| 3750 |
+
decorated.call(this, $e, options);
|
| 3751 |
+
}
|
| 3752 |
+
|
| 3753 |
+
MaximumSelectionLength.prototype.query =
|
| 3754 |
+
function (decorated, params, callback) {
|
| 3755 |
+
var self = this;
|
| 3756 |
+
|
| 3757 |
+
this.current(function (currentData) {
|
| 3758 |
+
var count = currentData != null ? currentData.length : 0;
|
| 3759 |
+
if (self.maximumSelectionLength > 0 &&
|
| 3760 |
+
count >= self.maximumSelectionLength) {
|
| 3761 |
+
self.trigger('results:message', {
|
| 3762 |
+
message: 'maximumSelected',
|
| 3763 |
+
args: {
|
| 3764 |
+
maximum: self.maximumSelectionLength
|
| 3765 |
+
}
|
| 3766 |
+
});
|
| 3767 |
+
return;
|
| 3768 |
+
}
|
| 3769 |
+
decorated.call(self, params, callback);
|
| 3770 |
+
});
|
| 3771 |
+
};
|
| 3772 |
+
|
| 3773 |
+
return MaximumSelectionLength;
|
| 3774 |
+
});
|
| 3775 |
+
|
| 3776 |
+
S2.define('select2/dropdown',[
|
| 3777 |
+
'jquery',
|
| 3778 |
+
'./utils'
|
| 3779 |
+
], function ($, Utils) {
|
| 3780 |
+
function Dropdown ($element, options) {
|
| 3781 |
+
this.$element = $element;
|
| 3782 |
+
this.options = options;
|
| 3783 |
+
|
| 3784 |
+
Dropdown.__super__.constructor.call(this);
|
| 3785 |
+
}
|
| 3786 |
+
|
| 3787 |
+
Utils.Extend(Dropdown, Utils.Observable);
|
| 3788 |
+
|
| 3789 |
+
Dropdown.prototype.render = function () {
|
| 3790 |
+
var $dropdown = $(
|
| 3791 |
+
'<span class="select2-dropdown">' +
|
| 3792 |
+
'<span class="select2-results"></span>' +
|
| 3793 |
+
'</span>'
|
| 3794 |
+
);
|
| 3795 |
+
|
| 3796 |
+
$dropdown.attr('dir', this.options.get('dir'));
|
| 3797 |
+
|
| 3798 |
+
this.$dropdown = $dropdown;
|
| 3799 |
+
|
| 3800 |
+
return $dropdown;
|
| 3801 |
+
};
|
| 3802 |
+
|
| 3803 |
+
Dropdown.prototype.bind = function () {
|
| 3804 |
+
// Should be implemented in subclasses
|
| 3805 |
+
};
|
| 3806 |
+
|
| 3807 |
+
Dropdown.prototype.position = function ($dropdown, $container) {
|
| 3808 |
+
// Should be implmented in subclasses
|
| 3809 |
+
};
|
| 3810 |
+
|
| 3811 |
+
Dropdown.prototype.destroy = function () {
|
| 3812 |
+
// Remove the dropdown from the DOM
|
| 3813 |
+
this.$dropdown.remove();
|
| 3814 |
+
};
|
| 3815 |
+
|
| 3816 |
+
return Dropdown;
|
| 3817 |
+
});
|
| 3818 |
+
|
| 3819 |
+
S2.define('select2/dropdown/search',[
|
| 3820 |
+
'jquery',
|
| 3821 |
+
'../utils'
|
| 3822 |
+
], function ($, Utils) {
|
| 3823 |
+
function Search () { }
|
| 3824 |
+
|
| 3825 |
+
Search.prototype.render = function (decorated) {
|
| 3826 |
+
var $rendered = decorated.call(this);
|
| 3827 |
+
|
| 3828 |
+
var $search = $(
|
| 3829 |
+
'<span class="select2-search select2-search--dropdown">' +
|
| 3830 |
+
'<input class="select2-search__field" type="search" tabindex="-1"' +
|
| 3831 |
+
' autocomplete="off" autocorrect="off" autocapitalize="off"' +
|
| 3832 |
+
' spellcheck="false" role="textbox" />' +
|
| 3833 |
+
'</span>'
|
| 3834 |
+
);
|
| 3835 |
+
|
| 3836 |
+
this.$searchContainer = $search;
|
| 3837 |
+
this.$search = $search.find('input');
|
| 3838 |
+
|
| 3839 |
+
$rendered.prepend($search);
|
| 3840 |
+
|
| 3841 |
+
return $rendered;
|
| 3842 |
+
};
|
| 3843 |
+
|
| 3844 |
+
Search.prototype.bind = function (decorated, container, $container) {
|
| 3845 |
+
var self = this;
|
| 3846 |
+
|
| 3847 |
+
decorated.call(this, container, $container);
|
| 3848 |
+
|
| 3849 |
+
this.$search.on('keydown', function (evt) {
|
| 3850 |
+
self.trigger('keypress', evt);
|
| 3851 |
+
|
| 3852 |
+
self._keyUpPrevented = evt.isDefaultPrevented();
|
| 3853 |
+
});
|
| 3854 |
+
|
| 3855 |
+
// Workaround for browsers which do not support the `input` event
|
| 3856 |
+
// This will prevent double-triggering of events for browsers which support
|
| 3857 |
+
// both the `keyup` and `input` events.
|
| 3858 |
+
this.$search.on('input', function (evt) {
|
| 3859 |
+
// Unbind the duplicated `keyup` event
|
| 3860 |
+
$(this).off('keyup');
|
| 3861 |
+
});
|
| 3862 |
+
|
| 3863 |
+
this.$search.on('keyup input', function (evt) {
|
| 3864 |
+
self.handleSearch(evt);
|
| 3865 |
+
});
|
| 3866 |
+
|
| 3867 |
+
container.on('open', function () {
|
| 3868 |
+
self.$search.attr('tabindex', 0);
|
| 3869 |
+
|
| 3870 |
+
self.$search.focus();
|
| 3871 |
+
|
| 3872 |
+
window.setTimeout(function () {
|
| 3873 |
+
self.$search.focus();
|
| 3874 |
+
}, 0);
|
| 3875 |
+
});
|
| 3876 |
+
|
| 3877 |
+
container.on('close', function () {
|
| 3878 |
+
self.$search.attr('tabindex', -1);
|
| 3879 |
+
|
| 3880 |
+
self.$search.val('');
|
| 3881 |
+
});
|
| 3882 |
+
|
| 3883 |
+
container.on('results:all', function (params) {
|
| 3884 |
+
if (params.query.term == null || params.query.term === '') {
|
| 3885 |
+
var showSearch = self.showSearch(params);
|
| 3886 |
+
|
| 3887 |
+
if (showSearch) {
|
| 3888 |
+
self.$searchContainer.removeClass('select2-search--hide');
|
| 3889 |
+
} else {
|
| 3890 |
+
self.$searchContainer.addClass('select2-search--hide');
|
| 3891 |
+
}
|
| 3892 |
+
}
|
| 3893 |
+
});
|
| 3894 |
+
};
|
| 3895 |
+
|
| 3896 |
+
Search.prototype.handleSearch = function (evt) {
|
| 3897 |
+
if (!this._keyUpPrevented) {
|
| 3898 |
+
var input = this.$search.val();
|
| 3899 |
+
|
| 3900 |
+
this.trigger('query', {
|
| 3901 |
+
term: input
|
| 3902 |
+
});
|
| 3903 |
+
}
|
| 3904 |
+
|
| 3905 |
+
this._keyUpPrevented = false;
|
| 3906 |
+
};
|
| 3907 |
+
|
| 3908 |
+
Search.prototype.showSearch = function (_, params) {
|
| 3909 |
+
return true;
|
| 3910 |
+
};
|
| 3911 |
+
|
| 3912 |
+
return Search;
|
| 3913 |
+
});
|
| 3914 |
+
|
| 3915 |
+
S2.define('select2/dropdown/hidePlaceholder',[
|
| 3916 |
+
|
| 3917 |
+
], function () {
|
| 3918 |
+
function HidePlaceholder (decorated, $element, options, dataAdapter) {
|
| 3919 |
+
this.placeholder = this.normalizePlaceholder(options.get('placeholder'));
|
| 3920 |
+
|
| 3921 |
+
decorated.call(this, $element, options, dataAdapter);
|
| 3922 |
+
}
|
| 3923 |
+
|
| 3924 |
+
HidePlaceholder.prototype.append = function (decorated, data) {
|
| 3925 |
+
data.results = this.removePlaceholder(data.results);
|
| 3926 |
+
|
| 3927 |
+
decorated.call(this, data);
|
| 3928 |
+
};
|
| 3929 |
+
|
| 3930 |
+
HidePlaceholder.prototype.normalizePlaceholder = function (_, placeholder) {
|
| 3931 |
+
if (typeof placeholder === 'string') {
|
| 3932 |
+
placeholder = {
|
| 3933 |
+
id: '',
|
| 3934 |
+
text: placeholder
|
| 3935 |
+
};
|
| 3936 |
+
}
|
| 3937 |
+
|
| 3938 |
+
return placeholder;
|
| 3939 |
+
};
|
| 3940 |
+
|
| 3941 |
+
HidePlaceholder.prototype.removePlaceholder = function (_, data) {
|
| 3942 |
+
var modifiedData = data.slice(0);
|
| 3943 |
+
|
| 3944 |
+
for (var d = data.length - 1; d >= 0; d--) {
|
| 3945 |
+
var item = data[d];
|
| 3946 |
+
|
| 3947 |
+
if (this.placeholder.id === item.id) {
|
| 3948 |
+
modifiedData.splice(d, 1);
|
| 3949 |
+
}
|
| 3950 |
+
}
|
| 3951 |
+
|
| 3952 |
+
return modifiedData;
|
| 3953 |
+
};
|
| 3954 |
+
|
| 3955 |
+
return HidePlaceholder;
|
| 3956 |
+
});
|
| 3957 |
+
|
| 3958 |
+
S2.define('select2/dropdown/infiniteScroll',[
|
| 3959 |
+
'jquery'
|
| 3960 |
+
], function ($) {
|
| 3961 |
+
function InfiniteScroll (decorated, $element, options, dataAdapter) {
|
| 3962 |
+
this.lastParams = {};
|
| 3963 |
+
|
| 3964 |
+
decorated.call(this, $element, options, dataAdapter);
|
| 3965 |
+
|
| 3966 |
+
this.$loadingMore = this.createLoadingMore();
|
| 3967 |
+
this.loading = false;
|
| 3968 |
+
}
|
| 3969 |
+
|
| 3970 |
+
InfiniteScroll.prototype.append = function (decorated, data) {
|
| 3971 |
+
this.$loadingMore.remove();
|
| 3972 |
+
this.loading = false;
|
| 3973 |
+
|
| 3974 |
+
decorated.call(this, data);
|
| 3975 |
+
|
| 3976 |
+
if (this.showLoadingMore(data)) {
|
| 3977 |
+
this.$results.append(this.$loadingMore);
|
| 3978 |
+
}
|
| 3979 |
+
};
|
| 3980 |
+
|
| 3981 |
+
InfiniteScroll.prototype.bind = function (decorated, container, $container) {
|
| 3982 |
+
var self = this;
|
| 3983 |
+
|
| 3984 |
+
decorated.call(this, container, $container);
|
| 3985 |
+
|
| 3986 |
+
container.on('query', function (params) {
|
| 3987 |
+
self.lastParams = params;
|
| 3988 |
+
self.loading = true;
|
| 3989 |
+
});
|
| 3990 |
+
|
| 3991 |
+
container.on('query:append', function (params) {
|
| 3992 |
+
self.lastParams = params;
|
| 3993 |
+
self.loading = true;
|
| 3994 |
+
});
|
| 3995 |
+
|
| 3996 |
+
this.$results.on('scroll', function () {
|
| 3997 |
+
var isLoadMoreVisible = $.contains(
|
| 3998 |
+
document.documentElement,
|
| 3999 |
+
self.$loadingMore[0]
|
| 4000 |
+
);
|
| 4001 |
+
|
| 4002 |
+
if (self.loading || !isLoadMoreVisible) {
|
| 4003 |
+
return;
|
| 4004 |
+
}
|
| 4005 |
+
|
| 4006 |
+
var currentOffset = self.$results.offset().top +
|
| 4007 |
+
self.$results.outerHeight(false);
|
| 4008 |
+
var loadingMoreOffset = self.$loadingMore.offset().top +
|
| 4009 |
+
self.$loadingMore.outerHeight(false);
|
| 4010 |
+
|
| 4011 |
+
if (currentOffset + 50 >= loadingMoreOffset) {
|
| 4012 |
+
self.loadMore();
|
| 4013 |
+
}
|
| 4014 |
+
});
|
| 4015 |
+
};
|
| 4016 |
+
|
| 4017 |
+
InfiniteScroll.prototype.loadMore = function () {
|
| 4018 |
+
this.loading = true;
|
| 4019 |
+
|
| 4020 |
+
var params = $.extend({}, {page: 1}, this.lastParams);
|
| 4021 |
+
|
| 4022 |
+
params.page++;
|
| 4023 |
+
|
| 4024 |
+
this.trigger('query:append', params);
|
| 4025 |
+
};
|
| 4026 |
+
|
| 4027 |
+
InfiniteScroll.prototype.showLoadingMore = function (_, data) {
|
| 4028 |
+
return data.pagination && data.pagination.more;
|
| 4029 |
+
};
|
| 4030 |
+
|
| 4031 |
+
InfiniteScroll.prototype.createLoadingMore = function () {
|
| 4032 |
+
var $option = $(
|
| 4033 |
+
'<li ' +
|
| 4034 |
+
'class="select2-results__option select2-results__option--load-more"' +
|
| 4035 |
+
'role="treeitem" aria-disabled="true"></li>'
|
| 4036 |
+
);
|
| 4037 |
+
|
| 4038 |
+
var message = this.options.get('translations').get('loadingMore');
|
| 4039 |
+
|
| 4040 |
+
$option.html(message(this.lastParams));
|
| 4041 |
+
|
| 4042 |
+
return $option;
|
| 4043 |
+
};
|
| 4044 |
+
|
| 4045 |
+
return InfiniteScroll;
|
| 4046 |
+
});
|
| 4047 |
+
|
| 4048 |
+
S2.define('select2/dropdown/attachBody',[
|
| 4049 |
+
'jquery',
|
| 4050 |
+
'../utils'
|
| 4051 |
+
], function ($, Utils) {
|
| 4052 |
+
function AttachBody (decorated, $element, options) {
|
| 4053 |
+
this.$dropdownParent = options.get('dropdownParent') || $(document.body);
|
| 4054 |
+
|
| 4055 |
+
decorated.call(this, $element, options);
|
| 4056 |
+
}
|
| 4057 |
+
|
| 4058 |
+
AttachBody.prototype.bind = function (decorated, container, $container) {
|
| 4059 |
+
var self = this;
|
| 4060 |
+
|
| 4061 |
+
var setupResultsEvents = false;
|
| 4062 |
+
|
| 4063 |
+
decorated.call(this, container, $container);
|
| 4064 |
+
|
| 4065 |
+
container.on('open', function () {
|
| 4066 |
+
self._showDropdown();
|
| 4067 |
+
self._attachPositioningHandler(container);
|
| 4068 |
+
|
| 4069 |
+
if (!setupResultsEvents) {
|
| 4070 |
+
setupResultsEvents = true;
|
| 4071 |
+
|
| 4072 |
+
container.on('results:all', function () {
|
| 4073 |
+
self._positionDropdown();
|
| 4074 |
+
self._resizeDropdown();
|
| 4075 |
+
});
|
| 4076 |
+
|
| 4077 |
+
container.on('results:append', function () {
|
| 4078 |
+
self._positionDropdown();
|
| 4079 |
+
self._resizeDropdown();
|
| 4080 |
+
});
|
| 4081 |
+
}
|
| 4082 |
+
});
|
| 4083 |
+
|
| 4084 |
+
container.on('close', function () {
|
| 4085 |
+
self._hideDropdown();
|
| 4086 |
+
self._detachPositioningHandler(container);
|
| 4087 |
+
});
|
| 4088 |
+
|
| 4089 |
+
this.$dropdownContainer.on('mousedown', function (evt) {
|
| 4090 |
+
evt.stopPropagation();
|
| 4091 |
+
});
|
| 4092 |
+
};
|
| 4093 |
+
|
| 4094 |
+
AttachBody.prototype.destroy = function (decorated) {
|
| 4095 |
+
decorated.call(this);
|
| 4096 |
+
|
| 4097 |
+
this.$dropdownContainer.remove();
|
| 4098 |
+
};
|
| 4099 |
+
|
| 4100 |
+
AttachBody.prototype.position = function (decorated, $dropdown, $container) {
|
| 4101 |
+
// Clone all of the container classes
|
| 4102 |
+
$dropdown.attr('class', $container.attr('class'));
|
| 4103 |
+
|
| 4104 |
+
$dropdown.removeClass('select2');
|
| 4105 |
+
$dropdown.addClass('select2-container--open');
|
| 4106 |
+
|
| 4107 |
+
$dropdown.css({
|
| 4108 |
+
position: 'absolute',
|
| 4109 |
+
top: -999999
|
| 4110 |
+
});
|
| 4111 |
+
|
| 4112 |
+
this.$container = $container;
|
| 4113 |
+
};
|
| 4114 |
+
|
| 4115 |
+
AttachBody.prototype.render = function (decorated) {
|
| 4116 |
+
var $container = $('<span></span>');
|
| 4117 |
+
|
| 4118 |
+
var $dropdown = decorated.call(this);
|
| 4119 |
+
$container.append($dropdown);
|
| 4120 |
+
|
| 4121 |
+
this.$dropdownContainer = $container;
|
| 4122 |
+
|
| 4123 |
+
return $container;
|
| 4124 |
+
};
|
| 4125 |
+
|
| 4126 |
+
AttachBody.prototype._hideDropdown = function (decorated) {
|
| 4127 |
+
this.$dropdownContainer.detach();
|
| 4128 |
+
};
|
| 4129 |
+
|
| 4130 |
+
AttachBody.prototype._attachPositioningHandler =
|
| 4131 |
+
function (decorated, container) {
|
| 4132 |
+
var self = this;
|
| 4133 |
+
|
| 4134 |
+
var scrollEvent = 'scroll.select2.' + container.id;
|
| 4135 |
+
var resizeEvent = 'resize.select2.' + container.id;
|
| 4136 |
+
var orientationEvent = 'orientationchange.select2.' + container.id;
|
| 4137 |
+
|
| 4138 |
+
var $watchers = this.$container.parents().filter(Utils.hasScroll);
|
| 4139 |
+
$watchers.each(function () {
|
| 4140 |
+
$(this).data('select2-scroll-position', {
|
| 4141 |
+
x: $(this).scrollLeft(),
|
| 4142 |
+
y: $(this).scrollTop()
|
| 4143 |
+
});
|
| 4144 |
+
});
|
| 4145 |
+
|
| 4146 |
+
$watchers.on(scrollEvent, function (ev) {
|
| 4147 |
+
var position = $(this).data('select2-scroll-position');
|
| 4148 |
+
$(this).scrollTop(position.y);
|
| 4149 |
+
});
|
| 4150 |
+
|
| 4151 |
+
$(window).on(scrollEvent + ' ' + resizeEvent + ' ' + orientationEvent,
|
| 4152 |
+
function (e) {
|
| 4153 |
+
self._positionDropdown();
|
| 4154 |
+
self._resizeDropdown();
|
| 4155 |
+
});
|
| 4156 |
+
};
|
| 4157 |
+
|
| 4158 |
+
AttachBody.prototype._detachPositioningHandler =
|
| 4159 |
+
function (decorated, container) {
|
| 4160 |
+
var scrollEvent = 'scroll.select2.' + container.id;
|
| 4161 |
+
var resizeEvent = 'resize.select2.' + container.id;
|
| 4162 |
+
var orientationEvent = 'orientationchange.select2.' + container.id;
|
| 4163 |
+
|
| 4164 |
+
var $watchers = this.$container.parents().filter(Utils.hasScroll);
|
| 4165 |
+
$watchers.off(scrollEvent);
|
| 4166 |
+
|
| 4167 |
+
$(window).off(scrollEvent + ' ' + resizeEvent + ' ' + orientationEvent);
|
| 4168 |
+
};
|
| 4169 |
+
|
| 4170 |
+
AttachBody.prototype._positionDropdown = function () {
|
| 4171 |
+
var $window = $(window);
|
| 4172 |
+
|
| 4173 |
+
var isCurrentlyAbove = this.$dropdown.hasClass('select2-dropdown--above');
|
| 4174 |
+
var isCurrentlyBelow = this.$dropdown.hasClass('select2-dropdown--below');
|
| 4175 |
+
|
| 4176 |
+
var newDirection = null;
|
| 4177 |
+
|
| 4178 |
+
var offset = this.$container.offset();
|
| 4179 |
+
|
| 4180 |
+
offset.bottom = offset.top + this.$container.outerHeight(false);
|
| 4181 |
+
|
| 4182 |
+
var container = {
|
| 4183 |
+
height: this.$container.outerHeight(false)
|
| 4184 |
+
};
|
| 4185 |
+
|
| 4186 |
+
container.top = offset.top;
|
| 4187 |
+
container.bottom = offset.top + container.height;
|
| 4188 |
+
|
| 4189 |
+
var dropdown = {
|
| 4190 |
+
height: this.$dropdown.outerHeight(false)
|
| 4191 |
+
};
|
| 4192 |
+
|
| 4193 |
+
var viewport = {
|
| 4194 |
+
top: $window.scrollTop(),
|
| 4195 |
+
bottom: $window.scrollTop() + $window.height()
|
| 4196 |
+
};
|
| 4197 |
+
|
| 4198 |
+
var enoughRoomAbove = viewport.top < (offset.top - dropdown.height);
|
| 4199 |
+
var enoughRoomBelow = viewport.bottom > (offset.bottom + dropdown.height);
|
| 4200 |
+
|
| 4201 |
+
var css = {
|
| 4202 |
+
left: offset.left,
|
| 4203 |
+
top: container.bottom
|
| 4204 |
+
};
|
| 4205 |
+
|
| 4206 |
+
// Determine what the parent element is to use for calciulating the offset
|
| 4207 |
+
var $offsetParent = this.$dropdownParent;
|
| 4208 |
+
|
| 4209 |
+
// For statically positoned elements, we need to get the element
|
| 4210 |
+
// that is determining the offset
|
| 4211 |
+
if ($offsetParent.css('position') === 'static') {
|
| 4212 |
+
$offsetParent = $offsetParent.offsetParent();
|
| 4213 |
+
}
|
| 4214 |
+
|
| 4215 |
+
var parentOffset = $offsetParent.offset();
|
| 4216 |
+
|
| 4217 |
+
css.top -= parentOffset.top;
|
| 4218 |
+
css.left -= parentOffset.left;
|
| 4219 |
+
|
| 4220 |
+
if (!isCurrentlyAbove && !isCurrentlyBelow) {
|
| 4221 |
+
newDirection = 'below';
|
| 4222 |
+
}
|
| 4223 |
+
|
| 4224 |
+
if (!enoughRoomBelow && enoughRoomAbove && !isCurrentlyAbove) {
|
| 4225 |
+
newDirection = 'above';
|
| 4226 |
+
} else if (!enoughRoomAbove && enoughRoomBelow && isCurrentlyAbove) {
|
| 4227 |
+
newDirection = 'below';
|
| 4228 |
+
}
|
| 4229 |
+
|
| 4230 |
+
if (newDirection == 'above' ||
|
| 4231 |
+
(isCurrentlyAbove && newDirection !== 'below')) {
|
| 4232 |
+
css.top = container.top - dropdown.height;
|
| 4233 |
+
}
|
| 4234 |
+
|
| 4235 |
+
if (newDirection != null) {
|
| 4236 |
+
this.$dropdown
|
| 4237 |
+
.removeClass('select2-dropdown--below select2-dropdown--above')
|
| 4238 |
+
.addClass('select2-dropdown--' + newDirection);
|
| 4239 |
+
this.$container
|
| 4240 |
+
.removeClass('select2-container--below select2-container--above')
|
| 4241 |
+
.addClass('select2-container--' + newDirection);
|
| 4242 |
+
}
|
| 4243 |
+
|
| 4244 |
+
this.$dropdownContainer.css(css);
|
| 4245 |
+
};
|
| 4246 |
+
|
| 4247 |
+
AttachBody.prototype._resizeDropdown = function () {
|
| 4248 |
+
var css = {
|
| 4249 |
+
width: this.$container.outerWidth(false) + 'px'
|
| 4250 |
+
};
|
| 4251 |
+
|
| 4252 |
+
if (this.options.get('dropdownAutoWidth')) {
|
| 4253 |
+
css.minWidth = css.width;
|
| 4254 |
+
css.width = 'auto';
|
| 4255 |
+
}
|
| 4256 |
+
|
| 4257 |
+
this.$dropdown.css(css);
|
| 4258 |
+
};
|
| 4259 |
+
|
| 4260 |
+
AttachBody.prototype._showDropdown = function (decorated) {
|
| 4261 |
+
this.$dropdownContainer.appendTo(this.$dropdownParent);
|
| 4262 |
+
|
| 4263 |
+
this._positionDropdown();
|
| 4264 |
+
this._resizeDropdown();
|
| 4265 |
+
};
|
| 4266 |
+
|
| 4267 |
+
return AttachBody;
|
| 4268 |
+
});
|
| 4269 |
+
|
| 4270 |
+
S2.define('select2/dropdown/minimumResultsForSearch',[
|
| 4271 |
+
|
| 4272 |
+
], function () {
|
| 4273 |
+
function countResults (data) {
|
| 4274 |
+
var count = 0;
|
| 4275 |
+
|
| 4276 |
+
for (var d = 0; d < data.length; d++) {
|
| 4277 |
+
var item = data[d];
|
| 4278 |
+
|
| 4279 |
+
if (item.children) {
|
| 4280 |
+
count += countResults(item.children);
|
| 4281 |
+
} else {
|
| 4282 |
+
count++;
|
| 4283 |
+
}
|
| 4284 |
+
}
|
| 4285 |
+
|
| 4286 |
+
return count;
|
| 4287 |
+
}
|
| 4288 |
+
|
| 4289 |
+
function MinimumResultsForSearch (decorated, $element, options, dataAdapter) {
|
| 4290 |
+
this.minimumResultsForSearch = options.get('minimumResultsForSearch');
|
| 4291 |
+
|
| 4292 |
+
if (this.minimumResultsForSearch < 0) {
|
| 4293 |
+
this.minimumResultsForSearch = Infinity;
|
| 4294 |
+
}
|
| 4295 |
+
|
| 4296 |
+
decorated.call(this, $element, options, dataAdapter);
|
| 4297 |
+
}
|
| 4298 |
+
|
| 4299 |
+
MinimumResultsForSearch.prototype.showSearch = function (decorated, params) {
|
| 4300 |
+
if (countResults(params.data.results) < this.minimumResultsForSearch) {
|
| 4301 |
+
return false;
|
| 4302 |
+
}
|
| 4303 |
+
|
| 4304 |
+
return decorated.call(this, params);
|
| 4305 |
+
};
|
| 4306 |
+
|
| 4307 |
+
return MinimumResultsForSearch;
|
| 4308 |
+
});
|
| 4309 |
+
|
| 4310 |
+
S2.define('select2/dropdown/selectOnClose',[
|
| 4311 |
+
|
| 4312 |
+
], function () {
|
| 4313 |
+
function SelectOnClose () { }
|
| 4314 |
+
|
| 4315 |
+
SelectOnClose.prototype.bind = function (decorated, container, $container) {
|
| 4316 |
+
var self = this;
|
| 4317 |
+
|
| 4318 |
+
decorated.call(this, container, $container);
|
| 4319 |
+
|
| 4320 |
+
container.on('close', function () {
|
| 4321 |
+
self._handleSelectOnClose();
|
| 4322 |
+
});
|
| 4323 |
+
};
|
| 4324 |
+
|
| 4325 |
+
SelectOnClose.prototype._handleSelectOnClose = function () {
|
| 4326 |
+
var $highlightedResults = this.getHighlightedResults();
|
| 4327 |
+
|
| 4328 |
+
// Only select highlighted results
|
| 4329 |
+
if ($highlightedResults.length < 1) {
|
| 4330 |
+
return;
|
| 4331 |
+
}
|
| 4332 |
+
|
| 4333 |
+
var data = $highlightedResults.data('data');
|
| 4334 |
+
|
| 4335 |
+
// Don't re-select already selected resulte
|
| 4336 |
+
if (
|
| 4337 |
+
(data.element != null && data.element.selected) ||
|
| 4338 |
+
(data.element == null && data.selected)
|
| 4339 |
+
) {
|
| 4340 |
+
return;
|
| 4341 |
+
}
|
| 4342 |
+
|
| 4343 |
+
this.trigger('select', {
|
| 4344 |
+
data: data
|
| 4345 |
+
});
|
| 4346 |
+
};
|
| 4347 |
+
|
| 4348 |
+
return SelectOnClose;
|
| 4349 |
+
});
|
| 4350 |
+
|
| 4351 |
+
S2.define('select2/dropdown/closeOnSelect',[
|
| 4352 |
+
|
| 4353 |
+
], function () {
|
| 4354 |
+
function CloseOnSelect () { }
|
| 4355 |
+
|
| 4356 |
+
CloseOnSelect.prototype.bind = function (decorated, container, $container) {
|
| 4357 |
+
var self = this;
|
| 4358 |
+
|
| 4359 |
+
decorated.call(this, container, $container);
|
| 4360 |
+
|
| 4361 |
+
container.on('select', function (evt) {
|
| 4362 |
+
self._selectTriggered(evt);
|
| 4363 |
+
});
|
| 4364 |
+
|
| 4365 |
+
container.on('unselect', function (evt) {
|
| 4366 |
+
self._selectTriggered(evt);
|
| 4367 |
+
});
|
| 4368 |
+
};
|
| 4369 |
+
|
| 4370 |
+
CloseOnSelect.prototype._selectTriggered = function (_, evt) {
|
| 4371 |
+
var originalEvent = evt.originalEvent;
|
| 4372 |
+
|
| 4373 |
+
// Don't close if the control key is being held
|
| 4374 |
+
if (originalEvent && originalEvent.ctrlKey) {
|
| 4375 |
+
return;
|
| 4376 |
+
}
|
| 4377 |
+
|
| 4378 |
+
this.trigger('close', {});
|
| 4379 |
+
};
|
| 4380 |
+
|
| 4381 |
+
return CloseOnSelect;
|
| 4382 |
+
});
|
| 4383 |
+
|
| 4384 |
+
S2.define('select2/i18n/en',[],function () {
|
| 4385 |
+
// English
|
| 4386 |
+
return {
|
| 4387 |
+
errorLoading: function () {
|
| 4388 |
+
return 'The results could not be loaded.';
|
| 4389 |
+
},
|
| 4390 |
+
inputTooLong: function (args) {
|
| 4391 |
+
var overChars = args.input.length - args.maximum;
|
| 4392 |
+
|
| 4393 |
+
var message = 'Please delete ' + overChars + ' character';
|
| 4394 |
+
|
| 4395 |
+
if (overChars != 1) {
|
| 4396 |
+
message += 's';
|
| 4397 |
+
}
|
| 4398 |
+
|
| 4399 |
+
return message;
|
| 4400 |
+
},
|
| 4401 |
+
inputTooShort: function (args) {
|
| 4402 |
+
var remainingChars = args.minimum - args.input.length;
|
| 4403 |
+
|
| 4404 |
+
var message = 'Please enter ' + remainingChars + ' or more characters';
|
| 4405 |
+
|
| 4406 |
+
return message;
|
| 4407 |
+
},
|
| 4408 |
+
loadingMore: function () {
|
| 4409 |
+
return 'Loading more results…';
|
| 4410 |
+
},
|
| 4411 |
+
maximumSelected: function (args) {
|
| 4412 |
+
var message = 'You can only select ' + args.maximum + ' item';
|
| 4413 |
+
|
| 4414 |
+
if (args.maximum != 1) {
|
| 4415 |
+
message += 's';
|
| 4416 |
+
}
|
| 4417 |
+
|
| 4418 |
+
return message;
|
| 4419 |
+
},
|
| 4420 |
+
noResults: function () {
|
| 4421 |
+
return 'No results found';
|
| 4422 |
+
},
|
| 4423 |
+
searching: function () {
|
| 4424 |
+
return 'Searching…';
|
| 4425 |
+
}
|
| 4426 |
+
};
|
| 4427 |
+
});
|
| 4428 |
+
|
| 4429 |
+
S2.define('select2/defaults',[
|
| 4430 |
+
'jquery',
|
| 4431 |
+
'require',
|
| 4432 |
+
|
| 4433 |
+
'./results',
|
| 4434 |
+
|
| 4435 |
+
'./selection/single',
|
| 4436 |
+
'./selection/multiple',
|
| 4437 |
+
'./selection/placeholder',
|
| 4438 |
+
'./selection/allowClear',
|
| 4439 |
+
'./selection/search',
|
| 4440 |
+
'./selection/eventRelay',
|
| 4441 |
+
|
| 4442 |
+
'./utils',
|
| 4443 |
+
'./translation',
|
| 4444 |
+
'./diacritics',
|
| 4445 |
+
|
| 4446 |
+
'./data/select',
|
| 4447 |
+
'./data/array',
|
| 4448 |
+
'./data/ajax',
|
| 4449 |
+
'./data/tags',
|
| 4450 |
+
'./data/tokenizer',
|
| 4451 |
+
'./data/minimumInputLength',
|
| 4452 |
+
'./data/maximumInputLength',
|
| 4453 |
+
'./data/maximumSelectionLength',
|
| 4454 |
+
|
| 4455 |
+
'./dropdown',
|
| 4456 |
+
'./dropdown/search',
|
| 4457 |
+
'./dropdown/hidePlaceholder',
|
| 4458 |
+
'./dropdown/infiniteScroll',
|
| 4459 |
+
'./dropdown/attachBody',
|
| 4460 |
+
'./dropdown/minimumResultsForSearch',
|
| 4461 |
+
'./dropdown/selectOnClose',
|
| 4462 |
+
'./dropdown/closeOnSelect',
|
| 4463 |
+
|
| 4464 |
+
'./i18n/en'
|
| 4465 |
+
], function ($, require,
|
| 4466 |
+
|
| 4467 |
+
ResultsList,
|
| 4468 |
+
|
| 4469 |
+
SingleSelection, MultipleSelection, Placeholder, AllowClear,
|
| 4470 |
+
SelectionSearch, EventRelay,
|
| 4471 |
+
|
| 4472 |
+
Utils, Translation, DIACRITICS,
|
| 4473 |
+
|
| 4474 |
+
SelectData, ArrayData, AjaxData, Tags, Tokenizer,
|
| 4475 |
+
MinimumInputLength, MaximumInputLength, MaximumSelectionLength,
|
| 4476 |
+
|
| 4477 |
+
Dropdown, DropdownSearch, HidePlaceholder, InfiniteScroll,
|
| 4478 |
+
AttachBody, MinimumResultsForSearch, SelectOnClose, CloseOnSelect,
|
| 4479 |
+
|
| 4480 |
+
EnglishTranslation) {
|
| 4481 |
+
function Defaults () {
|
| 4482 |
+
this.reset();
|
| 4483 |
+
}
|
| 4484 |
+
|
| 4485 |
+
Defaults.prototype.apply = function (options) {
|
| 4486 |
+
options = $.extend(true, {}, this.defaults, options);
|
| 4487 |
+
|
| 4488 |
+
if (options.dataAdapter == null) {
|
| 4489 |
+
if (options.ajax != null) {
|
| 4490 |
+
options.dataAdapter = AjaxData;
|
| 4491 |
+
} else if (options.data != null) {
|
| 4492 |
+
options.dataAdapter = ArrayData;
|
| 4493 |
+
} else {
|
| 4494 |
+
options.dataAdapter = SelectData;
|
| 4495 |
+
}
|
| 4496 |
+
|
| 4497 |
+
if (options.minimumInputLength > 0) {
|
| 4498 |
+
options.dataAdapter = Utils.Decorate(
|
| 4499 |
+
options.dataAdapter,
|
| 4500 |
+
MinimumInputLength
|
| 4501 |
+
);
|
| 4502 |
+
}
|
| 4503 |
+
|
| 4504 |
+
if (options.maximumInputLength > 0) {
|
| 4505 |
+
options.dataAdapter = Utils.Decorate(
|
| 4506 |
+
options.dataAdapter,
|
| 4507 |
+
MaximumInputLength
|
| 4508 |
+
);
|
| 4509 |
+
}
|
| 4510 |
+
|
| 4511 |
+
if (options.maximumSelectionLength > 0) {
|
| 4512 |
+
options.dataAdapter = Utils.Decorate(
|
| 4513 |
+
options.dataAdapter,
|
| 4514 |
+
MaximumSelectionLength
|
| 4515 |
+
);
|
| 4516 |
+
}
|
| 4517 |
+
|
| 4518 |
+
if (options.tags) {
|
| 4519 |
+
options.dataAdapter = Utils.Decorate(options.dataAdapter, Tags);
|
| 4520 |
+
}
|
| 4521 |
+
|
| 4522 |
+
if (options.tokenSeparators != null || options.tokenizer != null) {
|
| 4523 |
+
options.dataAdapter = Utils.Decorate(
|
| 4524 |
+
options.dataAdapter,
|
| 4525 |
+
Tokenizer
|
| 4526 |
+
);
|
| 4527 |
+
}
|
| 4528 |
+
|
| 4529 |
+
if (options.query != null) {
|
| 4530 |
+
var Query = require(options.amdBase + 'compat/query');
|
| 4531 |
+
|
| 4532 |
+
options.dataAdapter = Utils.Decorate(
|
| 4533 |
+
options.dataAdapter,
|
| 4534 |
+
Query
|
| 4535 |
+
);
|
| 4536 |
+
}
|
| 4537 |
+
|
| 4538 |
+
if (options.initSelection != null) {
|
| 4539 |
+
var InitSelection = require(options.amdBase + 'compat/initSelection');
|
| 4540 |
+
|
| 4541 |
+
options.dataAdapter = Utils.Decorate(
|
| 4542 |
+
options.dataAdapter,
|
| 4543 |
+
InitSelection
|
| 4544 |
+
);
|
| 4545 |
+
}
|
| 4546 |
+
}
|
| 4547 |
+
|
| 4548 |
+
if (options.resultsAdapter == null) {
|
| 4549 |
+
options.resultsAdapter = ResultsList;
|
| 4550 |
+
|
| 4551 |
+
if (options.ajax != null) {
|
| 4552 |
+
options.resultsAdapter = Utils.Decorate(
|
| 4553 |
+
options.resultsAdapter,
|
| 4554 |
+
InfiniteScroll
|
| 4555 |
+
);
|
| 4556 |
+
}
|
| 4557 |
+
|
| 4558 |
+
if (options.placeholder != null) {
|
| 4559 |
+
options.resultsAdapter = Utils.Decorate(
|
| 4560 |
+
options.resultsAdapter,
|
| 4561 |
+
HidePlaceholder
|
| 4562 |
+
);
|
| 4563 |
+
}
|
| 4564 |
+
|
| 4565 |
+
if (options.selectOnClose) {
|
| 4566 |
+
options.resultsAdapter = Utils.Decorate(
|
| 4567 |
+
options.resultsAdapter,
|
| 4568 |
+
SelectOnClose
|
| 4569 |
+
);
|
| 4570 |
+
}
|
| 4571 |
+
}
|
| 4572 |
+
|
| 4573 |
+
if (options.dropdownAdapter == null) {
|
| 4574 |
+
if (options.multiple) {
|
| 4575 |
+
options.dropdownAdapter = Dropdown;
|
| 4576 |
+
} else {
|
| 4577 |
+
var SearchableDropdown = Utils.Decorate(Dropdown, DropdownSearch);
|
| 4578 |
+
|
| 4579 |
+
options.dropdownAdapter = SearchableDropdown;
|
| 4580 |
+
}
|
| 4581 |
+
|
| 4582 |
+
if (options.minimumResultsForSearch !== 0) {
|
| 4583 |
+
options.dropdownAdapter = Utils.Decorate(
|
| 4584 |
+
options.dropdownAdapter,
|
| 4585 |
+
MinimumResultsForSearch
|
| 4586 |
+
);
|
| 4587 |
+
}
|
| 4588 |
+
|
| 4589 |
+
if (options.closeOnSelect) {
|
| 4590 |
+
options.dropdownAdapter = Utils.Decorate(
|
| 4591 |
+
options.dropdownAdapter,
|
| 4592 |
+
CloseOnSelect
|
| 4593 |
+
);
|
| 4594 |
+
}
|
| 4595 |
+
|
| 4596 |
+
if (
|
| 4597 |
+
options.dropdownCssClass != null ||
|
| 4598 |
+
options.dropdownCss != null ||
|
| 4599 |
+
options.adaptDropdownCssClass != null
|
| 4600 |
+
) {
|
| 4601 |
+
var DropdownCSS = require(options.amdBase + 'compat/dropdownCss');
|
| 4602 |
+
|
| 4603 |
+
options.dropdownAdapter = Utils.Decorate(
|
| 4604 |
+
options.dropdownAdapter,
|
| 4605 |
+
DropdownCSS
|
| 4606 |
+
);
|
| 4607 |
+
}
|
| 4608 |
+
|
| 4609 |
+
options.dropdownAdapter = Utils.Decorate(
|
| 4610 |
+
options.dropdownAdapter,
|
| 4611 |
+
AttachBody
|
| 4612 |
+
);
|
| 4613 |
+
}
|
| 4614 |
+
|
| 4615 |
+
if (options.selectionAdapter == null) {
|
| 4616 |
+
if (options.multiple) {
|
| 4617 |
+
options.selectionAdapter = MultipleSelection;
|
| 4618 |
+
} else {
|
| 4619 |
+
options.selectionAdapter = SingleSelection;
|
| 4620 |
+
}
|
| 4621 |
+
|
| 4622 |
+
// Add the placeholder mixin if a placeholder was specified
|
| 4623 |
+
if (options.placeholder != null) {
|
| 4624 |
+
options.selectionAdapter = Utils.Decorate(
|
| 4625 |
+
options.selectionAdapter,
|
| 4626 |
+
Placeholder
|
| 4627 |
+
);
|
| 4628 |
+
}
|
| 4629 |
+
|
| 4630 |
+
if (options.allowClear) {
|
| 4631 |
+
options.selectionAdapter = Utils.Decorate(
|
| 4632 |
+
options.selectionAdapter,
|
| 4633 |
+
AllowClear
|
| 4634 |
+
);
|
| 4635 |
+
}
|
| 4636 |
+
|
| 4637 |
+
if (options.multiple) {
|
| 4638 |
+
options.selectionAdapter = Utils.Decorate(
|
| 4639 |
+
options.selectionAdapter,
|
| 4640 |
+
SelectionSearch
|
| 4641 |
+
);
|
| 4642 |
+
}
|
| 4643 |
+
|
| 4644 |
+
if (
|
| 4645 |
+
options.containerCssClass != null ||
|
| 4646 |
+
options.containerCss != null ||
|
| 4647 |
+
options.adaptContainerCssClass != null
|
| 4648 |
+
) {
|
| 4649 |
+
var ContainerCSS = require(options.amdBase + 'compat/containerCss');
|
| 4650 |
+
|
| 4651 |
+
options.selectionAdapter = Utils.Decorate(
|
| 4652 |
+
options.selectionAdapter,
|
| 4653 |
+
ContainerCSS
|
| 4654 |
+
);
|
| 4655 |
+
}
|
| 4656 |
+
|
| 4657 |
+
options.selectionAdapter = Utils.Decorate(
|
| 4658 |
+
options.selectionAdapter,
|
| 4659 |
+
EventRelay
|
| 4660 |
+
);
|
| 4661 |
+
}
|
| 4662 |
+
|
| 4663 |
+
if (typeof options.language === 'string') {
|
| 4664 |
+
// Check if the language is specified with a region
|
| 4665 |
+
if (options.language.indexOf('-') > 0) {
|
| 4666 |
+
// Extract the region information if it is included
|
| 4667 |
+
var languageParts = options.language.split('-');
|
| 4668 |
+
var baseLanguage = languageParts[0];
|
| 4669 |
+
|
| 4670 |
+
options.language = [options.language, baseLanguage];
|
| 4671 |
+
} else {
|
| 4672 |
+
options.language = [options.language];
|
| 4673 |
+
}
|
| 4674 |
+
}
|
| 4675 |
+
|
| 4676 |
+
if ($.isArray(options.language)) {
|
| 4677 |
+
var languages = new Translation();
|
| 4678 |
+
options.language.push('en');
|
| 4679 |
+
|
| 4680 |
+
var languageNames = options.language;
|
| 4681 |
+
|
| 4682 |
+
for (var l = 0; l < languageNames.length; l++) {
|
| 4683 |
+
var name = languageNames[l];
|
| 4684 |
+
var language = {};
|
| 4685 |
+
|
| 4686 |
+
try {
|
| 4687 |
+
// Try to load it with the original name
|
| 4688 |
+
language = Translation.loadPath(name);
|
| 4689 |
+
} catch (e) {
|
| 4690 |
+
try {
|
| 4691 |
+
// If we couldn't load it, check if it wasn't the full path
|
| 4692 |
+
name = this.defaults.amdLanguageBase + name;
|
| 4693 |
+
language = Translation.loadPath(name);
|
| 4694 |
+
} catch (ex) {
|
| 4695 |
+
// The translation could not be loaded at all. Sometimes this is
|
| 4696 |
+
// because of a configuration problem, other times this can be
|
| 4697 |
+
// because of how Select2 helps load all possible translation files.
|
| 4698 |
+
if (options.debug && window.console && console.warn) {
|
| 4699 |
+
console.warn(
|
| 4700 |
+
'Select2: The language file for "' + name + '" could not be ' +
|
| 4701 |
+
'automatically loaded. A fallback will be used instead.'
|
| 4702 |
+
);
|
| 4703 |
+
}
|
| 4704 |
+
|
| 4705 |
+
continue;
|
| 4706 |
+
}
|
| 4707 |
+
}
|
| 4708 |
+
|
| 4709 |
+
languages.extend(language);
|
| 4710 |
+
}
|
| 4711 |
+
|
| 4712 |
+
options.translations = languages;
|
| 4713 |
+
} else {
|
| 4714 |
+
var baseTranslation = Translation.loadPath(
|
| 4715 |
+
this.defaults.amdLanguageBase + 'en'
|
| 4716 |
+
);
|
| 4717 |
+
var customTranslation = new Translation(options.language);
|
| 4718 |
+
|
| 4719 |
+
customTranslation.extend(baseTranslation);
|
| 4720 |
+
|
| 4721 |
+
options.translations = customTranslation;
|
| 4722 |
+
}
|
| 4723 |
+
|
| 4724 |
+
return options;
|
| 4725 |
+
};
|
| 4726 |
+
|
| 4727 |
+
Defaults.prototype.reset = function () {
|
| 4728 |
+
function stripDiacritics (text) {
|
| 4729 |
+
// Used 'uni range + named function' from http://jsperf.com/diacritics/18
|
| 4730 |
+
function match(a) {
|
| 4731 |
+
return DIACRITICS[a] || a;
|
| 4732 |
+
}
|
| 4733 |
+
|
| 4734 |
+
return text.replace(/[^\u0000-\u007E]/g, match);
|
| 4735 |
+
}
|
| 4736 |
+
|
| 4737 |
+
function matcher (params, data) {
|
| 4738 |
+
// Always return the object if there is nothing to compare
|
| 4739 |
+
if ($.trim(params.term) === '') {
|
| 4740 |
+
return data;
|
| 4741 |
+
}
|
| 4742 |
+
|
| 4743 |
+
// Do a recursive check for options with children
|
| 4744 |
+
if (data.children && data.children.length > 0) {
|
| 4745 |
+
// Clone the data object if there are children
|
| 4746 |
+
// This is required as we modify the object to remove any non-matches
|
| 4747 |
+
var match = $.extend(true, {}, data);
|
| 4748 |
+
|
| 4749 |
+
// Check each child of the option
|
| 4750 |
+
for (var c = data.children.length - 1; c >= 0; c--) {
|
| 4751 |
+
var child = data.children[c];
|
| 4752 |
+
|
| 4753 |
+
var matches = matcher(params, child);
|
| 4754 |
+
|
| 4755 |
+
// If there wasn't a match, remove the object in the array
|
| 4756 |
+
if (matches == null) {
|
| 4757 |
+
match.children.splice(c, 1);
|
| 4758 |
+
}
|
| 4759 |
+
}
|
| 4760 |
+
|
| 4761 |
+
// If any children matched, return the new object
|
| 4762 |
+
if (match.children.length > 0) {
|
| 4763 |
+
return match;
|
| 4764 |
+
}
|
| 4765 |
+
|
| 4766 |
+
// If there were no matching children, check just the plain object
|
| 4767 |
+
return matcher(params, match);
|
| 4768 |
+
}
|
| 4769 |
+
|
| 4770 |
+
var original = stripDiacritics(data.text).toUpperCase();
|
| 4771 |
+
var term = stripDiacritics(params.term).toUpperCase();
|
| 4772 |
+
|
| 4773 |
+
// Check if the text contains the term
|
| 4774 |
+
if (original.indexOf(term) > -1) {
|
| 4775 |
+
return data;
|
| 4776 |
+
}
|
| 4777 |
+
|
| 4778 |
+
// If it doesn't contain the term, don't return anything
|
| 4779 |
+
return null;
|
| 4780 |
+
}
|
| 4781 |
+
|
| 4782 |
+
this.defaults = {
|
| 4783 |
+
amdBase: './',
|
| 4784 |
+
amdLanguageBase: './i18n/',
|
| 4785 |
+
closeOnSelect: true,
|
| 4786 |
+
debug: false,
|
| 4787 |
+
dropdownAutoWidth: false,
|
| 4788 |
+
escapeMarkup: Utils.escapeMarkup,
|
| 4789 |
+
language: EnglishTranslation,
|
| 4790 |
+
matcher: matcher,
|
| 4791 |
+
minimumInputLength: 0,
|
| 4792 |
+
maximumInputLength: 0,
|
| 4793 |
+
maximumSelectionLength: 0,
|
| 4794 |
+
minimumResultsForSearch: 0,
|
| 4795 |
+
selectOnClose: false,
|
| 4796 |
+
sorter: function (data) {
|
| 4797 |
+
return data;
|
| 4798 |
+
},
|
| 4799 |
+
templateResult: function (result) {
|
| 4800 |
+
return result.text;
|
| 4801 |
+
},
|
| 4802 |
+
templateSelection: function (selection) {
|
| 4803 |
+
return selection.text;
|
| 4804 |
+
},
|
| 4805 |
+
theme: 'default',
|
| 4806 |
+
width: 'resolve'
|
| 4807 |
+
};
|
| 4808 |
+
};
|
| 4809 |
+
|
| 4810 |
+
Defaults.prototype.set = function (key, value) {
|
| 4811 |
+
var camelKey = $.camelCase(key);
|
| 4812 |
+
|
| 4813 |
+
var data = {};
|
| 4814 |
+
data[camelKey] = value;
|
| 4815 |
+
|
| 4816 |
+
var convertedData = Utils._convertData(data);
|
| 4817 |
+
|
| 4818 |
+
$.extend(this.defaults, convertedData);
|
| 4819 |
+
};
|
| 4820 |
+
|
| 4821 |
+
var defaults = new Defaults();
|
| 4822 |
+
|
| 4823 |
+
return defaults;
|
| 4824 |
+
});
|
| 4825 |
+
|
| 4826 |
+
S2.define('select2/options',[
|
| 4827 |
+
'require',
|
| 4828 |
+
'jquery',
|
| 4829 |
+
'./defaults',
|
| 4830 |
+
'./utils'
|
| 4831 |
+
], function (require, $, Defaults, Utils) {
|
| 4832 |
+
function Options (options, $element) {
|
| 4833 |
+
this.options = options;
|
| 4834 |
+
|
| 4835 |
+
if ($element != null) {
|
| 4836 |
+
this.fromElement($element);
|
| 4837 |
+
}
|
| 4838 |
+
|
| 4839 |
+
this.options = Defaults.apply(this.options);
|
| 4840 |
+
|
| 4841 |
+
if ($element && $element.is('input')) {
|
| 4842 |
+
var InputCompat = require(this.get('amdBase') + 'compat/inputData');
|
| 4843 |
+
|
| 4844 |
+
this.options.dataAdapter = Utils.Decorate(
|
| 4845 |
+
this.options.dataAdapter,
|
| 4846 |
+
InputCompat
|
| 4847 |
+
);
|
| 4848 |
+
}
|
| 4849 |
+
}
|
| 4850 |
+
|
| 4851 |
+
Options.prototype.fromElement = function ($e) {
|
| 4852 |
+
var excludedData = ['select2'];
|
| 4853 |
+
|
| 4854 |
+
if (this.options.multiple == null) {
|
| 4855 |
+
this.options.multiple = $e.prop('multiple');
|
| 4856 |
+
}
|
| 4857 |
+
|
| 4858 |
+
if (this.options.disabled == null) {
|
| 4859 |
+
this.options.disabled = $e.prop('disabled');
|
| 4860 |
+
}
|
| 4861 |
+
|
| 4862 |
+
if (this.options.language == null) {
|
| 4863 |
+
if ($e.prop('lang')) {
|
| 4864 |
+
this.options.language = $e.prop('lang').toLowerCase();
|
| 4865 |
+
} else if ($e.closest('[lang]').prop('lang')) {
|
| 4866 |
+
this.options.language = $e.closest('[lang]').prop('lang');
|
| 4867 |
+
}
|
| 4868 |
+
}
|
| 4869 |
+
|
| 4870 |
+
if (this.options.dir == null) {
|
| 4871 |
+
if ($e.prop('dir')) {
|
| 4872 |
+
this.options.dir = $e.prop('dir');
|
| 4873 |
+
} else if ($e.closest('[dir]').prop('dir')) {
|
| 4874 |
+
this.options.dir = $e.closest('[dir]').prop('dir');
|
| 4875 |
+
} else {
|
| 4876 |
+
this.options.dir = 'ltr';
|
| 4877 |
+
}
|
| 4878 |
+
}
|
| 4879 |
+
|
| 4880 |
+
$e.prop('disabled', this.options.disabled);
|
| 4881 |
+
$e.prop('multiple', this.options.multiple);
|
| 4882 |
+
|
| 4883 |
+
if ($e.data('select2Tags')) {
|
| 4884 |
+
if (this.options.debug && window.console && console.warn) {
|
| 4885 |
+
console.warn(
|
| 4886 |
+
'Select2: The `data-select2-tags` attribute has been changed to ' +
|
| 4887 |
+
'use the `data-data` and `data-tags="true"` attributes and will be ' +
|
| 4888 |
+
'removed in future versions of Select2.'
|
| 4889 |
+
);
|
| 4890 |
+
}
|
| 4891 |
+
|
| 4892 |
+
$e.data('data', $e.data('select2Tags'));
|
| 4893 |
+
$e.data('tags', true);
|
| 4894 |
+
}
|
| 4895 |
+
|
| 4896 |
+
if ($e.data('ajaxUrl')) {
|
| 4897 |
+
if (this.options.debug && window.console && console.warn) {
|
| 4898 |
+
console.warn(
|
| 4899 |
+
'Select2: The `data-ajax-url` attribute has been changed to ' +
|
| 4900 |
+
'`data-ajax--url` and support for the old attribute will be removed' +
|
| 4901 |
+
' in future versions of Select2.'
|
| 4902 |
+
);
|
| 4903 |
+
}
|
| 4904 |
+
|
| 4905 |
+
$e.attr('ajax--url', $e.data('ajaxUrl'));
|
| 4906 |
+
$e.data('ajax--url', $e.data('ajaxUrl'));
|
| 4907 |
+
}
|
| 4908 |
+
|
| 4909 |
+
var dataset = {};
|
| 4910 |
+
|
| 4911 |
+
// Prefer the element's `dataset` attribute if it exists
|
| 4912 |
+
// jQuery 1.x does not correctly handle data attributes with multiple dashes
|
| 4913 |
+
if ($.fn.jquery && $.fn.jquery.substr(0, 2) == '1.' && $e[0].dataset) {
|
| 4914 |
+
dataset = $.extend(true, {}, $e[0].dataset, $e.data());
|
| 4915 |
+
} else {
|
| 4916 |
+
dataset = $e.data();
|
| 4917 |
+
}
|
| 4918 |
+
|
| 4919 |
+
var data = $.extend(true, {}, dataset);
|
| 4920 |
+
|
| 4921 |
+
data = Utils._convertData(data);
|
| 4922 |
+
|
| 4923 |
+
for (var key in data) {
|
| 4924 |
+
if ($.inArray(key, excludedData) > -1) {
|
| 4925 |
+
continue;
|
| 4926 |
+
}
|
| 4927 |
+
|
| 4928 |
+
if ($.isPlainObject(this.options[key])) {
|
| 4929 |
+
$.extend(this.options[key], data[key]);
|
| 4930 |
+
} else {
|
| 4931 |
+
this.options[key] = data[key];
|
| 4932 |
+
}
|
| 4933 |
+
}
|
| 4934 |
+
|
| 4935 |
+
return this;
|
| 4936 |
+
};
|
| 4937 |
+
|
| 4938 |
+
Options.prototype.get = function (key) {
|
| 4939 |
+
return this.options[key];
|
| 4940 |
+
};
|
| 4941 |
+
|
| 4942 |
+
Options.prototype.set = function (key, val) {
|
| 4943 |
+
this.options[key] = val;
|
| 4944 |
+
};
|
| 4945 |
+
|
| 4946 |
+
return Options;
|
| 4947 |
+
});
|
| 4948 |
+
|
| 4949 |
+
S2.define('select2/core',[
|
| 4950 |
+
'jquery',
|
| 4951 |
+
'./options',
|
| 4952 |
+
'./utils',
|
| 4953 |
+
'./keys'
|
| 4954 |
+
], function ($, Options, Utils, KEYS) {
|
| 4955 |
+
var Select2 = function ($element, options) {
|
| 4956 |
+
if ($element.data('select2') != null) {
|
| 4957 |
+
$element.data('select2').destroy();
|
| 4958 |
+
}
|
| 4959 |
+
|
| 4960 |
+
this.$element = $element;
|
| 4961 |
+
|
| 4962 |
+
this.id = this._generateId($element);
|
| 4963 |
+
|
| 4964 |
+
options = options || {};
|
| 4965 |
+
|
| 4966 |
+
this.options = new Options(options, $element);
|
| 4967 |
+
|
| 4968 |
+
Select2.__super__.constructor.call(this);
|
| 4969 |
+
|
| 4970 |
+
// Set up the tabindex
|
| 4971 |
+
|
| 4972 |
+
var tabindex = $element.attr('tabindex') || 0;
|
| 4973 |
+
$element.data('old-tabindex', tabindex);
|
| 4974 |
+
$element.attr('tabindex', '-1');
|
| 4975 |
+
|
| 4976 |
+
// Set up containers and adapters
|
| 4977 |
+
|
| 4978 |
+
var DataAdapter = this.options.get('dataAdapter');
|
| 4979 |
+
this.dataAdapter = new DataAdapter($element, this.options);
|
| 4980 |
+
|
| 4981 |
+
var $container = this.render();
|
| 4982 |
+
|
| 4983 |
+
this._placeContainer($container);
|
| 4984 |
+
|
| 4985 |
+
var SelectionAdapter = this.options.get('selectionAdapter');
|
| 4986 |
+
this.selection = new SelectionAdapter($element, this.options);
|
| 4987 |
+
this.$selection = this.selection.render();
|
| 4988 |
+
|
| 4989 |
+
this.selection.position(this.$selection, $container);
|
| 4990 |
+
|
| 4991 |
+
var DropdownAdapter = this.options.get('dropdownAdapter');
|
| 4992 |
+
this.dropdown = new DropdownAdapter($element, this.options);
|
| 4993 |
+
this.$dropdown = this.dropdown.render();
|
| 4994 |
+
|
| 4995 |
+
this.dropdown.position(this.$dropdown, $container);
|
| 4996 |
+
|
| 4997 |
+
var ResultsAdapter = this.options.get('resultsAdapter');
|
| 4998 |
+
this.results = new ResultsAdapter($element, this.options, this.dataAdapter);
|
| 4999 |
+
this.$results = this.results.render();
|
| 5000 |
+
|
| 5001 |
+
this.results.position(this.$results, this.$dropdown);
|
| 5002 |
+
|
| 5003 |
+
// Bind events
|
| 5004 |
+
|
| 5005 |
+
var self = this;
|
| 5006 |
+
|
| 5007 |
+
// Bind the container to all of the adapters
|
| 5008 |
+
this._bindAdapters();
|
| 5009 |
+
|
| 5010 |
+
// Register any DOM event handlers
|
| 5011 |
+
this._registerDomEvents();
|
| 5012 |
+
|
| 5013 |
+
// Register any internal event handlers
|
| 5014 |
+
this._registerDataEvents();
|
| 5015 |
+
this._registerSelectionEvents();
|
| 5016 |
+
this._registerDropdownEvents();
|
| 5017 |
+
this._registerResultsEvents();
|
| 5018 |
+
this._registerEvents();
|
| 5019 |
+
|
| 5020 |
+
// Set the initial state
|
| 5021 |
+
this.dataAdapter.current(function (initialData) {
|
| 5022 |
+
self.trigger('selection:update', {
|
| 5023 |
+
data: initialData
|
| 5024 |
+
});
|
| 5025 |
+
});
|
| 5026 |
+
|
| 5027 |
+
// Hide the original select
|
| 5028 |
+
$element.addClass('select2-hidden-accessible');
|
| 5029 |
+
$element.attr('aria-hidden', 'true');
|
| 5030 |
+
|
| 5031 |
+
// Synchronize any monitored attributes
|
| 5032 |
+
this._syncAttributes();
|
| 5033 |
+
|
| 5034 |
+
$element.data('select2', this);
|
| 5035 |
+
};
|
| 5036 |
+
|
| 5037 |
+
Utils.Extend(Select2, Utils.Observable);
|
| 5038 |
+
|
| 5039 |
+
Select2.prototype._generateId = function ($element) {
|
| 5040 |
+
var id = '';
|
| 5041 |
+
|
| 5042 |
+
if ($element.attr('id') != null) {
|
| 5043 |
+
id = $element.attr('id');
|
| 5044 |
+
} else if ($element.attr('name') != null) {
|
| 5045 |
+
id = $element.attr('name') + '-' + Utils.generateChars(2);
|
| 5046 |
+
} else {
|
| 5047 |
+
id = Utils.generateChars(4);
|
| 5048 |
+
}
|
| 5049 |
+
|
| 5050 |
+
id = id.replace(/(:|\.|\[|\]|,)/g, '');
|
| 5051 |
+
id = 'select2-' + id;
|
| 5052 |
+
|
| 5053 |
+
return id;
|
| 5054 |
+
};
|
| 5055 |
+
|
| 5056 |
+
Select2.prototype._placeContainer = function ($container) {
|
| 5057 |
+
$container.insertAfter(this.$element);
|
| 5058 |
+
|
| 5059 |
+
var width = this._resolveWidth(this.$element, this.options.get('width'));
|
| 5060 |
+
|
| 5061 |
+
if (width != null) {
|
| 5062 |
+
$container.css('width', width);
|
| 5063 |
+
}
|
| 5064 |
+
};
|
| 5065 |
+
|
| 5066 |
+
Select2.prototype._resolveWidth = function ($element, method) {
|
| 5067 |
+
var WIDTH = /^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i;
|
| 5068 |
+
|
| 5069 |
+
if (method == 'resolve') {
|
| 5070 |
+
var styleWidth = this._resolveWidth($element, 'style');
|
| 5071 |
+
|
| 5072 |
+
if (styleWidth != null) {
|
| 5073 |
+
return styleWidth;
|
| 5074 |
+
}
|
| 5075 |
+
|
| 5076 |
+
return this._resolveWidth($element, 'element');
|
| 5077 |
+
}
|
| 5078 |
+
|
| 5079 |
+
if (method == 'element') {
|
| 5080 |
+
var elementWidth = $element.outerWidth(false);
|
| 5081 |
+
|
| 5082 |
+
if (elementWidth <= 0) {
|
| 5083 |
+
return 'auto';
|
| 5084 |
+
}
|
| 5085 |
+
|
| 5086 |
+
return elementWidth + 'px';
|
| 5087 |
+
}
|
| 5088 |
+
|
| 5089 |
+
if (method == 'style') {
|
| 5090 |
+
var style = $element.attr('style');
|
| 5091 |
+
|
| 5092 |
+
if (typeof(style) !== 'string') {
|
| 5093 |
+
return null;
|
| 5094 |
+
}
|
| 5095 |
+
|
| 5096 |
+
var attrs = style.split(';');
|
| 5097 |
+
|
| 5098 |
+
for (var i = 0, l = attrs.length; i < l; i = i + 1) {
|
| 5099 |
+
var attr = attrs[i].replace(/\s/g, '');
|
| 5100 |
+
var matches = attr.match(WIDTH);
|
| 5101 |
+
|
| 5102 |
+
if (matches !== null && matches.length >= 1) {
|
| 5103 |
+
return matches[1];
|
| 5104 |
+
}
|
| 5105 |
+
}
|
| 5106 |
+
|
| 5107 |
+
return null;
|
| 5108 |
+
}
|
| 5109 |
+
|
| 5110 |
+
return method;
|
| 5111 |
+
};
|
| 5112 |
+
|
| 5113 |
+
Select2.prototype._bindAdapters = function () {
|
| 5114 |
+
this.dataAdapter.bind(this, this.$container);
|
| 5115 |
+
this.selection.bind(this, this.$container);
|
| 5116 |
+
|
| 5117 |
+
this.dropdown.bind(this, this.$container);
|
| 5118 |
+
this.results.bind(this, this.$container);
|
| 5119 |
+
};
|
| 5120 |
+
|
| 5121 |
+
Select2.prototype._registerDomEvents = function () {
|
| 5122 |
+
var self = this;
|
| 5123 |
+
|
| 5124 |
+
this.$element.on('change.select2', function () {
|
| 5125 |
+
self.dataAdapter.current(function (data) {
|
| 5126 |
+
self.trigger('selection:update', {
|
| 5127 |
+
data: data
|
| 5128 |
+
});
|
| 5129 |
+
});
|
| 5130 |
+
});
|
| 5131 |
+
|
| 5132 |
+
this._sync = Utils.bind(this._syncAttributes, this);
|
| 5133 |
+
|
| 5134 |
+
if (this.$element[0].attachEvent) {
|
| 5135 |
+
this.$element[0].attachEvent('onpropertychange', this._sync);
|
| 5136 |
+
}
|
| 5137 |
+
|
| 5138 |
+
var observer = window.MutationObserver ||
|
| 5139 |
+
window.WebKitMutationObserver ||
|
| 5140 |
+
window.MozMutationObserver
|
| 5141 |
+
;
|
| 5142 |
+
|
| 5143 |
+
if (observer != null) {
|
| 5144 |
+
this._observer = new observer(function (mutations) {
|
| 5145 |
+
$.each(mutations, self._sync);
|
| 5146 |
+
});
|
| 5147 |
+
this._observer.observe(this.$element[0], {
|
| 5148 |
+
attributes: true,
|
| 5149 |
+
subtree: false
|
| 5150 |
+
});
|
| 5151 |
+
} else if (this.$element[0].addEventListener) {
|
| 5152 |
+
this.$element[0].addEventListener('DOMAttrModified', self._sync, false);
|
| 5153 |
+
}
|
| 5154 |
+
};
|
| 5155 |
+
|
| 5156 |
+
Select2.prototype._registerDataEvents = function () {
|
| 5157 |
+
var self = this;
|
| 5158 |
+
|
| 5159 |
+
this.dataAdapter.on('*', function (name, params) {
|
| 5160 |
+
self.trigger(name, params);
|
| 5161 |
+
});
|
| 5162 |
+
};
|
| 5163 |
+
|
| 5164 |
+
Select2.prototype._registerSelectionEvents = function () {
|
| 5165 |
+
var self = this;
|
| 5166 |
+
var nonRelayEvents = ['toggle', 'focus'];
|
| 5167 |
+
|
| 5168 |
+
this.selection.on('toggle', function () {
|
| 5169 |
+
self.toggleDropdown();
|
| 5170 |
+
});
|
| 5171 |
+
|
| 5172 |
+
this.selection.on('focus', function (params) {
|
| 5173 |
+
self.focus(params);
|
| 5174 |
+
});
|
| 5175 |
+
|
| 5176 |
+
this.selection.on('*', function (name, params) {
|
| 5177 |
+
if ($.inArray(name, nonRelayEvents) !== -1) {
|
| 5178 |
+
return;
|
| 5179 |
+
}
|
| 5180 |
+
|
| 5181 |
+
self.trigger(name, params);
|
| 5182 |
+
});
|
| 5183 |
+
};
|
| 5184 |
+
|
| 5185 |
+
Select2.prototype._registerDropdownEvents = function () {
|
| 5186 |
+
var self = this;
|
| 5187 |
+
|
| 5188 |
+
this.dropdown.on('*', function (name, params) {
|
| 5189 |
+
self.trigger(name, params);
|
| 5190 |
+
});
|
| 5191 |
+
};
|
| 5192 |
+
|
| 5193 |
+
Select2.prototype._registerResultsEvents = function () {
|
| 5194 |
+
var self = this;
|
| 5195 |
+
|
| 5196 |
+
this.results.on('*', function (name, params) {
|
| 5197 |
+
self.trigger(name, params);
|
| 5198 |
+
});
|
| 5199 |
+
};
|
| 5200 |
+
|
| 5201 |
+
Select2.prototype._registerEvents = function () {
|
| 5202 |
+
var self = this;
|
| 5203 |
+
|
| 5204 |
+
this.on('open', function () {
|
| 5205 |
+
self.$container.addClass('select2-container--open');
|
| 5206 |
+
});
|
| 5207 |
+
|
| 5208 |
+
this.on('close', function () {
|
| 5209 |
+
self.$container.removeClass('select2-container--open');
|
| 5210 |
+
});
|
| 5211 |
+
|
| 5212 |
+
this.on('enable', function () {
|
| 5213 |
+
self.$container.removeClass('select2-container--disabled');
|
| 5214 |
+
});
|
| 5215 |
+
|
| 5216 |
+
this.on('disable', function () {
|
| 5217 |
+
self.$container.addClass('select2-container--disabled');
|
| 5218 |
+
});
|
| 5219 |
+
|
| 5220 |
+
this.on('blur', function () {
|
| 5221 |
+
self.$container.removeClass('select2-container--focus');
|
| 5222 |
+
});
|
| 5223 |
+
|
| 5224 |
+
this.on('query', function (params) {
|
| 5225 |
+
if (!self.isOpen()) {
|
| 5226 |
+
self.trigger('open', {});
|
| 5227 |
+
}
|
| 5228 |
+
|
| 5229 |
+
this.dataAdapter.query(params, function (data) {
|
| 5230 |
+
self.trigger('results:all', {
|
| 5231 |
+
data: data,
|
| 5232 |
+
query: params
|
| 5233 |
+
});
|
| 5234 |
+
});
|
| 5235 |
+
});
|
| 5236 |
+
|
| 5237 |
+
this.on('query:append', function (params) {
|
| 5238 |
+
this.dataAdapter.query(params, function (data) {
|
| 5239 |
+
self.trigger('results:append', {
|
| 5240 |
+
data: data,
|
| 5241 |
+
query: params
|
| 5242 |
+
});
|
| 5243 |
+
});
|
| 5244 |
+
});
|
| 5245 |
+
|
| 5246 |
+
this.on('keypress', function (evt) {
|
| 5247 |
+
var key = evt.which;
|
| 5248 |
+
|
| 5249 |
+
if (self.isOpen()) {
|
| 5250 |
+
if (key === KEYS.ESC || key === KEYS.TAB ||
|
| 5251 |
+
(key === KEYS.UP && evt.altKey)) {
|
| 5252 |
+
self.close();
|
| 5253 |
+
|
| 5254 |
+
evt.preventDefault();
|
| 5255 |
+
} else if (key === KEYS.ENTER) {
|
| 5256 |
+
self.trigger('results:select', {});
|
| 5257 |
+
|
| 5258 |
+
evt.preventDefault();
|
| 5259 |
+
} else if ((key === KEYS.SPACE && evt.ctrlKey)) {
|
| 5260 |
+
self.trigger('results:toggle', {});
|
| 5261 |
+
|
| 5262 |
+
evt.preventDefault();
|
| 5263 |
+
} else if (key === KEYS.UP) {
|
| 5264 |
+
self.trigger('results:previous', {});
|
| 5265 |
+
|
| 5266 |
+
evt.preventDefault();
|
| 5267 |
+
} else if (key === KEYS.DOWN) {
|
| 5268 |
+
self.trigger('results:next', {});
|
| 5269 |
+
|
| 5270 |
+
evt.preventDefault();
|
| 5271 |
+
}
|
| 5272 |
+
} else {
|
| 5273 |
+
if (key === KEYS.ENTER || key === KEYS.SPACE ||
|
| 5274 |
+
(key === KEYS.DOWN && evt.altKey)) {
|
| 5275 |
+
self.open();
|
| 5276 |
+
|
| 5277 |
+
evt.preventDefault();
|
| 5278 |
+
}
|
| 5279 |
+
}
|
| 5280 |
+
});
|
| 5281 |
+
};
|
| 5282 |
+
|
| 5283 |
+
Select2.prototype._syncAttributes = function () {
|
| 5284 |
+
this.options.set('disabled', this.$element.prop('disabled'));
|
| 5285 |
+
|
| 5286 |
+
if (this.options.get('disabled')) {
|
| 5287 |
+
if (this.isOpen()) {
|
| 5288 |
+
this.close();
|
| 5289 |
+
}
|
| 5290 |
+
|
| 5291 |
+
this.trigger('disable', {});
|
| 5292 |
+
} else {
|
| 5293 |
+
this.trigger('enable', {});
|
| 5294 |
+
}
|
| 5295 |
+
};
|
| 5296 |
+
|
| 5297 |
+
/**
|
| 5298 |
+
* Override the trigger method to automatically trigger pre-events when
|
| 5299 |
+
* there are events that can be prevented.
|
| 5300 |
+
*/
|
| 5301 |
+
Select2.prototype.trigger = function (name, args) {
|
| 5302 |
+
var actualTrigger = Select2.__super__.trigger;
|
| 5303 |
+
var preTriggerMap = {
|
| 5304 |
+
'open': 'opening',
|
| 5305 |
+
'close': 'closing',
|
| 5306 |
+
'select': 'selecting',
|
| 5307 |
+
'unselect': 'unselecting'
|
| 5308 |
+
};
|
| 5309 |
+
|
| 5310 |
+
if (args === undefined) {
|
| 5311 |
+
args = {};
|
| 5312 |
+
}
|
| 5313 |
+
|
| 5314 |
+
if (name in preTriggerMap) {
|
| 5315 |
+
var preTriggerName = preTriggerMap[name];
|
| 5316 |
+
var preTriggerArgs = {
|
| 5317 |
+
prevented: false,
|
| 5318 |
+
name: name,
|
| 5319 |
+
args: args
|
| 5320 |
+
};
|
| 5321 |
+
|
| 5322 |
+
actualTrigger.call(this, preTriggerName, preTriggerArgs);
|
| 5323 |
+
|
| 5324 |
+
if (preTriggerArgs.prevented) {
|
| 5325 |
+
args.prevented = true;
|
| 5326 |
+
|
| 5327 |
+
return;
|
| 5328 |
+
}
|
| 5329 |
+
}
|
| 5330 |
+
|
| 5331 |
+
actualTrigger.call(this, name, args);
|
| 5332 |
+
};
|
| 5333 |
+
|
| 5334 |
+
Select2.prototype.toggleDropdown = function () {
|
| 5335 |
+
if (this.options.get('disabled')) {
|
| 5336 |
+
return;
|
| 5337 |
+
}
|
| 5338 |
+
|
| 5339 |
+
if (this.isOpen()) {
|
| 5340 |
+
this.close();
|
| 5341 |
+
} else {
|
| 5342 |
+
this.open();
|
| 5343 |
+
}
|
| 5344 |
+
};
|
| 5345 |
+
|
| 5346 |
+
Select2.prototype.open = function () {
|
| 5347 |
+
if (this.isOpen()) {
|
| 5348 |
+
return;
|
| 5349 |
+
}
|
| 5350 |
+
|
| 5351 |
+
this.trigger('query', {});
|
| 5352 |
+
};
|
| 5353 |
+
|
| 5354 |
+
Select2.prototype.close = function () {
|
| 5355 |
+
if (!this.isOpen()) {
|
| 5356 |
+
return;
|
| 5357 |
+
}
|
| 5358 |
+
|
| 5359 |
+
this.trigger('close', {});
|
| 5360 |
+
};
|
| 5361 |
+
|
| 5362 |
+
Select2.prototype.isOpen = function () {
|
| 5363 |
+
return this.$container.hasClass('select2-container--open');
|
| 5364 |
+
};
|
| 5365 |
+
|
| 5366 |
+
Select2.prototype.hasFocus = function () {
|
| 5367 |
+
return this.$container.hasClass('select2-container--focus');
|
| 5368 |
+
};
|
| 5369 |
+
|
| 5370 |
+
Select2.prototype.focus = function (data) {
|
| 5371 |
+
// No need to re-trigger focus events if we are already focused
|
| 5372 |
+
if (this.hasFocus()) {
|
| 5373 |
+
return;
|
| 5374 |
+
}
|
| 5375 |
+
|
| 5376 |
+
this.$container.addClass('select2-container--focus');
|
| 5377 |
+
this.trigger('focus', {});
|
| 5378 |
+
};
|
| 5379 |
+
|
| 5380 |
+
Select2.prototype.enable = function (args) {
|
| 5381 |
+
if (this.options.get('debug') && window.console && console.warn) {
|
| 5382 |
+
console.warn(
|
| 5383 |
+
'Select2: The `select2("enable")` method has been deprecated and will' +
|
| 5384 |
+
' be removed in later Select2 versions. Use $element.prop("disabled")' +
|
| 5385 |
+
' instead.'
|
| 5386 |
+
);
|
| 5387 |
+
}
|
| 5388 |
+
|
| 5389 |
+
if (args == null || args.length === 0) {
|
| 5390 |
+
args = [true];
|
| 5391 |
+
}
|
| 5392 |
+
|
| 5393 |
+
var disabled = !args[0];
|
| 5394 |
+
|
| 5395 |
+
this.$element.prop('disabled', disabled);
|
| 5396 |
+
};
|
| 5397 |
+
|
| 5398 |
+
Select2.prototype.data = function () {
|
| 5399 |
+
if (this.options.get('debug') &&
|
| 5400 |
+
arguments.length > 0 && window.console && console.warn) {
|
| 5401 |
+
console.warn(
|
| 5402 |
+
'Select2: Data can no longer be set using `select2("data")`. You ' +
|
| 5403 |
+
'should consider setting the value instead using `$element.val()`.'
|
| 5404 |
+
);
|
| 5405 |
+
}
|
| 5406 |
+
|
| 5407 |
+
var data = [];
|
| 5408 |
+
|
| 5409 |
+
this.dataAdapter.current(function (currentData) {
|
| 5410 |
+
data = currentData;
|
| 5411 |
+
});
|
| 5412 |
+
|
| 5413 |
+
return data;
|
| 5414 |
+
};
|
| 5415 |
+
|
| 5416 |
+
Select2.prototype.val = function (args) {
|
| 5417 |
+
if (this.options.get('debug') && window.console && console.warn) {
|
| 5418 |
+
console.warn(
|
| 5419 |
+
'Select2: The `select2("val")` method has been deprecated and will be' +
|
| 5420 |
+
' removed in later Select2 versions. Use $element.val() instead.'
|
| 5421 |
+
);
|
| 5422 |
+
}
|
| 5423 |
+
|
| 5424 |
+
if (args == null || args.length === 0) {
|
| 5425 |
+
return this.$element.val();
|
| 5426 |
+
}
|
| 5427 |
+
|
| 5428 |
+
var newVal = args[0];
|
| 5429 |
+
|
| 5430 |
+
if ($.isArray(newVal)) {
|
| 5431 |
+
newVal = $.map(newVal, function (obj) {
|
| 5432 |
+
return obj.toString();
|
| 5433 |
+
});
|
| 5434 |
+
}
|
| 5435 |
+
|
| 5436 |
+
this.$element.val(newVal).trigger('change');
|
| 5437 |
+
};
|
| 5438 |
+
|
| 5439 |
+
Select2.prototype.destroy = function () {
|
| 5440 |
+
this.$container.remove();
|
| 5441 |
+
|
| 5442 |
+
if (this.$element[0].detachEvent) {
|
| 5443 |
+
this.$element[0].detachEvent('onpropertychange', this._sync);
|
| 5444 |
+
}
|
| 5445 |
+
|
| 5446 |
+
if (this._observer != null) {
|
| 5447 |
+
this._observer.disconnect();
|
| 5448 |
+
this._observer = null;
|
| 5449 |
+
} else if (this.$element[0].removeEventListener) {
|
| 5450 |
+
this.$element[0]
|
| 5451 |
+
.removeEventListener('DOMAttrModified', this._sync, false);
|
| 5452 |
+
}
|
| 5453 |
+
|
| 5454 |
+
this._sync = null;
|
| 5455 |
+
|
| 5456 |
+
this.$element.off('.select2');
|
| 5457 |
+
this.$element.attr('tabindex', this.$element.data('old-tabindex'));
|
| 5458 |
+
|
| 5459 |
+
this.$element.removeClass('select2-hidden-accessible');
|
| 5460 |
+
this.$element.attr('aria-hidden', 'false');
|
| 5461 |
+
this.$element.removeData('select2');
|
| 5462 |
+
|
| 5463 |
+
this.dataAdapter.destroy();
|
| 5464 |
+
this.selection.destroy();
|
| 5465 |
+
this.dropdown.destroy();
|
| 5466 |
+
this.results.destroy();
|
| 5467 |
+
|
| 5468 |
+
this.dataAdapter = null;
|
| 5469 |
+
this.selection = null;
|
| 5470 |
+
this.dropdown = null;
|
| 5471 |
+
this.results = null;
|
| 5472 |
+
};
|
| 5473 |
+
|
| 5474 |
+
Select2.prototype.render = function () {
|
| 5475 |
+
var $container = $(
|
| 5476 |
+
'<span class="select2 select2-container">' +
|
| 5477 |
+
'<span class="selection"></span>' +
|
| 5478 |
+
'<span class="dropdown-wrapper" aria-hidden="true"></span>' +
|
| 5479 |
+
'</span>'
|
| 5480 |
+
);
|
| 5481 |
+
|
| 5482 |
+
$container.attr('dir', this.options.get('dir'));
|
| 5483 |
+
|
| 5484 |
+
this.$container = $container;
|
| 5485 |
+
|
| 5486 |
+
this.$container.addClass('select2-container--' + this.options.get('theme'));
|
| 5487 |
+
|
| 5488 |
+
$container.data('element', this.$element);
|
| 5489 |
+
|
| 5490 |
+
return $container;
|
| 5491 |
+
};
|
| 5492 |
+
|
| 5493 |
+
return Select2;
|
| 5494 |
+
});
|
| 5495 |
+
|
| 5496 |
+
S2.define('jquery-mousewheel',[
|
| 5497 |
+
'jquery'
|
| 5498 |
+
], function ($) {
|
| 5499 |
+
// Used to shim jQuery.mousewheel for non-full builds.
|
| 5500 |
+
return $;
|
| 5501 |
+
});
|
| 5502 |
+
|
| 5503 |
+
S2.define('jquery.select2',[
|
| 5504 |
+
'jquery',
|
| 5505 |
+
'jquery-mousewheel',
|
| 5506 |
+
|
| 5507 |
+
'./select2/core',
|
| 5508 |
+
'./select2/defaults'
|
| 5509 |
+
], function ($, _, Select2, Defaults) {
|
| 5510 |
+
if ($.fn.select2 == null) {
|
| 5511 |
+
// All methods that should return the element
|
| 5512 |
+
var thisMethods = ['open', 'close', 'destroy'];
|
| 5513 |
+
|
| 5514 |
+
$.fn.select2 = function (options) {
|
| 5515 |
+
options = options || {};
|
| 5516 |
+
|
| 5517 |
+
if (typeof options === 'object') {
|
| 5518 |
+
this.each(function () {
|
| 5519 |
+
var instanceOptions = $.extend(true, {}, options);
|
| 5520 |
+
|
| 5521 |
+
var instance = new Select2($(this), instanceOptions);
|
| 5522 |
+
});
|
| 5523 |
+
|
| 5524 |
+
return this;
|
| 5525 |
+
} else if (typeof options === 'string') {
|
| 5526 |
+
var ret;
|
| 5527 |
+
|
| 5528 |
+
this.each(function () {
|
| 5529 |
+
var instance = $(this).data('select2');
|
| 5530 |
+
|
| 5531 |
+
if (instance == null && window.console && console.error) {
|
| 5532 |
+
console.error(
|
| 5533 |
+
'The select2(\'' + options + '\') method was called on an ' +
|
| 5534 |
+
'element that is not using Select2.'
|
| 5535 |
+
);
|
| 5536 |
+
}
|
| 5537 |
+
|
| 5538 |
+
var args = Array.prototype.slice.call(arguments, 1);
|
| 5539 |
+
|
| 5540 |
+
ret = instance[options].apply(instance, args);
|
| 5541 |
+
});
|
| 5542 |
+
|
| 5543 |
+
// Check if we should be returning `this`
|
| 5544 |
+
if ($.inArray(options, thisMethods) > -1) {
|
| 5545 |
+
return this;
|
| 5546 |
+
}
|
| 5547 |
+
|
| 5548 |
+
return ret;
|
| 5549 |
+
} else {
|
| 5550 |
+
throw new Error('Invalid arguments for Select2: ' + options);
|
| 5551 |
+
}
|
| 5552 |
+
};
|
| 5553 |
+
}
|
| 5554 |
+
|
| 5555 |
+
if ($.fn.select2.defaults == null) {
|
| 5556 |
+
$.fn.select2.defaults = Defaults;
|
| 5557 |
+
}
|
| 5558 |
+
|
| 5559 |
+
return Select2;
|
| 5560 |
+
});
|
| 5561 |
+
|
| 5562 |
+
// Return the AMD loader configuration so it can be used outside of this file
|
| 5563 |
+
return {
|
| 5564 |
+
define: S2.define,
|
| 5565 |
+
require: S2.require
|
| 5566 |
+
};
|
| 5567 |
+
}());
|
| 5568 |
+
|
| 5569 |
+
// Autoload the jQuery bindings
|
| 5570 |
+
// We know that all of the modules exist above this, so we're safe
|
| 5571 |
+
var select2 = S2.require('jquery.select2');
|
| 5572 |
+
|
| 5573 |
+
// Hold the AMD module references on the jQuery function that was just loaded
|
| 5574 |
+
// This allows Select2 to use the internal loader outside of this file, such
|
| 5575 |
+
// as in the language files.
|
| 5576 |
+
jQuery.fn.select2.amd = S2;
|
| 5577 |
+
|
| 5578 |
+
// Return the Select2 instance for anyone who is importing it.
|
| 5579 |
+
return select2;
|
| 5580 |
+
}));
|
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
| 1 |
+
/*! Select2 4.0.2 | https://github.com/select2/select2/blob/master/LICENSE.md */!function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a("object"==typeof exports?require("jquery"):jQuery)}(function(a){var b=function(){if(a&&a.fn&&a.fn.select2&&a.fn.select2.amd)var b=a.fn.select2.amd;var b;return function(){if(!b||!b.requirejs){b?c=b:b={};var a,c,d;!function(b){function e(a,b){return u.call(a,b)}function f(a,b){var c,d,e,f,g,h,i,j,k,l,m,n=b&&b.split("/"),o=s.map,p=o&&o["*"]||{};if(a&&"."===a.charAt(0))if(b){for(a=a.split("/"),g=a.length-1,s.nodeIdCompat&&w.test(a[g])&&(a[g]=a[g].replace(w,"")),a=n.slice(0,n.length-1).concat(a),k=0;k<a.length;k+=1)if(m=a[k],"."===m)a.splice(k,1),k-=1;else if(".."===m){if(1===k&&(".."===a[2]||".."===a[0]))break;k>0&&(a.splice(k-1,2),k-=2)}a=a.join("/")}else 0===a.indexOf("./")&&(a=a.substring(2));if((n||p)&&o){for(c=a.split("/"),k=c.length;k>0;k-=1){if(d=c.slice(0,k).join("/"),n)for(l=n.length;l>0;l-=1)if(e=o[n.slice(0,l).join("/")],e&&(e=e[d])){f=e,h=k;break}if(f)break;!i&&p&&p[d]&&(i=p[d],j=k)}!f&&i&&(f=i,h=j),f&&(c.splice(0,h,f),a=c.join("/"))}return a}function g(a,c){return function(){var d=v.call(arguments,0);return"string"!=typeof d[0]&&1===d.length&&d.push(null),n.apply(b,d.concat([a,c]))}}function h(a){return function(b){return f(b,a)}}function i(a){return function(b){q[a]=b}}function j(a){if(e(r,a)){var c=r[a];delete r[a],t[a]=!0,m.apply(b,c)}if(!e(q,a)&&!e(t,a))throw new Error("No "+a);return q[a]}function k(a){var b,c=a?a.indexOf("!"):-1;return c>-1&&(b=a.substring(0,c),a=a.substring(c+1,a.length)),[b,a]}function l(a){return function(){return s&&s.config&&s.config[a]||{}}}var m,n,o,p,q={},r={},s={},t={},u=Object.prototype.hasOwnProperty,v=[].slice,w=/\.js$/;o=function(a,b){var c,d=k(a),e=d[0];return a=d[1],e&&(e=f(e,b),c=j(e)),e?a=c&&c.normalize?c.normalize(a,h(b)):f(a,b):(a=f(a,b),d=k(a),e=d[0],a=d[1],e&&(c=j(e))),{f:e?e+"!"+a:a,n:a,pr:e,p:c}},p={require:function(a){return g(a)},exports:function(a){var b=q[a];return"undefined"!=typeof b?b:q[a]={}},module:function(a){return{id:a,uri:"",exports:q[a],config:l(a)}}},m=function(a,c,d,f){var h,k,l,m,n,s,u=[],v=typeof d;if(f=f||a,"undefined"===v||"function"===v){for(c=!c.length&&d.length?["require","exports","module"]:c,n=0;n<c.length;n+=1)if(m=o(c[n],f),k=m.f,"require"===k)u[n]=p.require(a);else if("exports"===k)u[n]=p.exports(a),s=!0;else if("module"===k)h=u[n]=p.module(a);else if(e(q,k)||e(r,k)||e(t,k))u[n]=j(k);else{if(!m.p)throw new Error(a+" missing "+k);m.p.load(m.n,g(f,!0),i(k),{}),u[n]=q[k]}l=d?d.apply(q[a],u):void 0,a&&(h&&h.exports!==b&&h.exports!==q[a]?q[a]=h.exports:l===b&&s||(q[a]=l))}else a&&(q[a]=d)},a=c=n=function(a,c,d,e,f){if("string"==typeof a)return p[a]?p[a](c):j(o(a,c).f);if(!a.splice){if(s=a,s.deps&&n(s.deps,s.callback),!c)return;c.splice?(a=c,c=d,d=null):a=b}return c=c||function(){},"function"==typeof d&&(d=e,e=f),e?m(b,a,c,d):setTimeout(function(){m(b,a,c,d)},4),n},n.config=function(a){return n(a)},a._defined=q,d=function(a,b,c){if("string"!=typeof a)throw new Error("See almond README: incorrect module build, no module name");b.splice||(c=b,b=[]),e(q,a)||e(r,a)||(r[a]=[a,b,c])},d.amd={jQuery:!0}}(),b.requirejs=a,b.require=c,b.define=d}}(),b.define("almond",function(){}),b.define("jquery",[],function(){var b=a||$;return null==b&&console&&console.error&&console.error("Select2: An instance of jQuery or a jQuery-compatible library was not found. Make sure that you are including jQuery before Select2 on your web page."),b}),b.define("select2/utils",["jquery"],function(a){function b(a){var b=a.prototype,c=[];for(var d in b){var e=b[d];"function"==typeof e&&"constructor"!==d&&c.push(d)}return c}var c={};c.Extend=function(a,b){function c(){this.constructor=a}var d={}.hasOwnProperty;for(var e in b)d.call(b,e)&&(a[e]=b[e]);return c.prototype=b.prototype,a.prototype=new c,a.__super__=b.prototype,a},c.Decorate=function(a,c){function d(){var b=Array.prototype.unshift,d=c.prototype.constructor.length,e=a.prototype.constructor;d>0&&(b.call(arguments,a.prototype.constructor),e=c.prototype.constructor),e.apply(this,arguments)}function e(){this.constructor=d}var f=b(c),g=b(a);c.displayName=a.displayName,d.prototype=new e;for(var h=0;h<g.length;h++){var i=g[h];d.prototype[i]=a.prototype[i]}for(var j=(function(a){var b=function(){};a in d.prototype&&(b=d.prototype[a]);var e=c.prototype[a];return function(){var a=Array.prototype.unshift;return a.call(arguments,b),e.apply(this,arguments)}}),k=0;k<f.length;k++){var l=f[k];d.prototype[l]=j(l)}return d};var d=function(){this.listeners={}};return d.prototype.on=function(a,b){this.listeners=this.listeners||{},a in this.listeners?this.listeners[a].push(b):this.listeners[a]=[b]},d.prototype.trigger=function(a){var b=Array.prototype.slice;this.listeners=this.listeners||{},a in this.listeners&&this.invoke(this.listeners[a],b.call(arguments,1)),"*"in this.listeners&&this.invoke(this.listeners["*"],arguments)},d.prototype.invoke=function(a,b){for(var c=0,d=a.length;d>c;c++)a[c].apply(this,b)},c.Observable=d,c.generateChars=function(a){for(var b="",c=0;a>c;c++){var d=Math.floor(36*Math.random());b+=d.toString(36)}return b},c.bind=function(a,b){return function(){a.apply(b,arguments)}},c._convertData=function(a){for(var b in a){var c=b.split("-"),d=a;if(1!==c.length){for(var e=0;e<c.length;e++){var f=c[e];f=f.substring(0,1).toLowerCase()+f.substring(1),f in d||(d[f]={}),e==c.length-1&&(d[f]=a[b]),d=d[f]}delete a[b]}}return a},c.hasScroll=function(b,c){var d=a(c),e=c.style.overflowX,f=c.style.overflowY;return e!==f||"hidden"!==f&&"visible"!==f?"scroll"===e||"scroll"===f?!0:d.innerHeight()<c.scrollHeight||d.innerWidth()<c.scrollWidth:!1},c.escapeMarkup=function(a){var b={"\\":"\","&":"&","<":"<",">":">",'"':""","'":"'","/":"/"};return"string"!=typeof a?a:String(a).replace(/[&<>"'\/\\]/g,function(a){return b[a]})},c.appendMany=function(b,c){if("1.7"===a.fn.jquery.substr(0,3)){var d=a();a.map(c,function(a){d=d.add(a)}),c=d}b.append(c)},c}),b.define("select2/results",["jquery","./utils"],function(a,b){function c(a,b,d){this.$element=a,this.data=d,this.options=b,c.__super__.constructor.call(this)}return b.Extend(c,b.Observable),c.prototype.render=function(){var b=a('<ul class="select2-results__options" role="tree"></ul>');return this.options.get("multiple")&&b.attr("aria-multiselectable","true"),this.$results=b,b},c.prototype.clear=function(){this.$results.empty()},c.prototype.displayMessage=function(b){var c=this.options.get("escapeMarkup");this.clear(),this.hideLoading();var d=a('<li role="treeitem" aria-live="assertive" class="select2-results__option"></li>'),e=this.options.get("translations").get(b.message);d.append(c(e(b.args))),d[0].className+=" select2-results__message",this.$results.append(d)},c.prototype.hideMessages=function(){this.$results.find(".select2-results__message").remove()},c.prototype.append=function(a){this.hideLoading();var b=[];if(null==a.results||0===a.results.length)return void(0===this.$results.children().length&&this.trigger("results:message",{message:"noResults"}));a.results=this.sort(a.results);for(var c=0;c<a.results.length;c++){var d=a.results[c],e=this.option(d);b.push(e)}this.$results.append(b)},c.prototype.position=function(a,b){var c=b.find(".select2-results");c.append(a)},c.prototype.sort=function(a){var b=this.options.get("sorter");return b(a)},c.prototype.setClasses=function(){var b=this;this.data.current(function(c){var d=a.map(c,function(a){return a.id.toString()}),e=b.$results.find(".select2-results__option[aria-selected]");e.each(function(){var b=a(this),c=a.data(this,"data"),e=""+c.id;null!=c.element&&c.element.selected||null==c.element&&a.inArray(e,d)>-1?b.attr("aria-selected","true"):b.attr("aria-selected","false")});var f=e.filter("[aria-selected=true]");f.length>0?f.first().trigger("mouseenter"):e.first().trigger("mouseenter")})},c.prototype.showLoading=function(a){this.hideLoading();var b=this.options.get("translations").get("searching"),c={disabled:!0,loading:!0,text:b(a)},d=this.option(c);d.className+=" loading-results",this.$results.prepend(d)},c.prototype.hideLoading=function(){this.$results.find(".loading-results").remove()},c.prototype.option=function(b){var c=document.createElement("li");c.className="select2-results__option";var d={role:"treeitem","aria-selected":"false"};b.disabled&&(delete d["aria-selected"],d["aria-disabled"]="true"),null==b.id&&delete d["aria-selected"],null!=b._resultId&&(c.id=b._resultId),b.title&&(c.title=b.title),b.children&&(d.role="group",d["aria-label"]=b.text,delete d["aria-selected"]);for(var e in d){var f=d[e];c.setAttribute(e,f)}if(b.children){var g=a(c),h=document.createElement("strong");h.className="select2-results__group";a(h);this.template(b,h);for(var i=[],j=0;j<b.children.length;j++){var k=b.children[j],l=this.option(k);i.push(l)}var m=a("<ul></ul>",{"class":"select2-results__options select2-results__options--nested"});m.append(i),g.append(h),g.append(m)}else this.template(b,c);return a.data(c,"data",b),c},c.prototype.bind=function(b,c){var d=this,e=b.id+"-results";this.$results.attr("id",e),b.on("results:all",function(a){d.clear(),d.append(a.data),b.isOpen()&&d.setClasses()}),b.on("results:append",function(a){d.append(a.data),b.isOpen()&&d.setClasses()}),b.on("query",function(a){d.hideMessages(),d.showLoading(a)}),b.on("select",function(){b.isOpen()&&d.setClasses()}),b.on("unselect",function(){b.isOpen()&&d.setClasses()}),b.on("open",function(){d.$results.attr("aria-expanded","true"),d.$results.attr("aria-hidden","false"),d.setClasses(),d.ensureHighlightVisible()}),b.on("close",function(){d.$results.attr("aria-expanded","false"),d.$results.attr("aria-hidden","true"),d.$results.removeAttr("aria-activedescendant")}),b.on("results:toggle",function(){var a=d.getHighlightedResults();0!==a.length&&a.trigger("mouseup")}),b.on("results:select",function(){var a=d.getHighlightedResults();if(0!==a.length){var b=a.data("data");"true"==a.attr("aria-selected")?d.trigger("close",{}):d.trigger("select",{data:b})}}),b.on("results:previous",function(){var a=d.getHighlightedResults(),b=d.$results.find("[aria-selected]"),c=b.index(a);if(0!==c){var e=c-1;0===a.length&&(e=0);var f=b.eq(e);f.trigger("mouseenter");var g=d.$results.offset().top,h=f.offset().top,i=d.$results.scrollTop()+(h-g);0===e?d.$results.scrollTop(0):0>h-g&&d.$results.scrollTop(i)}}),b.on("results:next",function(){var a=d.getHighlightedResults(),b=d.$results.find("[aria-selected]"),c=b.index(a),e=c+1;if(!(e>=b.length)){var f=b.eq(e);f.trigger("mouseenter");var g=d.$results.offset().top+d.$results.outerHeight(!1),h=f.offset().top+f.outerHeight(!1),i=d.$results.scrollTop()+h-g;0===e?d.$results.scrollTop(0):h>g&&d.$results.scrollTop(i)}}),b.on("results:focus",function(a){a.element.addClass("select2-results__option--highlighted")}),b.on("results:message",function(a){d.displayMessage(a)}),a.fn.mousewheel&&this.$results.on("mousewheel",function(a){var b=d.$results.scrollTop(),c=d.$results.get(0).scrollHeight-b+a.deltaY,e=a.deltaY>0&&b-a.deltaY<=0,f=a.deltaY<0&&c<=d.$results.height();e?(d.$results.scrollTop(0),a.preventDefault(),a.stopPropagation()):f&&(d.$results.scrollTop(d.$results.get(0).scrollHeight-d.$results.height()),a.preventDefault(),a.stopPropagation())}),this.$results.on("mouseup",".select2-results__option[aria-selected]",function(b){var c=a(this),e=c.data("data");return"true"===c.attr("aria-selected")?void(d.options.get("multiple")?d.trigger("unselect",{originalEvent:b,data:e}):d.trigger("close",{})):void d.trigger("select",{originalEvent:b,data:e})}),this.$results.on("mouseenter",".select2-results__option[aria-selected]",function(b){var c=a(this).data("data");d.getHighlightedResults().removeClass("select2-results__option--highlighted"),d.trigger("results:focus",{data:c,element:a(this)})})},c.prototype.getHighlightedResults=function(){var a=this.$results.find(".select2-results__option--highlighted");return a},c.prototype.destroy=function(){this.$results.remove()},c.prototype.ensureHighlightVisible=function(){var a=this.getHighlightedResults();if(0!==a.length){var b=this.$results.find("[aria-selected]"),c=b.index(a),d=this.$results.offset().top,e=a.offset().top,f=this.$results.scrollTop()+(e-d),g=e-d;f-=2*a.outerHeight(!1),2>=c?this.$results.scrollTop(0):(g>this.$results.outerHeight()||0>g)&&this.$results.scrollTop(f)}},c.prototype.template=function(b,c){var d=this.options.get("templateResult"),e=this.options.get("escapeMarkup"),f=d(b,c);null==f?c.style.display="none":"string"==typeof f?c.innerHTML=e(f):a(c).append(f)},c}),b.define("select2/keys",[],function(){var a={BACKSPACE:8,TAB:9,ENTER:13,SHIFT:16,CTRL:17,ALT:18,ESC:27,SPACE:32,PAGE_UP:33,PAGE_DOWN:34,END:35,HOME:36,LEFT:37,UP:38,RIGHT:39,DOWN:40,DELETE:46};return a}),b.define("select2/selection/base",["jquery","../utils","../keys"],function(a,b,c){function d(a,b){this.$element=a,this.options=b,d.__super__.constructor.call(this)}return b.Extend(d,b.Observable),d.prototype.render=function(){var b=a('<span class="select2-selection" role="combobox" aria-haspopup="true" aria-expanded="false"></span>');return this._tabindex=0,null!=this.$element.data("old-tabindex")?this._tabindex=this.$element.data("old-tabindex"):null!=this.$element.attr("tabindex")&&(this._tabindex=this.$element.attr("tabindex")),b.attr("title",this.$element.attr("title")),b.attr("tabindex",this._tabindex),this.$selection=b,b},d.prototype.bind=function(a,b){var d=this,e=(a.id+"-container",a.id+"-results");this.container=a,this.$selection.on("focus",function(a){d.trigger("focus",a)}),this.$selection.on("blur",function(a){d._handleBlur(a)}),this.$selection.on("keydown",function(a){d.trigger("keypress",a),a.which===c.SPACE&&a.preventDefault()}),a.on("results:focus",function(a){d.$selection.attr("aria-activedescendant",a.data._resultId)}),a.on("selection:update",function(a){d.update(a.data)}),a.on("open",function(){d.$selection.attr("aria-expanded","true"),d.$selection.attr("aria-owns",e),d._attachCloseHandler(a)}),a.on("close",function(){d.$selection.attr("aria-expanded","false"),d.$selection.removeAttr("aria-activedescendant"),d.$selection.removeAttr("aria-owns"),d.$selection.focus(),d._detachCloseHandler(a)}),a.on("enable",function(){d.$selection.attr("tabindex",d._tabindex)}),a.on("disable",function(){d.$selection.attr("tabindex","-1")})},d.prototype._handleBlur=function(b){var c=this;window.setTimeout(function(){document.activeElement==c.$selection[0]||a.contains(c.$selection[0],document.activeElement)||c.trigger("blur",b)},1)},d.prototype._attachCloseHandler=function(b){a(document.body).on("mousedown.select2."+b.id,function(b){var c=a(b.target),d=c.closest(".select2"),e=a(".select2.select2-container--open");e.each(function(){var b=a(this);if(this!=d[0]){var c=b.data("element");c.select2("close")}})})},d.prototype._detachCloseHandler=function(b){a(document.body).off("mousedown.select2."+b.id)},d.prototype.position=function(a,b){var c=b.find(".selection");c.append(a)},d.prototype.destroy=function(){this._detachCloseHandler(this.container)},d.prototype.update=function(a){throw new Error("The `update` method must be defined in child classes.")},d}),b.define("select2/selection/single",["jquery","./base","../utils","../keys"],function(a,b,c,d){function e(){e.__super__.constructor.apply(this,arguments)}return c.Extend(e,b),e.prototype.render=function(){var a=e.__super__.render.call(this);return a.addClass("select2-selection--single"),a.html('<span class="select2-selection__rendered"></span><span class="select2-selection__arrow" role="presentation"><b role="presentation"></b></span>'),a},e.prototype.bind=function(a,b){var c=this;e.__super__.bind.apply(this,arguments);var d=a.id+"-container";this.$selection.find(".select2-selection__rendered").attr("id",d),this.$selection.attr("aria-labelledby",d),this.$selection.on("mousedown",function(a){1===a.which&&c.trigger("toggle",{originalEvent:a})}),this.$selection.on("focus",function(a){}),this.$selection.on("blur",function(a){}),a.on("selection:update",function(a){c.update(a.data)})},e.prototype.clear=function(){this.$selection.find(".select2-selection__rendered").empty()},e.prototype.display=function(a,b){var c=this.options.get("templateSelection"),d=this.options.get("escapeMarkup");return d(c(a,b))},e.prototype.selectionContainer=function(){return a("<span></span>")},e.prototype.update=function(a){if(0===a.length)return void this.clear();var b=a[0],c=this.$selection.find(".select2-selection__rendered"),d=this.display(b,c);c.empty().append(d),c.prop("title",b.title||b.text)},e}),b.define("select2/selection/multiple",["jquery","./base","../utils"],function(a,b,c){function d(a,b){d.__super__.constructor.apply(this,arguments)}return c.Extend(d,b),d.prototype.render=function(){var a=d.__super__.render.call(this);return a.addClass("select2-selection--multiple"),a.html('<ul class="select2-selection__rendered"></ul>'),a},d.prototype.bind=function(b,c){var e=this;d.__super__.bind.apply(this,arguments),this.$selection.on("click",function(a){e.trigger("toggle",{originalEvent:a})}),this.$selection.on("click",".select2-selection__choice__remove",function(b){if(!e.options.get("disabled")){var c=a(this),d=c.parent(),f=d.data("data");e.trigger("unselect",{originalEvent:b,data:f})}})},d.prototype.clear=function(){this.$selection.find(".select2-selection__rendered").empty()},d.prototype.display=function(a,b){var c=this.options.get("templateSelection"),d=this.options.get("escapeMarkup");return d(c(a,b))},d.prototype.selectionContainer=function(){var b=a('<li class="select2-selection__choice"><span class="select2-selection__choice__remove" role="presentation">×</span></li>');return b},d.prototype.update=function(a){if(this.clear(),0!==a.length){for(var b=[],d=0;d<a.length;d++){var e=a[d],f=this.selectionContainer(),g=this.display(e,f);f.append(g),f.prop("title",e.title||e.text),f.data("data",e),b.push(f)}var h=this.$selection.find(".select2-selection__rendered");c.appendMany(h,b)}},d}),b.define("select2/selection/placeholder",["../utils"],function(a){function b(a,b,c){this.placeholder=this.normalizePlaceholder(c.get("placeholder")),a.call(this,b,c)}return b.prototype.normalizePlaceholder=function(a,b){return"string"==typeof b&&(b={id:"",text:b}),b},b.prototype.createPlaceholder=function(a,b){var c=this.selectionContainer();return c.html(this.display(b)),c.addClass("select2-selection__placeholder").removeClass("select2-selection__choice"),c},b.prototype.update=function(a,b){var c=1==b.length&&b[0].id!=this.placeholder.id,d=b.length>1;if(d||c)return a.call(this,b);this.clear();var e=this.createPlaceholder(this.placeholder);this.$selection.find(".select2-selection__rendered").append(e)},b}),b.define("select2/selection/allowClear",["jquery","../keys"],function(a,b){function c(){}return c.prototype.bind=function(a,b,c){var d=this;a.call(this,b,c),null==this.placeholder&&this.options.get("debug")&&window.console&&console.error&&console.error("Select2: The `allowClear` option should be used in combination with the `placeholder` option."),this.$selection.on("mousedown",".select2-selection__clear",function(a){d._handleClear(a)}),b.on("keypress",function(a){d._handleKeyboardClear(a,b)})},c.prototype._handleClear=function(a,b){if(!this.options.get("disabled")){var c=this.$selection.find(".select2-selection__clear");if(0!==c.length){b.stopPropagation();for(var d=c.data("data"),e=0;e<d.length;e++){var f={data:d[e]};if(this.trigger("unselect",f),f.prevented)return}this.$element.val(this.placeholder.id).trigger("change"),this.trigger("toggle",{})}}},c.prototype._handleKeyboardClear=function(a,c,d){d.isOpen()||(c.which==b.DELETE||c.which==b.BACKSPACE)&&this._handleClear(c)},c.prototype.update=function(b,c){if(b.call(this,c),!(this.$selection.find(".select2-selection__placeholder").length>0||0===c.length)){var d=a('<span class="select2-selection__clear">×</span>');d.data("data",c),this.$selection.find(".select2-selection__rendered").prepend(d)}},c}),b.define("select2/selection/search",["jquery","../utils","../keys"],function(a,b,c){function d(a,b,c){a.call(this,b,c)}return d.prototype.render=function(b){var c=a('<li class="select2-search select2-search--inline"><input class="select2-search__field" type="search" tabindex="-1" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" role="textbox" aria-autocomplete="list" /></li>');this.$searchContainer=c,this.$search=c.find("input");var d=b.call(this);return this._transferTabIndex(),d},d.prototype.bind=function(a,b,d){var e=this;a.call(this,b,d),b.on("open",function(){e.$search.trigger("focus")}),b.on("close",function(){e.$search.val(""),e.$search.removeAttr("aria-activedescendant"),e.$search.trigger("focus")}),b.on("enable",function(){e.$search.prop("disabled",!1),e._transferTabIndex()}),b.on("disable",function(){e.$search.prop("disabled",!0)}),b.on("focus",function(a){e.$search.trigger("focus")}),b.on("results:focus",function(a){e.$search.attr("aria-activedescendant",a.id)}),this.$selection.on("focusin",".select2-search--inline",function(a){e.trigger("focus",a)}),this.$selection.on("focusout",".select2-search--inline",function(a){e._handleBlur(a)}),this.$selection.on("keydown",".select2-search--inline",function(a){a.stopPropagation(),e.trigger("keypress",a),e._keyUpPrevented=a.isDefaultPrevented();var b=a.which;if(b===c.BACKSPACE&&""===e.$search.val()){var d=e.$searchContainer.prev(".select2-selection__choice");if(d.length>0){var f=d.data("data");e.searchRemoveChoice(f),a.preventDefault()}}});var f=document.documentMode,g=f&&11>=f;this.$selection.on("input.searchcheck",".select2-search--inline",function(a){return g?void e.$selection.off("input.search input.searchcheck"):void e.$selection.off("keyup.search")}),this.$selection.on("keyup.search input.search",".select2-search--inline",function(a){if(g&&"input"===a.type)return void e.$selection.off("input.search input.searchcheck");var b=a.which;b!=c.SHIFT&&b!=c.CTRL&&b!=c.ALT&&b!=c.TAB&&e.handleSearch(a)})},d.prototype._transferTabIndex=function(a){this.$search.attr("tabindex",this.$selection.attr("tabindex")),this.$selection.attr("tabindex","-1")},d.prototype.createPlaceholder=function(a,b){this.$search.attr("placeholder",b.text)},d.prototype.update=function(a,b){var c=this.$search[0]==document.activeElement;this.$search.attr("placeholder",""),a.call(this,b),this.$selection.find(".select2-selection__rendered").append(this.$searchContainer),this.resizeSearch(),c&&this.$search.focus()},d.prototype.handleSearch=function(){if(this.resizeSearch(),!this._keyUpPrevented){var a=this.$search.val();this.trigger("query",{term:a})}this._keyUpPrevented=!1},d.prototype.searchRemoveChoice=function(a,b){this.trigger("unselect",{data:b}),this.$search.val(b.text),this.handleSearch()},d.prototype.resizeSearch=function(){this.$search.css("width","25px");var a="";if(""!==this.$search.attr("placeholder"))a=this.$selection.find(".select2-selection__rendered").innerWidth();else{var b=this.$search.val().length+1;a=.75*b+"em"}this.$search.css("width",a)},d}),b.define("select2/selection/eventRelay",["jquery"],function(a){function b(){}return b.prototype.bind=function(b,c,d){var e=this,f=["open","opening","close","closing","select","selecting","unselect","unselecting"],g=["opening","closing","selecting","unselecting"];b.call(this,c,d),c.on("*",function(b,c){if(-1!==a.inArray(b,f)){c=c||{};var d=a.Event("select2:"+b,{params:c});e.$element.trigger(d),-1!==a.inArray(b,g)&&(c.prevented=d.isDefaultPrevented())}})},b}),b.define("select2/translation",["jquery","require"],function(a,b){function c(a){this.dict=a||{}}return c.prototype.all=function(){return this.dict},c.prototype.get=function(a){return this.dict[a]},c.prototype.extend=function(b){this.dict=a.extend({},b.all(),this.dict)},c._cache={},c.loadPath=function(a){if(!(a in c._cache)){var d=b(a);c._cache[a]=d}return new c(c._cache[a])},c}),b.define("select2/diacritics",[],function(){var a={"Ⓐ":"A","A":"A","À":"A","Á":"A","Â":"A","Ầ":"A","Ấ":"A","Ẫ":"A","Ẩ":"A","Ã":"A","Ā":"A","Ă":"A","Ằ":"A","Ắ":"A","Ẵ":"A","Ẳ":"A","Ȧ":"A","Ǡ":"A","Ä":"A","Ǟ":"A","Ả":"A","Å":"A","Ǻ":"A","Ǎ":"A","Ȁ":"A","Ȃ":"A","Ạ":"A","Ậ":"A","Ặ":"A","Ḁ":"A","Ą":"A","Ⱥ":"A","Ɐ":"A","Ꜳ":"AA","Æ":"AE","Ǽ":"AE","Ǣ":"AE","Ꜵ":"AO","Ꜷ":"AU","Ꜹ":"AV","Ꜻ":"AV","Ꜽ":"AY","Ⓑ":"B","B":"B","Ḃ":"B","Ḅ":"B","Ḇ":"B","Ƀ":"B","Ƃ":"B","Ɓ":"B","Ⓒ":"C","C":"C","Ć":"C","Ĉ":"C","Ċ":"C","Č":"C","Ç":"C","Ḉ":"C","Ƈ":"C","Ȼ":"C","Ꜿ":"C","Ⓓ":"D","D":"D","Ḋ":"D","Ď":"D","Ḍ":"D","Ḑ":"D","Ḓ":"D","Ḏ":"D","Đ":"D","Ƌ":"D","Ɗ":"D","Ɖ":"D","Ꝺ":"D","DZ":"DZ","DŽ":"DZ","Dz":"Dz","Dž":"Dz","Ⓔ":"E","E":"E","È":"E","É":"E","Ê":"E","Ề":"E","Ế":"E","Ễ":"E","Ể":"E","Ẽ":"E","Ē":"E","Ḕ":"E","Ḗ":"E","Ĕ":"E","Ė":"E","Ë":"E","Ẻ":"E","Ě":"E","Ȅ":"E","Ȇ":"E","Ẹ":"E","Ệ":"E","Ȩ":"E","Ḝ":"E","Ę":"E","Ḙ":"E","Ḛ":"E","Ɛ":"E","Ǝ":"E","Ⓕ":"F","F":"F","Ḟ":"F","Ƒ":"F","Ꝼ":"F","Ⓖ":"G","G":"G","Ǵ":"G","Ĝ":"G","Ḡ":"G","Ğ":"G","Ġ":"G","Ǧ":"G","Ģ":"G","Ǥ":"G","Ɠ":"G","Ꞡ":"G","Ᵹ":"G","Ꝿ":"G","Ⓗ":"H","H":"H","Ĥ":"H","Ḣ":"H","Ḧ":"H","Ȟ":"H","Ḥ":"H","Ḩ":"H","Ḫ":"H","Ħ":"H","Ⱨ":"H","Ⱶ":"H","Ɥ":"H","Ⓘ":"I","I":"I","Ì":"I","Í":"I","Î":"I","Ĩ":"I","Ī":"I","Ĭ":"I","İ":"I","Ï":"I","Ḯ":"I","Ỉ":"I","Ǐ":"I","Ȉ":"I","Ȋ":"I","Ị":"I","Į":"I","Ḭ":"I","Ɨ":"I","Ⓙ":"J","J":"J","Ĵ":"J","Ɉ":"J","Ⓚ":"K","K":"K","Ḱ":"K","Ǩ":"K","Ḳ":"K","Ķ":"K","Ḵ":"K","Ƙ":"K","Ⱪ":"K","Ꝁ":"K","Ꝃ":"K","Ꝅ":"K","Ꞣ":"K","Ⓛ":"L","L":"L","Ŀ":"L","Ĺ":"L","Ľ":"L","Ḷ":"L","Ḹ":"L","Ļ":"L","Ḽ":"L","Ḻ":"L","Ł":"L","Ƚ":"L","Ɫ":"L","Ⱡ":"L","Ꝉ":"L","Ꝇ":"L","Ꞁ":"L","LJ":"LJ","Lj":"Lj","Ⓜ":"M","M":"M","Ḿ":"M","Ṁ":"M","Ṃ":"M","Ɱ":"M","Ɯ":"M","Ⓝ":"N","N":"N","Ǹ":"N","Ń":"N","Ñ":"N","Ṅ":"N","Ň":"N","Ṇ":"N","Ņ":"N","Ṋ":"N","Ṉ":"N","Ƞ":"N","Ɲ":"N","Ꞑ":"N","Ꞥ":"N","NJ":"NJ","Nj":"Nj","Ⓞ":"O","O":"O","Ò":"O","Ó":"O","Ô":"O","Ồ":"O","Ố":"O","Ỗ":"O","Ổ":"O","Õ":"O","Ṍ":"O","Ȭ":"O","Ṏ":"O","Ō":"O","Ṑ":"O","Ṓ":"O","Ŏ":"O","Ȯ":"O","Ȱ":"O","Ö":"O","Ȫ":"O","Ỏ":"O","Ő":"O","Ǒ":"O","Ȍ":"O","Ȏ":"O","Ơ":"O","Ờ":"O","Ớ":"O","Ỡ":"O","Ở":"O","Ợ":"O","Ọ":"O","Ộ":"O","Ǫ":"O","Ǭ":"O","Ø":"O","Ǿ":"O","Ɔ":"O","Ɵ":"O","Ꝋ":"O","Ꝍ":"O","Ƣ":"OI","Ꝏ":"OO","Ȣ":"OU","Ⓟ":"P","P":"P","Ṕ":"P","Ṗ":"P","Ƥ":"P","Ᵽ":"P","Ꝑ":"P","Ꝓ":"P","Ꝕ":"P","Ⓠ":"Q","Q":"Q","Ꝗ":"Q","Ꝙ":"Q","Ɋ":"Q","Ⓡ":"R","R":"R","Ŕ":"R","Ṙ":"R","Ř":"R","Ȑ":"R","Ȓ":"R","Ṛ":"R","Ṝ":"R","Ŗ":"R","Ṟ":"R","Ɍ":"R","Ɽ":"R","Ꝛ":"R","Ꞧ":"R","Ꞃ":"R","Ⓢ":"S","S":"S","ẞ":"S","Ś":"S","Ṥ":"S","Ŝ":"S","Ṡ":"S","Š":"S","Ṧ":"S","Ṣ":"S","Ṩ":"S","Ș":"S","Ş":"S","Ȿ":"S","Ꞩ":"S","Ꞅ":"S","Ⓣ":"T","T":"T","Ṫ":"T","Ť":"T","Ṭ":"T","Ț":"T","Ţ":"T","Ṱ":"T","Ṯ":"T","Ŧ":"T","Ƭ":"T","Ʈ":"T","Ⱦ":"T","Ꞇ":"T","Ꜩ":"TZ","Ⓤ":"U","U":"U","Ù":"U","Ú":"U","Û":"U","Ũ":"U","Ṹ":"U","Ū":"U","Ṻ":"U","Ŭ":"U","Ü":"U","Ǜ":"U","Ǘ":"U","Ǖ":"U","Ǚ":"U","Ủ":"U","Ů":"U","Ű":"U","Ǔ":"U","Ȕ":"U","Ȗ":"U","Ư":"U","Ừ":"U","Ứ":"U","Ữ":"U","Ử":"U","Ự":"U","Ụ":"U","Ṳ":"U","Ų":"U","Ṷ":"U","Ṵ":"U","Ʉ":"U","Ⓥ":"V","V":"V","Ṽ":"V","Ṿ":"V","Ʋ":"V","Ꝟ":"V","Ʌ":"V","Ꝡ":"VY","Ⓦ":"W","W":"W","Ẁ":"W","Ẃ":"W","Ŵ":"W","Ẇ":"W","Ẅ":"W","Ẉ":"W","Ⱳ":"W","Ⓧ":"X","X":"X","Ẋ":"X","Ẍ":"X","Ⓨ":"Y","Y":"Y","Ỳ":"Y","Ý":"Y","Ŷ":"Y","Ỹ":"Y","Ȳ":"Y","Ẏ":"Y","Ÿ":"Y","Ỷ":"Y","Ỵ":"Y","Ƴ":"Y","Ɏ":"Y","Ỿ":"Y","Ⓩ":"Z","Z":"Z","Ź":"Z","Ẑ":"Z","Ż":"Z","Ž":"Z","Ẓ":"Z","Ẕ":"Z","Ƶ":"Z","Ȥ":"Z","Ɀ":"Z","Ⱬ":"Z","Ꝣ":"Z","ⓐ":"a","a":"a","ẚ":"a","à":"a","á":"a","â":"a","ầ":"a","ấ":"a","ẫ":"a","ẩ":"a","ã":"a","ā":"a","ă":"a","ằ":"a","ắ":"a","ẵ":"a","ẳ":"a","ȧ":"a","ǡ":"a","ä":"a","ǟ":"a","ả":"a","å":"a","ǻ":"a","ǎ":"a","ȁ":"a","ȃ":"a","ạ":"a","ậ":"a","ặ":"a","ḁ":"a","ą":"a","ⱥ":"a","ɐ":"a","ꜳ":"aa","æ":"ae","ǽ":"ae","ǣ":"ae","ꜵ":"ao","ꜷ":"au","ꜹ":"av","ꜻ":"av","ꜽ":"ay","ⓑ":"b","b":"b","ḃ":"b","ḅ":"b","ḇ":"b","ƀ":"b","ƃ":"b","ɓ":"b","ⓒ":"c","c":"c","ć":"c","ĉ":"c","ċ":"c","č":"c","ç":"c","ḉ":"c","ƈ":"c","ȼ":"c","ꜿ":"c","ↄ":"c","ⓓ":"d","d":"d","ḋ":"d","ď":"d","ḍ":"d","ḑ":"d","ḓ":"d","ḏ":"d","đ":"d","ƌ":"d","ɖ":"d","ɗ":"d","ꝺ":"d","dz":"dz","dž":"dz","ⓔ":"e","e":"e","è":"e","é":"e","ê":"e","ề":"e","ế":"e","ễ":"e","ể":"e","ẽ":"e","ē":"e","ḕ":"e","ḗ":"e","ĕ":"e","ė":"e","ë":"e","ẻ":"e","ě":"e","ȅ":"e","ȇ":"e","ẹ":"e","ệ":"e","ȩ":"e","ḝ":"e","ę":"e","ḙ":"e","ḛ":"e","ɇ":"e","ɛ":"e","ǝ":"e","ⓕ":"f","f":"f","ḟ":"f","ƒ":"f","ꝼ":"f","ⓖ":"g","g":"g","ǵ":"g","ĝ":"g","ḡ":"g","ğ":"g","ġ":"g","ǧ":"g","ģ":"g","ǥ":"g","ɠ":"g","ꞡ":"g","ᵹ":"g","ꝿ":"g","ⓗ":"h","h":"h","ĥ":"h","ḣ":"h","ḧ":"h","ȟ":"h","ḥ":"h","ḩ":"h","ḫ":"h","ẖ":"h","ħ":"h","ⱨ":"h","ⱶ":"h","ɥ":"h","ƕ":"hv","ⓘ":"i","i":"i","ì":"i","í":"i","î":"i","ĩ":"i","ī":"i","ĭ":"i","ï":"i","ḯ":"i","ỉ":"i","ǐ":"i","ȉ":"i","ȋ":"i","ị":"i","į":"i","ḭ":"i","ɨ":"i","ı":"i","ⓙ":"j","j":"j","ĵ":"j","ǰ":"j","ɉ":"j","ⓚ":"k","k":"k","ḱ":"k","ǩ":"k","ḳ":"k","ķ":"k","ḵ":"k","ƙ":"k","ⱪ":"k","ꝁ":"k","ꝃ":"k","ꝅ":"k","ꞣ":"k","ⓛ":"l","l":"l","ŀ":"l","ĺ":"l","ľ":"l","ḷ":"l","ḹ":"l","ļ":"l","ḽ":"l","ḻ":"l","ſ":"l","ł":"l","ƚ":"l","ɫ":"l","ⱡ":"l","ꝉ":"l","ꞁ":"l","ꝇ":"l","lj":"lj","ⓜ":"m","m":"m","ḿ":"m","ṁ":"m","ṃ":"m","ɱ":"m","ɯ":"m","ⓝ":"n","n":"n","ǹ":"n","ń":"n","ñ":"n","ṅ":"n","ň":"n","ṇ":"n","ņ":"n","ṋ":"n","ṉ":"n","ƞ":"n","ɲ":"n","ʼn":"n","ꞑ":"n","ꞥ":"n","nj":"nj","ⓞ":"o","o":"o","ò":"o","ó":"o","ô":"o","ồ":"o","ố":"o","ỗ":"o","ổ":"o","õ":"o","ṍ":"o","ȭ":"o","ṏ":"o","ō":"o","ṑ":"o","ṓ":"o","ŏ":"o","ȯ":"o","ȱ":"o","ö":"o","ȫ":"o","ỏ":"o","ő":"o","ǒ":"o","ȍ":"o","ȏ":"o","ơ":"o","ờ":"o","ớ":"o","ỡ":"o","ở":"o","ợ":"o","ọ":"o","ộ":"o","ǫ":"o","ǭ":"o","ø":"o","ǿ":"o","ɔ":"o","ꝋ":"o","ꝍ":"o","ɵ":"o","ƣ":"oi","ȣ":"ou","ꝏ":"oo","ⓟ":"p","p":"p","ṕ":"p","ṗ":"p","ƥ":"p","ᵽ":"p","ꝑ":"p","ꝓ":"p","ꝕ":"p","ⓠ":"q","q":"q","ɋ":"q","ꝗ":"q","ꝙ":"q","ⓡ":"r","r":"r","ŕ":"r","ṙ":"r","ř":"r","ȑ":"r","ȓ":"r","ṛ":"r","ṝ":"r","ŗ":"r","ṟ":"r","ɍ":"r","ɽ":"r","ꝛ":"r","ꞧ":"r","ꞃ":"r","ⓢ":"s","s":"s","ß":"s","ś":"s","ṥ":"s","ŝ":"s","ṡ":"s","š":"s","ṧ":"s","ṣ":"s","ṩ":"s","ș":"s","ş":"s","ȿ":"s","ꞩ":"s","ꞅ":"s","ẛ":"s","ⓣ":"t","t":"t","ṫ":"t","ẗ":"t","ť":"t","ṭ":"t","ț":"t","ţ":"t","ṱ":"t","ṯ":"t","ŧ":"t","ƭ":"t","ʈ":"t","ⱦ":"t","ꞇ":"t","ꜩ":"tz","ⓤ":"u","u":"u","ù":"u","ú":"u","û":"u","ũ":"u","ṹ":"u","ū":"u","ṻ":"u","ŭ":"u","ü":"u","ǜ":"u","ǘ":"u","ǖ":"u","ǚ":"u","ủ":"u","ů":"u","ű":"u","ǔ":"u","ȕ":"u","ȗ":"u","ư":"u","ừ":"u","ứ":"u","ữ":"u","ử":"u","ự":"u","ụ":"u","ṳ":"u","ų":"u","ṷ":"u","ṵ":"u","ʉ":"u","ⓥ":"v","v":"v","ṽ":"v","ṿ":"v","ʋ":"v","ꝟ":"v","ʌ":"v","ꝡ":"vy","ⓦ":"w","w":"w","ẁ":"w","ẃ":"w","ŵ":"w","ẇ":"w","ẅ":"w","ẘ":"w","ẉ":"w","ⱳ":"w","ⓧ":"x","x":"x","ẋ":"x","ẍ":"x","ⓨ":"y","y":"y","ỳ":"y","ý":"y","ŷ":"y","ỹ":"y","ȳ":"y","ẏ":"y","ÿ":"y","ỷ":"y","ẙ":"y","ỵ":"y","ƴ":"y","ɏ":"y","ỿ":"y","ⓩ":"z","z":"z","ź":"z","ẑ":"z","ż":"z","ž":"z","ẓ":"z","ẕ":"z","ƶ":"z","ȥ":"z","ɀ":"z","ⱬ":"z","ꝣ":"z","Ά":"Α","Έ":"Ε","Ή":"Η","Ί":"Ι","Ϊ":"Ι","Ό":"Ο","Ύ":"Υ","Ϋ":"Υ","Ώ":"Ω","ά":"α","έ":"ε","ή":"η","ί":"ι","ϊ":"ι","ΐ":"ι","ό":"ο","ύ":"υ","ϋ":"υ","ΰ":"υ","ω":"ω","ς":"σ"};return a}),b.define("select2/data/base",["../utils"],function(a){function b(a,c){b.__super__.constructor.call(this)}return a.Extend(b,a.Observable),b.prototype.current=function(a){throw new Error("The `current` method must be defined in child classes.")},b.prototype.query=function(a,b){throw new Error("The `query` method must be defined in child classes.")},b.prototype.bind=function(a,b){},b.prototype.destroy=function(){},b.prototype.generateResultId=function(b,c){var d=b.id+"-result-";return d+=a.generateChars(4),d+=null!=c.id?"-"+c.id.toString():"-"+a.generateChars(4)},b}),b.define("select2/data/select",["./base","../utils","jquery"],function(a,b,c){function d(a,b){this.$element=a,this.options=b,d.__super__.constructor.call(this)}return b.Extend(d,a),d.prototype.current=function(a){var b=[],d=this;this.$element.find(":selected").each(function(){var a=c(this),e=d.item(a);b.push(e)}),a(b)},d.prototype.select=function(a){var b=this;if(a.selected=!0,c(a.element).is("option"))return a.element.selected=!0,void this.$element.trigger("change");if(this.$element.prop("multiple"))this.current(function(d){var e=[];a=[a],a.push.apply(a,d);for(var f=0;f<a.length;f++){var g=a[f].id;-1===c.inArray(g,e)&&e.push(g)}b.$element.val(e),b.$element.trigger("change")});else{var d=a.id;this.$element.val(d),this.$element.trigger("change")}},d.prototype.unselect=function(a){var b=this;if(this.$element.prop("multiple"))return a.selected=!1,
|
| 2 |
+
c(a.element).is("option")?(a.element.selected=!1,void this.$element.trigger("change")):void this.current(function(d){for(var e=[],f=0;f<d.length;f++){var g=d[f].id;g!==a.id&&-1===c.inArray(g,e)&&e.push(g)}b.$element.val(e),b.$element.trigger("change")})},d.prototype.bind=function(a,b){var c=this;this.container=a,a.on("select",function(a){c.select(a.data)}),a.on("unselect",function(a){c.unselect(a.data)})},d.prototype.destroy=function(){this.$element.find("*").each(function(){c.removeData(this,"data")})},d.prototype.query=function(a,b){var d=[],e=this,f=this.$element.children();f.each(function(){var b=c(this);if(b.is("option")||b.is("optgroup")){var f=e.item(b),g=e.matches(a,f);null!==g&&d.push(g)}}),b({results:d})},d.prototype.addOptions=function(a){b.appendMany(this.$element,a)},d.prototype.option=function(a){var b;a.children?(b=document.createElement("optgroup"),b.label=a.text):(b=document.createElement("option"),void 0!==b.textContent?b.textContent=a.text:b.innerText=a.text),a.id&&(b.value=a.id),a.disabled&&(b.disabled=!0),a.selected&&(b.selected=!0),a.title&&(b.title=a.title);var d=c(b),e=this._normalizeItem(a);return e.element=b,c.data(b,"data",e),d},d.prototype.item=function(a){var b={};if(b=c.data(a[0],"data"),null!=b)return b;if(a.is("option"))b={id:a.val(),text:a.text(),disabled:a.prop("disabled"),selected:a.prop("selected"),title:a.prop("title")};else if(a.is("optgroup")){b={text:a.prop("label"),children:[],title:a.prop("title")};for(var d=a.children("option"),e=[],f=0;f<d.length;f++){var g=c(d[f]),h=this.item(g);e.push(h)}b.children=e}return b=this._normalizeItem(b),b.element=a[0],c.data(a[0],"data",b),b},d.prototype._normalizeItem=function(a){c.isPlainObject(a)||(a={id:a,text:a}),a=c.extend({},{text:""},a);var b={selected:!1,disabled:!1};return null!=a.id&&(a.id=a.id.toString()),null!=a.text&&(a.text=a.text.toString()),null==a._resultId&&a.id&&null!=this.container&&(a._resultId=this.generateResultId(this.container,a)),c.extend({},b,a)},d.prototype.matches=function(a,b){var c=this.options.get("matcher");return c(a,b)},d}),b.define("select2/data/array",["./select","../utils","jquery"],function(a,b,c){function d(a,b){var c=b.get("data")||[];d.__super__.constructor.call(this,a,b),this.addOptions(this.convertToOptions(c))}return b.Extend(d,a),d.prototype.select=function(a){var b=this.$element.find("option").filter(function(b,c){return c.value==a.id.toString()});0===b.length&&(b=this.option(a),this.addOptions(b)),d.__super__.select.call(this,a)},d.prototype.convertToOptions=function(a){function d(a){return function(){return c(this).val()==a.id}}for(var e=this,f=this.$element.find("option"),g=f.map(function(){return e.item(c(this)).id}).get(),h=[],i=0;i<a.length;i++){var j=this._normalizeItem(a[i]);if(c.inArray(j.id,g)>=0){var k=f.filter(d(j)),l=this.item(k),m=c.extend(!0,{},j,l),n=this.option(m);k.replaceWith(n)}else{var o=this.option(j);if(j.children){var p=this.convertToOptions(j.children);b.appendMany(o,p)}h.push(o)}}return h},d}),b.define("select2/data/ajax",["./array","../utils","jquery"],function(a,b,c){function d(a,b){this.ajaxOptions=this._applyDefaults(b.get("ajax")),null!=this.ajaxOptions.processResults&&(this.processResults=this.ajaxOptions.processResults),d.__super__.constructor.call(this,a,b)}return b.Extend(d,a),d.prototype._applyDefaults=function(a){var b={data:function(a){return c.extend({},a,{q:a.term})},transport:function(a,b,d){var e=c.ajax(a);return e.then(b),e.fail(d),e}};return c.extend({},b,a,!0)},d.prototype.processResults=function(a){return a},d.prototype.query=function(a,b){function d(){var d=f.transport(f,function(d){var f=e.processResults(d,a);e.options.get("debug")&&window.console&&console.error&&(f&&f.results&&c.isArray(f.results)||console.error("Select2: The AJAX results did not return an array in the `results` key of the response.")),b(f)},function(){e.trigger("results:message",{message:"errorLoading"})});e._request=d}var e=this;null!=this._request&&(c.isFunction(this._request.abort)&&this._request.abort(),this._request=null);var f=c.extend({type:"GET"},this.ajaxOptions);"function"==typeof f.url&&(f.url=f.url.call(this.$element,a)),"function"==typeof f.data&&(f.data=f.data.call(this.$element,a)),this.ajaxOptions.delay&&""!==a.term?(this._queryTimeout&&window.clearTimeout(this._queryTimeout),this._queryTimeout=window.setTimeout(d,this.ajaxOptions.delay)):d()},d}),b.define("select2/data/tags",["jquery"],function(a){function b(b,c,d){var e=d.get("tags"),f=d.get("createTag");void 0!==f&&(this.createTag=f);var g=d.get("insertTag");if(void 0!==g&&(this.insertTag=g),b.call(this,c,d),a.isArray(e))for(var h=0;h<e.length;h++){var i=e[h],j=this._normalizeItem(i),k=this.option(j);this.$element.append(k)}}return b.prototype.query=function(a,b,c){function d(a,f){for(var g=a.results,h=0;h<g.length;h++){var i=g[h],j=null!=i.children&&!d({results:i.children},!0),k=i.text===b.term;if(k||j)return f?!1:(a.data=g,void c(a))}if(f)return!0;var l=e.createTag(b);if(null!=l){var m=e.option(l);m.attr("data-select2-tag",!0),e.addOptions([m]),e.insertTag(g,l)}a.results=g,c(a)}var e=this;return this._removeOldTags(),null==b.term||null!=b.page?void a.call(this,b,c):void a.call(this,b,d)},b.prototype.createTag=function(b,c){var d=a.trim(c.term);return""===d?null:{id:d,text:d}},b.prototype.insertTag=function(a,b,c){b.unshift(c)},b.prototype._removeOldTags=function(b){var c=(this._lastTag,this.$element.find("option[data-select2-tag]"));c.each(function(){this.selected||a(this).remove()})},b}),b.define("select2/data/tokenizer",["jquery"],function(a){function b(a,b,c){var d=c.get("tokenizer");void 0!==d&&(this.tokenizer=d),a.call(this,b,c)}return b.prototype.bind=function(a,b,c){a.call(this,b,c),this.$search=b.dropdown.$search||b.selection.$search||c.find(".select2-search__field")},b.prototype.query=function(a,b,c){function d(a){e.trigger("select",{data:a})}var e=this;b.term=b.term||"";var f=this.tokenizer(b,this.options,d);f.term!==b.term&&(this.$search.length&&(this.$search.val(f.term),this.$search.focus()),b.term=f.term),a.call(this,b,c)},b.prototype.tokenizer=function(b,c,d,e){for(var f=d.get("tokenSeparators")||[],g=c.term,h=0,i=this.createTag||function(a){return{id:a.term,text:a.term}};h<g.length;){var j=g[h];if(-1!==a.inArray(j,f)){var k=g.substr(0,h),l=a.extend({},c,{term:k}),m=i(l);null!=m?(e(m),g=g.substr(h+1)||"",h=0):h++}else h++}return{term:g}},b}),b.define("select2/data/minimumInputLength",[],function(){function a(a,b,c){this.minimumInputLength=c.get("minimumInputLength"),a.call(this,b,c)}return a.prototype.query=function(a,b,c){return b.term=b.term||"",b.term.length<this.minimumInputLength?void this.trigger("results:message",{message:"inputTooShort",args:{minimum:this.minimumInputLength,input:b.term,params:b}}):void a.call(this,b,c)},a}),b.define("select2/data/maximumInputLength",[],function(){function a(a,b,c){this.maximumInputLength=c.get("maximumInputLength"),a.call(this,b,c)}return a.prototype.query=function(a,b,c){return b.term=b.term||"",this.maximumInputLength>0&&b.term.length>this.maximumInputLength?void this.trigger("results:message",{message:"inputTooLong",args:{maximum:this.maximumInputLength,input:b.term,params:b}}):void a.call(this,b,c)},a}),b.define("select2/data/maximumSelectionLength",[],function(){function a(a,b,c){this.maximumSelectionLength=c.get("maximumSelectionLength"),a.call(this,b,c)}return a.prototype.query=function(a,b,c){var d=this;this.current(function(e){var f=null!=e?e.length:0;return d.maximumSelectionLength>0&&f>=d.maximumSelectionLength?void d.trigger("results:message",{message:"maximumSelected",args:{maximum:d.maximumSelectionLength}}):void a.call(d,b,c)})},a}),b.define("select2/dropdown",["jquery","./utils"],function(a,b){function c(a,b){this.$element=a,this.options=b,c.__super__.constructor.call(this)}return b.Extend(c,b.Observable),c.prototype.render=function(){var b=a('<span class="select2-dropdown"><span class="select2-results"></span></span>');return b.attr("dir",this.options.get("dir")),this.$dropdown=b,b},c.prototype.bind=function(){},c.prototype.position=function(a,b){},c.prototype.destroy=function(){this.$dropdown.remove()},c}),b.define("select2/dropdown/search",["jquery","../utils"],function(a,b){function c(){}return c.prototype.render=function(b){var c=b.call(this),d=a('<span class="select2-search select2-search--dropdown"><input class="select2-search__field" type="search" tabindex="-1" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" role="textbox" /></span>');return this.$searchContainer=d,this.$search=d.find("input"),c.prepend(d),c},c.prototype.bind=function(b,c,d){var e=this;b.call(this,c,d),this.$search.on("keydown",function(a){e.trigger("keypress",a),e._keyUpPrevented=a.isDefaultPrevented()}),this.$search.on("input",function(b){a(this).off("keyup")}),this.$search.on("keyup input",function(a){e.handleSearch(a)}),c.on("open",function(){e.$search.attr("tabindex",0),e.$search.focus(),window.setTimeout(function(){e.$search.focus()},0)}),c.on("close",function(){e.$search.attr("tabindex",-1),e.$search.val("")}),c.on("results:all",function(a){if(null==a.query.term||""===a.query.term){var b=e.showSearch(a);b?e.$searchContainer.removeClass("select2-search--hide"):e.$searchContainer.addClass("select2-search--hide")}})},c.prototype.handleSearch=function(a){if(!this._keyUpPrevented){var b=this.$search.val();this.trigger("query",{term:b})}this._keyUpPrevented=!1},c.prototype.showSearch=function(a,b){return!0},c}),b.define("select2/dropdown/hidePlaceholder",[],function(){function a(a,b,c,d){this.placeholder=this.normalizePlaceholder(c.get("placeholder")),a.call(this,b,c,d)}return a.prototype.append=function(a,b){b.results=this.removePlaceholder(b.results),a.call(this,b)},a.prototype.normalizePlaceholder=function(a,b){return"string"==typeof b&&(b={id:"",text:b}),b},a.prototype.removePlaceholder=function(a,b){for(var c=b.slice(0),d=b.length-1;d>=0;d--){var e=b[d];this.placeholder.id===e.id&&c.splice(d,1)}return c},a}),b.define("select2/dropdown/infiniteScroll",["jquery"],function(a){function b(a,b,c,d){this.lastParams={},a.call(this,b,c,d),this.$loadingMore=this.createLoadingMore(),this.loading=!1}return b.prototype.append=function(a,b){this.$loadingMore.remove(),this.loading=!1,a.call(this,b),this.showLoadingMore(b)&&this.$results.append(this.$loadingMore)},b.prototype.bind=function(b,c,d){var e=this;b.call(this,c,d),c.on("query",function(a){e.lastParams=a,e.loading=!0}),c.on("query:append",function(a){e.lastParams=a,e.loading=!0}),this.$results.on("scroll",function(){var b=a.contains(document.documentElement,e.$loadingMore[0]);if(!e.loading&&b){var c=e.$results.offset().top+e.$results.outerHeight(!1),d=e.$loadingMore.offset().top+e.$loadingMore.outerHeight(!1);c+50>=d&&e.loadMore()}})},b.prototype.loadMore=function(){this.loading=!0;var b=a.extend({},{page:1},this.lastParams);b.page++,this.trigger("query:append",b)},b.prototype.showLoadingMore=function(a,b){return b.pagination&&b.pagination.more},b.prototype.createLoadingMore=function(){var b=a('<li class="select2-results__option select2-results__option--load-more"role="treeitem" aria-disabled="true"></li>'),c=this.options.get("translations").get("loadingMore");return b.html(c(this.lastParams)),b},b}),b.define("select2/dropdown/attachBody",["jquery","../utils"],function(a,b){function c(b,c,d){this.$dropdownParent=d.get("dropdownParent")||a(document.body),b.call(this,c,d)}return c.prototype.bind=function(a,b,c){var d=this,e=!1;a.call(this,b,c),b.on("open",function(){d._showDropdown(),d._attachPositioningHandler(b),e||(e=!0,b.on("results:all",function(){d._positionDropdown(),d._resizeDropdown()}),b.on("results:append",function(){d._positionDropdown(),d._resizeDropdown()}))}),b.on("close",function(){d._hideDropdown(),d._detachPositioningHandler(b)}),this.$dropdownContainer.on("mousedown",function(a){a.stopPropagation()})},c.prototype.destroy=function(a){a.call(this),this.$dropdownContainer.remove()},c.prototype.position=function(a,b,c){b.attr("class",c.attr("class")),b.removeClass("select2"),b.addClass("select2-container--open"),b.css({position:"absolute",top:-999999}),this.$container=c},c.prototype.render=function(b){var c=a("<span></span>"),d=b.call(this);return c.append(d),this.$dropdownContainer=c,c},c.prototype._hideDropdown=function(a){this.$dropdownContainer.detach()},c.prototype._attachPositioningHandler=function(c,d){var e=this,f="scroll.select2."+d.id,g="resize.select2."+d.id,h="orientationchange.select2."+d.id,i=this.$container.parents().filter(b.hasScroll);i.each(function(){a(this).data("select2-scroll-position",{x:a(this).scrollLeft(),y:a(this).scrollTop()})}),i.on(f,function(b){var c=a(this).data("select2-scroll-position");a(this).scrollTop(c.y)}),a(window).on(f+" "+g+" "+h,function(a){e._positionDropdown(),e._resizeDropdown()})},c.prototype._detachPositioningHandler=function(c,d){var e="scroll.select2."+d.id,f="resize.select2."+d.id,g="orientationchange.select2."+d.id,h=this.$container.parents().filter(b.hasScroll);h.off(e),a(window).off(e+" "+f+" "+g)},c.prototype._positionDropdown=function(){var b=a(window),c=this.$dropdown.hasClass("select2-dropdown--above"),d=this.$dropdown.hasClass("select2-dropdown--below"),e=null,f=this.$container.offset();f.bottom=f.top+this.$container.outerHeight(!1);var g={height:this.$container.outerHeight(!1)};g.top=f.top,g.bottom=f.top+g.height;var h={height:this.$dropdown.outerHeight(!1)},i={top:b.scrollTop(),bottom:b.scrollTop()+b.height()},j=i.top<f.top-h.height,k=i.bottom>f.bottom+h.height,l={left:f.left,top:g.bottom},m=this.$dropdownParent;"static"===m.css("position")&&(m=m.offsetParent());var n=m.offset();l.top-=n.top,l.left-=n.left,c||d||(e="below"),k||!j||c?!j&&k&&c&&(e="below"):e="above",("above"==e||c&&"below"!==e)&&(l.top=g.top-h.height),null!=e&&(this.$dropdown.removeClass("select2-dropdown--below select2-dropdown--above").addClass("select2-dropdown--"+e),this.$container.removeClass("select2-container--below select2-container--above").addClass("select2-container--"+e)),this.$dropdownContainer.css(l)},c.prototype._resizeDropdown=function(){var a={width:this.$container.outerWidth(!1)+"px"};this.options.get("dropdownAutoWidth")&&(a.minWidth=a.width,a.width="auto"),this.$dropdown.css(a)},c.prototype._showDropdown=function(a){this.$dropdownContainer.appendTo(this.$dropdownParent),this._positionDropdown(),this._resizeDropdown()},c}),b.define("select2/dropdown/minimumResultsForSearch",[],function(){function a(b){for(var c=0,d=0;d<b.length;d++){var e=b[d];e.children?c+=a(e.children):c++}return c}function b(a,b,c,d){this.minimumResultsForSearch=c.get("minimumResultsForSearch"),this.minimumResultsForSearch<0&&(this.minimumResultsForSearch=1/0),a.call(this,b,c,d)}return b.prototype.showSearch=function(b,c){return a(c.data.results)<this.minimumResultsForSearch?!1:b.call(this,c)},b}),b.define("select2/dropdown/selectOnClose",[],function(){function a(){}return a.prototype.bind=function(a,b,c){var d=this;a.call(this,b,c),b.on("close",function(){d._handleSelectOnClose()})},a.prototype._handleSelectOnClose=function(){var a=this.getHighlightedResults();if(!(a.length<1)){var b=a.data("data");null!=b.element&&b.element.selected||null==b.element&&b.selected||this.trigger("select",{data:b})}},a}),b.define("select2/dropdown/closeOnSelect",[],function(){function a(){}return a.prototype.bind=function(a,b,c){var d=this;a.call(this,b,c),b.on("select",function(a){d._selectTriggered(a)}),b.on("unselect",function(a){d._selectTriggered(a)})},a.prototype._selectTriggered=function(a,b){var c=b.originalEvent;c&&c.ctrlKey||this.trigger("close",{})},a}),b.define("select2/i18n/en",[],function(){return{errorLoading:function(){return"The results could not be loaded."},inputTooLong:function(a){var b=a.input.length-a.maximum,c="Please delete "+b+" character";return 1!=b&&(c+="s"),c},inputTooShort:function(a){var b=a.minimum-a.input.length,c="Please enter "+b+" or more characters";return c},loadingMore:function(){return"Loading more results…"},maximumSelected:function(a){var b="You can only select "+a.maximum+" item";return 1!=a.maximum&&(b+="s"),b},noResults:function(){return"No results found"},searching:function(){return"Searching…"}}}),b.define("select2/defaults",["jquery","require","./results","./selection/single","./selection/multiple","./selection/placeholder","./selection/allowClear","./selection/search","./selection/eventRelay","./utils","./translation","./diacritics","./data/select","./data/array","./data/ajax","./data/tags","./data/tokenizer","./data/minimumInputLength","./data/maximumInputLength","./data/maximumSelectionLength","./dropdown","./dropdown/search","./dropdown/hidePlaceholder","./dropdown/infiniteScroll","./dropdown/attachBody","./dropdown/minimumResultsForSearch","./dropdown/selectOnClose","./dropdown/closeOnSelect","./i18n/en"],function(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z,A,B,C){function D(){this.reset()}D.prototype.apply=function(l){if(l=a.extend(!0,{},this.defaults,l),null==l.dataAdapter){if(null!=l.ajax?l.dataAdapter=o:null!=l.data?l.dataAdapter=n:l.dataAdapter=m,l.minimumInputLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,r)),l.maximumInputLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,s)),l.maximumSelectionLength>0&&(l.dataAdapter=j.Decorate(l.dataAdapter,t)),l.tags&&(l.dataAdapter=j.Decorate(l.dataAdapter,p)),(null!=l.tokenSeparators||null!=l.tokenizer)&&(l.dataAdapter=j.Decorate(l.dataAdapter,q)),null!=l.query){var C=b(l.amdBase+"compat/query");l.dataAdapter=j.Decorate(l.dataAdapter,C)}if(null!=l.initSelection){var D=b(l.amdBase+"compat/initSelection");l.dataAdapter=j.Decorate(l.dataAdapter,D)}}if(null==l.resultsAdapter&&(l.resultsAdapter=c,null!=l.ajax&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,x)),null!=l.placeholder&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,w)),l.selectOnClose&&(l.resultsAdapter=j.Decorate(l.resultsAdapter,A))),null==l.dropdownAdapter){if(l.multiple)l.dropdownAdapter=u;else{var E=j.Decorate(u,v);l.dropdownAdapter=E}if(0!==l.minimumResultsForSearch&&(l.dropdownAdapter=j.Decorate(l.dropdownAdapter,z)),l.closeOnSelect&&(l.dropdownAdapter=j.Decorate(l.dropdownAdapter,B)),null!=l.dropdownCssClass||null!=l.dropdownCss||null!=l.adaptDropdownCssClass){var F=b(l.amdBase+"compat/dropdownCss");l.dropdownAdapter=j.Decorate(l.dropdownAdapter,F)}l.dropdownAdapter=j.Decorate(l.dropdownAdapter,y)}if(null==l.selectionAdapter){if(l.multiple?l.selectionAdapter=e:l.selectionAdapter=d,null!=l.placeholder&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,f)),l.allowClear&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,g)),l.multiple&&(l.selectionAdapter=j.Decorate(l.selectionAdapter,h)),null!=l.containerCssClass||null!=l.containerCss||null!=l.adaptContainerCssClass){var G=b(l.amdBase+"compat/containerCss");l.selectionAdapter=j.Decorate(l.selectionAdapter,G)}l.selectionAdapter=j.Decorate(l.selectionAdapter,i)}if("string"==typeof l.language)if(l.language.indexOf("-")>0){var H=l.language.split("-"),I=H[0];l.language=[l.language,I]}else l.language=[l.language];if(a.isArray(l.language)){var J=new k;l.language.push("en");for(var K=l.language,L=0;L<K.length;L++){var M=K[L],N={};try{N=k.loadPath(M)}catch(O){try{M=this.defaults.amdLanguageBase+M,N=k.loadPath(M)}catch(P){l.debug&&window.console&&console.warn&&console.warn('Select2: The language file for "'+M+'" could not be automatically loaded. A fallback will be used instead.');continue}}J.extend(N)}l.translations=J}else{var Q=k.loadPath(this.defaults.amdLanguageBase+"en"),R=new k(l.language);R.extend(Q),l.translations=R}return l},D.prototype.reset=function(){function b(a){function b(a){return l[a]||a}return a.replace(/[^\u0000-\u007E]/g,b)}function c(d,e){if(""===a.trim(d.term))return e;if(e.children&&e.children.length>0){for(var f=a.extend(!0,{},e),g=e.children.length-1;g>=0;g--){var h=e.children[g],i=c(d,h);null==i&&f.children.splice(g,1)}return f.children.length>0?f:c(d,f)}var j=b(e.text).toUpperCase(),k=b(d.term).toUpperCase();return j.indexOf(k)>-1?e:null}this.defaults={amdBase:"./",amdLanguageBase:"./i18n/",closeOnSelect:!0,debug:!1,dropdownAutoWidth:!1,escapeMarkup:j.escapeMarkup,language:C,matcher:c,minimumInputLength:0,maximumInputLength:0,maximumSelectionLength:0,minimumResultsForSearch:0,selectOnClose:!1,sorter:function(a){return a},templateResult:function(a){return a.text},templateSelection:function(a){return a.text},theme:"default",width:"resolve"}},D.prototype.set=function(b,c){var d=a.camelCase(b),e={};e[d]=c;var f=j._convertData(e);a.extend(this.defaults,f)};var E=new D;return E}),b.define("select2/options",["require","jquery","./defaults","./utils"],function(a,b,c,d){function e(b,e){if(this.options=b,null!=e&&this.fromElement(e),this.options=c.apply(this.options),e&&e.is("input")){var f=a(this.get("amdBase")+"compat/inputData");this.options.dataAdapter=d.Decorate(this.options.dataAdapter,f)}}return e.prototype.fromElement=function(a){var c=["select2"];null==this.options.multiple&&(this.options.multiple=a.prop("multiple")),null==this.options.disabled&&(this.options.disabled=a.prop("disabled")),null==this.options.language&&(a.prop("lang")?this.options.language=a.prop("lang").toLowerCase():a.closest("[lang]").prop("lang")&&(this.options.language=a.closest("[lang]").prop("lang"))),null==this.options.dir&&(a.prop("dir")?this.options.dir=a.prop("dir"):a.closest("[dir]").prop("dir")?this.options.dir=a.closest("[dir]").prop("dir"):this.options.dir="ltr"),a.prop("disabled",this.options.disabled),a.prop("multiple",this.options.multiple),a.data("select2Tags")&&(this.options.debug&&window.console&&console.warn&&console.warn('Select2: The `data-select2-tags` attribute has been changed to use the `data-data` and `data-tags="true"` attributes and will be removed in future versions of Select2.'),a.data("data",a.data("select2Tags")),a.data("tags",!0)),a.data("ajaxUrl")&&(this.options.debug&&window.console&&console.warn&&console.warn("Select2: The `data-ajax-url` attribute has been changed to `data-ajax--url` and support for the old attribute will be removed in future versions of Select2."),a.attr("ajax--url",a.data("ajaxUrl")),a.data("ajax--url",a.data("ajaxUrl")));var e={};e=b.fn.jquery&&"1."==b.fn.jquery.substr(0,2)&&a[0].dataset?b.extend(!0,{},a[0].dataset,a.data()):a.data();var f=b.extend(!0,{},e);f=d._convertData(f);for(var g in f)b.inArray(g,c)>-1||(b.isPlainObject(this.options[g])?b.extend(this.options[g],f[g]):this.options[g]=f[g]);return this},e.prototype.get=function(a){return this.options[a]},e.prototype.set=function(a,b){this.options[a]=b},e}),b.define("select2/core",["jquery","./options","./utils","./keys"],function(a,b,c,d){var e=function(a,c){null!=a.data("select2")&&a.data("select2").destroy(),this.$element=a,this.id=this._generateId(a),c=c||{},this.options=new b(c,a),e.__super__.constructor.call(this);var d=a.attr("tabindex")||0;a.data("old-tabindex",d),a.attr("tabindex","-1");var f=this.options.get("dataAdapter");this.dataAdapter=new f(a,this.options);var g=this.render();this._placeContainer(g);var h=this.options.get("selectionAdapter");this.selection=new h(a,this.options),this.$selection=this.selection.render(),this.selection.position(this.$selection,g);var i=this.options.get("dropdownAdapter");this.dropdown=new i(a,this.options),this.$dropdown=this.dropdown.render(),this.dropdown.position(this.$dropdown,g);var j=this.options.get("resultsAdapter");this.results=new j(a,this.options,this.dataAdapter),this.$results=this.results.render(),this.results.position(this.$results,this.$dropdown);var k=this;this._bindAdapters(),this._registerDomEvents(),this._registerDataEvents(),this._registerSelectionEvents(),this._registerDropdownEvents(),this._registerResultsEvents(),this._registerEvents(),this.dataAdapter.current(function(a){k.trigger("selection:update",{data:a})}),a.addClass("select2-hidden-accessible"),a.attr("aria-hidden","true"),this._syncAttributes(),a.data("select2",this)};return c.Extend(e,c.Observable),e.prototype._generateId=function(a){var b="";return b=null!=a.attr("id")?a.attr("id"):null!=a.attr("name")?a.attr("name")+"-"+c.generateChars(2):c.generateChars(4),b=b.replace(/(:|\.|\[|\]|,)/g,""),b="select2-"+b},e.prototype._placeContainer=function(a){a.insertAfter(this.$element);var b=this._resolveWidth(this.$element,this.options.get("width"));null!=b&&a.css("width",b)},e.prototype._resolveWidth=function(a,b){var c=/^width:(([-+]?([0-9]*\.)?[0-9]+)(px|em|ex|%|in|cm|mm|pt|pc))/i;if("resolve"==b){var d=this._resolveWidth(a,"style");return null!=d?d:this._resolveWidth(a,"element")}if("element"==b){var e=a.outerWidth(!1);return 0>=e?"auto":e+"px"}if("style"==b){var f=a.attr("style");if("string"!=typeof f)return null;for(var g=f.split(";"),h=0,i=g.length;i>h;h+=1){var j=g[h].replace(/\s/g,""),k=j.match(c);if(null!==k&&k.length>=1)return k[1]}return null}return b},e.prototype._bindAdapters=function(){this.dataAdapter.bind(this,this.$container),this.selection.bind(this,this.$container),this.dropdown.bind(this,this.$container),this.results.bind(this,this.$container)},e.prototype._registerDomEvents=function(){var b=this;this.$element.on("change.select2",function(){b.dataAdapter.current(function(a){b.trigger("selection:update",{data:a})})}),this._sync=c.bind(this._syncAttributes,this),this.$element[0].attachEvent&&this.$element[0].attachEvent("onpropertychange",this._sync);var d=window.MutationObserver||window.WebKitMutationObserver||window.MozMutationObserver;null!=d?(this._observer=new d(function(c){a.each(c,b._sync)}),this._observer.observe(this.$element[0],{attributes:!0,subtree:!1})):this.$element[0].addEventListener&&this.$element[0].addEventListener("DOMAttrModified",b._sync,!1)},e.prototype._registerDataEvents=function(){var a=this;this.dataAdapter.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerSelectionEvents=function(){var b=this,c=["toggle","focus"];this.selection.on("toggle",function(){b.toggleDropdown()}),this.selection.on("focus",function(a){b.focus(a)}),this.selection.on("*",function(d,e){-1===a.inArray(d,c)&&b.trigger(d,e)})},e.prototype._registerDropdownEvents=function(){var a=this;this.dropdown.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerResultsEvents=function(){var a=this;this.results.on("*",function(b,c){a.trigger(b,c)})},e.prototype._registerEvents=function(){var a=this;this.on("open",function(){a.$container.addClass("select2-container--open")}),this.on("close",function(){a.$container.removeClass("select2-container--open")}),this.on("enable",function(){a.$container.removeClass("select2-container--disabled")}),this.on("disable",function(){a.$container.addClass("select2-container--disabled")}),this.on("blur",function(){a.$container.removeClass("select2-container--focus")}),this.on("query",function(b){a.isOpen()||a.trigger("open",{}),this.dataAdapter.query(b,function(c){a.trigger("results:all",{data:c,query:b})})}),this.on("query:append",function(b){this.dataAdapter.query(b,function(c){a.trigger("results:append",{data:c,query:b})})}),this.on("keypress",function(b){var c=b.which;a.isOpen()?c===d.ESC||c===d.TAB||c===d.UP&&b.altKey?(a.close(),b.preventDefault()):c===d.ENTER?(a.trigger("results:select",{}),b.preventDefault()):c===d.SPACE&&b.ctrlKey?(a.trigger("results:toggle",{}),b.preventDefault()):c===d.UP?(a.trigger("results:previous",{}),b.preventDefault()):c===d.DOWN&&(a.trigger("results:next",{}),b.preventDefault()):(c===d.ENTER||c===d.SPACE||c===d.DOWN&&b.altKey)&&(a.open(),b.preventDefault())})},e.prototype._syncAttributes=function(){this.options.set("disabled",this.$element.prop("disabled")),this.options.get("disabled")?(this.isOpen()&&this.close(),this.trigger("disable",{})):this.trigger("enable",{})},e.prototype.trigger=function(a,b){var c=e.__super__.trigger,d={open:"opening",close:"closing",select:"selecting",unselect:"unselecting"};if(void 0===b&&(b={}),a in d){var f=d[a],g={prevented:!1,name:a,args:b};if(c.call(this,f,g),g.prevented)return void(b.prevented=!0)}c.call(this,a,b)},e.prototype.toggleDropdown=function(){this.options.get("disabled")||(this.isOpen()?this.close():this.open())},e.prototype.open=function(){this.isOpen()||this.trigger("query",{})},e.prototype.close=function(){this.isOpen()&&this.trigger("close",{})},e.prototype.isOpen=function(){return this.$container.hasClass("select2-container--open")},e.prototype.hasFocus=function(){return this.$container.hasClass("select2-container--focus")},e.prototype.focus=function(a){this.hasFocus()||(this.$container.addClass("select2-container--focus"),this.trigger("focus",{}))},e.prototype.enable=function(a){this.options.get("debug")&&window.console&&console.warn&&console.warn('Select2: The `select2("enable")` method has been deprecated and will be removed in later Select2 versions. Use $element.prop("disabled") instead.'),(null==a||0===a.length)&&(a=[!0]);var b=!a[0];this.$element.prop("disabled",b)},e.prototype.data=function(){this.options.get("debug")&&arguments.length>0&&window.console&&console.warn&&console.warn('Select2: Data can no longer be set using `select2("data")`. You should consider setting the value instead using `$element.val()`.');var a=[];return this.dataAdapter.current(function(b){a=b}),a},e.prototype.val=function(b){if(this.options.get("debug")&&window.console&&console.warn&&console.warn('Select2: The `select2("val")` method has been deprecated and will be removed in later Select2 versions. Use $element.val() instead.'),null==b||0===b.length)return this.$element.val();var c=b[0];a.isArray(c)&&(c=a.map(c,function(a){return a.toString()})),this.$element.val(c).trigger("change")},e.prototype.destroy=function(){this.$container.remove(),this.$element[0].detachEvent&&this.$element[0].detachEvent("onpropertychange",this._sync),null!=this._observer?(this._observer.disconnect(),this._observer=null):this.$element[0].removeEventListener&&this.$element[0].removeEventListener("DOMAttrModified",this._sync,!1),this._sync=null,this.$element.off(".select2"),this.$element.attr("tabindex",this.$element.data("old-tabindex")),this.$element.removeClass("select2-hidden-accessible"),this.$element.attr("aria-hidden","false"),this.$element.removeData("select2"),this.dataAdapter.destroy(),this.selection.destroy(),this.dropdown.destroy(),this.results.destroy(),this.dataAdapter=null,this.selection=null,this.dropdown=null,this.results=null},e.prototype.render=function(){var b=a('<span class="select2 select2-container"><span class="selection"></span><span class="dropdown-wrapper" aria-hidden="true"></span></span>');return b.attr("dir",this.options.get("dir")),this.$container=b,this.$container.addClass("select2-container--"+this.options.get("theme")),b.data("element",this.$element),b},e}),b.define("jquery-mousewheel",["jquery"],function(a){return a}),b.define("jquery.select2",["jquery","jquery-mousewheel","./select2/core","./select2/defaults"],function(a,b,c,d){if(null==a.fn.select2){var e=["open","close","destroy"];a.fn.select2=function(b){if(b=b||{},"object"==typeof b)return this.each(function(){var d=a.extend(!0,{},b);new c(a(this),d)}),this;if("string"==typeof b){var d;return this.each(function(){var c=a(this).data("select2");null==c&&window.console&&console.error&&console.error("The select2('"+b+"') method was called on an element that is not using Select2.");var e=Array.prototype.slice.call(arguments,1);d=c[b].apply(c,e)}),a.inArray(b,e)>-1?this:d}throw new Error("Invalid arguments for Select2: "+b)}}return null==a.fn.select2.defaults&&(a.fn.select2.defaults=d),c}),{define:b.define,require:b.require}}(),c=b.require("jquery.select2");return a.fn.select2.amd=b,c});
|
|
@@ -0,0 +1,218 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
jQuery(function($) {
|
| 2 |
+
|
| 3 |
+
/**
|
| 4 |
+
* Check if the user requires an OTP field and if so, display it
|
| 5 |
+
*
|
| 6 |
+
* @param String form - DOM selector string
|
| 7 |
+
*
|
| 8 |
+
* @uses show_otp_field()
|
| 9 |
+
*
|
| 10 |
+
* @return Boolean - true if we got involved
|
| 11 |
+
*/
|
| 12 |
+
function check_and_possibly_show_otp_field(form) {
|
| 13 |
+
|
| 14 |
+
// If this is a "lost password" form, then exit
|
| 15 |
+
if ($(form).attr('id') === 'lostpasswordform' || $(form).attr('id') === 'resetpasswordform') return false;
|
| 16 |
+
|
| 17 |
+
// 'username' is used by WooCommerce
|
| 18 |
+
var username = $(form).find('[name="log"], [name="username"], #user_login, #affwp-login-user-login, #affwp-user-login').first().val();
|
| 19 |
+
|
| 20 |
+
if (!username.length) return false;
|
| 21 |
+
|
| 22 |
+
var $submit_button = $(form).find('input[name="wp-submit"], input[type="submit"], button[type="submit"]').first();
|
| 23 |
+
|
| 24 |
+
if (simba_tfasettings.hasOwnProperty('spinnerimg')) {
|
| 25 |
+
var styling = 'float:right; margin:6px 12px; width: 20px; height: 20px;';
|
| 26 |
+
if ($('#theme-my-login #wp-submit').length >0) {
|
| 27 |
+
styling = 'margin-left: 4px; position: relative; top: 4px; width: 20px; height: 20px; border:0px; box-shadow:none;';
|
| 28 |
+
}
|
| 29 |
+
$submit_button.after('<img class="simbaotp_spinner" src="'+simba_tfasettings.spinnerimg+'" style="'+styling+'">');
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
$.ajax({
|
| 33 |
+
url: simba_tfasettings.ajaxurl,
|
| 34 |
+
type: 'POST',
|
| 35 |
+
data: {
|
| 36 |
+
action: 'simbatfa-init-otp',
|
| 37 |
+
user: username
|
| 38 |
+
},
|
| 39 |
+
dataType: 'text',
|
| 40 |
+
success: function(resp) {
|
| 41 |
+
try {
|
| 42 |
+
var json_begins = resp.search('{"jsonstarter":"justhere"');
|
| 43 |
+
if (json_begins > -1) {
|
| 44 |
+
if (json_begins > 0) {
|
| 45 |
+
console.log("Expected JSON marker found at position: "+json_begins);
|
| 46 |
+
resp = resp.substring(json_begins);
|
| 47 |
+
}
|
| 48 |
+
} else {
|
| 49 |
+
console.log("Expected JSON marker not found");
|
| 50 |
+
console.log(resp);
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
response = JSON.parse(resp);
|
| 54 |
+
|
| 55 |
+
if (response.hasOwnProperty('php_output')) {
|
| 56 |
+
console.log("PHP output was returned (follows)");
|
| 57 |
+
console.log(response.php_output);
|
| 58 |
+
}
|
| 59 |
+
|
| 60 |
+
if (response.hasOwnProperty('extra_output')) {
|
| 61 |
+
console.log("Extra output was returned (follows)");
|
| 62 |
+
console.log(response.extra_output);
|
| 63 |
+
}
|
| 64 |
+
|
| 65 |
+
if (true === response.status) {
|
| 66 |
+
// Don't bother to remove the spinner if the form is being submitted.
|
| 67 |
+
$('.simbaotp_spinner').remove();
|
| 68 |
+
|
| 69 |
+
var user_can_trust = (response.hasOwnProperty('user_can_trust') && response.user_can_trust) ? true : false;
|
| 70 |
+
|
| 71 |
+
var user_already_trusted = (response.hasOwnProperty('user_already_trusted') && response.user_can_trust) ? true : false;
|
| 72 |
+
|
| 73 |
+
console.log("Simba TFA: User has OTP enabled: showing OTP field (user_can_trust="+user_can_trust+")");
|
| 74 |
+
|
| 75 |
+
show_otp_field(form, user_can_trust, user_already_trusted);
|
| 76 |
+
|
| 77 |
+
} else {
|
| 78 |
+
console.log("Simba TFA: User does not have OTP enabled: submitting form");
|
| 79 |
+
|
| 80 |
+
// For some reason, .submit() stopped working with TML 7.x. N.B. Used to do this only for form_type == 2 ("TML shortcode or widget, WP Members, bbPress, Ultimate Membership Pro, WooCommerce or Elementor login form")
|
| 81 |
+
$(form).find('input[type="submit"], button[type="submit"]').first().trigger('click');
|
| 82 |
+
// $('#wp-submit').parents('form').first().trigger('submit');
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
} catch(err) {
|
| 86 |
+
$('#login').html(resp);
|
| 87 |
+
console.log("Simba TFA: Error when processing response");
|
| 88 |
+
console.log(err);
|
| 89 |
+
console.log(resp);
|
| 90 |
+
}
|
| 91 |
+
},
|
| 92 |
+
error: function(jq_xhr, text_status, error_thrown) {
|
| 93 |
+
console.log("Simba TFA: AJAX error: "+error_thrown+": "+text_status);
|
| 94 |
+
console.log(jq_xhr);
|
| 95 |
+
if (jq_xhr.hasOwnProperty('responseText')) {
|
| 96 |
+
console.log(jq_xhr.responseText);
|
| 97 |
+
}
|
| 98 |
+
}
|
| 99 |
+
});
|
| 100 |
+
return true;
|
| 101 |
+
}
|
| 102 |
+
|
| 103 |
+
// Parameters: see check_and_possibly_show_otp_field
|
| 104 |
+
function show_otp_field(form, user_can_trust, user_already_trusted) {
|
| 105 |
+
|
| 106 |
+
var $submit_button;
|
| 107 |
+
|
| 108 |
+
user_can_trust = ('undefined' == typeof user_can_trust) ? false : user_can_trust;
|
| 109 |
+
user_already_trusted = ('undefined' == typeof user_already_trusted) ? false : user_already_trusted;
|
| 110 |
+
|
| 111 |
+
// name="Submit" is WP-Members. 'submit' is Theme My Login starting from 7.x
|
| 112 |
+
$submit_button = $(form).find('input[name="wp-submit"], input[name="Submit"], input[name="submit"]');
|
| 113 |
+
// This hasn't been needed for anything yet (Jul 2018), but is a decent back-stop that would have prevented some breakage in the past that needed manual attention:
|
| 114 |
+
if (0 == $submit_button.length) {
|
| 115 |
+
$submit_button = $(form).find('input[type="submit"], button[type="submit"]').first();
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
// Hide all elements in a browser-safe way
|
| 119 |
+
// .user-pass-wrap is the wrapper used (instead of a paragraph) on wp-login.php from WP 5.3
|
| 120 |
+
$submit_button.parents('form').first().find('p, .impu-form-line-fr, .tml-field-wrap, .user-pass-wrap, .elementor-field-type-text, .elementor-field-type-submit, .elementor-remember-me, .bbp-username, .bbp-password, .bbp-submit-wrapper').each(function(i) {
|
| 121 |
+
$(this).css('visibility', 'hidden').css('position', 'absolute');
|
| 122 |
+
// On the WooCommerce form, the 'required' asterisk in the child <span> still shows without this
|
| 123 |
+
$(this).find('span').css('visibility', 'hidden').css('position', 'absolute');
|
| 124 |
+
});
|
| 125 |
+
|
| 126 |
+
// WP-Members
|
| 127 |
+
$submit_button.parents('#wpmem_login').find('fieldset').css('visibility', 'hidden').css('position', 'absolute');
|
| 128 |
+
|
| 129 |
+
// Add new field and controls
|
| 130 |
+
var html = '';
|
| 131 |
+
|
| 132 |
+
html += '<label for="simba_two_factor_auth">' + simba_tfasettings.otp + '<br><input type="text" name="two_factor_code" id="simba_two_factor_auth" autocomplete="off" data-lpignore="true"';
|
| 133 |
+
|
| 134 |
+
if ($(form).hasClass('woocommerce-form-login')) {
|
| 135 |
+
// Retain compatibility with previous full-width layout
|
| 136 |
+
html += ' style="width: 100%;"';
|
| 137 |
+
}
|
| 138 |
+
|
| 139 |
+
html += '></label>';
|
| 140 |
+
|
| 141 |
+
html += '<p class="forgetmenot" style="font-size:small;';
|
| 142 |
+
|
| 143 |
+
if (!$(form).hasClass('woocommerce-form-login')) {
|
| 144 |
+
// Retain compatibility with previous full-width layout
|
| 145 |
+
html += ' max-width: 60%;';
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
html += '">'+simba_tfasettings.otp_login_help;
|
| 149 |
+
|
| 150 |
+
if (user_can_trust && ('https:' == window.location.protocol || 'localhost' === location.hostname || '127.0.0.1' === location.hostname)) {
|
| 151 |
+
|
| 152 |
+
html += '<br><input type="checkbox" name="simba_tfa_mark_as_trusted" id="simba_tfa_mark_as_trusted" value="1"><label for="simba_tfa_mark_as_trusted">'+ simba_tfasettings.mark_as_trusted+'</label>';
|
| 153 |
+
|
| 154 |
+
} else {
|
| 155 |
+
user_already_trusted = false;
|
| 156 |
+
}
|
| 157 |
+
|
| 158 |
+
html += '</p>';
|
| 159 |
+
|
| 160 |
+
var submit_button_text;
|
| 161 |
+
var submit_button_name;
|
| 162 |
+
|
| 163 |
+
if ('button' == $submit_button.prop('nodeName').toLowerCase()) {
|
| 164 |
+
submit_button_text = $submit_button.text().trim();
|
| 165 |
+
submit_button_name = $submit_button.attr('name');
|
| 166 |
+
} else {
|
| 167 |
+
submit_button_text = $submit_button.val();
|
| 168 |
+
submit_button_name = $submit_button.attr('name');
|
| 169 |
+
}
|
| 170 |
+
|
| 171 |
+
html += '<p class="submit"><input id="tfa_login_btn" class="button button-primary button-large" type="submit" ';
|
| 172 |
+
|
| 173 |
+
if ('undefined' !== typeof submit_button_name && '' != submit_button_name) { html += 'name="'+submit_button_name+'" '; }
|
| 174 |
+
|
| 175 |
+
html += 'value="' + submit_button_text + '"></p>';
|
| 176 |
+
|
| 177 |
+
$submit_button.prop('disabled', true);
|
| 178 |
+
|
| 179 |
+
$submit_button.parents('form').first().prepend(html);
|
| 180 |
+
$('#simba_two_factor_auth').trigger('focus');
|
| 181 |
+
|
| 182 |
+
if (user_can_trust && user_already_trusted) {
|
| 183 |
+
$('#simba_two_factor_auth').val(simba_tfasettings.is_trusted);
|
| 184 |
+
$('#tfa_login_btn').trigger('click');
|
| 185 |
+
}
|
| 186 |
+
|
| 187 |
+
}
|
| 188 |
+
|
| 189 |
+
/**
|
| 190 |
+
* This function gets attached to a form submission handler and decides whether to add an OTP field or not.
|
| 191 |
+
*
|
| 192 |
+
* @param Object e - submission event
|
| 193 |
+
*
|
| 194 |
+
* @return Boolean - whether to proceed with the submission or not
|
| 195 |
+
*/
|
| 196 |
+
var form_submit_handler = function(e) {
|
| 197 |
+
|
| 198 |
+
console.log('Simba TFA: form submit request');
|
| 199 |
+
|
| 200 |
+
var form = e.target;
|
| 201 |
+
$(form).off();
|
| 202 |
+
|
| 203 |
+
if (check_and_possibly_show_otp_field(form)) {
|
| 204 |
+
e.preventDefault();
|
| 205 |
+
return false;
|
| 206 |
+
}
|
| 207 |
+
|
| 208 |
+
return true;
|
| 209 |
+
|
| 210 |
+
};
|
| 211 |
+
|
| 212 |
+
if (simba_tfasettings.login_form_off_selectors) {
|
| 213 |
+
$(simba_tfasettings.login_form_off_selectors).off('submit');
|
| 214 |
+
}
|
| 215 |
+
|
| 216 |
+
$(simba_tfasettings.login_form_selectors).on('submit', form_submit_handler);
|
| 217 |
+
|
| 218 |
+
});
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,204 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
if (!defined('ABSPATH')) die('Access denied.');
|
| 3 |
+
|
| 4 |
+
class Simba_TFA_Frontend {
|
| 5 |
+
|
| 6 |
+
private $mother;
|
| 7 |
+
|
| 8 |
+
/**
|
| 9 |
+
* Class constructor
|
| 10 |
+
*
|
| 11 |
+
* @param Object $mother
|
| 12 |
+
*/
|
| 13 |
+
public function __construct($mother) {
|
| 14 |
+
|
| 15 |
+
$this->mother = $mother;
|
| 16 |
+
add_action('wp_ajax_tfa_frontend', array($this, 'ajax'));
|
| 17 |
+
add_shortcode('twofactor_user_settings', array($this, 'tfa_user_settings_front'));
|
| 18 |
+
}
|
| 19 |
+
|
| 20 |
+
/**
|
| 21 |
+
* Runs upon the WP action wp_ajax_tfa_frontend
|
| 22 |
+
*
|
| 23 |
+
* @uses die()
|
| 24 |
+
*/
|
| 25 |
+
public function ajax() {
|
| 26 |
+
$totp_controller = $this->mother->get_totp_controller();
|
| 27 |
+
global $current_user;
|
| 28 |
+
|
| 29 |
+
$return_array = array();
|
| 30 |
+
|
| 31 |
+
if (empty($_POST) || empty($_POST['subaction']) || !isset($_POST['nonce']) || !is_user_logged_in() || !wp_verify_nonce($_POST['nonce'], 'tfa_frontend_nonce')) die('Security check');
|
| 32 |
+
|
| 33 |
+
if ('savesettings' == $_POST['subaction']) {
|
| 34 |
+
if (empty($_POST['settings']) || !is_string($_POST['settings'])) die;
|
| 35 |
+
|
| 36 |
+
parse_str(stripslashes($_POST['settings']), $posted_settings);
|
| 37 |
+
|
| 38 |
+
if (isset($posted_settings['tfa_algorithm_type'])) {
|
| 39 |
+
$old_algorithm = $totp_controller->get_user_otp_algorithm($current_user->ID);
|
| 40 |
+
|
| 41 |
+
if ($old_algorithm != $posted_settings['tfa_algorithm_type'])
|
| 42 |
+
$totp_controller->changeUserAlgorithmTo($current_user->ID, $posted_settings['tfa_algorithm_type']);
|
| 43 |
+
|
| 44 |
+
//Re-fetch the algorithm type, url and private string
|
| 45 |
+
$variables = $this->tfa_fetch_assort_vars();
|
| 46 |
+
|
| 47 |
+
$return_array['qr'] = $totp_controller->tfa_qr_code_url($variables['algorithm_type'], $variables['url'], $variables['tfa_priv_key']);
|
| 48 |
+
$return_array['al_type_disp'] = $this->tfa_algorithm_info($variables['algorithm_type']);
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
if (isset($posted_settings['tfa_enable_tfa'])) {
|
| 52 |
+
|
| 53 |
+
$allow_enable_or_disable = false;
|
| 54 |
+
|
| 55 |
+
if (empty($posted_settings['require_current']) || !$posted_settings['tfa_enable_tfa']) {
|
| 56 |
+
$allow_enable_or_disable = true;
|
| 57 |
+
} else {
|
| 58 |
+
|
| 59 |
+
if (!isset($posted_settings['tfa_enable_current']) || '' == $posted_settings['tfa_enable_current']) {
|
| 60 |
+
$return_array['message'] = __('To enable TFA, you must enter the current code.', 'all-in-one-wp-security-and-firewall');
|
| 61 |
+
$return_array['error'] = 'code_absent';
|
| 62 |
+
} else {
|
| 63 |
+
// Third parameter: don't allow emergency codes
|
| 64 |
+
if ($totp_controller->check_code_for_user($current_user->ID, $posted_settings['tfa_enable_current'], false)) {
|
| 65 |
+
$allow_enable_or_disable = true;
|
| 66 |
+
} else {
|
| 67 |
+
$return_array['error'] = 'code_wrong';
|
| 68 |
+
$return_array['message'] = __('The TFA code you entered was incorrect.', 'all-in-one-wp-security-and-firewall');
|
| 69 |
+
}
|
| 70 |
+
}
|
| 71 |
+
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
if ($allow_enable_or_disable) $this->mother->change_tfa_enabled_status($current_user->ID, $posted_settings['tfa_enable_tfa']);
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
$return_array['result'] = 'saved';
|
| 78 |
+
|
| 79 |
+
echo json_encode($return_array);
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
die;
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
/**
|
| 86 |
+
* Make the algorithm information string easier to update
|
| 87 |
+
*
|
| 88 |
+
* @param String $algorithm_type - totp|hotp
|
| 89 |
+
*/
|
| 90 |
+
public function tfa_algorithm_info($algorithm_type) {
|
| 91 |
+
$al_type_disp = strtoupper($algorithm_type);
|
| 92 |
+
$al_type_desc = ($algorithm_type == 'totp' ? __('a time based', 'all-in-one-wp-security-and-firewall') : __('an event based', 'all-in-one-wp-security-and-firewall'));
|
| 93 |
+
|
| 94 |
+
return array('disp' => $al_type_disp, 'desc' => $al_type_desc);
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
/**
|
| 98 |
+
* Make the assorted required variables more accessible for ajax
|
| 99 |
+
*
|
| 100 |
+
* Returns: Site URL, private key, emergency codes, algorithm type
|
| 101 |
+
*
|
| 102 |
+
* @return Array
|
| 103 |
+
*/
|
| 104 |
+
public function tfa_fetch_assort_vars() {
|
| 105 |
+
global $current_user;
|
| 106 |
+
$totp_controller = $this->mother->get_totp_controller();
|
| 107 |
+
|
| 108 |
+
$url = preg_replace('/^https?:\/\//i', '', site_url());
|
| 109 |
+
|
| 110 |
+
$tfa_priv_key_64 = get_user_meta($current_user->ID, 'tfa_priv_key_64', true);
|
| 111 |
+
|
| 112 |
+
if (!$tfa_priv_key_64) $tfa_priv_key_64 = $totp_controller->addPrivateKey($current_user->ID);
|
| 113 |
+
|
| 114 |
+
$tfa_priv_key = trim($totp_controller->getPrivateKeyPlain($tfa_priv_key_64, $current_user->ID));
|
| 115 |
+
|
| 116 |
+
$algorithm_type = $totp_controller->get_user_otp_algorithm($current_user->ID);
|
| 117 |
+
|
| 118 |
+
return apply_filters('simba_tfa_fetch_assort_vars', array(
|
| 119 |
+
'url' => $url,
|
| 120 |
+
'tfa_priv_key_64' => $tfa_priv_key_64,
|
| 121 |
+
'tfa_priv_key' => $tfa_priv_key,
|
| 122 |
+
'emergency_str' => '<em>'.__('No emergency codes left. Sorry.', 'all-in-one-wp-security-and-firewall').'</em>',
|
| 123 |
+
'algorithm_type' => $algorithm_type
|
| 124 |
+
), $totp_controller, $current_user);
|
| 125 |
+
}
|
| 126 |
+
|
| 127 |
+
/**
|
| 128 |
+
* Paints out the 'save settings' button
|
| 129 |
+
*/
|
| 130 |
+
public function save_settings_button() {
|
| 131 |
+
echo '<button style="margin-left: 4px;margin-bottom: 10px" class="simbatfa_settings_save button button-primary">'.__('Save Settings', 'all-in-one-wp-security-and-firewall').'</button>';
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
/**
|
| 135 |
+
* Paint output for the TFA on/off radio
|
| 136 |
+
*
|
| 137 |
+
* @param String $style - valid values are 'show_current' and 'require_current'
|
| 138 |
+
*/
|
| 139 |
+
public function settings_enable_or_disable_output($style = 'show_current') {
|
| 140 |
+
$this->save_settings_javascript_output();
|
| 141 |
+
global $current_user;
|
| 142 |
+
?>
|
| 143 |
+
<div class="simbatfa_frontend_settings_box tfa_settings_form">
|
| 144 |
+
<p><?php $this->mother->paint_enable_tfa_radios($current_user->ID, true, $style); ?></p>
|
| 145 |
+
<button style="margin-left: 4px; margin-bottom: 10px;" class="button button-primary simbatfa_settings_save"><?php _e('Save Settings', 'all-in-one-wp-security-and-firewall'); ?></button>
|
| 146 |
+
</div>
|
| 147 |
+
<?php
|
| 148 |
+
}
|
| 149 |
+
|
| 150 |
+
/**
|
| 151 |
+
* Enqueue scripts
|
| 152 |
+
*/
|
| 153 |
+
public function save_settings_javascript_output() {
|
| 154 |
+
|
| 155 |
+
static $is_already_added = false;
|
| 156 |
+
if ($is_already_added) return;
|
| 157 |
+
$is_already_added = true;
|
| 158 |
+
|
| 159 |
+
$suffix = defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ? '' : '.min';
|
| 160 |
+
wp_register_script('jquery-blockui', $this->mother->includes_url().'/jquery.blockUI' . $suffix . '.js', array('jquery'), '2.60');
|
| 161 |
+
|
| 162 |
+
$script_ver = (defined('WP_DEBUG') && WP_DEBUG) ? time() : filemtime($this->mother->includes_dir().'/frontend-settings.js');
|
| 163 |
+
|
| 164 |
+
wp_enqueue_script('simba-tfa-frontend-settings', $this->mother->includes_url().'/frontend-settings.js', array('jquery-blockui'), $script_ver);
|
| 165 |
+
|
| 166 |
+
$ajax_url = admin_url('admin-ajax.php');
|
| 167 |
+
// It's possible that FORCE_ADMIN_SSL will make that SSL, whilst the user is on the front-end having logged in over non-SSL - and as a result, their login cookies won't get sent, and they're not registered as logged in.
|
| 168 |
+
if (!is_admin() && substr(strtolower($ajax_url), 0, 6) == 'https:' && !is_ssl()) {
|
| 169 |
+
$also_try = 'http:'.substr($ajax_url, 6);
|
| 170 |
+
} else {
|
| 171 |
+
$also_try = '';
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
$localize = array(
|
| 175 |
+
'ask' => __('You have unsaved settings.', 'all-in-one-wp-security-and-firewall'),
|
| 176 |
+
'saving' => __('Saving...', 'all-in-one-wp-security-and-firewall'),
|
| 177 |
+
'ajax_url' => $ajax_url,
|
| 178 |
+
'also_try' => $also_try,
|
| 179 |
+
'nonce' => wp_create_nonce('tfa_frontend_nonce'),
|
| 180 |
+
'response' => __('Response:', 'all-in-one-wp-security-and-firewall'),
|
| 181 |
+
);
|
| 182 |
+
|
| 183 |
+
wp_localize_script('simba-tfa-frontend-settings', 'simba_tfa_frontend', $localize);
|
| 184 |
+
|
| 185 |
+
}
|
| 186 |
+
|
| 187 |
+
/**
|
| 188 |
+
* Shortcode function for twofactor_user_settings
|
| 189 |
+
*
|
| 190 |
+
* @param Array $atts
|
| 191 |
+
* @param Null|String $content
|
| 192 |
+
*
|
| 193 |
+
* @return String
|
| 194 |
+
*/
|
| 195 |
+
public function tfa_user_settings_front($atts, $content = null) {
|
| 196 |
+
|
| 197 |
+
if (!is_user_logged_in()) return '';
|
| 198 |
+
|
| 199 |
+
global $current_user;
|
| 200 |
+
|
| 201 |
+
return $this->mother->include_template('shortcode-tfa-user-settings.php', array('is_activated_for_user' => $current_user->ID, 'tfa_frontend' => $this), true);
|
| 202 |
+
|
| 203 |
+
}
|
| 204 |
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/* TFA users list column */
|
| 2 |
+
th.column-tfa-status,
|
| 3 |
+
td.column-tfa-status {
|
| 4 |
+
text-align: center;
|
| 5 |
+
}
|
| 6 |
+
|
| 7 |
+
td.column-tfa-status span.dashicons-no {
|
| 8 |
+
color: red;
|
| 9 |
+
}
|
| 10 |
+
|
| 11 |
+
td.column-tfa-status span.dashicons-yes {
|
| 12 |
+
color: green;
|
| 13 |
+
}
|
|
@@ -0,0 +1,225 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
//namespace Base32;
|
| 3 |
+
|
| 4 |
+
/**
|
| 5 |
+
* Base32 encoder and decoder
|
| 6 |
+
*
|
| 7 |
+
* Last update: 2012-06-20
|
| 8 |
+
*
|
| 9 |
+
* RFC 4648 compliant
|
| 10 |
+
* @link http://www.ietf.org/rfc/rfc4648.txt
|
| 11 |
+
*
|
| 12 |
+
* Some groundwork based on this class
|
| 13 |
+
* https://github.com/NTICompass/PHP-Base32
|
| 14 |
+
*
|
| 15 |
+
* @author Christian Riesen <chris.riesen@gmail.com>
|
| 16 |
+
* @link http://christianriesen.com
|
| 17 |
+
* @license MIT License see LICENSE file
|
| 18 |
+
*/
|
| 19 |
+
class Base32
|
| 20 |
+
{
|
| 21 |
+
/**
|
| 22 |
+
* Table for encoding base32
|
| 23 |
+
*
|
| 24 |
+
* @var array
|
| 25 |
+
*/
|
| 26 |
+
private static $encode = array(
|
| 27 |
+
0 => 'A',
|
| 28 |
+
1 => 'B',
|
| 29 |
+
2 => 'C',
|
| 30 |
+
3 => 'D',
|
| 31 |
+
4 => 'E',
|
| 32 |
+
5 => 'F',
|
| 33 |
+
6 => 'G',
|
| 34 |
+
7 => 'H',
|
| 35 |
+
8 => 'I',
|
| 36 |
+
9 => 'J',
|
| 37 |
+
10 => 'K',
|
| 38 |
+
11 => 'L',
|
| 39 |
+
12 => 'M',
|
| 40 |
+
13 => 'N',
|
| 41 |
+
14 => 'O',
|
| 42 |
+
15 => 'P',
|
| 43 |
+
16 => 'Q',
|
| 44 |
+
17 => 'R',
|
| 45 |
+
18 => 'S',
|
| 46 |
+
19 => 'T',
|
| 47 |
+
20 => 'U',
|
| 48 |
+
21 => 'V',
|
| 49 |
+
22 => 'W',
|
| 50 |
+
23 => 'X',
|
| 51 |
+
24 => 'Y',
|
| 52 |
+
25 => 'Z',
|
| 53 |
+
26 => 2,
|
| 54 |
+
27 => 3,
|
| 55 |
+
28 => 4,
|
| 56 |
+
29 => 5,
|
| 57 |
+
30 => 6,
|
| 58 |
+
31 => 7,
|
| 59 |
+
32 => '=',
|
| 60 |
+
);
|
| 61 |
+
|
| 62 |
+
/**
|
| 63 |
+
* Table for decoding base32
|
| 64 |
+
*
|
| 65 |
+
* @var array
|
| 66 |
+
*/
|
| 67 |
+
private static $decode = array(
|
| 68 |
+
'A' => 0,
|
| 69 |
+
'B' => 1,
|
| 70 |
+
'C' => 2,
|
| 71 |
+
'D' => 3,
|
| 72 |
+
'E' => 4,
|
| 73 |
+
'F' => 5,
|
| 74 |
+
'G' => 6,
|
| 75 |
+
'H' => 7,
|
| 76 |
+
'I' => 8,
|
| 77 |
+
'J' => 9,
|
| 78 |
+
'K' => 10,
|
| 79 |
+
'L' => 11,
|
| 80 |
+
'M' => 12,
|
| 81 |
+
'N' => 13,
|
| 82 |
+
'O' => 14,
|
| 83 |
+
'P' => 15,
|
| 84 |
+
'Q' => 16,
|
| 85 |
+
'R' => 17,
|
| 86 |
+
'S' => 18,
|
| 87 |
+
'T' => 19,
|
| 88 |
+
'U' => 20,
|
| 89 |
+
'V' => 21,
|
| 90 |
+
'W' => 22,
|
| 91 |
+
'X' => 23,
|
| 92 |
+
'Y' => 24,
|
| 93 |
+
'Z' => 25,
|
| 94 |
+
2 => 26,
|
| 95 |
+
3 => 27,
|
| 96 |
+
4 => 28,
|
| 97 |
+
5 => 29,
|
| 98 |
+
6 => 30,
|
| 99 |
+
7 => 31,
|
| 100 |
+
'=' => 32,
|
| 101 |
+
);
|
| 102 |
+
|
| 103 |
+
/**
|
| 104 |
+
* Creates an array from a binary string into a given chunk size
|
| 105 |
+
*
|
| 106 |
+
* @param string $binaryString String to chunk
|
| 107 |
+
* @param integer $bits Number of bits per chunk
|
| 108 |
+
* @return array
|
| 109 |
+
*/
|
| 110 |
+
private static function chunk($binaryString, $bits)
|
| 111 |
+
{
|
| 112 |
+
$binaryString = chunk_split($binaryString, $bits, ' ');
|
| 113 |
+
|
| 114 |
+
if (substr($binaryString, (strlen($binaryString)) - 1) == ' ') {
|
| 115 |
+
$binaryString = substr($binaryString, 0, strlen($binaryString)-1);
|
| 116 |
+
}
|
| 117 |
+
|
| 118 |
+
return explode(' ', $binaryString);
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
/**
|
| 122 |
+
* Encodes into base32
|
| 123 |
+
*
|
| 124 |
+
* @param string $string Clear text string
|
| 125 |
+
* @return string Base32 encoded string
|
| 126 |
+
*/
|
| 127 |
+
public static function encode($string)
|
| 128 |
+
{
|
| 129 |
+
if (strlen($string) == 0) {
|
| 130 |
+
// Gives an empty string
|
| 131 |
+
return '';
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
// Convert string to binary
|
| 135 |
+
$binaryString = '';
|
| 136 |
+
|
| 137 |
+
foreach (str_split($string) as $s) {
|
| 138 |
+
// Return each character as an 8-bit binary string
|
| 139 |
+
$s = decbin(ord($s));
|
| 140 |
+
$binaryString .= str_pad($s, 8, 0, STR_PAD_LEFT);
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
// Break into 5-bit chunks, then break that into an array
|
| 144 |
+
$binaryArray = self::chunk($binaryString, 5);
|
| 145 |
+
|
| 146 |
+
// Pad array to be divisible by 8
|
| 147 |
+
while (count($binaryArray) % 8 !== 0) {
|
| 148 |
+
$binaryArray[] = null;
|
| 149 |
+
}
|
| 150 |
+
|
| 151 |
+
$base32String = '';
|
| 152 |
+
|
| 153 |
+
// Encode in base32
|
| 154 |
+
foreach ($binaryArray as $bin) {
|
| 155 |
+
$char = 32;
|
| 156 |
+
|
| 157 |
+
if (!is_null($bin)) {
|
| 158 |
+
// Pad the binary strings
|
| 159 |
+
$bin = str_pad($bin, 5, 0, STR_PAD_RIGHT);
|
| 160 |
+
$char = bindec($bin);
|
| 161 |
+
}
|
| 162 |
+
|
| 163 |
+
// Base32 character
|
| 164 |
+
$base32String .= self::$encode[$char];
|
| 165 |
+
}
|
| 166 |
+
|
| 167 |
+
return $base32String;
|
| 168 |
+
}
|
| 169 |
+
|
| 170 |
+
/**
|
| 171 |
+
* Decodes base32
|
| 172 |
+
*
|
| 173 |
+
* @param string $base32String Base32 encoded string
|
| 174 |
+
* @return string Clear text string
|
| 175 |
+
*/
|
| 176 |
+
public static function decode($base32String)
|
| 177 |
+
{
|
| 178 |
+
if (strlen($base32String) == 0) {
|
| 179 |
+
// Gives an empty string
|
| 180 |
+
return '';
|
| 181 |
+
}
|
| 182 |
+
|
| 183 |
+
// Only work in upper cases
|
| 184 |
+
$base32String = strtoupper($base32String);
|
| 185 |
+
|
| 186 |
+
// Remove anything that is not base32 alphabet
|
| 187 |
+
$pattern = '/[^A-Z2-7]/';
|
| 188 |
+
$replacement = '';
|
| 189 |
+
|
| 190 |
+
$base32String = preg_replace($pattern, '', $base32String);
|
| 191 |
+
|
| 192 |
+
$base32Array = str_split($base32String);
|
| 193 |
+
|
| 194 |
+
$binaryArray = array();
|
| 195 |
+
$string = '';
|
| 196 |
+
|
| 197 |
+
foreach ($base32Array as $str) {
|
| 198 |
+
$char = self::$decode[$str];
|
| 199 |
+
|
| 200 |
+
// Ignore the padding character
|
| 201 |
+
if ($char !== 32) {
|
| 202 |
+
$char = decbin($char);
|
| 203 |
+
$string .= str_pad($char, 5, 0, STR_PAD_LEFT);
|
| 204 |
+
}
|
| 205 |
+
}
|
| 206 |
+
|
| 207 |
+
while (strlen($string) %8 !== 0) {
|
| 208 |
+
$string = substr($string, 0, strlen($string)-1);
|
| 209 |
+
}
|
| 210 |
+
|
| 211 |
+
$binaryArray = self::chunk($string, 8);
|
| 212 |
+
|
| 213 |
+
$realString = '';
|
| 214 |
+
|
| 215 |
+
foreach ($binaryArray as $bin) {
|
| 216 |
+
// Pad each value to 8 bits
|
| 217 |
+
$bin = str_pad($bin, 8, 0, STR_PAD_RIGHT);
|
| 218 |
+
// Convert binary strings to ascii
|
| 219 |
+
$realString .= chr(bindec($bin));
|
| 220 |
+
}
|
| 221 |
+
|
| 222 |
+
return $realString;
|
| 223 |
+
}
|
| 224 |
+
}
|
| 225 |
+
|
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Copyright (c) 2008, Jakob Heuser
|
| 2 |
+
All rights reserved.
|
| 3 |
+
|
| 4 |
+
Redistribution and use in source and binary forms, with or without
|
| 5 |
+
modification, are permitted provided that the following conditions are met:
|
| 6 |
+
* Redistributions of source code must retain the above copyright
|
| 7 |
+
notice, this list of conditions and the following disclaimer.
|
| 8 |
+
* Redistributions in binary form must reproduce the above copyright
|
| 9 |
+
notice, this list of conditions and the following disclaimer in the
|
| 10 |
+
documentation and/or other materials provided with the distribution.
|
| 11 |
+
* Neither the name of HOTP-PHP nor the
|
| 12 |
+
names of its contributors may be used to endorse or promote products
|
| 13 |
+
derived from this software without specific prior written permission.
|
| 14 |
+
|
| 15 |
+
THIS SOFTWARE IS PROVIDED BY Jakob Heuser ''AS IS'' AND ANY
|
| 16 |
+
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
| 17 |
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
| 18 |
+
DISCLAIMED. IN NO EVENT SHALL <copyright holder> BE LIABLE FOR ANY
|
| 19 |
+
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
| 20 |
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
| 21 |
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
| 22 |
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
| 23 |
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
| 24 |
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
HOTP - PHP Based HMAC One Time Passwords
|
| 2 |
+
========================================
|
| 3 |
+
|
| 4 |
+
**What is HOTP**:
|
| 5 |
+
HOTP is a class that simplifies One Time Password systems for PHP Authentication. The HOTP/TOTP Algorithms have been around for a bit, so this is a straightforward class to meet the test vector requirements.
|
| 6 |
+
|
| 7 |
+
**What works with HOTP/TOTP**:
|
| 8 |
+
It's been tested to the test vectors, and I've verified the time-sync hashes against the following:
|
| 9 |
+
|
| 10 |
+
* Android: Mobile-OTP
|
| 11 |
+
* iPhone: OATH Token
|
| 12 |
+
|
| 13 |
+
**Why would I use this**:
|
| 14 |
+
Who wouldn't love a simple drop-in class for HMAC Based One Time Passwords? It's a great extra layer of security (creating two-factor auth) and it's pretty darn zippy.
|
| 15 |
+
|
| 16 |
+
**Okay you sold me. Give me some docs**:
|
| 17 |
+
|
| 18 |
+
* $result = HOTP::generateByCounter($key, $counter); // event based
|
| 19 |
+
* $result = HOTP::generateByTime($key, $window); // time based within a "window" of time
|
| 20 |
+
* $result = HOTP::generateByTimeWindow($key, $window, $min, $max); // same as generateByTime, but for $min windows before and $max windows after
|
| 21 |
+
|
| 22 |
+
with $result, you can do all sorts of neat things...
|
| 23 |
+
|
| 24 |
+
* $result->toString();
|
| 25 |
+
* $result->toHex();
|
| 26 |
+
* $result->doDec();
|
| 27 |
+
* $result->toHotp($length); // how many digits in your OTP?
|
|
@@ -0,0 +1,160 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
|
| 3 |
+
/*
|
| 4 |
+
HOTP Example File
|
| 5 |
+
*/
|
| 6 |
+
require_once 'hotp.php';
|
| 7 |
+
|
| 8 |
+
$key = '12345678901234567890';
|
| 9 |
+
|
| 10 |
+
$table = array(
|
| 11 |
+
'HOTP' => array(
|
| 12 |
+
0 => array(
|
| 13 |
+
'HMAC' => 'cc93cf18508d94934c64b65d8ba7667fb7cde4b0',
|
| 14 |
+
'hex' => '4c93cf18',
|
| 15 |
+
'dec' => '1284755224',
|
| 16 |
+
'hotp' => '755224',
|
| 17 |
+
),
|
| 18 |
+
1 => array(
|
| 19 |
+
'HMAC' => '75a48a19d4cbe100644e8ac1397eea747a2d33ab',
|
| 20 |
+
'hex' => '41397eea',
|
| 21 |
+
'dec' => '1094287082',
|
| 22 |
+
'hotp' => '287082',
|
| 23 |
+
),
|
| 24 |
+
2 => array(
|
| 25 |
+
'HMAC' => '0bacb7fa082fef30782211938bc1c5e70416ff44',
|
| 26 |
+
'hex' => '82fef30',
|
| 27 |
+
'dec' => '137359152',
|
| 28 |
+
'hotp' => '359152',
|
| 29 |
+
),
|
| 30 |
+
3 => array(
|
| 31 |
+
'HMAC' => '66c28227d03a2d5529262ff016a1e6ef76557ece',
|
| 32 |
+
'hex' => '66ef7655',
|
| 33 |
+
'dec' => '1726969429',
|
| 34 |
+
'hotp' => '969429',
|
| 35 |
+
),
|
| 36 |
+
4 => array(
|
| 37 |
+
'HMAC' => 'a904c900a64b35909874b33e61c5938a8e15ed1c',
|
| 38 |
+
'hex' => '61c5938a',
|
| 39 |
+
'dec' => '1640338314',
|
| 40 |
+
'hotp' => '338314',
|
| 41 |
+
),
|
| 42 |
+
5 => array(
|
| 43 |
+
'HMAC' => 'a37e783d7b7233c083d4f62926c7a25f238d0316',
|
| 44 |
+
'hex' => '33c083d4',
|
| 45 |
+
'dec' => '868254676',
|
| 46 |
+
'hotp' => '254676',
|
| 47 |
+
),
|
| 48 |
+
6 => array(
|
| 49 |
+
'HMAC' => 'bc9cd28561042c83f219324d3c607256c03272ae',
|
| 50 |
+
'hex' => '7256c032',
|
| 51 |
+
'dec' => '1918287922',
|
| 52 |
+
'hotp' => '287922',
|
| 53 |
+
),
|
| 54 |
+
7 => array(
|
| 55 |
+
'HMAC' => 'a4fb960c0bc06e1eabb804e5b397cdc4b45596fa',
|
| 56 |
+
'hex' => '4e5b397',
|
| 57 |
+
'dec' => '82162583',
|
| 58 |
+
'hotp' => '162583',
|
| 59 |
+
),
|
| 60 |
+
8 => array(
|
| 61 |
+
'HMAC' => '1b3c89f65e6c9e883012052823443f048b4332db',
|
| 62 |
+
'hex' => '2823443f',
|
| 63 |
+
'dec' => '673399871',
|
| 64 |
+
'hotp' => '399871',
|
| 65 |
+
),
|
| 66 |
+
9 => array(
|
| 67 |
+
'HMAC' => '1637409809a679dc698207310c8c7fc07290d9e5',
|
| 68 |
+
'hex' => '2679dc69',
|
| 69 |
+
'dec' => '645520489',
|
| 70 |
+
'hotp' => '520489',
|
| 71 |
+
),
|
| 72 |
+
),
|
| 73 |
+
'TOTP' => array(
|
| 74 |
+
'59' => array(
|
| 75 |
+
'totp' => '94287082',
|
| 76 |
+
),
|
| 77 |
+
'1111111109' => array(
|
| 78 |
+
'totp' => '07081804',
|
| 79 |
+
),
|
| 80 |
+
'1111111111' => array(
|
| 81 |
+
'totp' => '14050471',
|
| 82 |
+
),
|
| 83 |
+
'1234567890' => array(
|
| 84 |
+
'totp' => '89005924',
|
| 85 |
+
),
|
| 86 |
+
'2000000000' => array(
|
| 87 |
+
'totp' => '69279037',
|
| 88 |
+
),
|
| 89 |
+
),
|
| 90 |
+
);
|
| 91 |
+
|
| 92 |
+
echo <<<DOCBLOCK
|
| 93 |
+
<!DOCTYPE><html><head></head><body><pre>
|
| 94 |
+
http://www.ietf.org/rfc/rfc4226.txt
|
| 95 |
+
http://tools.ietf.org/html/draft-mraihi-totp-timebased-06
|
| 96 |
+
|
| 97 |
+
TEST VECTOR VERIFICATION
|
| 98 |
+
|
| 99 |
+
HOTP Tests:
|
| 100 |
+
|
| 101 |
+
DOCBLOCK;
|
| 102 |
+
|
| 103 |
+
echo "Count Method Value Pass/Fail\n";
|
| 104 |
+
echo "----------------------------------------------------------------------\n";
|
| 105 |
+
|
| 106 |
+
// loop over all HOTP table results, and calculate the matching value
|
| 107 |
+
foreach ($table['HOTP'] as $seed => $results) {
|
| 108 |
+
$hotp = HOTP::generateByCounter($key, $seed);
|
| 109 |
+
$first = true;
|
| 110 |
+
foreach ($results as $type => $calc) {
|
| 111 |
+
if ($first) {
|
| 112 |
+
echo str_pad($seed, 4, ' ', STR_PAD_LEFT);
|
| 113 |
+
$first = false;
|
| 114 |
+
}
|
| 115 |
+
else {
|
| 116 |
+
echo ' ';
|
| 117 |
+
}
|
| 118 |
+
echo ' ';
|
| 119 |
+
echo str_pad($type, 5, ' ', STR_PAD_RIGHT);
|
| 120 |
+
echo ' ';
|
| 121 |
+
echo str_pad($calc, 47, ' ', STR_PAD_RIGHT);
|
| 122 |
+
echo ' ';
|
| 123 |
+
$method = 'to'.(ucfirst(str_replace('HMAC', 'string', $type)));
|
| 124 |
+
echo str_pad(($calc == $hotp->$method(6)) ? '[OK]' : '[FAIL]', 9, ' ', STR_PAD_LEFT);
|
| 125 |
+
echo "\n";
|
| 126 |
+
}
|
| 127 |
+
}
|
| 128 |
+
|
| 129 |
+
echo <<<DOCBLOCK
|
| 130 |
+
|
| 131 |
+
TOTP Tests:
|
| 132 |
+
|
| 133 |
+
DOCBLOCK;
|
| 134 |
+
|
| 135 |
+
echo "Time (sec) Value Pass/Fail\n";
|
| 136 |
+
echo "----------------------------------------------------------------------\n";
|
| 137 |
+
|
| 138 |
+
// now echo over the TOTP table
|
| 139 |
+
foreach ($table['TOTP'] as $seed => $results) {
|
| 140 |
+
$totp = HOTP::generateByTime($key, 30, $seed);
|
| 141 |
+
$first = true;
|
| 142 |
+
foreach ($results as $type => $calc) {
|
| 143 |
+
if ($first) {
|
| 144 |
+
echo str_pad($seed, 10, ' ', STR_PAD_LEFT);
|
| 145 |
+
$first = false;
|
| 146 |
+
}
|
| 147 |
+
else {
|
| 148 |
+
echo ' ';
|
| 149 |
+
}
|
| 150 |
+
echo ' ';
|
| 151 |
+
echo str_pad($calc, 47, ' ', STR_PAD_RIGHT);
|
| 152 |
+
echo ' ';
|
| 153 |
+
$method = 'to'.(ucfirst(str_replace('totp', 'hotp', $type)));
|
| 154 |
+
echo str_pad(($calc == $totp->$method(8)) ? '[OK]' : '[FAIL]', 9, ' ', STR_PAD_LEFT);
|
| 155 |
+
echo "\n";
|
| 156 |
+
}
|
| 157 |
+
}
|
| 158 |
+
|
| 159 |
+
echo '</pre></body></html>';
|
| 160 |
+
|
|
@@ -0,0 +1,174 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
/**
|
| 3 |
+
* HOTP Class
|
| 4 |
+
* Based on the work of OAuth, and the sample implementation of HMAC OTP
|
| 5 |
+
* http://tools.ietf.org/html/draft-mraihi-oath-hmac-otp-04#appendix-D
|
| 6 |
+
* @author Jakob Heuser (firstname)@felocity.com
|
| 7 |
+
* @copyright 2011
|
| 8 |
+
* @license BSD
|
| 9 |
+
* @version 1.0
|
| 10 |
+
*/
|
| 11 |
+
class HOTP {
|
| 12 |
+
/**
|
| 13 |
+
* Generate a HOTP key based on a counter value (event based HOTP)
|
| 14 |
+
* @param string $key the key to use for hashing
|
| 15 |
+
* @param int $counter the number of attempts represented in this hashing
|
| 16 |
+
* @return HOTPResult a HOTP Result which can be truncated or output
|
| 17 |
+
*/
|
| 18 |
+
public static function generateByCounter($key, $counter) {
|
| 19 |
+
// the counter value can be more than one byte long,
|
| 20 |
+
// so we need to pack it down properly.
|
| 21 |
+
$cur_counter = array(0, 0, 0, 0, 0, 0, 0, 0);
|
| 22 |
+
for($i = 7; $i >= 0; $i--) {
|
| 23 |
+
$cur_counter[$i] = pack ('C*', $counter);
|
| 24 |
+
$counter = $counter >> 8;
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
$bin_counter = implode($cur_counter);
|
| 28 |
+
|
| 29 |
+
// Pad to 8 chars
|
| 30 |
+
if (strlen($bin_counter) < 8) {
|
| 31 |
+
$bin_counter = str_repeat (chr(0), 8 - strlen ($bin_counter)) . $bin_counter;
|
| 32 |
+
}
|
| 33 |
+
|
| 34 |
+
// HMAC
|
| 35 |
+
$hash = hash_hmac('sha1', $bin_counter, $key);
|
| 36 |
+
|
| 37 |
+
return new HOTPResult($hash);
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
/**
|
| 41 |
+
* Generate a HOTP key based on a timestamp and window size
|
| 42 |
+
* @param string $key the key to use for hashing
|
| 43 |
+
* @param int $window the size of the window a key is valid for in seconds
|
| 44 |
+
* @param int $timestamp a timestamp to calculate for, defaults to time()
|
| 45 |
+
* @return HOTPResult a HOTP Result which can be truncated or output
|
| 46 |
+
*/
|
| 47 |
+
public static function generateByTime($key, $window, $timestamp = false) {
|
| 48 |
+
if (!$timestamp && $timestamp !== 0) {
|
| 49 |
+
$timestamp = HOTP::getTime();
|
| 50 |
+
}
|
| 51 |
+
|
| 52 |
+
$counter = intval($timestamp / $window);
|
| 53 |
+
|
| 54 |
+
return HOTP::generateByCounter($key, $counter);
|
| 55 |
+
}
|
| 56 |
+
|
| 57 |
+
/**
|
| 58 |
+
* Generate a HOTP key collection based on a timestamp and window size
|
| 59 |
+
* all keys that could exist between a start and end time will be included
|
| 60 |
+
* in the returned array
|
| 61 |
+
* @param string $key the key to use for hashing
|
| 62 |
+
* @param int $window the size of the window a key is valid for in seconds
|
| 63 |
+
* @param int $min the minimum window to accept before $timestamp
|
| 64 |
+
* @param int $max the maximum window to accept after $timestamp
|
| 65 |
+
* @param int $timestamp a timestamp to calculate for, defaults to time()
|
| 66 |
+
* @return array of HOTPResult
|
| 67 |
+
*/
|
| 68 |
+
public static function generateByTimeWindow($key, $window, $min = -1, $max = 1, $timestamp = false) {
|
| 69 |
+
if (!$timestamp && $timestamp !== 0) {
|
| 70 |
+
$timestamp = HOTP::getTime();
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
$counter = intval($timestamp / $window);
|
| 74 |
+
$window = range($min, $max);
|
| 75 |
+
|
| 76 |
+
$out = array();
|
| 77 |
+
for ($i = 0; $i < count($window); $i++) {
|
| 78 |
+
$shift_counter = $window[$i];
|
| 79 |
+
$out[$shift_counter] = HOTP::generateByCounter($key, $counter + $shift_counter);
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
return $out;
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
/**
|
| 86 |
+
* Gets the current time
|
| 87 |
+
* Ensures we are operating in UTC for the entire framework
|
| 88 |
+
* Restores the timezone on exit.
|
| 89 |
+
* @return int the current time
|
| 90 |
+
*/
|
| 91 |
+
public static function getTime() {
|
| 92 |
+
return time(); // PHP's time is always UTC
|
| 93 |
+
}
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
/**
|
| 97 |
+
* The HOTPResult Class converts an HOTP item to various forms
|
| 98 |
+
* Supported formats include hex, decimal, string, and HOTP
|
| 99 |
+
* @author Jakob Heuser (firstname)@felocity.com
|
| 100 |
+
*/
|
| 101 |
+
class HOTPResult {
|
| 102 |
+
protected $hash;
|
| 103 |
+
protected $binary;
|
| 104 |
+
protected $decimal;
|
| 105 |
+
|
| 106 |
+
/**
|
| 107 |
+
* Build an HOTP Result
|
| 108 |
+
* @param string $value the value to construct with
|
| 109 |
+
*/
|
| 110 |
+
public function __construct($value) {
|
| 111 |
+
// store raw
|
| 112 |
+
$this->hash = $value;
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
/**
|
| 116 |
+
* Returns the string version of the HOTP
|
| 117 |
+
* @return string
|
| 118 |
+
*/
|
| 119 |
+
public function toString() {
|
| 120 |
+
return $this->hash;
|
| 121 |
+
}
|
| 122 |
+
|
| 123 |
+
/**
|
| 124 |
+
* Returns the hex version of the HOTP
|
| 125 |
+
* @return string
|
| 126 |
+
*/
|
| 127 |
+
public function toHex() {
|
| 128 |
+
if( !$this->hex )
|
| 129 |
+
{
|
| 130 |
+
$this->hex = dechex($this->toDec());
|
| 131 |
+
}
|
| 132 |
+
return $this->hex;
|
| 133 |
+
}
|
| 134 |
+
|
| 135 |
+
/**
|
| 136 |
+
* Returns the decimal version of the HOTP
|
| 137 |
+
* @return int
|
| 138 |
+
*/
|
| 139 |
+
public function toDec() {
|
| 140 |
+
if( !$this->decimal )
|
| 141 |
+
{
|
| 142 |
+
// store calculate decimal
|
| 143 |
+
$hmac_result = array();
|
| 144 |
+
|
| 145 |
+
// Convert to decimal
|
| 146 |
+
foreach(str_split($this->hash,2) as $hex)
|
| 147 |
+
{
|
| 148 |
+
$hmac_result[] = hexdec($hex);
|
| 149 |
+
}
|
| 150 |
+
|
| 151 |
+
$offset = $hmac_result[19] & 0xf;
|
| 152 |
+
|
| 153 |
+
$this->decimal = (
|
| 154 |
+
(($hmac_result[$offset+0] & 0x7f) << 24 ) |
|
| 155 |
+
(($hmac_result[$offset+1] & 0xff) << 16 ) |
|
| 156 |
+
(($hmac_result[$offset+2] & 0xff) << 8 ) |
|
| 157 |
+
($hmac_result[$offset+3] & 0xff)
|
| 158 |
+
);
|
| 159 |
+
}
|
| 160 |
+
return $this->decimal;
|
| 161 |
+
}
|
| 162 |
+
|
| 163 |
+
/**
|
| 164 |
+
* Returns the truncated decimal form of the HOTP
|
| 165 |
+
* @param int $length the length of the HOTP to return
|
| 166 |
+
* @return string
|
| 167 |
+
*/
|
| 168 |
+
public function toHOTP($length) {
|
| 169 |
+
$str = str_pad($this->toDec(), $length, "0", STR_PAD_LEFT);
|
| 170 |
+
$str = substr($str, (-1 * $length));
|
| 171 |
+
return $str;
|
| 172 |
+
}
|
| 173 |
+
|
| 174 |
+
}
|
|
@@ -0,0 +1,1015 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
|
| 3 |
+
if (!defined('ABSPATH')) die('No direct access.');
|
| 4 |
+
|
| 5 |
+
if (!class_exists('HOTP')) require_once(__DIR__.'/hotp-php-master/hotp.php');
|
| 6 |
+
if (!class_exists('Base32')) require_once(__DIR__.'/Base32/Base32.php');
|
| 7 |
+
|
| 8 |
+
class Simba_TFA_Provider_TOTP {
|
| 9 |
+
|
| 10 |
+
// @var Simba_Two_Factor_Authentication
|
| 11 |
+
private $tfa;
|
| 12 |
+
|
| 13 |
+
// @var String
|
| 14 |
+
private $salt_prefix;
|
| 15 |
+
|
| 16 |
+
// @var String
|
| 17 |
+
private $pw_prefix;
|
| 18 |
+
|
| 19 |
+
// @var Integer
|
| 20 |
+
private $time_window_size;
|
| 21 |
+
|
| 22 |
+
// @var Integer
|
| 23 |
+
private $check_back_time_windows;
|
| 24 |
+
|
| 25 |
+
// @var Integer
|
| 26 |
+
private $check_forward_time_windows;
|
| 27 |
+
|
| 28 |
+
// @var Integer
|
| 29 |
+
private $otp_length = 6;
|
| 30 |
+
|
| 31 |
+
// @var Integer
|
| 32 |
+
private $emergency_codes_length = 8;
|
| 33 |
+
|
| 34 |
+
// @var String
|
| 35 |
+
public $default_hmac = 'totp';
|
| 36 |
+
|
| 37 |
+
// @var Boolean
|
| 38 |
+
private $settings_saved = false;
|
| 39 |
+
|
| 40 |
+
/**
|
| 41 |
+
* Class constructor
|
| 42 |
+
*
|
| 43 |
+
* @param Simba_Two_Factor_Authentication main plugin class
|
| 44 |
+
*/
|
| 45 |
+
public function __construct($tfa) {
|
| 46 |
+
$this->tfa = $tfa;
|
| 47 |
+
|
| 48 |
+
$this->otp_helper = new HOTP();
|
| 49 |
+
|
| 50 |
+
add_action('plugins_loaded', array($this, 'plugins_loaded'));
|
| 51 |
+
|
| 52 |
+
add_action('admin_init', array($this, 'admin_init'));
|
| 53 |
+
|
| 54 |
+
if (!is_admin()) {
|
| 55 |
+
add_action('init', array($this, 'check_possible_reset'));
|
| 56 |
+
}
|
| 57 |
+
|
| 58 |
+
// Potentially show off-sync message for hotp
|
| 59 |
+
add_action('admin_notices', array($this, 'tfa_show_hotp_off_sync_message'));
|
| 60 |
+
}
|
| 61 |
+
|
| 62 |
+
/**
|
| 63 |
+
* Return whether or not this class detected and saved new settings
|
| 64 |
+
*
|
| 65 |
+
* @return Boolean
|
| 66 |
+
*/
|
| 67 |
+
public function were_settings_saved() {
|
| 68 |
+
return $this->settings_saved;
|
| 69 |
+
}
|
| 70 |
+
|
| 71 |
+
/**
|
| 72 |
+
* Runs upon the WP action admin_init
|
| 73 |
+
*/
|
| 74 |
+
public function admin_init() {
|
| 75 |
+
|
| 76 |
+
$this->check_possible_reset();
|
| 77 |
+
|
| 78 |
+
global $current_user;
|
| 79 |
+
|
| 80 |
+
if (!empty($_REQUEST['_tfa_activate_nonce']) && !empty($_POST['tfa_enable_tfa']) && wp_verify_nonce($_REQUEST['_tfa_activate_nonce'], 'tfa_activate') && !empty($_GET['settings-updated'])) {
|
| 81 |
+
$this->tfa->change_tfa_enabled_status($current_user->ID, $_POST['tfa_enable_tfa']);
|
| 82 |
+
$this->settings_saved = true;
|
| 83 |
+
}
|
| 84 |
+
|
| 85 |
+
if (!empty($_REQUEST['_tfa_algorithm_nonce']) && !empty($_POST['tfa_algorithm_type']) && !empty($_GET['settings-updated']) && wp_verify_nonce($_REQUEST['_tfa_algorithm_nonce'], 'tfa_algorithm')) {
|
| 86 |
+
|
| 87 |
+
$old_algorithm = $this->get_user_otp_algorithm($current_user->ID);
|
| 88 |
+
|
| 89 |
+
if ($old_algorithm != $_POST['tfa_algorithm_type']) {
|
| 90 |
+
$this->changeUserAlgorithmTo($current_user->ID, $_POST['tfa_algorithm_type']);
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
$this->settings_saved = true;
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
if (!empty($_GET['warning_button_clicked']) && !empty($_REQUEST['resyncnonce']) && wp_verify_nonce($_REQUEST['resyncnonce'], 'tfaresync')) {
|
| 97 |
+
delete_user_meta($current_user->ID, 'tfa_hotp_off_sync');
|
| 98 |
+
}
|
| 99 |
+
|
| 100 |
+
}
|
| 101 |
+
|
| 102 |
+
/**
|
| 103 |
+
* Enqueue adding of JavaScript for footer
|
| 104 |
+
*
|
| 105 |
+
*/
|
| 106 |
+
public function add_footer() {
|
| 107 |
+
|
| 108 |
+
static $added_footer = false;
|
| 109 |
+
if ($added_footer) return;
|
| 110 |
+
$added_footer = true;
|
| 111 |
+
|
| 112 |
+
$script_file = (defined('SCRIPT_DEBUG') && SCRIPT_DEBUG) ? 'jquery-qrcode.js' : 'jquery-qrcode.min.js';
|
| 113 |
+
$script_ver = (defined('WP_DEBUG') && WP_DEBUG) ? time() : filemtime($this->tfa->includes_dir()."/jquery-qrcode/$script_file");
|
| 114 |
+
wp_enqueue_script('jquery-qrcode', $this->tfa->includes_url()."/jquery-qrcode/$script_file", array('jquery'), $script_ver);
|
| 115 |
+
add_action(is_admin() ? 'admin_footer' : 'wp_footer', array($this, 'footer'));
|
| 116 |
+
|
| 117 |
+
}
|
| 118 |
+
|
| 119 |
+
/**
|
| 120 |
+
* Runs upon the WP actions wp_footer and admin_footer. Adds the necessary JavaScript for rendering and updating QR codes, and handling trusted devices removal in the admin area
|
| 121 |
+
*/
|
| 122 |
+
public function footer() {
|
| 123 |
+
$ajax_url = admin_url('admin-ajax.php');
|
| 124 |
+
// It's possible that FORCE_ADMIN_SSL will make that SSL, whilst the user is on the front-end having logged in over non-SSL - and as a result, their login cookies won't get sent, and they're not registered as logged in.
|
| 125 |
+
if (!is_admin() && substr(strtolower($ajax_url), 0, 6) == 'https:' && !is_ssl()) {
|
| 126 |
+
$also_try = 'http:'.substr($ajax_url, 6);
|
| 127 |
+
}
|
| 128 |
+
?>
|
| 129 |
+
<script>
|
| 130 |
+
jQuery(function($) {
|
| 131 |
+
|
| 132 |
+
// Render any QR codes
|
| 133 |
+
$('.simbaotp_qr_container').qrcode({
|
| 134 |
+
'render': 'image',
|
| 135 |
+
'text': $('.simbaotp_qr_container:first').data('qrcode'),
|
| 136 |
+
});
|
| 137 |
+
|
| 138 |
+
function update_otp_code() {
|
| 139 |
+
$('.simba_current_otp').html('<em><?php echo esc_attr(__('Updating...', 'all-in-one-wp-security-and-firewall'));?></em>');
|
| 140 |
+
|
| 141 |
+
$.post('<?php echo esc_js($ajax_url);?>', {
|
| 142 |
+
action: 'simbatfa_shared_ajax',
|
| 143 |
+
subaction: 'refreshotp',
|
| 144 |
+
nonce: '<?php echo esc_js(wp_create_nonce('tfa_shared_nonce'));?>'
|
| 145 |
+
}, function(response) {
|
| 146 |
+
var got_code = '';
|
| 147 |
+
try {
|
| 148 |
+
var resp = JSON.parse(response);
|
| 149 |
+
got_code = resp.code;
|
| 150 |
+
} catch(err) {
|
| 151 |
+
<?php if (!isset($also_try)) { ?>
|
| 152 |
+
alert("<?php echo esc_js(__('Response:', 'all-in-one-wp-security-and-firewall')); ?> "+response);
|
| 153 |
+
<?php } ?>
|
| 154 |
+
console.log(response);
|
| 155 |
+
console.log(err);
|
| 156 |
+
}
|
| 157 |
+
<?php
|
| 158 |
+
if (isset($also_try)) {
|
| 159 |
+
?>
|
| 160 |
+
$.post('<?php echo esc_js($also_try);?>', {
|
| 161 |
+
action: 'simbatfa_shared_ajax',
|
| 162 |
+
subaction: 'refreshotp',
|
| 163 |
+
nonce: '<?php echo esc_js(wp_create_nonce('tfa_shared_nonce'));?>'
|
| 164 |
+
}, function(response) {
|
| 165 |
+
try {
|
| 166 |
+
var resp = JSON.parse(response);
|
| 167 |
+
if (resp.code) {
|
| 168 |
+
$('.simba_current_otp').html(resp.code);
|
| 169 |
+
} else {
|
| 170 |
+
console.log(response);
|
| 171 |
+
console.log("TFA: no code found");
|
| 172 |
+
}
|
| 173 |
+
} catch(err) {
|
| 174 |
+
alert("<?php echo esc_js(__('Response:', 'all-in-one-wp-security-and-firewall')); ?> "+response);
|
| 175 |
+
console.log(response);
|
| 176 |
+
console.log(err);
|
| 177 |
+
}
|
| 178 |
+
});
|
| 179 |
+
<?php } else { ?>
|
| 180 |
+
if ('' != got_code) {
|
| 181 |
+
$('.simba_current_otp').html(got_code);
|
| 182 |
+
} else {
|
| 183 |
+
console.log("TFA: no code found");
|
| 184 |
+
}
|
| 185 |
+
<?php } ?>
|
| 186 |
+
});
|
| 187 |
+
}
|
| 188 |
+
|
| 189 |
+
var min_refresh_after = 30;
|
| 190 |
+
|
| 191 |
+
if (0 == $('body.settings_page_two-factor-auth').length) {
|
| 192 |
+
$('.simba_current_otp').each(function(ind, obj) {
|
| 193 |
+
var refresh_after = $(obj).data('refresh_after');
|
| 194 |
+
if (refresh_after > 0 && refresh_after < min_refresh_after) {
|
| 195 |
+
min_refresh_after = refresh_after;
|
| 196 |
+
}
|
| 197 |
+
});
|
| 198 |
+
|
| 199 |
+
// Update after the given seconds, and then every 30 seconds
|
| 200 |
+
setTimeout(function() {
|
| 201 |
+
setInterval(update_otp_code, 30000)
|
| 202 |
+
update_otp_code();
|
| 203 |
+
}, min_refresh_after * 1000);
|
| 204 |
+
}
|
| 205 |
+
|
| 206 |
+
// Handle clicks on the 'refresh' link
|
| 207 |
+
$('.simbaotp_refresh').on('click', function(e) {
|
| 208 |
+
e.preventDefault();
|
| 209 |
+
update_otp_code();
|
| 210 |
+
});
|
| 211 |
+
|
| 212 |
+
$('#tfa_trusted_devices_box').on('click', '.simbatfa-trust-remove', function(e) {
|
| 213 |
+
e.preventDefault();
|
| 214 |
+
var device_id = $(this).data('trusted-device-id');
|
| 215 |
+
$(this).parents('.simbatfa_trusted_device').css('opacity', '0.5');
|
| 216 |
+
if ('undefined' !== typeof device_id) {
|
| 217 |
+
$.post('<?php echo esc_js($ajax_url);?>', {
|
| 218 |
+
action: 'simbatfa_shared_ajax',
|
| 219 |
+
subaction: 'untrust_device',
|
| 220 |
+
nonce: '<?php echo esc_js(wp_create_nonce('tfa_shared_nonce'));?>',
|
| 221 |
+
device_id: device_id
|
| 222 |
+
}, function(response) {
|
| 223 |
+
var resp = JSON.parse(response);
|
| 224 |
+
if (resp.hasOwnProperty('trusted_list')) {
|
| 225 |
+
$('#tfa_trusted_devices_box_inner').html(resp.trusted_list);
|
| 226 |
+
}
|
| 227 |
+
});
|
| 228 |
+
}
|
| 229 |
+
});
|
| 230 |
+
});
|
| 231 |
+
</script>
|
| 232 |
+
<?php
|
| 233 |
+
}
|
| 234 |
+
|
| 235 |
+
/**
|
| 236 |
+
* Return a link to refresh the current OTP code
|
| 237 |
+
*
|
| 238 |
+
* @return String
|
| 239 |
+
*/
|
| 240 |
+
public function refresh_current_otp_link() {
|
| 241 |
+
return '<a href="#" class="simbaotp_refresh">'.__('(update)', 'all-in-one-wp-security-and-firewall').'</a>';
|
| 242 |
+
}
|
| 243 |
+
|
| 244 |
+
/**
|
| 245 |
+
* Echo the radio buttons for changing between TOTP/HOTP
|
| 246 |
+
*
|
| 247 |
+
* TODO: Hide this choice on new installs (TOTP only)
|
| 248 |
+
*
|
| 249 |
+
* @param Integer $user_id
|
| 250 |
+
*/
|
| 251 |
+
protected function print_algorithm_choice_radios($user_id) {
|
| 252 |
+
if (!$user_id) return;
|
| 253 |
+
|
| 254 |
+
$types = array(
|
| 255 |
+
'totp' => __('TOTP (time based - most common algorithm; used by Google Authenticator)', 'all-in-one-wp-security-and-firewall'),
|
| 256 |
+
'hotp' => __('HOTP (event based)', 'all-in-one-wp-security-and-firewall')
|
| 257 |
+
);
|
| 258 |
+
|
| 259 |
+
$setting = $this->get_user_otp_algorithm($user_id);
|
| 260 |
+
|
| 261 |
+
foreach ($types as $id => $name) {
|
| 262 |
+
print '<input type="radio" id="tfa_algorithm_type_'.esc_attr($id).'" name="tfa_algorithm_type" value="'.$id.'" '.($setting == $id ? 'checked="checked"' :'').'> <label for="tfa_algorithm_type_'.esc_attr($id).'">'.$name."</label><br>\n";
|
| 263 |
+
}
|
| 264 |
+
}
|
| 265 |
+
|
| 266 |
+
/**
|
| 267 |
+
* Print out the advanced settings box - choice of algorithm
|
| 268 |
+
*
|
| 269 |
+
* @param Boolean|Callable $submit_button_callback - if not a callback, then <form> tags will be added
|
| 270 |
+
*/
|
| 271 |
+
public function advanced_settings_box($submit_button_callback = false) {
|
| 272 |
+
|
| 273 |
+
global $current_user;
|
| 274 |
+
$algorithm_type = $this->get_user_otp_algorithm($current_user->ID);
|
| 275 |
+
|
| 276 |
+
?>
|
| 277 |
+
<h2 id="tfa_advanced_heading" style="clear:both;"><?php _e('Advanced settings', 'all-in-one-wp-security-and-firewall'); ?></h2>
|
| 278 |
+
|
| 279 |
+
<div id="tfa_advanced_box" class="tfa_settings_form" style="margin-top: 20px;">
|
| 280 |
+
|
| 281 |
+
<?php if (false === $submit_button_callback) { ?>
|
| 282 |
+
<form method="post" action="<?php print esc_url(add_query_arg('settings-updated', 'true', $_SERVER['REQUEST_URI'])); ?>">
|
| 283 |
+
<?php wp_nonce_field('tfa_algorithm', '_tfa_algorithm_nonce', false, true); ?>
|
| 284 |
+
<?php } ?>
|
| 285 |
+
|
| 286 |
+
<?php _e('Choose which algorithm for One Time Passwords you want to use.', 'all-in-one-wp-security-and-firewall'); ?>
|
| 287 |
+
<p>
|
| 288 |
+
<?php
|
| 289 |
+
$this->print_algorithm_choice_radios($current_user->ID);
|
| 290 |
+
if ('hotp' == $algorithm_type) {
|
| 291 |
+
$counter = $this->getUserCounter($current_user->ID);
|
| 292 |
+
print '<br>'.__('Your counter on the server is currently on', 'all-in-one-wp-security-and-firewall').': '.$counter;
|
| 293 |
+
}
|
| 294 |
+
?>
|
| 295 |
+
|
| 296 |
+
</p>
|
| 297 |
+
<?php if (false === $submit_button_callback) { submit_button(); echo '</form>'; } else { call_user_func($submit_button_callback); } ?>
|
| 298 |
+
|
| 299 |
+
</div>
|
| 300 |
+
<?php
|
| 301 |
+
}
|
| 302 |
+
|
| 303 |
+
/**
|
| 304 |
+
* Return an HTML snippet for the current OTP code
|
| 305 |
+
*
|
| 306 |
+
* @param Integer|Boolean $user_id
|
| 307 |
+
*
|
| 308 |
+
* @return String
|
| 309 |
+
*/
|
| 310 |
+
public function current_otp_code($user_id = false) {
|
| 311 |
+
global $current_user;
|
| 312 |
+
if (false == $user_id) $user_id = $current_user->ID;
|
| 313 |
+
$tfa_priv_key_64 = get_user_meta($user_id, 'tfa_priv_key_64', true);
|
| 314 |
+
if (!$tfa_priv_key_64) $tfa_priv_key_64 = $this->addPrivateKey($user_id);
|
| 315 |
+
$time_now = time();
|
| 316 |
+
$refresh_after = 30 - ($time_now % 30);
|
| 317 |
+
return '<span class="simba_current_otp" data-refresh_after="'.$refresh_after.'">'.$this->generateOTP($user_id, $tfa_priv_key_64).'</span>';
|
| 318 |
+
}
|
| 319 |
+
|
| 320 |
+
/**
|
| 321 |
+
* Runs upon the WP 'init' action - check for possible private key reset request from the user
|
| 322 |
+
*/
|
| 323 |
+
public function check_possible_reset() {
|
| 324 |
+
if (!empty($_GET['simbatfa_priv_key_reset']) && !empty($_REQUEST['nonce']) && wp_verify_nonce($_REQUEST['nonce'], 'simbatfa_reset_private_key')) {
|
| 325 |
+
$this->reset_private_key_and_emergency_codes();
|
| 326 |
+
exit;
|
| 327 |
+
}
|
| 328 |
+
}
|
| 329 |
+
|
| 330 |
+
/**
|
| 331 |
+
* Remove private key and emergency codes for the specified (or logged-in) user
|
| 332 |
+
*
|
| 333 |
+
* @param Boolean|Integer $user_id - WP user ID, or false for the currently logged-in user
|
| 334 |
+
* @param Boolean $redirect - if this is not false, then a redirection will occur - where to depends upon the value of $_REQUEST['noredirect']
|
| 335 |
+
*/
|
| 336 |
+
public function reset_private_key_and_emergency_codes($user_id = false, $redirect = true) {
|
| 337 |
+
|
| 338 |
+
if (!$user_id) {
|
| 339 |
+
global $current_user;
|
| 340 |
+
$user_id = $current_user->ID;
|
| 341 |
+
}
|
| 342 |
+
|
| 343 |
+
delete_user_meta($user_id, 'tfa_priv_key_64');
|
| 344 |
+
delete_user_meta($user_id, 'simba_tfa_emergency_codes_64');
|
| 345 |
+
|
| 346 |
+
if (!$redirect) return;
|
| 347 |
+
|
| 348 |
+
if (empty($_REQUEST['noredirect'])) {
|
| 349 |
+
// TODO: Re-factoring
|
| 350 |
+
wp_safe_redirect(admin_url('admin.php').'?page='. $this->tfa->get_user_settings_page_slug() .'&settings-updated=1');
|
| 351 |
+
} else {
|
| 352 |
+
$url = (is_ssl() ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . remove_query_arg(array('simbatfa_priv_key_reset', 'noredirect', 'nonce'));
|
| 353 |
+
wp_redirect(esc_url_raw($url));
|
| 354 |
+
}
|
| 355 |
+
}
|
| 356 |
+
|
| 357 |
+
/**
|
| 358 |
+
* Return HTML for a link to reset the current user's private key
|
| 359 |
+
*
|
| 360 |
+
* @return String
|
| 361 |
+
*/
|
| 362 |
+
public function reset_link() {
|
| 363 |
+
|
| 364 |
+
// TODO: Refactoring
|
| 365 |
+
$url_base = is_admin() ? admin_url('admin.php').'?page='. $this->tfa->get_user_settings_page_slug() .'&settings-updated=1' : (( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST']);
|
| 366 |
+
|
| 367 |
+
$add_query_args = array('simbatfa_priv_key_reset' => 1);
|
| 368 |
+
|
| 369 |
+
if (!is_admin()) $add_query_args['noredirect'] = 1;
|
| 370 |
+
|
| 371 |
+
$url = $url_base.add_query_arg($add_query_args);
|
| 372 |
+
|
| 373 |
+
$url = wp_nonce_url($url, 'simbatfa_reset_private_key', 'nonce');
|
| 374 |
+
|
| 375 |
+
return '<a href="javascript:if(confirm(\''.__('Warning: if you reset this key you will have to update your apps with the new one. Are you sure you want this?', 'all-in-one-wp-security-and-firewall').'\')) { window.location = \''.esc_js($url).'\'; }">'.__('Reset private key', 'all-in-one-wp-security-and-firewall').'</a>';
|
| 376 |
+
|
| 377 |
+
}
|
| 378 |
+
|
| 379 |
+
/**
|
| 380 |
+
* Output the current codes box
|
| 381 |
+
*
|
| 382 |
+
* @param Boolean|Integer $user_id
|
| 383 |
+
*/
|
| 384 |
+
public function current_codes_box($user_id = false) {
|
| 385 |
+
|
| 386 |
+
global $current_user;
|
| 387 |
+
if (false == $user_id) $user_id = $current_user->ID;
|
| 388 |
+
|
| 389 |
+
$admin = is_admin();
|
| 390 |
+
|
| 391 |
+
$this->add_footer();
|
| 392 |
+
|
| 393 |
+
$url = preg_replace('/^https?:\/\//i', '', site_url());
|
| 394 |
+
|
| 395 |
+
// TODO Replace this with an appropriate method
|
| 396 |
+
$tfa_priv_key_64 = get_user_meta($user_id, 'tfa_priv_key_64', true);
|
| 397 |
+
if (!$tfa_priv_key_64) $tfa_priv_key_64 = $this->addPrivateKey($user_id);
|
| 398 |
+
|
| 399 |
+
$tfa_priv_key = trim($this->getPrivateKeyPlain($tfa_priv_key_64, $user_id), "\x00..\x1F");
|
| 400 |
+
|
| 401 |
+
$tfa_priv_key_32 = Base32::encode($tfa_priv_key);
|
| 402 |
+
|
| 403 |
+
$algorithm_type = $this->get_user_otp_algorithm($user_id);
|
| 404 |
+
|
| 405 |
+
if ($admin && $current_user->ID != $user_id) {
|
| 406 |
+
$user = get_user_by('id', $user_id);
|
| 407 |
+
$user_descrip = htmlspecialchars($user->user_nicename.' - '.$user->user_email);
|
| 408 |
+
echo '<h2>'.sprintf(__('Current codes (login: %s)', 'all-in-one-wp-security-and-firewall'), $user_descrip).'</h2>';
|
| 409 |
+
}
|
| 410 |
+
|
| 411 |
+
?>
|
| 412 |
+
<div class="postbox" style="clear:both;">
|
| 413 |
+
|
| 414 |
+
<?php if ($admin) { ?>
|
| 415 |
+
<h3 style="padding: 10px 6px 0px; margin:4px 0 0; cursor: default;">
|
| 416 |
+
<span style="cursor: default;"><?php echo __('Current one-time password', 'all-in-one-wp-security-and-firewall').' ';
|
| 417 |
+
if ($current_user->ID == $user_id) { echo $this->refresh_current_otp_link(); } ?>
|
| 418 |
+
</span>
|
| 419 |
+
<div class="inside">
|
| 420 |
+
<p><strong style="font-size: 3em;"><?php echo $this->current_otp_code($user_id); ?></strong></p>
|
| 421 |
+
</div>
|
| 422 |
+
</h3>
|
| 423 |
+
<?php } else { ?>
|
| 424 |
+
|
| 425 |
+
<div class="inside">
|
| 426 |
+
<p class="simbatfa-frontend-current-otp" style="font-size: 1.5em; margin-top:6px;">
|
| 427 |
+
<strong><?php echo __('Current one-time password', 'all-in-one-wp-security-and-firewall').' '.$this->refresh_current_otp_link(); ?></strong> :
|
| 428 |
+
|
| 429 |
+
<?php
|
| 430 |
+
// TODO: Compare this with what's in current_otp_code() - why are we not using that?
|
| 431 |
+
$time_now = time();
|
| 432 |
+
$refresh_after = 30 - ($time_now % 30);
|
| 433 |
+
?><span class="simba_current_otp" data-refresh_after="<?php echo $refresh_after; ?>"><?php print $this->generateOTP($user_id, $tfa_priv_key_64); ?></span>
|
| 434 |
+
|
| 435 |
+
</p>
|
| 436 |
+
</div>
|
| 437 |
+
|
| 438 |
+
<?php } ?>
|
| 439 |
+
|
| 440 |
+
<?php if ($admin) { ?>
|
| 441 |
+
<h3 style="padding-left: 10px; cursor: default;">
|
| 442 |
+
<span style="cursor: default;"><?php _e('Setting up - either scan the code, or type in the private key', 'all-in-one-wp-security-and-firewall'); ?></span>
|
| 443 |
+
</h3>
|
| 444 |
+
<?php } else { ?>
|
| 445 |
+
<h2><?php _e('Setting up', 'all-in-one-wp-security-and-firewall'); ?></h2>
|
| 446 |
+
<?php } ?>
|
| 447 |
+
|
| 448 |
+
<div class="inside">
|
| 449 |
+
<p>
|
| 450 |
+
<?php
|
| 451 |
+
_e('For OTP apps that support using a camera to scan a setup code (below), that is the quickest way to set the app up (e.g. with Duo Mobile, Google Authenticator).', 'all-in-one-wp-security-and-firewall');
|
| 452 |
+
echo ' ';
|
| 453 |
+
_e('Otherwise, you can type the textual private key (shown below) into your app. Always keep private keys secret.', 'all-in-one-wp-security-and-firewall');
|
| 454 |
+
?>
|
| 455 |
+
|
| 456 |
+
<?php printf(__('You are currently using %s, %s', 'all-in-one-wp-security-and-firewall'), strtoupper($algorithm_type), ($algorithm_type == 'totp') ? __('a time based algorithm', 'all-in-one-wp-security-and-firewall') : __('an event based algorithm', 'all-in-one-wp-security-and-firewall')); ?>.
|
| 457 |
+
</p>
|
| 458 |
+
|
| 459 |
+
<?php $qr_url = $this->tfa_qr_code_url($algorithm_type, $url, $tfa_priv_key, $user_id); ?>
|
| 460 |
+
<div style="float: left; padding-right: 20px;" class="simbaotp_qr_container" data-qrcode="<?php echo esc_attr($qr_url); ?>"></div>
|
| 461 |
+
|
| 462 |
+
<p>
|
| 463 |
+
<?php
|
| 464 |
+
$this->print_private_keys('full', $user_id);
|
| 465 |
+
if ($current_user->ID == $user_id) {
|
| 466 |
+
echo $this->reset_link($admin);
|
| 467 |
+
} else {
|
| 468 |
+
echo '<a id="tfa-reset-privkey-for-user" data-user_id="'.$user_id.'" href="#">'.__('Reset private key', 'all-in-one-wp-security-and-firewall').'</a>';
|
| 469 |
+
}
|
| 470 |
+
?>
|
| 471 |
+
</p>
|
| 472 |
+
|
| 473 |
+
<?php
|
| 474 |
+
if ($admin || false !== apply_filters('simba_tfa_emergency_codes_user_settings', false, $user_id)) {
|
| 475 |
+
?>
|
| 476 |
+
|
| 477 |
+
<div style="min-height: 100px;">
|
| 478 |
+
<h3 class="normal" style="cursor: default"><?php _e('Emergency codes', 'all-in-one-wp-security-and-firewall'); ?></h3>
|
| 479 |
+
<?php
|
| 480 |
+
$default_text = '<a href="'.esc_url($this->tfa->get_premium_version_url()).'">'.__('One-time emergency codes are a feature of the Premium version of this plugin.', 'all-in-one-wp-security-and-firewall').'</a>';
|
| 481 |
+
echo apply_filters('simba_tfa_emergency_codes_user_settings', $default_text, $user_id);
|
| 482 |
+
?>
|
| 483 |
+
</div>
|
| 484 |
+
|
| 485 |
+
<?php } ?>
|
| 486 |
+
</div>
|
| 487 |
+
|
| 488 |
+
</div>
|
| 489 |
+
<?php
|
| 490 |
+
}
|
| 491 |
+
|
| 492 |
+
/**
|
| 493 |
+
* Print out HTML showing the specified user's private key
|
| 494 |
+
*
|
| 495 |
+
* @param String $type
|
| 496 |
+
* @param Boolean|Integer $user_id
|
| 497 |
+
*/
|
| 498 |
+
public function print_private_keys($type = 'full', $user_id = false) {
|
| 499 |
+
|
| 500 |
+
global $current_user;
|
| 501 |
+
if ($user_id == false) $user_id = $current_user->ID;
|
| 502 |
+
|
| 503 |
+
$tfa_priv_key_64 = get_user_meta($user_id, 'tfa_priv_key_64', true);
|
| 504 |
+
if (!$tfa_priv_key_64) $tfa_priv_key_64 = $this->addPrivateKey($user_id);
|
| 505 |
+
|
| 506 |
+
$tfa_priv_key = trim($this->getPrivateKeyPlain($tfa_priv_key_64, $user_id), "\x00..\x1F");
|
| 507 |
+
|
| 508 |
+
$tfa_priv_key_32 = Base32::encode($tfa_priv_key);
|
| 509 |
+
|
| 510 |
+
if ('full' == $type) {
|
| 511 |
+
?>
|
| 512 |
+
<strong><?php echo __('Private key (base 32 - used by Google Authenticator and Authy):', 'all-in-one-wp-security-and-firewall');?></strong>
|
| 513 |
+
<?php echo htmlspecialchars($tfa_priv_key_32); ?><br>
|
| 514 |
+
|
| 515 |
+
<strong><?php echo __('Private key:', 'all-in-one-wp-security-and-firewall');?></strong>
|
| 516 |
+
<?php echo htmlspecialchars($tfa_priv_key); ?><br>
|
| 517 |
+
<?php
|
| 518 |
+
} elseif ('plain' == $type) {
|
| 519 |
+
echo htmlspecialchars($tfa_priv_key);
|
| 520 |
+
} elseif ('base32' == $type) {
|
| 521 |
+
echo htmlspecialchars($tfa_priv_key_32);
|
| 522 |
+
} elseif ('base64' == $type) {
|
| 523 |
+
echo htmlspecialchars($tfa_priv_key_64);
|
| 524 |
+
}
|
| 525 |
+
}
|
| 526 |
+
|
| 527 |
+
/**
|
| 528 |
+
* Return the URL for a QR code image
|
| 529 |
+
*
|
| 530 |
+
* @param String $algorithm_type - 'totp' or 'hotp'
|
| 531 |
+
* @param String $url
|
| 532 |
+
* @param String $tfa_priv_key
|
| 533 |
+
* @param Boolean|Integer $user_id
|
| 534 |
+
*
|
| 535 |
+
* @return String
|
| 536 |
+
*/
|
| 537 |
+
public function tfa_qr_code_url($algorithm_type, $url, $tfa_priv_key, $user_id = false) {
|
| 538 |
+
global $current_user;
|
| 539 |
+
|
| 540 |
+
$user = (false == $user_id) ? $current_user : get_user_by('id', $user_id);
|
| 541 |
+
|
| 542 |
+
$encode = 'otpauth://'.$algorithm_type.'/'.$url.':'.rawurlencode($user->user_login).'?secret='.Base32::encode($tfa_priv_key).'&issuer='.$url.'&counter='.$this->getUserCounter($user->ID);
|
| 543 |
+
|
| 544 |
+
return $encode;
|
| 545 |
+
}
|
| 546 |
+
|
| 547 |
+
/**
|
| 548 |
+
* See if HOTP is off sync, and if show, print out a message
|
| 549 |
+
*/
|
| 550 |
+
public function tfa_show_hotp_off_sync_message() {
|
| 551 |
+
|
| 552 |
+
global $current_user;
|
| 553 |
+
$is_off_sync = get_user_meta($current_user->ID, 'tfa_hotp_off_sync', true);
|
| 554 |
+
if (!$is_off_sync) return;
|
| 555 |
+
|
| 556 |
+
?>
|
| 557 |
+
<div class="error">
|
| 558 |
+
<h3><?php _e('Two Factor Authentication re-sync needed', 'all-in-one-wp-security-and-firewall');?></h3>
|
| 559 |
+
<p>
|
| 560 |
+
<?php _e('You need to resync your device for Two Factor Authentication since the OTP you last used is many steps ahead of the server.', 'all-in-one-wp-security-and-firewall'); ?>
|
| 561 |
+
<br>
|
| 562 |
+
<?php _e('Please re-sync or you might not be able to log in if you generate more OTPs without logging in.', 'all-in-one-wp-security-and-firewall');?>
|
| 563 |
+
<br><br>
|
| 564 |
+
<a href="<?php echo esc_url(wp_nonce_url('admin.php?page='. $this->tfa->get_user_settings_page_slug() .'&warning_button_clicked=1', 'tfaresync', 'resyncnonce')); ?>" class="button"><?php _e('Click here and re-scan the QR-Code', 'all-in-one-wp-security-and-firewall');?></a>
|
| 565 |
+
</p>
|
| 566 |
+
</div>
|
| 567 |
+
|
| 568 |
+
<?php
|
| 569 |
+
|
| 570 |
+
}
|
| 571 |
+
|
| 572 |
+
/**
|
| 573 |
+
* Runs upon the WP action plugins_loaded
|
| 574 |
+
*/
|
| 575 |
+
public function plugins_loaded() {
|
| 576 |
+
$this->time_window_size = apply_filters('simbatfa_time_window_size', 30);
|
| 577 |
+
$this->check_back_time_windows = apply_filters('simbatfa_check_back_time_windows', 2);
|
| 578 |
+
$this->check_forward_time_windows = apply_filters('simbatfa_check_forward_time_windows', 1);
|
| 579 |
+
$this->check_forward_counter_window = apply_filters('simbatfa_check_forward_counter_window', 20);
|
| 580 |
+
|
| 581 |
+
$this->salt_prefix = defined('AUTH_SALT') ? AUTH_SALT : wp_salt('auth');
|
| 582 |
+
$this->pw_prefix = defined('AUTH_KEY') ? AUTH_KEY : get_site_option('auth_key');
|
| 583 |
+
}
|
| 584 |
+
|
| 585 |
+
/**
|
| 586 |
+
* Generate the current code for a specified user
|
| 587 |
+
*
|
| 588 |
+
* @param $user_id Integer - WordPress user ID
|
| 589 |
+
*
|
| 590 |
+
* @return String|Boolean - false if not set up
|
| 591 |
+
*/
|
| 592 |
+
public function get_current_code($user_id) {
|
| 593 |
+
|
| 594 |
+
$tfa_priv_key_64 = get_user_meta($user_id, 'tfa_priv_key_64', true);
|
| 595 |
+
|
| 596 |
+
if (!$tfa_priv_key_64) return false;
|
| 597 |
+
|
| 598 |
+
return $this->generateOTP($user_id, $tfa_priv_key_64);
|
| 599 |
+
|
| 600 |
+
}
|
| 601 |
+
|
| 602 |
+
public function print_default_hmac_radios() {
|
| 603 |
+
|
| 604 |
+
$setting = $this->tfa->get_option('tfa_default_hmac');
|
| 605 |
+
if (!$setting) $setting = $this->default_hmac;
|
| 606 |
+
|
| 607 |
+
$types = array('totp' => __('TOTP (time based - most common algorithm; used by Google Authenticator)', 'all-in-one-wp-security-and-firewall'), 'hotp' => __('HOTP (event based)', 'all-in-one-wp-security-and-firewall'));
|
| 608 |
+
|
| 609 |
+
foreach ($types as $id => $name) {
|
| 610 |
+
print '<input type="radio" id="tfa_default_hmac_'.esc_attr($id).'" name="tfa_default_hmac" value="'.$id.'" '.($setting == $id ? 'checked="checked"' :'').'> '.'<label for="tfa_default_hmac_'.esc_attr($id).'">'."$name</label><br>\n";
|
| 611 |
+
}
|
| 612 |
+
}
|
| 613 |
+
|
| 614 |
+
public function generateOTP($user_ID, $key_b64, $length = 6, $counter = false) {
|
| 615 |
+
|
| 616 |
+
$length = $length ? (int)$length : 6;
|
| 617 |
+
|
| 618 |
+
$key = $this->decryptString($key_b64, $user_ID);
|
| 619 |
+
$alg = $this->get_user_otp_algorithm($user_ID);
|
| 620 |
+
|
| 621 |
+
if ('hotp' == $alg) {
|
| 622 |
+
$db_counter = $this->getUserCounter($user_ID);
|
| 623 |
+
|
| 624 |
+
$counter = $counter ? $counter : $db_counter;
|
| 625 |
+
$otp_res = $this->otp_helper->generateByCounter($key, $counter);
|
| 626 |
+
} else {
|
| 627 |
+
//time() is supposed to be UTC
|
| 628 |
+
$time = $counter ? $counter : time();
|
| 629 |
+
$otp_res = $this->otp_helper->generateByTime($key, $this->time_window_size, $time);
|
| 630 |
+
}
|
| 631 |
+
$code = $otp_res->toHotp($length);
|
| 632 |
+
|
| 633 |
+
return $code;
|
| 634 |
+
}
|
| 635 |
+
|
| 636 |
+
/**
|
| 637 |
+
* Generate a list of OTP codes based on the user, key and time window
|
| 638 |
+
*
|
| 639 |
+
* @param Integer $user_ID - user ID
|
| 640 |
+
* @param String $key_b64 - the user's private key, in base64 format
|
| 641 |
+
*
|
| 642 |
+
* @return Array
|
| 643 |
+
*/
|
| 644 |
+
private function generate_otps_for_login_check($user_ID, $key_b64) {
|
| 645 |
+
$key = trim($this->decryptString($key_b64, $user_ID));
|
| 646 |
+
$alg = $this->get_user_otp_algorithm($user_ID);
|
| 647 |
+
|
| 648 |
+
if ('totp' == $alg) {
|
| 649 |
+
$otp_res = $this->otp_helper->generateByTimeWindow($key, $this->time_window_size, -1*$this->check_back_time_windows, $this->check_forward_time_windows);
|
| 650 |
+
} elseif ('hotp' == $alg) {
|
| 651 |
+
|
| 652 |
+
$counter = $this->getUserCounter($user_ID);
|
| 653 |
+
|
| 654 |
+
$otp_res = array();
|
| 655 |
+
|
| 656 |
+
for ($i = 0; $i < $this->check_forward_counter_window; $i++) {
|
| 657 |
+
$otp_res[] = $this->otp_helper->generateByCounter($key, $counter+$i);
|
| 658 |
+
}
|
| 659 |
+
}
|
| 660 |
+
return $otp_res;
|
| 661 |
+
}
|
| 662 |
+
|
| 663 |
+
|
| 664 |
+
/**
|
| 665 |
+
* Generate a private key for the user.
|
| 666 |
+
*
|
| 667 |
+
* @param Integer $user_id - WordPress user ID
|
| 668 |
+
* @param Boolean|String $key
|
| 669 |
+
*
|
| 670 |
+
* @return String
|
| 671 |
+
*/
|
| 672 |
+
public function addPrivateKey($user_id, $key = false) {
|
| 673 |
+
|
| 674 |
+
// To work with Google Authenticator it has to be 10 bytes = 16 chars in base32
|
| 675 |
+
$code = $key ? $key : strtoupper($this->randString(10));
|
| 676 |
+
|
| 677 |
+
// Encrypt the key
|
| 678 |
+
$code = $this->encryptString($code, $user_id);
|
| 679 |
+
|
| 680 |
+
// Add private key to usermeta
|
| 681 |
+
update_user_meta($user_id, 'tfa_priv_key_64', $code);
|
| 682 |
+
|
| 683 |
+
$alg = $this->get_user_otp_algorithm($user_id);
|
| 684 |
+
|
| 685 |
+
// This hook is used for generation of emergency codes to accompany the key
|
| 686 |
+
do_action('simba_tfa_adding_private_key', $alg, $user_id, $code, $this);
|
| 687 |
+
|
| 688 |
+
$this->changeUserAlgorithmTo($user_id, $alg);
|
| 689 |
+
|
| 690 |
+
return $code;
|
| 691 |
+
}
|
| 692 |
+
|
| 693 |
+
/**
|
| 694 |
+
* Port over keys that were encrypted with mcrypt and its non-compliant padding scheme, so that if the site is ever migrated to a server without mcrypt, they can still be decrypted
|
| 695 |
+
*/
|
| 696 |
+
public function potentially_port_private_keys() {
|
| 697 |
+
|
| 698 |
+
$simba_tfa_priv_key_format = get_site_option('simba_tfa_priv_key_format', false);
|
| 699 |
+
|
| 700 |
+
if ($simba_tfa_priv_key_format >= 1 || !function_exists('openssl_encrypt')) return;
|
| 701 |
+
|
| 702 |
+
$attempts = 0;
|
| 703 |
+
$successes = 0;
|
| 704 |
+
|
| 705 |
+
error_log("TFA: Beginning attempt to port private key encryption over to openssl");
|
| 706 |
+
|
| 707 |
+
global $wpdb;
|
| 708 |
+
|
| 709 |
+
$sql = "SELECT user_id, meta_value FROM ".$wpdb->usermeta." WHERE meta_key = 'tfa_priv_key_64'";
|
| 710 |
+
|
| 711 |
+
$user_results = $wpdb->get_results($sql);
|
| 712 |
+
|
| 713 |
+
foreach ($user_results as $u) {
|
| 714 |
+
$dec_openssl = $this->decryptString($u->meta_value, $u->user_id, true);
|
| 715 |
+
|
| 716 |
+
$ported = false;
|
| 717 |
+
if ('' == $dec_openssl) {
|
| 718 |
+
|
| 719 |
+
$attempts++;
|
| 720 |
+
|
| 721 |
+
$dec_default = $this->decryptString($u->meta_value, $u->user_id);
|
| 722 |
+
|
| 723 |
+
if ('' != $dec_default) {
|
| 724 |
+
|
| 725 |
+
$enc = $this->encryptString($dec_default, $u->user_id);
|
| 726 |
+
|
| 727 |
+
if ($enc) {
|
| 728 |
+
|
| 729 |
+
$ported = true;
|
| 730 |
+
$successes++;
|
| 731 |
+
update_user_meta($u->user_id, 'tfa_priv_key_64', $enc);
|
| 732 |
+
}
|
| 733 |
+
}
|
| 734 |
+
|
| 735 |
+
}
|
| 736 |
+
|
| 737 |
+
if ($ported) {
|
| 738 |
+
error_log("TFA: Successfully ported the key for user with ID ".$u->user_id." over to openssl");
|
| 739 |
+
} else {
|
| 740 |
+
error_log("TFA: Failed to port the key for user with ID ".$u->user_id." over to openssl");
|
| 741 |
+
}
|
| 742 |
+
}
|
| 743 |
+
|
| 744 |
+
if ($attempts == 0 || $successes > 0) update_site_option('simba_tfa_priv_key_format', 1);
|
| 745 |
+
|
| 746 |
+
}
|
| 747 |
+
|
| 748 |
+
public function getPrivateKeyPlain($enc, $user_ID) {
|
| 749 |
+
$dec = $this->decryptString($enc, $user_ID);
|
| 750 |
+
$this->potentially_port_private_keys();
|
| 751 |
+
return $dec;
|
| 752 |
+
}
|
| 753 |
+
|
| 754 |
+
/**
|
| 755 |
+
* @param Integer $user_id - WP user ID
|
| 756 |
+
* @param Boolean $generate_if_empty - generate some new codes if the list is empty
|
| 757 |
+
*
|
| 758 |
+
* @return String - human-usable codes, separated by ', ' (or a human-readable message, if there were none)
|
| 759 |
+
*/
|
| 760 |
+
public function get_emergency_codes_as_string($user_id, $generate_if_empty = false) {
|
| 761 |
+
|
| 762 |
+
$codes = get_user_meta($user_id, 'simba_tfa_emergency_codes_64', true);
|
| 763 |
+
if (!is_array($codes)) $codes = array();
|
| 764 |
+
|
| 765 |
+
if ($generate_if_empty && empty($codes)) {
|
| 766 |
+
$tfa_priv_key = get_user_meta($user_id, 'tfa_priv_key_64', true);
|
| 767 |
+
$algorithm = get_user_meta($user_id, 'tfa_algorithm_type', true);
|
| 768 |
+
do_action('simba_tfa_emergency_codes_empty', $algorithm, $user_id, $tfa_priv_key, $this);
|
| 769 |
+
$codes = get_user_meta($user_id, 'simba_tfa_emergency_codes_64', true);
|
| 770 |
+
if (!is_array($codes)) $codes = array();
|
| 771 |
+
}
|
| 772 |
+
|
| 773 |
+
$emergency_str = '';
|
| 774 |
+
|
| 775 |
+
foreach ($codes as $p_code) {
|
| 776 |
+
$emergency_str .= $this->decryptString($p_code, $user_id).', ';
|
| 777 |
+
}
|
| 778 |
+
|
| 779 |
+
$emergency_str = rtrim($emergency_str, ', ');
|
| 780 |
+
|
| 781 |
+
$emergency_str = $emergency_str ? $emergency_str : '<em>'.__('There are no emergency codes left. You will need to reset your private key.', 'all-in-one-wp-security-and-firewall').'</em>';
|
| 782 |
+
|
| 783 |
+
return $emergency_str;
|
| 784 |
+
}
|
| 785 |
+
|
| 786 |
+
/**
|
| 787 |
+
* Check a code for a user (checks the code only - does not check activation status etc.)
|
| 788 |
+
*
|
| 789 |
+
* @param Integer $user_id - WP user ID
|
| 790 |
+
* @param String $user_code - the code to check
|
| 791 |
+
* @param Boolean $allow_emergency_code - whether to check against emergency codes
|
| 792 |
+
*
|
| 793 |
+
* @return Boolean
|
| 794 |
+
*/
|
| 795 |
+
public function check_code_for_user($user_id, $user_code, $allow_emergency_code = true) {
|
| 796 |
+
|
| 797 |
+
$tfa_priv_key = get_user_meta($user_id, 'tfa_priv_key_64', true);
|
| 798 |
+
// $tfa_last_login = get_user_meta($user_id, 'tfa_last_login', true); // Unused
|
| 799 |
+
$tfa_last_pws_arr = get_user_meta($user_id, 'tfa_last_pws', true);
|
| 800 |
+
$tfa_last_pws = @$tfa_last_pws_arr ? $tfa_last_pws_arr : array();
|
| 801 |
+
$alg = $this->get_user_otp_algorithm($user_id);
|
| 802 |
+
|
| 803 |
+
$current_time_window = intval(time()/30);
|
| 804 |
+
|
| 805 |
+
//Give the user 1,5 minutes time span to enter/retrieve the code
|
| 806 |
+
//Or check $this->check_forward_counter_window number of events if hotp
|
| 807 |
+
$codes = $this->generate_otps_for_login_check($user_id, $tfa_priv_key);
|
| 808 |
+
|
| 809 |
+
//A recently used code was entered; that's not OK.
|
| 810 |
+
if (in_array($this->hash($user_code, $user_id), $tfa_last_pws)) return false;
|
| 811 |
+
|
| 812 |
+
$match = false;
|
| 813 |
+
foreach ($codes as $index => $code) {
|
| 814 |
+
if (trim($code->toHotp(6)) == trim($user_code)) {
|
| 815 |
+
$match = true;
|
| 816 |
+
$found_index = $index;
|
| 817 |
+
break;
|
| 818 |
+
}
|
| 819 |
+
}
|
| 820 |
+
|
| 821 |
+
// Check emergency codes
|
| 822 |
+
if (!$match) {
|
| 823 |
+
$emergency_codes = $allow_emergency_code ? get_user_meta($user_id, 'simba_tfa_emergency_codes_64', true) : array();
|
| 824 |
+
|
| 825 |
+
if (!$emergency_codes) return $match;
|
| 826 |
+
|
| 827 |
+
$dec = array();
|
| 828 |
+
foreach ($emergency_codes as $emergency_code)
|
| 829 |
+
$dec[] = trim($this->decryptString(trim($emergency_code), $user_id));
|
| 830 |
+
|
| 831 |
+
$in_array = array_search($user_code, $dec);
|
| 832 |
+
$match = $in_array !== false;
|
| 833 |
+
|
| 834 |
+
//Remove emergency code
|
| 835 |
+
if ($match) {
|
| 836 |
+
array_splice($emergency_codes, $in_array, 1);
|
| 837 |
+
update_user_meta($user_id, 'simba_tfa_emergency_codes_64', $emergency_codes);
|
| 838 |
+
do_action('simba_tfa_emergency_code_used', $user_id, $emergency_codes);
|
| 839 |
+
}
|
| 840 |
+
|
| 841 |
+
} else {
|
| 842 |
+
//Add the used code as well so it cant be used again
|
| 843 |
+
//Keep the two last codes
|
| 844 |
+
$tfa_last_pws[] = $this->hash($user_code, $user_id);
|
| 845 |
+
$nr_of_old_to_save = $alg == 'hotp' ? $this->check_forward_counter_window : $this->check_back_time_windows;
|
| 846 |
+
|
| 847 |
+
if (count($tfa_last_pws) > $nr_of_old_to_save) array_splice($tfa_last_pws, 0, 1);
|
| 848 |
+
|
| 849 |
+
update_user_meta($user_id, 'tfa_last_pws', $tfa_last_pws);
|
| 850 |
+
}
|
| 851 |
+
|
| 852 |
+
if ($match) {
|
| 853 |
+
//Save the time window when the last successful login took place
|
| 854 |
+
update_user_meta($user_id, 'tfa_last_login', $current_time_window);
|
| 855 |
+
|
| 856 |
+
//Update the counter if HOTP was used
|
| 857 |
+
if ($alg == 'hotp') {
|
| 858 |
+
$counter = $this->getUserCounter($user_id);
|
| 859 |
+
|
| 860 |
+
$enc_new_counter = $this->encryptString($counter+1, $user_id);
|
| 861 |
+
update_user_meta($user_id, 'tfa_hotp_counter', $enc_new_counter);
|
| 862 |
+
|
| 863 |
+
if ($found_index > 10) update_user_meta($user_id, 'tfa_hotp_off_sync', 1);
|
| 864 |
+
}
|
| 865 |
+
}
|
| 866 |
+
|
| 867 |
+
return $match;
|
| 868 |
+
|
| 869 |
+
}
|
| 870 |
+
|
| 871 |
+
public function getUserCounter($user_ID) {
|
| 872 |
+
$enc_counter = get_user_meta($user_ID, 'tfa_hotp_counter', true);
|
| 873 |
+
return $enc_counter ? trim($this->decryptString(trim($enc_counter), $user_ID)) : '';
|
| 874 |
+
}
|
| 875 |
+
|
| 876 |
+
public function changeUserAlgorithmTo($user_id, $new_algorithm) {
|
| 877 |
+
update_user_meta($user_id, 'tfa_algorithm_type', $new_algorithm);
|
| 878 |
+
delete_user_meta($user_id, 'tfa_hotp_off_sync');
|
| 879 |
+
|
| 880 |
+
$counter_start = rand(13, 999999999);
|
| 881 |
+
$enc_counter_start = $this->encryptString($counter_start, $user_id);
|
| 882 |
+
|
| 883 |
+
if ('hotp' == $new_algorithm) {
|
| 884 |
+
update_user_meta($user_id, 'tfa_hotp_counter', $enc_counter_start);
|
| 885 |
+
} else {
|
| 886 |
+
delete_user_meta($user_id, 'tfa_hotp_counter');
|
| 887 |
+
}
|
| 888 |
+
}
|
| 889 |
+
|
| 890 |
+
/**
|
| 891 |
+
* Whether HOTP or TOTP is being used
|
| 892 |
+
*
|
| 893 |
+
* @param Integer $user_id - WordPress user ID
|
| 894 |
+
*
|
| 895 |
+
* @return String - 'hotp' or 'totp'
|
| 896 |
+
*/
|
| 897 |
+
public function get_user_otp_algorithm($user_id) {
|
| 898 |
+
|
| 899 |
+
$setting = get_user_meta($user_id, 'tfa_algorithm_type', true);
|
| 900 |
+
|
| 901 |
+
$default_hmac = $this->tfa->get_option('tfa_default_hmac');
|
| 902 |
+
if (!$default_hmac) $default_hmac = $this->default_hmac;
|
| 903 |
+
|
| 904 |
+
return $setting ? $setting : $default_hmac;
|
| 905 |
+
}
|
| 906 |
+
|
| 907 |
+
private function get_iv_size() {
|
| 908 |
+
// mcrypt first, for backwards compatibility
|
| 909 |
+
if (function_exists('mcrypt_get_iv_size')) {
|
| 910 |
+
return $GLOBALS['simba_two_factor_authentication']->is_mcrypt_deprecated() ? @mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC) : mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC);
|
| 911 |
+
} elseif (function_exists('openssl_cipher_iv_length')) {
|
| 912 |
+
return openssl_cipher_iv_length('AES-128-CBC');
|
| 913 |
+
}
|
| 914 |
+
throw new Exception('One of the mcrypt or openssl PHP modules needs to be installed');
|
| 915 |
+
}
|
| 916 |
+
|
| 917 |
+
private function encrypt($key, $string, $iv) {
|
| 918 |
+
// Prefer OpenSSL, because it uses correct padding, and its output can be decrypted by mcrypt - whereas, the converse is not true
|
| 919 |
+
if (function_exists('openssl_encrypt')) {
|
| 920 |
+
return openssl_encrypt($string, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv);
|
| 921 |
+
} elseif (function_exists('mcrypt_encrypt')) {
|
| 922 |
+
return $GLOBALS['simba_two_factor_authentication']->is_mcrypt_deprecated() ? @mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $string, MCRYPT_MODE_CBC, $iv) : mcrypt_encrypt(MCRYPT_RIJNDAEL_128, $key, $string, MCRYPT_MODE_CBC, $iv);
|
| 923 |
+
}
|
| 924 |
+
throw new Exception('One of the mcrypt or openssl PHP modules needs to be installed');
|
| 925 |
+
}
|
| 926 |
+
|
| 927 |
+
private function decrypt($key, $enc, $iv, $force_openssl = false) {
|
| 928 |
+
// Prefer mcrypt, because it can decrypt the output of both mcrypt_encrypt() and openssl_decrypt(), whereas (because of mcrypt_encrypt() using bad padding), the converse is not true
|
| 929 |
+
if (function_exists('mcrypt_decrypt') && !$force_openssl) {
|
| 930 |
+
return $GLOBALS['simba_two_factor_authentication']->is_mcrypt_deprecated() ? @mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $enc, MCRYPT_MODE_CBC, $iv) : mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $enc, MCRYPT_MODE_CBC, $iv);
|
| 931 |
+
} elseif (function_exists('openssl_decrypt')) {
|
| 932 |
+
$decrypted = openssl_decrypt($enc, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv);
|
| 933 |
+
if (false === $decrypted && !$force_openssl) {
|
| 934 |
+
$extra = function_exists('wp_debug_backtrace_summary') ? " backtrace: ".wp_debug_backtrace_summary() : '';
|
| 935 |
+
error_log("TFA decryption failure: was your site migrated to a server without mcrypt? You may need to install mcrypt, or disable TFA, in order to successfully decrypt data that was previously encrypted with mcrypt.$extra");
|
| 936 |
+
}
|
| 937 |
+
return $decrypted;
|
| 938 |
+
}
|
| 939 |
+
if ($force_openssl) return false;
|
| 940 |
+
throw new Exception('One of the mcrypt or openssl PHP modules needs to be installed');
|
| 941 |
+
}
|
| 942 |
+
|
| 943 |
+
public function encryptString($string, $salt_suffix) {
|
| 944 |
+
$key = $this->hashAndBin($this->pw_prefix.$salt_suffix, $this->salt_prefix.$salt_suffix);
|
| 945 |
+
|
| 946 |
+
$iv_size = $this->get_iv_size();
|
| 947 |
+
$iv = $GLOBALS['simba_two_factor_authentication']->random_bytes($iv_size);
|
| 948 |
+
|
| 949 |
+
$enc = $this->encrypt($key, $string, $iv);
|
| 950 |
+
|
| 951 |
+
if (false === $enc) return false;
|
| 952 |
+
|
| 953 |
+
$enc = $iv.$enc;
|
| 954 |
+
$enc_b64 = base64_encode($enc);
|
| 955 |
+
return $enc_b64;
|
| 956 |
+
}
|
| 957 |
+
|
| 958 |
+
private function decryptString($enc_b64, $salt_suffix, $force_openssl = false) {
|
| 959 |
+
$key = $this->hashAndBin($this->pw_prefix.$salt_suffix, $this->salt_prefix.$salt_suffix);
|
| 960 |
+
|
| 961 |
+
$iv_size = $this->get_iv_size();
|
| 962 |
+
$enc_conc = bin2hex(base64_decode($enc_b64));
|
| 963 |
+
|
| 964 |
+
$iv = hex2bin(substr($enc_conc, 0, $iv_size*2));
|
| 965 |
+
$enc = hex2bin(substr($enc_conc, $iv_size*2));
|
| 966 |
+
|
| 967 |
+
$string = $this->decrypt($key, $enc, $iv, $force_openssl);
|
| 968 |
+
|
| 969 |
+
// Remove padding bytes
|
| 970 |
+
return rtrim($string, "\x00..\x1F");
|
| 971 |
+
}
|
| 972 |
+
|
| 973 |
+
private function hashAndBin($pw, $salt) {
|
| 974 |
+
$key = $this->hash($pw, $salt);
|
| 975 |
+
$key = pack('H*', $key);
|
| 976 |
+
// Yes: it's a null encryption key. See: https://wordpress.org/support/topic/warning-mcrypt_decrypt-key-of-size-0-not-supported-by-this-algorithm-only-k?replies=5#post-6806922
|
| 977 |
+
// Basically: the original plugin had a bug here, which caused a null encryption key. This fails on PHP 5.6+. But, fixing it would break backwards compatibility for existing installs - and note that the only unknown once you have access to the encrypted data is the AUTH_SALT and AUTH_KEY constants... which means that actually the intended encryption was non-portable, + problematic if you lose your wp-config.php or try to migrate data to another site, or changes these values. (Normally changing these values only causes a compulsory re-log-in - but with the intended encryption in the original author's plugin, it'd actually cause a permanent lock-out until you disabled his plugin). If someone has read-access to the database, then it'd be reasonable to assume they have read-access to wp-config.php too: or at least, the number of attackers who can do one and not the other would be small. The "encryption's" not worth it.
|
| 978 |
+
// In summary: this isn't encryption, and is not intended to be.
|
| 979 |
+
return str_repeat(chr(0), 16);
|
| 980 |
+
}
|
| 981 |
+
|
| 982 |
+
private function hash($pw, $salt) {
|
| 983 |
+
//$hash = hash_pbkdf2('sha256', $pw, $salt, 10);
|
| 984 |
+
//$hash = crypt($pw, '$5$'.$salt.'$');
|
| 985 |
+
$hash = md5($salt.$pw);
|
| 986 |
+
return $hash;
|
| 987 |
+
}
|
| 988 |
+
|
| 989 |
+
private function randString($len = 10) {
|
| 990 |
+
$chars = '23456789QWERTYUPASDFGHJKLZXCVBNM';
|
| 991 |
+
$chars = str_split($chars);
|
| 992 |
+
shuffle($chars);
|
| 993 |
+
if (function_exists('random_int')) {
|
| 994 |
+
$code = '';
|
| 995 |
+
for ($i = 1; $i <= $len; $i++) {
|
| 996 |
+
$code .= $chars[random_int(0, count($chars)-1)];
|
| 997 |
+
}
|
| 998 |
+
} else {
|
| 999 |
+
$code = implode('', array_splice($chars, 0, $len));
|
| 1000 |
+
}
|
| 1001 |
+
return $code;
|
| 1002 |
+
}
|
| 1003 |
+
|
| 1004 |
+
public function setUserHMACTypes() {
|
| 1005 |
+
// We need this because we dont want to change third party apps users algorithm
|
| 1006 |
+
$users = get_users(array('meta_key' => 'simbatfa_delivery_type', 'meta_value' => 'third-party-apps'));
|
| 1007 |
+
if (empty($users)) return;
|
| 1008 |
+
foreach ($users as $user) {
|
| 1009 |
+
$tfa_algorithm_type = get_user_meta($user->ID, 'tfa_algorithm_type', true);
|
| 1010 |
+
if ($tfa_algorithm_type) continue;
|
| 1011 |
+
update_user_meta($user->ID, 'tfa_algorithm_type', $this->get_user_otp_algorithm($user->ID));
|
| 1012 |
+
}
|
| 1013 |
+
}
|
| 1014 |
+
|
| 1015 |
+
}
|
|
@@ -0,0 +1,1250 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
|
| 3 |
+
if (!defined('ABSPATH')) die('Access denied.');
|
| 4 |
+
|
| 5 |
+
class Simba_Two_Factor_Authentication {
|
| 6 |
+
|
| 7 |
+
protected $frontend;
|
| 8 |
+
|
| 9 |
+
protected $totp_controller;
|
| 10 |
+
|
| 11 |
+
/**
|
| 12 |
+
* URL slug for the plugin's option page
|
| 13 |
+
*
|
| 14 |
+
* @var String
|
| 15 |
+
*/
|
| 16 |
+
private $user_settings_page_slug;
|
| 17 |
+
|
| 18 |
+
/**
|
| 19 |
+
* Settings page heading for plugin's option page
|
| 20 |
+
*
|
| 21 |
+
* @var String
|
| 22 |
+
*/
|
| 23 |
+
private $settings_page_heading;
|
| 24 |
+
|
| 25 |
+
/**
|
| 26 |
+
* Plugin translate url
|
| 27 |
+
*
|
| 28 |
+
* @var string
|
| 29 |
+
*/
|
| 30 |
+
private $plugin_translate_url;
|
| 31 |
+
|
| 32 |
+
/**
|
| 33 |
+
* URL slug for the site-wide administration options
|
| 34 |
+
*
|
| 35 |
+
* @var String
|
| 36 |
+
*/
|
| 37 |
+
private $site_wide_administration_url;
|
| 38 |
+
|
| 39 |
+
/**
|
| 40 |
+
* URL for the premium version
|
| 41 |
+
*
|
| 42 |
+
* @var String
|
| 43 |
+
*/
|
| 44 |
+
private $premium_version_url;
|
| 45 |
+
|
| 46 |
+
/**
|
| 47 |
+
* URL for the FAQ
|
| 48 |
+
*
|
| 49 |
+
* @var String
|
| 50 |
+
*/
|
| 51 |
+
private $faq_url;
|
| 52 |
+
|
| 53 |
+
/**
|
| 54 |
+
* Authentication slug. Verify that two-factor authentication should not be repeated for the same slug.
|
| 55 |
+
*
|
| 56 |
+
* @var String
|
| 57 |
+
*/
|
| 58 |
+
private $authentication_slug = 'updraft';
|
| 59 |
+
|
| 60 |
+
private static $is_authenticated = array();
|
| 61 |
+
|
| 62 |
+
/**
|
| 63 |
+
* Class Constructor, Set basic settings.
|
| 64 |
+
*
|
| 65 |
+
* @return Void
|
| 66 |
+
*/
|
| 67 |
+
public function __construct() {
|
| 68 |
+
|
| 69 |
+
require_once(__DIR__.'/providers/totp-hotp/loader.php');
|
| 70 |
+
|
| 71 |
+
$this->totp_controller = new Simba_TFA_Provider_TOTP($this);
|
| 72 |
+
|
| 73 |
+
// Process login form AJAX events
|
| 74 |
+
add_action('wp_ajax_nopriv_simbatfa-init-otp', array($this, 'tfaInitLogin'));
|
| 75 |
+
add_action('wp_ajax_simbatfa-init-otp', array($this, 'tfaInitLogin'));
|
| 76 |
+
|
| 77 |
+
add_action('wp_ajax_simbatfa_shared_ajax', array($this, 'shared_ajax'));
|
| 78 |
+
|
| 79 |
+
require_once($this->includes_dir().'/login-form-integrations.php');
|
| 80 |
+
new Simba_TFA_Login_Form_Integrations($this);
|
| 81 |
+
|
| 82 |
+
// Add TFA column on admin users list
|
| 83 |
+
add_action('manage_users_columns', array($this, 'manage_users_columns_tfa'));
|
| 84 |
+
add_action('wpmu_users_columns', array($this, 'manage_users_columns_tfa'));
|
| 85 |
+
add_action('manage_users_custom_column', array($this, 'manage_users_custom_column_tfa'), 10, 3);
|
| 86 |
+
|
| 87 |
+
// CSS for admin users screen
|
| 88 |
+
add_action('admin_print_styles-users.php', array($this, 'load_users_css'), 10, 0);
|
| 89 |
+
|
| 90 |
+
add_action('admin_init', array($this, 'register_two_factor_auth_settings'));
|
| 91 |
+
add_action('init', array($this, 'init'));
|
| 92 |
+
|
| 93 |
+
if (!defined('TWO_FACTOR_DISABLE') || !TWO_FACTOR_DISABLE) {
|
| 94 |
+
add_filter('authenticate', array($this, 'tfaVerifyCodeAndUser'), 99999999999, 3);
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
if (defined('DOING_AJAX') && DOING_AJAX && defined('WP_ADMIN') && WP_ADMIN && !empty($_REQUEST['action']) && 'simbatfa-init-otp' == $_REQUEST['action']) {
|
| 98 |
+
// Try to prevent PHP notices breaking the AJAX conversation
|
| 99 |
+
$this->output_buffering = true;
|
| 100 |
+
$this->logged = array();
|
| 101 |
+
set_error_handler(array($this, 'get_php_errors'), E_ALL & ~E_STRICT);
|
| 102 |
+
ob_start();
|
| 103 |
+
}
|
| 104 |
+
}
|
| 105 |
+
|
| 106 |
+
/**
|
| 107 |
+
* Give the filesystem path to the plugin's includes directory
|
| 108 |
+
*
|
| 109 |
+
* @return String
|
| 110 |
+
*/
|
| 111 |
+
public function includes_dir() {
|
| 112 |
+
return __DIR__.'/includes';
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
/**
|
| 116 |
+
* Give the URL for the plugin's includes directory
|
| 117 |
+
*
|
| 118 |
+
* @return String
|
| 119 |
+
*/
|
| 120 |
+
public function includes_url() {
|
| 121 |
+
return plugins_url('', __FILE__).'/includes';
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
/**
|
| 125 |
+
* Set URL slug for the plugin's option page.
|
| 126 |
+
*
|
| 127 |
+
* @param String Setting page URL slug.
|
| 128 |
+
* @return Void
|
| 129 |
+
*/
|
| 130 |
+
public function set_user_settings_page_slug($user_settings_page_slug) {
|
| 131 |
+
$this->user_settings_page_slug = $user_settings_page_slug;
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
/**
|
| 135 |
+
* Get URL slug for the plugin's option page.
|
| 136 |
+
*
|
| 137 |
+
* @return String Setting page URL slug.
|
| 138 |
+
*/
|
| 139 |
+
public function get_user_settings_page_slug() {
|
| 140 |
+
return $this->user_settings_page_slug;
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
/**
|
| 144 |
+
* Set settings page heading for plugin's option page
|
| 145 |
+
*
|
| 146 |
+
* @param String $settings_page_heading String.
|
| 147 |
+
*
|
| 148 |
+
* @return String
|
| 149 |
+
*/
|
| 150 |
+
public function set_settings_page_heading($settings_page_heading) {
|
| 151 |
+
$this->settings_page_heading = $settings_page_heading;
|
| 152 |
+
}
|
| 153 |
+
|
| 154 |
+
/**
|
| 155 |
+
* Get settings page heading for plugin's option page.
|
| 156 |
+
*
|
| 157 |
+
* @return String Setting page heading.
|
| 158 |
+
*/
|
| 159 |
+
public function get_settings_page_heading() {
|
| 160 |
+
return $this->settings_page_heading;
|
| 161 |
+
}
|
| 162 |
+
|
| 163 |
+
/**
|
| 164 |
+
* Set plugin translate url
|
| 165 |
+
*
|
| 166 |
+
* @param String $plugin_translate_url Plugin translation URL.
|
| 167 |
+
* @return Void
|
| 168 |
+
*/
|
| 169 |
+
public function set_plugin_translate_url($plugin_translate_url) {
|
| 170 |
+
$this->plugin_translate_url = $plugin_translate_url;
|
| 171 |
+
}
|
| 172 |
+
|
| 173 |
+
/**
|
| 174 |
+
* Get plugin translate url
|
| 175 |
+
*
|
| 176 |
+
* @return String Plugin translate URL
|
| 177 |
+
*/
|
| 178 |
+
public function get_plugin_translate_url() {
|
| 179 |
+
return $this->plugin_translate_url;
|
| 180 |
+
}
|
| 181 |
+
|
| 182 |
+
/**
|
| 183 |
+
* Set plugin premium version url
|
| 184 |
+
*
|
| 185 |
+
* @param String $premium_version_url Plugin premium version url.
|
| 186 |
+
* @return Void
|
| 187 |
+
*/
|
| 188 |
+
public function set_premium_version_url($premium_version_url) {
|
| 189 |
+
$this->premium_version_url = $premium_version_url;
|
| 190 |
+
}
|
| 191 |
+
|
| 192 |
+
/**
|
| 193 |
+
* Get plugin premium version URL.
|
| 194 |
+
*
|
| 195 |
+
* @return String Plugin premium version URL.
|
| 196 |
+
*/
|
| 197 |
+
public function get_premium_version_url() {
|
| 198 |
+
return $this->premium_version_url;
|
| 199 |
+
}
|
| 200 |
+
|
| 201 |
+
/**
|
| 202 |
+
* Set plugin FAQ URL
|
| 203 |
+
*
|
| 204 |
+
* @param String $faq_url Plugin FAQ URL.
|
| 205 |
+
* @return Void
|
| 206 |
+
*/
|
| 207 |
+
public function set_faq_url($faq_url) {
|
| 208 |
+
$this->faq_url = $faq_url;
|
| 209 |
+
}
|
| 210 |
+
|
| 211 |
+
/**
|
| 212 |
+
* Get plugin FAQ URL.
|
| 213 |
+
*
|
| 214 |
+
* @return String Plugin FAQ URL.
|
| 215 |
+
*/
|
| 216 |
+
public function get_faq_url() {
|
| 217 |
+
return $this->faq_url;
|
| 218 |
+
}
|
| 219 |
+
|
| 220 |
+
/**
|
| 221 |
+
* Set plugin site wide administration URL
|
| 222 |
+
*
|
| 223 |
+
* @param String $site_wide_administration_url Plugin site wide administration URL.
|
| 224 |
+
* @return Void
|
| 225 |
+
*/
|
| 226 |
+
public function set_site_wide_administration_url($site_wide_administration_url) {
|
| 227 |
+
$this->site_wide_administration_url = $site_wide_administration_url;
|
| 228 |
+
}
|
| 229 |
+
|
| 230 |
+
/**
|
| 231 |
+
* Get plugin site wide administration URL.
|
| 232 |
+
*
|
| 233 |
+
* @return String Plugin site wide administration URL
|
| 234 |
+
*/
|
| 235 |
+
public function get_site_wide_administration_url() {
|
| 236 |
+
return $this->site_wide_administration_url;
|
| 237 |
+
}
|
| 238 |
+
|
| 239 |
+
/**
|
| 240 |
+
* Give the filesystem path to the plugin's templates directory
|
| 241 |
+
*
|
| 242 |
+
* @return String
|
| 243 |
+
*/
|
| 244 |
+
public function templates_dir() {
|
| 245 |
+
return __DIR__.'/templates';
|
| 246 |
+
}
|
| 247 |
+
|
| 248 |
+
/**
|
| 249 |
+
* Include the user settings page code
|
| 250 |
+
*/
|
| 251 |
+
public function show_dashboard_user_settings_page() {
|
| 252 |
+
$this->include_template('user-settings.php');
|
| 253 |
+
}
|
| 254 |
+
|
| 255 |
+
/**
|
| 256 |
+
* Enqueue CSS styling on the users page
|
| 257 |
+
*/
|
| 258 |
+
public function load_users_css() {
|
| 259 |
+
wp_enqueue_style(
|
| 260 |
+
'tfa-users-css',
|
| 261 |
+
$this->includes_url().'/users.css',
|
| 262 |
+
array(),
|
| 263 |
+
$this->version,
|
| 264 |
+
'screen'
|
| 265 |
+
);
|
| 266 |
+
}
|
| 267 |
+
|
| 268 |
+
/**
|
| 269 |
+
* Add the 2FA label to the users list table header.
|
| 270 |
+
*
|
| 271 |
+
* @param Array $columns Table columns.
|
| 272 |
+
*
|
| 273 |
+
* @return Array
|
| 274 |
+
*/
|
| 275 |
+
public function manage_users_columns_tfa($columns = array()) {
|
| 276 |
+
$columns['tfa-status'] = __('2FA', 'all-in-one-wp-security-and-firewall');
|
| 277 |
+
return $columns;
|
| 278 |
+
}
|
| 279 |
+
|
| 280 |
+
/**
|
| 281 |
+
* Add status into TFA column.
|
| 282 |
+
*
|
| 283 |
+
* @param String $value String.
|
| 284 |
+
* @param String $column_name Column name.
|
| 285 |
+
* @param Integer $user_id User ID.
|
| 286 |
+
*
|
| 287 |
+
* @return String
|
| 288 |
+
*/
|
| 289 |
+
public function manage_users_custom_column_tfa($value = '', $column_name = '', $user_id = 0) {
|
| 290 |
+
|
| 291 |
+
// Only for this column name.
|
| 292 |
+
if ('tfa-status' === $column_name) {
|
| 293 |
+
|
| 294 |
+
if (!$this->is_activated_for_user($user_id)) {
|
| 295 |
+
$value = '—';
|
| 296 |
+
} elseif ($this->is_activated_by_user($user_id)) {
|
| 297 |
+
// Use value.
|
| 298 |
+
$value = '<span title="' . __( 'Enabled', 'all-in-one-wp-security-and-firewall' ) . '" class="dashicons dashicons-yes"></span>';
|
| 299 |
+
} else {
|
| 300 |
+
// No group.
|
| 301 |
+
$value = '<span title="' . __( 'Disabled', 'all-in-one-wp-security-and-firewall' ) . '" class="dashicons dashicons-no"></span>';
|
| 302 |
+
}
|
| 303 |
+
}
|
| 304 |
+
|
| 305 |
+
return $value;
|
| 306 |
+
}
|
| 307 |
+
|
| 308 |
+
/**
|
| 309 |
+
* Paint out an admin notice
|
| 310 |
+
*
|
| 311 |
+
* @param String $message - the caller should already have taken care of any escaping
|
| 312 |
+
* @param String $class
|
| 313 |
+
*/
|
| 314 |
+
public function show_admin_warning($message, $class = 'updated') {
|
| 315 |
+
echo '<div class="tfamessage '.$class.'">'."<p>$message</p></div>";
|
| 316 |
+
}
|
| 317 |
+
|
| 318 |
+
/**
|
| 319 |
+
* Runs upon the WP action admin_init
|
| 320 |
+
*/
|
| 321 |
+
public function register_two_factor_auth_settings() {
|
| 322 |
+
global $wp_roles;
|
| 323 |
+
if (!isset($wp_roles)) $wp_roles = new WP_Roles();
|
| 324 |
+
|
| 325 |
+
foreach ($wp_roles->role_names as $id => $name) {
|
| 326 |
+
register_setting('tfa_user_roles_group', 'tfa_'.$id);
|
| 327 |
+
register_setting('tfa_user_roles_trusted_group', 'tfa_trusted_'.$id);
|
| 328 |
+
register_setting('tfa_user_roles_required_group', 'tfa_required_'.$id);
|
| 329 |
+
}
|
| 330 |
+
|
| 331 |
+
if (is_multisite()) {
|
| 332 |
+
register_setting('tfa_user_roles_group', 'tfa__super_admin');
|
| 333 |
+
register_setting('tfa_user_roles_trusted_group', 'tfa_trusted__super_admin');
|
| 334 |
+
register_setting('tfa_user_roles_required_group', 'tfa_required__super_admin');
|
| 335 |
+
}
|
| 336 |
+
|
| 337 |
+
register_setting('tfa_user_roles_required_group', 'tfa_requireafter');
|
| 338 |
+
register_setting('tfa_user_roles_required_group', 'tfa_if_required_redirect_to');
|
| 339 |
+
register_setting('tfa_user_roles_required_group', 'tfa_hide_turn_off');
|
| 340 |
+
register_setting('tfa_user_roles_trusted_group', 'tfa_trusted_for');
|
| 341 |
+
register_setting('simba_tfa_woocommerce_group', 'tfa_wc_add_section');
|
| 342 |
+
register_setting('simba_tfa_woocommerce_group', 'tfa_bot_protection');
|
| 343 |
+
register_setting('simba_tfa_default_hmac_group', 'tfa_default_hmac');
|
| 344 |
+
register_setting('tfa_xmlrpc_status_group', 'tfa_xmlrpc_on');
|
| 345 |
+
}
|
| 346 |
+
|
| 347 |
+
/**
|
| 348 |
+
* See whether TFA is available or not for a particular user - i.e. whether the administrator has permitted it for their user level
|
| 349 |
+
*
|
| 350 |
+
* @param Integer $user_id - WordPress user ID
|
| 351 |
+
*
|
| 352 |
+
* @return Boolean
|
| 353 |
+
*/
|
| 354 |
+
public function is_activated_for_user($user_id) {
|
| 355 |
+
|
| 356 |
+
if (empty($user_id)) return false;
|
| 357 |
+
|
| 358 |
+
// Super admin is not a role (they are admins with an extra attribute); needs separate handling
|
| 359 |
+
if (is_multisite() && is_super_admin($user_id)) {
|
| 360 |
+
// This is always a final decision - we don't want it to drop through to the 'admin' role's setting
|
| 361 |
+
$role = '_super_admin';
|
| 362 |
+
$db_val = $this->get_option('tfa_'.$role);
|
| 363 |
+
// Defaults to true if no setting has been saved
|
| 364 |
+
return (false === $db_val || $db_val) ? true : false;
|
| 365 |
+
}
|
| 366 |
+
|
| 367 |
+
$roles = $this->get_user_roles($user_id);
|
| 368 |
+
|
| 369 |
+
// N.B. This populates with roles on the current site within a multisite
|
| 370 |
+
foreach ($roles as $role) {
|
| 371 |
+
$db_val = $this->get_option('tfa_'.$role);
|
| 372 |
+
if (false === $db_val || $db_val) return true;
|
| 373 |
+
}
|
| 374 |
+
|
| 375 |
+
return false;
|
| 376 |
+
|
| 377 |
+
}
|
| 378 |
+
|
| 379 |
+
/**
|
| 380 |
+
* Get all user roles for a given user (if on multisite, amalgamates all roles from all sites)
|
| 381 |
+
*
|
| 382 |
+
* @param Integer $user_id - WordPress user ID
|
| 383 |
+
*
|
| 384 |
+
* @return Array
|
| 385 |
+
*/
|
| 386 |
+
protected function get_user_roles($user_id) {
|
| 387 |
+
|
| 388 |
+
// Get roles on the main site
|
| 389 |
+
$user = new WP_User($user_id);
|
| 390 |
+
$roles = (array) $user->roles;
|
| 391 |
+
|
| 392 |
+
// On multisite, also check roles on non-main sites
|
| 393 |
+
if (is_multisite()) {
|
| 394 |
+
global $wpdb, $table_prefix;
|
| 395 |
+
$roles_db = $wpdb->get_results($wpdb->prepare("SELECT meta_key, meta_value FROM {$wpdb->usermeta} WHERE user_id=%d AND meta_key LIKE '".esc_sql($table_prefix)."%_capabilities'", $user_id));
|
| 396 |
+
if (is_array($roles_db)) {
|
| 397 |
+
foreach ($roles_db as $role_info) {
|
| 398 |
+
if (empty($role_info->meta_key) || !preg_match('/^'.$table_prefix.'\d+_capabilities$/', $role_info->meta_key) || empty($role_info->meta_value) || !preg_match('/^a:/', $role_info->meta_value)) continue;
|
| 399 |
+
$site_roles = unserialize($role_info->meta_value);
|
| 400 |
+
if (!is_array($site_roles)) continue;
|
| 401 |
+
foreach ($site_roles as $role => $active) {
|
| 402 |
+
if ($active && !in_array($role, $roles)) $roles[] = $role;
|
| 403 |
+
}
|
| 404 |
+
}
|
| 405 |
+
}
|
| 406 |
+
}
|
| 407 |
+
|
| 408 |
+
return $roles;
|
| 409 |
+
}
|
| 410 |
+
|
| 411 |
+
/**
|
| 412 |
+
* Check if TFA is required for a specified user
|
| 413 |
+
*
|
| 414 |
+
* N.B. - This doesn't check is_activated_for_user() - the caller would normally want to do that first
|
| 415 |
+
*
|
| 416 |
+
* @param $user_id Integer - the WP user ID
|
| 417 |
+
*
|
| 418 |
+
* @return Boolean
|
| 419 |
+
*/
|
| 420 |
+
public function is_required_for_user($user_id) {
|
| 421 |
+
return apply_filters('simba_tfa_required_for_user', $this->user_property_active($user_id, 'required_'), $user_id);
|
| 422 |
+
}
|
| 423 |
+
|
| 424 |
+
/**
|
| 425 |
+
* See if a particular user property is active
|
| 426 |
+
*
|
| 427 |
+
* @param Integer $user_id
|
| 428 |
+
* @param String $prefix - e.g. "required_", "trusted_"
|
| 429 |
+
*
|
| 430 |
+
* @return Boolean
|
| 431 |
+
*/
|
| 432 |
+
public function user_property_active($user_id, $prefix = 'required_') {
|
| 433 |
+
|
| 434 |
+
if (empty($user_id)) return false;
|
| 435 |
+
|
| 436 |
+
// Super admin is not a role (they are admins with an extra attribute); needs separate handling
|
| 437 |
+
if (is_multisite() && is_super_admin($user_id)) {
|
| 438 |
+
// This is always a final decision - we don't want it to drop through to the 'admin' role's setting
|
| 439 |
+
$role = '_super_admin';
|
| 440 |
+
$db_val = $this->get_option('tfa_'.$prefix.$role);
|
| 441 |
+
return $db_val ? true : false;
|
| 442 |
+
}
|
| 443 |
+
|
| 444 |
+
$roles = $this->get_user_roles($user_id);
|
| 445 |
+
|
| 446 |
+
foreach ($roles as $role) {
|
| 447 |
+
$db_val = $this->get_option('tfa_'.$prefix.$role);
|
| 448 |
+
if ($db_val) return true;
|
| 449 |
+
}
|
| 450 |
+
|
| 451 |
+
return false;
|
| 452 |
+
|
| 453 |
+
}
|
| 454 |
+
|
| 455 |
+
/**
|
| 456 |
+
* Whether TFA is activated by a specific user. Note that this doesn't check if TFA is enabled for the user's role; the caller should check that first.
|
| 457 |
+
*
|
| 458 |
+
* @param Integer $user_id
|
| 459 |
+
*
|
| 460 |
+
* @return Boolean
|
| 461 |
+
*/
|
| 462 |
+
public function is_activated_by_user($user_id) {
|
| 463 |
+
$enabled = get_user_meta($user_id, 'tfa_enable_tfa', true);
|
| 464 |
+
return !empty($enabled);
|
| 465 |
+
}
|
| 466 |
+
|
| 467 |
+
/**
|
| 468 |
+
* Get a list of trusted devices for the user
|
| 469 |
+
*
|
| 470 |
+
* @param Integer|Boolean $user_id - WordPress user ID, or false for the current user
|
| 471 |
+
*
|
| 472 |
+
* @return Array
|
| 473 |
+
*/
|
| 474 |
+
public function user_get_trusted_devices($user_id = false) {
|
| 475 |
+
|
| 476 |
+
if (false === $user_id) {
|
| 477 |
+
global $current_user;
|
| 478 |
+
$user_id = $current_user->ID;
|
| 479 |
+
}
|
| 480 |
+
|
| 481 |
+
$trusted_devices = get_user_meta($user_id, 'tfa_trusted_devices', true);
|
| 482 |
+
|
| 483 |
+
if (!is_array($trusted_devices)) $trusted_devices = array();
|
| 484 |
+
|
| 485 |
+
return $trusted_devices;
|
| 486 |
+
}
|
| 487 |
+
|
| 488 |
+
/**
|
| 489 |
+
* Trust the current device
|
| 490 |
+
*
|
| 491 |
+
* @param Integer $user_id - WordPress user ID
|
| 492 |
+
* @param Integer $trusted_for - time to trust for, in days
|
| 493 |
+
*/
|
| 494 |
+
public function trust_device($user_id, $trusted_for) {
|
| 495 |
+
|
| 496 |
+
$trusted_devices = $this->user_get_trusted_devices($user_id);
|
| 497 |
+
|
| 498 |
+
$time_now = time();
|
| 499 |
+
|
| 500 |
+
foreach ($trusted_devices as $k => $device) {
|
| 501 |
+
if (empty($device['until']) || $device['until'] <= $time_now) unset($trusted_devices[$k]);
|
| 502 |
+
}
|
| 503 |
+
|
| 504 |
+
$until = $time_now + $trusted_for * 86400;
|
| 505 |
+
|
| 506 |
+
$token = bin2hex($this->random_bytes(40));
|
| 507 |
+
|
| 508 |
+
$trusted_devices[] = array(
|
| 509 |
+
'ip' => $_SERVER['REMOTE_ADDR'],
|
| 510 |
+
'until' => $until,
|
| 511 |
+
'user_agent' => empty($_SERVER['HTTP_USER_AGENT']) ? '' : (string) $_SERVER['HTTP_USER_AGENT'],
|
| 512 |
+
'token' => $token
|
| 513 |
+
);
|
| 514 |
+
|
| 515 |
+
$this->user_set_trusted_devices($user_id, $trusted_devices);
|
| 516 |
+
|
| 517 |
+
$this->set_cookie('simbatfa_trust_token', $token, $until);
|
| 518 |
+
}
|
| 519 |
+
|
| 520 |
+
/**
|
| 521 |
+
* Returns true if running on a PHP version on which mcrypt has been deprecated
|
| 522 |
+
*
|
| 523 |
+
* @return Boolean
|
| 524 |
+
*/
|
| 525 |
+
public function is_mcrypt_deprecated() {
|
| 526 |
+
return (7 == PHP_MAJOR_VERSION && PHP_MINOR_VERSION >= 1);
|
| 527 |
+
}
|
| 528 |
+
|
| 529 |
+
/**
|
| 530 |
+
* Return the specified number of bytes
|
| 531 |
+
*
|
| 532 |
+
* @param Integer $bytes
|
| 533 |
+
*
|
| 534 |
+
* @throws Exception
|
| 535 |
+
*
|
| 536 |
+
* @return String
|
| 537 |
+
*/
|
| 538 |
+
public function random_bytes($bytes) {
|
| 539 |
+
if (function_exists('random_bytes')) {
|
| 540 |
+
return random_bytes($bytes);
|
| 541 |
+
} elseif (function_exists('mcrypt_create_iv')) {
|
| 542 |
+
return $this->is_mcrypt_deprecated() ? @mcrypt_create_iv($bytes, MCRYPT_RAND) : mcrypt_create_iv($bytes, MCRYPT_RAND);
|
| 543 |
+
} elseif (function_exists('openssl_random_pseudo_bytes')) {
|
| 544 |
+
return openssl_random_pseudo_bytes($bytes);
|
| 545 |
+
}
|
| 546 |
+
throw new Exception('One of the mcrypt or openssl PHP modules needs to be installed');
|
| 547 |
+
}
|
| 548 |
+
|
| 549 |
+
/**
|
| 550 |
+
* Set a cookie so that, however we logged in, it can be found
|
| 551 |
+
*
|
| 552 |
+
* @param String $name - the cookie name
|
| 553 |
+
* @param String $value - the cookie value
|
| 554 |
+
* @param Integer $expires - when the cookie expires, in epoch time. Defaults to 24 hours' time. Values in the past cause cookie deletion.
|
| 555 |
+
*/
|
| 556 |
+
protected function set_cookie($name, $value, $expires = null) {
|
| 557 |
+
if (null === $expires) $expires = time() + 86400;
|
| 558 |
+
$secure = is_ssl();
|
| 559 |
+
$secure_logged_in_cookie = ($secure && 'https' === parse_url(get_option('home'), PHP_URL_SCHEME));
|
| 560 |
+
$secure = apply_filters('secure_auth_cookie', $secure, get_current_user_id());
|
| 561 |
+
$secure_logged_in_cookie = apply_filters('secure_logged_in_cookie', $secure_logged_in_cookie, get_current_user_id(), $secure);
|
| 562 |
+
|
| 563 |
+
setcookie($name, $value, $expires, ADMIN_COOKIE_PATH, COOKIE_DOMAIN, $secure, true);
|
| 564 |
+
setcookie($name, $value, $expires, COOKIEPATH, COOKIE_DOMAIN, $secure_logged_in_cookie, true);
|
| 565 |
+
if (COOKIEPATH != SITECOOKIEPATH) {
|
| 566 |
+
setcookie($name, $value, $expires, SITECOOKIEPATH, COOKIE_DOMAIN, $secure_logged_in_cookie, true);
|
| 567 |
+
}
|
| 568 |
+
}
|
| 569 |
+
|
| 570 |
+
/**
|
| 571 |
+
* Get a list of trusted devices for the user
|
| 572 |
+
*
|
| 573 |
+
* @param Integer $user_id - WordPress user ID
|
| 574 |
+
* @param Array $trusted_devices - the list of devices
|
| 575 |
+
*/
|
| 576 |
+
public function user_set_trusted_devices($user_id, $trusted_devices) {
|
| 577 |
+
update_user_meta($user_id, 'tfa_trusted_devices', $trusted_devices);
|
| 578 |
+
}
|
| 579 |
+
|
| 580 |
+
/**
|
| 581 |
+
* Get the user capability needed for managing TFA users.
|
| 582 |
+
* You'll want to think carefully about changing this to a non-admin, as it can give the ability to lock admins out (though, if you have FTP/files access, you can always disable TFA or any plugin)
|
| 583 |
+
*
|
| 584 |
+
* @return String
|
| 585 |
+
*/
|
| 586 |
+
public function get_management_capability() {
|
| 587 |
+
return apply_filters('simba_tfa_management_capability', 'manage_options');
|
| 588 |
+
}
|
| 589 |
+
|
| 590 |
+
/**
|
| 591 |
+
* Used with set_error_handler()
|
| 592 |
+
*
|
| 593 |
+
* @param Integer $errno
|
| 594 |
+
* @param String $errstr
|
| 595 |
+
* @param String $errfile
|
| 596 |
+
* @param Integer $errline
|
| 597 |
+
*
|
| 598 |
+
* @return Boolean
|
| 599 |
+
*/
|
| 600 |
+
public function get_php_errors($errno, $errstr, $errfile, $errline) {
|
| 601 |
+
if (0 == error_reporting()) return true;
|
| 602 |
+
$logline = $this->php_error_to_logline($errno, $errstr, $errfile, $errline);
|
| 603 |
+
$this->logged[] = $logline;
|
| 604 |
+
# Don't pass it up the chain (since it's going to be output to the user always)
|
| 605 |
+
return true;
|
| 606 |
+
}
|
| 607 |
+
|
| 608 |
+
public function php_error_to_logline($errno, $errstr, $errfile, $errline) {
|
| 609 |
+
switch ($errno) {
|
| 610 |
+
case 1: $e_type = 'E_ERROR'; break;
|
| 611 |
+
case 2: $e_type = 'E_WARNING'; break;
|
| 612 |
+
case 4: $e_type = 'E_PARSE'; break;
|
| 613 |
+
case 8: $e_type = 'E_NOTICE'; break;
|
| 614 |
+
case 16: $e_type = 'E_CORE_ERROR'; break;
|
| 615 |
+
case 32: $e_type = 'E_CORE_WARNING'; break;
|
| 616 |
+
case 64: $e_type = 'E_COMPILE_ERROR'; break;
|
| 617 |
+
case 128: $e_type = 'E_COMPILE_WARNING'; break;
|
| 618 |
+
case 256: $e_type = 'E_USER_ERROR'; break;
|
| 619 |
+
case 512: $e_type = 'E_USER_WARNING'; break;
|
| 620 |
+
case 1024: $e_type = 'E_USER_NOTICE'; break;
|
| 621 |
+
case 2048: $e_type = 'E_STRICT'; break;
|
| 622 |
+
case 4096: $e_type = 'E_RECOVERABLE_ERROR'; break;
|
| 623 |
+
case 8192: $e_type = 'E_DEPRECATED'; break;
|
| 624 |
+
case 16384: $e_type = 'E_USER_DEPRECATED'; break;
|
| 625 |
+
case 30719: $e_type = 'E_ALL'; break;
|
| 626 |
+
default: $e_type = "E_UNKNOWN ($errno)"; break;
|
| 627 |
+
}
|
| 628 |
+
|
| 629 |
+
if (!is_string($errstr)) $errstr = serialize($errstr);
|
| 630 |
+
|
| 631 |
+
if (0 === strpos($errfile, ABSPATH)) $errfile = substr($errfile, strlen(ABSPATH));
|
| 632 |
+
|
| 633 |
+
return "PHP event: code $e_type: $errstr (line $errline, $errfile)";
|
| 634 |
+
|
| 635 |
+
}
|
| 636 |
+
|
| 637 |
+
/**
|
| 638 |
+
* Runs upon the WordPress 'init' action
|
| 639 |
+
*/
|
| 640 |
+
public function init() {
|
| 641 |
+
if ((!is_admin() || (defined('DOING_AJAX') && DOING_AJAX)) && is_user_logged_in() && file_exists($this->includes_dir().'/tfa_frontend.php')) {
|
| 642 |
+
$this->load_frontend();
|
| 643 |
+
} else {
|
| 644 |
+
add_shortcode('twofactor_user_settings', array($this, 'shortcode_when_not_logged_in'));
|
| 645 |
+
}
|
| 646 |
+
}
|
| 647 |
+
|
| 648 |
+
/**
|
| 649 |
+
* Return the Simba_TFA_Provider_TOTP object.
|
| 650 |
+
*
|
| 651 |
+
* @returns Simba_TFA_Provider_TOTP
|
| 652 |
+
*/
|
| 653 |
+
public function get_totp_controller() {
|
| 654 |
+
return $this->totp_controller;
|
| 655 |
+
}
|
| 656 |
+
|
| 657 |
+
/**
|
| 658 |
+
* "Shared" - i.e. could be called from either front-end or back-end
|
| 659 |
+
*/
|
| 660 |
+
public function shared_ajax() {
|
| 661 |
+
|
| 662 |
+
if (empty($_POST['subaction']) || empty($_POST['nonce']) || !is_user_logged_in() || !wp_verify_nonce($_POST['nonce'], 'tfa_shared_nonce')) die('Security check (3).');
|
| 663 |
+
|
| 664 |
+
global $current_user;
|
| 665 |
+
|
| 666 |
+
$subaction = $_POST['subaction'];
|
| 667 |
+
|
| 668 |
+
if ('refreshotp' == $subaction) {
|
| 669 |
+
|
| 670 |
+
$code = $this->totp_controller->get_current_code($current_user->ID);
|
| 671 |
+
|
| 672 |
+
if (false === $code) die(json_encode(array('code' => '')));
|
| 673 |
+
|
| 674 |
+
die(json_encode(array('code' => $code)));
|
| 675 |
+
|
| 676 |
+
} elseif ('untrust_device' == $subaction && isset($_POST['device_id'])) {
|
| 677 |
+
$this->untrust_device(stripslashes($_POST['device_id']));
|
| 678 |
+
ob_start();
|
| 679 |
+
$this->include_template('trusted-devices-inner-box.php', array('trusted_devices' => $this->user_get_trusted_devices()));
|
| 680 |
+
echo json_encode(array('trusted_list' => ob_get_clean()));
|
| 681 |
+
}
|
| 682 |
+
|
| 683 |
+
exit;
|
| 684 |
+
|
| 685 |
+
}
|
| 686 |
+
|
| 687 |
+
/**
|
| 688 |
+
* Mark a device as untrusted for the current user
|
| 689 |
+
*
|
| 690 |
+
* @param String $device_id
|
| 691 |
+
*/
|
| 692 |
+
protected function untrust_device($device_id) {
|
| 693 |
+
|
| 694 |
+
$trusted_devices = $this->user_get_trusted_devices();
|
| 695 |
+
|
| 696 |
+
unset($trusted_devices[$device_id]);
|
| 697 |
+
|
| 698 |
+
global $current_user;
|
| 699 |
+
$current_user_id = $current_user->ID;
|
| 700 |
+
|
| 701 |
+
$this->user_set_trusted_devices($current_user_id, $trusted_devices);
|
| 702 |
+
|
| 703 |
+
}
|
| 704 |
+
|
| 705 |
+
/**
|
| 706 |
+
* Called upon the AJAX action simbatfa-init-otp . Will die.
|
| 707 |
+
*
|
| 708 |
+
* Uses these keys from $_POST: user
|
| 709 |
+
*/
|
| 710 |
+
public function tfaInitLogin() {
|
| 711 |
+
|
| 712 |
+
if (empty($_POST['user'])) die('Security check (2).');
|
| 713 |
+
|
| 714 |
+
if (defined('TWO_FACTOR_DISABLE') && TWO_FACTOR_DISABLE) {
|
| 715 |
+
$res = array('result' => false, 'user_can_trust' => false);
|
| 716 |
+
} else {
|
| 717 |
+
|
| 718 |
+
if (!function_exists('sanitize_user')) require_once ABSPATH.WPINC.'/formatting.php';
|
| 719 |
+
|
| 720 |
+
// WP's password-checking sanitizes the supplied user, so we must do the same to check if TFA is enabled for them
|
| 721 |
+
$auth_info = array('log' => sanitize_user(stripslashes((string)$_POST['user'])));
|
| 722 |
+
|
| 723 |
+
if (!empty($_COOKIE['simbatfa_trust_token'])) $auth_info['trust_token'] = (string) $_COOKIE['simbatfa_trust_token'];
|
| 724 |
+
|
| 725 |
+
$res = $this->pre_auth($auth_info, 'array');
|
| 726 |
+
}
|
| 727 |
+
|
| 728 |
+
$results = array(
|
| 729 |
+
'jsonstarter' => 'justhere',
|
| 730 |
+
'status' => $res['result'],
|
| 731 |
+
);
|
| 732 |
+
|
| 733 |
+
if (!empty($res['user_can_trust'])) {
|
| 734 |
+
$results['user_can_trust'] = 1;
|
| 735 |
+
if (!empty($res['user_already_trusted'])) $results['user_already_trusted'] = 1;
|
| 736 |
+
}
|
| 737 |
+
|
| 738 |
+
|
| 739 |
+
if (!empty($this->output_buffering)) {
|
| 740 |
+
if (!empty($this->logged)) {
|
| 741 |
+
$results['php_output'] = $this->logged;
|
| 742 |
+
}
|
| 743 |
+
restore_error_handler();
|
| 744 |
+
$buffered = ob_get_clean();
|
| 745 |
+
if ($buffered) $results['extra_output'] = $buffered;
|
| 746 |
+
}
|
| 747 |
+
|
| 748 |
+
$results = apply_filters('simbatfa_check_tfa_requirements_ajax_response', $results);
|
| 749 |
+
|
| 750 |
+
echo json_encode($results);
|
| 751 |
+
|
| 752 |
+
exit;
|
| 753 |
+
}
|
| 754 |
+
|
| 755 |
+
/**
|
| 756 |
+
* Enable or disable TFA for a user
|
| 757 |
+
*
|
| 758 |
+
* @param Integer $user_id - the WordPress user ID
|
| 759 |
+
* @param String $setting - either "true" (to turn on) or "false" (to turn off)
|
| 760 |
+
*/
|
| 761 |
+
public function change_tfa_enabled_status($user_id, $setting) {
|
| 762 |
+
$previously_enabled = $this->is_activated_by_user($user_id) ? 1 : 0;
|
| 763 |
+
$setting = ('true' === $setting) ? 1 : 0;
|
| 764 |
+
update_user_meta($user_id, 'tfa_enable_tfa', $setting);
|
| 765 |
+
do_action('simba_tfa_activation_status_saved', $user_id, $setting, $previously_enabled, $this);
|
| 766 |
+
}
|
| 767 |
+
|
| 768 |
+
/**
|
| 769 |
+
* Here's where the login action happens. Called on the WP 'authenticate' action.
|
| 770 |
+
*
|
| 771 |
+
* @param WP_Error|WP_User $user
|
| 772 |
+
* @param String $username - this is not necessarily the WP username; it is whatever was typed in the form, so can be an email address
|
| 773 |
+
* @param String $password
|
| 774 |
+
*
|
| 775 |
+
* @return WP_Error|WP_User
|
| 776 |
+
*/
|
| 777 |
+
public function tfaVerifyCodeAndUser($user, $username, $password) {
|
| 778 |
+
// When both AIOWPS and Two Factor Authentication plugins are active, this function called more than once, To prevent it, this code is written.
|
| 779 |
+
if (isset(self::$is_authenticated[$this->authentication_slug]) && self::$is_authenticated[$this->authentication_slug]) {
|
| 780 |
+
return $user;
|
| 781 |
+
}
|
| 782 |
+
|
| 783 |
+
$original_user = $user;
|
| 784 |
+
$params = stripslashes_deep($_POST);
|
| 785 |
+
|
| 786 |
+
// If (only) the error was a wrong password, but it looks like the user appended a TFA code to their password, then have another go
|
| 787 |
+
if (is_wp_error($user) && array('incorrect_password') == $user->get_error_codes() && !isset($params['two_factor_code']) && false !== ($from_password = apply_filters('simba_tfa_tfa_from_password', false, $password))) {
|
| 788 |
+
// This forces a new password authentication below
|
| 789 |
+
$user = false;
|
| 790 |
+
}
|
| 791 |
+
|
| 792 |
+
if (is_wp_error($user)) {
|
| 793 |
+
$ret = $user;
|
| 794 |
+
} else {
|
| 795 |
+
|
| 796 |
+
if (is_object($user) && isset($user->ID) && isset($user->user_login)) {
|
| 797 |
+
$params['log'] = $user->user_login;
|
| 798 |
+
// Confirm that this is definitely a username regardless of its format
|
| 799 |
+
$may_be_email = false;
|
| 800 |
+
} else {
|
| 801 |
+
$params['log'] = $username;
|
| 802 |
+
$may_be_email = true;
|
| 803 |
+
}
|
| 804 |
+
|
| 805 |
+
$params['caller'] = $_SERVER['PHP_SELF'] ? $_SERVER['PHP_SELF'] : $_SERVER['REQUEST_URI'];
|
| 806 |
+
if (!empty($_COOKIE['simbatfa_trust_token'])) $params['trust_token'] = (string) $_COOKIE['simbatfa_trust_token'];
|
| 807 |
+
|
| 808 |
+
if (isset($from_password) && false !== $from_password) {
|
| 809 |
+
// Support login forms that can't be hooked via appending to the password
|
| 810 |
+
$speculatively_try_appendage = true;
|
| 811 |
+
$params['two_factor_code'] = $from_password['tfa_code'];
|
| 812 |
+
}
|
| 813 |
+
|
| 814 |
+
$code_ok = $this->authorise_user_from_login($params, $may_be_email);
|
| 815 |
+
|
| 816 |
+
if (is_wp_error($code_ok)) {
|
| 817 |
+
$ret = $code_ok;
|
| 818 |
+
} elseif (!$code_ok) {
|
| 819 |
+
$ret = new WP_Error('authentication_failed', '<strong>'.__('Error:', 'all-in-one-wp-security-and-firewall').'</strong> '.__('The one-time password (TFA code) you entered was incorrect.', 'all-in-one-wp-security-and-firewall'));
|
| 820 |
+
} elseif ($user) {
|
| 821 |
+
$ret = $user;
|
| 822 |
+
} else {
|
| 823 |
+
|
| 824 |
+
if (!empty($speculatively_try_appendage) && true === $code_ok) {
|
| 825 |
+
$password = $from_password['password'];
|
| 826 |
+
}
|
| 827 |
+
|
| 828 |
+
$username_is_email = false;
|
| 829 |
+
|
| 830 |
+
if (function_exists('wp_authenticate_username_password') && $may_be_email && filter_var($username, FILTER_VALIDATE_EMAIL)) {
|
| 831 |
+
global $wpdb;
|
| 832 |
+
// This has to match self::authorise_user_from_login()
|
| 833 |
+
$response = $wpdb->get_row($wpdb->prepare("SELECT ID, user_registered from ".$wpdb->users." WHERE user_email=%s", $username));
|
| 834 |
+
if (is_object($response)) $username_is_email = true;
|
| 835 |
+
}
|
| 836 |
+
|
| 837 |
+
$ret = $username_is_email ? wp_authenticate_email_password(null, $username, $password) : wp_authenticate_username_password(null, $username, $password);
|
| 838 |
+
}
|
| 839 |
+
|
| 840 |
+
}
|
| 841 |
+
|
| 842 |
+
$ret = apply_filters('simbatfa_verify_code_and_user_result', $ret, $original_user, $username, $password);
|
| 843 |
+
|
| 844 |
+
// If the TFA code was actually validated (not just not required, for example), then $code_ok is (boolean)true
|
| 845 |
+
if (isset($code_ok) && true === $code_ok && is_a($ret, 'WP_User')) {
|
| 846 |
+
if (!empty($params['simba_tfa_mark_as_trusted']) && $this->user_can_trust($ret->ID) && (is_ssl() || (!empty($_SERVER['SERVER_NAME']) && ('localhost' == $_SERVER['SERVER_NAME'] ||'127.0.0.1' == $_SERVER['SERVER_NAME'])))) {
|
| 847 |
+
|
| 848 |
+
$trusted_for = $this->get_option('tfa_trusted_for');
|
| 849 |
+
$trusted_for = (false === $trusted_for) ? 30 : (string) absint($trusted_for);
|
| 850 |
+
|
| 851 |
+
$this->trust_device($ret->ID, $trusted_for);
|
| 852 |
+
}
|
| 853 |
+
}
|
| 854 |
+
|
| 855 |
+
self::$is_authenticated[$this->authentication_slug] = true;
|
| 856 |
+
|
| 857 |
+
return $ret;
|
| 858 |
+
}
|
| 859 |
+
|
| 860 |
+
// N.B. - This doesn't check is_activated_for_user() - the caller would normally want to do that first
|
| 861 |
+
public function user_can_trust($user_id) {
|
| 862 |
+
// Default is false because this is a new feature and we don't want to surprise existing users by granting broader access than they expected upon an upgrade
|
| 863 |
+
return apply_filters('simba_tfa_user_can_trust', false, $user_id);
|
| 864 |
+
}
|
| 865 |
+
|
| 866 |
+
/**
|
| 867 |
+
* Should the user be asked for a TFA code? And optionally, is the user allowed to trust devices?
|
| 868 |
+
*
|
| 869 |
+
* @param Array $params - the key used is 'log', indicating the username or email address
|
| 870 |
+
* @param String $response_format - 'simple' (historic format) or 'array' (richer info)
|
| 871 |
+
*
|
| 872 |
+
* @return Boolean
|
| 873 |
+
*/
|
| 874 |
+
public function pre_auth($params, $response_format = 'simple') {
|
| 875 |
+
global $wpdb;
|
| 876 |
+
|
| 877 |
+
$query = filter_var($params['log'], FILTER_VALIDATE_EMAIL) ? $wpdb->prepare("SELECT ID, user_email from ".$wpdb->users." WHERE user_email=%s", $params['log']) : $wpdb->prepare("SELECT ID, user_email from ".$wpdb->users." WHERE user_login=%s", $params['log']);
|
| 878 |
+
$user = $wpdb->get_row($query);
|
| 879 |
+
|
| 880 |
+
if (!$user && filter_var($params['log'], FILTER_VALIDATE_EMAIL)) {
|
| 881 |
+
// Corner-case: login looks like an email, but is a username rather than email address
|
| 882 |
+
$user = $wpdb->get_row($wpdb->prepare("SELECT ID, user_email from ".$wpdb->users." WHERE user_login=%s", $params['log']));
|
| 883 |
+
}
|
| 884 |
+
|
| 885 |
+
$is_activated_for_user = true;
|
| 886 |
+
$is_activated_by_user = false;
|
| 887 |
+
|
| 888 |
+
$result = false;
|
| 889 |
+
|
| 890 |
+
$totp_controller = $this->totp_controller;
|
| 891 |
+
|
| 892 |
+
if ($user) {
|
| 893 |
+
$tfa_priv_key = get_user_meta($user->ID, 'tfa_priv_key_64', true);
|
| 894 |
+
$is_activated_for_user = $this->is_activated_for_user($user->ID);
|
| 895 |
+
$is_activated_by_user = $this->is_activated_by_user($user->ID);
|
| 896 |
+
|
| 897 |
+
if ($is_activated_for_user && $is_activated_by_user) {
|
| 898 |
+
|
| 899 |
+
// No private key yet, generate one. This shouldn't really be possible.
|
| 900 |
+
if (!$tfa_priv_key) $tfa_priv_key = $totp_controller->addPrivateKey($user->ID);
|
| 901 |
+
|
| 902 |
+
$code = $totp_controller->generateOTP($user->ID, $tfa_priv_key);
|
| 903 |
+
|
| 904 |
+
$result = true;
|
| 905 |
+
}
|
| 906 |
+
}
|
| 907 |
+
|
| 908 |
+
if ('array' != $response_format) return $result;
|
| 909 |
+
|
| 910 |
+
$ret = array('result' => $result);
|
| 911 |
+
|
| 912 |
+
if ($result) {
|
| 913 |
+
$ret['user_can_trust'] = $this->user_can_trust($user->ID);
|
| 914 |
+
if (!empty($params['trust_token']) && $this->user_trust_token_valid($user->ID, $params['trust_token'])) {
|
| 915 |
+
$ret['user_already_trusted'] = 1;
|
| 916 |
+
}
|
| 917 |
+
}
|
| 918 |
+
|
| 919 |
+
return $ret;
|
| 920 |
+
}
|
| 921 |
+
|
| 922 |
+
/**
|
| 923 |
+
* Print the radio buttons for enabling/disabling TFA
|
| 924 |
+
*
|
| 925 |
+
* @param Integer $user_id - the WordPress user ID
|
| 926 |
+
* @param Boolean $long_label - whether to use a long label rather than a short one
|
| 927 |
+
* @param String $style - valid values are "show_current" and "require_current"
|
| 928 |
+
*/
|
| 929 |
+
public function paint_enable_tfa_radios($user_id, $long_label = false, $style = 'show_current') {
|
| 930 |
+
|
| 931 |
+
if (!$user_id) return;
|
| 932 |
+
|
| 933 |
+
if ('require_current' != $style) $style = 'show_current';
|
| 934 |
+
|
| 935 |
+
$is_required = $this->is_required_for_user($user_id);
|
| 936 |
+
$is_activated = $this->is_activated_by_user($user_id);
|
| 937 |
+
|
| 938 |
+
if ($is_required) {
|
| 939 |
+
$require_after = absint($this->get_option('tfa_requireafter'));
|
| 940 |
+
echo '<p class="tfa_required_warning" style="font-weight:bold; font-style:italic;">'.sprintf(__('N.B. This site is configured to forbid you to log in if you disable two-factor authentication after your account is %d days old', 'all-in-one-wp-security-and-firewall'), $require_after).'</p>';
|
| 941 |
+
}
|
| 942 |
+
|
| 943 |
+
$tfa_enabled_label = $long_label ? __('Enable two-factor authentication', 'all-in-one-wp-security-and-firewall') : __('Enabled', 'all-in-one-wp-security-and-firewall');
|
| 944 |
+
|
| 945 |
+
if ('show_current' == $style) {
|
| 946 |
+
$tfa_enabled_label .= ' '.sprintf(__('(Current code: %s)', 'all-in-one-wp-security-and-firewall'), $this->get_totp_controller()->current_otp_code($user_id));
|
| 947 |
+
} elseif ('require_current' == $style) {
|
| 948 |
+
$tfa_enabled_label .= ' '.sprintf(__('(you must enter the current code: %s)', 'all-in-one-wp-security-and-firewall'), '<input type="text" class="tfa_enable_current" name="tfa_enable_current" size="6" style="height">');
|
| 949 |
+
}
|
| 950 |
+
|
| 951 |
+
$show_disable = ((is_multisite() && is_super_admin()) || (!is_multisite() && current_user_can($this->get_management_capability())) || false == $is_activated || !$is_required || !$this->get_option('tfa_hide_turn_off')) ? true : false;
|
| 952 |
+
|
| 953 |
+
$tfa_disabled_label = $long_label ? __('Disable two-factor authentication', 'all-in-one-wp-security-and-firewall') : __('Disabled', 'all-in-one-wp-security-and-firewall');
|
| 954 |
+
|
| 955 |
+
if ('require_current' == $style) echo '<input type="hidden" name="require_current" value="1">'."\n";
|
| 956 |
+
|
| 957 |
+
echo '<input type="radio" class="tfa_enable_radio" id="tfa_enable_tfa_true" name="tfa_enable_tfa" value="true" '.(true == $is_activated ? 'checked="checked"' : '').'> <label class="tfa_enable_radio_label" for="tfa_enable_tfa_true">'.apply_filters('simbatfa_radiolabel_enabled', $tfa_enabled_label, $long_label).'</label> <br>';
|
| 958 |
+
|
| 959 |
+
// Show the 'disabled' option if the user is an admin, or if it is currently set, or if TFA is not compulsory, or if the site owner doesn't require it to be hidden
|
| 960 |
+
// Note that this just hides the option in the UI. The user could POST to turn off TFA, but, since it's required, they won't be able to log in.
|
| 961 |
+
if ($show_disable) {
|
| 962 |
+
echo '<input type="radio" class="tfa_enable_radio" id="tfa_enable_tfa_false" name="tfa_enable_tfa" value="false" '.(false == $is_activated ? 'checked="checked"' :'').'> <label class="tfa_enable_radio_label" for="tfa_enable_tfa_false">'.apply_filters('simbatfa_radiolabel_disabled', $tfa_disabled_label, $long_label).'</label> <br>';
|
| 963 |
+
}
|
| 964 |
+
}
|
| 965 |
+
|
| 966 |
+
/**
|
| 967 |
+
* Retrieve a saved option
|
| 968 |
+
*
|
| 969 |
+
* @param String $key - option key
|
| 970 |
+
*
|
| 971 |
+
* @return Mixed
|
| 972 |
+
*/
|
| 973 |
+
public function get_option($key) {
|
| 974 |
+
if (!is_multisite()) return get_option($key);
|
| 975 |
+
$main_site_id = function_exists('get_main_site_id') ? get_main_site_id() : 1;
|
| 976 |
+
$get_option_site_id = apply_filters('simba_tfa_get_option_site_id', $main_site_id);
|
| 977 |
+
switch_to_blog($get_option_site_id);
|
| 978 |
+
$value = get_option($key);
|
| 979 |
+
restore_current_blog();
|
| 980 |
+
return $value;
|
| 981 |
+
}
|
| 982 |
+
|
| 983 |
+
/**
|
| 984 |
+
* Paint a list of checkboxes, one for each role
|
| 985 |
+
*
|
| 986 |
+
* @param String $prefix
|
| 987 |
+
* @param Integer $default - default value (0 or 1)
|
| 988 |
+
*/
|
| 989 |
+
public function list_user_roles_checkboxes($prefix = '', $default = 1) {
|
| 990 |
+
if (is_multisite()) {
|
| 991 |
+
// Not a real WP role; needs separate handling
|
| 992 |
+
$id = '_super_admin';
|
| 993 |
+
$name = __('Multisite Super Admin', 'all-in-one-wp-security-and-firewall');
|
| 994 |
+
$setting = $this->get_option('tfa_'.$prefix.$id);
|
| 995 |
+
$setting = ($setting === false) ? $default : ($setting ? 1 : 0);
|
| 996 |
+
|
| 997 |
+
echo '<input type="checkbox" id="tfa_'.$prefix.$id.'" name="tfa_'.$prefix.$id.'" value="1" '.($setting ? 'checked="checked"' :'').'> <label for="tfa_'.$prefix.$id.'">'.htmlspecialchars($name)."</label><br>\n";
|
| 998 |
+
}
|
| 999 |
+
|
| 1000 |
+
global $wp_roles;
|
| 1001 |
+
if (!isset($wp_roles)) $wp_roles = new WP_Roles();
|
| 1002 |
+
|
| 1003 |
+
foreach ($wp_roles->role_names as $id => $name) {
|
| 1004 |
+
$setting = $this->get_option('tfa_'.$prefix.$id);
|
| 1005 |
+
$setting = ($setting === false) ? $default : ($setting ? 1 : 0);
|
| 1006 |
+
|
| 1007 |
+
echo '<input type="checkbox" id="tfa_'.$prefix.$id.'" name="tfa_'.$prefix.$id.'" value="1" '.($setting ? 'checked="checked"' :'').'> <label for="tfa_'.$prefix.$id.'">'.htmlspecialchars($name)."</label><br>\n";
|
| 1008 |
+
}
|
| 1009 |
+
|
| 1010 |
+
}
|
| 1011 |
+
|
| 1012 |
+
public function tfa_list_xmlrpc_status_radios() {
|
| 1013 |
+
|
| 1014 |
+
$setting = $this->get_option('tfa_xmlrpc_on');
|
| 1015 |
+
$setting = $setting ? 1 : 0;
|
| 1016 |
+
|
| 1017 |
+
$types = array(
|
| 1018 |
+
'0' => __('Do not require 2FA over XMLRPC (best option if you must use XMLRPC and your client does not support 2FA)', 'all-in-one-wp-security-and-firewall'),
|
| 1019 |
+
'1' => __('Do require 2FA over XMLRPC (best option if you do not use XMLRPC or are unsure)', 'all-in-one-wp-security-and-firewall')
|
| 1020 |
+
);
|
| 1021 |
+
|
| 1022 |
+
foreach($types as $id => $name) {
|
| 1023 |
+
print '<input type="radio" name="tfa_xmlrpc_on" id="tfa_xmlrpc_on_'.$id.'" value="'.$id.'" '.($setting == $id ? 'checked="checked"' : '').'> <label for="tfa_xmlrpc_on_'.$id.'">'.htmlspecialchars($name)."</label><br>\n";
|
| 1024 |
+
}
|
| 1025 |
+
}
|
| 1026 |
+
|
| 1027 |
+
protected function is_caller_active() {
|
| 1028 |
+
|
| 1029 |
+
if (!defined('XMLRPC_REQUEST') || !XMLRPC_REQUEST) return true;
|
| 1030 |
+
|
| 1031 |
+
$saved_data = $this->get_option('tfa_xmlrpc_on');
|
| 1032 |
+
|
| 1033 |
+
return $saved_data ? true : false;
|
| 1034 |
+
|
| 1035 |
+
}
|
| 1036 |
+
|
| 1037 |
+
/**
|
| 1038 |
+
* @param Array $params
|
| 1039 |
+
* @param Boolean $may_be_email
|
| 1040 |
+
*
|
| 1041 |
+
* @return WP_Error|Boolean|Integer - WP_Error or false means failure; true or 1 means success, but true means the TFA code was validated
|
| 1042 |
+
*/
|
| 1043 |
+
public function authorise_user_from_login($params, $may_be_email = false) {
|
| 1044 |
+
|
| 1045 |
+
$params = apply_filters('simbatfa_auth_user_from_login_params', $params);
|
| 1046 |
+
|
| 1047 |
+
global $wpdb;
|
| 1048 |
+
|
| 1049 |
+
if (!$this->is_caller_active()) return 1;
|
| 1050 |
+
|
| 1051 |
+
$query = ($may_be_email && filter_var($params['log'], FILTER_VALIDATE_EMAIL)) ? $wpdb->prepare("SELECT ID, user_registered from ".$wpdb->users." WHERE user_email=%s", $params['log']) : $wpdb->prepare("SELECT ID, user_registered from ".$wpdb->users." WHERE user_login=%s", $params['log']);
|
| 1052 |
+
$response = $wpdb->get_row($query);
|
| 1053 |
+
|
| 1054 |
+
if (!$response && $may_be_email && filter_var($params['log'], FILTER_VALIDATE_EMAIL)) {
|
| 1055 |
+
// Corner-case: login looks like an email, but is a username rather than email address
|
| 1056 |
+
$response = $wpdb->get_row($wpdb->prepare("SELECT ID, user_registered from ".$wpdb->users." WHERE user_login=%s", $params['log']));
|
| 1057 |
+
}
|
| 1058 |
+
|
| 1059 |
+
$user_ID = is_object($response) ? $response->ID : false;
|
| 1060 |
+
$user_registered = is_object($response) ? $response->user_registered : false;
|
| 1061 |
+
|
| 1062 |
+
$user_code = isset($params['two_factor_code']) ? str_replace(' ', '', trim($params['two_factor_code'])) : '';
|
| 1063 |
+
|
| 1064 |
+
// This condition in theory should not be possible
|
| 1065 |
+
if (!$user_ID) return new WP_Error('tfa_user_not_found', apply_filters('simbatfa_tfa_user_not_found', '<strong>'.__('Error:', 'all-in-one-wp-security-and-firewall').'</strong> '.__('The indicated user could not be found.', 'all-in-one-wp-security-and-firewall')));
|
| 1066 |
+
|
| 1067 |
+
if (!$this->is_activated_for_user($user_ID)) return 1;
|
| 1068 |
+
|
| 1069 |
+
if (!empty($params['trust_token']) && $this->user_trust_token_valid($user_ID, $params['trust_token'])) {
|
| 1070 |
+
return 1;
|
| 1071 |
+
}
|
| 1072 |
+
|
| 1073 |
+
if (!$this->is_activated_by_user($user_ID)) {
|
| 1074 |
+
|
| 1075 |
+
if (!$this->is_required_for_user($user_ID)) return 1;
|
| 1076 |
+
|
| 1077 |
+
$require_after = absint($this->get_option('tfa_requireafter')) * 86400;
|
| 1078 |
+
|
| 1079 |
+
$account_age = time() - strtotime($user_registered);
|
| 1080 |
+
|
| 1081 |
+
if ($account_age > $require_after && apply_filters('simbatfa_enforce_require_after_check', true, $user_ID, $require_after, $account_age)) {
|
| 1082 |
+
return new WP_Error('tfa_required', apply_filters('simbatfa_notfa_forbidden_login', '<strong>'.__('Error:', 'all-in-one-wp-security-and-firewall').'</strong> '.__('The site owner has forbidden you to login without two-factor authentication. Please contact the site owner to re-gain access.', 'all-in-one-wp-security-and-firewall')));
|
| 1083 |
+
}
|
| 1084 |
+
|
| 1085 |
+
return 1;
|
| 1086 |
+
}
|
| 1087 |
+
|
| 1088 |
+
$tfa_creds_user_id = !empty($params['creds_user_id']) ? $params['creds_user_id'] : $user_ID;
|
| 1089 |
+
|
| 1090 |
+
if ($tfa_creds_user_id != $user_ID) {
|
| 1091 |
+
|
| 1092 |
+
// Authenticating using a different user's credentials (e.g. https://wordpress.org/plugins/use-administrator-password/)
|
| 1093 |
+
// In this case, we require that different user to have TFA active - so that this mechanism can't be used to avoid TFA
|
| 1094 |
+
|
| 1095 |
+
if (!$this->is_activated_for_user($tfa_creds_user_id) || !$this->is_activated_by_user($tfa_creds_user_id)) {
|
| 1096 |
+
return new WP_Error('tfa_required', apply_filters('simbatfa_notfa_forbidden_login_altuser', '<strong>'.__('Error:', 'all-in-one-wp-security-and-firewall').'</strong> '.__('You are attempting to log in to an account that has two-factor authentication enabled; this requires you to also have two-factor authentication enabled on the account whose credentials you are using.', 'all-in-one-wp-security-and-firewall')));
|
| 1097 |
+
}
|
| 1098 |
+
|
| 1099 |
+
}
|
| 1100 |
+
|
| 1101 |
+
return $this->totp_controller->check_code_for_user($tfa_creds_user_id, $user_code);
|
| 1102 |
+
|
| 1103 |
+
}
|
| 1104 |
+
|
| 1105 |
+
/**
|
| 1106 |
+
* Evaluate whether a trust token is valid for a user
|
| 1107 |
+
*
|
| 1108 |
+
* @param Integer $user_id - WP user ID
|
| 1109 |
+
* @param String $trust_token - trust token
|
| 1110 |
+
*
|
| 1111 |
+
* @return Boolean
|
| 1112 |
+
*/
|
| 1113 |
+
protected function user_trust_token_valid($user_id, $trust_token) {
|
| 1114 |
+
|
| 1115 |
+
if (!is_string($trust_token) || strlen($trust_token) < 30) return false;
|
| 1116 |
+
|
| 1117 |
+
$trusted_devices = $this->user_get_trusted_devices($user_id);
|
| 1118 |
+
|
| 1119 |
+
$time_now = time();
|
| 1120 |
+
|
| 1121 |
+
foreach ($trusted_devices as $device) {
|
| 1122 |
+
if (empty($device['until']) || $device['until'] <= $time_now) continue;
|
| 1123 |
+
if (!empty($device['token']) && $device['token'] === $trust_token) {
|
| 1124 |
+
return true;
|
| 1125 |
+
}
|
| 1126 |
+
}
|
| 1127 |
+
|
| 1128 |
+
return false;
|
| 1129 |
+
}
|
| 1130 |
+
|
| 1131 |
+
/**
|
| 1132 |
+
* This deals with the issue that wp-login.php does not redirect to a canonical URL. As a result, if a website is available under more than one host, then admin_url('admin-ajax.php') might return a different one than the visitor is using, resulting in AJAX failing due to CORS errors.
|
| 1133 |
+
*
|
| 1134 |
+
* @return String
|
| 1135 |
+
*/
|
| 1136 |
+
protected function get_ajax_url() {
|
| 1137 |
+
$ajax_url = admin_url('admin-ajax.php');
|
| 1138 |
+
$parsed_url = parse_url($ajax_url);
|
| 1139 |
+
if (strtolower($parsed_url['host']) !== strtolower($_SERVER['HTTP_HOST']) && !empty($parsed_url['path'])) {
|
| 1140 |
+
// Mismatch - return the relative URL only
|
| 1141 |
+
$ajax_url = $parsed_url['path'];
|
| 1142 |
+
}
|
| 1143 |
+
return $ajax_url;
|
| 1144 |
+
}
|
| 1145 |
+
|
| 1146 |
+
/**
|
| 1147 |
+
* Called not only upon the WP action login_enqueue_scripts, but potentially upon the action 'init' and various others from other plugins too. It can handle being called multiple times.
|
| 1148 |
+
*/
|
| 1149 |
+
public function login_enqueue_scripts() {
|
| 1150 |
+
|
| 1151 |
+
if (isset($_GET['action']) && 'logout ' != $_GET['action'] && 'login' != $_GET['action']) return;
|
| 1152 |
+
|
| 1153 |
+
static $already_done = false;
|
| 1154 |
+
if ($already_done) return;
|
| 1155 |
+
$already_done = true;
|
| 1156 |
+
|
| 1157 |
+
// Prevent cacheing when in debug mode
|
| 1158 |
+
$script_ver = (defined('WP_DEBUG') && WP_DEBUG) ? time() : filemtime($this->includes_dir().'/tfa.js');
|
| 1159 |
+
|
| 1160 |
+
wp_enqueue_script('tfa-ajax-request', $this->includes_url().'/tfa.js', array('jquery'), $script_ver);
|
| 1161 |
+
|
| 1162 |
+
$trusted_for = $this->get_option('tfa_trusted_for');
|
| 1163 |
+
$trusted_for = (false === $trusted_for) ? 30 : (string) absint($trusted_for);
|
| 1164 |
+
|
| 1165 |
+
$localize = array(
|
| 1166 |
+
'ajaxurl' => $this->get_ajax_url(),
|
| 1167 |
+
'click_to_enter_otp' => __("Click to enter One Time Password", 'all-in-one-wp-security-and-firewall'),
|
| 1168 |
+
'enter_username_first' => __('You have to enter a username first.', 'all-in-one-wp-security-and-firewall'),
|
| 1169 |
+
'otp' => __('One Time Password (i.e. 2FA)', 'all-in-one-wp-security-and-firewall'),
|
| 1170 |
+
'otp_login_help' => __('(check your OTP app to get this password)', 'all-in-one-wp-security-and-firewall'),
|
| 1171 |
+
'mark_as_trusted' => sprintf(_n('Trust this device (allow login without 2FA for %d day)', 'Trust this device (allow login without TFA for %d days)', $trusted_for, 'all-in-one-wp-security-and-firewall'), $trusted_for),
|
| 1172 |
+
'is_trusted' => __('(Trusted device)', 'all-in-one-wp-security-and-firewall'),
|
| 1173 |
+
'nonce' => wp_create_nonce('simba_tfa_loginform_nonce'),
|
| 1174 |
+
'login_form_selectors' => '',
|
| 1175 |
+
'login_form_off_selectors' => '',
|
| 1176 |
+
);
|
| 1177 |
+
|
| 1178 |
+
// Spinner exists since WC 3.8. Use the proper functions to avoid SSL warnings.
|
| 1179 |
+
if (file_exists(ABSPATH.'wp-admin/images/spinner-2x.gif')) {
|
| 1180 |
+
$localize['spinnerimg'] = admin_url('images/spinner-2x.gif');
|
| 1181 |
+
} elseif (file_exists(ABSPATH.WPINC.'/images/spinner-2x.gif')) {
|
| 1182 |
+
$localize['spinnerimg'] = includes_url('images/spinner-2x.gif');
|
| 1183 |
+
}
|
| 1184 |
+
|
| 1185 |
+
$localize = apply_filters('simba_tfa_login_enqueue_localize', $localize);
|
| 1186 |
+
|
| 1187 |
+
wp_localize_script('tfa-ajax-request', 'simba_tfasettings', $localize);
|
| 1188 |
+
|
| 1189 |
+
}
|
| 1190 |
+
|
| 1191 |
+
/**
|
| 1192 |
+
* Return or output view content
|
| 1193 |
+
*
|
| 1194 |
+
* @param String $path - path to template, usually relative to templates/ within the plugin directory
|
| 1195 |
+
* @param Array $extract_these - key/value pairs for substitution into the scope of the template
|
| 1196 |
+
* @param Boolean $return_instead_of_echo - what to do with the results
|
| 1197 |
+
*
|
| 1198 |
+
* @return String|Void
|
| 1199 |
+
*/
|
| 1200 |
+
public function include_template($path, $extract_these = array(), $return_instead_of_echo = false) {
|
| 1201 |
+
|
| 1202 |
+
if ($return_instead_of_echo) ob_start();
|
| 1203 |
+
|
| 1204 |
+
$template_file = apply_filters('simatfa_template_file', $this->templates_dir().'/'.$path, $path, $extract_these, $return_instead_of_echo);
|
| 1205 |
+
|
| 1206 |
+
do_action('simbatfa_before_template', $path, $return_instead_of_echo, $extract_these, $template_file);
|
| 1207 |
+
|
| 1208 |
+
if (!file_exists($template_file)) {
|
| 1209 |
+
error_log("TFA: template not found: $template_file (from $path)");
|
| 1210 |
+
echo __('Error:', 'all-in-one-wp-security-and-firewall').' '.__('two-factor-authentication', 'wp-optimize')." (".$path.")";
|
| 1211 |
+
} else {
|
| 1212 |
+
extract($extract_these);
|
| 1213 |
+
// The following are useful variables which can be used in the template.
|
| 1214 |
+
// They appear as unused, but may be used in the $template_file.
|
| 1215 |
+
$wpdb = $GLOBALS['wpdb'];// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- $wpdb might be used in the included template
|
| 1216 |
+
$simba_tfa = $this;// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- $wp_optimize might be used in the included template
|
| 1217 |
+
include $template_file;
|
| 1218 |
+
}
|
| 1219 |
+
|
| 1220 |
+
do_action('simbatfa_after_template', $path, $return_instead_of_echo, $extract_these, $template_file);
|
| 1221 |
+
|
| 1222 |
+
if ($return_instead_of_echo) return ob_get_clean();
|
| 1223 |
+
}
|
| 1224 |
+
|
| 1225 |
+
/**
|
| 1226 |
+
* Make sure that self::$frontend is the instance of Simba_TFA_Frontend, and return it
|
| 1227 |
+
*
|
| 1228 |
+
* @return Simba_TFA_Frontend
|
| 1229 |
+
*/
|
| 1230 |
+
public function load_frontend() {
|
| 1231 |
+
if (!class_exists('Simba_TFA_Frontend')) require_once($this->includes_dir().'/tfa_frontend.php');
|
| 1232 |
+
if (empty($this->frontend)) $this->frontend = new Simba_TFA_Frontend($this);
|
| 1233 |
+
return $this->frontend;
|
| 1234 |
+
}
|
| 1235 |
+
|
| 1236 |
+
// __return_empty_string() does not exist until WP 3.7
|
| 1237 |
+
public function shortcode_when_not_logged_in() {
|
| 1238 |
+
return '';
|
| 1239 |
+
}
|
| 1240 |
+
|
| 1241 |
+
/**
|
| 1242 |
+
* Set authentication slug.
|
| 1243 |
+
*
|
| 1244 |
+
* @param String $authentication_slug - Authentication slug. Verify that two-factor authentication should not be repeated for the same slug.
|
| 1245 |
+
*/
|
| 1246 |
+
public function set_authentication_slug($authentication_slug) {
|
| 1247 |
+
$this->authentication_slug = $authentication_slug;
|
| 1248 |
+
}
|
| 1249 |
+
|
| 1250 |
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
|
| 3 |
+
if (!defined('ABSPATH')) die('Access denied.');
|
| 4 |
+
|
| 5 |
+
?><div class="wrap">
|
| 6 |
+
|
| 7 |
+
<div>
|
| 8 |
+
<h1><?php echo esc_html(empty($settings_page_heading) ? __('Two Factor Authentication - Admin Settings', 'all-in-one-wp-security-and-firewall') : $settings_page_heading); ?></h1>
|
| 9 |
+
<?php
|
| 10 |
+
if (!empty($admin_settings_links) && is_array($admin_settings_links)) {
|
| 11 |
+
echo implode(' | ', array_map(function($val) {
|
| 12 |
+
return '<a href="'.esc_url($val['url']).'">' . esc_html($val['title']) . '</a>';
|
| 13 |
+
}, $admin_settings_links));
|
| 14 |
+
echo '<br>';
|
| 15 |
+
}
|
| 16 |
+
?>
|
| 17 |
+
</div>
|
| 18 |
+
|
| 19 |
+
<?php if (defined('TWO_FACTOR_DISABLE') && TWO_FACTOR_DISABLE) { ?>
|
| 20 |
+
<div class="error">
|
| 21 |
+
<h3><?php _e('Two Factor Authentication currently disabled', 'all-in-one-wp-security-and-firewall');?></h3>
|
| 22 |
+
<p>
|
| 23 |
+
<?php _e('Two factor authentication is currently disabled via the TWO_FACTOR_DISABLE constant (which is mostly likely to be defined in your wp-config.php)', 'all-in-one-wp-security-and-firewall'); ?>
|
| 24 |
+
</p>
|
| 25 |
+
</div>
|
| 26 |
+
<?php } ?>
|
| 27 |
+
|
| 28 |
+
<div style="max-width:800px;">
|
| 29 |
+
|
| 30 |
+
<?php
|
| 31 |
+
if (is_multisite()) {
|
| 32 |
+
if (is_super_admin()) {
|
| 33 |
+
?>
|
| 34 |
+
<p style="font-size: 120%; font-weight: bold;">
|
| 35 |
+
<?php _e('N.B. These two-factor settings apply to your entire WordPress network. (i.e. They are not localised to one particular site).', 'all-in-one-wp-security-and-firewall');?>
|
| 36 |
+
</p>
|
| 37 |
+
<?php
|
| 38 |
+
} else {
|
| 39 |
+
// Should not be possible to reach this; but an extra check does not hurt.
|
| 40 |
+
die('Security check');
|
| 41 |
+
}
|
| 42 |
+
}
|
| 43 |
+
?>
|
| 44 |
+
|
| 45 |
+
<form method="post" action="options.php" style="margin-top: 12px">
|
| 46 |
+
<?php settings_fields('tfa_user_roles_group'); ?>
|
| 47 |
+
<h2><?php _e('User roles', 'all-in-one-wp-security-and-firewall'); ?></h2>
|
| 48 |
+
<?php _e('Choose which user roles will have two factor authentication available.', 'all-in-one-wp-security-and-firewall'); ?>
|
| 49 |
+
<p>
|
| 50 |
+
<?php $simba_tfa->list_user_roles_checkboxes(); ?>
|
| 51 |
+
</p>
|
| 52 |
+
<?php submit_button(); ?>
|
| 53 |
+
</form>
|
| 54 |
+
|
| 55 |
+
<hr>
|
| 56 |
+
|
| 57 |
+
<div class="tfa-premium">
|
| 58 |
+
<h2><?php _e('Make two factor authentication compulsory', 'all-in-one-wp-security-and-firewall'); ?></h2>
|
| 59 |
+
|
| 60 |
+
<?php
|
| 61 |
+
|
| 62 |
+
$output = '<p><a href="'.esc_url($simba_tfa->get_premium_version_url()).'">'.__('Requiring users to use two-factor authentication is a feature of the Premium version of this plugin.', 'all-in-one-wp-security-and-firewall').'</a><p>';
|
| 63 |
+
echo apply_filters('simba_tfa_after_user_roles', $output);
|
| 64 |
+
|
| 65 |
+
?>
|
| 66 |
+
|
| 67 |
+
<hr>
|
| 68 |
+
<h2><?php _e('Trusted devices', 'all-in-one-wp-security-and-firewall'); ?></h2>
|
| 69 |
+
|
| 70 |
+
<form method="post" action="options.php" style="margin-top: 12px">
|
| 71 |
+
<?php settings_fields('tfa_user_roles_trusted_group'); ?>
|
| 72 |
+
<?php _e('Choose which user roles are permitted to mark devices they login on as trusted. This feature requires browser cookies and an https (i.e. SSL) connection to the website to work.', 'all-in-one-wp-security-and-firewall'); ?>
|
| 73 |
+
|
| 74 |
+
<?php
|
| 75 |
+
$output = '<p><a href="'.esc_url($simba_tfa->get_premium_version_url()).'">'.__('Allowing users to mark a device as trusted so that a two-factor code is only needed once in a specified number of days (instead of every login) is a feature of the Premium version of this plugin.', 'all-in-one-wp-security-and-firewall').'</a><p>';
|
| 76 |
+
echo apply_filters('simba_tfa_trusted_devices_config', $output);
|
| 77 |
+
?>
|
| 78 |
+
</form>
|
| 79 |
+
</div>
|
| 80 |
+
</form>
|
| 81 |
+
|
| 82 |
+
<div>
|
| 83 |
+
<hr>
|
| 84 |
+
<form method="post" action="options.php" style="margin-top: 40px">
|
| 85 |
+
<?php
|
| 86 |
+
settings_fields('tfa_xmlrpc_status_group');
|
| 87 |
+
?>
|
| 88 |
+
<h2><?php _e('XMLRPC requests', 'all-in-one-wp-security-and-firewall'); ?></h2>
|
| 89 |
+
<?php
|
| 90 |
+
|
| 91 |
+
echo '<p>';
|
| 92 |
+
echo __("XMLRPC is a feature within WordPress allowing other computers to talk to your WordPress install. For example, it could be used by an app on your tablet that allows you to blog directly from the app (instead of needing the WordPress dashboard).", 'all-in-one-wp-security-and-firewall');
|
| 93 |
+
echo '</p><p>';
|
| 94 |
+
|
| 95 |
+
echo __("Unfortunately, XMLRPC also provides a way for attackers to perform actions on your WordPress site, using only a password (i.e. without a two-factor password). More unfortunately, authors of legitimate programmes using XMLRPC have not yet added two-factor support to their code.", 'all-in-one-wp-security-and-firewall');
|
| 96 |
+
echo '</p><p>';
|
| 97 |
+
|
| 98 |
+
echo __("i.e. XMLRPC requests coming in to WordPress (whether from a legitimate app, or from an attacker) can only be verified using the password - not with a two-factor code. As a result, there not be an ideal option to pick below. You may have to choose between the convenience of using your apps, or the security of two factor authentication.", 'all-in-one-wp-security-and-firewall');
|
| 99 |
+
echo '</p>';
|
| 100 |
+
|
| 101 |
+
?>
|
| 102 |
+
<p>
|
| 103 |
+
<?php $simba_tfa->tfa_list_xmlrpc_status_radios(); ?>
|
| 104 |
+
</p>
|
| 105 |
+
<?php submit_button(); ?>
|
| 106 |
+
</form>
|
| 107 |
+
</div>
|
| 108 |
+
|
| 109 |
+
<div id="simba-tfa-admin-settings-algorithm">
|
| 110 |
+
<hr>
|
| 111 |
+
<form method="post" action="options.php" style="margin-top: 40px">
|
| 112 |
+
<?php settings_fields('simba_tfa_default_hmac_group'); ?>
|
| 113 |
+
<h2><?php _e('Default algorithm', 'all-in-one-wp-security-and-firewall'); ?></h2>
|
| 114 |
+
<?php _e('Your users can change this in their own settings if they want.', 'all-in-one-wp-security-and-firewall'); ?>
|
| 115 |
+
<p>
|
| 116 |
+
<?php
|
| 117 |
+
$totp_controller->print_default_hmac_radios();
|
| 118 |
+
?></p>
|
| 119 |
+
<?php submit_button(); ?>
|
| 120 |
+
</form>
|
| 121 |
+
</div>
|
| 122 |
+
|
| 123 |
+
<hr>
|
| 124 |
+
|
| 125 |
+
<?php
|
| 126 |
+
if (function_exists('WC')) {
|
| 127 |
+
|
| 128 |
+
?>
|
| 129 |
+
<br><br>
|
| 130 |
+
<h2><?php _e("WooCommerce integration", 'all-in-one-wp-security-and-firewall'); ?></h2>
|
| 131 |
+
<p>
|
| 132 |
+
<?php echo apply_filters('simba_tfa_settings_woocommerce', '<a href="'.esc_url($simba_tfa->get_premium_version_url()).'">'.__('The Premium version of this plugin allows you to add a configuration tab for users in the WooCommerce "My account" area, and anti-bot protection on the WooCommerce login form.', 'all-in-one-wp-security-and-firewall').'</a>'); ?>
|
| 133 |
+
</p>
|
| 134 |
+
<hr>
|
| 135 |
+
<?php } ?>
|
| 136 |
+
|
| 137 |
+
<br>
|
| 138 |
+
|
| 139 |
+
<div class="tfa-premium">
|
| 140 |
+
<br>
|
| 141 |
+
<h2><?php _e("Users' settings", 'all-in-one-wp-security-and-firewall'); ?></h2>
|
| 142 |
+
<p>
|
| 143 |
+
|
| 144 |
+
<?php
|
| 145 |
+
if (!class_exists('Simba_Two_Factor_Authentication_Premium')) { ?>
|
| 146 |
+
|
| 147 |
+
<a href="<?php echo esc_url($simba_tfa->get_premium_version_url()); ?>"><?php _e("The Premium version of this plugin allows you to see and reset the TFA settings of other users.", 'all-in-one-wp-security-and-firewall'); ?></a>
|
| 148 |
+
|
| 149 |
+
<a href="https://wordpress.org/plugins/user-switching/"><?php _e('Another way to do that is by using a user-switching plugin like this one.', 'all-in-one-wp-security-and-firewall'); ?></a>
|
| 150 |
+
|
| 151 |
+
<?php } ?>
|
| 152 |
+
|
| 153 |
+
<?php do_action('simba_tfa_users_settings'); ?>
|
| 154 |
+
|
| 155 |
+
<hr>
|
| 156 |
+
<?php if (!class_exists('Simba_Two_Factor_Authentication_Premium')) { ?>
|
| 157 |
+
<h2><?php _e('Premium version', 'all-in-one-wp-security-and-firewall'); ?></h2>
|
| 158 |
+
<p>
|
| 159 |
+
<a href="<?php echo esc_url($simba_tfa->get_premium_version_url()); ?>"><?php _e("If you want to say 'thank you' or help this plugin's development, or get extra features, then please take a look at the premium version of this plugin.", 'all-in-one-wp-security-and-firewall'); ?></a> <?php _e('It comes with these extra features:', 'all-in-one-wp-security-and-firewall');?><br>
|
| 160 |
+
</p>
|
| 161 |
+
<p>
|
| 162 |
+
<ul style="list-style: disc inside;">
|
| 163 |
+
<li><strong><?php _e('Emergency codes', 'all-in-one-wp-security-and-firewall');?></strong> - <?php _e('provide your users with one-time codes to use in case they lose their device.', 'all-in-one-wp-security-and-firewall');?></li>
|
| 164 |
+
<li><strong><?php _e('Make TFA compulsory', 'all-in-one-wp-security-and-firewall');?></strong> - <?php _e('require your users to set up TFA to be able to log in, after an optional grace period.', 'all-in-one-wp-security-and-firewall');?></li>
|
| 165 |
+
<li><strong><?php _e('Trusted devices', 'all-in-one-wp-security-and-firewall');?></strong> - <?php _e('allow privileged (or all) users to mark a device as trusted and thereby only needing to supply a TFA code upon login every so-many days (e.g. every 30 days) instead of on each login.', 'all-in-one-wp-security-and-firewall');?></li>
|
| 166 |
+
<li><strong><?php _e('Manage all users centrally', 'all-in-one-wp-security-and-firewall');?></strong> - <?php _e('enable, disable or see TFA codes for all your users from one central location.', 'all-in-one-wp-security-and-firewall');?></li>
|
| 167 |
+
<li><strong><?php _e('More shortcodes', 'all-in-one-wp-security-and-firewall');?></strong> - <?php _e('flexible shortcodes allowing you to design your front-end settings page for your users exactly as you wish.', 'all-in-one-wp-security-and-firewall');?></li>
|
| 168 |
+
<li><strong><?php _e('More WooCommerce features', 'all-in-one-wp-security-and-firewall');?></strong> - <?php _e('automatically add TFA settings in the WooCommerce account settings, and WooCommerce login form bot protection.', 'all-in-one-wp-security-and-firewall');?></li>
|
| 169 |
+
<li><strong><?php _e('Elementor support', 'all-in-one-wp-security-and-firewall');?></strong> - <?php _e('adds support for Elementor login forms.', 'all-in-one-wp-security-and-firewall');?></li>
|
| 170 |
+
<li><strong><?php _e('Any-form support', 'all-in-one-wp-security-and-firewall');?></strong> - <?php _e('adds support for any login form from any plugin via appending your TFA code onto the end of your password.', 'all-in-one-wp-security-and-firewall');?></li>
|
| 171 |
+
<li><strong><?php _e('Personal support', 'all-in-one-wp-security-and-firewall');?></strong> - <?php _e('access to our personal support desk for 12 months.', 'all-in-one-wp-security-and-firewall');?></li>
|
| 172 |
+
</ul>
|
| 173 |
+
</p>
|
| 174 |
+
<hr>
|
| 175 |
+
<?php } ?>
|
| 176 |
+
</div>
|
| 177 |
+
|
| 178 |
+
<h2><?php _e('Translations', 'all-in-one-wp-security-and-firewall'); ?></h2>
|
| 179 |
+
<p>
|
| 180 |
+
<?php echo sprintf(__("If you want to translate this plugin, please go to %s", 'all-in-one-wp-security-and-firewall'), '<a href="'.esc_url($simba_tfa->get_plugin_translate_url()).'">'.__('the wordpress.org translation website.', 'all-in-one-wp-security-and-firewall').'</a>').' '.__("Don't send us the translation file directly - plugin authors do not have access to the wordpress.org translation system (local language teams do).", 'all-in-one-wp-security-and-firewall'); ?>
|
| 181 |
+
<br>
|
| 182 |
+
</p>
|
| 183 |
+
|
| 184 |
+
</div>
|
| 185 |
+
</div>
|
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<p class="simba_tfa_personal_settings_notice simba_tfa_intro_notice">
|
| 2 |
+
<?php
|
| 3 |
+
|
| 4 |
+
echo __('These are your personal settings.', 'all-in-one-wp-security-and-firewall').' '.__('Nothing you change here will have any effect on other users.', 'all-in-one-wp-security-and-firewall');
|
| 5 |
+
|
| 6 |
+
if (is_multisite()) {
|
| 7 |
+
if (is_super_admin()) {
|
| 8 |
+
// Since WP 4.9
|
| 9 |
+
$main_site_id = function_exists('get_main_site_id') ? get_main_site_id() : 1;
|
| 10 |
+
$switched = switch_to_blog($main_site_id);
|
| 11 |
+
echo ' <a href="'.esc_url($simba_tfa->get_site_wide_administration_url()).'">'.__('The site-wide administration options are here.', 'all-in-one-wp-security-and-firewall').'</a>';
|
| 12 |
+
if ($switched) restore_current_blog();
|
| 13 |
+
}
|
| 14 |
+
} elseif (current_user_can($simba_tfa->get_management_capability())) {
|
| 15 |
+
echo ' <a href="'.esc_url($simba_tfa->get_site_wide_administration_url()).'">'.__('The site-wide administration options are here.', 'all-in-one-wp-security-and-firewall').'</a>';
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
?>
|
| 19 |
+
</p>
|
| 20 |
+
|
| 21 |
+
<p class="simba_tfa_verify_tfa_notice simba_tfa_intro_notice"><strong>
|
| 22 |
+
|
| 23 |
+
<?php echo apply_filters('simbatfa_message_you_should_verify', __('If you activate two-factor authentication, then verify that your two-factor application and this page show the same One-Time Password (within a minute of each other) before you log out.', 'all-in-one-wp-security-and-firewall')); ?></strong>
|
| 24 |
+
|
| 25 |
+
<?php if (current_user_can($simba_tfa->get_management_capability())) { ?>
|
| 26 |
+
<a href="<?php echo esc_url($simba_tfa->get_faq_url()); ?>"><?php _e('You should also bookmark the FAQs, which explain how to de-activate the plugin even if you cannot log in.', 'all-in-one-wp-security-and-firewall');?></a>
|
| 27 |
+
<?php } ?>
|
| 28 |
+
</p>
|
|
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
|
| 3 |
+
if (!defined('ABSPATH')) die('No direct access.');
|
| 4 |
+
|
| 5 |
+
if (!$is_activated_for_user) {
|
| 6 |
+
_e('Two factor authentication is not available for your user.', 'all-in-one-wp-security-and-firewall');
|
| 7 |
+
} else {
|
| 8 |
+
|
| 9 |
+
?>
|
| 10 |
+
|
| 11 |
+
<div class="wrap" style="padding-bottom:10px">
|
| 12 |
+
|
| 13 |
+
<?php $simba_tfa->include_template('settings-intro-notices.php'); ?>
|
| 14 |
+
|
| 15 |
+
<?php $tfa_frontend->settings_enable_or_disable_output(); ?>
|
| 16 |
+
|
| 17 |
+
<?php $simba_tfa->get_totp_controller()->current_codes_box(); ?>
|
| 18 |
+
|
| 19 |
+
<?php $simba_tfa->get_totp_controller()->advanced_settings_box(array($tfa_frontend, 'save_settings_button')); ?>
|
| 20 |
+
|
| 21 |
+
</div>
|
| 22 |
+
|
| 23 |
+
<?php $tfa_frontend->save_settings_javascript_output(); ?>
|
| 24 |
+
|
| 25 |
+
<?php
|
| 26 |
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php if (!defined('ABSPATH')) die('No direct access.'); ?>
|
| 2 |
+
|
| 3 |
+
<div id="tfa_trusted_devices_box_inner">
|
| 4 |
+
|
| 5 |
+
<p><?php _e('Trusted devices are devices which have previously logged in with a second factor, belonging to users who have been permitted to mark devices as trusted, and for which the user checked the checkbox on the login form to trust the device.', 'all-in-one-wp-security-and-firewall'); ?></p>
|
| 6 |
+
|
| 7 |
+
<?php
|
| 8 |
+
|
| 9 |
+
global $current_user;
|
| 10 |
+
|
| 11 |
+
$trusted_devices = $this->user_get_trusted_devices($current_user->ID);
|
| 12 |
+
|
| 13 |
+
if (empty($trusted_devices)) {
|
| 14 |
+
echo '<em>'.__('(none)', 'all-in-one-wp-security-and-firewall').'</em>';
|
| 15 |
+
}
|
| 16 |
+
|
| 17 |
+
foreach ($trusted_devices as $device_id => $device) {
|
| 18 |
+
|
| 19 |
+
if (!isset($device['token']) || '' == $device['token']) continue;
|
| 20 |
+
|
| 21 |
+
$user_agent = empty($device['user_agent']) ? __('(unspecified)', 'all-in-one-wp-security-and-firewall'): $device['user_agent'];
|
| 22 |
+
|
| 23 |
+
echo '<span class="simbatfa_trusted_device">'.sprintf(__('User agent %s logged in from IP address %s and is trusted until %s', 'all-in-one-wp-security-and-firewall'), '<strong>'.htmlspecialchars($user_agent).'</strong>', '<strong><a target="_blank" href="https://ipinfo.io/'.$device['ip'].'">'.htmlspecialchars($device['ip']).'</a></strong>', '<strong>'.date_i18n(get_option('time_format').' '.get_option('date_format'), $device['until']).'</strong>').' - <a href="#" class="simbatfa-trust-remove" data-trusted-device-id="'.esc_attr($device_id).'">'.__('Remove trust', 'all-in-one-wp-security-and-firewall').'</a></span><br>';
|
| 24 |
+
|
| 25 |
+
}
|
| 26 |
+
|
| 27 |
+
?>
|
| 28 |
+
|
| 29 |
+
</div>
|
|
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<?php
|
| 2 |
+
|
| 3 |
+
if (!defined('ABSPATH')) die('Access denied.');
|
| 4 |
+
|
| 5 |
+
global $current_user;
|
| 6 |
+
$totp_controller = $simba_tfa->get_totp_controller();
|
| 7 |
+
|
| 8 |
+
?>
|
| 9 |
+
<style>
|
| 10 |
+
#icon-tfa-plugin {
|
| 11 |
+
background: transparent url('<?php print plugin_dir_url(__FILE__); ?>img/tfa_admin_icon_32x32.png' ) no-repeat;
|
| 12 |
+
}
|
| 13 |
+
.inside > h3, .normal {
|
| 14 |
+
cursor: default;
|
| 15 |
+
margin-top: 20px;
|
| 16 |
+
}
|
| 17 |
+
</style>
|
| 18 |
+
<div class="wrap">
|
| 19 |
+
|
| 20 |
+
<h2><?php echo __('Two Factor Authentication', 'all-in-one-wp-security-and-firewall').' '.__('Settings', 'all-in-one-wp-security-and-firewall'); ?></h2>
|
| 21 |
+
|
| 22 |
+
<?php
|
| 23 |
+
|
| 24 |
+
if (!empty($totp_controller->were_settings_saved())) {
|
| 25 |
+
echo '<div class="updated notice is-dismissible">'."<p><strong>".__('Settings saved.', 'all-in-one-wp-security-and-firewall')."</strong></p></div>";
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
$simba_tfa->include_template('settings-intro-notices.php');
|
| 29 |
+
|
| 30 |
+
?>
|
| 31 |
+
|
| 32 |
+
<form method="post" action="<?php print esc_url(add_query_arg('settings-updated', 'true', $_SERVER['REQUEST_URI'])); ?>">
|
| 33 |
+
|
| 34 |
+
<?php wp_nonce_field('tfa_activate', '_tfa_activate_nonce', false, true); ?>
|
| 35 |
+
|
| 36 |
+
<h2><?php _e('Activate two factor authentication', 'all-in-one-wp-security-and-firewall'); ?></h2>
|
| 37 |
+
<p>
|
| 38 |
+
<?php
|
| 39 |
+
$utc_date = gmdate('Y-m-d H:i:s');
|
| 40 |
+
$date_now = get_date_from_gmt($utc_date, 'Y-m-d H:i:s');
|
| 41 |
+
echo sprintf(__('N.B. Getting your TFA app/device to generate the correct code depends upon a) you first setting it up by entering or scanning the code below into it, and b) upon your web-server and your TFA app/device agreeing upon the UTC time (within a minute or so). The current UTC time according to the server when this page loaded: %s, and in the time-zone you have configured in your WordPress settings: %s', 'all-in-one-wp-security-and-firewall'), htmlspecialchars($utc_date), htmlspecialchars($date_now));
|
| 42 |
+
?>
|
| 43 |
+
</p>
|
| 44 |
+
<p>
|
| 45 |
+
<?php
|
| 46 |
+
$simba_tfa->paint_enable_tfa_radios($current_user->ID);
|
| 47 |
+
?></p>
|
| 48 |
+
<?php submit_button(); ?>
|
| 49 |
+
</form>
|
| 50 |
+
|
| 51 |
+
<?php
|
| 52 |
+
|
| 53 |
+
$totp_controller->current_codes_box();
|
| 54 |
+
|
| 55 |
+
$totp_controller->advanced_settings_box();
|
| 56 |
+
|
| 57 |
+
do_action('simba_tfa_user_settings_after_advanced_settings');
|
| 58 |
+
|
| 59 |
+
?>
|
| 60 |
+
|
| 61 |
+
</div>
|
|
@@ -3,7 +3,7 @@ jQuery(document).ready(function($){
|
|
| 3 |
|
| 4 |
//Media Uploader - start
|
| 5 |
function aiowps_attach_media_uploader(key) {
|
| 6 |
-
jQuery('#' + key + '_button').click
|
| 7 |
text_element = jQuery('#' + key).attr('name');
|
| 8 |
button_element = jQuery('#' + key + '_button').attr('name');
|
| 9 |
tb_show('All In One Security - Please Select a File', 'media-upload.php?referer=aiowpsec&TB_iframe=true&post_id=0width=640&height=485');
|
|
@@ -31,7 +31,7 @@ jQuery(document).ready(function($){
|
|
| 31 |
|
| 32 |
//Triggers the more info toggle link
|
| 33 |
$(".aiowps_more_info_body").hide();//hide the more info on page load
|
| 34 |
-
$(
|
| 35 |
$(this).next(".aiowps_more_info_body").animate({ "height": "toggle"});
|
| 36 |
var toogle_char_ref = $(this).find(".aiowps_more_info_toggle_char");
|
| 37 |
var toggle_char_value = toogle_char_ref.text();
|
|
@@ -59,6 +59,46 @@ jQuery(document).ready(function($){
|
|
| 59 |
jQuery('input[name=aiowps_enable_brute_force_attack_prevention]').on('click', function() {
|
| 60 |
jQuery('input[name=aiowps_brute_force_secret_word]').prop('disabled', !jQuery(this).prop('checked'));
|
| 61 |
jQuery('input[name=aiowps_cookie_based_brute_force_redirect_url]').prop('disabled', !jQuery(this).prop('checked'));
|
|
|
|
|
|
|
| 62 |
});
|
| 63 |
// End of brute force attack prevention toggle handling
|
| 64 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
|
| 4 |
//Media Uploader - start
|
| 5 |
function aiowps_attach_media_uploader(key) {
|
| 6 |
+
jQuery('#' + key + '_button').on('click', function() {
|
| 7 |
text_element = jQuery('#' + key).attr('name');
|
| 8 |
button_element = jQuery('#' + key + '_button').attr('name');
|
| 9 |
tb_show('All In One Security - Please Select a File', 'media-upload.php?referer=aiowpsec&TB_iframe=true&post_id=0width=640&height=485');
|
| 31 |
|
| 32 |
//Triggers the more info toggle link
|
| 33 |
$(".aiowps_more_info_body").hide();//hide the more info on page load
|
| 34 |
+
$('.aiowps_more_info_anchor').on('click', function() {
|
| 35 |
$(this).next(".aiowps_more_info_body").animate({ "height": "toggle"});
|
| 36 |
var toogle_char_ref = $(this).find(".aiowps_more_info_toggle_char");
|
| 37 |
var toggle_char_value = toogle_char_ref.text();
|
| 59 |
jQuery('input[name=aiowps_enable_brute_force_attack_prevention]').on('click', function() {
|
| 60 |
jQuery('input[name=aiowps_brute_force_secret_word]').prop('disabled', !jQuery(this).prop('checked'));
|
| 61 |
jQuery('input[name=aiowps_cookie_based_brute_force_redirect_url]').prop('disabled', !jQuery(this).prop('checked'));
|
| 62 |
+
jQuery('input[name=aiowps_brute_force_attack_prevention_pw_protected_exception]').prop('disabled', !jQuery(this).prop('checked'));
|
| 63 |
+
jQuery('input[name=aiowps_brute_force_attack_prevention_ajax_exception]').prop('disabled', !jQuery(this).prop('checked'));
|
| 64 |
});
|
| 65 |
// End of brute force attack prevention toggle handling
|
| 66 |
+
|
| 67 |
+
/**
|
| 68 |
+
* Take a backup with UpdraftPlus if possible.
|
| 69 |
+
*
|
| 70 |
+
* @param {String} file_entities
|
| 71 |
+
*
|
| 72 |
+
* @return void
|
| 73 |
+
*/
|
| 74 |
+
function take_a_backup_with_updraftplus(file_entities) {
|
| 75 |
+
// Set default for file_entities to empty string
|
| 76 |
+
if ('undefined' == typeof file_entities) file_entities = '';
|
| 77 |
+
var exclude_files = file_entities ? 0 : 1;
|
| 78 |
+
|
| 79 |
+
if (typeof updraft_backupnow_inpage_go === 'function') {
|
| 80 |
+
updraft_backupnow_inpage_go(function () {
|
| 81 |
+
// Close the backup dialogue.
|
| 82 |
+
$('#updraft-backupnow-inpage-modal').dialog('close');
|
| 83 |
+
}, file_entities, 'autobackup', 0, exclude_files, 0);
|
| 84 |
+
}
|
| 85 |
+
}
|
| 86 |
+
if (jQuery('#aios-manual-db-backup-now').length) {
|
| 87 |
+
jQuery('#aios-manual-db-backup-now').on('click', function (e) {
|
| 88 |
+
e.preventDefault();
|
| 89 |
+
take_a_backup_with_updraftplus();
|
| 90 |
+
});
|
| 91 |
+
}
|
| 92 |
+
|
| 93 |
+
// Hide 2FA premium advertisement
|
| 94 |
+
if (jQuery('.tfa-premium').length) {
|
| 95 |
+
jQuery('.tfa-premium').hide();
|
| 96 |
+
}
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
// Start of trash spam comments toggle handling
|
| 100 |
+
jQuery('input[name=aiowps_enable_trash_spam_comments]').on('click', function() {
|
| 101 |
+
jQuery('input[name=aiowps_trash_spam_comments_after_days]').prop('disabled', !jQuery(this).prop('checked'));
|
| 102 |
+
});
|
| 103 |
+
// End of trash spam comments toggle handling
|
| 104 |
+
});
|
|
@@ -1225,13 +1225,13 @@ msgstr ""
|
|
| 1225 |
#: admin/wp-security-database-menu.php:31
|
| 1226 |
#: classes/grade-system/wp-security-feature-item-manager.php:62
|
| 1227 |
#@ all-in-one-wp-security-and-firewall
|
| 1228 |
-
msgid "
|
| 1229 |
msgstr ""
|
| 1230 |
|
| 1231 |
#: admin/wp-security-database-menu.php:30
|
| 1232 |
#: classes/grade-system/wp-security-feature-item-manager.php:60
|
| 1233 |
#@ all-in-one-wp-security-and-firewall
|
| 1234 |
-
msgid "
|
| 1235 |
msgstr ""
|
| 1236 |
|
| 1237 |
#: admin/wp-security-database-menu.php:93
|
|
@@ -1256,17 +1256,17 @@ msgstr ""
|
|
| 1256 |
|
| 1257 |
#: admin/wp-security-database-menu.php:131
|
| 1258 |
#@ all-in-one-wp-security-and-firewall
|
| 1259 |
-
msgid "Change
|
| 1260 |
msgstr ""
|
| 1261 |
|
| 1262 |
#: admin/wp-security-database-menu.php:134
|
| 1263 |
#@ all-in-one-wp-security-and-firewall
|
| 1264 |
-
msgid "Your WordPress
|
| 1265 |
msgstr ""
|
| 1266 |
|
| 1267 |
#: admin/wp-security-database-menu.php:135
|
| 1268 |
#@ all-in-one-wp-security-and-firewall
|
| 1269 |
-
msgid "The
|
| 1270 |
msgstr ""
|
| 1271 |
|
| 1272 |
#: admin/wp-security-database-menu.php:136
|
|
@@ -1281,7 +1281,7 @@ msgstr ""
|
|
| 1281 |
|
| 1282 |
#: admin/wp-security-database-menu.php:143
|
| 1283 |
#@ all-in-one-wp-security-and-firewall
|
| 1284 |
-
msgid "
|
| 1285 |
msgstr ""
|
| 1286 |
|
| 1287 |
#: admin/wp-security-database-menu.php:154
|
|
@@ -1292,7 +1292,7 @@ msgstr ""
|
|
| 1292 |
|
| 1293 |
#: admin/wp-security-database-menu.php:163
|
| 1294 |
#@ all-in-one-wp-security-and-firewall
|
| 1295 |
-
msgid "Current
|
| 1296 |
msgstr ""
|
| 1297 |
|
| 1298 |
#: admin/wp-security-database-menu.php:169
|
|
@@ -1304,7 +1304,7 @@ msgstr ""
|
|
| 1304 |
|
| 1305 |
#: admin/wp-security-database-menu.php:176
|
| 1306 |
#@ all-in-one-wp-security-and-firewall
|
| 1307 |
-
msgid "Generate
|
| 1308 |
msgstr ""
|
| 1309 |
|
| 1310 |
#: admin/wp-security-database-menu.php:179
|
|
@@ -1324,7 +1324,7 @@ msgstr ""
|
|
| 1324 |
|
| 1325 |
#: admin/wp-security-database-menu.php:186
|
| 1326 |
#@ all-in-one-wp-security-and-firewall
|
| 1327 |
-
msgid "Change
|
| 1328 |
msgstr ""
|
| 1329 |
|
| 1330 |
#: admin/wp-security-database-menu.php:207
|
|
@@ -1532,7 +1532,7 @@ msgstr ""
|
|
| 1532 |
|
| 1533 |
#: admin/wp-security-database-menu.php:513
|
| 1534 |
#@ all-in-one-wp-security-and-firewall
|
| 1535 |
-
msgid "
|
| 1536 |
msgstr ""
|
| 1537 |
|
| 1538 |
#: admin/wp-security-filescan-menu.php:23
|
|
@@ -3845,7 +3845,7 @@ msgstr ""
|
|
| 3845 |
|
| 3846 |
#: admin/wp-security-user-login-menu.php:277
|
| 3847 |
#@ all-in-one-wp-security-and-firewall
|
| 3848 |
-
msgid "All records from the Failed Logins table were deleted successfully
|
| 3849 |
msgstr ""
|
| 3850 |
|
| 3851 |
#: admin/wp-security-user-login-menu.php:292
|
| 1225 |
#: admin/wp-security-database-menu.php:31
|
| 1226 |
#: classes/grade-system/wp-security-feature-item-manager.php:62
|
| 1227 |
#@ all-in-one-wp-security-and-firewall
|
| 1228 |
+
msgid "Database backup"
|
| 1229 |
msgstr ""
|
| 1230 |
|
| 1231 |
#: admin/wp-security-database-menu.php:30
|
| 1232 |
#: classes/grade-system/wp-security-feature-item-manager.php:60
|
| 1233 |
#@ all-in-one-wp-security-and-firewall
|
| 1234 |
+
msgid "Database prefix"
|
| 1235 |
msgstr ""
|
| 1236 |
|
| 1237 |
#: admin/wp-security-database-menu.php:93
|
| 1256 |
|
| 1257 |
#: admin/wp-security-database-menu.php:131
|
| 1258 |
#@ all-in-one-wp-security-and-firewall
|
| 1259 |
+
msgid "Change database prefix"
|
| 1260 |
msgstr ""
|
| 1261 |
|
| 1262 |
#: admin/wp-security-database-menu.php:134
|
| 1263 |
#@ all-in-one-wp-security-and-firewall
|
| 1264 |
+
msgid "Your WordPress database is the most important asset of your website because it contains a lot of your site's precious information."
|
| 1265 |
msgstr ""
|
| 1266 |
|
| 1267 |
#: admin/wp-security-database-menu.php:135
|
| 1268 |
#@ all-in-one-wp-security-and-firewall
|
| 1269 |
+
msgid "The database is also a target for hackers via methods such as SQL injections and malicious and automated code which targets certain tables."
|
| 1270 |
msgstr ""
|
| 1271 |
|
| 1272 |
#: admin/wp-security-database-menu.php:136
|
| 1281 |
|
| 1282 |
#: admin/wp-security-database-menu.php:143
|
| 1283 |
#@ all-in-one-wp-security-and-firewall
|
| 1284 |
+
msgid "Database prefix options"
|
| 1285 |
msgstr ""
|
| 1286 |
|
| 1287 |
#: admin/wp-security-database-menu.php:154
|
| 1292 |
|
| 1293 |
#: admin/wp-security-database-menu.php:163
|
| 1294 |
#@ all-in-one-wp-security-and-firewall
|
| 1295 |
+
msgid "Current database table prefix"
|
| 1296 |
msgstr ""
|
| 1297 |
|
| 1298 |
#: admin/wp-security-database-menu.php:169
|
| 1304 |
|
| 1305 |
#: admin/wp-security-database-menu.php:176
|
| 1306 |
#@ all-in-one-wp-security-and-firewall
|
| 1307 |
+
msgid "Generate new database table prefix"
|
| 1308 |
msgstr ""
|
| 1309 |
|
| 1310 |
#: admin/wp-security-database-menu.php:179
|
| 1324 |
|
| 1325 |
#: admin/wp-security-database-menu.php:186
|
| 1326 |
#@ all-in-one-wp-security-and-firewall
|
| 1327 |
+
msgid "Change database prefix"
|
| 1328 |
msgstr ""
|
| 1329 |
|
| 1330 |
#: admin/wp-security-database-menu.php:207
|
| 1532 |
|
| 1533 |
#: admin/wp-security-database-menu.php:513
|
| 1534 |
#@ all-in-one-wp-security-and-firewall
|
| 1535 |
+
msgid "The database prefix change tasks have been completed."
|
| 1536 |
msgstr ""
|
| 1537 |
|
| 1538 |
#: admin/wp-security-filescan-menu.php:23
|
| 3845 |
|
| 3846 |
#: admin/wp-security-user-login-menu.php:277
|
| 3847 |
#@ all-in-one-wp-security-and-firewall
|
| 3848 |
+
msgid "All records from the Failed Logins table were deleted successfully."
|
| 3849 |
msgstr ""
|
| 3850 |
|
| 3851 |
#: admin/wp-security-user-login-menu.php:292
|
|
@@ -1351,12 +1351,12 @@ msgstr "Plages et adresses IP actuellement en lock-out"
|
|
| 1351 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:26
|
| 1352 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:31
|
| 1353 |
#: all-in-one-wp-security/classes/grade-system/wp-security-feature-item-manager.php:63
|
| 1354 |
-
msgid "
|
| 1355 |
msgstr "sauvegarde de BdD"
|
| 1356 |
|
| 1357 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:30
|
| 1358 |
#: all-in-one-wp-security/classes/grade-system/wp-security-feature-item-manager.php:61
|
| 1359 |
-
msgid "
|
| 1360 |
msgstr "Préfixe de BdD"
|
| 1361 |
|
| 1362 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:93
|
|
@@ -1385,7 +1385,7 @@ msgstr ""
|
|
| 1385 |
"chiffres, des lettres et « _ »."
|
| 1386 |
|
| 1387 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:131
|
| 1388 |
-
msgid "Change
|
| 1389 |
msgstr "Changer le préfixe de BdD"
|
| 1390 |
|
| 1391 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:134
|
|
@@ -1425,7 +1425,7 @@ msgstr ""
|
|
| 1425 |
"l’extension."
|
| 1426 |
|
| 1427 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:143
|
| 1428 |
-
msgid "
|
| 1429 |
msgstr "Options de préfixe BdD"
|
| 1430 |
|
| 1431 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:154
|
|
@@ -1435,7 +1435,7 @@ msgstr ""
|
|
| 1435 |
"Il est recommandé d’effectuer une %s avant d’utiliser cette fonctionnalité"
|
| 1436 |
|
| 1437 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:163
|
| 1438 |
-
msgid "Current
|
| 1439 |
msgstr "Préfixe actuel des tables de BdD"
|
| 1440 |
|
| 1441 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:169
|
|
@@ -1451,7 +1451,7 @@ msgstr ""
|
|
| 1451 |
"valeur par une autre."
|
| 1452 |
|
| 1453 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:176
|
| 1454 |
-
msgid "Generate
|
| 1455 |
msgstr "Générer un nouveau préfixe de tables BdD"
|
| 1456 |
|
| 1457 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:179
|
|
@@ -1475,7 +1475,7 @@ msgstr ""
|
|
| 1475 |
"des lettres et / ou des chiffres et / ou « _ ». Exemple : XyZ_3_"
|
| 1476 |
|
| 1477 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:186
|
| 1478 |
-
msgid "Change
|
| 1479 |
msgstr "Changer le préfixe des tables de la BdD"
|
| 1480 |
|
| 1481 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:207
|
|
@@ -1719,7 +1719,7 @@ msgstr ""
|
|
| 1719 |
"l’ancien préfixe BdD ont été actualisés avec succès !"
|
| 1720 |
|
| 1721 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:530
|
| 1722 |
-
msgid "
|
| 1723 |
msgstr "Les tâches de modification du préfixe BdD sont terminées."
|
| 1724 |
|
| 1725 |
#: all-in-one-wp-security/admin/wp-security-filescan-menu.php:24
|
|
@@ -4893,7 +4893,7 @@ msgstr ""
|
|
| 4893 |
"de tentatives de connexions infructueuses a échoué !"
|
| 4894 |
|
| 4895 |
#: all-in-one-wp-security/admin/wp-security-user-login-menu.php:277
|
| 4896 |
-
msgid "All records from the Failed Logins table were deleted successfully
|
| 4897 |
msgstr ""
|
| 4898 |
"Tous les enregistrements de la table des tentatives de connexions "
|
| 4899 |
"infructueuses ont été supprimés avec succès !"
|
| 1351 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:26
|
| 1352 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:31
|
| 1353 |
#: all-in-one-wp-security/classes/grade-system/wp-security-feature-item-manager.php:63
|
| 1354 |
+
msgid "Database backup"
|
| 1355 |
msgstr "sauvegarde de BdD"
|
| 1356 |
|
| 1357 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:30
|
| 1358 |
#: all-in-one-wp-security/classes/grade-system/wp-security-feature-item-manager.php:61
|
| 1359 |
+
msgid "Database prefix"
|
| 1360 |
msgstr "Préfixe de BdD"
|
| 1361 |
|
| 1362 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:93
|
| 1385 |
"chiffres, des lettres et « _ »."
|
| 1386 |
|
| 1387 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:131
|
| 1388 |
+
msgid "Change database prefix"
|
| 1389 |
msgstr "Changer le préfixe de BdD"
|
| 1390 |
|
| 1391 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:134
|
| 1425 |
"l’extension."
|
| 1426 |
|
| 1427 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:143
|
| 1428 |
+
msgid "Database prefix options"
|
| 1429 |
msgstr "Options de préfixe BdD"
|
| 1430 |
|
| 1431 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:154
|
| 1435 |
"Il est recommandé d’effectuer une %s avant d’utiliser cette fonctionnalité"
|
| 1436 |
|
| 1437 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:163
|
| 1438 |
+
msgid "Current database table prefix"
|
| 1439 |
msgstr "Préfixe actuel des tables de BdD"
|
| 1440 |
|
| 1441 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:169
|
| 1451 |
"valeur par une autre."
|
| 1452 |
|
| 1453 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:176
|
| 1454 |
+
msgid "Generate new database table prefix"
|
| 1455 |
msgstr "Générer un nouveau préfixe de tables BdD"
|
| 1456 |
|
| 1457 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:179
|
| 1475 |
"des lettres et / ou des chiffres et / ou « _ ». Exemple : XyZ_3_"
|
| 1476 |
|
| 1477 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:186
|
| 1478 |
+
msgid "Change database prefix"
|
| 1479 |
msgstr "Changer le préfixe des tables de la BdD"
|
| 1480 |
|
| 1481 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:207
|
| 1719 |
"l’ancien préfixe BdD ont été actualisés avec succès !"
|
| 1720 |
|
| 1721 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:530
|
| 1722 |
+
msgid "The database prefix change tasks have been completed."
|
| 1723 |
msgstr "Les tâches de modification du préfixe BdD sont terminées."
|
| 1724 |
|
| 1725 |
#: all-in-one-wp-security/admin/wp-security-filescan-menu.php:24
|
| 4893 |
"de tentatives de connexions infructueuses a échoué !"
|
| 4894 |
|
| 4895 |
#: all-in-one-wp-security/admin/wp-security-user-login-menu.php:277
|
| 4896 |
+
msgid "All records from the Failed Logins table were deleted successfully."
|
| 4897 |
msgstr ""
|
| 4898 |
"Tous les enregistrements de la table des tentatives de connexions "
|
| 4899 |
"infructueuses ont été supprimés avec succès !"
|
|
@@ -415,12 +415,12 @@ msgstr ""
|
|
| 415 |
|
| 416 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:23
|
| 417 |
#: all-in-one-wp-security/classes/grade-system/wp-security-feature-item-manager.php:57
|
| 418 |
-
msgid "
|
| 419 |
msgstr ""
|
| 420 |
|
| 421 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:24
|
| 422 |
#: all-in-one-wp-security/classes/grade-system/wp-security-feature-item-manager.php:59
|
| 423 |
-
msgid "
|
| 424 |
msgstr ""
|
| 425 |
|
| 426 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:84
|
|
@@ -445,7 +445,7 @@ msgid ""
|
|
| 445 |
msgstr ""
|
| 446 |
|
| 447 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:122
|
| 448 |
-
msgid "Change
|
| 449 |
msgstr ""
|
| 450 |
|
| 451 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:125
|
|
@@ -474,7 +474,7 @@ msgid ""
|
|
| 474 |
msgstr ""
|
| 475 |
|
| 476 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:134
|
| 477 |
-
msgid "
|
| 478 |
msgstr ""
|
| 479 |
|
| 480 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:145
|
|
@@ -483,7 +483,7 @@ msgid "It is recommended that you perform a %s before using this feature"
|
|
| 483 |
msgstr ""
|
| 484 |
|
| 485 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:154
|
| 486 |
-
msgid "Current
|
| 487 |
msgstr ""
|
| 488 |
|
| 489 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:160
|
|
@@ -495,7 +495,7 @@ msgid ""
|
|
| 495 |
msgstr ""
|
| 496 |
|
| 497 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:167
|
| 498 |
-
msgid "Generate
|
| 499 |
msgstr ""
|
| 500 |
|
| 501 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:170
|
|
@@ -515,7 +515,7 @@ msgid ""
|
|
| 515 |
msgstr ""
|
| 516 |
|
| 517 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:177
|
| 518 |
-
msgid "Change
|
| 519 |
msgstr ""
|
| 520 |
|
| 521 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:198
|
|
@@ -716,7 +716,7 @@ msgid ""
|
|
| 716 |
msgstr ""
|
| 717 |
|
| 718 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:499
|
| 719 |
-
msgid "
|
| 720 |
msgstr ""
|
| 721 |
|
| 722 |
#: all-in-one-wp-security/admin/wp-security-filescan-menu.php:22
|
|
@@ -2960,7 +2960,7 @@ msgid "User Login Feature - Delete all failed login records operation failed!"
|
|
| 2960 |
msgstr ""
|
| 2961 |
|
| 2962 |
#: all-in-one-wp-security/admin/wp-security-user-login-menu.php:498
|
| 2963 |
-
msgid "All records from the Failed Logins table were deleted successfully
|
| 2964 |
msgstr ""
|
| 2965 |
|
| 2966 |
#: all-in-one-wp-security/admin/wp-security-user-login-menu.php:513
|
| 415 |
|
| 416 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:23
|
| 417 |
#: all-in-one-wp-security/classes/grade-system/wp-security-feature-item-manager.php:57
|
| 418 |
+
msgid "Database prefix"
|
| 419 |
msgstr ""
|
| 420 |
|
| 421 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:24
|
| 422 |
#: all-in-one-wp-security/classes/grade-system/wp-security-feature-item-manager.php:59
|
| 423 |
+
msgid "Database backup"
|
| 424 |
msgstr ""
|
| 425 |
|
| 426 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:84
|
| 445 |
msgstr ""
|
| 446 |
|
| 447 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:122
|
| 448 |
+
msgid "Change database prefix"
|
| 449 |
msgstr ""
|
| 450 |
|
| 451 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:125
|
| 474 |
msgstr ""
|
| 475 |
|
| 476 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:134
|
| 477 |
+
msgid "Database prefix options"
|
| 478 |
msgstr ""
|
| 479 |
|
| 480 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:145
|
| 483 |
msgstr ""
|
| 484 |
|
| 485 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:154
|
| 486 |
+
msgid "Current database table prefix"
|
| 487 |
msgstr ""
|
| 488 |
|
| 489 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:160
|
| 495 |
msgstr ""
|
| 496 |
|
| 497 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:167
|
| 498 |
+
msgid "Generate new database table prefix"
|
| 499 |
msgstr ""
|
| 500 |
|
| 501 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:170
|
| 515 |
msgstr ""
|
| 516 |
|
| 517 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:177
|
| 518 |
+
msgid "Change database prefix"
|
| 519 |
msgstr ""
|
| 520 |
|
| 521 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:198
|
| 716 |
msgstr ""
|
| 717 |
|
| 718 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:499
|
| 719 |
+
msgid "The database prefix change tasks have been completed."
|
| 720 |
msgstr ""
|
| 721 |
|
| 722 |
#: all-in-one-wp-security/admin/wp-security-filescan-menu.php:22
|
| 2960 |
msgstr ""
|
| 2961 |
|
| 2962 |
#: all-in-one-wp-security/admin/wp-security-user-login-menu.php:498
|
| 2963 |
+
msgid "All records from the Failed Logins table were deleted successfully."
|
| 2964 |
msgstr ""
|
| 2965 |
|
| 2966 |
#: all-in-one-wp-security/admin/wp-security-user-login-menu.php:513
|
|
@@ -1511,12 +1511,12 @@ msgstr "기록 파일이 비었어요."
|
|
| 1511 |
#: admin/wp-security-database-menu.php:26
|
| 1512 |
#: admin/wp-security-database-menu.php:31
|
| 1513 |
#: classes/grade-system/wp-security-feature-item-manager.php:63
|
| 1514 |
-
msgid "
|
| 1515 |
msgstr "DB 백업"
|
| 1516 |
|
| 1517 |
#: admin/wp-security-database-menu.php:30
|
| 1518 |
#: classes/grade-system/wp-security-feature-item-manager.php:61
|
| 1519 |
-
msgid "
|
| 1520 |
msgstr "DB 접두사"
|
| 1521 |
|
| 1522 |
# @ all-in-one-wp-security-and-firewall
|
|
@@ -1551,7 +1551,7 @@ msgstr ""
|
|
| 1551 |
|
| 1552 |
# @ all-in-one-wp-security-and-firewall
|
| 1553 |
#: admin/wp-security-database-menu.php:133
|
| 1554 |
-
msgid "Change
|
| 1555 |
msgstr "데이터베이스 접두사 변경"
|
| 1556 |
|
| 1557 |
# @ all-in-one-wp-security-and-firewall
|
|
@@ -1593,7 +1593,7 @@ msgstr ""
|
|
| 1593 |
|
| 1594 |
# @ all-in-one-wp-security-and-firewall
|
| 1595 |
#: admin/wp-security-database-menu.php:145
|
| 1596 |
-
msgid "
|
| 1597 |
msgstr "DB 접두사 옵션"
|
| 1598 |
|
| 1599 |
# @ all-in-one-wp-security-and-firewall
|
|
@@ -1604,7 +1604,7 @@ msgstr "이 기능을 사용하기 전에 %s를 수행하는 것이 좋습니다
|
|
| 1604 |
|
| 1605 |
# @ all-in-one-wp-security-and-firewall
|
| 1606 |
#: admin/wp-security-database-menu.php:165
|
| 1607 |
-
msgid "Current
|
| 1608 |
msgstr "현재 DB 테이블 접두사"
|
| 1609 |
|
| 1610 |
# @ all-in-one-wp-security-and-firewall
|
|
@@ -1622,7 +1622,7 @@ msgstr ""
|
|
| 1622 |
|
| 1623 |
# @ all-in-one-wp-security-and-firewall
|
| 1624 |
#: admin/wp-security-database-menu.php:178
|
| 1625 |
-
msgid "Generate
|
| 1626 |
msgstr "새 DB 테이블 접두사 생성"
|
| 1627 |
|
| 1628 |
# @ all-in-one-wp-security-and-firewall
|
|
@@ -1649,7 +1649,7 @@ msgstr ""
|
|
| 1649 |
|
| 1650 |
# @ all-in-one-wp-security-and-firewall
|
| 1651 |
#: admin/wp-security-database-menu.php:188
|
| 1652 |
-
msgid "Change
|
| 1653 |
msgstr "DB 접두사 변경"
|
| 1654 |
|
| 1655 |
# @ all-in-one-wp-security-and-firewall
|
|
@@ -1914,7 +1914,7 @@ msgstr ""
|
|
| 1914 |
|
| 1915 |
# @ all-in-one-wp-security-and-firewall
|
| 1916 |
#: admin/wp-security-database-menu.php:532
|
| 1917 |
-
msgid "
|
| 1918 |
msgstr "DB 접두사 변경 작업이 완료되었습니다."
|
| 1919 |
|
| 1920 |
#: admin/wp-security-database-menu.php:575
|
|
@@ -5334,7 +5334,7 @@ msgstr ""
|
|
| 5334 |
|
| 5335 |
# @ all-in-one-wp-security-and-firewall
|
| 5336 |
#: admin/wp-security-user-login-menu.php:304
|
| 5337 |
-
msgid "All records from the Failed Logins table were deleted successfully
|
| 5338 |
msgstr ""
|
| 5339 |
|
| 5340 |
# @ all-in-one-wp-security-and-firewall
|
| 1511 |
#: admin/wp-security-database-menu.php:26
|
| 1512 |
#: admin/wp-security-database-menu.php:31
|
| 1513 |
#: classes/grade-system/wp-security-feature-item-manager.php:63
|
| 1514 |
+
msgid "Database backup"
|
| 1515 |
msgstr "DB 백업"
|
| 1516 |
|
| 1517 |
#: admin/wp-security-database-menu.php:30
|
| 1518 |
#: classes/grade-system/wp-security-feature-item-manager.php:61
|
| 1519 |
+
msgid "Database prefix"
|
| 1520 |
msgstr "DB 접두사"
|
| 1521 |
|
| 1522 |
# @ all-in-one-wp-security-and-firewall
|
| 1551 |
|
| 1552 |
# @ all-in-one-wp-security-and-firewall
|
| 1553 |
#: admin/wp-security-database-menu.php:133
|
| 1554 |
+
msgid "Change database prefix"
|
| 1555 |
msgstr "데이터베이스 접두사 변경"
|
| 1556 |
|
| 1557 |
# @ all-in-one-wp-security-and-firewall
|
| 1593 |
|
| 1594 |
# @ all-in-one-wp-security-and-firewall
|
| 1595 |
#: admin/wp-security-database-menu.php:145
|
| 1596 |
+
msgid "Database prefix options"
|
| 1597 |
msgstr "DB 접두사 옵션"
|
| 1598 |
|
| 1599 |
# @ all-in-one-wp-security-and-firewall
|
| 1604 |
|
| 1605 |
# @ all-in-one-wp-security-and-firewall
|
| 1606 |
#: admin/wp-security-database-menu.php:165
|
| 1607 |
+
msgid "Current database table prefix"
|
| 1608 |
msgstr "현재 DB 테이블 접두사"
|
| 1609 |
|
| 1610 |
# @ all-in-one-wp-security-and-firewall
|
| 1622 |
|
| 1623 |
# @ all-in-one-wp-security-and-firewall
|
| 1624 |
#: admin/wp-security-database-menu.php:178
|
| 1625 |
+
msgid "Generate new database table prefix"
|
| 1626 |
msgstr "새 DB 테이블 접두사 생성"
|
| 1627 |
|
| 1628 |
# @ all-in-one-wp-security-and-firewall
|
| 1649 |
|
| 1650 |
# @ all-in-one-wp-security-and-firewall
|
| 1651 |
#: admin/wp-security-database-menu.php:188
|
| 1652 |
+
msgid "Change database prefix"
|
| 1653 |
msgstr "DB 접두사 변경"
|
| 1654 |
|
| 1655 |
# @ all-in-one-wp-security-and-firewall
|
| 1914 |
|
| 1915 |
# @ all-in-one-wp-security-and-firewall
|
| 1916 |
#: admin/wp-security-database-menu.php:532
|
| 1917 |
+
msgid "The database prefix change tasks have been completed."
|
| 1918 |
msgstr "DB 접두사 변경 작업이 완료되었습니다."
|
| 1919 |
|
| 1920 |
#: admin/wp-security-database-menu.php:575
|
| 5334 |
|
| 5335 |
# @ all-in-one-wp-security-and-firewall
|
| 5336 |
#: admin/wp-security-user-login-menu.php:304
|
| 5337 |
+
msgid "All records from the Failed Logins table were deleted successfully."
|
| 5338 |
msgstr ""
|
| 5339 |
|
| 5340 |
# @ all-in-one-wp-security-and-firewall
|
|
@@ -2130,7 +2130,7 @@ msgid "The deletion of the import file failed. Please delete this file manually
|
|
| 2130 |
msgstr "Het verwijderen van het importbestand is mislukt. Gelieve dit bestand handmatig te verwijderen via het media-menu voor beveiligingsdoeleinden."
|
| 2131 |
|
| 2132 |
#: admin/wp-security-user-login-menu.php:395
|
| 2133 |
-
msgid "All records from the Failed Logins table were deleted successfully
|
| 2134 |
msgstr "Alle records uit de lijst mislukte inlogpogingen zijn met succes verwijderd!"
|
| 2135 |
|
| 2136 |
#: admin/wp-security-user-login-menu.php:391
|
|
@@ -2821,7 +2821,7 @@ msgstr "Naam"
|
|
| 2821 |
#: classes/grade-system/wp-security-feature-item-manager.php:67
|
| 2822 |
#: admin/wp-security-database-menu.php:29
|
| 2823 |
#: admin/wp-security-database-menu.php:34
|
| 2824 |
-
msgid "
|
| 2825 |
msgstr "DB back-up"
|
| 2826 |
|
| 2827 |
#: admin/wp-security-database-menu.php:185
|
|
@@ -3119,15 +3119,15 @@ msgstr "Logs bekijken"
|
|
| 3119 |
|
| 3120 |
#: classes/grade-system/wp-security-feature-item-manager.php:65
|
| 3121 |
#: admin/wp-security-database-menu.php:33
|
| 3122 |
-
msgid "
|
| 3123 |
msgstr "DB-prefix"
|
| 3124 |
|
| 3125 |
#: admin/wp-security-database-menu.php:136
|
| 3126 |
-
msgid "Change
|
| 3127 |
msgstr "Verander database-prefix"
|
| 3128 |
|
| 3129 |
#: admin/wp-security-database-menu.php:148
|
| 3130 |
-
msgid "
|
| 3131 |
msgstr "Opties database-prefix"
|
| 3132 |
|
| 3133 |
#: admin/wp-security-dashboard-menu.php:204
|
|
@@ -3171,7 +3171,7 @@ msgid "wp-config.php file was updated successfully!"
|
|
| 3171 |
msgstr "wp-config.php is succesvol bijgewerkt!"
|
| 3172 |
|
| 3173 |
#: admin/wp-security-database-menu.php:535
|
| 3174 |
-
msgid "
|
| 3175 |
msgstr "Het veranderen van de DB-prefix is afgerond."
|
| 3176 |
|
| 3177 |
#: admin/wp-security-dashboard-menu.php:423
|
|
@@ -3266,11 +3266,11 @@ msgid "<strong>ERROR</strong>: The table prefix can only contain numbers, letter
|
|
| 3266 |
msgstr "<strong>FOUT</strong>: het tabel voorvoegsel kan alleen cijfers, letters en underscores bevatten."
|
| 3267 |
|
| 3268 |
#: admin/wp-security-database-menu.php:139
|
| 3269 |
-
msgid "Your WordPress
|
| 3270 |
msgstr "Uw WordPress-DB is de belangrijkste troef van uw website, omdat deze heel veel kostbare informatie van uw site bevat."
|
| 3271 |
|
| 3272 |
#: admin/wp-security-database-menu.php:140
|
| 3273 |
-
msgid "The
|
| 3274 |
msgstr "De DB is ook een doelwit voor hackers via methoden zoals SQL-injecties en kwaadaardige en geautomatiseerde code die zich op bepaalde tabellen richt."
|
| 3275 |
|
| 3276 |
#: admin/wp-security-database-menu.php:141
|
|
@@ -3895,11 +3895,11 @@ msgid "Backup Time Interval"
|
|
| 3895 |
msgstr "Backup-intervaltijd"
|
| 3896 |
|
| 3897 |
#: admin/wp-security-database-menu.php:168
|
| 3898 |
-
msgid "Current
|
| 3899 |
msgstr "Huidige database-prefix"
|
| 3900 |
|
| 3901 |
#: admin/wp-security-database-menu.php:181
|
| 3902 |
-
msgid "Generate
|
| 3903 |
msgstr "Nieuwe database-prefix aanmaken"
|
| 3904 |
|
| 3905 |
#: admin/wp-security-database-menu.php:184
|
|
@@ -3907,7 +3907,7 @@ msgid "Check this if you want the plugin to generate a random 6 character string
|
|
| 3907 |
msgstr "Activeer dit als u wilt dat de plug-in een willekeurige tekenreeks van 6 karakters voor de tabel-prefix genereert"
|
| 3908 |
|
| 3909 |
#: admin/wp-security-database-menu.php:191
|
| 3910 |
-
msgid "Change
|
| 3911 |
msgstr "Verander database-prefix"
|
| 3912 |
|
| 3913 |
#: admin/wp-security-database-menu.php:229
|
| 2130 |
msgstr "Het verwijderen van het importbestand is mislukt. Gelieve dit bestand handmatig te verwijderen via het media-menu voor beveiligingsdoeleinden."
|
| 2131 |
|
| 2132 |
#: admin/wp-security-user-login-menu.php:395
|
| 2133 |
+
msgid "All records from the Failed Logins table were deleted successfully."
|
| 2134 |
msgstr "Alle records uit de lijst mislukte inlogpogingen zijn met succes verwijderd!"
|
| 2135 |
|
| 2136 |
#: admin/wp-security-user-login-menu.php:391
|
| 2821 |
#: classes/grade-system/wp-security-feature-item-manager.php:67
|
| 2822 |
#: admin/wp-security-database-menu.php:29
|
| 2823 |
#: admin/wp-security-database-menu.php:34
|
| 2824 |
+
msgid "Database backup"
|
| 2825 |
msgstr "DB back-up"
|
| 2826 |
|
| 2827 |
#: admin/wp-security-database-menu.php:185
|
| 3119 |
|
| 3120 |
#: classes/grade-system/wp-security-feature-item-manager.php:65
|
| 3121 |
#: admin/wp-security-database-menu.php:33
|
| 3122 |
+
msgid "Database prefix"
|
| 3123 |
msgstr "DB-prefix"
|
| 3124 |
|
| 3125 |
#: admin/wp-security-database-menu.php:136
|
| 3126 |
+
msgid "Change database prefix"
|
| 3127 |
msgstr "Verander database-prefix"
|
| 3128 |
|
| 3129 |
#: admin/wp-security-database-menu.php:148
|
| 3130 |
+
msgid "Database prefix options"
|
| 3131 |
msgstr "Opties database-prefix"
|
| 3132 |
|
| 3133 |
#: admin/wp-security-dashboard-menu.php:204
|
| 3171 |
msgstr "wp-config.php is succesvol bijgewerkt!"
|
| 3172 |
|
| 3173 |
#: admin/wp-security-database-menu.php:535
|
| 3174 |
+
msgid "The database prefix change tasks have been completed."
|
| 3175 |
msgstr "Het veranderen van de DB-prefix is afgerond."
|
| 3176 |
|
| 3177 |
#: admin/wp-security-dashboard-menu.php:423
|
| 3266 |
msgstr "<strong>FOUT</strong>: het tabel voorvoegsel kan alleen cijfers, letters en underscores bevatten."
|
| 3267 |
|
| 3268 |
#: admin/wp-security-database-menu.php:139
|
| 3269 |
+
msgid "Your WordPress database is the most important asset of your website because it contains a lot of your site's precious information."
|
| 3270 |
msgstr "Uw WordPress-DB is de belangrijkste troef van uw website, omdat deze heel veel kostbare informatie van uw site bevat."
|
| 3271 |
|
| 3272 |
#: admin/wp-security-database-menu.php:140
|
| 3273 |
+
msgid "The database is also a target for hackers via methods such as SQL injections and malicious and automated code which targets certain tables."
|
| 3274 |
msgstr "De DB is ook een doelwit voor hackers via methoden zoals SQL-injecties en kwaadaardige en geautomatiseerde code die zich op bepaalde tabellen richt."
|
| 3275 |
|
| 3276 |
#: admin/wp-security-database-menu.php:141
|
| 3895 |
msgstr "Backup-intervaltijd"
|
| 3896 |
|
| 3897 |
#: admin/wp-security-database-menu.php:168
|
| 3898 |
+
msgid "Current database table prefix"
|
| 3899 |
msgstr "Huidige database-prefix"
|
| 3900 |
|
| 3901 |
#: admin/wp-security-database-menu.php:181
|
| 3902 |
+
msgid "Generate new database table prefix"
|
| 3903 |
msgstr "Nieuwe database-prefix aanmaken"
|
| 3904 |
|
| 3905 |
#: admin/wp-security-database-menu.php:184
|
| 3907 |
msgstr "Activeer dit als u wilt dat de plug-in een willekeurige tekenreeks van 6 karakters voor de tabel-prefix genereert"
|
| 3908 |
|
| 3909 |
#: admin/wp-security-database-menu.php:191
|
| 3910 |
+
msgid "Change database prefix"
|
| 3911 |
msgstr "Verander database-prefix"
|
| 3912 |
|
| 3913 |
#: admin/wp-security-database-menu.php:229
|
|
@@ -1218,13 +1218,13 @@ msgstr ""
|
|
| 1218 |
#: admin/wp-security-database-menu.php:26
|
| 1219 |
#: admin/wp-security-database-menu.php:31
|
| 1220 |
#: classes/grade-system/wp-security-feature-item-manager.php:62
|
| 1221 |
-
msgid "
|
| 1222 |
msgstr ""
|
| 1223 |
|
| 1224 |
# @ all-in-one-wp-security-and-firewall
|
| 1225 |
#: admin/wp-security-database-menu.php:30
|
| 1226 |
#: classes/grade-system/wp-security-feature-item-manager.php:60
|
| 1227 |
-
msgid "
|
| 1228 |
msgstr ""
|
| 1229 |
|
| 1230 |
# @ all-in-one-wp-security-and-firewall
|
|
@@ -1249,17 +1249,17 @@ msgstr ""
|
|
| 1249 |
|
| 1250 |
# @ all-in-one-wp-security-and-firewall
|
| 1251 |
#: admin/wp-security-database-menu.php:131
|
| 1252 |
-
msgid "Change
|
| 1253 |
msgstr ""
|
| 1254 |
|
| 1255 |
# @ all-in-one-wp-security-and-firewall
|
| 1256 |
#: admin/wp-security-database-menu.php:134
|
| 1257 |
-
msgid "Your WordPress
|
| 1258 |
msgstr ""
|
| 1259 |
|
| 1260 |
# @ all-in-one-wp-security-and-firewall
|
| 1261 |
#: admin/wp-security-database-menu.php:135
|
| 1262 |
-
msgid "The
|
| 1263 |
msgstr ""
|
| 1264 |
|
| 1265 |
# @ all-in-one-wp-security-and-firewall
|
|
@@ -1274,7 +1274,7 @@ msgstr ""
|
|
| 1274 |
|
| 1275 |
# @ all-in-one-wp-security-and-firewall
|
| 1276 |
#: admin/wp-security-database-menu.php:143
|
| 1277 |
-
msgid "
|
| 1278 |
msgstr ""
|
| 1279 |
|
| 1280 |
# @ all-in-one-wp-security-and-firewall
|
|
@@ -1285,7 +1285,7 @@ msgstr ""
|
|
| 1285 |
|
| 1286 |
# @ all-in-one-wp-security-and-firewall
|
| 1287 |
#: admin/wp-security-database-menu.php:163
|
| 1288 |
-
msgid "Current
|
| 1289 |
msgstr ""
|
| 1290 |
|
| 1291 |
# @ all-in-one-wp-security-and-firewall
|
|
@@ -1297,7 +1297,7 @@ msgstr ""
|
|
| 1297 |
|
| 1298 |
# @ all-in-one-wp-security-and-firewall
|
| 1299 |
#: admin/wp-security-database-menu.php:176
|
| 1300 |
-
msgid "Generate
|
| 1301 |
msgstr ""
|
| 1302 |
|
| 1303 |
# @ all-in-one-wp-security-and-firewall
|
|
@@ -1317,7 +1317,7 @@ msgstr ""
|
|
| 1317 |
|
| 1318 |
# @ all-in-one-wp-security-and-firewall
|
| 1319 |
#: admin/wp-security-database-menu.php:186
|
| 1320 |
-
msgid "Change
|
| 1321 |
msgstr ""
|
| 1322 |
|
| 1323 |
# @ all-in-one-wp-security-and-firewall
|
|
@@ -1524,7 +1524,7 @@ msgstr ""
|
|
| 1524 |
|
| 1525 |
# @ all-in-one-wp-security-and-firewall
|
| 1526 |
#: admin/wp-security-database-menu.php:513
|
| 1527 |
-
msgid "
|
| 1528 |
msgstr ""
|
| 1529 |
|
| 1530 |
# @ all-in-one-wp-security-and-firewall
|
|
@@ -3835,7 +3835,7 @@ msgstr ""
|
|
| 3835 |
|
| 3836 |
# @ all-in-one-wp-security-and-firewall
|
| 3837 |
#: admin/wp-security-user-login-menu.php:277
|
| 3838 |
-
msgid "All records from the Failed Logins table were deleted successfully
|
| 3839 |
msgstr ""
|
| 3840 |
|
| 3841 |
# @ all-in-one-wp-security-and-firewall
|
| 1218 |
#: admin/wp-security-database-menu.php:26
|
| 1219 |
#: admin/wp-security-database-menu.php:31
|
| 1220 |
#: classes/grade-system/wp-security-feature-item-manager.php:62
|
| 1221 |
+
msgid "Database backup"
|
| 1222 |
msgstr ""
|
| 1223 |
|
| 1224 |
# @ all-in-one-wp-security-and-firewall
|
| 1225 |
#: admin/wp-security-database-menu.php:30
|
| 1226 |
#: classes/grade-system/wp-security-feature-item-manager.php:60
|
| 1227 |
+
msgid "Database prefix"
|
| 1228 |
msgstr ""
|
| 1229 |
|
| 1230 |
# @ all-in-one-wp-security-and-firewall
|
| 1249 |
|
| 1250 |
# @ all-in-one-wp-security-and-firewall
|
| 1251 |
#: admin/wp-security-database-menu.php:131
|
| 1252 |
+
msgid "Change database prefix"
|
| 1253 |
msgstr ""
|
| 1254 |
|
| 1255 |
# @ all-in-one-wp-security-and-firewall
|
| 1256 |
#: admin/wp-security-database-menu.php:134
|
| 1257 |
+
msgid "Your WordPress database is the most important asset of your website because it contains a lot of your site's precious information."
|
| 1258 |
msgstr ""
|
| 1259 |
|
| 1260 |
# @ all-in-one-wp-security-and-firewall
|
| 1261 |
#: admin/wp-security-database-menu.php:135
|
| 1262 |
+
msgid "The database is also a target for hackers via methods such as SQL injections and malicious and automated code which targets certain tables."
|
| 1263 |
msgstr ""
|
| 1264 |
|
| 1265 |
# @ all-in-one-wp-security-and-firewall
|
| 1274 |
|
| 1275 |
# @ all-in-one-wp-security-and-firewall
|
| 1276 |
#: admin/wp-security-database-menu.php:143
|
| 1277 |
+
msgid "Database prefix options"
|
| 1278 |
msgstr ""
|
| 1279 |
|
| 1280 |
# @ all-in-one-wp-security-and-firewall
|
| 1285 |
|
| 1286 |
# @ all-in-one-wp-security-and-firewall
|
| 1287 |
#: admin/wp-security-database-menu.php:163
|
| 1288 |
+
msgid "Current database table prefix"
|
| 1289 |
msgstr ""
|
| 1290 |
|
| 1291 |
# @ all-in-one-wp-security-and-firewall
|
| 1297 |
|
| 1298 |
# @ all-in-one-wp-security-and-firewall
|
| 1299 |
#: admin/wp-security-database-menu.php:176
|
| 1300 |
+
msgid "Generate new database table prefix"
|
| 1301 |
msgstr ""
|
| 1302 |
|
| 1303 |
# @ all-in-one-wp-security-and-firewall
|
| 1317 |
|
| 1318 |
# @ all-in-one-wp-security-and-firewall
|
| 1319 |
#: admin/wp-security-database-menu.php:186
|
| 1320 |
+
msgid "Change database prefix"
|
| 1321 |
msgstr ""
|
| 1322 |
|
| 1323 |
# @ all-in-one-wp-security-and-firewall
|
| 1524 |
|
| 1525 |
# @ all-in-one-wp-security-and-firewall
|
| 1526 |
#: admin/wp-security-database-menu.php:513
|
| 1527 |
+
msgid "The database prefix change tasks have been completed."
|
| 1528 |
msgstr ""
|
| 1529 |
|
| 1530 |
# @ all-in-one-wp-security-and-firewall
|
| 3835 |
|
| 3836 |
# @ all-in-one-wp-security-and-firewall
|
| 3837 |
#: admin/wp-security-user-login-menu.php:277
|
| 3838 |
+
msgid "All records from the Failed Logins table were deleted successfully."
|
| 3839 |
msgstr ""
|
| 3840 |
|
| 3841 |
# @ all-in-one-wp-security-and-firewall
|
|
@@ -1121,12 +1121,12 @@ msgstr "Arquivo de log está vazio!"
|
|
| 1121 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-database-menu.php:26
|
| 1122 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-database-menu.php:31
|
| 1123 |
#: ../../plugins/all-in-one-wp-security-and-firewall/classes/grade-system/wp-security-feature-item-manager.php:63
|
| 1124 |
-
msgid "
|
| 1125 |
msgstr "Backup do BD"
|
| 1126 |
|
| 1127 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-database-menu.php:30
|
| 1128 |
#: ../../plugins/all-in-one-wp-security-and-firewall/classes/grade-system/wp-security-feature-item-manager.php:61
|
| 1129 |
-
msgid "
|
| 1130 |
msgstr "Prefixo de BD"
|
| 1131 |
|
| 1132 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-database-menu.php:94
|
|
@@ -1146,15 +1146,15 @@ msgid "<strong>ERROR</strong>: The table prefix can only contain numbers, letter
|
|
| 1146 |
msgstr "<strong>ERRO</strong>: O prefixo da tabela pode conter apenas números, letras e sublinhados."
|
| 1147 |
|
| 1148 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-database-menu.php:132
|
| 1149 |
-
msgid "Change
|
| 1150 |
msgstr "Alterar prefixo do banco de dados"
|
| 1151 |
|
| 1152 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-database-menu.php:135
|
| 1153 |
-
msgid "Your WordPress
|
| 1154 |
msgstr "Seu banco de dados WordPress é o ativo mais importante de seu site, porque ele contém muitas informações preciosas do seu site."
|
| 1155 |
|
| 1156 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-database-menu.php:136
|
| 1157 |
-
msgid "The
|
| 1158 |
msgstr "O banco de dados também é um alvo para hackers através de métodos tais como injeções de SQL e código malicioso e automatizado que tem como alvo determinadas tabelas."
|
| 1159 |
|
| 1160 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-database-menu.php:137
|
|
@@ -1166,7 +1166,7 @@ msgid "This feature allows you to easily change the prefix to a value of your ch
|
|
| 1166 |
msgstr "Esse recurso permite que você altere facilmente o prefixo para um valor de sua escolha ou para um valor aleatório definido por este plugin."
|
| 1167 |
|
| 1168 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-database-menu.php:144
|
| 1169 |
-
msgid "
|
| 1170 |
msgstr "Opções de prefixo do banco de dados"
|
| 1171 |
|
| 1172 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-database-menu.php:155
|
|
@@ -1175,7 +1175,7 @@ msgid "It is recommended that you perform a %s before using this feature"
|
|
| 1175 |
msgstr "É recomendável que você execute um %s antes de usar este recurso"
|
| 1176 |
|
| 1177 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-database-menu.php:164
|
| 1178 |
-
msgid "Current
|
| 1179 |
msgstr "Atual prefixo da tabela BD"
|
| 1180 |
|
| 1181 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-database-menu.php:170
|
|
@@ -1187,7 +1187,7 @@ msgstr ""
|
|
| 1187 |
" Para aumentar a segurança do seu site, você deve considerar mudar o valor de prefixo do banco de dados para outro valor."
|
| 1188 |
|
| 1189 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-database-menu.php:177
|
| 1190 |
-
msgid "Generate
|
| 1191 |
msgstr "Gerar novo prefixo da tabela BD"
|
| 1192 |
|
| 1193 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-database-menu.php:180
|
|
@@ -1203,7 +1203,7 @@ msgid "Choose your own DB prefix by specifying a string which contains letters a
|
|
| 1203 |
msgstr "Escolha o seu próprio prefixo do banco de dados especificando uma sequência de caracteres que contém letras, números ou sublinhados. Exemplo: xyz_"
|
| 1204 |
|
| 1205 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-database-menu.php:187
|
| 1206 |
-
msgid "Change
|
| 1207 |
msgstr "Alterar prefixo do banco de dados"
|
| 1208 |
|
| 1209 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-database-menu.php:208
|
|
@@ -1380,7 +1380,7 @@ msgid "The usermeta table records which had references to the old DB prefix were
|
|
| 1380 |
msgstr "Os registros da tabela usermeta que tinham referências ao antigo prefixo DB foram atualizados com sucesso!"
|
| 1381 |
|
| 1382 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-database-menu.php:531
|
| 1383 |
-
msgid "
|
| 1384 |
msgstr "As tarefas de mudança de prefixo do banco de dados foram concluídas."
|
| 1385 |
|
| 1386 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-filescan-menu.php:24
|
|
@@ -3765,7 +3765,7 @@ msgid "User Login Feature - Delete all failed login records operation failed!"
|
|
| 3765 |
msgstr "Recurso de login de usuário - Excluir todas as falhas de operação de registros de falhas de login!"
|
| 3766 |
|
| 3767 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-user-login-menu.php:277
|
| 3768 |
-
msgid "All records from the Failed Logins table were deleted successfully
|
| 3769 |
msgstr "Todos os registros da tabela falhas de logins foram excluídos com sucesso!"
|
| 3770 |
|
| 3771 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-user-login-menu.php:292
|
| 1121 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-database-menu.php:26
|
| 1122 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-database-menu.php:31
|
| 1123 |
#: ../../plugins/all-in-one-wp-security-and-firewall/classes/grade-system/wp-security-feature-item-manager.php:63
|
| 1124 |
+
msgid "Database backup"
|
| 1125 |
msgstr "Backup do BD"
|
| 1126 |
|
| 1127 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-database-menu.php:30
|
| 1128 |
#: ../../plugins/all-in-one-wp-security-and-firewall/classes/grade-system/wp-security-feature-item-manager.php:61
|
| 1129 |
+
msgid "Database prefix"
|
| 1130 |
msgstr "Prefixo de BD"
|
| 1131 |
|
| 1132 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-database-menu.php:94
|
| 1146 |
msgstr "<strong>ERRO</strong>: O prefixo da tabela pode conter apenas números, letras e sublinhados."
|
| 1147 |
|
| 1148 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-database-menu.php:132
|
| 1149 |
+
msgid "Change database prefix"
|
| 1150 |
msgstr "Alterar prefixo do banco de dados"
|
| 1151 |
|
| 1152 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-database-menu.php:135
|
| 1153 |
+
msgid "Your WordPress database is the most important asset of your website because it contains a lot of your site's precious information."
|
| 1154 |
msgstr "Seu banco de dados WordPress é o ativo mais importante de seu site, porque ele contém muitas informações preciosas do seu site."
|
| 1155 |
|
| 1156 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-database-menu.php:136
|
| 1157 |
+
msgid "The database is also a target for hackers via methods such as SQL injections and malicious and automated code which targets certain tables."
|
| 1158 |
msgstr "O banco de dados também é um alvo para hackers através de métodos tais como injeções de SQL e código malicioso e automatizado que tem como alvo determinadas tabelas."
|
| 1159 |
|
| 1160 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-database-menu.php:137
|
| 1166 |
msgstr "Esse recurso permite que você altere facilmente o prefixo para um valor de sua escolha ou para um valor aleatório definido por este plugin."
|
| 1167 |
|
| 1168 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-database-menu.php:144
|
| 1169 |
+
msgid "Database prefix options"
|
| 1170 |
msgstr "Opções de prefixo do banco de dados"
|
| 1171 |
|
| 1172 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-database-menu.php:155
|
| 1175 |
msgstr "É recomendável que você execute um %s antes de usar este recurso"
|
| 1176 |
|
| 1177 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-database-menu.php:164
|
| 1178 |
+
msgid "Current database table prefix"
|
| 1179 |
msgstr "Atual prefixo da tabela BD"
|
| 1180 |
|
| 1181 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-database-menu.php:170
|
| 1187 |
" Para aumentar a segurança do seu site, você deve considerar mudar o valor de prefixo do banco de dados para outro valor."
|
| 1188 |
|
| 1189 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-database-menu.php:177
|
| 1190 |
+
msgid "Generate new database table prefix"
|
| 1191 |
msgstr "Gerar novo prefixo da tabela BD"
|
| 1192 |
|
| 1193 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-database-menu.php:180
|
| 1203 |
msgstr "Escolha o seu próprio prefixo do banco de dados especificando uma sequência de caracteres que contém letras, números ou sublinhados. Exemplo: xyz_"
|
| 1204 |
|
| 1205 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-database-menu.php:187
|
| 1206 |
+
msgid "Change database prefix"
|
| 1207 |
msgstr "Alterar prefixo do banco de dados"
|
| 1208 |
|
| 1209 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-database-menu.php:208
|
| 1380 |
msgstr "Os registros da tabela usermeta que tinham referências ao antigo prefixo DB foram atualizados com sucesso!"
|
| 1381 |
|
| 1382 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-database-menu.php:531
|
| 1383 |
+
msgid "The database prefix change tasks have been completed."
|
| 1384 |
msgstr "As tarefas de mudança de prefixo do banco de dados foram concluídas."
|
| 1385 |
|
| 1386 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-filescan-menu.php:24
|
| 3765 |
msgstr "Recurso de login de usuário - Excluir todas as falhas de operação de registros de falhas de login!"
|
| 3766 |
|
| 3767 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-user-login-menu.php:277
|
| 3768 |
+
msgid "All records from the Failed Logins table were deleted successfully."
|
| 3769 |
msgstr "Todos os registros da tabela falhas de logins foram excluídos com sucesso!"
|
| 3770 |
|
| 3771 |
#: ../../plugins/all-in-one-wp-security-and-firewall/admin/wp-security-user-login-menu.php:292
|
|
@@ -1316,12 +1316,12 @@ msgstr "В настоящий момент заблокированны IP-ад
|
|
| 1316 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:26
|
| 1317 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:31
|
| 1318 |
#: all-in-one-wp-security/classes/grade-system/wp-security-feature-item-manager.php:63
|
| 1319 |
-
msgid "
|
| 1320 |
msgstr "Резервное копирование БД"
|
| 1321 |
|
| 1322 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:30
|
| 1323 |
#: all-in-one-wp-security/classes/grade-system/wp-security-feature-item-manager.php:61
|
| 1324 |
-
msgid "
|
| 1325 |
msgstr "Префикс таблиц БД"
|
| 1326 |
|
| 1327 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:93
|
|
@@ -1351,7 +1351,7 @@ msgstr ""
|
|
| 1351 |
"цифры и символ подчеркивания."
|
| 1352 |
|
| 1353 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:131
|
| 1354 |
-
msgid "Change
|
| 1355 |
msgstr "Изменение префикса таблиц базы данных"
|
| 1356 |
|
| 1357 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:134
|
|
@@ -1390,7 +1390,7 @@ msgstr ""
|
|
| 1390 |
"значение или на сгенерированное плагином."
|
| 1391 |
|
| 1392 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:143
|
| 1393 |
-
msgid "
|
| 1394 |
msgstr "Опции изменения префикса таблиц БД"
|
| 1395 |
|
| 1396 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:154
|
|
@@ -1399,7 +1399,7 @@ msgid "It is recommended that you perform a %s before using this feature"
|
|
| 1399 |
msgstr "Рекомендуется перед изменением префикса создать %s базы данных"
|
| 1400 |
|
| 1401 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:163
|
| 1402 |
-
msgid "Current
|
| 1403 |
msgstr "Текущий префикс таблиц БД"
|
| 1404 |
|
| 1405 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:169
|
|
@@ -1413,7 +1413,7 @@ msgstr ""
|
|
| 1413 |
"Для усиления безопасности, измените его на любое другое значение."
|
| 1414 |
|
| 1415 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:176
|
| 1416 |
-
msgid "Generate
|
| 1417 |
msgstr "Сгенерировать новый префикс таблиц БД"
|
| 1418 |
|
| 1419 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:179
|
|
@@ -1436,7 +1436,7 @@ msgstr ""
|
|
| 1436 |
"символ подчеркивания. Например: xyz_"
|
| 1437 |
|
| 1438 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:186
|
| 1439 |
-
msgid "Change
|
| 1440 |
msgstr "Изменить префикс таблиц"
|
| 1441 |
|
| 1442 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:207
|
|
@@ -1673,7 +1673,7 @@ msgstr ""
|
|
| 1673 |
"данных, успешно обновлены!"
|
| 1674 |
|
| 1675 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:530
|
| 1676 |
-
msgid "
|
| 1677 |
msgstr "Префикс таблиц базы данных успешно изменен."
|
| 1678 |
|
| 1679 |
#: all-in-one-wp-security/admin/wp-security-filescan-menu.php:24
|
|
@@ -4773,7 +4773,7 @@ msgid "User Login Feature - Delete all failed login records operation failed!"
|
|
| 4773 |
msgstr "Записи об ошибочных попытках авторизации удалить не удалось!"
|
| 4774 |
|
| 4775 |
#: all-in-one-wp-security/admin/wp-security-user-login-menu.php:277
|
| 4776 |
-
msgid "All records from the Failed Logins table were deleted successfully
|
| 4777 |
msgstr "Все записи об ошибочных попытках авторизации удалены!"
|
| 4778 |
|
| 4779 |
#: all-in-one-wp-security/admin/wp-security-user-login-menu.php:292
|
| 1316 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:26
|
| 1317 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:31
|
| 1318 |
#: all-in-one-wp-security/classes/grade-system/wp-security-feature-item-manager.php:63
|
| 1319 |
+
msgid "Database backup"
|
| 1320 |
msgstr "Резервное копирование БД"
|
| 1321 |
|
| 1322 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:30
|
| 1323 |
#: all-in-one-wp-security/classes/grade-system/wp-security-feature-item-manager.php:61
|
| 1324 |
+
msgid "Database prefix"
|
| 1325 |
msgstr "Префикс таблиц БД"
|
| 1326 |
|
| 1327 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:93
|
| 1351 |
"цифры и символ подчеркивания."
|
| 1352 |
|
| 1353 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:131
|
| 1354 |
+
msgid "Change database prefix"
|
| 1355 |
msgstr "Изменение префикса таблиц базы данных"
|
| 1356 |
|
| 1357 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:134
|
| 1390 |
"значение или на сгенерированное плагином."
|
| 1391 |
|
| 1392 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:143
|
| 1393 |
+
msgid "Database prefix options"
|
| 1394 |
msgstr "Опции изменения префикса таблиц БД"
|
| 1395 |
|
| 1396 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:154
|
| 1399 |
msgstr "Рекомендуется перед изменением префикса создать %s базы данных"
|
| 1400 |
|
| 1401 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:163
|
| 1402 |
+
msgid "Current database table prefix"
|
| 1403 |
msgstr "Текущий префикс таблиц БД"
|
| 1404 |
|
| 1405 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:169
|
| 1413 |
"Для усиления безопасности, измените его на любое другое значение."
|
| 1414 |
|
| 1415 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:176
|
| 1416 |
+
msgid "Generate new database table prefix"
|
| 1417 |
msgstr "Сгенерировать новый префикс таблиц БД"
|
| 1418 |
|
| 1419 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:179
|
| 1436 |
"символ подчеркивания. Например: xyz_"
|
| 1437 |
|
| 1438 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:186
|
| 1439 |
+
msgid "Change database prefix"
|
| 1440 |
msgstr "Изменить префикс таблиц"
|
| 1441 |
|
| 1442 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:207
|
| 1673 |
"данных, успешно обновлены!"
|
| 1674 |
|
| 1675 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:530
|
| 1676 |
+
msgid "The database prefix change tasks have been completed."
|
| 1677 |
msgstr "Префикс таблиц базы данных успешно изменен."
|
| 1678 |
|
| 1679 |
#: all-in-one-wp-security/admin/wp-security-filescan-menu.php:24
|
| 4773 |
msgstr "Записи об ошибочных попытках авторизации удалить не удалось!"
|
| 4774 |
|
| 4775 |
#: all-in-one-wp-security/admin/wp-security-user-login-menu.php:277
|
| 4776 |
+
msgid "All records from the Failed Logins table were deleted successfully."
|
| 4777 |
msgstr "Все записи об ошибочных попытках авторизации удалены!"
|
| 4778 |
|
| 4779 |
#: all-in-one-wp-security/admin/wp-security-user-login-menu.php:292
|
|
@@ -1146,12 +1146,12 @@ msgstr ""
|
|
| 1146 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:26
|
| 1147 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:31
|
| 1148 |
#: all-in-one-wp-security/classes/grade-system/wp-security-feature-item-manager.php:63
|
| 1149 |
-
msgid "
|
| 1150 |
msgstr ""
|
| 1151 |
|
| 1152 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:30
|
| 1153 |
#: all-in-one-wp-security/classes/grade-system/wp-security-feature-item-manager.php:61
|
| 1154 |
-
msgid "
|
| 1155 |
msgstr ""
|
| 1156 |
|
| 1157 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:93
|
|
@@ -1176,7 +1176,7 @@ msgid ""
|
|
| 1176 |
msgstr ""
|
| 1177 |
|
| 1178 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:131
|
| 1179 |
-
msgid "Change
|
| 1180 |
msgstr ""
|
| 1181 |
|
| 1182 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:134
|
|
@@ -1205,7 +1205,7 @@ msgid ""
|
|
| 1205 |
msgstr ""
|
| 1206 |
|
| 1207 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:143
|
| 1208 |
-
msgid "
|
| 1209 |
msgstr ""
|
| 1210 |
|
| 1211 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:154
|
|
@@ -1214,7 +1214,7 @@ msgid "It is recommended that you perform a %s before using this feature"
|
|
| 1214 |
msgstr ""
|
| 1215 |
|
| 1216 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:163
|
| 1217 |
-
msgid "Current
|
| 1218 |
m
|
| 1146 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:26
|
| 1147 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:31
|
| 1148 |
#: all-in-one-wp-security/classes/grade-system/wp-security-feature-item-manager.php:63
|
| 1149 |
+
msgid "Database backup"
|
| 1150 |
msgstr ""
|
| 1151 |
|
| 1152 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:30
|
| 1153 |
#: all-in-one-wp-security/classes/grade-system/wp-security-feature-item-manager.php:61
|
| 1154 |
+
msgid "Database prefix"
|
| 1155 |
msgstr ""
|
| 1156 |
|
| 1157 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:93
|
| 1176 |
msgstr ""
|
| 1177 |
|
| 1178 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:131
|
| 1179 |
+
msgid "Change database prefix"
|
| 1180 |
msgstr ""
|
| 1181 |
|
| 1182 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:134
|
| 1205 |
msgstr ""
|
| 1206 |
|
| 1207 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:143
|
| 1208 |
+
msgid "Database prefix options"
|
| 1209 |
msgstr ""
|
| 1210 |
|
| 1211 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:154
|
| 1214 |
msgstr ""
|
| 1215 |
|
| 1216 |
#: all-in-one-wp-security/admin/wp-security-database-menu.php:163
|
| 1217 |
+
msgid "Current database table prefix"
|
| 1218 |
m
|
