Completed
Push — master ( ed6913...63632c )
by Patrick
01:43
created

ShiftAPI::doSignup()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
nc 3
nop 5
dl 0
loc 16
rs 9.7333
c 0
b 0
f 0
1
<?php
2
class ShiftAPI extends VolunteerAPI
3
{
4
    use Processor;
5
6
    public function __construct()
7
    {
8
        parent::__construct('shifts');
9
    }
10
11
    public function setup($app)
12
    {
13
        parent::setup($app);
14
        $app->post('/Actions/CreateGroup', array($this, 'createGroup'));
15
        $app->post('/Actions/NewGroup', array($this, 'newGroup'));
16
        $app->post('/Actions/DeleteGroup', array($this, 'deleteGroup'));
17
        $app->post('/{shift}/Actions/Signup[/]', array($this, 'signup'));
18
        $app->post('/{shift}/Actions/Abandon[/]', array($this, 'abandon'));
19
        $app->post('/{shift}/Actions/Approve[/]', array($this, 'approvePending'));
20
        $app->post('/{shift}/Actions/Disapprove[/]', array($this, 'disapprovePending')); 
21
        $app->post('/{shift}/Actions/StartGroupSignup', array($this, 'startGroupSignup'));
22
        $app->post('/{shift}/Actions/GenerateGroupLink', array($this, 'generateGroupLink'));
23
        $app->post('/{shift}/Actions/EmptyShift[/]', array($this, 'emptyShift'));
24
        $app->post('/{shift}/Actions/ForceShiftEmpty[/]', array($this, 'forceEmpty'));
25
    }
26
27
    protected function canCreate($request)
28
    {
29
        //Check is handled by validateCreate...
30
        return true;
31
    }
32
33
    protected function canUpdate($request, $entity)
34
    {
35
 	if($this->isVolunteerAdmin($request))
36
        {
37
            return true;
38
        }
39
        return $this->isUserDepartmentLead($entity['departmentID'], $this->user);
40
    }
41
42
    protected function canDelete($request, $entity)
43
    {
44
        return $this->canUpdate($request, $entity);
45
    }
46
47
    protected function validateCreate(&$obj, $request)
48
    {
49
        if($this->isVolunteerAdmin($request))
50
        {
51
            return true;
52
        }
53
        if(!isset($obj['departmentID']))
54
        {
55
             return false;
56
        }
57
        return $this->isUserDepartmentLead($obj['departmentID'], $this->user);
58
    }
59
60
    protected function processEntry($entry, $request)
61
    {
62
        return $this->processShift($entry, $request);
63
    }
64
65
    protected function postUpdateAction($newObj, $request, $oldObj)
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
66
    {
67
        $oldShift = new \VolunteerShift(false, $oldObj);
68
        if($oldShift->isFilled() && ($oldObj['startTime'] != $newObj['startTime'] || $oldObj['endTime'] != $newObj['endTime']))
69
        {
70
            $email = new \Emails\ShiftEmail($oldShift, 'shiftChangedSource');
71
            $emailProvider = \EmailProvider::getInstance();
72
            if($emailProvider->sendEmail($email) === false)
73
            {
74
                throw new \Exception('Unable to send email!');
75
            }
76
        }
77
        return true;
78
    }
79
80
    protected function postDeleteAction($entry)
81
    {
82
        if(empty($entry))
83
        {
84
            return true;
85
        }
86
        $shift = new \VolunteerShift(false, $entry[0]);
87 View Code Duplication
        if($shift->isFilled())
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
88
        {
89
            $email = new \Emails\ShiftEmail($shift, 'shiftCanceledSource');
90
            $emailProvider = \EmailProvider::getInstance();
91
            if($emailProvider->sendEmail($email) === false)
92
            {
93
                throw new \Exception('Unable to send email!');
94
            } 
95
        }
96
        return true;
97
    }
98
99
    protected function genUUID()
100
    {
101
        return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
102
            // 32 bits for "time_low"
103
            mt_rand(0, 0xffff), mt_rand(0, 0xffff),
104
105
            // 16 bits for "time_mid"
106
            mt_rand(0, 0xffff),
107
108
            // 16 bits for "time_hi_and_version",
109
            // four most significant bits holds version number 4
110
            mt_rand(0, 0x0fff) | 0x4000,
111
112
            // 16 bits, 8 bits for "clk_seq_hi_res",
113
            // 8 bits for "clk_seq_low",
114
            // two most significant bits holds zero and one for variant DCE1.1
115
            mt_rand(0, 0x3fff) | 0x8000,
116
117
            // 48 bits for "node"
118
            mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
119
        );
120
    }
