forked from silverstripe/silverstripe-cms
/
Widget.php
232 lines (199 loc) · 5.67 KB
/
Widget.php
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
<?php
/**
* Widgets let CMS authors drag and drop small pieces of functionality into
* defined areas of their websites.
*
* ## Forms
* You can use forms in widgets by implementing a {@link Widget_Controller}.
* See {@link Widget_Controller} for more information.
*
* @package cms
* @subpackage widgets
*/
class Widget extends DataObject {
static $db = array(
"Sort" => "Int",
"Enabled" => "Boolean"
);
static $defaults = array(
'Enabled' => true
);
static $has_one = array(
"Parent" => "WidgetArea",
);
static $has_many = array();
static $many_many = array();
static $belongs_many_many = array();
static $default_sort = "\"Sort\"";
static $title = "Widget Title";
static $cmsTitle = "Name of this widget";
static $description = "Description of what this widget does.";
function getCMSFields() {
$fields = new FieldList();
$this->extend('updateCMSFields', $fields);
return $fields;
}
/**
* Note: Overloaded in {@link Widget_Controller}.
*
* @return string HTML
*/
function WidgetHolder() {
return $this->renderWith("WidgetHolder");
}
/**
* Renders the widget content in a custom template with the same name as the current class.
* This should be the main point of output customization.
*
* Invoked from within WidgetHolder.ss, which contains
* the "framing" around the custom content, like a title.
*
* Note: Overloaded in {@link Widget_Controller}.
*
* @return string HTML
*/
function Content() {
return $this->renderWith(array_reverse(ClassInfo::ancestry($this->class)));
}
function Title() {
return Object::get_static($this->class, 'title');
}
function CMSTitle() {
return Object::get_static($this->class, 'cmsTitle');
}
function Description() {
return Object::get_static($this->class, 'description');
}
function DescriptionSegment() {
return $this->renderWith('WidgetDescription');
}
/**
* @see Widget_Controller->editablesegment()
*/
function EditableSegment() {
return $this->renderWith('WidgetEditor');
}
function CMSEditor() {
$output = '';
$fields = $this->getCMSFields();
foreach($fields as $field) {
$name = $field->Name();
$field->setValue($this->getField($name));
$renderedField = $field->FieldHolder();
$renderedField = preg_replace("/name=\"([A-Za-z0-9\-_]+)\"/", "name=\"Widget[" . $this->ID . "][\\1]\"", $renderedField);
$renderedField = preg_replace("/id=\"([A-Za-z0-9\-_]+)\"/", "id=\"Widget[" . $this->ID . "][\\1]\"", $renderedField);
$output .= $renderedField;
}
return $output;
}
function ClassName() {
return $this->class;
}
function Name() {
return "Widget[".$this->ID."]";
}
function populateFromPostData($data) {
foreach($data as $name => $value) {
if($name != "Type") {
$this->setField($name, $value);
}
}
$this->write();
// The field must be written to ensure a unique ID.
$this->Name = $this->class.$this->ID;
$this->write();
}
}
/**
* Optional controller for every widget which has its own logic,
* e.g. in forms. It always handles a single widget, usually passed
* in as a database identifier through the controller URL.
* Needs to be constructed as a nested controller
* within a {@link ContentController}.
*
* ## Forms
* You can add forms like in any other sapphire controller.
* If you need access to the widget from within a form,
* you can use `$this->controller->getWidget()` inside the form logic.
* Note: Widget controllers currently only work on {@link Page} objects,
* because the logic is implemented in {@link ContentController->handleWidget()}.
* Copy this logic and the URL rules to enable it for other controllers.
*
* @package cms
* @subpackage widgets
*/
class Widget_Controller extends Controller {
/**
* @var Widget
*/
protected $widget;
static $allowed_actions = array(
'editablesegment'
);
function __construct($widget = null) {
// TODO This shouldn't be optional, is only necessary for editablesegment()
if($widget) {
$this->widget = $widget;
$this->failover = $widget;
}
parent::__construct();
}
public function Link($action = null) {
$segment = Controller::join_links('widget', ($this->widget ? $this->widget->ID : null), $action);
if(Director::get_current_page()) {
return Director::get_current_page()->Link($segment);
} else {
return Controller::curr()->Link($segment);
}
}
/**
* @return Widget
*/
function getWidget() {
return $this->widget;
}
/**
* Overloaded from {@link Widget->Content()}
* to allow for controller/form linking.
*
* @return string HTML
*/
function Content() {
return $this->renderWith(array_reverse(ClassInfo::ancestry($this->widget->class)));
}
/**
* Overloaded from {@link Widget->WidgetHolder()}
* to allow for controller/form linking.
*
* @return string HTML
*/
function WidgetHolder() {
return $this->renderWith("WidgetHolder");
}
/**
* Uses the `WidgetEditor.ss` template and {@link Widget->editablesegment()}
* to render a administrator-view of the widget. It is assumed that this
* view contains form elements which are submitted and saved through {@link WidgetAreaEditor}
* within the CMS interface.
*
* @return string HTML
*/
function editablesegment() {
$className = $this->urlParams['ID'];
if(class_exists($className) && is_subclass_of($className, 'Widget')) {
$obj = new $className();
return $obj->EditableSegment();
} else {
user_error("Bad widget class: $className", E_USER_WARNING);
return "Bad widget class name given";
}
}
}
/**
* @package cms
* @subpackage widgets
*/
class Widget_TreeDropdownField extends TreeDropdownField {
function FieldHolder($properties = array()) {}
function Field($properties = array()) {}
}