Passed
Push — master ( 07800a...2da942 )
by Paul
06:15
created

ReviewController::filterReviewPostData()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 11.1035

Importance

Changes 0
Metric Value
eloc 7
c 0
b 0
f 0
dl 0
loc 12
ccs 3
cts 8
cp 0.375
rs 9.6111
cc 5
nc 5
nop 2
crap 11.1035
1
<?php
2
3
namespace GeminiLabs\SiteReviews\Controllers;
4
5
use GeminiLabs\SiteReviews\Commands\AssignPosts;
6
use GeminiLabs\SiteReviews\Commands\AssignTerms;
7
use GeminiLabs\SiteReviews\Commands\AssignUsers;
8
use GeminiLabs\SiteReviews\Commands\CreateReview;
9
use GeminiLabs\SiteReviews\Commands\ToggleStatus;
10
use GeminiLabs\SiteReviews\Commands\UnassignPosts;
11
use GeminiLabs\SiteReviews\Commands\UnassignTerms;
12
use GeminiLabs\SiteReviews\Commands\UnassignUsers;
13
use GeminiLabs\SiteReviews\Database;
14
use GeminiLabs\SiteReviews\Database\Cache;
15
use GeminiLabs\SiteReviews\Database\CountManager;
16
use GeminiLabs\SiteReviews\Database\Query;
17
use GeminiLabs\SiteReviews\Database\ReviewManager;
18
use GeminiLabs\SiteReviews\Database\TaxonomyManager;
19
use GeminiLabs\SiteReviews\Defaults\RatingDefaults;
20
use GeminiLabs\SiteReviews\Helper;
21
use GeminiLabs\SiteReviews\Helpers\Arr;
22
use GeminiLabs\SiteReviews\Helpers\Cast;
23
use GeminiLabs\SiteReviews\Modules\Html\ReviewHtml;
24
use GeminiLabs\SiteReviews\Review;
25
26
class ReviewController extends Controller
27
{
28
    /**
29
     * @return void
30
     * @action admin_action_approve
31
     */
32
    public function approve()
33
    {
34
        if (glsr()->id == filter_input(INPUT_GET, 'plugin')) {
35
            check_admin_referer('approve-review_'.($postId = $this->getPostId()));
36
            $this->execute(new ToggleStatus($postId, 'publish'));
37
            wp_safe_redirect(wp_get_referer());
38
            exit;
39
        }
40
    }
41
42
    /**
43
     * @param array $posts
44
     * @return array
45
     * @filter the_posts
46
     */
47
    public function filterPostsToCacheReviews($posts)
48
    {
49
        $reviews = array_filter($posts, function ($post) {
50
            return glsr()->post_type === $post->post_type;
51
        });
52
        if ($postIds = wp_list_pluck($reviews, 'ID')) {
53
            glsr(Query::class)->reviews([], $postIds); // this caches the associated Review objects
54
        }
55
        return $posts;
56
    }
57
58
    /**
59
     * @param array $data
60
     * @param array $postArr
61
     * @return array
62
     * @filter wp_insert_post_data
63
     */
64 14
    public function filterReviewPostData($data, $postArr)
65
    {
66 14
        if (empty($postArr['ID']) || glsr()->post_type !== glsr_get($postArr, 'post_type')) {
67 14
            return $data;
68
        }
69
        if (empty(filter_input(INPUT_POST, 'post_author'))) {
70
            $data['post_author'] = 0; // the review has an unknown author
71
        }
72
        if (isset($_POST['post_author_override'])) {
73
            $data['post_author'] = $_POST['post_author_override']; // use the value from the author meta box
74
        }
75
        return $data;
76
    }
77
78
    /**
79
     * @param string $template
80
     * @return string
81
     * @filter site-reviews/rendered/template/review
82
     */
83 6
    public function filterReviewTemplate($template, array $data)
84
    {
85 6
        $search = 'id="review-';
86 6
        $dataType = Arr::get($data, 'review.type', 'local');
87 6
        $replace = sprintf('data-type="%s" %s', $dataType, $search);
88 6
        return str_replace($search, $replace, $template);
89
    }
90
91
    /**
92
     * @param string $operator
93
     * @return string
94
     * @filter site-reviews/query/sql/clause/operator
95
     */
96 7
    public function filterSqlClauseOperator($operator)
97
    {
98 7
        $operators = ['loose' => 'OR', 'strict' => 'AND'];
99 7
        return Arr::get($operators, glsr_get_option('reviews.assignment', 'strict', 'string'), $operator);
100
    }
101
102
    /**
103
     * @return array
104
     * @filter site-reviews/review/build/after
105
     */
106 6
    public function filterTemplateTags(array $tags, Review $review, ReviewHtml $reviewHtml)
107
    {
108 6
        $tags['assigned_links'] = $reviewHtml->buildTemplateTag($review, 'assigned_links', $review->assigned_posts);
109 6
        return $tags;
110
    }
111
112
    /**
113
     * Triggered when one or more categories are added or removed from a review.
114
     * 
115
     * @param int $postId
116
     * @param array $terms
117
     * @param array $newTTIds
118
     * @param string $taxonomy
119
     * @param bool $append
120
     * @param array $oldTTIds
121
     * @return void
122
     * @action set_object_terms
123
     */
124 5
    public function onAfterChangeAssignedTerms($postId, $terms, $newTTIds, $taxonomy, $append, $oldTTIds)
125
    {
126 5
        if (Review::isReview($postId)) {
127 2
            $review = glsr(Query::class)->review($postId);
128 2
            $diff = $this->getAssignedDiffs($oldTTIds, $newTTIds);
129 2
            $this->execute(new UnassignTerms($review, $diff['old']));
130 2
            $this->execute(new AssignTerms($review, $diff['new']));
131
        }
132 5
    }
133
134
    /**
135
     * Triggered when a post status changes or when a review is approved|unapproved|trashed.
136
     * 
137
     * @param string $oldStatus
138
     * @param string $newStatus
139
     * @param \WP_Post $post
140
     * @return void
141
     * @action transition_post_status
142
     */
143 14
    public function onAfterChangeStatus($newStatus, $oldStatus, $post)
144
    {
145 14
        if (in_array($oldStatus, ['new', $newStatus])) {
146 14
            return;
147
        }
148
        $isPublished = 'publish' === $newStatus;
149
        if (Review::isReview($post)) {
150
            glsr(ReviewManager::class)->update($post->ID, ['is_approved' => $isPublished]);
151
            glsr(Cache::class)->delete($post->ID, 'reviews');
152
            glsr(CountManager::class)->recalculate();
153
        } else {
154
            glsr(ReviewManager::class)->updateAssignedPost($post->ID, $isPublished);
155
        }
156
    }
157
158
    /**
159
     * Triggered when a review's assigned post IDs are updated.
160
     * 
161
     * @return void
162
     * @action site-reviews/review/updated/post_ids
163
     */
164
    public function onChangeAssignedPosts(Review $review, array $postIds = [])
165
    {
166
        $diff = $this->getAssignedDiffs($review->assigned_posts, $postIds);
167
        $this->execute(new UnassignPosts($review, $diff['old']));
168
        $this->execute(new AssignPosts($review, $diff['new']));
169
    }
170
171
    /**
172
     * Triggered when a review's assigned users IDs are updated.
173
     * 
174
     * @return void
175
     * @action site-reviews/review/updated/user_ids
176
     */
177
    public function onChangeAssignedUsers(Review $review, array $userIds = [])
178
    {
179
        $diff = $this->getAssignedDiffs($review->assigned_users, $userIds);
180
        $this->execute(new UnassignUsers($review, $diff['old']));
181
        $this->execute(new AssignUsers($review, $diff['new']));
182
    }
183
184
    /**
185
     * Triggered after a review is created.
186
     * 
187
     * @return void
188
     * @action site-reviews/review/created
189
     */
190 13
    public function onCreatedReview(Review $review, CreateReview $command)
191
    {
192 13
        $this->execute(new AssignPosts($review, $command->assigned_posts));
193 13
        $this->execute(new AssignUsers($review, $command->assigned_users));
194 13
    }
195
196
    /**
197
     * Triggered when a review is created.
198
     * 
199
     * @param int $postId
200
     * @return void
201
     * @action site-reviews/review/create
202
     */
203 13
    public function onCreateReview($postId, CreateReview $command)
204
    {
205 13
        $values = glsr()->args($command->toArray()); // this filters the values
206 13
        $data = glsr(RatingDefaults::class)->restrict($values->toArray());
207 13
        $data['review_id'] = $postId;
208 13
        $data['is_approved'] = 'publish' === get_post_status($postId);
209 13
        if (false === glsr(Database::class)->insert('ratings', $data)) {
210
            wp_delete_post($postId, true); // remove post as review was not created
211
            return;
212
        }
213 13
        if (!empty($values->response)) {
214
            glsr(Database::class)->metaSet($postId, 'response', $values->response); // save the response if one is provided
215
        }
216 13
        glsr(TaxonomyManager::class)->setTerms($postId, $values->assigned_terms); // terms are assigned with the set_object_terms hook
217 13
        foreach ($values->custom as $key => $value) {
218
            glsr(Database::class)->metaSet($postId, 'custom_'.$key, $value);
219
        }
220 13
    }
221
222
    /**
223
     * Triggered when a review or other post type is deleted and the posts table uses the MyISAM engine.
224
     * 
225
     * @param int $postId
226
     * @param \WP_Post $post
227
     * @return void
228
     * @action deleted_post
229
     */
230
    public function onDeletePost($postId, $post)
231
    {
232
        if (glsr()->post_type === $post->post_type) {
233
            $this->onDeleteReview($postId);
234
            return;
235
        }
236
        $reviews = glsr(Query::class)->reviews([
237
            'assigned_posts' => $postId,
238
            'per_page' => -1,
239
            'status' => 'all',
240
        ]);
241
        if (glsr(Database::class)->delete('assigned_posts', ['post_id' => $postId])) {
242
            array_walk($reviews, function ($review) {
243
                glsr(Cache::class)->delete($review->ID, 'reviews');
244
            });
245
        }
246
    }
247
248
    /**
249
     * Triggered when a review is deleted and the posts table uses the MyISAM engine.
250
     * 
251
     * @param int $reviewId
252
     * @return void
253
     * @see $this->onDeletePost()
254
     */
255
    public function onDeleteReview($reviewId)
256
    {
257
        if (glsr(Database::class)->delete('ratings', ['review_id' => $reviewId])) {
258
            glsr(Cache::class)->delete($reviewId, 'reviews');
259
        }
260
    }
261
262
    /**
263
     * Triggered when a user is deleted and the users table uses the MyISAM engine.
264
     * 
265
     * @param int $userId
266
     * @return void
267
     * @action deleted_user
268
     */
269
    public function onDeleteUser($userId)
270
    {
271
        $reviews = glsr(Query::class)->reviews([
272
            'assigned_users' => $userId,
273
            'per_page' => -1,
274
            'status' => 'all',
275
        ]);
276
        if (glsr(Database::class)->delete('assigned_users', ['user_id' => $userId])) {
277
            array_walk($reviews, function ($review) {
278
                glsr(Cache::class)->delete($review->ID, 'reviews');
279
            });
280
        }
281
    }
282
283
    /**
284
     * Triggered when a review is edited or trashed.
285
     * It's unnecessary to trigger a term recount as this is done by the set_object_terms hook
286
     * We need to use "edit_post" to support revisions (vs "save_post").
287
     *
288
     * @param int $postId
289
     * @param \WP_Post $post
290
     * @param \WP_Post $oldPost
291
     * @return void
292
     * @action post_updated
293
     */
294
    public function onEditReview($postId, $post, $oldPost)
295
    {
296
        if (!glsr()->can('edit_posts') || !$this->isEditedReview($post, $oldPost)) {
297
            return;
298
        }
299
        $review = glsr(Query::class)->review($postId);
300
        if ('post' === glsr_current_screen()->base) {
301
            $this->updateReview($review);
302
        } else {
303
            $this->bulkUpdateReview($review);
304
        }
305
    }
306
307
    /**
308
     * @return void
309
     * @action admin_action_unapprove
310
     */
311
    public function unapprove()
312
    {
313
        if (glsr()->id == filter_input(INPUT_GET, 'plugin')) {
314
            check_admin_referer('unapprove-review_'.($postId = $this->getPostId()));
315
            $this->execute(new ToggleStatus($postId, 'pending'));
316
            wp_safe_redirect(wp_get_referer());
317
            exit;
318
        }
319
    }
320
321
    /**
322
     * @return void
323
     */
324
    protected function bulkUpdateReview(Review $review)
325
    {
326
        if ($assignedPostIds = filter_input(INPUT_GET, 'post_ids', FILTER_SANITIZE_NUMBER_INT, FILTER_FORCE_ARRAY)) {
327
            glsr()->action('review/updated/post_ids', $review, Cast::toArray($assignedPostIds)); // trigger a recount of assigned posts
328
        }
329
        if ($assignedUserIds = filter_input(INPUT_GET, 'user_ids', FILTER_SANITIZE_NUMBER_INT, FILTER_FORCE_ARRAY)) {
330
            glsr()->action('review/updated/user_ids', $review, Cast::toArray($assignedUserIds)); // trigger a recount of assigned users
331
        }
332
        $review = glsr(Query::class)->review($review->ID); // get a fresh copy of the review
333
        glsr()->action('review/saved', $review, []); // pass an empty array since review values are unchanged
334
    }
335
336
    /**
337
     * @return array
338
     */
339 2
    protected function getAssignedDiffs(array $existing, array $replacements)
340
    {
341 2
        sort($existing);
342 2
        sort($replacements);
343 2
        $new = $old = [];
344 2
        if ($existing !== $replacements) {
345 2
            $ignored = array_intersect($existing, $replacements);
346 2
            $new = array_diff($replacements, $ignored);
347 2
            $old = array_diff($existing, $ignored);
348
        }
349
        return [
350 2
            'new' => $new,
351 2
            'old' => $old,
352
        ];
353
    }
354
355
    /**
356
     * @param \WP_Post $post
357
     * @param \WP_Post $oldPost
358
     * @return bool
359
     */
360
    protected function isEditedReview($post, $oldPost)
361
    {
362
        if (glsr()->post_type !== $post->post_type) {
363
            return false;
364
        }
365
        if (in_array('trash', [$post->post_status, $oldPost->post_status])) {
366
            return false; // trashed posts cannot be edited
367
        }
368
        $input = 'edit' === glsr_current_screen()->base ? INPUT_GET : INPUT_POST;
369
        return 'glsr_action' !== filter_input($input, 'action'); // abort if not a proper post update (i.e. approve/unapprove)
370
    }
371
372
    /**
373
     * @return void
374
     */
375
    protected function updateReview(Review $review)
376
    {
377
        $assignedPostIds = filter_input(INPUT_POST, 'post_ids', FILTER_SANITIZE_NUMBER_INT, FILTER_FORCE_ARRAY);
378
        $assignedUserIds = filter_input(INPUT_POST, 'user_ids', FILTER_SANITIZE_NUMBER_INT, FILTER_FORCE_ARRAY);
379
        glsr()->action('review/updated/post_ids', $review, Cast::toArray($assignedPostIds)); // trigger a recount of assigned posts
380
        glsr()->action('review/updated/user_ids', $review, Cast::toArray($assignedUserIds)); // trigger a recount of assigned users
381
        glsr(MetaboxController::class)->saveResponseMetabox($review);
382
        $submittedValues = Helper::filterInputArray(glsr()->id);
383
        if (Arr::get($submittedValues, 'is_editing_review')) {
384
            $submittedValues['rating'] = Arr::get($submittedValues, 'rating');
385
            glsr(ReviewManager::class)->update($review->ID, $submittedValues);
386
            glsr(ReviewManager::class)->updateCustom($review->ID, $submittedValues);
387
        }
388
        $review = glsr(Query::class)->review($review->ID); // get a fresh copy of the review
389
        glsr()->action('review/saved', $review, $submittedValues);
390
    }
391
}
392