Test Failed
Push — develop ( 325b33...c75138 )
by Paul
09:01
created

ReviewManager::postStatus()   A

Complexity

Conditions 6
Paths 24

Size

Total Lines 12
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

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