Passed
Branch master (c92198)
by Paul
06:33
created

ReviewController::onChangeAssignedUsers()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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