Passed
Push — master ( 2715f7...fbc634 )
by Laurent
02:37
created

app_RecordSet::hasAccess()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 4
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 6
rs 10
1
<?php
2
//-------------------------------------------------------------------------
3
// OVIDENTIA http://www.ovidentia.org
4
// Ovidentia is free software; you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation; either version 2, or (at your option)
7
// any later version.
8
//
9
// This program is distributed in the hope that it will be useful, but
10
// WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
// See the GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with this program; if not, write to the Free Software
16
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17
// USA.
18
//-------------------------------------------------------------------------
19
/**
20
 * @license http://opensource.org/licenses/gpl-license.php GNU General Public License (GPL)
21
 * @copyright Copyright (c) 2018 by CANTICO ({@link http://www.cantico.fr})
22
 */
23
24
25
$App = app_App();
26
$App->includeBase();
27
28
29
/**
30
 * @method app_Record   get(mixed $criteria)
31
 * @method app_Record   request(mixed $criteria)
32
 * @method app_Record[] select(\ORM_Criteria $criteria = null)
33
 * @method app_Record   newRecord()
34
 */
35
class app_RecordSet extends ORM_RecordSet implements app_Object_Interface
36
{
37
    /**
38
     * @var Func_App
39
     */
40
    protected $app = null;
41
42
    protected $accessRights = null;
43
44
    /**
45
     * @var array
46
     */
47
    private $customFields = null;
48
49
    /**
50
     * @param Func_App $app
51
     */
52
    public function __construct(Func_App $app)
53
    {
54
        parent::__construct();
55
        $this->setApp($app);
56
        $this->accessRights = array();
57
    }
58
59
    /**
60
     * {@inheritDoc}
61
     * @see app_Object_Interface::setApp()
62
     */
63
    public function setApp(Func_App $app)
64
    {
65
        $this->app = $app;
66
        return $this;
67
    }
68
69
70
    /**
71
     * {@inheritDoc}
72
     * @see app_Object_Interface::App()
73
     */
74
    public function App()
75
    {
76
        return $this->app;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->app returns the type Func_App which is incompatible with the return type mandated by app_Object_Interface::App() of app_Object_Interface.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
77
    }
78
79
    /**
80
     * @param string $setName
81
     */
82
    protected function extractSetPrefixAndName($setName)
83
    {
84
        return explode('_', $setName);
85
    }
86
87
    /**
88
     * Similar to the ORM_RecordSet::join() method but instanciates the
89
     * joined RecordSet using App methods.
90
     *
91
     * @see ORM_RecordSet::join()
92
     */
93
    public function join($fkFieldName)
94
    {
95
        $fkField = $this->getField($fkFieldName);
96
        if (!($fkField instanceof ORM_FkField)) {
97
            return $this;
98
        }
99
        $setName = $fkField->getForeignSetName();
100
101
        if (!$setName || 'Set' === $setName) {
102
            throw new Exception('The set name is missing on foreign key field '.$fkFieldName);
103
        }
104
105
        list($prefix, $appSetName) = $this->extractSetPrefixAndName($setName);
106
107
        $set = $this->App()->$appSetName();
108
        $set->setName($fkField->getName());
109
        $set->setDescription($fkField->getDescription());
110
111
        $this->aField[$fkFieldName] = $set;
112
        $set->setParentSet($this);
113
        return $this;
114
    }
115
116
117
//     /**
118
//      *
119
//      * @param string $accessName
120
//      * @param string $type
121
//      */
122
//     public function addAccessRight($accessName, $type = 'acl')
123
//     {
124
//         $this->accessRights[$accessName] = $type;
125
//     }
126
127
128
//     /**
129
//      * @return array
130
//      */
131
//     public function getAccessRights()
132
//     {
133
//         return $this->accessRights;
134
//     }
135
136
137
138
    /**
139
     * @return app_CustomField[]
140
     */
141
    public function getCustomFields()
142
    {
143
        $App = $this->App();
144
145
        if (null === $this->customFields) {
146
            $this->customFields = array();
147
148
            if (isset($App->CustomField)) {
149
                $customFieldSet = $App->CustomFieldSet();
150
                $object = mb_substr(get_class($this), mb_strlen($App->classPrefix), -mb_strlen('Set'));
151
                try {
152
                    $customFields = $customFieldSet->select($customFieldSet->object->is($object));
153
154
                    foreach ($customFields as $customfield) {
155
                        $this->customFields[] = $customfield;
156
157
                        /*@var $customfield app_CustomField */
158
159
                    }
160
                } catch (ORM_BackEndSelectException $e) {
161
                    // table does not exist, this error is thrown by the install program while creating the sets
162
                }
163
            }
164
        }
165
166
        return $this->customFields;
167
    }
168
169
170
171
172
    /**
173
     * @return app_CustomField[]
174
     */
175
    public function selectCustomFields()
176
    {
177
        $App = $this->App();
178
179
        $customFieldSet= $App->CustomFieldSet();
180
        $object = mb_substr(get_class($this), mb_strlen($App->classPrefix), -mb_strlen('Set'));
181
182
        $customFields = $customFieldSet->select($customFieldSet->object->is($object));
183
184
        return $customFields;
185
    }
186
187
188
    /**
189
     * @return self
190
     */
191
    public function addCustomFields()
192
    {
193
        $customFields = $this->selectCustomFields();
194
        foreach ($customFields as $customField) {
195
            /*@var $customField app_CustomField */
196
            $description = $customField->name;
197
            $ormField = $customField->getORMField()
198
                ->setDescription($description);
199
            if ($ormField instanceof ORM_FkField) {
200
                $this->hasOne($customField->fieldname, $ormField->getForeignSetName())
201
                    ->setDescription($description);
202
            } else {
203
                $this->addFields($ormField);
204
            }
205
        }
206
207
        return $this;
208
    }
209
210
211
    /**
212
     * Similar to ORM_RecordSet::get() method  but throws a app_NotFoundException if the
213
     * record is not found.
214
     *
215
     * @since 1.0.18
216
     *
217
     * @throws ORM_Exception
218
     * @throws app_NotFoundException
219
     *
220
     * @param ORM_Criteria|string $mixedParam    Criteria for selecting records
221
     *                                           or the value for selecting record
222
     * @param string              $sPropertyName The name of the property on which
223
     *                                           the value applies. If not
224
     *                                           specified or null, the set's
225
     *                                           primary key will be used.
226
     *
227
     * @return app_Record
228
     */
229
    public function request($mixedParam = null, $sPropertyName = null)
230
    {
231
        $record = $this->get($mixedParam, $sPropertyName);
0 ignored issues
show
Unused Code introduced by
The call to app_RecordSet::get() has too many arguments starting with $sPropertyName. ( Ignorable by Annotation )

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

231
        /** @scrutinizer ignore-call */ 
232
        $record = $this->get($mixedParam, $sPropertyName);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
232
        if (!isset($record)) {
233
            // This will remove the default criteria for TraceableRecords and
234
            // fetch even 'deleted' ones.
235
            $this->setDefaultCriteria(null);
236
            $record = $this->get($mixedParam, $sPropertyName);
237
            if (isset($record)) {
238
                throw new app_DeletedRecordException($record, $mixedParam);
0 ignored issues
show
Unused Code introduced by
The call to app_DeletedRecordException::__construct() has too many arguments starting with $mixedParam. ( Ignorable by Annotation )

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

238
                throw /** @scrutinizer ignore-call */ new app_DeletedRecordException($record, $mixedParam);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
239
            }
240
            throw new app_NotFoundException($this, $mixedParam);
0 ignored issues
show
Bug introduced by
It seems like $mixedParam can also be of type ORM_Criteria; however, parameter $id of app_NotFoundException::__construct() does only seem to accept 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

240
            throw new app_NotFoundException($this, /** @scrutinizer ignore-type */ $mixedParam);
Loading history...
241
        }
242
        return $record;
243
    }
244
245
246
    /**
247
     * Defines if records can be created by the current user.
248
     *
249
     * @return boolean
250
     */
251
    public function isCreatable()
252
    {
253
        return false;
254
    }
255
256
257
    /**
258
     * Returns a criterion matching records readable by the current user.
259
     *
260
     * @since 1.0.21
261
     *
262
     * @return ORM_Criterion
263
     */
264
    public function isReadable()
265
    {
266
        bab_debug('The query in '.get_class($this).' use the default isReadable() method: none()');
267
        return $this->none();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->none() returns the type ORM_FalseCriterion which is incompatible with the documented return type ORM_Criterion.
Loading history...
268
    }
269
270
271
    /**
272
     * Returns a criterion matching records updatable by the current user.
273
     *
274
     * @since 1.0.21
275
     *
276
     * @return ORM_Criterion
277
     */
278
    public function isUpdatable()
279
    {
280
        return $this->none();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->none() returns the type ORM_FalseCriterion which is incompatible with the documented return type ORM_Criterion.
Loading history...
281
    }
282
283
    /**
284
     * Returns a criterion matching records deletable by the current user.
285
     *
286
     * @since 1.0.21
287
     *
288
     * @return ORM_Criterion
289
     */
290
    public function isDeletable()
291
    {
292
        return $this->none();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->none() returns the type ORM_FalseCriterion which is incompatible with the documented return type ORM_Criterion.
Loading history...
293
    }
294
295
296
297
    /**
298
     * Returns a criterion matching records deletable by the current user.
299
     *
300
     * @param string    $accessType
301
     *
302
     * @return ORM_Criterion
303
     */
304
    public function hasAccess($accessType)
305
    {
306
        $App = $this->App();
307
        $user = bab_getUserId();
308
        $accessManager = $App->AccessManager();
309
        return $accessManager->getAccessCriterion($this, $accessType, $user);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $accessManager->g...is, $accessType, $user) returns the type ORM_FalseCriterion which is incompatible with the documented return type ORM_Criterion.
Loading history...
310
    }
