Passed
Push — master ( 83cee0...2131d7 )
by Andreas
19:23
created

net_nehmer_comments_comment   A

Complexity

Total Complexity 40

Size/Duplication

Total Lines 313
Duplicated Lines 0 %

Test Coverage

Coverage 32.42%

Importance

Changes 0
Metric Value
eloc 155
dl 0
loc 313
ccs 48
cts 148
cp 0.3242
rs 9.2
c 0
b 0
f 0
wmc 40

13 Methods

Rating   Name   Duplication   Size   Complexity  
A get_class_magic_default_privileges() 0 7 1
A get_logs() 0 32 5
A count_by_objectguid_filter_anonymous() 0 6 1
B _cache_ratings() 0 49 9
A get_default_status() 0 14 2
A moderate() 0 18 3
A _construct_message() 0 28 2
A _prepare_query() 0 16 3
A _on_updated() 0 7 2
A list_by_objectguid_filter_anonymous() 0 12 2
A list_by_objectguid() 0 10 2
B _send_notifications() 0 35 7
A count_by_objectguid() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like net_nehmer_comments_comment often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use net_nehmer_comments_comment, and based on these observations, apply Extract Interface, too.

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 longstring $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 $__midcom_class_name__ = __CLASS__;
27
    public $__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 $_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()
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 186
    public static function list_by_objectguid(string $guid, $limit = false, string $order = 'ASC', $paging = false)
