Merge branch 'history-panel'

This commit is contained in:
Ingo Schommer 2011-09-19 21:07:45 +02:00
commit b924fdbde6
29 changed files with 837 additions and 508 deletions

View File

@ -1,12 +0,0 @@
<?php
class CMSPageHistoryController extends CMSMain {
static $url_segment = 'page/history';
static $url_rule = '/$Action/$ID/$OtherID';
static $url_priority = 42;
function getEditForm($id = null, $fields = null) {
return "Not implemented yet";
}
}

View File

@ -1,11 +1,12 @@
<?php
/**
* The main "content" area of the CMS.
*
* This class creates a 2-frame layout - left-tree and right-form - to sit beneath the main
* admin menu.
*
* @package cms
* @subpackage content
* @subpackage controller
* @todo Create some base classes to contain the generic functionality that will be replicated.
*/
class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionProvider {
@ -29,13 +30,11 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
static $allowed_actions = array(
'addpage',
'buildbrokenlinks',
'compareversions',
'deleteitems',
'DeleteItemsForm',
'dialog',
'duplicate',
'duplicatewithchildren',
'getversion',
'publishall',
'publishitems',
'PublishItemsForm',
@ -43,8 +42,6 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
'sidereport',
'SideReportsForm',
'submit',
'versions',
'VersionsForm',
'EditForm',
'AddForm',
'SearchForm',
@ -78,6 +75,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
CMS_DIR . '/javascript/CMSMain.js',
CMS_DIR . '/javascript/CMSMain.EditForm.js',
CMS_DIR . '/javascript/CMSMain.AddForm.js',
CMS_DIR . '/javascript/CMSPageHistoryController.js',
CMS_DIR . '/javascript/SilverStripeNavigator.js'
)
);
@ -172,12 +170,12 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
$fields = new FieldSet(
new TextField('Term', _t('CMSSearch.FILTERLABELTEXT', 'Content')),
$dateGroup = new FieldGroup(
$dateFrom = new DateField('LastEditedFrom', _t('CMSSearch.FilterDateFrom', 'from')),
$dateTo = new DateField('LastEditedTo', _t('CMSSearch.FilterDateFrom', 'to'))
$dateFrom = new DateField('LastEditedFrom', _t('CMSSearch.FILTERDATEFROM', 'From')),
$dateTo = new DateField('LastEditedTo', _t('CMSSearch.FILTERDATETO', 'To'))
),
new DropdownField(
'FilterClass',
_t('CMSMain.SearchTreeFormPagesDropdown', 'Pages'),
_t('CMSMain.PAGES', 'Pages'),
$filterMap
),
new DropdownField(
@ -347,18 +345,23 @@ JS;
return $form->forTemplate();
}
/**
* Get a database record to be managed by the CMS
* Get a database record to be managed by the CMS.
*
* @param int $id Record ID
* @param int $versionID optional Version id of the given record
*/
public function getRecord($id) {
public function getRecord($id, $versionID = null) {
$treeClass = $this->stat('tree_class');
if($id instanceof $treeClass) {
return $id;
} else if($id && is_numeric($id)) {
$version = isset($_REQUEST['Version']) ? $_REQUEST['Version'] : null;
if(is_numeric($version)) {
$record = Versioned::get_version($treeClass, $id, $version);
if(isset($_REQUEST['Version'])) $versionID = (int) $_REQUEST['Version'];
if($versionID) {
$record = Versioned::get_version($treeClass, $id, $versionID);
} else {
$record = DataObject::get_one($treeClass, "\"$treeClass\".\"ID\" = $id");
}
@ -470,7 +473,7 @@ JS;
$readonlyFields = $form->Fields()->makeReadonly();
$form->setFields($readonlyFields);
}
$this->extend('updateEditForm', $form);
return $form;
@ -909,100 +912,6 @@ JS;
}
}
/**
* @return Form
*/
function VersionsForm() {
$pageID = ($this->request->requestVar('ID')) ? $this->request->requestVar('ID') : $this->currentPageID();
$page = $this->getRecord($pageID);
if($page) {
$versions = $page->allVersions(
($this->request->requestVar('ShowUnpublished')) ?
"" : "\"SiteTree\".\"WasPublished\" = 1"
);
// inject link to cms
if($versions) foreach($versions as $k => $version) {
$version->CMSLink = sprintf('%s/%s/%s',
$this->Link('getversion'),
$version->ID,
$version->Version
);
}
$vd = new ViewableData();
$versionsHtml = $vd->customise(
array('Versions'=>$versions)
)->renderWith('CMSMain_versions');
} else {
$versionsHtml = '';
}
$form = new Form(
$this,
'VersionsForm',
new FieldSet(
new CheckboxField(
'ShowUnpublished',
_t('CMSMain_left.ss.SHOWUNPUB','Show unpublished versions')
),
new LiteralField('VersionsHtml', $versionsHtml),
new HiddenField('ID', false, $pageID),
new HiddenField('Locale', false, $this->Locale)
),
new FieldSet(
new FormAction(
'versions',
_t('CMSMain.BTNREFRESH','Refresh')
),
new FormAction(
'compareversions',
_t('CMSMain.BTNCOMPAREVERSIONS','Compare Versions')
)
)
);
$form->loadDataFrom($this->request->requestVars());
$form->setFormMethod('GET');
$form->unsetValidator();
return $form;
}
/**
* Get the versions of the current page
*/
function versions() {
$form = $this->VersionsForm();
return (Director::is_ajax()) ? $form->forTemplate() : $form;
}
/**
* Roll a page back to a previous version
*/
function rollback($data, $form) {
$this->extend('onBeforeRollback', $data['ID']);
if(isset($data['Version']) && (bool)$data['Version']) {
$record = $this->performRollback($data['ID'], $data['Version']);
$message = sprintf(
_t('CMSMain.ROLLEDBACKVERSION',"Rolled back to version #%d. New version number is #%d"),
$data['Version'],
$record->Version
);
} else {
$record = $this->performRollback($data['ID'], "Live");
$message = sprintf(
_t('CMSMain.ROLLEDBACKPUB',"Rolled back to published version. New version number is #%d"),
$record->Version
);
}
$this->response->addHeader('X-Status', $message);
$form = $this->getEditForm($record->ID);
return $form->forTemplate();
}
function publish($data, $form) {
$data['publish'] = '1';
@ -1028,187 +937,6 @@ JS;
return $form->forTemplate();
}
function performRollback($id, $version) {
$record = DataObject::get_by_id($this->stat('tree_class'), $id);
if($record && !$record->canEdit()) return Security::permissionFailure($this);
$record->doRollbackTo($version);
return $record;
}
/**
* Supports both direct URL links (format: admin/getversion/<page-id>/<version>),
* and through GET parameters: admin/getversion/?ID=<page-id>&Versions[]=<version>
*/
function getversion() {
$id = ($this->request->param('ID')) ?
$this->request->param('ID') : $this->request->requestVar('ID');
$version = ($this->request->param('OtherID')) ?
$this->request->param('OtherID') : $this->request->requestVar('Versions');
$record = Versioned::get_version("SiteTree", $id, $version);
if($record) {
if($record && !$record->canView()) return Security::permissionFailure($this);
$fields = $record->getCMSFields($this);
$fields->removeByName("Status");
$fields->push(new HiddenField("ID"));
$fields->push(new HiddenField("Version"));
$versionAuthor = DataObject::get_by_id('Member', $record->AuthorID);
if(!$versionAuthor) $versionAuthor = new ArrayData(array('Title' => 'Unknown author'));
$fields->insertBefore(
new LiteralField(
'YouAreViewingHeader',
'<p class="message notice">' .
sprintf(
_t(
'CMSMain.VIEWING',
"You are viewing version #%s, created %s by %s",
PR_MEDIUM,
'Version number is a linked string, created is a relative time (e.g. 2 days ago), by a specific author'
),
"<a href=\"admin/getversion/$record->ID/$version\" title=\"" . ($versionAuthor ? $versionAuthor->Title : '') . "\">$version</a>",
$record->obj('LastEdited')->Ago(),
($versionAuthor ? $versionAuthor->Title : '')
) .
'</p>'
),
'Root'
);
$actions = $record->getCMSActions();
// encode the message to appear in the body of the email
$archiveURL = Director::absoluteBaseURL() . $record->URLSegment . '?archiveDate=' . $record->obj('LastEdited')->URLDatetime();
// Ensure that source file comments are disabled
SSViewer::set_source_file_comments(false);
$archiveEmailMessage = urlencode( $this->customise( array( 'ArchiveDate' => $record->obj('LastEdited'), 'ArchiveURL' => $archiveURL ) )->renderWith( 'ViewArchivedEmail' ) );
$archiveEmailMessage = preg_replace( '/\+/', '%20', $archiveEmailMessage );
$fields->push( new HiddenField( 'ArchiveEmailMessage', '', $archiveEmailMessage ) );
$fields->push( new HiddenField( 'ArchiveEmailSubject', '', preg_replace( '/\+/', '%20', urlencode( 'Archived version of ' . $record->Title ) ) ) );
$fields->push( new HiddenField( 'ArchiveURL', '', $archiveURL ) );
$form = new Form($this, "EditForm", $fields, $actions);
$form->loadDataFrom($record);
$form->loadDataFrom(array(
"ID" => $id,
"Version" => $version,
));
// historical version shouldn't be editable
$readonlyFields = $form->Fields()->makeReadonly();
$form->setFields($readonlyFields);
$templateData = $this->customise(array(
"EditForm" => $form
));
SSViewer::setOption('rewriteHashlinks', false);
if(Director::is_ajax()) {
$result = $templateData->renderWith(array($this->class . '_right', 'LeftAndMain_right'));
$parts = split('</?form[^>]*>', $result);
$content = $parts[sizeof($parts)-2];
if($this->ShowSwitchView()) {
$content .= '<div id="AjaxSwitchView">' . $this->SwitchView($record) . '</div>';
}
return $content;
} else {
return $templateData->renderWith('LeftAndMain');
}
}
}
function compareversions() {
$id = ($this->request->param('ID')) ?
$this->request->param('ID') : $this->request->requestVar('ID');
$versions = $this->request->requestVar('Versions');
$version1 = ($versions && isset($versions[0])) ?
$versions[0] : $this->request->getVar('From');
$version2 = ($versions && isset($versions[1])) ?
$versions[1] : $this->request->getVar('To');
if( $version1 > $version2 ) {
$toVersion = $version1;
$fromVersion = $version2;
} else {
$toVersion = $version2;
$fromVersion = $version1;
}
if(!$toVersion || !$toVersion) return false;
$page = DataObject::get_by_id("SiteTree", $id);
if($page && !$page->canView()) return Security::permissionFailure($this);
$record = $page->compareVersions($fromVersion, $toVersion);
$fromVersionRecord = Versioned::get_version('SiteTree', $id, $fromVersion);
$toVersionRecord = Versioned::get_version('SiteTree', $id, $toVersion);
if(!$fromVersionRecord) user_error("Can't find version $fromVersion of page $id", E_USER_ERROR);
if(!$toVersionRecord) user_error("Can't find version $toVersion of page $id", E_USER_ERROR);
if($record) {
$fromDateNice = $fromVersionRecord->obj('LastEdited')->Ago();
$toDateNice = $toVersionRecord->obj('LastEdited')->Ago();
$fromAuthor = DataObject::get_by_id('Member', $fromVersionRecord->AuthorID);
if(!$fromAuthor) $fromAuthor = new ArrayData(array('Title' => 'Unknown author'));
$toAuthor = DataObject::get_by_id('Member', $toVersionRecord->AuthorID);
if(!$toAuthor) $toAuthor = new ArrayData(array('Title' => 'Unknown author'));
$fields = $record->getCMSFields($this);
$fields->push(new HiddenField("ID"));
$fields->push(new HiddenField("Version"));
$fields->insertBefore(
new LiteralField(
'YouAreComparingHeader',
'<p class="message notice">' .
sprintf(
_t('CMSMain.COMPARINGV',"Comparing versions %s and %s"),
"<a href=\"admin/getversion/$id/$fromVersionRecord->Version\" title=\"$fromAuthor->Title\">$fromVersionRecord->Version</a> <small>($fromDateNice)</small>",
"<a href=\"admin/getversion/$id/$toVersionRecord->Version\" title=\"$toAuthor->Title\">$toVersionRecord->Version</a> <small>($toDateNice)</small>"
) .
'</p>'
),
"Root"
);
$actions = new FieldSet();
$form = new Form($this, "EditForm", $fields, $actions);
$form->loadDataFrom($record);
$form->loadDataFrom(array(
"ID" => $id,
"Version" => $fromVersion,
));
$form->addExtraClass('compare');
// comparison views shouldn't be editable
$readonlyFields = $form->Fields()->makeReadonly();
$form->setFields($readonlyFields);
foreach($form->Fields()->dataFields() as $field) {
$field->dontEscape = true;
}
if($this->isAjax()) {
return $form->forTemplate();
} else {
$templateData = $this->customise(array(
"EditForm" => $form
));
return $templateData->renderWith('LeftAndMain');
}
}
}
function sendFormToBrowser($templateData) {
if(Director::is_ajax()) {
SSViewer::setOption('rewriteHashlinks', false);

View File

@ -0,0 +1,418 @@
<?php
/**
* @package cms
* @subpackage controllers
*/
class CMSPageHistoryController extends CMSMain {
static $url_segment = 'page/history';
static $url_rule = '/$Action/$ID/$VersionID/$OtherVersionID';
static $url_priority = 42;
static $menu_title = 'History';
static $allowed_actions = array(
'VersionsForm',
'compare'
);
public static $url_handlers = array(
'$Action/$ID/$VersionID/$OtherVersionID' => 'handleAction'
);
/**
* @return array
*/
function show($request) {
$form = $this->ShowVersionForm(
$request->param('VersionID')
);
if($this->isAjax()) {
$content = $form->forTemplate();
} else {
$content = $this->customise(array('EditForm' => $form))->renderWith($this->getViewer('show'));
}
return $content;
}
/**
* @return array
*/
function compare($request) {
$form = $this->CompareVersionsForm(
$request->param('VersionID'),
$request->param('OtherVersionID')
);
if($this->isAjax()) {
$content = $form->forTemplate();
} else {
$content = $this->customise(array('EditForm' => $form))->renderWith($this->getViewer('show'));
}
return $content;
}
/**
* @return array
*/
function rollback() {
return $this->doRollback(array(
'ID' => $this->currentPageID(),
'Version' => $this->request->param('VersionID')
), null);
}
/**
* Returns the read only version of the edit form. Detaches all {@link FormAction}
* instances attached since only action relates to revert.
*
* Permission checking is done at the {@link CMSMain::getEditForm()} level.
*
* @param int $id ID of the record to show
* @param array $fields optional
* @param int $versionID
* @param int $compare Compare mode
*
* @return Form
*/
function getEditForm($id = null, $fields = null, $versionID = null, $compareID = null) {
if(!$id) $id = $this->currentPageID();
$record = $this->getRecord($id, $versionID);
$versionID = ($record) ? $record->Version : $versionID;
$form = parent::getEditForm($record, ($record) ? $record->getCMSFields() : null);
$form->setActions(new FieldSet(
$revert = new FormAction('doRollback', _t('CMSPageHistoryController.REVERTTOTHISVERSION', 'Revert to this version'))
));
$fields = $form->Fields();
$fields->removeByName("Status");
$fields->push(new HiddenField("ID"));
$fields->push(new HiddenField("Version"));
$fields = $fields->makeReadonly();
foreach($fields->dataFields() as $field) {
$field->dontEscape = true;
$field->reserveNL = true;
}
if($compareID) {
$link = Controller::join_links(
$this->Link('show'),
$id
);
$view = _t('CMSPageHistoryController.VIEW',"view");
$message = sprintf(
_t('CMSPageHistoryController.COMPARINGVERSION',"Comparing versions %s and %s."),
sprintf('%s (<a href="%s">%s</a>)', $versionID, Controller::join_links($link, $versionID), $view),
sprintf('%s (<a href="%s">%s</a>)', $compareID, Controller::join_links($link, $compareID), $view)
);
$revert->setReadonly(true);
}
else {
$message = sprintf(
_t('CMSPageHistoryController.VIEWINGVERSION',"Currently viewing version %s."), $versionID
);
}
$fields->addFieldToTab('Root.Main',
new LiteralField('CurrentlyViewingMessage', $this->customise(array(
'Content' => $message,
'Classes' => 'notice'
))->renderWith(array('CMSMain_notice'))),
"Title"
);
$form->setFields($fields->makeReadonly());
$form->loadDataFrom(array(
"ID" => $id,
"Version" => $versionID,
));
if(($record && $record->isLatestVersion())) {
$revert->setReadonly(true);
}
$form->removeExtraClass('cms-content');
return $form;
}
/**
* Version select form. Main interface between selecting versions to view
* and comparing multiple versions.
*
* Because we can reload the page directly to a compare view (history/compare/1/2/3)
* this form has to adapt to those parameters as well.
*
* @return Form
*/
function VersionsForm() {
$id = $this->currentPageID();
$page = $this->getRecord($id);
$versionsHtml = '';
$action = $this->request->param('Action');
$versionID = $this->request->param('VersionID');
$otherVersionID = $this->request->param('OtherVersionID');
$showUnpublishedChecked = 0;
$compareModeChecked = ($action == "compare");
if($page) {
$versions = $page->allVersions();
$versionID = (!$versionID) ? $page->Version : $versionID;
if($versions) {
foreach($versions as $k => $version) {
$active = false;
if($version->Version == $versionID || $version->Version == $otherVersionID) {
$active = true;
if(!$version->WasPublished) $showUnpublishedChecked = 1;
}
$version->Active = ($active);
}
}
$vd = new ViewableData();
$versionsHtml = $vd->customise(array(
'Versions' => $versions
))->renderWith('CMSPageHistoryController_versions');
}
$form = new Form(
$this,
'VersionsForm',
new FieldSet(
new CheckboxField(
'ShowUnpublished',
_t('CMSPageHistoryController.SHOWUNPUBLISHED','Show unpublished versions'),
$showUnpublishedChecked
),
new CheckboxField(
'CompareMode',
_t('CMSPageHistoryController.COMPAREMODE', 'Compare mode'),
$compareModeChecked
),
new LiteralField('VersionsHtml', $versionsHtml),
$hiddenID = new HiddenField('ID', false, "")
),
new FieldSet(
new FormAction(
'doCompare', _t('CMSPageHistoryController.COMPAREVERSIONS','Compare Versions')
),
new FormAction(
'doShowVersion', _t('CMSPageHistoryController.SHOWVERSION','Show Version')
)
)
);
$form->loadDataFrom($this->request->requestVars());
$hiddenID->setValue($id);
$form->unsetValidator();
return $form;
}
/**
* Process the {@link VersionsForm} compare function between two pages.
*
* @param array
* @param Form
*
* @return html
*/
function doCompare($data, $form) {
$versions = $data['Versions'];
if(count($versions) < 2) return null;
$id = $this->currentPageID();
$version1 = array_shift($versions);
$version2 = array_shift($versions);
$form = $this->CompareVersionsForm($version1, $version2);
// javascript solution, render into template
if($this->isAjax()) {
return $this->customise(array(
"EditForm" => $form
))->renderWith(array(
$this->class . '_EditForm',
'LeftAndMain_Content'
));
}
// non javascript, redirect the user to the page
$this->redirect(Controller::join_links(
$this->Link('compare'),
$version1,
$version2
));
}
/**
* Process the {@link VersionsForm} show version function. Only requires
* one page to be selected.
*
* @param array
* @param Form
*
* @return html
*/
function doShowVersion($data, $form) {
$versionID = null;
if(isset($data['Versions']) && is_array($data['Versions'])) {
$versionID = array_shift($data['Versions']);
}
if(!$versionID) return;
if($this->isAjax()) {
return $this->customise(array(
"EditForm" => $this->ShowVersionForm($versionID)
))->renderWith(array(
$this->class . '_EditForm',
'LeftAndMain_Content'
));
}
// non javascript, redirect the user to the page
$this->redirect(Controller::join_links(
$this->Link('version'),
$versionID
));
}
/**
* Rolls a site back to a given version ID
*
* @param array
* @param Form
*
* @return html
*/
function doRollback($data, $form) {
$this->extend('onBeforeRollback', $data['ID']);
$id = (isset($data['ID'])) ? (int) $data['ID'] : null;
$version = (isset($data['Version'])) ? (int) $data['Version'] : null;
if(isset($data['Version']) && (bool)$data['Version']) {
$record = $this->performRollback($data['ID'], $data['Version']);
$message = sprintf(
_t('CMSMain.ROLLEDBACKVERSION',"Rolled back to version #%d. New version number is #%d"),
$data['Version'],
$record->Version
);
} else {
$record = $this->performRollback($data['ID'], "Live");
$message = sprintf(
_t('CMSMain.ROLLEDBACKPUB',"Rolled back to published version. New version number is #%d"),
$record->Version
);
}
if($this->isAjax()) {
$this->response->addHeader('X-Status', $message);
$form = $this->getEditForm($record->ID);
return $form->forTemplate();
}
return array(
'EditForm' => $this->customise(array(
'Message' => $message,
'Status' => 'success'
))->renderWith('CMSMain_notice')
);
}
/**
* Performs a rollback of the a given
*
* @param int $id record ID
* @param int $version version ID to rollback to
*/
function performRollback($id, $version) {
$record = DataObject::get_by_id($this->stat('tree_class'), $id);
if($record && !$record->canEdit()) return Security::permissionFailure($this);
$record->doRollbackTo($version);
return $record;
}
/**
* @return Form
*/
function ShowVersionForm($versionID = null) {
if(!$versionID) return null;
$id = $this->currentPageID();
$form = $this->getEditForm($id, null, $versionID);
return $form;
}
/**
* @return Form
*/
function CompareVersionsForm($versionID, $otherVersionID) {
if($versionID > $otherVersionID) {
$toVersion = $versionID;
$fromVersion = $otherVersionID;
} else {
$toVersion = $otherVersionID;
$fromVersion = $versionID;
}
if(!$toVersion || !$toVersion) return false;
$id = $this->currentPageID();
$page = DataObject::get_by_id("SiteTree", $id);
if($page && !$page->canView()) {
return Security::permissionFailure($this);
}
$record = $page->compareVersions($fromVersion, $toVersion);
$fromVersionRecord = Versioned::get_version('SiteTree', $id, $fromVersion);
$toVersionRecord = Versioned::get_version('SiteTree', $id, $toVersion);
if(!$fromVersionRecord) {
user_error("Can't find version $fromVersion of page $id", E_USER_ERROR);
}
if(!$toVersionRecord) {
user_error("Can't find version $toVersion of page $id", E_USER_ERROR);
}
if($record) {
$form = $this->getEditForm($id, null, null, true);
$form->setActions(new FieldSet());
$form->loadDataFrom($record);
$form->loadDataFrom(array(
"ID" => $id,
"Version" => $fromVersion,
));
$form->addExtraClass('compare');
return $form;
}
}
}

View File

@ -1,13 +1,19 @@
<?php
/**
* @package cms
*/
class CMSPageSettingsController extends CMSMain {
static $url_segment = 'page/settings';
static $url_rule = '/$Action/$ID/$OtherID';
static $url_priority = 42;
function getEditForm($id = null, $fields = null) {
$record = $this->getRecord($id ? $id : $this->currentPageID());
return parent::getEditForm($record, ($record) ? $record->getSettingsFields() : null);
}
}

View File

@ -1,4 +1,8 @@
<?php
/**
* @package cms
*/
class CMSPagesController extends CMSMain {
static $url_segment = 'pages';
@ -16,18 +20,18 @@ class CMSPagesController extends CMSMain {
if($request->param('ID')) {
$c = new CMSPageEditController();
return $this->redirect(Controller::join_links($c->Link('show'), $request->param('ID')));
} else {
return parent::show($request);
}
return parent::show($request);
}
function Link($action = null) {
// Special case: All show links should redirect to the page edit interface instead (mostly from tree nodes)
if(preg_match('/^show/', $action)) {
return singleton('CMSPageEditController')->Link($action);
} else {
return parent::Link($action);
}
return parent::Link($action);
}
}

View File

@ -0,0 +1,5 @@
/** Style custom to the CMSMain admin interface. CMSMain extends the built in sapphire 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. ----------------------------------------------------------------- */
#cms-page-history-versions tr.loading { color: #999; }
#cms-page-history-versions tr.loading td:hover { cursor: none; }
#cms-page-history-versions td:hover { cursor: pointer; }

View File

@ -194,32 +194,6 @@
}
});
/**
* Class: .cms-edit-form .Actions #Form_EditForm_action_email
*
* Email containing the link to the archived version of the page.
* Visible on readonly older versions of a specific page at the moment.
*/
$('.cms-edit-form .Actions #Form_EditForm_action_email').entwine({
/**
* Function: onclick
*
* Parameters:
* (Event) e
*/
onclick: function(e) {
window.open(
'mailto:?subject='
+ $('input[name=ArchiveEmailSubject]', this[0].form).val()
+ '&body='
+ $(':input[name=ArchiveEmailMessage]', this[0].form).val(),
'archiveemail'
);
return false;
}
});
/**
* Class: .cms-edit-form .Actions #Form_EditForm_action_print
*

View File

@ -213,133 +213,5 @@
return false;
}
});
/**
* Class: #Form_VersionsForm
*
* Simple form showing versions of a specific page.
*/
$('#Form_VersionsForm').entwine({
onmatch: function() {
var self = this;
// set button to be available in form submit event later on
this.find(':submit').bind('click', function(e) {
self.data('_clickedButton', this);
});
this.bind('submit', function(e) {
return self._submit();
});
// integrate with sitetree selection changes
jQuery('.cms-tree').bind('select_node.jstree', function(e, data) {
var node = data.rslt.obj;
self.find(':input[name=ID]').val(node ? $(node).data('id') : null);
if(self.is(':visible')) self.trigger('submit');
});
// refresh when field is selected
// TODO coupling
$('#treepanes').bind('accordionchange', function(e, ui) {
if($(ui.newContent).attr('id') == 'Form_VersionsForm') self.trigger('submit');
});
// submit when 'show unpublished versions' checkbox is changed
this.find(':input[name=ShowUnpublished]').bind('change', function(e) {
// force the refresh button, not 'compare versions'
self.data('_clickedButton', self.find(':submit[name=action_versions]'));
self.trigger('submit');
});
// move submit button to the top
// this.find('#ReportClass').after(this.find('.Actions'));
// links in results
this.find('td').bind('click', function(e) {
var td = $(this);
// exclude checkboxes
if($(e.target).is(':input')) return true;
var link = $(this).siblings('.versionlink').find('a').attr('href');
td.addClass('loading');
jQuery('.cms-content').entwine('ss').loadForm(
link,
null,
function(e) {
td.removeClass('loading');
}
);
return false;
});
// compare versions action
this.find(':submit[name=action_compareversions]').bind('click', function(e) {
// validation: only allow selection of exactly two versions
var versions = self.find(':input[name=Versions[]]:checked');
if(versions.length != 2) {
alert(ss.i18n._t(
'CMSMain.VALIDATIONTWOVERSION',
'Please select two versions'
));
return false;
}
// overloaded submission: refresh the right form instead
self.data('_clickedButton', this);
self._submit(true);
return false;
});
this._super();
},
/**
* Function: _submit
*
* Parameters:
* (bool) loadEditForm - Determines if responses should show in current panel,
* or in the edit form (in the case of 'compare versions').
*/
_submit: function(loadEditForm) {
var self = this;
// Don't submit with empty ID
if(!this.find(':input[name=ID]').val()) return false;
var $button = (self.data('_clickedButton')) ? $(self.data('_clickedButton')) : this.find(':submit:first');
$button.addClass('loading');
var data = this.serializeArray();
data.push({name:$button.attr('name'), value: $button.val()});
if(loadEditForm) {
jQuery('.cms-content').entwine('ss').loadForm(
this.attr('action'),
null,
function(e) {
$button.removeClass('loading');
},
{data: data, type: 'POST'}
);
} else {
jQuery.ajax({
url: this.attr('action'),
data: data,
dataType: 'html',
success: function(data, status) {
self.replaceWith(data);
},
complete: function(xmlhttp, status) {
$button.removeClass('loading');
}
});
}
return false;
}
});
});
})(jQuery);

