Passed
Push — develop ( ec1998...551614 )
by Paul
05:52
created

MyCredHook::userExceedsPerDayLimit()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 17
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 13
c 1
b 0
f 0
dl 0
loc 17
ccs 0
cts 14
cp 0
rs 9.8333
cc 3
nc 3
nop 1
crap 12
1
<?php
2
3
namespace GeminiLabs\SiteReviews\Integrations\MyCred;
4
5
use GeminiLabs\SiteReviews\Database\Query;
6
use GeminiLabs\SiteReviews\Helpers\Arr;
7
use GeminiLabs\SiteReviews\Helpers\Str;
8
use GeminiLabs\SiteReviews\Integrations\MyCred\Defaults\AssignedAuthorDefaults;
9
use GeminiLabs\SiteReviews\Integrations\MyCred\Defaults\AssignedUserDefaults;
10
use GeminiLabs\SiteReviews\Integrations\MyCred\Defaults\ReviewerDefaults;
11
use GeminiLabs\SiteReviews\Review;
12
13
class MyCredHook extends \myCRED_Hook
14
{
15
    public function __construct($preferences, $type = MYCRED_DEFAULT_TYPE_KEY)
0 ignored issues
show
Bug introduced by
The constant GeminiLabs\SiteReviews\I...MYCRED_DEFAULT_TYPE_KEY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
16
    {
17
        $args = [
18
            'id' => Str::snakeCase(glsr()->id),
19
            'defaults' => [
20
                'reviewer' => glsr(ReviewerDefaults::class)->defaults(),
21
                'assigned_author' => glsr(AssignedAuthorDefaults::class)->defaults(),
22
                'assigned_user' => glsr(AssignedUserDefaults::class)->defaults(),
23
            ],
24
        ];
25
        parent::__construct($args, $preferences, $type);
26
    }
27
28
    /**
29
     * @action site-reviews/review/created
30
     */
31
    public function onReviewCreated(Review $review): void
32
    {
33
        $review = glsr(Query::class)->review($review->ID); // get a fresh copy of the review
34
        if ($review->is_approved) {
35
            $this->reviewApproved($review);
36
        }
37
    }
38
39
    /**
40
     * @action site-reviews/review/transitioned
41
     */
42
    public function onReviewStatusChanged(Review $review, string $new, string $old): void
43
    {
44
        if (!in_array('publish', [$new, $old])) {
45
            return;
46
        }
47
        $review = glsr(Query::class)->review($review->ID); // get a fresh copy of the review
48
        if ('publish' === $new) {
49
            $this->reviewApproved($review);
50
        } elseif ('publish' === $old && 'trash' === $new) {
51
            $this->reviewTrashed($review);
52
        } elseif ('publish' === $old && 'trash' !== $new) {
53
            $this->reviewUnapproved($review);
54
        }
55
    }
56
57
    public function preferences()
58
    {
59
        glsr()->render('integrations/mycred/preferences', [
60
            'hook' => $this,
61
            'step' => Arr::get([1, .1, .01, .001, .0001], $this->core->format['decimals'], 1),
62
        ]);
63
    }
64
65
    public function run()
66
    {
67
        add_action('site-reviews/review/created', [$this, 'onReviewCreated'], 20);
68
        add_action('site-reviews/review/transitioned', [$this, 'onReviewStatusChanged'], 20, 3);
69
    }
70
71
    public function sanitise_preferences($data)
72
    {
73
        $sanitized = [];
74
        $sanitized['reviewer'] = glsr(ReviewerDefaults::class)->restrict(Arr::get($data, 'reviewer'));
75
        $sanitized['assigned_author'] = glsr(AssignedAuthorDefaults::class)->restrict(Arr::get($data, 'assigned_author'));
76
        $sanitized['assigned_user'] = glsr(AssignedUserDefaults::class)->restrict(Arr::get($data, 'assigned_user'));
77
        return $sanitized;
78
    }
79
80
    protected function getEntryFor(string $key, bool $isDeduction = false): string
81
    {
82
        $suffix = $isDeduction ? '_deduction' : '';
83
        return Arr::getAs('string', $this->prefs, sprintf('%s.log%s', $key, $suffix));
84
    }
85
86
    /**
87
     * @return int|float
88
     */
89
    protected function getPointsFor(string $key, bool $isDeduction = false)
90
    {
91
        $suffix = $isDeduction ? '_deduction' : '';
92
        $points = Arr::get($this->prefs, sprintf('%s.points%s', $key, $suffix), 0);
93
        $points = $this->core->number($points);
94
        return $isDeduction
95
            ? $this->core->zero() - $points
96
            : $this->core->zero() + $points;
97
    }
98
99
    protected function processAuthorPoints(string $reference, Review $review, bool $isDeduction = false): void
100
    {
101
        $points = $this->getPointsFor('assigned_author', $isDeduction);
102
        if ($points === $this->core->zero()) {
103
            return;
104
        }
105
        foreach ($review->assigned_posts as $postId) {
106
            $post = mycred_get_post($postId);
107
            if (Arr::get($post, 'post_author') === $review->author_id) {
108
                continue;
109
            }
110
            $this->core->add_creds(
111
                $reference,
112
                $post->post_author,
113
                $points,
114
                $this->getEntryFor('assigned_author', $isDeduction),
115
                $postId,
116
                [
117
                    'ref_type' => 'post',
118
                    'review_id' => $review->ID,
119
                ],
120
                $this->mycred_type
121
            );
122
        }
123
    }
124
125
    protected function processReviewerPoints(string $reference, Review $review, bool $isDeduction = false): void
126
    {
127
        $points = $this->getPointsFor('reviewer', $isDeduction);
128
        if ($points === $this->core->zero()) {
129
            return;
130
        }
131
        if ($this->userExceedsLimits($review, $isDeduction)) {
132
            return;
133
        }
134
        $this->core->add_creds(
135
            $reference,
136
            $review->author_id,
137
            $points,
138
            $this->getEntryFor('reviewer', $isDeduction),
139
            $review->ID,
140
            [
141
                'ref_type' => glsr()->post_type,
142
            ],
143
            $this->mycred_type
144
        );
145
    }
146
147
    protected function processUserPoints(string $reference, Review $review, bool $isDeduction = false): void
148
    {
149
        $points = $this->getPointsFor('assigned_user', $isDeduction);
150
        if ($points === $this->core->zero()) {
151
            return;
152
        }
153
        foreach ($review->assigned_users as $userId) {
154
            if ($userId === $review->author_id) {
155
                continue;
156
            }
157
            $this->core->add_creds(
158
                $reference,
159
                $userId,
160
                $points,
161
                $this->getEntryFor('assigned_user', $isDeduction),
162
                $review->ID,
163
                [
164
                    'ref_type' => glsr()->post_type,
165
                ],
166
                $this->mycred_type
167
            );
168
        }
169
    }
170
171
    protected function reviewApproved(Review $review): void
172
    {
173
        $this->processReviewerPoints('review_approved', $review, false);
174
        $this->processAuthorPoints('review_approved', $review, false);
175
        $this->processUserPoints('review_approved', $review, false);
176
    }
177
178
    protected function reviewTrashed(Review $review): void
179
    {
180
        $this->processReviewerPoints('review_trashed', $review, true);
181
        $this->processAuthorPoints('review_trashed', $review, true);
182
        $this->processUserPoints('review_trashed', $review, true);
183
    }
184
185
    protected function reviewUnapproved(Review $review): void
186
    {
187
        $this->processReviewerPoints('review_unapproved', $review, true);
188
        $this->processAuthorPoints('review_unapproved', $review, true);
189
        $this->processUserPoints('review_unapproved', $review, true);
190
    }
191
192
    protected function userExceedsLimits(Review $review, bool $isDeduction): bool
193
    {
194
        if (empty($review->author_id)) {
195
            return true;
196
        }
197
        $limits = [
198
            'per_day' => [
199
                date('Y-m-d', strtotime($review->date)),
200
            ],
201
            'per_post' => $review->assigned_posts ?: [0],
202
        ];
203
        foreach ($limits as $key => $values) {
204
            if ($this->userLimitExceeded($review->author_id, $isDeduction, $key, $values)) {
205
                return true;
206
            }
207
        }
208
        return false;
209
    }
210
211
    protected function userLimitExceeded(int $userId, bool $isDeduction, string $key, array $values): bool
212
    {
213
        $limit = Arr::getAs('int', $this->prefs, 'reviewer.'.$key);
214
        if (0 === $limit) {
215
            return false;
216
        }
217
        $limitExceeded = true;
218
        $metaKey = $this->userLimitMetaKey($key);
219
        $metaValue = Arr::consolidate(mycred_get_user_meta($userId, $metaKey, '', true));
0 ignored issues
show
Bug introduced by
The function mycred_get_user_meta was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

219
        $metaValue = Arr::consolidate(/** @scrutinizer ignore-call */ mycred_get_user_meta($userId, $metaKey, '', true));
Loading history...
220
        foreach ($values as $id) {
221
            $total = Arr::getAs('int', $metaValue, $id, 0);
222
            $metaValue[$id] = $isDeduction ? max(0, --$total) : ++$total;
223
            if ($total > $limit) {
224
                continue;
225
            }
226
            if (0 === $total) {
227
                unset($metaValue[$id]);
228
            }
229
            $limitExceeded = false;
230
        }
231
        if (false === $limitExceeded) {
232
            mycred_update_user_meta($userId, $metaKey, '', $metaValue);
0 ignored issues
show
Bug introduced by
The function mycred_update_user_meta was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

232
            /** @scrutinizer ignore-call */ 
233
            mycred_update_user_meta($userId, $metaKey, '', $metaValue);
Loading history...
233
        }
234
        return $limitExceeded;
235
    }
236
237
    protected function userLimitMetaKey(string $key): string
238
    {
239
        return $this->is_main_type
240
            ? sprintf('mycred_review_limit_%s', $key)
241
            : sprintf('mycred_review_limit_%s_%s', $key, $this->mycred_type);
242
    }
243
}
244