121
122
    public function createGroup($request, $response)
123
    {
124
        $array = $request->getParsedBody();
125
        $count = count($array);
126
        $entArray = array();
127
        $uuid = $this->genUUID();
128
        $dataTable = $this->getDataTable();
129
        //User must be able to edit all shifts
130
        for($i = 0; $i < $count; $i++)
131
        {
132
            $filter = $this->getFilterForPrimaryKey($array[$i]);
133
            $entity = $dataTable->read($filter);
134
            if($entity === false || !isset($entity[0]))
135
            {
136
                return $response->withStatus(404);
137
            }
138
            $entity = $entity[0];
139
            if(!$this->canUpdate($request, $entity))
140
            {
141
                return $response->withStatus(401);
142
            }
143
            $entity['groupID'] = $uuid;
144
            array_push($entArray, $entity);
145
        }
146
        //If we got here we can update them all
147
        $myRet = true;
148
        $errors = array();
149
        for($i = 0; $i < $count; $i++)
150
        {
151
            $filter = $this->getFilterForPrimaryKey($array[$i]);
152
            $ret = $dataTable->update($filter, $entArray[$i]);
153
            if($ret === false)
154
            {
155
               $myRet = false;
156
               array_push($errors, $array[$i]);
157
            }
158
        }
159
        if($myRet)
160
        {
161
            return $response->withJson($myRet);
162
        }
163
        else
164
        {
165
            return $response->withJson(array('res'=>$myRet, 'errors'=>$errors));
166
        }
167
    }
168
169
    public function newGroup($request, $response)
170
    {
171
        if(!$this->canCreate($request))
172
        {
173
            return $response->withStatus(401);
174
        }
175
        $data = $request->getParsedBody();
176
        $shift = array();
177
        $shift['groupID'] = $this->genUUID();
178
        $shift['departmentID'] = $data['groupDepartmentID'];
179
        $shift['earlyLate'] = $data['groupEarlyLate'];
180
        $shift['enabled'] = $data['groupEnabled'];
181
        $shift['endTime'] = $data['groupEndTime'];
182
        $shift['eventID'] = $data['groupEvent'];
183
        $shift['name'] = $data['groupName'];
184
        $shift['startTime'] = $data['groupStartTime'];
185
        $dataTable = $this->getDataTable();
186
        $ret = true;
187
        foreach($data['roles'] as $role=>$count)
188
        {
189
            $count = intval($count);
190
            for($i = 0; $i < $count; $i++)
191
            {
192
                $shift['roleID'] = $role;
193
                if($dataTable->create($shift) === false)
194
                {
195
                    $ret = false;
196
                }
197
            }
198
        }
199
        return $response->withJSON($ret);
200
    }
201
202
    public function deleteGroup($request, $response)
203
    {
204
        $data = $request->getParsedBody();
205
        $dataTable = $this->getDataTable();
206
        $filter = new \Data\Filter('groupID eq '.$data['groupID']);
207
        $entities = $dataTable->read($filter);
208
        if(empty($entities))
209
        {
210
            return $response->withStatus(404);
211
        }
212
        if(!$this->canUpdate($request, $entities[0]))
213
        {
214
            return $response->withStatus(401);
215
        }
216
        $res = $dataTable->delete($filter);
217
        if($res)
218
        {
219
            return $response->withJSON($res);
220
        }
221
        return $response->withJSON($res, 500);
222
    }
223
224
    protected function doSignup($uid, $status, $entity, $filter, $dataTable)
225
    {
226
        if(isset($entity['earlyLate']) && $entity['earlyLate'] !== '-1')
227
        {
228
            $event = new \VolunteerEvent($entity['eventID']);
229
            if(!$event->hasVolOnEEList($uid, intval($entity['earlyLate'])))
230
            {
231
                $status = 'pending';
232
                $entity['needEEApproval'] = true;
233
                $event->addToEEList($uid, intval($entity['earlyLate']));
234
            }
235
        }
236
        $entity['participant'] = $uid;
237
        $entity['status'] = $status;
238
        return $dataTable->update($filter, $entity);
239
    }