View File

@ -0,0 +1,155 @@
(function($) {
/**
* File: CMSPageHistoryController.js
*
* Handles related interactions between the version selection form on the
* left hand side of the panel and the version displaying on the right
* hand side.
*/
$.entwine('ss', function($){
/**
* Class: #Form_VersionsForm
*
* The left hand side version selection form is the main interface for
* users to select a version to view, or to compare two versions
*/
$('#Form_VersionsForm').entwine({
/**
* Constructor
*/
onmatch: function() {
var self = this;
/**
* Event: :input[name=ShowUnpublished] change
*
* Changing the show unpublished checkbox toggles whether to show
* or hide the unpublished versions. Because those rows may be being
* compared this also ensures those rows are unselected.
*/
this.find(':input[name=ShowUnpublished]').bind('change', function(e) {
if($(this).attr("checked")) {
self.find("tr[data-published=false]").show();
}
else {
self.find("tr[data-published=false]").hide()._unselect();
}
});
this._super();
},
/**
* Function: submit.
*
* Submits either the compare versions form or the view single form
* display based on whether we have two or 1 option selected
*
* Todo:
* Handle coupling to admin url
*/
onsubmit: function(e, d) {
var id, self = this;
id = this.find(':input[name=ID]').val();
if(!id) return false;
var button, url, selected, to, from, compare, data;
compare = (this.find(":input[name=CompareMode]").is(":checked"));
selected = this.find("table input[type=checkbox]").filter(":checked");
if(compare) {
if(selected.length != 2) return false;
to = selected.eq(0).val();
from = selected.eq(1).val();
button = this.find(':submit[name=action_doCompare]');
url = 'admin/page/history/compare/'+ [id,from,to].join('/') +"/";
}
else {
to = selected.eq(0).val();
button = this.find(':submit[name=action_doShowVersion]');
url = 'admin/page/history/show/'+ [id,to].join('/') + "/";
}
window.History.pushState({selector: '.cms-edit-form'}, '', url);
return false;
}
});
/**
* Class: #Form_VersionsForm tr
*
* An individual row in the versions form. Selecting the row updates
* the edit form depending on whether we're showing individual version
* information or displaying comparsion.
*/
$("#Form_VersionsForm tbody tr").entwine({
/**
* Function: onclick
*
* Selects or deselects the row (if in compare mode). Will trigger
* an update of the edit form if either selected (in single mode)
* or if this is the second row selected (in compare mode)
*/
onclick: function(e) {
var compare, selected;
// compare mode
compare = this.parents("form").find(':input[name=CompareMode]').attr("checked"),
selected = this.siblings(".active");
if(compare && this.hasClass('active')) {
this._unselect();
return;
}
else if(compare) {
// check if we have already selected more than two.
if(selected.length > 1) {
return alert(ss.i18n._t('ONLYSELECTTWO', 'Can only compare two versions at at time.'));
}
this._select();
// if this is the second selected then we can compare.
if(selected.length == 1) {
this.parents('form').submit();
}
return;
}
else {
this._select();
selected._unselect();
this.parents("form").submit();
}
},
/**
* Function: _unselect()
*
* Unselects the row from the form selection.
*/
_unselect: function() {
this.removeClass('active');
this.find(":input[type=checkbox]").attr("checked", false);
},
/**
* Function: _select()
*
* Selects the currently matched row in the form selection
*/
_select: function() {
this.addClass('active');
this.find(":input[type=checkbox]").attr("checked", true);
}
})
});
})(jQuery);

