Passed
Push — master ( 02ae83...de4532 )
by Andreas
26:58
created

org_openpsa_calendar_event_dba   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 314
Duplicated Lines 0 %

Test Coverage

Coverage 63.36%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 126
c 2
b 0
f 0
dl 0
loc 314
ccs 83
cts 131
cp 0.6336
rs 8.96
wmc 43

14 Methods

Rating   Name   Duplication   Size   Complexity  
A get_label() 0 7 2
A _on_creating() 0 3 1
B _prepare_save() 0 41 9
A _check_timerange() 0 23 4
A _on_loaded() 0 23 4
A _on_updating() 0 4 1
A details_text() 0 10 1
A _get_resources() 0 5 1
A implode_members() 0 8 2
A _on_created() 0 7 2
A _get_em() 0 12 2
A _get_participants() 0 5 1
B _on_updated() 0 41 8
A _on_deleting() 0 24 5

How to fix   Complexity   

Complex Class

Complex classes like org_openpsa_calendar_event_dba 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 org_openpsa_calendar_event_dba, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * @package org.openpsa.calendar
4
 * @author Nemein Oy, http://www.nemein.com/
5
 * @copyright Nemein Oy, http://www.nemein.com/
6
 * @license http://www.gnu.org/licenses/gpl.html GNU General Public License
7
 */
8
9
/**
10
 * MidCOM wrapper for org_openpsa_event with various helper functions
11
 * refactored from OpenPSA 1.x calendar
12
 *
13
 * @todo Figure out a good way to always use UTC for internal time storage
14
 * @property integer $start
15
 * @property integer $end
16
 * @property string $title
17
 * @property string $description
18
 * @property integer $type
19
 * @property string $extra
20
 * @property boolean $busy
21
 * @property integer $up
22
 * @property string $location
23
 * @property boolean $tentative
24
 * @property string $externalGuid
25
 * @property string $vCalSerialized
26
 * @property integer $orgOpenpsaAccesstype
27
 * @package org.openpsa.calendar
28
 */
