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 -197 2.3.152.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,171 +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 isSingleObjectRestRequest(mixed $request, WP_Post $post): bool
273 - {
274 - $routeId = $request instanceof WP_REST_Request ? ($request->get_url_params()['id'] ?? null) : null;
275 -
276 - return $routeId !== null && (int) $routeId === (int) $post->ID;
277 - }
278 -
279 - private function setRestField(array &$data, string $field, string $value): void
280 - {
281 - if (array_key_exists($field, $data) === false) {
282 - return;
283 - }
284 -
285 - if (is_array($data[$field]) === false) {
286 - $data[$field] = $value;
287 -
288 - return;
289 - }
290 -
291 - $restrictedValues = ['rendered' => $value, 'raw' => $value, 'protected' => false];
292 -
293 - foreach ($restrictedValues as $key => $restrictedValue) {
294 - if (array_key_exists($key, $data[$field]) === true) {
295 - $data[$field][$key] = $restrictedValue;
296 - }
297 - }
298 - }
299 -
300 329 /**
330 + * Checks the access of the attached file.
331 + * @param string $file
332 + * @param int|string $attachmentId
333 + * @return string|false
301 334 * @throws UserGroupTypeException
302 335 */
303 - public function restrictRestResponse(mixed $response, mixed $post = null, mixed $request = null): mixed
336 + public function getAttachedFile(string $file, $attachmentId)
304 337 {
305 - if (($response instanceof WP_REST_Response) === false
306 - || ($post instanceof WP_Post) === false
307 - || $this->accessHandler->checkObjectAccess($post->post_type, $post->ID) === true
308 - ) {
309 - return $response;
310 - }
311 -
312 - if ($this->removePostFromList($post->post_type) === true
313 - && $this->isSingleObjectRestRequest($request, $post) === true
314 - ) {
315 - return $this->getRestAccessDeniedError();
316 - }
317 -
318 - $restrictedContent = $this->processPostContent($post);
319 - $data = (array) $response->get_data();
320 -
321 - $this->setRestField($data, 'content', $restrictedContent);
322 - $this->setRestField($data, 'excerpt', $restrictedContent);
323 -
324 - if ($this->mainConfig->hidePostTypeTitle($post->post_type) === true) {
325 - $this->setRestField($data, 'title', $this->mainConfig->getPostTypeTitle($post->post_type));
326 - }
327 -
328 - $response->set_data($data);
329 -
330 - return $response;
331 - }
332 -
333 - /**
334 - * @throws UserGroupTypeException
335 - */
336 - public function excludeRestrictedPostsFromRestQuery(array $queryArgs): array
337 - {
338 - $excludedPosts = $this->accessHandler->getExcludedPosts();
339 -
340 - if ($excludedPosts !== []) {
341 - $queryArgs['post__not_in'] = $this->addExcludedPosts($queryArgs['post__not_in'] ?? [], $excludedPosts);
342 - }
343 -
344 - return $queryArgs;
345 - }
346 -
347 - private function getRestBaseToPostTypeMap(): array
348 - {
349 - if ($this->restBaseToPostTypeMap !== null) {
350 - return $this->restBaseToPostTypeMap;
351 - }
352 -
353 - $this->restBaseToPostTypeMap = [];
354 -
355 - foreach ((array) $this->objectHandler->getPostTypes() as $postType) {
356 - $restBase = $this->wordpress->getPostTypeObject($postType)?->rest_base;
357 - $this->restBaseToPostTypeMap[empty($restBase) === true ? $postType : $restBase] = $postType;
358 - }
359 -
360 - return $this->restBaseToPostTypeMap;
361 - }
362 -
363 - /**
364 - * @return array{0: string, 1: int}|null
365 - */
366 - private function getGuardedRestRouteTarget(WP_REST_Request $request): ?array
367 - {
368 - if (preg_match(self::REST_OBJECT_ROUTE_PATTERN, (string) $request->get_route(), $matches) !== 1) {
369 - return null;
370 - }
371 -
372 - $isSubResourceRoute = ($matches[3] ?? '') !== '';
373 - $isReadingRequest = in_array(
374 - strtoupper((string) $request->get_method()),
375 - Wordpress::REST_READING_METHODS,
376 - true
377 - );
378 -
379 - if ($isReadingRequest === true && $isSubResourceRoute === false) {
380 - return null;
381 - }
382 -
383 - $postType = $this->getRestBaseToPostTypeMap()[$matches[1]] ?? null;
384 -
385 - return $postType === null ? null : [$postType, (int) $matches[2]];
386 - }
387 -
388 - /**
389 - * @throws UserGroupTypeException
390 - */
391 - public function restrictRestRequest(mixed $result, mixed $server = null, mixed $request = null): mixed
392 - {
393 - if (($request instanceof WP_REST_Request) === false) {
394 - return $result;
395 - }
396 -
397 - $routeTarget = $this->getGuardedRestRouteTarget($request);
398 -
399 - $this->wordpress->setRestRequestContext(
400 - $routeTarget !== null || $request->get_param('context') === 'edit'
401 - );
402 -
403 - if ($result !== null
404 - || $routeTarget === null
405 - || $this->accessHandler->checkObjectAccess($routeTarget[0], $routeTarget[1], true) === true
406 - ) {
407 - return $result;
408 - }
409 -
410 - return $this->getRestAccessDeniedError();
411 - }
412 -
413 - /**
414 - * @throws UserGroupTypeException
415 - */
416 - public function getAttachedFile(string $file, int|string|null $attachmentId): bool|string
417 - {
418 338 $isImage = (bool) preg_match('/(?i)\.(jpg|jpeg|jpe|png|gif)$/', $file);
419 339
420 340 if ($isImage === false && $this->mainConfig->lockFile() === true) {
421 341 $hasAccess = $this->accessHandler->checkObjectAccess(ObjectHandler::ATTACHMENT_OBJECT_TYPE, $attachmentId);
@@ -425,8 +345,12 @@
425 345 return $file;
426 346 }
427 347
428 348 /**
349 + * Adds the excluded posts filter to the given query.
350 + * @param string $query
351 + * @param string $table
352 + * @return string
429 353 * @throws UserGroupTypeException
430 354 */
431 355 private function addQueryExcludedPostFilter(string $query, string $table): string
432 356 {
@@ -432,10 +356,10 @@
432 356 {
433 357 $excludedPosts = $this->accessHandler->getExcludedPosts();
434 358
435 359 if ($excludedPosts !== []) {
436 - $excludedPostsStr = implode(', ', array_map('intval', $excludedPosts));
437 - $query .= " AND $table.ID NOT IN ($excludedPostsStr) ";
360 + $excludedPostsStr = implode(', ', $excludedPosts);
361 + $query .= " AND {$table}.ID NOT IN ($excludedPostsStr) ";
438 362 }
439 363
440 364 return $query;
441 365 }
@@ -440,8 +364,11 @@
440 364 return $query;
441 365 }
442 366
443 367 /**
368 + * The function for the posts_where_paged filter.
369 + * @param string $query The where sql statement.
370 + * @return string
444 371 * @throws UserGroupTypeException
445 372 */
446 373 public function showPostSql(string $query): string
447 374 {
@@ -448,8 +375,12 @@
448 375 return $this->addQueryExcludedPostFilter($query, $this->database->getPostsTable());
449 376 }
450 377
451 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
452 383 * @throws UserGroupTypeException
453 384 */
454 385 public function showNextPreviousPost(string $query): string
455 386 {
@@ -455,15 +386,22 @@
455 386 {
456 387 return $this->addQueryExcludedPostFilter($query, 'p');
457 388 }
458 389
390 + /**
391 + * Returns the post count query.
392 + * @param array $excludedPosts
393 + * @param string $type
394 + * @param string $perm
395 + * @return string
396 + */
459 397 private function getPostCountQuery(array $excludedPosts, string $type, string $perm): string
460 398 {
461 - $excludedPosts = implode(', ', array_map('intval', $excludedPosts));
462 - $query = "SELECT post_status, COUNT(*) AS num_posts
463 - FROM {$this->database->getPostsTable()}
399 + $excludedPosts = implode('\', \'', $excludedPosts);
400 + $query = "SELECT post_status, COUNT(*) AS num_posts
401 + FROM {$this->database->getPostsTable()}
464 402 WHERE post_type = %s
465 - AND ID NOT IN ($excludedPosts)";
403 + AND ID NOT IN ('{$excludedPosts}')";
466 404
467 405 if ('readable' === $perm
468 406 && $this->wordpress->isUserLoggedIn() === true
469 407 && $this->wordpress->currentUserCan(
@@ -480,8 +418,13 @@
480 418 return $query;
481 419 }
482 420
483 421 /**
422 + * Function for the wp_count_posts filter.
423 + * @param stdClass $counts
424 + * @param string $type
425 + * @param string $perm
426 + * @return stdClass
484 427 * @throws UserGroupTypeException
485 428 */
486 429 public function showPostCount(stdClass $counts, string $type, string $perm): stdClass
487 430 {
@@ -507,8 +450,13 @@
507 450
508 451 return $this->cachedCounts[$type];
509 452 }
510 453
454 + /**
455 + * Checks if the post comment should be completely hidden.
456 + * @param string $postType
457 + * @return bool
458 + */
511 459 private function hidePostComment(string $postType): bool
512 460 {
513 461 return $this->mainConfig->lockPostTypeComments($postType) === true
514 462 || $this->mainConfig->hidePostType($postType) === true
@@ -515,12 +463,14 @@
515 463 || $this->wordpressConfig->atAdminPanel() === true;
516 464 }
517 465
518 466 /**
467 + * The function for the comments_array filter.
519 468 * @param WP_Comment[] $comments The comments.
469 + * @return array
520 470 * @throws UserGroupTypeException
521 471 */
522 - public function showComment(array $comments = []): array
472 + public function showComment($comments = []): array
523 473 {
524 474 $showComments = [];
525 475
526 476 foreach ($comments as $comment) {
@@ -544,11 +494,15 @@
544 494 return $showComments;
545 495 }
546 496
547 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
548 502 * @throws UserGroupTypeException
549 503 */
550 - public function showEditLink(?string $link, int|string|null $postId): string
504 + public function showEditLink(?string $link, $postId): string
551 505 {
552 506 if ($this->mainConfig->hideEditLinkOnNoAccess() === true
553 507 && $this->accessHandler->checkObjectAccess(ObjectHandler::GENERAL_POST_OBJECT_TYPE, $postId, true) === false
554 508 ) {