View File

@ -0,0 +1,31 @@
/**
* Style custom to the CMSMain admin interface. CMSMain extends the built in
* sapphire 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.
* ----------------------------------------------------------------- */
#cms-page-history-versions {
tr {
&.loading {
color: #999;
td {
&:hover {
cursor: none;
}
}
}
}
td {
&:hover {
cursor: pointer;
}
}
}

View File

@ -0,0 +1,3 @@
<div class="message $Classes">
<p>$Content</p>
</div>

View File

@ -1,40 +0,0 @@
<table id="Versions">
<thead>
<tr>
<td class="checkbox"></td>
<td>#</td>
<td><% _t('WHEN','When') %></td>
<td><% _t('AUTHOR','User') %></td>
<td><% _t('PUBR','Publisher') %></td>
</tr>
</thead>
<tbody>
<% control Versions %>
<tr id="page-$RecordID-version-$Version" class="$EvenOdd $PublishedClass">
<td class="checkbox">
<input type="checkbox" name="Versions[]" id="Versions_$Version" value="$Version" />
</td>
<td class="versionlink">
<a href="$CMSLink">$Version</a>
</td>
<td class="$LastEdited" title="$LastEdited.Ago - $LastEdited.Nice">
$LastEdited.Ago
</td>
<td>
$Author.FirstName $Author.Surname.Initial
</td>
<td>
<% if Published %>
<% if Publisher %>
$Publisher.FirstName $Publisher.Surname.Initial
<% else %>
<% _t('UNKNOWN','Unknown') %>
<% end_if %>
<% else %>
<% _t('NOTPUB','Not published') %>
<% end_if %>
</td>
</tr>
<% end_control %>
</tbody>
</table>

