AppRecord::setApp()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 2
c 1
b 0
f 1
dl 0
loc 4
rs 10
cc 1
nc 1
nop 1
1
<?php
2
3
// -------------------------------------------------------------------------
4
// OVIDENTIA http://www.ovidentia.org
5
// Ovidentia is free software; you can redistribute it and/or modify
6
// it under the terms of the GNU General Public License as published by
7
// the Free Software Foundation; either version 2, or (at your option)
8
// any later version.
9
//
10
// This program is distributed in the hope that it will be useful, but
11
// WITHOUT ANY WARRANTY; without even the implied warranty of
12
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
// See the GNU General Public License for more details.
14
//
15
// You should have received a copy of the GNU General Public License
16
// along with this program; if not, write to the Free Software
17
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
18
// USA.
19
// -------------------------------------------------------------------------
20
/**
21
 * @license http://opensource.org/licenses/gpl-license.php GNU General Public License (GPL)
22
 * @copyright Copyright (c) 2022 by SI4YOU ({@link https://www.siforyou.com})
23
 */
24
namespace Capwelton\LibApp\Set;
25
26
use Capwelton\LibApp\AppObjectInterface;
27
use Capwelton\LibApp\Func_App;
28
use Capwelton\LibApp\Exceptions\AppException;
29
use Capwelton\LibApp\Ctrl\AppController;
30
use Capwelton\LibApp\Exceptions\AppAccessException;
31
use Capwelton\LibOrm\ORMRecord;
0 ignored issues
show
Bug introduced by
The type Capwelton\LibOrm\ORMRecord was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
32
use Capwelton\LibOrm\Field\ORMFkField;
0 ignored issues
show
Bug introduced by
The type Capwelton\LibOrm\Field\ORMFkField was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
33
use Capwelton\LibOrm\MySql\ORMManyRelation;
0 ignored issues
show
Bug introduced by
The type Capwelton\LibOrm\MySql\ORMManyRelation was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
34
35
/**
36
 * @property int $id
37
 *
38
 * @method AppRecordSet getParentSet()
39
 */
