Passed
Push — master ( cb809f...d53a79 )
by Andreas
10:24
created

net_nehmer_comments_comment   A

Complexity

Total Complexity 38

Size/Duplication

Total Lines 296
Duplicated Lines 0 %

Test Coverage

Coverage 32%

Importance

Changes 0
Metric Value
eloc 148
dl 0
loc 296
ccs 48
cts 150
cp 0.32
rs 9.36
c 0
b 0
f 0
wmc 38

12 Methods

Rating   Name   Duplication   Size   Complexity  
A list_by_objectguid_filter_anonymous() 0 12 2
A list_by_objectguid() 0 10 2
A count_by_objectguid() 0 4 1
A _prepare_query() 0 16 3
A get_logs() 0 32 5
A get_class_magic_default_privileges() 0 7 1
B _cache_ratings() 0 49 9
A get_default_status() 0 14 2
A moderate() 0 16 2
A _construct_message() 0 28 2
A _on_updated() 0 7 2
B _send_notifications() 0 33 7
1
<?php
2
/**
3
 * @package net.nehmer.comments
4
 * @author The Midgard Project, http://www.midgard-project.org
5
 * @copyright The Midgard Project, http://www.midgard-project.org
6
 * @license http://www.gnu.org/licenses/lgpl.html GNU Lesser General Public License
7
 */
8
9
/**
10
 * Comments main comment class
11
 *
12
 * Comments link up to the object they refer to.
13
 *
14
 * @property string $author
15
 * @property string $objectguid
16
 * @property string $title
17
 * @property string $content
18
 * @property integer $rating
19
 * @property string $remoteid Remote ID for comments fetched from external source (rss …)
20
 * @property string $ip
21
 * @property integer $status
22
 * @package net.nehmer.comments
23
 */
