mirror of
https://github.com/silverstripe/silverstripe-cms
synced 2024-10-22 08:05:56 +02:00
Moved widgets into core
git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/cms/trunk@39562 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
parent
c52da55f62
commit
112d7ed1ad
@ -66,9 +66,9 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
|
|
||||||
Requirements::themedCSS('typography');
|
Requirements::themedCSS('typography');
|
||||||
|
|
||||||
// For Blog
|
// For Widgets
|
||||||
Requirements::css("blog/css/WidgetAreaEditor.css");
|
Requirements::css("cms/css/WidgetAreaEditor.css");
|
||||||
Requirements::javascript("blog/javascript/WidgetAreaEditor.js");
|
Requirements::javascript("cms/javascript/WidgetAreaEditor.js");
|
||||||
|
|
||||||
// HACK ALERT:
|
// HACK ALERT:
|
||||||
// We need a better way of including all of the CSS that *might* be used by this application.
|
// We need a better way of including all of the CSS that *might* be used by this application.
|
||||||
|
98
code/WidgetAreaEditor.php
Normal file
98
code/WidgetAreaEditor.php
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class WidgetAreaEditor extends FormField {
|
||||||
|
function FieldHolder() {
|
||||||
|
return $this->renderWith("WidgetAreaEditor");
|
||||||
|
}
|
||||||
|
|
||||||
|
function AvailableWidgets() {
|
||||||
|
$classes = ClassInfo::subclassesFor('Widget');
|
||||||
|
array_shift($classes);
|
||||||
|
$widgets= new DataObjectSet();
|
||||||
|
|
||||||
|
foreach($classes as $class) {
|
||||||
|
$widgets->push(singleton($class));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $widgets;
|
||||||
|
}
|
||||||
|
|
||||||
|
function UsedWidgets() {
|
||||||
|
$relationName = $this->name;
|
||||||
|
|
||||||
|
$widgets = $this->form->getRecord()->$relationName()->Widgets();
|
||||||
|
|
||||||
|
return $widgets;
|
||||||
|
}
|
||||||
|
|
||||||
|
function IdxField() {
|
||||||
|
return $this->id() . 'ID';
|
||||||
|
}
|
||||||
|
|
||||||
|
function Value() {
|
||||||
|
$relationName = $this->name;
|
||||||
|
return $this->form->getRecord()->$relationName()->ID;
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveInto(DataObject $record) {
|
||||||
|
$name = $this->name;
|
||||||
|
$idName = $name . "ID";
|
||||||
|
|
||||||
|
|
||||||
|
$widgetarea = $record->$name();
|
||||||
|
|
||||||
|
$widgetarea->write();
|
||||||
|
$record->$idName = $widgetarea->ID;
|
||||||
|
|
||||||
|
$widgets = $widgetarea->Widgets();
|
||||||
|
|
||||||
|
// store the field IDs and delete the missing fields
|
||||||
|
// alternatively, we could delete all the fields and re add them
|
||||||
|
$missingWidgets = array();
|
||||||
|
|
||||||
|
foreach($widgets as $existingWidget){
|
||||||
|
$missingWidgets[$existingWidget->ID] = $existingWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write the new widgets to the database
|
||||||
|
if(isset($_REQUEST['Widget'])){
|
||||||
|
|
||||||
|
foreach(array_keys( $_REQUEST['Widget'] ) as $newWidgetID ) {
|
||||||
|
$newWidgetData = $_REQUEST['Widget'][$newWidgetID];
|
||||||
|
|
||||||
|
// `ParentID`=0 is for the new page
|
||||||
|
$widget = DataObject::get_one( 'Widget', "(`ParentID`='{$record->$name()->ID}' OR `ParentID`=0) AND `Widget`.`ID`='$newWidgetID'" );
|
||||||
|
|
||||||
|
// check if we are updating an existing widget
|
||||||
|
if($widget && isset($missingWidgets[$widget->ID]))
|
||||||
|
unset($missingWidgets[$widget->ID] );
|
||||||
|
|
||||||
|
// create a new object
|
||||||
|
if(!$widget && !empty($newWidgetData['Type']) && class_exists($newWidgetData['Type'])) {
|
||||||
|
$widget = new $newWidgetData['Type']();
|
||||||
|
$widget->ID = 0;
|
||||||
|
$widget->ParentID = $record->$name()->ID;
|
||||||
|
|
||||||
|
if(!is_subclass_of($widget, 'Widget')) {
|
||||||
|
$widget = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($widget) {
|
||||||
|
if($widget->ParentID == 0) {
|
||||||
|
$widget->ParentID = $record->$name()->ID;
|
||||||
|
}
|
||||||
|
$widget->populateFromPostData($newWidgetData);
|
||||||
|
//$editable->write();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove the fields not saved
|
||||||
|
foreach($missingWidgets as $removedWidget) {
|
||||||
|
if(isset($removedWidget) && is_numeric($removedWidget->ID)) $removedWidget->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
52
css/WidgetAreaEditor.css
Normal file
52
css/WidgetAreaEditor.css
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
div.availableWidgetsHolder,
|
||||||
|
div.usedWidgetsHolder {
|
||||||
|
width: 49%;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.availableWidgetsHolder {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.usedWidgetsHolder {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.usedWidgets {
|
||||||
|
min-height: 100px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.usedWidgets div.Widget,
|
||||||
|
div.availableWidgets div.Widget {
|
||||||
|
width: 90%;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-top: none;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
div.widgetDescription p,
|
||||||
|
div.widgetFields {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.deleteWidget {
|
||||||
|
margin: 0;
|
||||||
|
height: 2.2em;
|
||||||
|
line-height: 2.2em;
|
||||||
|
font-size: 1.2em;
|
||||||
|
}
|
||||||
|
span.widgetDelete {
|
||||||
|
padding-left: 20px;
|
||||||
|
margin-right: 8px;
|
||||||
|
float: right;
|
||||||
|
background: url(../images/delete2.png) no-repeat left center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
div.usedWidgets div.Widget h3,
|
||||||
|
div.availableWidgets div.Widget h3 {
|
||||||
|
font-size: 1.3em;
|
||||||
|
height: 1.5em;
|
||||||
|
color: #fff;
|
||||||
|
line-height: 1.5em;
|
||||||
|
text-indent: 5px;
|
||||||
|
background: #7ab7ec url(../images/blogTitleBg.gif);
|
||||||
|
margin: 0;
|
||||||
|
}
|
157
javascript/WidgetAreaEditor.js
Normal file
157
javascript/WidgetAreaEditor.js
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
WidgetAreaEditor = Class.create();
|
||||||
|
WidgetAreaEditor.applyTo('div.WidgetAreaEditor');
|
||||||
|
|
||||||
|
WidgetAreaEditor.prototype = {
|
||||||
|
initialize: function() {
|
||||||
|
UsedWidget.applyToChildren($('WidgetAreaEditor_usedWidgets'), 'div.Widget');
|
||||||
|
|
||||||
|
// Make available widgets draggable
|
||||||
|
var availableWidgets = $('WidgetAreaEditor_availableWidgets').childNodes;
|
||||||
|
for(var i = 0; i < availableWidgets.length; i++) {
|
||||||
|
var widget = availableWidgets[i];
|
||||||
|
if(widget.id)
|
||||||
|
new Draggable(widget.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create dummy sortable to prevent javascript errors
|
||||||
|
Sortable.create('WidgetAreaEditor_availableWidgets', {tag: 'li', handle: 'handle', containment: []});
|
||||||
|
// Used widgets are sortable
|
||||||
|
Sortable.create('WidgetAreaEditor_usedWidgets', {tag: 'div', handle: 'handle', containment: ['WidgetAreaEditor_availableWidgets', 'WidgetAreaEditor_usedWidgets'], onUpdate: this.updateWidgets});
|
||||||
|
|
||||||
|
// Figure out maxid, this is used when creating new widgets
|
||||||
|
this.maxid = 0;
|
||||||
|
|
||||||
|
var usedWidgets = $('WidgetAreaEditor_usedWidgets').childNodes;
|
||||||
|
for(var i = 0; i < usedWidgets.length; i++) {
|
||||||
|
var widget = usedWidgets[i];
|
||||||
|
if(widget.id) {
|
||||||
|
widgetid = widget.id.match(/Widget\[([0-9]+)\]/i);
|
||||||
|
if(widgetid && parseInt(widgetid[1]) > this.maxid)
|
||||||
|
this.maxid = parseInt(widgetid[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure correct sort values are written when page is saved
|
||||||
|
$('Form_EditForm').observeMethod('BeforeSave', this.beforeSave.bind(this));
|
||||||
|
},
|
||||||
|
|
||||||
|
beforeSave: function() {
|
||||||
|
// Ensure correct sort values are written when page is saved
|
||||||
|
var usedWidgets = $('WidgetAreaEditor_usedWidgets');
|
||||||
|
|
||||||
|
if(usedWidgets) {
|
||||||
|
this.sortWidgets();
|
||||||
|
|
||||||
|
var children = usedWidgets.childNodes;
|
||||||
|
|
||||||
|
for( var i = 0; i < children.length; ++i ) {
|
||||||
|
var child = children[i];
|
||||||
|
|
||||||
|
if(child.beforeSave) {
|
||||||
|
child.beforeSave();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
updateWidgets: function() {
|
||||||
|
// This is called when an available widgets is dragged over to used widgets.
|
||||||
|
// It inserts the editor form into the new used widget
|
||||||
|
var usedWidgets = $('WidgetAreaEditor_usedWidgets').childNodes;
|
||||||
|
for(var i = 0; i < usedWidgets.length; i++) {
|
||||||
|
var widget = usedWidgets[i];
|
||||||
|
if(widget.id && (widget.id.indexOf("Widget[") != 0)) {
|
||||||
|
new Ajax.Request(widget.id + "/EditableSegment", {onSuccess : $('WidgetAreaEditor_usedWidgets').parentNode.parentNode.insertWidgetEditor.bind(this)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
insertWidgetEditor: function(response) {
|
||||||
|
// Remove placeholder text
|
||||||
|
if($('NoWidgets')) {
|
||||||
|
$('WidgetAreaEditor_usedWidgets').removeChild($('NoWidgets'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the new widget
|
||||||
|
var usedWidgets = $('WidgetAreaEditor_usedWidgets').childNodes;
|
||||||
|
for(var i = 0; i < usedWidgets.length; i++) {
|
||||||
|
var widget = usedWidgets[i];
|
||||||
|
if(widget.id && (widget.id.indexOf("Widget[") != 0)) {
|
||||||
|
// Clone the widget so we can put it back in the available widgets column
|
||||||
|
clone = widget.cloneNode(true);
|
||||||
|
|
||||||
|
// Give the widget a unique id
|
||||||
|
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.childNodes[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() {
|
||||||
|
// Order the sort by the order the widgets are in the list
|
||||||
|
var usedWidgets = $('WidgetAreaEditor_usedWidgets');
|
||||||
|
|
||||||
|
if (usedWidgets) {
|
||||||
|
widgets = usedWidgets.childNodes;
|
||||||
|
|
||||||
|
for( i = 0; div = widgets[i]; i++ ) {
|
||||||
|
var fields = div.getElementsByTagName('input');
|
||||||
|
for( j = 0; field = fields.item(j); j++ ) {
|
||||||
|
if( field.name == div.id + '[Sort]' ) {
|
||||||
|
field.value = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteWidget: function(widgetToRemove) {
|
||||||
|
// Remove a widget from the used widgets column
|
||||||
|
$('WidgetAreaEditor_usedWidgets').removeChild(widgetToRemove);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UsedWidget = Class.create();
|
||||||
|
|
||||||
|
UsedWidget.prototype = {
|
||||||
|
initialize: function() {
|
||||||
|
// Call deleteWidget when delete button is pushed
|
||||||
|
this.deleteButton = this.findDescendant('span', 'widgetDelete');
|
||||||
|
if(this.deleteButton)
|
||||||
|
this.deleteButton.onclick = this.deleteWidget.bind(this);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Taken from FieldEditor
|
||||||
|
findDescendant: function(tag, clsName, element) {
|
||||||
|
if(!element)
|
||||||
|
element = this;
|
||||||
|
|
||||||
|
var descendants = element.getElementsByTagName(tag);
|
||||||
|
|
||||||
|
for(var i = 0; i < descendants.length; i++) {
|
||||||
|
var el = descendants[i];
|
||||||
|
|
||||||
|
if(tag.toUpperCase() == el.tagName && el.className.indexOf( clsName ) != -1)
|
||||||
|
return el;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
deleteWidget: function() {
|
||||||
|
this.parentNode.parentNode.parentNode.deleteWidget(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
25
templates/WidgetAreaEditor.ss
Normal file
25
templates/WidgetAreaEditor.ss
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<div class="WidgetAreaEditor" id="WidgetAreaEditor" name="$Name">
|
||||||
|
<input type="hidden" id="$Name" name="$IdxField" value="$Value" />
|
||||||
|
<div class="availableWidgetsHolder">
|
||||||
|
<h2>Available Widgets</h2>
|
||||||
|
<div class="availableWidgets" id="WidgetAreaEditor_availableWidgets">
|
||||||
|
<% control AvailableWidgets %>
|
||||||
|
$DescriptionSegment
|
||||||
|
<% end_control %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="usedWidgetsHolder">
|
||||||
|
<h2>Used Widgets</h2>
|
||||||
|
<div class="usedWidgets" id="WidgetAreaEditor_usedWidgets">
|
||||||
|
<% if UsedWidgets %>
|
||||||
|
<% control UsedWidgets %>
|
||||||
|
$EditableSegment
|
||||||
|
<% end_control %>
|
||||||
|
<% else %>
|
||||||
|
<div id="NoWidgets">
|
||||||
|
<p>To add widgets, drag them from the left area to here.</p>
|
||||||
|
</div>
|
||||||
|
<% end_if %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
6
templates/WidgetDescription.ss
Normal file
6
templates/WidgetDescription.ss
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<div class="Widget" id="$ClassName">
|
||||||
|
<h3>$CMSTitle</h3>
|
||||||
|
<div class="widgetDescription">
|
||||||
|
<p>$Description</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
12
templates/WidgetEditor.ss
Normal file
12
templates/WidgetEditor.ss
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<div class="$ClassName Widget" id="$Name">
|
||||||
|
<h3 class="handle">$CMSTitle</h3>
|
||||||
|
<div class="widgetDescription">
|
||||||
|
<p>$Description</p>
|
||||||
|
</div>
|
||||||
|
<div class="widgetFields">
|
||||||
|
$CMSEditor
|
||||||
|
<input type="hidden" name="$Name[Type]" value="$ClassName" />
|
||||||
|
<input type="hidden" name="$Name[Sort]" value="$Sort" />
|
||||||
|
</div>
|
||||||
|
<p class="deleteWidget"><span class="widgetDelete">Delete</span></p>
|
||||||
|
</div>
|
Loading…
Reference in New Issue
Block a user