View File

@ -0,0 +1,23 @@
<table id="cms-page-history-versions">
<thead>
<tr>
<th class="ui-helper-hidden"></th>
<th><% _t('WHEN','When') %></th>
<th><% _t('AUTHOR','Author') %></th>
<th><% _t('PUBLISHER','Publisher') %></th>
</tr>
</thead>
<tbody>
<% control Versions %>
<tr id="page-$RecordID-version-$Version" class="$EvenOdd $PublishedClass<% if not WasPublished %><% if not Active %> ui-helper-hidden<% end_if %><% end_if %><% if Active %> active<% end_if %>" data-published="<% if WasPublished %>true<% else %>false<% end_if %>">
<td class="ui-helper-hidden"><input type="checkbox" name="Versions[]" id="cms-version-{$Version}" value="$Version"<% if Active %> checked="checked"<% end_if %> /></td>
<% control LastEdited %>
<td class="last-edited first-column" title="$Ago - $Nice">$Nice</td>
<% end_control %>
<td><% if Author %>$Author.FirstName $Author.Surname.Initial<% else %><% _t('UNKNOWN','Unknown') %><% end_if %></td>
<td class="last-column"><% if Published %><% if Publisher %>$Publisher.FirstName $Publisher.Surname.Initial<% else %><% _t('UNKNOWN','Unknown') %><% end_if %><% else %><% _t('NOTPUBLISHED','Not published') %><% end_if %></td>
</tr>
<% end_control %>
</tbody>
</table>

