mirror of
https://github.com/silverstripe/silverstripe-reports
synced 2024-10-22 11:05:53 +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');
|
||||
|
||||
// For Blog
|
||||
Requirements::css("blog/css/WidgetAreaEditor.css");
|
||||
Requirements::javascript("blog/javascript/WidgetAreaEditor.js");
|
||||
// For Widgets
|
||||
Requirements::css("cms/css/WidgetAreaEditor.css");
|
||||
Requirements::javascript("cms/javascript/WidgetAreaEditor.js");
|
||||
|
||||
// HACK ALERT:
|
||||
// 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