API CHANGE FormField::Field() and FormField::FieldHolder() now render into templates on each FormField instead of creating HTML from PHP

This commit is contained in:
Sean Harvey 2011-03-23 17:12:25 +13:00 committed by Ingo Schommer
parent b3c08dba12
commit 9e548f501e
40 changed files with 346 additions and 571 deletions

View File

@ -5,8 +5,6 @@
* @subpackage fields-basic
*/
class CheckboxField extends FormField {
protected $disabled;
function setValue($value) {
$this->value = ($value) ? 1 : 0;
@ -15,54 +13,18 @@ class CheckboxField extends FormField {
function dataValue() {
return ($this->value) ? 1 : NULL;
}
function Value() {
return ($this->value) ? 1 : 0;
}
function Field() {
$attributes = array(
'type' => 'checkbox',
'class' => ($this->extraClass() ? $this->extraClass() : ''),
'id' => $this->id(),
'name' => $this->getName(),
'value' => 1,
'checked' => $this->value ? 'checked' : '',
'tabindex' => $this->getTabIndex()
);
if($this->disabled) $attributes['disabled'] = 'disabled';
return $this->createTag('input', $attributes);
function Field($properties = array()) {
return $this->customise($properties)->renderWith('CheckboxField');
}
/**
* Checkboxes use the RightLabelledFieldHolder template, to put the field on the left
* and the label on the right. See {@link FormField::FieldHolder} for more information about
* how FieldHolder works.
*/
function FieldHolder() {
if($this->labelLeft) {
return parent::FieldHolder();
} else {
extract($this->getXMLValues(array( 'Name', 'Field', 'Title', 'Message', 'MessageType' )),
EXTR_SKIP);
$messageBlock = isset($Message) ? "<span class=\"message $MessageType\">$Message</span>" : '';
$Type = $this->XML_val('Type');
$extraClass = $this->XML_val('extraClass');
return <<<HTML
<div id="$Name" class="field $Type $extraClass">
$Field
<label class="right" for="{$this->id()}">$Title</label>
$messageBlock
</div>
HTML;
}
}
function useLabelLeft( $labelLeft = true ) {
$this->labelLeft = $labelLeft;
$this->setFieldHolderTemplate(($this->fieldHolderTemplate) ? $this->fieldHolderTemplate : 'CheckboxFieldHolder');
return parent::FieldHolder();
}
/**
@ -79,7 +41,6 @@ HTML;
/**
* Returns a readonly version of this field
*/
function performReadonlyTransformation() {
$field = new CheckboxField_Readonly($this->name, $this->title, $this->value ? _t('CheckboxField.YES', 'Yes') : _t('CheckboxField.NO', 'No'));
$field->setForm($this->form);
@ -91,6 +52,7 @@ HTML;
$clone->setDisabled(true);
return $clone;
}
}
/**
@ -99,6 +61,7 @@ HTML;
* @subpackage fields-basic
*/
class CheckboxField_Readonly extends ReadonlyField {
function performReadonlyTransformation() {
return clone $this;
}
@ -106,32 +69,5 @@ class CheckboxField_Readonly extends ReadonlyField {
function setValue($val) {
$this->value = (int)($val) ? _t('CheckboxField.YES', 'Yes') : _t('CheckboxField.NO', 'No');
}
}
/**
* Single checkbox field, disabled
* @package forms
* @subpackage fields-basic
*/
class CheckboxField_Disabled extends CheckboxField {
protected $disabled = true;
/**
* Returns a single checkbox field - used by templates.
*/
function Field() {
$attributes = array(
'type' => 'checkbox',
'class' => ($this->extraClass() ? $this->extraClass() : ''),
'id' => $this->id(),
'name' => $this->getName(),
'tabindex' => $this->getTabIndex(),
'checked' => ($this->value) ? 'checked' : false,
'disabled' => 'disabled'
);
return $this->createTag('input', $attributes);
}
}
?>
}

View File

@ -29,9 +29,6 @@
* array. Is it also appropriate to accept so many different
* types of data when just using an array would be appropriate?
*
* @todo Make use of FormField->createTag() to generate the
* HTML tag(s) for this field.
*
* @package forms
* @subpackage fields-basic
*/
@ -47,14 +44,13 @@ class CheckboxSetField extends OptionsetField {
/**
* @todo Explain different source data that can be used with this field,
* e.g. SQLMap, ArrayList or an array.
*
* @todo Should use CheckboxField FieldHolder rather than constructing own markup.
*/
function Field() {
function Field($properties = array()) {
Requirements::css(SAPPHIRE_DIR . '/css/CheckboxSetField.css');
$source = $this->source;
$values = $this->value;
$items = array();
// Get values from the join, if available
if(is_object($this->form)) {
@ -109,30 +105,35 @@ class CheckboxSetField extends OptionsetField {
$options = "<li>No options available</li>";
}
if($source) foreach($source as $index => $item) {
if($item instanceof DataObject) {
$key = $item->ID;
$value = $item->Title;
} else {
$key = $index;
$value = $item;
}
$odd = ($odd + 1) % 2;
$extraClass = $odd ? 'odd' : 'even';
$extraClass .= ' val' . str_replace(' ', '', $key);
$itemID = $this->id() . '_' . ereg_replace('[^a-zA-Z0-9]+', '', $key);
$checked = '';
if(isset($items)) {
$checked = (in_array($key, $items) || in_array($key, $this->defaultItems)) ? ' checked="checked"' : '';
}
if($source) {
foreach($source as $value => $item) {
if($item instanceof DataObject) {
$value = $item->ID;
$title = $item->Title;
} else {
$title = $item;
}
$disabled = ($this->disabled || in_array($key, $this->disabledItems)) ? $disabled = ' disabled="disabled"' : '';
$options .= "<li class=\"$extraClass\"><input id=\"$itemID\" name=\"$this->name[$key]\" type=\"checkbox\" value=\"$key\"$checked $disabled class=\"checkbox\" /> <label for=\"$itemID\">$value</label></li>\n";
$itemID = $this->ID() . '_' . preg_replace('/[^a-zA-Z0-9]/', '', $value);
$odd = ($odd + 1) % 2;
$extraClass = $odd ? 'odd' : 'even';
$extraClass .= ' val' . preg_replace('/[^a-zA-Z0-9\-\_]/', '_', $value);
$options[] = new ArrayData(array(
'ID' => $itemID,
'Class' => $extraClass,
'Name' => $this->name,
'Value' => $value,
'Title' => $title,
'isChecked' => in_array($value, $items) || in_array($value, $this->defaultItems),
'isDisabled' => $this->disabled || in_array($value, $this->disabledItems)
));
}
}
return "<ul id=\"{$this->id()}\" class=\"optionset checkboxsetfield{$this->extraClass()}\">\n$options</ul>\n";
$properties = array_merge($properties, array('Options' => new ArrayList($options)));
return $this->customise($properties)->renderWith('CheckboxSetField');
}
function setDisabled($val) {
@ -287,5 +288,4 @@ class CheckboxSetField extends OptionsetField {
return FormField::ExtraOptions();
}
}
?>
}

View File

@ -57,5 +57,5 @@ class DatalessField extends FormField {
function getAllowHTML() {
return $this->allowHTML;
}
}
?>
}

View File

@ -130,67 +130,45 @@ class DropdownField extends FormField {
parent::__construct($name, ($title===null) ? $name : $title, $value, $form);
}
/**
* Returns a <select> tag containing all the appropriate <option> tags.
* Makes use of {@link FormField->createTag()} to generate the <select>
* tag and option elements inside is as the content of the <select>.
*
* @return string HTML tag for this dropdown field
*/
function Field() {
$options = '';
function Field($properties = array()) {
$source = $this->getSource();
$options = array();
if($source) {
// For SQLMap sources, the empty string needs to be added specially
// SQLMap needs this to add an empty value to the options
if(is_object($source) && $this->emptyString) {
$options .= $this->createTag('option', array('value' => ''), $this->emptyString);
$options[] = new ArrayData(array(
'Value' => $this->emptyString,
'Title' => '',
));
}
foreach($source as $value => $title) {
// Blank value of field and source (e.g. "" => "(Any)")
$selected = false;
if($value === '' && ($this->value === '' || $this->value === null)) {
$selected = 'selected';
$selected = true;
} else {
// Normal value from the source
if($value) {
$selected = ($value == $this->value) ? 'selected' : null;
} else {
// Do a type check comparison, we might have an array key of 0
$selected = ($value === $this->value) ? 'selected' : null;
}
$this->isSelected = ($selected) ? true : false;
// check against value, fallback to a type check comparison when !value
$selected = ($value) ? $value == $this->value : $value === $this->value;
$this->isSelected = $selected;
}
$options .= $this->createTag(
'option',
array(
'selected' => $selected,
'value' => $value
),
Convert::raw2xml($title)
);
$options[] = new ArrayData(array(
'Title' => $title,
'Value' => $value,
'Selected' => $selected,
));
}
}
$attributes = array(
'class' => ($this->extraClass() ? $this->extraClass() : ''),
'id' => $this->id(),
'name' => $this->name,
'tabindex' => $this->getTabIndex()
);
if($this->disabled) $attributes['disabled'] = 'disabled';
return $this->createTag('select', $attributes, $options);
$properties = array_merge($properties, array('Options' => new ArrayList($options)));
return $this->customise($properties)->renderWith('DropdownField');
}
/**
* @return boolean
*/
function isSelected(){
function isSelected() {
return $this->isSelected;
}
@ -227,7 +205,7 @@ class DropdownField extends FormField {
function getHasEmptyDefault() {
return $this->hasEmptyDefault;
}
/**
* Set the default selection label, e.g. "select...".
* Defaults to an empty string. Automatically sets
@ -239,7 +217,7 @@ class DropdownField extends FormField {
$this->setHasEmptyDefault(true);
$this->emptyString = $str;
}
/**
* @return string
*/
@ -254,13 +232,13 @@ class DropdownField extends FormField {
$field->setReadonly(true);
return $field;
}
function extraClass(){
function extraClass() {
$ret = parent::extraClass();
if($this->extraClass) $ret .= " $this->extraClass";
return $ret;
}
/**
* Set form being disabled
*/

View File

@ -39,7 +39,19 @@ if(typeof fromAnOnBlur != 'undefined'){
}
JS;
}
public function Field($properties = array()) {
return $this->customise($properties)->renderWith('TextField');
}
/**
* Returns the field type - used by templates.
* @return string
*/
function Type() {
return 'text';
}
/**
* Validates for RFC 2822 compliant email adresses.
*
@ -69,5 +81,5 @@ JS;
return true;
}
}
}
?>

View File

@ -110,27 +110,11 @@ class FileField extends FormField {
parent::__construct($name, $title, $value, $form, $rightTitle);
}
public function Field() {
return $this->createTag(
'input',
array(
"type" => "file",
"name" => $this->name,
"id" => $this->id(),
"tabindex" => $this->getTabIndex()
)
) .
$this->createTag(
'input',
array(
"type" => "hidden",
"name" => "MAX_FILE_SIZE",
"value" => $this->getValidator()->getAllowedMaxFileSize(),
"tabindex" => $this->getTabIndex()
)
);
public function Field($properties = array()) {
$properties = array_merge($properties, array('MaxFileSize' => $this->getValidator()->getAllowedMaxFileSize()));
return $this->customise($properties)->renderWith('FileField');
}
public function saveInto(DataObject $record) {
if(!isset($_FILES[$this->name])) return false;
$fileClass = File::get_class_for_file_extension(pathinfo($_FILES[$this->name]['name'], PATHINFO_EXTENSION));

View File

@ -89,13 +89,11 @@ class FileIFrameField extends FileField {
'value' => $this->attrValue()
)
);
} else {
return sprintf(_t (
'FileIFrameField.ATTACHONCESAVED', '%ss can be attached once you have saved the record for the first time.'
), $this->FileTypeName());
}
$this->setValue(sprintf(_t (
'FileIFrameField.ATTACHONCESAVED', '%ss can be attached once you have saved the record for the first time.'
), $this->FileTypeName()));
return FormField::field();
}
/**
@ -119,7 +117,7 @@ class FileIFrameField extends FileField {
Requirements::css('sapphire/css/FileIFrameField.css');
return $this->renderWith($this->template);
return $this->renderWith('FileIframeField_iframe');
}
/**

View File

@ -15,10 +15,6 @@
* )
* </code>
*
* <b>Labels</b>
*
* By default, FormAction will use the title as the label for the left margin. This can look redundant on the form. If you'd rather have just the button alone with as pictured above try using {@link FormAction_WithoutLabel} instead.
*
* @package forms
* @subpackage actions
*/
@ -53,22 +49,19 @@ class FormAction extends FormField {
* @param form The parent form, auto-set when the field is placed inside a form
* @param extraData A piece of extra data that can be extracted with $this->extraData. Useful for
* calling $form->buttonClicked()->extraData()
* @param extraClass A CSS class to apply to the button in addition to 'action'
*/
function __construct($action, $title = "", $form = null, $extraData = null, $extraClass = '') {
function __construct($action, $title = "", $form = null, $extraData = null) {
$this->extraData = $extraData;
$this->addExtraClass($extraClass);
$this->action = "action_$action";
parent::__construct($this->action, $title, null, $form);
}
static function create($action, $title = "", $extraData = null, $extraClass = null) {
return new FormAction($action, $title, null, $extraData, $extraClass);
static function create($action, $title = "", $extraData = null) {
return new FormAction($action, $title, null, $extraData);
}
function actionName() {
return substr($this->name,7);
return substr($this->name, 7);
}
/**
@ -82,47 +75,23 @@ class FormAction extends FormField {
function extraData() {
return $this->extraData;
}
/**
* Create a submit input, or button tag
* using {@link FormField->createTag()} functionality.
*
* @return HTML code for the input OR button element
*/
function Field() {
if($this->useButtonTag) {
$attributes = array(
'class' => 'action' . ($this->extraClass() ? $this->extraClass() : ''),
'id' => $this->id(),
'type' => 'submit',
'name' => $this->action,
'tabindex' => $this->getTabIndex()
);
if($this->isReadonly()) {
$attributes['disabled'] = 'disabled';
$attributes['class'] = $attributes['class'] . ' disabled';
}
return $this->createTag('button', $attributes, $this->buttonContent ? $this->buttonContent : $this->Title());
} else {
$attributes = array(
'class' => 'action' . ($this->extraClass() ? $this->extraClass() : ''),
'id' => $this->id(),
'type' => 'submit',
'name' => $this->action,
'value' => $this->Title(),
'tabindex' => $this->getTabIndex()
);
if($this->isReadonly()) {
$attributes['disabled'] = 'disabled';
$attributes['class'] = $attributes['class'] . ' disabled';
}
$attributes['title'] = ($this->description) ? $this->description : $this->Title();
return $this->createTag('input', $attributes);
}
function Field($properties = array()) {
$properties = array_merge(
$properties,
array(
'Name' => $this->action,
'Title' => ($this->description) ? $this->description : $this->Title(),
'UseButtonTag' => $this->useButtonTag
)
);
return $this->customise($properties)->renderWith('FormAction');
}
public function Type() {
return ($this->useButtonTag) ? 'button' : 'submit';
}
/**
* Does not transform to readonly by purpose.
* Globally disabled buttons would break the CMS.
@ -132,19 +101,5 @@ class FormAction extends FormField {
$clone->setReadonly(true);
return $clone;
}
function readonlyField() {
return $this;
}
}
/**
* @package forms
* @subpackage actions
*/
class FormAction_WithoutLabel extends FormAction {
function Title(){
return null;
}
}
?>
}

View File

@ -18,7 +18,12 @@
* @subpackage core
*/
class FormField extends RequestHandler {
/**
* @var Form
*/
protected $form;
protected $name, $title, $value ,$message, $messageType, $extraClass;
/**
@ -77,7 +82,13 @@ class FormField extends RequestHandler {
* @var Custom Validation Message for the Field
*/
protected $customValidationMessage = "";
/**
* Template name to render this FormField field holder into.
* @var string
*/
protected $fieldHolderTemplate;
/**
* Create a new field.
* @param name The internal field name, passed to forms.
@ -108,7 +119,7 @@ class FormField extends RequestHandler {
* The ID is generated as FormName_FieldName. All Field functions should ensure
* that this ID is included in the field.
*/
function id() {
function ID() {
$name = ereg_replace('(^-)|(-$)','',ereg_replace('[^A-Za-z0-9_-]+','-',$this->name));
if($this->form) return $this->form->FormName() . '_' . $name;
else return $name;
@ -130,10 +141,6 @@ class FormField extends RequestHandler {
Deprecation::notice('3.0', 'Use getName() instead.');
return $this->getName();
}
function attrName() {
return $this->name;
}
/**
* Returns the field message, used by form validation.
@ -191,11 +198,11 @@ class FormField extends RequestHandler {
function setTitle($val) {
$this->title = $val;
}
function RightTitle() {
return $this->rightTitle;
}
function setRightTitle($val) {
$this->rightTitle = $val;
}
@ -203,11 +210,11 @@ class FormField extends RequestHandler {
function LeftTitle() {
return $this->leftTitle;
}
function setLeftTitle($val) {
function setLeftTitle($val) {
$this->leftTitle = $val;
}
/**
* Set tabindex HTML attribute
* (defaults to none).
@ -217,10 +224,9 @@ class FormField extends RequestHandler {
public function setTabIndex($index) {
$this->tabIndex = $index;
}
/**
* Get tabindex (if previously set)
*
* @return int
*/
public function getTabIndex() {
@ -327,7 +333,15 @@ class FormField extends RequestHandler {
function getForm() {
return $this->form;
}
public function getFieldHolderTemplate() {
return $this->fieldHolderTemplate;
}
public function setFieldHolderTemplate($template) {
$this->fieldHolderTemplate = $template;
}
/**
* Return TRUE if security token protection is enabled on the parent {@link Form}.
*
@ -343,7 +357,7 @@ class FormField extends RequestHandler {
* Sets the error message to be displayed on the form field
* Set by php validation of the form
*/
function setError($message,$messageType){
function setError($message, $messageType) {
$this->message = $message;
$this->messageType = $messageType;
}
@ -370,7 +384,7 @@ class FormField extends RequestHandler {
public function getCustomValidationMessage() {
return $this->customValidationMessage;
}
/**
* Set name of template (without path or extension)
*
@ -394,67 +408,33 @@ class FormField extends RequestHandler {
* representation of the field on the form, whereas Field will give you the core editing widget,
* such as an input tag.
*
* Our base FormField class just returns a span containing the value. This should be overridden!
* @param array $properties key value pairs of template variables
* @return string
*/
function Field() {
if($this->value) $value = $this->dontEscape ? $this->value : Convert::raw2xml($this->value);
else $value = '<i>(' . _t('FormField.NONE', 'none') . ')</i>';
$attributes = array(
'id' => $this->id(),
'class' => 'readonly' . ($this->extraClass() ? $this->extraClass() : '')
);
$hiddenAttributes = array(
'type' => 'hidden',
'name' => $this->name,
'value' => $this->value,
'tabindex' => $this->getTabIndex()
);
$containerSpan = $this->createTag('span', $attributes, $value);
$hiddenInput = $this->createTag('input', $hiddenAttributes);
return $containerSpan . "\n" . $hiddenInput;
}
/**
* Returns a "Field Holder" for this field - used by templates.
* Forms are constructed from by concatenating a number of these field holders. The default
* field holder is a label and form field inside a paragraph tag.
*
* Composite fields can override FieldHolder to create whatever visual effects you like. It's
* a good idea to put the actual HTML for field holders into templates. The default field holder
* is the DefaultFieldHolder template. This lets you override the HTML for specific sites, if it's
* necessary.
*
* @todo Add "validationError" if needed.
*/
function FieldHolder() {
$Title = $this->XML_val('Title');
$Message = $this->XML_val('Message');
$MessageType = $this->XML_val('MessageType');
$RightTitle = $this->XML_val('RightTitle');
$Type = $this->XML_val('Type');
$extraClass = $this->XML_val('extraClass');
$Name = $this->XML_val('Name');
$Field = $this->XML_val('Field');
// Only of the the following titles should apply
$titleBlock = (!empty($Title)) ? "<label class=\"left\" for=\"{$this->id()}\">$Title</label>" : "";
$rightTitleBlock = (!empty($RightTitle)) ? "<label class=\"right\" for=\"{$this->id()}\">$RightTitle</label>" : "";
// $MessageType is also used in {@link extraClass()} with a "holder-" prefix
$messageBlock = (!empty($Message)) ? "<span class=\"message $MessageType\">$Message</span>" : "";
return <<<HTML
<div id="$Name" class="field $Type $extraClass">$titleBlock<div class="middleColumn">$Field</div>$rightTitleBlock$messageBlock</div>
HTML;
function Field($properties = array()) {
return $this->customise($properties)->renderWith('FormField');
}
/**
* Returns a restricted field holder used within things like FieldGroups.
* Returns a "field holder" for this field - used by templates.
*
* Forms are constructed by concatenating a number of these field holders.
* The default field holder is a label and a form field inside a div.
* @see FieldHolder.ss
*
* @param array $properties key value pairs of template variables
* @return string
*/
function SmallFieldHolder() {
function FieldHolder($properties = array()) {
return $this->customise($properties)->renderWith(
($this->fieldHolderTemplate) ? $this->fieldHolderTemplate : 'FieldHolder'
);
}
/**
* Returns a restricted field holder used within things like FieldGroups.
*/
function SmallFieldHolder() {
$result = '';
// set label
if($title = $this->RightTitle()){
@ -464,19 +444,20 @@ HTML;
} elseif($title = $this->Title()) {
$result .= "<label for=\"" . $this->id() . "\">{$title}</label>\n";
}
$result .= $this->Field();
return $result;
}
$result .= $this->Field();
return $result;
}
/**
* Returns true if this field is a composite field.
* To create composite field types, you should subclass {@link CompositeField}.
*/
function isComposite() { return false; }
function isComposite() {
return false;
}
/**
* Returns true if this field has its own data.
* Some fields, such as titles and composite fields, don't actually have any data. It doesn't
@ -485,7 +466,9 @@ HTML;
*
* @see FieldList::collateDataFields()
*/
function hasData() { return true; }
function hasData() {
return true;
}
/**
* @return boolean
@ -559,13 +542,18 @@ HTML;
* Returns the field type - used by templates.
* The field type is the class name with the word Field dropped off the end, all lowercase.
* It's handy for assigning HTML classes.
* @return string
*/
function Type() {return strtolower(ereg_replace('Field$','',$this->class)); }
function Type() {
return strtolower(ereg_replace('Field$', '', $this->class));
}
/**
* Construct and return HTML tag.
*
* @return string HTML tag
* @deprecated 3.0 Please define your own FormField template using {@link setFieldTemplate()}
* and/or {@link renderFieldTemplate()}
*
* @todo Transform to static helper method.
*/
function createTag($tag, $attributes, $content = null) {
@ -670,5 +658,4 @@ HTML;
else user_error("rootFieldSet() called on $this->class object without a containerFieldSet", E_USER_ERROR);
}
}
?>
}

View File

@ -29,17 +29,13 @@ class HeaderField extends DatalessField {
parent::__construct($name, $title, null, $form);
}
function Field() {
$attributes = array(
'class' => $this->extraClass(),
'id' => $this->id()
);
return $this->createTag(
"h{$this->headingLevel}",
$attributes,
($this->getAllowHTML() ? $this->title : Convert::raw2xml($this->title))
);
public function getHeadingLevel() {
return $this->headingLevel;
}
}
?>
function Field($properties = array()) {
return $this->customise($properties)->renderWith('HeaderField');
}
}

View File

@ -5,23 +5,21 @@
* @subpackage fields-dataless
*/
class HiddenField extends FormField {
/**
* Returns an hidden input field, class="hidden" and type="hidden"
*/
function Field() {
$extraClass = $this->extraClass();
//if($this->name=="ShowChooseOwn")Debug::show($this->value);
return "<input class=\"hidden$extraClass\" type=\"hidden\" id=\"" . $this->id() . "\" name=\"{$this->name}\" value=\"" . $this->attrValue() . "\" />";
function Field($properties = array()) {
return $this->customise($properties)->renderWith('HiddenField');
}
function FieldHolder() {
return $this->Field();
}
function performReadonlyTransformation() {
$clone = clone $this;
$clone->setReadonly(true);
return $clone;
}
function IsHidden() {
return true;
}

View File

@ -26,20 +26,12 @@ class LabelField extends DatalessField {
parent::__construct($name, $title, $form);
}
/**
* Returns a label containing the title, and an HTML class if given.
*/
function Field() {
$attributes = array(
'class' => $this->extraClass(),
'id' => $this->id()
);
return $this->createTag(
'label',
$attributes,
($this->getAllowHTML() ? $this->title : Convert::raw2xml($this->title))
);
function Field($properties = array()) {
return $this->customise($properties)->renderWith('LabelField');
}
}
?>
}

View File

@ -30,11 +30,11 @@ class LiteralField extends DatalessField {
function FieldHolder() {
return is_object($this->content) ? $this->content->forTemplate() : $this->content;
}
function Field() {
return $this->FieldHolder();
}
/**
* Sets the content of this field to a new value
*
@ -63,4 +63,5 @@ class LiteralField extends DatalessField {
$clone->setReadonly(true);
return $clone;
}
}

View File

@ -82,6 +82,7 @@ class NullableField extends FormField {
$nullableCheckbox = new CheckboxField($this->getIsNullId());
}
$nullableCheckbox->setValue(is_null($this->dataValue()));
return $this->valueField->Field() . ' ' . $nullableCheckbox->Field() . '&nbsp;<span>' . $this->getIsNullLabel().'</span>';
}

View File

@ -60,59 +60,39 @@ class OptionsetField extends DropdownField {
* @var Array
*/
protected $disabledItems = array();
/**
* Creates a new optionset field.
* @param name The field name
* @param title The field title
* @param source An map of the dropdown items
* @param value The current value
* @param form The parent form
*/
function __construct($name, $title = "", $source = array(), $value = "", $form = null) {
parent::__construct($name, $title, $source, $value, $form);
function Field($properties = array()) {
$source = $this->getSource();
$odd = 0;
$options = array();
if($source) {
foreach($source as $value => $title) {
$itemID = $this->ID() . '_' . preg_replace('/[^a-zA-Z0-9]/', '', $value);
$odd = ($odd + 1) % 2;
$extraClass = $odd ? 'odd' : 'even';
$extraClass .= ' val' . preg_replace('/[^a-zA-Z0-9\-\_]/', '_', $value);
$options[] = new ArrayData(array(
'ID' => $itemID,
'Class' => $extraClass,
'Name' => $this->name,
'Value' => $value,
'Title' => $title,
'isChecked' => $value == $this->value,
'isDisabled' => $this->disabled || in_array($value, $this->disabledItems),
));
}
}
$properties = array_merge($properties, array('Options' => new ArrayList($options)));
return $this->customise($properties)->renderWith('OptionsetField');
}
/**
* Create a UL tag containing sets of radio buttons and labels. The IDs are set to
* FieldID_ItemKey, where ItemKey is the key with all non-alphanumerics removed.
*
* @todo Should use CheckboxField FieldHolder rather than constructing own markup.
*/
function Field() {
$options = '';
$odd = 0;
$source = $this->getSource();
foreach($source as $key => $value) {
$itemID = $this->id() . "_" . ereg_replace('[^a-zA-Z0-9]+','',$key);
if($key == $this->value/* || $useValue */) {
$useValue = false;
$checked = " checked=\"checked\"";
} else {
$checked="";
}
$odd = ($odd + 1) % 2;
$extraClass = $odd ? "odd" : "even";
$extraClass .= " val" . preg_replace('/[^a-zA-Z0-9\-\_]/','_', $key);
$disabled = ($this->disabled || in_array($key, $this->disabledItems)) ? 'disabled="disabled"' : '';
$options .= "<li class=\"".$extraClass."\"><input id=\"$itemID\" name=\"$this->name\" type=\"radio\" value=\"$key\"$checked $disabled class=\"radio\" /> <label for=\"$itemID\">$value</label></li>\n";
}
$id = $this->id();
return "<ul id=\"$id\" class=\"optionset {$this->extraClass()}\">\n$options</ul>\n";
}
protected $disabled = false;
function setDisabled($val) {
$this->disabled = $val;
}
function performReadonlyTransformation() {
// Source and values are DataObject sets.
$items = $this->getSource();
$field = new LookupField($this->name,$this->title ? $this->title : "" ,$items,$this->value);
$field = new LookupField($this->name, $this->title ? $this->title : '', $items, $this->value);
$field->setForm($this->form);
$field->setReadonly(true);
return $field;
@ -138,5 +118,5 @@ class OptionsetField extends DropdownField {
function ExtraOptions() {
return new ArrayList();
}
}
?>

View File

@ -14,5 +14,4 @@ class ReadonlyField extends FormField {
function performReadonlyTransformation() {
return clone $this;
}
}
?>
}

View File

@ -6,28 +6,9 @@
* @subpackage actions
*/
class ResetFormAction extends FormAction {
function Field() {
$attributes = array(
'class' => 'action' . ($this->extraClass() ? $this->extraClass() : ''),
'id' => $this->id(),
'type' => 'reset',
'name' => $this->action,
);
if($this->isReadonly()) {
$attributes['disabled'] = 'disabled';
$attributes['class'] = $attributes['class'] . ' disabled';
}
$attributes['title'] = ($this->description) ? $this->description : ($this->dontEscape) ? $this->Title() : $this->attrTitle();
if($this->useButtonTag) {
return $this->createTag('button', $attributes, $this->attrTitle());
}
return $this->createTag('input', $attributes);
public function Type() {
return 'reset';
}
}
?>
}

View File

@ -14,7 +14,7 @@ class TextField extends FormField {
/**
* Returns an input field, class="text" and type="text" with an optional maxlength
*/
function __construct($name, $title = null, $value = "", $maxLength = null, $form = null){
function __construct($name, $title = null, $value = '', $maxLength = null, $form = null) {
$this->maxLength = $maxLength;
parent::__construct($name, $title, $value, $form);
@ -33,28 +33,22 @@ class TextField extends FormField {
function getMaxLength() {
return $this->maxLength;
}
function Field() {
$attributes = array(
'type' => 'text',
'class' => 'text' . ($this->extraClass() ? $this->extraClass() : ''),
'id' => $this->id(),
'name' => $this->getName(),
'value' => $this->Value(),
'tabindex' => $this->getTabIndex(),
'maxlength' => ($this->maxLength) ? $this->maxLength : null,
'size' => ($this->maxLength) ? min( $this->maxLength, 30 ) : null
function Field($properties = array()) {
$properties = array_merge(
$properties,
array(
'MaxLength' => ($this->getMaxLength()) ? $this->getMaxLength() : null,
'Size' => ($this->getMaxLength()) ? min($this->getMaxLength(), 30) : null
)
);
if($this->disabled) $attributes['disabled'] = 'disabled';
return $this->createTag('input', $attributes);
return $this->customise($properties)->renderWith('TextField');
}
function InternallyLabelledField() {
if(!$this->value) $this->value = $this->Title();
return $this->Field();
}
}
?>
}

View File

@ -21,8 +21,9 @@
* @subpackage fields-basic
*/
class TextareaField extends FormField {
protected $rows, $cols, $disabled = false, $readonly = false;
protected $rows, $cols;
/**
* Create a new textarea field.
*
@ -38,7 +39,7 @@ class TextareaField extends FormField {
$this->cols = $cols;
parent::__construct($name, $title, $value, $form);
}
/**
* Create the <textarea> or <span> HTML tag with the
* attributes for this instance of TextareaField. This
@ -46,47 +47,19 @@ class TextareaField extends FormField {
*
* @return HTML code for the textarea OR span element
*/
function Field() {
if($this->readonly) {
$attributes = array(
'id' => $this->id(),
'class' => 'readonly' . ($this->extraClass() ? $this->extraClass() : ''),
'name' => $this->name,
'readonly' => 'readonly'
);
function Field($properties = array()) {
$properties = array_merge(
$properties,
array(
'Rows' => $this->rows,
'Cols' => $this->cols,
'Value' => htmlentities($this->value, ENT_COMPAT, 'UTF-8')
)
);
$value = (($this->value) ? nl2br(htmlentities($this->value, ENT_COMPAT, 'UTF-8')) : '<i>(' . _t('FormField.NONE', 'none') . ')</i>');
$hiddenAttributes = array(
'type' => 'hidden',
'name' => $this->name,
'value' => $value
);
$containerSpan = $this->createTag(
'span',
$attributes,
$value
);
$hiddenInput = $this->createTag('input', $hiddenAttributes);
return $containerSpan . "\n" . $hiddenInput;
} else {
$attributes = array(
'id' => $this->id(),
'class' => ($this->extraClass() ? $this->extraClass() : ''),
'name' => $this->name,
'rows' => $this->rows,
'cols' => $this->cols,
'tabindex' => $this->getTabIndex()
);
if($this->disabled) $attributes['disabled'] = 'disabled';
return $this->createTag('textarea', $attributes, htmlentities($this->value, ENT_COMPAT, 'UTF-8'));
}
return $this->customise($properties)->renderWith('TextareaField');
}
/**
* Performs a readonly transformation on this field. You should still be able
* to copy from this field, and it should still send when you submit
@ -112,9 +85,9 @@ class TextareaField extends FormField {
$clone->setReadonly(false);
return $clone;
}
function Type() {
return parent::Type() . ( $this->readonly ? ' readonly' : '' );
return parent::Type() . ($this->readonly ? ' readonly' : '');
}
/**

View File

@ -127,11 +127,15 @@ class TreeDropdownField extends FormField {
$this->searchCallback = $callback;
}
public function getShowSearch() {
return $this->showSearch;
}
/**
* @return string
*/
public function Field() {
public function Field($properties = array()) {
Requirements::add_i18n_javascript(SAPPHIRE_DIR . '/javascript/lang');
Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery/jquery.js');
@ -149,34 +153,22 @@ class TreeDropdownField extends FormField {
} else {
$title = _t('DropdownField.CHOOSE', '(Choose)', PR_MEDIUM, 'start value of a dropdown');
}
// TODO Implement for TreeMultiSelectField
$metadata = array(
'id' => $record ? $record->ID : null,
'ClassName' => $record ? $record->ClassName : $this->sourceObject
);
return $this->createTag(
'div',
array (
'id' => "TreeDropdownField_{$this->id()}",
'class' => 'TreeDropdownField single' . ($this->extraClass() ? " {$this->extraClass()}" : '') . ($this->showSearch ? " searchable" : ''),
'data-url-tree' => $this->form ? $this->Link('tree') : "",
'data-title' => $title,
// Any additional data from the selected record
'data-metadata' => ($metadata) ? Convert::raw2json($metadata) : null
),
$this->createTag(
'input',
array (
'id' => $this->id(),
'type' => 'hidden',
'class' => 'text' . ($this->extraClass() ? $this->extraClass() : ''),
'name' => $this->name,
'value' => $this->value
)
$properties = array_merge(
$properties,
array(
'Title' => $title,
'Metadata' => ($metadata) ? Convert::raw2json($metadata) : null
)
);
return $this->customise($properties)->renderWith('TreeDropdownField');
}
/**

View File

@ -0,0 +1 @@
<input id="$ID" class="checkbox$extraClass" type="$Type" value="1" name="$Name"<% if TabIndex %> tabindex="$TabIndex"<% end_if %><% if isDisabled %> disabled<% end_if %><% if Value %> checked<% end_if %>>

View File

@ -0,0 +1,5 @@
<div id="$Name" class="field $Type<% if extraClass %>$extraClass<% end_if %>">
$Field
<label class="right" for="$ID">$Title</label>
<% if Message %><span class="message $MessageType">$messageBlock</span><% end_if %>
</div>

View File

@ -0,0 +1,8 @@
<ul id="$ID" class="optionset checkboxset$extraClass">
<% control Options %>
<li class="$Class">
<input id="$ID" class="checkbox" name="$Name" type="checkbox" value="$Value"<% if isChecked %> checked<% end_if %><% if isDisabled %> disabled<% end_if %>>
<label for="$ID">$Title</label>
</li>
<% end_control %>
</ul>

View File

@ -0,0 +1,5 @@
<select id="$ID" class="dropdown$extraClass" name="$Name"<% if TabIndex %> tabindex="$TabIndex"<% end_if %><% if isDisabled %> disabled<% end_if %>>
<% control Options %>
<option value="$Value"<% if Selected %> selected<% end_if %>>$Title</option>
<% end_control %>
</select>

View File

@ -0,0 +1,6 @@
<div id="$Name" class="field $Type<% if extraClass %>$extraClass<% end_if %>">
<% if Title %><label class="left" for="$ID">$Title</label><% end_if %>
<div class="middleColumn">$Field</div>
<% if RightTitle %><label class="right" for="$ID">$RightTitle</label><% end_if %>
<% if Message %><span class="message $MessageType">$Message</span><% end_if %>
</div>

View File

@ -0,0 +1,2 @@
<input id="$ID" class="file$extraClass" type="file" name="$Name"<% if TabIndex %> tabindex="$TabIndex"<% end_if %>>
<input type="hidden" name="MAX_FILE_SIZE" value="$MaxFileSize">

View File

@ -0,0 +1,5 @@
<% if UseButtonTag %>
<button id="$ID" class="action$extraClass" type="$Type" title="$Title" value="$Title" name="$Name"<% if TabIndex %> $TabIndex<% end_if %><% if isDisabled %> disabled<% end_if %>></button>
<% else %>
<input id="$ID" class="action$extraClass" type="$Type" title="$Title" value="$Title" name="$Name"<% if TabIndex %> $TabIndex<% end_if %><% if isDisabled %> disabled<% end_if %>>
<% end_if %>

View File

@ -0,0 +1,2 @@
<span id="$ID" class="field$extraClass">$NiceValue</span>
<hidden type="hidden" name="$Name" value="$Value"<% if TabIndex %> tabindex="$TabIndex"<% end_if %>>

View File

@ -0,0 +1 @@
<h$HeadingLevel id="$ID" class="header$extraClass">$Title</h$HeadingLevel>

View File

@ -0,0 +1 @@
<input class="hidden$extraClass" type="hidden" id="$ID" name="$Name" value="$Value">

View File

@ -0,0 +1 @@
<label id="$ID" class="label$extraClass">$Title</label>

View File

@ -0,0 +1,8 @@
<ul id="$ID" class="optionset$extraClass">
<% control Options %>
<li class="$Class">
<input id="$ID" class="radio" name="$Name" type="radio" value="$Value"<% if isChecked %> checked<% end_if %><% if isDisabled %> disabled<% end_if %>>
<label for="$ID">$Title</label>
</li>
<% end_control %>
</ul>

View File

@ -0,0 +1 @@
<input id="$ID" class="text$extraClass" type="$Type" value="$Value" name="$Name"<% if TabIndex %> tabindex="$TabIndex"<% end_if %><% if MaxLength %> maxlength="$MaxLength"<% end_if %><% if Size %> size="$Size"<% end_if %><% if isDisabled %> disabled<% end_if %> />

View File

@ -0,0 +1,5 @@
<% if isReadonly %>
<span id="$ID" class="textarea readonly$extraClass"><% if Value %>$Value<% else %><em>(<% _t('NONE', 'none') %>)</em><% end_if %></span>
<% else %>
<textarea id="$ID" class="textarea$extraClass" name="$Name" rows="$Rows" cols="$Cols"<% if isDisabled %> disabled<% end_if %>></textarea>
<% end_if %>

View File

@ -0,0 +1,3 @@
<div id="TreeDropdownField_$ID" class="TreeDropdownField single$extraClass<% if ShowSearch %> searchable<% end_if %>" data-url-tree="$Link(tree)" data-tile="$Title"<% if Metadata %> data-metadata="$Metadata"<% end_if %>>
<input id="$ID" type="hidden" name="$Name" value="$Value">
</div>

View File

@ -47,14 +47,6 @@ class CheckboxSetFieldTest extends SapphireTest {
);
}
function testAddExtraClass() {
/* CheckboxSetField has an extra class name and is in the HTML the field returns */
$cboxSetField = new CheckboxSetField('FeelingOk', 'Are you feeling ok?', array(0 => 'No', 1 => 'Yes'), '', null, '(Select one)');
$cboxSetField->addExtraClass('thisIsMyExtraClassForCheckboxSetField');
preg_match('/thisIsMyExtraClassForCheckboxSetField/', $cboxSetField->Field(), $matches);
$this->assertTrue($matches[0] == 'thisIsMyExtraClassForCheckboxSetField');
}
function testSaveWithNothingSelected() {
$article = $this->objFromFixture('CheckboxSetFieldTest_Article', 'articlewithouttags');

View File

@ -5,14 +5,6 @@
*/
class DropdownFieldTest extends SapphireTest {
function testAddExtraClass() {
/* DropdownField has an extra class name and is in the HTML the field returns */
$dropdownField = new DropdownField('FeelingOk', 'Are you feeling ok?', array(0 => 'No', 1 => 'Yes'), '', null, '(Select one)');
$dropdownField->addExtraClass('thisIsMyExtraClassForDropdownField');
preg_match('/thisIsMyExtraClassForDropdownField/', $dropdownField->Field(), $matches);
$this->assertEquals($matches[0], 'thisIsMyExtraClassForDropdownField');
}
function testGetSource() {
$source = array(1=>'one');
$field = new DropdownField('Field', null, $source);

View File

@ -5,26 +5,6 @@
*/
class FormFieldTest extends SapphireTest {
function testFieldHasExtraClass() {
/* TextField has an extra class name and is in the HTML the field returns */
$textField = new TextField('Name');
$textField->addExtraClass('thisIsMyClassNameForTheFormField');
preg_match('/thisIsMyClassNameForTheFormField/', $textField->Field(), $matches);
$this->assertTrue($matches[0] == 'thisIsMyClassNameForTheFormField');
/* EmailField has an extra class name and is in the HTML the field returns */
$emailField = new EmailField('Email');
$emailField->addExtraClass('thisIsMyExtraClassForEmailField');
preg_match('/thisIsMyExtraClassForEmailField/', $emailField->Field(), $matches);
$this->assertTrue($matches[0] == 'thisIsMyExtraClassForEmailField');
/* OptionsetField has an extra class name and is in the HTML the field returns */
$optionsetField = new OptionsetField('FeelingOk', 'Are you feeling ok?', array(0 => 'No', 1 => 'Yes'), '', null, '(Select one)');
$optionsetField->addExtraClass('thisIsMyExtraClassForOptionsetField');
preg_match('/thisIsMyExtraClassForOptionsetField/', $optionsetField->Field(), $matches);
$this->assertTrue($matches[0] == 'thisIsMyExtraClassForOptionsetField');
}
function testEveryFieldTransformsReadonlyAsClone() {
$fieldClasses = ClassInfo::subclassesFor('FormField');
foreach($fieldClasses as $fieldClass) {

View File

@ -8,6 +8,6 @@ class LabelFieldTest extends SapphireTest {
function testFieldHasNoNameAttribute() {
$field = new LabelField('MyName', 'MyTitle');
$this->assertEquals($field->Field(), '<label id="MyName">MyTitle</label>');
$this->assertEquals($field->Field(), '<label id="MyName" class="label">MyTitle</label>');
}
}