311
}
312
313
314
315
/**
316
 *
317
 *
318
 * @method app_RecordSet getParentSet()
319
 */
320
class app_Record extends ORM_Record implements app_Object_Interface
321
{
322
    /**
323
     * @var Func_App
324
     */
325
    protected $app = null;
326
327
328
329
330
    /**
331
     * Returns the value of the specified field or an iterator if $sFieldName represents a 'Many relation'.
332
     *
333
     * @param string $sFieldName The name of the field or relation for which the value must be returned.
334
     * @param array  $args       Optional arguments.
335
     *
336
     * @return mixed The value of the field or null if the field is not a part of the record.
337
     */
338
    public function __call($sFieldName, $args)
339
    {
340
        $value = $this->oParentSet->getBackend()->getRecordValue($this, $sFieldName);
341
        $field = $this->oParentSet->$sFieldName;
342
        if (!is_null($value) && $field instanceof ORM_FkField) {
343
344
            $sClassName = $field->getForeignSetName();
345
346
            $App = $this->App();
347
            $prefixLength = mb_strlen($App->classPrefix);
348
            $methodName = mb_substr($sClassName, $prefixLength);
349
            $set = $App->$methodName();
350
351
            $set->setName($field->getName());
352
            $set->setDescription($field->getDescription());
353
354
            $record = $set->get($value);
355
            return $record;
356
        }
357
        return $value;
358
    }
359
360
    /**
361
     * {@inheritDoc}
362
     * @see app_Object_Interface::setApp()
363
     */
364
    public function setApp(Func_App $app)
365
    {
366
        $this->app = $app;
367
        return $this;
368
    }
369
370
    /**
371
     * Get APP object to use with this record
372
     *
373
     * @return Func_App
374
     */
375
    public function App()
376
    {
377
        if (!isset($this->app)) {
378
            // If the app object was not specified (through the setApp() method),
379
            // we set it as parent set's App.
380
            $this->setApp($this->getParentSet()->App());
381
        }
382
        return $this->app;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->app returns the type Func_App which is incompatible with the return type mandated by app_Object_Interface::App() of app_Object_Interface.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
383
    }
384
385
386
    /**
387
     * Returns the base class name of a record.
388
     * For example xxx_Contact will return 'Contact'.
389
     *
390
     * @since 1.0.40
391
     * @return string
392
     */
393
    public function getClassName()
394
    {
395
        list(, $classname) = explode('_', get_class($this));
396
        return $classname;
397
    }
398
399
400
    /**
401
     * Returns the string reference corresponding to the record.
402
     *
403
     * @return string 	A reference string (e.g. Contact:12)
404
     */
405
    public function getRef()
406
    {
407
        if (!isset($this->id)) {
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on app_Record. Since you implemented __get, consider adding a @property annotation.
Loading history...
408
            throw new app_Exception('Trying to get the reference string of a record without an id.');
409
        }
410
        $classname = $this->getClassName();
411
        return $classname . ':' . $this->id;
412
    }
413
414
415
    /**
416
     * @return app_Controller
417
     */
418
    public function getController()
419
    {
420
        $App = $this->App();
421
422
        $ctrlName = $this->getClassName();
423
        return $App->Controller()->$ctrlName();
424
    }
425
426
427
    /**
428
     * Deletes the record with respect to referential integrity.
429
     *
430
     * Uses referential integrity as defined by hasManyRelation to delete/update
431
     * referenced elements.
432
     *
433
     * @see app_RecordSet::hasMany()
434
     *
435
     * @return self
436
     */
437
    public function delete()
438
    {
439
        $App = $this->App();
440
441
        $set = $this->getParentSet();
442
        $recordIdName = $set->getPrimaryKey();
443
        $recordId = $this->$recordIdName;
444
445
        // Uses referential integrity as defined by hasManyRelation to delete/update
446
        // referenced elements.
447
        $manyRelations = $set->getHasManyRelations();
448
449
450
        foreach ($manyRelations as $manyRelation) {
451
            /* @var $manyRelation ORM_ManyRelation */
452
453
            $foreignSetClassName = $manyRelation->getForeignSetClassName();
454
            $foreignSetFieldName = $manyRelation->getForeignFieldName();
455
            $method = mb_substr($foreignSetClassName, mb_strlen($App->classPrefix));
456
            $foreignSet = $App->$method();
457
458
            switch ($manyRelation->getOnDeleteMethod()) {
459
460
                case ORM_ManyRelation::ON_DELETE_SET_NULL:
461
462
                    $foreignRecords = $foreignSet->select($foreignSet->$foreignSetFieldName->is($recordId));
463
464
                    foreach ($foreignRecords as $foreignRecord) {
465
                        $foreignRecord->$foreignSetFieldName = 0;
466
                        $foreignRecord->save();
467
                    }
468
469
                    break;
470
471
                case ORM_ManyRelation::ON_DELETE_CASCADE:
472
473
                    $foreignRecords = $foreignSet->select($foreignSet->$foreignSetFieldName->is($recordId));
474
475
                    foreach ($foreignRecords as $foreignRecord) {
476
                        $foreignRecord->delete();
477
                    }
478
479
                    break;
480
481
                case ORM_ManyRelation::ON_DELETE_NO_ACTION:
482
                default:
483
                    break;
484
485
            }
486
        }
487
488
489
        // We remove all links to and from this record.
490
        $linkSet = $App->LinkSet();
491
492
        $linkSet->delete(
493
            $linkSet->sourceClass->is(get_class($this))->_AND_($linkSet->sourceId->is($recordId))
494
            ->_OR_(
495
                $linkSet->targetClass->is(get_class($this))->_AND_($linkSet->targetId->is($recordId))
496
            )
497
        );
498
499
500
        $set->delete($set->$recordIdName->is($recordId));
501
502
        return $this;
503
    }
504
505
506
507
    /**
508
     * Reassociates all data asociated to the record to another
509
     * specified one.
510
     *
511
     * @param	int		$id
512
     *
513
     * @return self
514
     */
515
    public function replaceWith($id)
516
    {
517
        $App = $this->App();
518
519
        $set = $this->getParentSet();
520
        $recordIdName = $set->getPrimaryKey();
521
        $recordId = $this->$recordIdName;
522
523
        // Use referential integrity as defined by hasManyRelation to delete/update
524
        // referenced elements.
525
        $manyRelations = $set->getHasManyRelations();
526
527
528
        foreach ($manyRelations as $manyRelation) {
529
            /* @var $manyRelation ORM_ManyRelation */
530
531
            $foreignSetClassName = $manyRelation->getForeignSetClassName();
532
            $foreignSetFieldName = $manyRelation->getForeignFieldName();
533
            $method = mb_substr($foreignSetClassName, mb_strlen($App->classPrefix));
534
            $foreignSet = $App->$method();
535
            // $foreignSet = new $foreignSetClassName($App);
536
            $foreignRecords = $foreignSet->select($foreignSet->$foreignSetFieldName->is($recordId));
537
538
            foreach ($foreignRecords as $foreignRecord) {
539
                $foreignRecord->$foreignSetFieldName = $id;
540
                $foreignRecord->save();
541
            }
542
        }
543
544
545
        // We replace all links to and from this record.
546
        $linkSet = $App->LinkSet();
547
548
        $links = $linkSet->select(
549
            $linkSet->sourceClass->is(get_class($this))->_AND_($linkSet->sourceId->is($recordId))
0 ignored issues
show
Unused Code introduced by
The call to app_LinkSet::select() has too many arguments starting with $linkSet->sourceClass->i...ourceId->is($recordId)). ( Ignorable by Annotation )

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

549
        /** @scrutinizer ignore-call */ 
550
        $links = $linkSet->select(

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
550
        );
551
552
        foreach ($links as $link) {
553
            $link->sourceId = $id;
554
            $link->save();
555
        }
556
557
        $links = $linkSet->select(
558
            $linkSet->targetClass->is(get_class($this))->_AND_($linkSet->targetId->is($recordId))
559
        );
560
561
        foreach ($links as $link) {
562
            $link->targetId = $id;
563
            $link->save();
564
        }
565
566
        return $this;
567
    }
568
569
570
    /**
571
     *
572
     *
573
     * @return array
574
     */
575
    public function getRelatedRecords()
576
    {
577
        $App = $this->App();
578
579
        $set = $this->getParentSet();
580
        $recordIdName = $set->getPrimaryKey();
581
        $recordId = $this->$recordIdName;
582
583
        // Use referential integrity as defined by hasManyRelation to delete/update
584
        // referenced elements.
585
        $manyRelations = $set->getHasManyRelations();
586
587
        $relatedRecords = array();
588
589
        foreach ($manyRelations as $manyRelation) {
590
            /* @var $manyRelation ORM_ManyRelation */
591
592
            $foreignSetClassName = $manyRelation->getForeignSetClassName();
593
            $foreignSetFieldName = $manyRelation->getForeignFieldName();
594
595
            $method = mb_substr($foreignSetClassName, mb_strlen($App->classPrefix));
596
            $foreignSet = $App->$method();
597
            // $foreignSet = new $foreignSetClassName($App);
598
            $foreignRecords = $foreignSet->select($foreignSet->$foreignSetFieldName->is($recordId));
599
600
601
            if ($foreignRecords->count() > 0) {
602
                $relatedRecords[$foreignSetClassName] = $foreignRecords;
603
            }
604
        }
605
606
        return $relatedRecords;
607
    }
608
609
610
611
612
613
614
    /**
615
     * Upload path for record attachments
616
     *
617
     * @return bab_Path
618
     */
619
    public function uploadPath()
620
    {
621
        $path = $this->App()->uploadPath();
0 ignored issues
show
Bug introduced by
The method uploadPath() does not exist on Func_App. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

621
        $path = $this->App()->/** @scrutinizer ignore-call */ uploadPath();
Loading history...
622
623
        if (null === $path)
624
        {
625
            throw new Exception('Missing upload path information');
626
            return null;
0 ignored issues
show
Unused Code introduced by
return null is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
627
        }
628
629
        $path->push(get_class($this));
630
        $path->push($this->id);
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on app_Record. Since you implemented __get, consider adding a @property annotation.
Loading history...
631
632
        return $path;
633
    }
634
635
636
637
638
639
640
    /**
641
     * import a value into a traceable record property if the value is not equal
642
     *
643
     * @param	string	$name		property name
644
     * @param	mixed	$value		value to set
645
     *
646
     * @return int		1 : the value has been modified | 0 : no change
647
     */
648
    protected function importProperty($name, $value)
649
    {
650
        if (((string) $this->$name) !== ((string) $value)) {
651
            $this->$name = $value;
652
            return 1;
653
        }
654
655
        return 0;
656
    }
657
658
659
660
    /**
661
     * import a value into a tracable record property if the value is not equal, try with multiple date format
662
     * this method work for date field 0000-00-00
663
     *
664
     * @param	string	$name		property name
665
     * @param	mixed	$value		value to set
666
     *
667
     * @return int		1 : the value has been modified | 0 : no change
668
     */
669
    protected function importDate($name, $value)
670
    {
671
        if (preg_match('/[0-9]{4}-[0-9]{2}-[0-9]{2}/',$value)) {
672
            return $this->importProperty($name, $value);
673
        }
674
675
        // try in DD/MM/YYYY format
676
677
        if (preg_match('/(?P<day>[0-9]+)\/(?P<month>[0-9]+)\/(?P<year>[0-9]{2,4})/',$value, $matches)) {
678
679
            $value = sprintf('%04d-%02d-%02d', (int) $matches['year'], (int) $matches['month'], (int) $matches['day']);
680
681
            return $this->importProperty($name, $value);
682
        }
683
684
    }
685
686
687
688
689
    /**
690
     *
691
     * @return string[]
692
     */
693
    public function getViews()
694
    {
695
        $App = $this->App();
696
697
        $customSectionSet = $App->CustomSectionSet();
698
        $customSections = $customSectionSet->select($customSectionSet->object->is($this->getClassName()));
699
        $customSections->groupBy($customSectionSet->view);
700
701
        $views = array();
702
        foreach ($customSections as $customSection) {
703
            $views[] = $customSection->view;
704
        }
705
706
        if (empty($views)) {
707
            $views[] = '';
708
        }
709
710
        return $views;
711
    }
712
713
714
715
    /**
716
     * Checks if the record is readable by the current user.
717
     * @since 1.0.21
718
     * @return bool
719
     */
720
    public function isReadable()
721
    {
722
        $set = $this->getParentSet();
723
        return $set->select($set->isReadable()->_AND_($set->id->is($this->id)))->count() == 1;
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on app_Record. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
The property id does not exist on app_RecordSet. Since you implemented __get, consider adding a @property annotation.
Loading history...
724
    }
725
726
727
    /**
728
     * Checks if the record is updatable by the current user.
729
     * @since 1.0.21
730
     * @return bool
731
     */
732
    public function isUpdatable()
733
    {
734
        $set = $this->getParentSet();
735
        return $set->select($set->isUpdatable()->_AND_($set->id->is($this->id)))->count() == 1;
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on app_Record. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
The property id does not exist on app_RecordSet. Since you implemented __get, consider adding a @property annotation.
Loading history...
736
    }
737
738
    /**
739
     * Checks if the record is deletable by the current user.
740
     * @since 1.0.21
741
     * @return bool
742
     */
743
    public function isDeletable()
744
    {
745
        $set = $this->getParentSet();
746
        return $set->select($set->isDeletable()->_AND_($set->id->is($this->id)))->count() == 1;
0 ignored issues
show
Bug Best Practice introduced by
The property id does not exist on app_RecordSet. Since you implemented __get, consider adding a @property annotation.
Loading history...
Bug Best Practice introduced by
The property id does not exist on app_Record. Since you implemented __get, consider adding a @property annotation.
Loading history...
747
    }
748
749
750
    /**
751
     * Ensures that the record is readable by the current user or throws an exception.
752
     * @since 1.0.40
753
     * @param string $message
754
     * @throws app_AccessException
755
     */
756
    public function requireReadable($message = null)
757
    {
758
        if (!$this->isReadable()) {
759
            $App = $this->App();
760
            if (!isset($message)) {
761
                $message = $App->translate('Access denied');
762
            }
763
            throw new app_AccessException($message);
764
        }
765
    }
766
767
    /**
768
     * Ensures that the record is updatable by the current user or throws an exception.
769
     * @since 1.0.40
770
     * @param string $message
771
     * @throws app_AccessException
772
     */
773
    public function requireUpdatable($message = null)
774
    {
775
        if (!$this->isUpdatable()) {
776
            $App = $this->App();
777
            if (!isset($message)) {
778
                $message = $App->translate('Access denied');
779
            }
780
            throw new app_AccessException($message);
781
        }
782
    }
783
784
    /**
785
     * Ensures that the record is deletable by the current user or throws an exception.
786
     * @since 1.0.40
787
     * @param string $message
788
     * @throws app_AccessException
789
     */
790
    public function requireDeletable($message = null)
791
    {
792
        if (!$this->isDeletable()) {
793
            $App = $this->App();
794
            if (!isset($message)) {
795
                $message = $App->translate('Access denied');
796
            }
797
            throw new app_AccessException($message);
798
        }
799
    }
800
}
801
802