Version Description
- Fixed the bug that triggers PHP warnings when blocked user is trying to login
- Fixed the bug with get current post method in the core API
- WARNING Experimental approach! to the post access that enormously improve AAM performance
- Added custom capability "edit_permalink" that control ability to edit post permalink
Download this release
Release Info
Developer | vasyl_m |
Plugin | Advanced Access Manager |
Version | 5.3.2 |
Comparing to | |
See all releases |
Code changes from version 5.3.1 to 5.3.2
- Application/Backend/Feature/Settings/Tools.php +2 -2
- Application/Backend/Filter.php +0 -51
- Application/Backend/Manager.php +24 -0
- Application/Core/API.php +4 -43
- Application/Core/Gateway.php +12 -0
- Application/Core/Object/Cache.php +1 -26
- Application/Core/Object/Post.php +0 -31
- Application/Core/Object/Visibility.php +148 -0
- Application/Core/Subject/User.php +1 -17
- Application/Extension/List.php +3 -3
- Application/Frontend/Filter.php +40 -2
- Application/Shared/Manager.php +72 -71
- aam.php +9 -2
- media/js/aam.js +2 -2
- readme.txt +7 -1
Application/Backend/Feature/Settings/Tools.php
CHANGED
@@ -36,7 +36,7 @@ class AAM_Backend_Feature_Settings_Tools extends AAM_Backend_Feature_Abstract {
|
|
36 |
'content' => base64_encode(json_encode($exporter->run()))
|
37 |
));
|
38 |
}
|
39 |
-
|
40 |
/**
|
41 |
*
|
42 |
* @return type
|
@@ -108,4 +108,4 @@ class AAM_Backend_Feature_Settings_Tools extends AAM_Backend_Feature_Abstract {
|
|
108 |
));
|
109 |
}
|
110 |
|
111 |
-
}
|
36 |
'content' => base64_encode(json_encode($exporter->run()))
|
37 |
));
|
38 |
}
|
39 |
+
|
40 |
/**
|
41 |
*
|
42 |
* @return type
|
108 |
));
|
109 |
}
|
110 |
|
111 |
+
}
|
Application/Backend/Filter.php
CHANGED
@@ -24,11 +24,6 @@ class AAM_Backend_Filter {
|
|
24 |
*/
|
25 |
private static $_instance = null;
|
26 |
|
27 |
-
/**
|
28 |
-
* pre_get_posts flag
|
29 |
-
*/
|
30 |
-
protected $skip = false;
|
31 |
-
|
32 |
/**
|
33 |
* Initialize backend filters
|
34 |
*
|
@@ -58,11 +53,6 @@ class AAM_Backend_Filter {
|
|
58 |
//default category filder
|
59 |
add_filter('pre_option_default_category', array($this, 'filterDefaultCategory'));
|
60 |
|
61 |
-
//add post filter for LIST restriction
|
62 |
-
if (!AAM::isAAM() && AAM_Core_Config::get('core.settings.checkPostVisibility', true)) {
|
63 |
-
add_filter('found_posts', array($this, 'filterPostCount'), 999, 2);
|
64 |
-
}
|
65 |
-
|
66 |
add_action('pre_post_update', array($this, 'prePostUpdate'), 10, 2);
|
67 |
|
68 |
//user/role filters
|
@@ -285,47 +275,6 @@ class AAM_Backend_Filter {
|
|
285 |
return ($default ? $default : $category);
|
286 |
}
|
287 |
|
288 |
-
/**
|
289 |
-
* Filter post count for pagination
|
290 |
-
*
|
291 |
-
* @param int $counter
|
292 |
-
* @param WP_Query $query
|
293 |
-
*
|
294 |
-
* @return array
|
295 |
-
*
|
296 |
-
* @access public
|
297 |
-
*/
|
298 |
-
public function filterPostCount($counter, $query) {
|
299 |
-
$filtered = array();
|
300 |
-
$subject = AAM::getUser();
|
301 |
-
|
302 |
-
foreach ($query->posts as $post) {
|
303 |
-
if (isset($post->post_type)) {
|
304 |
-
$type = $post->post_type;
|
305 |
-
} else {
|
306 |
-
$type = AAM_Core_API::getQueryPostType($query);
|
307 |
-
}
|
308 |
-
|
309 |
-
$object = $subject->getObject(
|
310 |
-
'post', (is_a($post, 'WP_Post') ? $post->ID : $post)
|
311 |
-
);
|
312 |
-
|
313 |
-
$hidden = $object->get('backend.hidden');
|
314 |
-
$list = $object->get('backend.list');
|
315 |
-
|
316 |
-
if (empty($hidden) && empty($list)) {
|
317 |
-
$filtered[] = $post;
|
318 |
-
} else {
|
319 |
-
$counter--;
|
320 |
-
$query->post_count--;
|
321 |
-
}
|
322 |
-
}
|
323 |
-
|
324 |
-
$query->posts = $filtered;
|
325 |
-
|
326 |
-
return $counter;
|
327 |
-
}
|
328 |
-
|
329 |
/**
|
330 |
* Post update hook
|
331 |
*
|
24 |
*/
|
25 |
private static $_instance = null;
|
26 |
|
|
|
|
|
|
|
|
|
|
|
27 |
/**
|
28 |
* Initialize backend filters
|
29 |
*
|
53 |
//default category filder
|
54 |
add_filter('pre_option_default_category', array($this, 'filterDefaultCategory'));
|
55 |
|
|
|
|
|
|
|
|
|
|
|
56 |
add_action('pre_post_update', array($this, 'prePostUpdate'), 10, 2);
|
57 |
|
58 |
//user/role filters
|
275 |
return ($default ? $default : $category);
|
276 |
}
|
277 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
278 |
/**
|
279 |
* Post update hook
|
280 |
*
|
Application/Backend/Manager.php
CHANGED
@@ -54,6 +54,9 @@ class AAM_Backend_Manager {
|
|
54 |
//post title decorator
|
55 |
add_filter('the_title', array($this, 'theTitle'), 999, 2);
|
56 |
|
|
|
|
|
|
|
57 |
//screen options & contextual help hooks
|
58 |
add_filter('screen_options_show_screen', array($this, 'screenOptions'));
|
59 |
add_filter('contextual_help', array($this, 'helpOptions'), 10, 3);
|
@@ -151,6 +154,13 @@ class AAM_Backend_Manager {
|
|
151 |
'On ConfigPress tab, change [extention.directory] option to [core.extention.directory]', 'b', 'b'
|
152 |
);
|
153 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
154 |
}
|
155 |
|
156 |
/**
|
@@ -169,6 +179,20 @@ class AAM_Backend_Manager {
|
|
169 |
return $caps;
|
170 |
}
|
171 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
172 |
/**
|
173 |
* Profile updated hook
|
174 |
*
|
54 |
//post title decorator
|
55 |
add_filter('the_title', array($this, 'theTitle'), 999, 2);
|
56 |
|
57 |
+
//permalink manager
|
58 |
+
add_filter('get_sample_permalink_html', array($this, 'getPermalinkHtml'), 10, 5);
|
59 |
+
|
60 |
//screen options & contextual help hooks
|
61 |
add_filter('screen_options_show_screen', array($this, 'screenOptions'));
|
62 |
add_filter('contextual_help', array($this, 'helpOptions'), 10, 3);
|
154 |
'On ConfigPress tab, change [extention.directory] option to [core.extention.directory]', 'b', 'b'
|
155 |
);
|
156 |
}
|
157 |
+
|
158 |
+
$tmpl = AAM_Core_Config::get('login.shortcode.template', null);
|
159 |
+
if (!empty($tmpl)) {
|
160 |
+
AAM_Core_Console::add(
|
161 |
+
'On ConfigPress tab, change [login.shortcode.template] option to [feature.secureLogin.shortcode.template]', 'b', 'b'
|
162 |
+
);
|
163 |
+
}
|
164 |
}
|
165 |
|
166 |
/**
|
179 |
return $caps;
|
180 |
}
|
181 |
|
182 |
+
/**
|
183 |
+
*
|
184 |
+
* @param string $html
|
185 |
+
* @return string
|
186 |
+
*/
|
187 |
+
public function getPermalinkHtml($html) {
|
188 |
+
if (AAM_Core_API::capabilityExists('edit_permalink')
|
189 |
+
&& !AAM::getUser()->hasCapability('edit_permalink')) {
|
190 |
+
$html = '';
|
191 |
+
}
|
192 |
+
|
193 |
+
return $html;
|
194 |
+
}
|
195 |
+
|
196 |
/**
|
197 |
* Profile updated hook
|
198 |
*
|
Application/Core/API.php
CHANGED
@@ -368,49 +368,6 @@ final class AAM_Core_API {
|
|
368 |
return (!empty($version) ? $version : null);
|
369 |
}
|
370 |
|
371 |
-
/**
|
372 |
-
* Get filtered post list
|
373 |
-
*
|
374 |
-
* Return only posts that are restricted to LIST or LIST TO OTHERS for the
|
375 |
-
* current user. This function is shared by both frontend and backend
|
376 |
-
*
|
377 |
-
* @param WP_Query $query
|
378 |
-
* @param string $area
|
379 |
-
*
|
380 |
-
* @return array
|
381 |
-
*
|
382 |
-
* @access public
|
383 |
-
*/
|
384 |
-
public static function getFilteredPostList($query) {
|
385 |
-
$filtered = array();
|
386 |
-
|
387 |
-
$type = self::getQueryPostType($query);
|
388 |
-
$area = AAM_Core_Api_Area::get();
|
389 |
-
|
390 |
-
if ($type) {
|
391 |
-
$cache = AAM::getUser()->getObject('cache')->getMergedOption();
|
392 |
-
$posts = (isset($cache['post']) ? $cache['post'] : array());
|
393 |
-
|
394 |
-
foreach($posts as $id => $option) {
|
395 |
-
if (!empty($option["{$area}.list"])
|
396 |
-
|| !empty($option["{$area}.hidden"])) {
|
397 |
-
$filtered[] = $id;
|
398 |
-
}
|
399 |
-
}
|
400 |
-
}
|
401 |
-
|
402 |
-
if (is_single()) {
|
403 |
-
$post = self::getCurrentPost();
|
404 |
-
$in = ($post ? array_search($post->ID, $filtered) : false);
|
405 |
-
|
406 |
-
if ($in !== false) {
|
407 |
-
$filtered = array_splice($filtered, $in, 1);
|
408 |
-
}
|
409 |
-
}
|
410 |
-
|
411 |
-
return (is_array($filtered) ? $filtered : array());
|
412 |
-
}
|
413 |
-
|
414 |
/**
|
415 |
* Get Query post type
|
416 |
*
|
@@ -459,6 +416,10 @@ final class AAM_Core_API {
|
|
459 |
break;
|
460 |
}
|
461 |
}
|
|
|
|
|
|
|
|
|
462 |
}
|
463 |
|
464 |
$user = AAM::getUser();
|
368 |
return (!empty($version) ? $version : null);
|
369 |
}
|
370 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
371 |
/**
|
372 |
* Get Query post type
|
373 |
*
|
416 |
break;
|
417 |
}
|
418 |
}
|
419 |
+
} elseif (!empty($wp_query->query_vars['p'])) {
|
420 |
+
$res = get_post($wp_query->query_vars['p']);
|
421 |
+
} elseif (!empty($wp_query->query_vars['page_id'])) {
|
422 |
+
$res = get_post($wp_query->query_vars['page_id']);
|
423 |
}
|
424 |
|
425 |
$user = AAM::getUser();
|
Application/Core/Gateway.php
CHANGED
@@ -217,6 +217,18 @@ final class AAM_Core_Gateway implements AAM_Core_Contract_Api {
|
|
217 |
return $subject;
|
218 |
}
|
219 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
220 |
/**
|
221 |
* Get instance of the API gateway
|
222 |
*
|
217 |
return $subject;
|
218 |
}
|
219 |
|
220 |
+
/**
|
221 |
+
* Log any critical message
|
222 |
+
*
|
223 |
+
* @param string $message
|
224 |
+
* @param string $markers...
|
225 |
+
*
|
226 |
+
* @access public
|
227 |
+
*/
|
228 |
+
public function log() {
|
229 |
+
call_user_func_array('AAM_Core_Console::add', func_get_args());
|
230 |
+
}
|
231 |
+
|
232 |
/**
|
233 |
* Get instance of the API gateway
|
234 |
*
|
Application/Core/Object/Cache.php
CHANGED
@@ -72,7 +72,7 @@ class AAM_Core_Object_Cache extends AAM_Core_Object {
|
|
72 |
*
|
73 |
* @access public
|
74 |
*/
|
75 |
-
public function get($type, $id, $default = array()) {
|
76 |
$option = $this->getOption();
|
77 |
|
78 |
return (isset($option[$type][$id]) ? $option[$type][$id] : $default);
|
@@ -101,31 +101,6 @@ class AAM_Core_Object_Cache extends AAM_Core_Object {
|
|
101 |
return $this->getSubject()->deleteOption('cache');
|
102 |
}
|
103 |
|
104 |
-
/**
|
105 |
-
*
|
106 |
-
* @return type
|
107 |
-
*/
|
108 |
-
public function getMergedOption() {
|
109 |
-
$options = array($this->getOption());
|
110 |
-
$subject = $this->getSubject();
|
111 |
-
|
112 |
-
while($subject = $subject->getParent()) {
|
113 |
-
$options[] = $subject->getObject('cache')->getOption();
|
114 |
-
}
|
115 |
-
|
116 |
-
$merged = array();
|
117 |
-
|
118 |
-
// Important to reverse as lower subject level overrides higher. For example,
|
119 |
-
// Role level overrides Default level.
|
120 |
-
foreach(array_reverse($options) as $option) {
|
121 |
-
if (is_array($option)) {
|
122 |
-
$merged = array_replace_recursive($merged, $option);
|
123 |
-
}
|
124 |
-
}
|
125 |
-
|
126 |
-
return $merged;
|
127 |
-
}
|
128 |
-
|
129 |
/**
|
130 |
* Read object from parent subject
|
131 |
*
|
72 |
*
|
73 |
* @access public
|
74 |
*/
|
75 |
+
public function get($type, $id = 0, $default = array()) {
|
76 |
$option = $this->getOption();
|
77 |
|
78 |
return (isset($option[$type][$id]) ? $option[$type][$id] : $default);
|
101 |
return $this->getSubject()->deleteOption('cache');
|
102 |
}
|
103 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
104 |
/**
|
105 |
* Read object from parent subject
|
106 |
*
|
Application/Core/Object/Post.php
CHANGED
@@ -141,37 +141,6 @@ class AAM_Core_Object_Post extends AAM_Core_Object {
|
|
141 |
$subject->getObject('cache')->add('post', $post->ID, false);
|
142 |
} else {
|
143 |
$subject->getObject('cache')->add('post', $post->ID, $option);
|
144 |
-
|
145 |
-
// Determine if post is hidden or not. This is more complex calculation
|
146 |
-
// as it is based on the combination of several options
|
147 |
-
// TODO: this check does not belong here
|
148 |
-
if (in_array($subject::UID, array('user'))) {
|
149 |
-
$this->determineVisibility($post, 'frontend', $option);
|
150 |
-
$this->determineVisibility($post, 'backend', $option);
|
151 |
-
$this->determineVisibility($post, 'api', $option);
|
152 |
-
}
|
153 |
-
}
|
154 |
-
}
|
155 |
-
|
156 |
-
/**
|
157 |
-
* Determine if post is visible for current subject
|
158 |
-
*
|
159 |
-
* @param WP_Post $post
|
160 |
-
* @param string $area
|
161 |
-
*
|
162 |
-
* @param boolean $option
|
163 |
-
*
|
164 |
-
* @access protected
|
165 |
-
*/
|
166 |
-
protected function determineVisibility($post, $area, &$option) {
|
167 |
-
$list = !empty($option["{$area}.list"]);
|
168 |
-
$others = !empty($option["{$area}.list_others"]);
|
169 |
-
|
170 |
-
if ($list || ($others && ($post->post_author != $this->getSubject()->ID))) {
|
171 |
-
$option["{$area}.hidden"] = true;
|
172 |
-
|
173 |
-
// Cache result but only if visibility is true!
|
174 |
-
$this->getSubject()->getObject('cache')->add('post', $post->ID, $option);
|
175 |
}
|
176 |
}
|
177 |
|
141 |
$subject->getObject('cache')->add('post', $post->ID, false);
|
142 |
} else {
|
143 |
$subject->getObject('cache')->add('post', $post->ID, $option);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
144 |
}
|
145 |
}
|
146 |
|
Application/Core/Object/Visibility.php
ADDED
@@ -0,0 +1,148 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
+
* Post visibility object
|
12 |
+
*
|
13 |
+
* @package AAM
|
14 |
+
* @author Vasyl Martyniuk <vasyl@vasyltech.com>
|
15 |
+
*/
|
16 |
+
class AAM_Core_Object_Visibility extends AAM_Core_Object {
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Constructor
|
20 |
+
*
|
21 |
+
* @param AAM_Core_Subject $subject
|
22 |
+
*
|
23 |
+
* @return void
|
24 |
+
*
|
25 |
+
* @access public
|
26 |
+
*/
|
27 |
+
public function __construct(AAM_Core_Subject $subject) {
|
28 |
+
parent::__construct($subject);
|
29 |
+
|
30 |
+
$this->initialize();
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
*
|
35 |
+
* @global type $wpdb
|
36 |
+
*/
|
37 |
+
public function initialize() {
|
38 |
+
global $wpdb;
|
39 |
+
|
40 |
+
$subject = $this->getSubject();
|
41 |
+
|
42 |
+
// Read cache first
|
43 |
+
$option = $subject->getObject('cache')->get('visibility');
|
44 |
+
if ($option === false) { //if false, then the cache is empty but exists
|
45 |
+
$option = array();
|
46 |
+
} elseif (empty($option)) {
|
47 |
+
$query = "SELECT pm.`post_id`, pm.`meta_value`, p.`post_type` FROM {$wpdb->postmeta} AS pm ";
|
48 |
+
$query .= "LEFT JOIN {$wpdb->posts} AS p ON (pm.`post_id` = p.ID) ";
|
49 |
+
$query .= "WHERE pm.`meta_key` = %s";
|
50 |
+
|
51 |
+
if ($wpdb->query($wpdb->prepare($query, $this->getOptionName('post')))) {
|
52 |
+
foreach($wpdb->last_result as $row) {
|
53 |
+
$settings = maybe_unserialize($row->meta_value);
|
54 |
+
$this->pushOptions('post', $row->post_id . '|' . $row->post_type, $settings);
|
55 |
+
}
|
56 |
+
}
|
57 |
+
|
58 |
+
// Override the frontend.list for current post
|
59 |
+
$post = AAM_Core_API::getCurrentPost(); // current post
|
60 |
+
if ($post) {
|
61 |
+
$option = $this->getOption();
|
62 |
+
$option['post'][$post->ID . '|' . $post->post_type]['frontend.list'] = 0;
|
63 |
+
$this->setOption($option);
|
64 |
+
}
|
65 |
+
|
66 |
+
do_action('aam-visibility-initialize-action', $this);
|
67 |
+
|
68 |
+
// inherit settings from parent
|
69 |
+
$option = $subject->inheritFromParent('visibility', 0);
|
70 |
+
if (!empty($option)) {
|
71 |
+
$option = array_replace_recursive($option, $this->getOption());
|
72 |
+
} else {
|
73 |
+
$option = $this->getOption();
|
74 |
+
}
|
75 |
+
|
76 |
+
if (in_array($subject::UID, array('user', 'visitor'))) {
|
77 |
+
$subject->getObject('cache')->add(
|
78 |
+
'visibility', 0, empty($option) ? false : $option
|
79 |
+
);
|
80 |
+
}
|
81 |
+
}
|
82 |
+
|
83 |
+
$this->setOption($option);
|
84 |
+
}
|
85 |
+
|
86 |
+
/**
|
87 |
+
*
|
88 |
+
* @param type $object
|
89 |
+
* @param type $id
|
90 |
+
* @param type $options
|
91 |
+
* @return type
|
92 |
+
*/
|
93 |
+
public function pushOptions($object, $id, $options) {
|
94 |
+
$filtered = array();
|
95 |
+
$listOptions = apply_filters(
|
96 |
+
'aam-post-list-options-filter',
|
97 |
+
array('frontend.list', 'backend.list', 'api.list')
|
98 |
+
);
|
99 |
+
|
100 |
+
foreach($options as $key => $value) {
|
101 |
+
if (in_array($key, $listOptions)) {
|
102 |
+
$filtered[$key] = $value;
|
103 |
+
}
|
104 |
+
}
|
105 |
+
|
106 |
+
if (!empty($filtered)) {
|
107 |
+
$option = $this->getOption();
|
108 |
+
if (isset($option[$object][$id])) {
|
109 |
+
$option[$object][$id] = array_merge($option[$object][$id], $filtered);
|
110 |
+
} else {
|
111 |
+
$option[$object][$id] = $filtered;
|
112 |
+
}
|
113 |
+
$this->setOption($option);
|
114 |
+
}
|
115 |
+
|
116 |
+
return $filtered;
|
117 |
+
}
|
118 |
+
|
119 |
+
/**
|
120 |
+
*
|
121 |
+
* @param type $object
|
122 |
+
* @param type $id
|
123 |
+
* @return type
|
124 |
+
*/
|
125 |
+
public function has($object, $id = null) {
|
126 |
+
$option = $this->getOption();
|
127 |
+
|
128 |
+
return (is_null($id) ? isset($option[$object]) : isset($option[$object][$id]));
|
129 |
+
}
|
130 |
+
|
131 |
+
/**
|
132 |
+
* Generate option name
|
133 |
+
*
|
134 |
+
* @return string
|
135 |
+
*
|
136 |
+
* @access protected
|
137 |
+
*/
|
138 |
+
protected function getOptionName($object) {
|
139 |
+
$subject = $this->getSubject();
|
140 |
+
|
141 |
+
//prepare option name
|
142 |
+
$meta_key = 'aam-' . $object . '-access-' . $subject->getUID();
|
143 |
+
$meta_key .= ($subject->getId() ? $subject->getId() : '');
|
144 |
+
|
145 |
+
return $meta_key;
|
146 |
+
}
|
147 |
+
|
148 |
+
}
|
Application/Core/Subject/User.php
CHANGED
@@ -20,15 +20,6 @@ class AAM_Core_Subject_User extends AAM_Core_Subject {
|
|
20 |
*/
|
21 |
const UID = 'user';
|
22 |
|
23 |
-
/**
|
24 |
-
* Skip initialization
|
25 |
-
*
|
26 |
-
* Avoid recurring loop when user is locked
|
27 |
-
*
|
28 |
-
* @var type
|
29 |
-
*/
|
30 |
-
protected static $skipInit = false;
|
31 |
-
|
32 |
/**
|
33 |
* AAM Capability Key
|
34 |
*
|
@@ -51,7 +42,7 @@ class AAM_Core_Subject_User extends AAM_Core_Subject {
|
|
51 |
public function __construct($id) {
|
52 |
parent::__construct($id);
|
53 |
|
54 |
-
if (get_current_user_id() == $id
|
55 |
//check if user is expired
|
56 |
$expired = get_user_option('aam_user_expiration');
|
57 |
if (!empty($expired)) {
|
@@ -61,13 +52,6 @@ class AAM_Core_Subject_User extends AAM_Core_Subject {
|
|
61 |
}
|
62 |
}
|
63 |
|
64 |
-
//check if user is locked
|
65 |
-
if ($this->user_status == 1) {
|
66 |
-
self::$skipInit = true;
|
67 |
-
wp_logout();
|
68 |
-
self::$skipInit = false;
|
69 |
-
}
|
70 |
-
|
71 |
//check if user's role expired
|
72 |
$roleExpire = get_user_option('aam-role-expires');
|
73 |
if ($roleExpire && ($roleExpire <= time())) {
|
20 |
*/
|
21 |
const UID = 'user';
|
22 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
23 |
/**
|
24 |
* AAM Capability Key
|
25 |
*
|
42 |
public function __construct($id) {
|
43 |
parent::__construct($id);
|
44 |
|
45 |
+
if (get_current_user_id() == $id) {
|
46 |
//check if user is expired
|
47 |
$expired = get_user_option('aam_user_expiration');
|
48 |
if (!empty($expired)) {
|
52 |
}
|
53 |
}
|
54 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
55 |
//check if user's role expired
|
56 |
$roleExpire = get_user_option('aam-role-expires');
|
57 |
if ($roleExpire && ($roleExpire <= time())) {
|
Application/Extension/List.php
CHANGED
@@ -22,7 +22,7 @@ class AAM_Extension_List {
|
|
22 |
'description' => 'Get the complete list of all available premium extensions in one package. Any new premium extensions in the future will be available for no additional cost.',
|
23 |
'url' => 'https://aamplugin.com/complete-package',
|
24 |
'version' => (defined('AAM_COMPLETE_PACKAGE') ? constant('AAM_COMPLETE_PACKAGE') : null),
|
25 |
-
'latest' => '3.7.
|
26 |
),
|
27 |
'AAM_PLUS_PACKAGE' => array(
|
28 |
'title' => 'Plus Package',
|
@@ -31,7 +31,7 @@ class AAM_Extension_List {
|
|
31 |
'description' => 'The best selling extension with the most advanced content management features for the WordPress CMS. Manage granular access to any post, page, custom post type, category, custom hierarchical taxonomy or define the default access to all your content for all users, roles and visitors.',
|
32 |
'url' => 'https://aamplugin.com/extension/plus-package',
|
33 |
'version' => (defined('AAM_PLUS_PACKAGE') ? constant('AAM_PLUS_PACKAGE') : null),
|
34 |
-
'latest' => '3.
|
35 |
),
|
36 |
'AAM_IP_CHECK' => array(
|
37 |
'title' => 'IP Check',
|
@@ -95,7 +95,7 @@ class AAM_Extension_List {
|
|
95 |
'type' => 'GNU',
|
96 |
'tag' => 'ALPHA',
|
97 |
'license' => 'AAMSOCIALLOGIN',
|
98 |
-
'description' => 'Login to your website with social networks like Facebook, Twitter, Instagram etc. This is the open source solution and you can find it on the <a href="https://github.com/aamplugin/social-login-extension" target="_blank">Github here</a>.',
|
99 |
'version' => (defined('AAM_SOCIAL_LOGIN') ? constant('AAM_SOCIAL_LOGIN') : null),
|
100 |
'latest' => '0.1'
|
101 |
),
|
22 |
'description' => 'Get the complete list of all available premium extensions in one package. Any new premium extensions in the future will be available for no additional cost.',
|
23 |
'url' => 'https://aamplugin.com/complete-package',
|
24 |
'version' => (defined('AAM_COMPLETE_PACKAGE') ? constant('AAM_COMPLETE_PACKAGE') : null),
|
25 |
+
'latest' => '3.7.3' // TODO - Increase version after Plus Package feedback loop
|
26 |
),
|
27 |
'AAM_PLUS_PACKAGE' => array(
|
28 |
'title' => 'Plus Package',
|
31 |
'description' => 'The best selling extension with the most advanced content management features for the WordPress CMS. Manage granular access to any post, page, custom post type, category, custom hierarchical taxonomy or define the default access to all your content for all users, roles and visitors.',
|
32 |
'url' => 'https://aamplugin.com/extension/plus-package',
|
33 |
'version' => (defined('AAM_PLUS_PACKAGE') ? constant('AAM_PLUS_PACKAGE') : null),
|
34 |
+
'latest' => '3.7'
|
35 |
),
|
36 |
'AAM_IP_CHECK' => array(
|
37 |
'title' => 'IP Check',
|
95 |
'type' => 'GNU',
|
96 |
'tag' => 'ALPHA',
|
97 |
'license' => 'AAMSOCIALLOGIN',
|
98 |
+
'description' => 'Login to your website with social networks like Facebook, Twitter, Instagram etc. <a href="https://aamplugin.com/help/how-does-aam-social-login-works" target="_blank">Read more.</a> This is the open source solution and you can find it on the <a href="https://github.com/aamplugin/social-login-extension" target="_blank">Github here</a>.',
|
99 |
'version' => (defined('AAM_SOCIAL_LOGIN') ? constant('AAM_SOCIAL_LOGIN') : null),
|
100 |
'latest' => '0.1'
|
101 |
),
|
Application/Frontend/Filter.php
CHANGED
@@ -41,8 +41,12 @@ class AAM_Frontend_Filter {
|
|
41 |
|
42 |
//important to keep this option optional for optimization reasons
|
43 |
if (AAM_Core_Config::get('core.settings.checkPostVisibility', true)) {
|
|
|
|
|
44 |
//filter navigation pages & taxonomies
|
45 |
add_filter('wp_get_nav_menu_items', array($this, 'getNavigationMenu'), 999);
|
|
|
|
|
46 |
}
|
47 |
|
48 |
//widget filters
|
@@ -124,10 +128,10 @@ class AAM_Frontend_Filter {
|
|
124 |
foreach ($pages as $i => $page) {
|
125 |
if (in_array($page->type, array('post_type', 'custom'))) {
|
126 |
$object = AAM::getUser()->getObject('post', $page->object_id);
|
127 |
-
$
|
128 |
$list = $object->get('frontend.list');
|
129 |
|
130 |
-
if ($
|
131 |
unset($pages[$i]);
|
132 |
}
|
133 |
}
|
@@ -137,6 +141,40 @@ class AAM_Frontend_Filter {
|
|
137 |
return $pages;
|
138 |
}
|
139 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
140 |
/**
|
141 |
* Filter frontend widgets
|
142 |
*
|
41 |
|
42 |
//important to keep this option optional for optimization reasons
|
43 |
if (AAM_Core_Config::get('core.settings.checkPostVisibility', true)) {
|
44 |
+
// TODO: figure out how to remove these two hooks and inject "visibility"
|
45 |
+
// object instead
|
46 |
//filter navigation pages & taxonomies
|
47 |
add_filter('wp_get_nav_menu_items', array($this, 'getNavigationMenu'), 999);
|
48 |
+
// filter navigation pages & taxonomies
|
49 |
+
add_filter('get_pages', array($this, 'filterPages'), 999);
|
50 |
}
|
51 |
|
52 |
//widget filters
|
128 |
foreach ($pages as $i => $page) {
|
129 |
if (in_array($page->type, array('post_type', 'custom'))) {
|
130 |
$object = AAM::getUser()->getObject('post', $page->object_id);
|
131 |
+
$others = $object->get('frontend.list_others');
|
132 |
$list = $object->get('frontend.list');
|
133 |
|
134 |
+
if (($others && ($object->post_author != get_current_user_id())) || $list) {
|
135 |
unset($pages[$i]);
|
136 |
}
|
137 |
}
|
141 |
return $pages;
|
142 |
}
|
143 |
|
144 |
+
/**
|
145 |
+
* Filter posts from the list
|
146 |
+
*
|
147 |
+
* @param array $pages
|
148 |
+
*
|
149 |
+
* @return array
|
150 |
+
*
|
151 |
+
* @access public
|
152 |
+
*/
|
153 |
+
public function filterPages($pages) {
|
154 |
+
$current = AAM_Core_API::getCurrentPost();
|
155 |
+
|
156 |
+
if (is_array($pages)) {
|
157 |
+
$area = AAM_Core_Api_Area::get();
|
158 |
+
|
159 |
+
foreach ($pages as $i => $post) {
|
160 |
+
if ($current && ($current->ID == $post->ID)) { continue; }
|
161 |
+
|
162 |
+
// TODO: refactor this to AAM API standalone
|
163 |
+
$object = AAM::getUser()->getObject('post', $post->ID);
|
164 |
+
$list = $object->get($area. '.list');
|
165 |
+
$others = $object->get($area. '.list_others');
|
166 |
+
|
167 |
+
if ($list || ($others && ($post->post_author != get_current_user_id()))) {
|
168 |
+
unset($pages[$i]);
|
169 |
+
}
|
170 |
+
}
|
171 |
+
|
172 |
+
$pages = array_values($pages);
|
173 |
+
}
|
174 |
+
|
175 |
+
return $pages;
|
176 |
+
}
|
177 |
+
|
178 |
/**
|
179 |
* Filter frontend widgets
|
180 |
*
|
Application/Shared/Manager.php
CHANGED
@@ -61,12 +61,12 @@ class AAM_Shared_Manager {
|
|
61 |
// Control post visibility
|
62 |
//important to keep this option optional for optimization reasons
|
63 |
if (AAM_Core_Config::get('core.settings.checkPostVisibility', true)) {
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
}
|
71 |
|
72 |
//filter post content
|
@@ -76,6 +76,72 @@ class AAM_Shared_Manager {
|
|
76 |
return self::$_instance;
|
77 |
}
|
78 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
79 |
/**
|
80 |
* Disable REST API
|
81 |
*
|
@@ -143,71 +209,6 @@ class AAM_Shared_Manager {
|
|
143 |
return $caps;
|
144 |
}
|
145 |
|
146 |
-
/**
|
147 |
-
* Filter posts from the list
|
148 |
-
*
|
149 |
-
* @param array $posts
|
150 |
-
*
|
151 |
-
* @return array
|
152 |
-
*
|
153 |
-
* @access public
|
154 |
-
*/
|
155 |
-
public function filterPostList($posts) {
|
156 |
-
$current = AAM_Core_API::getCurrentPost();
|
157 |
-
|
158 |
-
if (is_array($posts)) {
|
159 |
-
$area = AAM_Core_Api_Area::get();
|
160 |
-
|
161 |
-
foreach ($posts as $i => $post) {
|
162 |
-
if ($current && ($current->ID == $post->ID)) { continue; }
|
163 |
-
|
164 |
-
// TODO: refactor this to AAM API standalone
|
165 |
-
$object = AAM::getUser()->getObject('post', $post->ID);
|
166 |
-
$hidden = $object->get($area. '.hidden');
|
167 |
-
$list = $object->get($area. '.list');
|
168 |
-
|
169 |
-
if ($hidden || $list) {
|
170 |
-
unset($posts[$i]);
|
171 |
-
}
|
172 |
-
}
|
173 |
-
|
174 |
-
$posts = array_values($posts);
|
175 |
-
}
|
176 |
-
|
177 |
-
return $posts;
|
178 |
-
}
|
179 |
-
|
180 |
-
/**
|
181 |
-
* Build pre-post query request
|
182 |
-
*
|
183 |
-
* This is used to solve the problem or pagination
|
184 |
-
*
|
185 |
-
* @param stdClass $query
|
186 |
-
*
|
187 |
-
* @return void
|
188 |
-
*
|
189 |
-
* @access public
|
190 |
-
*/
|
191 |
-
public function preparePostQuery($query) {
|
192 |
-
static $skip = false;
|
193 |
-
|
194 |
-
if ($skip === false) { // avoid loop
|
195 |
-
$skip = true;
|
196 |
-
// TODO: refactor this to AAM API standalone
|
197 |
-
$filtered = AAM_Core_API::getFilteredPostList($query);
|
198 |
-
$skip = false;
|
199 |
-
|
200 |
-
if (isset($query->query_vars['post__not_in'])
|
201 |
-
&& is_array($query->query_vars['post__not_in'])) {
|
202 |
-
$query->query_vars['post__not_in'] = array_merge(
|
203 |
-
$query->query_vars['post__not_in'], $filtered
|
204 |
-
);
|
205 |
-
} else {
|
206 |
-
$query->query_vars['post__not_in'] = $filtered;
|
207 |
-
}
|
208 |
-
}
|
209 |
-
}
|
210 |
-
|
211 |
/**
|
212 |
* Filter pages fields
|
213 |
*
|
61 |
// Control post visibility
|
62 |
//important to keep this option optional for optimization reasons
|
63 |
if (AAM_Core_Config::get('core.settings.checkPostVisibility', true)) {
|
64 |
+
add_filter(
|
65 |
+
'posts_clauses_request',
|
66 |
+
array(self::$_instance, 'filterPostQuery'),
|
67 |
+
999,
|
68 |
+
2
|
69 |
+
);
|
70 |
}
|
71 |
|
72 |
//filter post content
|
76 |
return self::$_instance;
|
77 |
}
|
78 |
|
79 |
+
/**
|
80 |
+
*
|
81 |
+
* @global WPDB $wpdb
|
82 |
+
* @param type $clauses
|
83 |
+
* @param type $wpQuery
|
84 |
+
* @return type
|
85 |
+
*/
|
86 |
+
public function filterPostQuery($clauses, $wpQuery) {
|
87 |
+
global $wpdb;
|
88 |
+
|
89 |
+
$query = '';
|
90 |
+
$option = AAM::getUser()->getObject('visibility')->getOption();
|
91 |
+
|
92 |
+
//echo '<pre>'; print_r($option);
|
93 |
+
|
94 |
+
if (!empty($option['post'])) {
|
95 |
+
if (!empty($wpQuery->query['post_type'])) {
|
96 |
+
$postType = $wpQuery->query['post_type'];
|
97 |
+
} elseif (!empty($wpQuery->query_vars['post_type'])) {
|
98 |
+
$postType = $wpQuery->query_vars['post_type'];
|
99 |
+
} elseif ($wpQuery->is_attachment) {
|
100 |
+
$postType = 'attachment';
|
101 |
+
} elseif ($wpQuery->is_page) {
|
102 |
+
$postType = 'page';
|
103 |
+
} else {
|
104 |
+
$postType = 'post';
|
105 |
+
}
|
106 |
+
|
107 |
+
$not = array();
|
108 |
+
$area = AAM_Core_Api_Area::get();
|
109 |
+
|
110 |
+
foreach($option['post'] as $id => $access) {
|
111 |
+
$chunks = explode('|', $id);
|
112 |
+
|
113 |
+
if ($postType == $chunks[1]) {
|
114 |
+
if (!empty($access["{$area}.list"])) {
|
115 |
+
$not[] = $chunks[0];
|
116 |
+
}
|
117 |
+
}
|
118 |
+
}
|
119 |
+
|
120 |
+
$chunks = array();
|
121 |
+
if (!empty($not)) {
|
122 |
+
$query .= " AND {$wpdb->posts}.ID NOT IN (" . implode(',', $not) . ")";
|
123 |
+
}
|
124 |
+
}
|
125 |
+
|
126 |
+
$clauses['where'] .= apply_filters(
|
127 |
+
'aam-post-where-clause-filter', $query, $wpQuery, $option
|
128 |
+
);
|
129 |
+
|
130 |
+
if (strpos($clauses['where'], $wpdb->term_relationships) !== false) {
|
131 |
+
if (strpos($clauses['join'], $wpdb->term_relationships) === false) {
|
132 |
+
$clauses['join'] .= " LEFT JOIN {$wpdb->term_relationships} ON ({$wpdb->posts}.ID = {$wpdb->term_relationships}.object_id)";
|
133 |
+
}
|
134 |
+
|
135 |
+
if (empty($clauses['groupby'])) {
|
136 |
+
$clauses['groupby'] = "{$wpdb->posts}.ID";
|
137 |
+
}
|
138 |
+
}
|
139 |
+
|
140 |
+
//echo '<pre>'; print_r($clauses); die();
|
141 |
+
|
142 |
+
return $clauses;
|
143 |
+
}
|
144 |
+
|
145 |
/**
|
146 |
* Disable REST API
|
147 |
*
|
209 |
return $caps;
|
210 |
}
|
211 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
212 |
/**
|
213 |
* Filter pages fields
|
214 |
*
|
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: 5.3.
|
7 |
Author: Vasyl Martyniuk <vasyl@vasyltech.com>
|
8 |
Author URI: https://vasyltech.com
|
9 |
|
@@ -141,10 +141,17 @@ class AAM {
|
|
141 |
*/
|
142 |
public static function getInstance() {
|
143 |
if (is_null(self::$_instance)) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
144 |
load_plugin_textdomain(
|
145 |
AAM_KEY, false, dirname(plugin_basename(__FILE__)) . '/Lang/'
|
146 |
);
|
147 |
-
self::$_instance = new self;
|
148 |
|
149 |
//load all installed extension
|
150 |
AAM_Extension_Repository::getInstance()->load();
|
3 |
/**
|
4 |
Plugin Name: Advanced Access Manager
|
5 |
Description: All you need to manage access to your WordPress website
|
6 |
+
Version: 5.3.2
|
7 |
Author: Vasyl Martyniuk <vasyl@vasyltech.com>
|
8 |
Author URI: https://vasyltech.com
|
9 |
|
141 |
*/
|
142 |
public static function getInstance() {
|
143 |
if (is_null(self::$_instance)) {
|
144 |
+
self::$_instance = new self;
|
145 |
+
|
146 |
+
// Logout user if he/she is blocked
|
147 |
+
$user = self::$_instance->getUser();
|
148 |
+
if ($user->getUID() == 'user' && $user->user_status == 1) {
|
149 |
+
wp_logout();
|
150 |
+
}
|
151 |
+
|
152 |
load_plugin_textdomain(
|
153 |
AAM_KEY, false, dirname(plugin_basename(__FILE__)) . '/Lang/'
|
154 |
);
|
|
|
155 |
|
156 |
//load all installed extension
|
157 |
AAM_Extension_Repository::getInstance()->load();
|
media/js/aam.js
CHANGED
@@ -2715,7 +2715,7 @@
|
|
2715 |
}
|
2716 |
});
|
2717 |
}
|
2718 |
-
|
2719 |
/**
|
2720 |
*
|
2721 |
* @returns {undefined}
|
@@ -2768,7 +2768,7 @@
|
|
2768 |
}
|
2769 |
});
|
2770 |
});
|
2771 |
-
|
2772 |
$('#clear-cache').bind('click', function () {
|
2773 |
$.ajax(aamLocal.ajaxurl, {
|
2774 |
type: 'POST',
|
2715 |
}
|
2716 |
});
|
2717 |
}
|
2718 |
+
|
2719 |
/**
|
2720 |
*
|
2721 |
* @returns {undefined}
|
2768 |
}
|
2769 |
});
|
2770 |
});
|
2771 |
+
|
2772 |
$('#clear-cache').bind('click', function () {
|
2773 |
$.ajax(aamLocal.ajaxurl, {
|
2774 |
type: 'POST',
|
readme.txt
CHANGED
@@ -3,7 +3,7 @@ Contributors: vasyltech
|
|
3 |
Tags: access, role, user, capability, page access, post access, content access, comments, security, login redirect, membership, backend lockdown, wp-admin, 404, rest api, xml rpc
|
4 |
Requires at least: 4.0
|
5 |
Tested up to: 4.9.6
|
6 |
-
Stable tag: 5.3.
|
7 |
|
8 |
The most powerful access management plugin for WordPress websites.
|
9 |
|
@@ -66,6 +66,12 @@ https://www.youtube.com/watch?v=yiOhjaacNJc
|
|
66 |
|
67 |
== Changelog ==
|
68 |
|
|
|
|
|
|
|
|
|
|
|
|
|
69 |
= 5.3.1 =
|
70 |
* Fixed bug with deprecated cache object to keep it backward compatible
|
71 |
* Fixed bug with teaser message on none latin alphabet
|
3 |
Tags: access, role, user, capability, page access, post access, content access, comments, security, login redirect, membership, backend lockdown, wp-admin, 404, rest api, xml rpc
|
4 |
Requires at least: 4.0
|
5 |
Tested up to: 4.9.6
|
6 |
+
Stable tag: 5.3.2
|
7 |
|
8 |
The most powerful access management plugin for WordPress websites.
|
9 |
|
66 |
|
67 |
== Changelog ==
|
68 |
|
69 |
+
= 5.3.2 =
|
70 |
+
* Fixed the bug that triggers PHP warnings when blocked user is trying to login
|
71 |
+
* Fixed the bug with get current post method in the core API
|
72 |
+
* WARNING Experimental approach! to the post access that enormously improve AAM performance
|
73 |
+
* Added custom capability "edit_permalink" that control ability to edit post permalink
|
74 |
+
|
75 |
= 5.3.1 =
|
76 |
* Fixed bug with deprecated cache object to keep it backward compatible
|
77 |
* Fixed bug with teaser message on none latin alphabet
|