Merge branch '106-add-edit-records-rc'

Conflicts:
	admin/code/LeftAndMain.php
	admin/css/screen.css
	admin/scss/_style.scss
	admin/templates/Includes/LeftAndMain_EditForm.ss
	css/GridField.css
	filesystem/Folder.php
	forms/gridfield/GridField.php
	forms/gridfield/GridFieldDefaultColumns.php
	forms/gridfield/GridFieldPopupForms.php
This commit is contained in:
Ingo Schommer 2012-02-27 23:58:10 +01:00
commit bcc73de85e
25 changed files with 407 additions and 141 deletions

View File

@ -473,8 +473,7 @@ class LeftAndMain extends Controller {
/**
* Return a list of appropriate templates for this class, with the given suffix
*/
protected function getTemplatesWithSuffix($suffix) {
$templates = array();
public function getTemplatesWithSuffix($suffix) {
$classes = array_reverse(ClassInfo::ancestry($this->class));
foreach($classes as $class) {
$template = $class . $suffix;

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

@ -109,8 +109,8 @@ article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav,
.cms .ui-widget-header .ui-dialog-title { padding: 6px 0; text-shadow: #ced7dc 1px 1px 0; }
.cms .ui-widget-header a.ui-dialog-titlebar-close { position: absolute; top: -8px; right: -15px; width: 30px; height: 30px; z-index: 100000; }
.cms .ui-widget-header a.ui-state-hover { border-color: transparent; background: transparent; }
.cms .ui-widget-header a.ui-state-hover .ui-icon-closethick { background: url('../images/sprites-32x32-s9a54b65fa5.png') 0 -42px no-repeat; }
.cms .ui-widget-header .ui-icon-closethick { background: url('../images/sprites-32x32-s9a54b65fa5.png') 0 -82px no-repeat; width: 30px; height: 30px; }
.cms .ui-widget-header a.ui-state-hover .ui-icon-closethick { background: url('../images/sprites-32x32-sa4e142f7f0.png') 0 -252px no-repeat; }
.cms .ui-widget-header .ui-icon-closethick { background: url('../images/sprites-32x32-sa4e142f7f0.png') 0 -292px no-repeat; width: 30px; height: 30px; }
.cms .ui-state-hover { cursor: pointer; }
.cms .ui-widget input, .cms .ui-widget select, .cms .ui-widget textarea, .cms .ui-widget button { color: #444444; font-size: 12px; font-family: Arial, sans-serif; }
@ -383,7 +383,7 @@ form.member-profile-form #Groups .middleColumn .TreeDropdownField { width: 90%;
form.member-profile-form #Permissions .optionset li { float: none; width: auto; }
.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; }
@ -447,9 +447,18 @@ body.cms-dialog { overflow: auto; background: url("../images/textures/bg_cms_mai
/** -------------------------------------------- Step labels -------------------------------------------- */
.step-label > * { display: inline-block; vertical-align: top; }
.step-label .flyout { height: 18px; font-size: 14px; font-weight: bold; -moz-border-radius-topleft: 3px; -webkit-border-top-left-radius: 3px; -o-border-top-left-radius: 3px; -ms-border-top-left-radius: 3px; -khtml-border-top-left-radius: 3px; border-top-left-radius: 3px; -moz-border-radius-bottomleft: 3px; -webkit-border-bottom-left-radius: 3px; -o-border-bottom-left-radius: 3px; -ms-border-bottom-left-radius: 3px; -khtml-border-bottom-left-radius: 3px; border-bottom-left-radius: 3px; background-color: #667980; padding: 4px 3px 4px 6px; text-align: center; text-shadow: none; color: #fff; }
.step-label .arrow { height: 26px; width: 10px; background: url('../images/sprites-32x32-s9a54b65fa5.png') 0 -213px no-repeat; margin-right: 4px; }
.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 { display: block; margin: 0; padding: 0; background: none; list-style-type: none; }
.cms .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; }
@ -564,7 +573,7 @@ li.class-ErrorPage > a .jstree-pageicon { background-position: 0 -112px; }
.cms-logo span { font-weight: bold; font-size: 14px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; o-text-overflow: ellipsis; }
.cms-login-status { border-top: 1px solid #19435c; height: 24px; padding: 7px 4px 0 4px; overflow: hidden; line-height: 16px; font-size: 11px; }
.cms-login-status .logout-link { display: inline-block; height: 16px; width: 16px; float: left; margin: 0 8px 0 3px; background: url('../images/sprites-32x32-s9a54b65fa5.png') 0 -123px no-repeat; text-indent: -9999em; }
.cms-login-status .logout-link { display: inline-block; height: 16px; width: 16px; float: left; margin: 0 8px 0 3px; background: url('../images/sprites-32x32-sa4e142f7f0.png') 0 -669px no-repeat; text-indent: -9999em; }
.cms-menu { z-index: 80; background: #c6d7df; border-right: 1px solid #8c99a1; width: 191px; /* 8x24 - 1 */ -moz-box-shadow: rgba(107, 120, 123, 0.5) 0 0 8px; -webkit-box-shadow: rgba(107, 120, 123, 0.5) 0 0 8px; -o-box-shadow: rgba(107, 120, 123, 0.5) 0 0 8px; box-shadow: rgba(107, 120, 123, 0.5) 0 0 8px; }
.cms-menu a { text-decoration: none; }
@ -584,12 +593,12 @@ li.class-ErrorPage > a .jstree-pageicon { background-position: 0 -112px; }
.cms-menu-list li a .icon { display: inline-block; float: left; margin: 4px 10px 0 4px; filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=70); opacity: 0.7; }
.cms-menu-list li a .text { display: inline-block; float: left; }
.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-s9a54b65fa5.png') 0 -149px no-repeat; vertical-align: middle; }
.cms-menu-list li a .toggle-children.opened .toggle-children-icon { background: url('../images/sprites-32x32-s9a54b65fa5.png') 0 -165px no-repeat; }
.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-s9a54b65fa5.png') 0 -181px no-repeat; }
.cms-menu-list li.current a .toggle-children.opened .toggle-children-icon { background: url('../images/sprites-32x32-s9a54b65fa5.png') 0 -197px no-repeat; }
.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; }
.cms-menu-list li.current ul { border-top: none; display: block; }
.cms-menu-list li.current li { background-color: #287099; }
.cms-menu-list li.current li a { font-size: 11px; padding: 0 10px 0 40px; height: 32px; line-height: 32px; color: #e2f0f7; background: none; border-top: 1px solid #338dc1; border-bottom: 1px solid #1e5270; }

View File

@ -828,7 +828,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%;
}
}
@ -973,6 +973,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));
}
@ -1146,4 +1147,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" 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

@ -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>