Completed
Push — master ( f3b14d...1fe4c3 )
by Damian
10s
created

WidgetController::__construct()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 9
rs 9.6666
c 0
b 0
f 0
cc 2
eloc 5
nc 2
nop 1
1
<?php
2
3
namespace SilverStripe\Widgets\Controllers;
4
5
use SilverStripe\Admin\LeftAndMain;
6
use SilverStripe\Control\Controller;
7
use SilverStripe\Control\Director;
8
use SilverStripe\Core\ClassInfo;
9
use SilverStripe\i18n\i18n;
10
use SilverStripe\Security\Member;
11
use SilverStripe\Widgets\Model\Widget;
12
13
/**
14
 * Optional controller for every widget which has its own logic, e.g. in forms.
15
 *
16
 * It always handles a single widget, usually passed in as a database
17
 * identifier through the controller URL. Needs to be constructed as a nested
18
 * controller within a {@link ContentController}.
19
 *
20
 * ## Forms
21
 * You can add forms like in any other SilverStripe controller. If you need
22
 * access to the widget from within a form, you can use
23
 * `$this->controller->getWidget()` inside the form logic.
24
 *
25
 * Note: Widget controllers currently only work on {@link Page} objects,
26
 * because the logic is implemented in {@link ContentController->handleWidget()}.
27
 * Copy this logic and the URL rules to enable it for other controllers.
28
 *
29
 * @package widgets
30
 */
31
class WidgetController extends Controller
32
{
33
    /**
34
     * @var Widget
35
     */
36
    protected $widget;
37
38
    /**
39
     * @var array
40
     */
41
    private static $allowed_actions = array(
0 ignored issues
show
Unused Code introduced by
The property $allowed_actions 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...
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
42
        'editablesegment'
43
    );
44
45
    /**
46
     * @param Widget $widget
47
     */
48
    public function __construct($widget = null)
49
    {
50
        if ($widget) {
51
            $this->widget = $widget;
52
            $this->failover = $widget;
53
        }
54
55
        parent::__construct();
56
    }
57
58
    /**
59
     * @param string $action
60
     * @return string
61
     */
62
    public function Link($action = null)
63
    {
64
        $id = ($this->widget) ? $this->widget->ID : null;
65
        $segment = Controller::join_links('widget', $id, $action);
66
67
        $page = Director::get_current_page();
68
        if ($page && !($page instanceof WidgetController)) {
69
            return $page->Link($segment);
70
        }
71
72
        if ($controller = $this->getParentController()) {
73
            return $controller->Link($segment);
74
        }
75
76
        return $segment;
77
    }
78
79
    /**
80
     * Cycles up the controller stack until it finds a non-widget controller
81
     * This is needed becauseController::currreturns the widget controller,
82
     * which means anyLinkfunction turns into endless loop.
83
     *
84
     * @return Controller
85
     */
86
    public function getParentController()
87
    {
88
        foreach (Controller::$controller_stack as $controller) {
89
            if (!($controller instanceof WidgetController)) {
90
                return $controller;
91
            }
92
        }
93
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by SilverStripe\Widgets\Con...er::getParentController of type SilverStripe\Control\Controller.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
94
    }
95
96
    /**
97
     * @return Widget
98
     */
99
    public function getWidget()
100
    {
101
        return $this->widget;
102
    }
103
104
    /**
105
     * Overloaded from {@link Widget->Content()} to allow for controller / form
106
     * linking.
107
     *
108
     * @return string HTML
109
     */
110
    public function Content()
111
    {
112
        return $this->renderWith(array_reverse(ClassInfo::ancestry($this->widget->class)));
113
    }
114
115
    /**
116
     * Overloaded from {@link Widget->WidgetHolder()} to allow for controller/
117
     * form linking.
118
     *
119
     * @return string HTML
120
     */
121
    public function WidgetHolder()
122
    {
123
        return $this->renderWith("WidgetHolder");
124
    }
125
126
    /**
127
     * Uses the `WidgetEditor.ss` template and {@link Widget->editablesegment()}
128
     * to render a administrator-view of the widget. It is assumed that this
129
     * view contains form elements which are submitted and saved through
130
     * {@link WidgetAreaEditor} within the CMS interface.
131
     *
132
     * @return string HTML
133
     */
134
    public function editablesegment()
135
    {
136
        // use left and main to set the html config
137
        $leftandmain = LeftAndMain::create();
138
        $leftandmain->doInit();
139
140
        // Decode if fully qualified - @see Widget::ClassName
141
        $className = str_replace('_', '\\', $this->urlParams['ID']);
142
        if (class_exists('Translatable') && Member::currentUserID()) {
143
            // set current locale based on logged in user's locale
144
            $locale = Member::currentUser()->Locale;
145
            i18n::set_locale($locale);
146
        }
147
        if (class_exists($className) && is_subclass_of($className, Widget::class)) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of might return inconsistent results on some PHP versions if \SilverStripe\Widgets\Model\Widget::class can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
148
            $obj = new $className();
149
            return $obj->EditableSegment();
150
        } else {
151
            user_error("Bad widget class: $className", E_USER_WARNING);
152
            return "Bad widget class name given";
153
        }
154
    }
155
}
156