mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
#92 MoreLessField: Switch to ToggleField and remove
FEATURE Refactored MoreLessField->ToggleField FEATURE Refactored TogglePanel->ToggleCompositeField FEATURE Degrading gracefully (javascript), using behaviour+classes+prototype, partially i18ned, improved markup git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/sapphire/trunk@43660 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
parent
ba85ce7a0b
commit
35fb0cd0c5
@ -1003,7 +1003,7 @@ class SiteTree extends DataObject {
|
|||||||
new TextField("MetaTitle", "Title"),
|
new TextField("MetaTitle", "Title"),
|
||||||
new TextareaField("MetaDescription", "Description"),
|
new TextareaField("MetaDescription", "Description"),
|
||||||
new TextareaField("MetaKeywords", "Keywords"),
|
new TextareaField("MetaKeywords", "Keywords"),
|
||||||
new TogglePanel("Advanced Options...",array(
|
new ToggleCompositeField("Advanced Options...",array(
|
||||||
new TextareaField("ExtraMeta","Custom Meta Tags"),
|
new TextareaField("ExtraMeta","Custom Meta Tags"),
|
||||||
new LiteralField("", "<p>Manually specify a Priority for this page: (valid values are from 0 to 1, a zero will remove this page from the index)</p>"),
|
new LiteralField("", "<p>Manually specify a Priority for this page: (valid values are from 0 to 1, a zero will remove this page from the index)</p>"),
|
||||||
new NumericField("Priority","Page Priority")),
|
new NumericField("Priority","Page Priority")),
|
||||||
|
@ -619,15 +619,20 @@ class Translatable extends DataObjectDecorator {
|
|||||||
$tasks = array(
|
$tasks = array(
|
||||||
'dup' => array(),
|
'dup' => array(),
|
||||||
);
|
);
|
||||||
foreach ($fields as $field) {
|
foreach($fields as $field) {
|
||||||
if ($field->isComposite()) {
|
if ($field->isComposite()) {
|
||||||
$innertasks = $this->duplicateOrReplaceFields($field->FieldSet());
|
$innertasks = $this->duplicateOrReplaceFields($field->FieldSet());
|
||||||
$tasks['dup'] = array_merge($tasks['dup'],$innertasks['dup']);
|
$tasks['dup'] = array_merge($tasks['dup'],$innertasks['dup']);
|
||||||
}
|
}
|
||||||
else if (($fieldname = $field->Name()) && array_key_exists($fieldname,$this->original_values)) {
|
else if(($fieldname = $field->Name()) && array_key_exists($fieldname,$this->original_values)) {
|
||||||
// Get a copy of the original field to show the untranslated value
|
// Get a copy of the original field to show the untranslated value
|
||||||
if (is_subclass_of($field->class,'TextareaField')) $nonEditableField = new MoreLessField($fieldname,$field->Title(),'','+','-');
|
if(is_subclass_of($field->class,'TextareaField')) {
|
||||||
else $nonEditableField = $field->performDisabledTransformation();
|
$nonEditableField = new ToggleField($fieldname,$field->Title(),'','+','-');
|
||||||
|
$nonEditableField->labelMore = '+';
|
||||||
|
$nonEditableField->labelLess = '-';
|
||||||
|
} else {
|
||||||
|
$nonEditableField = $field->performDisabledTransformation();
|
||||||
|
}
|
||||||
|
|
||||||
$nonEditableField_holder = new CompositeField($nonEditableField);
|
$nonEditableField_holder = new CompositeField($nonEditableField);
|
||||||
$nonEditableField_holder->setName($fieldname.'_holder');
|
$nonEditableField_holder->setName($fieldname.'_holder');
|
||||||
@ -736,3 +741,4 @@ class Translatable extends DataObjectDecorator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
?>
|
@ -1,60 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A field that shows only a part of its contents.
|
|
||||||
* Using the 'more' and 'less' links you can switch to the complete or to the partial text, respectively
|
|
||||||
*/
|
|
||||||
|
|
||||||
class MoreLessField extends ReadonlyField {
|
|
||||||
|
|
||||||
protected $moreText;
|
|
||||||
protected $lessText;
|
|
||||||
protected $charNum;
|
|
||||||
/**
|
|
||||||
* Creates a new More/Less field.
|
|
||||||
* @param name The field name
|
|
||||||
* @param title The field title
|
|
||||||
* @param value The current value
|
|
||||||
* @param moreText Text shown as a link to see the full content of the field
|
|
||||||
* @param lessText Text shown as a link to see the partial view of the field content
|
|
||||||
* @param chars Number of chars to preview. If zero it'll show the first line or sentence.
|
|
||||||
*/
|
|
||||||
function __construct($name, $title = "", $value = "", $moreText = 'more', $lessText = 'less', $chars = 0) {
|
|
||||||
$this->moreText = $moreText;
|
|
||||||
$this->lessText = $lessText;
|
|
||||||
$this->charNum = $chars;
|
|
||||||
parent::__construct($name, $title, $value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function Field() {
|
|
||||||
$valforInput = $this->value ? Convert::raw2att($this->value) : "";
|
|
||||||
$rawInput = Convert::html2raw($valforInput);
|
|
||||||
if ($this->charNum) $reducedVal = substr($rawInput,0,$this->charNum);
|
|
||||||
else $reducedVal = ereg_replace('([^\.]\.)[[:space:]].*','\\1',$rawInput);
|
|
||||||
if (strlen($reducedVal) < strlen($rawInput)) {
|
|
||||||
return <<<HTML
|
|
||||||
<div class="readonly typography" id="{$this->id()}_reduced" style="display: inline;">$reducedVal
|
|
||||||
<a onclick="\$('{$this->id()}_reduced').style.display='none'; \$('{$this->id()}').style.display='inline'; return false;" href="#"> $this->moreText</a>
|
|
||||||
</div>
|
|
||||||
<div class="readonly typography" id="{$this->id()}" style="display: none;">$this->value
|
|
||||||
<a onclick="\$('{$this->id()}').style.display='none'; \$('{$this->id()}_reduced').style.display='inline'; return false;" href="#"> $this->lessText</a>
|
|
||||||
</div>
|
|
||||||
<br /><input type="hidden" name="$this->name" value="$valforInput" />
|
|
||||||
HTML;
|
|
||||||
} else {
|
|
||||||
$this->dontEscape = true;
|
|
||||||
return parent::Field();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function setMoreText($moreText) {
|
|
||||||
$this->moreText = $moreText;
|
|
||||||
}
|
|
||||||
|
|
||||||
function setLessText($lessText) {
|
|
||||||
$this->lessText = $lessText;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
51
forms/ToggleCompositeField.php
Executable file
51
forms/ToggleCompositeField.php
Executable file
@ -0,0 +1,51 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Allows visibility of a group of fields to be toggled using '+' and '-' icons
|
||||||
|
*/
|
||||||
|
class ToggleCompositeField extends CompositeField {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var $headingLevel int
|
||||||
|
*/
|
||||||
|
public $headingLevel = 2;
|
||||||
|
|
||||||
|
function __construct($title, $children) {
|
||||||
|
$this->title = $title;
|
||||||
|
$this->name = ereg_replace('[^A-Za-z0-9]','',$this->title);
|
||||||
|
|
||||||
|
$this->startClosed(true);
|
||||||
|
|
||||||
|
parent::__construct($children);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function FieldHolder() {
|
||||||
|
Requirements::javascript("jsparty/prototype.js");
|
||||||
|
Requirements::javascript("jsparty/behaviour.js");
|
||||||
|
Requirements::javascript("jsparty/prototype_improvements.js");
|
||||||
|
Requirements::javascript("sapphire/javascript/ToggleCompositeField.js");
|
||||||
|
|
||||||
|
return $this->renderWith("ToggleCompositeField");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the field should render open or closed by default.
|
||||||
|
*
|
||||||
|
* @param boolean
|
||||||
|
*/
|
||||||
|
public function startClosed($bool) {
|
||||||
|
($bool) ? $this->addExtraClass('startClosed') : $this->removeExtraClass('startClosed');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
public function HeadingLevel() {
|
||||||
|
return $this->headingLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function Type() {
|
||||||
|
return ' toggleCompositeField';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
99
forms/ToggleField.php
Executable file
99
forms/ToggleField.php
Executable file
@ -0,0 +1,99 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ReadonlyField with added toggle-capabilities - will preview the first sentence of the contained text-value,
|
||||||
|
* and show the full content by a javascript-switch.
|
||||||
|
*
|
||||||
|
* Caution: Strips HTML-encoding for the preview.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class ToggleField extends ReadonlyField {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var $labelMore string Text shown as a link to see the full content of the field
|
||||||
|
*/
|
||||||
|
public $labelMore;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var $labelLess string Text shown as a link to see the partial view of the field content
|
||||||
|
*/
|
||||||
|
public $labelLess;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var $truncateMethod string (FirstSentence|FirstParagraph) @see {Text}
|
||||||
|
*/
|
||||||
|
public $truncateMethod = 'FirstSentence';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var $truncateChars int Number of chars to preview (optional).
|
||||||
|
* Truncating will be applied with $truncateMethod by default.
|
||||||
|
*/
|
||||||
|
public $truncateChars;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param name The field name
|
||||||
|
* @param title The field title
|
||||||
|
* @param value The current value
|
||||||
|
*/
|
||||||
|
function __construct($name, $title = "", $value = "") {
|
||||||
|
$this->labelMore = _t('ToggleField.MORE', 'more');
|
||||||
|
$this->labelLess = _t('ToggleField.LESS', 'less');
|
||||||
|
|
||||||
|
$this->startClosed(true);
|
||||||
|
|
||||||
|
parent::__construct($name, $title, $value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Field() {
|
||||||
|
$content = '';
|
||||||
|
|
||||||
|
Requirements::javascript("jsparty/prototype.js");
|
||||||
|
Requirements::javascript("jsparty/behaviour.js");
|
||||||
|
Requirements::javascript("jsparty/prototype_improvements.js");
|
||||||
|
Requirements::javascript("sapphire/javascript/ToggleField.js");
|
||||||
|
|
||||||
|
if($this->startClosed) $this->addExtraClass('startClosed');
|
||||||
|
|
||||||
|
$valforInput = $this->value ? Convert::raw2att($this->value) : "";
|
||||||
|
$rawInput = Convert::html2raw($valforInput);
|
||||||
|
|
||||||
|
if($this->charNum) $reducedVal = substr($rawInput,0,$this->charNum);
|
||||||
|
else $reducedVal = DBField::create('Text',$rawInput)->{$this->truncateMethod}();
|
||||||
|
|
||||||
|
// only create togglefield if the truncated content is shorter
|
||||||
|
if(strlen($reducedVal) < strlen($rawInput)) {
|
||||||
|
$content = <<<HTML
|
||||||
|
<div class="readonly typography contentLess" style="display: none">
|
||||||
|
$reducedVal
|
||||||
|
<a href="#" class="triggerMore">$this->labelMore</a>
|
||||||
|
</div>
|
||||||
|
<div class="readonly typography contentMore">
|
||||||
|
$this->value
|
||||||
|
<a href="#" class="triggerLess">$this->labelLess</a>
|
||||||
|
</div>
|
||||||
|
<br />
|
||||||
|
<input type="hidden" name="$this->name" value="$valforInput" />
|
||||||
|
HTML;
|
||||||
|
} else {
|
||||||
|
$this->dontEscape = true;
|
||||||
|
$content = parent::Field();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $content;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if the field should render open or closed by default.
|
||||||
|
*
|
||||||
|
* @param boolean
|
||||||
|
*/
|
||||||
|
public function startClosed($bool) {
|
||||||
|
($bool) ? $this->addExtraClass('startClosed') : $this->removeExtraClass('startClosed');
|
||||||
|
}
|
||||||
|
|
||||||
|
function Type() {
|
||||||
|
return "toggleField";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
@ -1,41 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allows visibility of a group of fields to be toggled using + and - icons
|
|
||||||
*/
|
|
||||||
class TogglePanel extends CompositeField {
|
|
||||||
protected $closed = false;
|
|
||||||
|
|
||||||
function __construct($title, $children, $startClosed = false) {
|
|
||||||
$this->title = $title;
|
|
||||||
$this->closed = $startClosed;
|
|
||||||
$this->name = ereg_replace('[^A-Za-z0-9]','',$this->title);
|
|
||||||
parent::__construct($children);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function FieldHolder() {
|
|
||||||
Requirements::javascript("jsparty/prototype.js");
|
|
||||||
Requirements::javascript("jsparty/behaviour.js");
|
|
||||||
Requirements::javascript("jsparty/prototype_improvements.js");
|
|
||||||
Requirements::javascript("sapphire/javascript/TogglePanel.js");
|
|
||||||
|
|
||||||
return $this->renderWith("TogglePanel");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setClosed($closed) {
|
|
||||||
$this->closed = $closed;
|
|
||||||
}
|
|
||||||
public function getClosed() {
|
|
||||||
return $this->closed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function ClosedClass() {
|
|
||||||
if($this->closed) return " closed";
|
|
||||||
}
|
|
||||||
public function ClosedStyle() {
|
|
||||||
if($this->closed) return "style=\"display: none\"";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
?>
|
|
26
javascript/ToggleCompositeField.js
Executable file
26
javascript/ToggleCompositeField.js
Executable file
@ -0,0 +1,26 @@
|
|||||||
|
var ToggleCompositeField = Class.create();
|
||||||
|
ToggleCompositeField.prototype = {
|
||||||
|
initialize: function() {
|
||||||
|
var rules = {};
|
||||||
|
rules['#' + this.id + ' .trigger'] = {
|
||||||
|
onclick: function(e) {
|
||||||
|
this.toggle();
|
||||||
|
Event.stop(e); return false;
|
||||||
|
}.bind(this)
|
||||||
|
};
|
||||||
|
Behaviour.register(rules);
|
||||||
|
|
||||||
|
// close content by default
|
||||||
|
if(Element.hasClassName(this, 'startClosed')) {
|
||||||
|
Element.toggle($$('#' + this.id + ' .contentMore')[0]);
|
||||||
|
}
|
||||||
|
Element.toggle($$('#' + this.id + ' .triggerClosed')[0]);
|
||||||
|
},
|
||||||
|
|
||||||
|
toggle: function() {
|
||||||
|
Element.toggle($$('#' + this.id + ' .contentMore')[0]);
|
||||||
|
Element.toggle($$('#' + this.id + ' .triggerClosed')[0]);
|
||||||
|
Element.toggle($$('#' + this.id + ' .triggerOpened')[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ToggleCompositeField.applyTo('div.toggleCompositeField');
|
29
javascript/ToggleField.js
Normal file
29
javascript/ToggleField.js
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
var ToggleField = Class.create();
|
||||||
|
ToggleField.prototype = {
|
||||||
|
initialize: function() {
|
||||||
|
var rules = {};
|
||||||
|
rules['#' + this.id + ' .triggerMore'] = {
|
||||||
|
onclick: function(e) {
|
||||||
|
this.toggle();
|
||||||
|
Event.stop(e); return false;
|
||||||
|
}.bind(this)
|
||||||
|
};
|
||||||
|
rules['#' + this.id + ' .triggerLess'] = {
|
||||||
|
onclick: function(e) {
|
||||||
|
this.toggle();
|
||||||
|
Event.stop(e); return false;
|
||||||
|
}.bind(this)
|
||||||
|
};
|
||||||
|
Behaviour.register(rules);
|
||||||
|
|
||||||
|
if(Element.hasClassName(this, 'startClosed')) {
|
||||||
|
this.toggle();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
toggle: function() {
|
||||||
|
Element.toggle($$('#' + this.id + ' .contentLess')[0]);
|
||||||
|
Element.toggle($$('#' + this.id + ' .contentMore')[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ToggleField.applyTo('div.toggleField');
|
@ -1,17 +0,0 @@
|
|||||||
Behaviour.register({
|
|
||||||
'h2.TogglePanelHeader' : {
|
|
||||||
onclick : function() {
|
|
||||||
var contentDiv = $('panel_' + this.id);
|
|
||||||
var toggleID = this.id.replace('panel_','') + '_toggle';
|
|
||||||
Element.toggle(toggleID + '_closed');
|
|
||||||
Element.toggle(toggleID + '_open');
|
|
||||||
if(contentDiv.style.display == 'none') {
|
|
||||||
contentDiv.style.display = '';
|
|
||||||
Element.removeClassName(this, 'closed');
|
|
||||||
} else {
|
|
||||||
contentDiv.style.display = 'none';
|
|
||||||
Element.addClassName(this, 'closed');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
12
templates/ToggleCompositeField.ss
Executable file
12
templates/ToggleCompositeField.ss
Executable file
@ -0,0 +1,12 @@
|
|||||||
|
<div id="$Name" class="$Type $extraClass">
|
||||||
|
<h$HeadingLevel style="cursor: pointer;" class="trigger$ClosedClass">
|
||||||
|
<img class="triggerClosed" src="sapphire/images/toggle-closed.gif" alt="+" style="display:none;" title="Show" />
|
||||||
|
<img class="triggerOpened" src="sapphire/images/toggle-open.gif" alt="-" style="display:none;" title="Hide" />
|
||||||
|
$Title
|
||||||
|
</h$HeadingLevel>
|
||||||
|
<div class="contentMore">
|
||||||
|
<% control FieldSet %>
|
||||||
|
$FieldHolder
|
||||||
|
<% end_control %>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -1,10 +0,0 @@
|
|||||||
<h2 id="$id" style="cursor: pointer;" class="TogglePanelHeader$ClosedClass">
|
|
||||||
<img id="{$id}_toggle_closed" src="sapphire/images/toggle-closed.gif" alt="+" title="Show" />
|
|
||||||
<img id="{$id}_toggle_open" src="sapphire/images/toggle-open.gif" alt="-" style="display:none;" title="Hide" />
|
|
||||||
$Title
|
|
||||||
</h2>
|
|
||||||
<div id="panel_$id" $ClosedStyle>
|
|
||||||
<% control FieldSet %>
|
|
||||||
$FieldHolder
|
|
||||||
<% end_control %>
|
|
||||||
</div>
|
|
Loading…
Reference in New Issue
Block a user