24
class net_nehmer_comments_comment extends midcom_core_dbaobject
25
{
26
    public string $__midcom_class_name__ = __CLASS__;
27
    public string $__mgdschema_class_name__ = 'net_nehmer_comments_comment_db';
28
29
    // New messages enter at 4, and can be lowered or raised
30
    const JUNK = 1;
31
    const ABUSE = 2;
32
    const REPORTED_ABUSE = 3;
33
    const NEW_ANONYMOUS = 4;
34
    const NEW_USER = 5;
35
    const MODERATED = 6;
36
37
    var bool $_send_notification = false;
38
39
    /**
40
     * DBA magic defaults which assign write privileges for all USERS, so that they can
41
     * add new comments at will.
42
     */
43
    public function get_class_magic_default_privileges() : array
44
    {
45
        return [
46
            'EVERYONE' => [],
47
            'ANONYMOUS' => [],
48
            'USERS' => [
49
                'midgard:create' => MIDCOM_PRIVILEGE_ALLOW
50
            ],
51
        ];
52
    }
53
54
    /**
55
     * Returns a list of comments applicable to a given object, ordered by creation
56
     * date.
57
     *
58
     * @return net_nehmer_comments_comment[] List of applicable comments.
59
     */
60 210
    public static function list_by_objectguid(string $guid, $limit = false, string $order = 'ASC', $paging = false)
61
    {
62 210
        $qb = self::_prepare_query($guid, $paging, $limit);
63 210
        $qb->add_order('metadata.published', $order);
64
65 210
        if ($paging !== false) {
66
            return $qb;
67
        }
68
69 210
        return $qb->execute();
70
    }
71
72
    /**
73
     * Returns a list of comments applicable to a given object
74
     * not displaying empty comments or anonymous posts,
75
     * ordered by creation date.
76
     *
77
     * @return net_nehmer_comments_comment[] List of applicable comments.
78
     */
79 1
    public static function list_by_objectguid_filter_anonymous(string $guid, $limit = false, string $order = 'ASC', $paging = false)
80
    {
81 1
        $qb = self::_prepare_query($guid, $paging, $limit);
82 1
        $qb->add_order('metadata.published', $order);
83 1
        $qb->add_constraint('author', '<>', '');
84 1
        $qb->add_constraint('content', '<>', '');
85
86 1
        if ($paging !== false) {
87
            return $qb;
88
        }
89
90 1
        return $qb->execute();
91
    }
92
93
    /**
94
     * Returns the number of comments associated with a given object. This is intended for
95
     * outside usage to render stuff like "15 comments". The count is executed unchecked.
96
     */
97
    public static function count_by_objectguid(string $guid) : int
98
    {
99
        $qb = self::_prepare_query($guid);
100
        return $qb->count_unchecked();
101
    }
102
103 211
    private static function _prepare_query(string $guid, $paging = false, $limit = false) : midcom_core_querybuilder
104
    {
105 211
        if ($paging !== false) {
106
            $qb = new org_openpsa_qbpager(self::class, 'net_nehmer_comments_comment');
107
            $qb->results_per_page = $paging;
108
        } else {
109 211
            $qb = self::new_query_builder();
110 211
            if ($limit) {
111 2
                $qb->set_limit($limit);
112
            }
113
        }
114
115 211
        $qb->add_constraint('status', 'IN', self::get_default_status());
116 211
        $qb->add_constraint('objectguid', '=', $guid);
117
118 211
        return $qb;
119
    }
120
121
    public function get_logs() : array
122
    {
123
        $log_entries = [];
124
        $logs = $this->list_parameters('net.nehmer.comments:moderation_log');
125
        foreach ($logs as $action => $details) {
126
            // TODO: Show everything only to moderators
127
            $log_action  = explode(':', $action);
128
            $log_details = explode(':', $details);
129
130
            if (count($log_action) == 2) {
131
                switch ($log_details[0]) {
132
                    case 'anonymous':
133
                        $reporter = 'anonymous';
134
                        break;
135
                    case 'linksleeve':
136
                        $reporter = 'linksleeve';
137
                        break;
138
                    default:
139
                        $user = midcom::get()->auth->get_user($log_details[0]);
140
                        $reporter = $user->name;
141
                        break;
142
                }
143
144
                $log_entries[$log_action[1]] = [
145
                    'action'   => $log_action[0],
146
                    'reporter' => $reporter,
147
                    'ip'       => $log_details[1],
148
                    'browser'  => $log_details[2],
149
                ];
150
            }
151
        }
152
        return $log_entries;
153
    }
154
155 1
    public function moderate(int $status, string $action, $reporter = null) : bool
156
    {
157 1
        $this->status = $status;
158 1
        if (!$this->update()) {
159
            return false;
160
        }
161
        // Log who reported it
162 1
        $reporter ??= midcom::get()->auth->user->guid ?? 'anonymous';
163 1
        $browser = str_replace(':', '_', $_SERVER['HTTP_USER_AGENT']);
164 1
        $address = str_replace(':', '_', $_SERVER['REMOTE_ADDR']);
165
166 1
        $log_action = [$action, gmdate('Ymd\This')];
167 1
        $log_details = [$reporter, $address, $browser];
168
169 1
        $this->set_parameter('net.nehmer.comments:moderation_log', implode(':', $log_action), implode(':', $log_details));
170 1
        return true;
171
    }
172
173 211
    public static function get_default_status() : array
174
    {
175 211
        $view_status = [
176 211
            self::NEW_ANONYMOUS,
177 211
            self::NEW_USER,
178 211
            self::MODERATED,
179 211
        ];
180
181 211
        $config = midcom_baseclasses_components_configuration::get('net.nehmer.comments', 'config');
182 211
        if ($config->get('show_reported_abuse_as_normal')) {
183 211
            $view_status[] = self::REPORTED_ABUSE;
184
        }
185
186 211
        return $view_status;
187
    }
188
189
    /**
190
     * Update possible ratings cache as requested in configuration
191
     */
192 2
    private function _cache_ratings()
193
    {
194 2
        $config = midcom_baseclasses_components_configuration::get('net.nehmer.comments', 'config');
195
196 2
        if (   $config->get('ratings_enable')
197 2
            && (    $config->get('ratings_cache_to_object')
198 2
                 || $config->get('comment_count_cache_to_object'))) {
199
            // Handle ratings
200
            $comments = self::list_by_objectguid($this->objectguid);
201
            $ratings_total = 0;
202
            $rating_comments = 0;
203
            $value = 0;
204
            foreach ($comments as $comment) {
205
                if (!empty($comment->rating)) {
206
                    $rating_comments++;
207
                    $ratings_total += $comment->rating;
208
                }
209
            }
210
211
            // Get parent object
212
            $parent_property = $config->get('ratings_cache_to_object_property');
213
            midcom::get()->auth->request_sudo('net.nehmer.comments');
214
            $parent_object = $this->get_parent();
215
216
            if ($config->get('ratings_cache_total')) {
217
                $value = $ratings_total;
218
            } elseif ($rating_comments != 0) {
219
                $value = $ratings_total / $rating_comments;
220
            }
221
222
            if ($config->get('ratings_cache_to_object_property_metadata')) {
223
                $parent_object->metadata->set($parent_property, round($value));
224
            } else {
225
                $orig_rcs = $parent_object->_use_rcs;
226
                $parent_object->_use_rcs = (boolean) $config->get('ratings_cache_to_object_use_rcs');
227
                // TODO: Figure out whether to round
228
                $parent_object->$parent_property = $value;
229
                $parent_object->update();
230
                $parent_object->_use_rcs = $orig_rcs;
231
            }
232
233
            $parent_property = $config->get('comment_count_cache_to_object_property');
234
            $orig_rcs = $parent_object->_use_rcs;
235
            $parent_object->_use_rcs = (boolean) $config->get('comment_count_cache_to_object_use_rcs');
236
            $parent_object->$parent_property = count($comments);
237
            $parent_object->update();
238
            $parent_object->_use_rcs = $orig_rcs;
239
240
            midcom::get()->auth->drop_sudo();
241
        }
242
    }
243
244
    private function _send_notifications()
245
    {
246
        if (   empty($this->title)
247
            && empty($this->content)) {
248
            // No need to send notifications about empty rating entries
249
            return;
250
        }
251
        //Get the parent object
252
        $parent = $this->get_parent();
253
        if (!$parent) {
254
            return;
255
        }
256
257
        // Construct the message
258
        $message = $this->_construct_message();
259
260
        $authors = explode('|', substr($parent->metadata->authors, 1, -1));
261
        if (empty($authors)) {
262
            // Fall back to original creator if authors are not set for some reason
263
            $authors = [$parent->metadata->creator];
264
        }
265
266
        //Go through all the authors
267
        foreach ($authors as $author) {
268
            // Send the notification to each author of the original document
269
            org_openpsa_notifications::notify('net.nehmer.comments:comment_posted', $author, $message);
270
        }
271
272
        $subscriptions = $parent->list_parameters('net.nehmer.comments:subscription');
273
        //Go through each subscription
274
        foreach (array_keys($subscriptions) as $user_guid) {
275
            // Send notice
276
            org_openpsa_notifications::notify('net.nehmer.comments:subscription', $user_guid, $message);
277
        }
278
    }
279
280
    /**
281
     * Construct the message
282
     */
283
    private function _construct_message() : array
284
    {
285
        // Construct the message
286
        $message = [];
287
288
        // Resolve parent title
289
        $parent_object = $this->get_parent();
290
        $ref = midcom_helper_reflector::get($parent_object);
0 ignored issues
show
Bug introduced by
It seems like $parent_object can also be of type null; however, parameter $src of midcom_helper_reflector::get() does only seem to accept object|string, maybe add an additional type check? ( Ignorable by Annotation )

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

290
        $ref = midcom_helper_reflector::get(/** @scrutinizer ignore-type */ $parent_object);
Loading history...
291
        $parent_title = $ref->get_object_label($parent_object);
0 ignored issues
show
Bug introduced by
It seems like $parent_object can also be of type null; however, parameter $object of midcom_helper_reflector::get_object_label() does only seem to accept object, maybe add an additional type check? ( Ignorable by Annotation )

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

291
        $parent_title = $ref->get_object_label(/** @scrutinizer ignore-type */ $parent_object);
Loading history...
292
293
        // Resolve commenting user
294
        $auth = midcom::get()->auth;
295
        if ($auth->user) {
296
            $user_string = "{$auth->user->name} ({$auth->user->username})";
297
        } else {
298
            $user_string = "{$this->author} (" . midcom::get()->i18n->get_string('anonymous', 'midcom') . ")";
299
        }
300
301
        $message['title'] = sprintf(midcom::get()->i18n->get_string('page %s has been commented by %s', 'net.nehmer.comments'), $parent_title, $user_string);
302
303
        $message['content']  = "{$this->title}\n";
304
        $message['content'] .= "{$this->content}\n\n";
305
        $message['content'] .= midcom::get()->i18n->get_string('link to page', 'net.nemein.wiki') . ":\n";
306
        $message['content'] .= midcom::get()->permalinks->create_permalink($this->objectguid);
307
308
        $message['abstract'] = $message['title'];
309
310
        return $message;
311
    }
312
313 2
    public function _on_updated()
314
    {
315 2
        $this->_cache_ratings();
316
317 2
        if ($this->_send_notification) {
318
            //Notify authors and subscribers about the new comment
319
            $this->_send_notifications();
320
        }
321
    }
322
}
323