mirror of
https://github.com/silverstripe/silverstripe-cms
synced 2024-10-22 08:05:56 +02:00
BUGFIX WidgetArea now works. Can have multiple areas on a page, and has unit tests
git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/cms/trunk@90831 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
parent
c59911dce2
commit
a0649efa49
@ -42,14 +42,14 @@ class WidgetAreaEditor extends FormField {
|
|||||||
function saveInto(DataObject $record) {
|
function saveInto(DataObject $record) {
|
||||||
$name = $this->name;
|
$name = $this->name;
|
||||||
$idName = $name . "ID";
|
$idName = $name . "ID";
|
||||||
|
|
||||||
$widgetarea = $record->getComponent($name);
|
$widgetarea = $record->getComponent($name);
|
||||||
$widgetarea->write();
|
$widgetarea->write();
|
||||||
|
|
||||||
$record->$idName = $widgetarea->ID;
|
$record->$idName = $widgetarea->ID;
|
||||||
|
|
||||||
$widgets = $widgetarea->Widgets();
|
$widgets = $widgetarea->Widgets();
|
||||||
|
|
||||||
// store the field IDs and delete the missing fields
|
// store the field IDs and delete the missing fields
|
||||||
// alternatively, we could delete all the fields and re add them
|
// alternatively, we could delete all the fields and re add them
|
||||||
$missingWidgets = array();
|
$missingWidgets = array();
|
||||||
@ -60,48 +60,50 @@ class WidgetAreaEditor extends FormField {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// write the new widgets to the database
|
|
||||||
if(isset($_REQUEST['Widget'])) {
|
if(isset($_REQUEST['Widget'])) {
|
||||||
foreach(array_keys($_REQUEST['Widget']) as $newWidgetID) {
|
foreach(array_keys($_REQUEST['Widget']) as $widgetAreaName) {
|
||||||
$newWidgetData = $_REQUEST['Widget'][$newWidgetID];
|
if ($widgetAreaName !== $this->name) {
|
||||||
|
continue;
|
||||||
// Sometimes the id is "new-1" or similar, ensure this doesn't get into the query
|
|
||||||
if(!is_numeric($newWidgetID)) {
|
|
||||||
$newWidgetID = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// \"ParentID\" = '0' is for the new page
|
foreach(array_keys($_REQUEST['Widget'][$widgetAreaName]) as $newWidgetID) {
|
||||||
$widget = DataObject::get_one(
|
$newWidgetData = $_REQUEST['Widget'][$widgetAreaName][$newWidgetID];
|
||||||
'Widget',
|
|
||||||
sprintf(
|
// Sometimes the id is "new-1" or similar, ensure this doesn't get into the query
|
||||||
'("ParentID" = %d OR "ParentID" = 0) AND "Widget"."ID" = %d',
|
if(!is_numeric($newWidgetID)) {
|
||||||
$record->$name()->ID,
|
$newWidgetID = 0;
|
||||||
(int)$newWidgetID
|
}
|
||||||
)
|
|
||||||
);
|
// \"ParentID\" = '0' is for the new page
|
||||||
|
$widget = DataObject::get_one(
|
||||||
// check if we are updating an existing widget
|
'Widget',
|
||||||
if($widget && isset($missingWidgets[$widget->ID])) {
|
"(\"ParentID\" = '{$record->$name()->ID}' OR \"ParentID\" = '0') AND \"Widget\".\"ID\" = '$newWidgetID'"
|
||||||
unset($missingWidgets[$widget->ID]);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
// create a new object
|
// check if we are updating an existing widget
|
||||||
if(!$widget && !empty($newWidgetData['Type']) && class_exists($newWidgetData['Type'])) {
|
if($widget && isset($missingWidgets[$widget->ID])) {
|
||||||
$widget = new $newWidgetData['Type']();
|
unset($missingWidgets[$widget->ID]);
|
||||||
$widget->ID = 0;
|
}
|
||||||
$widget->ParentID = $record->$name()->ID;
|
|
||||||
|
// create a new object
|
||||||
if(!is_subclass_of($widget, 'Widget')) {
|
if(!$widget && !empty($newWidgetData['Type']) && class_exists($newWidgetData['Type'])) {
|
||||||
$widget = null;
|
$widget = new $newWidgetData['Type']();
|
||||||
}
|
$widget->ID = 0;
|
||||||
}
|
$widget->ParentID = $record->$name()->ID;
|
||||||
|
|
||||||
if($widget) {
|
if(!is_subclass_of($widget, 'Widget')) {
|
||||||
if($widget->ParentID == 0) {
|
$widget = null;
|
||||||
$widget->ParentID = $record->$name()->ID;
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($widget) {
|
||||||
|
if($widget->ParentID == 0) {
|
||||||
|
$widget->ParentID = $record->$name()->ID;
|
||||||
|
}
|
||||||
|
// echo "Saving $widget->ID into $name/$widget->ParentID\n<br/>";
|
||||||
|
$widget->populateFromPostData($newWidgetData);
|
||||||
}
|
}
|
||||||
|
|
||||||
$widget->populateFromPostData($newWidgetData);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -109,7 +111,9 @@ class WidgetAreaEditor extends FormField {
|
|||||||
// remove the fields not saved
|
// remove the fields not saved
|
||||||
if($missingWidgets) {
|
if($missingWidgets) {
|
||||||
foreach($missingWidgets as $removedWidget) {
|
foreach($missingWidgets as $removedWidget) {
|
||||||
if(isset($removedWidget) && is_numeric($removedWidget->ID)) $removedWidget->delete();
|
if(isset($removedWidget) && is_numeric($removedWidget->ID)) {
|
||||||
|
$removedWidget->delete();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,7 +15,7 @@ div.usedWidgets {
|
|||||||
border: 1px #CCC dotted;
|
border: 1px #CCC dotted;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
}
|
}
|
||||||
#NoWidgets {
|
.NoWidgets {
|
||||||
padding: 50px; /* Make this nice and big and easily 'droppable' */
|
padding: 50px; /* Make this nice and big and easily 'droppable' */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,53 +1,97 @@
|
|||||||
WidgetAreaEditorClass = Class.create();
|
WidgetAreaEditorClass = Class.create();
|
||||||
WidgetAreaEditorClass.applyTo('div.WidgetAreaEditor');
|
|
||||||
|
|
||||||
WidgetAreaEditorClass.prototype = {
|
WidgetAreaEditorClass.prototype = {
|
||||||
initialize: function() {
|
initialize: function() {
|
||||||
UsedWidget.applyToChildren($('WidgetAreaEditor_usedWidgets'), 'div.Widget');
|
this.name = this.getAttribute('name');
|
||||||
|
this.rewriteWidgetAreaAttributes();
|
||||||
|
UsedWidget.applyToChildren($('usedWidgets-'+this.name), 'div.Widget');
|
||||||
|
|
||||||
// Make available widgets draggable
|
// Make available widgets draggable
|
||||||
var availableWidgets = $('WidgetAreaEditor_availableWidgets').childNodes;
|
var availableWidgets = $('availableWidgets-'+this.name).childNodes;
|
||||||
|
|
||||||
for(var i = 0; i < availableWidgets.length; i++) {
|
for(var i = 0; i < availableWidgets.length; i++) {
|
||||||
var widget = availableWidgets[i];
|
var widget = availableWidgets[i];
|
||||||
if(widget.id)
|
// Don't run on comments, whitespace, etc
|
||||||
new Draggable(widget.id);
|
if (widget.nodeType == 1) {
|
||||||
|
// Gotta change their ID's because otherwise we get clashes between two tabs
|
||||||
|
widget.id = widget.id + '-'+this.name;
|
||||||
|
if(widget.id) {
|
||||||
|
widget.onclick = function(event) {
|
||||||
|
parts = event.currentTarget.id.split('-');
|
||||||
|
var widgetArea = parts.pop();
|
||||||
|
var className = parts.pop();
|
||||||
|
$('WidgetAreaEditor-'+widgetArea).addWidget(className, widgetArea);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Create dummy sortable to prevent javascript errors
|
// Create dummy sortable to prevent javascript errors
|
||||||
Sortable.create('WidgetAreaEditor_availableWidgets', {
|
Sortable.create('availableWidgets-'+this.name, {
|
||||||
tag: 'li',
|
tag: 'li',
|
||||||
handle: 'handle',
|
handle: 'handle',
|
||||||
containment: []
|
containment: []
|
||||||
});
|
});
|
||||||
|
|
||||||
// Used widgets are sortable
|
// Used widgets are sortable
|
||||||
Sortable.create('WidgetAreaEditor_usedWidgets', {
|
Sortable.create('usedWidgets-'+this.name, {
|
||||||
tag: 'div',
|
tag: 'div',
|
||||||
handle: 'handle',
|
handle: 'handle',
|
||||||
containment: ['WidgetAreaEditor_availableWidgets', 'WidgetAreaEditor_usedWidgets'],
|
containment: ['availableWidgets-'+this.name, 'usedWidgets-'+this.name],
|
||||||
onUpdate: this.updateWidgets
|
onUpdate: this.updateWidgets
|
||||||
});
|
});
|
||||||
|
|
||||||
// Figure out maxid, this is used when creating new widgets
|
// Figure out maxid, this is used when creating new widgets
|
||||||
this.maxid = 0;
|
this.maxid = 0;
|
||||||
|
|
||||||
var usedWidgets = $('WidgetAreaEditor_usedWidgets').childNodes;
|
var usedWidgets = $('usedWidgets-'+this.name).childNodes;
|
||||||
for(var i = 0; i < usedWidgets.length; i++) {
|
for(var i = 0; i < usedWidgets.length; i++) {
|
||||||
var widget = usedWidgets[i];
|
var widget = usedWidgets[i];
|
||||||
if(widget.id) {
|
if(widget.id) {
|
||||||
widgetid = widget.id.match(/Widget\[([0-9]+)\]/i);
|
widgetid = widget.id.match(/\Widget\[(.+?)\]\[([0-9]+)\]/i);
|
||||||
if(widgetid && parseInt(widgetid[1]) > this.maxid)
|
if(widgetid && parseInt(widgetid[2]) > this.maxid) {
|
||||||
this.maxid = parseInt(widgetid[1]);
|
this.maxid = parseInt(widgetid[2]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure correct sort values are written when page is saved
|
// Ensure correct sort values are written when page is saved
|
||||||
$('Form_EditForm').observeMethod('BeforeSave', this.beforeSave.bind(this));
|
$('Form_EditForm').observeMethod('BeforeSave', this.beforeSave.bind(this));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
rewriteWidgetAreaAttributes: function() {
|
||||||
|
this.name = this.getAttribute('name');
|
||||||
|
|
||||||
|
var monkeyWith = function(widgets, name) {
|
||||||
|
for(var i = 0; i < widgets.length; i++) {
|
||||||
|
widget = widgets[i];
|
||||||
|
if (!widget.getAttribute('rewritten') && (widget.id || widget.name)) {
|
||||||
|
if (widget.id && widget.id.indexOf('Widget[') === 0) {
|
||||||
|
var newValue = widget.id.replace(/Widget\[/, 'Widget['+name+'][');
|
||||||
|
//console.log('Renaming '+widget.tagName+' ID '+widget.id+' to '+newValue);
|
||||||
|
widget.id = newValue;
|
||||||
|
}
|
||||||
|
if (widget.name && widget.name.indexOf('Widget[') === 0) {
|
||||||
|
var newValue = widget.name.replace(/Widget\[/, 'Widget['+name+'][');
|
||||||
|
//console.log('Renaming '+widget.tagName+' Name '+widget.name+' to '+newValue);
|
||||||
|
widget.name = newValue;
|
||||||
|
}
|
||||||
|
widget.setAttribute('rewritten', 'yes');
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//console.log('Skipping '+(widget.id ? widget.id : (widget.name ? widget.name : 'unknown '+widget.tagName)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
monkeyWith($$('#WidgetAreaEditor-'+this.name+' .Widget'), this.name);
|
||||||
|
monkeyWith($$('#WidgetAreaEditor-'+this.name+' .Widget *'), this.name);
|
||||||
|
},
|
||||||
|
|
||||||
beforeSave: function() {
|
beforeSave: function() {
|
||||||
// Ensure correct sort values are written when page is saved
|
// Ensure correct sort values are written when page is saved
|
||||||
var usedWidgets = $('WidgetAreaEditor_usedWidgets');
|
var usedWidgets = $('usedWidgets-'+this.name);
|
||||||
|
|
||||||
if(usedWidgets) {
|
if(usedWidgets) {
|
||||||
this.sortWidgets();
|
this.sortWidgets();
|
||||||
@ -64,15 +108,37 @@ WidgetAreaEditorClass.prototype = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
addWidget: function(className, holder) {
|
||||||
|
this.name = holder;
|
||||||
|
new Ajax.Request('Widget_Controller/EditableSegment/' + className, {
|
||||||
|
onSuccess : $('usedWidgets-'+holder).parentNode.parentNode.insertWidgetEditor.bind(this)
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
updateWidgets: function() {
|
updateWidgets: function() {
|
||||||
|
|
||||||
|
// Gotta get the name of the current dohickey based off the ID
|
||||||
|
this.name = this.element.id.split('-').pop();
|
||||||
|
|
||||||
|
// alert(this.name);
|
||||||
|
|
||||||
|
// Gotta get the name of the current dohickey based off the ID
|
||||||
|
this.name = this.element.id.split('-').pop();
|
||||||
|
|
||||||
|
|
||||||
// This is called when an available widgets is dragged over to used widgets.
|
// This is called when an available widgets is dragged over to used widgets.
|
||||||
// It inserts the editor form into the new used widget
|
// It inserts the editor form into the new used widget
|
||||||
var usedWidgets = $('WidgetAreaEditor_usedWidgets').childNodes;
|
|
||||||
|
var usedWidgets = $('usedWidgets-'+this.name).childNodes;
|
||||||
for(var i = 0; i < usedWidgets.length; i++) {
|
for(var i = 0; i < usedWidgets.length; i++) {
|
||||||
var widget = usedWidgets[i];
|
var widget = usedWidgets[i];
|
||||||
if(widget.id && (widget.id.indexOf("Widget[") != 0) && (widget.id != 'NoWidgets')) {
|
if(widget.id && (widget.id.indexOf("Widget[") != 0) && (widget.id != 'NoWidgets-'+this.name)) {
|
||||||
new Ajax.Request('Widget_Controller/EditableSegment/' + widget.id, {
|
// Need to remove the -$Name part.
|
||||||
onSuccess : $('WidgetAreaEditor_usedWidgets').parentNode.parentNode.insertWidgetEditor.bind(this)
|
var wIdArray = widget.id.split('-');
|
||||||
|
wIdArray.pop();
|
||||||
|
|
||||||
|
new Ajax.Request('Widget_Controller/EditableSegment/' + wIdArray.join('-'), {
|
||||||
|
onSuccess : $('usedWidgets-'+this.name).parentNode.parentNode.insertWidgetEditor.bind(this)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -80,47 +146,31 @@ WidgetAreaEditorClass.prototype = {
|
|||||||
|
|
||||||
insertWidgetEditor: function(response) {
|
insertWidgetEditor: function(response) {
|
||||||
// Remove placeholder text
|
// Remove placeholder text
|
||||||
if($('NoWidgets')) {
|
if($('NoWidgets-'+this.name)) {
|
||||||
$('WidgetAreaEditor_usedWidgets').removeChild($('NoWidgets'));
|
$('usedWidgets-'+this.name).removeChild($('NoWidgets-'+this.name));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var usedWidgets = $('usedWidgets-'+this.name).childNodes;
|
||||||
|
|
||||||
|
// Give the widget a unique id
|
||||||
|
widget = document.createElement('div');
|
||||||
|
widget.innerHTML = response.responseText.replace(/Widget\[0\]/gi, "Widget[new-" + (++$('usedWidgets-'+this.name).parentNode.parentNode.maxid) + "]");
|
||||||
|
|
||||||
// Find the new widget
|
$('usedWidgets-'+this.name).appendChild(widget.childNodes[0]);
|
||||||
var usedWidgets = $('WidgetAreaEditor_usedWidgets').childNodes;
|
$('usedWidgets-'+this.name).parentNode.parentNode.rewriteWidgetAreaAttributes();
|
||||||
for(var i = 0; i < usedWidgets.length; i++) {
|
UsedWidget.applyToChildren($('usedWidgets-'+this.name), 'div.Widget');
|
||||||
var widget = usedWidgets[i];
|
|
||||||
if(widget.id && (widget.id.indexOf("Widget[") != 0)) {
|
Sortable.create('usedWidgets-SideBar', {
|
||||||
// Clone the widget so we can put it back in the available widgets column
|
tag: 'div',
|
||||||
clone = widget.cloneNode(true);
|
handle: 'handle',
|
||||||
|
containment: ['availableWidgets-'+this.name, 'usedWidgets-'+this.name],
|
||||||
// Give the widget a unique id
|
onUpdate: $('usedWidgets-'+this.name).parentNode.parentNode.updateWidgets
|
||||||
widget.innerHTML = response.responseText.replace(/Widget\[0\]/gi, "Widget[new-" + (++$('WidgetAreaEditor_usedWidgets').parentNode.parentNode.maxid) + "]");
|
});
|
||||||
|
|
||||||
// Replace the available widget with the used widget with editor form
|
|
||||||
widget.parentNode.insertBefore($(widget).getElementsByClassName('Widget')[0], widget);
|
|
||||||
widget.parentNode.removeChild(widget);
|
|
||||||
|
|
||||||
// Put the clone into the available widgets column
|
|
||||||
$('WidgetAreaEditor_availableWidgets').appendChild(clone);
|
|
||||||
|
|
||||||
// Reapply behaviour
|
|
||||||
new Draggable(clone.id);
|
|
||||||
|
|
||||||
Sortable.create('WidgetAreaEditor_usedWidgets', {
|
|
||||||
tag: 'div',
|
|
||||||
handle: 'handle',
|
|
||||||
containment: ['WidgetAreaEditor_availableWidgets', 'WidgetAreaEditor_usedWidgets'],
|
|
||||||
onUpdate: $('WidgetAreaEditor_usedWidgets').parentNode.parentNode.updateWidgets
|
|
||||||
});
|
|
||||||
|
|
||||||
UsedWidget.applyToChildren($('WidgetAreaEditor_usedWidgets'), 'div.Widget');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
sortWidgets: function() {
|
sortWidgets: function() {
|
||||||
// Order the sort by the order the widgets are in the list
|
// Order the sort by the order the widgets are in the list
|
||||||
var usedWidgets = $('WidgetAreaEditor_usedWidgets');
|
var usedWidgets = $('usedWidgets-'+this.name);
|
||||||
|
|
||||||
if(usedWidgets) {
|
if(usedWidgets) {
|
||||||
widgets = usedWidgets.childNodes;
|
widgets = usedWidgets.childNodes;
|
||||||
@ -144,7 +194,8 @@ WidgetAreaEditorClass.prototype = {
|
|||||||
|
|
||||||
deleteWidget: function(widgetToRemove) {
|
deleteWidget: function(widgetToRemove) {
|
||||||
// Remove a widget from the used widgets column
|
// Remove a widget from the used widgets column
|
||||||
$('WidgetAreaEditor_usedWidgets').removeChild(widgetToRemove);
|
$('usedWidgets-'+this.name).removeChild(widgetToRemove);
|
||||||
|
// TODO ... re-create NoWidgets div?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,3 +231,10 @@ UsedWidget.prototype = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Loop over all WidgetAreas and fire 'em up
|
||||||
|
var wAs = $$('.WidgetAreaEditor');
|
||||||
|
for(var i = 0; i < wAs.length; i++) {
|
||||||
|
WidgetAreaEditorClass.applyTo('div#'+wAs[i].id);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -1,15 +1,15 @@
|
|||||||
<div class="WidgetAreaEditor" id="WidgetAreaEditor" name="$Name">
|
<div class="WidgetAreaEditor" id="WidgetAreaEditor-$Name" name="$Name">
|
||||||
<input type="hidden" id="$Name" name="$IdxField" value="$Value" />
|
<input type="hidden" id="$Name" name="$IdxField" value="$Value" />
|
||||||
<div class="availableWidgetsHolder">
|
<div class="availableWidgetsHolder">
|
||||||
<h2><% _t('AVAILABLE', 'Available Widgets') %></h2>
|
<h2><% _t('AVAILABLE', 'Available Widgets') %></h2>
|
||||||
<p> </p>
|
<p> </p>
|
||||||
<div class="availableWidgets" id="WidgetAreaEditor_availableWidgets">
|
<div class="availableWidgets" id="availableWidgets-$Name">
|
||||||
<% if AvailableWidgets %>
|
<% if AvailableWidgets %>
|
||||||
<% control AvailableWidgets %>
|
<% control AvailableWidgets %>
|
||||||
$DescriptionSegment
|
$DescriptionSegment
|
||||||
<% end_control %>
|
<% end_control %>
|
||||||
<% else %>
|
<% else %>
|
||||||
<div id="NoWidgets">
|
<div class="NoWidgets" id="NoWidgets-$Name">
|
||||||
<p><% _t('NOAVAIL', 'There are currently no widgets available.') %></p>
|
<p><% _t('NOAVAIL', 'There are currently no widgets available.') %></p>
|
||||||
</div>
|
</div>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
@ -19,13 +19,13 @@
|
|||||||
<h2><% _t('INUSE', 'Widgets currently used') %></h2>
|
<h2><% _t('INUSE', 'Widgets currently used') %></h2>
|
||||||
<p><% _t('TOADD', 'To add widgets, drag them from the left area to here.') %></p>
|
<p><% _t('TOADD', 'To add widgets, drag them from the left area to here.') %></p>
|
||||||
|
|
||||||
<div class="usedWidgets" id="WidgetAreaEditor_usedWidgets">
|
<div class="usedWidgets" id="usedWidgets-$Name">
|
||||||
<% if UsedWidgets %>
|
<% if UsedWidgets %>
|
||||||
<% control UsedWidgets %>
|
<% control UsedWidgets %>
|
||||||
$EditableSegment
|
$EditableSegment
|
||||||
<% end_control %>
|
<% end_control %>
|
||||||
<% else %>
|
<% else %>
|
||||||
<div id="NoWidgets"></div>
|
<div class="NoWidgets" id="NoWidgets-$Name"></div>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
Reference in New Issue
Block a user