Passed
Push — master ( 36a770...8ce521 )
by Laurent
02:13
created

app_RecordSet::setAccessManager()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 4
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
 *
31
 * @property ORM_PkField        $id
32
 *
33
 * @method app_Record   get(mixed $criteria)
34
 * @method app_Record   request(mixed $criteria)
35
 * @method app_Record[] select(\ORM_Criteria $criteria = null)
36
 * @method app_Record   newRecord()
37
 */
38
class app_RecordSet extends ORM_RecordSet implements app_Object_Interface
39
{
40
    /**
41
     * @var Func_App
42
     */
43
    protected $app = null;
44
45
    protected $accessRights = null;
46
47
    /**
48
     * @var app_AccessManager
49
     */
50
    private $accessManager = null;
51
52
    /**
53
     * @var array
54
     */
55
    private $customFields = null;
56
57
58
    /**
59
     * @param Func_App $app
60
     */
61
    public function __construct(Func_App $app)
62
    {
63
        parent::__construct();
64
        $this->setApp($app);
65
        $this->accessRights = array();
66
67
        $this->setAccessManager($app->AccessManager());
68
        $this->setPrimaryKey('id');
69
    }
70
71
    /**
72
     * {@inheritDoc}
73
     * @see app_Object_Interface::setApp()
74
     */
75
    public function setApp(Func_App $app)
76
    {
77
        $this->app = $app;
78
        return $this;
79
    }
80
81
82
    /**
83
     * {@inheritDoc}
84
     * @see app_Object_Interface::App()
85
     */
86
    public function App()
87
    {
88
        return $this->app;
89
    }
90
91
    /**
92
     * @param string $setName
93
     */
94
    protected function extractSetPrefixAndName($setName)
95
    {
96
        return explode('_', $setName);
97
    }
98
99
    /**
100
     * Similar to the ORM_RecordSet::join() method but instanciates the
101
     * joined RecordSet using App methods.
102
     *
103
     * @see ORM_RecordSet::join()
104
     */
105
    public function join($fkFieldName)
106
    {
107
        $fkField = $this->getField($fkFieldName);
108
        if (!($fkField instanceof ORM_FkField)) {
109
            return $this;
110
        }
111
        $setName = $fkField->getForeignSetName();
112
113
        if (!$setName || 'Set' === $setName) {
114
            throw new Exception('The set name is missing on foreign key field '.$fkFieldName);
115
        }
116
117
        list($prefix, $appSetName) = $this->extractSetPrefixAndName($setName);
118
119
        $set = $this->App()->$appSetName();
120
        $set->setName($fkField->getName());
121
        $set->setDescription($fkField->getDescription());
122
123
        $this->aField[$fkFieldName] = $set;
124
        $set->setParentSet($this);
125
        return $this;
126
    }
127
128
129
//     /**
130
//      *
131
//      * @param string $accessName
132
//      * @param string $type
133
//      */
134
//     public function addAccessRight($accessName, $type = 'acl')
135
//     {
136
//         $this->accessRights[$accessName] = $type;
137
//     }
138
139
140
//     /**
141
//      * @return array
142
//      */
143
//     public function getAccessRights()
144
//     {
145
//         return $this->accessRights;
146
//     }
147
148
149
150
    /**
151
     * @return app_CustomField[]
152
     */
153
    public function getCustomFields()
154
    {
155
        $App = $this->App();
156
157
        if (null === $this->customFields) {
158
            $this->customFields = array();
159
160
            if (isset($App->CustomField)) {
161
                $customFieldSet = $App->CustomFieldSet();
162
                $object = mb_substr(get_class($this), mb_strlen($App->classPrefix), -mb_strlen('Set'));
163
                try {
164
                    $customFields = $customFieldSet->select($customFieldSet->object->is($object));
165
166
                    foreach ($customFields as $customfield) {
167
                        $this->customFields[] = $customfield;
168
169
                        /*@var $customfield app_CustomField */
170
171
                    }
172
                } catch (ORM_BackEndSelectException $e) {
173
                    // table does not exist, this error is thrown by the install program while creating the sets
174
                }
175
            }
176
        }
177
178
        return $this->customFields;
179
    }
180
181
182
183
184
    /**
185
     * @return app_CustomField[]
186
     */
187
    public function selectCustomFields()
188
    {
189
        $App = $this->App();
190
191
        $customFieldSet= $App->CustomFieldSet();
192
        $object = mb_substr(get_class($this), mb_strlen($App->classPrefix), -mb_strlen('Set'));
193
194
        $customFields = $customFieldSet->select($customFieldSet->object->is($object));
195
196
        return $customFields;
197
    }
198
199
200
    /**
201
     * @return self
202
     */
203
    public function addCustomFields()
204
    {
205
        $customFields = $this->selectCustomFields();
206
        foreach ($customFields as $customField) {
207
            /*@var $customField app_CustomField */
208
            $description = $customField->name;
209
            $ormField = $customField->getORMField()
210
                ->setDescription($description);
211
            if ($ormField instanceof ORM_FkField) {
212
                $this->hasOne($customField->fieldname, $ormField->getForeignSetName())
213
                    ->setDescription($description);
214
            } else {
215
                $this->addFields($ormField);
216
            }
217
        }
218
219
        return $this;
220
    }
221
222
223
    /**
224
     * Similar to ORM_RecordSet::get() method  but throws a app_NotFoundException if the
225
     * record is not found.
226
     *
227
     * @since 1.0.18
228
     *
229
     * @throws ORM_Exception
230
     * @throws app_NotFoundException
231
     *
232
     * @param ORM_Criteria|string $mixedParam    Criteria for selecting records
233
     *                                           or the value for selecting record
234
     * @param string              $sPropertyName The name of the property on which
235
     *                                           the value applies. If not
236
     *                                           specified or null, the set's
237
     *                                           primary key will be used.
238
     *
239
     * @return app_Record
240
     */
241
    public function request($mixedParam = null, $sPropertyName = null)
242
    {
243
        $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

243
        /** @scrutinizer ignore-call */ 
244
        $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...
244
        if (!isset($record)) {
245
            // This will remove the default criteria for TraceableRecords and
246
            // fetch even 'deleted' ones.
247
            $this->setDefaultCriteria(null);
248
            $record = $this->get($mixedParam, $sPropertyName);
249
            if (isset($record)) {
250
                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

250
                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...
251
            }
252
            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

252
            throw new app_NotFoundException($this, /** @scrutinizer ignore-type */ $mixedParam);
Loading history...
253
        }
254
        return $record;
255
    }
256
257
258
259
    /**
260
     * @param app_AccessManager $accessManager
261
     * @return self
262
     */
263
    public function setAccessManager(app_AccessManager $accessManager)
264
    {
265
        $this->accessManager = $accessManager;
266
        return $this;
267
    }
268
269
270
    /**
271
     * @return app_AccessManager
272
     */
273
    public function getAccessManager()
274
    {
275
        return $this->accessManager;
276
    }
277
278
279
    /**
280
     * Defines if records can be created by the current user.
281
     *
282
     * @return boolean
283
     */
284
    public function isCreatable()
285
    {
286
        return false;
287
    }
288
289
290
291
    /**
292
     *
293
     * @return ORM_Criteria
294
     */
295
    public function isReadable()
296
    {
297
        return $this->hasAccess('read');
298
    }
299
300
    /**
301
     *
302
     * @return ORM_Criteria
303
     */
304
    public function isUpdatable()
305
    {
306
        return $this->hasAccess('update');
307
    }
308
309
    /**
310
     *
311
     * @return ORM_Criteria
312
     */
313
    public function isDeletable()
314
    {
315
        return $this->hasAccess('delete');
316
    }
317
318
319
320
    /**
321
     * Returns a criterion matching records deletable by the current user.
322
     *
323
     * @param string    $accessType
324
     * @param int|null  $user
325
     *
326
     * @return ORM_Criterion
327
     */
328
    public function hasAccess($accessType, $user = null)
329
    {
330
        $accessManager = $this->getAccessManager();
331
        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...
332
    }
333
}
334
335
336
337
/**
338
 * @property int        $id
339
 *
340
 * @method app_RecordSet getParentSet()
341
 */
