Permalink
Browse files

NEW Clickable URL preview in CMS

- Refactored SiteTreeURLSegmentField to render controls in template
rather than JS for better clientside performance, and cleaner behaviour.
- Added dynamic ellipsis to start of URL, to retain most relevant
part of the URL (the last bits)
- Added "suffix" setting to field, which defaults to ?stage=Stage
- Removed prefix from edit view to leave more room for URL

Thanks to @sunnysideup for getting this started in
#269
  • Loading branch information...
1 parent 931b726 commit 00097a5d5d34a4e1277b2c0f1821a2302d70a04a @chillu chillu committed Feb 3, 2013
View
23 code/forms/SiteTreeURLSegmentField.php
@@ -15,7 +15,7 @@ class SiteTreeURLSegmentField extends TextField {
/**
* @var string
*/
- protected $helpText, $urlPrefix;
+ protected $helpText, $urlPrefix, $urlSuffix;
static $allowed_actions = array(
'suggest'
@@ -25,6 +25,16 @@ public function Value() {
return rawurldecode($this->value);
}
+ public function getAttributes() {
+ return array_merge(
+ parent::getAttributes(),
+ array(
+ 'data-prefix' => $this->getURLPrefix(),
+ 'data-suffix' => '?stage=Stage'
@nathanbrauer
nathanbrauer Jan 13, 2016

Replace with:

                'data-suffix' => $this->getURLSuffix(),
@tractorcow
tractorcow Jan 13, 2016

Let's also add '?stage=Stage' as the default return for getURLSuffix() if it's not set.

+ )
+ );
+ }
+
public function Field($properties = array()) {
Requirements::javascript(CMS_DIR . '/javascript/SiteTreeURLSegmentField.js');
Requirements::add_i18n_javascript(CMS_DIR . '/javascript/lang', false, true);
@@ -85,9 +95,20 @@ public function getURLPrefix(){
return $this->urlPrefix;
}
+ public function getURLSuffix() {
+ return $this->urlSuffix;
+ }
+
+ public function setURLSuffix($suffix) {
+ $this->urlSuffix = $suffix;
+ }
public function Type() {
return 'text urlsegment';
}
+ public function getURL() {
+ return Controller::join_links($this->getURLPrefix(), $this->Value(), $this->getURLSuffix());
+ }
+
}
View
5 code/model/SiteTree.php
@@ -1829,11 +1829,8 @@ public function getCMSFields() {
(self::nested_urls() && $this->ParentID ? $this->Parent()->RelativeLink(true) : null)
);
-
-
- $url = (strlen($baseLink) > 36) ? "..." .substr($baseLink, -32) : $baseLink;
$urlsegment = new SiteTreeURLSegmentField("URLSegment", $this->fieldLabel('URLSegment'));
- $urlsegment->setURLPrefix($url);
+ $urlsegment->setURLPrefix($baseLink);
@nathanbrauer
nathanbrauer Jan 13, 2016

Replace with:

        $urlsegment->setURLPrefix($baseLink)->setURLSuffix("?stage=Stage");
$helpText = (self::nested_urls() && count($this->Children())) ? $this->fieldLabel('LinkChangeNote') : '';
if(!URLSegmentFilter::$default_allow_multibyte) {
$helpText .= $helpText ? '<br />' : '';
View
7 css/screen.css
@@ -22,10 +22,11 @@
/** ------------------------------------------------------------------ URLSegment field ----------------------------------------------------------------- */
.field.urlsegment.loading { background: url(../images/loading.gif) no-repeat 162px 8px; }
-.field.urlsegment .prefix, .field.urlsegment .preview { padding-top: 8px; display: inline-block; }
-.field.urlsegment .prefix { color: #777; }
-.field.urlsegment .cancel, .field.urlsegment .update, .field.urlsegment .edit { margin-left: 7px; }
+.field.urlsegment .preview { padding-top: 8px; display: inline-block; }
+.field.urlsegment input.text { width: 250px; }
+.field.urlsegment input.text, .field.urlsegment .cancel, .field.urlsegment .update, .field.urlsegment .edit { margin-right: 8px; }
.field.urlsegment .help { margin-left: 0; }
+.field.urlsegment .edit-holder { display: none; }
#Form_EditForm #Title .update { margin-left: 7px; }
View
240 javascript/SiteTreeURLSegmentField.js
@@ -2,215 +2,125 @@
$.entwine('ss', function($) {
/**
* Class: .field.urlsegment
- *
- * Input validation on the URLSegment field
+ *
+ * Provides enhanced functionality (read-only/edit switch) and
+ * input validation on the URLSegment field
*/
$('.field.urlsegment:not(.readonly)').entwine({
- /**
- * Constructor: onmatch
- */
+ // Roughly matches the field width including edit button
+ MaxPreviewLength: 55,
+
+ Ellipsis: '...',
+
onmatch : function() {
// Only initialize the field if it contains an editable field.
// This ensures we don't get bogus previews on readonly fields.
- if(this.find(':text').length) {
- this._addActions(); // add elements and actions for editing
- this.edit(); // toggle
- this._autoInputWidth(); // set width of input field
- }
+ if(this.find(':text').length) this.toggleEdit(false);
+ this.redraw();
this._super();
},
- onunmatch: function() {
- this._super();
+
+ redraw: function() {
+ var field = this.find(':text'),
+ url = field.data('prefix') + field.val(),
+ previewUrl = url;
+
+ // Truncate URL if required (ignoring the suffix, retaining the full value)
+ if(url.length > this.getMaxPreviewLength()) {
+ previewUrl = this.getEllipsis() + url.substr(url.length - this.getMaxPreviewLength(), url.length);
+ }
+
+ // Transfer current value to holder
+ this.find('.preview').attr('href', url + field.data('suffix')).text(previewUrl);
},
-
+
/**
- * Function: edit
- *
- * Toggles the edit state of the field
- *
- * Return URLSegemnt val()
- *
- * Parameters:
- * (Bool) auto (optional, triggers a second toggle)
+ * @param Boolean
*/
- edit: function(auto) {
-
- var field = this.find(':text'),
- holder = this.find('.preview'),
- edit = this.find('.edit'),
- update = this.find('.update'),
- cancel = this.find('.cancel'),
- help = this.find('.help');
-
- // transfer current value to holder
- holder.text(field.val());
-
- // toggle elements
- if (field.is(':visible')) {
- update.hide();
- cancel.hide();
- field.hide();
- holder.show();
- edit.show();
- help.hide();
- } else {
- edit.hide();
- holder.hide();
- field.show();
- update.show();
- cancel.show();
- help.show();
+ toggleEdit: function(toggle) {
+ var field = this.find(':text');
+
+ this.find('.preview-holder')[toggle ? 'hide' : 'show']();
+ this.find('.edit-holder')[toggle ? 'show' : 'hide']();
+
+ if(toggle) {
+ field.data("origval", field.val()); //retain current value for cancel
+ field.focus();
}
-
- // field updated from another fields value
- // reset to original state
- if (auto) this.edit();
-
- return field.val();
},
/**
- * Function: update
- *
* Commits the change of the URLSegment to the field
- * Optional: pass in (String)
- * to update the URLSegment
+ * Optional: pass in (String) to update the URLSegment
*/
update: function() {
-
var self = this,
field = this.find(':text'),
- holder = this.find('.preview'),
- currentVal = holder.text(),
- updateVal,
- title = arguments[0];
-
- if (title && title !== "") {
- updateVal = title;
- } else {
- updateVal = field.val();
- }
+ currentVal = field.data('origval'),
+ title = arguments[0],
+ updateVal = (title && title !== "") ? title : field.val();
if (currentVal != updateVal) {
- self.addClass('loading');
- self.suggest(updateVal, function(data) {
- var newVal = decodeURIComponent(data.value);
- field.val(newVal);
- self.edit(title);
+ this.addClass('loading');
+ this.suggest(updateVal, function(data) {
+ field.val(decodeURIComponent(data.value));
+ self.toggleEdit(false);
self.removeClass('loading');
+ self.redraw();
});
} else {
- self.edit();
+ this.toggleEdit(false);
+ this.redraw();
}
},
/**
- * Function: cancel
- *
* Cancels any changes to the field
- *
- * Return URLSegemnt val()
- *
*/
cancel: function() {
- var field = this.find(':text'),
- holder = this.find('.preview');
- field.val(holder.text());
- this.edit();
-
- return field.val();
+ var field = this.find(':text');
+ field.val(field.data("origval"));
+ this.toggleEdit(false);
},
/**
- * Function: suggest
- *
* Return a value matching the criteria.
*
- * Parameters:
- * (String) val
- * (Function) callback
+ * @param (String)
+ * @param (Function)
*/
suggest: function(val, callback) {
- var field = this.find(':text'), urlParts = $.path.parseUrl(this.closest('form').attr('action')),
+ var field = this.find(':text'),
+ urlParts = $.path.parseUrl(this.closest('form').attr('action')),
url = urlParts.hrefNoSearch + '/field/' + field.attr('name') + '/suggest/?value=' + encodeURIComponent(val);
if(urlParts.search) url += '&' + urlParts.search.replace(/^\?/, '');
- $.get(
- url,
- function(data) {callback.apply(this, arguments);}
- );
-
- },
-
- /**
- * Function: _addActions
- *
- * Utility to add edit buttons and actions
- *
- */
- _addActions: function() {
- var self = this,
- field = this.find(':text'),
- preview,
- editAction,
- updateAction,
- cancelAction;
-
- // element to display non-editable text
- preview = $('<span />', {
- 'class': 'preview'
- });
-
- // edit button
- editAction = $('<button />', {
- 'class': 'ss-ui-button ss-ui-button-small edit',
- 'text': ss.i18n._t('URLSEGMENT.Edit', 'Edit'),
- 'click': function(e) {
- e.preventDefault();
- self.edit();
- self.find(':text').focus();
- }
- });
-
- // update button
- updateAction = $('<button />', {
- 'class': 'update ss-ui-button-small',
- 'text': ss.i18n._t('URLSEGMENT.OK', 'OK'),
- 'click': function(e) {
- e.preventDefault();
- self.update();
- }
- });
-
- // cancel button
- cancelAction = $('<button />', {
- 'class': 'cancel ss-ui-action-minor ss-ui-button-small',
- 'href': '#',
- 'text': ss.i18n._t('URLSEGMENT.Cancel', 'Cancel'),
- 'click': function(e) {
- e.preventDefault();
- self.cancel();
- }
- });
-
- // insert elements
- preview.insertAfter('.prefix');
- editAction.insertAfter(field);
- cancelAction.insertAfter(field);
- updateAction.insertAfter(field);
- },
-
- /**
- * Function: _autoInputWidth
- *
- * Sets the width of input so it lines up with the other fields
- */
- _autoInputWidth: function() {
- var field = this.find(':text');
- field.width((field.width() + 15) - this.find('.prefix').width());
+ $.get(url, function(data) {callback.apply(this, arguments);});
+ }
+ });
+
+ $('.field.urlsegment .edit').entwine({
+ onclick: function(e) {
+ e.preventDefault();
+ this.closest('.field').toggleEdit(true);
+ }
+ });
+
+ $('.field.urlsegment .update').entwine({
+ onclick: function(e) {
+ e.preventDefault();
+ this.closest('.field').update();
+ }
+ });
+
+ $('.field.urlsegment .cancel').entwine({
+ onclick: function(e) {
+ e.preventDefault();
+ this.closest('.field').cancel();
}
});
});
+
}(jQuery));
View
13 scss/_CMSMain.scss
@@ -98,23 +98,26 @@
background: url(../images/loading.gif) no-repeat 162px 8px;
}
- .prefix,
.preview {
padding-top: 8px;
display: inline-block;
}
- .prefix {
- color: #777;
+ input.text {
+ width: 250px; // ensure there's enough room for buttons
}
- .cancel, .update, .edit {
- margin-left: 7px;
+ input.text, .cancel, .update, .edit {
+ margin-right: 8px;
}
.help {
margin-left: 0;
}
+
+ .edit-holder {
+ display: none;
+ }
}
#Form_EditForm #Title .update {
View
22 templates/forms/SiteTreeURLSegmentField.ss
@@ -1,4 +1,18 @@
-<span class="prefix">$URLPrefix</span><input $AttributesHTML />
-<% if HelpText %>
-<p class="help">$HelpText</p>
-<% end_if %>
+<div class="preview-holder">
+ <a class="preview" href="$URL" target="_blank">
+ $URL
+ </a>
+ <button class="ss-ui-button ss-ui-button-small edit">
+ <% _t('URLSegmentField.Edit', 'Edit') %>
+ </button>
+</div>
+<div class="edit-holder">
+ <input $AttributesHTML />
+ <button class="update ss-ui-button-small">
+ <% _t('URLSegmentField.OK', 'OK') %>
+ </button>
+ <button class="cancel ss-ui-button-small ss-ui-action-minor">
+ <% _t('URLSegmentField.Cancel', 'Cancel') %>
+ </button>
+ <% if HelpText %><p class="help">$HelpText</p><% end_if %>
+</div>

0 comments on commit 00097a5

Please sign in to comment.