240
241
    public function signup($request, $response, $args)
242
    {
243
        $this->validateLoggedIn($request);
244
        $shiftId = $args['shift'];
245
        $dataTable = $this->getDataTable();
246
        $filter = $this->getFilterForPrimaryKey($shiftId);
247
        $entity = $dataTable->read($filter);
248
        if(empty($entity))
249
        {
250
            return $response->withStatus(404);
251
        }
252
        $entity = $entity[0];
253 View Code Duplication
        if(isset($entity['participant']) && strlen($entity['participant']) > 0)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
254
        {
255
            return $response->withStatus(401);
256
        }
257
        $shift = new \VolunteerShift($shiftId, $entity);
258
        $entity = $this->processShift($entity, $request);
259
        if(isset($entity['minShifts']) && $entity['minShifts'] > 0)
260
        {
261
          $shift->makeCopy($dataTable);
262
        }
263
        if(isset($entity['overlap']) && $entity['overlap'])
264
        {
265
            $overlaps = $shift->findOverlaps($this->user->uid);
266
            $count = count($overlaps);
267
            $leads = array();
268
            for($i = 0; $i < $count; $i++)
269
            {
270
                $dept = new \VolunteerDepartment($overlaps[$i]->departmentID);
271
                $leads = array_merge($leads, $dept->getLeadEmails());
272
                $overlaps[$i]->status = 'pending';
273
                $tmp = new \Data\Filter('_id eq '.$overlaps[$i]->{'_id'});
274
                $res = $dataTable->update($tmp, $overlaps[$i]);
275
                if($res === false)
276
                {
277
                    return $response->withJSON(array('err'=>'Unable to update overlap with id '.$overlaps[$i]->{'_id'}));
278
                }
279
            }
280
            $dept = new \VolunteerDepartment($entity['departmentID']);
281
            $leads = array_merge($leads, $dept->getLeadEmails());
282
            $leads = array_unique($leads);
283
            $ret = $this->doSignup($this->user->uid, 'pending', $entity, $filter, $dataTable);
284
            $profile = new \VolunteerProfile($this->user->uid);
285
            $email = new \Emails\TwoShiftsAtOnceEmail($profile);
286
            $email->addLeads($leads);
287
            $emailProvider = \EmailProvider::getInstance();
288
            if($emailProvider->sendEmail($email) === false)
289
            {
290
                throw new \Exception('Unable to send duplicate email!');
291
            }
292
            return $response->withJSON($ret);
293
        }
294 View Code Duplication
        if(isset($entity['available']) && $entity['available'])
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
295
        {
296
            $ret = $this->doSignup($this->user->uid, 'filled', $entity, $filter, $dataTable);
297
            return $response->withJSON($ret);
298
        }
299 View Code Duplication
        if(isset($entity['status']) && $entity['status'] === 'groupPending')
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
300
        {
301
            $ret = $this->doSignup($this->user->uid, 'filled', $entity, $filter, $dataTable);
302
            return $response->withJSON($ret);
303
        }
304
        print_r($entity); die();
305
    }
306
307
    public function abandon($request, $response, $args)
308
    {
309
        $this->validateLoggedIn($request);
310
        $shiftId = $args['shift'];
311
        $dataTable = $this->getDataTable();
312
        $filter = $this->getFilterForPrimaryKey($shiftId);
313
        $entity = $dataTable->read($filter);
314
        if(empty($entity))
315
        {
316
            return $response->withStatus(404);
317
        }
318
        $entity = $entity[0];
319
        if(!isset($entity['participant']) || $entity['participant'] !== $this->user->uid)
320
        {
321
            return $response->withStatus(401);
322
        }
323
        $entity['participant'] = '';
324
        $entity['status'] = 'unfilled';
325
        return $response->withJSON($dataTable->update($filter, $entity));
326
    }
327
328
    public function approvePending($request, $response, $args)
