PluginProbe
User Access Manager / 2.2.13
User Access Manager v2.2.13
2.3.20 2.3.19 2.3.18 2.3.17 2.3.16 2.3.15 2.3.14 2.3.13 trunk 0.6 0.6.1 0.6.2 0.7 0.7 Beta 0.7.0.1 0.8 0.8.0.1 0.8.0.2 0.9 0.9.1 0.9.1.1 0.9.1.2 0.9.1.3 0.9.1.4 1.0 All 136 releases
← All changes | src/Controller/Frontend/PostController.php +151 -202 2.3.162.2.13 View file →
@@ -1,5 +1,18 @@
1 1 <?php
2 +/**
3 + * FrontendPostController.php
4 + *
5 + * The FrontendPostController class file.
6 + *
7 + * PHP versions 5
8 + *
9 + * @author Alexander Schneider <alexanderschneider85@gmail.com>
10 + * @copyright 2008-2017 Alexander Schneider
11 + * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2
12 + * @version SVN: $id$
13 + * @link http://wordpress.org/extend/plugins/user-access-manager/
14 + */
2 15
3 16 declare(strict_types=1);
4 17
5 18 namespace UserAccessManager\Controller\Frontend;
@@ -16,38 +29,59 @@
16 29 use UserAccessManager\UserGroup\UserGroupTypeException;
17 30 use UserAccessManager\Util\Util;
18 31 use UserAccessManager\Wrapper\Php;
19 32 use UserAccessManager\Wrapper\Wordpress;
20 -use WeakMap;
21 33 use WP_Comment;
22 -use WP_Error;
23 34 use WP_Hook;
24 35 use WP_Post;
25 36 use WP_Query;
26 -use WP_REST_Request;
27 -use WP_REST_Response;
28 37
38 +/**
39 + * Class FrontendPostController
40 + *
41 + * @package UserAccessManager\Controller
42 + */
29 43 class PostController extends ContentController
30 44 {
31 - private const REST_OBJECT_ROUTE_PATTERN = '#^/[^/]+/v\d+/([^/]+)/(\d+)(?:/(\w+))?#';
45 + /**
46 + * @var Database
47 + */
48 + private $database;
32 49
33 - private array $wordpressFilters = [];
34 - private stdClass|array|null $cachedCounts = [];
35 - private ?array $restBaseToPostTypeMap = null;
50 + /**
51 + * @var array
52 + */
53 + private $wordpressFilters = [];
36 54
37 - private WeakMap $posts;
55 + /**
56 + * @var null|stdClass
57 + */
58 + private $cachedCounts = [];
38 59
60 + /**
61 + * PostController constructor.
62 + * @param Php $php
63 + * @param Wordpress $wordpress
64 + * @param WordpressConfig $wordpressConfig
65 + * @param MainConfig $mainConfig
66 + * @param Database $database
67 + * @param Util $util
68 + * @param ObjectHandler $objectHandler
69 + * @param UserHandler $userHandler
70 + * @param UserGroupHandler $userGroupHandler
71 + * @param AccessHandler $accessHandler
72 + */
39 73 public function __construct(
40 74 Php $php,
41 75 Wordpress $wordpress,
42 76 WordpressConfig $wordpressConfig,
43 77 MainConfig $mainConfig,
78 + Database $database,
44 79 Util $util,
45 80 ObjectHandler $objectHandler,
46 81 UserHandler $userHandler,
47 82 UserGroupHandler $userGroupHandler,
48 - AccessHandler $accessHandler,
49 - private Database $database
83 + AccessHandler $accessHandler
50 84 ) {
51 85 parent::__construct(
52 86 $php,
53 87 $wordpress,
@@ -58,17 +92,25 @@
58 92 $userHandler,
59 93 $userGroupHandler,
60 94 $accessHandler
61 95 );
62 -
63 - $this->posts = new WeakMap();
96 + $this->database = $database;
64 97 }
65 98
99 + /**
100 + * Return the wordpress filters.
101 + * @return array
102 + */
66 103 public function getWordpressFilters(): array
67 104 {
68 105 return $this->wordpressFilters;
69 106 }
70 107
108 + /**
109 + * Returns true if the filters are suppressed.
110 + * @param WP_Query $wpQuery
111 + * @return bool
112 + */
71 113 private function filtersSuppressed(WP_Query $wpQuery): bool
72 114 {
73 115 return isset($wpQuery->query_vars['suppress_filters']) === true
74 116 && $wpQuery->query_vars['suppress_filters'] === true;
@@ -73,25 +115,24 @@
73 115 return isset($wpQuery->query_vars['suppress_filters']) === true
74 116 && $wpQuery->query_vars['suppress_filters'] === true;
75 117 }
76 118
77 - private function addExcludedPosts(mixed $postsNotIn, array $excludedPosts): array
78 - {
79 - return array_unique(array_merge((array) $postsNotIn, $excludedPosts));
80 - }
81 -
82 119 /**
120 + * Manipulates the wordpress query object to filter content.
121 + * @param WP_Query $wpQuery The wordpress query object.
83 122 * @throws UserGroupTypeException
84 123 */
85 - public function parseQuery(WP_Query $wpQuery): void
124 + public function parseQuery(WP_Query $wpQuery)
86 125 {
87 126 if ($this->filtersSuppressed($wpQuery) === true) {
88 127 $excludedPosts = $this->accessHandler->getExcludedPosts();
89 128
90 129 if ($excludedPosts !== []) {
91 - $wpQuery->query_vars['post__not_in'] = $this->addExcludedPosts(
92 - $wpQuery->query_vars['post__not_in'] ?? [],
93 - $excludedPosts
130 + $postsNotIn = (isset($wpQuery->query_vars['post__not_in']) === true) ?
131 + $wpQuery->query_vars['post__not_in'] : [];
132 +
133 + $wpQuery->query_vars['post__not_in'] = array_unique(
134 + array_merge($postsNotIn, $excludedPosts)
94 135 );
95 136 }
96 137 }
97 138 }
@@ -96,9 +137,11 @@
96 137 }
97 138 }
98 139
99 140 /**
141 + * Extracts the user access manager filters and returns true if it was successful.
100 142 * @param WP_Hook[] $filters
143 + * @return bool
101 144 */
102 145 private function extractOwnFilters(array $filters): bool
103 146 {
104 147 if (isset($filters['the_posts']->callbacks[10]) === true) {
@@ -116,8 +159,16 @@
116 159
117 160 return false;
118 161 }
119 162
163 + /**
164 + * If filters are suppressed we still want to filter posts, so we have to turn the suppression off,
165 + * remove all other filters than the ones from the user access manager and store them to restore
166 + * them later.
167 + * @param array|null $posts
168 + * @param WP_Query $query
169 + * @return null|array
170 + */
120 171 public function postsPreQuery(?array $posts, WP_Query $query): ?array
121 172 {
122 173 if ($this->filtersSuppressed($query) === true) {
123 174 $filters = $this->wordpress->getFilters();
@@ -137,9 +188,12 @@
137 188
138 189 return $posts;
139 190 }
140 191
141 - private function restoreFilters(): void
192 + /**
193 + * Restores the filters to normal.
194 + */
195 + private function restoreFilters()
142 196 {
143 197 if (count($this->wordpressFilters) > 0) {
144 198 $filters = $this->wordpress->getFilters();
145 199
@@ -151,9 +205,14 @@
151 205 $this->wordpressFilters = [];
152 206 }
153 207 }
154 208
155 - private function getPost(mixed $post): bool|WP_Post
209 + /**
210 + * Tries to get the post from the given mixed data.
211 + * @param mixed $post
212 + * @return false|WP_Post
213 + */
214 + private function getPost($post)
156 215 {
157 216 if ($post instanceof WP_post) {
158 217 return $post;
159 218 } elseif (is_int($post) === true) {
@@ -164,8 +223,13 @@
164 223
165 224 return false;
166 225 }
167 226
227 + /**
228 + * Processes the post content and searches for the more tag.
229 + * @param WP_Post $post
230 + * @return string
231 + */
168 232 private function processPostContent(WP_Post $post): string
169 233 {
170 234 $uamPostContent = htmlspecialchars_decode($this->mainConfig->getPostTypeContent($post->post_type));
171 235
@@ -178,17 +242,20 @@
178 242 return stripslashes($uamPostContent);
179 243 }
180 244
181 245 /**
246 + * Modifies the content of the post by the given settings.
247 + * @param WP_Post $post The current post.
248 + * @return null|WP_Post
182 249 * @throws UserGroupTypeException
183 250 */
184 - private function processPost(WP_Post $post): WP_Post|bool
251 + private function processPost(WP_Post $post): ?WP_Post
185 252 {
186 253 $post->post_title .= $this->adminOutput($post->post_type, $post->ID);
187 254
188 255 if ($this->accessHandler->checkObjectAccess($post->post_type, $post->ID) === false) {
189 256 if ($this->removePostFromList($post->post_type) === true) {
190 - return false;
257 + return null;
191 258 }
192 259
193 260 $post->post_content = $this->processPostContent($post);
194 261
@@ -204,19 +271,13 @@
204 271 return $post;
205 272 }
206 273
207 274 /**
275 + * Filters the raw posts.
276 + * @param array $rawPosts
277 + * @return array
208 278 * @throws UserGroupTypeException
209 279 */
210 - private function getProcessedPost(WP_Post $post): ?WP_Post
211 - {
212 - $post = $this->posts[$post] ??= $this->processPost($post);
213 - return $post === false ? null : $post;
214 - }
215 -
216 - /**
217 - * @throws UserGroupTypeException
218 - */
219 280 private function filterRawPosts(array $rawPosts): array
220 281 {
221 282 $filteredPosts = [];
222 283
@@ -223,9 +284,9 @@
223 284 foreach ($rawPosts as $rawPost) {
224 285 $post = $this->getPost($rawPost);
225 286
226 287 if ($post !== false) {
227 - $post = $this->getProcessedPost($post);
288 + $post = $this->processPost($post);
228 289
229 290 if ($post !== null) {
230 291 $filteredPosts[] = $post;
231 292 }
@@ -237,8 +298,11 @@
237 298 return $filteredPosts;
238 299 }
239 300
240 301 /**
302 + * The function for the the_posts filter.
303 + * @param null|array $showPosts The posts.
304 + * @return array
241 305 * @throws UserGroupTypeException
242 306 */
243 307 public function showPosts(?array $showPosts = []): ?array
244 308 {
@@ -251,176 +315,27 @@
251 315 return $showPosts;
252 316 }
253 317
254 318 /**
319 + * The function for the get_pages filter.
255 320 * @param WP_Post[] $rawPages The pages.
321 + * @return array
256 322 * @throws UserGroupTypeException
257 323 */
258 - public function showPages(array $rawPages = []): array
324 + public function showPages($rawPages = []): array
259 325 {
260 - return $this->filterRawPosts($rawPages);
326 + return $this->filterRawPosts((array) $rawPages);
261 327 }
262 328
263 - private function getRestAccessDeniedError(): WP_Error
264 - {
265 - return $this->wordpress->getWpError(
266 - 'uam_rest_access_denied',
267 - TXT_UAM_REST_ACCESS_DENIED,
268 - ['status' => $this->wordpress->isUserLoggedIn() === true ? 403 : 401]
269 - );
270 - }
271 -
272 - private function setRestField(array &$data, string $field, string $value): void
273 - {
274 - if (array_key_exists($field, $data) === false) {
275 - return;
276 - }
277 -
278 - if (is_array($data[$field]) === false) {
279 - $data[$field] = $value;
280 -
281 - return;
282 - }
283 -
284 - $restrictedValues = ['rendered' => $value, 'raw' => $value, 'protected' => false];
285 -
286 - foreach ($restrictedValues as $key => $restrictedValue) {
287 - if (array_key_exists($key, $data[$field]) === true) {
288 - $data[$field][$key] = $restrictedValue;
289 - }
290 - }
291 - }
292 -
293 329 /**
330 + * Checks the access of the attached file.
331 + * @param string $file
332 + * @param int|string $attachmentId
333 + * @return string|false
294 334 * @throws UserGroupTypeException
295 335 */
296 - public function restrictRestResponse(mixed $response, mixed $post = null, mixed $request = null): mixed
336 + public function getAttachedFile(string $file, $attachmentId)
297 337 {
298 - if (($response instanceof WP_REST_Response) === false
299 - || ($post instanceof WP_Post) === false
300 - || $this->accessHandler->checkObjectAccess($post->post_type, $post->ID) === true
301 - ) {
302 - return $response;
303 - }
304 -
305 - $restrictedContent = $this->processPostContent($post);
306 - $data = (array) $response->get_data();
307 -
308 - $this->setRestField($data, 'content', $restrictedContent);
309 - $this->setRestField($data, 'excerpt', $restrictedContent);
310 -
311 - if ($this->mainConfig->hidePostTypeTitle($post->post_type) === true) {
312 - $this->setRestField($data, 'title', $this->mainConfig->getPostTypeTitle($post->post_type));
313 - }
314 -
315 - $response->set_data($data);
316 -
317 - return $response;
318 - }
319 -
320 - /**
321 - * @throws UserGroupTypeException
322 - */
323 - public function excludeRestrictedPostsFromRestQuery(array $queryArgs): array
324 - {
325 - $excludedPosts = $this->accessHandler->getExcludedPosts();
326 -
327 - if ($excludedPosts !== []) {
328 - $queryArgs['post__not_in'] = $this->addExcludedPosts($queryArgs['post__not_in'] ?? [], $excludedPosts);
329 - }
330 -
331 - return $queryArgs;
332 - }
333 -
334 - private function getRestBaseToPostTypeMap(): array
335 - {
336 - if ($this->restBaseToPostTypeMap !== null) {
337 - return $this->restBaseToPostTypeMap;
338 - }
339 -
340 - $this->restBaseToPostTypeMap = [];
341 -
342 - foreach ((array) $this->objectHandler->getPostTypes() as $postType) {
343 - $restBase = $this->wordpress->getPostTypeObject($postType)?->rest_base;
344 - $this->restBaseToPostTypeMap[empty($restBase) === true ? $postType : $restBase] = $postType;
345 - }
346 -
347 - return $this->restBaseToPostTypeMap;
348 - }
349 -
350 - /**
351 - * @return null|array{type: string, id: int, addressesSubResource: bool}
352 - */
353 - private function getRestRouteTarget(WP_REST_Request $request): ?array
354 - {
355 - if (preg_match(self::REST_OBJECT_ROUTE_PATTERN, (string) $request->get_route(), $matches) !== 1) {
356 - return null;
357 - }
358 -
359 - $postType = $this->getRestBaseToPostTypeMap()[$matches[1]] ?? null;
360 -
361 - return $postType === null ? null : [
362 - 'type' => $postType,
363 - 'id' => (int) $matches[2],
364 - 'addressesSubResource' => ($matches[3] ?? '') !== ''
365 - ];
366 - }
367 -
368 - private function isReadingRestRequest(WP_REST_Request $request): bool
369 - {
370 - return in_array(strtoupper((string) $request->get_method()), Wordpress::REST_READING_METHODS, true);
371 - }
372 -
373 - private function isEditingRestRoute(bool $addressesSubResource, WP_REST_Request $request): bool
374 - {
375 - return $this->isReadingRestRequest($request) === false || $addressesSubResource === true;
376 - }
377 -
378 - /**
379 - * @throws UserGroupTypeException
380 - */
381 - private function hasRestRouteAccess(string $objectType, int $objectId, bool $isEditingRoute): bool
382 - {
383 - if ($isEditingRoute === true) {
384 - return $this->accessHandler->checkObjectAccess($objectType, $objectId, true);
385 - }
386 -
387 - return $this->removePostFromList($objectType) === false
388 - || $this->accessHandler->checkObjectAccess($objectType, $objectId);
389 - }
390 -
391 - /**
392 - * @throws UserGroupTypeException
393 - */
394 - public function restrictRestRequest(mixed $result, mixed $server = null, mixed $request = null): mixed
395 - {
396 - if (($request instanceof WP_REST_Request) === false) {
397 - return $result;
398 - }
399 -
400 - $routeTarget = $this->getRestRouteTarget($request);
401 - $isEditingRoute = $routeTarget !== null
402 - && $this->isEditingRestRoute($routeTarget['addressesSubResource'], $request);
403 -
404 - $this->wordpress->setRestRequestContext(
405 - $isEditingRoute === true || $request->get_param('context') === 'edit'
406 - );
407 -
408 - if ($result !== null || $routeTarget === null) {
409 - return $result;
410 - }
411 -
412 - ['type' => $type, 'id' => $id] = $routeTarget;
413 -
414 - return $this->hasRestRouteAccess($type, $id, $isEditingRoute) === true ?
415 - $result : $this->getRestAccessDeniedError();
416 - }
417 -
418 - /**
419 - * @throws UserGroupTypeException
420 - */
421 - public function getAttachedFile(string $file, int|string|null $attachmentId): bool|string
422 - {
423 338 $isImage = (bool) preg_match('/(?i)\.(jpg|jpeg|jpe|png|gif)$/', $file);
424 339
425 340 if ($isImage === false && $this->mainConfig->lockFile() === true) {
426 341 $hasAccess = $this->accessHandler->checkObjectAccess(ObjectHandler::ATTACHMENT_OBJECT_TYPE, $attachmentId);
@@ -430,8 +345,12 @@
430 345 return $file;
431 346 }
432 347
433 348 /**
349 + * Adds the excluded posts filter to the given query.
350 + * @param string $query
351 + * @param string $table
352 + * @return string
434 353 * @throws UserGroupTypeException
435 354 */
436 355 private function addQueryExcludedPostFilter(string $query, string $table): string
437 356 {
@@ -437,10 +356,10 @@
437 356 {
438 357 $excludedPosts = $this->accessHandler->getExcludedPosts();
439 358
440 359 if ($excludedPosts !== []) {
441 - $excludedPostsStr = implode(', ', array_map('intval', $excludedPosts));
442 - $query .= " AND $table.ID NOT IN ($excludedPostsStr) ";
360 + $excludedPostsStr = implode(', ', $excludedPosts);
361 + $query .= " AND {$table}.ID NOT IN ($excludedPostsStr) ";
443 362 }
444 363
445 364 return $query;
446 365 }
@@ -445,8 +364,11 @@
445 364 return $query;
446 365 }
447 366
448 367 /**
368 + * The function for the posts_where_paged filter.
369 + * @param string $query The where sql statement.
370 + * @return string
449 371 * @throws UserGroupTypeException
450 372 */
451 373 public function showPostSql(string $query): string
452 374 {
@@ -453,8 +375,12 @@
453 375 return $this->addQueryExcludedPostFilter($query, $this->database->getPostsTable());
454 376 }
455 377
456 378 /**
379 + * The function for the get_previous_post_where and
380 + * the get_next_post_where filter.
381 + * @param string $query The current sql string.
382 + * @return string
457 383 * @throws UserGroupTypeException
458 384 */
459 385 public function showNextPreviousPost(string $query): string
460 386 {
@@ -460,15 +386,22 @@
460 386 {
461 387 return $this->addQueryExcludedPostFilter($query, 'p');
462 388 }
463 389
390 + /**
391 + * Returns the post count query.
392 + * @param array $excludedPosts
393 + * @param string $type
394 + * @param string $perm
395 + * @return string
396 + */
464 397 private function getPostCountQuery(array $excludedPosts, string $type, string $perm): string
465 398 {
466 - $excludedPosts = implode(', ', array_map('intval', $excludedPosts));
467 - $query = "SELECT post_status, COUNT(*) AS num_posts
468 - FROM {$this->database->getPostsTable()}
399 + $excludedPosts = implode('\', \'', $excludedPosts);
400 + $query = "SELECT post_status, COUNT(*) AS num_posts
401 + FROM {$this->database->getPostsTable()}
469 402 WHERE post_type = %s
470 - AND ID NOT IN ($excludedPosts)";
403 + AND ID NOT IN ('{$excludedPosts}')";
471 404
472 405 if ('readable' === $perm
473 406 && $this->wordpress->isUserLoggedIn() === true
474 407 && $this->wordpress->currentUserCan(
@@ -485,8 +418,13 @@
485 418 return $query;
486 419 }
487 420
488 421 /**
422 + * Function for the wp_count_posts filter.
423 + * @param stdClass $counts
424 + * @param string $type
425 + * @param string $perm
426 + * @return stdClass
489 427 * @throws UserGroupTypeException
490 428 */
491 429 public function showPostCount(stdClass $counts, string $type, string $perm): stdClass
492 430 {
@@ -512,8 +450,13 @@
512 450
513 451 return $this->cachedCounts[$type];
514 452 }
515 453
454 + /**
455 + * Checks if the post comment should be completely hidden.
456 + * @param string $postType
457 + * @return bool
458 + */
516 459 private function hidePostComment(string $postType): bool
517 460 {
518 461 return $this->mainConfig->lockPostTypeComments($postType) === true
519 462 || $this->mainConfig->hidePostType($postType) === true
@@ -520,12 +463,14 @@
520 463 || $this->wordpressConfig->atAdminPanel() === true;
521 464 }
522 465
523 466 /**
467 + * The function for the comments_array filter.
524 468 * @param WP_Comment[] $comments The comments.
469 + * @return array
525 470 * @throws UserGroupTypeException
526 471 */
527 - public function showComment(array $comments = []): array
472 + public function showComment($comments = []): array
528 473 {
529 474 $showComments = [];
530 475
531 476 foreach ($comments as $comment) {
@@ -549,11 +494,15 @@
549 494 return $showComments;
550 495 }
551 496
552 497 /**
498 + * The function for the edit_post_link filter.
499 + * @param null|string $link The edit link.
500 + * @param int|string $postId The _iId of the post.
501 + * @return string
553 502 * @throws UserGroupTypeException
554 503 */
555 - public function showEditLink(?string $link, int|string|null $postId): string
504 + public function showEditLink(?string $link, $postId): string
556 505 {
557 506 if ($this->mainConfig->hideEditLinkOnNoAccess() === true
558 507 && $this->accessHandler->checkObjectAccess(ObjectHandler::GENERAL_POST_OBJECT_TYPE, $postId, true) === false
559 508 ) {