GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — master (#111)
by
unknown
03:02
created

Block::getCurrentPage()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4
rs 10
c 0
b 0
f 0
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
3
namespace SheaDawson\Blocks\model;
4
5
use SheaDawson\Blocks\BlockManager;
6
7
use SilverStripe\ORM\DataObject;
8
use SilverStripe\ORM\Versioning\Versioned;
9
10
use SilverStripe\Core\ClassInfo;
11
12
use SilverStripe\View\Requirements;
13
use SilverStripe\View\SSViewer;
14
15
use SilverStripe\Control\Controller;
16
17
use SilverStripe\Security\PermissionProvider;
18
use SilverStripe\Security\Permission;
19
use SilverStripe\Security\Group;
20
use SilverStripe\Security\Member;
21
22
use SilverStripe\Forms\DropdownField;
23
use SilverStripe\Forms\HTMLText;
24
use SilverStripe\Forms\OptionsetField;
25
use SilverStripe\Forms\ListboxField;
26
use SilverStripe\Forms\Tab;
27
/**
28
 * Block
29
 * Subclass this basic Block with your more interesting ones.
30
 *
31
 * @author Shea Dawson <[email protected]>
32
 */
33
class Block extends DataObject implements PermissionProvider
34
{
35
36
	private static $table_name = 'Block';
0 ignored issues
show
Unused Code introduced by
The property $table_name is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
37
38
    /**
39
     * @var array
40
     */
41
    private static $db = array(
0 ignored issues
show
Unused Code introduced by
The property $db is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
42
        'Title' => 'Varchar(255)',
43
        'CanViewType' => "Enum('Anyone, LoggedInUsers, OnlyTheseUsers', 'Anyone')",
44
        'ExtraCSSClasses' => 'Varchar',
45
        // these are legacy fields, in place to make migrations from old blocks version easier
46
        'Weight' => 'Int',
47
        'Area' => 'Varchar',
48
        'Published' => 'Boolean',
49
    );
50
51
    /**
52
     * @var array
53
     */
54
    private static $many_many = array(
0 ignored issues
show
Unused Code introduced by
The property $many_many is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
55
        "ViewerGroups" => "SilverStripe\Security\Group",
56
    );
57
58
    /**
59
     * @var array
60
     */
61
    private static $belongs_many_many = array(
0 ignored issues
show
Unused Code introduced by
The property $belongs_many_many is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
62
        "Pages" => "SilverStripe\CMS\Model\SiteTree",
63
        "BlockSets" => "SheaDawson\Blocks\model\BlockSet",
64
    );
65
66
    private static $summary_fields = array(
0 ignored issues
show
Unused Code introduced by
The property $summary_fields is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
67
        'singular_name',
68
        'Title',
69
        'isPublishedField',
70
        'UsageListAsString',
71
    );
72
73
    private static $searchable_fields = array(
0 ignored issues
show
Unused Code introduced by
The property $searchable_fields is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
74
        'Title',
75
        'ClassName',
76
    );
77
78
    public function fieldLabels($includerelations = true)
79
    {
80
        $labels =  parent::fieldLabels($includerelations);
81
82
        $labels = array_merge($labels, array(
83
            'singular_name' => _t('Block.BlockType', 'Block Type'),
84
            'Title' => _t('Block.Title', 'Title'),
85
            'isPublishedField' => _t('Block.IsPublishedField', 'Published'),
86
            'UsageListAsString' => _t('Block.UsageListAsString', 'Used on'),
87
            'ExtraCSSClasses' => _t('Block.ExtraCSSClasses', 'Extra CSS Classes'),
88
            'ClassName' => _t('Block.BlockType', 'Block Type'),
89
        ));
90
91
        return $labels;
92
    }
93
94
    public function getDefaultSearchContext()
95
    {
96
        $context = parent::getDefaultSearchContext();
97
98
        $results = $this->blockManager->getBlockClasses();
99
        if (sizeof($results) > 1) {
100
            $classfield = new DropdownField('ClassName', _t('Block.BlockType', 'Block Type'));
101
            $classfield->setSource($results);
102
            $classfield->setEmptyString(_t('Block.Any', '(any)'));
103
            $context->addField($classfield);
104
        }
105
106
        return $context;
107
    }
108
109
    /**
110
     * @var array
111
     */
112
    private static $default_sort = array('Title' => 'ASC');
0 ignored issues
show
Unused Code introduced by
The property $default_sort is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
113
114
    /**
115
     * @var array
116
     */
117
    private static $dependencies = array(
0 ignored issues
show
Unused Code introduced by
The property $dependencies is not used and could be removed.

This check marks private properties in classes that are never used. Those properties can be removed.

Loading history...
118
        'blockManager' => '%$SheaDawson\\Blocks\\BlockManager',
119
    );
120
121
    /**
122
     * @var BlockManager
123
     */
124
    public $blockManager;
125
126
    /**
127
     * @var BlockController
128
     */
129
    protected $controller;
130
131
    /**
132
     * @return mixed
133
     */
134
    public function getTypeForGridfield()
135
    {
136
        return $this->singular_name();
137
    }
138
139
    public function getCMSFields()
140
    {
141
        $self = $this;
142
        $this->beforeUpdateCMSFields(function($fields) use($self) {
143
            /** @var FieldList $fields */
144
            Requirements::add_i18n_javascript(BLOCKS_DIR . '/javascript/lang');
145
146
            // this line is a temporary patch until I can work out why this dependency isn't being
147
            // loaded in some cases...
148
            if (!$self->blockManager) {
149
                $self->blockManager = singleton('BlockManager');
150
            }
151
152
            // ClassNmae - block type/class field
153
            $classes = $self->blockManager->getBlockClasses();
154
            $fields->addFieldToTab('Root.Main', DropdownField::create('ClassName', _t('Block.BlockType', 'Block Type'), $classes)->addExtraClass('block-type'), 'Title');
155
156
            // BlockArea - display areas field if on page edit controller
157
            if (Controller::curr()->class == 'CMSPageEditController') {
158
                $currentPage = Controller::curr()->currentPage();
159
                $areas = $self->blockManager->getAreasForPageType($currentPage->ClassName);
160
                $fields->addFieldToTab(
161
                    'Root.Main',
162
                    $blockAreaField = DropdownField::create('ManyMany[BlockArea]', _t('Block.BlockArea', 'Block Area'), $areas),
163
                    'ClassName'
164
                );
165
166
                if (count($areas) > 1) {
167
                    $blockAreaField->setEmptyString('(Select one)');
168
                }
169
170
                if (BlockManager::config()->get('block_area_preview')) {
171
                    $blockAreaField->setRightTitle($currentPage->areasPreviewButton());
172
                }
173
            }
174
175
            $fields->removeFieldFromTab('Root', 'BlockSets');
176
            $fields->removeFieldFromTab('Root', 'Pages');
177
178
            // legacy fields, will be removed in later release
179
            $fields->removeByName('Weight');
180
            $fields->removeByName('Area');
181
            $fields->removeByName('Published');
182
183
            if ($self->blockManager->getUseExtraCSSClasses()) {
184
                $fields->addFieldToTab('Root.Main', $fields->dataFieldByName('ExtraCSSClasses'), 'Title');
185
            } else {
186
                $fields->removeByName('ExtraCSSClasses');
187
            }
188
189
            // Viewer groups
190
            $fields->removeFieldFromTab('Root', 'ViewerGroups');
191
            $groupsMap = Group::get()->map('ID', 'Breadcrumbs')->toArray();
192
            asort($groupsMap);
193
            $viewersOptionsField = new OptionsetField(
194
                'CanViewType',
195
                _t('SiteTree.ACCESSHEADER', 'Who can view this page?')
196
            );
197
            $viewerGroupsField = ListboxField::create('ViewerGroups', _t('SiteTree.VIEWERGROUPS', 'Viewer Groups'))
198
                ->setSource($groupsMap)
199
                ->setAttribute(
200
                    'data-placeholder',
201
                    _t('SiteTree.GroupPlaceholder', 'Click to select group')
202
                );
203
            $viewersOptionsSource = array();
204
            $viewersOptionsSource['Anyone'] = _t('SiteTree.ACCESSANYONE', 'Anyone');
205
            $viewersOptionsSource['LoggedInUsers'] = _t('SiteTree.ACCESSLOGGEDIN', 'Logged-in users');
206
            $viewersOptionsSource['OnlyTheseUsers'] = _t('SiteTree.ACCESSONLYTHESE', 'Only these people (choose from list)');
207
            $viewersOptionsField->setSource($viewersOptionsSource)->setValue('Anyone');
208
209
            $fields->addFieldToTab('Root', new Tab('ViewerGroups', _t('Block.ViewerGroups', 'Viewer Groups')));
210
            $fields->addFieldsToTab('Root.ViewerGroups', array(
211
                $viewersOptionsField,
212
                $viewerGroupsField,
213
            ));
214
215
            // Disabled for now, until we can list ALL pages this block is applied to (inc via sets)
216
            // As otherwise it could be misleading
217
            // Show a GridField (list only) with pages which this block is used on
218
            // $fields->removeFieldFromTab('Root.Pages', 'Pages');
0 ignored issues
show
Unused Code Comprehensibility introduced by
73% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
219
            // $fields->addFieldsToTab('Root.Pages',
0 ignored issues
show
Unused Code Comprehensibility introduced by
72% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
220
            //         new GridField(
221
            //                 'Pages',
222
            //                 'Used on pages',
223
            //                 $self->Pages(),
0 ignored issues
show
Unused Code Comprehensibility introduced by
72% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
224
            //                 $gconf = GridFieldConfig_Base::create()));
0 ignored issues
show
Unused Code Comprehensibility introduced by
54% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
225
            // enhance gridfield with edit links to pages if GFEditSiteTreeItemButtons is available
226
            // a GFRecordEditor (default) combined with BetterButtons already gives the possibility to
227
            // edit versioned records (Pages), but STbutton loads them in their own interface instead
228
            // of GFdetailform
229
            // if(class_exists('GridFieldEditSiteTreeItemButton')) {
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
230
            //     $gconf->addComponent(new GridFieldEditSiteTreeItemButton());
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
231
            // }
232
        });
233
        return parent::getCMSFields();
234
    }
235
236
    /**
237
     * Renders this block with appropriate templates
238
     * looks for templates that match BlockClassName_AreaName
239
     * falls back to BlockClassName.
240
     *
241
     * @return string
242
     **/
243
    public function forTemplate()
244
    {
245
        if ($this->BlockArea) {
246
            $template = array($this->class.'_'.$this->BlockArea);
247
248
            if (SSViewer::hasTemplate($template)) {
249
                return $this->renderWith($template);
250
            }
251
        }
252
253
        return $this->renderWith($this->ClassName, $this->getController());
254
    }
255
256
    /**
257
     * @return string
258
     */
259
    public function BlockHTML()
260
    {
261
        return $this->forTemplate();
262
    }
263
264
    /*
265
     * Checks if deletion was an actual deletion, not just unpublish
266
     * If so, remove relations
267
     */
268
    public function onAfterDelete()
269
    {
270
        parent::onAfterDelete();
271
        if (Versioned::current_stage() == 'Stage') {
272
            $this->Pages()->removeAll();
273
            $this->BlockSets()->removeAll();
274
        }
275
    }
276
277
    /**
278
     * Remove relations onAfterDuplicate.
279
     */
280
    public function onAfterDuplicate()
281
    {
282
        // remove relations to pages & blocksets duplicated from the original item
283
        $this->Pages()->removeAll();
284
        $this->BlockSets()->removeAll();
285
    }
286
287
    /*
288
     * Base permissions
289
     */
290
    public function canView($member = null)
291
    {
292
        if (!$member || !(is_a($member, 'Member')) || is_numeric($member)) {
293
            $member = Member::currentUserID();
294
        }
295
296
        // admin override
297
        if ($member && Permission::checkMember($member, array('ADMIN', 'SITETREE_VIEW_ALL'))) {
298
            return true;
299
        }
300
301
        // Standard mechanism for accepting permission changes from extensions
302
        $extended = $this->extendedCan('canView', $member);
303
        if ($extended !== null) {
304
            return $extended;
305
        }
306
307
        // check for empty spec
308
        if (!$this->CanViewType || $this->CanViewType == 'Anyone') {
309
            return true;
310
        }
311
312
        // check for any logged-in users
313
        if ($this->CanViewType == 'LoggedInUsers' && $member) {
314
            return true;
315
        }
316
317
        // check for specific groups
318
        if ($member && is_numeric($member)) {
319
            $member = Member::get()->byID($member);
320
        }
321
        if ($this->CanViewType == 'OnlyTheseUsers' && $member && $member->inGroups($this->ViewerGroups())) {
322
            return true;
323
        }
324
325
        return false;
326
    }
327
328
    public function canEdit($member = null)
329
    {
330
        return Permission::check('ADMIN') || Permission::check('BLOCK_EDIT');
331
    }
332
333
    public function canDelete($member = null)
334
    {
335
        return Permission::check('ADMIN') || Permission::check('BLOCK_DELETE');
336
    }
337
338
    public function canCreate($member = null, $context = array())
339
    {
340
        return Permission::check('ADMIN') || Permission::check('BLOCK_CREATE');
341
    }
342
343
    public function canPublish($member = null)
344
    {
345
        return Permission::check('ADMIN') || Permission::check('BLOCK_PUBLISH');
346
    }
347
348
    public function providePermissions()
349
    {
350
        return array(
351
            'BLOCK_EDIT' => array(
352
                'name' => _t('Block.EditBlock', 'Edit a Block'),
353
                'category' => _t('Block.PermissionCategory', 'Blocks'),
354
            ),
355
            'BLOCK_DELETE' => array(
356
                'name' => _t('Block.DeleteBlock', 'Delete a Block'),
357
                'category' => _t('Block.PermissionCategory', 'Blocks'),
358
            ),
359
            'BLOCK_CREATE' => array(
360
                'name' => _t('Block.CreateBlock', 'Create a Block'),
361
                'category' => _t('Block.PermissionCategory', 'Blocks'),
362
            ),
363
            'BLOCK_PUBLISH' => array(
364
                'name' => _t('Block.PublishBlock', 'Publish a Block'),
365
                'category' => _t('Block.PermissionCategory', 'Blocks'),
366
            ),
367
        );
368
    }
369
370
    public function onAfterWrite()
371
    {
372
        parent::onAfterWrite();
373
        if ($this->hasExtension('FilesystemPublisher')) {
374
            $this->republish($this);
375
        }
376
    }
377
378
    /**
379
     * Get a list of URL's to republish when this block changes
380
     * if using StaticPublisher module.
381
     */
382
    public function pagesAffectedByChanges()
383
    {
384
        $pages = $this->Pages();
385
        $urls = array();
386
        foreach ($pages as $page) {
387
            $urls[] = $page->Link();
388
        }
389
390
        return $urls;
391
    }
392
393
    /*
394
     * Get a list of Page and Blockset titles this block is used on
395
     */
396
    public function UsageListAsString()
397
    {
398
        $pages = implode(', ', $this->Pages()->column('URLSegment'));
399
        $sets = implode(', ', $this->BlockSets()->column('Title'));
400
        $_t_pages = _t('Block.PagesAsString', 'Pages: {pages}', '', array('pages' => $pages));
0 ignored issues
show
Documentation introduced by
array('pages' => $pages) is of type array<string,string,{"pages":"string"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
401
        $_t_sets = _t('Block.BlockSetsAsString', 'Block Sets: {sets}', '', array('sets' => $sets));
0 ignored issues
show
Documentation introduced by
array('sets' => $sets) is of type array<string,string,{"sets":"string"}>, but the function expects a string.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
402
        if ($pages && $sets) {
403
            return "$_t_pages<br />$_t_sets";
404
        }
405
        if ($pages) {
406
            return $_t_pages;
407
        }
408
        if ($sets) {
409
            return $_t_sets;
410
        }
411
    }
412
413
    /**
414
     * Check if this block has been published.
415
     *
416
     * @return bool True if this page has been published.
417
     */
418
    public function isPublished()
419
    {
420
        if (!$this->exists()) {
421
            return false;
422
        }
423
424
        return (DB::query("SELECT \"ID\" FROM \"Block_Live\" WHERE \"ID\" = $this->ID")->value())
425
            ? 1
426
            : 0;
427
    }
428
429
    /**
430
     * Check if this block has been published.
431
     *
432
     * @return bool True if this page has been published.
433
     */
434
    public function isPublishedNice()
435
    {
436
        $field = Boolean::create('isPublished');
437
        $field->setValue($this->isPublished());
438
439
        return $field->Nice();
440
    }
441
442
    /**
443
     * @return HTMLText
444
     */
445
    public function isPublishedIcon()
446
    {
447
        $obj = HTMLText::create();
448
        if ($this->isPublished()) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->isPublished() of type false|integer is loosely compared to true; this is ambiguous if the integer can be zero. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For integer values, zero is a special case, in particular the following results might be unexpected:

0   == false // true
0   == null  // true
123 == false // false
123 == null  // false

// It is often better to use strict comparison
0 === false // false
0 === null  // false
Loading history...
449
            $obj->setValue('<img src="' . FRAMEWORK_ADMIN_DIR . '/images/alert-good.gif" />');
450
        } else {
451
            $obj->setValue('<img src="' . FRAMEWORK_ADMIN_DIR . '/images/alert-bad.gif" />');
452
        }
453
        return $obj;
454
    }
455
456
    /**
457
     * CSS Classes to apply to block element in template.
458
     *
459
     * @return string $classes
460
     */
461
    public function CSSClasses($stopAtClass = 'DataObject')
462
    {
463
        $classes = strtolower(parent::CSSClasses($stopAtClass));
464
465
        if (!empty($classes) && ($prefix = $this->blockManager->getPrefixDefaultCSSClasses())) {
466
            $classes = $prefix.str_replace(' ', " {$prefix}", $classes);
467
        }
468
469
        if ($this->blockManager->getUseExtraCSSClasses()) {
470
            $classes = $this->ExtraCSSClasses ? $classes." $this->ExtraCSSClasses" : $classes;
471
        }
472
473
        return $classes;
474
    }
475
476
    /**
477
     * Access current page scope from Block templates with $CurrentPage
478
     *
479
     * @return Controller
480
     */
481
    public function getCurrentPage()
482
    {
483
        return Controller::curr();
484
    }
485
486
    /**
487
     * @throws Exception
488
     *
489
     * @return BlockController
490
     */
491
    public function getController()
492
    {
493
        if ($this->controller) {
494
            return $this->controller;
495
        }
496
        foreach (array_reverse(ClassInfo::ancestry($this->class)) as $blockClass) {
497
            $controllerClass = "{$blockClass}Controller";
498
            if (class_exists($controllerClass)) {
499
                break;
500
            }
501
        }
502
        if (!class_exists($controllerClass)) {
0 ignored issues
show
Bug introduced by
The variable $controllerClass does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
503
            throw new Exception("Could not find controller class for $this->classname");
504
        }
505
        $this->controller = Injector::inst()->create($controllerClass, $this);
506
        $this->controller->init();
507
508
        return $this->controller;
509
    }
510
}
511