61
    {
62 186
        $qb = self::_prepare_query($guid, $paging, $limit);
63 186
        $qb->add_order('metadata.published', $order);
64
65 186
        if ($paging !== false) {
66
            return $qb;
67
        }
68
69 186
        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 5
    public static function count_by_objectguid(string $guid) : int
98
    {
99 5
        $qb = self::_prepare_query($guid);
100 5
        return $qb->count_unchecked();
101
    }
102
103
    /**
104
     * Returns the number of comments associated with a given object by actual registered users.
105
     * This is intended for outside usage to render stuff like "15 comments". The count is
106
     * executed unchecked.
107
     */
108
    public static function count_by_objectguid_filter_anonymous(string $guid) : int
109
    {
110
        $qb = self::_prepare_query($guid);
111
        $qb->add_constraint('author', '<>', '');
112
        $qb->add_constraint('content', '<>', '');
113
        return $qb->count_unchecked();
114
    }
115
116 192
    private static function _prepare_query(string $guid, $paging = false, $limit = false)
117
    {
118 192
        if ($paging !== false) {
119
            $qb = new org_openpsa_qbpager(self::class, 'net_nehmer_comments_comment');
120
            $qb->results_per_page = $paging;
121
        } else {
122 192
            $qb = self::new_query_builder();
123 192
            if ($limit) {
124 2
                $qb->set_limit($limit);
125
            }
126
        }
127
128 192
        $qb->add_constraint('status', 'IN', self::get_default_status());
129 192
        $qb->add_constraint('objectguid', '=', $guid);
130
131 192
        return $qb;
132
    }
133
134
    public function get_logs() : array
135
    {
136
        $log_entries = [];
137
        $logs = $this->list_parameters('net.nehmer.comments:moderation_log');
138
        foreach ($logs as $action => $details) {
139
            // TODO: Show everything only to moderators
140
            $log_action  = explode(':', $action);
141
            $log_details = explode(':', $details);
142
143
            if (count($log_action) == 2) {
144
                switch ($log_details[0]) {
145
                    case 'anonymous':
146
                        $reporter = 'anonymous';
147
                        break;
148
                    case 'linksleeve':
149
                        $reporter = 'linksleeve';
150
                        break;
151
                    default:
152
                        $user = midcom::get()->auth->get_user($log_details[0]);
153
                        $reporter = $user->name;
154
                        break;
155
                }
156
157
                $log_entries[$log_action[1]] = [
158
                    'action'   => $log_action[0],
159
                    'reporter' => $reporter,
160
                    'ip'       => $log_details[1],
161
                    'browser'  => $log_details[2],
162
                ];
163
            }
164
        }
165
        return $log_entries;
166
    }
167
168 1
    public function moderate(int $status, string $action, $reporter = null) : bool
169
    {
170 1
        $this->status = $status;
171 1
        if (!$this->update()) {
172
            return false;
173
        }
174
        // Log who reported it
175 1
        if ($reporter === null) {
176 1
            $reporter = midcom::get()->auth->user->guid ?? 'anonymous';
177
        }
178 1
        $browser = str_replace(':', '_', $_SERVER['HTTP_USER_AGENT']);
179 1
        $address = str_replace(':', '_', $_SERVER['REMOTE_ADDR']);
180
181 1
        $log_action = [$action, gmdate('Ymd\This')];
182 1
        $log_details = [$reporter, $address, $browser];
183
184 1
        $this->set_parameter('net.nehmer.comments:moderation_log', implode(':', $log_action), implode(':', $log_details));
185 1
        return true;
186
    }
187
188 192
    public static function get_default_status() : array
189
    {
190
        $view_status = [
191 192
            self::NEW_ANONYMOUS,
192
            self::NEW_USER,
193
            self::MODERATED,
194
        ];
195
196 192
        $config = midcom_baseclasses_components_configuration::get('net.nehmer.comments', 'config');
197 192
        if ($config->get('show_reported_abuse_as_normal')) {
198 192
            $view_status[] = self::REPORTED_ABUSE;
199
        }
200
201 192
        return $view_status;
202
    }
203
204
    /**
205
     * Update possible ratings cache as requested in configuration
206
     */
207 2
    private function _cache_ratings()
208
    {
209 2
        $config = midcom_baseclasses_components_configuration::get('net.nehmer.comments', 'config');
210
211 2
        if (   $config->get('ratings_enable')
212
            && (    $config->get('ratings_cache_to_object')
213 2
                 || $config->get('comment_count_cache_to_object'))) {
214
            // Handle ratings
215
            $comments = self::list_by_objectguid($this->objectguid);
216
            $ratings_total = 0;
217
            $rating_comments = 0;
218
            $value = 0;
219
            foreach ($comments as $comment) {
220
                if (!empty($comment->rating)) {
221
                    $rating_comments++;
222
                    $ratings_total += $comment->rating;
223
                }
224
            }
225
226
            // Get parent object
227
            $parent_property = $config->get('ratings_cache_to_object_property');
228
            midcom::get()->auth->request_sudo('net.nehmer.comments');
229
            $parent_object = midcom::get()->dbfactory->get_object_by_guid($this->objectguid);
230
231
            if ($config->get('ratings_cache_total')) {
232
                $value = $ratings_total;
233
            } elseif ($rating_comments != 0) {
234
                $value = $ratings_total / $rating_comments;
235
            }
236
237
            if ($config->get('ratings_cache_to_object_property_metadata')) {
238
                $parent_object->metadata->set($parent_property, round($value));
239
            } else {
240
                $orig_rcs = $parent_object->_use_rcs;
241
                $parent_object->_use_rcs = (boolean) $config->get('ratings_cache_to_object_use_rcs');
242
                // TODO: Figure out whether to round
243
                $parent_object->$parent_property = $value;
244
                $parent_object->update();
245
                $parent_object->_use_rcs = $orig_rcs;
246
            }
247
248
            $parent_property = $config->get('comment_count_cache_to_object_property');
249
            $orig_rcs = $parent_object->_use_rcs;
250
            $parent_object->_use_rcs = (boolean) $config->get('comment_count_cache_to_object_use_rcs');
251
            $parent_object->$parent_property = count($comments);
252
            $parent_object->update();
253
            $parent_object->_use_rcs = $orig_rcs;
254
255
            midcom::get()->auth->drop_sudo();
256
        }
257 2
    }
258
259
    private function _send_notifications()
260
    {
261
        if (   empty($this->title)
262
            && empty($this->content)) {
263
            // No need to send notifications about empty rating entries
264
            return;
265
        }
266
        //Get the parent object
267
        try {
268
            $parent = midcom::get()->dbfactory->get_object_by_guid($this->objectguid);
269
        } catch (midcom_error $e) {
270
            $e->log();
271
            return;
272
        }
273
274
        // Construct the message
275
        $message = $this->_construct_message();
276
277
        $authors = explode('|', substr($parent->metadata->authors, 1, -1));
278
        if (empty($authors)) {
279
            // Fall back to original creator if authors are not set for some reason
280
            $authors = [$parent->metadata->creator];
281
        }
282
283
        //Go through all the authors
284
        foreach ($authors as $author) {
285
            // Send the notification to each author of the original document
286
            org_openpsa_notifications::notify('net.nehmer.comments:comment_posted', $author, $message);
287
        }
288
289
        $subscriptions = $parent->list_parameters('net.nehmer.comments:subscription');
290
        //Go through each subscription
291
        foreach (array_keys($subscriptions) as $user_guid) {
292
            // Send notice
293
            org_openpsa_notifications::notify('net.nehmer.comments:subscription', $user_guid, $message);
294
        }
295
    }
296
297
    /**
298
     * Construct the message
299
     */
300
    private function _construct_message() : array
301
    {
302
        // Construct the message
303
        $message = [];
304
305
        // Resolve parent title
306
        $parent_object = midcom::get()->dbfactory->get_object_by_guid($this->objectguid);
307
        $ref = midcom_helper_reflector::get($parent_object);
308
        $parent_title = $ref->get_object_label($parent_object);
309
310
        // Resolve commenting user
311
        $auth = midcom::get()->auth;
312
        if ($auth->user) {
313
            $user_string = "{$auth->user->name} ({$auth->user->username})";
314
        } else {
315
            $user_string = "{$this->author} (" . midcom::get()->i18n->get_string('anonymous', 'midcom') . ")";
316
        }
317
318
        $message['title'] = sprintf(midcom::get()->i18n->get_string('page %s has been commented by %s', 'net.nehmer.comments'), $parent_title, $user_string);
319
320
        $message['content']  = "{$this->title}\n";
321
        $message['content'] .= "{$this->content}\n\n";
322
        $message['content'] .= midcom::get()->i18n->get_string('link to page', 'net.nemein.wiki') . ":\n";
323
        $message['content'] .= midcom::get()->permalinks->create_permalink($this->objectguid);
324
325
        $message['abstract'] = $message['title'];
326
327
        return $message;
328
    }
329
330 2
    public function _on_updated()
331
    {
332 2
        $this->_cache_ratings();
333
334 2
        if ($this->_send_notification) {
335
            //Notify authors and subscribers about the new comment
336
            $this->_send_notifications();
337
        }
338 2
    }
339
}
340