40
class AppRecord extends ORMRecord implements AppObjectInterface
41
{
42
    
43
    /**
44
     * @var Func_App
45
     */
46
    protected $app = null;
47
    
48
    /**
49
     * Returns the value of the specified field or an iterator if $sFieldName represents a 'Many relation'.
50
     *
51
     * @param string $sFieldName
52
     *            The name of the field or relation for which the value must be returned.
53
     * @param array $args
54
     *            Optional arguments.
55
     * @return mixed The value of the field or null if the field is not a part of the record.
56
     */
57
    public function __call($sFieldName, $args)
58
    {
59
        $value = $this->oParentSet->getBackend()->getRecordValue($this, $sFieldName);
60
        $field = $this->oParentSet->$sFieldName;
61
        if(! is_null($value) && $field instanceof ORMFkField){
62
            
63
            $sClassName = $field->getForeignSetName();
64
            
65
            $App = $this->App();
66
            $prefixLength = mb_strlen($App->classPrefix);
67
            $methodName = mb_substr($sClassName, $prefixLength);
68
            $set = $App->$methodName();
69
            
70
            $set->setName($field->getName());
71
            $set->setDescription($field->getDescription());
72
            
73
            $record = $set->get($value);
74
            return $record;
75
        }
76
        return $value;
77
    }
78
    
79
    /**
80
     * {@inheritdoc}
81
     * @see AppObjectInterface::setApp()
82
     */
83
    public function setApp(Func_App $app)
84
    {
85
        $this->app = $app;
86
        return $this;
87
    }
88
    
89
    /**
90
     * Get APP object to use with this record
91
     *
92
     * @return Func_App
93
     */
94
    public function App()
95
    {
96
        if(! isset($this->app)){
97
            // If the app object was not specified (through the setApp() method),
98
            // we set it as parent set's App.
99
            $this->setApp($this->getParentSet()
100
                ->App());
101
        }
102
        return $this->app;
103
    }
104
    
105
    /**
106
     * Returns the base class name of a record.
107
     * For example xxx_Contact will return 'Contact'.
108
     *
109
     * @since 1.0.40
110
     * @return string
111
     */
112
    public function getClassName()
113
    {
114
        $App = $this->App();
115
        $rClass = new \ReflectionClass(get_class($this));
116
        $component = $App->getComponentByName($rClass->getShortName());
117
        if(isset($component)){
118
            return $component->getRecordClassName();
119
            // list(, $classname) = explode('_', $component->getRecordClassName());
120
            // return $classname;
121
        }
122
        list (, $classname) = explode('_', get_class($this));
123
        return $classname;
124
    }
125
    
126
    /**
127
     * Returns the string reference corresponding to the record.
128
     *
129
     * @return string A reference string (e.g. Contact:12)
130
     */
131
    public function getRef()
132
    {
133
        if(! isset($this->id)){
134
            throw new AppException('Trying to get the reference string of a record without an id.');
135
        }
136
        $classname = $this->getClassName();
137
        return $classname . ':' . $this->id;
138
    }
139
    
140
    /**
141
     * @return AppController
142
     */
143
    public function getController()
144
    {
145
        $App = $this->App();
146
        
147
        $ctrlName = $this->getClassName();
148
        return $App->Controller()->$ctrlName();
149
    }
150
    
151
    /**
152
     * Deletes the record with respect to referential integrity.
153
     * Uses referential integrity as defined by hasManyRelation to delete/update
154
     * referenced elements.
155
     *
156
     * @see AppRecordSet::hasMany()
157
     *
158
     * @return self
159
     */
160
    public function delete($deletedStatus = null)
161
    {
162
        $App = $this->App();
163
        
164
        if(! isset($deletedStatus)){
165
            $deletedStatus = AppTraceableRecord::DELETED_STATUS_DELETED;
166
        }
167
        
168
        $set = $this->getParentSet();
169
        $recordIdName = $set->getPrimaryKey();
170
        $recordId = $this->$recordIdName;
171
        
172
        // Uses referential integrity as defined by hasManyRelation to delete/update
173
        // referenced elements.
174
        $manyRelations = $set->getHasManyRelations();
175
        
176
        foreach ($manyRelations as $manyRelation){
177
            /* @var $manyRelation ORMManyRelation */
178
            
179
            $foreignSetClassName = $manyRelation->getForeignSetClassName();
180
            $foreignSetFieldName = $manyRelation->getForeignFieldName();
181
            $method = mb_substr($foreignSetClassName, mb_strlen($App->classPrefix));
182
            $foreignSet = $App->$method();
183
            
184
            switch ($manyRelation->getOnDeleteMethod()) {
185
                
186
                case ORMManyRelation::ON_DELETE_SET_NULL:
187
                    
188
                    $foreignRecords = $foreignSet->select($foreignSet->$foreignSetFieldName->is($recordId));
189
                    
190
                    foreach ($foreignRecords as $foreignRecord){
191
                        $foreignRecord->$foreignSetFieldName = 0;
192
                        $foreignRecord->save();
193
                    }
194
                    
195
                    break;
196
                
197
                case ORMManyRelation::ON_DELETE_CASCADE:
198
                    
199
                    $foreignRecords = $foreignSet->select($foreignSet->$foreignSetFieldName->is($recordId));
200
                    
201
                    foreach ($foreignRecords as $foreignRecord){
202
                        $foreignRecord->delete();
203
                    }
204
                    
205
                    break;
206
                
207
                case ORMManyRelation::ON_DELETE_NO_ACTION:
208
                default:
209
                    break;
210
            }
211
        }
212
        
213
        // We remove all links to and from this record.
214
        $linkSet = $App->LinkSet();
215
        $recordClassName = $set->newRecord()->getClassName();
216
        
217
        $linkSet->delete($linkSet->sourceClass->is($recordClassName)
218
            ->_AND_($linkSet->sourceId->is($recordId))
219
            ->_OR_($linkSet->targetClass->is($recordClassName)
220
            ->_AND_($linkSet->targetId->is($recordId))));
221
        
222
        $set->delete($set->$recordIdName->is($recordId), $deletedStatus);
223
        
224
        return $this;
225
    }
226
    
227
    /**
228
     * Reassociates all data asociated to the record to another
229
     * specified one.
230
     *
231
     * @param int $id
232
     *
233
     * @return self
234
     */
235
    public function replaceWith($id)
236
    {
237
        $App = $this->App();
238
        
239
        $set = $this->getParentSet();
240
        $recordIdName = $set->getPrimaryKey();
241
        $recordId = $this->$recordIdName;
242
        
243
        // Use referential integrity as defined by hasManyRelation to delete/update
244
        // referenced elements.
245
        $manyRelations = $set->getHasManyRelations();
246
        
247
        foreach ($manyRelations as $manyRelation){
248
            /* @var $manyRelation ORMManyRelation */
249
            
250
            $foreignSetClassName = $manyRelation->getForeignSetClassName();
251
            $foreignSetFieldName = $manyRelation->getForeignFieldName();
252
            $method = mb_substr($foreignSetClassName, mb_strlen($App->classPrefix));
253
            $foreignSet = $App->$method();
254
            // $foreignSet = new $foreignSetClassName($App);
255
            $foreignRecords = $foreignSet->select($foreignSet->$foreignSetFieldName->is($recordId));
256
            
257
            foreach ($foreignRecords as $foreignRecord){
258
                $foreignRecord->$foreignSetFieldName = $id;
259
                $foreignRecord->save();
260
            }
261
        }
262
        
263
        // We replace all links to and from this record.
264
        $linkSet = $App->LinkSet();
265
        $recordClassName = $set->newRecord()->getClassName();
266
        
267
        $links = $linkSet->select($linkSet->sourceClass->is($recordClassName)
0 ignored issues
show
Unused Code introduced by
The call to Capwelton\LibApp\Set\AppLinkSet::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

267
        /** @scrutinizer ignore-call */ 
268
        $links = $linkSet->select($linkSet->sourceClass->is($recordClassName)

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...
268
            ->_AND_($linkSet->sourceId->is($recordId)));
269
        
270
        foreach ($links as $link){
271
            $link->sourceId = $id;
272
            $link->save();
273
        }
274
        
275
        $links = $linkSet->select($linkSet->targetClass->is($recordClassName)
276
            ->_AND_($linkSet->targetId->is($recordId)));
277
        
278
        foreach ($links as $link){
279
            $link->targetId = $id;
280
            $link->save();
281
        }
282
        
283
        return $this;
284
    }
285
    
286
    /**
287
     * @return array
288
     */
289
    public function getRelatedRecords()
290
    {
291
        $App = $this->App();
292
        
293
        $set = $this->getParentSet();
294
        $recordIdName = $set->getPrimaryKey();
295
        $recordId = $this->$recordIdName;
296
        
297
        // Use referential integrity as defined by hasManyRelation to delete/update
298
        // referenced elements.
299
        $manyRelations = $set->getHasManyRelations();
300
        
301
        $relatedRecords = array();
302
        
303
        foreach ($manyRelations as $manyRelation){
304
            /* @var $manyRelation ORMManyRelation */
305
            
306
            $foreignSetClassName = $manyRelation->getForeignSetClassName();
307
            $foreignSetFieldName = $manyRelation->getForeignFieldName();
308
            
309
            $method = mb_substr($foreignSetClassName, mb_strlen($App->classPrefix));
310
            $foreignSet = $App->$method();
311
            // $foreignSet = new $foreignSetClassName($App);
312
            $foreignRecords = $foreignSet->select($foreignSet->$foreignSetFieldName->is($recordId));
313
            
314
            if($foreignRecords->count() > 0){
315
                $relatedRecords[$foreignSetClassName] = $foreignRecords;
316
            }
317
        }
318
        
319
        return $relatedRecords;
320
    }
321
    
322
    /**
323
     * Upload path for record attachments
324
     *
325
     * @return \bab_Path
326
     */
327
    public function uploadPath()
328
    {
329
        $path = $this->App()->uploadPath();
330
        
331
        if(null === $path){
332
            throw new \Exception('Missing upload path information');
333
            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...
334
        }
335
        
336
        $path->push($this->getClassName());
337
        $path->push($this->id);
338
        
339
        return $path;
340
    }
341
    
342
    /**
343
     * @return bool
344
     */
345
    public function isLinkedTo(AppRecord $source, $linkType = null)
346
    {
347
        $App = $this->App();
348
        $linkSet = $App->LinkSet();
349
        
350
        $sourceClass = $source->getClassName();
351
        if(strpos($sourceClass, '\\')){
352
            $targetClass = (new \ReflectionClass($sourceClass))->getShortName();
0 ignored issues
show
Unused Code introduced by
The assignment to $targetClass is dead and can be removed.
Loading history...
353
        }
354
        
355
        $targetClass = $this->getClassName();
356
        if(strpos($targetClass, '\\')){
357
            $targetClass = (new \ReflectionClass($targetClass))->getShortName();
358
        }
359
        
360
        $criteria = $linkSet->sourceClass->is($sourceClass)
361
            ->_AND_($linkSet->sourceId->is($source->id))
362
            ->_AND_($linkSet->targetClass->is($targetClass))
363
            ->_AND_($linkSet->targetId->is($this->id));
364
        if(isset($linkType)){
365
            if(is_array($linkType)){
366
                $criteria = $criteria->_AND_($linkSet->type->in($linkType));
367
            }
368
            else{
369
                $criteria = $criteria->_AND_($linkSet->type->is($linkType));
370
            }
371
        }
372
        $links = $linkSet->select($criteria);
0 ignored issues
show
Unused Code introduced by
The call to Capwelton\LibApp\Set\AppLinkSet::select() has too many arguments starting with $criteria. ( Ignorable by Annotation )

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

372
        /** @scrutinizer ignore-call */ 
373
        $links = $linkSet->select($criteria);

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...
373
        
374
        $isLinked = ($links->count() > 0);
375
        
376
        $linkSet->__destruct();
377
        unset($linkSet);
378
        
379
        return $isLinked;
380
    }
381
    
382
    /**
383
     * @return bool
384
     */
385
    public function isSourceOf(AppRecord $target, $linkType = null)
386
    {
387
        $App = $this->App();
388
        $linkSet = $App->LinkSet();
389
        
390
        $sourceClass = $this->getClassName();
391
        if(strpos($sourceClass, '\\')){
392
            $targetClass = (new \ReflectionClass($sourceClass))->getShortName();
0 ignored issues
show
Unused Code introduced by
The assignment to $targetClass is dead and can be removed.
Loading history...
393
        }
394
        
395
        $targetClass = $target->getClassName();
396
        if(strpos($targetClass, '\\')){
397
            $targetClass = (new \ReflectionClass($targetClass))->getShortName();
398
        }
399
        
400
        $criteria = $linkSet->all($linkSet->targetClass->is($targetClass), $linkSet->targetId->is($target->id), $linkSet->sourceClass->is($sourceClass), $linkSet->sourceId->is($this->id));
401
        
402
        if(isset($linkType)){
403
            if(is_array($linkType)){
404
                $criteria = $criteria->_AND_($linkSet->type->in($linkType));
405
            }
406
            else{
407
                $criteria = $criteria->_AND_($linkSet->type->is($linkType));
408
            }
409
        }
410
        $links = $linkSet->select($criteria);
0 ignored issues
show
Unused Code introduced by
The call to Capwelton\LibApp\Set\AppLinkSet::select() has too many arguments starting with $criteria. ( Ignorable by Annotation )

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

410
        /** @scrutinizer ignore-call */ 
411
        $links = $linkSet->select($criteria);

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...
411
        
412
        $isLinked = ($links->count() > 0);
413
        
414
        $linkSet->__destruct();
415
        unset($linkSet);
416
        
417
        return $isLinked;
418
    }
419
    
420
    /**
421
     * Link record to $source
422
     *
423
     * @param AppRecord $source
424
     * @param string $linkType
425
     * @param string $data
426
     * @return self
427
     */
428
    public function linkTo(AppRecord $source, $linkType = '', $data = '')
429
    {
430
        $linkSet = $this->App()->LinkSet();
431
        
432
        $link = $linkSet->create($source, $this, $linkType, $data);
433
        
434
        $link->__destruct();
435
        unset($link);
436
        
437
        $linkSet->__destruct();
438
        unset($linkSet);
439
        
440
        return $this;
441
    }
442
    
443
    /**
444
     * Unlink record from $source
445
     *
446
     * @return self
447
     */
448
    public function unlinkFrom(AppRecord $source, $linkType = null)
449
    {
450
        $linkSet = $this->App()->LinkSet();
451
        
452
        $linkSet->deleteLink($source, $this, $linkType);
453
        
454
        return $this;
455
    }
456
    
457
    /**
458
     * import a value into a traceable record property if the value is not equal
459
     *
460
     * @param string $name
461
     *            property name
462
     * @param mixed $value
463
     *            value to set
464
     * @return int 1 : the value has been modified | 0 : no change
465
     */
466
    protected function importProperty($name, $value)
467
    {
468
        if(((string) $this->$name) !== ((string) $value)){
469
            $this->$name = $value;
470
            return 1;
471
        }
472
        
473
        return 0;
474
    }
475
    
476
    /**
477
     * import a value into a tracable record property if the value is not equal, try with multiple date format
478
     * this method work for date field 0000-00-00
479
     *
480
     * @param string $name
481
     *            property name
482
     * @param mixed $value
483
     *            value to set
484
     * @return int 1 : the value has been modified | 0 : no change
485
     */
486
    protected function importDate($name, $value)
487
    {
488
        if(preg_match('/[0-9]{4}-[0-9]{2}-[0-9]{2}/', $value)){
489
            return $this->importProperty($name, $value);
490
        }
491
        
492
        // try in DD/MM/YYYY format
493
        
494
        if(preg_match('/(?P<day>[0-9]+)\/(?P<month>[0-9]+)\/(?P<year>[0-9]{2,4})/', $value, $matches)){
495
            
496
            $value = sprintf('%04d-%02d-%02d', (int) $matches['year'], (int) $matches['month'], (int) $matches['day']);
497
            
498
            return $this->importProperty($name, $value);
499
        }
500
    }
501
    
502
    /**
503
     * @return string[]
504
     */
505
    public function getViews()
506
    {
507
        $App = $this->App();
508
        
509
        $customContainerSet = $App->CustomContainerSet();
510
        $customContainers = $customContainerSet->select($customContainerSet->object->is($this->getClassName()));
511
        $customContainers->groupBy($customContainerSet->view);
512
        
513
        $views = array();
514
        foreach ($customContainers as $customContainer){
515
            $views[] = $customContainer->view;
516
        }
517
        
518
        if(empty($views)){
519
            $views[] = '';
520
        }
521
        
522
        return $views;
523
    }
524
    
525
    /**
526
     * Checks if the record is readable by the current user.
527
     *
528
     * @since 1.0.21
529
     * @return bool
530
     */
531
    public function isReadable()
532
    {
533
        $set = $this->getParentSet();
534
        return $set->select($set->isReadable()
535
            ->_AND_($set->id->is($this->id)))
536
            ->count() == 1;
537
    }
538
    
539
    /**
540
     * Checks if the record is updatable by the current user.
541
     *
542
     * @since 1.0.21
543
     * @return bool
544
     */
545
    public function isUpdatable()
546
    {
547
        $set = $this->getParentSet();
548
        return $set->select($set->isUpdatable()
549
            ->_AND_($set->id->is($this->id)))
550
            ->count() == 1;
551
    }
552
    
553
    /**
554
     * Checks if the record is deletable by the current user.
555
     *
556
     * @since 1.0.21
557
     * @return bool
558
     */
559
    public function isDeletable()
560
    {
561
        $set = $this->getParentSet();
562
        return $set->select($set->isDeletable()
563
            ->_AND_($set->id->is($this->id)))
564
            ->count() == 1;
565
    }
566
    
567
    /**
568
     * Checks if the record can be put to the trash by the current user.
569
     *
570
     * @return bool
571
     */
572
    public function isRemovable()
573
    {
574
        $set = $this->getParentSet();
575
        return $set->select($set->isRemovable()
576
            ->_AND_($set->id->is($this->id)))
577
            ->count() == 1;
578
    }
579
    
580
    /**
581
     * Checks if the record can be restored from the trash by the current user.
582
     *
583
     * @return bool
584
     */
585
    public function isRestorable()
586
    {
587
        $set = $this->getParentSet();
588
        return $set->select($set->isRestorable()
589
            ->_AND_($set->id->is($this->id)))
590
            ->count() == 1;
591
    }
592
    
593
    /**
594
     * Ensures that the record is readable by the current user or throws an exception.
595
     *
596
     * @since 1.0.40
597
     * @param string $message
598
     * @throws AppAccessException
599
     */
600
    public function requireReadable($message = null)
601
    {
602
        if(! $this->isReadable()){
603
            $App = $this->App();
604
            if(! isset($message)){
605
                $message = $App->translate('Access denied');
606
            }
607
            throw new AppAccessException($message);
608
        }
609
    }
610
    
611
    /**
612
     * Ensures that the record is updatable by the current user or throws an exception.
613
     *
614
     * @since 1.0.40
615
     * @param string $message
616
     * @throws AppAccessException
617
     */
618
    public function requireUpdatable($message = null)
619
    {
620
        if(! $this->isUpdatable()){
621
            $App = $this->App();
622
            if(! isset($message)){
623
                $message = $App->translate('Access denied');
624
            }
625
            throw new AppAccessException($message);
626
        }
627
    }
628
    
629
    /**
630
     * Ensures that the record is deletable by the current user or throws an exception.
631
     *
632
     * @since 1.0.40
633
     * @param string $message
634
     * @throws AppAccessException
635
     */
636
    public function requireDeletable($message = null)
637
    {
638
        if(! $this->isDeletable()){
639
            $App = $this->App();
640
            if(! isset($message)){
641
                $message = $App->translate('Access denied');
642
            }
643
            throw new AppAccessException($message);
644
        }
645
    }
646
}