329
    {
330
        if(!$this->canCreate($request))
331
        {
332
            return $response->withStatus(401);
333
        }
334
        $shiftId = $args['shift'];
335
        $dataTable = $this->getDataTable();
336
        $filter = $this->getFilterForPrimaryKey($shiftId);
337
        $entity = $dataTable->read($filter);
338
        if(empty($entity))
339
        {
340
            return $response->withStatus(404);
341
        }
342
        $entity = $entity[0];
343
        $entity['status'] = 'filled';
344
        return $response->withJSON($dataTable->update($filter, $entity));
345
    }
346
347
    public function disapprovePending($request, $response, $args)
348
    {
349
        if(!$this->canCreate($request))
350
        {
351
            return $response->withStatus(401);
352
        }
353
        $shiftId = $args['shift'];
354
        $dataTable = $this->getDataTable();
355
        $filter = $this->getFilterForPrimaryKey($shiftId);
356
        $entity = $dataTable->read($filter);
357
        if(empty($entity))
358
        {
359
            return $response->withStatus(404);
360
        }
361
        $entity['participant'] = '';
362
        $entity['status'] = 'unfilled';
363
        $profile = new \VolunteerProfile($this->user->uid);
364
        $email = new \Emails\PendingRejectedEmail($profile);
365
        $email->setShift($entity);
366
        $emailProvider = \EmailProvider::getInstance();
367
        if($emailProvider->sendEmail($email) === false)
368
        {
369
            throw new \Exception('Unable to send duplicate email!');
370
        }
371
        return $response->withJSON($dataTable->update($filter, $entity));
372
    }
373
374
    public function startGroupSignup($request, $response, $args)
375
    {
376
        $this->validateLoggedIn($request);
377
        $shiftId = $args['shift'];
378
        $dataTable = $this->getDataTable();
379
        $filter = $this->getFilterForPrimaryKey($shiftId);
380
        $entity = $dataTable->read($filter);
381
        if(empty($entity))
382
        {
383
            return $response->withStatus(404);
384
        }
385
        $entity = $entity[0];
386 View Code Duplication
        if(isset($entity['participant']) && strlen($entity['participant']) > 0)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
387
        {
388
            return $response->withStatus(401);
389
        }
390
        $filter = new \Data\Filter('groupID eq '.$entity['groupID'].' and enabled eq true');
391
        $entities = $dataTable->read($filter);
392
        $count = count($entities);
393
        $dept = new \VolunteerDepartment($entity['departmentID']);
394
        $res = array();
395
        $res['department'] = $dept->departmentName;
0 ignored issues
show
Bug introduced by
The property departmentName does not seem to exist in VolunteerDepartment.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
396
        $res['earlyLate'] = $entity['earlyLate'];
397
        $res['endTime'] = $entity['endTime'];
398
        $res['eventID'] = $entity['eventID'];
399
        $res['name'] = $entity['name'];
400
        $res['startTime'] = $entity['startTime'];
401
        $res['groupID'] = $entity['groupID'];
402
        $res['shifts'] = array();
403
        $roles = array();
404
        for($i = 0; $i < $count; $i++)
405
        {
406 View Code Duplication
            if(isset($entities[$i]['status']) && ($entities[$i]['status'] === 'filled' || $entities[$i]['status'] === 'pending'))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
407
            {
408
                continue;
409
            }
410
            if(!isset($roles[$entities[$i]['roleID']]))
411
            {
412
                $roles[$entities[$i]['roleID']] = new \VolunteerRole($entities[$i]['roleID']);
413
            }
414
            $role = $roles[$entities[$i]['roleID']];
415
            $entities[$i]['role'] = $role->display_name;
416
            array_push($res['shifts'], $entities[$i]);
417
        }
418
        return $response->withJSON($res);
419
    }
420
421
    public function generateGroupLink($request, $response, $args)
