Convert to psr-2

This commit is contained in:
Damian Mooyman 2016-07-21 17:53:59 +12:00
parent e620576e97
commit 57048fd901
56 changed files with 6119 additions and 5737 deletions

View File

@ -1,7 +1,7 @@
<?php <?php
if(!defined('USERFORMS_DIR')) { if (!defined('USERFORMS_DIR')) {
define('USERFORMS_DIR', basename(__DIR__)); define('USERFORMS_DIR', basename(__DIR__));
} }
Deprecation::notification_version('3.0', 'userforms'); Deprecation::notification_version('3.0', 'userforms');

View File

@ -7,113 +7,118 @@
* *
* @package userforms * @package userforms
*/ */
class UserFormsGridFieldFilterHeader extends GridFieldFilterHeader { class UserFormsGridFieldFilterHeader extends GridFieldFilterHeader
{
/** /**
* A map of name => value of columns from all submissions * A map of name => value of columns from all submissions
* @var array * @var array
*/ */
protected $columns; protected $columns;
public function setColumns($columns) { public function setColumns($columns)
$this->columns = $columns; {
} $this->columns = $columns;
}
public function handleAction(GridField $gridField, $actionName, $arguments, $data) { public function handleAction(GridField $gridField, $actionName, $arguments, $data)
if(!$this->checkDataType($gridField->getList())) { {
return; if (!$this->checkDataType($gridField->getList())) {
} return;
}
if($actionName === 'filter') { if ($actionName === 'filter') {
$gridField->State->UserFormsGridField = array( $gridField->State->UserFormsGridField = array(
'filter' => isset($data['FieldNameFilter']) ? $data['FieldNameFilter'] : null, 'filter' => isset($data['FieldNameFilter']) ? $data['FieldNameFilter'] : null,
'value' => isset($data['FieldValue']) ? $data['FieldValue'] : null, 'value' => isset($data['FieldValue']) ? $data['FieldValue'] : null,
'start' => isset($data['StartFilter']) ? $data['StartFilter'] : null, 'start' => isset($data['StartFilter']) ? $data['StartFilter'] : null,
'end' => isset($data['EndFilter']) ? $data['EndFilter'] : null 'end' => isset($data['EndFilter']) ? $data['EndFilter'] : null
); );
} }
} }
public function getHTMLFragments($gridField) { public function getHTMLFragments($gridField)
$fields = new ArrayList(); {
$state = $gridField->State->UserFormsGridField; $fields = new ArrayList();
$state = $gridField->State->UserFormsGridField;
$selectedField = $state->filter; $selectedField = $state->filter;
$selectedValue = $state->value; $selectedValue = $state->value;
// show dropdown of all the fields available from the submitted form fields // show dropdown of all the fields available from the submitted form fields
// that have been saved. Takes the titles from the currently live form. // that have been saved. Takes the titles from the currently live form.
$columnField = new DropdownField('FieldNameFilter', ''); $columnField = new DropdownField('FieldNameFilter', '');
$columnField->setSource($this->columns); $columnField->setSource($this->columns);
$columnField->setEmptyString(_t('UserFormsGridFieldFilterHeader.FILTERSUBMISSIONS', 'Filter Submissions..')); $columnField->setEmptyString(_t('UserFormsGridFieldFilterHeader.FILTERSUBMISSIONS', 'Filter Submissions..'));
$columnField->setHasEmptyDefault(true); $columnField->setHasEmptyDefault(true);
$columnField->setValue($selectedField); $columnField->setValue($selectedField);
$valueField = new TextField('FieldValue', '', $selectedValue); $valueField = new TextField('FieldValue', '', $selectedValue);
$columnField->addExtraClass('ss-gridfield-sort'); $columnField->addExtraClass('ss-gridfield-sort');
$columnField->addExtraClass('no-change-track'); $columnField->addExtraClass('no-change-track');
$valueField->addExtraClass('ss-gridfield-sort'); $valueField->addExtraClass('ss-gridfield-sort');
$valueField->addExtraClass('no-change-track'); $valueField->addExtraClass('no-change-track');
$valueField->setAttribute( $valueField->setAttribute(
'placeholder', 'placeholder',
_t('UserFormsGridFieldFilterHeader.WHEREVALUEIS', 'where value is..' _t('UserFormsGridFieldFilterHeader.WHEREVALUEIS', 'where value is..'
)); ));
$fields->push(new FieldGroup(new CompositeField( $fields->push(new FieldGroup(new CompositeField(
$columnField, $columnField,
$valueField $valueField
))); )));
$fields->push(new FieldGroup(new CompositeField( $fields->push(new FieldGroup(new CompositeField(
$start = new DateField('StartFilter', _t('UserFormsGridFieldFilterHeader.FROM', 'From')), $start = new DateField('StartFilter', _t('UserFormsGridFieldFilterHeader.FROM', 'From')),
$end = new DateField('EndFilter', _t('UserFormsGridFieldFilterHeader.TILL', 'Till')) $end = new DateField('EndFilter', _t('UserFormsGridFieldFilterHeader.TILL', 'Till'))
))); )));
foreach(array($start, $end) as $date) { foreach (array($start, $end) as $date) {
$date->setConfig('showcalendar', true); $date->setConfig('showcalendar', true);
$date->setConfig('dateformat', 'y-mm-dd'); $date->setConfig('dateformat', 'y-mm-dd');
$date->setConfig('datavalueformat', 'y-mm-dd'); $date->setConfig('datavalueformat', 'y-mm-dd');
$date->addExtraClass('no-change-track'); $date->addExtraClass('no-change-track');
} }
$end->setValue($state->end); $end->setValue($state->end);
$start->setValue($state->start); $start->setValue($state->start);
$fields->push($actions = new FieldGroup( $fields->push($actions = new FieldGroup(
GridField_FormAction::create($gridField, 'filter', false, 'filter', null) GridField_FormAction::create($gridField, 'filter', false, 'filter', null)
->addExtraClass('ss-gridfield-button-filter') ->addExtraClass('ss-gridfield-button-filter')
->setAttribute('title', _t('GridField.Filter', "Filter")) ->setAttribute('title', _t('GridField.Filter', "Filter"))
->setAttribute('id', 'action_filter_' . $gridField->getModelClass() . '_' . $columnField), ->setAttribute('id', 'action_filter_' . $gridField->getModelClass() . '_' . $columnField),
GridField_FormAction::create($gridField, 'reset', false, 'reset', null) GridField_FormAction::create($gridField, 'reset', false, 'reset', null)
->addExtraClass('ss-gridfield-button-close') ->addExtraClass('ss-gridfield-button-close')
->setAttribute('title', _t('GridField.ResetFilter', "Reset")) ->setAttribute('title', _t('GridField.ResetFilter', "Reset"))
->setAttribute('id', 'action_reset_' . $gridField->getModelClass() . '_' . $columnField) ->setAttribute('id', 'action_reset_' . $gridField->getModelClass() . '_' . $columnField)
) )
); );
$actions->addExtraClass('filter-buttons'); $actions->addExtraClass('filter-buttons');
$actions->addExtraClass('no-change-track'); $actions->addExtraClass('no-change-track');
$forTemplate = new ArrayData(array( $forTemplate = new ArrayData(array(
'Fields' => $fields 'Fields' => $fields
)); ));
return array( return array(
'header' => $forTemplate->renderWith('GridFieldFilterHeader_Row') 'header' => $forTemplate->renderWith('GridFieldFilterHeader_Row')
); );
} }
public function getManipulatedData(GridField $gridField, SS_List $dataList) { public function getManipulatedData(GridField $gridField, SS_List $dataList)
$state = $gridField->State; {
$state = $gridField->State;
if($filter = $state->UserFormsGridField->toArray()) { if ($filter = $state->UserFormsGridField->toArray()) {
if(isset($filter['filter']) && $filter['filter'] && isset($filter['value']) && $filter['value']) { if (isset($filter['filter']) && $filter['filter'] && isset($filter['value']) && $filter['value']) {
$dataList = $dataList->where(sprintf(" $dataList = $dataList->where(sprintf("
SELECT COUNT(*) FROM SubmittedFormField SELECT COUNT(*) FROM SubmittedFormField
WHERE ( WHERE (
ParentID = SubmittedForm.ID AND ParentID = SubmittedForm.ID AND
@ -121,24 +126,24 @@ class UserFormsGridFieldFilterHeader extends GridFieldFilterHeader {
Value LIKE '%s' Value LIKE '%s'
) > 0", ) > 0",
Convert::raw2sql($filter['filter']), Convert::raw2sql($filter['filter']),
Convert::raw2sql($filter['value']) Convert::raw2sql($filter['value'])
)); ));
} }
if(isset($filter['start']) && $filter['start']) { if (isset($filter['start']) && $filter['start']) {
$dataList = $dataList->filter(array( $dataList = $dataList->filter(array(
'Created:GreaterThan' => $filter['start'] 'Created:GreaterThan' => $filter['start']
)); ));
} }
if(isset($filter['end']) && $filter['end']) { if (isset($filter['end']) && $filter['end']) {
$dataList = $dataList->filter(array( $dataList = $dataList->filter(array(
'Created:LessThan' => $filter['end'] 'Created:LessThan' => $filter['end']
)); ));
} }
} }
return $dataList; return $dataList;
} }
} }

View File

@ -5,140 +5,147 @@
* *
* {@see EditableFileField} * {@see EditableFileField}
*/ */
class SecureEditableFileField extends DataExtension { class SecureEditableFileField extends DataExtension
{
/** /**
* Path to secure files location under assets * Path to secure files location under assets
* *
* @config * @config
* @var type * @var type
*/ */
private static $secure_folder_name = 'SecureUploads'; private static $secure_folder_name = 'SecureUploads';
/** /**
* Disable file security if a user-defined mechanism is in place * Disable file security if a user-defined mechanism is in place
* *
* @config * @config
* @var bool * @var bool
*/ */
private static $disable_security = false; private static $disable_security = false;
/* /*
* Check if file security is enabled * Check if file security is enabled
* *
* @return bool * @return bool
*/ */
public function getIsSecurityEnabled() { public function getIsSecurityEnabled()
// Skip if requested {
if($this->owner->config()->disable_security) { // Skip if requested
return false; if ($this->owner->config()->disable_security) {
} return false;
}
// Check for necessary security module // Check for necessary security module
if(!class_exists('SecureFileExtension')) { if (!class_exists('SecureFileExtension')) {
trigger_error('SecureEditableFileField requires secureassets module', E_USER_WARNING); trigger_error('SecureEditableFileField requires secureassets module', E_USER_WARNING);
return false; return false;
} }
return true; return true;
} }
public function requireDefaultRecords() { public function requireDefaultRecords()
// Skip if disabled {
if(!$this->getIsSecurityEnabled()) { // Skip if disabled
return; if (!$this->getIsSecurityEnabled()) {
} return;
}
// Update all instances of editablefilefield which do NOT have a secure folder assigned // Update all instances of editablefilefield which do NOT have a secure folder assigned
foreach(EditableFileField::get() as $fileField) { foreach (EditableFileField::get() as $fileField) {
// Skip if secured // Skip if secured
if($fileField->getIsSecure()) { if ($fileField->getIsSecure()) {
continue; continue;
} }
// Force this field to secure itself on write // Force this field to secure itself on write
$fileField->write(false, false, true); $fileField->write(false, false, true);
DB::alteration_message( DB::alteration_message(
"Restricting editable file field \"{$fileField->Title}\" to secure folder", "Restricting editable file field \"{$fileField->Title}\" to secure folder",
"changed" "changed"
); );
} }
} }
/** /**
* Secure this field before saving * Secure this field before saving
*/ */
public function onBeforeWrite() { public function onBeforeWrite()
$this->makeSecure(); {
} $this->makeSecure();
}
/** /**
* Ensure this field is secured, but does not write changes to the database * Ensure this field is secured, but does not write changes to the database
*/ */
public function makeSecure() { public function makeSecure()
// Skip if disabled or already secure {
if(!$this->getIsSecurityEnabled() || $this->owner->getIsSecure()) { // Skip if disabled or already secure
return; if (!$this->getIsSecurityEnabled() || $this->owner->getIsSecure()) {
} return;
}
// Ensure folder exists // Ensure folder exists
$folder = $this->owner->Folder(); $folder = $this->owner->Folder();
if(!$folder || !$folder->exists()) { if (!$folder || !$folder->exists()) {
// Create new folder in default location // Create new folder in default location
$folder = Folder::find_or_make($this->owner->config()->secure_folder_name); $folder = Folder::find_or_make($this->owner->config()->secure_folder_name);
$this->owner->FolderID = $folder->ID; $this->owner->FolderID = $folder->ID;
} elseif ($this->isFolderSecured($folder)) {
// If folder exists and is secure stop
return;
}
} elseif($this->isFolderSecured($folder)) { // Make secure
// If folder exists and is secure stop $folder->CanViewType = 'OnlyTheseUsers';
return; $folder->ViewerGroups()->add($this->findAdminGroup());
} $folder->write();
}
// Make secure /**
$folder->CanViewType = 'OnlyTheseUsers'; * Find target group to record
$folder->ViewerGroups()->add($this->findAdminGroup()); *
$folder->write(); * @return Group
} */
protected function findAdminGroup()
{
singleton('Group')->requireDefaultRecords();
return Permission::get_groups_by_permission('ADMIN')->First();
}
/** /**
* Find target group to record * Determine if the field is secure
* *
* @return Group * @return bool
*/ */
protected function findAdminGroup() { public function getIsSecure()
singleton('Group')->requireDefaultRecords(); {
return Permission::get_groups_by_permission('ADMIN')->First(); return $this->isFolderSecured($this->owner->Folder());
} }
/** /**
* Determine if the field is secure * Check if a Folder object is secure
* *
* @return bool * @param Folder $folder
*/ * @return boolean
public function getIsSecure() { */
return $this->isFolderSecured($this->owner->Folder()); protected function isFolderSecured($folder)
} {
if (! ($folder instanceof Folder) || !$folder->exists()) {
return false;
}
/** switch ($folder->CanViewType) {
* Check if a Folder object is secure case 'OnlyTheseUsers':
* return true;
* @param Folder $folder case 'Inherit':
* @return boolean $parent = $folder->Parent();
*/ return $parent && $parent->exists() && $this->isFolderSecured($parent);
protected function isFolderSecured($folder) { case 'Anyone':
if(! ($folder instanceof Folder) || !$folder->exists()) { case 'LoggedInUsers':
return false; default:
} return false;
}
switch($folder->CanViewType) { }
case 'OnlyTheseUsers':
return true;
case 'Inherit':
$parent = $folder->Parent();
return $parent && $parent->exists() && $this->isFolderSecured($parent);
case 'Anyone':
case 'LoggedInUsers':
default:
return false;
}
}
} }

View File

@ -3,235 +3,245 @@
/** /**
* @package userforms * @package userforms
*/ */
class UserFormFieldEditorExtension extends DataExtension { class UserFormFieldEditorExtension extends DataExtension
{
/** /**
* @var array * @var array
*/ */
private static $has_many = array( private static $has_many = array(
'Fields' => 'EditableFormField' 'Fields' => 'EditableFormField'
); );
/** /**
* Adds the field editor to the page. * Adds the field editor to the page.
* *
* @return FieldList * @return FieldList
*/ */
public function updateCMSFields(FieldList $fields) { public function updateCMSFields(FieldList $fields)
$fieldEditor = $this->getFieldEditorGrid(); {
$fieldEditor = $this->getFieldEditorGrid();
$fields->insertAfter(new Tab('FormFields', _t('UserFormFieldEditorExtension.FORMFIELDS', 'Form Fields')), 'Main'); $fields->insertAfter(new Tab('FormFields', _t('UserFormFieldEditorExtension.FORMFIELDS', 'Form Fields')), 'Main');
$fields->addFieldToTab('Root.FormFields', $fieldEditor); $fields->addFieldToTab('Root.FormFields', $fieldEditor);
return $fields; return $fields;
} }
/** /**
* Gets the field editor, for adding and removing EditableFormFields. * Gets the field editor, for adding and removing EditableFormFields.
* *
* @return GridField * @return GridField
*/ */
public function getFieldEditorGrid() { public function getFieldEditorGrid()
Requirements::javascript(USERFORMS_DIR . '/javascript/FieldEditor.js'); {
Requirements::javascript(USERFORMS_DIR . '/javascript/FieldEditor.js');
$fields = $this->owner->Fields(); $fields = $this->owner->Fields();
$this->createInitialFormStep(true); $this->createInitialFormStep(true);
$editableColumns = new GridFieldEditableColumns(); $editableColumns = new GridFieldEditableColumns();
$fieldClasses = singleton('EditableFormField')->getEditableFieldClasses(); $fieldClasses = singleton('EditableFormField')->getEditableFieldClasses();
$editableColumns->setDisplayFields(array( $editableColumns->setDisplayFields(array(
'ClassName' => function($record, $column, $grid) use ($fieldClasses) { 'ClassName' => function ($record, $column, $grid) use ($fieldClasses) {
if($record instanceof EditableFormField) { if ($record instanceof EditableFormField) {
return $record->getInlineClassnameField($column, $fieldClasses); return $record->getInlineClassnameField($column, $fieldClasses);
} }
}, },
'Title' => function($record, $column, $grid) { 'Title' => function ($record, $column, $grid) {
if($record instanceof EditableFormField) { if ($record instanceof EditableFormField) {
return $record->getInlineTitleField($column); return $record->getInlineTitleField($column);
} }
} }
)); ));
$config = GridFieldConfig::create() $config = GridFieldConfig::create()
->addComponents( ->addComponents(
$editableColumns, $editableColumns,
new GridFieldButtonRow(), new GridFieldButtonRow(),
GridFieldAddClassesButton::create('EditableTextField') GridFieldAddClassesButton::create('EditableTextField')
->setButtonName(_t('UserFormFieldEditorExtension.ADD_FIELD', 'Add Field')) ->setButtonName(_t('UserFormFieldEditorExtension.ADD_FIELD', 'Add Field'))
->setButtonClass('ss-ui-action-constructive'), ->setButtonClass('ss-ui-action-constructive'),
GridFieldAddClassesButton::create('EditableFormStep') GridFieldAddClassesButton::create('EditableFormStep')
->setButtonName(_t('UserFormFieldEditorExtension.ADD_PAGE_BREAK', 'Add Page Break')), ->setButtonName(_t('UserFormFieldEditorExtension.ADD_PAGE_BREAK', 'Add Page Break')),
GridFieldAddClassesButton::create(array('EditableFieldGroup', 'EditableFieldGroupEnd')) GridFieldAddClassesButton::create(array('EditableFieldGroup', 'EditableFieldGroupEnd'))
->setButtonName(_t('UserFormFieldEditorExtension.ADD_FIELD_GROUP', 'Add Field Group')), ->setButtonName(_t('UserFormFieldEditorExtension.ADD_FIELD_GROUP', 'Add Field Group')),
new GridFieldEditButton(), new GridFieldEditButton(),
new GridFieldDeleteAction(), new GridFieldDeleteAction(),
new GridFieldToolbarHeader(), new GridFieldToolbarHeader(),
new GridFieldOrderableRows('Sort'), new GridFieldOrderableRows('Sort'),
new GridFieldDetailForm() new GridFieldDetailForm()
); );
$fieldEditor = GridField::create( $fieldEditor = GridField::create(
'Fields', 'Fields',
_t('UserDefinedForm.FIELDS', 'Fields'), _t('UserDefinedForm.FIELDS', 'Fields'),
$fields, $fields,
$config $config
)->addExtraClass('uf-field-editor'); )->addExtraClass('uf-field-editor');
return $fieldEditor; return $fieldEditor;
} }
/** /**
* A UserForm must have at least one step. * A UserForm must have at least one step.
* If no steps exist, create an initial step, and put all fields inside it. * If no steps exist, create an initial step, and put all fields inside it.
* *
* @param bool $force * @param bool $force
* @return void * @return void
*/ */
public function createInitialFormStep($force = false) { public function createInitialFormStep($force = false)
// Only invoke once saved {
if(!$this->owner->exists()) { // Only invoke once saved
return; if (!$this->owner->exists()) {
} return;
}
// Check if first field is a step // Check if first field is a step
$fields = $this->owner->Fields(); $fields = $this->owner->Fields();
$firstField = $fields->first(); $firstField = $fields->first();
if($firstField instanceof EditableFormStep) { if ($firstField instanceof EditableFormStep) {
return; return;
} }
// Don't create steps on write if there are no formfields, as this // Don't create steps on write if there are no formfields, as this
// can create duplicate first steps during publish of new records // can create duplicate first steps during publish of new records
if(!$force && !$firstField) { if (!$force && !$firstField) {
return; return;
} }
// Re-apply sort to each field starting at 2 // Re-apply sort to each field starting at 2
$next = 2; $next = 2;
foreach($fields as $field) { foreach ($fields as $field) {
$field->Sort = $next++; $field->Sort = $next++;
$field->write(); $field->write();
} }
// Add step // Add step
$step = EditableFormStep::create(); $step = EditableFormStep::create();
$step->Title = _t('EditableFormStep.TITLE_FIRST', 'First Page'); $step->Title = _t('EditableFormStep.TITLE_FIRST', 'First Page');
$step->Sort = 1; $step->Sort = 1;
$step->write(); $step->write();
$fields->add($step); $fields->add($step);
} }
/** /**
* Ensure that at least one page exists at the start * Ensure that at least one page exists at the start
*/ */
public function onAfterWrite() { public function onAfterWrite()
$this->createInitialFormStep(); {
} $this->createInitialFormStep();
}
/** /**
* @see SiteTree::doPublish * @see SiteTree::doPublish
* @param Page $original * @param Page $original
* *
* @return void * @return void
*/ */
public function onAfterPublish($original) { public function onAfterPublish($original)
// Remove fields on the live table which could have been orphaned. {
$live = Versioned::get_by_stage("EditableFormField", "Live") // Remove fields on the live table which could have been orphaned.
->filter('ParentID', $original->ID); $live = Versioned::get_by_stage("EditableFormField", "Live")
->filter('ParentID', $original->ID);
if($live) { if ($live) {
foreach($live as $field) { foreach ($live as $field) {
$field->doDeleteFromStage('Live'); $field->doDeleteFromStage('Live');
} }
} }
foreach($this->owner->Fields() as $field) { foreach ($this->owner->Fields() as $field) {
$field->doPublish('Stage', 'Live'); $field->doPublish('Stage', 'Live');
} }
} }
/** /**
* @see SiteTree::doUnpublish * @see SiteTree::doUnpublish
* @param Page $page * @param Page $page
* *
* @return void * @return void
*/ */
public function onAfterUnpublish($page) { public function onAfterUnpublish($page)
foreach($page->Fields() as $field) { {
$field->doDeleteFromStage('Live'); foreach ($page->Fields() as $field) {
} $field->doDeleteFromStage('Live');
} }
}
/** /**
* @see SiteTree::duplicate * @see SiteTree::duplicate
* @param DataObject $newPage * @param DataObject $newPage
* *
* @return DataObject * @return DataObject
*/ */
public function onAfterDuplicate($newPage) { public function onAfterDuplicate($newPage)
// List of EditableFieldGroups, where the {
// key of the array is the ID of the old end group // List of EditableFieldGroups, where the
$fieldGroups = array(); // key of the array is the ID of the old end group
foreach($this->owner->Fields() as $field) { $fieldGroups = array();
$newField = $field->duplicate(false); foreach ($this->owner->Fields() as $field) {
$newField->ParentID = $newPage->ID; $newField = $field->duplicate(false);
$newField->ParentClass = $newPage->ClassName; $newField->ParentID = $newPage->ID;
$newField->Version = 0; $newField->ParentClass = $newPage->ClassName;
$newField->write(); $newField->Version = 0;
$newField->write();
// If we encounter a group start, record it for later use // If we encounter a group start, record it for later use
if($field instanceof EditableFieldGroup) { if ($field instanceof EditableFieldGroup) {
$fieldGroups[$field->EndID] = $newField; $fieldGroups[$field->EndID] = $newField;
} }
// If we encounter an end group, link it back to the group start // If we encounter an end group, link it back to the group start
if($field instanceof EditableFieldGroupEnd && isset($fieldGroups[$field->ID])) { if ($field instanceof EditableFieldGroupEnd && isset($fieldGroups[$field->ID])) {
$groupStart = $fieldGroups[$field->ID]; $groupStart = $fieldGroups[$field->ID];
$groupStart->EndID = $newField->ID; $groupStart->EndID = $newField->ID;
$groupStart->write(); $groupStart->write();
} }
foreach ($field->DisplayRules() as $customRule) { foreach ($field->DisplayRules() as $customRule) {
$newRule = $customRule->duplicate(false); $newRule = $customRule->duplicate(false);
$newRule->ParentID = $newField->ID; $newRule->ParentID = $newField->ID;
$newRule->Version = 0; $newRule->Version = 0;
$newRule->write(); $newRule->write();
} }
} }
return $newPage; return $newPage;
} }
/** /**
* @see SiteTree::getIsModifiedOnStage * @see SiteTree::getIsModifiedOnStage
* @param boolean $isModified * @param boolean $isModified
* *
* @return boolean * @return boolean
*/ */
public function getIsModifiedOnStage($isModified) { public function getIsModifiedOnStage($isModified)
if(!$isModified) { {
foreach($this->owner->Fields() as $field) { if (!$isModified) {
if($field->getIsModifiedOnStage()) { foreach ($this->owner->Fields() as $field) {
$isModified = true; if ($field->getIsModifiedOnStage()) {
break; $isModified = true;
} break;
} }
} }
}
return $isModified; return $isModified;
} }
/** /**
* @see SiteTree::doRevertToLive * @see SiteTree::doRevertToLive
* @param Page $page * @param Page $page
* *
* @return void * @return void
*/ */
public function onAfterRevertToLive($page) { public function onAfterRevertToLive($page)
foreach($page->Fields() as $field) { {
$field->publish('Live', 'Stage', false); foreach ($page->Fields() as $field) {
$field->writeWithoutVersion(); $field->publish('Live', 'Stage', false);
} $field->writeWithoutVersion();
} }
}
} }

View File

@ -1,126 +1,128 @@
<?php <?php
class UserFormValidator extends RequiredFields { class UserFormValidator extends RequiredFields
public function php($data) { {
if(!parent::php($data)) { public function php($data)
return false; {
} if (!parent::php($data)) {
return false;
}
// Skip unsaved records // Skip unsaved records
if(empty($data['ID']) || !is_numeric($data['ID'])) { if (empty($data['ID']) || !is_numeric($data['ID'])) {
return true; return true;
} }
$fields = EditableFormField::get()->filter('ParentID', $data['ID'])->sort('"Sort" ASC'); $fields = EditableFormField::get()->filter('ParentID', $data['ID'])->sort('"Sort" ASC');
// Current nesting // Current nesting
$stack = array(); $stack = array();
$conditionalStep = false; // Is the current step conditional? $conditionalStep = false; // Is the current step conditional?
foreach($fields as $field) { foreach ($fields as $field) {
if($field instanceof EditableFormStep) { if ($field instanceof EditableFormStep) {
// Page at top level, or after another page is ok // Page at top level, or after another page is ok
if(empty($stack) || (count($stack) === 1 && $stack[0] instanceof EditableFormStep)) { if (empty($stack) || (count($stack) === 1 && $stack[0] instanceof EditableFormStep)) {
$stack = array($field); $stack = array($field);
$conditionalStep = $field->EffectiveDisplayRules()->count() > 0; $conditionalStep = $field->EffectiveDisplayRules()->count() > 0;
continue; continue;
} }
$this->validationError( $this->validationError(
'FormFields', 'FormFields',
_t( _t(
"UserFormValidator.UNEXPECTED_BREAK", "UserFormValidator.UNEXPECTED_BREAK",
"Unexpected page break '{name}' inside nested field '{group}'", "Unexpected page break '{name}' inside nested field '{group}'",
array( array(
'name' => $field->CMSTitle, 'name' => $field->CMSTitle,
'group' => end($stack)->CMSTitle 'group' => end($stack)->CMSTitle
) )
), ),
'error' 'error'
); );
return false; return false;
} }
// Validate no pages // Validate no pages
if(empty($stack)) { if (empty($stack)) {
$this->validationError( $this->validationError(
'FormFields', 'FormFields',
_t( _t(
"UserFormValidator.NO_PAGE", "UserFormValidator.NO_PAGE",
"Field '{name}' found before any pages", "Field '{name}' found before any pages",
array( array(
'name' => $field->CMSTitle 'name' => $field->CMSTitle
) )
), ),
'error' 'error'
); );
return false; return false;
} }
// Nest field group // Nest field group
if($field instanceof EditableFieldGroup) { if ($field instanceof EditableFieldGroup) {
$stack[] = $field; $stack[] = $field;
continue; continue;
} }
// Unnest field group // Unnest field group
if($field instanceof EditableFieldGroupEnd) { if ($field instanceof EditableFieldGroupEnd) {
$top = end($stack); $top = end($stack);
// Check that the top is a group at all // Check that the top is a group at all
if(!$top instanceof EditableFieldGroup) { if (!$top instanceof EditableFieldGroup) {
$this->validationError( $this->validationError(
'FormFields', 'FormFields',
_t( _t(
"UserFormValidator.UNEXPECTED_GROUP_END", "UserFormValidator.UNEXPECTED_GROUP_END",
"'{name}' found without a matching group", "'{name}' found without a matching group",
array( array(
'name' => $field->CMSTitle 'name' => $field->CMSTitle
) )
), ),
'error' 'error'
); );
return false; return false;
} }
// Check that the top is the right group // Check that the top is the right group
if($top->EndID != $field->ID) { if ($top->EndID != $field->ID) {
$this->validationError( $this->validationError(
'FormFields', 'FormFields',
_t( _t(
"UserFormValidator.WRONG_GROUP_END", "UserFormValidator.WRONG_GROUP_END",
"'{name}' found closes the wrong group '{group}'", "'{name}' found closes the wrong group '{group}'",
array( array(
'name' => $field->CMSTitle, 'name' => $field->CMSTitle,
'group' => $top->CMSTitle 'group' => $top->CMSTitle
) )
), ),
'error' 'error'
); );
return false; return false;
} }
// Unnest group // Unnest group
array_pop($stack); array_pop($stack);
} }
// Normal field type // Normal field type
if($conditionalStep && $field->Required) { if ($conditionalStep && $field->Required) {
$this->validationError( $this->validationError(
'FormFields', 'FormFields',
_t( _t(
"UserFormValidator.CONDITIONAL_REQUIRED", "UserFormValidator.CONDITIONAL_REQUIRED",
"Required field '{name}' cannot be placed within a conditional page", "Required field '{name}' cannot be placed within a conditional page",
array( array(
'name' => $field->CMSTitle 'name' => $field->CMSTitle
) )
), ),
'error' 'error'
); );
return false; return false;
} }
} }
return true; return true;
} }
} }

View File

@ -3,63 +3,65 @@
/** /**
* @package userforms * @package userforms
*/ */
class UserFormsCheckboxSetField extends CheckboxSetField { class UserFormsCheckboxSetField extends CheckboxSetField
{
/** /**
* jQuery validate requires that the value of the option does not contain * jQuery validate requires that the value of the option does not contain
* the actual value of the input. * the actual value of the input.
* *
* @return ArrayList * @return ArrayList
*/ */
public function getOptions() { public function getOptions()
$options = parent::getOptions(); {
$options = parent::getOptions();
foreach($options as $option) { foreach ($options as $option) {
$option->Name = "{$this->name}[]"; $option->Name = "{$this->name}[]";
} }
return $options; return $options;
} }
/** /**
* @inheritdoc * @inheritdoc
* *
* @return array * @return array
*/ */
public function getSourceAsArray() public function getSourceAsArray()
{ {
$array = parent::getSourceAsArray(); $array = parent::getSourceAsArray();
return array_values($array); return array_values($array);
} }
/** /**
* @inheritdoc * @inheritdoc
* *
* @param Validator $validator * @param Validator $validator
* *
* @return bool * @return bool
*/ */
public function validate($validator) public function validate($validator)
{ {
// get the previous values (could contain comma-delimited list) // get the previous values (could contain comma-delimited list)
$previous = $value = $this->Value(); $previous = $value = $this->Value();
if (is_string($value) && strstr($value, ",")) { if (is_string($value) && strstr($value, ",")) {
$value = explode(",", $value); $value = explode(",", $value);
} }
// set the value as an array for parent validation // set the value as an array for parent validation
$this->setValue($value); $this->setValue($value);
$validated = parent::validate($validator); $validated = parent::validate($validator);
// restore previous value after validation // restore previous value after validation
$this->setValue($previous); $this->setValue($previous);
return $validated; return $validated;
} }
} }

View File

@ -3,45 +3,49 @@
/** /**
* Represents a composite field group, which may contain other groups * Represents a composite field group, which may contain other groups
*/ */
abstract class UserFormsCompositeField extends CompositeField implements UserFormsFieldContainer { abstract class UserFormsCompositeField extends CompositeField implements UserFormsFieldContainer
{
/** /**
* Parent field * Parent field
* *
* @var UserFormsFieldContainer * @var UserFormsFieldContainer
*/ */
protected $parent = null; protected $parent = null;
public function getParent() { public function getParent()
return $this->parent; {
} return $this->parent;
}
public function setParent(UserFormsFieldContainer $parent) { public function setParent(UserFormsFieldContainer $parent)
$this->parent = $parent; {
return $this; $this->parent = $parent;
} return $this;
}
public function processNext(EditableFormField $field) { public function processNext(EditableFormField $field)
// When we find a step, bubble up to the top {
if($field instanceof EditableFormStep) { // When we find a step, bubble up to the top
return $this->getParent()->processNext($field); if ($field instanceof EditableFormStep) {
} return $this->getParent()->processNext($field);
}
// Skip over fields that don't generate formfields // Skip over fields that don't generate formfields
$formField = $field->getFormField(); $formField = $field->getFormField();
if(!$formField) { if (!$formField) {
return $this; return $this;
} }
// Save this field // Save this field
$this->push($formField); $this->push($formField);
// Nest fields that are containers // Nest fields that are containers
if($formField instanceof UserFormsFieldContainer) { if ($formField instanceof UserFormsFieldContainer) {
return $formField->setParent($this); return $formField->setParent($this);
} }
// Add any subsequent fields to this // Add any subsequent fields to this
return $this; return $this;
} }
} }

View File

@ -3,28 +3,29 @@
/** /**
* Represents a field container which can iteratively process nested fields, converting it into a fieldset * Represents a field container which can iteratively process nested fields, converting it into a fieldset
*/ */
interface UserFormsFieldContainer { interface UserFormsFieldContainer
{
/** /**
* Process the next field in the list, returning the container to add the next field to. * Process the next field in the list, returning the container to add the next field to.
* *
* @param EditableFormField $field * @param EditableFormField $field
* @return EditableContainerField * @return EditableContainerField
*/ */
public function processNext(EditableFormField $field); public function processNext(EditableFormField $field);
/** /**
* Set the parent * Set the parent
* *
* @param UserFormsFieldContainer $parent * @param UserFormsFieldContainer $parent
* @return $this * @return $this
*/ */
public function setParent(UserFormsFieldContainer $parent); public function setParent(UserFormsFieldContainer $parent);
/** /**
* Get the parent * Get the parent
* *
* @return UserFormsFieldContainer * @return UserFormsFieldContainer
*/ */
public function getParent(); public function getParent();
} }

View File

@ -3,41 +3,45 @@
/** /**
* A list of formfields which allows for iterative processing of nested composite fields * A list of formfields which allows for iterative processing of nested composite fields
*/ */
class UserFormsFieldList extends FieldList implements UserFormsFieldContainer { class UserFormsFieldList extends FieldList implements UserFormsFieldContainer
{
public function processNext(EditableFormField $field) { public function processNext(EditableFormField $field)
$formField = $field->getFormField(); {
if(!$formField) { $formField = $field->getFormField();
return $this; if (!$formField) {
} return $this;
}
$this->push($formField); $this->push($formField);
if($formField instanceof UserFormsFieldContainer) { if ($formField instanceof UserFormsFieldContainer) {
return $formField->setParent($this); return $formField->setParent($this);
} }
return $this; return $this;
} }
public function getParent() { public function getParent()
// Field list does not have a parent {
return null; // Field list does not have a parent
} return null;
}
public function setParent(UserFormsFieldContainer $parent) { public function setParent(UserFormsFieldContainer $parent)
return $this; {
} return $this;
}
/**
* Remove all empty steps
*/
public function clearEmptySteps() {
foreach($this as $field) {
if($field instanceof UserFormsStepField && count($field->getChildren()) === 0) {
$this->remove($field);
}
}
}
/**
* Remove all empty steps
*/
public function clearEmptySteps()
{
foreach ($this as $field) {
if ($field instanceof UserFormsStepField && count($field->getChildren()) === 0) {
$this->remove($field);
}
}
}
} }

View File

@ -3,25 +3,29 @@
/** /**
* Front end composite field for userforms * Front end composite field for userforms
*/ */
class UserFormsGroupField extends UserFormsCompositeField { class UserFormsGroupField extends UserFormsCompositeField
{
public function __construct($children = null) { public function __construct($children = null)
parent::__construct($children); {
$this->setTag('fieldset'); parent::__construct($children);
} $this->setTag('fieldset');
}
public function getLegend() { public function getLegend()
// Legend defaults to title {
return parent::getLegend() ?: $this->Title(); // Legend defaults to title
} return parent::getLegend() ?: $this->Title();
}
public function processNext(EditableFormField $field) { public function processNext(EditableFormField $field)
// When ending a group, jump up one level {
if($field instanceof EditableFieldGroupEnd) { // When ending a group, jump up one level
return $this->getParent(); if ($field instanceof EditableFieldGroupEnd) {
} return $this->getParent();
}
// Otherwise behave as per normal composite field // Otherwise behave as per normal composite field
return parent::processNext($field); return parent::processNext($field);
} }
} }

View File

@ -3,42 +3,46 @@
/** /**
* Represents a page step in a form, which may contain form fields or other groups * Represents a page step in a form, which may contain form fields or other groups
*/ */
class UserFormsStepField extends UserFormsCompositeField { class UserFormsStepField extends UserFormsCompositeField
{
private static $casting = array( private static $casting = array(
'StepNumber' => 'Int' 'StepNumber' => 'Int'
); );
/** /**
* Numeric index (1 based) of this step * Numeric index (1 based) of this step
* *
* Null if unassigned * Null if unassigned
* *
* @var int|null * @var int|null
*/ */
protected $number = null; protected $number = null;
public function FieldHolder($properties = array()) { public function FieldHolder($properties = array())
return $this->Field($properties); {
} return $this->Field($properties);
}
/** /**
* Get the step number * Get the step number
* *
* @return int|null * @return int|null
*/ */
public function getStepNumber() { public function getStepNumber()
return $this->number; {
} return $this->number;
}
/** /**
* Re-assign this step to another number * Re-assign this step to another number
* *
* @param type $number * @param type $number
* @return $this * @return $this
*/ */
public function setStepNumber($number) { public function setStepNumber($number)
$this->number = $number; {
return $this; $this->number = $number;
} return $this;
}
} }

View File

@ -7,11 +7,13 @@
* @deprecated since version 4.0 * @deprecated since version 4.0
* @package userforms * @package userforms
*/ */
class UserformsTreeDropdownField extends TreeDropdownField { class UserformsTreeDropdownField extends TreeDropdownField
{
public function __construct($name, $title = null, $sourceObject = 'Group', $keyField = 'ID', $labelField = 'TreeTitle', $showSearch = true) { public function __construct($name, $title = null, $sourceObject = 'Group', $keyField = 'ID', $labelField = 'TreeTitle', $showSearch = true)
parent::__construct($name, $title, $sourceObject, $keyField, $labelField, $showSearch); {
parent::__construct($name, $title, $sourceObject, $keyField, $labelField, $showSearch);
Deprecation::notice('4.0', __CLASS__ . " is deprecated"); Deprecation::notice('4.0', __CLASS__ . " is deprecated");
} }
} }

View File

@ -3,230 +3,246 @@
/** /**
* A button which allows objects to be created with a specified classname(s) * A button which allows objects to be created with a specified classname(s)
*/ */
class GridFieldAddClassesButton extends Object implements GridField_HTMLProvider, GridField_ActionProvider { class GridFieldAddClassesButton extends Object implements GridField_HTMLProvider, GridField_ActionProvider
{
/** /**
* Name of fragment to insert into * Name of fragment to insert into
* *
* @var string * @var string
*/ */
protected $targetFragment; protected $targetFragment;
/** /**
* Button title * Button title
* *
* @var string * @var string
*/ */
protected $buttonName; protected $buttonName;
/** /**
* Additonal CSS classes for the button * Additonal CSS classes for the button
* *
* @var string * @var string
*/ */
protected $buttonClass = null; protected $buttonClass = null;
/** /**
* Class names * Class names
* *
* @var array * @var array
*/ */
protected $modelClasses = null; protected $modelClasses = null;
/** /**
* @param array $classes Class or list of classes to create. * @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 * 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 * @param string $targetFragment The fragment to render the button into
*/ */
public function __construct($classes, $targetFragment = 'buttons-before-left') { public function __construct($classes, $targetFragment = 'buttons-before-left')
parent::__construct(); {
$this->setClasses($classes); parent::__construct();
$this->setFragment($targetFragment); $this->setClasses($classes);
} $this->setFragment($targetFragment);
}
/** /**
* Change the button name * Change the button name
* *
* @param string $name * @param string $name
* @return $this * @return $this
*/ */
public function setButtonName($name) { public function setButtonName($name)
$this->buttonName = $name; {
return $this; $this->buttonName = $name;
} return $this;
}
/** /**
* Get the button name * Get the button name
* *
* @return string * @return string
*/ */
public function getButtonName() { public function getButtonName()
return $this->buttonName; {
} return $this->buttonName;
}
/** /**
* Gets the fragment name this button is rendered into. * Gets the fragment name this button is rendered into.
* *
* @return string * @return string
*/ */
public function getFragment() { public function getFragment()
return $this->targetFragment; {
} return $this->targetFragment;
}
/** /**
* Sets the fragment name this button is rendered into. * Sets the fragment name this button is rendered into.
* *
* @param string $fragment * @param string $fragment
* @return GridFieldAddNewInlineButton $this * @return GridFieldAddNewInlineButton $this
*/ */
public function setFragment($fragment) { public function setFragment($fragment)
$this->targetFragment = $fragment; {
return $this; $this->targetFragment = $fragment;
} return $this;
}
/** /**
* Get extra button class * Get extra button class
* *
* @return string * @return string
*/ */
public function getButtonClass() { public function getButtonClass()
return $this->buttonClass; {
} return $this->buttonClass;
}
/** /**
* Sets extra CSS classes for this button * Sets extra CSS classes for this button
* *
* @param string $buttonClass * @param string $buttonClass
* @return $this * @return $this
*/ */
public function setButtonClass($buttonClass) { public function setButtonClass($buttonClass)
$this->buttonClass = $buttonClass; {
return $this; $this->buttonClass = $buttonClass;
} return $this;
}
/** /**
* Get the classes of the objects to create * Get the classes of the objects to create
* *
* @return array * @return array
*/ */
public function getClasses() { public function getClasses()
return $this->modelClasses; {
} return $this->modelClasses;
}
/** /**
* Gets the list of classes which can be created, with checks for permissions. * Gets the list of classes which can be created, with checks for permissions.
* Will fallback to the default model class for the given DataGrid * Will fallback to the default model class for the given DataGrid
* *
* @param DataGrid $grid * @param DataGrid $grid
* @return array * @return array
*/ */
public function getClassesCreate($grid) { public function getClassesCreate($grid)
// Get explicit or fallback class list {
$classes = $this->getClasses(); // Get explicit or fallback class list
if(empty($classes) && $grid) { $classes = $this->getClasses();
$classes = array($grid->getModelClass()); if (empty($classes) && $grid) {
} $classes = array($grid->getModelClass());
}
// Filter out classes without permission // Filter out classes without permission
return array_filter($classes, function($class) { return array_filter($classes, function ($class) {
return singleton($class)->canCreate(); return singleton($class)->canCreate();
}); });
} }
/** /**
* Specify the classes to create * Specify the classes to create
* *
* @param array $classes * @param array $classes
*/ */
public function setClasses($classes) { public function setClasses($classes)
if(!is_array($classes)) { {
$classes = $classes ? array($classes) : array(); if (!is_array($classes)) {
} $classes = $classes ? array($classes) : array();
$this->modelClasses = $classes; }
} $this->modelClasses = $classes;
}
public function getHTMLFragments($grid) { public function getHTMLFragments($grid)
// Check create permission {
$singleton = singleton($grid->getModelClass()); // Check create permission
if(!$singleton->canCreate()) { $singleton = singleton($grid->getModelClass());
return array(); if (!$singleton->canCreate()) {
} return array();
}
// Get button name // Get button name
$buttonName = $this->getButtonName(); $buttonName = $this->getButtonName();
if(!$buttonName) { if (!$buttonName) {
// provide a default button name, can be changed by calling {@link setButtonName()} on this component // provide a default button name, can be changed by calling {@link setButtonName()} on this component
$objectName = $singleton->i18n_singular_name(); $objectName = $singleton->i18n_singular_name();
$buttonName = _t('GridField.Add', 'Add {name}', array('name' => $objectName)); $buttonName = _t('GridField.Add', 'Add {name}', array('name' => $objectName));
} }
$addAction = new GridField_FormAction( $addAction = new GridField_FormAction(
$grid, $grid,
$this->getAction(), $this->getAction(),
$buttonName, $buttonName,
$this->getAction(), $this->getAction(),
array() array()
); );
$addAction->setAttribute('data-icon', 'add'); $addAction->setAttribute('data-icon', 'add');
if($this->getButtonClass()) { if ($this->getButtonClass()) {
$addAction->addExtraClass($this->getButtonClass()); $addAction->addExtraClass($this->getButtonClass());
} }
return array( return array(
$this->targetFragment => $addAction->forTemplate() $this->targetFragment => $addAction->forTemplate()
); );
} }
/** /**
* {@inheritDoc} * {@inheritDoc}
*/ */
public function getActions($gridField) { public function getActions($gridField)
return array( {
$this->getAction() return array(
); $this->getAction()
} );
}
/** /**
* Get the action suburl for this component * Get the action suburl for this component
* *
* @return string * @return string
*/ */
protected function getAction() { protected function getAction()
return 'add-classes-' . strtolower(implode('-', $this->getClasses())); {
} return 'add-classes-' . strtolower(implode('-', $this->getClasses()));
}
public function handleAction(GridField $gridField, $actionName, $arguments, $data) { public function handleAction(GridField $gridField, $actionName, $arguments, $data)
switch(strtolower($actionName)) { {
case $this->getAction(): switch (strtolower($actionName)) {
return $this->handleAdd($gridField); case $this->getAction():
default: return $this->handleAdd($gridField);
return null; default:
} return null;
} }
}
/** /**
* Handles adding a new instance of a selected class. * Handles adding a new instance of a selected class.
* *
* @param GridField $grid * @param GridField $grid
* @return null * @return null
*/ */
public function handleAdd($grid) { public function handleAdd($grid)
$classes = $this->getClassesCreate($grid); {
if(empty($classes)) { $classes = $this->getClassesCreate($grid);
throw new SS_HTTPResponse_Exception(400); if (empty($classes)) {
} throw new SS_HTTPResponse_Exception(400);
}
// Add item to gridfield // Add item to gridfield
$list = $grid->getList(); $list = $grid->getList();
foreach($classes as $class) { foreach ($classes as $class) {
$item = $class::create(); $item = $class::create();
$item->write(); $item->write();
$list->add($item); $list->add($item);
} }
// Should trigger a simple reload // Should trigger a simple reload
return null; return null;
} }
} }

View File

@ -3,167 +3,175 @@
/** /**
* @package userforms * @package userforms
*/ */
class UserForm extends Form { class UserForm extends Form
{
/** /**
* @param Controller $controller * @param Controller $controller
* @param string $name * @param string $name
*/ */
public function __construct(Controller $controller, $name = 'Form') { public function __construct(Controller $controller, $name = 'Form')
{
$this->controller = $controller;
$this->setRedirectToFormOnValidationError(true);
$this->controller = $controller; parent::__construct(
$this->setRedirectToFormOnValidationError(true); $controller,
$name,
new FieldList(),
new FieldList()
);
parent::__construct( $this->setFields($fields = $this->getFormFields());
$controller, $fields->setForm($this);
$name, $this->setActions($actions = $this->getFormActions());
new FieldList(), $actions->setForm($this);
new FieldList() $this->setValidator($this->getRequiredFields());
);
$this->setFields($fields = $this->getFormFields()); // This needs to be re-evaluated since fields have been assigned
$fields->setForm($this); $this->setupFormErrors();
$this->setActions($actions = $this->getFormActions());
$actions->setForm($this);
$this->setValidator($this->getRequiredFields());
// This needs to be re-evaluated since fields have been assigned // Number each page
$this->setupFormErrors(); $stepNumber = 1;
foreach ($this->getSteps() as $step) {
$step->setStepNumber($stepNumber++);
}
// Number each page if ($controller->DisableCsrfSecurityToken) {
$stepNumber = 1; $this->disableSecurityToken();
foreach($this->getSteps() as $step) { }
$step->setStepNumber($stepNumber++);
}
if($controller->DisableCsrfSecurityToken) { $data = Session::get("FormInfo.{$this->FormName()}.data");
$this->disableSecurityToken();
}
$data = Session::get("FormInfo.{$this->FormName()}.data"); if (is_array($data)) {
$this->loadDataFrom($data);
}
if(is_array($data)) { $this->extend('updateForm');
$this->loadDataFrom($data); }
}
$this->extend('updateForm'); public function setupFormErrors()
} {
// Suppress setupFormErrors if fields haven't been bootstrapped
if ($this->fields && $this->fields->exists()) {
return parent::setupFormErrors();
}
public function setupFormErrors() return $this;
{ }
// Suppress setupFormErrors if fields haven't been bootstrapped
if($this->fields && $this->fields->exists()) {
return parent::setupFormErrors();
}
return $this; /**
} * Used for partial caching in the template.
*
* @return string
*/
public function getLastEdited()
{
return $this->controller->LastEdited;
}
/** /**
* Used for partial caching in the template. * @return bool
* */
* @return string public function getDisplayErrorMessagesAtTop()
*/ {
public function getLastEdited() { return (bool)$this->controller->DisplayErrorMessagesAtTop;
return $this->controller->LastEdited; }
}
/** /**
* @return bool * Return the fieldlist, filtered to only contain steps
*/ *
public function getDisplayErrorMessagesAtTop() { * @return ArrayList
return (bool)$this->controller->DisplayErrorMessagesAtTop; */
} public function getSteps()
{
return $this->Fields()->filterByCallback(function ($field) {
return $field instanceof UserFormsStepField;
});
}
/** /**
* Return the fieldlist, filtered to only contain steps * Get the form fields for the form on this page. Can modify this FieldSet
* * by using {@link updateFormFields()} on an {@link Extension} subclass which
* @return ArrayList * is applied to this controller.
*/ *
public function getSteps() { * This will be a list of top level composite steps
return $this->Fields()->filterByCallback(function($field) { *
return $field instanceof UserFormsStepField; * @return FieldList
}); */
} public function getFormFields()
{
$fields = new UserFormsFieldList();
$target = $fields;
foreach ($this->controller->Fields() as $field) {
$target = $target->processNext($field);
}
$fields->clearEmptySteps();
$this->extend('updateFormFields', $fields);
$fields->setForm($this);
return $fields;
}
/** /**
* Get the form fields for the form on this page. Can modify this FieldSet * Generate the form actions for the UserDefinedForm. You
* by using {@link updateFormFields()} on an {@link Extension} subclass which * can manipulate these by using {@link updateFormActions()} on
* is applied to this controller. * a decorator.
* *
* This will be a list of top level composite steps * @todo Make form actions editable via their own field editor.
* *
* @return FieldList * @return FieldList
*/ */
public function getFormFields() { public function getFormActions()
$fields = new UserFormsFieldList(); {
$target = $fields; $submitText = ($this->controller->SubmitButtonText) ? $this->controller->SubmitButtonText : _t('UserDefinedForm.SUBMITBUTTON', 'Submit');
foreach ($this->controller->Fields() as $field) { $clearText = ($this->controller->ClearButtonText) ? $this->controller->ClearButtonText : _t('UserDefinedForm.CLEARBUTTON', 'Clear');
$target = $target->processNext($field);
}
$fields->clearEmptySteps();
$this->extend('updateFormFields', $fields);
$fields->setForm($this);
return $fields;
}
/** $actions = new FieldList(
* Generate the form actions for the UserDefinedForm. You new FormAction("process", $submitText)
* can manipulate these by using {@link updateFormActions()} on );
* a decorator.
*
* @todo Make form actions editable via their own field editor.
*
* @return FieldList
*/
public function getFormActions() {
$submitText = ($this->controller->SubmitButtonText) ? $this->controller->SubmitButtonText : _t('UserDefinedForm.SUBMITBUTTON', 'Submit');
$clearText = ($this->controller->ClearButtonText) ? $this->controller->ClearButtonText : _t('UserDefinedForm.CLEARBUTTON', 'Clear');
$actions = new FieldList( if ($this->controller->ShowClearButton) {
new FormAction("process", $submitText) $actions->push(new ResetFormAction("clearForm", $clearText));
); }
if($this->controller->ShowClearButton) { $this->extend('updateFormActions', $actions);
$actions->push(new ResetFormAction("clearForm", $clearText)); $actions->setForm($this);
} return $actions;
}
$this->extend('updateFormActions', $actions); /**
$actions->setForm($this); * Get the required form fields for this form.
return $actions; *
} * @return RequiredFields
*/
public function getRequiredFields()
{
// Generate required field validator
$requiredNames = $this
->getController()
->Fields()
->filter('Required', true)
->column('Name');
$required = new RequiredFields($requiredNames);
$this->extend('updateRequiredFields', $required);
$required->setForm($this);
return $required;
}
/** /**
* Get the required form fields for this form. * Override some we can add UserForm specific attributes to the form.
* *
* @return RequiredFields * @return array
*/ */
public function getRequiredFields() { public function getAttributes()
// Generate required field validator {
$requiredNames = $this $attrs = parent::getAttributes();
->getController()
->Fields()
->filter('Required', true)
->column('Name');
$required = new RequiredFields($requiredNames);
$this->extend('updateRequiredFields', $required);
$required->setForm($this);
return $required;
}
/** $attrs['class'] = $attrs['class'] . ' userform';
* Override some we can add UserForm specific attributes to the form. $attrs['data-livevalidation'] = (bool)$this->controller->EnableLiveValidation;
* $attrs['data-toperrors'] = (bool)$this->controller->DisplayErrorMessagesAtTop;
* @return array $attrs['data-hidefieldlabels'] = (bool)$this->controller->HideFieldLabels;
*/
public function getAttributes() {
$attrs = parent::getAttributes();
$attrs['class'] = $attrs['class'] . ' userform'; return $attrs;
$attrs['data-livevalidation'] = (bool)$this->controller->EnableLiveValidation; }
$attrs['data-toperrors'] = (bool)$this->controller->DisplayErrorMessagesAtTop;
$attrs['data-hidefieldlabels'] = (bool)$this->controller->HideFieldLabels;
return $attrs;
}
} }

View File

@ -7,100 +7,107 @@
* @method EditableFormField Parent() * @method EditableFormField Parent()
* @package userforms * @package userforms
*/ */
class EditableCustomRule extends DataObject { class EditableCustomRule extends DataObject
{
private static $condition_options = array( private static $condition_options = array(
"IsBlank" => "Is blank", "IsBlank" => "Is blank",
"IsNotBlank" => "Is not blank", "IsNotBlank" => "Is not blank",
"HasValue" => "Equals", "HasValue" => "Equals",
"ValueNot" => "Doesn't equal", "ValueNot" => "Doesn't equal",
"ValueLessThan" => "Less than", "ValueLessThan" => "Less than",
"ValueLessThanEqual" => "Less than or equal", "ValueLessThanEqual" => "Less than or equal",
"ValueGreaterThan" => "Greater than", "ValueGreaterThan" => "Greater than",
"ValueGreaterThanEqual" => "Greater than or equal" "ValueGreaterThanEqual" => "Greater than or equal"
); );
private static $db = array( private static $db = array(
'Display' => 'Enum("Show,Hide")', 'Display' => 'Enum("Show,Hide")',
'ConditionOption' => 'Enum("IsBlank,IsNotBlank,HasValue,ValueNot,ValueLessThan,ValueLessThanEqual,ValueGreaterThan,ValueGreaterThanEqual")', 'ConditionOption' => 'Enum("IsBlank,IsNotBlank,HasValue,ValueNot,ValueLessThan,ValueLessThanEqual,ValueGreaterThan,ValueGreaterThanEqual")',
'FieldValue' => 'Varchar(255)' 'FieldValue' => 'Varchar(255)'
); );
private static $has_one = array( private static $has_one = array(
'Parent' => 'EditableFormField', 'Parent' => 'EditableFormField',
'ConditionField' => 'EditableFormField' 'ConditionField' => 'EditableFormField'
); );
/** /**
* Built in extensions required * Built in extensions required
* *
* @config * @config
* @var array * @var array
*/ */
private static $extensions = array( private static $extensions = array(
"Versioned('Stage', 'Live')" "Versioned('Stage', 'Live')"
); );
/** /**
* Publish this custom rule to the live site * Publish this custom rule to the live site
* *
* Wrapper for the {@link Versioned} publish function * Wrapper for the {@link Versioned} publish function
*/ */
public function doPublish($fromStage, $toStage, $createNewVersion = false) { public function doPublish($fromStage, $toStage, $createNewVersion = false)
$this->publish($fromStage, $toStage, $createNewVersion); {
} $this->publish($fromStage, $toStage, $createNewVersion);
}
/** /**
* Delete this custom rule from a given stage * Delete this custom rule from a given stage
* *
* Wrapper for the {@link Versioned} deleteFromStage function * Wrapper for the {@link Versioned} deleteFromStage function
*/ */
public function doDeleteFromStage($stage) { public function doDeleteFromStage($stage)
$this->deleteFromStage($stage); {
} $this->deleteFromStage($stage);
}
/** /**
* @param Member $member * @param Member $member
* @return bool * @return bool
*/ */
public function canDelete($member = null) { public function canDelete($member = null)
return $this->canEdit($member); {
} return $this->canEdit($member);
}
/** /**
* @param Member $member * @param Member $member
* @return bool * @return bool
*/ */
public function canEdit($member = null) { public function canEdit($member = null)
{
return $this->Parent()->canEdit($member); return $this->Parent()->canEdit($member);
} }
/** /**
* @param Member $member * @param Member $member
* @return bool * @return bool
*/ */
public function canView($member = null) { public function canView($member = null)
return $this->Parent()->canView($member); {
} return $this->Parent()->canView($member);
}
/** /**
* Return whether a user can create an object of this type * Return whether a user can create an object of this type
* *
* @param Member $member * @param Member $member
* @param array $context Virtual parameter to allow context to be passed in to check * @param array $context Virtual parameter to allow context to be passed in to check
* @return bool * @return bool
*/ */
public function canCreate($member = null) { public function canCreate($member = null)
// Check parent page {
// Check parent page
$parent = $this->getCanCreateContext(func_get_args()); $parent = $this->getCanCreateContext(func_get_args());
if($parent) { if ($parent) {
return $parent->canEdit($member); return $parent->canEdit($member);
} }
// Fall back to secure admin permissions // Fall back to secure admin permissions
return parent::canCreate($member); return parent::canCreate($member);
} }
/** /**
* Helper method to check the parent for this object * Helper method to check the parent for this object
@ -108,13 +115,14 @@ class EditableCustomRule extends DataObject {
* @param array $args List of arguments passed to canCreate * @param array $args List of arguments passed to canCreate
* @return DataObject Some parent dataobject to inherit permissions from * @return DataObject Some parent dataobject to inherit permissions from
*/ */
protected function getCanCreateContext($args) { protected function getCanCreateContext($args)
{
// Inspect second parameter to canCreate for a 'Parent' context // Inspect second parameter to canCreate for a 'Parent' context
if(isset($args[1]['Parent'])) { if (isset($args[1]['Parent'])) {
return $args[1]['Parent']; return $args[1]['Parent'];
} }
// Hack in currently edited page if context is missing // Hack in currently edited page if context is missing
if(Controller::has_curr() && Controller::curr() instanceof CMSMain) { if (Controller::has_curr() && Controller::curr() instanceof CMSMain) {
return Controller::curr()->currentPage(); return Controller::curr()->currentPage();
} }
@ -126,7 +134,8 @@ class EditableCustomRule extends DataObject {
* @param Member $member * @param Member $member
* @return bool * @return bool
*/ */
public function canPublish($member = null) { public function canPublish($member = null)
{
return $this->canEdit($member); return $this->canEdit($member);
} }
@ -134,7 +143,8 @@ class EditableCustomRule extends DataObject {
* @param Member $member * @param Member $member
* @return bool * @return bool
*/ */
public function canUnpublish($member = null) { public function canUnpublish($member = null)
{
return $this->canDelete($member); return $this->canDelete($member);
} }
} }

View File

@ -4,178 +4,180 @@
* @package userforms * @package userforms
*/ */
class UserDefinedForm extends Page { class UserDefinedForm extends Page
{
/**
* @var string
*/
private static $icon = 'userforms/images/sitetree_icon.png';
/** /**
* @var string * @var string
*/ */
private static $icon = 'userforms/images/sitetree_icon.png'; private static $description = 'Adds a customizable form.';
/** /**
* @var string * @var string Required Identifier
*/ */
private static $description = 'Adds a customizable form.'; private static $required_identifier = null;
/** /**
* @var string Required Identifier * @var string
*/ */
private static $required_identifier = null; private static $email_template_directory = 'userforms/templates/email/';
/** /**
* @var string * Should this module automatically upgrade on dev/build?
*/ *
private static $email_template_directory = 'userforms/templates/email/';
/**
* Should this module automatically upgrade on dev/build?
*
* @config
* @var bool
*/
private static $upgrade_on_build = true;
/**
* Built in extensions required by this page
* @config
* @var array
*/
private static $extensions = array(
'UserFormFieldEditorExtension'
);
/**
* @var array Fields on the user defined form page.
*/
private static $db = array(
"SubmitButtonText" => "Varchar",
"ClearButtonText" => "Varchar",
"OnCompleteMessage" => "HTMLText",
"ShowClearButton" => "Boolean",
'DisableSaveSubmissions' => 'Boolean',
'EnableLiveValidation' => 'Boolean',
'HideFieldLabels' => 'Boolean',
'DisplayErrorMessagesAtTop' => 'Boolean',
'DisableAuthenicatedFinishAction' => 'Boolean',
'DisableCsrfSecurityToken' => 'Boolean'
);
/**
* @var array Default values of variables when this page is created
*/
private static $defaults = array(
'Content' => '$UserDefinedForm',
'DisableSaveSubmissions' => 0,
'OnCompleteMessage' => '<p>Thanks, we\'ve received your submission.</p>'
);
/**
* @var array
*/
private static $has_many = array(
"Submissions" => "SubmittedForm",
"EmailRecipients" => "UserDefinedForm_EmailRecipient"
);
/**
* @var array
* @config
*/
private static $casting = array(
'ErrorContainerID' => 'Text'
);
/**
* Error container selector which matches the element for grouped messages
*
* @var string
* @config
*/
private static $error_container_id = 'error-container';
/**
* The configuration used to determine whether a confirmation message is to
* appear when navigating away from a partially completed form.
*
* @var boolean
* @config
*/
private static $enable_are_you_sure = true;
/**
* @var bool
* @config * @config
*/ * @var bool
private static $recipients_warning_enabled = false; */
private static $upgrade_on_build = true;
/** /**
* Temporary storage of field ids when the form is duplicated. * Built in extensions required by this page
* Example layout: array('EditableCheckbox3' => 'EditableCheckbox14') * @config
* @var array * @var array
*/ */
protected $fieldsFromTo = array(); private static $extensions = array(
'UserFormFieldEditorExtension'
);
/** /**
* @return FieldList * @var array Fields on the user defined form page.
*/ */
public function getCMSFields() { private static $db = array(
Requirements::css(USERFORMS_DIR . '/css/UserForm_cms.css'); "SubmitButtonText" => "Varchar",
"ClearButtonText" => "Varchar",
"OnCompleteMessage" => "HTMLText",
"ShowClearButton" => "Boolean",
'DisableSaveSubmissions' => 'Boolean',
'EnableLiveValidation' => 'Boolean',
'HideFieldLabels' => 'Boolean',
'DisplayErrorMessagesAtTop' => 'Boolean',
'DisableAuthenicatedFinishAction' => 'Boolean',
'DisableCsrfSecurityToken' => 'Boolean'
);
$self = $this; /**
* @var array Default values of variables when this page is created
*/
private static $defaults = array(
'Content' => '$UserDefinedForm',
'DisableSaveSubmissions' => 0,
'OnCompleteMessage' => '<p>Thanks, we\'ve received your submission.</p>'
);
$this->beforeUpdateCMSFields(function($fields) use ($self) { /**
* @var array
*/
private static $has_many = array(
"Submissions" => "SubmittedForm",
"EmailRecipients" => "UserDefinedForm_EmailRecipient"
);
// define tabs /**
$fields->findOrMakeTab('Root.FormOptions', _t('UserDefinedForm.CONFIGURATION', 'Configuration')); * @var array
$fields->findOrMakeTab('Root.Recipients', _t('UserDefinedForm.RECIPIENTS', 'Recipients')); * @config
$fields->findOrMakeTab('Root.Submissions', _t('UserDefinedForm.SUBMISSIONS', 'Submissions')); */
private static $casting = array(
'ErrorContainerID' => 'Text'
);
// text to show on complete /**
$onCompleteFieldSet = new CompositeField( * Error container selector which matches the element for grouped messages
$label = new LabelField('OnCompleteMessageLabel',_t('UserDefinedForm.ONCOMPLETELABEL', 'Show on completion')), *
$editor = new HtmlEditorField( 'OnCompleteMessage', '', _t('UserDefinedForm.ONCOMPLETEMESSAGE', $self->OnCompleteMessage)) * @var string
); * @config
*/
private static $error_container_id = 'error-container';
$onCompleteFieldSet->addExtraClass('field'); /**
* The configuration used to determine whether a confirmation message is to
* appear when navigating away from a partially completed form.
*
* @var boolean
* @config
*/
private static $enable_are_you_sure = true;
$editor->setRows(3); /**
$label->addExtraClass('left'); * @var bool
* @config
*/
private static $recipients_warning_enabled = false;
// Define config for email recipients /**
$emailRecipientsConfig = GridFieldConfig_RecordEditor::create(10); * Temporary storage of field ids when the form is duplicated.
$emailRecipientsConfig->getComponentByType('GridFieldAddNewButton') * Example layout: array('EditableCheckbox3' => 'EditableCheckbox14')
->setButtonName( * @var array
_t('UserDefinedForm.ADDEMAILRECIPIENT', 'Add Email Recipient') */
); protected $fieldsFromTo = array();
// who do we email on submission /**
$emailRecipients = new GridField( * @return FieldList
'EmailRecipients', */
_t('UserDefinedForm.EMAILRECIPIENTS', 'Email Recipients'), public function getCMSFields()
$self->EmailRecipients(), {
$emailRecipientsConfig Requirements::css(USERFORMS_DIR . '/css/UserForm_cms.css');
);
$emailRecipients
->getConfig()
->getComponentByType('GridFieldDetailForm')
->setItemRequestClass('UserFormRecipientItemRequest');
$fields->addFieldsToTab('Root.FormOptions', $onCompleteFieldSet); $self = $this;
$fields->addFieldToTab('Root.Recipients', $emailRecipients);
$fields->addFieldsToTab('Root.FormOptions', $self->getFormOptions()); $this->beforeUpdateCMSFields(function ($fields) use ($self) {
// define tabs
$fields->findOrMakeTab('Root.FormOptions', _t('UserDefinedForm.CONFIGURATION', 'Configuration'));
$fields->findOrMakeTab('Root.Recipients', _t('UserDefinedForm.RECIPIENTS', 'Recipients'));
$fields->findOrMakeTab('Root.Submissions', _t('UserDefinedForm.SUBMISSIONS', 'Submissions'));
// text to show on complete
$onCompleteFieldSet = new CompositeField(
$label = new LabelField('OnCompleteMessageLabel', _t('UserDefinedForm.ONCOMPLETELABEL', 'Show on completion')),
$editor = new HtmlEditorField('OnCompleteMessage', '', _t('UserDefinedForm.ONCOMPLETEMESSAGE', $self->OnCompleteMessage))
);
$onCompleteFieldSet->addExtraClass('field');
$editor->setRows(3);
$label->addExtraClass('left');
// Define config for email recipients
$emailRecipientsConfig = GridFieldConfig_RecordEditor::create(10);
$emailRecipientsConfig->getComponentByType('GridFieldAddNewButton')
->setButtonName(
_t('UserDefinedForm.ADDEMAILRECIPIENT', 'Add Email Recipient')
);
// who do we email on submission
$emailRecipients = new GridField(
'EmailRecipients',
_t('UserDefinedForm.EMAILRECIPIENTS', 'Email Recipients'),
$self->EmailRecipients(),
$emailRecipientsConfig
);
$emailRecipients
->getConfig()
->getComponentByType('GridFieldDetailForm')
->setItemRequestClass('UserFormRecipientItemRequest');
$fields->addFieldsToTab('Root.FormOptions', $onCompleteFieldSet);
$fields->addFieldToTab('Root.Recipients', $emailRecipients);
$fields->addFieldsToTab('Root.FormOptions', $self->getFormOptions());
// view the submissions // view the submissions
$submissions = new GridField( $submissions = new GridField(
'Submissions', 'Submissions',
_t('UserDefinedForm.SUBMISSIONS', 'Submissions'), _t('UserDefinedForm.SUBMISSIONS', 'Submissions'),
$self->Submissions()->sort('Created', 'DESC') $self->Submissions()->sort('Created', 'DESC')
); );
// make sure a numeric not a empty string is checked against this int column for SQL server // make sure a numeric not a empty string is checked against this int column for SQL server
$parentID = (!empty($self->ID)) ? (int) $self->ID : 0; $parentID = (!empty($self->ID)) ? (int) $self->ID : 0;
// get a list of all field names and values used for print and export CSV views of the GridField below. // get a list of all field names and values used for print and export CSV views of the GridField below.
$columnSQL = <<<SQL $columnSQL = <<<SQL
SELECT "SubmittedFormField"."Name" as "Name", "SubmittedFormField"."Title" as "Title", COALESCE("EditableFormField"."Sort", 999) AS "Sort" SELECT "SubmittedFormField"."Name" as "Name", "SubmittedFormField"."Title" as "Title", COALESCE("EditableFormField"."Sort", 999) AS "Sort"
FROM "SubmittedFormField" FROM "SubmittedFormField"
LEFT JOIN "SubmittedForm" ON "SubmittedForm"."ID" = "SubmittedFormField"."ParentID" LEFT JOIN "SubmittedForm" ON "SubmittedForm"."ID" = "SubmittedFormField"."ParentID"
@ -183,147 +185,152 @@ LEFT JOIN "EditableFormField" ON "EditableFormField"."Title" = "SubmittedFormFie
WHERE "SubmittedForm"."ParentID" = '$parentID' WHERE "SubmittedForm"."ParentID" = '$parentID'
ORDER BY "Sort", "Title" ORDER BY "Sort", "Title"
SQL; SQL;
// Sanitise periods in title // Sanitise periods in title
$columns = array(); $columns = array();
foreach(DB::query($columnSQL)->map() as $name => $title) { foreach (DB::query($columnSQL)->map() as $name => $title) {
$columns[$name] = trim(strtr($title, '.', ' ')); $columns[$name] = trim(strtr($title, '.', ' '));
} }
$config = new GridFieldConfig(); $config = new GridFieldConfig();
$config->addComponent(new GridFieldToolbarHeader()); $config->addComponent(new GridFieldToolbarHeader());
$config->addComponent($sort = new GridFieldSortableHeader()); $config->addComponent($sort = new GridFieldSortableHeader());
$config->addComponent($filter = new UserFormsGridFieldFilterHeader()); $config->addComponent($filter = new UserFormsGridFieldFilterHeader());
$config->addComponent(new GridFieldDataColumns()); $config->addComponent(new GridFieldDataColumns());
$config->addComponent(new GridFieldEditButton()); $config->addComponent(new GridFieldEditButton());
$config->addComponent(new GridFieldDeleteAction()); $config->addComponent(new GridFieldDeleteAction());
$config->addComponent(new GridFieldPageCount('toolbar-header-right')); $config->addComponent(new GridFieldPageCount('toolbar-header-right'));
$config->addComponent($pagination = new GridFieldPaginator(25)); $config->addComponent($pagination = new GridFieldPaginator(25));
$config->addComponent(new GridFieldDetailForm()); $config->addComponent(new GridFieldDetailForm());
$config->addComponent(new GridFieldButtonRow('after')); $config->addComponent(new GridFieldButtonRow('after'));
$config->addComponent($export = new GridFieldExportButton('buttons-after-left')); $config->addComponent($export = new GridFieldExportButton('buttons-after-left'));
$config->addComponent($print = new GridFieldPrintButton('buttons-after-left')); $config->addComponent($print = new GridFieldPrintButton('buttons-after-left'));
/** /**
* Support for {@link https://github.com/colymba/GridFieldBulkEditingTools} * Support for {@link https://github.com/colymba/GridFieldBulkEditingTools}
*/ */
if(class_exists('GridFieldBulkManager')) { if (class_exists('GridFieldBulkManager')) {
$config->addComponent(new GridFieldBulkManager()); $config->addComponent(new GridFieldBulkManager());
} }
$sort->setThrowExceptionOnBadDataType(false); $sort->setThrowExceptionOnBadDataType(false);
$filter->setThrowExceptionOnBadDataType(false); $filter->setThrowExceptionOnBadDataType(false);
$pagination->setThrowExceptionOnBadDataType(false); $pagination->setThrowExceptionOnBadDataType(false);
// attach every column to the print view form // attach every column to the print view form
$columns['Created'] = 'Created'; $columns['Created'] = 'Created';
$filter->setColumns($columns); $filter->setColumns($columns);
// print configuration // print configuration
$print->setPrintHasHeader(true); $print->setPrintHasHeader(true);
$print->setPrintColumns($columns); $print->setPrintColumns($columns);
// export configuration // export configuration
$export->setCsvHasHeader(true); $export->setCsvHasHeader(true);
$export->setExportColumns($columns); $export->setExportColumns($columns);
$submissions->setConfig($config); $submissions->setConfig($config);
$fields->addFieldToTab('Root.Submissions', $submissions); $fields->addFieldToTab('Root.Submissions', $submissions);
$fields->addFieldToTab('Root.FormOptions', new CheckboxField('DisableSaveSubmissions', _t('UserDefinedForm.SAVESUBMISSIONS', 'Disable Saving Submissions to Server'))); $fields->addFieldToTab('Root.FormOptions', new CheckboxField('DisableSaveSubmissions', _t('UserDefinedForm.SAVESUBMISSIONS', 'Disable Saving Submissions to Server')));
}); });
$fields = parent::getCMSFields(); $fields = parent::getCMSFields();
if($this->EmailRecipients()->Count() == 0 && static::config()->recipients_warning_enabled) { if ($this->EmailRecipients()->Count() == 0 && static::config()->recipients_warning_enabled) {
$fields->addFieldToTab("Root.Main", new LiteralField("EmailRecipientsWarning", $fields->addFieldToTab("Root.Main", new LiteralField("EmailRecipientsWarning",
"<p class=\"message warning\">" . _t("UserDefinedForm.NORECIPIENTS", "<p class=\"message warning\">" . _t("UserDefinedForm.NORECIPIENTS",
"Warning: You have not configured any recipients. Form submissions may be missed.") "Warning: You have not configured any recipients. Form submissions may be missed.")
. "</p>"), "Title"); . "</p>"), "Title");
} }
return $fields; return $fields;
} }
/** /**
* Allow overriding the EmailRecipients on a {@link DataExtension} * Allow overriding the EmailRecipients on a {@link DataExtension}
* so you can customise who receives an email. * so you can customise who receives an email.
* Converts the RelationList to an ArrayList so that manipulation * Converts the RelationList to an ArrayList so that manipulation
* of the original source data isn't possible. * of the original source data isn't possible.
* *
* @return ArrayList * @return ArrayList
*/ */
public function FilteredEmailRecipients($data = null, $form = null) { public function FilteredEmailRecipients($data = null, $form = null)
$recipients = new ArrayList($this->EmailRecipients()->toArray()); {
$recipients = new ArrayList($this->EmailRecipients()->toArray());
// Filter by rules // Filter by rules
$recipients = $recipients->filterByCallback(function($recipient) use ($data, $form) { $recipients = $recipients->filterByCallback(function ($recipient) use ($data, $form) {
return $recipient->canSend($data, $form); return $recipient->canSend($data, $form);
}); });
$this->extend('updateFilteredEmailRecipients', $recipients, $data, $form); $this->extend('updateFilteredEmailRecipients', $recipients, $data, $form);
return $recipients; return $recipients;
} }
/** /**
* Custom options for the form. You can extend the built in options by * Custom options for the form. You can extend the built in options by
* using {@link updateFormOptions()} * using {@link updateFormOptions()}
* *
* @return FieldList * @return FieldList
*/ */
public function getFormOptions() { public function getFormOptions()
$submit = ($this->SubmitButtonText) ? $this->SubmitButtonText : _t('UserDefinedForm.SUBMITBUTTON', 'Submit'); {
$clear = ($this->ClearButtonText) ? $this->ClearButtonText : _t('UserDefinedForm.CLEARBUTTON', 'Clear'); $submit = ($this->SubmitButtonText) ? $this->SubmitButtonText : _t('UserDefinedForm.SUBMITBUTTON', 'Submit');
$clear = ($this->ClearButtonText) ? $this->ClearButtonText : _t('UserDefinedForm.CLEARBUTTON', 'Clear');
$options = new FieldList( $options = new FieldList(
new TextField("SubmitButtonText", _t('UserDefinedForm.TEXTONSUBMIT', 'Text on submit button:'), $submit), new TextField("SubmitButtonText", _t('UserDefinedForm.TEXTONSUBMIT', 'Text on submit button:'), $submit),
new TextField("ClearButtonText", _t('UserDefinedForm.TEXTONCLEAR', 'Text on clear button:'), $clear), new TextField("ClearButtonText", _t('UserDefinedForm.TEXTONCLEAR', 'Text on clear button:'), $clear),
new CheckboxField("ShowClearButton", _t('UserDefinedForm.SHOWCLEARFORM', 'Show Clear Form Button'), $this->ShowClearButton), new CheckboxField("ShowClearButton", _t('UserDefinedForm.SHOWCLEARFORM', 'Show Clear Form Button'), $this->ShowClearButton),
new CheckboxField("EnableLiveValidation", _t('UserDefinedForm.ENABLELIVEVALIDATION', 'Enable live validation')), new CheckboxField("EnableLiveValidation", _t('UserDefinedForm.ENABLELIVEVALIDATION', 'Enable live validation')),
new CheckboxField("HideFieldLabels", _t('UserDefinedForm.HIDEFIELDLABELS', 'Hide field labels')), new CheckboxField("HideFieldLabels", _t('UserDefinedForm.HIDEFIELDLABELS', 'Hide field labels')),
new CheckboxField("DisplayErrorMessagesAtTop", _t('UserDefinedForm.DISPLAYERRORMESSAGESATTOP', 'Display error messages above the form?')), new CheckboxField("DisplayErrorMessagesAtTop", _t('UserDefinedForm.DISPLAYERRORMESSAGESATTOP', 'Display error messages above the form?')),
new CheckboxField('DisableCsrfSecurityToken', _t('UserDefinedForm.DISABLECSRFSECURITYTOKEN', 'Disable CSRF Token')), new CheckboxField('DisableCsrfSecurityToken', _t('UserDefinedForm.DISABLECSRFSECURITYTOKEN', 'Disable CSRF Token')),
new CheckboxField('DisableAuthenicatedFinishAction', _t('UserDefinedForm.DISABLEAUTHENICATEDFINISHACTION', 'Disable Authentication on finish action')) new CheckboxField('DisableAuthenicatedFinishAction', _t('UserDefinedForm.DISABLEAUTHENICATEDFINISHACTION', 'Disable Authentication on finish action'))
); );
$this->extend('updateFormOptions', $options); $this->extend('updateFormOptions', $options);
return $options; return $options;
} }
/** /**
* Get the HTML id of the error container displayed above the form. * Get the HTML id of the error container displayed above the form.
* *
* @return string * @return string
*/ */
public function getErrorContainerID() { public function getErrorContainerID()
return $this->config()->error_container_id; {
} return $this->config()->error_container_id;
}
public function requireDefaultRecords() { public function requireDefaultRecords()
parent::requireDefaultRecords(); {
parent::requireDefaultRecords();
if(!$this->config()->upgrade_on_build) { if (!$this->config()->upgrade_on_build) {
return; return;
} }
// Perform migrations // Perform migrations
Injector::inst() Injector::inst()
->create('UserFormsUpgradeService') ->create('UserFormsUpgradeService')
->setQuiet(true) ->setQuiet(true)
->run(); ->run();
DB::alteration_message('Migrated userforms', 'changed'); DB::alteration_message('Migrated userforms', 'changed');
} }
/** /**
* Validate formfields * Validate formfields
*/ */
public function getCMSValidator() { public function getCMSValidator()
return new UserFormValidator(); {
} return new UserFormValidator();
}
} }
/** /**
@ -332,232 +339,238 @@ SQL;
* @package userforms * @package userforms
*/ */
class UserDefinedForm_Controller extends Page_Controller { class UserDefinedForm_Controller extends Page_Controller
{
private static $finished_anchor = '#uff'; private static $finished_anchor = '#uff';
private static $allowed_actions = array( private static $allowed_actions = array(
'index', 'index',
'ping', 'ping',
'Form', 'Form',
'finished' 'finished'
); );
public function init() { public function init()
parent::init(); {
parent::init();
// load the jquery // load the jquery
$lang = i18n::get_lang_from_locale(i18n::get_locale()); $lang = i18n::get_lang_from_locale(i18n::get_locale());
Requirements::css(USERFORMS_DIR . '/css/UserForm.css'); Requirements::css(USERFORMS_DIR . '/css/UserForm.css');
Requirements::javascript(FRAMEWORK_DIR .'/thirdparty/jquery/jquery.js'); Requirements::javascript(FRAMEWORK_DIR .'/thirdparty/jquery/jquery.js');
Requirements::javascript(USERFORMS_DIR . '/thirdparty/jquery-validate/jquery.validate.min.js'); Requirements::javascript(USERFORMS_DIR . '/thirdparty/jquery-validate/jquery.validate.min.js');
Requirements::add_i18n_javascript(USERFORMS_DIR . '/javascript/lang'); Requirements::add_i18n_javascript(USERFORMS_DIR . '/javascript/lang');
Requirements::javascript(USERFORMS_DIR . '/javascript/UserForm.js'); Requirements::javascript(USERFORMS_DIR . '/javascript/UserForm.js');
Requirements::javascript( Requirements::javascript(
USERFORMS_DIR . "/thirdparty/jquery-validate/localization/messages_{$lang}.min.js" USERFORMS_DIR . "/thirdparty/jquery-validate/localization/messages_{$lang}.min.js"
); );
Requirements::javascript( Requirements::javascript(
USERFORMS_DIR . "/thirdparty/jquery-validate/localization/methods_{$lang}.min.js" USERFORMS_DIR . "/thirdparty/jquery-validate/localization/methods_{$lang}.min.js"
); );
if($this->HideFieldLabels) { if ($this->HideFieldLabels) {
Requirements::javascript(USERFORMS_DIR . '/thirdparty/Placeholders.js/Placeholders.min.js'); Requirements::javascript(USERFORMS_DIR . '/thirdparty/Placeholders.js/Placeholders.min.js');
} }
// Bind a confirmation message when navigating away from a partially completed form. // Bind a confirmation message when navigating away from a partially completed form.
$page = $this->data(); $page = $this->data();
if($page::config()->enable_are_you_sure) { if ($page::config()->enable_are_you_sure) {
Requirements::javascript(USERFORMS_DIR . '/thirdparty/jquery.are-you-sure/jquery.are-you-sure.js'); Requirements::javascript(USERFORMS_DIR . '/thirdparty/jquery.are-you-sure/jquery.are-you-sure.js');
} }
} }
/** /**
* Using $UserDefinedForm in the Content area of the page shows * Using $UserDefinedForm in the Content area of the page shows
* where the form should be rendered into. If it does not exist * where the form should be rendered into. If it does not exist
* then default back to $Form. * then default back to $Form.
* *
* @return array * @return array
*/ */
public function index() { public function index()
if($this->Content && $form = $this->Form()) { {
$hasLocation = stristr($this->Content, '$UserDefinedForm'); if ($this->Content && $form = $this->Form()) {
if($hasLocation) { $hasLocation = stristr($this->Content, '$UserDefinedForm');
$content = preg_replace('/(<p[^>]*>)?\\$UserDefinedForm(<\\/p>)?/i', $form->forTemplate(), $this->Content); if ($hasLocation) {
return array( $content = preg_replace('/(<p[^>]*>)?\\$UserDefinedForm(<\\/p>)?/i', $form->forTemplate(), $this->Content);
'Content' => DBField::create_field('HTMLText', $content), return array(
'Form' => "" 'Content' => DBField::create_field('HTMLText', $content),
); 'Form' => ""
} );
} }
}
return array( return array(
'Content' => DBField::create_field('HTMLText', $this->Content), 'Content' => DBField::create_field('HTMLText', $this->Content),
'Form' => $this->Form() 'Form' => $this->Form()
); );
} }
/** /**
* Keep the session alive for the user. * Keep the session alive for the user.
* *
* @return int * @return int
*/ */
public function ping() { public function ping()
return 1; {
} return 1;
}
/** /**
* Get the form for the page. Form can be modified by calling {@link updateForm()} * Get the form for the page. Form can be modified by calling {@link updateForm()}
* on a UserDefinedForm extension. * on a UserDefinedForm extension.
* *
* @return Forms * @return Forms
*/ */
public function Form() { public function Form()
$form = UserForm::create($this); {
$this->generateConditionalJavascript(); $form = UserForm::create($this);
return $form; $this->generateConditionalJavascript();
} return $form;
}
/** /**
* Generate the javascript for the conditional field show / hiding logic. * Generate the javascript for the conditional field show / hiding logic.
* *
* @return void * @return void
*/ */
public function generateConditionalJavascript() { public function generateConditionalJavascript()
$default = ""; {
$rules = ""; $default = "";
$rules = "";
$watch = array(); $watch = array();
$watchLoad = array(); $watchLoad = array();
if($this->Fields()) { if ($this->Fields()) {
foreach($this->Fields() as $field) { foreach ($this->Fields() as $field) {
$holderSelector = $field->getSelectorHolder(); $holderSelector = $field->getSelectorHolder();
// Is this Field Show by Default // Is this Field Show by Default
if(!$field->ShowOnLoad) { if (!$field->ShowOnLoad) {
$default .= "{$holderSelector}.hide().trigger('userform.field.hide');\n"; $default .= "{$holderSelector}.hide().trigger('userform.field.hide');\n";
} }
// Check for field dependencies / default // Check for field dependencies / default
foreach($field->EffectiveDisplayRules() as $rule) { foreach ($field->EffectiveDisplayRules() as $rule) {
// Get the field which is effected // Get the field which is effected
$formFieldWatch = EditableFormField::get()->byId($rule->ConditionFieldID); $formFieldWatch = EditableFormField::get()->byId($rule->ConditionFieldID);
// Skip deleted fields // Skip deleted fields
if(!$formFieldWatch) { if (!$formFieldWatch) {
continue; continue;
} }
$fieldToWatch = $formFieldWatch->getSelectorField($rule); $fieldToWatch = $formFieldWatch->getSelectorField($rule);
$fieldToWatchOnLoad = $formFieldWatch->getSelectorField($rule, true); $fieldToWatchOnLoad = $formFieldWatch->getSelectorField($rule, true);
// show or hide? // show or hide?
$view = ($rule->Display == 'Hide') ? 'hide' : 'show'; $view = ($rule->Display == 'Hide') ? 'hide' : 'show';
$opposite = ($view == "show") ? "hide" : "show"; $opposite = ($view == "show") ? "hide" : "show";
// what action do we need to keep track of. Something nicer here maybe? // what action do we need to keep track of. Something nicer here maybe?
// @todo encapulsation // @todo encapulsation
$action = "change"; $action = "change";
if($formFieldWatch instanceof EditableTextField) { if ($formFieldWatch instanceof EditableTextField) {
$action = "keyup"; $action = "keyup";
} }
// is this field a special option field // is this field a special option field
$checkboxField = false; $checkboxField = false;
$radioField = false; $radioField = false;
if(in_array($formFieldWatch->ClassName, array('EditableCheckboxGroupField', 'EditableCheckbox'))) { if (in_array($formFieldWatch->ClassName, array('EditableCheckboxGroupField', 'EditableCheckbox'))) {
$action = "click"; $action = "click";
$checkboxField = true; $checkboxField = true;
} else if ($formFieldWatch->ClassName == "EditableRadioField") { } elseif ($formFieldWatch->ClassName == "EditableRadioField") {
$radioField = true; $radioField = true;
} }
// and what should we evaluate // and what should we evaluate
switch($rule->ConditionOption) { switch ($rule->ConditionOption) {
case 'IsNotBlank': case 'IsNotBlank':
$expression = ($checkboxField || $radioField) ? '$(this).is(":checked")' :'$(this).val() != ""'; $expression = ($checkboxField || $radioField) ? '$(this).is(":checked")' :'$(this).val() != ""';
break; break;
case 'IsBlank': case 'IsBlank':
$expression = ($checkboxField || $radioField) ? '!($(this).is(":checked"))' : '$(this).val() == ""'; $expression = ($checkboxField || $radioField) ? '!($(this).is(":checked"))' : '$(this).val() == ""';
break; break;
case 'HasValue': case 'HasValue':
if ($checkboxField) { if ($checkboxField) {
$expression = '$(this).prop("checked")'; $expression = '$(this).prop("checked")';
} else if ($radioField) { } elseif ($radioField) {
// We cannot simply get the value of the radio group, we need to find the checked option first. // We cannot simply get the value of the radio group, we need to find the checked option first.
$expression = '$(this).parents(".field, .control-group").find("input:checked").val()=="'. $rule->FieldValue .'"'; $expression = '$(this).parents(".field, .control-group").find("input:checked").val()=="'. $rule->FieldValue .'"';
} else { } else {
$expression = '$(this).val() == "'. $rule->FieldValue .'"'; $expression = '$(this).val() == "'. $rule->FieldValue .'"';
} }
break; break;
case 'ValueLessThan': case 'ValueLessThan':
$expression = '$(this).val() < parseFloat("'. $rule->FieldValue .'")'; $expression = '$(this).val() < parseFloat("'. $rule->FieldValue .'")';
break; break;
case 'ValueLessThanEqual': case 'ValueLessThanEqual':
$expression = '$(this).val() <= parseFloat("'. $rule->FieldValue .'")'; $expression = '$(this).val() <= parseFloat("'. $rule->FieldValue .'")';
break; break;
case 'ValueGreaterThan': case 'ValueGreaterThan':
$expression = '$(this).val() > parseFloat("'. $rule->FieldValue .'")'; $expression = '$(this).val() > parseFloat("'. $rule->FieldValue .'")';
break; break;
case 'ValueGreaterThanEqual': case 'ValueGreaterThanEqual':
$expression = '$(this).val() >= parseFloat("'. $rule->FieldValue .'")'; $expression = '$(this).val() >= parseFloat("'. $rule->FieldValue .'")';
break; break;
default: // ==HasNotValue default: // ==HasNotValue
if ($checkboxField) { if ($checkboxField) {
$expression = '!$(this).prop("checked")'; $expression = '!$(this).prop("checked")';
} else if ($radioField) { } elseif ($radioField) {
// We cannot simply get the value of the radio group, we need to find the checked option first. // We cannot simply get the value of the radio group, we need to find the checked option first.
$expression = '$(this).parents(".field, .control-group").find("input:checked").val()!="'. $rule->FieldValue .'"'; $expression = '$(this).parents(".field, .control-group").find("input:checked").val()!="'. $rule->FieldValue .'"';
} else { } else {
$expression = '$(this).val() != "'. $rule->FieldValue .'"'; $expression = '$(this).val() != "'. $rule->FieldValue .'"';
} }
break; break;
} }
if(!isset($watch[$fieldToWatch])) { if (!isset($watch[$fieldToWatch])) {
$watch[$fieldToWatch] = array(); $watch[$fieldToWatch] = array();
} }
$watch[$fieldToWatch][] = array( $watch[$fieldToWatch][] = array(
'expression' => $expression, 'expression' => $expression,
'holder_selector' => $holderSelector, 'holder_selector' => $holderSelector,
'view' => $view, 'view' => $view,
'opposite' => $opposite, 'opposite' => $opposite,
'action' => $action 'action' => $action
); );
$watchLoad[$fieldToWatchOnLoad] = true; $watchLoad[$fieldToWatchOnLoad] = true;
} }
} }
} }
if($watch) { if ($watch) {
foreach($watch as $key => $values) { foreach ($watch as $key => $values) {
$logic = array(); $logic = array();
$actions = array(); $actions = array();
foreach($values as $rule) { foreach ($values as $rule) {
// Assign action // Assign action
$actions[$rule['action']] = $rule['action']; $actions[$rule['action']] = $rule['action'];
// Assign behaviour // Assign behaviour
$expression = $rule['expression']; $expression = $rule['expression'];
$holder = $rule['holder_selector']; $holder = $rule['holder_selector'];
$view = $rule['view']; // hide or show $view = $rule['view']; // hide or show
$opposite = $rule['opposite']; $opposite = $rule['opposite'];
// Generated javascript for triggering visibility // Generated javascript for triggering visibility
$logic[] = <<<"EOS" $logic[] = <<<"EOS"
if({$expression}) { if({$expression}) {
{$holder} {$holder}
.{$view}() .{$view}()
@ -568,33 +581,33 @@ if({$expression}) {
.trigger('userform.field.{$opposite}'); .trigger('userform.field.{$opposite}');
} }
EOS; EOS;
} }
$logic = implode("\n", $logic); $logic = implode("\n", $logic);
$rules .= $key.".each(function() {\n $rules .= $key.".each(function() {\n
$(this).data('userformConditions', function() {\n $(this).data('userformConditions', function() {\n
$logic\n $logic\n
}); \n }); \n
});\n"; });\n";
foreach($actions as $action) { foreach ($actions as $action) {
$rules .= $key.".$action(function() { $rules .= $key.".$action(function() {
$(this).data('userformConditions').call(this);\n $(this).data('userformConditions').call(this);\n
});\n"; });\n";
} }
} }
} }
if($watchLoad) { if ($watchLoad) {
foreach($watchLoad as $key => $value) { foreach ($watchLoad as $key => $value) {
$rules .= $key.".each(function() { $rules .= $key.".each(function() {
$(this).data('userformConditions').call(this);\n $(this).data('userformConditions').call(this);\n
});\n"; });\n";
} }
} }
// Only add customScript if $default or $rules is defined // Only add customScript if $default or $rules is defined
if($default || $rules) { if ($default || $rules) {
Requirements::customScript(<<<JS Requirements::customScript(<<<JS
(function($) { (function($) {
$(document).ready(function() { $(document).ready(function() {
$default $default
@ -604,265 +617,266 @@ EOS;
})(jQuery); })(jQuery);
JS JS
, 'UserFormsConditional'); , 'UserFormsConditional');
} }
} }
/** /**
* Process the form that is submitted through the site * Process the form that is submitted through the site
* *
* {@see UserForm::validate()} for validation step prior to processing * {@see UserForm::validate()} for validation step prior to processing
* *
* @param array $data * @param array $data
* @param Form $form * @param Form $form
* *
* @return Redirection * @return Redirection
*/ */
public function process($data, $form) { public function process($data, $form)
$submittedForm = Object::create('SubmittedForm'); {
$submittedForm->SubmittedByID = ($id = Member::currentUserID()) ? $id : 0; $submittedForm = Object::create('SubmittedForm');
$submittedForm->ParentID = $this->ID; $submittedForm->SubmittedByID = ($id = Member::currentUserID()) ? $id : 0;
$submittedForm->ParentID = $this->ID;
// if saving is not disabled save now to generate the ID // if saving is not disabled save now to generate the ID
if(!$this->DisableSaveSubmissions) { if (!$this->DisableSaveSubmissions) {
$submittedForm->write(); $submittedForm->write();
} }
$attachments = array(); $attachments = array();
$submittedFields = new ArrayList(); $submittedFields = new ArrayList();
foreach($this->Fields() as $field) { foreach ($this->Fields() as $field) {
if(!$field->showInReports()) { if (!$field->showInReports()) {
continue; continue;
} }
$submittedField = $field->getSubmittedFormField(); $submittedField = $field->getSubmittedFormField();
$submittedField->ParentID = $submittedForm->ID; $submittedField->ParentID = $submittedForm->ID;
$submittedField->Name = $field->Name; $submittedField->Name = $field->Name;
$submittedField->Title = $field->getField('Title'); $submittedField->Title = $field->getField('Title');
// save the value from the data // save the value from the data
if($field->hasMethod('getValueFromData')) { if ($field->hasMethod('getValueFromData')) {
$submittedField->Value = $field->getValueFromData($data); $submittedField->Value = $field->getValueFromData($data);
} else { } else {
if(isset($data[$field->Name])) { if (isset($data[$field->Name])) {
$submittedField->Value = $data[$field->Name]; $submittedField->Value = $data[$field->Name];
} }
} }
if(!empty($data[$field->Name])) { if (!empty($data[$field->Name])) {
if(in_array("EditableFileField", $field->getClassAncestry())) { if (in_array("EditableFileField", $field->getClassAncestry())) {
if(isset($_FILES[$field->Name])) { if (isset($_FILES[$field->Name])) {
$foldername = $field->getFormField()->getFolderName(); $foldername = $field->getFormField()->getFolderName();
// create the file from post data // create the file from post data
$upload = new Upload(); $upload = new Upload();
$file = new File(); $file = new File();
$file->ShowInSearch = 0; $file->ShowInSearch = 0;
try { try {
$upload->loadIntoFile($_FILES[$field->Name], $file, $foldername); $upload->loadIntoFile($_FILES[$field->Name], $file, $foldername);
} catch( ValidationException $e ) { } catch (ValidationException $e) {
$validationResult = $e->getResult(); $validationResult = $e->getResult();
$form->addErrorMessage($field->Name, $validationResult->message(), 'bad'); $form->addErrorMessage($field->Name, $validationResult->message(), 'bad');
Controller::curr()->redirectBack(); Controller::curr()->redirectBack();
return; return;
} }
// write file to form field // write file to form field
$submittedField->UploadedFileID = $file->ID; $submittedField->UploadedFileID = $file->ID;
// attach a file only if lower than 1MB // attach a file only if lower than 1MB
if($file->getAbsoluteSize() < 1024*1024*1) { if ($file->getAbsoluteSize() < 1024*1024*1) {
$attachments[] = $file; $attachments[] = $file;
} }
} }
} }
} }
$submittedField->extend('onPopulationFromField', $field); $submittedField->extend('onPopulationFromField', $field);
if(!$this->DisableSaveSubmissions) { if (!$this->DisableSaveSubmissions) {
$submittedField->write(); $submittedField->write();
} }
$submittedFields->push($submittedField); $submittedFields->push($submittedField);
} }
$emailData = array( $emailData = array(
"Sender" => Member::currentUser(), "Sender" => Member::currentUser(),
"Fields" => $submittedFields "Fields" => $submittedFields
); );
$this->extend('updateEmailData', $emailData, $attachments); $this->extend('updateEmailData', $emailData, $attachments);
// email users on submit. // email users on submit.
if($recipients = $this->FilteredEmailRecipients($data, $form)) { if ($recipients = $this->FilteredEmailRecipients($data, $form)) {
foreach ($recipients as $recipient) {
$email = new UserFormRecipientEmail($submittedFields);
$mergeFields = $this->getMergeFieldsMap($emailData['Fields']);
if ($attachments) {
foreach ($attachments as $file) {
if ($file->ID != 0) {
$email->attachFile(
$file->Filename,
$file->Filename,
HTTP::get_mime_type($file->Filename)
);
}
}
}
$parsedBody = SSViewer::execute_string($recipient->getEmailBodyContent(), $mergeFields);
foreach($recipients as $recipient) { if (!$recipient->SendPlain && $recipient->emailTemplateExists()) {
$email = new UserFormRecipientEmail($submittedFields); $email->setTemplate($recipient->EmailTemplate);
$mergeFields = $this->getMergeFieldsMap($emailData['Fields']); }
if($attachments) { $email->populateTemplate($recipient);
foreach($attachments as $file) { $email->populateTemplate($emailData);
if($file->ID != 0) { $email->setFrom($recipient->EmailFrom);
$email->attachFile( $email->setBody($parsedBody);
$file->Filename, $email->setTo($recipient->EmailAddress);
$file->Filename, $email->setSubject($recipient->EmailSubject);
HTTP::get_mime_type($file->Filename)
);
}
}
}
$parsedBody = SSViewer::execute_string($recipient->getEmailBodyContent(), $mergeFields); if ($recipient->EmailReplyTo) {
$email->setReplyTo($recipient->EmailReplyTo);
}
if (!$recipient->SendPlain && $recipient->emailTemplateExists()) { // check to see if they are a dynamic reply to. eg based on a email field a user selected
$email->setTemplate($recipient->EmailTemplate); if ($recipient->SendEmailFromField()) {
} $submittedFormField = $submittedFields->find('Name', $recipient->SendEmailFromField()->Name);
$email->populateTemplate($recipient); if ($submittedFormField && is_string($submittedFormField->Value)) {
$email->populateTemplate($emailData); $email->setReplyTo($submittedFormField->Value);
$email->setFrom($recipient->EmailFrom); }
$email->setBody($parsedBody); }
$email->setTo($recipient->EmailAddress); // check to see if they are a dynamic reciever eg based on a dropdown field a user selected
$email->setSubject($recipient->EmailSubject); if ($recipient->SendEmailToField()) {
$submittedFormField = $submittedFields->find('Name', $recipient->SendEmailToField()->Name);
if($recipient->EmailReplyTo) { if ($submittedFormField && is_string($submittedFormField->Value)) {
$email->setReplyTo($recipient->EmailReplyTo); $email->setTo($submittedFormField->Value);
} }
}
// check to see if they are a dynamic reply to. eg based on a email field a user selected // check to see if there is a dynamic subject
if($recipient->SendEmailFromField()) { if ($recipient->SendEmailSubjectField()) {
$submittedFormField = $submittedFields->find('Name', $recipient->SendEmailFromField()->Name); $submittedFormField = $submittedFields->find('Name', $recipient->SendEmailSubjectField()->Name);
if($submittedFormField && is_string($submittedFormField->Value)) { if ($submittedFormField && trim($submittedFormField->Value)) {
$email->setReplyTo($submittedFormField->Value); $email->setSubject($submittedFormField->Value);
} }
} }
// check to see if they are a dynamic reciever eg based on a dropdown field a user selected
if($recipient->SendEmailToField()) {
$submittedFormField = $submittedFields->find('Name', $recipient->SendEmailToField()->Name);
if($submittedFormField && is_string($submittedFormField->Value)) { $this->extend('updateEmail', $email, $recipient, $emailData);
$email->setTo($submittedFormField->Value);
}
}
// check to see if there is a dynamic subject if ($recipient->SendPlain) {
if($recipient->SendEmailSubjectField()) { $body = strip_tags($recipient->getEmailBodyContent()) . "\n";
$submittedFormField = $submittedFields->find('Name', $recipient->SendEmailSubjectField()->Name); if (isset($emailData['Fields']) && !$recipient->HideFormData) {
foreach ($emailData['Fields'] as $Field) {
$body .= $Field->Title .': '. $Field->Value ." \n";
}
}
if($submittedFormField && trim($submittedFormField->Value)) { $email->setBody($body);
$email->setSubject($submittedFormField->Value); $email->sendPlain();
} } else {
} $email->send();
}
}
}
$this->extend('updateEmail', $email, $recipient, $emailData); $submittedForm->extend('updateAfterProcess');
if($recipient->SendPlain) { Session::clear("FormInfo.{$form->FormName()}.errors");
$body = strip_tags($recipient->getEmailBodyContent()) . "\n"; Session::clear("FormInfo.{$form->FormName()}.data");
if(isset($emailData['Fields']) && !$recipient->HideFormData) {
foreach($emailData['Fields'] as $Field) {
$body .= $Field->Title .': '. $Field->Value ." \n";
}
}
$email->setBody($body); $referrer = (isset($data['Referrer'])) ? '?referrer=' . urlencode($data['Referrer']) : "";
$email->sendPlain();
}
else {
$email->send();
}
}
}
$submittedForm->extend('updateAfterProcess'); // set a session variable from the security ID to stop people accessing
// the finished method directly.
if (!$this->DisableAuthenicatedFinishAction) {
if (isset($data['SecurityID'])) {
Session::set('FormProcessed', $data['SecurityID']);
} else {
// if the form has had tokens disabled we still need to set FormProcessed
// to allow us to get through the finshed method
if (!$this->Form()->getSecurityToken()->isEnabled()) {
$randNum = rand(1, 1000);
$randHash = md5($randNum);
Session::set('FormProcessed', $randHash);
Session::set('FormProcessedNum', $randNum);
}
}
}
Session::clear("FormInfo.{$form->FormName()}.errors"); if (!$this->DisableSaveSubmissions) {
Session::clear("FormInfo.{$form->FormName()}.data"); Session::set('userformssubmission'. $this->ID, $submittedForm->ID);
}
$referrer = (isset($data['Referrer'])) ? '?referrer=' . urlencode($data['Referrer']) : ""; return $this->redirect($this->Link('finished') . $referrer . $this->config()->finished_anchor);
}
// set a session variable from the security ID to stop people accessing /**
// the finished method directly. * Allows the use of field values in email body.
if(!$this->DisableAuthenicatedFinishAction) { *
if (isset($data['SecurityID'])) { * @param ArrayList fields
Session::set('FormProcessed',$data['SecurityID']); * @return ArrayData
} else { */
// if the form has had tokens disabled we still need to set FormProcessed private function getMergeFieldsMap($fields = array())
// to allow us to get through the finshed method {
if (!$this->Form()->getSecurityToken()->isEnabled()) { $data = new ArrayData(array());
$randNum = rand(1, 1000);
$randHash = md5($randNum);
Session::set('FormProcessed',$randHash);
Session::set('FormProcessedNum',$randNum);
}
}
}
if(!$this->DisableSaveSubmissions) { foreach ($fields as $field) {
Session::set('userformssubmission'. $this->ID, $submittedForm->ID); $data->setField($field->Name, DBField::create_field('Text', $field->Value));
} }
return $this->redirect($this->Link('finished') . $referrer . $this->config()->finished_anchor); return $data;
} }
/** /**
* Allows the use of field values in email body. * This action handles rendering the "finished" message, which is
* * customizable by editing the ReceivedFormSubmission template.
* @param ArrayList fields *
* @return ArrayData * @return ViewableData
*/ */
private function getMergeFieldsMap($fields = array()) { public function finished()
$data = new ArrayData(array()); {
$submission = Session::get('userformssubmission'. $this->ID);
foreach ($fields as $field) { if ($submission) {
$data->setField($field->Name, DBField::create_field('Text', $field->Value)); $submission = SubmittedForm::get()->byId($submission);
} }
return $data; $referrer = isset($_GET['referrer']) ? urldecode($_GET['referrer']) : null;
}
/** if (!$this->DisableAuthenicatedFinishAction) {
* This action handles rendering the "finished" message, which is $formProcessed = Session::get('FormProcessed');
* customizable by editing the ReceivedFormSubmission template.
*
* @return ViewableData
*/
public function finished() {
$submission = Session::get('userformssubmission'. $this->ID);
if($submission) { if (!isset($formProcessed)) {
$submission = SubmittedForm::get()->byId($submission); return $this->redirect($this->Link() . $referrer);
} } else {
$securityID = Session::get('SecurityID');
// make sure the session matches the SecurityID and is not left over from another form
if ($formProcessed != $securityID) {
// they may have disabled tokens on the form
$securityID = md5(Session::get('FormProcessedNum'));
if ($formProcessed != $securityID) {
return $this->redirect($this->Link() . $referrer);
}
}
}
$referrer = isset($_GET['referrer']) ? urldecode($_GET['referrer']) : null; Session::clear('FormProcessed');
}
if(!$this->DisableAuthenicatedFinishAction) { return $this->customise(array(
$formProcessed = Session::get('FormProcessed'); 'Content' => $this->customise(array(
'Submission' => $submission,
if (!isset($formProcessed)) { 'Link' => $referrer
return $this->redirect($this->Link() . $referrer); ))->renderWith('ReceivedFormSubmission'),
} else { 'Form' => '',
$securityID = Session::get('SecurityID'); ));
// make sure the session matches the SecurityID and is not left over from another form }
if ($formProcessed != $securityID) {
// they may have disabled tokens on the form
$securityID = md5(Session::get('FormProcessedNum'));
if ($formProcessed != $securityID) {
return $this->redirect($this->Link() . $referrer);
}
}
}
Session::clear('FormProcessed');
}
return $this->customise(array(
'Content' => $this->customise(array(
'Submission' => $submission,
'Link' => $referrer
))->renderWith('ReceivedFormSubmission'),
'Form' => '',
));
}
} }

View File

@ -7,53 +7,58 @@
* @package userforms * @package userforms
*/ */
class EditableCheckbox extends EditableFormField { class EditableCheckbox extends EditableFormField
{
private static $singular_name = 'Checkbox Field'; private static $singular_name = 'Checkbox Field';
private static $plural_name = 'Checkboxes'; private static $plural_name = 'Checkboxes';
private static $db = array( private static $db = array(
'CheckedDefault' => 'Boolean' // from CustomSettings 'CheckedDefault' => 'Boolean' // from CustomSettings
); );
/** /**
* @return FieldList * @return FieldList
*/ */
public function getCMSFields() { public function getCMSFields()
$fields = parent::getCMSFields(); {
$fields = parent::getCMSFields();
$fields->replaceField('Default', CheckboxField::create( $fields->replaceField('Default', CheckboxField::create(
"CheckedDefault", "CheckedDefault",
_t('EditableFormField.CHECKEDBYDEFAULT', 'Checked by Default?') _t('EditableFormField.CHECKEDBYDEFAULT', 'Checked by Default?')
)); ));
return $fields; return $fields;
} }
public function getFormField() { public function getFormField()
$field = CheckboxField::create($this->Name, $this->EscapedTitle, $this->CheckedDefault) {
->setFieldHolderTemplate('UserFormsCheckboxField_holder') $field = CheckboxField::create($this->Name, $this->EscapedTitle, $this->CheckedDefault)
->setTemplate('UserFormsCheckboxField'); ->setFieldHolderTemplate('UserFormsCheckboxField_holder')
->setTemplate('UserFormsCheckboxField');
$this->doUpdateFormField($field); $this->doUpdateFormField($field);
return $field; return $field;
} }
public function getValueFromData($data) { public function getValueFromData($data)
$value = (isset($data[$this->Name])) ? $data[$this->Name] : false; {
$value = (isset($data[$this->Name])) ? $data[$this->Name] : false;
return ($value) ? _t('EditableFormField.YES', 'Yes') : _t('EditableFormField.NO', 'No'); return ($value) ? _t('EditableFormField.YES', 'Yes') : _t('EditableFormField.NO', 'No');
} }
public function migrateSettings($data) { public function migrateSettings($data)
// Migrate 'Default' setting to 'CheckedDefault' {
if(isset($data['Default'])) { // Migrate 'Default' setting to 'CheckedDefault'
$this->CheckedDefault = (bool)$data['Default']; if (isset($data['Default'])) {
unset($data['Default']); $this->CheckedDefault = (bool)$data['Default'];
} unset($data['Default']);
}
parent::migrateSettings($data); parent::migrateSettings($data);
} }
} }

View File

@ -7,52 +7,56 @@
* @package userforms * @package userforms
*/ */
class EditableCheckboxGroupField extends EditableMultipleOptionField { class EditableCheckboxGroupField extends EditableMultipleOptionField
{
private static $singular_name = "Checkbox Group"; private static $singular_name = "Checkbox Group";
private static $plural_name = "Checkbox Groups"; private static $plural_name = "Checkbox Groups";
public function getFormField() { public function getFormField()
$field = new UserFormsCheckboxSetField($this->Name, $this->EscapedTitle, $this->getOptionsMap()); {
$field->setFieldHolderTemplate('UserFormsMultipleOptionField_holder'); $field = new UserFormsCheckboxSetField($this->Name, $this->EscapedTitle, $this->getOptionsMap());
$field->setFieldHolderTemplate('UserFormsMultipleOptionField_holder');
// Set the default checked items // Set the default checked items
$defaultCheckedItems = $this->getDefaultOptions(); $defaultCheckedItems = $this->getDefaultOptions();
if ($defaultCheckedItems->count()) { if ($defaultCheckedItems->count()) {
$field->setDefaultItems($defaultCheckedItems->map('EscapedTitle')->keys()); $field->setDefaultItems($defaultCheckedItems->map('EscapedTitle')->keys());
} }
$this->doUpdateFormField($field); $this->doUpdateFormField($field);
return $field; return $field;
} }
public function getValueFromData($data) { public function getValueFromData($data)
$result = ''; {
$entries = (isset($data[$this->Name])) ? $data[$this->Name] : false; $result = '';
$entries = (isset($data[$this->Name])) ? $data[$this->Name] : false;
if($entries) { if ($entries) {
if(!is_array($data[$this->Name])) { if (!is_array($data[$this->Name])) {
$entries = array($data[$this->Name]); $entries = array($data[$this->Name]);
} }
foreach($entries as $selected => $value) { foreach ($entries as $selected => $value) {
if(!$result) { if (!$result) {
$result = $value; $result = $value;
} else { } else {
$result .= ", " . $value; $result .= ", " . $value;
} }
} }
} }
return $result; return $result;
} }
public function getSelectorField(EditableCustomRule $rule, $forOnLoad = false) { public function getSelectorField(EditableCustomRule $rule, $forOnLoad = false)
// watch out for checkboxs as the inputs don't have values but are 'checked {
// @todo - Test this // watch out for checkboxs as the inputs don't have values but are 'checked
if($rule->FieldValue) { // @todo - Test this
return "$(\"input[name='{$this->Name}[]'][value='{$rule->FieldValue}']\")"; if ($rule->FieldValue) {
} else { return "$(\"input[name='{$this->Name}[]'][value='{$rule->FieldValue}']\")";
return "$(\"input[name='{$this->Name}[]']:first\")"; } else {
} return "$(\"input[name='{$this->Name}[]']:first\")";
} }
}
} }

View File

@ -5,45 +5,51 @@
* *
* @package userforms * @package userforms
*/ */
class EditableCountryDropdownField extends EditableFormField { class EditableCountryDropdownField extends EditableFormField
{
private static $singular_name = 'Country Dropdown'; private static $singular_name = 'Country Dropdown';
private static $plural_name = 'Country Dropdowns'; private static $plural_name = 'Country Dropdowns';
/** /**
* @return FieldList * @return FieldList
*/ */
public function getCMSFields() { public function getCMSFields()
$fields = parent::getCMSFields(); {
$fields = parent::getCMSFields();
$fields->removeByName('Default'); $fields->removeByName('Default');
return $fields; return $fields;
} }
public function getFormField() { public function getFormField()
$field = CountryDropdownField::create($this->Name, $this->EscapedTitle) {
->setFieldHolderTemplate('UserFormsField_holder') $field = CountryDropdownField::create($this->Name, $this->EscapedTitle)
->setTemplate('UserFormsDropdownField'); ->setFieldHolderTemplate('UserFormsField_holder')
->setTemplate('UserFormsDropdownField');
$this->doUpdateFormField($field); $this->doUpdateFormField($field);
return $field; return $field;
} }
public function getValueFromData($data) { public function getValueFromData($data)
if(isset($data[$this->Name])) { {
$source = $this->getFormField()->getSource(); if (isset($data[$this->Name])) {
return $source[$data[$this->Name]]; $source = $this->getFormField()->getSource();
} return $source[$data[$this->Name]];
} }
}
public function getIcon() { public function getIcon()
return USERFORMS_DIR . '/images/editabledropdown.png'; {
} return USERFORMS_DIR . '/images/editabledropdown.png';
}
public function getSelectorField(EditableCustomRule $rule, $forOnLoad = false) { public function getSelectorField(EditableCustomRule $rule, $forOnLoad = false)
return "$(\"select[name='{$this->Name}']\")"; {
} return "$(\"select[name='{$this->Name}']\")";
} }
}

View File

@ -7,60 +7,65 @@
* @package userforms * @package userforms
*/ */
class EditableDateField extends EditableFormField { class EditableDateField extends EditableFormField
{
private static $singular_name = 'Date Field'; private static $singular_name = 'Date Field';
private static $plural_name = 'Date Fields'; private static $plural_name = 'Date Fields';
private static $db = array( private static $db = array(
'DefaultToToday' => 'Boolean' // From customsettings 'DefaultToToday' => 'Boolean' // From customsettings
); );
/** /**
* @return FieldList * @return FieldList
*/ */
public function getCMSFields() { public function getCMSFields()
$this->beforeUpdateCMSFields(function(FieldList $fields) { {
$fields->addFieldToTab( $this->beforeUpdateCMSFields(function (FieldList $fields) {
'Root.Main', $fields->addFieldToTab(
CheckboxField::create( 'Root.Main',
'DefaultToToday', CheckboxField::create(
_t('EditableFormField.DEFAULTTOTODAY', 'Default to Today?') 'DefaultToToday',
), _t('EditableFormField.DEFAULTTOTODAY', 'Default to Today?')
'RightTitle' ),
); 'RightTitle'
}); );
});
return parent::getCMSFields(); return parent::getCMSFields();
} }
/** /**
* Return the form field * Return the form field
* *
*/ */
public function getFormField() { public function getFormField()
$defaultValue = $this->DefaultToToday {
? SS_Datetime::now()->Format('Y-m-d') $defaultValue = $this->DefaultToToday
: $this->Default; ? SS_Datetime::now()->Format('Y-m-d')
: $this->Default;
$field = EditableDateField_FormField::create( $this->Name, $this->EscapedTitle, $defaultValue) $field = EditableDateField_FormField::create($this->Name, $this->EscapedTitle, $defaultValue)
->setConfig('showcalendar', true) ->setConfig('showcalendar', true)
->setFieldHolderTemplate('UserFormsField_holder') ->setFieldHolderTemplate('UserFormsField_holder')
->setTemplate('UserFormsField'); ->setTemplate('UserFormsField');
$this->doUpdateFormField($field); $this->doUpdateFormField($field);
return $field; return $field;
} }
} }
/** /**
* @package userforms * @package userforms
*/ */
class EditableDateField_FormField extends DateField { class EditableDateField_FormField extends DateField
{
public function Type() { public function Type()
return "date-alt text"; {
} return "date-alt text";
} }
}

View File

@ -7,41 +7,45 @@
* @package userforms * @package userforms
*/ */
class EditableDropdown extends EditableMultipleOptionField { class EditableDropdown extends EditableMultipleOptionField
{
private static $singular_name = 'Dropdown Field'; private static $singular_name = 'Dropdown Field';
private static $plural_name = 'Dropdowns'; private static $plural_name = 'Dropdowns';
/** /**
* @return FieldList * @return FieldList
*/ */
public function getCMSFields() { public function getCMSFields()
$fields = parent::getCMSFields(); {
$fields = parent::getCMSFields();
$fields->removeByName('Default'); $fields->removeByName('Default');
return $fields; return $fields;
} }
/** /**
* @return DropdownField * @return DropdownField
*/ */
public function getFormField() { public function getFormField()
$field = DropdownField::create($this->Name, $this->EscapedTitle, $this->getOptionsMap()) {
->setFieldHolderTemplate('UserFormsField_holder') $field = DropdownField::create($this->Name, $this->EscapedTitle, $this->getOptionsMap())
->setTemplate('UserFormsDropdownField'); ->setFieldHolderTemplate('UserFormsField_holder')
->setTemplate('UserFormsDropdownField');
// Set default // Set default
$defaultOption = $this->getDefaultOptions()->first(); $defaultOption = $this->getDefaultOptions()->first();
if($defaultOption) { if ($defaultOption) {
$field->setValue($defaultOption->EscapedTitle); $field->setValue($defaultOption->EscapedTitle);
} }
$this->doUpdateFormField($field); $this->doUpdateFormField($field);
return $field; return $field;
} }
public function getSelectorField(EditableCustomRule $rule, $forOnLoad = false) { public function getSelectorField(EditableCustomRule $rule, $forOnLoad = false)
return "$(\"select[name='{$this->Name}']\")"; {
} return "$(\"select[name='{$this->Name}']\")";
} }
}

View File

@ -7,56 +7,61 @@
* @package userforms * @package userforms
*/ */
class EditableEmailField extends EditableFormField { class EditableEmailField extends EditableFormField
{
private static $singular_name = 'Email Field'; private static $singular_name = 'Email Field';
private static $plural_name = 'Email Fields'; private static $plural_name = 'Email Fields';
private static $db = array( private static $db = array(
'Placeholder' => 'Varchar(255)' 'Placeholder' => 'Varchar(255)'
); );
public function getCMSFields() { public function getCMSFields()
$this->beforeUpdateCMSFields(function($fields) { {
$fields->addFieldToTab( $this->beforeUpdateCMSFields(function ($fields) {
'Root.Main', $fields->addFieldToTab(
TextField::create( 'Root.Main',
'Placeholder', TextField::create(
_t('EditableTextField.PLACEHOLDER', 'Placeholder') 'Placeholder',
) _t('EditableTextField.PLACEHOLDER', 'Placeholder')
); )
}); );
});
return parent::getCMSFields(); return parent::getCMSFields();
} }
public function getSetsOwnError() { public function getSetsOwnError()
return true; {
} return true;
}
public function getFormField() { public function getFormField()
$field = EmailField::create($this->Name, $this->EscapedTitle, $this->Default) {
->setFieldHolderTemplate('UserFormsField_holder') $field = EmailField::create($this->Name, $this->EscapedTitle, $this->Default)
->setTemplate('UserFormsField'); ->setFieldHolderTemplate('UserFormsField_holder')
->setTemplate('UserFormsField');
$this->doUpdateFormField($field); $this->doUpdateFormField($field);
return $field; return $field;
} }
/** /**
* Updates a formfield with the additional metadata specified by this field * Updates a formfield with the additional metadata specified by this field
* *
* @param FormField $field * @param FormField $field
*/ */
protected function updateFormField($field) { protected function updateFormField($field)
parent::updateFormField($field); {
parent::updateFormField($field);
$field->setAttribute('data-rule-email', true); $field->setAttribute('data-rule-email', true);
if($this->Placeholder) { if ($this->Placeholder) {
$field->setAttribute('placeholder', $this->Placeholder); $field->setAttribute('placeholder', $this->Placeholder);
} }
} }
} }

View File

@ -3,90 +3,98 @@
/** /**
* Specifies that this ends a group of fields * Specifies that this ends a group of fields
*/ */
class EditableFieldGroup extends EditableFormField { class EditableFieldGroup extends EditableFormField
{
private static $has_one = array( private static $has_one = array(
'End' => 'EditableFieldGroupEnd' 'End' => 'EditableFieldGroupEnd'
); );
/** /**
* Disable selection of group class * Disable selection of group class
* *
* @config * @config
* @var bool * @var bool
*/ */
private static $hidden = true; private static $hidden = true;
/** /**
* Non-data field type * Non-data field type
* *
* @var type * @var type
*/ */
private static $literal = true; private static $literal = true;
public function getCMSFields() { public function getCMSFields()
$fields = parent::getCMSFields(); {
$fields->removeByName(array('MergeField', 'Default', 'Validation', 'DisplayRules')); $fields = parent::getCMSFields();
return $fields; $fields->removeByName(array('MergeField', 'Default', 'Validation', 'DisplayRules'));
} return $fields;
}
public function getCMSTitle() { public function getCMSTitle()
$title = $this->getFieldNumber() {
?: $this->Title $title = $this->getFieldNumber()
?: 'group'; ?: $this->Title
?: 'group';
return _t( return _t(
'EditableFieldGroupEnd.FIELD_GROUP_START', 'EditableFieldGroupEnd.FIELD_GROUP_START',
'Group {group}', 'Group {group}',
array( array(
'group' => $title 'group' => $title
) )
); );
} }
public function getInlineClassnameField($column, $fieldClasses) { public function getInlineClassnameField($column, $fieldClasses)
return new LabelField($column, $this->CMSTitle); {
} return new LabelField($column, $this->CMSTitle);
}
public function showInReports() { public function showInReports()
return false; {
} return false;
}
public function getFormField() { public function getFormField()
$field = UserFormsGroupField::create() {
->setTitle($this->EscapedTitle ?: false) $field = UserFormsGroupField::create()
->setName($this->Name); ->setTitle($this->EscapedTitle ?: false)
$this->doUpdateFormField($field); ->setName($this->Name);
return $field; $this->doUpdateFormField($field);
} return $field;
}
protected function updateFormField($field) { protected function updateFormField($field)
// set the right title on this field {
if($this->RightTitle) { // set the right title on this field
// Since this field expects raw html, safely escape the user data prior if ($this->RightTitle) {
$field->setRightTitle(Convert::raw2xml($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 this field has an extra class
if($this->ExtraClass) { if ($this->ExtraClass) {
$field->addExtraClass($this->ExtraClass); $field->addExtraClass($this->ExtraClass);
} }
} }
protected function onBeforeDelete() { protected function onBeforeDelete()
parent::onBeforeDelete(); {
parent::onBeforeDelete();
// Ensures EndID is lazy-loaded for onAfterDelete // Ensures EndID is lazy-loaded for onAfterDelete
$this->EndID; $this->EndID;
} }
protected function onAfterDelete() { protected function onAfterDelete()
parent::onAfterDelete(); {
parent::onAfterDelete();
// Delete end
if(($end = $this->End()) && $end->exists()) {
$end->delete();
}
}
// Delete end
if (($end = $this->End()) && $end->exists()) {
$end->delete();
}
}
} }

View File

@ -3,92 +3,100 @@
/** /**
* Specifies that this ends a group of fields * Specifies that this ends a group of fields
*/ */
class EditableFieldGroupEnd extends EditableFormField { class EditableFieldGroupEnd extends EditableFormField
{
private static $belongs_to = array( private static $belongs_to = array(
'Group' => 'EditableFieldGroup' 'Group' => 'EditableFieldGroup'
); );
/** /**
* Disable selection of group class * Disable selection of group class
* *
* @config * @config
* @var bool * @var bool
*/ */
private static $hidden = true; private static $hidden = true;
/** /**
* Non-data type * Non-data type
* *
* @config * @config
* @var bool * @var bool
*/ */
private static $literal = true; private static $literal = true;
public function getCMSTitle() { public function getCMSTitle()
$group = $this->Group(); {
return _t( $group = $this->Group();
'EditableFieldGroupEnd.FIELD_GROUP_END', return _t(
'{group} end', 'EditableFieldGroupEnd.FIELD_GROUP_END',
array( '{group} end',
'group' => ($group && $group->exists()) ? $group->CMSTitle : 'Group' array(
) 'group' => ($group && $group->exists()) ? $group->CMSTitle : 'Group'
); )
} );
}
public function getCMSFields() { public function getCMSFields()
$fields = parent::getCMSFields(); {
$fields->removeByName(array('MergeField', 'Default', 'Validation', 'DisplayRules')); $fields = parent::getCMSFields();
return $fields; $fields->removeByName(array('MergeField', 'Default', 'Validation', 'DisplayRules'));
} return $fields;
}
public function getInlineClassnameField($column, $fieldClasses) { public function getInlineClassnameField($column, $fieldClasses)
return new LabelField($column, $this->CMSTitle); {
} return new LabelField($column, $this->CMSTitle);
}
public function getInlineTitleField($column) { public function getInlineTitleField($column)
return HiddenField::create($column); {
} return HiddenField::create($column);
}
public function getFormField() { public function getFormField()
return null; {
} return null;
}
public function showInReports() { public function showInReports()
return false; {
} return false;
}
public function onAfterWrite() { public function onAfterWrite()
parent::onAfterWrite(); {
parent::onAfterWrite();
// If this is not attached to a group, find the first group prior to this // If this is not attached to a group, find the first group prior to this
// with no end attached // with no end attached
$group = $this->Group(); $group = $this->Group();
if(!($group && $group->exists()) && $this->ParentID) { if (!($group && $group->exists()) && $this->ParentID) {
$group = EditableFieldGroup::get() $group = EditableFieldGroup::get()
->filter(array( ->filter(array(
'ParentID' => $this->ParentID, 'ParentID' => $this->ParentID,
'Sort:LessThanOrEqual' => $this->Sort 'Sort:LessThanOrEqual' => $this->Sort
)) ))
->where('"EditableFieldGroup"."EndID" IS NULL OR "EditableFieldGroup"."EndID" = 0') ->where('"EditableFieldGroup"."EndID" IS NULL OR "EditableFieldGroup"."EndID" = 0')
->sort('"Sort" DESC') ->sort('"Sort" DESC')
->first(); ->first();
// When a group is found, attach it to this end // When a group is found, attach it to this end
if($group) { if ($group) {
$group->EndID = $this->ID; $group->EndID = $this->ID;
$group->write(); $group->write();
} }
} }
} }
protected function onAfterDelete() { protected function onAfterDelete()
parent::onAfterDelete(); {
parent::onAfterDelete();
// Delete group
if(($group = $this->Group()) && $group->exists()) {
$group->delete();
}
}
// Delete group
if (($group = $this->Group()) && $group->exists()) {
$group->delete();
}
}
} }

View File

@ -6,99 +6,105 @@
* @package userforms * @package userforms
*/ */
class EditableFileField extends EditableFormField { class EditableFileField extends EditableFormField
{
private static $singular_name = 'File Upload Field'; private static $singular_name = 'File Upload Field';
private static $plural_names = 'File Fields'; private static $plural_names = 'File Fields';
private static $has_one = array( private static $has_one = array(
'Folder' => 'Folder' // From CustomFields 'Folder' => 'Folder' // From CustomFields
); );
/** /**
* Further limit uploadable file extensions in addition to the restrictions * Further limit uploadable file extensions in addition to the restrictions
* imposed by the File.allowed_extensions global configuration. * imposed by the File.allowed_extensions global configuration.
* @config * @config
*/ */
private static $allowed_extensions_blacklist = array( private static $allowed_extensions_blacklist = array(
'htm', 'html', 'xhtml', 'swf', 'xml' 'htm', 'html', 'xhtml', 'swf', 'xml'
); );
/** /**
* @return FieldList * @return FieldList
*/ */
public function getCMSFields() { public function getCMSFields()
$fields = parent::getCMSFields(); {
$fields = parent::getCMSFields();
$fields->addFieldToTab( $fields->addFieldToTab(
'Root.Main', 'Root.Main',
TreeDropdownField::create( TreeDropdownField::create(
'FolderID', 'FolderID',
_t('EditableUploadField.SELECTUPLOADFOLDER', 'Select upload folder'), _t('EditableUploadField.SELECTUPLOADFOLDER', 'Select upload folder'),
'Folder' 'Folder'
) )
); );
$fields->addFieldToTab("Root.Main", new LiteralField("FileUploadWarning", $fields->addFieldToTab("Root.Main", new LiteralField("FileUploadWarning",
"<p class=\"message notice\">" . _t("UserDefinedForm.FileUploadWarning", "<p class=\"message notice\">" . _t("UserDefinedForm.FileUploadWarning",
"Files uploaded through this field could be publicly accessible if the exact URL is known") "Files uploaded through this field could be publicly accessible if the exact URL is known")
. "</p>"), "Type"); . "</p>"), "Type");
return $fields; return $fields;
} }
public function getFormField() { public function getFormField()
{
$field = FileField::create($this->Name, $this->EscapedTitle) $field = FileField::create($this->Name, $this->EscapedTitle)
->setFieldHolderTemplate('UserFormsField_holder') ->setFieldHolderTemplate('UserFormsField_holder')
->setTemplate('UserFormsFileField'); ->setTemplate('UserFormsFileField');
$field->setFieldHolderTemplate('UserFormsField_holder') $field->setFieldHolderTemplate('UserFormsField_holder')
->setTemplate('UserFormsFileField'); ->setTemplate('UserFormsFileField');
$field->getValidator()->setAllowedExtensions( $field->getValidator()->setAllowedExtensions(
array_diff( array_diff(
// filter out '' since this would be a regex problem on JS end // filter out '' since this would be a regex problem on JS end
array_filter(Config::inst()->get('File', 'allowed_extensions')), array_filter(Config::inst()->get('File', 'allowed_extensions')),
$this->config()->allowed_extensions_blacklist $this->config()->allowed_extensions_blacklist
) )
); );
$folder = $this->Folder(); $folder = $this->Folder();
if($folder && $folder->exists()) { if ($folder && $folder->exists()) {
$field->setFolderName( $field->setFolderName(
preg_replace("/^assets\//","", $folder->Filename) preg_replace("/^assets\//", "", $folder->Filename)
); );
} }
$this->doUpdateFormField($field); $this->doUpdateFormField($field);
return $field; return $field;
} }
/** /**
* Return the value for the database, link to the file is stored as a * Return the value for the database, link to the file is stored as a
* relation so value for the field can be null. * relation so value for the field can be null.
* *
* @return string * @return string
*/ */
public function getValueFromData() { public function getValueFromData()
return null; {
} return null;
}
public function getSubmittedFormField() { public function getSubmittedFormField()
return new SubmittedFileField(); {
} return new SubmittedFileField();
}
public function migrateSettings($data) { public function migrateSettings($data)
// Migrate 'Folder' setting to 'FolderID' {
if(isset($data['Folder'])) { // Migrate 'Folder' setting to 'FolderID'
$this->FolderID = $data['Folder']; if (isset($data['Folder'])) {
unset($data['Folder']); $this->FolderID = $data['Folder'];
} unset($data['Folder']);
}
parent::migrateSettings($data); parent::migrateSettings($data);
} }
} }

View File

@ -17,380 +17,387 @@ use SilverStripe\Forms\SegmentField;
* @method UserDefinedForm Parent() Parent page * @method UserDefinedForm Parent() Parent page
* @method DataList DisplayRules() List of EditableCustomRule objects * @method DataList DisplayRules() List of EditableCustomRule objects
*/ */
class EditableFormField extends DataObject { class EditableFormField extends DataObject
{
/** /**
* Set to true to hide from class selector * Set to true to hide from class selector
* *
* @config * @config
* @var bool * @var bool
*/ */
private static $hidden = false; private static $hidden = false;
/** /**
* Define this field as abstract (not inherited) * Define this field as abstract (not inherited)
* *
* @config * @config
* @var bool * @var bool
*/ */
private static $abstract = true; private static $abstract = true;
/** /**
* Flag this field type as non-data (e.g. literal, header, html) * Flag this field type as non-data (e.g. literal, header, html)
* *
* @config * @config
* @var bool * @var bool
*/ */
private static $literal = false; private static $literal = false;
/** /**
* Default sort order * Default sort order
* *
* @config * @config
* @var string * @var string
*/ */
private static $default_sort = '"Sort"'; private static $default_sort = '"Sort"';
/** /**
* A list of CSS classes that can be added * A list of CSS classes that can be added
* *
* @var array * @var array
*/ */
public static $allowed_css = array(); public static $allowed_css = array();
/** /**
* @config * @config
* @var array * @var array
*/ */
private static $summary_fields = array( private static $summary_fields = array(
'Title' 'Title'
); );
/** /**
* @config * @config
* @var array * @var array
*/ */
private static $db = array( private static $db = array(
"Name" => "Varchar", "Name" => "Varchar",
"Title" => "Varchar(255)", "Title" => "Varchar(255)",
"Default" => "Varchar(255)", "Default" => "Varchar(255)",
"Sort" => "Int", "Sort" => "Int",
"Required" => "Boolean", "Required" => "Boolean",
"CustomErrorMessage" => "Varchar(255)", "CustomErrorMessage" => "Varchar(255)",
"CustomRules" => "Text", // @deprecated from 2.0 "CustomRules" => "Text", // @deprecated from 2.0
"CustomSettings" => "Text", // @deprecated from 2.0 "CustomSettings" => "Text", // @deprecated from 2.0
"Migrated" => "Boolean", // set to true when migrated "Migrated" => "Boolean", // set to true when migrated
"ExtraClass" => "Text", // from CustomSettings "ExtraClass" => "Text", // from CustomSettings
"RightTitle" => "Varchar(255)", // from CustomSettings "RightTitle" => "Varchar(255)", // from CustomSettings
"ShowOnLoad" => "Boolean(1)", // from CustomSettings "ShowOnLoad" => "Boolean(1)", // from CustomSettings
); );
private static $defaults = array( private static $defaults = array(
'ShowOnLoad' => true, 'ShowOnLoad' => true,
); );
/** /**
* @config * @config
* @var array * @var array
*/ */
private static $has_one = array( private static $has_one = array(
"Parent" => "UserDefinedForm", "Parent" => "UserDefinedForm",
); );
/** /**
* Built in extensions required * Built in extensions required
* *
* @config * @config
* @var array * @var array
*/ */
private static $extensions = array( private static $extensions = array(
"Versioned('Stage', 'Live')" "Versioned('Stage', 'Live')"
); );
/** /**
* @config * @config
* @var array * @var array
*/ */
private static $has_many = array( private static $has_many = array(
"DisplayRules" => "EditableCustomRule.Parent" // from CustomRules "DisplayRules" => "EditableCustomRule.Parent" // from CustomRules
); );
/** /**
* @var bool * @var bool
*/ */
protected $readonly; protected $readonly;
/** /**
* Set the visibility of an individual form field * Set the visibility of an individual form field
* *
* @param bool * @param bool
*/ */
public function setReadonly($readonly = true) { public function setReadonly($readonly = true)
$this->readonly = $readonly; {
} $this->readonly = $readonly;
}
/** /**
* Returns whether this field is readonly * Returns whether this field is readonly
* *
* @return bool * @return bool
*/ */
private function isReadonly() { private function isReadonly()
return $this->readonly; {
} return $this->readonly;
}
/** /**
* @return FieldList * @return FieldList
*/ */
public function getCMSFields() { public function getCMSFields()
$fields = new FieldList(new TabSet('Root')); {
$fields = new FieldList(new TabSet('Root'));
// Main tab // Main tab
$fields->addFieldsToTab( $fields->addFieldsToTab(
'Root.Main', 'Root.Main',
array( array(
ReadonlyField::create( ReadonlyField::create(
'Type', 'Type',
_t('EditableFormField.TYPE', 'Type'), _t('EditableFormField.TYPE', 'Type'),
$this->i18n_singular_name() $this->i18n_singular_name()
), ),
LiteralField::create( LiteralField::create(
'MergeField', 'MergeField',
_t( _t(
'EditableFormField.MERGEFIELDNAME', 'EditableFormField.MERGEFIELDNAME',
'<div class="field readonly">' . '<div class="field readonly">' .
'<label class="left">Merge field</label>' . '<label class="left">Merge field</label>' .
'<div class="middleColumn">' . '<div class="middleColumn">' .
'<span class="readonly">$' . $this->Name . '</span>' . '<span class="readonly">$' . $this->Name . '</span>' .
'</div>' . '</div>' .
'</div>' '</div>'
) )
), ),
TextField::create('Title'), TextField::create('Title'),
TextField::create('Default', _t('EditableFormField.DEFAULT', 'Default value')), TextField::create('Default', _t('EditableFormField.DEFAULT', 'Default value')),
TextField::create('RightTitle', _t('EditableFormField.RIGHTTITLE', 'Right title')), TextField::create('RightTitle', _t('EditableFormField.RIGHTTITLE', 'Right title')),
SegmentField::create('Name')->setModifiers(array( SegmentField::create('Name')->setModifiers(array(
UnderscoreSegmentFieldModifier::create()->setDefault('FieldName'), UnderscoreSegmentFieldModifier::create()->setDefault('FieldName'),
DisambiguationSegmentFieldModifier::create(), DisambiguationSegmentFieldModifier::create(),
))->setPreview($this->Name) ))->setPreview($this->Name)
) )
); );
// Custom settings // Custom settings
if (!empty(self::$allowed_css)) { if (!empty(self::$allowed_css)) {
$cssList = array(); $cssList = array();
foreach(self::$allowed_css as $k => $v) { foreach (self::$allowed_css as $k => $v) {
if (!is_array($v)) { if (!is_array($v)) {
$cssList[$k]=$v; $cssList[$k]=$v;
} elseif ($k === $this->ClassName) { } elseif ($k === $this->ClassName) {
$cssList = array_merge($cssList, $v); $cssList = array_merge($cssList, $v);
} }
} }
$fields->addFieldToTab('Root.Main', $fields->addFieldToTab('Root.Main',
DropdownField::create( DropdownField::create(
'ExtraClass', 'ExtraClass',
_t('EditableFormField.EXTRACLASS_TITLE', 'Extra Styling/Layout'), _t('EditableFormField.EXTRACLASS_TITLE', 'Extra Styling/Layout'),
$cssList $cssList
)->setDescription(_t( )->setDescription(_t(
'EditableFormField.EXTRACLASS_SELECT', 'EditableFormField.EXTRACLASS_SELECT',
'Select from the list of allowed styles' 'Select from the list of allowed styles'
)) ))
); );
} else { } else {
$fields->addFieldToTab('Root.Main', $fields->addFieldToTab('Root.Main',
TextField::create( TextField::create(
'ExtraClass', 'ExtraClass',
_t('EditableFormField.EXTRACLASS_Title', 'Extra CSS classes') _t('EditableFormField.EXTRACLASS_Title', 'Extra CSS classes')
)->setDescription(_t( )->setDescription(_t(
'EditableFormField.EXTRACLASS_MULTIPLE', 'EditableFormField.EXTRACLASS_MULTIPLE',
'Separate each CSS class with a single space' 'Separate each CSS class with a single space'
)) ))
); );
} }
// Validation // Validation
$validationFields = $this->getFieldValidationOptions(); $validationFields = $this->getFieldValidationOptions();
if($validationFields && $validationFields->count()) { if ($validationFields && $validationFields->count()) {
$fields->addFieldsToTab('Root.Validation', $validationFields); $fields->addFieldsToTab('Root.Validation', $validationFields);
} }
// Add display rule fields // Add display rule fields
$displayFields = $this->getDisplayRuleFields(); $displayFields = $this->getDisplayRuleFields();
if($displayFields && $displayFields->count()) { if ($displayFields && $displayFields->count()) {
$fields->addFieldsToTab('Root.DisplayRules', $displayFields); $fields->addFieldsToTab('Root.DisplayRules', $displayFields);
} }
$this->extend('updateCMSFields', $fields); $this->extend('updateCMSFields', $fields);
return $fields; return $fields;
} }
/** /**
* Return fields to display on the 'Display Rules' tab * Return fields to display on the 'Display Rules' tab
* *
* @return FieldList * @return FieldList
*/ */
protected function getDisplayRuleFields() { protected function getDisplayRuleFields()
// Check display rules {
if($this->Required) { // Check display rules
return new FieldList( if ($this->Required) {
LabelField::create(_t( return new FieldList(
'EditableFormField.DISPLAY_RULES_DISABLED', LabelField::create(_t(
'Display rules are not enabled for required fields. ' . 'EditableFormField.DISPLAY_RULES_DISABLED',
'Please uncheck "Is this field Required?" under "Validation" to re-enable.' 'Display rules are not enabled for required fields. ' .
))->addExtraClass('message warning') 'Please uncheck "Is this field Required?" under "Validation" to re-enable.'
); ))->addExtraClass('message warning')
} );
$self = $this; }
$allowedClasses = array_keys($this->getEditableFieldClasses(false)); $self = $this;
$editableColumns = new GridFieldEditableColumns(); $allowedClasses = array_keys($this->getEditableFieldClasses(false));
$editableColumns->setDisplayFields(array( $editableColumns = new GridFieldEditableColumns();
'Display' => '', $editableColumns->setDisplayFields(array(
'ConditionFieldID' => function($record, $column, $grid) use ($allowedClasses, $self) { 'Display' => '',
return DropdownField::create( 'ConditionFieldID' => function ($record, $column, $grid) use ($allowedClasses, $self) {
$column, return DropdownField::create(
'', $column,
EditableFormField::get() '',
->filter(array( EditableFormField::get()
'ParentID' => $self->ParentID, ->filter(array(
'ClassName' => $allowedClasses 'ParentID' => $self->ParentID,
)) 'ClassName' => $allowedClasses
->exclude(array( ))
'ID' => $self->ID ->exclude(array(
)) 'ID' => $self->ID
->map('ID', 'Title') ))
); ->map('ID', 'Title')
}, );
'ConditionOption' => function($record, $column, $grid) { },
$options = Config::inst()->get('EditableCustomRule', 'condition_options'); 'ConditionOption' => function ($record, $column, $grid) {
return DropdownField::create($column, '', $options); $options = Config::inst()->get('EditableCustomRule', 'condition_options');
}, return DropdownField::create($column, '', $options);
'FieldValue' => function($record, $column, $grid) { },
return TextField::create($column); 'FieldValue' => function ($record, $column, $grid) {
}, return TextField::create($column);
'ParentID' => function($record, $column, $grid) use ($self) { },
return HiddenField::create($column, '', $self->ID); 'ParentID' => function ($record, $column, $grid) use ($self) {
} return HiddenField::create($column, '', $self->ID);
)); }
));
// Custom rules // Custom rules
$customRulesConfig = GridFieldConfig::create() $customRulesConfig = GridFieldConfig::create()
->addComponents( ->addComponents(
$editableColumns, $editableColumns,
new GridFieldButtonRow(), new GridFieldButtonRow(),
new GridFieldToolbarHeader(), new GridFieldToolbarHeader(),
new GridFieldAddNewInlineButton(), new GridFieldAddNewInlineButton(),
new GridFieldDeleteAction() new GridFieldDeleteAction()
); );
return new FieldList( return new FieldList(
CheckboxField::create('ShowOnLoad') CheckboxField::create('ShowOnLoad')
->setDescription(_t( ->setDescription(_t(
'EditableFormField.SHOWONLOAD', 'EditableFormField.SHOWONLOAD',
'Initial visibility before processing these rules' 'Initial visibility before processing these rules'
)), )),
GridField::create( GridField::create(
'DisplayRules', 'DisplayRules',
_t('EditableFormField.CUSTOMRULES', 'Custom Rules'), _t('EditableFormField.CUSTOMRULES', 'Custom Rules'),
$this->DisplayRules(), $this->DisplayRules(),
$customRulesConfig $customRulesConfig
) )
); );
} }
public function onBeforeWrite() { public function onBeforeWrite()
parent::onBeforeWrite(); {
parent::onBeforeWrite();
// Set a field name. // Set a field name.
if(!$this->Name) { if (!$this->Name) {
// New random name // New random name
$this->Name = $this->generateName(); $this->Name = $this->generateName();
} elseif ($this->Name === 'Field') {
throw new ValidationException('Field name cannot be "Field"');
}
} elseif($this->Name === 'Field') { if (!$this->Sort && $this->ParentID) {
throw new ValidationException('Field name cannot be "Field"'); $parentID = $this->ParentID;
} $this->Sort = EditableFormField::get()
->filter('ParentID', $parentID)
->max('Sort') + 1;
}
}
if(!$this->Sort && $this->ParentID) { /**
$parentID = $this->ParentID; * Generate a new non-conflicting Name value
$this->Sort = EditableFormField::get() *
->filter('ParentID', $parentID) * @return string
->max('Sort') + 1; */
} protected function generateName()
} {
do {
// Generate a new random name after this class
$class = get_class($this);
$entropy = substr(sha1(uniqid()), 0, 5);
$name = "{$class}_{$entropy}";
/** // Check if it conflicts
* Generate a new non-conflicting Name value $exists = EditableFormField::get()->filter('Name', $name)->count() > 0;
* } while ($exists);
* @return string return $name;
*/ }
protected function generateName() {
do {
// Generate a new random name after this class
$class = get_class($this);
$entropy = substr(sha1(uniqid()), 0, 5);
$name = "{$class}_{$entropy}";
// Check if it conflicts /**
$exists = EditableFormField::get()->filter('Name', $name)->count() > 0; * Flag indicating that this field will set its own error message via data-msg='' attributes
} while($exists); *
return $name; * @return bool
} */
public function getSetsOwnError()
{
return false;
}
/** /**
* Flag indicating that this field will set its own error message via data-msg='' attributes * Return whether a user can delete this form field
* * based on whether they can edit the page
* @return bool *
*/
public function getSetsOwnError() {
return false;
}
/**
* Return whether a user can delete this form field
* based on whether they can edit the page
*
* @param Member $member * @param Member $member
* @return bool * @return bool
*/ */
public function canDelete($member = null) { public function canDelete($member = null)
return $this->canEdit($member); {
} return $this->canEdit($member);
}
/** /**
* Return whether a user can edit this form field * Return whether a user can edit this form field
* based on whether they can edit the page * based on whether they can edit the page
* *
* @param Member $member * @param Member $member
* @return bool * @return bool
*/ */
public function canEdit($member = null) { public function canEdit($member = null)
{
$parent = $this->Parent(); $parent = $this->Parent();
if($parent && $parent->exists()) { if ($parent && $parent->exists()) {
return $parent->canEdit($member) && !$this->isReadonly(); return $parent->canEdit($member) && !$this->isReadonly();
} else if (!$this->exists() && Controller::has_curr()) { } elseif (!$this->exists() && Controller::has_curr()) {
// This is for GridFieldOrderableRows support as it checks edit permissions on // This is for GridFieldOrderableRows support as it checks edit permissions on
// singleton of the class. Allows editing of User Defined Form pages by // singleton of the class. Allows editing of User Defined Form pages by
// 'Content Authors' and those with permission to edit the UDF page. (ie. CanEditType/EditorGroups) // 'Content Authors' and those with permission to edit the UDF page. (ie. CanEditType/EditorGroups)
// This is to restore User Forms 2.x backwards compatibility. // This is to restore User Forms 2.x backwards compatibility.
$controller = Controller::curr(); $controller = Controller::curr();
if ($controller && $controller instanceof CMSPageEditController) if ($controller && $controller instanceof CMSPageEditController) {
{ $parent = $controller->getRecord($controller->currentPageID());
$parent = $controller->getRecord($controller->currentPageID()); // Only allow this behaviour on pages using UserFormFieldEditorExtension, such
// Only allow this behaviour on pages using UserFormFieldEditorExtension, such // as UserDefinedForm page type.
// as UserDefinedForm page type. if ($parent && $parent->hasExtension('UserFormFieldEditorExtension')) {
if ($parent && $parent->hasExtension('UserFormFieldEditorExtension')) return $parent->canEdit($member);
{ }
return $parent->canEdit($member); }
} }
}
}
// Fallback to secure admin permissions // Fallback to secure admin permissions
return parent::canEdit($member); return parent::canEdit($member);
} }
/** /**
* Return whether a user can view this form field * Return whether a user can view this form field
@ -399,32 +406,34 @@ class EditableFormField extends DataObject {
* @param Member $member * @param Member $member
* @return bool * @return bool
*/ */
public function canView($member = null) { public function canView($member = null)
$parent = $this->Parent(); {
if($parent && $parent->exists()) { $parent = $this->Parent();
return $parent->canView($member); if ($parent && $parent->exists()) {
} return $parent->canView($member);
}
return true; return true;
} }
/** /**
* Return whether a user can create an object of this type * Return whether a user can create an object of this type
* *
* @param Member $member * @param Member $member
* @param array $context Virtual parameter to allow context to be passed in to check * @param array $context Virtual parameter to allow context to be passed in to check
* @return bool * @return bool
*/ */
public function canCreate($member = null) { public function canCreate($member = null)
// Check parent page {
// Check parent page
$parent = $this->getCanCreateContext(func_get_args()); $parent = $this->getCanCreateContext(func_get_args());
if($parent) { if ($parent) {
return $parent->canEdit($member); return $parent->canEdit($member);
} }
// Fall back to secure admin permissions // Fall back to secure admin permissions
return parent::canCreate($member); return parent::canCreate($member);
} }
/** /**
* Helper method to check the parent for this object * Helper method to check the parent for this object
@ -432,13 +441,14 @@ class EditableFormField extends DataObject {
* @param array $args List of arguments passed to canCreate * @param array $args List of arguments passed to canCreate
* @return SiteTree Parent page instance * @return SiteTree Parent page instance
*/ */
protected function getCanCreateContext($args) { protected function getCanCreateContext($args)
{
// Inspect second parameter to canCreate for a 'Parent' context // Inspect second parameter to canCreate for a 'Parent' context
if(isset($args[1]['Parent'])) { if (isset($args[1]['Parent'])) {
return $args[1]['Parent']; return $args[1]['Parent'];
} }
// Hack in currently edited page if context is missing // Hack in currently edited page if context is missing
if(Controller::has_curr() && Controller::curr() instanceof CMSMain) { if (Controller::has_curr() && Controller::curr() instanceof CMSMain) {
return Controller::curr()->currentPage(); return Controller::curr()->currentPage();
} }
@ -452,7 +462,8 @@ class EditableFormField extends DataObject {
* @param Member $member * @param Member $member
* @return bool * @return bool
*/ */
public function canPublish($member = null) { public function canPublish($member = null)
{
return $this->canEdit($member); return $this->canEdit($member);
} }
@ -462,445 +473,483 @@ class EditableFormField extends DataObject {
* @param Member $member * @param Member $member
* @return bool * @return bool
*/ */
public function canUnpublish($member = null) { public function canUnpublish($member = null)
{
return $this->canDelete($member); return $this->canDelete($member);
} }
/** /**
* Publish this Form Field to the live site * Publish this Form Field to the live site
* *
* Wrapper for the {@link Versioned} publish function * Wrapper for the {@link Versioned} publish function
*/ */
public function doPublish($fromStage, $toStage, $createNewVersion = false) { public function doPublish($fromStage, $toStage, $createNewVersion = false)
$this->publish($fromStage, $toStage, $createNewVersion); {
$this->publish($fromStage, $toStage, $createNewVersion);
// Don't forget to publish the related custom rules... // Don't forget to publish the related custom rules...
foreach ($this->DisplayRules() as $rule) { foreach ($this->DisplayRules() as $rule) {
$rule->doPublish($fromStage, $toStage, $createNewVersion); $rule->doPublish($fromStage, $toStage, $createNewVersion);
} }
} }
/** /**
* Delete this field from a given stage * Delete this field from a given stage
* *
* Wrapper for the {@link Versioned} deleteFromStage function * Wrapper for the {@link Versioned} deleteFromStage function
*/ */
public function doDeleteFromStage($stage) { public function doDeleteFromStage($stage)
// Remove custom rules in this stage {
$rules = Versioned::get_by_stage('EditableCustomRule', $stage) // Remove custom rules in this stage
->filter('ParentID', $this->ID); $rules = Versioned::get_by_stage('EditableCustomRule', $stage)
foreach ($rules as $rule) { ->filter('ParentID', $this->ID);
$rule->deleteFromStage($stage); foreach ($rules as $rule) {
} $rule->deleteFromStage($stage);
}
// Remove record // Remove record
$this->deleteFromStage($stage); $this->deleteFromStage($stage);
} }
/** /**
* checks wether record is new, copied from Sitetree * checks wether record is new, copied from Sitetree
*/ */
function isNew() { public function isNew()
if(empty($this->ID)) return true; {
if (empty($this->ID)) {
return true;
}
if(is_numeric($this->ID)) return false; if (is_numeric($this->ID)) {
return false;
}
return stripos($this->ID, 'new') === 0; return stripos($this->ID, 'new') === 0;
} }
/** /**
* checks if records is changed on stage * checks if records is changed on stage
* @return boolean * @return boolean
*/ */
public function getIsModifiedOnStage() { public function getIsModifiedOnStage()
// new unsaved fields could be never be published {
if($this->isNew()) return false; // new unsaved fields could be never be published
if ($this->isNew()) {
return false;
}
$stageVersion = Versioned::get_versionnumber_by_stage('EditableFormField', 'Stage', $this->ID); $stageVersion = Versioned::get_versionnumber_by_stage('EditableFormField', 'Stage', $this->ID);
$liveVersion = Versioned::get_versionnumber_by_stage('EditableFormField', 'Live', $this->ID); $liveVersion = Versioned::get_versionnumber_by_stage('EditableFormField', 'Live', $this->ID);
return ($stageVersion && $stageVersion != $liveVersion); return ($stageVersion && $stageVersion != $liveVersion);
} }
/** /**
* @deprecated since version 4.0 * @deprecated since version 4.0
*/ */
public function getSettings() { public function getSettings()
Deprecation::notice('4.0', 'getSettings is deprecated'); {
return (!empty($this->CustomSettings)) ? unserialize($this->CustomSettings) : array(); Deprecation::notice('4.0', 'getSettings is deprecated');
} return (!empty($this->CustomSettings)) ? unserialize($this->CustomSettings) : array();
}
/** /**
* @deprecated since version 4.0 * @deprecated since version 4.0
*/ */
public function setSettings($settings = array()) { public function setSettings($settings = array())
Deprecation::notice('4.0', 'setSettings is deprecated'); {
$this->CustomSettings = serialize($settings); Deprecation::notice('4.0', 'setSettings is deprecated');
} $this->CustomSettings = serialize($settings);
}
/** /**
* @deprecated since version 4.0 * @deprecated since version 4.0
*/ */
public function setSetting($key, $value) { public function setSetting($key, $value)
Deprecation::notice('4.0', "setSetting({$key}) is deprecated"); {
$settings = $this->getSettings(); Deprecation::notice('4.0', "setSetting({$key}) is deprecated");
$settings[$key] = $value; $settings = $this->getSettings();
$settings[$key] = $value;
$this->setSettings($settings); $this->setSettings($settings);
} }
/** /**
* Set the allowed css classes for the extraClass custom setting * Set the allowed css classes for the extraClass custom setting
* *
* @param array The permissible CSS classes to add * @param array The permissible CSS classes to add
*/ */
public function setAllowedCss(array $allowed) { public function setAllowedCss(array $allowed)
if (is_array($allowed)) { {
foreach ($allowed as $k => $v) { if (is_array($allowed)) {
self::$allowed_css[$k] = (!is_null($v)) ? $v : $k; foreach ($allowed as $k => $v) {
} self::$allowed_css[$k] = (!is_null($v)) ? $v : $k;
} }
} }
}
/** /**
* @deprecated since version 4.0 * @deprecated since version 4.0
*/ */
public function getSetting($setting) { public function getSetting($setting)
Deprecation::notice("4.0", "getSetting({$setting}) is deprecated"); {
Deprecation::notice("4.0", "getSetting({$setting}) is deprecated");
$settings = $this->getSettings(); $settings = $this->getSettings();
if(isset($settings) && count($settings) > 0) { if (isset($settings) && count($settings) > 0) {
if(isset($settings[$setting])) { if (isset($settings[$setting])) {
return $settings[$setting]; return $settings[$setting];
} }
} }
return ''; return '';
} }
/** /**
* Get the path to the icon for this field type, relative to the site root. * Get the path to the icon for this field type, relative to the site root.
* *
* @return string * @return string
*/ */
public function getIcon() { public function getIcon()
return USERFORMS_DIR . '/images/' . strtolower($this->class) . '.png'; {
} return USERFORMS_DIR . '/images/' . strtolower($this->class) . '.png';
}
/** /**
* Return whether or not this field has addable options * Return whether or not this field has addable options
* such as a dropdown field or radio set * such as a dropdown field or radio set
* *
* @return bool * @return bool
*/ */
public function getHasAddableOptions() { public function getHasAddableOptions()
return false; {
} return false;
}
/** /**
* Return whether or not this field needs to show the extra * Return whether or not this field needs to show the extra
* options dropdown list * options dropdown list
* *
* @return bool * @return bool
*/ */
public function showExtraOptions() { public function showExtraOptions()
return true; {
} return true;
}
/** /**
* Returns the Title for rendering in the front-end (with XML values escaped) * Returns the Title for rendering in the front-end (with XML values escaped)
* *
* @return string * @return string
*/ */
public function getEscapedTitle() { public function getEscapedTitle()
return Convert::raw2xml($this->Title); {
} return Convert::raw2xml($this->Title);
}
/** /**
* Find the numeric indicator (1.1.2) that represents it's nesting value * Find the numeric indicator (1.1.2) that represents it's nesting value
* *
* Only useful for fields attached to a current page, and that contain other fields such as pages * Only useful for fields attached to a current page, and that contain other fields such as pages
* or groups * or groups
* *
* @return string * @return string
*/ */
public function getFieldNumber() { public function getFieldNumber()
// Check if exists {
if(!$this->exists()) { // Check if exists
return null; if (!$this->exists()) {
} return null;
// Check parent }
$form = $this->Parent(); // Check parent
if(!$form || !$form->exists() || !($fields = $form->Fields())) { $form = $this->Parent();
return null; if (!$form || !$form->exists() || !($fields = $form->Fields())) {
} return null;
}
$prior = 0; // Number of prior group at this level $prior = 0; // Number of prior group at this level
$stack = array(); // Current stack of nested groups, where the top level = the page $stack = array(); // Current stack of nested groups, where the top level = the page
foreach($fields->map('ID', 'ClassName') as $id => $className) { foreach ($fields->map('ID', 'ClassName') as $id => $className) {
if($className === 'EditableFormStep') { if ($className === 'EditableFormStep') {
$priorPage = empty($stack) ? $prior : $stack[0]; $priorPage = empty($stack) ? $prior : $stack[0];
$stack = array($priorPage + 1); $stack = array($priorPage + 1);
$prior = 0; $prior = 0;
} elseif($className === 'EditableFieldGroup') { } elseif ($className === 'EditableFieldGroup') {
$stack[] = $prior + 1; $stack[] = $prior + 1;
$prior = 0; $prior = 0;
} elseif($className === 'EditableFieldGroupEnd') { } elseif ($className === 'EditableFieldGroupEnd') {
$prior = array_pop($stack); $prior = array_pop($stack);
} }
if($id == $this->ID) { if ($id == $this->ID) {
return implode('.', $stack); return implode('.', $stack);
} }
} }
return null; return null;
} }
public function getCMSTitle() { public function getCMSTitle()
return $this->i18n_singular_name() . ' (' . $this->Title . ')'; {
} return $this->i18n_singular_name() . ' (' . $this->Title . ')';
}
/** /**
* @deprecated since version 4.0 * @deprecated since version 4.0
*/ */
public function getFieldName($field = false) { public function getFieldName($field = false)
Deprecation::notice('4.0', "getFieldName({$field}) is deprecated"); {
return ($field) ? "Fields[".$this->ID."][".$field."]" : "Fields[".$this->ID."]"; Deprecation::notice('4.0', "getFieldName({$field}) is deprecated");
} return ($field) ? "Fields[".$this->ID."][".$field."]" : "Fields[".$this->ID."]";
}
/** /**
* @deprecated since version 4.0 * @deprecated since version 4.0
*/ */
public function getSettingName($field) { public function getSettingName($field)
Deprecation::notice('4.0', "getSettingName({$field}) is deprecated"); {
$name = $this->getFieldName('CustomSettings'); Deprecation::notice('4.0', "getSettingName({$field}) is deprecated");
$name = $this->getFieldName('CustomSettings');
return $name . '[' . $field .']'; return $name . '[' . $field .']';
} }
/** /**
* Append custom validation fields to the default 'Validation' * Append custom validation fields to the default 'Validation'
* section in the editable options view * section in the editable options view
* *
* @return FieldList * @return FieldList
*/ */
public function getFieldValidationOptions() { public function getFieldValidationOptions()
$fields = new FieldList( {
CheckboxField::create('Required', _t('EditableFormField.REQUIRED', 'Is this field Required?')) $fields = new FieldList(
->setDescription(_t('EditableFormField.REQUIRED_DESCRIPTION', 'Please note that conditional fields can\'t be required')), CheckboxField::create('Required', _t('EditableFormField.REQUIRED', 'Is this field Required?'))
TextField::create('CustomErrorMessage', _t('EditableFormField.CUSTOMERROR','Custom Error Message')) ->setDescription(_t('EditableFormField.REQUIRED_DESCRIPTION', 'Please note that conditional fields can\'t be required')),
); TextField::create('CustomErrorMessage', _t('EditableFormField.CUSTOMERROR', 'Custom Error Message'))
);
$this->extend('updateFieldValidationOptions', $fields); $this->extend('updateFieldValidationOptions', $fields);
return $fields; return $fields;
} }
/** /**
* Return a FormField to appear on the front end. Implement on * Return a FormField to appear on the front end. Implement on
* your subclass. * your subclass.
* *
* @return FormField * @return FormField
*/ */
public function getFormField() { public function getFormField()
user_error("Please implement a getFormField() on your EditableFormClass ". $this->ClassName, E_USER_ERROR); {
} user_error("Please implement a getFormField() on your EditableFormClass ". $this->ClassName, E_USER_ERROR);
}
/** /**
* Updates a formfield with extensions * Updates a formfield with extensions
* *
* @param FormField $field * @param FormField $field
*/ */
public function doUpdateFormField($field) { public function doUpdateFormField($field)
$this->extend('beforeUpdateFormField', $field); {
$this->updateFormField($field); $this->extend('beforeUpdateFormField', $field);
$this->extend('afterUpdateFormField', $field); $this->updateFormField($field);
} $this->extend('afterUpdateFormField', $field);
}
/** /**
* Updates a formfield with the additional metadata specified by this field * Updates a formfield with the additional metadata specified by this field
* *
* @param FormField $field * @param FormField $field
*/ */
protected function updateFormField($field) { protected function updateFormField($field)
// set the error / formatting messages {
$field->setCustomValidationMessage($this->getErrorMessage()->RAW()); // set the error / formatting messages
$field->setCustomValidationMessage($this->getErrorMessage()->RAW());
// set the right title on this field // set the right title on this field
if($this->RightTitle) { if ($this->RightTitle) {
// Since this field expects raw html, safely escape the user data prior // Since this field expects raw html, safely escape the user data prior
$field->setRightTitle(Convert::raw2xml($this->RightTitle)); $field->setRightTitle(Convert::raw2xml($this->RightTitle));
} }
// if this field is required add some // if this field is required add some
if($this->Required) { if ($this->Required) {
// Required validation can conflict so add the Required validation messages as input attributes // Required validation can conflict so add the Required validation messages as input attributes
$errorMessage = $this->getErrorMessage()->HTML(); $errorMessage = $this->getErrorMessage()->HTML();
$field->addExtraClass('requiredField'); $field->addExtraClass('requiredField');
$field->setAttribute('data-rule-required', 'true'); $field->setAttribute('data-rule-required', 'true');
$field->setAttribute('data-msg-required', $errorMessage); $field->setAttribute('data-msg-required', $errorMessage);
if($identifier = UserDefinedForm::config()->required_identifier) { if ($identifier = UserDefinedForm::config()->required_identifier) {
$title = $field->Title() . " <span class='required-identifier'>". $identifier . "</span>"; $title = $field->Title() . " <span class='required-identifier'>". $identifier . "</span>";
$field->setTitle($title); $field->setTitle($title);
} }
} }
// if this field has an extra class // if this field has an extra class
if($this->ExtraClass) { if ($this->ExtraClass) {
$field->addExtraClass($this->ExtraClass); $field->addExtraClass($this->ExtraClass);
} }
} }
/** /**
* Return the instance of the submission field class * Return the instance of the submission field class
* *
* @return SubmittedFormField * @return SubmittedFormField
*/ */
public function getSubmittedFormField() { public function getSubmittedFormField()
return new SubmittedFormField(); {
} return new SubmittedFormField();
}
/** /**
* Show this form field (and its related value) in the reports and in emails. * Show this form field (and its related value) in the reports and in emails.
* *
* @return bool * @return bool
*/ */
public function showInReports() { public function showInReports()
return true; {
} return true;
}
/** /**
* Return the error message for this field. Either uses the custom * Return the error message for this field. Either uses the custom
* one (if provided) or the default SilverStripe message * one (if provided) or the default SilverStripe message
* *
* @return Varchar * @return Varchar
*/ */
public function getErrorMessage() { public function getErrorMessage()
$title = strip_tags("'". ($this->Title ? $this->Title : $this->Name) . "'"); {
$standard = sprintf(_t('Form.FIELDISREQUIRED', '%s is required').'.', $title); $title = strip_tags("'". ($this->Title ? $this->Title : $this->Name) . "'");
$standard = sprintf(_t('Form.FIELDISREQUIRED', '%s is required').'.', $title);
// only use CustomErrorMessage if it has a non empty value // only use CustomErrorMessage if it has a non empty value
$errorMessage = (!empty($this->CustomErrorMessage)) ? $this->CustomErrorMessage : $standard; $errorMessage = (!empty($this->CustomErrorMessage)) ? $this->CustomErrorMessage : $standard;
return DBField::create_field('Varchar', $errorMessage); return DBField::create_field('Varchar', $errorMessage);
} }
/** /**
* Invoked by UserFormUpgradeService to migrate settings specific to this field from CustomSettings * Invoked by UserFormUpgradeService to migrate settings specific to this field from CustomSettings
* to the field proper * to the field proper
* *
* @param array $data Unserialised data * @param array $data Unserialised data
*/ */
public function migrateSettings($data) { public function migrateSettings($data)
// Map 'Show' / 'Hide' to boolean {
if(isset($data['ShowOnLoad'])) { // Map 'Show' / 'Hide' to boolean
$this->ShowOnLoad = $data['ShowOnLoad'] === '' || ($data['ShowOnLoad'] && $data['ShowOnLoad'] !== 'Hide'); if (isset($data['ShowOnLoad'])) {
unset($data['ShowOnLoad']); $this->ShowOnLoad = $data['ShowOnLoad'] === '' || ($data['ShowOnLoad'] && $data['ShowOnLoad'] !== 'Hide');
} unset($data['ShowOnLoad']);
}
// Migrate all other settings // Migrate all other settings
foreach($data as $key => $value) { foreach ($data as $key => $value) {
if($this->hasField($key)) { if ($this->hasField($key)) {
$this->setField($key, $value); $this->setField($key, $value);
} }
} }
} }
/** /**
* Get the formfield to use when editing this inline in gridfield * Get the formfield to use when editing this inline in gridfield
* *
* @param string $column name of column * @param string $column name of column
* @param array $fieldClasses List of allowed classnames if this formfield has a selectable class * @param array $fieldClasses List of allowed classnames if this formfield has a selectable class
* @return FormField * @return FormField
*/ */
public function getInlineClassnameField($column, $fieldClasses) { public function getInlineClassnameField($column, $fieldClasses)
return DropdownField::create($column, false, $fieldClasses); {
} return DropdownField::create($column, false, $fieldClasses);
}
/** /**
* Get the formfield to use when editing the title inline * Get the formfield to use when editing the title inline
* *
* @param string $column * @param string $column
* @return FormField * @return FormField
*/ */
public function getInlineTitleField($column) { public function getInlineTitleField($column)
return TextField::create($column, false) {
->setAttribute('placeholder', _t('EditableFormField.TITLE', 'Title')) return TextField::create($column, false)
->setAttribute('data-placeholder', _t('EditableFormField.TITLE', 'Title')); ->setAttribute('placeholder', _t('EditableFormField.TITLE', 'Title'))
} ->setAttribute('data-placeholder', _t('EditableFormField.TITLE', 'Title'));
}
/** /**
* Get the JS expression for selecting the holder for this field * Get the JS expression for selecting the holder for this field
* *
* @return string * @return string
*/ */
public function getSelectorHolder() { public function getSelectorHolder()
return "$(\"#{$this->Name}\")"; {
} return "$(\"#{$this->Name}\")";
}
/** /**
* Gets the JS expression for selecting the value for this field * Gets the JS expression for selecting the value for this field
* *
* @param EditableCustomRule $rule Custom rule this selector will be used with * @param EditableCustomRule $rule Custom rule this selector will be used with
* @param bool $forOnLoad Set to true if this will be invoked on load * @param bool $forOnLoad Set to true if this will be invoked on load
*/ */
public function getSelectorField(EditableCustomRule $rule, $forOnLoad = false) { public function getSelectorField(EditableCustomRule $rule, $forOnLoad = false)
return "$(\"input[name='{$this->Name}']\")"; {
} return "$(\"input[name='{$this->Name}']\")";
}
/** /**
* Get the list of classes that can be selected and used as data-values * Get the list of classes that can be selected and used as data-values
* *
* @param $includeLiterals Set to false to exclude non-data fields * @param $includeLiterals Set to false to exclude non-data fields
* @return array * @return array
*/ */
public function getEditableFieldClasses($includeLiterals = true) { public function getEditableFieldClasses($includeLiterals = true)
$classes = ClassInfo::getValidSubClasses('EditableFormField'); {
$classes = ClassInfo::getValidSubClasses('EditableFormField');
// Remove classes we don't want to display in the dropdown. // Remove classes we don't want to display in the dropdown.
$editableFieldClasses = array(); $editableFieldClasses = array();
foreach ($classes as $class) { foreach ($classes as $class) {
// Skip abstract / hidden classes // Skip abstract / hidden classes
if(Config::inst()->get($class, 'abstract', Config::UNINHERITED) || Config::inst()->get($class, 'hidden') if (Config::inst()->get($class, 'abstract', Config::UNINHERITED) || Config::inst()->get($class, 'hidden')
) { ) {
continue; continue;
} }
if(!$includeLiterals && Config::inst()->get($class, 'literal')) { if (!$includeLiterals && Config::inst()->get($class, 'literal')) {
continue; continue;
} }
$singleton = singleton($class); $singleton = singleton($class);
if(!$singleton->canCreate()) { if (!$singleton->canCreate()) {
continue; continue;
} }
$editableFieldClasses[$class] = $singleton->i18n_singular_name(); $editableFieldClasses[$class] = $singleton->i18n_singular_name();
} }
asort($editableFieldClasses); asort($editableFieldClasses);
return $editableFieldClasses; return $editableFieldClasses;
} }
/** /**
* @return EditableFormFieldValidator * @return EditableFormFieldValidator
*/ */
public function getCMSValidator() { public function getCMSValidator()
return EditableFormFieldValidator::create() {
->setRecord($this); return EditableFormFieldValidator::create()
} ->setRecord($this);
}
/**
* Determine effective display rules for this field.
*
* @return SS_List
*/
public function EffectiveDisplayRules() {
if($this->Required) {
return new ArrayList();
}
return $this->DisplayRules();
}
/**
* Determine effective display rules for this field.
*
* @return SS_List
*/
public function EffectiveDisplayRules()
{
if ($this->Required) {
return new ArrayList();
}
return $this->DisplayRules();
}
} }

View File

@ -1,61 +1,65 @@
<?php <?php
class EditableFormFieldValidator extends RequiredFields { class EditableFormFieldValidator extends RequiredFields
{
/** /**
* *
* @var EditableFormField * @var EditableFormField
*/ */
protected $record = null; protected $record = null;
/** /**
* *
* @param EditableFormField $record * @param EditableFormField $record
* @return $this * @return $this
*/ */
public function setRecord($record) { public function setRecord($record)
$this->record = $record; {
return $this; $this->record = $record;
} return $this;
}
/* /*
* @return EditableFormField * @return EditableFormField
*/ */
public function getRecord() { public function getRecord()
return $this->record; {
} return $this->record;
}
public function php($data) { public function php($data)
if(!parent::php($data)) { {
return false; if (!parent::php($data)) {
} return false;
}
// Skip unsaved records // Skip unsaved records
if(!$this->record || !$this->record->exists()) { if (!$this->record || !$this->record->exists()) {
return true; return true;
} }
// Skip validation if not required // Skip validation if not required
if(empty($data['Required'])) { if (empty($data['Required'])) {
return; return;
} }
// Skip validation if no rules // Skip validation if no rules
$count = EditableCustomRule::get()->filter('ParentID', $this->record->ID)->count(); $count = EditableCustomRule::get()->filter('ParentID', $this->record->ID)->count();
if($count == 0) { if ($count == 0) {
return true; return true;
} }
// Both required = true and rules > 0 should error // Both required = true and rules > 0 should error
$this->validationError( $this->validationError(
'Required_Error', 'Required_Error',
_t( _t(
"EditableFormFieldValidator.REQUIRED_ERROR", "EditableFormFieldValidator.REQUIRED_ERROR",
"Form fields cannot be required and have conditional display rules." "Form fields cannot be required and have conditional display rules."
), ),
'error' 'error'
); );
return false; return false;
} }
} }

View File

@ -5,89 +5,97 @@
* @package userforms * @package userforms
*/ */
class EditableFormHeading extends EditableFormField { class EditableFormHeading extends EditableFormField
{
private static $singular_name = 'Heading'; private static $singular_name = 'Heading';
private static $plural_name = 'Headings'; private static $plural_name = 'Headings';
private static $literal = true; private static $literal = true;
private static $db = array( private static $db = array(
'Level' => 'Int(3)', // From CustomSettings 'Level' => 'Int(3)', // From CustomSettings
'HideFromReports' => 'Boolean(0)' // from CustomSettings 'HideFromReports' => 'Boolean(0)' // from CustomSettings
); );
private static $defaults = array( private static $defaults = array(
'Level' => 3, 'Level' => 3,
'HideFromReports' => false 'HideFromReports' => false
); );
/** /**
* @return FieldList * @return FieldList
*/ */
public function getCMSFields() { public function getCMSFields()
$fields = parent::getCMSFields(); {
$fields = parent::getCMSFields();
$fields->removeByName(array('Default', 'Validation', 'RightTitle')); $fields->removeByName(array('Default', 'Validation', 'RightTitle'));
$levels = array( $levels = array(
'1' => '1', '1' => '1',
'2' => '2', '2' => '2',
'3' => '3', '3' => '3',
'4' => '4', '4' => '4',
'5' => '5', '5' => '5',
'6' => '6' '6' => '6'
); );
$fields->addFieldsToTab('Root.Main', array( $fields->addFieldsToTab('Root.Main', array(
DropdownField::create( DropdownField::create(
'Level', 'Level',
_t('EditableFormHeading.LEVEL', 'Select Heading Level'), _t('EditableFormHeading.LEVEL', 'Select Heading Level'),
$levels $levels
), ),
CheckboxField::create( CheckboxField::create(
'HideFromReports', 'HideFromReports',
_t('EditableLiteralField.HIDEFROMREPORT', 'Hide from reports?') _t('EditableLiteralField.HIDEFROMREPORT', 'Hide from reports?')
) )
)); ));
return $fields; return $fields;
} }
public function getFormField() { public function getFormField()
$labelField = new HeaderField($this->Name, $this->EscapedTitle, $this->Level); {
$labelField->addExtraClass('FormHeading'); $labelField = new HeaderField($this->Name, $this->EscapedTitle, $this->Level);
$labelField->setAttribute('data-id', $this->Name); $labelField->addExtraClass('FormHeading');
$this->doUpdateFormField($labelField); $labelField->setAttribute('data-id', $this->Name);
return $labelField; $this->doUpdateFormField($labelField);
} return $labelField;
}
protected function updateFormField($field) { protected function updateFormField($field)
// set the right title on this field {
if($this->RightTitle) { // set the right title on this field
// Since this field expects raw html, safely escape the user data prior if ($this->RightTitle) {
$field->setRightTitle(Convert::raw2xml($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($this->ExtraClass) { // if this field has an extra class
$field->addExtraClass($this->ExtraClass); if ($this->ExtraClass) {
} $field->addExtraClass($this->ExtraClass);
} }
}
public function showInReports() { public function showInReports()
return !$this->HideFromReports; {
} return !$this->HideFromReports;
}
public function getFieldValidationOptions() { public function getFieldValidationOptions()
return false; {
} return false;
}
public function getSelectorHolder() { public function getSelectorHolder()
return "$(\":header[data-id='{$this->Name}']\")"; {
} return "$(\":header[data-id='{$this->Name}']\")";
}
public function getLevel() { public function getLevel()
return $this->getField('Level') ?: 3; {
} return $this->getField('Level') ?: 3;
}
} }

View File

@ -4,83 +4,91 @@
* *
* @package userforms * @package userforms
*/ */
class EditableFormStep extends EditableFormField { class EditableFormStep extends EditableFormField
{
private static $singular_name = 'Page Break'; private static $singular_name = 'Page Break';
private static $plural_name = 'Page Breaks'; private static $plural_name = 'Page Breaks';
/** /**
* Disable selection of step class * Disable selection of step class
* *
* @config * @config
* @var bool * @var bool
*/ */
private static $hidden = true; private static $hidden = true;
/** /**
* @return FieldList * @return FieldList
*/ */
public function getCMSFields() { public function getCMSFields()
$fields = parent::getCMSFields(); {
$fields = parent::getCMSFields();
$fields->removeByName(array('MergeField', 'Default', 'Validation', 'RightTitle')); $fields->removeByName(array('MergeField', 'Default', 'Validation', 'RightTitle'));
return $fields; return $fields;
} }
/** /**
* @return FormField * @return FormField
*/ */
public function getFormField() { public function getFormField()
$field = UserFormsStepField::create() {
->setName($this->Name) $field = UserFormsStepField::create()
->setTitle($this->EscapedTitle); ->setName($this->Name)
$this->doUpdateFormField($field); ->setTitle($this->EscapedTitle);
return $field; $this->doUpdateFormField($field);
} return $field;
}
protected function updateFormField($field) { protected function updateFormField($field)
// if this field has an extra class {
if($this->ExtraClass) { // if this field has an extra class
$field->addExtraClass($this->ExtraClass); if ($this->ExtraClass) {
} $field->addExtraClass($this->ExtraClass);
} }
}
/** /**
* @return boolean * @return boolean
*/ */
public function showInReports() { public function showInReports()
return false; {
} return false;
}
public function getInlineClassnameField($column, $fieldClasses) { public function getInlineClassnameField($column, $fieldClasses)
return new LabelField( {
$column, return new LabelField(
$this->CMSTitle $column,
); $this->CMSTitle
} );
}
public function getCMSTitle() { public function getCMSTitle()
$title = $this->getFieldNumber() {
?: $this->Title $title = $this->getFieldNumber()
?: ''; ?: $this->Title
?: '';
return _t( return _t(
'EditableFormStep.STEP_TITLE', 'EditableFormStep.STEP_TITLE',
'Page {page}', 'Page {page}',
array( array(
'page' => $title 'page' => $title
) )
); );
} }
/** /**
* Get the JS expression for selecting the holder for this field * Get the JS expression for selecting the holder for this field
* *
* @return string * @return string
*/ */
public function getSelectorHolder() { public function getSelectorHolder()
return "$(\".step-button-wrapper[data-for='{$this->Name}']\")"; {
} return "$(\".step-button-wrapper[data-for='{$this->Name}']\")";
}
} }

View File

@ -7,142 +7,154 @@
* @package userforms * @package userforms
*/ */
class EditableLiteralField extends EditableFormField { class EditableLiteralField extends EditableFormField
{
private static $singular_name = 'HTML Block'; private static $singular_name = 'HTML Block';
private static $plural_name = 'HTML Blocks'; private static $plural_name = 'HTML Blocks';
/** /**
* Mark as literal only * Mark as literal only
* *
* @config * @config
* @var bool * @var bool
*/ */
private static $literal = true; private static $literal = true;
/** /**
* Get the name of the editor config to use for HTML sanitisation. Defaults to the active config. * Get the name of the editor config to use for HTML sanitisation. Defaults to the active config.
* *
* @var string * @var string
* @config * @config
*/ */
private static $editor_config = null; private static $editor_config = null;
private static $db = array( private static $db = array(
'Content' => 'HTMLText', // From CustomSettings 'Content' => 'HTMLText', // From CustomSettings
'HideFromReports' => 'Boolean(0)', // from CustomSettings 'HideFromReports' => 'Boolean(0)', // from CustomSettings
'HideLabel' => 'Boolean(0)' 'HideLabel' => 'Boolean(0)'
); );
private static $defaults = array( private static $defaults = array(
'HideFromReports' => false 'HideFromReports' => false
); );
/** /**
* Returns the {@see HtmlEditorConfig} instance to use for sanitisation * Returns the {@see HtmlEditorConfig} instance to use for sanitisation
* *
* @return HtmlEditorConfig * @return HtmlEditorConfig
*/ */
protected function getEditorConfig() { protected function getEditorConfig()
$editorConfig = $this->config()->editor_config; {
if($editorConfig) return HtmlEditorConfig::get($editorConfig); $editorConfig = $this->config()->editor_config;
return HtmlEditorConfig::get_active(); if ($editorConfig) {
} return HtmlEditorConfig::get($editorConfig);
}
return HtmlEditorConfig::get_active();
}
/** /**
* Safely sanitise html content, if enabled * Safely sanitise html content, if enabled
* *
* @param string $content Raw html * @param string $content Raw html
* @return string Safely sanitised html * @return string Safely sanitised html
*/ */
protected function sanitiseContent($content) { protected function sanitiseContent($content)
// Check if sanitisation is enabled {
if(!HtmlEditorField::config()->sanitise_server_side) return $content; // Check if sanitisation is enabled
if (!HtmlEditorField::config()->sanitise_server_side) {
return $content;
}
// Perform sanitisation // Perform sanitisation
$htmlValue = Injector::inst()->create('HTMLValue', $content); $htmlValue = Injector::inst()->create('HTMLValue', $content);
$santiser = Injector::inst()->create('HtmlEditorSanitiser', $this->getEditorConfig()); $santiser = Injector::inst()->create('HtmlEditorSanitiser', $this->getEditorConfig());
$santiser->sanitise($htmlValue); $santiser->sanitise($htmlValue);
return $htmlValue->getContent(); return $htmlValue->getContent();
} }
/** /**
* Get HTML Content of this literal field * Get HTML Content of this literal field
* *
* @return string * @return string
*/ */
public function getContent() { public function getContent()
// Apply html editor sanitisation rules {
$content = $this->getField('Content'); // Apply html editor sanitisation rules
return $this->sanitiseContent($content); $content = $this->getField('Content');
} return $this->sanitiseContent($content);
}
/** /**
* Set the content with the given value * Set the content with the given value
* *
* @param string $content * @param string $content
*/ */
public function setContent($content) { public function setContent($content)
// Apply html editor sanitisation rules {
$content = $this->sanitiseContent($content); // Apply html editor sanitisation rules
$this->setField('Content', $content); $content = $this->sanitiseContent($content);
} $this->setField('Content', $content);
}
/** /**
* @return FieldList * @return FieldList
*/ */
public function getCMSFields() { public function getCMSFields()
$fields = parent::getCMSFields(); {
$fields = parent::getCMSFields();
$fields->removeByName(array('Default', 'Validation', 'RightTitle')); $fields->removeByName(array('Default', 'Validation', 'RightTitle'));
$fields->addFieldsToTab('Root.Main', array( $fields->addFieldsToTab('Root.Main', array(
HTMLEditorField::create('Content', _t('EditableLiteralField.CONTENT', 'HTML')) HTMLEditorField::create('Content', _t('EditableLiteralField.CONTENT', 'HTML'))
->setRows(4) ->setRows(4)
->setColumns(20), ->setColumns(20),
CheckboxField::create( CheckboxField::create(
'HideFromReports', 'HideFromReports',
_t('EditableLiteralField.HIDEFROMREPORT', 'Hide from reports?') _t('EditableLiteralField.HIDEFROMREPORT', 'Hide from reports?')
), ),
CheckboxField::create( CheckboxField::create(
'HideLabel', 'HideLabel',
_t('EditableLiteralField.HIDELABEL', "Hide 'Title' label on frontend?") _t('EditableLiteralField.HIDELABEL', "Hide 'Title' label on frontend?")
) )
)); ));
return $fields; return $fields;
} }
public function getFormField() { public function getFormField()
// Build label and css classes {
$label = ''; // Build label and css classes
$classes = $this->ExtraClass; $label = '';
if(empty($this->Title) || $this->HideLabel) { $classes = $this->ExtraClass;
$classes .= " nolabel"; if (empty($this->Title) || $this->HideLabel) {
} else { $classes .= " nolabel";
$label = "<label class='left'>{$this->EscapedTitle}</label>"; } else {
} $label = "<label class='left'>{$this->EscapedTitle}</label>";
}
$field = new LiteralField( $field = new LiteralField(
"LiteralField[{$this->ID}]", "LiteralField[{$this->ID}]",
sprintf( sprintf(
"<div id='%s' class='field text %s'> "<div id='%s' class='field text %s'>
%s %s
<div class='middleColumn literalFieldArea'>%s</div>". <div class='middleColumn literalFieldArea'>%s</div>".
"</div>", "</div>",
Convert::raw2htmlname($this->Name), Convert::raw2htmlname($this->Name),
Convert::raw2att($classes), Convert::raw2att($classes),
$label, $label,
$this->Content $this->Content
) )
); );
// When dealing with literal fields there is no further customisation that can be added at this point // When dealing with literal fields there is no further customisation that can be added at this point
return $field; return $field;
} }
public function showInReports() { public function showInReports()
return ! $this->HideFromReports; {
} return ! $this->HideFromReports;
}
} }

View File

@ -5,55 +5,59 @@
* @package userforms * @package userforms
*/ */
class EditableMemberListField extends EditableFormField { class EditableMemberListField extends EditableFormField
{
private static $singular_name = 'Member List Field'; private static $singular_name = 'Member List Field';
private static $plural_name = 'Member List Fields'; private static $plural_name = 'Member List Fields';
private static $has_one = array( private static $has_one = array(
'Group' => 'Group' 'Group' => 'Group'
); );
/** /**
* @return FieldList * @return FieldList
*/ */
public function getCMSFields() { public function getCMSFields()
$fields = parent::getCMSFields(); {
$fields = parent::getCMSFields();
$fields->removeByName('Default'); $fields->removeByName('Default');
$fields->removeByName('Validation'); $fields->removeByName('Validation');
$fields->addFieldToTab( $fields->addFieldToTab(
'Root.Main', 'Root.Main',
DropdownField::create( DropdownField::create(
"GroupID", "GroupID",
_t('EditableFormField.GROUP', 'Group'), _t('EditableFormField.GROUP', 'Group'),
Group::get()->map() Group::get()->map()
)->setEmptyString(' ') )->setEmptyString(' ')
); );
return $fields; return $fields;
} }
public function getFormField() { public function getFormField()
if(empty($this->GroupID)) { {
return false; if (empty($this->GroupID)) {
} return false;
}
$members = Member::map_in_groups($this->GroupID); $members = Member::map_in_groups($this->GroupID);
$field = new DropdownField($this->Name, $this->EscapedTitle, $members); $field = new DropdownField($this->Name, $this->EscapedTitle, $members);
$this->doUpdateFormField($field); $this->doUpdateFormField($field);
return $field; return $field;
} }
public function getValueFromData($data) { public function getValueFromData($data)
if(isset($data[$this->Name])) { {
$memberID = $data[$this->Name]; if (isset($data[$this->Name])) {
$member = Member::get()->byID($memberID); $memberID = $data[$this->Name];
return $member ? $member->getName() : ""; $member = Member::get()->byID($memberID);
} return $member ? $member->getName() : "";
}
return false; return false;
} }
} }

View File

@ -13,183 +13,192 @@
* @package userforms * @package userforms
*/ */
class EditableMultipleOptionField extends EditableFormField { class EditableMultipleOptionField extends EditableFormField
{
/** /**
* Define this field as abstract (not inherited) * Define this field as abstract (not inherited)
* *
* @config * @config
* @var bool * @var bool
*/ */
private static $abstract = true; private static $abstract = true;
private static $has_many = array( private static $has_many = array(
"Options" => "EditableOption" "Options" => "EditableOption"
); );
/** /**
* @return FieldList * @return FieldList
*/ */
public function getCMSFields() { public function getCMSFields()
$fields = parent::getCMSFields(); {
$fields = parent::getCMSFields();
$editableColumns = new GridFieldEditableColumns(); $editableColumns = new GridFieldEditableColumns();
$editableColumns->setDisplayFields(array( $editableColumns->setDisplayFields(array(
'Title' => array( 'Title' => array(
'title' => _t('EditableMultipleOptionField.TITLE', 'Title'), 'title' => _t('EditableMultipleOptionField.TITLE', 'Title'),
'callback' => function($record, $column, $grid) { 'callback' => function ($record, $column, $grid) {
return TextField::create($column);
}
),
'Value' => array(
'title' => _t('EditableMultipleOptionField.VALUE', 'Value'),
'callback' => function($record, $column, $grid) {
return TextField::create($column); return TextField::create($column);
} }
), ),
'Default' => array( 'Value' => array(
'title' => _t('EditableMultipleOptionField.DEFAULT', 'Selected by default?'), 'title' => _t('EditableMultipleOptionField.VALUE', 'Value'),
'callback' => function($record, $column, $grid) { 'callback' => function ($record, $column, $grid) {
return CheckboxField::create($column); return TextField::create($column);
} }
) ),
)); 'Default' => array(
'title' => _t('EditableMultipleOptionField.DEFAULT', 'Selected by default?'),
'callback' => function ($record, $column, $grid) {
return CheckboxField::create($column);
}
)
));
$optionsConfig = GridFieldConfig::create() $optionsConfig = GridFieldConfig::create()
->addComponents( ->addComponents(
new GridFieldToolbarHeader(), new GridFieldToolbarHeader(),
new GridFieldTitleHeader(), new GridFieldTitleHeader(),
new GridFieldOrderableRows('Sort'), new GridFieldOrderableRows('Sort'),
$editableColumns, $editableColumns,
new GridFieldButtonRow(), new GridFieldButtonRow(),
new GridFieldAddNewInlineButton(), new GridFieldAddNewInlineButton(),
new GridFieldDeleteAction() new GridFieldDeleteAction()
); );
$optionsGrid = GridField::create( $optionsGrid = GridField::create(
'Options', 'Options',
_t('EditableFormField.CUSTOMOPTIONS', 'Options'), _t('EditableFormField.CUSTOMOPTIONS', 'Options'),
$this->Options(), $this->Options(),
$optionsConfig $optionsConfig
); );
$fields->insertAfter(new Tab('Options', _t('EditableMultipleOptionField.OPTIONSTAB','Options')), 'Main'); $fields->insertAfter(new Tab('Options', _t('EditableMultipleOptionField.OPTIONSTAB', 'Options')), 'Main');
$fields->addFieldToTab('Root.Options', $optionsGrid); $fields->addFieldToTab('Root.Options', $optionsGrid);
return $fields; return $fields;
} }
/** /**
* Publishing Versioning support. * Publishing Versioning support.
* *
* When publishing it needs to handle copying across / publishing * When publishing it needs to handle copying across / publishing
* each of the individual field options * each of the individual field options
* *
* @return void * @return void
*/ */
public function doPublish($fromStage, $toStage, $createNewVersion = false) { public function doPublish($fromStage, $toStage, $createNewVersion = false)
$live = Versioned::get_by_stage("EditableOption", "Live", "\"EditableOption\".\"ParentID\" = $this->ID"); {
$live = Versioned::get_by_stage("EditableOption", "Live", "\"EditableOption\".\"ParentID\" = $this->ID");
if($live) { if ($live) {
foreach($live as $option) { foreach ($live as $option) {
$option->delete(); $option->delete();
} }
} }
if($this->Options()) { if ($this->Options()) {
foreach($this->Options() as $option) { foreach ($this->Options() as $option) {
$option->publish($fromStage, $toStage, $createNewVersion); $option->publish($fromStage, $toStage, $createNewVersion);
} }
} }
parent::doPublish($fromStage, $toStage, $createNewVersion); parent::doPublish($fromStage, $toStage, $createNewVersion);
} }
/** /**
* Unpublishing Versioning support * Unpublishing Versioning support
* *
* When unpublishing the field it has to remove all options attached * When unpublishing the field it has to remove all options attached
* *
* @return void * @return void
*/ */
public function doDeleteFromStage($stage) { public function doDeleteFromStage($stage)
// Remove options {
$options = Versioned::get_by_stage('EditableOption', $stage) // Remove options
->filter('ParentID', $this->ID); $options = Versioned::get_by_stage('EditableOption', $stage)
foreach($options as $option) { ->filter('ParentID', $this->ID);
$option->deleteFromStage($stage); foreach ($options as $option) {
} $option->deleteFromStage($stage);
}
parent::doDeleteFromStage($stage); parent::doDeleteFromStage($stage);
} }
/** /**
* Deletes all the options attached to this field before deleting the * Deletes all the options attached to this field before deleting the
* field. Keeps stray options from floating around * field. Keeps stray options from floating around
* *
* @return void * @return void
*/ */
public function delete() { public function delete()
$options = $this->Options(); {
$options = $this->Options();
if($options) { if ($options) {
foreach($options as $option) { foreach ($options as $option) {
$option->delete(); $option->delete();
} }
} }
parent::delete(); parent::delete();
} }
/** /**
* Duplicate a pages content. We need to make sure all the fields attached * Duplicate a pages content. We need to make sure all the fields attached
* to that page go with it * to that page go with it
* *
* @return DataObject * @return DataObject
*/ */
public function duplicate($doWrite = true) { public function duplicate($doWrite = true)
$clonedNode = parent::duplicate(); {
$clonedNode = parent::duplicate();
foreach($this->Options() as $field) { foreach ($this->Options() as $field) {
$newField = $field->duplicate(false); $newField = $field->duplicate(false);
$newField->ParentID = $clonedNode->ID; $newField->ParentID = $clonedNode->ID;
$newField->Version = 0; $newField->Version = 0;
$newField->write(); $newField->write();
} }
return $clonedNode; return $clonedNode;
} }
/** /**
* Return whether or not this field has addable options such as a * Return whether or not this field has addable options such as a
* {@link EditableDropdownField} or {@link EditableRadioField} * {@link EditableDropdownField} or {@link EditableRadioField}
* *
* @return bool * @return bool
*/ */
public function getHasAddableOptions() { public function getHasAddableOptions()
return true; {
} return true;
}
/** /**
* Gets map of field options suitable for use in a form * Gets map of field options suitable for use in a form
* *
* @return array * @return array
*/ */
protected function getOptionsMap() { protected function getOptionsMap()
$optionSet = $this->Options(); {
$optionMap = $optionSet->map('Value', 'Title'); $optionSet = $this->Options();
if($optionMap instanceof SS_Map) { $optionMap = $optionSet->map('Value', 'Title');
return $optionMap->toArray(); if ($optionMap instanceof SS_Map) {
} return $optionMap->toArray();
return $optionMap; }
} return $optionMap;
}
/** /**
* Returns all default options * Returns all default options
* *
* @return SS_List * @return SS_List
*/ */
protected function getDefaultOptions() { protected function getDefaultOptions()
return $this->Options()->filter('Default', 1); {
} return $this->Options()->filter('Default', 1);
}
} }

View File

@ -7,81 +7,87 @@
* @package userforms * @package userforms
*/ */
class EditableNumericField extends EditableFormField { class EditableNumericField extends EditableFormField
{
private static $singular_name = 'Numeric Field'; private static $singular_name = 'Numeric Field';
private static $plural_name = 'Numeric Fields'; private static $plural_name = 'Numeric Fields';
private static $db = array( private static $db = array(
'MinValue' => 'Int', 'MinValue' => 'Int',
'MaxValue' => 'Int', 'MaxValue' => 'Int',
'Placeholder' => 'Varchar(255)' 'Placeholder' => 'Varchar(255)'
); );
public function getSetsOwnError() { public function getSetsOwnError()
return true; {
} return true;
}
public function getCMSFields() { public function getCMSFields()
$this->beforeUpdateCMSFields(function($fields) { {
$fields->addFieldToTab( $this->beforeUpdateCMSFields(function ($fields) {
'Root.Main', $fields->addFieldToTab(
TextField::create( 'Root.Main',
'Placeholder', TextField::create(
_t('EditableTextField.PLACEHOLDER', 'Placeholder') 'Placeholder',
) _t('EditableTextField.PLACEHOLDER', 'Placeholder')
); )
}); );
});
return parent::getCMSFields(); return parent::getCMSFields();
} }
/** /**
* @return NumericField * @return NumericField
*/ */
public function getFormField() { public function getFormField()
$field = NumericField::create($this->Name, $this->EscapedTitle, $this->Default) {
->setFieldHolderTemplate('UserFormsField_holder') $field = NumericField::create($this->Name, $this->EscapedTitle, $this->Default)
->setTemplate('UserFormsField') ->setFieldHolderTemplate('UserFormsField_holder')
->addExtraClass('number'); ->setTemplate('UserFormsField')
->addExtraClass('number');
$this->doUpdateFormField($field); $this->doUpdateFormField($field);
return $field; return $field;
} }
public function getFieldValidationOptions() { public function getFieldValidationOptions()
$fields = parent::getFieldValidationOptions(); {
$fields->push(FieldGroup::create( $fields = parent::getFieldValidationOptions();
_t("EditableNumericField.RANGE", "Allowed numeric range"), $fields->push(FieldGroup::create(
array( _t("EditableNumericField.RANGE", "Allowed numeric range"),
new NumericField('MinValue', false), array(
new LiteralField('RangeValue', _t("EditableNumericField.RANGE_TO", "to")), new NumericField('MinValue', false),
new NumericField('MaxValue', false) new LiteralField('RangeValue', _t("EditableNumericField.RANGE_TO", "to")),
) new NumericField('MaxValue', false)
)); )
return $fields; ));
} return $fields;
}
/** /**
* Updates a formfield with the additional metadata specified by this field * Updates a formfield with the additional metadata specified by this field
* *
* @param FormField $field * @param FormField $field
*/ */
protected function updateFormField($field) { protected function updateFormField($field)
parent::updateFormField($field); {
parent::updateFormField($field);
if($this->MinValue) { if ($this->MinValue) {
$field->setAttribute('data-rule-min', $this->MinValue); $field->setAttribute('data-rule-min', $this->MinValue);
} }
if($this->MaxValue) { if ($this->MaxValue) {
$field->setAttribute('data-rule-max', $this->MaxValue); $field->setAttribute('data-rule-max', $this->MaxValue);
} }
if($this->Placeholder) { if ($this->Placeholder) {
$field->setAttribute('placeholder', $this->Placeholder); $field->setAttribute('placeholder', $this->Placeholder);
} }
} }
} }

View File

@ -7,30 +7,31 @@
* @method EditableMultipleOptionField Parent() * @method EditableMultipleOptionField Parent()
* @package userforms * @package userforms
*/ */
class EditableOption extends DataObject { class EditableOption extends DataObject
{
private static $default_sort = "Sort"; private static $default_sort = "Sort";
private static $db = array( private static $db = array(
"Name" => "Varchar(255)", "Name" => "Varchar(255)",
"Title" => "Varchar(255)", "Title" => "Varchar(255)",
"Default" => "Boolean", "Default" => "Boolean",
"Sort" => "Int", "Sort" => "Int",
"Value" => "Varchar(255)", "Value" => "Varchar(255)",
); );
private static $has_one = array( private static $has_one = array(
"Parent" => "EditableMultipleOptionField", "Parent" => "EditableMultipleOptionField",
); );
private static $extensions = array( private static $extensions = array(
"Versioned('Stage', 'Live')" "Versioned('Stage', 'Live')"
); );
private static $summary_fields = array( private static $summary_fields = array(
'Title', 'Title',
'Default' 'Default'
); );
protected static $allow_empty_values = false; protected static $allow_empty_values = false;
@ -39,7 +40,8 @@ class EditableOption extends DataObject {
* *
* @return boolean * @return boolean
*/ */
public static function allow_empty_values() { public static function allow_empty_values()
{
return (bool) self::$allow_empty_values; return (bool) self::$allow_empty_values;
} }
@ -48,57 +50,63 @@ class EditableOption extends DataObject {
* *
* @param boolean $allow * @param boolean $allow
*/ */
public static function set_allow_empty_values($allow) { public static function set_allow_empty_values($allow)
{
self::$allow_empty_values = (bool) $allow; self::$allow_empty_values = (bool) $allow;
} }
/** /**
* @param Member $member * @param Member $member
* *
* @return boolean * @return boolean
*/ */
public function canEdit($member = null) { public function canEdit($member = null)
return $this->Parent()->canEdit($member); {
} return $this->Parent()->canEdit($member);
}
/** /**
* @param Member $member * @param Member $member
* *
* @return boolean * @return boolean
*/ */
public function canDelete($member = null) { public function canDelete($member = null)
return $this->canEdit($member); {
} return $this->canEdit($member);
}
public function getEscapedTitle() { public function getEscapedTitle()
return Convert::raw2att($this->Title); {
} return Convert::raw2att($this->Title);
}
/** /**
* @param Member $member * @param Member $member
* @return bool * @return bool
*/ */
public function canView($member = null) { public function canView($member = null)
return $this->Parent()->canView($member); {
} return $this->Parent()->canView($member);
}
/** /**
* Return whether a user can create an object of this type * Return whether a user can create an object of this type
* *
* @param Member $member * @param Member $member
* @param array $context Virtual parameter to allow context to be passed in to check * @param array $context Virtual parameter to allow context to be passed in to check
* @return bool * @return bool
*/ */
public function canCreate($member = null) { public function canCreate($member = null)
// Check parent page {
// Check parent page
$parent = $this->getCanCreateContext(func_get_args()); $parent = $this->getCanCreateContext(func_get_args());
if($parent) { if ($parent) {
return $parent->canEdit($member); return $parent->canEdit($member);
} }
// Fall back to secure admin permissions // Fall back to secure admin permissions
return parent::canCreate($member); return parent::canCreate($member);
} }
/** /**
* Helper method to check the parent for this object * Helper method to check the parent for this object
@ -106,13 +114,14 @@ class EditableOption extends DataObject {
* @param array $args List of arguments passed to canCreate * @param array $args List of arguments passed to canCreate
* @return DataObject Some parent dataobject to inherit permissions from * @return DataObject Some parent dataobject to inherit permissions from
*/ */
protected function getCanCreateContext($args) { protected function getCanCreateContext($args)
{
// Inspect second parameter to canCreate for a 'Parent' context // Inspect second parameter to canCreate for a 'Parent' context
if(isset($args[1]['Parent'])) { if (isset($args[1]['Parent'])) {
return $args[1]['Parent']; return $args[1]['Parent'];
} }
// Hack in currently edited page if context is missing // Hack in currently edited page if context is missing
if(Controller::has_curr() && Controller::curr() instanceof CMSMain) { if (Controller::has_curr() && Controller::curr() instanceof CMSMain) {
return Controller::curr()->currentPage(); return Controller::curr()->currentPage();
} }
@ -124,7 +133,8 @@ class EditableOption extends DataObject {
* @param Member $member * @param Member $member
* @return bool * @return bool
*/ */
public function canPublish($member = null) { public function canPublish($member = null)
{
return $this->canEdit($member); return $this->canEdit($member);
} }
@ -132,7 +142,8 @@ class EditableOption extends DataObject {
* @param Member $member * @param Member $member
* @return bool * @return bool
*/ */
public function canUnpublish($member = null) { public function canUnpublish($member = null)
{
return $this->canDelete($member); return $this->canDelete($member);
} }
@ -145,7 +156,7 @@ class EditableOption extends DataObject {
public function getValue() public function getValue()
{ {
$value = $this->getField('Value'); $value = $this->getField('Value');
if(empty($value) && !self::allow_empty_values()) { if (empty($value) && !self::allow_empty_values()) {
return $this->Title; return $this->Title;
} }
return $value; return $value;

View File

@ -7,39 +7,43 @@
* @package userforms * @package userforms
*/ */
class EditableRadioField extends EditableMultipleOptionField { class EditableRadioField extends EditableMultipleOptionField
{
private static $singular_name = 'Radio Group'; private static $singular_name = 'Radio Group';
private static $plural_name = 'Radio Groups'; private static $plural_name = 'Radio Groups';
/** /**
* @return FieldList * @return FieldList
*/ */
public function getCMSFields() { public function getCMSFields()
$fields = parent::getCMSFields(); {
$fields = parent::getCMSFields();
$fields->removeByName('Default'); $fields->removeByName('Default');
return $fields; return $fields;
} }
public function getFormField() { public function getFormField()
$field = OptionsetField::create($this->Name, $this->EscapedTitle, $this->getOptionsMap()); {
$field->setFieldHolderTemplate('UserFormsMultipleOptionField_holder'); $field = OptionsetField::create($this->Name, $this->EscapedTitle, $this->getOptionsMap());
$field->setFieldHolderTemplate('UserFormsMultipleOptionField_holder');
// Set default item // Set default item
$defaultOption = $this->getDefaultOptions()->first(); $defaultOption = $this->getDefaultOptions()->first();
if($defaultOption) { if ($defaultOption) {
$field->setValue($defaultOption->EscapedTitle); $field->setValue($defaultOption->EscapedTitle);
} }
$this->doUpdateFormField($field); $this->doUpdateFormField($field);
return $field; return $field;
} }
public function getSelectorField(EditableCustomRule $rule, $forOnLoad = false) { public function getSelectorField(EditableCustomRule $rule, $forOnLoad = false)
// We only want to trigger on load once for the radio group - hence we focus on the first option only. {
$first = $forOnLoad ? ':first' : ''; // We only want to trigger on load once for the radio group - hence we focus on the first option only.
return "$(\"input[name='{$this->Name}']{$first}\")"; $first = $forOnLoad ? ':first' : '';
} return "$(\"input[name='{$this->Name}']{$first}\")";
}
} }

View File

@ -7,109 +7,114 @@
* @package userforms * @package userforms
*/ */
class EditableTextField extends EditableFormField { class EditableTextField extends EditableFormField
{
private static $singular_name = 'Text Field'; private static $singular_name = 'Text Field';
private static $plural_name = 'Text Fields'; private static $plural_name = 'Text Fields';
private static $db = array( private static $db = array(
'MinLength' => 'Int', 'MinLength' => 'Int',
'MaxLength' => 'Int', 'MaxLength' => 'Int',
'Rows' => 'Int(1)', 'Rows' => 'Int(1)',
'Placeholder' => 'Varchar(255)' 'Placeholder' => 'Varchar(255)'
); );
private static $defaults = array( private static $defaults = array(
'Rows' => 1 'Rows' => 1
); );
public function getCMSFields() { public function getCMSFields()
$this->beforeUpdateCMSFields(function($fields) { {
$fields->addFieldToTab( $this->beforeUpdateCMSFields(function ($fields) {
'Root.Main', $fields->addFieldToTab(
NumericField::create( 'Root.Main',
'Rows', NumericField::create(
_t('EditableTextField.NUMBERROWS', 'Number of rows') 'Rows',
)->setDescription(_t( _t('EditableTextField.NUMBERROWS', 'Number of rows')
'EditableTextField.NUMBERROWS_DESCRIPTION', )->setDescription(_t(
'Fields with more than one row will be generated as a textarea' 'EditableTextField.NUMBERROWS_DESCRIPTION',
)) 'Fields with more than one row will be generated as a textarea'
); ))
);
$fields->addFieldToTab( $fields->addFieldToTab(
'Root.Main', 'Root.Main',
TextField::create( TextField::create(
'Placeholder', 'Placeholder',
_t('EditableTextField.PLACEHOLDER', 'Placeholder') _t('EditableTextField.PLACEHOLDER', 'Placeholder')
) )
); );
}); });
return parent::getCMSFields(); return parent::getCMSFields();
} }
/** /**
* @return FieldList * @return FieldList
*/ */
public function getFieldValidationOptions() { public function getFieldValidationOptions()
$fields = parent::getFieldValidationOptions(); {
$fields = parent::getFieldValidationOptions();
$fields->merge(array( $fields->merge(array(
FieldGroup::create( FieldGroup::create(
_t('EditableTextField.TEXTLENGTH', 'Allowed text length'), _t('EditableTextField.TEXTLENGTH', 'Allowed text length'),
array( array(
NumericField::create('MinLength', false), NumericField::create('MinLength', false),
LiteralField::create('RangeLength', _t("EditableTextField.RANGE_TO", "to")), LiteralField::create('RangeLength', _t("EditableTextField.RANGE_TO", "to")),
NumericField::create('MaxLength', false) NumericField::create('MaxLength', false)
) )
) )
)); ));
return $fields; return $fields;
} }
/** /**
* @return TextareaField|TextField * @return TextareaField|TextField
*/ */
public function getFormField() { public function getFormField()
if($this->Rows > 1) { {
$field = TextareaField::create($this->Name, $this->EscapedTitle, $this->Default) if ($this->Rows > 1) {
->setFieldHolderTemplate('UserFormsField_holder') $field = TextareaField::create($this->Name, $this->EscapedTitle, $this->Default)
->setTemplate('UserFormsTextareaField') ->setFieldHolderTemplate('UserFormsField_holder')
->setRows($this->Rows); ->setTemplate('UserFormsTextareaField')
} else { ->setRows($this->Rows);
$field = TextField::create($this->Name, $this->EscapedTitle, $this->Default) } else {
->setFieldHolderTemplate('UserFormsField_holder') $field = TextField::create($this->Name, $this->EscapedTitle, $this->Default)
->setTemplate('UserFormsField'); ->setFieldHolderTemplate('UserFormsField_holder')
} ->setTemplate('UserFormsField');
}
$this->doUpdateFormField($field); $this->doUpdateFormField($field);
return $field; return $field;
} }
/** /**
* Updates a formfield with the additional metadata specified by this field * Updates a formfield with the additional metadata specified by this field
* *
* @param FormField $field * @param FormField $field
*/ */
protected function updateFormField($field) { protected function updateFormField($field)
parent::updateFormField($field); {
parent::updateFormField($field);
if(is_numeric($this->MinLength) && $this->MinLength > 0) { if (is_numeric($this->MinLength) && $this->MinLength > 0) {
$field->setAttribute('data-rule-minlength', intval($this->MinLength)); $field->setAttribute('data-rule-minlength', intval($this->MinLength));
} }
if(is_numeric($this->MaxLength) && $this->MaxLength > 0) { if (is_numeric($this->MaxLength) && $this->MaxLength > 0) {
if($field instanceof TextField) { if ($field instanceof TextField) {
$field->setMaxLength(intval($this->MaxLength)); $field->setMaxLength(intval($this->MaxLength));
} }
$field->setAttribute('data-rule-maxlength', intval($this->MaxLength)); $field->setAttribute('data-rule-maxlength', intval($this->MaxLength));
} }
if($this->Placeholder) { if ($this->Placeholder) {
$field->setAttribute('placeholder', $this->Placeholder); $field->setAttribute('placeholder', $this->Placeholder);
} }
} }
} }

View File

@ -7,305 +7,312 @@
* *
* @package userforms * @package userforms
*/ */
class UserDefinedForm_EmailRecipient extends DataObject { class UserDefinedForm_EmailRecipient extends DataObject
{
private static $db = array( private static $db = array(
'EmailAddress' => 'Varchar(200)', 'EmailAddress' => 'Varchar(200)',
'EmailSubject' => 'Varchar(200)', 'EmailSubject' => 'Varchar(200)',
'EmailFrom' => 'Varchar(200)', 'EmailFrom' => 'Varchar(200)',
'EmailReplyTo' => 'Varchar(200)', 'EmailReplyTo' => 'Varchar(200)',
'EmailBody' => 'Text', 'EmailBody' => 'Text',
'EmailBodyHtml' => 'HTMLText', 'EmailBodyHtml' => 'HTMLText',
'EmailTemplate' => 'Varchar', 'EmailTemplate' => 'Varchar',
'SendPlain' => 'Boolean', 'SendPlain' => 'Boolean',
'HideFormData' => 'Boolean', 'HideFormData' => 'Boolean',
'CustomRulesCondition' => 'Enum("And,Or")' 'CustomRulesCondition' => 'Enum("And,Or")'
); );
private static $has_one = array( private static $has_one = array(
'Form' => 'UserDefinedForm', 'Form' => 'UserDefinedForm',
'SendEmailFromField' => 'EditableFormField', 'SendEmailFromField' => 'EditableFormField',
'SendEmailToField' => 'EditableFormField', 'SendEmailToField' => 'EditableFormField',
'SendEmailSubjectField' => 'EditableFormField' 'SendEmailSubjectField' => 'EditableFormField'
); );
private static $has_many = array( private static $has_many = array(
'CustomRules' => 'UserDefinedForm_EmailRecipientCondition' 'CustomRules' => 'UserDefinedForm_EmailRecipientCondition'
); );
private static $summary_fields = array( private static $summary_fields = array(
'EmailAddress', 'EmailAddress',
'EmailSubject', 'EmailSubject',
'EmailFrom' 'EmailFrom'
); );
/** /**
* Setting this to true will allow you to select "risky" fields as * Setting this to true will allow you to select "risky" fields as
* email recipient, such as free-text entry fields. * email recipient, such as free-text entry fields.
* *
* It's advisable to leave this off. * It's advisable to leave this off.
* *
* @config * @config
* @var bool * @var bool
*/ */
private static $allow_unbound_recipient_fields = false; private static $allow_unbound_recipient_fields = false;
public function summaryFields() { public function summaryFields()
$fields = parent::summaryFields(); {
if(isset($fields['EmailAddress'])) { $fields = parent::summaryFields();
$fields['EmailAddress'] = _t('UserDefinedForm.EMAILADDRESS', 'Email'); if (isset($fields['EmailAddress'])) {
} $fields['EmailAddress'] = _t('UserDefinedForm.EMAILADDRESS', 'Email');
if(isset($fields['EmailSubject'])) { }
$fields['EmailSubject'] = _t('UserDefinedForm.EMAILSUBJECT', 'Subject'); if (isset($fields['EmailSubject'])) {
} $fields['EmailSubject'] = _t('UserDefinedForm.EMAILSUBJECT', 'Subject');
if(isset($fields['EmailFrom'])) { }
$fields['EmailFrom'] = _t('UserDefinedForm.EMAILFROM', 'From'); if (isset($fields['EmailFrom'])) {
} $fields['EmailFrom'] = _t('UserDefinedForm.EMAILFROM', 'From');
return $fields; }
} return $fields;
}
/** /**
* Get instance of UserDefinedForm when editing in getCMSFields * Get instance of UserDefinedForm when editing in getCMSFields
* *
* @return UserDefinedFrom * @return UserDefinedFrom
*/ */
protected function getFormParent() { protected function getFormParent()
$formID = $this->FormID {
? $this->FormID $formID = $this->FormID
: Session::get('CMSMain.currentPage'); ? $this->FormID
return UserDefinedForm::get()->byID($formID); : Session::get('CMSMain.currentPage');
} return UserDefinedForm::get()->byID($formID);
}
public function getTitle() { public function getTitle()
if($this->EmailAddress) { {
return $this->EmailAddress; if ($this->EmailAddress) {
} return $this->EmailAddress;
if($this->EmailSubject) { }
return $this->EmailSubject; if ($this->EmailSubject) {
} return $this->EmailSubject;
return parent::getTitle(); }
} return parent::getTitle();
}
/** /**
* Generate a gridfield config for editing filter rules * Generate a gridfield config for editing filter rules
* *
* @return GridFieldConfig * @return GridFieldConfig
*/ */
protected function getRulesConfig() { protected function getRulesConfig()
$formFields = $this->getFormParent()->Fields(); {
$formFields = $this->getFormParent()->Fields();
$config = GridFieldConfig::create() $config = GridFieldConfig::create()
->addComponents( ->addComponents(
new GridFieldButtonRow('before'), new GridFieldButtonRow('before'),
new GridFieldToolbarHeader(), new GridFieldToolbarHeader(),
new GridFieldAddNewInlineButton(), new GridFieldAddNewInlineButton(),
new GridFieldDeleteAction(), new GridFieldDeleteAction(),
$columns = new GridFieldEditableColumns() $columns = new GridFieldEditableColumns()
); );
$columns->setDisplayFields(array( $columns->setDisplayFields(array(
'ConditionFieldID' => function($record, $column, $grid) use ($formFields) { 'ConditionFieldID' => function ($record, $column, $grid) use ($formFields) {
return DropdownField::create($column, false, $formFields->map('ID', 'Title')); return DropdownField::create($column, false, $formFields->map('ID', 'Title'));
}, },
'ConditionOption' => function($record, $column, $grid) { 'ConditionOption' => function ($record, $column, $grid) {
$options = UserDefinedForm_EmailRecipientCondition::config()->condition_options; $options = UserDefinedForm_EmailRecipientCondition::config()->condition_options;
return DropdownField::create($column, false, $options); return DropdownField::create($column, false, $options);
}, },
'ConditionValue' => function($record, $column, $grid) { 'ConditionValue' => function ($record, $column, $grid) {
return TextField::create($column); return TextField::create($column);
} }
)); ));
return $config; return $config;
} }
/** /**
* @return FieldList * @return FieldList
*/ */
public function getCMSFields() { public function getCMSFields()
Requirements::javascript(USERFORMS_DIR . '/javascript/Recipient.js'); {
Requirements::javascript(USERFORMS_DIR . '/javascript/Recipient.js');
// Determine optional field values // Determine optional field values
$form = $this->getFormParent(); $form = $this->getFormParent();
// predefined choices are also candidates // predefined choices are also candidates
$multiOptionFields = EditableMultipleOptionField::get()->filter('ParentID', $form->ID); $multiOptionFields = EditableMultipleOptionField::get()->filter('ParentID', $form->ID);
// if they have email fields then we could send from it // if they have email fields then we could send from it
$validEmailFromFields = EditableEmailField::get()->filter('ParentID', $form->ID); $validEmailFromFields = EditableEmailField::get()->filter('ParentID', $form->ID);
// For the subject, only one-line entry boxes make sense // For the subject, only one-line entry boxes make sense
$validSubjectFields = ArrayList::create( $validSubjectFields = ArrayList::create(
EditableTextField::get() EditableTextField::get()
->filter('ParentID', $form->ID) ->filter('ParentID', $form->ID)
->exclude('Rows:GreaterThan', 1) ->exclude('Rows:GreaterThan', 1)
->toArray() ->toArray()
); );
$validSubjectFields->merge($multiOptionFields); $validSubjectFields->merge($multiOptionFields);
// Check valid email-recipient fields // Check valid email-recipient fields
if($this->config()->allow_unbound_recipient_fields) { if ($this->config()->allow_unbound_recipient_fields) {
// To address can only be email fields or multi option fields // To address can only be email fields or multi option fields
$validEmailToFields = ArrayList::create($validEmailFromFields->toArray()); $validEmailToFields = ArrayList::create($validEmailFromFields->toArray());
$validEmailToFields->merge($multiOptionFields); $validEmailToFields->merge($multiOptionFields);
} else { } else {
// To address cannot be unbound, so restrict to pre-defined lists // To address cannot be unbound, so restrict to pre-defined lists
$validEmailToFields = $multiOptionFields; $validEmailToFields = $multiOptionFields;
} }
// Build fieldlist // Build fieldlist
$fields = FieldList::create(Tabset::create('Root')->addExtraClass('EmailRecipientForm')); $fields = FieldList::create(Tabset::create('Root')->addExtraClass('EmailRecipientForm'));
// Configuration fields // Configuration fields
$fields->addFieldsToTab('Root.EmailDetails', array( $fields->addFieldsToTab('Root.EmailDetails', array(
// Subject // Subject
FieldGroup::create( FieldGroup::create(
TextField::create('EmailSubject', _t('UserDefinedForm.TYPESUBJECT', 'Type subject')) TextField::create('EmailSubject', _t('UserDefinedForm.TYPESUBJECT', 'Type subject'))
->setAttribute('style', 'min-width: 400px;'), ->setAttribute('style', 'min-width: 400px;'),
DropdownField::create( DropdownField::create(
'SendEmailSubjectFieldID', 'SendEmailSubjectFieldID',
_t('UserDefinedForm.SELECTAFIELDTOSETSUBJECT', '.. or select a field to use as the subject'), _t('UserDefinedForm.SELECTAFIELDTOSETSUBJECT', '.. or select a field to use as the subject'),
$validSubjectFields->map('ID', 'Title') $validSubjectFields->map('ID', 'Title')
)->setEmptyString('') )->setEmptyString('')
) )
->setTitle(_t('UserDefinedForm.EMAILSUBJECT', 'Email subject')), ->setTitle(_t('UserDefinedForm.EMAILSUBJECT', 'Email subject')),
// To // To
FieldGroup::create( FieldGroup::create(
TextField::create('EmailAddress', _t('UserDefinedForm.TYPETO', 'Type to address')) TextField::create('EmailAddress', _t('UserDefinedForm.TYPETO', 'Type to address'))
->setAttribute('style', 'min-width: 400px;'), ->setAttribute('style', 'min-width: 400px;'),
DropdownField::create( DropdownField::create(
'SendEmailToFieldID', 'SendEmailToFieldID',
_t('UserDefinedForm.ORSELECTAFIELDTOUSEASTO', '.. or select a field to use as the to address'), _t('UserDefinedForm.ORSELECTAFIELDTOUSEASTO', '.. or select a field to use as the to address'),
$validEmailToFields->map('ID', 'Title') $validEmailToFields->map('ID', 'Title')
)->setEmptyString(' ') )->setEmptyString(' ')
) )
->setTitle(_t('UserDefinedForm.SENDEMAILTO','Send email to')) ->setTitle(_t('UserDefinedForm.SENDEMAILTO', 'Send email to'))
->setDescription(_t( ->setDescription(_t(
'UserDefinedForm.SENDEMAILTO_DESCRIPTION', 'UserDefinedForm.SENDEMAILTO_DESCRIPTION',
'You may enter multiple email addresses as a comma separated list.' 'You may enter multiple email addresses as a comma separated list.'
)), )),
// From // From
TextField::create('EmailFrom', _t('UserDefinedForm.FROMADDRESS','Send email from')) TextField::create('EmailFrom', _t('UserDefinedForm.FROMADDRESS', 'Send email from'))
->setDescription(_t( ->setDescription(_t(
'UserDefinedForm.EmailFromContent', 'UserDefinedForm.EmailFromContent',
"The from address allows you to set who the email comes from. On most servers this ". "The from address allows you to set who the email comes from. On most servers this ".
"will need to be set to an email address on the same domain name as your site. ". "will need to be set to an email address on the same domain name as your site. ".
"For example on yoursite.com the from address may need to be something@yoursite.com. ". "For example on yoursite.com the from address may need to be something@yoursite.com. ".
"You can however, set any email address you wish as the reply to address." "You can however, set any email address you wish as the reply to address."
)), )),
// Reply-To // Reply-To
FieldGroup::create( FieldGroup::create(
TextField::create('EmailReplyTo', _t('UserDefinedForm.TYPEREPLY', 'Type reply address')) TextField::create('EmailReplyTo', _t('UserDefinedForm.TYPEREPLY', 'Type reply address'))
->setAttribute('style', 'min-width: 400px;'), ->setAttribute('style', 'min-width: 400px;'),
DropdownField::create( DropdownField::create(
'SendEmailFromFieldID', 'SendEmailFromFieldID',
_t('UserDefinedForm.ORSELECTAFIELDTOUSEASFROM', '.. or select a field to use as reply to address'), _t('UserDefinedForm.ORSELECTAFIELDTOUSEASFROM', '.. or select a field to use as reply to address'),
$validEmailFromFields->map('ID', 'Title') $validEmailFromFields->map('ID', 'Title')
)->setEmptyString(' ') )->setEmptyString(' ')
) )
->setTitle(_t('UserDefinedForm.REPLYADDRESS', 'Email for reply to')) ->setTitle(_t('UserDefinedForm.REPLYADDRESS', 'Email for reply to'))
->setDescription(_t( ->setDescription(_t(
'UserDefinedForm.REPLYADDRESS_DESCRIPTION', 'UserDefinedForm.REPLYADDRESS_DESCRIPTION',
'The email address which the recipient is able to \'reply\' to.' 'The email address which the recipient is able to \'reply\' to.'
)) ))
)); ));
$fields->fieldByName('Root.EmailDetails')->setTitle(_t('UserDefinedForm_EmailRecipient.EMAILDETAILSTAB', 'Email Details'));
$fields->fieldByName('Root.EmailDetails')->setTitle(_t('UserDefinedForm_EmailRecipient.EMAILDETAILSTAB', 'Email Details')); // Only show the preview link if the recipient has been saved.
if (!empty($this->EmailTemplate)) {
$preview = sprintf(
'<p><a href="%s" target="_blank" class="ss-ui-button">%s</a></p><em>%s</em>',
"admin/pages/edit/EditForm/field/EmailRecipients/item/{$this->ID}/preview",
_t('UserDefinedForm.PREVIEW_EMAIL', 'Preview email'),
_t('UserDefinedForm.PREVIEW_EMAIL_DESCRIPTION', 'Note: Unsaved changes will not appear in the preview.')
);
} else {
$preview = sprintf(
'<em>%s</em>',
_t(
'UserDefinedForm.PREVIEW_EMAIL_UNAVAILABLE',
'You can preview this email once you have saved the Recipient.'
)
);
}
// Only show the preview link if the recipient has been saved. // Email templates
if (!empty($this->EmailTemplate)) { $fields->addFieldsToTab('Root.EmailContent', array(
$preview = sprintf( CheckboxField::create('HideFormData', _t('UserDefinedForm.HIDEFORMDATA', 'Hide form data from email?')),
'<p><a href="%s" target="_blank" class="ss-ui-button">%s</a></p><em>%s</em>', CheckboxField::create(
"admin/pages/edit/EditForm/field/EmailRecipients/item/{$this->ID}/preview", 'SendPlain',
_t('UserDefinedForm.PREVIEW_EMAIL', 'Preview email'), _t('UserDefinedForm.SENDPLAIN', 'Send email as plain text? (HTML will be stripped)')
_t('UserDefinedForm.PREVIEW_EMAIL_DESCRIPTION', 'Note: Unsaved changes will not appear in the preview.') ),
); DropdownField::create(
} else { 'EmailTemplate',
$preview = sprintf( _t('UserDefinedForm.EMAILTEMPLATE', 'Email template'),
'<em>%s</em>', $this->getEmailTemplateDropdownValues()
_t( )->addExtraClass('toggle-html-only'),
'UserDefinedForm.PREVIEW_EMAIL_UNAVAILABLE', HTMLEditorField::create('EmailBodyHtml', _t('UserDefinedForm.EMAILBODYHTML', 'Body'))
'You can preview this email once you have saved the Recipient.' ->addExtraClass('toggle-html-only'),
) TextareaField::create('EmailBody', _t('UserDefinedForm.EMAILBODY', 'Body'))
); ->addExtraClass('toggle-plain-only'),
} LiteralField::create(
'EmailPreview',
'<div id="EmailPreview" class="field toggle-html-only">' . $preview . '</div>'
)
));
$fields->fieldByName('Root.EmailContent')->setTitle(_t('UserDefinedForm_EmailRecipient.EMAILCONTENTTAB', 'Email Content'));
// Email templates // Custom rules for sending this field
$fields->addFieldsToTab('Root.EmailContent', array( $grid = new GridField(
CheckboxField::create('HideFormData', _t('UserDefinedForm.HIDEFORMDATA', 'Hide form data from email?')), "CustomRules",
CheckboxField::create( _t('EditableFormField.CUSTOMRULES', 'Custom Rules'),
'SendPlain', $this->CustomRules(),
_t('UserDefinedForm.SENDPLAIN', 'Send email as plain text? (HTML will be stripped)') $this->getRulesConfig()
), );
DropdownField::create( $grid->setDescription(_t(
'EmailTemplate', 'UserDefinedForm.RulesDescription',
_t('UserDefinedForm.EMAILTEMPLATE', 'Email template'), 'Emails will only be sent to the recipient if the custom rules are met. If no rules are defined, this receipient will receive notifications for every submission.'
$this->getEmailTemplateDropdownValues() ));
)->addExtraClass('toggle-html-only'), $fields->addFieldsToTab('Root.CustomRules', array(
HTMLEditorField::create('EmailBodyHtml', _t('UserDefinedForm.EMAILBODYHTML','Body')) new DropdownField(
->addExtraClass('toggle-html-only'), 'CustomRulesCondition',
TextareaField::create('EmailBody', _t('UserDefinedForm.EMAILBODY','Body')) _t('UserDefinedForm.SENDIF', 'Send condition'),
->addExtraClass('toggle-plain-only'), array(
LiteralField::create( 'Or' => _t('UserDefinedForm.SENDIFOR', 'Any conditions are true'),
'EmailPreview', 'And' => _t('UserDefinedForm.SENDIFAND', 'All conditions are true')
'<div id="EmailPreview" class="field toggle-html-only">' . $preview . '</div>' )
) ),
)); $grid
));
$fields->fieldByName('Root.CustomRules')->setTitle(_t('UserDefinedForm_EmailRecipient.CUSTOMRULESTAB', 'Custom Rules'));
$fields->fieldByName('Root.EmailContent')->setTitle(_t('UserDefinedForm_EmailRecipient.EMAILCONTENTTAB', 'Email Content')); $this->extend('updateCMSFields', $fields);
return $fields;
}
// Custom rules for sending this field /**
$grid = new GridField( * Return whether a user can create an object of this type
"CustomRules", *
_t('EditableFormField.CUSTOMRULES', 'Custom Rules'),
$this->CustomRules(),
$this->getRulesConfig()
);
$grid->setDescription(_t(
'UserDefinedForm.RulesDescription',
'Emails will only be sent to the recipient if the custom rules are met. If no rules are defined, this receipient will receive notifications for every submission.'
));
$fields->addFieldsToTab('Root.CustomRules', array(
new DropdownField(
'CustomRulesCondition',
_t('UserDefinedForm.SENDIF', 'Send condition'),
array(
'Or' => _t('UserDefinedForm.SENDIFOR', 'Any conditions are true'),
'And' => _t('UserDefinedForm.SENDIFAND', 'All conditions are true')
)
),
$grid
));
$fields->fieldByName('Root.CustomRules')->setTitle(_t('UserDefinedForm_EmailRecipient.CUSTOMRULESTAB', 'Custom Rules'));
$this->extend('updateCMSFields', $fields);
return $fields;
}
/**
* Return whether a user can create an object of this type
*
* @param Member $member * @param Member $member
* @param array $context Virtual parameter to allow context to be passed in to check * @param array $context Virtual parameter to allow context to be passed in to check
* @return bool * @return bool
*/ */
public function canCreate($member = null) { public function canCreate($member = null)
// Check parent page {
// Check parent page
$parent = $this->getCanCreateContext(func_get_args()); $parent = $this->getCanCreateContext(func_get_args());
if($parent) { if ($parent) {
return $parent->canEdit($member); return $parent->canEdit($member);
} }
// Fall back to secure admin permissions // Fall back to secure admin permissions
return parent::canCreate($member); return parent::canCreate($member);
} }
/** /**
* Helper method to check the parent for this object * Helper method to check the parent for this object
@ -313,13 +320,14 @@ class UserDefinedForm_EmailRecipient extends DataObject {
* @param array $args List of arguments passed to canCreate * @param array $args List of arguments passed to canCreate
* @return SiteTree Parent page instance * @return SiteTree Parent page instance
*/ */
protected function getCanCreateContext($args) { protected function getCanCreateContext($args)
{
// Inspect second parameter to canCreate for a 'Parent' context // Inspect second parameter to canCreate for a 'Parent' context
if(isset($args[1]['Form'])) { if (isset($args[1]['Form'])) {
return $args[1]['Form']; return $args[1]['Form'];
} }
// Hack in currently edited page if context is missing // Hack in currently edited page if context is missing
if(Controller::has_curr() && Controller::curr() instanceof CMSMain) { if (Controller::has_curr() && Controller::curr() instanceof CMSMain) {
return Controller::curr()->currentPage(); return Controller::curr()->currentPage();
} }
@ -327,104 +335,111 @@ class UserDefinedForm_EmailRecipient extends DataObject {
return null; return null;
} }
/** /**
* @param Member * @param Member
* *
* @return boolean * @return boolean
*/ */
public function canView($member = null) { public function canView($member = null)
return $this->Form()->canView($member); {
} return $this->Form()->canView($member);
}
/** /**
* @param Member * @param Member
* *
* @return boolean * @return boolean
*/ */
public function canEdit($member = null) { public function canEdit($member = null)
return $this->Form()->canEdit($member); {
} return $this->Form()->canEdit($member);
}
/** /**
* @param Member * @param Member
* *
* @return boolean * @return boolean
*/ */
public function canDelete($member = null) { public function canDelete($member = null)
return $this->canEdit($member); {
} return $this->canEdit($member);
}
/* /*
* Determine if this recipient may receive notifications for this submission * Determine if this recipient may receive notifications for this submission
* *
* @param array $data * @param array $data
* @param Form $form * @param Form $form
* @return bool * @return bool
*/ */
public function canSend($data, $form) { public function canSend($data, $form)
// Skip if no rules configured {
$customRules = $this->CustomRules(); // Skip if no rules configured
if(!$customRules->count()) { $customRules = $this->CustomRules();
return true; if (!$customRules->count()) {
} return true;
}
// Check all rules // Check all rules
$isAnd = $this->CustomRulesCondition === 'And'; $isAnd = $this->CustomRulesCondition === 'And';
foreach($customRules as $customRule) { foreach ($customRules as $customRule) {
$matches = $customRule->matches($data, $form); $matches = $customRule->matches($data, $form);
if($isAnd && !$matches) { if ($isAnd && !$matches) {
return false; return false;
} }
if(!$isAnd && $matches) { if (!$isAnd && $matches) {
return true; return true;
} }
} }
// Once all rules are checked // Once all rules are checked
return $isAnd; return $isAnd;
} }
/** /**
* Make sure the email template saved against the recipient exists on the file system. * Make sure the email template saved against the recipient exists on the file system.
* *
* @param string * @param string
* *
* @return boolean * @return boolean
*/ */
public function emailTemplateExists($template = '') { public function emailTemplateExists($template = '')
$t = ($template ? $template : $this->EmailTemplate); {
$t = ($template ? $template : $this->EmailTemplate);
return in_array($t, $this->getEmailTemplateDropdownValues()); return in_array($t, $this->getEmailTemplateDropdownValues());
} }
/** /**
* Get the email body for the current email format * Get the email body for the current email format
* *
* @return string * @return string
*/ */
public function getEmailBodyContent() { public function getEmailBodyContent()
return $this->SendPlain ? $this->EmailBody : $this->EmailBodyHtml; {
} return $this->SendPlain ? $this->EmailBody : $this->EmailBodyHtml;
}
/** /**
* Gets a list of email templates suitable for populating the email template dropdown. * Gets a list of email templates suitable for populating the email template dropdown.
* *
* @return array * @return array
*/ */
public function getEmailTemplateDropdownValues() { public function getEmailTemplateDropdownValues()
$templates = array(); {
$templates = array();
$finder = new SS_FileFinder(); $finder = new SS_FileFinder();
$finder->setOption('name_regex', '/^.*\.ss$/'); $finder->setOption('name_regex', '/^.*\.ss$/');
$found = $finder->find(BASE_PATH . '/' . UserDefinedForm::config()->email_template_directory); $found = $finder->find(BASE_PATH . '/' . UserDefinedForm::config()->email_template_directory);
foreach ($found as $key => $value) { foreach ($found as $key => $value) {
$template = pathinfo($value); $template = pathinfo($value);
$templates[$template['filename']] = $template['filename']; $templates[$template['filename']] = $template['filename'];
} }
return $templates; return $templates;
} }
} }

View File

@ -6,71 +6,74 @@
* *
* @method UserDefinedForm_EmailRecipient Parent() * @method UserDefinedForm_EmailRecipient Parent()
*/ */
class UserDefinedForm_EmailRecipientCondition extends DataObject { class UserDefinedForm_EmailRecipientCondition extends DataObject
{
/** /**
* List of options * List of options
* *
* @config * @config
* @var array * @var array
*/ */
private static $condition_options = array( private static $condition_options = array(
"IsBlank" => "Is blank", "IsBlank" => "Is blank",
"IsNotBlank" => "Is not blank", "IsNotBlank" => "Is not blank",
"Equals" => "Equals", "Equals" => "Equals",
"NotEquals" => "Doesn't equal" "NotEquals" => "Doesn't equal"
); );
private static $db = array( private static $db = array(
'ConditionOption' => 'Enum("IsBlank,IsNotBlank,Equals,NotEquals")', 'ConditionOption' => 'Enum("IsBlank,IsNotBlank,Equals,NotEquals")',
'ConditionValue' => 'Varchar' 'ConditionValue' => 'Varchar'
); );
private static $has_one = array( private static $has_one = array(
'Parent' => 'UserDefinedForm_EmailRecipient', 'Parent' => 'UserDefinedForm_EmailRecipient',
'ConditionField' => 'EditableFormField' 'ConditionField' => 'EditableFormField'
); );
/** /**
* Determine if this rule matches the given condition * Determine if this rule matches the given condition
* *
* @param array $data * @param array $data
* @param Form $form * @param Form $form
* @return bool * @return bool
*/ */
public function matches($data, $form) { public function matches($data, $form)
$fieldName = $this->ConditionField()->Name; {
$fieldValue = isset($data[$fieldName]) ? $data[$fieldName] : null; $fieldName = $this->ConditionField()->Name;
switch($this->ConditionOption) { $fieldValue = isset($data[$fieldName]) ? $data[$fieldName] : null;
case 'IsBlank': switch ($this->ConditionOption) {
return empty($fieldValue); case 'IsBlank':
case 'IsNotBlank': return empty($fieldValue);
return !empty($fieldValue); case 'IsNotBlank':
default: return !empty($fieldValue);
$matches = is_array($fieldValue) default:
? in_array($this->ConditionValue, $fieldValue) $matches = is_array($fieldValue)
: $this->ConditionValue === (string) $fieldValue; ? in_array($this->ConditionValue, $fieldValue)
return ($this->ConditionOption === 'Equals') === (bool)$matches; : $this->ConditionValue === (string) $fieldValue;
} return ($this->ConditionOption === 'Equals') === (bool)$matches;
} }
}
/** /**
* Return whether a user can create an object of this type * Return whether a user can create an object of this type
* *
* @param Member $member * @param Member $member
* @param array $context Virtual parameter to allow context to be passed in to check * @param array $context Virtual parameter to allow context to be passed in to check
* @return bool * @return bool
*/ */
public function canCreate($member = null) { public function canCreate($member = null)
// Check parent page {
// Check parent page
$parent = $this->getCanCreateContext(func_get_args()); $parent = $this->getCanCreateContext(func_get_args());
if($parent) { if ($parent) {
return $parent->canEdit($member); return $parent->canEdit($member);
} }
// Fall back to secure admin permissions // Fall back to secure admin permissions
return parent::canCreate($member); return parent::canCreate($member);
} }
/** /**
* Helper method to check the parent for this object * Helper method to check the parent for this object
@ -78,13 +81,14 @@ class UserDefinedForm_EmailRecipientCondition extends DataObject {
* @param array $args List of arguments passed to canCreate * @param array $args List of arguments passed to canCreate
* @return SiteTree Parent page instance * @return SiteTree Parent page instance
*/ */
protected function getCanCreateContext($args) { protected function getCanCreateContext($args)
{
// Inspect second parameter to canCreate for a 'Parent' context // Inspect second parameter to canCreate for a 'Parent' context
if(isset($args[1]['Parent'])) { if (isset($args[1]['Parent'])) {
return $args[1]['Parent']; return $args[1]['Parent'];
} }
// Hack in currently edited page if context is missing // Hack in currently edited page if context is missing
if(Controller::has_curr() && Controller::curr() instanceof CMSMain) { if (Controller::has_curr() && Controller::curr() instanceof CMSMain) {
return Controller::curr()->currentPage(); return Controller::curr()->currentPage();
} }
@ -92,30 +96,33 @@ class UserDefinedForm_EmailRecipientCondition extends DataObject {
return null; return null;
} }
/** /**
* @param Member * @param Member
* *
* @return boolean * @return boolean
*/ */
public function canView($member = null) { public function canView($member = null)
return $this->Parent()->canView($member); {
} return $this->Parent()->canView($member);
}
/** /**
* @param Member * @param Member
* *
* @return boolean * @return boolean
*/ */
public function canEdit($member = null) { public function canEdit($member = null)
return $this->Parent()->canEdit($member); {
} return $this->Parent()->canEdit($member);
}
/** /**
* @param Member * @param Member
* *
* @return boolean * @return boolean
*/ */
public function canDelete($member = null) { public function canDelete($member = null)
return $this->canEdit($member); {
} return $this->canEdit($member);
}
} }

View File

@ -8,23 +8,26 @@
* @package userforms * @package userforms
*/ */
class UserFormRecipientEmail extends Email { class UserFormRecipientEmail extends Email
{
protected $ss_template = "SubmittedFormEmail"; protected $ss_template = "SubmittedFormEmail";
protected $data; protected $data;
public function __construct($submittedFields = null) { public function __construct($submittedFields = null)
parent::__construct($submittedFields = null); {
} parent::__construct($submittedFields = null);
}
/** /**
* Set the "Reply-To" header with an email address rather than append as * Set the "Reply-To" header with an email address rather than append as
* {@link Email::replyTo} does. * {@link Email::replyTo} does.
* *
* @param string $email The email address to set the "Reply-To" header to * @param string $email The email address to set the "Reply-To" header to
*/ */
public function setReplyTo($email) { public function setReplyTo($email)
$this->customHeaders['Reply-To'] = $email; {
} $this->customHeaders['Reply-To'] = $email;
}
} }

View File

@ -5,47 +5,50 @@
* *
* @package userforms * @package userforms
*/ */
class UserFormRecipientItemRequest extends GridFieldDetailForm_ItemRequest { class UserFormRecipientItemRequest extends GridFieldDetailForm_ItemRequest
{
private static $allowed_actions = array( private static $allowed_actions = array(
'edit', 'edit',
'view', 'view',
'ItemEditForm', 'ItemEditForm',
'preview' 'preview'
); );
/** /**
* Renders a preview of the recipient email. * Renders a preview of the recipient email.
*/ */
public function preview() { public function preview()
return $this->customise(new ArrayData(array( {
'Body' => $this->record->getEmailBodyContent(), return $this->customise(new ArrayData(array(
'HideFormData' => $this->record->HideFormData, 'Body' => $this->record->getEmailBodyContent(),
'Fields' => $this->getPreviewFieldData() 'HideFormData' => $this->record->HideFormData,
)))->renderWith($this->record->EmailTemplate); 'Fields' => $this->getPreviewFieldData()
} )))->renderWith($this->record->EmailTemplate);
}
/** /**
* Get some placeholder field values to display in the preview * Get some placeholder field values to display in the preview
* @return ArrayList * @return ArrayList
*/ */
private function getPreviewFieldData() { private function getPreviewFieldData()
$data = new ArrayList(); {
$data = new ArrayList();
$fields = $this->record->Form()->Fields()->filter(array( $fields = $this->record->Form()->Fields()->filter(array(
'ClassName:not' => 'EditableLiteralField', 'ClassName:not' => 'EditableLiteralField',
'ClassName:not' => 'EditableFormHeading' 'ClassName:not' => 'EditableFormHeading'
)); ));
foreach ($fields as $field) { foreach ($fields as $field) {
$data->push(new ArrayData(array( $data->push(new ArrayData(array(
'Name' => $field->Name, 'Name' => $field->Name,
'Title' => $field->Title, 'Title' => $field->Title,
'Value' => '$' . $field->Name, 'Value' => '$' . $field->Name,
'FormattedValue' => '$' . $field->Name 'FormattedValue' => '$' . $field->Name
))); )));
} }
return $data; return $data;
} }
} }

View File

@ -7,63 +7,68 @@
* @package userforms * @package userforms
*/ */
class SubmittedFileField extends SubmittedFormField { class SubmittedFileField extends SubmittedFormField
{
private static $has_one = array( private static $has_one = array(
"UploadedFile" => "File" "UploadedFile" => "File"
); );
/** /**
* Return the value of this field for inclusion into things such as * Return the value of this field for inclusion into things such as
* reports. * reports.
* *
* @return string * @return string
*/ */
public function getFormattedValue() { public function getFormattedValue()
$name = $this->getFileName(); {
$link = $this->getLink(); $name = $this->getFileName();
$title = _t('SubmittedFileField.DOWNLOADFILE', 'Download File'); $link = $this->getLink();
$title = _t('SubmittedFileField.DOWNLOADFILE', 'Download File');
if($link) { if ($link) {
return DBField::create_field('HTMLText', sprintf( return DBField::create_field('HTMLText', sprintf(
'%s - <a href="%s" target="_blank">%s</a>', '%s - <a href="%s" target="_blank">%s</a>',
$name, $link, $title $name, $link, $title
)); ));
} }
return false; return false;
} }
/** /**
* Return the value for this field in the CSV export. * Return the value for this field in the CSV export.
* *
* @return string * @return string
*/ */
public function getExportValue() { public function getExportValue()
return ($link = $this->getLink()) ? $link : ""; {
} return ($link = $this->getLink()) ? $link : "";
}
/** /**
* Return the link for the file attached to this submitted form field. * Return the link for the file attached to this submitted form field.
* *
* @return string * @return string
*/ */
public function getLink() { public function getLink()
if($file = $this->UploadedFile()) { {
if(trim($file->getFilename(), '/') != trim(ASSETS_DIR,'/')) { if ($file = $this->UploadedFile()) {
return $this->UploadedFile()->URL; if (trim($file->getFilename(), '/') != trim(ASSETS_DIR, '/')) {
} return $this->UploadedFile()->URL;
} }
} }
}
/** /**
* Return the name of the file, if present * Return the name of the file, if present
* *
* @return string * @return string
*/ */
public function getFileName() { public function getFileName()
if($this->UploadedFile()) { {
return $this->UploadedFile()->Name; if ($this->UploadedFile()) {
} return $this->UploadedFile()->Name;
} }
}
} }

View File

@ -5,143 +5,150 @@
* @package userforms * @package userforms
*/ */
class SubmittedForm extends DataObject { class SubmittedForm extends DataObject
{
private static $has_one = array( private static $has_one = array(
"SubmittedBy" => "Member", "SubmittedBy" => "Member",
"Parent" => "UserDefinedForm", "Parent" => "UserDefinedForm",
); );
private static $has_many = array( private static $has_many = array(
"Values" => "SubmittedFormField" "Values" => "SubmittedFormField"
); );
private static $summary_fields = array( private static $summary_fields = array(
'ID', 'ID',
'Created' 'Created'
); );
/** /**
* Returns the value of a relation or, in the case of this form, the value * Returns the value of a relation or, in the case of this form, the value
* of a given child {@link SubmittedFormField} * of a given child {@link SubmittedFormField}
* *
* @param string * @param string
* *
* @return mixed * @return mixed
*/ */
public function relField($fieldName) { public function relField($fieldName)
// default case {
if($value = parent::relField($fieldName)) { // default case
return $value; if ($value = parent::relField($fieldName)) {
} return $value;
}
// check values for a form field with the matching name. // check values for a form field with the matching name.
$formField = SubmittedFormField::get()->filter(array( $formField = SubmittedFormField::get()->filter(array(
'ParentID' => $this->ID, 'ParentID' => $this->ID,
'Name' => $fieldName 'Name' => $fieldName
))->first(); ))->first();
if($formField) { if ($formField) {
return $formField->getFormattedValue(); return $formField->getFormattedValue();
} }
} }
/** /**
* @return FieldList * @return FieldList
*/ */
public function getCMSFields() { public function getCMSFields()
{
$self = $this;
$self = $this; $this->beforeUpdateCMSFields(function ($fields) use ($self) {
$fields->removeByName('Values');
$fields->dataFieldByName('SubmittedByID')->setDisabled(true);
$this->beforeUpdateCMSFields(function($fields) use ($self) { $values = new GridField(
$fields->removeByName('Values'); 'Values',
$fields->dataFieldByName('SubmittedByID')->setDisabled(true); 'SubmittedFormField',
$self->Values()->sort('Created', 'ASC')
);
$values = new GridField( $config = new GridFieldConfig();
'Values', $config->addComponent(new GridFieldDataColumns());
'SubmittedFormField', $config->addComponent(new GridFieldExportButton());
$self->Values()->sort('Created', 'ASC') $config->addComponent(new GridFieldPrintButton());
); $values->setConfig($config);
$config = new GridFieldConfig(); $fields->addFieldToTab('Root.Main', $values);
$config->addComponent(new GridFieldDataColumns()); });
$config->addComponent(new GridFieldExportButton());
$config->addComponent(new GridFieldPrintButton());
$values->setConfig($config);
$fields->addFieldToTab('Root.Main', $values); $fields = parent::getCMSFields();
});
$fields = parent::getCMSFields(); return $fields;
}
return $fields; /**
} * @param Member
*
* @return boolean
*/
public function canCreate($member = null)
{
$extended = $this->extendedCan(__FUNCTION__, $member);
if ($extended !== null) {
return $extended;
}
return $this->Parent()->canCreate();
}
/** /**
* @param Member * @param Member
* *
* @return boolean * @return boolean
*/ */
public function canCreate($member = null) { public function canView($member = null)
$extended = $this->extendedCan(__FUNCTION__, $member); {
if($extended !== null) { $extended = $this->extendedCan(__FUNCTION__, $member);
return $extended; if ($extended !== null) {
} return $extended;
return $this->Parent()->canCreate(); }
} return $this->Parent()->canView();
}
/** /**
* @param Member * @param Member
* *
* @return boolean * @return boolean
*/ */
public function canView($member = null) { public function canEdit($member = null)
$extended = $this->extendedCan(__FUNCTION__, $member); {
if($extended !== null) { $extended = $this->extendedCan(__FUNCTION__, $member);
return $extended; if ($extended !== null) {
} return $extended;
return $this->Parent()->canView(); }
} return $this->Parent()->canEdit();
}
/** /**
* @param Member * @param Member
* *
* @return boolean * @return boolean
*/ */
public function canEdit($member = null) { public function canDelete($member = null)
$extended = $this->extendedCan(__FUNCTION__, $member); {
if($extended !== null) { $extended = $this->extendedCan(__FUNCTION__, $member);
return $extended; if ($extended !== null) {
} return $extended;
return $this->Parent()->canEdit(); }
} return $this->Parent()->canDelete();
}
/** /**
* @param Member * Before we delete this form make sure we delete all the
* * field values so that we don't leave old data round
* @return boolean *
*/ * @return void
public function canDelete($member = null) { */
$extended = $this->extendedCan(__FUNCTION__, $member); protected function onBeforeDelete()
if($extended !== null) { {
return $extended; if ($this->Values()) {
} foreach ($this->Values() as $value) {
return $this->Parent()->canDelete(); $value->delete();
} }
}
/** parent::onBeforeDelete();
* Before we delete this form make sure we delete all the }
* field values so that we don't leave old data round
*
* @return void
*/
protected function onBeforeDelete() {
if($this->Values()) {
foreach($this->Values() as $value) {
$value->delete();
}
}
parent::onBeforeDelete();
}
} }

View File

@ -5,93 +5,101 @@
* @package userforms * @package userforms
*/ */
class SubmittedFormField extends DataObject { class SubmittedFormField extends DataObject
{
private static $db = array( private static $db = array(
"Name" => "Varchar", "Name" => "Varchar",
"Value" => "Text", "Value" => "Text",
"Title" => "Varchar(255)" "Title" => "Varchar(255)"
); );
private static $has_one = array( private static $has_one = array(
"Parent" => "SubmittedForm" "Parent" => "SubmittedForm"
); );
private static $summary_fields = array( private static $summary_fields = array(
'Title' => 'Title', 'Title' => 'Title',
'FormattedValue' => 'Value' 'FormattedValue' => 'Value'
); );
/** /**
* @param Member * @param Member
* *
* @return boolean * @return boolean
*/ */
public function canCreate($member = null) { public function canCreate($member = null)
return $this->Parent()->canCreate(); {
} return $this->Parent()->canCreate();
}
/** /**
* @param Member * @param Member
* *
* @return boolean * @return boolean
*/ */
public function canView($member = null) { public function canView($member = null)
return $this->Parent()->canView(); {
} return $this->Parent()->canView();
}
/** /**
* @param Member * @param Member
* *
* @return boolean * @return boolean
*/ */
public function canEdit($member = null) { public function canEdit($member = null)
return $this->Parent()->canEdit(); {
} return $this->Parent()->canEdit();
}
/** /**
* @param Member * @param Member
* *
* @return boolean * @return boolean
*/ */
public function canDelete($member = null) { public function canDelete($member = null)
return $this->Parent()->canDelete(); {
} return $this->Parent()->canDelete();
}
/** /**
* Generate a formatted value for the reports and email notifications. * Generate a formatted value for the reports and email notifications.
* Converts new lines (which are stored in the database text field) as * Converts new lines (which are stored in the database text field) as
* <brs> so they will output as newlines in the reports * <brs> so they will output as newlines in the reports
* *
* @return string * @return string
*/ */
public function getFormattedValue() { public function getFormattedValue()
return nl2br($this->dbObject('Value')->ATT()); {
} return nl2br($this->dbObject('Value')->ATT());
}
/** /**
* Return the value of this submitted form field suitable for inclusion * Return the value of this submitted form field suitable for inclusion
* into the CSV * into the CSV
* *
* @return Text * @return Text
*/ */
public function getExportValue() { public function getExportValue()
return $this->Value; {
} return $this->Value;
}
/** /**
* Find equivalent editable field for this submission. * Find equivalent editable field for this submission.
* *
* Note the field may have been modified or deleted from the original form * Note the field may have been modified or deleted from the original form
* so this may not always return the data you expect. If you need to save * so this may not always return the data you expect. If you need to save
* a particular state of editable form field at time of submission, copy * a particular state of editable form field at time of submission, copy
* that value to the submission. * that value to the submission.
* *
* @return EditableFormField * @return EditableFormField
*/ */
public function getEditableField() { public function getEditableField()
return $this->Parent()->Parent()->Fields()->filter(array( {
'Name' => $this->Name return $this->Parent()->Parent()->Fields()->filter(array(
))->First(); 'Name' => $this->Name
} ))->First();
}
} }

View File

@ -2,53 +2,56 @@
use SilverStripe\Forms\SegmentFieldModifier\AbstractSegmentFieldModifier; use SilverStripe\Forms\SegmentFieldModifier\AbstractSegmentFieldModifier;
class DisambiguationSegmentFieldModifier extends AbstractSegmentFieldModifier { class DisambiguationSegmentFieldModifier extends AbstractSegmentFieldModifier
/** {
* @inheritdoc /**
* * @inheritdoc
* @param string $value *
* * @param string $value
* @return string *
*/ * @return string
public function getPreview($value) { */
if($this->form instanceof Form && $record = $this->form->getRecord()) { public function getPreview($value)
$parent = $record->Parent(); {
if ($this->form instanceof Form && $record = $this->form->getRecord()) {
$parent = $record->Parent();
$try = $value; $try = $value;
$sibling = EditableformField::get() $sibling = EditableformField::get()
->filter('ParentID', $parent->ID) ->filter('ParentID', $parent->ID)
->filter('Name', $try) ->filter('Name', $try)
->where('"ID" != ' . $record->ID) ->where('"ID" != ' . $record->ID)
->first(); ->first();
$counter = 1; $counter = 1;
while($sibling !== null) { while ($sibling !== null) {
$try = $value . '_' . $counter++; $try = $value . '_' . $counter++;
$sibling = EditableformField::get() $sibling = EditableformField::get()
->filter('ParentID', $parent->ID) ->filter('ParentID', $parent->ID)
->filter('Name', $try) ->filter('Name', $try)
->first(); ->first();
} }
if ($try !== $value) { if ($try !== $value) {
return $try; return $try;
} }
} }
return $value; return $value;
} }
/** /**
* @inheritdoc * @inheritdoc
* *
* @param string $value * @param string $value
* *
* @return string * @return string
*/ */
public function getSuggestion($value) { public function getSuggestion($value)
return $this->getPreview($value); {
} return $this->getPreview($value);
}
} }

View File

@ -2,26 +2,29 @@
use SilverStripe\Forms\SegmentFieldModifier\SlugSegmentFieldModifier; use SilverStripe\Forms\SegmentFieldModifier\SlugSegmentFieldModifier;
class UnderscoreSegmentFieldModifier extends SlugSegmentFieldModifier { class UnderscoreSegmentFieldModifier extends SlugSegmentFieldModifier
/** {
* @inheritdoc /**
* * @inheritdoc
* @param string $value *
* * @param string $value
* @return string *
*/ * @return string
public function getPreview($value) { */
return str_replace('-', '_', parent::getPreview($value)); public function getPreview($value)
} {
return str_replace('-', '_', parent::getPreview($value));
}
/** /**
* @inheritdoc * @inheritdoc
* *
* @param string $value * @param string $value
* *
* @return string * @return string
*/ */
public function getSuggestion($value) { public function getSuggestion($value)
return str_replace('-', '_', parent::getSuggestion($value)); {
} return str_replace('-', '_', parent::getSuggestion($value));
}
} }

View File

@ -8,54 +8,54 @@
* @package userforms * @package userforms
*/ */
class UserFormsColumnCleanTask extends MigrationTask { class UserFormsColumnCleanTask extends MigrationTask
{
protected $title = "UserForms EditableFormField Column Clean task"; protected $title = "UserForms EditableFormField Column Clean task";
protected $description = "Removes unused columns from EditableFormField for MySQL databases;"; protected $description = "Removes unused columns from EditableFormField for MySQL databases;";
protected $tables = array('EditableFormField'); protected $tables = array('EditableFormField');
protected $keepColumns = array('ID'); protected $keepColumns = array('ID');
/** /**
* Publish the existing forms. * Publish the existing forms.
* *
*/ */
public function run($request) { public function run($request)
foreach ($this->tables as $db) { {
$obj = new $db(); foreach ($this->tables as $db) {
$columns = $obj->database_fields($db); $obj = new $db();
$query = "SHOW COLUMNS FROM $db"; $columns = $obj->database_fields($db);
$liveColumns = DB::query($query)->column(); $query = "SHOW COLUMNS FROM $db";
$backedUp = 0; $liveColumns = DB::query($query)->column();
$query = "SHOW TABLES LIKE 'Backup_$db'"; $backedUp = 0;
$tableExists = DB::query($query)->value(); $query = "SHOW TABLES LIKE 'Backup_$db'";
if ($tableExists != null) { $tableExists = DB::query($query)->value();
echo "Tasks run already on $db exiting"; if ($tableExists != null) {
return; echo "Tasks run already on $db exiting";
} return;
$backedUp = 0; }
foreach ($liveColumns as $index => $column) { $backedUp = 0;
if ($backedUp == 0) { foreach ($liveColumns as $index => $column) {
echo "Backing up $db <br />"; if ($backedUp == 0) {
echo "Creating Backup_$db <br />"; echo "Backing up $db <br />";
// backup table echo "Creating Backup_$db <br />";
$query = "CREATE TABLE Backup_$db LIKE $db"; // backup table
DB::query($query); $query = "CREATE TABLE Backup_$db LIKE $db";
echo "Populating Backup_$db <br />"; DB::query($query);
$query = "INSERT Backup_$db SELECT * FROM $db"; echo "Populating Backup_$db <br />";
DB::query($query); $query = "INSERT Backup_$db SELECT * FROM $db";
$backedUp = 1; DB::query($query);
} $backedUp = 1;
if (!isset($columns[$column]) && !in_array($column, $this->keepColumns)) { }
echo "Dropping $column from $db <br />"; if (!isset($columns[$column]) && !in_array($column, $this->keepColumns)) {
$query = "ALTER TABLE $db DROP COLUMN $column"; echo "Dropping $column from $db <br />";
DB::query($query); $query = "ALTER TABLE $db DROP COLUMN $column";
} DB::query($query);
} }
} }
} }
}
} }

View File

@ -3,216 +3,225 @@
/** /**
* Service to support upgrade of userforms module * Service to support upgrade of userforms module
*/ */
class UserFormsUpgradeService { class UserFormsUpgradeService
{
/** /**
* @var bool * @var bool
*/ */
protected $quiet; protected $quiet;
public function run() { public function run()
$this->log("Upgrading formfield rules and custom settings"); {
$this->log("Upgrading formfield rules and custom settings");
// List of rules that have been created in all stages // List of rules that have been created in all stages
$fields = Versioned::get_including_deleted('EditableFormField'); $fields = Versioned::get_including_deleted('EditableFormField');
foreach($fields as $field) { foreach ($fields as $field) {
$this->upgradeField($field); $this->upgradeField($field);
} }
} }
/** /**
* Migrate a versioned field in all stages * Migrate a versioned field in all stages
* *
* @param EditableFormField $field * @param EditableFormField $field
*/ */
protected function upgradeField(EditableFormField $field) { protected function upgradeField(EditableFormField $field)
$this->log("Upgrading formfield ID = ".$field->ID); {
$this->log("Upgrading formfield ID = ".$field->ID);
// Check versions this field exists on // Check versions this field exists on
$filter = sprintf('"EditableFormField"."ID" = \'%d\' AND "Migrated" = 0', $field->ID); $filter = sprintf('"EditableFormField"."ID" = \'%d\' AND "Migrated" = 0', $field->ID);
$stageField = Versioned::get_one_by_stage('EditableFormField', 'Stage', $filter); $stageField = Versioned::get_one_by_stage('EditableFormField', 'Stage', $filter);
$liveField = Versioned::get_one_by_stage('EditableFormField', 'Live', $filter); $liveField = Versioned::get_one_by_stage('EditableFormField', 'Live', $filter);
if($stageField) { if ($stageField) {
$this->upgradeFieldInStage($stageField, 'Stage'); $this->upgradeFieldInStage($stageField, 'Stage');
} }
if($liveField) { if ($liveField) {
$this->upgradeFieldInStage($liveField, 'Live'); $this->upgradeFieldInStage($liveField, 'Live');
} }
} }
/** /**
* Migrate a versioned field in a single stage * Migrate a versioned field in a single stage
* *
* @param EditableFormField $field * @param EditableFormField $field
* @param stage $stage * @param stage $stage
*/ */
protected function upgradeFieldInStage(EditableFormField $field, $stage) { protected function upgradeFieldInStage(EditableFormField $field, $stage)
Versioned::reading_stage($stage); {
Versioned::reading_stage($stage);
// Migrate field rules // Migrate field rules
$this->migrateRules($field, $stage); $this->migrateRules($field, $stage);
// Migrate custom settings // Migrate custom settings
$this->migrateCustomSettings($field, $stage); $this->migrateCustomSettings($field, $stage);
// Flag as migrated // Flag as migrated
$field->Migrated = true; $field->Migrated = true;
$field->write(); $field->write();
} }
/** /**
* Migrate custom rules for the given field * Migrate custom rules for the given field
* *
* @param EditableFormField $field * @param EditableFormField $field
* @param string $stage * @param string $stage
*/ */
protected function migrateRules(EditableFormField $field, $stage) { protected function migrateRules(EditableFormField $field, $stage)
$rulesData = $field->CustomRules {
? unserialize($field->CustomRules) $rulesData = $field->CustomRules
: array(); ? unserialize($field->CustomRules)
: array();
// Skip blank rules or fields with custom rules already // Skip blank rules or fields with custom rules already
if(empty($rulesData) || $field->DisplayRules()->count()) { if (empty($rulesData) || $field->DisplayRules()->count()) {
return; return;
} }
// Check value of this condition // Check value of this condition
foreach($rulesData as $ruleDataItem) { foreach ($rulesData as $ruleDataItem) {
if(empty($ruleDataItem['ConditionOption']) || empty($ruleDataItem['Display'])) { if (empty($ruleDataItem['ConditionOption']) || empty($ruleDataItem['Display'])) {
continue; continue;
} }
// Get data for this rule // Get data for this rule
$conditionOption = $ruleDataItem['ConditionOption']; $conditionOption = $ruleDataItem['ConditionOption'];
$display = $ruleDataItem['Display']; $display = $ruleDataItem['Display'];
$conditionFieldName = empty($ruleDataItem['ConditionField']) ? null : $ruleDataItem['ConditionField']; $conditionFieldName = empty($ruleDataItem['ConditionField']) ? null : $ruleDataItem['ConditionField'];
$value = isset($ruleDataItem['Value']) $value = isset($ruleDataItem['Value'])
? $ruleDataItem['Value'] ? $ruleDataItem['Value']
: null; : null;
// Create rule // Create rule
$rule = $this->findOrCreateRule($field, $stage, $conditionOption, $display, $conditionFieldName, $value); $rule = $this->findOrCreateRule($field, $stage, $conditionOption, $display, $conditionFieldName, $value);
$this->log("Upgrading rule ID = " . $rule->ID); $this->log("Upgrading rule ID = " . $rule->ID);
} }
} }
/** /**
* Migrate custom settings for the given field * Migrate custom settings for the given field
* *
* @param EditableFormField $field * @param EditableFormField $field
* @param string $stage * @param string $stage
*/ */
protected function migrateCustomSettings(EditableFormField $field, $stage) { protected function migrateCustomSettings(EditableFormField $field, $stage)
// Custom settings include: {
// - ExtraClass // Custom settings include:
// - RightTitle // - ExtraClass
// - ShowOnLoad (show or '' are treated as true) // - RightTitle
// // - ShowOnLoad (show or '' are treated as true)
// - CheckedDefault (new field on EditableCheckbox - should be read from old "default" value) //
// - Default (EditableCheckbox) // - CheckedDefault (new field on EditableCheckbox - should be read from old "default" value)
// - DefaultToToday (EditableDateField) // - Default (EditableCheckbox)
// - Folder (EditableFileField) // - DefaultToToday (EditableDateField)
// - Level (EditableFormHeading) // - Folder (EditableFileField)
// - HideFromReports (EditableFormHeading / EditableLiteralField) // - Level (EditableFormHeading)
// - Content (EditableLiteralField) // - HideFromReports (EditableFormHeading / EditableLiteralField)
// - GroupID (EditableMemberListField) // - Content (EditableLiteralField)
// - MinValue (EditableNumericField) // - GroupID (EditableMemberListField)
// - MaxValue (EditableNumericField) // - MinValue (EditableNumericField)
// - MinLength (EditableTextField) // - MaxValue (EditableNumericField)
// - MaxLength (EditableTextField) // - MinLength (EditableTextField)
// - Rows (EditableTextField) // - MaxLength (EditableTextField)
// - Rows (EditableTextField)
$customSettings = $field->CustomSettings $customSettings = $field->CustomSettings
? unserialize($field->CustomSettings) ? unserialize($field->CustomSettings)
: array(); : array();
// Skip blank rules or fields with custom rules already // Skip blank rules or fields with custom rules already
if(empty($customSettings)) { if (empty($customSettings)) {
return; return;
} }
$field->migrateSettings($customSettings); $field->migrateSettings($customSettings);
$field->write(); $field->write();
} }
/** /**
* Create or find an existing field with the matched specification * Create or find an existing field with the matched specification
* *
* @param EditableFormField $field * @param EditableFormField $field
* @param string $stage * @param string $stage
* @param string $conditionOption * @param string $conditionOption
* @param string $display * @param string $display
* @param string $conditionFieldName * @param string $conditionFieldName
* @param string $value * @param string $value
* @return EditableCustomRule * @return EditableCustomRule
*/ */
protected function findOrCreateRule(EditableFormField $field, $stage, $conditionOption, $display, $conditionFieldName, $value) { protected function findOrCreateRule(EditableFormField $field, $stage, $conditionOption, $display, $conditionFieldName, $value)
// Get id of field {
$conditionField = $conditionFieldName // Get id of field
? EditableFormField::get()->filter('Name', $conditionFieldName)->first() $conditionField = $conditionFieldName
: null; ? EditableFormField::get()->filter('Name', $conditionFieldName)->first()
: null;
// If live, search stage record for matching one // If live, search stage record for matching one
if($stage === 'Live') { if ($stage === 'Live') {
$list = Versioned::get_by_stage('EditableCustomRule', 'Stage') $list = Versioned::get_by_stage('EditableCustomRule', 'Stage')
->filter(array( ->filter(array(
'ParentID' => $field->ID, 'ParentID' => $field->ID,
'ConditionFieldID' => $conditionField ? $conditionField->ID : 0, 'ConditionFieldID' => $conditionField ? $conditionField->ID : 0,
'Display' => $display, 'Display' => $display,
'ConditionOption' => $conditionOption 'ConditionOption' => $conditionOption
)); ));
if($value) { if ($value) {
$list = $list->filter('FieldValue', $value); $list = $list->filter('FieldValue', $value);
} else { } else {
$list = $list->where('"FieldValue" IS NULL OR "FieldValue" = \'\''); $list = $list->where('"FieldValue" IS NULL OR "FieldValue" = \'\'');
} }
$rule = $list->first(); $rule = $list->first();
if($rule) { if ($rule) {
$rule->write(); $rule->write();
$rule->publish("Stage", "Live"); $rule->publish("Stage", "Live");
return $rule; return $rule;
} }
} }
// If none found, or in stage, create new record // If none found, or in stage, create new record
$rule = new EditableCustomRule(); $rule = new EditableCustomRule();
$rule->ParentID = $field->ID; $rule->ParentID = $field->ID;
$rule->ConditionFieldID = $conditionField ? $conditionField->ID : 0; $rule->ConditionFieldID = $conditionField ? $conditionField->ID : 0;
$rule->Display = $display; $rule->Display = $display;
$rule->ConditionOption = $conditionOption; $rule->ConditionOption = $conditionOption;
$rule->FieldValue = $value; $rule->FieldValue = $value;
$rule->write(); $rule->write();
return $rule; return $rule;
} }
public function log($message) { public function log($message)
if($this->getQuiet()) { {
return; if ($this->getQuiet()) {
} return;
if(Director::is_cli()) { }
echo "{$message}\n"; if (Director::is_cli()) {
} else { echo "{$message}\n";
echo "{$message}<br />"; } else {
} echo "{$message}<br />";
} }
}
/** /**
* Set if this service should be quiet * Set if this service should be quiet
* *
* @param bool $quiet * @param bool $quiet
* @return $ths * @return $ths
*/ */
public function setQuiet($quiet) { public function setQuiet($quiet)
$this->quiet = $quiet; {
return $this; $this->quiet = $quiet;
} return $this;
}
public function getQuiet() {
return $this->quiet;
}
public function getQuiet()
{
return $this->quiet;
}
} }

View File

@ -5,17 +5,19 @@
* *
* @author dmooyman * @author dmooyman
*/ */
class UserFormsUpgradeTask extends BuildTask { class UserFormsUpgradeTask extends BuildTask
{
protected $title = "UserForms 3.0 Migration Tool"; protected $title = "UserForms 3.0 Migration Tool";
protected $description = "Upgrade tool for sites upgrading to userforms 3.0"; protected $description = "Upgrade tool for sites upgrading to userforms 3.0";
public function run($request) { public function run($request)
$service = Injector::inst()->create('UserFormsUpgradeService'); {
$service->log("Upgrading userforms module"); $service = Injector::inst()->create('UserFormsUpgradeService');
$service->setQuiet(false) $service->log("Upgrading userforms module");
->run(); $service->setQuiet(false)
$service->log("Done"); ->run();
} $service->log("Done");
}
} }

View File

@ -10,30 +10,29 @@
* @package userforms * @package userforms
*/ */
class UserFormsVersionedTask extends MigrationTask { class UserFormsVersionedTask extends MigrationTask
{
protected $title = "UserForms Versioned Initial Migration"; protected $title = "UserForms Versioned Initial Migration";
protected $description = "Publishes the existing forms"; protected $description = "Publishes the existing forms";
/** /**
* Publish the existing forms. * Publish the existing forms.
* *
*/ */
public function run($request) { public function run($request)
$forms = Versioned::get_by_stage('UserDefinedForm', 'Live'); {
$forms = Versioned::get_by_stage('UserDefinedForm', 'Live');
if($forms) { if ($forms) {
foreach($forms as $form) { foreach ($forms as $form) {
echo "Publishing $form->Title <br />"; echo "Publishing $form->Title <br />";
$form->doPublish(); $form->doPublish();
} }
echo "Complete"; echo "Complete";
} } else {
else { echo "No Forms Found";
echo "No Forms Found"; }
} }
}
} }

View File

@ -4,167 +4,178 @@
* @package userforms * @package userforms
*/ */
class EditableFormFieldTest extends FunctionalTest { class EditableFormFieldTest extends FunctionalTest
{
static $fixture_file = 'userforms/tests/EditableFormFieldTest.yml'; public static $fixture_file = 'userforms/tests/EditableFormFieldTest.yml';
function testFormFieldPermissions() { public function testFormFieldPermissions()
$text = $this->objFromFixture('EditableTextField', 'basic-text'); {
$text = $this->objFromFixture('EditableTextField', 'basic-text');
$this->logInWithPermission('ADMIN'); $this->logInWithPermission('ADMIN');
$this->assertTrue($text->canCreate()); $this->assertTrue($text->canCreate());
$this->assertTrue($text->canView()); $this->assertTrue($text->canView());
$this->assertTrue($text->canEdit()); $this->assertTrue($text->canEdit());
$this->assertTrue($text->canDelete()); $this->assertTrue($text->canDelete());
$text->setReadonly(true); $text->setReadonly(true);
$this->assertTrue($text->canView()); $this->assertTrue($text->canView());
$this->assertFalse($text->canEdit()); $this->assertFalse($text->canEdit());
$this->assertFalse($text->canDelete()); $this->assertFalse($text->canDelete());
$text->setReadonly(false); $text->setReadonly(false);
$this->assertTrue($text->canView()); $this->assertTrue($text->canView());
$this->assertTrue($text->canEdit()); $this->assertTrue($text->canEdit());
$this->assertTrue($text->canDelete()); $this->assertTrue($text->canDelete());
$member = Member::currentUser(); $member = Member::currentUser();
$member->logout(); $member->logout();
$this->logInWithPermission('SITETREE_VIEW_ALL'); $this->logInWithPermission('SITETREE_VIEW_ALL');
$this->assertFalse($text->canCreate()); $this->assertFalse($text->canCreate());
$text->setReadonly(false); $text->setReadonly(false);
$this->assertTrue($text->canView()); $this->assertTrue($text->canView());
$this->assertFalse($text->canEdit()); $this->assertFalse($text->canEdit());
$this->assertFalse($text->canDelete()); $this->assertFalse($text->canDelete());
$text->setReadonly(true); $text->setReadonly(true);
$this->assertTrue($text->canView()); $this->assertTrue($text->canView());
$this->assertFalse($text->canEdit()); $this->assertFalse($text->canEdit());
$this->assertFalse($text->canDelete()); $this->assertFalse($text->canDelete());
} }
function testCustomRules() { public function testCustomRules()
$this->logInWithPermission('ADMIN'); {
$form = $this->objFromFixture('UserDefinedForm', 'custom-rules-form'); $this->logInWithPermission('ADMIN');
$form = $this->objFromFixture('UserDefinedForm', 'custom-rules-form');
$checkbox = $form->Fields()->find('ClassName', 'EditableCheckbox'); $checkbox = $form->Fields()->find('ClassName', 'EditableCheckbox');
$field = $form->Fields()->find('ClassName', 'EditableTextField'); $field = $form->Fields()->find('ClassName', 'EditableTextField');
$rules = $checkbox->DisplayRules(); $rules = $checkbox->DisplayRules();
// form has 2 fields - a checkbox and a text field // form has 2 fields - a checkbox and a text field
// it has 1 rule - when ticked the checkbox hides the text field // it has 1 rule - when ticked the checkbox hides the text field
$this->assertEquals(1, $rules->Count()); $this->assertEquals(1, $rules->Count());
$this->assertEquals($rules, $checkbox->EffectiveDisplayRules()); $this->assertEquals($rules, $checkbox->EffectiveDisplayRules());
$checkboxRule = $rules->First(); $checkboxRule = $rules->First();
$checkboxRule->ConditionFieldID = $field->ID; $checkboxRule->ConditionFieldID = $field->ID;
$this->assertEquals($checkboxRule->Display, 'Hide'); $this->assertEquals($checkboxRule->Display, 'Hide');
$this->assertEquals($checkboxRule->ConditionOption, 'HasValue'); $this->assertEquals($checkboxRule->ConditionOption, 'HasValue');
$this->assertEquals($checkboxRule->FieldValue, '6'); $this->assertEquals($checkboxRule->FieldValue, '6');
// If field is required then all custom rules are disabled // If field is required then all custom rules are disabled
$checkbox->Required = true; $checkbox->Required = true;
$this->assertEquals(0, $checkbox->EffectiveDisplayRules()->count()); $this->assertEquals(0, $checkbox->EffectiveDisplayRules()->count());
} }
/** /**
* @covers EditableOption::getValue * @covers EditableOption::getValue
*/ */
public function testEditableOptionEmptyValue() { public function testEditableOptionEmptyValue()
$option = $this->objFromFixture('EditableOption', 'option-1'); {
$option->Value = ''; $option = $this->objFromFixture('EditableOption', 'option-1');
$option->Value = '';
// Disallow empty values // Disallow empty values
EditableOption::set_allow_empty_values(false); EditableOption::set_allow_empty_values(false);
$this->assertEquals($option->Title, $option->Value); $this->assertEquals($option->Title, $option->Value);
$option->Value = 'test'; $option->Value = 'test';
$this->assertEquals('test', $option->Value); $this->assertEquals('test', $option->Value);
// Allow empty values // Allow empty values
EditableOption::set_allow_empty_values(true); EditableOption::set_allow_empty_values(true);
$option->Value = ''; $option->Value = '';
$this->assertEquals('', $option->Value); $this->assertEquals('', $option->Value);
} }
function testEditableDropdownField() { public function testEditableDropdownField()
$dropdown = $this->objFromFixture('EditableDropdown', 'basic-dropdown'); {
$dropdown = $this->objFromFixture('EditableDropdown', 'basic-dropdown');
$field = $dropdown->getFormField(); $field = $dropdown->getFormField();
$this->assertThat($field, $this->isInstanceOf('DropdownField')); $this->assertThat($field, $this->isInstanceOf('DropdownField'));
$values = $field->getSource(); $values = $field->getSource();
$this->assertEquals(array('Option 1' => 'Option 1', 'Option 2' => 'Option 2'), $values); $this->assertEquals(array('Option 1' => 'Option 1', 'Option 2' => 'Option 2'), $values);
} }
function testEditableRadioField() { public function testEditableRadioField()
$radio = $this->objFromFixture('EditableRadioField', 'radio-field'); {
$radio = $this->objFromFixture('EditableRadioField', 'radio-field');
$field = $radio->getFormField(); $field = $radio->getFormField();
$this->assertThat($field, $this->isInstanceOf('OptionsetField')); $this->assertThat($field, $this->isInstanceOf('OptionsetField'));
$values = $field->getSource(); $values = $field->getSource();
$this->assertEquals(array('Option 5' => 'Option 5', 'Option 6' => 'Option 6'), $values); $this->assertEquals(array('Option 5' => 'Option 5', 'Option 6' => 'Option 6'), $values);
} }
function testMultipleOptionDuplication() { public function testMultipleOptionDuplication()
$dropdown = $this->objFromFixture('EditableDropdown','basic-dropdown'); {
$dropdown = $this->objFromFixture('EditableDropdown', 'basic-dropdown');
$clone = $dropdown->duplicate(); $clone = $dropdown->duplicate();
$this->assertEquals($clone->Options()->Count(), $dropdown->Options()->Count()); $this->assertEquals($clone->Options()->Count(), $dropdown->Options()->Count());
foreach($clone->Options() as $option) { foreach ($clone->Options() as $option) {
$orginal = $dropdown->Options()->find('Title', $option->Title); $orginal = $dropdown->Options()->find('Title', $option->Title);
$this->assertEquals($orginal->Sort, $option->Sort); $this->assertEquals($orginal->Sort, $option->Sort);
} }
} }
public function testFileField() { public function testFileField()
$fileField = $this->objFromFixture('EditableFileField', 'file-field'); {
$formField = $fileField->getFormField(); $fileField = $this->objFromFixture('EditableFileField', 'file-field');
$formField = $fileField->getFormField();
$this->assertContains('jpg', $formField->getValidator()->getAllowedExtensions()); $this->assertContains('jpg', $formField->getValidator()->getAllowedExtensions());
$this->assertNotContains('notallowedextension', $formField->getValidator()->getAllowedExtensions()); $this->assertNotContains('notallowedextension', $formField->getValidator()->getAllowedExtensions());
} }
public function testFileFieldAllowedExtensionsBlacklist() { public function testFileFieldAllowedExtensionsBlacklist()
Config::inst()->update('EditableFileField', 'allowed_extensions_blacklist', array('jpg')); {
$fileField = $this->objFromFixture('EditableFileField', 'file-field'); Config::inst()->update('EditableFileField', 'allowed_extensions_blacklist', array('jpg'));
$formField = $fileField->getFormField(); $fileField = $this->objFromFixture('EditableFileField', 'file-field');
$formField = $fileField->getFormField();
$this->assertNotContains('jpg', $formField->getValidator()->getAllowedExtensions()); $this->assertNotContains('jpg', $formField->getValidator()->getAllowedExtensions());
} }
/** /**
* Verify that unique names are automatically generated for each formfield * Verify that unique names are automatically generated for each formfield
*/ */
public function testUniqueName() { public function testUniqueName()
$textfield1 = new EditableTextField(); {
$this->assertEmpty($textfield1->Name); $textfield1 = new EditableTextField();
$this->assertEmpty($textfield1->Name);
// Write values // Write values
$textfield1->write(); $textfield1->write();
$textfield2 = new EditableTextField(); $textfield2 = new EditableTextField();
$textfield2->write(); $textfield2->write();
$checkboxField = new EditableCheckbox(); $checkboxField = new EditableCheckbox();
$checkboxField->write(); $checkboxField->write();
// Test values are in the expected format // Test values are in the expected format
$this->assertRegExp('/^EditableTextField_.+/', $textfield1->Name); $this->assertRegExp('/^EditableTextField_.+/', $textfield1->Name);
$this->assertRegExp('/^EditableTextField_.+/', $textfield2->Name); $this->assertRegExp('/^EditableTextField_.+/', $textfield2->Name);
$this->assertRegExp('/^EditableCheckbox_.+/', $checkboxField->Name); $this->assertRegExp('/^EditableCheckbox_.+/', $checkboxField->Name);
$this->assertNotEquals($textfield1->Name, $textfield2->Name); $this->assertNotEquals($textfield1->Name, $textfield2->Name);
} }
public function testLengthRange() { public function testLengthRange()
{
/** @var EditableTextField $textField */ /** @var EditableTextField $textField */
$textField = $this->objFromFixture('EditableTextField', 'basic-text'); $textField = $this->objFromFixture('EditableTextField', 'basic-text');
@ -193,6 +204,4 @@ class EditableFormFieldTest extends FunctionalTest {
$this->assertEquals(10, $attributes['data-rule-minlength']); $this->assertEquals(10, $attributes['data-rule-minlength']);
$this->assertEquals(20, $attributes['data-rule-maxlength']); $this->assertEquals(20, $attributes['data-rule-maxlength']);
} }
} }

View File

@ -3,40 +3,44 @@
/** /**
* Tests the {@see EditableLiteralField} class * Tests the {@see EditableLiteralField} class
*/ */
class EditableLiteralFieldTest extends SapphireTest { class EditableLiteralFieldTest extends SapphireTest
{
public function setUp() { public function setUp()
parent::setUp(); {
HtmlEditorConfig::set_active('cms'); parent::setUp();
} HtmlEditorConfig::set_active('cms');
}
/** /**
* Tests the sanitisation of HTML content * Tests the sanitisation of HTML content
*/ */
public function testSanitisation() { public function testSanitisation()
$rawContent = '<h1>Welcome</h1><script>alert("Hello!");</script><p>Giant Robots!</p>'; {
$safeContent = '<h1>Welcome</h1><p>Giant Robots!</p>'; $rawContent = '<h1>Welcome</h1><script>alert("Hello!");</script><p>Giant Robots!</p>';
$field = new EditableLiteralField(); $safeContent = '<h1>Welcome</h1><p>Giant Robots!</p>';
$field = new EditableLiteralField();
// Test with sanitisation enabled // Test with sanitisation enabled
Config::inst()->update('HtmlEditorField', 'sanitise_server_side', true); Config::inst()->update('HtmlEditorField', 'sanitise_server_side', true);
$field->setContent($rawContent); $field->setContent($rawContent);
$this->assertEquals($safeContent, $field->getContent()); $this->assertEquals($safeContent, $field->getContent());
// Test with sanitisation disabled // Test with sanitisation disabled
Config::inst()->remove('HtmlEditorField', 'sanitise_server_side'); Config::inst()->remove('HtmlEditorField', 'sanitise_server_side');
$field->setContent($rawContent); $field->setContent($rawContent);
$this->assertEquals($rawContent, $field->getContent()); $this->assertEquals($rawContent, $field->getContent());
} }
public function testHideLabel() { public function testHideLabel()
$field = new EditableLiteralField(array( {
'Title' => 'Test label' $field = new EditableLiteralField(array(
)); 'Title' => 'Test label'
));
$this->assertContains('Test label', $field->getFormField()->Field()); $this->assertContains('Test label', $field->getFormField()->Field());
$field->HideLabel = true; $field->HideLabel = true;
$this->assertNotContains('Test label', $field->getFormField()->Field()); $this->assertNotContains('Test label', $field->getFormField()->Field());
} }
} }

View File

@ -5,79 +5,85 @@
* *
* @author dmooyman * @author dmooyman
*/ */
class SecureEditableFileFieldTest extends SapphireTest { class SecureEditableFileFieldTest extends SapphireTest
{
protected $usesDatabase = true; protected $usesDatabase = true;
public function setUp() { public function setUp()
parent::setUp(); {
parent::setUp();
if(!class_exists('SecureFileExtension')) { if (!class_exists('SecureFileExtension')) {
$this->skipTest = true; $this->skipTest = true;
$this->markTestSkipped(get_class() . ' skipped unless running with securefiles'); $this->markTestSkipped(get_class() . ' skipped unless running with securefiles');
} }
Config::inst()->update('EditableFileField', 'secure_folder_name', 'SecureEditableFileFieldTest/SecureUploads'); Config::inst()->update('EditableFileField', 'secure_folder_name', 'SecureEditableFileFieldTest/SecureUploads');
$this->clearPath(); $this->clearPath();
} }
public function tearDown() { public function tearDown()
$this->clearPath(); {
parent::tearDown(); $this->clearPath();
} parent::tearDown();
}
protected function clearPath() { protected function clearPath()
if(file_exists(ASSETS_PATH . '/SecureEditableFileFieldTest')) { {
Filesystem::removeFolder(ASSETS_PATH . '/SecureEditableFileFieldTest'); if (file_exists(ASSETS_PATH . '/SecureEditableFileFieldTest')) {
} Filesystem::removeFolder(ASSETS_PATH . '/SecureEditableFileFieldTest');
} }
}
/** /**
* Test that newly created folders are secure * Test that newly created folders are secure
*/ */
public function testCreateFolder() { public function testCreateFolder()
$field = new EditableFileField(); {
$field->write(); $field = new EditableFileField();
$this->assertTrue($field->getIsSecure()); $field->write();
$this->assertTrue($field->Folder()->exists()); $this->assertTrue($field->getIsSecure());
$this->assertEquals('assets/SecureEditableFileFieldTest/SecureUploads/', $field->Folder()->Filename); $this->assertTrue($field->Folder()->exists());
$this->assertEquals('OnlyTheseUsers', $field->Folder()->CanViewType); $this->assertEquals('assets/SecureEditableFileFieldTest/SecureUploads/', $field->Folder()->Filename);
$this->assertEquals(1, $field->Folder()->ViewerGroups()->first()->Permissions()->filter('code', 'ADMIN')->count()); $this->assertEquals('OnlyTheseUsers', $field->Folder()->CanViewType);
} $this->assertEquals(1, $field->Folder()->ViewerGroups()->first()->Permissions()->filter('code', 'ADMIN')->count());
}
/** /**
* Test new folders that are created without security enabled * Test new folders that are created without security enabled
*/ */
public function testCreateInsecure() { public function testCreateInsecure()
Config::inst()->update('EditableFileField', 'disable_security', true); {
Config::inst()->update('EditableFileField', 'disable_security', true);
// Esure folder is created without a folder // Esure folder is created without a folder
$field = new EditableFileField(); $field = new EditableFileField();
$field->write(); $field->write();
$this->assertFalse($field->getIsSecure()); $this->assertFalse($field->getIsSecure());
$this->assertFalse($field->Folder()->exists()); $this->assertFalse($field->Folder()->exists());
// Assigning a non-secure folder doesn't secure this // Assigning a non-secure folder doesn't secure this
$folder = Folder::find_or_make('SecureEditableFileFieldTest/PublicFolder'); $folder = Folder::find_or_make('SecureEditableFileFieldTest/PublicFolder');
$field->FolderID = $folder->ID; $field->FolderID = $folder->ID;
$field->write(); $field->write();
$this->assertFalse($field->getIsSecure()); $this->assertFalse($field->getIsSecure());
$this->assertTrue($field->Folder()->exists()); $this->assertTrue($field->Folder()->exists());
$this->assertEquals('assets/SecureEditableFileFieldTest/PublicFolder/', $field->Folder()->Filename); $this->assertEquals('assets/SecureEditableFileFieldTest/PublicFolder/', $field->Folder()->Filename);
$this->assertEquals('Inherit', $field->Folder()->CanViewType); $this->assertEquals('Inherit', $field->Folder()->CanViewType);
// Enabling security and re-saving will force this field to be made secure (but not changed) // Enabling security and re-saving will force this field to be made secure (but not changed)
Config::inst()->update('EditableFileField', 'disable_security', false); Config::inst()->update('EditableFileField', 'disable_security', false);
singleton('EditableFileField')->requireDefaultRecords(); singleton('EditableFileField')->requireDefaultRecords();
// Reload record from DB // Reload record from DB
$field = EditableFileField::get()->byID($field->ID); $field = EditableFileField::get()->byID($field->ID);
// Existing folder is now secured (retro-actively secures any old uploads) // Existing folder is now secured (retro-actively secures any old uploads)
$this->assertTrue($field->getIsSecure()); $this->assertTrue($field->getIsSecure());
$this->assertTrue($field->Folder()->exists()); $this->assertTrue($field->Folder()->exists());
$this->assertEquals('assets/SecureEditableFileFieldTest/PublicFolder/', $field->Folder()->Filename); $this->assertEquals('assets/SecureEditableFileFieldTest/PublicFolder/', $field->Folder()->Filename);
$this->assertEquals('OnlyTheseUsers', $field->Folder()->CanViewType); $this->assertEquals('OnlyTheseUsers', $field->Folder()->CanViewType);
$this->assertEquals(1, $field->Folder()->ViewerGroups()->first()->Permissions()->filter('code', 'ADMIN')->count()); $this->assertEquals(1, $field->Folder()->ViewerGroups()->first()->Permissions()->filter('code', 'ADMIN')->count());
} }
} }

View File

@ -4,282 +4,296 @@
* @package userforms * @package userforms
*/ */
class UserDefinedFormControllerTest extends FunctionalTest { class UserDefinedFormControllerTest extends FunctionalTest
{
static $fixture_file = 'UserDefinedFormTest.yml'; public static $fixture_file = 'UserDefinedFormTest.yml';
public function testProcess() { public function testProcess()
$form = $this->setupFormFrontend(); {
$form = $this->setupFormFrontend();
$controller = new UserDefinedFormControllerTest_Controller($form); $controller = new UserDefinedFormControllerTest_Controller($form);
$this->autoFollowRedirection = false; $this->autoFollowRedirection = false;
$this->clearEmails(); $this->clearEmails();
// load the form // load the form
$this->get($form->URLSegment); $this->get($form->URLSegment);
$field = $this->objFromFixture('EditableTextField', 'basic-text'); $field = $this->objFromFixture('EditableTextField', 'basic-text');
$response = $this->submitForm('UserForm_Form', null, array($field->Name => 'Basic Value')); $response = $this->submitForm('UserForm_Form', null, array($field->Name => 'Basic Value'));
// should have a submitted form field now // should have a submitted form field now
$submitted = DataObject::get('SubmittedFormField', "\"Name\" = 'basic-text-name'"); $submitted = DataObject::get('SubmittedFormField', "\"Name\" = 'basic-text-name'");
$this->assertDOSAllMatch(array('Name' => 'basic-text-name', 'Value' => 'Basic Value', 'Title' => 'Basic Text Field'), $submitted); $this->assertDOSAllMatch(array('Name' => 'basic-text-name', 'Value' => 'Basic Value', 'Title' => 'Basic Text Field'), $submitted);
// check emails // check emails
$this->assertEmailSent('test@example.com', 'no-reply@example.com', 'Email Subject'); $this->assertEmailSent('test@example.com', 'no-reply@example.com', 'Email Subject');
$email = $this->findEmail('test@example.com', 'no-reply@example.com', 'Email Subject'); $email = $this->findEmail('test@example.com', 'no-reply@example.com', 'Email Subject');
// assert that the email has the field title and the value html email // assert that the email has the field title and the value html email
$parser = new CSSContentParser($email['content']); $parser = new CSSContentParser($email['content']);
$title = $parser->getBySelector('strong'); $title = $parser->getBySelector('strong');
$this->assertEquals('Basic Text Field', (string) $title[0], 'Email contains the field name'); $this->assertEquals('Basic Text Field', (string) $title[0], 'Email contains the field name');
$value = $parser->getBySelector('dd'); $value = $parser->getBySelector('dd');
$this->assertEquals('Basic Value', (string) $value[0], 'Email contains the value'); $this->assertEquals('Basic Value', (string) $value[0], 'Email contains the value');
// no html // no html
$this->assertEmailSent('nohtml@example.com', 'no-reply@example.com', 'Email Subject'); $this->assertEmailSent('nohtml@example.com', 'no-reply@example.com', 'Email Subject');
$nohtml = $this->findEmail('nohtml@example.com', 'no-reply@example.com', 'Email Subject'); $nohtml = $this->findEmail('nohtml@example.com', 'no-reply@example.com', 'Email Subject');
$this->assertContains('Basic Text Field: Basic Value', $nohtml['content'], 'Email contains no html'); $this->assertContains('Basic Text Field: Basic Value', $nohtml['content'], 'Email contains no html');
// no data // no data
$this->assertEmailSent('nodata@example.com', 'no-reply@example.com', 'Email Subject'); $this->assertEmailSent('nodata@example.com', 'no-reply@example.com', 'Email Subject');
$nodata = $this->findEmail('nodata@example.com', 'no-reply@example.com', 'Email Subject'); $nodata = $this->findEmail('nodata@example.com', 'no-reply@example.com', 'Email Subject');
$parser = new CSSContentParser($nodata['content']); $parser = new CSSContentParser($nodata['content']);
$list = $parser->getBySelector('dl'); $list = $parser->getBySelector('dl');
$this->assertFalse(isset($list[0]), 'Email contains no fields'); $this->assertFalse(isset($list[0]), 'Email contains no fields');
// check to see if the user was redirected (301) // check to see if the user was redirected (301)
$this->assertEquals($response->getStatusCode(), 302); $this->assertEquals($response->getStatusCode(), 302);
$this->assertStringEndsWith('finished#uff', $response->getHeader('Location')); $this->assertStringEndsWith('finished#uff', $response->getHeader('Location'));
} }
public function testValidation() { public function testValidation()
$form = $this->setupFormFrontend('email-form'); {
$form = $this->setupFormFrontend('email-form');
// Post with no fields // Post with no fields
$this->get($form->URLSegment); $this->get($form->URLSegment);
$response = $this->submitForm('UserForm_Form', null, array()); $response = $this->submitForm('UserForm_Form', null, array());
$this->assertPartialMatchBySelector( $this->assertPartialMatchBySelector(
'.field .message', '.field .message',
array('This field is required') array('This field is required')
); );
// Post with all fields, but invalid email // Post with all fields, but invalid email
$this->get($form->URLSegment); $this->get($form->URLSegment);
$this->submitForm('UserForm_Form', null, array( $this->submitForm('UserForm_Form', null, array(
'required-email' => 'invalid', 'required-email' => 'invalid',
'required-text' => 'bob' 'required-text' => 'bob'
)); ));
$this->assertPartialMatchBySelector( $this->assertPartialMatchBySelector(
'.field .message', '.field .message',
array('Please enter an email address') array('Please enter an email address')
); );
// Post with only required // Post with only required
$this->get($form->URLSegment); $this->get($form->URLSegment);
$this->submitForm('UserForm_Form', null, array( $this->submitForm('UserForm_Form', null, array(
'required-text' => 'bob' 'required-text' => 'bob'
)); ));
$this->assertPartialMatchBySelector( $this->assertPartialMatchBySelector(
'p', 'p',
array("Thanks, we've received your submission.") array("Thanks, we've received your submission.")
); );
} }
public function testFinished() { public function testFinished()
$form = $this->setupFormFrontend(); {
$form = $this->setupFormFrontend();
// set formProcessed and SecurityID to replicate the form being filled out // set formProcessed and SecurityID to replicate the form being filled out
$this->session()->inst_set('SecurityID', 1); $this->session()->inst_set('SecurityID', 1);
$this->session()->inst_set('FormProcessed', 1); $this->session()->inst_set('FormProcessed', 1);
$response = $this->get($form->URLSegment.'/finished'); $response = $this->get($form->URLSegment.'/finished');
$this->assertContains($form->OnCompleteMessage ,$response->getBody()); $this->assertContains($form->OnCompleteMessage, $response->getBody());
} }
public function testAppendingFinished() { public function testAppendingFinished()
$form = $this->setupFormFrontend(); {
$form = $this->setupFormFrontend();
// replicate finished being added to the end of the form URL without the form being filled out // replicate finished being added to the end of the form URL without the form being filled out
$this->session()->inst_set('SecurityID', 1); $this->session()->inst_set('SecurityID', 1);
$this->session()->inst_set('FormProcessed', null); $this->session()->inst_set('FormProcessed', null);
$response = $this->get($form->URLSegment.'/finished'); $response = $this->get($form->URLSegment.'/finished');
$this->assertNotContains($form->OnCompleteMessage ,$response->getBody()); $this->assertNotContains($form->OnCompleteMessage, $response->getBody());
} }
public function testForm() { public function testForm()
$form = $this->objFromFixture('UserDefinedForm', 'basic-form-page'); {
$form = $this->objFromFixture('UserDefinedForm', 'basic-form-page');
$controller = new UserDefinedFormControllerTest_Controller($form); $controller = new UserDefinedFormControllerTest_Controller($form);
// test form // test form
$this->assertEquals($controller->Form()->getName(), 'Form', 'The form is referenced as Form'); $this->assertEquals($controller->Form()->getName(), 'Form', 'The form is referenced as Form');
$this->assertEquals($controller->Form()->Fields()->Count(), 1); // disabled SecurityID token fields $this->assertEquals($controller->Form()->Fields()->Count(), 1); // disabled SecurityID token fields
$this->assertEquals($controller->Form()->Actions()->Count(), 1); $this->assertEquals($controller->Form()->Actions()->Count(), 1);
$this->assertEquals(count($controller->Form()->getValidator()->getRequired()), 0); $this->assertEquals(count($controller->Form()->getValidator()->getRequired()), 0);
$requiredForm = $this->objFromFixture('UserDefinedForm', 'validation-form'); $requiredForm = $this->objFromFixture('UserDefinedForm', 'validation-form');
$controller = new UserDefinedFormControllerTest_Controller($requiredForm); $controller = new UserDefinedFormControllerTest_Controller($requiredForm);
$this->assertEquals($controller->Form()->Fields()->Count(), 1); // disabled SecurityID token fields $this->assertEquals($controller->Form()->Fields()->Count(), 1); // disabled SecurityID token fields
$this->assertEquals($controller->Form()->Actions()->Count(), 1); $this->assertEquals($controller->Form()->Actions()->Count(), 1);
$this->assertEquals(count($controller->Form()->getValidator()->getRequired()), 1); $this->assertEquals(count($controller->Form()->getValidator()->getRequired()), 1);
} }
public function testGetFormFields() { public function testGetFormFields()
// generating the fieldset of fields {
$form = $this->objFromFixture('UserDefinedForm', 'basic-form-page'); // generating the fieldset of fields
$form = $this->objFromFixture('UserDefinedForm', 'basic-form-page');
$controller = new UserDefinedFormControllerTest_Controller($form); $controller = new UserDefinedFormControllerTest_Controller($form);
$formSteps = $controller->Form()->getFormFields();
$firstStep = $formSteps->first();
$formSteps = $controller->Form()->getFormFields(); $this->assertEquals($formSteps->Count(), 1);
$firstStep = $formSteps->first(); $this->assertEquals($firstStep->getChildren()->Count(), 1);
$this->assertEquals($formSteps->Count(), 1); // custom error message on a form field
$this->assertEquals($firstStep->getChildren()->Count(), 1); $requiredForm = $this->objFromFixture('UserDefinedForm', 'validation-form');
$controller = new UserDefinedFormControllerTest_Controller($requiredForm);
// custom error message on a form field UserDefinedForm::config()->required_identifier = "*";
$requiredForm = $this->objFromFixture('UserDefinedForm', 'validation-form');
$controller = new UserDefinedFormControllerTest_Controller($requiredForm);
UserDefinedForm::config()->required_identifier = "*"; $formSteps = $controller->Form()->getFormFields();
$firstStep = $formSteps->first();
$firstField = $firstStep->getChildren()->first();
$formSteps = $controller->Form()->getFormFields(); $this->assertEquals('Custom Error Message', $firstField->getCustomValidationMessage());
$firstStep = $formSteps->first(); $this->assertEquals($firstField->Title(), 'Required Text Field <span class=\'required-identifier\'>*</span>');
$firstField = $firstStep->getChildren()->first();
$this->assertEquals('Custom Error Message', $firstField->getCustomValidationMessage()); // test custom right title
$this->assertEquals($firstField->Title(), 'Required Text Field <span class=\'required-identifier\'>*</span>'); $field = $form->Fields()->limit(1, 1)->First();
$field->RightTitle = 'Right Title';
$field->write();
// test custom right title $controller = new UserDefinedFormControllerTest_Controller($form);
$field = $form->Fields()->limit(1, 1)->First(); $formSteps = $controller->Form()->getFormFields();
$field->RightTitle = 'Right Title'; $firstStep = $formSteps->first();
$field->write();
$controller = new UserDefinedFormControllerTest_Controller($form); $this->assertEquals($firstStep->getChildren()->First()->RightTitle(), "Right Title");
$formSteps = $controller->Form()->getFormFields();
$firstStep = $formSteps->first();
$this->assertEquals($firstStep->getChildren()->First()->RightTitle(), "Right Title"); // test empty form
$emptyForm = $this->objFromFixture('UserDefinedForm', 'empty-form');
$controller = new UserDefinedFormControllerTest_Controller($emptyForm);
// test empty form $this->assertFalse($controller->Form()->getFormFields()->exists());
$emptyForm = $this->objFromFixture('UserDefinedForm', 'empty-form'); }
$controller = new UserDefinedFormControllerTest_Controller($emptyForm);
$this->assertFalse($controller->Form()->getFormFields()->exists()); public function testGetFormActions()
} {
// generating the fieldset of actions
$form = $this->objFromFixture('UserDefinedForm', 'basic-form-page');
public function testGetFormActions() $controller = new UserDefinedFormControllerTest_Controller($form);
{ $actions = $controller->Form()->getFormActions();
// generating the fieldset of actions
$form = $this->objFromFixture('UserDefinedForm', 'basic-form-page');
$controller = new UserDefinedFormControllerTest_Controller($form); // by default will have 1 submit button which links to process
$actions = $controller->Form()->getFormActions(); $expected = new FieldList(new FormAction('process', 'Submit'));
$expected->setForm($controller->Form());
// by default will have 1 submit button which links to process $this->assertEquals($actions, $expected);
$expected = new FieldList(new FormAction('process', 'Submit'));
$expected->setForm($controller->Form());
$this->assertEquals($actions, $expected); // the custom popup should have a reset button and a custom text
$custom = $this->objFromFixture('UserDefinedForm', 'form-with-reset-and-custom-action');
$controller = new UserDefinedFormControllerTest_Controller($custom);
$actions = $controller->Form()->getFormActions();
// the custom popup should have a reset button and a custom text $expected = new FieldList(new FormAction('process', 'Custom Button'));
$custom = $this->objFromFixture('UserDefinedForm', 'form-with-reset-and-custom-action'); $expected->push(new ResetFormAction("clearForm", "Clear"));
$controller = new UserDefinedFormControllerTest_Controller($custom); $expected->setForm($controller->Form());
$actions = $controller->Form()->getFormActions();
$expected = new FieldList(new FormAction('process', 'Custom Button')); $this->assertEquals($actions, $expected);
$expected->push(new ResetFormAction("clearForm", "Clear")); }
$expected->setForm($controller->Form());
$this->assertEquals($actions, $expected); public function testRenderingIntoFormTemplate()
} {
$form = $this->setupFormFrontend();
public function testRenderingIntoFormTemplate() { $form->Content = 'This is some content without a form nested between it';
$form = $this->setupFormFrontend(); $form->doPublish();
$form->Content = 'This is some content without a form nested between it'; $controller = new UserDefinedFormControllerTest_Controller($form);
$form->doPublish();
$controller = new UserDefinedFormControllerTest_Controller($form); // check to see if $Form is replaced to inside the content
$index = new ArrayData($controller->index());
$parser = new CSSContentParser($index->renderWith(array('UserDefinedFormControllerTest')));
// check to see if $Form is replaced to inside the content $this->checkTemplateIsCorrect($parser);
$index = new ArrayData($controller->index()); }
$parser = new CSSContentParser($index->renderWith(array('UserDefinedFormControllerTest')));
$this->checkTemplateIsCorrect($parser); public function testRenderingIntoTemplateWithSubstringReplacement()
} {
$form = $this->setupFormFrontend();
public function testRenderingIntoTemplateWithSubstringReplacement() { $controller = new UserDefinedFormControllerTest_Controller($form);
$form = $this->setupFormFrontend();
$controller = new UserDefinedFormControllerTest_Controller($form); // check to see if $Form is replaced to inside the content
$index = new ArrayData($controller->index());
$parser = new CSSContentParser($index->renderWith(array('UserDefinedFormControllerTest')));
// check to see if $Form is replaced to inside the content $this->checkTemplateIsCorrect($parser);
$index = new ArrayData($controller->index()); }
$parser = new CSSContentParser($index->renderWith(array('UserDefinedFormControllerTest'))); /**
* Publish a form for use on the frontend
*
* @param string $fixtureName
* @return UserDefinedForm
*/
protected function setupFormFrontend($fixtureName = 'basic-form-page')
{
$form = $this->objFromFixture('UserDefinedForm', $fixtureName);
$this->logInWithPermission('ADMIN');
$this->checkTemplateIsCorrect($parser); $form->doPublish();
}
/**
* Publish a form for use on the frontend
*
* @param string $fixtureName
* @return UserDefinedForm
*/
protected function setupFormFrontend($fixtureName = 'basic-form-page') {
$form = $this->objFromFixture('UserDefinedForm', $fixtureName);
$this->logInWithPermission('ADMIN');
$form->doPublish(); $member = Member::currentUser();
$member->logOut();
$member = Member::currentUser(); return $form;
$member->logOut(); }
return $form; public function checkTemplateIsCorrect($parser)
} {
$this->assertArrayHasKey(0, $parser->getBySelector('form#UserForm_Form'));
public function checkTemplateIsCorrect($parser) { // check for the input
$this->assertArrayHasKey(0, $parser->getBySelector('form#UserForm_Form')); $this->assertArrayHasKey(0, $parser->getBySelector('input.text'));
// check for the input // check for the label and the text
$this->assertArrayHasKey(0, $parser->getBySelector('input.text')); $label = $parser->getBySelector('label.left');
$this->assertArrayHasKey(0, $label);
// check for the label and the text $this->assertEquals((string) $label[0][0], "Basic Text Field", "Label contains correct field name");
$label = $parser->getBySelector('label.left');
$this->assertArrayHasKey(0, $label);
$this->assertEquals((string) $label[0][0], "Basic Text Field", "Label contains correct field name"); // check for the action
$action = $parser->getBySelector('input.action');
$this->assertArrayHasKey(0, $action);
// check for the action $this->assertEquals((string) $action[0]['value'], "Submit", "Submit button has default text");
$action = $parser->getBySelector('input.action'); }
$this->assertArrayHasKey(0, $action);
$this->assertEquals((string) $action[0]['value'], "Submit", "Submit button has default text");
}
} }
class UserDefinedFormControllerTest_Controller extends UserDefinedForm_Controller implements TestOnly { class UserDefinedFormControllerTest_Controller extends UserDefinedForm_Controller implements TestOnly
{
/** /**
* Overloaded to avoid inconsistencies between 2.4.2 and 2.4.3 (disables all security tokens in unit tests by default) * Overloaded to avoid inconsistencies between 2.4.2 and 2.4.3 (disables all security tokens in unit tests by default)
*/ */
public function Form() { public function Form()
$form = parent::Form(); {
$form = parent::Form();
if($form) $form->disableSecurityToken(); if ($form) {
$form->disableSecurityToken();
return $form; }
}
return $form;
}
} }

View File

@ -3,421 +3,436 @@
/** /**
* @package userforms * @package userforms
*/ */
class UserDefinedFormTest extends FunctionalTest { class UserDefinedFormTest extends FunctionalTest
{
static $fixture_file = 'UserDefinedFormTest.yml'; public static $fixture_file = 'UserDefinedFormTest.yml';
public function testRollbackToVersion() { public function testRollbackToVersion()
$this->markTestSkipped( {
'UserDefinedForm::rollback() has not been implemented completely' $this->markTestSkipped(
); 'UserDefinedForm::rollback() has not been implemented completely'
);
// @todo // @todo
$this->logInWithPermission('ADMIN'); $this->logInWithPermission('ADMIN');
$form = $this->objFromFixture('UserDefinedForm', 'basic-form-page'); $form = $this->objFromFixture('UserDefinedForm', 'basic-form-page');
$form->SubmitButtonText = 'Button Text'; $form->SubmitButtonText = 'Button Text';
$form->write(); $form->write();
$form->doPublish(); $form->doPublish();
$origVersion = $form->Version; $origVersion = $form->Version;
$form->SubmitButtonText = 'Updated Button Text'; $form->SubmitButtonText = 'Updated Button Text';
$form->write(); $form->write();
$form->doPublish(); $form->doPublish();
// check published site // check published site
$updated = Versioned::get_one_by_stage("UserDefinedForm", "Stage", "\"UserDefinedForm\".\"ID\" = $form->ID"); $updated = Versioned::get_one_by_stage("UserDefinedForm", "Stage", "\"UserDefinedForm\".\"ID\" = $form->ID");
$this->assertEquals($updated->SubmitButtonText, 'Updated Button Text'); $this->assertEquals($updated->SubmitButtonText, 'Updated Button Text');
$form->doRollbackTo($origVersion); $form->doRollbackTo($origVersion);
$orignal = Versioned::get_one_by_stage("UserDefinedForm", "Stage", "\"UserDefinedForm\".\"ID\" = $form->ID"); $orignal = Versioned::get_one_by_stage("UserDefinedForm", "Stage", "\"UserDefinedForm\".\"ID\" = $form->ID");
$this->assertEquals($orignal->SubmitButtonText, 'Button Text'); $this->assertEquals($orignal->SubmitButtonText, 'Button Text');
} }
public function testGetCMSFields() { public function testGetCMSFields()
$this->logInWithPermission('ADMIN'); {
$form = $this->objFromFixture('UserDefinedForm', 'basic-form-page'); $this->logInWithPermission('ADMIN');
$form = $this->objFromFixture('UserDefinedForm', 'basic-form-page');
$fields = $form->getCMSFields(); $fields = $form->getCMSFields();
$this->assertTrue($fields->dataFieldByName('Fields') !== null); $this->assertTrue($fields->dataFieldByName('Fields') !== null);
$this->assertTrue($fields->dataFieldByName('EmailRecipients') != null); $this->assertTrue($fields->dataFieldByName('EmailRecipients') != null);
$this->assertTrue($fields->dataFieldByName('Submissions') != null); $this->assertTrue($fields->dataFieldByName('Submissions') != null);
$this->assertTrue($fields->dataFieldByName('OnCompleteMessage') != null); $this->assertTrue($fields->dataFieldByName('OnCompleteMessage') != null);
} }
public function testEmailRecipientPopup() { public function testEmailRecipientPopup()
$this->logInWithPermission('ADMIN'); {
$this->logInWithPermission('ADMIN');
$form = $this->objFromFixture('UserDefinedForm', 'basic-form-page'); $form = $this->objFromFixture('UserDefinedForm', 'basic-form-page');
$popup = new UserDefinedForm_EmailRecipient(); $popup = new UserDefinedForm_EmailRecipient();
$popup->FormID = $form->ID; $popup->FormID = $form->ID;
$fields = $popup->getCMSFields(); $fields = $popup->getCMSFields();
$this->assertTrue($fields->dataFieldByName('EmailSubject') !== null); $this->assertTrue($fields->dataFieldByName('EmailSubject') !== null);
$this->assertTrue($fields->dataFieldByName('EmailFrom') !== null); $this->assertTrue($fields->dataFieldByName('EmailFrom') !== null);
$this->assertTrue($fields->dataFieldByName('EmailAddress') !== null); $this->assertTrue($fields->dataFieldByName('EmailAddress') !== null);
$this->assertTrue($fields->dataFieldByName('HideFormData') !== null); $this->assertTrue($fields->dataFieldByName('HideFormData') !== null);
$this->assertTrue($fields->dataFieldByName('SendPlain') !== null); $this->assertTrue($fields->dataFieldByName('SendPlain') !== null);
$this->assertTrue($fields->dataFieldByName('EmailBody') !== null); $this->assertTrue($fields->dataFieldByName('EmailBody') !== null);
// add an email field, it should now add a or from X address picker // add an email field, it should now add a or from X address picker
$email = $this->objFromFixture('EditableEmailField','email-field'); $email = $this->objFromFixture('EditableEmailField', 'email-field');
$form->Fields()->add($email); $form->Fields()->add($email);
$popup->write(); $popup->write();
$fields = $popup->getCMSFields(); $fields = $popup->getCMSFields();
$this->assertThat($fields->dataFieldByName('SendEmailToFieldID'), $this->isInstanceOf('DropdownField')); $this->assertThat($fields->dataFieldByName('SendEmailToFieldID'), $this->isInstanceOf('DropdownField'));
// if the front end has checkboxs or dropdown they can select from that can also be used to send things // if the front end has checkboxs or dropdown they can select from that can also be used to send things
$dropdown = $this->objFromFixture('EditableDropdown', 'department-dropdown'); $dropdown = $this->objFromFixture('EditableDropdown', 'department-dropdown');
$form->Fields()->add($dropdown); $form->Fields()->add($dropdown);
$fields = $popup->getCMSFields(); $fields = $popup->getCMSFields();
$this->assertTrue($fields->dataFieldByName('SendEmailToFieldID') !== null); $this->assertTrue($fields->dataFieldByName('SendEmailToFieldID') !== null);
$popup->delete(); $popup->delete();
} }
function testGetEmailBodyContent() { public function testGetEmailBodyContent()
$recipient = new UserDefinedForm_EmailRecipient(); {
$recipient = new UserDefinedForm_EmailRecipient();
$emailBody = 'not html'; $emailBody = 'not html';
$emailBodyHtml = '<p>html</p>'; $emailBodyHtml = '<p>html</p>';
$recipient->EmailBody = $emailBody; $recipient->EmailBody = $emailBody;
$recipient->EmailBodyHtml = $emailBodyHtml; $recipient->EmailBodyHtml = $emailBodyHtml;
$recipient->write(); $recipient->write();
$this->assertEquals($recipient->SendPlain, 0); $this->assertEquals($recipient->SendPlain, 0);
$this->assertEquals($recipient->getEmailBodyContent(), $emailBodyHtml); $this->assertEquals($recipient->getEmailBodyContent(), $emailBodyHtml);
$recipient->SendPlain = 1; $recipient->SendPlain = 1;
$recipient->write(); $recipient->write();
$this->assertEquals($recipient->getEmailBodyContent(), $emailBody); $this->assertEquals($recipient->getEmailBodyContent(), $emailBody);
$recipient->delete(); $recipient->delete();
} }
function testGetEmailTemplateDropdownValues() { public function testGetEmailTemplateDropdownValues()
$recipient = new UserDefinedForm_EmailRecipient(); {
$recipient = new UserDefinedForm_EmailRecipient();
$defaultValues = array('SubmittedFormEmail' => 'SubmittedFormEmail'); $defaultValues = array('SubmittedFormEmail' => 'SubmittedFormEmail');
$this->assertEquals($recipient->getEmailTemplateDropdownValues(), $defaultValues); $this->assertEquals($recipient->getEmailTemplateDropdownValues(), $defaultValues);
} }
function testEmailTemplateExists() { public function testEmailTemplateExists()
$recipient = new UserDefinedForm_EmailRecipient(); {
$recipient = new UserDefinedForm_EmailRecipient();
// Set the default template // Set the default template
$recipient->EmailTemplate = current(array_keys($recipient->getEmailTemplateDropdownValues())); $recipient->EmailTemplate = current(array_keys($recipient->getEmailTemplateDropdownValues()));
$recipient->write(); $recipient->write();
// The default template exists // The default template exists
$this->assertTrue($recipient->emailTemplateExists()); $this->assertTrue($recipient->emailTemplateExists());
// A made up template doesn't exists // A made up template doesn't exists
$this->assertFalse($recipient->emailTemplateExists('MyTemplateThatsNotThere')); $this->assertFalse($recipient->emailTemplateExists('MyTemplateThatsNotThere'));
$recipient->delete(); $recipient->delete();
} }
function testCanEditAndDeleteRecipient() { public function testCanEditAndDeleteRecipient()
$form = $this->objFromFixture('UserDefinedForm', 'basic-form-page'); {
$form = $this->objFromFixture('UserDefinedForm', 'basic-form-page');
$this->logInWithPermission('ADMIN'); $this->logInWithPermission('ADMIN');
foreach($form->EmailRecipients() as $recipient) { foreach ($form->EmailRecipients() as $recipient) {
$this->assertTrue($recipient->canEdit()); $this->assertTrue($recipient->canEdit());
$this->assertTrue($recipient->canDelete()); $this->assertTrue($recipient->canDelete());
} }
$member = Member::currentUser(); $member = Member::currentUser();
$member->logOut(); $member->logOut();
$this->logInWithPermission('SITETREE_VIEW_ALL'); $this->logInWithPermission('SITETREE_VIEW_ALL');
foreach($form->EmailRecipients() as $recipient) { foreach ($form->EmailRecipients() as $recipient) {
$this->assertFalse($recipient->canEdit()); $this->assertFalse($recipient->canEdit());
$this->assertFalse($recipient->canDelete()); $this->assertFalse($recipient->canDelete());
} }
} }
function testPublishing() { public function testPublishing()
$this->logInWithPermission('ADMIN'); {
$this->logInWithPermission('ADMIN');
$form = $this->objFromFixture('UserDefinedForm', 'basic-form-page'); $form = $this->objFromFixture('UserDefinedForm', 'basic-form-page');
$form->write(); $form->write();
$form->doPublish(); $form->doPublish();
$live = Versioned::get_one_by_stage("UserDefinedForm", "Live", "\"UserDefinedForm_Live\".\"ID\" = $form->ID"); $live = Versioned::get_one_by_stage("UserDefinedForm", "Live", "\"UserDefinedForm_Live\".\"ID\" = $form->ID");
$this->assertNotNull($live); $this->assertNotNull($live);
$this->assertEquals(2, $live->Fields()->Count()); // one page and one field $this->assertEquals(2, $live->Fields()->Count()); // one page and one field
$dropdown = $this->objFromFixture('EditableDropdown', 'basic-dropdown'); $dropdown = $this->objFromFixture('EditableDropdown', 'basic-dropdown');
$form->Fields()->add($dropdown); $form->Fields()->add($dropdown);
$stage = Versioned::get_one_by_stage("UserDefinedForm", "Stage", "\"UserDefinedForm\".\"ID\" = $form->ID"); $stage = Versioned::get_one_by_stage("UserDefinedForm", "Stage", "\"UserDefinedForm\".\"ID\" = $form->ID");
$this->assertEquals(3, $stage->Fields()->Count()); $this->assertEquals(3, $stage->Fields()->Count());
// should not have published the dropdown // should not have published the dropdown
$liveDropdown = Versioned::get_one_by_stage("EditableFormField", "Live", "\"EditableFormField_Live\".\"ID\" = $dropdown->ID"); $liveDropdown = Versioned::get_one_by_stage("EditableFormField", "Live", "\"EditableFormField_Live\".\"ID\" = $dropdown->ID");
$this->assertNull($liveDropdown); $this->assertNull($liveDropdown);
// when publishing it should have added it // when publishing it should have added it
$form->doPublish(); $form->doPublish();
$live = Versioned::get_one_by_stage("UserDefinedForm", "Live", "\"UserDefinedForm_Live\".\"ID\" = $form->ID"); $live = Versioned::get_one_by_stage("UserDefinedForm", "Live", "\"UserDefinedForm_Live\".\"ID\" = $form->ID");
$this->assertEquals(3, $live->Fields()->Count()); $this->assertEquals(3, $live->Fields()->Count());
// edit the title // edit the title
$text = $form->Fields()->limit(1, 1)->First(); $text = $form->Fields()->limit(1, 1)->First();
$text->Title = 'Edited title'; $text->Title = 'Edited title';
$text->write(); $text->write();
$liveText = Versioned::get_one_by_stage("EditableFormField", "Live", "\"EditableFormField_Live\".\"ID\" = $text->ID"); $liveText = Versioned::get_one_by_stage("EditableFormField", "Live", "\"EditableFormField_Live\".\"ID\" = $text->ID");
$this->assertFalse($liveText->Title == $text->Title); $this->assertFalse($liveText->Title == $text->Title);
$form->doPublish(); $form->doPublish();
$liveText = Versioned::get_one_by_stage("EditableFormField", "Live", "\"EditableFormField_Live\".\"ID\" = $text->ID"); $liveText = Versioned::get_one_by_stage("EditableFormField", "Live", "\"EditableFormField_Live\".\"ID\" = $text->ID");
$this->assertTrue($liveText->Title == $text->Title); $this->assertTrue($liveText->Title == $text->Title);
// Add a display rule to the dropdown // Add a display rule to the dropdown
$displayRule = new EditableCustomRule(); $displayRule = new EditableCustomRule();
$displayRule->ParentID = $dropdown->ID; $displayRule->ParentID = $dropdown->ID;
$displayRule->ConditionFieldID = $text->ID; $displayRule->ConditionFieldID = $text->ID;
$displayRule->write(); $displayRule->write();
$ruleID = $displayRule->ID; $ruleID = $displayRule->ID;
// Not live // Not live
$liveRule = Versioned::get_one_by_stage("EditableCustomRule", "Live", "\"EditableCustomRule_Live\".\"ID\" = $ruleID"); $liveRule = Versioned::get_one_by_stage("EditableCustomRule", "Live", "\"EditableCustomRule_Live\".\"ID\" = $ruleID");
$this->assertEmpty($liveRule); $this->assertEmpty($liveRule);
// Publish form, it's now live // Publish form, it's now live
$form->doPublish(); $form->doPublish();
$liveRule = Versioned::get_one_by_stage("EditableCustomRule", "Live", "\"EditableCustomRule_Live\".\"ID\" = $ruleID"); $liveRule = Versioned::get_one_by_stage("EditableCustomRule", "Live", "\"EditableCustomRule_Live\".\"ID\" = $ruleID");
$this->assertNotEmpty($liveRule); $this->assertNotEmpty($liveRule);
// Remove rule // Remove rule
$displayRule->delete(); $displayRule->delete();
// Live rule still exists // Live rule still exists
$liveRule = Versioned::get_one_by_stage("EditableCustomRule", "Live", "\"EditableCustomRule_Live\".\"ID\" = $ruleID"); $liveRule = Versioned::get_one_by_stage("EditableCustomRule", "Live", "\"EditableCustomRule_Live\".\"ID\" = $ruleID");
$this->assertNotEmpty($liveRule); $this->assertNotEmpty($liveRule);
// Publish form, it should remove this rule // Publish form, it should remove this rule
$form->doPublish(); $form->doPublish();
$liveRule = Versioned::get_one_by_stage("EditableCustomRule", "Live", "\"EditableCustomRule_Live\".\"ID\" = $ruleID"); $liveRule = Versioned::get_one_by_stage("EditableCustomRule", "Live", "\"EditableCustomRule_Live\".\"ID\" = $ruleID");
$this->assertEmpty($liveRule); $this->assertEmpty($liveRule);
} }
function testUnpublishing() { public function testUnpublishing()
$this->logInWithPermission('ADMIN'); {
$form = $this->objFromFixture('UserDefinedForm', 'basic-form-page'); $this->logInWithPermission('ADMIN');
$form->write(); $form = $this->objFromFixture('UserDefinedForm', 'basic-form-page');
$this->assertEquals(0, DB::query("SELECT COUNT(*) FROM \"EditableFormField_Live\"")->value()); $form->write();
$form->doPublish(); $this->assertEquals(0, DB::query("SELECT COUNT(*) FROM \"EditableFormField_Live\"")->value());
$form->doPublish();
// assert that it exists and has a field // assert that it exists and has a field
$live = Versioned::get_one_by_stage("UserDefinedForm", "Live", "\"UserDefinedForm_Live\".\"ID\" = $form->ID"); $live = Versioned::get_one_by_stage("UserDefinedForm", "Live", "\"UserDefinedForm_Live\".\"ID\" = $form->ID");
$this->assertTrue(isset($live)); $this->assertTrue(isset($live));
$this->assertEquals(2, DB::query("SELECT COUNT(*) FROM \"EditableFormField_Live\"")->value()); $this->assertEquals(2, DB::query("SELECT COUNT(*) FROM \"EditableFormField_Live\"")->value());
// unpublish // unpublish
$form->doUnpublish(); $form->doUnpublish();
$this->assertNull(Versioned::get_one_by_stage("UserDefinedForm", "Live", "\"UserDefinedForm_Live\".\"ID\" = $form->ID")); $this->assertNull(Versioned::get_one_by_stage("UserDefinedForm", "Live", "\"UserDefinedForm_Live\".\"ID\" = $form->ID"));
$this->assertEquals(0, DB::query("SELECT COUNT(*) FROM \"EditableFormField_Live\"")->value()); $this->assertEquals(0, DB::query("SELECT COUNT(*) FROM \"EditableFormField_Live\"")->value());
} }
function testDoRevertToLive() { public function testDoRevertToLive()
$this->logInWithPermission('ADMIN'); {
$form = $this->objFromFixture('UserDefinedForm', 'basic-form-page'); $this->logInWithPermission('ADMIN');
$field = $form->Fields()->First(); $form = $this->objFromFixture('UserDefinedForm', 'basic-form-page');
$field = $form->Fields()->First();
$field->Title = 'Title';
$field->write(); $field->Title = 'Title';
$field->write();
$form->doPublish();
$form->doPublish();
$field->Title = 'Edited title';
$field->write(); $field->Title = 'Edited title';
$field->write();
// check that the published version is not updated
$live = Versioned::get_one_by_stage("EditableFormField", "Live", "\"EditableFormField_Live\".\"ID\" = $field->ID"); // check that the published version is not updated
$this->assertEquals('Title', $live->Title); $live = Versioned::get_one_by_stage("EditableFormField", "Live", "\"EditableFormField_Live\".\"ID\" = $field->ID");
$this->assertEquals('Title', $live->Title);
// revert back to the live data
$form->doRevertToLive(); // revert back to the live data
$form->flushCache(); $form->doRevertToLive();
$form->flushCache();
$check = Versioned::get_one_by_stage("EditableFormField", "Stage", "\"EditableFormField\".\"ID\" = $field->ID");
$check = Versioned::get_one_by_stage("EditableFormField", "Stage", "\"EditableFormField\".\"ID\" = $field->ID");
$this->assertEquals('Title', $check->Title);
} $this->assertEquals('Title', $check->Title);
}
function testDuplicatingForm() {
$this->logInWithPermission('ADMIN'); public function testDuplicatingForm()
$form = $this->objFromFixture('UserDefinedForm', 'basic-form-page'); {
$this->logInWithPermission('ADMIN');
$duplicate = $form->duplicate(); $form = $this->objFromFixture('UserDefinedForm', 'basic-form-page');
$this->assertEquals($form->Fields()->Count(), $duplicate->Fields()->Count()); $duplicate = $form->duplicate();
$this->assertEquals($form->EmailRecipients()->Count(), $form->EmailRecipients()->Count());
$this->assertEquals($form->Fields()->Count(), $duplicate->Fields()->Count());
// can't compare object since the dates/ids change $this->assertEquals($form->EmailRecipients()->Count(), $form->EmailRecipients()->Count());
$this->assertEquals($form->Fields()->First()->Title, $duplicate->Fields()->First()->Title);
// can't compare object since the dates/ids change
// Test duplicate with group $this->assertEquals($form->Fields()->First()->Title, $duplicate->Fields()->First()->Title);
$form2 = $this->objFromFixture('UserDefinedForm', 'page-with-group');
$form2Validator = new UserFormValidator(); // Test duplicate with group
$form2Validator->setForm(new Form(new Controller(), 'Form', new FieldList(), new FieldList())); $form2 = $this->objFromFixture('UserDefinedForm', 'page-with-group');
$this->assertTrue($form2Validator->php($form2->toMap())); $form2Validator = new UserFormValidator();
$form2Validator->setForm(new Form(new Controller(), 'Form', new FieldList(), new FieldList()));
// Check field groups exist $this->assertTrue($form2Validator->php($form2->toMap()));
$form2GroupStart = $form2->Fields()->filter('ClassName', 'EditableFieldGroup')->first();
$form2GroupEnd = $form2->Fields()->filter('ClassName', 'EditableFieldGroupEnd')->first(); // Check field groups exist
$this->assertEquals($form2GroupEnd->ID, $form2GroupStart->EndID); $form2GroupStart = $form2->Fields()->filter('ClassName', 'EditableFieldGroup')->first();
$form2GroupEnd = $form2->Fields()->filter('ClassName', 'EditableFieldGroupEnd')->first();
// Duplicate this $this->assertEquals($form2GroupEnd->ID, $form2GroupStart->EndID);
$form3 = $form2->duplicate();
$form3Validator = new UserFormValidator(); // Duplicate this
$form3Validator->setForm(new Form(new Controller(), 'Form', new FieldList(), new FieldList())); $form3 = $form2->duplicate();
$this->assertTrue($form3Validator->php($form3->toMap())); $form3Validator = new UserFormValidator();
$form3Validator->setForm(new Form(new Controller(), 'Form', new FieldList(), new FieldList()));
// Check field groups exist $this->assertTrue($form3Validator->php($form3->toMap()));
$form3GroupStart = $form3->Fields()->filter('ClassName', 'EditableFieldGroup')->first();
$form3GroupEnd = $form3->Fields()->filter('ClassName', 'EditableFieldGroupEnd')->first(); // Check field groups exist
$this->assertEquals($form3GroupEnd->ID, $form3GroupStart->EndID); $form3GroupStart = $form3->Fields()->filter('ClassName', 'EditableFieldGroup')->first();
$this->assertNotEquals($form2GroupEnd->ID, $form3GroupStart->EndID); $form3GroupEnd = $form3->Fields()->filter('ClassName', 'EditableFieldGroupEnd')->first();
} $this->assertEquals($form3GroupEnd->ID, $form3GroupStart->EndID);
$this->assertNotEquals($form2GroupEnd->ID, $form3GroupStart->EndID);
function testFormOptions() { }
$this->logInWithPermission('ADMIN');
$form = $this->objFromFixture('UserDefinedForm', 'basic-form-page'); public function testFormOptions()
{
$fields = $form->getFormOptions(); $this->logInWithPermission('ADMIN');
$submit = $fields->fieldByName('SubmitButtonText'); $form = $this->objFromFixture('UserDefinedForm', 'basic-form-page');
$reset = $fields->fieldByName('ShowClearButton');
$fields = $form->getFormOptions();
$this->assertEquals($submit->Title(), 'Text on submit button:'); $submit = $fields->fieldByName('SubmitButtonText');
$this->assertEquals($reset->Title(), 'Show Clear Form Button'); $reset = $fields->fieldByName('ShowClearButton');
}
$this->assertEquals($submit->Title(), 'Text on submit button:');
public function testEmailRecipientFilters() { $this->assertEquals($reset->Title(), 'Show Clear Form Button');
$form = $this->objFromFixture('UserDefinedForm', 'filtered-form-page'); }
// Check unfiltered recipients public function testEmailRecipientFilters()
$result0 = $form {
->EmailRecipients() $form = $this->objFromFixture('UserDefinedForm', 'filtered-form-page');
->sort('EmailAddress')
->column('EmailAddress'); // Check unfiltered recipients
$this->assertEquals( $result0 = $form
array( ->EmailRecipients()
'filtered1@example.com', ->sort('EmailAddress')
'filtered2@example.com', ->column('EmailAddress');
'unfiltered@example.com' $this->assertEquals(
), array(
$result0 'filtered1@example.com',
); 'filtered2@example.com',
'unfiltered@example.com'
// check filters based on given data ),
$result1 = $form->FilteredEmailRecipients( $result0
array( );
'your-name' => 'Value',
'address' => '', // check filters based on given data
'street' => 'Anything', $result1 = $form->FilteredEmailRecipients(
'city' => 'Matches Not Equals', array(
'colours' => array('Red') // matches 2 'your-name' => 'Value',
), null 'address' => '',
) 'street' => 'Anything',
->sort('EmailAddress') 'city' => 'Matches Not Equals',
->column('EmailAddress'); 'colours' => array('Red') // matches 2
$this->assertEquals( ), null
array( )
'filtered2@example.com', ->sort('EmailAddress')
'unfiltered@example.com' ->column('EmailAddress');
), $this->assertEquals(
$result1 array(
); 'filtered2@example.com',
'unfiltered@example.com'
// Check all positive matches ),
$result2 = $form->FilteredEmailRecipients( $result1
array( );
'your-name' => '',
'address' => 'Anything', // Check all positive matches
'street' => 'Matches Equals', $result2 = $form->FilteredEmailRecipients(
'city' => 'Anything', array(
'colours' => array('Red', 'Blue') // matches 2 'your-name' => '',
), null 'address' => 'Anything',
) 'street' => 'Matches Equals',
->sort('EmailAddress') 'city' => 'Anything',
->column('EmailAddress'); 'colours' => array('Red', 'Blue') // matches 2
$this->assertEquals( ), null
array( )
'filtered1@example.com', ->sort('EmailAddress')
'filtered2@example.com', ->column('EmailAddress');
'unfiltered@example.com' $this->assertEquals(
), array(
$result2 'filtered1@example.com',
); 'filtered2@example.com',
'unfiltered@example.com'
),
$result3 = $form->FilteredEmailRecipients( $result2
array( );
'your-name' => 'Should be blank but is not',
'address' => 'Anything',
'street' => 'Matches Equals', $result3 = $form->FilteredEmailRecipients(
'city' => 'Anything', array(
'colours' => array('Blue') 'your-name' => 'Should be blank but is not',
), null 'address' => 'Anything',
)->column('EmailAddress'); 'street' => 'Matches Equals',
$this->assertEquals( 'city' => 'Anything',
array( 'colours' => array('Blue')
'unfiltered@example.com' ), null
), )->column('EmailAddress');
$result3 $this->assertEquals(
); array(
'unfiltered@example.com'
),
$result4 = $form->FilteredEmailRecipients( $result3
array( );
'your-name' => '',
'address' => 'Anything',
'street' => 'Wrong value for this field', $result4 = $form->FilteredEmailRecipients(
'city' => '', array(
'colours' => array('Blue', 'Green') 'your-name' => '',
), null 'address' => 'Anything',
)->column('EmailAddress'); 'street' => 'Wrong value for this field',
$this->assertEquals( 'city' => '',
array( 'colours' => array('Blue', 'Green')
'unfiltered@example.com' ), null
), )->column('EmailAddress');
$result4 $this->assertEquals(
); array(
} 'unfiltered@example.com'
),
public function testIndex() { $result4
// Test that the $UserDefinedForm is stripped out );
$page = $this->objFromFixture('UserDefinedForm', 'basic-form-page'); }
$page->publish('Stage', 'Live');
public function testIndex()
$result = $this->get($page->Link()); {
$body = Convert::nl2os($result->getBody(), ''); // strip out newlines // Test that the $UserDefinedForm is stripped out
$this->assertFalse($result->isError()); $page = $this->objFromFixture('UserDefinedForm', 'basic-form-page');
$this->assertContains('<p>Here is my form</p><form', $body); $page->publish('Stage', 'Live');
$this->assertContains('</form><p>Thank you for filling it out</p>', $body);
$result = $this->get($page->Link());
$this->assertNotContains('<p>$UserDefinedForm</p>', $body); $body = Convert::nl2os($result->getBody(), ''); // strip out newlines
$this->assertNotContains('<p></p>', $body); $this->assertFalse($result->isError());
$this->assertNotContains('</p><p>Thank you for filling it out</p>', $body); $this->assertContains('<p>Here is my form</p><form', $body);
} $this->assertContains('</form><p>Thank you for filling it out</p>', $body);
}
$this->assertNotContains('<p>$UserDefinedForm</p>', $body);
$this->assertNotContains('<p></p>', $body);
$this->assertNotContains('</p><p>Thank you for filling it out</p>', $body);
}
}

View File

@ -1,18 +1,20 @@
<?php <?php
class UserFormTest extends SapphireTest { class UserFormTest extends SapphireTest
{
protected static $fixture_file = 'UserDefinedFormTest.yml'; protected static $fixture_file = 'UserDefinedFormTest.yml';
/** /**
* Tests that a form will not generate empty pages * Tests that a form will not generate empty pages
*/ */
public function testEmptyPages() { public function testEmptyPages()
$page = $this->objFromFixture('UserDefinedForm', 'empty-page'); {
$this->assertEquals(5, $page->Fields()->count()); $page = $this->objFromFixture('UserDefinedForm', 'empty-page');
$controller = ModelAsController::controller_for($page); $this->assertEquals(5, $page->Fields()->count());
$form = new UserForm($controller); $controller = ModelAsController::controller_for($page);
$this->assertEquals(2, $form->getSteps()->count()); $form = new UserForm($controller);
} $this->assertEquals(2, $form->getSteps()->count());
} }
}

View File

@ -1,216 +1,220 @@
<?php <?php
class UserFormsUpgradeServiceTest extends SapphireTest { class UserFormsUpgradeServiceTest extends SapphireTest
{
static $fixture_file = 'UserFormsUpgradeServiceTest.yml'; public static $fixture_file = 'UserFormsUpgradeServiceTest.yml';
public function setUp() { public function setUp()
Config::inst()->update('UserDefinedForm', 'upgrade_on_build', false); {
parent::setUp(); Config::inst()->update('UserDefinedForm', 'upgrade_on_build', false);
parent::setUp();
// Assign rules programatically // Assign rules programatically
$field1 = $this->objFromFixture('EditableTextField', 'text1'); $field1 = $this->objFromFixture('EditableTextField', 'text1');
$field2 = $this->objFromFixture('EditableTextField', 'text2'); $field2 = $this->objFromFixture('EditableTextField', 'text2');
$field3 = $this->objFromFixture('EditableTextField', 'text3'); $field3 = $this->objFromFixture('EditableTextField', 'text3');
$field3->CustomRules = serialize(array( $field3->CustomRules = serialize(array(
array( array(
'Display' => 'Show', 'Display' => 'Show',
'ConditionField' => $field1->Name, 'ConditionField' => $field1->Name,
'ConditionOption' => 'IsBlank', 'ConditionOption' => 'IsBlank',
'Value' => '' 'Value' => ''
), ),
array( array(
'Display' => 'Hide', 'Display' => 'Hide',
'ConditionField' => $field2->Name, 'ConditionField' => $field2->Name,
'ConditionOption' => 'HasValue', 'ConditionOption' => 'HasValue',
'Value' => 'bob' 'Value' => 'bob'
) )
)); ));
$field3->write(); $field3->write();
// Assign settings programatically // Assign settings programatically
$field4 = $this->objFromFixture('EditableTextField', 'text4'); $field4 = $this->objFromFixture('EditableTextField', 'text4');
$field4->CustomSettings = serialize(array( $field4->CustomSettings = serialize(array(
'MinLength' => 20, 'MinLength' => 20,
'MaxLength' => 100, 'MaxLength' => 100,
'Rows' => 4, 'Rows' => 4,
'ExtraClass' => 'special class', 'ExtraClass' => 'special class',
'RightTitle' => 'My Field', 'RightTitle' => 'My Field',
'ShowOnLoad' => '', 'ShowOnLoad' => '',
'Default' => 'Enter your text here' 'Default' => 'Enter your text here'
)); ));
$field4->write(); $field4->write();
$numeric1 = $this->objFromFixture('EditableNumericField', 'numeric1'); $numeric1 = $this->objFromFixture('EditableNumericField', 'numeric1');
$numeric1->CustomSettings = serialize(array( $numeric1->CustomSettings = serialize(array(
'RightTitle' => 'Number of %', 'RightTitle' => 'Number of %',
'Default' => 1, 'Default' => 1,
'MinValue' => 1, 'MinValue' => 1,
'MaxValue' => 100, 'MaxValue' => 100,
'ShowOnLoad' => 'Show' 'ShowOnLoad' => 'Show'
)); ));
$numeric1->write(); $numeric1->write();
$group1 = $this->objFromFixture('Group', 'group1'); $group1 = $this->objFromFixture('Group', 'group1');
$members1 = $this->objFromFixture('EditableMemberListField', 'members1'); $members1 = $this->objFromFixture('EditableMemberListField', 'members1');
$members1->CustomSettings = serialize(array( $members1->CustomSettings = serialize(array(
'RightTitle' => 'Select group', 'RightTitle' => 'Select group',
'GroupID' => $group1->ID, 'GroupID' => $group1->ID,
'ShowOnLoad' => 'Hide' 'ShowOnLoad' => 'Hide'
)); ));
$members1->write(); $members1->write();
$literal1 = $this->objFromFixture('EditableLiteralField', 'literal1'); $literal1 = $this->objFromFixture('EditableLiteralField', 'literal1');
$literal1->CustomSettings = serialize(array( $literal1->CustomSettings = serialize(array(
'HideFromReports' => 1, 'HideFromReports' => 1,
'RightTitle' => 'Literal', 'RightTitle' => 'Literal',
'Content' => '<p>Content</p>', 'Content' => '<p>Content</p>',
'ShowOnLoad' => true 'ShowOnLoad' => true
)); ));
$literal1->write(); $literal1->write();
$heading1 = $this->objFromFixture('EditableFormHeading', 'heading1'); $heading1 = $this->objFromFixture('EditableFormHeading', 'heading1');
$heading1->CustomSettings = serialize(array( $heading1->CustomSettings = serialize(array(
'RightTitle' => 'Right', 'RightTitle' => 'Right',
'Level' => 3, 'Level' => 3,
'HideFromReports' => true, 'HideFromReports' => true,
'ShowOnLoad' => false 'ShowOnLoad' => false
)); ));
$heading1->write(); $heading1->write();
$folder = $this->objFromFixture('Folder', 'folder1'); $folder = $this->objFromFixture('Folder', 'folder1');
$file1 = $this->objFromFixture('EditableFileField', 'file1'); $file1 = $this->objFromFixture('EditableFileField', 'file1');
$file1->CustomSettings = serialize(array( $file1->CustomSettings = serialize(array(
'RightTitle' => 'File field', 'RightTitle' => 'File field',
'Folder' => $folder->ID 'Folder' => $folder->ID
)); ));
$file1->write(); $file1->write();
$date1 = $this->objFromFixture('EditableDateField', 'date1'); $date1 = $this->objFromFixture('EditableDateField', 'date1');
$date1->CustomSettings = serialize(array( $date1->CustomSettings = serialize(array(
'RightTitle' => 'Date field', 'RightTitle' => 'Date field',
'DefaultToToday' => '1' 'DefaultToToday' => '1'
)); ));
$date1->write(); $date1->write();
$checkbox1 = $this->objFromFixture('EditableCheckbox', 'checkbox1'); $checkbox1 = $this->objFromFixture('EditableCheckbox', 'checkbox1');
$checkbox1->CustomSettings = serialize(array( $checkbox1->CustomSettings = serialize(array(
'Default' => true, 'Default' => true,
'RightTitle' => 'Check this' 'RightTitle' => 'Check this'
)); ));
$checkbox1->write(); $checkbox1->write();
}
} /**
* @return UserFormsUpgradeService;
*/
protected function getService()
{
return singleton('UserFormsUpgradeService');
}
/** /**
* @return UserFormsUpgradeService; * Tests migration of custom rules
*/ */
protected function getService() { public function testCustomRulesMigration()
return singleton('UserFormsUpgradeService'); {
} $service = $this->getService();
$service->setQuiet(true);
$service->run();
/** $field1 = $this->objFromFixture('EditableTextField', 'text1');
* Tests migration of custom rules $field2 = $this->objFromFixture('EditableTextField', 'text2');
*/ $field3 = $this->objFromFixture('EditableTextField', 'text3');
public function testCustomRulesMigration() {
$service = $this->getService();
$service->setQuiet(true);
$service->run();
$field1 = $this->objFromFixture('EditableTextField', 'text1'); $this->assertDOSEquals(array(
$field2 = $this->objFromFixture('EditableTextField', 'text2'); array(
$field3 = $this->objFromFixture('EditableTextField', 'text3'); 'Display' => 'Show',
'ConditionFieldID' => $field1->ID,
'ConditionOption' => 'IsBlank'
),
array(
'Display' => 'Hide',
'ConditionFieldID' => $field2->ID,
'ConditionOption' => 'HasValue',
'FieldValue' => 'bob'
)
), $field3->DisplayRules());
}
$this->assertDOSEquals(array( /**
array( * Tests migration of all custom settings
'Display' => 'Show', */
'ConditionFieldID' => $field1->ID, public function testCustomSettingsMigration()
'ConditionOption' => 'IsBlank' {
), $service = $this->getService();
array( $service->setQuiet(true);
'Display' => 'Hide', $service->run();
'ConditionFieldID' => $field2->ID,
'ConditionOption' => 'HasValue',
'FieldValue' => 'bob'
)
), $field3->DisplayRules());
}
/** $group1 = $this->objFromFixture('Group', 'group1');
* Tests migration of all custom settings $form = $this->objFromFixture('UserDefinedForm', 'form-with-settings');
*/ $folder = $this->objFromFixture('Folder', 'folder1');
public function testCustomSettingsMigration() {
$service = $this->getService();
$service->setQuiet(true);
$service->run();
$group1 = $this->objFromFixture('Group', 'group1'); $this->assertDOSEquals(array(
$form = $this->objFromFixture('UserDefinedForm', 'form-with-settings'); array(
$folder = $this->objFromFixture('Folder', 'folder1'); 'ClassName' => 'EditableTextField',
'Title' => 'Text with rule',
$this->assertDOSEquals(array( 'MinLength' => 20,
array( 'MaxLength' => 100,
'ClassName' => 'EditableTextField', 'Rows' => 4,
'Title' => 'Text with rule', 'ExtraClass' => 'special class',
'MinLength' => 20, 'RightTitle' => 'My Field',
'MaxLength' => 100, 'ShowOnLoad' => true,
'Rows' => 4, 'Default' => 'Enter your text here',
'ExtraClass' => 'special class', ),
'RightTitle' => 'My Field', array(
'ShowOnLoad' => true, 'ClassName' => 'EditableNumericField',
'Default' => 'Enter your text here', 'Title' => 'Numeric 1',
), 'RightTitle' => 'Number of %',
array( 'Default' => 1,
'ClassName' => 'EditableNumericField', 'MinValue' => 1,
'Title' => 'Numeric 1', 'MaxValue' => 100,
'RightTitle' => 'Number of %', 'ShowOnLoad' => true,
'Default' => 1, ),
'MinValue' => 1, array(
'MaxValue' => 100, 'ClassName' => 'EditableMemberListField',
'ShowOnLoad' => true, 'Title' => 'Members 1',
), 'RightTitle' => 'Select group',
array( 'GroupID' => $group1->ID,
'ClassName' => 'EditableMemberListField', 'ShowOnLoad' => false,
'Title' => 'Members 1', ),
'RightTitle' => 'Select group', array(
'GroupID' => $group1->ID, 'ClassName' => 'EditableLiteralField',
'ShowOnLoad' => false, 'Title' => 'Literal 1',
), 'HideFromReports' => true,
array( 'RightTitle' => 'Literal',
'ClassName' => 'EditableLiteralField', 'Content' => '<p>Content</p>',
'Title' => 'Literal 1', 'ShowOnLoad' => true,
'HideFromReports' => true, ),
'RightTitle' => 'Literal', array(
'Content' => '<p>Content</p>', 'ClassName' => 'EditableFormHeading',
'ShowOnLoad' => true, 'Title' => 'Heading 1',
), 'RightTitle' => 'Right',
array( 'Level' => 3,
'ClassName' => 'EditableFormHeading', 'HideFromReports' => true,
'Title' => 'Heading 1', 'ShowOnLoad' => false,
'RightTitle' => 'Right', ),
'Level' => 3, array(
'HideFromReports' => true, 'ClassName' => 'EditableFileField',
'ShowOnLoad' => false, 'Title' => 'File 1',
), 'RightTitle' => 'File field',
array( 'FolderID' => $folder->ID,
'ClassName' => 'EditableFileField', ),
'Title' => 'File 1', array(
'RightTitle' => 'File field', 'ClassName' => 'EditableDateField',
'FolderID' => $folder->ID, 'Title' => 'Date 1',
), 'RightTitle' => 'Date field',
array( 'DefaultToToday' => true,
'ClassName' => 'EditableDateField', ),
'Title' => 'Date 1', array(
'RightTitle' => 'Date field', 'ClassName' => 'EditableCheckbox',
'DefaultToToday' => true, 'Title' => 'Checkbox 1',
), 'CheckedDefault' => true,
array( 'RightTitle' => 'Check this',
'ClassName' => 'EditableCheckbox', ),
'Title' => 'Checkbox 1', ), $form->Fields());
'CheckedDefault' => true, }
'RightTitle' => 'Check this',
),
), $form->Fields());
}
} }