Merge branch 'master' into integration

Conflicts:
	admin/css/screen.css
This commit is contained in:
Ingo Schommer 2012-02-28 00:35:17 +01:00
commit f3fe62e740
29 changed files with 431 additions and 156 deletions

View File

@ -473,7 +473,7 @@ class LeftAndMain extends Controller {
/**
* Return a list of appropriate templates for this class, with the given suffix
*/
protected function getTemplatesWithSuffix($suffix) {
public function getTemplatesWithSuffix($suffix) {
$templates = array();
$classes = array_reverse(ClassInfo::ancestry($this->class));
foreach($classes as $class) {
@ -1217,7 +1217,11 @@ class LeftAndMain extends Controller {
* @return string
*/
public function CMSVersion() {
$sapphireVersion = file_get_contents(BASE_PATH . '/cms/silverstripe_version');
if(file_exists(CMS_PATH . '/silverstripe_version')) {
$sapphireVersion = file_get_contents(CMS_PATH . '/silverstripe_version');
} else {
$sapphireVersion = file_get_contents(SAPPHIRE_PATH . '/silverstripe_version');
}
if(!$sapphireVersion) $sapphireVersion = _t('LeftAndMain.VersionUnknown', 'unknown');
return sprintf(
"sapphire: %s",

View File

@ -107,7 +107,7 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider {
*/
function RootForm() {
$config = new GridFieldConfig_Base(25);
$config->addComponent(new GridFieldPopupForms($this, 'RootForm'));
$config->addComponent(new GridFieldPopupForms());
$config->addComponent(new GridFieldExporter());
$memberList = new GridField('Members', 'All members', DataList::create('Member'), $config);

View File

@ -392,7 +392,7 @@ form.member-profile-form .ui-corner-all, form.member-profile-form .ui-corner-top
form.member-profile-form .ui-corner-all, form.member-profile-form .ui-corner-top, form.member-profile-form .ui-corner-left, form.member-profile-form .ui-corner-tl { border-radius: 0; }
.cms .cms-content { border-right: 1px solid rgba(201, 205, 206, 0.8); -moz-box-shadow: 3px 0 4px rgba(0, 0, 0, 0.15); -webkit-box-shadow: 3px 0 4px rgba(0, 0, 0, 0.15); -o-box-shadow: 3px 0 4px rgba(0, 0, 0, 0.15); box-shadow: 3px 0 4px rgba(0, 0, 0, 0.15); -moz-border-radius: 0; -webkit-border-radius: 0; -o-border-radius: 0; -ms-border-radius: 0; -khtml-border-radius: 0; border-radius: 0; }
.cms .cms-content-fields { /* always show a y scroll bar as popups like TreeDropdowns can trigger longer pages and the extra scroll bar doesn't fire our sizing bar */ overflow-y: auto; overflow-x: auto; background: none; -moz-box-shadow: rgba(107, 120, 123, 0.5) 0 0 4px; -webkit-box-shadow: rgba(107, 120, 123, 0.5) 0 0 4px; -o-box-shadow: rgba(107, 120, 123, 0.5) 0 0 4px; box-shadow: rgba(107, 120, 123, 0.5) 0 0 4px; }
.cms .cms-content-fields { /* always show a y scroll bar as popups like TreeDropdowns can trigger longer pages and the extra scroll bar doesn't fire our sizing bar */ overflow-y: auto; overflow-x: auto; background: none; width: 100%; }
/** -------------------------------------------- Panels -------------------------------------------- */
.cms-panel { overflow: hidden; }
@ -463,6 +463,15 @@ body.cms-dialog { overflow: auto; background: url("../images/textures/bg_cms_mai
.step-label .arrow { height: 26px; width: 10px; background: url('../images/sprites-32x32-sa4e142f7f0.png') 0 -759px no-repeat; margin-right: 4px; }
.step-label .title { height: 18px; padding: 4px; }
/** -------------------------------------------- Item Edit Form -------------------------------------------- */
#Form_ItemEditForm .field { width: 100%; }
#Form_ItemEditForm .field .fieldgroup { height: 230px; float: left; }
#Form_ItemEditForm .field .fieldgroup .fieldgroup-field.first { position: relative; margin-left: 0px; }
#Form_ItemEditForm .field .fieldgroup .fieldgroup-field { clear: both; width: 500px; margin-left: 420px; }
#Form_ItemEditForm .field .fieldgroup .fieldgroup-field img { -moz-border-radius: 8px; -webkit-border-radius: 8px; -o-border-radius: 8px; -ms-border-radius: 8px; -khtml-border-radius: 8px; border-radius: 8px; border-width: 3px; border-style: dashed; border-color: #8c99a1; position: absolute; top: 8px; }
#Form_ItemEditForm .field .fieldgroup .fieldgroup-field label { font-weight: bold; float: left; padding-top: 8px; line-height: 16px; text-shadow: white 1px 1px 0; }
#Form_ItemEditForm .field .fieldgroup .fieldgroup-field span { float: left; padding-left: 8px; font-style: italic; color: #777777; }
/** This file defines the jstree base styling (see http://jstree.com), as well as any customizations (see bottom of file). The styles are usually added through jstree.js on DOM load, but we need it earlier in order to correctly display the uninitialized tree. */
.cms .jstree ul, .TreeDropdownField .treedropdownfield-panel .jstree ul { display: block; margin: 0; padding: 0; background: none; list-style-type: none; }
.cms .jstree li, .TreeDropdownField .treedropdownfield-panel .jstree li { display: block; margin: 0; padding: 0; list-style-type: none; display: block; min-height: 18px; line-height: 18px; white-space: nowrap; margin-left: 18px; min-width: 18px; }
@ -599,6 +608,7 @@ li.class-ErrorPage > a .jstree-pageicon { background-position: 0 -112px; }
.cms-menu-list li a .toggle-children { display: inline-block; float: right; width: 20px; height: 100%; cursor: pointer; }
.cms-menu-list li a .toggle-children .toggle-children-icon { display: inline-block; width: 8px; height: 8px; background: url('../images/sprites-32x32-sa4e142f7f0.png') 0 -695px no-repeat; vertical-align: middle; }
.cms-menu-list li a .toggle-children.opened .toggle-children-icon { background: url('../images/sprites-32x32-sa4e142f7f0.png') 0 -711px no-repeat; }
.cms-menu-list li ul { display: none; }
.cms-menu-list li.current a { color: white; text-shadow: #1e5270 0 -1px 0; border-top: 1px solid #55a4d2; border-bottom: 1px solid #1e5270; background-color: #338dc1; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #338dc1), color-stop(100%, #287099)); background-image: -webkit-linear-gradient(#338dc1, #287099); background-image: -moz-linear-gradient(#338dc1, #287099); background-image: -o-linear-gradient(#338dc1, #287099); background-image: -ms-linear-gradient(#338dc1, #287099); background-image: linear-gradient(#338dc1, #287099); }
.cms-menu-list li.current a .toggle-children .toggle-children-icon { background: url('../images/sprites-32x32-sa4e142f7f0.png') 0 -727px no-repeat; }
.cms-menu-list li.current a .toggle-children.opened .toggle-children-icon { background: url('../images/sprites-32x32-sa4e142f7f0.png') 0 -743px no-repeat; }

View File

@ -879,7 +879,7 @@ form.member-profile-form {
overflow-y: auto;
overflow-x: auto;
background: none;
@include box-shadow($color-shadow-dark 0 0 ($grid-x / 2));
width:100%;
}
}
@ -1051,6 +1051,7 @@ form.member-profile-form {
background-color: #92a5b2;
padding: $grid-y/2 $grid-x/2 $grid-y/2 $grid-x*2;
border-bottom: 2px solid #8399a7;
@include background-image(linear-gradient(#ced7dc, #92a5b2));
}
@ -1224,4 +1225,50 @@ body.cms-dialog {
padding: 4px;
}
}
}
/** --------------------------------------------
* Item Edit Form
* -------------------------------------------- */
#Form_ItemEditForm {
.field {
width:100%;
.fieldgroup {
height:230px;
float:left;
.fieldgroup-field.first {
position:relative;
margin-left:0px;
}
.fieldgroup-field {
clear:both;
width:500px;
margin-left:420px;
img {
@include border-radius (8px);
border: {
width:3px;
style:dashed;
color:$color-menu-border;
}
position:absolute;
top:8px;
}
label {
font-weight:bold;
float:left;
padding-top:8px;
line-height:16px;
text-shadow: $color-text-light 1px 1px 0;
}
span {
float:left;
padding-left:8px;
font-style:italic;
color:lighten($color-text, 20%);
}
}
}
}
}

View File

@ -1,4 +0,0 @@
<div class="cms-content-fields center">
<a class="backlink ss-ui-button" href="$Backlink">Back</a>
$ItemEditForm
</div>

View File

@ -21,6 +21,13 @@
<% end_if %>
<!-- <div class="cms-content-search">...</div> -->
<% if Backlink %>
<a class="backlink ss-ui-button cms-panel-link" data-icon="back" href="$Backlink">
<% _t('Back', 'Back') %>
</a>
<% end_if %>
</div>
</div>
@ -28,7 +35,7 @@
$EditFormTools
<% end_control %>
<div class="cms-content-fields center cms-panel-padded">
<div class="cms-content-fields center <% if not Fields.hasTabset %>cms-panel-padded<% end_if %>">
<% if Message %>
<p id="{$FormName}_error" class="message $MessageType">$Message</p>
<% else %>

View File

@ -298,6 +298,23 @@ class Controller extends RequestHandler {
public function hasAction($action) {
return parent::hasAction($action) || $this->hasActionTemplate($action);
}
/**
* Removes all the "action" part of the current URL and returns the result.
* If no action parameter is present, returns the full URL
* @static
* @return String
*/
public function removeAction($fullURL, $action = null) {
if (!$action) $action = $this->getAction(); //default to current action
$returnURL = $fullURL;
if (($pos = strpos($fullURL, $action)) !== false) {
$returnURL = substr($fullURL,0,$pos);
}
return $returnURL;
}
/**
* Returns TRUE if this controller has a template that is specifically designed to handle a specific action.

View File

@ -9,20 +9,22 @@
.cms table.ss-gridfield-table tbody td button { border: none; background: none; margin: 0 0 0 2px; padding: 0; width: auto; text-shadow: none; }
.cms table.ss-gridfield-table tfoot { color: #1d2224; }
.cms table.ss-gridfield-table tfoot tr td { background: #95a5ab; padding: .7em; border-bottom: 1px solid rgba(0, 0, 0, 0.1); }
.cms table.ss-gridfield-table tr.sortable-header th { background: #7f9198; }
.cms table.ss-gridfield-table tr.title { -moz-border-radius-topleft: 7px; -webkit-border-top-left-radius: 7px; -o-border-top-left-radius: 7px; -ms-border-top-left-radius: 7px; -khtml-border-top-left-radius: 7px; border-top-left-radius: 7px; -moz-border-radius-topright: 7px; -webkit-border-top-right-radius: 7px; -o-border-top-right-radius: 7px; -ms-border-top-right-radius: 7px; -khtml-border-top-right-radius: 7px; border-top-right-radius: 7px; }
.cms table.ss-gridfield-table tr.title th { position: relative; background: #7f9198; border-top: 1px solid rgba(0, 0, 0, 0.1); padding: 5px; min-height: 40px; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #b1c0c5), color-stop(100%, #7f9198)); background-image: -webkit-linear-gradient(#b1c0c5, #7f9198); background-image: -moz-linear-gradient(#b1c0c5, #7f9198); background-image: -o-linear-gradient(#b1c0c5, #7f9198); background-image: -ms-linear-gradient(#b1c0c5, #7f9198); background-image: linear-gradient(#b1c0c5, #7f9198); -moz-border-radius-topleft: 7px; -webkit-border-top-left-radius: 7px; -o-border-top-left-radius: 7px; -ms-border-top-left-radius: 7px; -khtml-border-top-left-radius: 7px; border-top-left-radius: 7px; -moz-border-radius-topright: 7px; -webkit-border-top-right-radius: 7px; -o-border-top-right-radius: 7px; -ms-border-top-right-radius: 7px; -khtml-border-top-right-radius: 7px; border-top-right-radius: 7px; text-shadow: rgba(0, 0, 0, 0.3) 0px -1px 0; }
.cms table.ss-gridfield-table tr.title th h2 { padding: 0px; font-size: 16px; color: #fff; margin: 0; display: inline; }
.cms table.ss-gridfield-table tr.title th .new { font-size: 14px; border-color: rgba(0, 0, 0, 0.1); float: right; }
.cms table.ss-gridfield-table tr.sortable-header { background: #bac8ce; }
.cms table.ss-gridfield-table tr:hover { background: #FFFAD6 !important; }
.cms table.ss-gridfield-table tr:first-child { background: transparent; }
.cms table.ss-gridfield-table tr.ss-gridfield-even { background: #f0f4f7; }
.cms table.ss-gridfield-table tr.ss-gridfield-even.ss-gridfield-last { border-bottom: none; }
.cms table.ss-gridfield-table tr.even { background: #f0f4f7; }
.cms table.ss-gridfield-table tr th { font-weight: bold; font-size: 12px; color: #FFF; padding: 5px; border-right: 1px solid rgba(0, 0, 0, 0.1); }
.cms table.ss-gridfield-table tr th.main:first-child { -moz-border-radius-topleft: 7px; -webkit-border-top-left-radius: 7px; -o-border-top-left-radius: 7px; -ms-border-top-left-radius: 7px; -khtml-border-top-left-radius: 7px; border-top-left-radius: 7px; }
.cms table.ss-gridfield-table tr th.main:last-child { -moz-border-radius-topright: 7px; -webkit-border-top-right-radius: 7px; -o-border-top-right-radius: 7px; -ms-border-top-right-radius: 7px; -khtml-border-top-right-radius: 7px; border-top-right-radius: 7px; }
.cms table.ss-gridfield-table tr th div.fieldgroup, .cms table.ss-gridfield-table tr th div.fieldgroup-field { width: auto; }
.cms table.ss-gridfield-table tr th div.fieldgroup { min-width: 200px; padding-right: 0; }
.cms table.ss-gridfield-table tr th.extra, .cms table.ss-gridfield-table tr th.action { background: #7f9198; padding: 0; cursor: default; }
.cms table.ss-gridfield-table tr th.extra, .cms table.ss-gridfield-table tr th.action { padding: 0; cursor: default; }
.cms table.ss-gridfield-table tr th.extra button.ss-ui-button, .cms table.ss-gridfield-table tr th.extra button:hover.ss-ui-button, .cms table.ss-gridfield-table tr th.action button.ss-ui-button, .cms table.ss-gridfield-table tr th.action button:hover.ss-ui-button { margin-left: .9em; color: #222; }
.cms table.ss-gridfield-table tr th.main { border-top: 1px solid rgba(0, 0, 0, 0.1); color: #fff; background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #b1c0c5), color-stop(100%, #7f9198)); background-image: -webkit-linear-gradient(#b1c0c5, #7f9198); background-image: -moz-linear-gradient(#b1c0c5, #7f9198); background-image: -o-linear-gradient(#b1c0c5, #7f9198); background-image: -ms-linear-gradient(#b1c0c5, #7f9198); background-image: linear-gradient(#b1c0c5, #7f9198); }
.cms table.ss-gridfield-table tr th.main { border-top: 1px solid rgba(0, 0, 0, 0.1); color: #fff; background: #9cb0b9; border-bottom: 1px solid rgba(0, 0, 0, 0.1); }
.cms table.ss-gridfield-table tr th.main span { text-shadow: rgba(0, 0, 0, 0.3) 0px -1px 0; }
.cms table.ss-gridfield-table tr th.extra { background: #bac8ce; padding: 5px; border-top: rgba(0, 0, 0, 0.3); }
.cms table.ss-gridfield-table tr th.extra span { width: auto; display: inline; position: static; }

View File

@ -119,6 +119,8 @@ class File extends DataObject {
'cab','arj','tar','zip','zipx','sit','sitx','gz','tgz','bz2','ace','arc','pkg','dmg','hqx','jar',
'xml','pdf',
);
protected static $labelSeparator = ':';
/**
* @var If this is true, then restrictions set in {@link $allowed_max_file_size} and
@ -255,23 +257,58 @@ class File extends DataObject {
return $this->canEdit($member);
}
/*
* Generate and return the preview image / file upload / replace field for this File
* @return FormField
*/
protected function getFilePreview() {
//file upload
$uploadField = new UploadField('UploadField','Upload Field');
$uploadField->setConfig('previewMaxWidth', 40);
$uploadField->setConfig('previewMaxHeight', 30);
//$uploadField->setTemplate('FileEditUploadField');
if ($this->ParentID) {
$parent = $this->Parent();
if ($parent) { //set the parent that the Upload field should use for uploads
$uploadField->setFolderName($parent->getFilename());
$uploadField->setRecord($parent);
//TODO: make the uploadField replace the existing file
}
}
return $uploadField;
}
/**
* Returns the fields to power the edit screen of files in the CMS
* @return FieldList
*/
function getCMSFields() {
$urlLink = "<div class='field readonly'>";
$urlLink .= "<label class='left'>"._t('AssetTableField.URL','URL')."</label>";
$urlLink .= "<span class='readonly'><a href='{$this->Link()}' target='_blank'>{$this->RelativeLink()}</a></span>";
$urlLink .= "</div>";
//create the file attributes in a FieldGroup
$filePreview = new FieldGroup(
$this->getFilePreview(),
new ReadonlyField("FileType", _t('AssetTableField.TYPE','File type').self::$labelSeparator),
new ReadonlyField("Filename", _t('AssetTableField.FILENAME','File name').self::$labelSeparator),
new ReadonlyField("Size", _t('AssetTableField.SIZE','File size').self::$labelSeparator, $this->getSize()),
new DateField_Disabled("Created", _t('AssetTableField.CREATED','First uploaded').self::$labelSeparator),
new DateField_Disabled("LastEdited", _t('AssetTableField.LASTEDIT','Last changed').self::$labelSeparator)
);
$filePreview->setTitle("File preview");
$filePreview->setName("FilePreview");
return new FieldList(
new TabSet('Root',
new Tab('Main',
new Tab('Main',
$filePreview,
new TextField("Title", _t('AssetTableField.TITLE','Title')),
new TextField("Name", _t('AssetTableField.FILENAME','Filename')),
new LiteralField("AbsoluteURL", $urlLink),
new ReadonlyField("FileType", _t('AssetTableField.TYPE','Type')),
new ReadonlyField("Size", _t('AssetTableField.SIZE','Size'), $this->getSize()),
new DropdownField("OwnerID", _t('AssetTableField.OWNER','Owner'), Member::mapInCMSGroups()),
new DateField_Disabled("Created", _t('AssetTableField.CREATED','First uploaded')),
new DateField_Disabled("LastEdited", _t('AssetTableField.LASTEDIT','Last changed'))
new LiteralField("AbsoluteURL", $urlLink), //TODO: replace this is a proper preview
new DropdownField("OwnerID", _t('AssetTableField.OWNER','Owner'), Member::mapInCMSGroups())
)
)
);

View File

@ -97,14 +97,21 @@ class FieldGroup extends CompositeField {
$spaceZebra = isset($this->zebra) ? " fieldgroup-$this->zebra" : '';
$idAtt = isset($this->id) ? " id=\"{$this->id}\"" : '';
$content = "<div class=\"fieldgroup$spaceZebra\"$idAtt>";
$count = 1;
foreach($fs as $subfield) {
$childZebra = (!isset($childZebra) || $childZebra == "odd") ? "even" : "odd";
if($subfield->hasMethod('setZebra')) {
$subfield->setZebra($childZebra);
}
$content .= "<div class=\"fieldgroup-field\">" . $subfield->{$this->subfieldParam}() . "</div>";
//label the first and last fields of each surrounding div
if ($count == 1) $firstLast = "first";
elseif ($count == count($fs)) $firstLast = "last";
else $firstLast = '';
$content .= "<div class=\"fieldgroup-field $firstLast\">" . $subfield->{$this->subfieldParam}() . "</div>";
$count++;
}
$content .= "</div>";

View File

@ -379,10 +379,9 @@ class Form extends RequestHandler {
if($field = $this->checkFieldsForAction($field->FieldList(), $funcName)) {
return $field;
}
} elseif (!$field->hasMethod($funcName)) {
continue;
} elseif ($field->hasMethod($funcName)) {
return $field;
}
return $field;
}
}

View File

@ -80,8 +80,6 @@ class GridField extends FormField {
*/
public function __construct($name, $title = null, SS_List $dataList = null, GridFieldConfig $config = null) {
parent::__construct($name, $title, null);
FormField::__construct($name);
if($dataList) {
$this->setList($dataList);
@ -386,6 +384,7 @@ class GridField extends FormField {
$item->augmentColumns($this, $columns);
}
}
return $columns;
}
@ -404,8 +403,11 @@ class GridField extends FormField {
}
if(!empty($this->columnDispatch[$column])) {
$handler = $this->columnDispatch[$column];
return $handler->getColumnContent($this, $record, $column);
$content = "";
foreach($this->columnDispatch[$column] as $handler) {
$content .= $handler->getColumnContent($this, $record, $column);
}
return $content;
} else {
throw new InvalidArgumentException("Bad column '$column'");
}
@ -427,15 +429,18 @@ class GridField extends FormField {
}
if(!empty($this->columnDispatch[$column])) {
$handler = $this->columnDispatch[$column];
$attrs = $handler->getColumnAttributes($this, $record, $column);
if(is_array($attrs)) {
return $attrs;
} elseif($attrs) {
throw new LogicException("Non-array response from " . get_class($handler) . "::getColumnAttributes()");
} else {
return array();
$attrs = array();
foreach($this->columnDispatch[$column] as $handler) {
$column_attrs = $handler->getColumnAttributes($this, $record, $column);
if(is_array($column_attrs))
$attrs = array_merge($attrs, $column_attrs);
elseif($column_attrs)
throw new LogicException("Non-array response from " . get_class($handler) . "::getColumnAttributes()");
}
return $attrs;
} else {
throw new InvalidArgumentException("Bad column '$column'");
}
@ -456,13 +461,19 @@ class GridField extends FormField {
}
if(!empty($this->columnDispatch[$column])) {
$handler = $this->columnDispatch[$column];
$metadata = $handler->getColumnMetadata($this, $column);
if(is_array($metadata)) {
return $metadata;
} elseif($metadata) {
throw new LogicException("Non-array response from " . get_class($handler) . "::getColumnMetadata()");
$metadata = array();
foreach($this->columnDispatch[$column] as $handler) {
$column_metadata = $handler->getColumnMetadata($this, $column);
if(is_array($column_metadata))
$metadata = array_merge($metadata, $column_metadata);
else
throw new LogicException("Non-array response from " . get_class($handler) . "::getColumnMetadata()");
}
return $metadata;
}
throw new InvalidArgumentException("Bad column '$column'");
}
@ -489,10 +500,10 @@ class GridField extends FormField {
if($item instanceof GridField_ColumnProvider) {
$columns = $item->getColumnsHandled($this);
foreach($columns as $column) {
$this->columnDispatch[$column] = $item;
$this->columnDispatch[$column][] = $item;
}
}
}
}
}
/**
@ -729,23 +740,18 @@ class GridField_Action extends FormAction {
$actionData['StateID'] = $id;
// And generate field
$attributes = array(
'class' => ($this->extraClass() ? $this->extraClass() : '') . ' action-' . $this->actionName,
'id' => $this->id(),
'type' => 'submit',
$data = new ArrayData(array(
'Class' => ($this->extraClass() ? $this->extraClass() : '') . ($this->isReadonly() ? ' disabled' : ''),
'ID' => $this->id(),
// Note: This field needs to be less than 65 chars, otherwise Suhosin security patch
// will strip it from the requests
'name' => 'action_gridFieldAlterAction'. '?' . http_build_query($actionData),
'tabindex' => $this->getTabIndex(),
'data-url' => $this->gridField->Link(),
);
'Name' => 'action_gridFieldAlterAction'. '?' . http_build_query($actionData),
'Disabled' => $this->isReadonly(),
'Label' => $this->buttonLabel,
'DataURL' => $this->gridField->Link(),
));
if($this->isReadonly()) {
$attributes['disabled'] = 'disabled';
$attributes['class'] = $attributes['class'] . ' disabled';
}
return $this->createTag('button', $attributes, $this->buttonLabel);
return $data->renderWith('GridField_Action');
}
/**

View File

@ -1,7 +1,10 @@
<?php
/**
* This class is an GridField Component that add Delete action for Objects in the GridField
*
* Provides the entry point to editing a single record presented by the grid.
* Doesn't show an edit view on its own or modifies the record, but rather relies on routing conventions
* established in {@link getColumnContent()}. The default routing applies to
* the {@link GridFieldPopupForms} component, which has to be added separately
* to the grid field configuration.
*/
class GridFieldAction_Edit implements GridField_ColumnProvider {
@ -12,7 +15,8 @@ class GridFieldAction_Edit implements GridField_ColumnProvider {
* @param array $columns
*/
public function augmentColumns($gridField, &$columns) {
$columns[] = 'EditAction';
if(!in_array('Actions', $columns))
$columns[] = 'Actions';
}
/**
@ -35,7 +39,7 @@ class GridFieldAction_Edit implements GridField_ColumnProvider {
* @return array
*/
public function getColumnMetadata($gridField, $columnName) {
if($columnName == 'EditAction') {
if($columnName == 'Actions') {
return array('title' => '');
}
}
@ -47,7 +51,7 @@ class GridFieldAction_Edit implements GridField_ColumnProvider {
* @return type
*/
public function getColumnsHandled($gridField) {
return array('EditAction');
return array('Actions');
}
/**
@ -57,7 +61,7 @@ class GridFieldAction_Edit implements GridField_ColumnProvider {
* @return array
*/
public function getActions($gridField) {
return array('deleterecord');
return array();
}
/**
@ -68,7 +72,11 @@ class GridFieldAction_Edit implements GridField_ColumnProvider {
* @return string - the HTML for the column
*/
public function getColumnContent($gridField, $record, $columnName) {
return sprintf('<a class="action-edit" href="%s">%s</a>', Controller::join_links($gridField->Link('item'), $record->ID, 'edit'), _t('GridAction.Edit', 'edit'));
$data = new ArrayData(array(
'Link' => Controller::join_links($gridField->Link('item'), $record->ID, 'edit')
));
return $data->renderWith('GridFieldAction_Edit');
}
/**
@ -98,7 +106,8 @@ class GridFieldAction_Delete implements GridField_ColumnProvider, GridField_Acti
* @param array $columns
*/
public function augmentColumns($gridField, &$columns) {
$columns[] = 'DeleteAction';
if(!in_array('Actions', $columns))
$columns[] = 'Actions';
}
/**
@ -121,7 +130,7 @@ class GridFieldAction_Delete implements GridField_ColumnProvider, GridField_Acti
* @return array
*/
public function getColumnMetadata($gridField, $columnName) {
if($columnName == 'DeleteAction') {
if($columnName == 'Actions') {
return array('title' => '');
}
}
@ -133,7 +142,7 @@ class GridFieldAction_Delete implements GridField_ColumnProvider, GridField_Acti
* @return type
*/
public function getColumnsHandled($gridField) {
return array('DeleteAction');
return array('Actions');
}
/**
@ -175,12 +184,11 @@ class GridFieldAction_Delete implements GridField_ColumnProvider, GridField_Acti
* @return void
*/
public function handleAction(GridField $gridField, $actionName, $arguments, $data) {
$id = $arguments['RecordID'];
$item = $gridField->getList()->byID($id);
if(!$item) return;
if($actionName == 'deleterecord') {
$item->delete();
$id = $arguments['RecordID'];
$item = $gridField->getList()->byID($id);
if(!$item) return;
$item->delete();
}
}
}

View File

@ -87,15 +87,16 @@ class GridFieldConfig_Base extends GridFieldConfig {
* @param int $itemsPerPage - How many items per page should show up per page
* @return GridFieldConfig_Base
*/
public static function create($itemsPerPage=25){
return new GridFieldConfig_Base($itemsPerPage=25);
public static function create($itemsPerPage=15){
return new GridFieldConfig_Base($itemsPerPage=15);
}
/**
*
* @param int $itemsPerPage - How many items per page should show up
*/
public function __construct($itemsPerPage=25) {
public function __construct($itemsPerPage=15) {
$this->addComponent(new GridFieldTitle());
$this->addComponent(new GridFieldSortableHeader());
$this->addComponent(new GridFieldFilter());
$this->addComponent(new GridFieldDefaultColumns());
@ -142,5 +143,6 @@ class GridFieldConfig_ManyManyEditor extends GridFieldConfig {
$this->addComponent(new GridFieldAction_Edit());
$this->addComponent(new GridFieldRelationDelete());
$this->addComponent(new GridFieldPaginator($itemsPerPage));
$this->addComponent(new GridFieldPopupForms());
}
}

View File

@ -18,7 +18,7 @@ class GridFieldPaginator implements GridField_HTMLProvider, GridField_DataManipu
*
* @var int
*/
protected $itemsPerPage = 25;
protected $itemsPerPage = 15;
/**
* Which template to use for rendering
@ -31,7 +31,7 @@ class GridFieldPaginator implements GridField_HTMLProvider, GridField_DataManipu
*
* @param int $itemsPerPage - How many items should be displayed per page
*/
public function __construct($itemsPerPage=25) {
public function __construct($itemsPerPage=15) {
$this->itemsPerPage = $itemsPerPage;
}

View File

@ -1,7 +1,10 @@
<?php
/**
* Provides view and edit forms at GridField-specific URLs. These can be placed into pop-ups by an appropriate front-end.
* Provides view and edit forms at GridField-specific URLs.
* These can be placed into pop-ups by an appropriate front-end.
* Usually added to a grid field alongside of {@link GridFieldAction_Edit}
* which takes care of linking the individual rows to their edit view.
*
* The URLs provided will be off the following form:
* - <FormURL>/field/<GridFieldName>/item/<RecordID>
@ -9,22 +12,18 @@
*/
class GridFieldPopupForms implements GridField_URLHandler {
/**
* @var String
*/
protected $template = 'GridFieldItemEditView';
protected $template = 'GridFieldPopupForms';
/**
*
* @var Controller
*/
protected $popupController;
/**
*
* @var string
*/
protected $popupFormName;
protected $name;
function getURLHandlers($gridField) {
return array(
@ -41,12 +40,10 @@ class GridFieldPopupForms implements GridField_URLHandler {
* The arguments are experimental API's to support partial content to be passed back to whatever
* controller who wants to display the getCMSFields
*
* @param Controller $popupController The controller object that will be used to render the pop-up forms
* @param string $popupFormName The name of the edit form to place into the pop-up form
* @param string $name The name of the edit form to place into the pop-up form
*/
public function __construct($popupController, $popupFormName) {
$this->popupController = $popupController;
$this->popupFormName = $popupFormName;
public function __construct($name = 'DetailForm') {
$this->name = $name;
}
/**
@ -56,10 +53,22 @@ class GridFieldPopupForms implements GridField_URLHandler {
* @return GridFieldPopupForm_ItemRequest
*/
public function handleItem($gridField, $request) {
$record = $gridField->getList()->byId($request->param("ID"));
$handler = new GridFieldPopupForm_ItemRequest($gridField, $this, $record, $this->popupController, $this->popupFormName);
$controller = $gridField->getForm()->Controller();
if(is_numeric($request->param('ID'))) {
$record = $gridField->getList()->byId($request->param("ID"));
} else {
$record = Object::create($gridField->getModelClass());
}
if(!$class = ClassInfo::exists(get_class($this) . "_ItemRequest")) {
$class = 'GridFieldPopupForm_ItemRequest';
}
$handler = Object::create($class, $gridField, $this, $record, $controller, $this->name);
$handler->setTemplate($this->template);
return $handler;
return $handler->handleRequest($request, $gridField);
}
/**
@ -75,6 +84,20 @@ class GridFieldPopupForms implements GridField_URLHandler {
function getTemplate() {
return $this->template;
}
/**
* @param String
*/
function setName($name) {
$this->name = $name;
}
/**
* @return String
*/
function getName() {
return $this->name;
}
}
class GridFieldPopupForm_ItemRequest extends RequestHandler {
@ -137,15 +160,29 @@ class GridFieldPopupForm_ItemRequest extends RequestHandler {
}
public function Link($action = null) {
return Controller::join_links($this->gridField->Link('item'), $this->record->ID, $action);
return Controller::join_links($this->gridField->Link('item'), $this->record->ID ? $this->record->ID : 'new', $action);
}
function edit($request) {
$controller = $this->popupController;
$form = $this->ItemEditForm($this->gridField, $request);
// TODO Coupling with CMS
if($controller instanceof LeftAndMain) {
$form->addExtraClass('cms-edit-form');
$form->setTemplate($controller->getTemplatesWithSuffix('_EditForm'));
$form->addExtraClass('cms-content center ss-tabset ' . $controller->BaseCSSClasses());
if($form->Fields()->hasTabset()) $form->Fields()->findOrMakeTab('Root')->setTemplate('CMSTabSet');
// TODO Link back to controller action (and edited root record) rather than index,
// which requires more URL knowledge than the current link to this field gives us.
// The current root record is held in session only,
// e.g. page/edit/show/6/ vs. page/edit/EditForm/field/MyGridField/....
$form->Backlink = $controller->Link();
}
$return = $this->customise(array(
'Backlink' => $this->gridField->getForm()->Controller()->Link(),
'ItemEditForm' => $this->ItemEditForm($this->gridField, $request),
'Backlink' => $controller->Link(),
'ItemEditForm' => $form,
))->renderWith($this->template);
if($controller->isAjax()) {
@ -154,7 +191,8 @@ class GridFieldPopupForm_ItemRequest extends RequestHandler {
// If not requested by ajax, we need to render it within the controller context+template
return $controller->customise(array(
$this->popupFormName => $return,
// TODO Allow customization
'Content' => $return,
));
}
}
@ -163,35 +201,40 @@ class GridFieldPopupForm_ItemRequest extends RequestHandler {
* Builds an item edit form. The arguments to getCMSFields() are the popupController and
* popupFormName, however this is an experimental API and may change.
*
* In the future, we will probably need to come up with a tigher object representing a partially
* @todo In the future, we will probably need to come up with a tigher object representing a partially
* complete controller with gaps for extra functionality. This, for example, would be a better way
* of letting Security/login put its log-in form inside a UI specified elsewhere.
*
* @return Form
*/
function ItemEditForm() {
$request = $this->popupController->getRequest();
$form = new Form(
$this,
'ItemEditForm',
// WARNING: The arguments passed here are a little arbitrary. This API will need cleanup
$this->record->getCMSFields($this->popupController, $this->popupFormName),
new FieldList(
$saveAction = new FormAction('doSave', _t('GridFieldDetailsForm.Save', 'Save'))
$saveAction = new FormAction('doSave', _t('GridFieldDetailsForm.Save', 'Save')),
$deleteAction = new FormAction('doDelete', _t('GridFieldDetailsForm.Delete', 'Delete'))
)
);
$saveAction->addExtraClass('ss-ui-action-constructive icon-accept');
$saveAction->addExtraClass('ss-ui-action-constructive');
$deleteAction->addExtraClass('ss-ui-action-destructive');
$form->loadDataFrom($this->record);
return $form;
}
function doSave($data, $form) {
$new_record = $this->record->ID == 0;
try {
$form->saveInto($this->record);
$this->record->write();
if($new_record)
$this->gridField->getList()->add($this->record);
} catch(ValidationException $e) {
$form->sessionMessage($e->getResult()->message(), 'bad');
return Director::redirectBack();
return Controller::curr()->redirectBack();
}
// TODO Save this item into the given relationship
@ -207,6 +250,34 @@ class GridFieldPopupForm_ItemRequest extends RequestHandler {
return $this->popupController->redirectBack();
}
function doDelete($data, $form) {
try {
$toDelete = $this->record;
if (!$toDelete->canDelete()) {
throw new ValidationException(_t('GridFieldDetailsForm.DeletePermissionsFailure',"No delete permissions"),0);
}
$toDelete->delete();
} catch(ValidationException $e) {
$form->sessionMessage($e->getResult()->message(), 'bad');
return Director::redirectBack();
}
$message = sprintf(
_t('ComplexTableField.SUCCESSEDIT2', 'Deleted %s %s'),
$this->record->singular_name(),
'<a href="' . $this->Link('edit') . '">"' . htmlspecialchars($this->record->Title, ENT_QUOTES) . '"</a>'
);
$form->sessionMessage($message, 'good');
//when an item is deleted, redirect to the revelant admin section without the action parameter
$controller = Controller::curr();
$noActionURL = $controller->removeAction($data['url']);
return Director::redirect($noActionURL, 302); //redirect back to admin section
}
/**
* @param String
*/

View File

@ -0,0 +1,13 @@
<?php
class GridFieldTitle implements GridField_HTMLProvider {
function getHTMLFragments($gridField) {
return array(
'header' => $gridField->customise(array(
'NewLink' => Controller::join_links($gridField->Link('item'), 'new')
))->renderWith('GridFieldTitle')
);
}
}
?>

View File

@ -112,7 +112,21 @@
).css({'position':'relative','margin':'0 auto','width':'65%'}).appendTo(eleInput.closest('th'));
}
}
});
});
$('fieldset.ss-gridfield .new-link').entwine({
onclick: function(e) {
$(this).trigger('opennewview', $(this).prop('href'));
return false;
}
});
$('fieldset.ss-gridfield .edit-link').entwine({
onclick: function(e) {
$(this).trigger('openeditview', $(this).prop('href'));
return false;
}
});
/**
* Allows selection of one or more rows in the grid field.
@ -146,4 +160,4 @@
});
}(jQuery));
}(jQuery));

View File

@ -63,7 +63,7 @@ class HasManyList extends RelationList {
* @param $itemID The ID of the item to be removed
*/
function removeByID($itemID) {
$item = $this->byID($item);
$item = $this->byID($itemID);
return $this->remove($item);
}

View File

@ -72,6 +72,21 @@ class Image extends File {
parent::defineMethods();
}
/*
* Generate and return the preview image / file upload / replace field for this File
* @return FormField
*/
protected function getFilePreview() {
$formattedImage = $this->getFormattedImage('AssetLibraryPreview');
$thumbnail = $formattedImage ? $formattedImage->URL : '';
$previewField = new LiteralField("ImageFull",
"<img id='thumbnailImage' class='thumbnail-preview' src='{$thumbnail}?r=" . rand(1,100000) . "' alt='{$this->Name}' />\n"
);
return $previewField;
}
function getCMSFields() {
$fields = parent::getCMSFields();
@ -81,19 +96,9 @@ class Image extends File {
$urlLink .= "<span class='readonly'><a href='{$this->Link()}'>{$this->RelativeLink()}</a></span>";
$urlLink .= "</div>";
$big = $this->URL;
$formattedImage = $this->getFormattedImage('AssetLibraryPreview');
$thumbnail = $formattedImage ? $formattedImage->URL : '';
// Hmm this required the translated string to be appended to BottomRoot to add this to the Main tab
$fields->addFieldToTab('Root.Main',
new ReadonlyField("Dimensions", _t('AssetTableField.DIM','Dimensions'))
);
$fields->addFieldToTab('Root.Main',
new LiteralField("ImageFull",
"<img id='thumbnailImage' src='{$thumbnail}?r=" . rand(1,100000) . "' alt='{$this->Name}' />"
)
);
//attach the addition file information for an image to the existing FieldGroup create in the parent class
$fileAttributes = $fields->FieldByName('Root.Main.FilePreview');
$fileAttributes->push(new ReadonlyField("Dimensions", _t('AssetTableField.DIM','Dimensions').self::$labelSeparator));
return $fields;
}

View File

@ -86,11 +86,34 @@ $gf_border_radius: 7px;
}
}
tr {
&.sortable-header {
tr {
&.title {
@include border-top-radius($gf_border_radius);
th {
position: relative;
background: $gf_colour_gradient_dark;
border-top: 1px solid $gf_colour_border;
padding: 5px;
min-height: 40px;
@include background-image(linear-gradient($gf_colour_gradient_light, $gf_colour_gradient_dark));
@include border-top-radius($gf_border_radius);
@include single-text-shadow($gf_colour_text_shadow, 0px, -1px, 0);
h2{
padding: 0px;
font-size: 16px;
color:#fff;
margin:0;
display:inline;
}
.new{
font-size: 14px;
border-color: $gf_colour_border;
float: right;
}
}
}
&.sortable-header {
background: $gf_colour_subheader;
}
&:hover {
background: #FFFAD6 !important;
@ -116,10 +139,10 @@ $gf_border_radius: 7px;
padding: 5px;
border-right: 1px solid $gf_colour_border;
&.main:first-child{
@include border-top-left-radius($gf_border_radius);
//@include border-top-left-radius($gf_border_radius);
}
&.main:last-child{
@include border-top-right-radius($gf_border_radius);
//@include border-top-right-radius($gf_border_radius);
}
div {
&.fieldgroup,&.fieldgroup-field {
@ -131,7 +154,6 @@ $gf_border_radius: 7px;
}
}
&.extra,&.action {
background: $gf_colour_gradient_dark;
padding: 0;
cursor: default;
button,button:hover {
@ -144,7 +166,8 @@ $gf_border_radius: 7px;
&.main{
border-top: 1px solid $gf_colour_border;
color:#fff;
@include background-image(linear-gradient($gf_colour_gradient_light, $gf_colour_gradient_dark));
background: darken($gf_colour_subheader,10%);
border-bottom: 1px solid $gf_colour_border;
span{
@include single-text-shadow($gf_colour_text_shadow, 0px, -1px, 0);
}

View File

@ -14,11 +14,17 @@ class PartialMatchFilter extends SearchFilter {
public function apply(DataQuery $query) {
$this->model = $query->applyRelation($this->relation);
return $query->where(sprintf(
"%s LIKE '%%%s%%'",
$this->getDbName(),
Convert::raw2sql($this->getValue())
));
$where = array();
if(is_array($this->getValue())) {
foreach($this->getValue() as $value) {
$where[]= sprintf("%s LIKE '%%%s%%'", $this->getDbName(), Convert::raw2sql($value));
}
} else {
$where[] = sprintf("%s LIKE '%%%s%%'", $this->getDbName(), Convert::raw2sql($this->getValue()));
}
return $query->where(implode(' OR ', $where));
}
public function isEmpty() {

View File

@ -63,7 +63,6 @@ class Group extends DataObject {
Requirements::javascript(SAPPHIRE_DIR . '/javascript/PermissionCheckboxSetField.js');
$config = new GridFieldConfig_ManyManyEditor('FirstName', true, 20);
$config->addComponent(new GridFieldPopupForms(Controller::curr(), 'EditForm'));
$config->addComponent(new GridFieldExporter());
$memberList = new GridField('Members','Members', $this->Members(), $config);

View File

@ -0,0 +1 @@
<a class="edit-link" href="$Link">edit</a>

View File

@ -0,0 +1,3 @@
<tr class="title">
<th colspan="$ColumnCount"><h2>$Title</h2> <a href="$NewLink" class="action ss-ui-action-constructive ss-ui-button ui-button ui-widget ui-state-default ui-corner-all new new-link">Add new</a></th>
</tr>

View File

@ -0,0 +1 @@
<button id="$ID" class="$Class" name="$Name" type="submit" data-url="$DataURL"<% if Disabled %> disabled="disabled"<% end_if %>>$Label</button>

View File

@ -62,11 +62,6 @@ class ArrayDataTest extends SapphireTest {
// $arrayData = new ArrayData($array);
}
function testForTemplateWorks() {
$arrayData = new ArrayData(array('b' => 'bunny'));
$this->assertEquals("array (\n 'b' => 'bunny',\n)", $arrayData->forTemplate());
}
function testSetField() {
$arrayData = new ArrayData(array());

View File

@ -55,22 +55,28 @@ class ViewableDataTest extends SapphireTest {
$this->assertEquals('test', $viewableData->XML_val('test'));
$this->assertEquals('casted', $viewableData->XML_val('alwaysCasted'));
$this->assertEquals('overwritten', $newViewableData->XML_val('test'));
$this->assertEquals('overwritten', $newViewableData->XML_val('alwaysCasted'));
$this->assertEquals('castable', $viewableData->forTemplate());
$this->assertEquals('castable', $newViewableData->forTemplate());
}
public function testObjectCustomise() {
$viewableData = new ViewableDataTest_Castable();
$newViewableData = $viewableData->customise(new ViewableDataTest_RequiresCasting());
$this->assertEquals('test', $viewableData->XML_val('test'));
$this->assertEquals('casted', $viewableData->XML_val('alwaysCasted'));
$this->assertEquals('overwritten', $newViewableData->XML_val('test'));
$this->assertEquals('casted', $newViewableData->XML_val('alwaysCasted'));
$this->assertEquals('castable', $viewableData->forTemplate());
$this->assertEquals('casted', $newViewableData->forTemplate());
}
public function testRAWVal() {
$data = new ViewableDataTest_Castable();
$data->test = 'This &amp; This';
@ -161,7 +167,10 @@ class ViewableDataTest_Castable extends ViewableData {
public function castedUnsafeXML() {
return $this->unsafeXML();
}
public function forTemplate() {
return 'castable';
}
}
class ViewableDataTest_RequiresCasting extends ViewableData {

View File

@ -101,13 +101,6 @@ class ArrayData extends ViewableData {
return $obj;
}
/**
* This is pretty crude, but it helps diagnose error situations
*/
function forTemplate() {
return var_export($this->array, true);
}
/**
* @deprecated 3.0 Use {@link ArrayData::toMap()}.
*/