Test Failed
Push — main ( 4244ba...f56735 )
by Paul
10:05 queued 12s
created

ReviewManager::update()   B

Complexity

Conditions 6
Paths 10

Size

Total Lines 29
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 6

Importance

Changes 1
Bugs 1 Features 0
Metric Value
eloc 22
c 1
b 1
f 0
dl 0
loc 29
ccs 16
cts 16
cp 1
rs 8.9457
cc 6
nc 10
nop 2
crap 6
1
<?php
2
3
namespace GeminiLabs\SiteReviews\Database;
4
5
use GeminiLabs\SiteReviews\Commands\CreateReview;
6
use GeminiLabs\SiteReviews\Database;
7
use GeminiLabs\SiteReviews\Defaults\CustomFieldsDefaults;
8
use GeminiLabs\SiteReviews\Defaults\RatingDefaults;
9
use GeminiLabs\SiteReviews\Defaults\UpdateReviewDefaults;
10
use GeminiLabs\SiteReviews\Helpers\Arr;
11
use GeminiLabs\SiteReviews\Helpers\Cast;
12
use GeminiLabs\SiteReviews\Modules\Sanitizer;
13
use GeminiLabs\SiteReviews\Request;
14
use GeminiLabs\SiteReviews\Review;
15
use GeminiLabs\SiteReviews\Reviews;
16
17
class ReviewManager
18
{
19
    public function assignPost(Review $review, int $postId): bool
20
    {
21
        if (!glsr()->can('assign_post', $postId)) {
22
            return false;
23
        }
24 2
        $where = [
25
            'is_published' => $this->isPublishedPost($postId),
26 2
            'post_id' => $postId,
27 1
            'rating_id' => $review->rating_id,
28
        ];
29 2
        if ($result = glsr(Database::class)->insert('assigned_posts', $where)) {
30 2
            glsr(Cache::class)->delete($review->ID, 'reviews');
31 2
            if (!defined('WP_IMPORTING')) {
32 2
                glsr(CountManager::class)->posts($postId);
33 2
            }
34 2
        }
35 2
        return Cast::toInt($result) > 0;
36 2
    }
37 2
38
    public function assignTerm(Review $review, int $termId): bool
39
    {
40 2
        $where = [
41
            'rating_id' => $review->rating_id,
42
            'term_id' => $termId,
43
        ];
44
        if ($result = glsr(Database::class)->insert('assigned_terms', $where)) {
45
            glsr(Cache::class)->delete($review->ID, 'reviews');
46
            if (!defined('WP_IMPORTING')) {
47 2
                glsr(CountManager::class)->terms($termId);
48
            }
49 2
        }
50 2
        return Cast::toInt($result) > 0;
51 2
    }
52 2
53 2
    public function assignUser(Review $review, int $userId): bool
54 2
    {
55 2
        $where = [
56 2
            'rating_id' => $review->rating_id,
57
            'user_id' => $userId,
58
        ];
59 2
        if ($result = glsr(Database::class)->insert('assigned_users', $where)) {
60
            glsr(Cache::class)->delete($review->ID, 'reviews');
61
            if (!defined('WP_IMPORTING')) {
62
                glsr(CountManager::class)->users($userId);
63
            }
64
        }
65
        return Cast::toInt($result) > 0;
66 2
    }
67
68 2
    /**
69 2
     * @return Review|false
70 2
     */
71 2
    public function create(CreateReview $command, ?int $postId = null)
72 2
    {
73 2
        if (empty($postId)) {
74 2
            $postId = $this->createRaw($command);
75 2
        }
76
        if (empty($postId)) {
77
            return false;
78 2
        }
79
        $review = $this->get($postId);
80
        if ($review->isValid()) {
81
            glsr()->action('review/created', $review, $command);
82
            return $this->get($review->ID); // return a fresh copy of the review
83
        }
84 24
        return false;
85
    }
86 24
87 24
    /**
88
     * @return Review|false
89 24
     */
90 24
    public function createFromPost(int $postId, array $data = [])
91 24
    {
92 24
        if (!Review::isReview($postId)) {
93
            return false;
94
        }
95
        $command = new CreateReview(new Request($data));
96
        glsr()->action('review/create', $postId, $command);
97
        return $this->create($command, $postId);
98
    }
99
100
    /**
101
     * @return int|false
102
     */
103
    public function createRaw(CreateReview $command)
104
    {
105
        $values = glsr()->args($command->toArray()); // this filters the values
106
        $submitted = $command->request->toArray();
107
        $metaInput = [
108
            '_submitted' => $submitted, // save the original submitted request in metadata
109
            '_submitted_hash' => md5(maybe_serialize($submitted)),
110
        ];
111
        $values = [
112
            'comment_status' => 'closed',
113 24
            'meta_input' => $metaInput,
114
            'ping_status' => 'closed',
115 24
            'post_author' => $values->author_id,
116 24
            'post_content' => $values->content,
117 24
            'post_date' => $values->date,
118 24
            'post_date_gmt' => $values->date_gmt,
119 24
            'post_modified' => $values->date,
120 24
            'post_modified_gmt' => $values->date_gmt,
121 24
            'post_name' => uniqid($values->type),
122 24
            'post_status' => $this->postStatus($command),
123 24
            'post_title' => $values->title,
124 24
            'post_type' => glsr()->post_type,
125 24
        ];
126 24
        $values = glsr()->filterArray('review/create/post_data', $values, $command);
127 24
        $postId = wp_insert_post($values, true);
128 24
        if (is_wp_error($postId)) {
129 24
            glsr_log()->error($postId->get_error_message())->debug($values);
130 24
            return false;
131 24
        }
132 24
        glsr()->action('review/create', $postId, $command);
133
        return $postId;
134
    }
135
136 24
    public function deleteRating(int $reviewId): bool
137 24
    {
138
        $result = glsr(Database::class)->delete('ratings', ['review_id' => $reviewId]);
139
        if ($result) {
140
            glsr(Cache::class)->delete($reviewId, 'reviews');
141
        }
142
        return Cast::toInt($result) > 0;
143
    }
144
145
    public function deleteRevisions(int $reviewId): void
146
    {
147
        $revisionIds = glsr(Query::class)->revisionIds((int) $reviewId);
148
        foreach ($revisionIds as $revisionId) {
149
            wp_delete_post_revision($revisionId);
150
        }
151
    }
152
153
    public function get(int $reviewId, bool $bypassCache = false): Review
154
    {
155
        return glsr(Query::class)->review($reviewId, $bypassCache);
156
    }
157
158
    public function reviews(array $args = []): Reviews
159
    {
160
        $args = (new NormalizePaginationArgs($args))->toArray();
161
        $results = glsr(Query::class)->reviews($args);
162
        $total = $this->total($args, $results);
163
        $reviews = new Reviews($results, $total, $args);
164
        glsr()->action('get/reviews', $reviews, $args);
165
        return $reviews;
166
    }
167
168
    public function total(array $args = [], array $reviews = []): int
169
    {
170 24
        return glsr(Query::class)->totalReviews($args, $reviews);
171
    }
172 24
173 24
    public function unassignPost(Review $review, int $postId): bool
174 24
    {
175
        if (!glsr()->can('unassign_post', $postId)) {
176
            return false;
177
        }
178
        $where = [
179
            'post_id' => $postId,
180 2
            'rating_id' => $review->rating_id,
181
        ];
182 2
        if ($result = glsr(Database::class)->delete('assigned_posts', $where)) {
183 2
            glsr(Cache::class)->delete($review->ID, 'reviews');
184 2
            glsr(CountManager::class)->posts($postId);
185 2
        }
186 2
        return Cast::toInt($result) > 0;
187 2
    }
188
189
    public function unassignTerm(Review $review, int $termId): bool
190
    {
191
        $where = [
192
            'rating_id' => $review->rating_id,
193 2
            'term_id' => $termId,
194
        ];
195 2
        if ($result = glsr(Database::class)->delete('assigned_terms', $where)) {
196
            glsr(Cache::class)->delete($review->ID, 'reviews');
197
            glsr(CountManager::class)->terms($termId);
198
        }
199
        return Cast::toInt($result) > 0;
200
    }
201
202 1
    public function unassignUser(Review $review, int $userId): bool
203
    {
204 1
        $where = [
205
            'rating_id' => $review->rating_id,
206
            'user_id' => $userId,
207 1
        ];
208 1
        if ($result = glsr(Database::class)->delete('assigned_users', $where)) {
209 1
            glsr(Cache::class)->delete($review->ID, 'reviews');
210 1
            glsr(CountManager::class)->users($userId);
211 1
        }
212 1
        return Cast::toInt($result) > 0;
213 1
    }
214
215 1
    /**
216
     * @return Review|false
217
     */
218
    public function update(int $reviewId, array $data = [])
219
    {
220
        $oldPost = get_post($reviewId);
221
        if (-1 === $this->updateRating($reviewId, $data)) {
222 1
            glsr_log('update rating failed');
223
            return false;
224 1
        }
225 1
        if (-1 === $this->updateReview($reviewId, $data)) {
226 1
            glsr_log('update review failed');
227 1
            return false;
228 1
        }
229 1
        $this->updateCustom($reviewId, $data);
230 1
        $this->updateResponse($reviewId, $data);
231
        $review = $this->get($reviewId);
232 1
        if (isset($data['assigned_posts'])) {
233
            $assignedPosts = glsr(Sanitizer::class)->sanitizePostIds($data['assigned_posts']);
234
            glsr()->action('review/updated/post_ids', $review, $assignedPosts); // triggers a recount of assigned posts
235
        }
236
        if (isset($data['assigned_terms'])) {
237
            $assignedTerms = glsr(Sanitizer::class)->sanitizeTermIds($data['assigned_terms']);
238
            wp_set_object_terms($reviewId, $assignedTerms, glsr()->taxonomy); // triggers a recount of assigned_terms
239 1
        }
240
        if (isset($data['assigned_users'])) {
241 1
            $assignedUsers = glsr(Sanitizer::class)->sanitizeUserIds($data['assigned_users']);
242 1
            glsr()->action('review/updated/user_ids', $review, $assignedUsers); // triggers a recount of assigned posts
243 1
        }
244 1
        $review = $this->get($reviewId); // get a fresh copy of the review
245 1
        glsr()->action('review/updated', $review, $data, $oldPost);
246 1
        return $review;
247 1
    }
248
249 1
    public function updateAssignedPost(int $postId): bool
250
    {
251
        $result = glsr(Database::class)->update('assigned_posts',
252
            ['is_published' => $this->isPublishedPost($postId)],
253
            ['post_id' => Cast::toInt($postId)]
254
        );
255
        return Cast::toInt($result) > 0;
256
    }
257
258
    public function updateCustom(int $reviewId, array $data = []): void
259
    {
260
        $data = glsr(CustomFieldsDefaults::class)->merge($data);
261
        $data = Arr::prefixKeys($data, 'custom_');
262
        foreach ($data as $metaKey => $metaValue) {
263
            glsr(Database::class)->metaSet($reviewId, $metaKey, $metaValue);
264
        }
265
    }
266
267
    public function updateRating(int $reviewId, array $data = []): int
268
    {
269
        $review = $this->get($reviewId);
270
        glsr(Cache::class)->delete($reviewId, 'reviews');
271
        $sanitized = glsr(RatingDefaults::class)->restrict($data);
272
        $data = array_intersect_key($sanitized, $data);
273
        if (empty($data)) {
274
            return 0;
275
        }
276
        $result = glsr(Database::class)->update('ratings', $data, [
277
            'review_id' => $reviewId,
278
        ]);
279
        if (false === $result) {
280
            return -1;
281
        }
282
        $rating = $data['rating'] ?? '';
283
        if (is_numeric($rating) && $review->rating !== $data['rating']) {
284
            glsr(CountManager::class)->recalculate();
285
        }
286
        return Cast::toInt($result);
287
    }
288
289
    public function updateResponse(int $reviewId, array $data): int
290
    {
291
        if (!array_key_exists('response', $data)) {
292
            return 0;
293
        }
294
        $response = Arr::get($data, 'response');
295
        $response = Cast::toString($response);
296
        $response = glsr(Sanitizer::class)->sanitizeTextHtml($response);
297
        $review = glsr_get_review($reviewId);
298
        if (empty($response) && empty($review->response)) {
299
            return 0;
300
        }
301
        glsr(Database::class)->metaSet($review->ID, 'response', $response); // prefixed metakey
302
        // This should run immediately after saving the response
303
        // but before adding the "response_by" meta_value!
304
        glsr()->action('review/responded', $review, $response);
305
        glsr(Database::class)->metaSet($review->ID, 'response_by', get_current_user_id()); // prefixed metakey
306
        glsr(Cache::class)->delete($review->ID, 'reviews');
307
        return 1;
308
    }
309
310
    public function updateReview(int $reviewId, array $data = []): int
311
    {
312
        if (glsr()->post_type !== get_post_type($reviewId)) {
313
            glsr_log()->error("Review update failed: Post ID [{$reviewId}] is not a review.");
314
            return -1;
315
        }
316
        glsr(Cache::class)->delete($reviewId, 'reviews');
317
        $sanitized = glsr(UpdateReviewDefaults::class)->restrict($data);
318
        if ($data = array_intersect_key($sanitized, $data)) {
319
            $data = array_filter([
320
                'post_content' => Arr::get($data, 'content'),
321
                'post_date' => Arr::get($data, 'date'),
322
                'post_date_gmt' => Arr::get($data, 'date_gmt'),
323
                'post_status' => Arr::get($data, 'status'),
324
                'post_title' => Arr::get($data, 'title'),
325
            ]);
326
        }
327
        if (empty($data)) {
328
            return 0;
329
        }
330
        $data = wp_parse_args(['ID' => $reviewId], $data);
331
        $result = wp_update_post($data, true);
332
        if (is_wp_error($result)) {
333
            glsr_log()->error($result->get_error_message())->debug($data);
334
            return -1;
335
        }
336
        return Cast::toInt($result);
337
    }
338
339
    protected function isPublishedPost(int $postId): bool
340
    {
341
        $isPublished = 'publish' === get_post_status($postId);
342
        return glsr()->filterBool('post/is-published', $isPublished, $postId);
343
    }
344
345
    protected function postStatus(CreateReview $command): string
346
    {
347
        $isApproved = $command->is_approved;
348
        $isFormSubmission = !defined('WP_IMPORTING') && !glsr()->retrieve('glsr_create_review', false);
349
        if ($isFormSubmission) {
350
            $requireApproval = glsr(OptionManager::class)->getBool('settings.general.require.approval');
351
            $requireApprovalForRating = glsr(OptionManager::class)->getInt('settings.general.require.approval_for', 5);
352
            $isApproved = !$requireApproval || $command->rating > $requireApprovalForRating;
353
        }
354
        return !$isApproved || ('local' === $command->type && $command->request->cast('blacklisted', 'bool'))
355
            ? 'pending'
356
            : 'publish';
357
    }
358
}
359