342
class app_Record extends ORM_Record implements app_Object_Interface
343
{
344
    /**
345
     * @var Func_App
346
     */
347
    protected $app = null;
348
349
350
351
352
    /**
353
     * Returns the value of the specified field or an iterator if $sFieldName represents a 'Many relation'.
354
     *
355
     * @param string $sFieldName The name of the field or relation for which the value must be returned.
356
     * @param array  $args       Optional arguments.
357
     *
358
     * @return mixed The value of the field or null if the field is not a part of the record.
359
     */
360
    public function __call($sFieldName, $args)
361
    {
362
        $value = $this->oParentSet->getBackend()->getRecordValue($this, $sFieldName);
363
        $field = $this->oParentSet->$sFieldName;
364
        if (!is_null($value) && $field instanceof ORM_FkField) {
365
366
            $sClassName = $field->getForeignSetName();
367
368
            $App = $this->App();
369
            $prefixLength = mb_strlen($App->classPrefix);
370
            $methodName = mb_substr($sClassName, $prefixLength);
371
            $set = $App->$methodName();
372
373
            $set->setName($field->getName());
374
            $set->setDescription($field->getDescription());
375
376
            $record = $set->get($value);
377
            return $record;
378
        }
379
        return $value;
380
    }
381
382
    /**
383
     * {@inheritDoc}
384
     * @see app_Object_Interface::setApp()
385
     */
386
    public function setApp(Func_App $app)
387
    {
388
        $this->app = $app;
389
        return $this;
390
    }
391
392
    /**
393
     * Get APP object to use with this record
394
     *
395
     * @return Func_App
396
     */
397
    public function App()
398
    {
399
        if (!isset($this->app)) {
400
            // If the app object was not specified (through the setApp() method),
401
            // we set it as parent set's App.
402
            $this->setApp($this->getParentSet()->App());
403
        }
404
        return $this->app;
405
    }
406
407
408
    /**
409
     * Returns the base class name of a record.
410
     * For example xxx_Contact will return 'Contact'.
411
     *
412
     * @since 1.0.40
413
     * @return string
414
     */
415
    public function getClassName()
416
    {
417
        list(, $classname) = explode('_', get_class($this));
418
        return $classname;
419
    }
420
421
422
    /**
423
     * Returns the string reference corresponding to the record.
424
     *
425
     * @return string 	A reference string (e.g. Contact:12)
426
     */
427
    public function getRef()
428
    {
429
        if (!isset($this->id)) {
430
            throw new app_Exception('Trying to get the reference string of a record without an id.');
431
        }
432
        $classname = $this->getClassName();
433
        return $classname . ':' . $this->id;
434
    }
435
436
437
    /**
438
     * @return app_Controller
439
     */
440
    public function getController()
441
    {
442
        $App = $this->App();
443
444
        $ctrlName = $this->getClassName();
445
        return $App->Controller()->$ctrlName();
446
    }
447
448
449
    /**
450
     * Deletes the record with respect to referential integrity.
451
     *
452
     * Uses referential integrity as defined by hasManyRelation to delete/update
453
     * referenced elements.
454
     *
455
     * @see app_RecordSet::hasMany()
456
     *
457
     * @return self
458
     */
459
    public function delete()
460
    {
461
        $App = $this->App();
462
463
        $set = $this->getParentSet();
464
        $recordIdName = $set->getPrimaryKey();
465
        $recordId = $this->$recordIdName;
466
467
        // Uses referential integrity as defined by hasManyRelation to delete/update
468
        // referenced elements.
469
        $manyRelations = $set->getHasManyRelations();
470
471
472
        foreach ($manyRelations as $manyRelation) {
473
            /* @var $manyRelation ORM_ManyRelation */
474
475
            $foreignSetClassName = $manyRelation->getForeignSetClassName();
476
            $foreignSetFieldName = $manyRelation->getForeignFieldName();
477
            $method = mb_substr($foreignSetClassName, mb_strlen($App->classPrefix));
478
            $foreignSet = $App->$method();
479
480
            switch ($manyRelation->getOnDeleteMethod()) {
481
482
                case ORM_ManyRelation::ON_DELETE_SET_NULL:
483
484
                    $foreignRecords = $foreignSet->select($foreignSet->$foreignSetFieldName->is($recordId));
485
486
                    foreach ($foreignRecords as $foreignRecord) {
487
                        $foreignRecord->$foreignSetFieldName = 0;
488
                        $foreignRecord->save();
489
                    }
490
491
                    break;
492
493
                case ORM_ManyRelation::ON_DELETE_CASCADE:
494
495
                    $foreignRecords = $foreignSet->select($foreignSet->$foreignSetFieldName->is($recordId));
496
497
                    foreach ($foreignRecords as $foreignRecord) {
498
                        $foreignRecord->delete();
499
                    }
500
501
                    break;
502
503
                case ORM_ManyRelation::ON_DELETE_NO_ACTION:
504
                default:
505
                    break;
506
507
            }
508
        }
509
510
511
        // We remove all links to and from this record.
512
        $linkSet = $App->LinkSet();
513
514
        $linkSet->delete(
515
            $linkSet->sourceClass->is(get_class($this))->_AND_($linkSet->sourceId->is($recordId))
516
            ->_OR_(
517
                $linkSet->targetClass->is(get_class($this))->_AND_($linkSet->targetId->is($recordId))
518
            )
519
        );
520
521
522
        $set->delete($set->$recordIdName->is($recordId));
523
524
        return $this;
525
    }
526
527
528
529
    /**
530
     * Reassociates all data asociated to the record to another
531
     * specified one.
532
     *
533
     * @param	int		$id
534
     *
535
     * @return self
536
     */
537
    public function replaceWith($id)
538
    {
539
        $App = $this->App();
540
541
        $set = $this->getParentSet();
542
        $recordIdName = $set->getPrimaryKey();
543
        $recordId = $this->$recordIdName;
544
545
        // Use referential integrity as defined by hasManyRelation to delete/update
546
        // referenced elements.
547
        $manyRelations = $set->getHasManyRelations();
548
549
550
        foreach ($manyRelations as $manyRelation) {
551
            /* @var $manyRelation ORM_ManyRelation */
552
553
            $foreignSetClassName = $manyRelation->getForeignSetClassName();
554
            $foreignSetFieldName = $manyRelation->getForeignFieldName();
555
            $method = mb_substr($foreignSetClassName, mb_strlen($App->classPrefix));
556
            $foreignSet = $App->$method();
557
            // $foreignSet = new $foreignSetClassName($App);
558
            $foreignRecords = $foreignSet->select($foreignSet->$foreignSetFieldName->is($recordId));
559
560
            foreach ($foreignRecords as $foreignRecord) {
561
                $foreignRecord->$foreignSetFieldName = $id;
562
                $foreignRecord->save();
563
            }
564
        }
565
566
567
        // We replace all links to and from this record.
568
        $linkSet = $App->LinkSet();
569
570
        $links = $linkSet->select(
571
            $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

571
        /** @scrutinizer ignore-call */ 
572
        $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...
572
        );
573
574
        foreach ($links as $link) {
575
            $link->sourceId = $id;
576
            $link->save();
577
        }
578
579
        $links = $linkSet->select(
580
            $linkSet->targetClass->is(get_class($this))->_AND_($linkSet->targetId->is($recordId))
581
        );
582
583
        foreach ($links as $link) {
584
            $link->targetId = $id;
585
            $link->save();
586
        }
587
588
        return $this;
589
    }
590
591
592
    /**
593
     *
594
     *
595
     * @return array
596
     */
597
    public function getRelatedRecords()
598
    {
599
        $App = $this->App();
600
601
        $set = $this->getParentSet();
602
        $recordIdName = $set->getPrimaryKey();
603
        $recordId = $this->$recordIdName;
604
605
        // Use referential integrity as defined by hasManyRelation to delete/update
606
        // referenced elements.
607
        $manyRelations = $set->getHasManyRelations();
608
609
        $relatedRecords = array();
610
611
        foreach ($manyRelations as $manyRelation) {
612
            /* @var $manyRelation ORM_ManyRelation */
613
614
            $foreignSetClassName = $manyRelation->getForeignSetClassName();
615
            $foreignSetFieldName = $manyRelation->getForeignFieldName();
616
617
            $method = mb_substr($foreignSetClassName, mb_strlen($App->classPrefix));
618
            $foreignSet = $App->$method();
619
            // $foreignSet = new $foreignSetClassName($App);
620
            $foreignRecords = $foreignSet->select($foreignSet->$foreignSetFieldName->is($recordId));
621
622
623
            if ($foreignRecords->count() > 0) {
624
                $relatedRecords[$foreignSetClassName] = $foreignRecords;
625
            }
626
        }
627
628
        return $relatedRecords;
629
    }
630
631
632
633
634
635
636
    /**
637
     * Upload path for record attachments
638
     *
639
     * @return bab_Path
640
     */
641
    public function uploadPath()
642
    {
643
        $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

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