Skip to content
This repository
Browse code

API CHANGE Moved Widget API to new module (https://github.com/silvers…

…tripe/silverstripe-widgets), incl. WidgetArea and WidgetAreaEditor classes
  • Loading branch information...
commit 8a72b32e95e759fe2c22dd807c51ab8e962d0dbb 1 parent 4fd7573
Ingo Schommer authored April 18, 2012
232  code/widgets/Widget.php
... ...
@@ -1,232 +0,0 @@
1  
-<?php
2  
-/**
3  
- * Widgets let CMS authors drag and drop small pieces of functionality into 
4  
- * defined areas of their websites.
5  
- * 
6  
- * ## Forms
7  
- * You can use forms in widgets by implementing a {@link Widget_Controller}.
8  
- * See {@link Widget_Controller} for more information.
9  
- * 
10  
- * @package cms
11  
- * @subpackage widgets
12  
- */
13  
-class Widget extends DataObject {
14  
-	static $db = array(
15  
-		"Sort" => "Int",
16  
-		"Enabled" => "Boolean"
17  
-	);
18  
-	
19  
-	static $defaults = array(
20  
-		'Enabled' => true
21  
-	);
22  
-	
23  
-	static $has_one = array(
24  
-		"Parent" => "WidgetArea",
25  
-	);
26  
-	
27  
-	static $has_many = array();
28  
-	static $many_many = array();
29  
-	static $belongs_many_many = array();
30  
-	
31  
-	static $default_sort = "\"Sort\"";
32  
-	
33  
-	static $title = "Widget Title";
34  
-	static $cmsTitle = "Name of this widget";
35  
-	static $description = "Description of what this widget does.";
36  
-	
37  
-	function getCMSFields() {
38  
-		$fields = new FieldList();
39  
-		$this->extend('updateCMSFields', $fields);
40  
-		return $fields;
41  
-	}
42  
-	
43  
-	/**
44  
-	 * Note: Overloaded in {@link Widget_Controller}.
45  
-	 * 
46  
-	 * @return string HTML
47  
-	 */
48  
-	function WidgetHolder() {
49  
-		return $this->renderWith("WidgetHolder");
50  
-	}
51  
-	
52  
-	/**
53  
-	 * Renders the widget content in a custom template with the same name as the current class.
54  
-	 * This should be the main point of output customization.
55  
-	 * 
56  
-	 * Invoked from within WidgetHolder.ss, which contains
57  
-	 * the "framing" around the custom content, like a title.
58  
-	 * 
59  
-	 * Note: Overloaded in {@link Widget_Controller}.
60  
-	 * 
61  
-	 * @return string HTML
62  
-	 */
63  
-	function Content() {
64  
-		return $this->renderWith(array_reverse(ClassInfo::ancestry($this->class)));
65  
-	}
66  
-	
67  
-	function Title() {
68  
-		return Object::get_static($this->class, 'title');
69  
-	}
70  
-	
71  
-	function CMSTitle() {
72  
-		return Object::get_static($this->class, 'cmsTitle');
73  
-	}
74  
-	
75  
-	function Description() {
76  
-		return Object::get_static($this->class, 'description');
77  
-	}
78  
-	
79  
-	function DescriptionSegment() {
80  
-		return $this->renderWith('WidgetDescription'); 
81  
-	}
82  
-	
83  
-	/**
84  
-	 * @see Widget_Controller->editablesegment()
85  
-	 */
86  
-	function EditableSegment() {
87  
-		return $this->renderWith('WidgetEditor'); 
88  
-	}
89  
-	
90  
-	function CMSEditor() {
91  
-		$output = '';
92  
-		$fields = $this->getCMSFields();
93  
-		foreach($fields as $field) {
94  
-			$name = $field->Name();
95  
-			$field->setValue($this->getField($name));
96  
-			$renderedField = $field->FieldHolder();
97  
-			$renderedField = preg_replace("/name=\"([A-Za-z0-9\-_]+)\"/", "name=\"Widget[" . $this->ID . "][\\1]\"", $renderedField);
98  
-			$renderedField = preg_replace("/id=\"([A-Za-z0-9\-_]+)\"/", "id=\"Widget[" . $this->ID . "][\\1]\"", $renderedField);
99  
-			$output .= $renderedField;
100  
-		}
101  
-		return $output;
102  
-	}
103  
-	
104  
-	function ClassName() {
105  
-		return $this->class;
106  
-	}
107  
-	
108  
-	function Name() {
109  
-		return "Widget[".$this->ID."]";
110  
-	}
111  
-
112  
-	function populateFromPostData($data) {
113  
-		foreach($data as $name => $value) {
114  
-			if($name != "Type") {
115  
-				$this->setField($name, $value);
116  
-			}
117  
-		}
118  
-		
119  
-		$this->write();
120  
-		
121  
-		// The field must be written to ensure a unique ID.
122  
-		$this->Name = $this->class.$this->ID;
123  
-		$this->write();
124  
-	}
125  
-	
126  
-}
127  
-
128  
-/**
129  
- * Optional controller for every widget which has its own logic,
130  
- * e.g. in forms. It always handles a single widget, usually passed
131  
- * in as a database identifier through the controller URL.
132  
- * Needs to be constructed as a nested controller
133  
- * within a {@link ContentController}.
134  
- * 
135  
- * ## Forms
136  
- * You can add forms like in any other SilverStripe controller.
137  
- * If you need access to the widget from within a form,
138  
- * you can use `$this->controller->getWidget()` inside the form logic.
139  
- * Note: Widget controllers currently only work on {@link Page} objects,
140  
- * because the logic is implemented in {@link ContentController->handleWidget()}.
141  
- * Copy this logic and the URL rules to enable it for other controllers.
142  
- * 
143  
- * @package cms
144  
- * @subpackage widgets
145  
- */
146  
-class Widget_Controller extends Controller {
147  
-	
148  
-	/**
149  
-	 * @var Widget
150  
-	 */
151  
-	protected $widget;
152  
-	
153  
-	static $allowed_actions = array( 
154  
-		'editablesegment'
155  
-	);
156  
-	
157  
-	function __construct($widget = null) {
158  
-		// TODO This shouldn't be optional, is only necessary for editablesegment()
159  
-		if($widget) {
160  
-			$this->widget = $widget;
161  
-			$this->failover = $widget;
162  
-		}
163  
-		
164  
-		parent::__construct();
165  
-	}
166  
-	
167  
-	public function Link($action = null) {
168  
-		$segment = Controller::join_links('widget', ($this->widget ? $this->widget->ID : null), $action);
169  
-		
170  
-		if(Director::get_current_page()) {
171  
-			return Director::get_current_page()->Link($segment);
172  
-		} else {
173  
-			return Controller::curr()->Link($segment);
174  
-		}
175  
-	}
176  
-	
177  
-	/**
178  
-	 * @return Widget
179  
-	 */
180  
-	function getWidget() {
181  
-		return $this->widget;
182  
-	}
183  
-	
184  
-	/**
185  
-	 * Overloaded from {@link Widget->Content()}
186  
-	 * to allow for controller/form linking.
187  
-	 * 
188  
-	 * @return string HTML
189  
-	 */
190  
-	function Content() {
191  
-		return $this->renderWith(array_reverse(ClassInfo::ancestry($this->widget->class)));
192  
-	}
193  
-	
194  
-	/**
195  
-	 * Overloaded from {@link Widget->WidgetHolder()}
196  
-	 * to allow for controller/form linking.
197  
-	 * 
198  
-	 * @return string HTML
199  
-	 */
200  
-	function WidgetHolder() {
201  
-		return $this->renderWith("WidgetHolder");
202  
-	}
203  
-	
204  
-	/**
205  
-	 * Uses the `WidgetEditor.ss` template and {@link Widget->editablesegment()}
206  
-	 * to render a administrator-view of the widget. It is assumed that this
207  
-	 * view contains form elements which are submitted and saved through {@link WidgetAreaEditor}
208  
-	 * within the CMS interface.
209  
-	 * 
210  
-	 * @return string HTML
211  
-	 */
212  
-	function editablesegment() {
213  
-		$className = $this->urlParams['ID'];
214  
-		if(class_exists($className) && is_subclass_of($className, 'Widget')) {
215  
-			$obj = new $className();
216  
-			return $obj->EditableSegment();
217  
-		} else {
218  
-			user_error("Bad widget class: $className", E_USER_WARNING);
219  
-			return "Bad widget class name given";
220  
-		}
221  
-	}	
222  
-}
223  
-
224  
-/**
225  
- * @package cms
226  
- * @subpackage widgets
227  
- */
228  
-class Widget_TreeDropdownField extends TreeDropdownField {
229  
-	function FieldHolder($properties = array()) {}
230  
-	function Field($properties = array()) {}
231  
-}
232  
-
72  code/widgets/WidgetArea.php
... ...
@@ -1,72 +0,0 @@
1  
-<?php
2  
-/**
3  
- * Represents a set of widgets shown on a page.
4  
- * @package cms
5  
- * @subpackage widgets
6  
- */
7  
-class WidgetArea extends DataObject {
8  
-	
9  
-	static $db = array();
10  
-	
11  
-	static $has_one = array();
12  
-	
13  
-	static $has_many = array(
14  
-		"Widgets" => "Widget"
15  
-	);
16  
-	
17  
-	static $many_many = array();
18  
-	
19  
-	static $belongs_many_many = array();
20  
-	
21  
-	public $template = __CLASS__;
22  
-	
23  
-	/**
24  
-	 * Used in template instead of {@link Widgets()}
25  
-	 * to wrap each widget in its controller, making
26  
-	 * it easier to access and process form logic
27  
-	 * and actions stored in {@link Widget_Controller}.
28  
-	 * 
29  
-	 * @return SS_List Collection of {@link Widget_Controller}
30  
-	 */
31  
-	function WidgetControllers() {
32  
-		$controllers = new ArrayList();
33  
-
34  
-		foreach($this->ItemsToRender() as $widget) {
35  
-			// find controller
36  
-			$controllerClass = '';
37  
-			foreach(array_reverse(ClassInfo::ancestry($widget->class)) as $widgetClass) {
38  
-				$controllerClass = "{$widgetClass}_Controller";
39  
-				if(class_exists($controllerClass)) break;
40  
-			}
41  
-			$controller = new $controllerClass($widget);
42  
-			$controller->init();
43  
-			$controllers->push($controller);
44  
-		}
45  
-
46  
-		return $controllers;
47  
-	}
48  
-	
49  
-	function Items() {
50  
-		return $this->getComponents('Widgets');
51  
-	}
52  
-	
53  
-	function ItemsToRender() {
54  
-		return $this->getComponents('Widgets', "\"Widget\".\"Enabled\" = 1");
55  
-	}
56  
-	
57  
-	function forTemplate() {
58  
-		return $this->renderWith($this->template); 
59  
-	}
60  
-	
61  
-	function setTemplate($template) {
62  
-		$this->template = $template;
63  
-	}
64  
-	
65  
-	function onBeforeDelete() {
66  
-		parent::onBeforeDelete();
67  
-		foreach($this->Widgets() as $widget) {
68  
-			$widget->delete();
69  
-		}
70  
-	}
71  
-}
72  
-
142  code/widgets/WidgetAreaEditor.php
... ...
@@ -1,142 +0,0 @@
1  
-<?php
2  
-/**
3  
- * Special field type for selecting and configuring widgets on a page.
4  
- * @package cms
5  
- * @subpackage content
6  
- */
7  
-class WidgetAreaEditor extends FormField {
8  
-	/**
9  
-	 * 3 variables to hold titles for the template
10  
-	 */
11  
-	public $InUseTitle;
12  
-	public $AvailableTitle;
13  
-	public $ToAddTitle;
14  
-
15  
-	function __construct($name, $widgetClasses = array('Widget'), $maxWidgets = 0) {
16  
-		$this->MaxWidgets = $maxWidgets;
17  
-		$this->widgetClasses = $widgetClasses;
18  
-		
19  
-		parent::__construct($name);
20  
-	}
21  
-	
22  
-	function FieldHolder($properties = array()) {
23  
-		Requirements::css(CMS_DIR . '/css/WidgetAreaEditor.css');
24  
-		Requirements::javascript(THIRDPARTY_DIR . "/prototype/prototype.js");
25  
-		Requirements::javascript(THIRDPARTY_DIR . '/behaviour/behaviour.js');
26  
-		Requirements::javascript(CMS_DIR . '/javascript/WidgetAreaEditor.js');
27  
-
28  
-		return $this->renderWith("WidgetAreaEditor");
29  
-	}
30  
-	
31  
-	function AvailableWidgets() {
32  
-		
33  
-		$widgets= new ArrayList();
34  
-		
35  
-		foreach($this->widgetClasses as $widgetClass) {
36  
-			$classes = ClassInfo::subclassesFor($widgetClass);
37  
-			array_shift($classes);
38  
-			foreach($classes as $class) {
39  
-				$widgets->push(singleton($class));
40  
-			}
41  
-		}
42  
-		
43  
-		return $widgets;
44  
-	}
45  
-	
46  
-	function UsedWidgets() {
47  
-		// Call class_exists() to load Widget.php earlier and avoid a segfault
48  
-		class_exists('Widget');
49  
-		
50  
-		$relationName = $this->name;
51  
-		$widgets = $this->form->getRecord()->getComponent($relationName)->Items();
52  
-		return $widgets;
53  
-	}
54  
-	
55  
-	function IdxField() {
56  
-		return $this->id() . 'ID';
57  
-	}
58  
-	
59  
-	function Value() {
60  
-		$relationName = $this->name;
61  
-		return $this->form->getRecord()->getComponent($relationName)->ID;
62  
-	}
63  
-	
64  
-	function saveInto(DataObjectInterface $record) {
65  
-		$name = $this->name;
66  
-		$idName = $name . "ID";
67  
-
68  
-		$widgetarea = $record->getComponent($name);
69  
-		$widgetarea->write();
70  
-		
71  
-		$record->$idName = $widgetarea->ID;
72  
-	
73  
-		$widgets = $widgetarea->Items();
74  
-	
75  
-		// store the field IDs and delete the missing fields
76  
-		// alternatively, we could delete all the fields and re add them
77  
-		$missingWidgets = array();
78  
-		
79  
-		if($widgets) {
80  
-			foreach($widgets as $existingWidget) {
81  
-				$missingWidgets[$existingWidget->ID] = $existingWidget;
82  
-			}
83  
-		}
84  
-		
85  
-		if(isset($_REQUEST['Widget'])) {
86  
-			foreach(array_keys($_REQUEST['Widget']) as $widgetAreaName) {
87  
-				if ($widgetAreaName !== $this->name) {
88  
-					continue;
89  
-				}
90  
-
91  
-				foreach(array_keys($_REQUEST['Widget'][$widgetAreaName]) as $newWidgetID) {
92  
-					$newWidgetData = $_REQUEST['Widget'][$widgetAreaName][$newWidgetID];
93  
-
94  
-					// Sometimes the id is "new-1" or similar, ensure this doesn't get into the query
95  
-					if(!is_numeric($newWidgetID)) {
96  
-						$newWidgetID = 0;
97  
-					}
98  
-				
99  
-					// \"ParentID\" = '0' is for the new page
100  
-			  		$widget = DataObject::get_one(
101  
-						'Widget',
102  
-						"(\"ParentID\" = '{$record->$name()->ID}' OR \"ParentID\" = '0') AND \"Widget\".\"ID\" = '$newWidgetID'"
103  
-					);
104  
-
105  
-		  		
106  
-			  		// check if we are updating an existing widget
107  
-					if($widget && isset($missingWidgets[$widget->ID])) {
108  
-			  			unset($missingWidgets[$widget->ID]);
109  
-					}
110  
-					
111  
-			  		// create a new object
112  
-			  		if(!$widget && !empty($newWidgetData['Type']) && class_exists($newWidgetData['Type'])) {
113  
-			  			$widget = new $newWidgetData['Type']();
114  
-			  			$widget->ID = 0;
115  
-			  			$widget->ParentID = $record->$name()->ID;
116  
-
117  
-			  			if(!is_subclass_of($widget, 'Widget')) {
118  
-			  				$widget = null;
119  
-			  			}
120  
-			  		}
121  
-		  		
122  
-					if($widget) {
123  
-						if($widget->ParentID == 0) {
124  
-							$widget->ParentID = $record->$name()->ID;
125  
-						}
126  
-						// echo "Saving $widget->ID into $name/$widget->ParentID\n<br/>";
127  
-						$widget->populateFromPostData($newWidgetData);
128  
-					}
129  
-				}
130  
-			}
131  
-		}
132  
-		
133  
-		// remove the fields not saved
134  
-		if($missingWidgets) {
135  
-			foreach($missingWidgets as $removedWidget) {
136  
-				if(isset($removedWidget) && is_numeric($removedWidget->ID)) {
137  
-					$removedWidget->delete();
138  
-				}
139  
-			}
140  
-		}
141  
-	}
142  
-}
282  javascript/WidgetAreaEditor.js
... ...
@@ -1,282 +0,0 @@
1  
-// Shortcut-function (until we update to Prototye v1.5)
2  
-if(typeof $$ != "Function") $$ = document.getElementsBySelector;
3  
-
4  
-/**
5  
- * File: WidgetAreaEditor.js
6  
- */
7  
-
8  
-/**
9  
- * Class: WidgetAreaEditorClass
10  
- */
11  
-WidgetAreaEditorClass = Class.create();
12  
-WidgetAreaEditorClass.prototype = {
13  
-	initialize: function() {
14  
-		this.name = this.getAttribute('name');
15  
-		this.rewriteWidgetAreaAttributes();
16  
-		UsedWidget.applyToChildren(document.getElementById('usedWidgets-'+this.name), 'div.Widget');
17  
-
18  
-		var availableWidgets = document.getElementById('availableWidgets-'+this.name).childNodes;
19  
-		
20  
-		for(var i = 0; i < availableWidgets.length; i++) {
21  
-			var widget = availableWidgets[i];
22  
-			// Don't run on comments, whitespace, etc
23  
-			if (widget.nodeType == 1) {
24  
-				// Gotta change their ID's because otherwise we get clashes between two tabs
25  
-				widget.id = widget.id + '-'+this.name;
26  
-			}
27  
-		}
28  
-	
29  
-	
30  
-		// Create dummy sortable to prevent javascript errors
31  
-		Sortable.create('availableWidgets-'+this.name, {
32  
-			tag: 'li',
33  
-			handle: 'handle',
34  
-			containment: []
35  
-		});
36  
-		
37  
-		// Used widgets are sortable
38  
-		Sortable.create('usedWidgets-'+this.name, {
39  
-			tag: 'div',
40  
-			handle: 'handle',
41  
-			containment: ['availableWidgets-'+this.name, 'usedWidgets-'+this.name],
42  
-			onUpdate: this.updateWidgets
43  
-		});
44  
-		
45  
-		// Figure out maxid, this is used when creating new widgets
46  
-		this.maxid = 0;
47  
-		
48  
-		var usedWidgets = document.getElementById('usedWidgets-'+this.name).childNodes;
49  
-		for(var i = 0; i < usedWidgets.length; i++) {
50  
-			var widget = usedWidgets[i];
51  
-			if(widget.id) {
52  
-				widgetid = widget.id.match(/\Widget\[(.+?)\]\[([0-9]+)\]/i);
53  
-				if(widgetid && parseInt(widgetid[2]) > this.maxid) {
54  
-					this.maxid = parseInt(widgetid[2]);
55  
-				}
56  
-			}
57  
-		}
58  
-
59  
-		// Ensure correct sort values are written when page is saved
60  
-		// TODO Adjust to new event listeners
61  
-		jQuery('.cms-edit-form').bind('ajaxsubmit', this.beforeSave.bind(this));
62  
-	},
63  
-	
64  
-	rewriteWidgetAreaAttributes: function() {
65  
-		this.name = this.getAttribute('name');
66  
-
67  
-		var monkeyWith = function(widgets, name) {
68  
-			if (!widgets) {
69  
-				return;
70  
-			}
71  
-			for(var i = 0; i < widgets.length; i++) {
72  
-				widget = widgets[i];
73  
-				if (!widget.getAttribute('rewritten') && (widget.id || widget.name)) {
74  
-					if (widget.id && widget.id.indexOf('Widget[') === 0) {
75  
-						var newValue = widget.id.replace(/Widget\[/, 'Widget['+name+'][');
76  
-						//console.log('Renaming '+widget.tagName+' ID '+widget.id+' to '+newValue);
77  
-						widget.id = newValue;
78  
-					}
79  
-					if (widget.name && widget.name.indexOf('Widget[') === 0) {
80  
-						var newValue = widget.name.replace(/Widget\[/, 'Widget['+name+'][');
81  
-						//console.log('Renaming '+widget.tagName+' Name '+widget.name+' to '+newValue);
82  
-						widget.name = newValue;
83  
-					}
84  
-					widget.setAttribute('rewritten', 'yes');
85  
-				}
86  
-				else {
87  
-					//console.log('Skipping '+(widget.id ? widget.id : (widget.name ? widget.name : 'unknown '+widget.tagName)));
88  
-				}
89  
-			}
90  
-		}
91  
-		
92  
-		monkeyWith($$('#WidgetAreaEditor-'+this.name+' .Widget'), this.name);
93  
-		monkeyWith($$('#WidgetAreaEditor-'+this.name+' .Widget *'), this.name);
94  
-	},
95  
-	
96  
-	beforeSave: function() {
97  
-		// Ensure correct sort values are written when page is saved
98  
-		var usedWidgets = document.getElementById('usedWidgets-'+this.name);
99  
-		
100  
-		if(usedWidgets) {
101  
-			this.sortWidgets();
102  
-		
103  
-			var children = usedWidgets.childNodes;
104  
-		
105  
-			for( var i = 0; i < children.length; ++i ) {
106  
-				var child = children[i];
107  
-			
108  
-				if(child.beforeSave) {
109  
-					child.beforeSave();
110  
-				}
111  
-			}
112  
-		}
113  
-	},
114  
-	
115  
-	addWidget: function(className, holder) {
116  
-		
117  
-		if (document.getElementById('WidgetAreaEditor-'+holder).getAttribute('maxwidgets')) {
118  
-			var maxCount = document.getElementById('WidgetAreaEditor-'+holder).getAttribute('maxwidgets');
119  
-			var count = $$('#usedWidgets-'+holder+' .Widget').length;
120  
-			if (count+1 > maxCount) {
121  
-				alert(ss.i18n._t('WidgetAreaEditor.TOOMANY'));
122  
-				return;
123  
-			}
124  
-		}
125  
-		
126  
-		
127  
-		this.name = holder;
128  
-		jQuery.ajax({
129  
-			'url': 'Widget_Controller/EditableSegment/' + className, 
130  
-			'success' : document.getElementById('usedWidgets-'+holder).parentNode.parentNode.insertWidgetEditor.bind(this)
131  
-		});
132  
-	},
133  
-
134  
-	updateWidgets: function() {
135  
-		var self = this;
136  
-
137  
-		// Gotta get the name of the current dohickey based off the ID
138  
-		this.name = this.element.id.split('-').pop();
139  
-
140  
-		// alert(this.name);
141  
-	
142  
-		// Gotta get the name of the current dohickey based off the ID
143  
-		this.name = this.element.id.split('-').pop();
144  
-		
145  
-
146  
-		// This is called when an available widgets is dragged over to used widgets.
147  
-		// It inserts the editor form into the new used widget
148  
-
149  
-		var usedWidgets = document.getElementById('usedWidgets-'+this.name).childNodes;
150  
-		for(var i = 0; i < usedWidgets.length; i++) {
151  
-			var widget = usedWidgets[i];
152  
-			if(widget.id && (widget.id.indexOf("Widget[") != 0) && (widget.id != 'NoWidgets-'+this.name)) {
153  
-				// Need to remove the -$Name part.
154  
-				var wIdArray = widget.id.split('-');
155  
-				wIdArray.pop();
156  
-
157  
-				jQuery.ajax({
158  
-					'url': 'Widget_Controller/EditableSegment/' + wIdArray.join('-'),
159  
-					'success' : function() {
160  
-						document.getElementById('usedWidgets-'+self.name).parentNode.parentNode.insertWidgetEditor();
161  
-					}
162  
-				});
163  
-			}
164  
-		}
165  
-	},
166  
-	
167  
-	insertWidgetEditor: function(response) {
168  
-		// Remove placeholder text
169  
-		if(document.getElementById('NoWidgets-'+this.name)) {
170  
-			document.getElementById('usedWidgets-'+this.name).removeChild(document.getElementById('NoWidgets-'+this.name));
171  
-		}
172  
-
173  
-		var usedWidgets = document.getElementById('usedWidgets-'+this.name).childNodes;
174  
-		
175  
-		// Give the widget a unique id
176  
-		widgetContent = response.responseText.replace(/Widget\[0\]/gi, "Widget[new-" + (++document.getElementById('usedWidgets-'+this.name).parentNode.parentNode.maxid) + "]");
177  
-		new Insertion.Top(document.getElementById('usedWidgets-'+this.name), widgetContent);
178  
-		
179  
-		document.getElementById('usedWidgets-'+this.name).parentNode.parentNode.rewriteWidgetAreaAttributes();
180  
-		UsedWidget.applyToChildren(document.getElementById('usedWidgets-'+this.name), 'div.Widget');
181  
-		
182  
-		// Repply some common form controls
183  
-		WidgetTreeDropdownField.applyTo('div.usedWidgets .TreeDropdownField');
184  
-		
185  
-		Sortable.create('usedWidgets-'+this.name, {
186  
-			tag: 'div',
187  
-			handle: 'handle',
188  
-			containment: ['availableWidgets-'+this.name, 'usedWidgets-'+this.name],
189  
-			onUpdate: document.getElementById('usedWidgets-'+this.name).parentNode.parentNode.updateWidgets
190  
-		});
191  
-	},
192  
-	
193  
-	sortWidgets: function() {
194  
-		// Order the sort by the order the widgets are in the list
195  
-		var usedWidgets = document.getElementById('usedWidgets-'+this.name);
196  
-		
197  
-		if(usedWidgets) {
198  
-			widgets = usedWidgets.childNodes;
199  
-			
200  
-			for(i = 0; i < widgets.length; i++) {
201  
-				var div = widgets[i];
202  
-
203  
-				if(div.nodeName != '#comment') {
204  
-					var fields = div.getElementsByTagName('input');
205  
-					
206  
-					for(j = 0; field = fields.item(j); j++) {
207  
-						if(field.name == div.id + '[Sort]') {
208  
-							field.value = i;
209  
-						}
210  
-					}
211  
-				}
212  
-				
213  
-			}
214  
-		}
215  
-	},
216  
-	
217  
-	deleteWidget: function(widgetToRemove) {
218  
-		// Remove a widget from the used widgets column
219  
-		document.getElementById('usedWidgets-'+this.name).removeChild(widgetToRemove);
220  
-		// TODO ... re-create NoWidgets div?
221  
-	}
222  
-}
223  
-
224  
-/**
225  
- * Class: UsedWidget
226  
- */
227  
-UsedWidget = Class.create();
228  
-UsedWidget.prototype = {
229  
-	initialize: function() {
230  
-		// Call deleteWidget when delete button is pushed
231  
-		this.deleteButton = this.findDescendant('span', 'widgetDelete');
232  
-		if(this.deleteButton)
233  
-			this.deleteButton.onclick = this.deleteWidget.bind(this);
234  
-	},
235  
-	
236  
-	// Taken from FieldEditor
237  
-	findDescendant: function(tag, clsName, element) {
238  
-		if(!element)
239  
-			element = this;
240  
-		
241  
-		var descendants = element.getElementsByTagName(tag);
242  
-		
243  
-		for(var i = 0; i < descendants.length; i++) {
244  
-			var el = descendants[i];
245  
-			
246  
-			if(tag.toUpperCase() == el.tagName && el.className.indexOf( clsName ) != -1)
247  
-				return el;
248  
-		}
249  
-		
250  
-		return null;
251  
-	},
252  
-	
253  
-	deleteWidget: function() {
254  
-		this.parentNode.parentNode.parentNode.deleteWidget(this);
255  
-	}
256  
-}
257  
-
258  
-/**
259  
- * Class: AvailableWidgetHeader
260  
- */
261  
-AvailableWidgetHeader = Class.create();
262  
-AvailableWidgetHeader.prototype = {
263  
-	onclick: function(event) {
264  
-		parts = this.parentNode.id.split('-');
265  
-		var widgetArea = parts.pop();
266  
-		var className = parts.pop();
267  
-		document.getElementById('WidgetAreaEditor-'+widgetArea).addWidget(className, widgetArea);
268  
-	}
269  
-}
270  
-AvailableWidgetHeader.applyTo('div.availableWidgets .Widget h3');
271  
-
272  
-/**
273  
- * Class: WidgetTreeDropdownField
274  
- */
275  
-WidgetTreeDropdownField = Class.extend('TreeDropdownField');
276  
-WidgetTreeDropdownField.prototype = {
277  
-	getName: function() {
278  
-		return 'Widget_TDF_Endpoint';
279  
-	}
280  
-}
281  
-WidgetTreeDropdownField.applyTo('div.usedWidgets .TreeDropdownField');
282  
-WidgetAreaEditorClass.applyTo('.WidgetAreaEditor');
3  templates/WidgetArea.ss
... ...
@@ -1,3 +0,0 @@
1  
-<% control WidgetControllers %>
2  
-	$WidgetHolder
3  
-<% end_control %>
32  templates/WidgetAreaEditor.ss
... ...
@@ -1,32 +0,0 @@
1  
-<div class="WidgetAreaEditor" id="WidgetAreaEditor-$Name" name="$Name"<% if MaxWidgets %> maxwidgets="$MaxWidgets"<% end_if %>>
2  
-	<input type="hidden" id="$Name" name="$IdxField" value="$Value" />
3  
-	<div class="availableWidgetsHolder">
4  
-		<h2><% _t('AVAILABLE', 'Available Widgets') %></h2>
5  
-		<p><% _t('AVAILWIDGETS', 'Click a widget title below to use it on this page.') %></p>
6  
-		<div class="availableWidgets" id="availableWidgets-$Name">
7  
-			<% if AvailableWidgets %>
8  
-				<% control AvailableWidgets %>
9  
-					$DescriptionSegment
10  
-				<% end_control %>
11  
-			<% else %>
12  
-				<div class="NoWidgets" id="NoWidgets-$Name">
13  
-					<p><% _t('NOAVAIL', 'There are currently no widgets available.') %></p>
14  
-				</div>
15  
-			<% end_if %>
16  
-		</div>
17  
-	</div>
18  
-	<div class="usedWidgetsHolder">
19  
-		<h2><% _t('INUSE', 'Widgets currently used') %></h2>
20  
-		<p><% _t('TOSORT', 'To sort currently used widgets on this page, drag them up and down.') %></p>
21  
-		
22  
-		<div class="usedWidgets" id="usedWidgets-$Name">
23  
-			<% if UsedWidgets %>
24  
-				<% control UsedWidgets %>
25  
-					$EditableSegment
26  
-				<% end_control %>
27  
-			<% else %>
28  
-				<div class="NoWidgets" id="NoWidgets-$Name"></div>
29  
-			<% end_if %>
30  
-		</div>
31  
-	</div>
32  
-</div>
6  templates/WidgetDescription.ss
... ...
@@ -1,6 +0,0 @@
1  
-<div class="Widget" id="$ClassName">
2  
-	<h3 title="<% _t('CLICKTOADDWIDGET', 'Click to add this widget') %>">$CMSTitle</h3>
3  
-	<div class="widgetDescription">
4  
-		<p>$Description</p>
5  
-	</div>
6  
-</div>
12  templates/WidgetEditor.ss
... ...
@@ -1,12 +0,0 @@
1  
-<div class="$ClassName Widget" id="$Name">
2  
-	<h3 class="handle">$CMSTitle</h3>
3  
-	<div class="widgetDescription">
4  
-		<p>$Description</p>
5  
-	</div>
6  
-	<div class="widgetFields">
7  
-		$CMSEditor
8  
-		<input type="hidden" name="$Name[Type]" value="$ClassName" />   
9  
-		<input type="hidden" name="$Name[Sort]" value="$Sort" />
10  
-	</div>
11  
-	<p class="deleteWidget"><span class="widgetDelete"><% _t('DELETE', 'Delete') %></span></p>
12  
-</div>
4  templates/WidgetHolder.ss
... ...
@@ -1,4 +0,0 @@
1  
-<div class="WidgetHolder $ClassName<% if FirstLast %> $FirstLast<% end_if %>">
2  
-	<% if Title %><h3>$Title</h3><% end_if %>
3  
-	$Content
4  
-</div>
471  tests/widgets/WidgetAreaEditorTest.php
... ...
@@ -1,471 +0,0 @@
1  
-<?php
2  
-/**
3  
- * @package cms
4  
- * @subpackage tests
5  
- */
6  
-class WidgetAreaEditorTest extends SapphireTest {
7  
-	/**
8  
-	 * This is the widget you want to use for your unit tests.
9  
-	 */
10  
-	protected $widgetToTest = 'WidgetAreaEditorTest_TestWidget';
11  
-
12  
-	protected $extraDataObjects = array(
13  
-		'WidgetAreaEditorTest_FakePage',
14  
-		'WidgetAreaEditorTest_TestWidget',
15  
-	);
16  
-	
17  
-	protected $usesDatabase = true;
18  
-	
19  
-	function testFillingOneArea() {
20  
-		$oldRequest = $_REQUEST;
21  
-		
22  
-		$_REQUEST = array(
23  
-			'Widget' => array(
24  
-				'BottomBar' => array(
25  
-					'new-1' => array(
26  
-						'Title' => 'MyTestWidget',
27  
-						'Type' => $this->widgetToTest,
28  
-						'Sort' => 0
29  
-					)
30  
-				)
31  
-			)
32  
-		);
33  
-		
34  
-		$editorSide = new WidgetAreaEditor('SideBar');
35  
-		$editorBott = new WidgetAreaEditor('BottomBar');
36  
-		$page = new WidgetAreaEditorTest_FakePage();
37  
-
38  
-		$editorSide->saveInto($page);
39  
-		$editorBott->saveInto($page);
40  
-		$page->write();
41  
-		$page->flushCache();
42  
-		$page->BottomBar()->flushCache();
43  
-		$page->SideBar()->flushCache();
44  
-
45  
-		$this->assertEquals($page->BottomBar()->Widgets()->Count(), 1);
46  
-		$this->assertEquals($page->SideBar()->Widgets()->Count(), 0);
47  
-		
48  
-		$_REQUEST = $oldRequest;
49  
-	}
50  
-
51  
-	function testFillingTwoAreas() {
52  
-		$oldRequest = $_REQUEST;
53  
-		
54  
-		$_REQUEST = array(
55  
-			'Widget' => array(
56  
-				'SideBar' => array(
57  
-					'new-1' => array(
58  
-						'Title' => 'MyTestWidgetSide',
59  
-						'Type' => $this->widgetToTest,
60  
-						'Sort' => 0
61  
-					)
62  
-				),
63  
-				'BottomBar' => array(
64  
-					'new-1' => array(
65  
-						'Title' => 'MyTestWidgetBottom',
66  
-						'Type' => $this->widgetToTest,
67  
-						'Sort' => 0
68  
-					)
69  
-				)
70  
-			)
71  
-		);
72  
-		
73  
-		$editorSide = new WidgetAreaEditor('SideBar');
74  
-		$editorBott = new WidgetAreaEditor('BottomBar');
75  
-		$page = new WidgetAreaEditorTest_FakePage();
76  
-
77  
-		$editorSide->saveInto($page);
78  
-		$editorBott->saveInto($page);
79  
-		$page->write();
80  
-		$page->flushCache();
81  
-		$page->BottomBar()->flushCache();
82  
-		$page->SideBar()->flushCache();
83  
-		
84  
-		// Make sure they both got saved
85  
-		$this->assertEquals($page->BottomBar()->Widgets()->Count(), 1);
86  
-		$this->assertEquals($page->SideBar()->Widgets()->Count(), 1);
87  
-		
88  
-		$sideWidgets = $page->SideBar()->Widgets()->toArray();
89  
-		$bottWidgets = $page->BottomBar()->Widgets()->toArray();
90  
-		$this->assertEquals($sideWidgets[0]->Title(), 'MyTestWidgetSide');
91  
-		$this->assertEquals($bottWidgets[0]->Title(), 'MyTestWidgetBottom');
92  
-		
93  
-		$_REQUEST = $oldRequest;
94  
-	}
95  
-		
96  
-	function testDeletingOneWidgetFromOneArea() {
97  
-		$oldRequest = $_REQUEST;
98  
-		
99  
-		// First get some widgets in there
100  
-		$_REQUEST = array(
101  
-			'Widget' => array(
102  
-				'SideBar' => array(
103  
-					'new-1' => array(
104  
-						'Title' => 'MyTestWidgetSide',
105  
-						'Type' => $this->widgetToTest,
106  
-						'Sort' => 0
107  
-					)
108  
-				),
109  
-				'BottomBar' => array(
110  
-					'new-1' => array(
111  
-						'Title' => 'MyTestWidgetBottom',
112  
-						'Type' => $this->widgetToTest,
113  
-						'Sort' => 0
114  
-					)
115  
-				)
116  
-			)
117  
-		);
118  
-		
119  
-		$editorSide = new WidgetAreaEditor('SideBar');
120  
-		$editorBott = new WidgetAreaEditor('BottomBar');
121  
-		$page = new WidgetAreaEditorTest_FakePage();
122  
-
123  
-		$editorSide->saveInto($page);
124  
-		$editorBott->saveInto($page);
125  
-		$page->write();
126  
-		$page->flushCache();
127  
-		$page->BottomBar()->flushCache();
128  
-		$page->SideBar()->flushCache();
129  
-		$sideWidgets = $page->SideBar()->Widgets()->toArray();
130  
-		$bottWidgets = $page->BottomBar()->Widgets()->toArray();
131  
-		
132  
-		// Save again (after removing the SideBar's widget)
133  
-		$_REQUEST = array(
134  
-			'Widget' => array(
135  
-				'SideBar' => array(
136  
-				),
137  
-				'BottomBar' => array(
138  
-					$bottWidgets[0]->ID => array(
139  
-						'Title' => 'MyTestWidgetBottom',
140  
-						'Type' => $this->widgetToTest,
141  
-						'Sort' => 0
142  
-					)
143  
-				)
144  
-			)
145  
-		);
146  
-
147  
-		$editorSide->saveInto($page);
148  
-		$editorBott->saveInto($page);
149  
-
150  
-		$page->write();
151  
-		$page->flushCache();
152  
-		$page->BottomBar()->flushCache();
153  
-		$page->SideBar()->flushCache();
154  
-		$sideWidgets = $page->SideBar()->Widgets()->toArray();
155  
-		$bottWidgets = $page->BottomBar()->Widgets()->toArray();
156  
-		
157  
-		$this->assertEquals($page->BottomBar()->Widgets()->Count(), 1);
158  
-		$this->assertEquals($bottWidgets[0]->Title(), 'MyTestWidgetBottom');
159  
-		$this->assertEquals($page->SideBar()->Widgets()->Count(), 0);
160  
-		
161  
-		$_REQUEST = $oldRequest;
162  
-	}
163  
-
164  
-	function testDeletingAWidgetFromEachArea() {
165  
-		$oldRequest = $_REQUEST;
166  
-		
167  
-		// First get some widgets in there
168  
-		$_REQUEST = array(
169  
-			'Widget' => array(
170  
-				'SideBar' => array(
171  
-					'new-1' => array(
172  
-						'Title' => 'MyTestWidgetSide',
173  
-						'Type' => $this->widgetToTest,
174  
-						'Sort' => 0
175  
-					)
176  
-				),
177  
-				'BottomBar' => array(
178  
-					'new-1' => array(
179  
-						'Title' => 'MyTestWidgetBottom',
180  
-						'Type' => $this->widgetToTest,
181  
-						'Sort' => 0
182  
-					)
183  
-				)
184  
-			)
185  
-		);
186  
-		
187  
-		$editorSide = new WidgetAreaEditor('SideBar');
188  
-		$editorBott = new WidgetAreaEditor('BottomBar');
189  
-		$page = new WidgetAreaEditorTest_FakePage();
190  
-
191  
-		$editorSide->saveInto($page);
192  
-		$editorBott->saveInto($page);
193  
-		$page->write();
194  
-		$page->flushCache();
195  
-		$page->BottomBar()->flushCache();
196  
-		$page->SideBar()->flushCache();
197  
-		$sideWidgets = $page->SideBar()->Widgets()->toArray();
198  
-		$bottWidgets = $page->BottomBar()->Widgets()->toArray();
199  
-		
200  
-		// Save again (after removing the SideBar's widget)
201  
-		$_REQUEST = array(
202  
-			'Widget' => array(
203  
-				'SideBar' => array(
204  
-				),
205  
-				'BottomBar' => array(
206  
-				)
207  
-			)
208  
-		);
209  
-
210  
-		$editorSide->saveInto($page);
211  
-		$editorBott->saveInto($page);
212  
-		
213  
-		$page->write();
214  
-		$page->flushCache();
215  
-		$page->BottomBar()->flushCache();
216  
-		$page->SideBar()->flushCache();
217  
-		$sideWidgets = $page->SideBar()->Widgets()->toArray();
218  
-		$bottWidgets = $page->BottomBar()->Widgets()->toArray();
219  
-		
220  
-		$this->assertEquals($page->BottomBar()->Widgets()->Count(), 0);
221  
-		$this->assertEquals($page->SideBar()->Widgets()->Count(), 0);
222  
-		
223  
-		$_REQUEST = $oldRequest;
224  
-	}
225  
-	
226  
-	function testEditingOneWidget() {
227  
-		$oldRequest = $_REQUEST;
228  
-		
229  
-		// First get some widgets in there
230  
-		$_REQUEST = array(
231  
-			'Widget' => array(
232  
-				'SideBar' => array(
233  
-					'new-1' => array(
234  
-						'Title' => 'MyTestWidgetSide',
235  
-						'Type' => $this->widgetToTest,
236  
-						'Sort' => 0
237  
-					)
238  
-				),
239  
-				'BottomBar' => array(
240  
-					'new-1' => array(
241  
-						'Title' => 'MyTestWidgetBottom',
242  
-						'Type' => $this->widgetToTest,
243  
-						'Sort' => 0
244  
-					)
245  
-				)
246  
-			)
247  
-		);
248  
-		
249  
-		$editorSide = new WidgetAreaEditor('SideBar');
250  
-		$editorBott = new WidgetAreaEditor('BottomBar');
251  
-		$page = new WidgetAreaEditorTest_FakePage();
252  
-
253  
-		$editorSide->saveInto($page);
254  
-		$editorBott->saveInto($page);
255  
-		$page->write();
256  
-		$page->flushCache();
257  
-		$page->BottomBar()->flushCache();
258  
-		$page->SideBar()->flushCache();
259  
-		$sideWidgets = $page->SideBar()->Widgets()->toArray();
260  
-		$bottWidgets = $page->BottomBar()->Widgets()->toArray();
261  
-		
262  
-		// Save again (after removing the SideBar's widget)
263  
-		$_REQUEST = array(
264  
-			'Widget' => array(
265  
-				'SideBar' => array(
266  
-					$sideWidgets[0]->ID => array(
267  
-						'Title' => 'MyTestWidgetSide-edited',
268  
-						'Type' => $this->widgetToTest,
269  
-						'Sort' => 0
270  
-					)
271  
-				),
272  
-				'BottomBar' => array(
273  
-					$bottWidgets[0]->ID => array(
274  
-						'Title' => 'MyTestWidgetBottom',
275  
-						'Type' => $this->widgetToTest,
276  
-						'Sort' => 0
277  
-					)
278  
-				)
279  
-			)
280  
-		);
281  
-		
282  
-
283  
-		$editorSide->saveInto($page);
284  
-		$editorBott->saveInto($page);
285  
-
286  
-		$page->write();
287  
-		$page->flushCache();
288  
-		$page->BottomBar()->flushCache();
289  
-		$page->SideBar()->flushCache();
290  
-		$sideWidgets = $page->SideBar()->Widgets()->toArray();
291  
-		$bottWidgets = $page->BottomBar()->Widgets()->toArray();
292  
-		
293  
-		$this->assertEquals($page->BottomBar()->Widgets()->Count(), 1);
294  
-		$this->assertEquals($page->SideBar()->Widgets()->Count(), 1);
295  
-		$this->assertEquals($bottWidgets[0]->Title(), 'MyTestWidgetBottom');
296  
-		$this->assertEquals($sideWidgets[0]->Title(), 'MyTestWidgetSide-edited');
297  
-		
298  
-		
299  
-		$_REQUEST = $oldRequest;
300  
-	}
301  
-
302  
-	function testEditingAWidgetFromEachArea() {
303  
-		$oldRequest = $_REQUEST;
304  
-		
305  
-		// First get some widgets in there
306  
-		$_REQUEST = array(
307  
-			'Widget' => array(
308  
-				'SideBar' => array(
309  
-					'new-1' => array(
310  
-						'Title' => 'MyTestWidgetSide',
311  
-						'Type' => $this->widgetToTest,
312  
-						'Sort' => 0
313  
-					)
314  
-				),
315  
-				'BottomBar' => array(
316  
-					'new-1' => array(
317  
-						'Title' => 'MyTestWidgetBottom',
318  
-						'Type' => $this->widgetToTest,
319  
-						'Sort' => 0
320  
-					)
321  
-				)
322  
-			)
323  
-		);
324  
-		
325  
-		$editorSide = new WidgetAreaEditor('SideBar');
326  
-		$editorBott = new WidgetAreaEditor('BottomBar');
327  
-		$page = new WidgetAreaEditorTest_FakePage();
328  
-
329  
-		$editorSide->saveInto($page);
330