Version Description
- Fixed the bug with AAM media control for files with special characters
- Added secure login widget and shortcode
- Deprecated Security feature
Download this release
Release Info
Developer | vasyl_m |
Plugin | Advanced Access Manager |
Version | 4.9.2 |
Comparing to | |
See all releases |
Code changes from version 4.9.1 to 4.9.2
- Application/Backend/Feature/Security.php +0 -27
- Application/Backend/Filter.php +14 -0
- Application/Backend/Manager.php +19 -0
- Application/Backend/Widget/Login.php +92 -0
- Application/Backend/phtml/security.phtml +3 -36
- Application/Backend/phtml/widget/login-backend.phtml +27 -0
- Application/Backend/phtml/widget/login-frontend.phtml +61 -0
- Application/Core/Config.php +16 -1
- Application/Core/ConfigPress.php +1 -1
- Application/Core/Login.php +290 -0
- Application/Core/Media.php +1 -1
- Application/Frontend/Manager.php +26 -196
- Application/Frontend/phtml/login.phtml +46 -0
- Application/Shortcode/Strategy/Login.php +23 -13
- Application/Shortcode/Strategy/LoginRedirect.php +87 -0
- aam.php +4 -1
- media/js/aam-login.js +68 -0
- readme.txt +9 -1
Application/Backend/Feature/Security.php
CHANGED
@@ -29,33 +29,6 @@ class AAM_Backend_Feature_Security extends AAM_Backend_Feature_Abstract {
|
|
29 |
return 'security.phtml';
|
30 |
}
|
31 |
|
32 |
-
/**
|
33 |
-
* Save AAM utility options
|
34 |
-
*
|
35 |
-
* @return string
|
36 |
-
*
|
37 |
-
* @access public
|
38 |
-
*/
|
39 |
-
public function save() {
|
40 |
-
$param = AAM_Core_Request::post('param');
|
41 |
-
$value = stripslashes(AAM_Core_Request::post('value'));
|
42 |
-
|
43 |
-
AAM_Core_Config::set($param, $value);
|
44 |
-
|
45 |
-
return json_encode(array('status' => 'success'));
|
46 |
-
}
|
47 |
-
|
48 |
-
/**
|
49 |
-
*
|
50 |
-
* @return type
|
51 |
-
*/
|
52 |
-
public function getOptionList() {
|
53 |
-
$filename = dirname(__FILE__) . '/../View/SecurityOptionList.php';
|
54 |
-
$options = include $filename;
|
55 |
-
|
56 |
-
return apply_filters('aam-security-option-list-filter', $options);
|
57 |
-
}
|
58 |
-
|
59 |
/**
|
60 |
* Register Contact/Hire feature
|
61 |
*
|
29 |
return 'security.phtml';
|
30 |
}
|
31 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
32 |
/**
|
33 |
* Register Contact/Hire feature
|
34 |
*
|
Application/Backend/Filter.php
CHANGED
@@ -472,6 +472,13 @@ class AAM_Backend_Filter {
|
|
472 |
break;
|
473 |
|
474 |
default:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
475 |
break;
|
476 |
}
|
477 |
} elseif (is_a($post, 'WP_Post')) {
|
@@ -487,6 +494,13 @@ class AAM_Backend_Filter {
|
|
487 |
break;
|
488 |
|
489 |
default:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
490 |
break;
|
491 |
}
|
492 |
}
|
472 |
break;
|
473 |
|
474 |
default:
|
475 |
+
$allCaps = apply_filters(
|
476 |
+
'aam-check-user-cap-filter',
|
477 |
+
$allCaps,
|
478 |
+
$metaCaps,
|
479 |
+
$args,
|
480 |
+
$this
|
481 |
+
);
|
482 |
break;
|
483 |
}
|
484 |
} elseif (is_a($post, 'WP_Post')) {
|
494 |
break;
|
495 |
|
496 |
default:
|
497 |
+
$allCaps = apply_filters(
|
498 |
+
'aam-check-user-cap-filter',
|
499 |
+
$allCaps,
|
500 |
+
$metaCaps,
|
501 |
+
$args,
|
502 |
+
$this
|
503 |
+
);
|
504 |
break;
|
505 |
}
|
506 |
}
|
Application/Backend/Manager.php
CHANGED
@@ -88,6 +88,10 @@ class AAM_Backend_Manager {
|
|
88 |
//control admin area
|
89 |
add_action('admin_init', array($this, 'adminInit'));
|
90 |
|
|
|
|
|
|
|
|
|
91 |
//register backend hooks and filters
|
92 |
if (AAM_Core_Config::get('backend-access-control', true)) {
|
93 |
AAM_Backend_Filter::register();
|
@@ -97,6 +101,17 @@ class AAM_Backend_Manager {
|
|
97 |
AAM_Backend_View_CodePinch::bootstrap();
|
98 |
}
|
99 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
100 |
/**
|
101 |
*
|
102 |
*/
|
@@ -137,6 +152,10 @@ class AAM_Backend_Manager {
|
|
137 |
return $text;
|
138 |
}
|
139 |
|
|
|
|
|
|
|
|
|
140 |
/**
|
141 |
*
|
142 |
*/
|
88 |
//control admin area
|
89 |
add_action('admin_init', array($this, 'adminInit'));
|
90 |
|
91 |
+
//register login widget
|
92 |
+
add_action('widgets_init', array($this, 'registerLoginWidget'));
|
93 |
+
add_action('wp_ajax_nopriv_aamlogin', array($this, 'handleLogin'));
|
94 |
+
|
95 |
//register backend hooks and filters
|
96 |
if (AAM_Core_Config::get('backend-access-control', true)) {
|
97 |
AAM_Backend_Filter::register();
|
101 |
AAM_Backend_View_CodePinch::bootstrap();
|
102 |
}
|
103 |
|
104 |
+
/**
|
105 |
+
*
|
106 |
+
* @return type
|
107 |
+
*/
|
108 |
+
public function handleLogin() {
|
109 |
+
$login = AAM_Core_Login::getInstance();
|
110 |
+
|
111 |
+
echo json_encode($login->execute());
|
112 |
+
exit;
|
113 |
+
}
|
114 |
+
|
115 |
/**
|
116 |
*
|
117 |
*/
|
152 |
return $text;
|
153 |
}
|
154 |
|
155 |
+
public function registerLoginWidget() {
|
156 |
+
register_widget('AAM_Backend_Widget_Login');
|
157 |
+
}
|
158 |
+
|
159 |
/**
|
160 |
*
|
161 |
*/
|
Application/Backend/Widget/Login.php
ADDED
@@ -0,0 +1,92 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* ======================================================================
|
5 |
+
* LICENSE: This file is subject to the terms and conditions defined in *
|
6 |
+
* file 'license.txt', which is part of this source code package. *
|
7 |
+
* ======================================================================
|
8 |
+
*/
|
9 |
+
|
10 |
+
class AAM_Backend_Widget_Login extends WP_Widget {
|
11 |
+
|
12 |
+
public $args = array();
|
13 |
+
|
14 |
+
/**
|
15 |
+
*
|
16 |
+
*/
|
17 |
+
public function __construct() {
|
18 |
+
$options = array(
|
19 |
+
'description' => __( "AAM Secure Login Widget", AAM_KEY)
|
20 |
+
);
|
21 |
+
|
22 |
+
parent::__construct(false, 'AAM Secure Login', $options);
|
23 |
+
}
|
24 |
+
|
25 |
+
/**
|
26 |
+
*
|
27 |
+
* @param type $args
|
28 |
+
* @param type $instance
|
29 |
+
*/
|
30 |
+
public function widget($args, $instance) {
|
31 |
+
$this->args = array_merge($args, $this->normalize($instance));
|
32 |
+
|
33 |
+
require(
|
34 |
+
AAM_Core_Config::get(
|
35 |
+
'login.widget.template',
|
36 |
+
dirname(__DIR__) . '/phtml/widget/login-frontend.phtml'
|
37 |
+
)
|
38 |
+
);
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
*
|
43 |
+
* @param type $instance
|
44 |
+
*/
|
45 |
+
public function form($instance) {
|
46 |
+
$instance = $this->normalize($instance);
|
47 |
+
|
48 |
+
require(dirname(__DIR__) . '/phtml/widget/login-backend.phtml');
|
49 |
+
}
|
50 |
+
|
51 |
+
/**
|
52 |
+
*
|
53 |
+
* @param type $new
|
54 |
+
* @param type $old
|
55 |
+
* @return type
|
56 |
+
*/
|
57 |
+
public function update($new, $old) {
|
58 |
+
if ($new['login-timeout'] != $old['login-timeout']) {
|
59 |
+
AAM_Core_Config::set('login-timeout', $new['login-timeout']);
|
60 |
+
}
|
61 |
+
|
62 |
+
if ($new['brute-force-lockout'] != $old['brute-force-lockout']) {
|
63 |
+
AAM_Core_Config::set('brute-force-lockout', $new['brute-force-lockout']);
|
64 |
+
}
|
65 |
+
|
66 |
+
return parent::update($new, $old);
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
*
|
71 |
+
* @param type $instance
|
72 |
+
* @return type
|
73 |
+
*/
|
74 |
+
protected function normalize($instance) {
|
75 |
+
$instance['login-title'] = AAM_Core_Config::get('login-title');
|
76 |
+
$instance['login-ip-track'] = AAM_Core_Config::get('login-ip-track');
|
77 |
+
$instance['brute-force-lockout'] = AAM_Core_Config::get('brute-force-lockout');
|
78 |
+
|
79 |
+
if (empty($instance['login-title'])) {
|
80 |
+
$instance['login-title'] = __('Login', AAM_KEY);
|
81 |
+
}
|
82 |
+
|
83 |
+
if (empty($instance['user-title'])) {
|
84 |
+
$instance['user-title'] = __('Howdy, %username%', AAM_KEY);
|
85 |
+
}
|
86 |
+
|
87 |
+
$instance['redirect'] = AAM_Core_Request::get('redirect_to');
|
88 |
+
|
89 |
+
return $instance;
|
90 |
+
}
|
91 |
+
|
92 |
+
}
|
Application/Backend/phtml/security.phtml
CHANGED
@@ -1,40 +1,7 @@
|
|
1 |
<?php if (defined('AAM_KEY')) { ?>
|
2 |
<div class="aam-feature" id="security-content">
|
3 |
-
<
|
4 |
-
<
|
5 |
-
|
6 |
-
|
7 |
-
<?php foreach($this->getOptionList() as $id => $option) { ?>
|
8 |
-
<tr>
|
9 |
-
<td>
|
10 |
-
<span class='aam-setting-title'><?php echo $option['title']; ?></span>
|
11 |
-
<p class="aam-setting-description">
|
12 |
-
<?php echo $option['descr']; ?>
|
13 |
-
</p>
|
14 |
-
</td>
|
15 |
-
<td class="text-center">
|
16 |
-
<input type="checkbox" name="<?php echo $id; ?>" id="security-<?php echo $id; ?>" <?php echo ($option['value'] ? 'checked' : ''); ?> /> <label for="security-<?php echo $id; ?>"></label>
|
17 |
-
</td>
|
18 |
-
</tr>
|
19 |
-
<?php } ?>
|
20 |
-
|
21 |
-
<?php do_action('aam-post-security-content-action'); ?>
|
22 |
-
</tbody>
|
23 |
-
</table>
|
24 |
-
|
25 |
-
<?php if (AAM_Backend_View_CodePinch::isInstalled() === false) { ?>
|
26 |
-
<div class="metabox-holder">
|
27 |
-
<div class="postbox">
|
28 |
-
<div class="inside text-center">
|
29 |
-
<div class="inner-sm">
|
30 |
-
<p class="text-muted text-larger"><?php echo __('Improve your website security and keep your website error free with CodePinch.', AAM_KEY); ?></p>
|
31 |
-
<a href="<?php echo AAM_Backend_View_CodePinch::getUrl(AAM_CODEPINCH_AFFILIATE_CODE); ?>" class="btn btn-lg btn-success" target="_blank">
|
32 |
-
Install Free Plugin
|
33 |
-
</a>
|
34 |
-
</div>
|
35 |
-
</div>
|
36 |
-
</div>
|
37 |
-
</div>
|
38 |
-
<?php } ?>
|
39 |
</div>
|
40 |
<?php }
|
1 |
<?php if (defined('AAM_KEY')) { ?>
|
2 |
<div class="aam-feature" id="security-content">
|
3 |
+
<p class="aam-notification">
|
4 |
+
<?php echo sprintf(AAM_Backend_View_Helper::preparePhrase('[Warning!] The Security feature has been deprecated and will be removed in AAM 5.0. This feature has been replaced with AAM Secure Login Widget and Shortcode. For more information please check %sHow does AAM Secure Login works%s article. For any feedback of questions, do not hesistate to contact us directly.', 'strong'), '<a href="https://aamplugin.com/help/how-does-aam-secure-login-works" target="_blank">', '</a>'); ?>
|
5 |
+
</p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
6 |
</div>
|
7 |
<?php }
|
Application/Backend/phtml/widget/login-backend.phtml
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if (defined('AAM_KEY')) { ?>
|
2 |
+
<p>
|
3 |
+
<label for="<?php echo $this->get_field_id('login-title'); ?>"><?php echo __('Login Title', AAM_KEY); ?>: </label>
|
4 |
+
<input type="text" class="widefat" id="<?php echo $this->get_field_id('login-title'); ?>" name="<?php echo $this->get_field_name('login-title'); ?>" value="<?php echo esc_attr($instance['login-title']); ?>" />
|
5 |
+
</p>
|
6 |
+
|
7 |
+
<p>
|
8 |
+
<label for="<?php echo $this->get_field_id('user-title'); ?>"><?php echo __('Logged In Title', AAM_KEY); ?>: </label>
|
9 |
+
<input type="text" class="widefat" id="<?php echo $this->get_field_id('user-title'); ?>" name="<?php echo $this->get_field_name('user-title'); ?>" value="<?php echo esc_attr($instance['user-title']); ?>" />
|
10 |
+
</p>
|
11 |
+
|
12 |
+
<p>
|
13 |
+
<input type="checkbox" id="<?php echo $this->get_field_id('login-timeout'); ?>" name="<?php echo $this->get_field_name('login-timeout'); ?>" value="1" <?php echo !empty($instance['login-timeout']) ? 'checked="checked"' : ""; ?> />
|
14 |
+
<label for="<?php echo $this->get_field_id('login-timeout'); ?>"><strong><?php echo __('Login Timeout', AAM_KEY); ?></strong></label>
|
15 |
+
<small style="line-height:1.35em; display:block; margin-top: 5px;">Delay the login process for 1 second to significantly reduce the chance for brute force or dictionary attack.</small>
|
16 |
+
</p>
|
17 |
+
|
18 |
+
<p style="margin-bottom: 1em;">
|
19 |
+
<input type="checkbox" id="<?php echo $this->get_field_id('brute-force-lockout'); ?>" name="<?php echo $this->get_field_name('brute-force-lockout'); ?>" value="1" <?php echo !empty($instance['brute-force-lockout']) ? 'checked="checked"' : ""; ?> />
|
20 |
+
<label for="<?php echo $this->get_field_id('brute-force-lockout'); ?>"><strong><?php echo __('Brute Force Lockout', AAM_KEY); ?></strong></label>
|
21 |
+
<small style="line-height:1.35em; display:block; margin-top: 5px;">Automatically reject login attempts if number of unsuccessful login attempts is more than 20 over the period of 2 minutes.</small>
|
22 |
+
</p>
|
23 |
+
|
24 |
+
<p style="background-color: #fafafa; border-left: 3px solid #337ab7; font-size: 1em; line-height: 1.35em; margin-bottom: 1em; padding: 10px; font-size: 0.8em;">
|
25 |
+
<?php echo sprintf(__('For more advanced setup like login/logout redirects or custom styling, please check %sthis article%s.', AAM_KEY), '<a href="https://aamplugin.com/help/how-does-aam-secure-login-works" target="_blank">', '</a>'); ?>
|
26 |
+
</p>
|
27 |
+
<?php }
|
Application/Backend/phtml/widget/login-frontend.phtml
ADDED
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if (defined('AAM_KEY')) { ?>
|
2 |
+
<?php
|
3 |
+
echo $this->args['before_widget'];
|
4 |
+
|
5 |
+
if(!is_user_logged_in()) {
|
6 |
+
echo $this->args['before_title'];
|
7 |
+
echo apply_filters('widget_title', $this->args['login-title'], $this->args, $this->id_base);
|
8 |
+
echo $this->args['after_title'];
|
9 |
+
}elseif(is_user_logged_in()) {
|
10 |
+
echo $this->args['before_title'];
|
11 |
+
echo str_replace('%username%', AAM::getUser()->display_name, $this->args['user-title']);
|
12 |
+
echo $this->args['after_title'];
|
13 |
+
}
|
14 |
+
?>
|
15 |
+
|
16 |
+
<?php if(!is_user_logged_in()) { ?>
|
17 |
+
<div id="<?php echo $this->get_field_id('error'); ?>" style="display: none; margin-bottom: 15px; border-left: 4px solid #dc3232; padding: 6px;"></div>
|
18 |
+
|
19 |
+
<div id="<?php echo $this->get_field_id('loginform'); ?>">
|
20 |
+
<p>
|
21 |
+
<label for="user_login">Username or Email Address<br>
|
22 |
+
<input id="<?php echo $this->get_field_id('log'); ?>" class="input" value="" size="20" type="text" />
|
23 |
+
</label>
|
24 |
+
</p>
|
25 |
+
|
26 |
+
<p>
|
27 |
+
<label for="user_pass">Password<br>
|
28 |
+
<input id="<?php echo $this->get_field_id('pwd'); ?>" class="input" value="" size="20" type="password" />
|
29 |
+
</label>
|
30 |
+
</p>
|
31 |
+
|
32 |
+
<p class="forgetmenot">
|
33 |
+
<label for="rememberme">
|
34 |
+
<input id="<?php echo $this->get_field_id('rememberme'); ?>" value="forever" type="checkbox"/> Remember Me
|
35 |
+
</label>
|
36 |
+
</p>
|
37 |
+
|
38 |
+
<p class="submit">
|
39 |
+
<input class="button button-primary button-large aam-login-submit" data-prefix="<?php echo $this->get_field_id(''); ?>" value="Log In" type="submit" />
|
40 |
+
<input id="<?php echo $this->get_field_id('redirect'); ?>" value="<?php echo $this->args['redirect']; ?>" type="hidden" />
|
41 |
+
</p>
|
42 |
+
</div>
|
43 |
+
|
44 |
+
<p id="<?php echo $this->get_field_id('nav'); ?>">
|
45 |
+
<a href="<?php echo esc_url( wp_lostpassword_url() ); ?>">Lost your password?</a>
|
46 |
+
</p>
|
47 |
+
<?php } else { ?>
|
48 |
+
<div style="display: table; width: 100%;">
|
49 |
+
<div style="display:table-cell; width: 30%; text-align: center; vertical-align: middle;">
|
50 |
+
<?php echo get_avatar(AAM::getUser()->ID, "50"); ?>
|
51 |
+
</div>
|
52 |
+
<div style="display:table-cell;">
|
53 |
+
<a href="<?php echo esc_url(get_admin_url()); ?>"><?php echo __('Dashboard'); ?></a><br/>
|
54 |
+
<a href="<?php echo esc_url(get_admin_url(null, 'profile.php')); ?>"><?php echo __('Edit My Profile'); ?></a><br/>
|
55 |
+
<a href="<?php echo esc_url(wp_logout_url()); ?>"><?php echo __('Log Out'); ?></a>
|
56 |
+
</div>
|
57 |
+
</div>
|
58 |
+
<?php } ?>
|
59 |
+
|
60 |
+
<?php echo $this->args['after_widget'];
|
61 |
+
}
|
Application/Core/Config.php
CHANGED
@@ -65,7 +65,22 @@ class AAM_Core_Config {
|
|
65 |
$response = self::readConfigPress($option, $default);
|
66 |
}
|
67 |
|
68 |
-
return
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
69 |
}
|
70 |
|
71 |
/**
|
65 |
$response = self::readConfigPress($option, $default);
|
66 |
}
|
67 |
|
68 |
+
return self::normalize(
|
69 |
+
apply_filters('aam-filter-config-get', $response, $option
|
70 |
+
));
|
71 |
+
}
|
72 |
+
|
73 |
+
/**
|
74 |
+
*
|
75 |
+
* @param type $setting
|
76 |
+
* @return type
|
77 |
+
*/
|
78 |
+
protected static function normalize($setting) {
|
79 |
+
return str_replace(
|
80 |
+
array('{ABSPATH}'),
|
81 |
+
array(ABSPATH),
|
82 |
+
$setting
|
83 |
+
);
|
84 |
}
|
85 |
|
86 |
/**
|
Application/Core/ConfigPress.php
CHANGED
@@ -12,7 +12,7 @@
|
|
12 |
*
|
13 |
* @package AAM
|
14 |
* @author Vasyl Martyniuk <vasyl@vasyltech.com>
|
15 |
-
* @todo Deprecated - Remove in May
|
16 |
*/
|
17 |
final class AAM_Core_ConfigPress {
|
18 |
|
12 |
*
|
13 |
* @package AAM
|
14 |
* @author Vasyl Martyniuk <vasyl@vasyltech.com>
|
15 |
+
* @todo Deprecated - Remove in May 2018
|
16 |
*/
|
17 |
final class AAM_Core_ConfigPress {
|
18 |
|
Application/Core/Login.php
ADDED
@@ -0,0 +1,290 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* ======================================================================
|
5 |
+
* LICENSE: This file is subject to the terms and conditions defined in *
|
6 |
+
* file 'license.txt', which is part of this source code package. *
|
7 |
+
* ======================================================================
|
8 |
+
*/
|
9 |
+
|
10 |
+
/**
|
11 |
+
* AAM Core login
|
12 |
+
*
|
13 |
+
* @package AAM
|
14 |
+
* @author Vasyl Martyniuk <vasyl@vasyltech.com>
|
15 |
+
*/
|
16 |
+
class AAM_Core_Login {
|
17 |
+
|
18 |
+
/**
|
19 |
+
*
|
20 |
+
* @var type
|
21 |
+
*/
|
22 |
+
protected $aamLogin = false;
|
23 |
+
|
24 |
+
/**
|
25 |
+
*
|
26 |
+
* @var type
|
27 |
+
*/
|
28 |
+
protected static $instance = null;
|
29 |
+
|
30 |
+
/**
|
31 |
+
*
|
32 |
+
*/
|
33 |
+
protected function __construct() {
|
34 |
+
//login hook
|
35 |
+
add_action('wp_login', array($this, 'login'), 10, 2);
|
36 |
+
add_action('wp_logout', array($this, 'logout'));
|
37 |
+
|
38 |
+
//user login control
|
39 |
+
add_filter('wp_authenticate_user', array($this, 'authenticate'), 1, 2);
|
40 |
+
|
41 |
+
//login process
|
42 |
+
add_filter('login_message', array($this, 'loginMessage'));
|
43 |
+
|
44 |
+
//security controls
|
45 |
+
add_action('login_form_login', array($this, 'watch'), 1);
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
*
|
50 |
+
* @param type $username
|
51 |
+
* @param type $user
|
52 |
+
*/
|
53 |
+
public function login($username, $user = null) {
|
54 |
+
if (is_a($user, 'WP_User')) {
|
55 |
+
$this->updateLoginCounter(-1);
|
56 |
+
|
57 |
+
AAM_Core_API::deleteOption('aam-user-switch-' . $user->ID);
|
58 |
+
|
59 |
+
if ($this->aamLogin === false) {
|
60 |
+
$redirect = $this->getLoginRedirect($user);
|
61 |
+
|
62 |
+
if ($redirect !== null) {
|
63 |
+
AAM_Core_API::redirect($redirect);
|
64 |
+
}
|
65 |
+
}
|
66 |
+
}
|
67 |
+
}
|
68 |
+
|
69 |
+
/**
|
70 |
+
*
|
71 |
+
* @param type $user
|
72 |
+
* @return type
|
73 |
+
*/
|
74 |
+
protected function getLoginRedirect($user) {
|
75 |
+
$redirect = null;
|
76 |
+
$subject = new AAM_Core_Subject_User($user->ID);
|
77 |
+
$object = $subject->getObject('loginRedirect');
|
78 |
+
|
79 |
+
//if Login redirect is defined
|
80 |
+
$type = $object->get('login.redirect.type');
|
81 |
+
|
82 |
+
if (!empty($type) && $type !== 'default') {
|
83 |
+
$redirect = $object->get("login.redirect.{$type}");
|
84 |
+
}
|
85 |
+
|
86 |
+
return $redirect;
|
87 |
+
}
|
88 |
+
|
89 |
+
/**
|
90 |
+
*
|
91 |
+
*/
|
92 |
+
public function logout() {
|
93 |
+
$object = AAM::getUser()->getObject('logoutRedirect');
|
94 |
+
$type = $object->get('logout.redirect.type');
|
95 |
+
|
96 |
+
if (!empty($type) && $type !== 'default') {
|
97 |
+
$redirect = $object->get("logout.redirect.{$type}");
|
98 |
+
AAM_Core_API::redirect($redirect);
|
99 |
+
}
|
100 |
+
}
|
101 |
+
|
102 |
+
/**
|
103 |
+
*
|
104 |
+
* @param type $message
|
105 |
+
* @return type
|
106 |
+
*/
|
107 |
+
public function loginMessage($message) {
|
108 |
+
$reason = AAM_Core_Request::get('reason');
|
109 |
+
|
110 |
+
if (empty($message) && ($reason == 'access-denied')) {
|
111 |
+
$message = AAM_Core_Config::get(
|
112 |
+
'login.redirect.message',
|
113 |
+
'<p class="message">' . __('Access denied. Please login to get access.', AAM_KEY) . '</p>'
|
114 |
+
);
|
115 |
+
}
|
116 |
+
|
117 |
+
return $message;
|
118 |
+
}
|
119 |
+
|
120 |
+
/**
|
121 |
+
*
|
122 |
+
*/
|
123 |
+
public function watch() {
|
124 |
+
//Login Timeout
|
125 |
+
if (AAM_Core_Config::get('login-timeout', false)) {
|
126 |
+
@sleep(intval(AAM_Core_Config::get('security.login.timeout', 1)));
|
127 |
+
}
|
128 |
+
|
129 |
+
//Brute Force Lockout
|
130 |
+
if (AAM_Core_Config::get('brute-force-lockout', false)) {
|
131 |
+
$this->updateLoginCounter(1);
|
132 |
+
}
|
133 |
+
}
|
134 |
+
|
135 |
+
/**
|
136 |
+
* Control User Block flag
|
137 |
+
*
|
138 |
+
* @param WP_Error $user
|
139 |
+
*
|
140 |
+
* @return WP_Error|WP_User
|
141 |
+
*
|
142 |
+
* @access public
|
143 |
+
*/
|
144 |
+
public function authenticate($user) {
|
145 |
+
if (is_a($user, 'WP_User') && $user->user_status == 1) {
|
146 |
+
$user = new WP_Error();
|
147 |
+
|
148 |
+
$message = 'ERROR]: User is locked. Please contact your website ';
|
149 |
+
$message .= 'administrator.';
|
150 |
+
|
151 |
+
$user->add(
|
152 |
+
'authentication_failed',
|
153 |
+
AAM_Backend_View_Helper::preparePhrase($message, 'strong')
|
154 |
+
);
|
155 |
+
}
|
156 |
+
|
157 |
+
return $user;
|
158 |
+
}
|
159 |
+
|
160 |
+
/**
|
161 |
+
*
|
162 |
+
* @return type
|
163 |
+
* @throws Exception
|
164 |
+
*/
|
165 |
+
public function execute() {
|
166 |
+
$this->aamLogin = true;
|
167 |
+
|
168 |
+
$response = array(
|
169 |
+
'status' => 'failure',
|
170 |
+
'redirect' => AAM_Core_Request::post('redirect')
|
171 |
+
);
|
172 |
+
|
173 |
+
$log = sanitize_user(AAM_Core_Request::post('log'));
|
174 |
+
|
175 |
+
try {
|
176 |
+
$user = wp_signon(array(), $this->checkUserSSL($log));
|
177 |
+
|
178 |
+
if (is_wp_error($user)) {
|
179 |
+
Throw new Exception($user->get_error_message());
|
180 |
+
}
|
181 |
+
$redirect = $this->getLoginRedirect($user);
|
182 |
+
|
183 |
+
if (empty($response['redirect'])) {
|
184 |
+
$response['redirect'] = ($redirect ? $this->normalizeRedirect($redirect) : admin_url());
|
185 |
+
}
|
186 |
+
|
187 |
+
$response['status'] = 'success';
|
188 |
+
} catch (Exception $ex) {
|
189 |
+
$response['reason'] = $ex->getMessage();
|
190 |
+
}
|
191 |
+
|
192 |
+
return $response;
|
193 |
+
}
|
194 |
+
|
195 |
+
/**
|
196 |
+
*
|
197 |
+
* @param type $redirect
|
198 |
+
* @return type
|
199 |
+
*/
|
200 |
+
protected function normalizeRedirect($redirect) {
|
201 |
+
$normalized = null;
|
202 |
+
|
203 |
+
if (filter_var($redirect, FILTER_VALIDATE_URL)) {
|
204 |
+
$normalized = $redirect;
|
205 |
+
} elseif (preg_match('/^[\d]+$/', $redirect)) {
|
206 |
+
$normalized = get_page_link($redirect);
|
207 |
+
} elseif (is_callable($redirect)) {
|
208 |
+
$normalized = call_user_func($redirect);
|
209 |
+
}
|
210 |
+
|
211 |
+
return $normalized;
|
212 |
+
}
|
213 |
+
|
214 |
+
/**
|
215 |
+
*
|
216 |
+
* @param type $increment
|
217 |
+
*/
|
218 |
+
protected function updateLoginCounter($increment) {
|
219 |
+
$attempts = get_transient('aam-login-attemtps');
|
220 |
+
|
221 |
+
if ($attempts !== false) {
|
222 |
+
$timeout = get_option('_transient_timeout_aam-login-attemtps') - time();
|
223 |
+
$attempts = intval($attempts) + $increment;
|
224 |
+
} else {
|
225 |
+
$attempts = 1;
|
226 |
+
$timeout = strtotime(
|
227 |
+
'+' . AAM_Core_Config::get('security.login.period', '20 minutes')
|
228 |
+
) - time();
|
229 |
+
}
|
230 |
+
|
231 |
+
if ($attempts >= AAM_Core_Config::get('security.login.attempts', 20)) {
|
232 |
+
wp_safe_redirect(site_url('index.php'));
|
233 |
+
exit;
|
234 |
+
} else {
|
235 |
+
set_transient('aam-login-attemtps', $attempts, $timeout);
|
236 |
+
}
|
237 |
+
}
|
238 |
+
|
239 |
+
/**
|
240 |
+
*
|
241 |
+
* @param type $log
|
242 |
+
* @param type $pwd
|
243 |
+
* @throws Exception
|
244 |
+
*/
|
245 |
+
protected function validate($log, $pwd) {
|
246 |
+
if (empty($log) || empty($pwd)) {
|
247 |
+
Throw new Exception(__('Username and password are required', AAM_KEY));
|
248 |
+
}
|
249 |
+
}
|
250 |
+
|
251 |
+
/**
|
252 |
+
*
|
253 |
+
* @param type $log
|
254 |
+
* @return boolean
|
255 |
+
*/
|
256 |
+
protected function checkUserSSL($log) {
|
257 |
+
$secure = false;
|
258 |
+
$user = get_user_by((strpos($log, '@') ? 'email' : 'login'), $log);
|
259 |
+
|
260 |
+
if ($user) {
|
261 |
+
if (!force_ssl_admin() && get_user_option('use_ssl', $user->ID)) {
|
262 |
+
$secure = true;
|
263 |
+
force_ssl_admin(true);
|
264 |
+
}
|
265 |
+
}
|
266 |
+
|
267 |
+
return $secure;
|
268 |
+
}
|
269 |
+
|
270 |
+
/**
|
271 |
+
*
|
272 |
+
* @return type
|
273 |
+
*/
|
274 |
+
public static function getInstance() {
|
275 |
+
if (is_null(self::$instance)) {
|
276 |
+
self::$instance = new self;
|
277 |
+
}
|
278 |
+
|
279 |
+
return self::$instance;
|
280 |
+
}
|
281 |
+
|
282 |
+
/**
|
283 |
+
*
|
284 |
+
* @return type
|
285 |
+
*/
|
286 |
+
public static function bootstrap() {
|
287 |
+
return self::getInstance();
|
288 |
+
}
|
289 |
+
|
290 |
+
}
|
Application/Core/Media.php
CHANGED
@@ -73,7 +73,7 @@ class AAM_Core_Media {
|
|
73 |
*/
|
74 |
protected function initialize() {
|
75 |
$media = filter_input(INPUT_GET, 'aam-media');
|
76 |
-
$request = ($media != '1' ? $media : AAM_Core_Request::server('REQUEST_URI'));
|
77 |
$root = AAM_Core_Request::server('DOCUMENT_ROOT');
|
78 |
|
79 |
$this->request = str_replace('\\', '/', $root . $request);
|
73 |
*/
|
74 |
protected function initialize() {
|
75 |
$media = filter_input(INPUT_GET, 'aam-media');
|
76 |
+
$request = ($media != '1' ? $media : urldecode(AAM_Core_Request::server('REQUEST_URI')));
|
77 |
$root = AAM_Core_Request::server('DOCUMENT_ROOT');
|
78 |
|
79 |
$this->request = str_replace('\\', '/', $root . $request);
|
Application/Frontend/Manager.php
CHANGED
@@ -38,14 +38,13 @@ class AAM_Frontend_Manager {
|
|
38 |
*/
|
39 |
public function __construct() {
|
40 |
if (AAM_Core_Config::get('frontend-access-control', true)) {
|
41 |
-
//login hook
|
42 |
-
add_action('wp_login', array($this, 'login'), 10, 2);
|
43 |
-
add_action('wp_logout', array($this, 'logout'));
|
44 |
-
|
45 |
//control WordPress frontend
|
46 |
add_action('wp', array($this, 'wp'), 999);
|
47 |
add_action('404_template', array($this, 'themeRedirect'), 999);
|
48 |
|
|
|
|
|
|
|
49 |
if (AAM_Core_Config::get('check-post-visibility', true)) {
|
50 |
//filter navigation pages & taxonomies
|
51 |
add_filter('get_pages', array($this, 'thePosts'), 999);
|
@@ -60,8 +59,6 @@ class AAM_Frontend_Manager {
|
|
60 |
add_filter('sidebars_widgets', array($this, 'widgetFilter'), 999);
|
61 |
//get control over commenting stuff
|
62 |
add_filter('comments_open', array($this, 'commentOpen'), 10, 2);
|
63 |
-
//user login control
|
64 |
-
add_filter('wp_authenticate_user', array($this, 'authenticate'), 1, 2);
|
65 |
|
66 |
//password protected filter
|
67 |
add_filter('post_password_required', array($this, 'isProtected'), 10, 2);
|
@@ -75,9 +72,6 @@ class AAM_Frontend_Manager {
|
|
75 |
//core AAM filter
|
76 |
add_filter('aam-object-filter', array($this, 'getObject'), 10, 4);
|
77 |
|
78 |
-
//login process
|
79 |
-
add_filter('login_message', array($this, 'loginMessage'));
|
80 |
-
|
81 |
//manage password check expiration
|
82 |
add_filter('post_password_expires', array($this, 'postPasswordExpires'));
|
83 |
|
@@ -85,12 +79,12 @@ class AAM_Frontend_Manager {
|
|
85 |
$this->checkAdminBar();
|
86 |
}
|
87 |
|
|
|
|
|
|
|
88 |
if (AAM_Core_Request::get('action') == 'aam-auth') {
|
89 |
$this->doubleAuthentication();
|
90 |
}
|
91 |
-
|
92 |
-
//security controls
|
93 |
-
add_action('login_form_login', array($this, 'loginSubmit'), 1);
|
94 |
}
|
95 |
|
96 |
/**
|
@@ -110,20 +104,28 @@ class AAM_Frontend_Manager {
|
|
110 |
|
111 |
/**
|
112 |
*
|
113 |
-
* @param type $message
|
114 |
-
* @return type
|
115 |
*/
|
116 |
-
public function
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
125 |
|
126 |
-
|
127 |
}
|
128 |
|
129 |
/**
|
@@ -153,178 +155,6 @@ class AAM_Frontend_Manager {
|
|
153 |
return $object;
|
154 |
}
|
155 |
|
156 |
-
/**
|
157 |
-
*
|
158 |
-
*/
|
159 |
-
public function loginSubmit() {
|
160 |
-
//Login Timeout
|
161 |
-
if (AAM_Core_Config::get('login-timeout', false)) {
|
162 |
-
@sleep(intval(AAM_Core_Config::get('security.login.timeout', 1)));
|
163 |
-
}
|
164 |
-
|
165 |
-
//Brute Force Lockout
|
166 |
-
if (AAM_Core_Config::get('brute-force-lockout', false)) {
|
167 |
-
$this->updateLoginCounter(1);
|
168 |
-
}
|
169 |
-
}
|
170 |
-
|
171 |
-
/**
|
172 |
-
*
|
173 |
-
* @param type $username
|
174 |
-
* @param type $user
|
175 |
-
*/
|
176 |
-
public function login($username, $user = null) {
|
177 |
-
if (is_a($user, 'WP_User')) {
|
178 |
-
$this->updateLoginCounter(-1);
|
179 |
-
|
180 |
-
AAM_Core_API::deleteOption('aam-user-switch-' . $user->ID);
|
181 |
-
|
182 |
-
$subject = new AAM_Core_Subject_User($user->ID);
|
183 |
-
$object = $subject->getObject('loginRedirect');
|
184 |
-
|
185 |
-
//if Login redirect is defined
|
186 |
-
$type = $object->get('login.redirect.type');
|
187 |
-
|
188 |
-
if (!empty($type) && $type !== 'default') {
|
189 |
-
$redirect = $object->get("login.redirect.{$type}");
|
190 |
-
AAM_Core_API::redirect($redirect);
|
191 |
-
}
|
192 |
-
}
|
193 |
-
}
|
194 |
-
|
195 |
-
/**
|
196 |
-
*
|
197 |
-
*/
|
198 |
-
public function logout() {
|
199 |
-
$object = AAM::getUser()->getObject('logoutRedirect');
|
200 |
-
$type = $object->get('logout.redirect.type');
|
201 |
-
|
202 |
-
if (!empty($type) && $type !== 'default') {
|
203 |
-
$redirect = $object->get("logout.redirect.{$type}");
|
204 |
-
AAM_Core_API::redirect($redirect);
|
205 |
-
}
|
206 |
-
}
|
207 |
-
|
208 |
-
/**
|
209 |
-
*
|
210 |
-
* @param type $increment
|
211 |
-
*/
|
212 |
-
protected function updateLoginCounter($increment) {
|
213 |
-
$attempts = get_transient('aam-login-attemtps');
|
214 |
-
|
215 |
-
if ($attempts !== false) {
|
216 |
-
$timeout = get_option('_transient_timeout_aam-login-attemtps') - time();
|
217 |
-
$attempts = intval($attempts) + $increment;
|
218 |
-
} else {
|
219 |
-
$attempts = 1;
|
220 |
-
$timeout = strtotime(
|
221 |
-
'+' . AAM_Core_Config::get('security.login.period', '2 minutes')
|
222 |
-
) - time();
|
223 |
-
}
|
224 |
-
|
225 |
-
if ($attempts >= AAM_Core_Config::get('security.login.attempts', 20)) {
|
226 |
-
wp_safe_redirect(site_url('index.php'));
|
227 |
-
exit;
|
228 |
-
} else {
|
229 |
-
set_transient('aam-login-attemtps', $attempts, $timeout);
|
230 |
-
}
|
231 |
-
}
|
232 |
-
|
233 |
-
/**
|
234 |
-
* Control User Block flag
|
235 |
-
*
|
236 |
-
* @param WP_Error $user
|
237 |
-
*
|
238 |
-
* @return WP_Error|WP_User
|
239 |
-
*
|
240 |
-
* @access public
|
241 |
-
*/
|
242 |
-
public function authenticate($user) {
|
243 |
-
if (is_a($user, 'WP_User') && $user->user_status == 1) {
|
244 |
-
$user = new WP_Error();
|
245 |
-
|
246 |
-
$message = '[ERROR]: User is locked. Please contact your website ';
|
247 |
-
$message .= 'administrator.';
|
248 |
-
|
249 |
-
$user->add(
|
250 |
-
'authentication_failed',
|
251 |
-
AAM_Backend_View_Helper::preparePhrase($message, 'strong')
|
252 |
-
);
|
253 |
-
} elseif (AAM_Core_Config::get('login-ip-track', false)) {
|
254 |
-
$baseIp = get_user_meta($user->ID, 'aam-login-ip', true);
|
255 |
-
$token = get_transient("aam-user-{$user->ID}-login-token");
|
256 |
-
$ip = AAM_Core_Request::server('REMOTE_ADDR');
|
257 |
-
$utoken = AAM_Core_Request::cookie('aam-login-token');
|
258 |
-
|
259 |
-
if (empty($baseIp)) {
|
260 |
-
update_user_meta($user->ID, 'aam-login-ip', $ip);
|
261 |
-
}
|
262 |
-
|
263 |
-
if (empty($token)) {
|
264 |
-
$token = sha1(srand(time()));
|
265 |
-
setcookie('aam-login-token', $token, time() + 1209600 , '/');
|
266 |
-
set_transient("aam-user-{$user->ID}-login-token", $token, 1209600);
|
267 |
-
}
|
268 |
-
|
269 |
-
if (!empty($baseIp) && ($token != $utoken) && ($baseIp != $ip)) {
|
270 |
-
$key = get_password_reset_key($user);
|
271 |
-
update_user_meta($user->ID, 'aam-login-key', $key);
|
272 |
-
|
273 |
-
if ( is_multisite() ) {
|
274 |
-
$blogname = get_network()->site_name;
|
275 |
-
} else {
|
276 |
-
$blogname = wp_specialchars_decode(get_option('blogname'), ENT_QUOTES);
|
277 |
-
}
|
278 |
-
|
279 |
-
$title = sprintf( __('[%s] Double Authentication'), $blogname );
|
280 |
-
|
281 |
-
$message = sprintf(__('Someone was trying to login from the different IP address %s:'), $ip) . "\r\n\r\n";
|
282 |
-
$message .= sprintf(__('Website: %s'), network_home_url( '/' )) . "\r\n";
|
283 |
-
$message .= sprintf(__('Username: %s'), $user->user_login) . "\r\n\r\n";
|
284 |
-
$message .= __('Visit the following address to authorize the login:') . "\r\n\r\n";
|
285 |
-
$message .= '<' . network_site_url("index.php?action=aam-auth&key={$key}&login={$user->user_login}") . ">\r\n";
|
286 |
-
|
287 |
-
wp_mail($user->user_email, $title, $message);
|
288 |
-
|
289 |
-
$user = new WP_Error();
|
290 |
-
|
291 |
-
$message = '[ERROR]: Double authentication is required. ';
|
292 |
-
$message .= 'Please check your email or enter username and ';
|
293 |
-
$message .= 'password again to resend the email.';
|
294 |
-
|
295 |
-
$user->add(
|
296 |
-
'authentication_failed',
|
297 |
-
AAM_Backend_View_Helper::preparePhrase($message, 'strong')
|
298 |
-
);
|
299 |
-
}
|
300 |
-
}
|
301 |
-
|
302 |
-
return $user;
|
303 |
-
}
|
304 |
-
|
305 |
-
/**
|
306 |
-
*
|
307 |
-
*/
|
308 |
-
protected function doubleAuthentication() {
|
309 |
-
$login = AAM_Core_Request::get('login');
|
310 |
-
$key = AAM_Core_Request::get('key');
|
311 |
-
$user = get_user_by('login', $login);
|
312 |
-
|
313 |
-
if (is_a($user, 'WP_User')) {
|
314 |
-
$stored = get_user_meta($user->ID, 'aam-login-key', true);
|
315 |
-
|
316 |
-
if ($stored == $key) {
|
317 |
-
update_user_meta($user->ID, 'aam-login-ip', AAM_Core_Request::server('REMOTE_ADDR'));
|
318 |
-
delete_user_meta($user->ID, 'aam-login-key');
|
319 |
-
wp_safe_redirect(site_url('wp-login.php'));
|
320 |
-
exit;
|
321 |
-
}
|
322 |
-
}
|
323 |
-
|
324 |
-
wp_safe_redirect(site_url('index.php'));
|
325 |
-
exit;
|
326 |
-
}
|
327 |
-
|
328 |
/**
|
329 |
*
|
330 |
* @param type $response
|
38 |
*/
|
39 |
public function __construct() {
|
40 |
if (AAM_Core_Config::get('frontend-access-control', true)) {
|
|
|
|
|
|
|
|
|
41 |
//control WordPress frontend
|
42 |
add_action('wp', array($this, 'wp'), 999);
|
43 |
add_action('404_template', array($this, 'themeRedirect'), 999);
|
44 |
|
45 |
+
//support login widget
|
46 |
+
add_action('wp_enqueue_scripts', array($this, 'printJavascript'));
|
47 |
+
|
48 |
if (AAM_Core_Config::get('check-post-visibility', true)) {
|
49 |
//filter navigation pages & taxonomies
|
50 |
add_filter('get_pages', array($this, 'thePosts'), 999);
|
59 |
add_filter('sidebars_widgets', array($this, 'widgetFilter'), 999);
|
60 |
//get control over commenting stuff
|
61 |
add_filter('comments_open', array($this, 'commentOpen'), 10, 2);
|
|
|
|
|
62 |
|
63 |
//password protected filter
|
64 |
add_filter('post_password_required', array($this, 'isProtected'), 10, 2);
|
72 |
//core AAM filter
|
73 |
add_filter('aam-object-filter', array($this, 'getObject'), 10, 4);
|
74 |
|
|
|
|
|
|
|
75 |
//manage password check expiration
|
76 |
add_filter('post_password_expires', array($this, 'postPasswordExpires'));
|
77 |
|
79 |
$this->checkAdminBar();
|
80 |
}
|
81 |
|
82 |
+
//register login widget
|
83 |
+
add_action('widgets_init', array($this, 'registerLoginWidget'));
|
84 |
+
|
85 |
if (AAM_Core_Request::get('action') == 'aam-auth') {
|
86 |
$this->doubleAuthentication();
|
87 |
}
|
|
|
|
|
|
|
88 |
}
|
89 |
|
90 |
/**
|
104 |
|
105 |
/**
|
106 |
*
|
|
|
|
|
107 |
*/
|
108 |
+
public function registerLoginWidget() {
|
109 |
+
register_widget('AAM_Backend_Widget_Login');
|
110 |
+
}
|
111 |
+
|
112 |
+
/**
|
113 |
+
* Print javascript libraries
|
114 |
+
*
|
115 |
+
* @return void
|
116 |
+
*
|
117 |
+
* @access public
|
118 |
+
*/
|
119 |
+
public function printJavascript() {
|
120 |
+
wp_enqueue_script('aam-login', AAM_MEDIA . '/js/aam-login.js');
|
121 |
+
|
122 |
+
//add plugin localization
|
123 |
+
$locals = array(
|
124 |
+
'nonce' => wp_create_nonce('aam_ajax'),
|
125 |
+
'ajaxurl' => admin_url('admin-ajax.php')
|
126 |
+
);
|
127 |
|
128 |
+
wp_localize_script('aam-login', 'aamLocal', $locals);
|
129 |
}
|
130 |
|
131 |
/**
|
155 |
return $object;
|
156 |
}
|
157 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
158 |
/**
|
159 |
*
|
160 |
* @param type $response
|
Application/Frontend/phtml/login.phtml
ADDED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if (defined('AAM_KEY')) { ?>
|
2 |
+
<?php if(!is_user_logged_in()) { ?>
|
3 |
+
<div id="<?php echo $this->args['id'] . '-error'; ?>" style="display: none; margin-bottom: 15px; border-left: 4px solid #dc3232; padding: 12px;"></div>
|
4 |
+
|
5 |
+
<div id="<?php echo $this->args['id'] . '-loginform'; ?>">
|
6 |
+
<p>
|
7 |
+
<label for="user_login">Username or Email Address<br>
|
8 |
+
<input id="<?php echo $this->args['id'] . '-log'; ?>" class="input" value="" size="20" type="text" />
|
9 |
+
</label>
|
10 |
+
</p>
|
11 |
+
|
12 |
+
<p>
|
13 |
+
<label for="user_pass">Password<br>
|
14 |
+
<input id="<?php echo $this->args['id'] . '-pwd'; ?>" class="input" value="" size="20" type="password" />
|
15 |
+
</label>
|
16 |
+
</p>
|
17 |
+
|
18 |
+
<p class="forgetmenot">
|
19 |
+
<label for="rememberme">
|
20 |
+
<input id="<?php echo $this->args['id'] . '-rememberme'; ?>" value="forever" type="checkbox"/> Remember Me
|
21 |
+
</label>
|
22 |
+
</p>
|
23 |
+
|
24 |
+
<p class="submit">
|
25 |
+
<button class="button button-primary button-large aam-login-submit" data-prefix="<?php echo $this->args['id'] . '-'; ?>">Login</button>
|
26 |
+
<input id="<?php echo $this->args['id'] . '-redirect'; ?>" value="<?php echo $this->args['redirect']; ?>" type="hidden" />
|
27 |
+
</p>
|
28 |
+
</div>
|
29 |
+
|
30 |
+
<p id="<?php echo $this->args['id'] . '-nav'; ?>">
|
31 |
+
<a href="<?php echo esc_url( wp_lostpassword_url() ); ?>">Lost your password?</a>
|
32 |
+
</p>
|
33 |
+
<?php } else { ?>
|
34 |
+
<div style="text-align: center;">
|
35 |
+
<div style="padding: 30px;">
|
36 |
+
<h3><?php echo str_replace('%username%', AAM::getUser()->display_name, $this->args['user-title']); ?></h3>
|
37 |
+
<?php echo get_avatar(AAM::getUser()->ID, "200"); ?>
|
38 |
+
</div>
|
39 |
+
<div>
|
40 |
+
<a href="<?php echo esc_url(get_admin_url()); ?>"><?php echo __('Dashboard'); ?></a> |
|
41 |
+
<a href="<?php echo esc_url(get_admin_url(null, 'profile.php')); ?>"><?php echo __('Edit My Profile'); ?></a> |
|
42 |
+
<a href="<?php echo esc_url(wp_logout_url()); ?>"><?php echo __('Log Out'); ?></a>
|
43 |
+
</div>
|
44 |
+
</div>
|
45 |
+
<?php }
|
46 |
+
}
|
Application/Shortcode/Strategy/Login.php
CHANGED
@@ -8,7 +8,7 @@
|
|
8 |
*/
|
9 |
|
10 |
/**
|
11 |
-
* AAM shortcode strategy for login
|
12 |
*
|
13 |
* @package AAM
|
14 |
* @author Vasyl Martyniuk <vasyl@vasyltech.com>
|
@@ -31,9 +31,10 @@ class AAM_Shortcode_Strategy_Login implements AAM_Shortcode_Strategy_Interface {
|
|
31 |
* Initialize shortcode decorator
|
32 |
*
|
33 |
* Expecting attributes in $args are:
|
34 |
-
* "
|
35 |
-
* "
|
36 |
-
* "
|
|
|
37 |
*
|
38 |
* @param type $args
|
39 |
* @param type $content
|
@@ -48,20 +49,29 @@ class AAM_Shortcode_Strategy_Login implements AAM_Shortcode_Strategy_Interface {
|
|
48 |
*
|
49 |
*/
|
50 |
public function run() {
|
51 |
-
$
|
52 |
-
|
53 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
54 |
|
55 |
if (isset($this->args['callback'])) {
|
56 |
-
$
|
57 |
} else {
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
|
|
|
|
|
|
62 |
}
|
63 |
|
64 |
-
return $
|
65 |
}
|
66 |
|
67 |
/**
|
8 |
*/
|
9 |
|
10 |
/**
|
11 |
+
* AAM shortcode strategy for login form
|
12 |
*
|
13 |
* @package AAM
|
14 |
* @author Vasyl Martyniuk <vasyl@vasyltech.com>
|
31 |
* Initialize shortcode decorator
|
32 |
*
|
33 |
* Expecting attributes in $args are:
|
34 |
+
* "id" => unique form Id
|
35 |
+
* "user-title" => Logged in user title
|
36 |
+
* "redirect" => Redirect to URL
|
37 |
+
* "callback" => callback function that returns the login button
|
38 |
*
|
39 |
* @param type $args
|
40 |
* @param type $content
|
49 |
*
|
50 |
*/
|
51 |
public function run() {
|
52 |
+
$this->args['id'] = isset($this->args['id']) ? $this->args['id'] : uniqid();
|
53 |
+
|
54 |
+
if (empty($this->args['user-title'])) {
|
55 |
+
$this->args['user-title'] = __('Howdy, %username%', AAM_KEY);
|
56 |
+
}
|
57 |
+
|
58 |
+
if (empty($this->args['redirect'])) {
|
59 |
+
$this->args['redirect'] = AAM_Core_Request::get('redirect_to');
|
60 |
+
}
|
61 |
|
62 |
if (isset($this->args['callback'])) {
|
63 |
+
$content = call_user_func($this->args['callback'], $this);
|
64 |
} else {
|
65 |
+
ob_start();
|
66 |
+
require AAM_Core_Config::get(
|
67 |
+
'login.shortcode.template',
|
68 |
+
dirname(__DIR__) . '/../Frontend/phtml/login.phtml'
|
69 |
+
);
|
70 |
+
$content = ob_get_contents();
|
71 |
+
ob_end_clean();
|
72 |
}
|
73 |
|
74 |
+
return $content;
|
75 |
}
|
76 |
|
77 |
/**
|
Application/Shortcode/Strategy/LoginRedirect.php
ADDED
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* ======================================================================
|
5 |
+
* LICENSE: This file is subject to the terms and conditions defined in *
|
6 |
+
* file 'license.txt', which is part of this source code package. *
|
7 |
+
* ======================================================================
|
8 |
+
*/
|
9 |
+
|
10 |
+
/**
|
11 |
+
* AAM shortcode strategy for login button
|
12 |
+
*
|
13 |
+
* @package AAM
|
14 |
+
* @author Vasyl Martyniuk <vasyl@vasyltech.com>
|
15 |
+
*/
|
16 |
+
class AAM_Shortcode_Strategy_LoginRedirect implements AAM_Shortcode_Strategy_Interface {
|
17 |
+
|
18 |
+
/**
|
19 |
+
*
|
20 |
+
* @var type
|
21 |
+
*/
|
22 |
+
protected $args;
|
23 |
+
|
24 |
+
/**
|
25 |
+
*
|
26 |
+
* @var type
|
27 |
+
*/
|
28 |
+
protected $content;
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Initialize shortcode decorator
|
32 |
+
*
|
33 |
+
* Expecting attributes in $args are:
|
34 |
+
* "class" => CSS class for login button
|
35 |
+
* "label" => Login button label
|
36 |
+
* "callback" => callback function that returns the login button
|
37 |
+
*
|
38 |
+
* @param type $args
|
39 |
+
* @param type $content
|
40 |
+
*/
|
41 |
+
public function __construct($args, $content) {
|
42 |
+
$this->args = $args;
|
43 |
+
$this->content = $content;
|
44 |
+
}
|
45 |
+
|
46 |
+
/**
|
47 |
+
* Process shortcode
|
48 |
+
*
|
49 |
+
*/
|
50 |
+
public function run() {
|
51 |
+
$redirect = AAM_Core_Request::server('REQUEST_URI');
|
52 |
+
$class = (isset($this->args['class']) ? $this->args['class'] : '');
|
53 |
+
$label = (isset($this->args['label']) ? $this->args['label'] : 'Login');
|
54 |
+
|
55 |
+
if (isset($this->args['callback'])) {
|
56 |
+
$button = call_user_func($this->args['callback'], $this);
|
57 |
+
} else {
|
58 |
+
$url = add_query_arg(
|
59 |
+
'reason',
|
60 |
+
'access-denied',
|
61 |
+
wp_login_url($redirect)
|
62 |
+
);
|
63 |
+
|
64 |
+
$button = '<a href="' . $url . '" ';
|
65 |
+
$button .= 'class="' . $class . '">' . $label . '</a>';
|
66 |
+
}
|
67 |
+
|
68 |
+
return $button;
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
*
|
73 |
+
* @return type
|
74 |
+
*/
|
75 |
+
public function getArgs() {
|
76 |
+
return $this->args;
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
*
|
81 |
+
* @return type
|
82 |
+
*/
|
83 |
+
public function getContent() {
|
84 |
+
return $this->content;
|
85 |
+
}
|
86 |
+
|
87 |
+
}
|
aam.php
CHANGED
@@ -3,7 +3,7 @@
|
|
3 |
/**
|
4 |
Plugin Name: Advanced Access Manager
|
5 |
Description: All you need to manage access to your WordPress website
|
6 |
-
Version: 4.9.
|
7 |
Author: Vasyl Martyniuk <vasyl@vasyltech.com>
|
8 |
Author URI: https://vasyltech.com
|
9 |
|
@@ -141,6 +141,9 @@ class AAM {
|
|
141 |
|
142 |
//load media control
|
143 |
AAM_Core_Media::bootstrap();
|
|
|
|
|
|
|
144 |
}
|
145 |
|
146 |
return self::$_instance;
|
3 |
/**
|
4 |
Plugin Name: Advanced Access Manager
|
5 |
Description: All you need to manage access to your WordPress website
|
6 |
+
Version: 4.9.2
|
7 |
Author: Vasyl Martyniuk <vasyl@vasyltech.com>
|
8 |
Author URI: https://vasyltech.com
|
9 |
|
141 |
|
142 |
//load media control
|
143 |
AAM_Core_Media::bootstrap();
|
144 |
+
|
145 |
+
//login control
|
146 |
+
AAM_Core_Login::bootstrap();
|
147 |
}
|
148 |
|
149 |
return self::$_instance;
|
media/js/aam-login.js
ADDED
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
if (typeof jQuery !== 'undefined') {
|
2 |
+
(function ($) {
|
3 |
+
$('document').ready(function () {
|
4 |
+
$('.aam-login-submit').each(function () {
|
5 |
+
$(this).bind('click', function () {
|
6 |
+
var button = $(this);
|
7 |
+
var prefix = $(this).data('prefix');
|
8 |
+
|
9 |
+
var log = $.trim($('#' + prefix + 'log').val());
|
10 |
+
var pwd = $('#' + prefix + 'pwd').val();
|
11 |
+
|
12 |
+
if (log && pwd) {
|
13 |
+
$('#' + prefix + 'error').css('display', 'none');
|
14 |
+
|
15 |
+
$.ajax(aamLocal.ajaxurl, {
|
16 |
+
data: {
|
17 |
+
log: log,
|
18 |
+
pwd: pwd,
|
19 |
+
action: 'aamlogin',
|
20 |
+
redirect: $('#' + prefix + 'redirect').val(),
|
21 |
+
rememberme: ($('#' + prefix + 'rememberme').prop('checked') ? 1 : 0)
|
22 |
+
},
|
23 |
+
dataType: 'json',
|
24 |
+
type: 'POST',
|
25 |
+
beforeSend: function() {
|
26 |
+
button.attr({
|
27 |
+
disabled: 'disabled',
|
28 |
+
'data-original-label': button.val()
|
29 |
+
}).val('Wait...');
|
30 |
+
},
|
31 |
+
success: function(response) {
|
32 |
+
console.log(response);
|
33 |
+
if (response.status === "success") {
|
34 |
+
if (response.redirect) {
|
35 |
+
location.href = response.redirect;
|
36 |
+
}
|
37 |
+
} else {
|
38 |
+
$('#' + prefix + 'error').html(
|
39 |
+
response.reason
|
40 |
+
).css('display', 'block');
|
41 |
+
}
|
42 |
+
},
|
43 |
+
error: function() {
|
44 |
+
$('#' + prefix + 'error').html(
|
45 |
+
'<strong>ERROR:</strong> Unexpected error.'
|
46 |
+
).css('display', 'block');
|
47 |
+
},
|
48 |
+
complete: function() {
|
49 |
+
button.attr({
|
50 |
+
disabled: null
|
51 |
+
}).val(button.attr('data-original-label'));
|
52 |
+
}
|
53 |
+
});
|
54 |
+
|
55 |
+
} else {
|
56 |
+
$('#' + prefix + 'error').html(
|
57 |
+
'<strong>ERROR:</strong> Username and password are required.'
|
58 |
+
).css('display', 'block');
|
59 |
+
}
|
60 |
+
});
|
61 |
+
});
|
62 |
+
});
|
63 |
+
})(jQuery);
|
64 |
+
} else {
|
65 |
+
console.log(
|
66 |
+
'AAM requires jQuery library in order for login widget to work'
|
67 |
+
);
|
68 |
+
}
|
readme.txt
CHANGED
@@ -3,7 +3,7 @@ Contributors: vasyltech
|
|
3 |
Tags: access, role, user, capability, page access, post access, comments, security, login redirect, brute force attack, double authentication, membership, backend lockdown, wp-admin, 404, activity tracking
|
4 |
Requires at least: 3.8
|
5 |
Tested up to: 4.8.1
|
6 |
-
Stable tag: 4.9.
|
7 |
|
8 |
Manage access to your website for any user, role or visitors for both frontend and backend.
|
9 |
|
@@ -24,6 +24,9 @@ also manage access to categories, custom hierarchical taxonomies or setup the de
|
|
24 |
access to all posts and categories. Refer to [How to manage WordPress post and category access](https://aamplugin.com/help/how-to-manage-wordpress-post-and-category-access)
|
25 |
to learn more about this feature.
|
26 |
|
|
|
|
|
|
|
27 |
= Track Any User Activities =
|
28 |
Track any user or visitor activities on your website with AAM User Activity extension. For more information about this
|
29 |
feature refer to the [How to track any WordPress user activity](https://aamplugin.com/help/how-to-track-any-wordpress-user-activity)
|
@@ -110,6 +113,11 @@ Check our [help page](https://aamplugin.com/help) to find out more about AAM.
|
|
110 |
|
111 |
== Changelog ==
|
112 |
|
|
|
|
|
|
|
|
|
|
|
113 |
= 4.9.1 =
|
114 |
* Improved UI
|
115 |
* Improved [aam] shortcode
|
3 |
Tags: access, role, user, capability, page access, post access, comments, security, login redirect, brute force attack, double authentication, membership, backend lockdown, wp-admin, 404, activity tracking
|
4 |
Requires at least: 3.8
|
5 |
Tested up to: 4.8.1
|
6 |
+
Stable tag: 4.9.2
|
7 |
|
8 |
Manage access to your website for any user, role or visitors for both frontend and backend.
|
9 |
|
24 |
access to all posts and categories. Refer to [How to manage WordPress post and category access](https://aamplugin.com/help/how-to-manage-wordpress-post-and-category-access)
|
25 |
to learn more about this feature.
|
26 |
|
27 |
+
= AJAX secure login widget & shotcode =
|
28 |
+
Use secure AJAX login for your site with easy-to-use widget and shortcode. For more information check [How does AAM Secure Login works](https://aamplugin.com/help/how-does-aam-secure-login-works)
|
29 |
+
|
30 |
= Track Any User Activities =
|
31 |
Track any user or visitor activities on your website with AAM User Activity extension. For more information about this
|
32 |
feature refer to the [How to track any WordPress user activity](https://aamplugin.com/help/how-to-track-any-wordpress-user-activity)
|
113 |
|
114 |
== Changelog ==
|
115 |
|
116 |
+
= 4.9.2 =
|
117 |
+
* Fixed the bug with AAM media control for files with special characters
|
118 |
+
* Added secure login widget and shortcode
|
119 |
+
* Deprecated Security feature
|
120 |
+
|
121 |
= 4.9.1 =
|
122 |
* Improved UI
|
123 |
* Improved [aam] shortcode
|