422
    {
423
        $this->validateLoggedIn($request);
424
        $shiftId = $args['shift'];
425
        $dataTable = $this->getDataTable();
426
        $filter = $this->getFilterForPrimaryKey($shiftId);
427
        $entity = $dataTable->read($filter);
428
        if(empty($entity))
429
        {
430
            return $response->withStatus(404);
431
        }
432
        $entity = $entity[0];
433 View Code Duplication
        if(isset($entity['participant']) && strlen($entity['participant']) > 0)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
434
        {
435
            return $response->withStatus(401);
436
        }
437
        $data = $request->getParsedBody();
438
        $myShift = $data['myshift'];
439
        $roles = array();
440
        foreach($data as $key => $value)
441
        {
442
            if(substr($key, 0, 6) === "roles.")
443
            {
444
                $roles[substr($key, 6)] = $value;
445
            }
446
        }
447
        $filter = new \Data\Filter('groupID eq '.$entity['groupID'].' and enabled eq true');
448
        $entities = $dataTable->read($filter);
449
        $count = count($entities);
450
        $uuid = $this->genUUID();
451
        for($i = 0; $i < $count; $i++)
452
        {
453 View Code Duplication
            if(isset($entities[$i]['status']) && ($entities[$i]['status'] === 'filled' || $entities[$i]['status'] === 'pending'))
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
454
            {
455
                $entities[$i] = false;
456
                continue;
457
            }
458
            if((string)$entities[$i]['_id'] === (string)new \MongoDB\BSON\ObjectId($myShift))
459
            {
460
                $entities[$i]['participant'] = $this->user->uid;
461
                $entities[$i]['status'] = 'filled';
462
                $entities[$i]['signupLink'] = $uuid;
463
            }
464
            else if(isset($roles[$entities[$i]['roleID']]))
465
            {
466
                $entities[$i]['status'] = 'groupPending';
467
                $entities[$i]['signupLink'] = $uuid;
468
                $roles[$entities[$i]['roleID']]--;
469
                if($roles[$entities[$i]['roleID']] === 0)
470
                {
471
                    unset($roles[$entities[$i]['roleID']]);
472
                }
473
            }
474
            else
475
            {
476
                $entities[$i] = false;
477
            }
478
        }
479
        if(count($roles) !== 0)
480
        {
481
            throw new \Exception('Not enough shifts to fullfill requests');
482
        }
483
        for($i = 0; $i < $count; $i++)
484
        {
485
            if($entities[$i] === false)
486
            {
487
                continue;
488
            }
489
            $filter = new \Data\Filter('_id eq '.$entities[$i]['_id']);
490
            $res = $dataTable->update($filter, $entities[$i]);
491
            if($res === false)
492
            {
493
                throw new \Exception('Not able to save shift '.$entities[$i]['_id']);
494
            }
495
        }
496
        return $response->withJSON(array('uuid' => $uuid));
497
    }
498
499
    function emptyShift($request, $response, $args)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
500
    {
501
        $this->validateLoggedIn($request);
502
        $shiftId = $args['shift'];
503
        $dataTable = $this->getDataTable();
504
        $filter = $this->getFilterForPrimaryKey($shiftId);
505
        $entity = $dataTable->read($filter);
506
        if(empty($entity))
507
        {
508
            return $response->withStatus(404);
509
        }
510
        $entity = $entity[0];
511
        if(!$this->canUpdate($request, $entity))
512
        {
513
            return $response->withStatus(401);
514
        }
515
        $shift = new \VolunteerShift(false, $entity);
516
        $entity['participant'] = '';
517
        $entity['status'] = 'unfilled';
518
        $ret = $dataTable->update($filter, $entity);
519 View Code Duplication
        if($ret)
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
520
        {
521
            $email = new \Emails\ShiftEmail($shift, 'shiftEmptiedSource');
522
            $emailProvider = \EmailProvider::getInstance();
523
            if($emailProvider->sendEmail($email) === false)
524
            {
525
                throw new \Exception('Unable to send email!');
526
            }
527
        }
528
        return $response->withJSON($ret);
529
    }
530
531
    function forceEmpty($request, $response, $args)
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
532
    {
533
        $this->validateLoggedIn($request);
534
        $shiftId = $args['shift'];
535
        $dataTable = $this->getDataTable();
536
        $filter = $this->getFilterForPrimaryKey($shiftId);
537
        $entity = $dataTable->read($filter);
538
        if(empty($entity))
539
        {
540
            return $response->withStatus(404);
541
        }
542
        $entity = $entity[0];
543
        if(!$this->canUpdate($request, $entity))
544
        {
545
            return $response->withStatus(401);
546
        }
547
        $entity['participant'] = '';
548
        $entity['status'] = 'unfilled';
549
        return $response->withJSON($dataTable->update($filter, $entity));
550
    }
551
}
552