aboutsummaryrefslogtreecommitdiff
path: root/lib/classes/Widget.php
blob: 3ccdf56b671dbe79ccb1f8d0e745a4bec3a101af (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
<?php
/**
 * Generic Widget
 *
 * @author  Jan-Hendrik Willms <tleilax+studip@gmail.com>
 * @license GPL 2 or later
 * @since 3.1
 */
class Widget
{
    /**
     * Contains the elements of the widget.
     */
    protected $elements = [];

    /**
     * Contains the template used to render the widget.
     */
    protected $template = 'widgets/widget';

    /**
     * Contains additional template variables
     */
    protected $template_variables = [];

    /**
     * Layout for this widget
     */
    protected $layout = 'widgets/widget-layout';

    /**
     * Forced rendering?
     */
    protected $forced_rendering = false;

    /**
     * @var array classes for the layout <div> around the widget
     */
    protected $layout_css_classes = [];

    /**
     * Add an element to the widget.
     *
     * @template E of WidgetElement
     * @param E $element The actual element
     * @param String $index   Index/name of the element
     * @return E
     */
    public function addElement(WidgetElement $element, $index = null): WidgetElement
    {
        $index = $index ?: $this->guessIndex($element);

        $this->elements[$index] = $element;

        return $element;
    }

    /**
     * Insert an element before a specific other element or at the end of the
     * list if the specified position is invalid.
     *
     * @template E of WidgetElement
     * @param E $element The actual element
     * @param String        $before_index Insert element before this element.
     * @param String        $index        Index/name of the element
     * @return E
     */
    public function insertElement(WidgetElement $element, $before_index, $index = null): WidgetElement
    {
        $index = $index ?: $this->guessIndex($element);

        $inserted = false;

        $elements = [];
        foreach ($this->elements as $idx => $elmnt) {
            if ($idx === $before_index) {
                $inserted = true;
                $elements[$index] = $element;
            }
            $elements[$idx] = $elmnt;
        }

        if (!$inserted) {
            $elements[$index] = $element;
        }

        $this->elements = $elements;

        return $element;
    }

    /**
     * Tries to guess an appropriate index name for the element.
     *
     * @param WidgetElement $element The element in question
     * @return String Appropriate index name
     */
    protected function guessIndex(WidgetElement $element)
    {
        $class = get_class($element);
        if ($class !== 'WidgetElement') {
            $index = mb_strtolower($class);
            $index = str_replace('element', '', $index);
            $index .= '-' . md5(serialize($element));
        } else {
            $index = md5(serialize($element));
        }

        $temp    = $index;
        $counter = 0;
        while (array_key_exists($temp, $this->elements)) {
            $temp = sprintf('%s-%u', $index, $counter++);
        }
        $index = $temp;

        return $index;
    }

    /**
     * Retrieve the element at the specified position.
     *
     * @param String $index Index/name of the element to retrieve.
     * @return WidgetElement The element at the specified position.
     * @throws Exception if the specified position is invalid
     */
    public function getElement($index)
    {
        if (!isset($this->elements[$index])) {
            throw new Exception('Trying to retrieve unknown widget element "' . $index . '"');
        }
        return $this->elements[$index];
    }

    /**
     * Returns all elements of the widget.
     * @return array of WidgetElement
     */
    public function getElements()
    {
        return $this->elements;
    }

    /**
     * Removes the element at the specified position.
     *
     * @param String $index Index/name of the element to remove.
     * @throws Exception if the specified position is invalid
     */
    public function removeElement($index)
    {
        if (!isset($this->elements[$index])) {
            throw new Exception('Trying to remove unknown widget element "' . $index . '"');
        }
        unset($this->elements[$index]);
    }

    /**
     * Returns whether this widget has any elements.
     *
     * @return bool True if the widget has at least one element, false
     *              otherwise.
     */
    public function hasElements()
    {
        return count($this->elements) > 0;
    }

    /**
     * Returns whether an element exists at the given index.
     *
     * @param String $index Index/name of the element to check for.
     * @return bool Does a widget exist at the given index?
     */
    public function hasElement($index)
    {
        return isset($this->elements[$index]);
    }

    /**
     * Force rendering
     *
     * @param bool $state Whether to force rendering or not
     */
    public function forceRendering($state = true)
    {
        $this->forced_rendering = $state;
    }

    /**
     * Adds a css class to the layout <div> around the widget.
     */
    public function addLayoutCSSClass($css_class)
    {
        if (!in_array($css_class, $this->layout_css_classes)) {
            $this->layout_css_classes[] = $css_class;
        }
    }

    /**
     * Removes a css class from the layout <div> around the widget.
     */
    public function removeLayoutCSSClass($css_class)
    {
        $this->layout_classes = array_diff($this->layout_css_class, [$css_class]);
    }

    /**
     * Renders the widget.
     * The widget will only be rendered if it contains at least one element.
     *
     * @return String The THML code of the rendered sidebar widget
     */
    public function render($variables = [])
    {
        $content = '';

        if ($this->hasElements() || $this->forced_rendering) {

            $template = $GLOBALS['template_factory']->open($this->template);
            $template->set_attributes($variables + $this->template_variables);
            $template->elements = $this->elements;

            if ($this->layout) {
                $layout = $GLOBALS['template_factory']->open($this->layout);
                $layout->layout_css_classes = $this->layout_css_classes;
                $template->set_layout($layout);
            }

            $content = $template->render();
        }

        return $content;
    }

    public function __isset($offset)
    {
        return isset($this->template_variables[$offset]);
    }

    public function __get($offset)
    {
        return $this->template_variables[$offset] ?? null;
    }

    public function __set($offset, $value)
    {
        $this->template_variables[$offset] = $value;
    }

    public function __unset($offset)
    {
        unset($this->template_variables[$offset]);
    }
}