Trac 6981: Opt-in URL Updating

This commit is contained in:
Ryan Wachtl 2012-05-09 19:01:57 -05:00
parent dedb9d0461
commit bd94c6f336
5 changed files with 280 additions and 55 deletions

View File

@ -1835,11 +1835,11 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
$rootTab = new TabSet("Root",
$tabMain = new Tab('Main',
new TextField("Title", $this->fieldLabel('Title')),
$urlsegment,
new TextField("MenuTitle", $this->fieldLabel('MenuTitle')),
$htmlField = new HtmlEditorField("Content", _t('SiteTree.HTMLEDITORTITLE', "Content", 'HTML editor title'))
),
$tabMeta = new Tab('Metadata',
$urlsegment,
new TextField("MetaTitle", $this->fieldLabel('MetaTitle')),
new TextareaField("MetaKeywords", $this->fieldLabel('MetaKeywords'), 1),
new TextareaField("MetaDescription", $this->fieldLabel('MetaDescription')),

View File

@ -3,42 +3,91 @@
/** ----------------------------- CMS Components ------------------------------ */
/** Style custom to the CMSMain admin interface. CMSMain extends the built in SilverStripe admin section styles. As much as possible we want to use those built in styles. If anything in this file can be implemented in a generic way then it should be include in the admin scss files. @package cms */
/** ------------------------------------------------------------------ Page History Section. ----------------------------------------------------------------- */
/* line 15, ../scss/_CMSMain.scss */
#cms-page-history-versions tr.loading { color: #999; }
/* line 20, ../scss/_CMSMain.scss */
#cms-page-history-versions tr.loading td:hover { cursor: none; }
/* line 27, ../scss/_CMSMain.scss */
#cms-page-history-versions td:hover { cursor: pointer; }
/* line 34, ../scss/_CMSMain.scss */
.CMSPageHistoryController ins { background-color: #DFD; padding: 2px; text-decoration: none; }
/* line 40, ../scss/_CMSMain.scss */
.CMSPageHistoryController del { background-color: #FDD; padding: 2px; color: #ff4444; }
/** -------------------------------------------- Tree View (collapsed for sidebar) -------------------------------------------- */
/* line 51, ../scss/_CMSMain.scss */
#cms-content-treeview .cms-tree-expand-trigger { display: none; }
/* line 58, ../scss/_CMSMain.scss */
.cms-content-tools #cms-content-treeview .cms-content-toolbar { border-bottom: none; box-shadow: none; margin-bottom: 8px; }
/* line 62, ../scss/_CMSMain.scss */
.cms-content-tools #cms-content-treeview .cms-content-toolbar .cms-page-add-button { margin-top: 8px; }
/* line 67, ../scss/_CMSMain.scss */
.cms-content-tools #cms-content-treeview .cms-tree-view-modes, .cms-content-tools #cms-content-treeview .cms-content-batchactions { display: none; }
/* line 70, ../scss/_CMSMain.scss */
.cms-content-tools #cms-content-treeview .cms-tree-expand-trigger { display: inline-block; margin: 0 0 2px 0; position: absolute; top: 8px; right: 4px; }
/* line 76, ../scss/_CMSMain.scss */
.cms-content-tools #cms-content-treeview .cms-tree-expand-trigger span.ui-button-text { padding-right: 8px; }
/* line 83, ../scss/_CMSMain.scss */
.cms-content-tools #cms-content-treeview .cms-tree .badge, .cms-content-tools #cms-content-treeview .cms-tree a > .jstree-icon { display: none; }
/** ------------------------------------------------------------------ URLSegment field ----------------------------------------------------------------- */
/* line 94, ../scss/_CMSMain.scss */
.URLPrefix, .URLSegmentHolder { padding-top: 8px; display: inline-block; }
/* line 99, ../scss/_CMSMain.scss */
.URLPrefix { color: #777; }
/* line 103, ../scss/_CMSMain.scss */
#Form_EditForm_URLSegment { margin-left: 7px; }
/* line 106, ../scss/_CMSMain.scss */
#Form_EditForm_URLSegment.disabled { color: #444; padding-left: 0px; margin-left: 0px; background: none; border-color: transparent; }
/* line 117, ../scss/_CMSMain.scss */
#Form_EditForm_URLSegment_Cancel, #Form_EditForm_URLSegment_Update, #Form_EditForm_URLSegment_Edit { margin-left: 7px; }
/* line 2, ../scss/_AssetAdmin.scss */
.cms .AssetAdmin .cms-content-fields { overflow: hidden; }
/* line 4, ../scss/_AssetAdmin.scss */
.cms .AssetAdmin .cms-content-fields .cms-edit-form.AssetAdmin { overflow-y: auto; }
/* line 7, ../scss/_AssetAdmin.scss */
.cms .AssetAdmin .cms-content-fields .cms-content-tools .cms-panel-content { overflow: hidden; }
/* line 9, ../scss/_AssetAdmin.scss */
.cms .AssetAdmin .cms-content-fields .cms-content-tools .cms-panel-content .cms-search-form { height: 100%; }
/* line 17, ../scss/_AssetAdmin.scss */
.cms .AssetAdmin .cms-content-header-tabs .ui-tabs-nav li a { font-weight: bold; line-height: 16px; padding: 12px 20px 12px; text-indent: -9999em; }
/* line 23, ../scss/_AssetAdmin.scss */
.cms .AssetAdmin .cms-content-header-tabs .ui-tabs-nav li a.content-treeview { background: url(../images/content-header-tabs-sprite.png) no-repeat 2px 0px; }
/* line 26, ../scss/_AssetAdmin.scss */
.cms .AssetAdmin .cms-content-header-tabs .ui-tabs-nav li a.content-galleryview { background: url(../images/content-header-tabs-sprite.png) no-repeat -160px 0px; }
/* line 29, ../scss/_AssetAdmin.scss */
.cms .AssetAdmin .cms-content-header-tabs .ui-tabs-nav li a.content-listview { background: url(../images/content-header-tabs-sprite.png) no-repeat -39px 0px; }
/* line 37, ../scss/_AssetAdmin.scss */
.cms .AssetAdmin .cms-content-header-tabs .ui-tabs-nav .ui-state-active, .cms .AssetAdmin .cms-content-header-tabs .ui-tabs-nav .ui-widget-content .ui-state-active, .cms .AssetAdmin .cms-content-header-tabs .ui-tabs-nav .ui-widget-header .ui-state-active { border-top: none; }
/* line 43, ../scss/_AssetAdmin.scss */
.cms .AssetAdmin .cms-content-header-tabs .ui-tabs-nav .ui-state-active a.content-treeview, .cms .AssetAdmin .cms-content-header-tabs .ui-tabs-nav .ui-widget-content .ui-state-active a.content-treeview, .cms .AssetAdmin .cms-content-header-tabs .ui-tabs-nav .ui-widget-header .ui-state-active a.content-treeview { background: url(../images/content-header-tabs-sprite.png) no-repeat 1px -40px; }
/* line 46, ../scss/_AssetAdmin.scss */
.cms .AssetAdmin .cms-content-header-tabs .ui-tabs-nav .ui-state-active a.content-galleryview, .cms .AssetAdmin .cms-content-header-tabs .ui-tabs-nav .ui-widget-content .ui-state-active a.content-galleryview, .cms .AssetAdmin .cms-content-header-tabs .ui-tabs-nav .ui-widget-header .ui-state-active a.content-galleryview { background: url(../images/content-header-tabs-sprite.png) no-repeat -161px -40px; }
/* line 49, ../scss/_AssetAdmin.scss */
.cms .AssetAdmin .cms-content-header-tabs .ui-tabs-nav .ui-state-active a.content-listview, .cms .AssetAdmin .cms-content-header-tabs .ui-tabs-nav .ui-widget-content .ui-state-active a.content-listview, .cms .AssetAdmin .cms-content-header-tabs .ui-tabs-nav .ui-widget-header .ui-state-active a.content-listview { background: url(../images/content-header-tabs-sprite.png) no-repeat -38px -40px; }
/* line 58, ../scss/_AssetAdmin.scss */
.cms .AssetAdmin .cms-content-toolbar .cms-page-add-button { background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(0%, #f3f3f3), color-stop(100%, #d9d9d9)); background-image: -webkit-linear-gradient(#f3f3f3, #d9d9d9); background-image: -moz-linear-gradient(#f3f3f3, #d9d9d9); background-image: -o-linear-gradient(#f3f3f3, #d9d9d9); background-image: -ms-linear-gradient(#f3f3f3, #d9d9d9); background-image: linear-gradient(#f3f3f3, #d9d9d9); border-color: #c0c0c2; }
/* line 61, ../scss/_AssetAdmin.scss */
.cms .AssetAdmin .cms-content-toolbar .cms-page-add-button span.btn-icon-add { height: 17px; }
/* line 64, ../scss/_AssetAdmin.scss */
.cms .AssetAdmin .cms-content-toolbar .cms-page-add-button span.ui-button-text { color: #393939; text-shadow: white 0 1px 1px; }
/* line 72, ../scss/_AssetAdmin.scss */
.cms .AssetAdmin #Root_TreeView .cms-tree ul .class-Folder a span.text span.jstree-foldericon { background: url(../images/blue-folder-horizontal.png) no-repeat; width: 16px; height: 16px; float: left; display: block; margin-right: 4px; }
/* line 83, ../scss/_AssetAdmin.scss */
.cms .AssetAdmin #Form_EditForm_File td { padding-top: 0; padding-bottom: 0; }
/* line 87, ../scss/_AssetAdmin.scss */
.cms .AssetAdmin #Form_EditForm_File td.bottom-all { padding: 0.7em; }
/* line 91, ../scss/_AssetAdmin.scss */
.cms .AssetAdmin #Form_EditForm_File td.col-StripThumbnail { padding: 0; width: 32px; height: 32px; display: block; }
/* line 97, ../scss/_AssetAdmin.scss */
.cms .AssetAdmin #Form_EditForm_File td.col-StripThumbnail img { width: 32px; height: 32px; }
/* line 103, ../scss/_AssetAdmin.scss */
.cms .AssetAdmin #Form_EditForm_File tr[data-class=Folder] td.col-StripThumbnail { background: transparent url(../images/treeicons/blue-folder-horizontal.png) no-repeat top left; }
/* line 108, ../scss/_AssetAdmin.scss */
.cms .AssetAdmin #Form_EditForm_File tr[data-class=File] td.col-StripThumbnail { background: transparent url(../images/treeicons/blue-document.png) no-repeat top left; }

View File

@ -27,31 +27,118 @@
*/
onmatch : function() {
var self = this;
// intercept change event, do our own writing
this.bind('change', function(e) {
if(!self.val()) return;
self.addClass('disabled').parents('.field:first').addClass('loading');
var oldVal = self.val();
self.suggest(oldVal, function(data) {
self.removeClass('disabled').parents('.field:first').removeClass('loading');
var newVal = decodeURIComponent(data.value);
self.val(newVal);
if(oldVal != newVal) {
var confirmMessage = ss.i18n.sprintf(
ss.i18n._t('UPDATEURL.CONFIRMURLCHANGED', 'The URL has been changed to\n"%s'),
data.value
);
jQuery.noticeAdd({'text':confirmMessage});
}
});
});
// add elements and actions for editing
self._addActions();
// toggle
self.edit();
// set width of input field
self._autoInputWidth();
this._super();
},
/**
* Function: edit
*
* Toggles the edit state of the field
*
* Return URLSegemnt val()
*
* Parameters:
* (Bool) external (optional)
*/
edit: function(external) {
var self = this,
holder = $('#URLSegmentHolder'),
edit = $('#Form_EditForm_URLSegment_Edit'),
update = $('#Form_EditForm_URLSegment_Update'),
cancel = $('#Form_EditForm_URLSegment_Cancel');
// transfer current value to holder
holder.text(self.val());
// toggle elements
if (self.is(':visible')) {
update.hide();
cancel.hide();
self.hide();
holder.show();
edit.show();
} else {
edit.hide();
holder.hide();
self.show();
update.show();
cancel.show();
}
// field updated from another fields value
// reset to original state
if (external) {
self.edit();
}
return self.val();
},
/**
* Function: update
*
* Commits the change of the URLSegment to the field
* Optional: pass in another element to use its value
* to update the URLSegment (ex. from Title)
*
* Parameters:
* (jQuery Wrapped DOM element) field (optional)
*/
update: function(field) {
var self = this,
holder = $('#URLSegmentHolder'),
currentVal = holder.text(),
updateVal,
external = null;
if (field && field.val() != "") {
updateVal = field.val();
external = true;
} else {
updateVal = self.val();
}
if (currentVal != updateVal) {
self.suggest(updateVal, function(data) {
var newVal = decodeURIComponent(data.value);
self.val(newVal);
self.edit(external);
});
} else {
self.edit();
}
},
/**
* Function: cancel
*
* Cancels any changes to the field
*
* Return URLSegemnt val()
*
*/
cancel: function() {
var self = this,
holder = $('#URLSegmentHolder');
self.val(holder.text());
self.edit();
return self.val();
},
/**
* Function: suggest
@ -63,13 +150,89 @@
* (Function) callback
*/
suggest: function(val, callback) {
$.get(
this.parents('form:first').attr('action') +
'/field/URLSegment/suggest/?value=' + encodeURIComponent(this.val()),
'/field/URLSegment/suggest/?value=' + encodeURIComponent(val),
function(data) {
callback.apply(this, arguments);
}
);
},
/**
* Function: _addActions
*
* Utility to add edit buttons and actions
*
*/
_addActions: function() {
var self = this,
holder,
editAction,
updateAction,
cancelAction;
// element to display non-editable text
holder = $('<span />', {
'id': 'URLSegmentHolder'
});
// edit button
editAction = $('<button />', {
'id': 'Form_EditForm_URLSegment_Edit',
'class': 'ss-ui-button',
'text': 'Edit',
'click': function(e) {
e.preventDefault();
self.edit();
}
});
// update button
updateAction = $('<button />', {
'id': 'Form_EditForm_URLSegment_Update',
'text': 'OK',
'click': function(e) {
e.preventDefault();
self.update();
}
});
// cancel button
cancelAction = $('<a />', {
'id': 'Form_EditForm_URLSegment_Cancel',
'href': '#',
'text': 'cancel',
'click': function(e) {
e.preventDefault();
self.cancel();
}
});
// insert elements
holder.insertAfter('.URLPrefix');
editAction.insertAfter(self);
cancelAction.insertAfter(self);
updateAction.insertAfter(self);
},
/**
* Function: _autoInputWidth
*
* Sets the width of input so it lines up with the other fields
*
*/
_autoInputWidth: function() {
var self = this,
prefix_width,
new_width;
prefix_width = $('.URLPrefix').width();
new_width = ((self.width() + 9) - prefix_width);
self.width(new_width);
}
});
@ -82,12 +245,20 @@
// Constructor: onmatch
onmatch : function() {
var self = this;
var URLSegment = $('.cms-edit-form input[name=URLSegment]');
var LiveURLSegment = $('.cms-edit-form input[name=LiveURLSegment]');
this.bind('change', function(e) {
self.updatePageTitleHeading();
self.updateURLSegment(jQuery('.cms-edit-form input[name=URLSegment]'));
// TODO We should really user-confirm these changes
self.parents('form').find('input[name=MetaTitle], input[name=MenuTitle]').val(self.val());
// Criteria for defining a "new" page
if ( (URLSegment.val().indexOf("new") == 0) && LiveURLSegment.val() == "" ) {
self.updatePageTitleHeading();
self.parents('form').find('input[name=MetaTitle], input[name=MenuTitle]').val(self.val());
// update the URLSegment
URLSegment.update(self);
} else {
return;
}
});
this._super();
@ -100,32 +271,6 @@
*/
updatePageTitleHeading: function() {
$('#page-title-heading').text(this.val());
},
/**
* Function: updateURLSegment
*
* Parameters:
* (DOMElement) field
*/
updateURLSegment: function(field) {
if(!field || !field.length) return;
// TODO The new URL value is determined asynchronously,
// which means we need to come up with an alternative system
// to ask user permission to change it.
// TODO language/logic coupling
var isNew = this.val().indexOf("new") == 0;
var confirmMessage = ss.i18n._t(
'UPDATEURL.CONFIRMSIMPLE',
'Do you want to update the URL from your new page title?'
);
// don't ask for replacement if record is considered 'new' as defined by its title
if(isNew || confirm(confirmMessage)) {
field.val(this.val()).trigger('change');
}
}
});

View File

@ -85,4 +85,35 @@
}
}
}
}
/** ------------------------------------------------------------------
* URLSegment field
* ----------------------------------------------------------------- */
.URLPrefix,
.URLSegmentHolder {
padding-top: 8px;
display: inline-block;
}
.URLPrefix {
color: #777;
}
#Form_EditForm_URLSegment {
margin-left: 7px;
&.disabled {
color: #444;
padding-left: 0px;
margin-left: 0px;
background: none;
border-color: transparent;
}
}
#Form_EditForm_URLSegment_Cancel,
#Form_EditForm_URLSegment_Update,
#Form_EditForm_URLSegment_Edit {
margin-left: 7px;
}

View File

@ -1,4 +1,4 @@
<span>$URLPrefix</span>&nbsp;<input $AttributesHTML />
<span class="URLPrefix">$URLPrefix</span><input $AttributesHTML />
<% if HelpText %>
<p class="help">$HelpText</p>
<% end_if %>