29
class org_openpsa_calendar_event_dba extends midcom_core_dbaobject
30
{
31
    public $__midcom_class_name__ = __CLASS__;
32
    public $__mgdschema_class_name__ = 'org_openpsa_event';
33
34
    /**
35
     * list of participants
36
     *
37
     * (stored as eventmembers, referenced here for easier access)
38
     *
39
     * @var array
40
     */
41
    public $participants = [];
42
43
    /**
44
     * like $participants but for resources.
45
     *
46
     * @var array
47
     */
48
    public $resources = [];
49
50
    /**
51
     * vCalendar (or similar external source) GUID for this event
52
     *
53
     * (for vCalendar imports)
54
     *
55
     * @var string
56
     */
57
    private $old_externalGuid = '';
58
59
    /**
60
     * Send notifications to participants of the event
61
     *
62
     * @var boolean
63
     */
64
    var $send_notify = true;
65
66
    /**
67
     * Send notification also to current user
68
     *
69
     * @var boolean
70
     */
71
    public $send_notify_me = false;
72
73
    /**
74
     * Used to work around DM creation features to get correct notification type out
75
     *
76
     * @var boolean
77
     */
78
    var $notify_force_add = false;
79
80
    public $search_relatedtos = true;
81
    public $ignorebusy_em = false;
82
    public $rob_tentative = false;
83
84 2
    public function get_label() : string
85
    {
86 2
        if ($this->start == 0) {
87
            return $this->title;
88
        }
89 2
        $formatter = midcom::get()->i18n->get_l10n()->get_formatter();
90 2
        return $formatter->date($this->start) . " {$this->title}";
91
    }
92
93 11
    public function _on_loaded()
94
    {
95 11
        $l10n = midcom::get()->i18n->get_l10n('org.openpsa.calendar');
96
97
        // Preserve vCal GUIDs once set
98 11
        $this->old_externalGuid = $this->externalGuid;
99
100
        // Hide details if we're not allowed to see them
101 11
        if (!$this->can_do('org.openpsa.calendar:read')) {
102
            $keep = ['metadata', 'id', 'guid', 'start', 'end', 'orgOpenpsaAccesstype'];
103
            $hide = array_diff($this->get_properties(), $keep);
104
            foreach ($hide as $key) {
105
                $this->$key = null;
106
            }
107
            $this->title = $l10n->get('private event');
108
        }
109
        // Check for empty title
110 11
        if (!$this->title) {
111 7
            $this->title = $l10n->get('untitled');
112
        }
113
114
        // Populate resources and participants list
115 11
        $this->_get_em();
116 11
    }
117
118
    /**
119
     * Preparations related to all save operations (=create/update)
120
     */
121 9
    private function _prepare_save() : bool
122
    {
123
        // Make sure we have accessType
124 9
        if (!$this->orgOpenpsaAccesstype) {
125 9
            $this->orgOpenpsaAccesstype = org_openpsa_core_acl::ACCESS_PUBLIC;
126
        }
127
128
        // Make sure we can actually reserve the resources we need
129 9
        $resources = array_keys(array_filter($this->resources));
130 9
        $checker = new org_openpsa_calendar_event_resource_dba;
131 9
        foreach ($resources as $id) {
132
            $checker->resource = $id;
133
            if (!$checker->verify_can_reserve()) {
134
                debug_add("Cannot reserve resource #{$id}, returning false", MIDCOM_LOG_ERROR);
135
                midcom_connection::set_error(MGD_ERR_ACCESS_DENIED);
136
                return false;
137
            }
138
        }
139
140
        //Check up
141 9
        if (   !$this->up
142 9
            && $this->title != '__org_openpsa_calendar') {
143 7
            $root_event = org_openpsa_calendar_interface::find_root_event();
144 7
            $this->up = $root_event->id;
145
        }
146
147
        //check for busy participants/resources
148 9
        if (!$this->ignorebusy_em) {
149 9
            $conflictmanager = new org_openpsa_calendar_conflictmanager($this);
150 9
            if (!$conflictmanager->run($this->rob_tentative)) {
151
                debug_add("Unresolved resource conflicts, aborting", MIDCOM_LOG_WARN);
152
                return false;
153
            }
154
        }
155
156
        //Preserve vCal GUIDs once set
157 9
        if ($this->old_externalGuid) {
158
            $this->externalGuid = $this->old_externalGuid;
159
        }
160
161 9
        return $this->_check_timerange();
162
    }
163
164 9
    private function _check_timerange() : bool
165
    {
166 9
        if (   !$this->start
167 9
            || !$this->end) {
168 1
            debug_add('Event must have start and end timestamps');
169 1
            midcom_connection::set_error(MGD_ERR_RANGE);
170 1
            return false;
171
        }
172
173
        /*
174
         * Force start and end seconds to 1 and 0 respectively
175
         * (to avoid stupid one second overlaps)
176
         */
177 9
        $this->start = floor($this->start / 60) * 60 + 1;
178 9
        $this->end = (floor($this->end / 60) * 60);
179
180 9
        if ($this->end < $this->start) {
181
            debug_add('Event cannot end before it starts, aborting');
182
            midcom_connection::set_error(MGD_ERR_RANGE);
183
            return false;
184
        }
185
186 9
        return true;
187
    }
188
189 9
    public function _on_creating() : bool
190
    {
191 9
        return $this->_prepare_save();
192
    }
193
194 9
    public function _on_created()
195
    {
196
        //TODO: handle the repeats somehow (if set)
197
198 9
        if ($this->search_relatedtos) {
199
            //TODO: add check for failed additions
200 9
            (new org_openpsa_relatedto_finder_event($this))->process();
201
        }
202 9
    }
203
204 2
    public function _on_updating() : bool
205
    {
206
        //TODO: Handle repeats
207 2
        return $this->_prepare_save();
208
    }
209
210 2
    public function _on_updated()
211
    {
212 2
        $this->_get_em();
213 2
        if ($this->send_notify) {
214 2
            $message_type = 'update';
215 2
            if ($this->notify_force_add) {
216
                $message_type = 'add';
217
            }
218
219 2
            foreach ($this->_get_participants() as $res_object) {
220
                debug_add("Notifying participant #{$res_object->id}");
221
                $res_object->notify($message_type, $this);
222
            }
223
224 2
            foreach ($this->_get_resources() as $res_object) {
225
                debug_add("Notifying resource #{$res_object->id}");
226
                $res_object->notify($message_type, $this);
227
            }
228
        }
229
230
        // Handle ACL accordingly
231 2
        foreach (array_keys($this->participants) as $person_id) {
232
            $user = midcom::get()->auth->get_user($person_id);
233
234
            // All participants can read and update
235
            $this->set_privilege('org.openpsa.calendar:read', $user->id, MIDCOM_PRIVILEGE_ALLOW);
236
            $this->set_privilege('midgard:read', $user->id, MIDCOM_PRIVILEGE_ALLOW);
237
            $this->set_privilege('midgard:update', $user->id, MIDCOM_PRIVILEGE_ALLOW);
238
            $this->set_privilege('midgard:delete', $user->id, MIDCOM_PRIVILEGE_ALLOW);
239
            $this->set_privilege('midgard:create', $user->id, MIDCOM_PRIVILEGE_ALLOW);
240
            $this->set_privilege('midgard:privileges', $user->id, MIDCOM_PRIVILEGE_ALLOW);
241
        }
242
243 2
        if ($this->orgOpenpsaAccesstype == org_openpsa_core_acl::ACCESS_PRIVATE) {
244
            $this->set_privilege('org.openpsa.calendar:read', 'EVERYONE', MIDCOM_PRIVILEGE_DENY);
245
        } else {
246 2
            $this->set_privilege('org.openpsa.calendar:read', 'EVERYONE', MIDCOM_PRIVILEGE_ALLOW);
247
        }
248
249 2
        if ($this->search_relatedtos) {
250 2
            (new org_openpsa_relatedto_finder_event($this))->process();
251
        }
252 2
    }
253
254
    /**
255
     * @return org_openpsa_calendar_event_member_dba[]
256
     */
257 9
    private function _get_participants() : array
258
    {
259 9
        $qb = org_openpsa_calendar_event_member_dba::new_query_builder();
260 9
        $qb->add_constraint('eid', '=', $this->id);
261 9
        return $qb->execute_unchecked();
262
    }
263
264
    /**
265
     * @return org_openpsa_calendar_event_resource_dba[]
266
     */
267 9
    private function _get_resources() : array
268
    {
269 9
        $qb = org_openpsa_calendar_event_resource_dba::new_query_builder();
270 9
        $qb->add_constraint('event', '=', $this->id);
271 9
        return $qb->execute_unchecked();
272
    }
273
274 9
    public function _on_deleting() : bool
275
    {
276
        //Remove participants
277 9
        midcom::get()->auth->request_sudo('org.openpsa.calendar');
278 9
        foreach ($this->_get_participants() as $obj) {
279 3
            if ($this->send_notify) {
280 3
                $obj->notify('cancel', $this);
281
            }
282 3
            $obj->notify_person = false;
283 3
            $obj->delete();
284
        }
285
286
        //Remove resources
287 9
        foreach ($this->_get_resources() as $obj) {
288
            if ($this->send_notify) {
289
                $obj->notify('cancel', $this);
290
            }
291
            $obj->delete();
292
        }
293
294
        //Remove event parameters
295 9
        midcom::get()->auth->drop_sudo();
296
297 9
        return parent::_on_deleting();
298
    }
299
300
    /**
301
     * Fills $this->participants and $this->resources
302
     */
303 11
    private function _get_em()
304
    {
305 11
        if (!$this->id) {
306
            return;
307
        }
308
309
        // Participants
310 11
        $mc = org_openpsa_calendar_event_member_dba::new_collector('eid', $this->id);
311 11
        $this->participants = array_fill_keys($mc->get_values('uid'), true);
312
        // Resources
313 11
        $mc2 = org_openpsa_calendar_event_resource_dba::new_collector('event', $this->id);
314 11
        $this->resources = array_fill_keys($mc2->get_values('resource'), true);
315 11
    }
316
317
    /**
318
     * Returns a string describing the event and its participants
319
     */
320
    public function details_text(string $nl) : string
321
    {
322
        $l10n = midcom::get()->i18n->get_l10n('org.openpsa.calendar');
323
        $str = sprintf($l10n->get('location: %s') . $nl, $this->location);
324
        $str .= sprintf($l10n->get('time: %s') . $nl, $l10n->get_formatter()->timeframe($this->start, $this->end));
325
        $str .= sprintf($l10n->get('participants: %s') . $nl, $this->implode_members($this->participants));
326
        $str .= sprintf($l10n->get('resources: %s') . $nl, $this->implode_members($this->resources));
327
        //TODO: Tentative, overlaps, public
328
        $str .= sprintf($l10n->get('description: %s') . $nl, $this->description);
329
        return $str;
330
    }
331
332
    /**
333
     * Returns a comma separated list of persons from array
334
     */
335
    private function implode_members(array $array) : string
336
    {
337
        $output = [];
338
        foreach (array_keys($array) as $pid) {
339
            $person = midcom_db_person::get_cached($pid);
340
            $output[] = $person->name;
341
        }
342
        return implode(', ', $output);
343
    }
344
}
345