mirror of
https://github.com/silverstripe/silverstripe-userforms.git
synced 2024-10-22 17:05:42 +02:00
Merge remote-tracking branch 'open-sausages/feature/field-groups' into feature/multi-page-forms-v2
This commit is contained in:
commit
5c0f172ee5
1
.gitignore
vendored
1
.gitignore
vendored
@ -1 +1,2 @@
|
||||
.DS_Store
|
||||
.sass-cache/
|
||||
|
@ -37,17 +37,17 @@ class UserFormFieldEditorExtension extends DataExtension {
|
||||
$this->createInitialFormStep(true);
|
||||
|
||||
$editableColumns = new GridFieldEditableColumns();
|
||||
$fieldClasses = $this->getEditableFieldClasses();
|
||||
$editableColumns->setDisplayFields(array(
|
||||
'ClassName' => function($record, $column, $grid) {
|
||||
if($record instanceof EditableFormStep) {
|
||||
return new LabelField($column, "Page Break");
|
||||
} else {
|
||||
return DropdownField::create($column, '', $this->getEditableFieldClasses());
|
||||
'ClassName' => function($record, $column, $grid) use ($fieldClasses) {
|
||||
if($record instanceof EditableFormField) {
|
||||
return $record->getInlineClassnameField($column, $fieldClasses);
|
||||
}
|
||||
},
|
||||
'Title' => function($record, $column, $grid) {
|
||||
return TextField::create($column, ' ')
|
||||
->setAttribute('placeholder', _t('UserDefinedForm.TITLE', 'Title'));
|
||||
if($record instanceof EditableFormField) {
|
||||
return $record->getInlineTitleField($column);
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
@ -55,8 +55,13 @@ class UserFormFieldEditorExtension extends DataExtension {
|
||||
->addComponents(
|
||||
$editableColumns,
|
||||
new GridFieldButtonRow(),
|
||||
$addField = new GridFieldAddNewInlineButton(),
|
||||
$addStep = new GridFieldAddItemInlineButton('EditableFormStep'),
|
||||
GridFieldAddClassesButton::create('EditableFormField')
|
||||
->setButtonName(_t('UserFormFieldEditorExtension.ADD_FIELD', 'Add Field'))
|
||||
->setButtonClass('ss-ui-action-constructive'),
|
||||
GridFieldAddClassesButton::create('EditableFormStep')
|
||||
->setButtonName(_t('UserFormFieldEditorExtension.ADD_PAGE_BREAK', 'Add Page Break')),
|
||||
GridFieldAddClassesButton::create(array('EditableFieldGroup', 'EditableFieldGroupEnd'))
|
||||
->setButtonName(_t('UserFormFieldEditorExtension.ADD_FIELD_GROUP', 'Add Field Group')),
|
||||
new GridFieldEditButton(),
|
||||
new GridFieldDeleteAction(),
|
||||
new GridFieldToolbarHeader(),
|
||||
@ -64,9 +69,6 @@ class UserFormFieldEditorExtension extends DataExtension {
|
||||
new GridState_Component(),
|
||||
new GridFieldDetailForm()
|
||||
);
|
||||
$addField->setTitle('Add Field');
|
||||
$addStep->setTitle('Add Page Break');
|
||||
$addStep->setExtraClass('uf-gridfield-steprow');
|
||||
|
||||
$fieldEditor = GridField::create(
|
||||
'Fields',
|
||||
@ -113,7 +115,7 @@ class UserFormFieldEditorExtension extends DataExtension {
|
||||
|
||||
// Add step
|
||||
$step = EditableFormStep::create();
|
||||
$step->Title = _t('EditableFormStep.TITLE_FIRST', 'First Step');
|
||||
$step->Title = _t('EditableFormStep.TITLE_FIRST', 'First Page');
|
||||
$step->Sort = 1;
|
||||
$step->write();
|
||||
$fields->add($step);
|
||||
@ -126,21 +128,20 @@ class UserFormFieldEditorExtension extends DataExtension {
|
||||
$classes = ClassInfo::getValidSubClasses('EditableFormField');
|
||||
|
||||
// Remove classes we don't want to display in the dropdown.
|
||||
$classes = array_diff($classes, array(
|
||||
'EditableFormField',
|
||||
'EditableMultipleOptionField'
|
||||
));
|
||||
|
||||
$editableFieldClasses = array();
|
||||
|
||||
foreach ($classes as $key => $className) {
|
||||
$singleton = singleton($className);
|
||||
|
||||
foreach ($classes as $class) {
|
||||
if(in_array($class, array('EditableFormField', 'EditableMultipleOptionField'))
|
||||
|| Config::inst()->get($class, 'hidden')
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$singleton = singleton($class);
|
||||
if(!$singleton->canCreate()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$editableFieldClasses[$className] = $singleton->i18n_singular_name();
|
||||
$editableFieldClasses[$class] = $singleton->i18n_singular_name();
|
||||
}
|
||||
|
||||
return $editableFieldClasses;
|
||||
|
108
code/extensions/UserFormValidator.php
Normal file
108
code/extensions/UserFormValidator.php
Normal file
@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
|
||||
class UserFormValidator extends RequiredFields {
|
||||
public function php($data) {
|
||||
if(!parent::php($data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Skip unsaved records
|
||||
if(empty($data['ID']) || !is_numeric($data['ID'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$fields = EditableFormField::get()->filter('ParentID', $data['ID'])->sort('"Sort" ASC');
|
||||
|
||||
// Current nesting
|
||||
$stack = array();
|
||||
foreach($fields as $field) {
|
||||
if($field instanceof EditableFormStep) {
|
||||
// Page at top level, or after another page is ok
|
||||
if(empty($stack) || (count($stack) === 1 && $stack[0] instanceof EditableFormStep)) {
|
||||
$stack = array($field);
|
||||
continue;
|
||||
}
|
||||
|
||||
$this->validationError(
|
||||
'FormFields',
|
||||
_t(
|
||||
"UserFormValidator.UNEXPECTED_BREAK",
|
||||
"Unexpected page break '{name}' inside nested field '{group}'",
|
||||
array(
|
||||
'name' => $field->CMSTitle,
|
||||
'group' => end($stack)->CMSTitle
|
||||
)
|
||||
),
|
||||
'error'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate no pages
|
||||
if(empty($stack)) {
|
||||
$this->validationError(
|
||||
'FormFields',
|
||||
_t(
|
||||
"UserFormValidator.NO_PAGE",
|
||||
"Field '{name}' found before any pages",
|
||||
array(
|
||||
'name' => $field->CMSTitle
|
||||
)
|
||||
),
|
||||
'error'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Nest field group
|
||||
if($field instanceof EditableFieldGroup) {
|
||||
$stack[] = $field;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Unnest field group
|
||||
if($field instanceof EditableFieldGroupEnd) {
|
||||
$top = end($stack);
|
||||
|
||||
// Check that the top is a group at all
|
||||
if(!$top instanceof EditableFieldGroup) {
|
||||
$this->validationError(
|
||||
'FormFields',
|
||||
_t(
|
||||
"UserFormValidator.UNEXPECTED_GROUP_END",
|
||||
"'{name}' found without a matching group",
|
||||
array(
|
||||
'name' => $field->CMSTitle
|
||||
)
|
||||
),
|
||||
'error'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check that the top is the right group
|
||||
if($top->EndID != $field->ID) {
|
||||
$this->validationError(
|
||||
'FormFields',
|
||||
_t(
|
||||
"UserFormValidator.WRONG_GROUP_END",
|
||||
"'{name}' found closes the wrong group '{group}'",
|
||||
array(
|
||||
'name' => $field->CMSTitle,
|
||||
'group' => $top->CMSTitle
|
||||
)
|
||||
),
|
||||
'error'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Unnest group
|
||||
array_pop($stack);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
47
code/formfields/UserFormsCompositeField.php
Normal file
47
code/formfields/UserFormsCompositeField.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Represents a composite field group, which may contain other groups
|
||||
*/
|
||||
abstract class UserFormsCompositeField extends CompositeField implements UserFormsFieldContainer {
|
||||
|
||||
/**
|
||||
* Parent field
|
||||
*
|
||||
* @var UserFormsFieldContainer
|
||||
*/
|
||||
protected $parent = null;
|
||||
|
||||
public function getParent() {
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
public function setParent(UserFormsFieldContainer $parent) {
|
||||
$this->parent = $parent;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function processNext(EditableFormField $field) {
|
||||
// When we find a step, bubble up to the top
|
||||
if($field instanceof EditableFormStep) {
|
||||
return $this->getParent()->processNext($field);
|
||||
}
|
||||
|
||||
// Skip over fields that don't generate formfields
|
||||
$formField = $field->getFormField();
|
||||
if(!$formField) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
// Save this field
|
||||
$this->push($formField);
|
||||
|
||||
// Nest fields that are containers
|
||||
if($formField instanceof UserFormsFieldContainer) {
|
||||
return $formField->setParent($this);
|
||||
}
|
||||
|
||||
// Add any subsequent fields to this
|
||||
return $this;
|
||||
}
|
||||
}
|
30
code/formfields/UserFormsFieldContainer.php
Normal file
30
code/formfields/UserFormsFieldContainer.php
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Represents a field container which can iteratively process nested fields, converting it into a fieldset
|
||||
*/
|
||||
interface UserFormsFieldContainer {
|
||||
|
||||
/**
|
||||
* Process the next field in the list, returning the container to add the next field to.
|
||||
*
|
||||
* @param EditableFormField $field
|
||||
* @return EditableContainerField
|
||||
*/
|
||||
public function processNext(EditableFormField $field);
|
||||
|
||||
/**
|
||||
* Set the parent
|
||||
*
|
||||
* @param UserFormsFieldContainer $parent
|
||||
* @return $this
|
||||
*/
|
||||
public function setParent(UserFormsFieldContainer $parent);
|
||||
|
||||
/**
|
||||
* Get the parent
|
||||
*
|
||||
* @return UserFormsFieldContainer
|
||||
*/
|
||||
public function getParent();
|
||||
}
|
32
code/formfields/UserFormsFieldList.php
Normal file
32
code/formfields/UserFormsFieldList.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* A list of formfields which allows for iterative processing of nested composite fields
|
||||
*/
|
||||
class UserFormsFieldList extends FieldList implements UserFormsFieldContainer {
|
||||
|
||||
public function processNext(EditableFormField $field) {
|
||||
$formField = $field->getFormField();
|
||||
if(!$formField) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->push($formField);
|
||||
|
||||
if($formField instanceof UserFormsFieldContainer) {
|
||||
return $formField->setParent($this);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getParent() {
|
||||
// Field list does not have a parent
|
||||
return null;
|
||||
}
|
||||
|
||||
public function setParent(UserFormsFieldContainer $parent) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
27
code/formfields/UserFormsGroupField.php
Normal file
27
code/formfields/UserFormsGroupField.php
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Front end composite field for userforms
|
||||
*/
|
||||
class UserFormsGroupField extends UserFormsCompositeField {
|
||||
|
||||
public function __construct($children = null) {
|
||||
parent::__construct($children);
|
||||
$this->setTag('fieldset');
|
||||
}
|
||||
|
||||
public function getLegend() {
|
||||
// Legend defaults to title
|
||||
return parent::getLegend() ?: $this->Title();
|
||||
}
|
||||
|
||||
public function processNext(EditableFormField $field) {
|
||||
// When ending a group, jump up one level
|
||||
if($field instanceof EditableFieldGroupEnd) {
|
||||
return $this->getParent();
|
||||
}
|
||||
|
||||
// Otherwise behave as per normal composite field
|
||||
return parent::processNext($field);
|
||||
}
|
||||
}
|
7
code/formfields/UserFormsStepField.php
Normal file
7
code/formfields/UserFormsStepField.php
Normal file
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Represents a page step in a form, which may contain form fields or other groups
|
||||
*/
|
||||
class UserFormsStepField extends UserFormsCompositeField {
|
||||
}
|
225
code/forms/GridFieldAddClassesButton.php
Normal file
225
code/forms/GridFieldAddClassesButton.php
Normal file
@ -0,0 +1,225 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* A button which allows objects to be created with a specified classname(s)
|
||||
*/
|
||||
class GridFieldAddClassesButton extends Object implements GridField_HTMLProvider, GridField_URLHandler {
|
||||
|
||||
private static $allowed_actions = array(
|
||||
'handleAdd'
|
||||
);
|
||||
|
||||
/**
|
||||
* Name of fragment to insert into
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $targetFragment;
|
||||
|
||||
/**
|
||||
* Button title
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $buttonName;
|
||||
|
||||
/**
|
||||
* Additonal CSS classes for the button
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $buttonClass = null;
|
||||
|
||||
/**
|
||||
* Class names
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $modelClasses = null;
|
||||
|
||||
/**
|
||||
* @param array $classes Class or list of classes to create.
|
||||
* If you enter more than one class, each click of the "add" button will create one of each
|
||||
* @param string $targetFragment The fragment to render the button into
|
||||
*/
|
||||
public function __construct($classes, $targetFragment = 'buttons-before-left') {
|
||||
parent::__construct();
|
||||
$this->setClasses($classes);
|
||||
$this->setFragment($targetFragment);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the button name
|
||||
*
|
||||
* @param string $name
|
||||
* @return $this
|
||||
*/
|
||||
public function setButtonName($name) {
|
||||
$this->buttonName = $name;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the button name
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getButtonName() {
|
||||
return $this->buttonName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the fragment name this button is rendered into.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getFragment() {
|
||||
return $this->targetFragment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the fragment name this button is rendered into.
|
||||
*
|
||||
* @param string $fragment
|
||||
* @return GridFieldAddNewInlineButton $this
|
||||
*/
|
||||
public function setFragment($fragment) {
|
||||
$this->targetFragment = $fragment;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get extra button class
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getButtonClass() {
|
||||
return $this->buttonClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets extra CSS classes for this button
|
||||
*
|
||||
* @param string $buttonClass
|
||||
* @return $this
|
||||
*/
|
||||
public function setButtonClass($buttonClass) {
|
||||
$this->buttonClass = $buttonClass;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get the classes of the objects to create
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getClasses() {
|
||||
return $this->modelClasses;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of classes which can be created, with checks for permissions.
|
||||
* Will fallback to the default model class for the given DataGrid
|
||||
*
|
||||
* @param DataGrid $grid
|
||||
* @return array
|
||||
*/
|
||||
public function getClassesCreate($grid) {
|
||||
// Get explicit or fallback class list
|
||||
$classes = $this->modelClasses;
|
||||
if(empty($classes) && $grid) {
|
||||
$classes = array($grid->getModelClass());
|
||||
}
|
||||
|
||||
// Filter out classes without permission
|
||||
return array_filter($classes, function($class) {
|
||||
return singleton($class)->canCreate();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the classes to create
|
||||
*
|
||||
* @param array $classes
|
||||
*/
|
||||
public function setClasses($classes) {
|
||||
if(!is_array($classes)) {
|
||||
$classes = $classes ? array($classes) : array();
|
||||
}
|
||||
$this->modelClasses = $classes;
|
||||
}
|
||||
|
||||
public function getHTMLFragments($grid) {
|
||||
// Check create permission
|
||||
$singleton = singleton($grid->getModelClass());
|
||||
if(!$singleton->canCreate()) {
|
||||
return array();
|
||||
}
|
||||
|
||||
// Get button name
|
||||
$buttonName = $this->getButtonName();
|
||||
if(!$buttonName) {
|
||||
// provide a default button name, can be changed by calling {@link setButtonName()} on this component
|
||||
$objectName = $singleton->i18n_singular_name();
|
||||
$buttonName = _t('GridField.Add', 'Add {name}', array('name' => $objectName));
|
||||
}
|
||||
|
||||
$data = new ArrayData(array(
|
||||
'Title' => $this->getButtonName(),
|
||||
'ButtonClass' => $this->getButtonClass(),
|
||||
'Link' => Controller::join_links($grid->Link(), $this->getAction())
|
||||
));
|
||||
|
||||
return array(
|
||||
$this->getFragment() => $data->renderWith(__CLASS__)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the action suburl for this component
|
||||
*
|
||||
* @return type
|
||||
*/
|
||||
protected function getAction() {
|
||||
$classes = implode('-', $this->getClasses());
|
||||
return Controller::join_links('add-classes', $classes);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getURLHandlers($grid) {
|
||||
return array(
|
||||
$this->getAction() => 'handleAdd'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles adding a new instance of a selected class.
|
||||
*
|
||||
* @param GridField $grid
|
||||
* @param SS_HTTPRequest $request
|
||||
*/
|
||||
public function handleAdd($grid, $request) {
|
||||
$classes = $this->getClassesCreate($grid);
|
||||
|
||||
if(empty($classes)) {
|
||||
throw new SS_HTTPResponse_Exception(400);
|
||||
}
|
||||
|
||||
// Add item to gridfield
|
||||
$list = $grid->getList();
|
||||
foreach($classes as $class) {
|
||||
$item = $class::create();
|
||||
$item->write();
|
||||
$list->add($item);
|
||||
}
|
||||
|
||||
// Return directly to the gridfield again
|
||||
return $grid
|
||||
->getForm()
|
||||
->getController()
|
||||
->redirectBack();
|
||||
}
|
||||
}
|
@ -75,31 +75,11 @@ class UserForm extends Form {
|
||||
* @return FieldList
|
||||
*/
|
||||
public function getFormFields() {
|
||||
$fields = new FieldList();
|
||||
$emptyStep = null; // Last empty step, which may or may not later have children
|
||||
|
||||
$fields = new UserFormsFieldList();
|
||||
$target = $fields;
|
||||
foreach ($this->controller->Fields() as $field) {
|
||||
// When we encounter a step, save it
|
||||
if ($field instanceof EditableFormStep) {
|
||||
$emptyStep = $field->getFormField();
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ensure that the last field is a step
|
||||
if($emptyStep) {
|
||||
// When we reach the first non-step field, any empty step will no longer be empty
|
||||
$fields->push($emptyStep);
|
||||
$emptyStep = null;
|
||||
|
||||
} elseif(! $fields->last()) {
|
||||
// If no steps have been saved yet, warn
|
||||
trigger_error('Missing first step in form', E_USER_WARNING);
|
||||
$fields->push(singleton('EditableFormStep')->getFormField());
|
||||
}
|
||||
|
||||
$fields->last()->push($field->getFormField());
|
||||
$target = $target->processNext($field);
|
||||
}
|
||||
|
||||
$this->extend('updateFormFields', $fields);
|
||||
return $fields;
|
||||
}
|
||||
|
@ -1,246 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Allows inline adding of classes with a default type
|
||||
*
|
||||
* Provides an alternative to GridFieldAddNewInlineButton, but allows you to set a classname
|
||||
*/
|
||||
class GridFieldAddItemInlineButton implements GridField_HTMLProvider, GridField_SaveHandler {
|
||||
|
||||
/**
|
||||
* Fragment id
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $fragment;
|
||||
|
||||
/**
|
||||
* Button title
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $title;
|
||||
|
||||
/**
|
||||
* Class name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $modelClass = null;
|
||||
|
||||
/**
|
||||
* Extra CSS classes for this row
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $extraClass = null;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param string $class Name of class to default to
|
||||
* @param string $fragment The fragment to render the button into
|
||||
*/
|
||||
public function __construct($class, $fragment = 'buttons-before-left') {
|
||||
$this->setClass($class);
|
||||
$this->setFragment($fragment);
|
||||
$this->setTitle(_t('GridFieldExtensions.ADD', 'Add'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the fragment name this button is rendered into.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getFragment() {
|
||||
return $this->fragment;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the fragment name this button is rendered into.
|
||||
*
|
||||
* @param string $fragment
|
||||
* @return GridFieldAddNewInlineButton $this
|
||||
*/
|
||||
public function setFragment($fragment) {
|
||||
$this->fragment = $fragment;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the button title text.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getTitle() {
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the button title text.
|
||||
*
|
||||
* @param string $title
|
||||
* @return GridFieldAddNewInlineButton $this
|
||||
*/
|
||||
public function setTitle($title) {
|
||||
$this->title = $title;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getHTMLFragments($grid) {
|
||||
if($grid->getList() && !singleton($grid->getModelClass())->canCreate()) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$fragment = $this->getFragment();
|
||||
|
||||
if(!$editable = $grid->getConfig()->getComponentByType('GridFieldEditableColumns')) {
|
||||
throw new Exception('Inline adding requires the editable columns component');
|
||||
}
|
||||
|
||||
Requirements::javascript(THIRDPARTY_DIR . '/javascript-templates/tmpl.js');
|
||||
GridFieldExtensions::include_requirements();
|
||||
Requirements::javascript(USERFORMS_DIR . '/javascript/GridFieldAddItemInlineButton.js');
|
||||
|
||||
$data = new ArrayData(array(
|
||||
'Title' => $this->getTitle(),
|
||||
'TemplateName' => $this->getRowTemplateName()
|
||||
));
|
||||
|
||||
return array(
|
||||
$fragment => $data->renderWith(__CLASS__),
|
||||
'after' => $this->getItemRowTemplate($grid, $editable)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Because getRowTemplate is private
|
||||
*
|
||||
* @param GridField $grid
|
||||
* @param GridFieldEditableColumns $editable
|
||||
* @return type
|
||||
*/
|
||||
protected function getItemRowTemplate(GridField $grid, GridFieldEditableColumns $editable) {
|
||||
$columns = new ArrayList();
|
||||
$handled = array_keys($editable->getDisplayFields($grid));
|
||||
|
||||
$record = Object::create($this->getClass());
|
||||
|
||||
$fields = $editable->getFields($grid, $record);
|
||||
|
||||
foreach($grid->getColumns() as $column) {
|
||||
$content = null;
|
||||
if(in_array($column, $handled)) {
|
||||
$field = $fields->dataFieldByName($column);
|
||||
if($field) {
|
||||
$field->setName(sprintf(
|
||||
'%s[%s][%s][{%%=o.num%%}][%s]', $grid->getName(), __CLASS__, $this->getClass(), $field->getName()
|
||||
));
|
||||
} else {
|
||||
$field = $fields->fieldByName($column);
|
||||
}
|
||||
if($field) {
|
||||
$content = $field->Field();
|
||||
}
|
||||
}
|
||||
|
||||
$attrs = '';
|
||||
|
||||
foreach($grid->getColumnAttributes($record, $column) as $attr => $val) {
|
||||
$attrs .= sprintf(' %s="%s"', $attr, Convert::raw2att($val));
|
||||
}
|
||||
|
||||
$columns->push(new ArrayData(array(
|
||||
'Content' => $content,
|
||||
'Attributes' => $attrs,
|
||||
'IsActions' => $column == 'Actions'
|
||||
)));
|
||||
}
|
||||
|
||||
$data = new ArrayData(array(
|
||||
'Columns' => $columns,
|
||||
'ExtraClass' => $this->getExtraClass(),
|
||||
'RecordClass' => $this->getClass(),
|
||||
'TemplateName' => $this->getRowTemplateName()
|
||||
));
|
||||
return $data->renderWith(__CLASS__ . '_Row');
|
||||
}
|
||||
|
||||
public function handleSave(GridField $grid, DataObjectInterface $record) {
|
||||
$list = $grid->getList();
|
||||
$value = $grid->Value();
|
||||
$class = $this->getClass();
|
||||
|
||||
if(!isset($value[__CLASS__][$class]) || !is_array($value[__CLASS__][$class])) {
|
||||
return;
|
||||
}
|
||||
|
||||
$editable = $grid->getConfig()->getComponentByType('GridFieldEditableColumns');
|
||||
$form = $editable->getForm($grid, $record);
|
||||
|
||||
if(!singleton($class)->canCreate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Process records matching the specified class
|
||||
foreach($value[__CLASS__][$class] as $fields) {
|
||||
$item = $class::create();
|
||||
$extra = array();
|
||||
|
||||
$form->loadDataFrom($fields, Form::MERGE_CLEAR_MISSING);
|
||||
$form->saveInto($item);
|
||||
|
||||
if($list instanceof ManyManyList) {
|
||||
$extra = array_intersect_key($form->getData(), (array) $list->getExtraFields());
|
||||
}
|
||||
|
||||
$item->write();
|
||||
$list->add($item, $extra);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the class of the object to create
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getClass() {
|
||||
return $this->modelClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the class to create
|
||||
*
|
||||
* @param string $class
|
||||
*/
|
||||
public function setClass($class) {
|
||||
$this->modelClass = $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get extra CSS classes for this row
|
||||
*
|
||||
* @return type
|
||||
*/
|
||||
public function getExtraClass() {
|
||||
return $this->extraClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets extra CSS classes for this row
|
||||
*
|
||||
* @param string $extraClass
|
||||
*/
|
||||
public function setExtraClass($extraClass) {
|
||||
$this->extraClass = $extraClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get name of item template
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getRowTemplateName() {
|
||||
return 'ss-gridfield-add-inline-template-' . $this->getClass();
|
||||
}
|
||||
}
|
@ -177,6 +177,8 @@ SQL;
|
||||
$config->addComponent(new GridFieldDetailForm());
|
||||
$config->addComponent($export = new GridFieldExportButton());
|
||||
$config->addComponent($print = new GridFieldPrintButton());
|
||||
|
||||
Requirements::javascript(USERFORMS_DIR . '/javascript/Gridfield.js');
|
||||
|
||||
/**
|
||||
* Support for {@link https://github.com/colymba/GridFieldBulkEditingTools}
|
||||
@ -284,6 +286,14 @@ SQL;
|
||||
|
||||
DB::alteration_message('Migrated userforms', 'changed');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Validate formfields
|
||||
*/
|
||||
public function getCMSValidator() {
|
||||
return new UserFormValidator();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
80
code/model/editableformfields/EditableFieldGroup.php
Normal file
80
code/model/editableformfields/EditableFieldGroup.php
Normal file
@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Specifies that this ends a group of fields
|
||||
*/
|
||||
class EditableFieldGroup extends EditableFormField {
|
||||
|
||||
private static $has_one = array(
|
||||
'End' => 'EditableFieldGroupEnd'
|
||||
);
|
||||
|
||||
/**
|
||||
* Disable selection of group class
|
||||
*
|
||||
* @config
|
||||
* @var bool
|
||||
*/
|
||||
private static $hidden = true;
|
||||
|
||||
public function getCMSFields() {
|
||||
$fields = parent::getCMSFields();
|
||||
$fields->removeByName(array('MergeField', 'Default', 'Validation', 'DisplayRules'));
|
||||
return $fields;
|
||||
}
|
||||
|
||||
public function getCMSTitle() {
|
||||
return _t(
|
||||
'EditableFieldGroupEnd.FIELD_GROUP_START',
|
||||
'Start of {group}',
|
||||
array(
|
||||
'group' => $this->Title ?: 'group'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function getInlineClassnameField($column, $fieldClasses) {
|
||||
return new LabelField($column, $this->CMSTitle);
|
||||
}
|
||||
|
||||
public function showInReports() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getFormField() {
|
||||
$field = UserFormsGroupField::create()
|
||||
->setTitle($this->EscapedTitle ?: false);
|
||||
$this->doUpdateFormField($field);
|
||||
return $field;
|
||||
}
|
||||
|
||||
protected function updateFormField($field) {
|
||||
// set the right title on this field
|
||||
if($this->RightTitle) {
|
||||
// Since this field expects raw html, safely escape the user data prior
|
||||
$field->setRightTitle(Convert::raw2xml($this->RightTitle));
|
||||
}
|
||||
|
||||
// if this field has an extra class
|
||||
if($field->ExtraClass) {
|
||||
$field->addExtraClass($field->ExtraClass);
|
||||
}
|
||||
}
|
||||
|
||||
protected function onBeforeDelete() {
|
||||
parent::onBeforeDelete();
|
||||
|
||||
// Ensures EndID is lazy-loaded for onAfterDelete
|
||||
$this->EndID;
|
||||
}
|
||||
|
||||
protected function onAfterDelete() {
|
||||
parent::onAfterDelete();
|
||||
|
||||
// Delete end
|
||||
if(($end = $this->End()) && $end->exists()) {
|
||||
$end->delete();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
86
code/model/editableformfields/EditableFieldGroupEnd.php
Normal file
86
code/model/editableformfields/EditableFieldGroupEnd.php
Normal file
@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Specifies that this ends a group of fields
|
||||
*/
|
||||
class EditableFieldGroupEnd extends EditableFormField {
|
||||
|
||||
private static $belongs_to = array(
|
||||
'Group' => 'EditableFieldGroup'
|
||||
);
|
||||
|
||||
/**
|
||||
* Disable selection of group class
|
||||
*
|
||||
* @config
|
||||
* @var bool
|
||||
*/
|
||||
private static $hidden = true;
|
||||
|
||||
public function getCMSTitle() {
|
||||
$group = $this->Group();
|
||||
return _t(
|
||||
'EditableFieldGroupEnd.FIELD_GROUP_END',
|
||||
'End of {group}',
|
||||
array(
|
||||
'group' => ($group && $group->exists() && $group->Title) ? $group->Title : 'group'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
public function getCMSFields() {
|
||||
$fields = parent::getCMSFields();
|
||||
$fields->removeByName(array('MergeField', 'Default', 'Validation', 'DisplayRules'));
|
||||
return $fields;
|
||||
}
|
||||
|
||||
public function getInlineClassnameField($column, $fieldClasses) {
|
||||
return new LabelField($column, $this->CMSTitle);
|
||||
}
|
||||
|
||||
public function getInlineTitleField($column) {
|
||||
return HiddenField::create($column);
|
||||
}
|
||||
|
||||
public function getFormField() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function showInReports() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function onAfterWrite() {
|
||||
parent::onAfterWrite();
|
||||
|
||||
// If this is not attached to a group, find the first group prior to this
|
||||
// with no end attached
|
||||
$group = $this->Group();
|
||||
if(!($group && $group->exists()) && $this->ParentID) {
|
||||
$group = EditableFieldGroup::get()
|
||||
->filter(array(
|
||||
'ParentID' => $this->ParentID,
|
||||
'Sort:LessThanOrEqual' => $this->Sort
|
||||
))
|
||||
->where('"EditableFieldGroup"."EndID" IS NULL OR "EditableFieldGroup"."EndID" = 0')
|
||||
->sort('"Sort" DESC')
|
||||
->first();
|
||||
|
||||
// When a group is found, attach it to this end
|
||||
if($group) {
|
||||
$group->EndID = $this->ID;
|
||||
$group->write();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function onAfterDelete() {
|
||||
parent::onAfterDelete();
|
||||
|
||||
// Delete group
|
||||
if(($group = $this->Group()) && $group->exists()) {
|
||||
$group->delete();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -8,6 +8,14 @@
|
||||
*/
|
||||
class EditableFormField extends DataObject {
|
||||
|
||||
/**
|
||||
* Set to true to hide from class selector
|
||||
*
|
||||
* @config
|
||||
* @var bool
|
||||
*/
|
||||
private static $hidden = false;
|
||||
|
||||
/**
|
||||
* Default sort order
|
||||
*
|
||||
@ -442,6 +450,10 @@ class EditableFormField extends DataObject {
|
||||
return Convert::raw2xml($this->Title);
|
||||
}
|
||||
|
||||
public function getCMSTitle() {
|
||||
return $this->i18n_singular_name() . ' (' . $this->Title . ')';
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since version 4.0
|
||||
*/
|
||||
@ -616,4 +628,27 @@ class EditableFormField extends DataObject {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the formfield to use when editing this inline in gridfield
|
||||
*
|
||||
* @param string $column name of column
|
||||
* @param array $fieldClasses List of allowed classnames if this formfield has a selectable class
|
||||
* @return FormField
|
||||
*/
|
||||
public function getInlineClassnameField($column, $fieldClasses) {
|
||||
return DropdownField::create($column, false, $fieldClasses);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the formfield to use when editing the title inline
|
||||
*
|
||||
* @param string $column
|
||||
* @return FormField
|
||||
*/
|
||||
public function getInlineTitleField($column) {
|
||||
return TextField::create($column, false)
|
||||
->setAttribute('placeholder', _t('EditableFormField.TITLE', 'Title'))
|
||||
->setAttribute('data-placeholder', _t('EditableFormField.TITLE', 'Title'));
|
||||
}
|
||||
}
|
||||
|
@ -6,17 +6,17 @@
|
||||
*/
|
||||
class EditableFormStep extends EditableFormField {
|
||||
|
||||
/**
|
||||
* @config
|
||||
* @var string
|
||||
*/
|
||||
private static $singular_name = 'Step';
|
||||
private static $singular_name = 'Page Break';
|
||||
|
||||
private static $plural_name = 'Page Breaks';
|
||||
|
||||
/**
|
||||
* Disable selection of step class
|
||||
*
|
||||
* @config
|
||||
* @var string
|
||||
* @var bool
|
||||
*/
|
||||
private static $plural_name = 'Steps';
|
||||
private static $hidden = true;
|
||||
|
||||
/**
|
||||
* @return FieldList
|
||||
@ -24,10 +24,7 @@ class EditableFormStep extends EditableFormField {
|
||||
public function getCMSFields() {
|
||||
$fields = parent::getCMSFields();
|
||||
|
||||
$fields->removeByName('MergeField');
|
||||
$fields->removeByName('Default');
|
||||
$fields->removeByName('Validation');
|
||||
$fields->removeByName('CustomRules');
|
||||
$fields->removeByName(array('MergeField', 'Default', 'Validation', 'DisplayRules'));
|
||||
|
||||
return $fields;
|
||||
}
|
||||
@ -36,7 +33,7 @@ class EditableFormStep extends EditableFormField {
|
||||
* @return FormField
|
||||
*/
|
||||
public function getFormField() {
|
||||
$field = CompositeField::create()
|
||||
$field = UserFormsStepField::create()
|
||||
->setTitle($this->EscapedTitle);
|
||||
$this->doUpdateFormField($field);
|
||||
return $field;
|
||||
@ -55,4 +52,19 @@ class EditableFormStep extends EditableFormField {
|
||||
public function showInReports() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getInlineClassnameField($column, $fieldClasses) {
|
||||
return new LabelField(
|
||||
$column,
|
||||
$this->CMSTitle
|
||||
);
|
||||
}
|
||||
|
||||
public function getCMSTitle() {
|
||||
$title = $this->i18n_singular_name();
|
||||
if($this->Title) {
|
||||
$title .= ' (' . $this->Title . ')';
|
||||
}
|
||||
return $title;
|
||||
}
|
||||
}
|
||||
|
12
config.rb
Normal file
12
config.rb
Normal file
@ -0,0 +1,12 @@
|
||||
require 'compass/import-once/activate'
|
||||
# Require any additional compass plugins here.
|
||||
|
||||
# Set this to the root of your project when deployed:
|
||||
http_path = "/"
|
||||
css_dir = "css"
|
||||
sass_dir = "scss"
|
||||
images_dir = "images"
|
||||
javascripts_dir = "javascript"
|
||||
|
||||
line_comments = false
|
||||
|
@ -1,38 +1,37 @@
|
||||
/**
|
||||
* Lightweight base styles for the front-end form.
|
||||
*/
|
||||
.userform-progress .step-buttons,
|
||||
.step-navigation .step-buttons {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.userform-progress .step-buttons {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.userform-progress .step-button-jump {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.userform-progress .step-buttons .step-button-wrapper,
|
||||
.step-navigation .step-buttons .step-button-wrapper {
|
||||
display: inline-block;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.userform-progress .progress {
|
||||
position: relative;
|
||||
height: 1em;
|
||||
background: #eee;
|
||||
position: relative;
|
||||
height: 1em;
|
||||
background: #eee;
|
||||
}
|
||||
.userform-progress .progress .progress-bar {
|
||||
position: absolute;
|
||||
height: 1em;
|
||||
background: #666;
|
||||
}
|
||||
.userform-progress .step-buttons {
|
||||
margin-left: 0;
|
||||
position: relative;
|
||||
}
|
||||
.userform-progress .step-buttons .step-button-wrapper {
|
||||
display: inline-block;
|
||||
list-style-type: none;
|
||||
}
|
||||
.userform-progress .step-button-jump {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.userform-progress .progress .progress-bar {
|
||||
position: absolute;
|
||||
height: 1em;
|
||||
background: #666;
|
||||
.step-navigation .step-buttons {
|
||||
margin-left: 0;
|
||||
}
|
||||
.step-navigation .step-buttons .step-button-wrapper {
|
||||
display: inline-block;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.userform {
|
||||
clear: both;
|
||||
clear: both;
|
||||
}
|
||||
|
@ -1,24 +1,28 @@
|
||||
|
||||
.cms .ss-gridfield > div.ss-gridfield-buttonrow-before {
|
||||
margin-bottom: 10px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/**
|
||||
* Styles for page steps
|
||||
* Styles for cms
|
||||
*/
|
||||
.cms table.ss-gridfield-table .ss-gridfield-inline-new.uf-gridfield-steprow,
|
||||
.cms table.ss-gridfield-table .ss-gridfield-item[data-class='EditableFormStep'],
|
||||
.cms table.ss-gridfield-table .ss-gridfield-inline-new.uf-gridfield-steprow:hover,
|
||||
.cms table.ss-gridfield-table .ss-gridfield-item[data-class='EditableFormStep']:hover
|
||||
{
|
||||
background: #dae2e7;
|
||||
.cms .ss-gridfield > div.ss-gridfield-buttonrow-before {
|
||||
margin-bottom: 10px;
|
||||
overflow: auto;
|
||||
}
|
||||
.cms table.ss-gridfield-table .ss-gridfield-item[data-class='EditableFormStep'], .cms table.ss-gridfield-table .ss-gridfield-item[data-class='EditableFormStep']:hover {
|
||||
background: #dae2e7;
|
||||
}
|
||||
.cms table.ss-gridfield-table .ss-gridfield-item[data-class='EditableFormStep'] label {
|
||||
font-weight: bold;
|
||||
color: #444;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
.cms table.ss-gridfield-table .ss-gridfield-item[data-class^='EditableFieldGroup'], .cms table.ss-gridfield-table .ss-gridfield-item[data-class^='EditableFieldGroup']:hover {
|
||||
background: #E7EFF4;
|
||||
}
|
||||
.cms table.ss-gridfield-table .ss-gridfield-item[data-class^='EditableFieldGroup'] label {
|
||||
font-weight: bold;
|
||||
}
|
||||
.cms table.ss-gridfield-table .ss-gridfield-item[data-class='EditableFieldGroupEnd'] .col-Title input {
|
||||
display: none;
|
||||
}
|
||||
.cms table.ss-gridfield-table .ss-gridfield-item.inFieldGroup .col-reorder,
|
||||
.cms table.ss-gridfield-table .ss-gridfield-item.inFieldGroup .handle {
|
||||
background: #E7EFF4;
|
||||
}
|
||||
|
||||
.cms table.ss-gridfield-table .ss-gridfield-inline-new.uf-gridfield-steprow label,
|
||||
.cms table.ss-gridfield-table .ss-gridfield-item[data-class='EditableFormStep'] label
|
||||
{
|
||||
font-weight: bold;
|
||||
color: #393939;
|
||||
font-size: 1.1em;
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
(function($) {
|
||||
$.entwine("ss", function($) {
|
||||
// See gridfieldextensions/javascript/GridFieldExtensions.js
|
||||
|
||||
$(".ss-gridfield.ss-gridfield-editable").entwine({
|
||||
onaddnewiteminline: function(e, template) {
|
||||
var tmpl = window.tmpl;
|
||||
var row = this.find("." + template);
|
||||
var num = this.data("add-inline-num") || 1;
|
||||
|
||||
tmpl.cache[template] = tmpl(row.html());
|
||||
|
||||
this.find("tbody").append(tmpl(template, { num: num }));
|
||||
this.find(".ss-gridfield-no-items").hide();
|
||||
this.data("add-inline-num", num + 1);
|
||||
}
|
||||
});
|
||||
|
||||
$(".ss-gridfield-add-new-item-inline").entwine({
|
||||
onclick: function() {
|
||||
// Get custom class from button
|
||||
var template = this.data('template');
|
||||
this.getGridField().trigger("addnewiteminline", template);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
$(".ss-gridfield-delete-inline").entwine({
|
||||
onclick: function() {
|
||||
var msg = ss.i18n._t("GridFieldExtensions.CONFIRMDEL", "Are you sure you want to delete this?");
|
||||
|
||||
if(confirm(msg)) {
|
||||
this.parents("tr").remove();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
})(jQuery);
|
23
javascript/Gridfield.js
Normal file
23
javascript/Gridfield.js
Normal file
@ -0,0 +1,23 @@
|
||||
/**
|
||||
* form builder behaviour.
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
$.entwine('ss', function($) {
|
||||
$(".ss-gridfield-orderable tbody").entwine({
|
||||
onmatch: function() {
|
||||
this._super();
|
||||
|
||||
this.find('.ss-gridfield-item')
|
||||
.removeClass('inFieldGroup');
|
||||
|
||||
this.find('.ss-gridfield-item[data-class="EditableFieldGroup"]')
|
||||
.nextUntil('.ss-gridfield-item[data-class="EditableFieldGroupEnd"]')
|
||||
.addClass('inFieldGroup');
|
||||
},
|
||||
onunmatch: function () {
|
||||
this._super();
|
||||
}
|
||||
});
|
||||
});
|
||||
}(jQuery));
|
@ -322,6 +322,7 @@ jQuery(function ($) {
|
||||
|
||||
this.$el = element instanceof jQuery ? element : $(element);
|
||||
this.$buttons = this.$el.find('.step-button-jump');
|
||||
this.$jsAlign = this.$el.find('.js-align');
|
||||
|
||||
// Update the progress bar when 'step' buttons are clicked.
|
||||
this.$buttons.each(function (i, stepButton) {
|
||||
@ -337,9 +338,9 @@ jQuery(function ($) {
|
||||
});
|
||||
|
||||
// Spaces out the steps below progress bar evenly
|
||||
this.$buttons.each(function (index, button) {
|
||||
this.$jsAlign.each(function (index, button) {
|
||||
var $button = $(button),
|
||||
leftPercent = (100 / (self.$buttons.length - 1) * index + '%'),
|
||||
leftPercent = (100 / (self.$jsAlign.length - 1) * index + '%'),
|
||||
buttonOffset = -1 * ($button.innerWidth() / 2);
|
||||
|
||||
$button.css({left: leftPercent, marginLeft: buttonOffset});
|
||||
|
47
scss/UserForm.scss
Normal file
47
scss/UserForm.scss
Normal file
@ -0,0 +1,47 @@
|
||||
/**
|
||||
* Lightweight base styles for the front-end form.
|
||||
*/
|
||||
|
||||
.userform-progress {
|
||||
.progress {
|
||||
position: relative;
|
||||
height: 1em;
|
||||
background: #eee;
|
||||
|
||||
.progress-bar {
|
||||
position: absolute;
|
||||
height: 1em;
|
||||
background: #666;
|
||||
}
|
||||
}
|
||||
|
||||
.step-buttons {
|
||||
margin-left: 0;
|
||||
position: relative;
|
||||
|
||||
.step-button-wrapper {
|
||||
display: inline-block;
|
||||
list-style-type: none;
|
||||
}
|
||||
}
|
||||
|
||||
.step-button-jump {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.step-navigation {
|
||||
.step-buttons {
|
||||
margin-left: 0;
|
||||
|
||||
.step-button-wrapper {
|
||||
display: inline-block;
|
||||
list-style-type: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.userform {
|
||||
clear: both;
|
||||
}
|
50
scss/UserForm_cms.scss
Normal file
50
scss/UserForm_cms.scss
Normal file
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* Styles for cms
|
||||
*/
|
||||
|
||||
.cms {
|
||||
.ss-gridfield > div.ss-gridfield-buttonrow-before {
|
||||
margin-bottom: 10px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
table.ss-gridfield-table {
|
||||
.ss-gridfield-item[data-class='EditableFormStep'] {
|
||||
&, &:hover {
|
||||
background: #dae2e7;
|
||||
}
|
||||
|
||||
label {
|
||||
font-weight: bold;
|
||||
color: #444;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
}
|
||||
|
||||
.ss-gridfield-item[data-class^='EditableFieldGroup'] {
|
||||
&, &:hover {
|
||||
background: #E7EFF4;
|
||||
}
|
||||
|
||||
label {
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.ss-gridfield-item[data-class='EditableFieldGroupEnd'] {
|
||||
.col-Title input {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.ss-gridfield-item.inFieldGroup {
|
||||
.col-reorder,
|
||||
.handle {
|
||||
background: #E7EFF4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
<ul class="step-buttons">
|
||||
<% loop $NumberOfSteps %>
|
||||
<li class="step-button-wrapper<% if $Pos == '1' %> current<% end_if %>">
|
||||
<button class="step-button-jump" disabled="disabled">$Pos</button>
|
||||
<button class="step-button-jump js-align" disabled="disabled">$Pos</button><!-- Remove js-align class to remove javascript positioning -->
|
||||
</li>
|
||||
<% end_loop %>
|
||||
<ul>
|
||||
|
3
templates/gridfield/GridFieldAddClassesButton.ss
Normal file
3
templates/gridfield/GridFieldAddClassesButton.ss
Normal file
@ -0,0 +1,3 @@
|
||||
<a href="$Link.ATT" class="action action-detail ss-ui-button ui-button ui-widget ui-state-default ui-corner-all new new-link $ButtonClass.ATT" data-icon="add">
|
||||
$Title.XML
|
||||
</a>
|
@ -1,3 +0,0 @@
|
||||
<button href="$Link" class="ss-gridfield-add-new-item-inline ss-ui-button" data-icon="add" data-template="$TemplateName.ATT">
|
||||
$Title
|
||||
</button>
|
@ -1,13 +0,0 @@
|
||||
<script type="text/x-tmpl" class="$TemplateName">
|
||||
<tr class="ss-gridfield-inline-new $ExtraClass.ATT">
|
||||
<% loop $Columns %>
|
||||
<% if $IsActions %>
|
||||
<td$Attributes>
|
||||
<button class="ss-gridfield-delete-inline gridfield-button-delete ss-ui-button" data-icon="cross-circle"></button>
|
||||
</td>
|
||||
<% else %>
|
||||
<td$Attributes>$Content</td>
|
||||
<% end_if %>
|
||||
<% end_loop %>
|
||||
</tr>
|
||||
</script>
|
Loading…
Reference in New Issue
Block a user