View File

@ -1,3 +1,15 @@
<div class="cms-content center $BaseCSSClasses">
<i>Not implemented yet</i>
<div class="cms-content center $BaseCSSClasses" data-layout="{type: 'border'}">
<div class="cms-content-tools west cms-panel" data-expandOnClick="true">
<div class="cms-content-header north">
<div>
<h2><% _t('CMSPageHistoryController.History','History') %></h2>
</div>
</div>
<div class="cms-panel-content cms-helper-hide-actions">
$VersionsForm
</div>
</div>
$EditForm
</div>

View File

@ -1,3 +0,0 @@
<div class="cms-content center $BaseCSSClasses">
<i>Not implemented yet</i>
</div>

View File

@ -22,13 +22,11 @@
<div class="cms-content-tools west cms-panel" data-expandOnClick="true">
<h3 class="cms-panel-header">Filter</h3>
<h3 class="cms-panel-header"><% _t('FILTER', 'Filter') %></h3>
<div class="cms-panel-content">
$SearchForm
</div>
</div>
<div class="cms-content-fields center ui-widget-content">

View File

@ -0,0 +1,145 @@
<?php
/**
* @package cms
* @subpackage tests
*/
class CMSPageHistoryControllerTest extends FunctionalTest {
static $fixture_file = 'CMSPageHistoryControllerTest.yml';
private $versionUnpublishedCheck, $versionPublishCheck, $versionUnpublishedCheck2;
private $page;
function setUp() {
parent::setUp();
$this->loginWithPermission('ADMIN');
// creates a series of published, unpublished versions of a page
$this->page = new Page();
$this->page->URLSegment = "test";
$this->page->Content = "new content";
$this->page->write();
$this->versionUnpublishedCheck = $this->page->Version;
$this->page->Content = "some further content";
$this->page->write();
$this->page->publish('Stage', 'Live');
$this->versionPublishCheck = $this->page->Version;
$this->page->Content = "No, more changes please";
$this->page->Title = "Changing titles too";
$this->page->write();
$this->versionUnpublishedCheck2 = $this->page->Version;
$this->page->Title = "Final Change";
$this->page->write();
$this->page->publish('Stage', 'Live');
$this->versionPublishCheck2 = $this->page->Version;
}
function testGetEditForm() {
$controller = new CMSPageHistoryController();
// should get the latest version which we cannot rollback to
$form = $controller->getEditForm($this->page->ID);
$this->assertTrue($form->Actions()->dataFieldByName('action_doRollback')->isReadonly());
$this->assertEquals($this->page->ID, $form->dataFieldByName('ID')->Value());
$this->assertEquals($this->versionPublishCheck2, $form->dataFieldByName('Version')->Value());
$this->assertContains(
sprintf("Currently viewing version %s.", $this->versionPublishCheck2),
$form->Fields()->fieldByName('Root.Main.CurrentlyViewingMessage')->getContent()
);
// edit form with a given version
$form = $controller->getEditForm($this->page->ID, null, $this->versionPublishCheck);
$this->assertFalse($form->Actions()->dataFieldByName('action_doRollback')->isReadonly());
$this->assertEquals($this->page->ID, $form->dataFieldByName('ID')->Value());
$this->assertEquals($this->versionPublishCheck, $form->dataFieldByName('Version')->Value());
$this->assertContains(
sprintf("Currently viewing version %s.", $this->versionPublishCheck),
$form->Fields()->fieldByName('Root.Main.CurrentlyViewingMessage')->getContent()
);
// check that compare mode updates the message
$form = $controller->getEditForm($this->page->ID, null, $this->versionPublishCheck, $this->versionPublishCheck2);
$this->assertContains(
sprintf("Comparing versions %s", $this->versionPublishCheck),
$form->Fields()->fieldByName('Root.Main.CurrentlyViewingMessage')->getContent()
);
$this->assertContains(
sprintf("and %s", $this->versionPublishCheck2),
$form->Fields()->fieldByName('Root.Main.CurrentlyViewingMessage')->getContent()
);
}
/**
* @todo should be less tied to cms theme.
* @todo check highlighting for comparing pages.
*/
function testVersionsForm() {
$history = $this->get('admin/page/history/show/'. $this->page->ID);
$form = $this->cssParser()->getBySelector("#Form_VersionsForm");
$this->assertEquals(1, count($form));
// check the page ID is present
$hidden = $form[0]->xpath("fieldset/input[@type='hidden']");
$this->assertThat($hidden, $this->logicalNot($this->isNull()), 'Hidden ID field exists');
$this->assertEquals(4, (int) $hidden[0]->attributes()->value);
// ensure that all the versions are present in the table and displayed
$rows = $form[0]->xpath("fieldset/table/tbody/tr");
$this->assertEquals(4, count($rows));
}
function testVersionsFormTableContainsInformation() {
$history = $this->get('admin/page/history/show/'. $this->page->ID);
$form = $this->cssParser()->getBySelector("#Form_VersionsForm");
$rows = $form[0]->xpath("fieldset/table/tbody/tr");
$expected = array(
array('version' => $this->versionPublishCheck2, 'status' => 'published'),
array('version' => $this->versionUnpublishedCheck2, 'status' => 'internal'),
array('version' => $this->versionPublishCheck, 'status' => 'published'),
array('version' => $this->versionUnpublishedCheck, 'status' => 'internal')
);
// goes the reverse order that we created in setUp()
$i = 0;
foreach($rows as $tr) {
// data-link must be present for the javascript to load new
$this->assertContains($expected[$i]['status'], (string) $tr->attributes()->class);
$i++;
}
// test highlighting
$this->assertContains('active', (string) $rows[0]->attributes()->class);
$this->assertThat((string) $rows[1]->attributes()->class, $this->logicalNot($this->stringContains('active')));
}
function testVersionsFormSelectsUnpublishedCheckbox() {
$history = $this->get('admin/page/history/show/'. $this->page->ID);
$checkbox = $this->cssParser()->getBySelector("#Form_VersionsForm #ShowUnpublished input");
$this->assertThat($checkbox[0], $this->logicalNot($this->isNull()));
$checked = $checkbox[0]->attributes()->checked;
$this->assertThat($checked, $this->logicalNot($this->stringContains('checked')));
// viewing an unpublished
$history = $this->get('admin/page/history/show/'.$this->page->ID .'/'.$this->versionUnpublishedCheck);
$checkbox = $this->cssParser()->getBySelector("#Form_VersionsForm #ShowUnpublished input");
$this->assertThat($checkbox[0], $this->logicalNot($this->isNull()));
$this->assertEquals('checked', (string) $checkbox[0]->attributes()->checked);
}
}

View File

@ -0,0 +1,10 @@
Page:
page1:
Title: Page 1
Sort: 1
page2:
Title: Page 2
Sort: 2
page3:
Title: Page 3
Sort: 3