API Deprecate delete in favour of archive

Remove "delete from live" duplicate action in favour of existing "unpublish" which is more consistent with current terminology
Add pop-up verification to destructive actions
Fix bug in reporting publishing of error pages
Restoring a page also restores parents
This commit is contained in:
Damian Mooyman 2015-05-15 11:50:23 +12:00
parent 747e87baeb
commit a72bd16f42
12 changed files with 491 additions and 261 deletions

View File

@ -43,8 +43,14 @@ abstract class CMSBatchAction extends Object {
$errors = 0;
foreach($status as $k => $v) {
if ($k == 'errors') $errors = count($v);
else $count += count($v);
switch($k) {
case 'error':
$errors += count($v);
break;
case 'success':
$count += count($v);
break;
}
}
$response = Controller::curr()->getResponse();
@ -78,21 +84,26 @@ abstract class CMSBatchAction extends Object {
* }
*/
public function batchaction(SS_List $objs, $helperMethod, $successMessage, $arguments = array()) {
$status = array('modified' => array(), 'error' => array());
$status = array('modified' => array(), 'error' => array(), 'deleted' => array(), 'success' => array());
foreach($objs as $obj) {
// Perform the action
$id = $obj->ID;
if (!call_user_func_array(array($obj, $helperMethod), $arguments)) {
$status['error'][$obj->ID] = '';
$status['error'][$id] = $id;
} else {
$status['success'][$id] = $id;
}
// Now make sure the tree title is appropriately updated
$publishedRecord = DataObject::get_by_id($this->managedClass, $obj->ID);
$publishedRecord = DataObject::get_by_id($this->managedClass, $id);
if ($publishedRecord) {
$status['modified'][$publishedRecord->ID] = array(
$status['modified'][$id] = array(
'TreeTitle' => $publishedRecord->TreeTitle,
);
} else {
$status['deleted'][$id] = $id;
}
$obj->destroy();
unset($obj);
@ -153,4 +164,15 @@ abstract class CMSBatchAction extends Object {
public function canView() {
return true;
}
/**
* Given a list of object IDs, filter out which items can have this batch action applied
* to them.
*
* @param array $ids List of object ids
* @return array Filtered list of $ids
*/
public function applicablePages($ids) {
return $ids;
}
}

View File

@ -37,6 +37,21 @@ class CMSBatchActionHandler extends RequestHandler {
*/
protected $recordClass = 'SiteTree';
/**
* @param string $parentController
* @param string $urlSegment
* @param string $recordClass
*/
public function __construct($parentController, $urlSegment, $recordClass = null) {
$this->parentController = $parentController;
$this->urlSegment = $urlSegment;
if($recordClass) {
$this->recordClass = $recordClass;
}
parent::__construct();
}
/**
* Register a new batch action. Each batch action needs to be represented by a subclass
* of {@link CMSBatchAction}.
@ -47,120 +62,98 @@ class CMSBatchActionHandler extends RequestHandler {
*/
public static function register($urlSegment, $batchActionClass, $recordClass = 'SiteTree') {
if(is_subclass_of($batchActionClass, 'CMSBatchAction')) {
Config::inst()->update('CMSBatchActionHandler', 'batch_actions',
array($urlSegment => array('class' => $batchActionClass, 'recordClass' => $recordClass))
Config::inst()->update(
'CMSBatchActionHandler',
'batch_actions',
array(
$urlSegment => array(
'class' => $batchActionClass,
'recordClass' => $recordClass
)
)
);
} else {
user_error("CMSBatchActionHandler::register() - Bad class '$batchActionClass'", E_USER_ERROR);
}
}
/**
* @param string $parentController
* @param string $urlSegment
* @param string $recordClass
*/
public function __construct($parentController, $urlSegment, $recordClass = null) {
$this->parentController = $parentController;
$this->urlSegment = $urlSegment;
if($recordClass) $this->recordClass = $recordClass;
parent::__construct();
}
public function Link() {
return Controller::join_links($this->parentController->Link(), $this->urlSegment);
}
/**
* Invoke a batch action
*
* @param SS_HTTPRequest $request
* @return SS_HTTPResponse
*/
public function handleBatchAction($request) {
// This method can't be called without ajax.
if(!$request->isAjax()) {
$this->parentController->redirectBack();
return;
return $this->parentController->redirectBack();
}
// Protect against CSRF on destructive action
if(!SecurityToken::inst()->checkRequest($request)) return $this->httpError(400);
$actions = $this->batchActions();
$actionClass = $actions[$request->param('BatchAction')]['class'];
$actionHandler = new $actionClass();
if(!SecurityToken::inst()->checkRequest($request)) {
return $this->httpError(400);
}
// Find the action handler
$action = $request->param('BatchAction');
$actionHandler = $this->actionByName($action);
// Sanitise ID list and query the database for apges
$ids = preg_split('/ *, */', trim($request->requestVar('csvIDs')));
foreach($ids as $k => $v) if(!is_numeric($v)) unset($ids[$k]);
$csvIDs = $request->requestVar('csvIDs');
$ids = $this->cleanIDs($csvIDs);
if($ids) {
if(class_exists('Translatable') && SiteTree::has_extension('Translatable')) {
Translatable::disable_locale_filter();
}
$recordClass = $this->recordClass;
$pages = DataObject::get($recordClass)->byIDs($ids);
if(class_exists('Translatable') && SiteTree::has_extension('Translatable')) {
Translatable::enable_locale_filter();
}
$record_class = $this->recordClass;
if($record_class::has_extension('Versioned')) {
// If we didn't query all the pages, then find the rest on the live site
if(!$pages || $pages->Count() < sizeof($ids)) {
$idsFromLive = array();
foreach($ids as $id) $idsFromLive[$id] = true;
if($pages) foreach($pages as $page) unset($idsFromLive[$page->ID]);
$idsFromLive = array_keys($idsFromLive);
$livePages = Versioned::get_by_stage($this->recordClass, 'Live')->byIDs($idsFromLive);
if($pages) {
// Can't merge into a DataList, need to condense into an actual list first
// (which will retrieve all records as objects, so its an expensive operation)
$pages = new ArrayList($pages->toArray());
$pages->merge($livePages);
} else {
$pages = $livePages;
}
}
}
} else {
$pages = new ArrayList();
}
// Filter ids by those which are applicable to this action
// Enforces front end filter in LeftAndMain.BatchActions.js:refreshSelected
$ids = $actionHandler->applicablePages($ids);
// Query ids and pass to action to process
$pages = $this->getPages($ids);
return $actionHandler->run($pages);
}
/**
* Respond with the list of applicable pages for a given filter
*
* @param SS_HTTPRequest $request
* @return SS_HTTPResponse
*/
public function handleApplicablePages($request) {
// Find the action handler
$actions = Config::inst()->get($this->class, 'batch_actions', Config::FIRST_SET);
$actionClass = $actions[$request->param('BatchAction')];
$actionHandler = new $actionClass['class']();
$action = $request->param('BatchAction');
$actionHandler = $this->actionByName($action);
// Sanitise ID list and query the database for apges
$ids = preg_split('/ *, */', trim($request->requestVar('csvIDs')));
foreach($ids as $k => $id) $ids[$k] = (int)$id;
$ids = array_filter($ids);
$csvIDs = $request->requestVar('csvIDs');
$ids = $this->cleanIDs($csvIDs);
if($actionHandler->hasMethod('applicablePages')) {
$applicableIDs = $actionHandler->applicablePages($ids);
} else {
$applicableIDs = $ids;
}
// Filter by applicable pages
$applicableIDs = $actionHandler->applicablePages($ids);
$response = new SS_HTTPResponse(json_encode($applicableIDs));
$response->addHeader("Content-type", "application/json");
return $response;
}
/**
* Check if this action has a confirmation step
*
* @param SS_HTTPRequest $request
* @return SS_HTTPResponse
*/
public function handleConfirmation($request) {
// Find the action handler
$actions = Config::inst()->get($this->class, 'batch_actions', Config::FIRST_SET);
$actionClass = $actions[$request->param('BatchAction')];
$actionHandler = new $actionClass();
$action = $request->param('BatchAction');
$actionHandler = $this->actionByName($action);
// Sanitise ID list and query the database for apges
$ids = preg_split('/ *, */', trim($request->requestVar('csvIDs')));
foreach($ids as $k => $id) $ids[$k] = (int)$id;
$ids = array_filter($ids);
$csvIDs = $request->requestVar('csvIDs');
$ids = $this->cleanIDs($csvIDs);
// Check dialog
if($actionHandler->hasMethod('confirmationDialog')) {
$response = new SS_HTTPResponse(json_encode($actionHandler->confirmationDialog($ids)));
} else {
@ -171,19 +164,36 @@ class CMSBatchActionHandler extends RequestHandler {
return $response;
}
/**
* Get an action for a given name
*
* @param string $name Name of the action
* @return CMSBatchAction An instance of the given batch action
* @throws InvalidArgumentException if invalid action name is passed.
*/
protected function actionByName($name) {
// Find the action handler
$actions = $this->batchActions();
if(!isset($actions[$name]['class'])) {
throw new InvalidArgumentException("Invalid batch action: {$name}");
}
return $this->buildAction($actions[$name]['class']);
}
/**
* Return a SS_List of ArrayData objects containing the following pieces of info
* about each batch action:
* - Link
* - Title
*
* @return ArrayList
*/
public function batchActionList() {
$actions = $this->batchActions();
$actionList = new ArrayList();
foreach($actions as $urlSegment => $action) {
$actionClass = $action['class'];
$actionObj = new $actionClass();
$actionObj = $this->buildAction($action['class']);
if($actionObj->canView()) {
$actionDef = new ArrayData(array(
"Link" => Controller::join_links($this->Link(), $urlSegment),
@ -196,6 +206,34 @@ class CMSBatchActionHandler extends RequestHandler {
return $actionList;
}
/**
* Safely generate batch action object for a given classname
*
* @param string $class Class name to check
* @return CMSBatchAction An instance of the given batch action
* @throws InvalidArgumentException if invalid action class is passed.
*/
protected function buildAction($class) {
if(!is_subclass_of($class, 'CMSBatchAction')) {
throw new InvalidArgumentException("{$class} is not a valid subclass of CMSBatchAction");
}
return $class::singleton();
}
/**
* Sanitise ID list from string input
*
* @param string $csvIDs
* @return array List of IDs as ints
*/
protected function cleanIDs($csvIDs) {
$ids = preg_split('/ *, */', trim($csvIDs));
foreach($ids as $k => $id) {
$ids[$k] = (int)$id;
}
return array_filter($ids);
}
/**
* Get all registered actions through the static defaults set by {@link register()}.
* Filters for the currently set {@link recordClass}.
@ -203,12 +241,49 @@ class CMSBatchActionHandler extends RequestHandler {
* @return array See {@link register()} for the returned format.
*/
public function batchActions() {
$actions = Config::inst()->get($this->class, 'batch_actions', Config::FIRST_SET);
if($actions) foreach($actions as $action) {
if($action['recordClass'] != $this->recordClass) unset($action);
}
$actions = $this->config()->batch_actions;
$recordClass = $this->recordClass;
$actions = array_filter($actions, function($action) use ($recordClass) {
return $action['recordClass'] === $recordClass;
});
return $actions;
}
/**
* Safely query and return all pages queried
*
* @param array $ids
* @return SS_List
*/
protected function getPages($ids) {
// Check empty set
if(empty($ids)) {
return new ArrayList();
}
$recordClass = $this->recordClass;
// Bypass translatable filter
if(class_exists('Translatable') && $recordClass::has_extension('Translatable')) {
Translatable::disable_locale_filter();
}
// Bypass versioned filter
if($recordClass::has_extension('Versioned')) {
// Workaround for get_including_deleted not supporting byIDs filter very well
// Ensure we select both stage / live records
$pages = Versioned::get_including_deleted($recordClass, array(
'"RecordID" IN ('.DB::placeholders($ids).')' => $ids
));
} else {
$pages = DataObject::get($recordClass)->byIDs($ids);
}
if(class_exists('Translatable') && $recordClass::has_extension('Translatable')) {
Translatable::enable_locale_filter();
}
return $pages;
}
}

View File

@ -9,7 +9,7 @@
/** ----------------------------------------------- Application Logo (CMS Logo) Must be 24px x 24px ------------------------------------------------ */
.cms .ss-ui-button { background-color: #e6e6e6; }
.cms .ss-ui-button.ui-state-hover { background-color: #f3f3f3; }
.cms .ss-ui-button.ss-ui-action-constructive { background-color: #1f9433; }
.cms .ss-ui-button.ss-ui-action-constructive { background-color: #1F9433; }
.cms .ss-ui-button.ss-ui-action-constructive.ui-state-hover { background-color: #23a93a; }
.cms .ss-ui-button.ss-gridfield-button-filter { background: #55a4d2 url(../../images/icons/filter-icons.png) no-repeat -14px 4px; }
@ -20,7 +20,7 @@
.ss-gridfield-button-filter.ss-ui-button.hover-alike { background-color: #338DC1; background-position: -16px 6px; filter: none; }
.ss-gridfield-button-reset.ss-ui-button { background: #e6e6e6 url(../images/filter-icons.png) no-repeat 8px 5px; filter: none; }
.ss-gridfield-button-reset.ss-ui-button.filtered:hover { background: red url(../images/filter-icons.png) no-repeat 8px -17px; filter: none; }
.ss-gridfield-button-reset.ss-ui-button.filtered:hover { background: #f00 url(../images/filter-icons.png) no-repeat 8px -17px; filter: none; }
.ss-gridfield-button-reset.ss-ui-button.filtered:active { background: #e60000 url(../images/filter-icons.png) no-repeat 9px -16px; filter: none; }
.cms table.ss-gridfield-table tr td { border-right: 1px solid #9a9a9a; }
@ -54,78 +54,78 @@ fieldset.switch-states .switch input.state-name { margin-left: -20px; }
.cms-content-controls .preview-size-selector { display: none; }
/** Helper SCSS file for generating sprites for the interface. */
.btn-icon-sprite, .ui-state-default .btn-icon-accept, .ui-widget-content .btn-icon-accept, .ui-state-default .btn-icon-accept_disabled, .ui-widget-content .btn-icon-accept_disabled, .ui-state-default .btn-icon-add, .ui-widget-content .btn-icon-add, .ui-state-default .btn-icon-addMedia, .ui-widget-content .btn-icon-addMedia, .ui-state-default .btn-icon-add_disabled, .ui-widget-content .btn-icon-add_disabled, .ui-state-default .btn-icon-addpage, .ui-widget-content .btn-icon-addpage, .ui-state-default .btn-icon-addpage_disabled, .ui-widget-content .btn-icon-addpage_disabled, .ui-state-default .btn-icon-arrow-circle-135-left, .ui-widget-content .btn-icon-arrow-circle-135-left, .ui-state-default .btn-icon-arrow-circle-double, .ui-widget-content .btn-icon-arrow-circle-double, .ui-state-default .btn-icon-back, .ui-widget-content .btn-icon-back, .ui-state-default .btn-icon-back_disabled, .ui-widget-content .btn-icon-back_disabled, .ui-state-default .btn-icon-chain--arrow, .ui-widget-content .btn-icon-chain--arrow, .ui-state-default .btn-icon-chain--exclamation, .ui-widget-content .btn-icon-chain--exclamation, .ui-state-default .btn-icon-chain--minus, .ui-widget-content .btn-icon-chain--minus, .ui-state-default .btn-icon-chain--pencil, .ui-widget-content .btn-icon-chain--pencil, .ui-state-default .btn-icon-chain--plus, .ui-widget-content .btn-icon-chain--plus, .ui-state-default .btn-icon-chain-small, .ui-widget-content .btn-icon-chain-small, .ui-state-default .btn-icon-chain-unchain, .ui-widget-content .btn-icon-chain-unchain, .ui-state-default .btn-icon-chain, .ui-widget-content .btn-icon-chain, .ui-state-default .btn-icon-cross-circle, .ui-widget-content .btn-icon-cross-circle, .ui-state-default .btn-icon-cross-circle_disabled, .ui-widget-content .btn-icon-cross-circle_disabled, .ui-state-default .btn-icon-cross, .ui-widget-content .btn-icon-cross, .ui-state-default .btn-icon-decline, .ui-widget-content .btn-icon-decline, .ui-state-default .btn-icon-decline_disabled, .ui-widget-content .btn-icon-decline_disabled, .ui-state-default .btn-icon-delete, .ui-widget-content .btn-icon-delete, .ui-state-default .btn-icon-deleteLight, .ui-widget-content .btn-icon-deleteLight, .ui-state-default .btn-icon-disk, .ui-widget-content .btn-icon-disk, .ui-state-default .btn-icon-document--pencil, .ui-widget-content .btn-icon-document--pencil, .ui-state-default .btn-icon-download-csv, .ui-widget-content .btn-icon-download-csv, .ui-state-default .btn-icon-drive-upload, .ui-widget-content .btn-icon-drive-upload, .ui-state-default .btn-icon-drive-upload_disabled, .ui-widget-content .btn-icon-drive-upload_disabled, .ui-state-default .btn-icon-grid_print, .ui-widget-content .btn-icon-grid_print, .ui-state-default .btn-icon-information, .ui-widget-content .btn-icon-information, .ui-state-default .btn-icon-magnifier, .ui-widget-content .btn-icon-magnifier, .ui-state-default .btn-icon-minus-circle, .ui-widget-content .btn-icon-minus-circle, .ui-state-default .btn-icon-minus-circle_disabled, .ui-widget-content .btn-icon-minus-circle_disabled, .ui-state-default .btn-icon-navigation, .ui-widget-content .btn-icon-navigation, .ui-state-default .btn-icon-navigation_disabled, .ui-widget-content .btn-icon-navigation_disabled, .ui-state-default .btn-icon-network-cloud, .ui-widget-content .btn-icon-network-cloud, .ui-state-default .btn-icon-network-cloud_disabled, .ui-widget-content .btn-icon-network-cloud_disabled, .ui-state-default .btn-icon-pencil, .ui-widget-content .btn-icon-pencil, .ui-state-default .btn-icon-pencil_disabled, .ui-widget-content .btn-icon-pencil_disabled, .ui-state-default .btn-icon-plug-disconnect-prohibition, .ui-widget-content .btn-icon-plug-disconnect-prohibition, .ui-state-default .btn-icon-plug-disconnect-prohibition_disabled, .ui-widget-content .btn-icon-plug-disconnect-prohibition_disabled, .ui-state-default .btn-icon-preview, .ui-widget-content .btn-icon-preview, .ui-state-default .btn-icon-preview_disabled, .ui-widget-content .btn-icon-preview_disabled, .ui-state-default .btn-icon-settings, .ui-widget-content .btn-icon-settings, .ui-state-default .btn-icon-settings_disabled, .ui-widget-content .btn-icon-settings_disabled, .ui-state-default .btn-icon-unpublish, .ui-widget-content .btn-icon-unpublish, .ui-state-default .btn-icon-unpublish_disabled, .ui-widget-content .btn-icon-unpublish_disabled { background: url('../images/btn-icon-se43cec5357.png') no-repeat; }
.btn-icon-sprite, .ui-state-default .btn-icon-accept, .ui-widget-content .btn-icon-accept, .ui-state-default .btn-icon-accept_disabled, .ui-widget-content .btn-icon-accept_disabled, .ui-state-default .btn-icon-add, .ui-widget-content .btn-icon-add, .ui-state-default .btn-icon-addMedia, .ui-widget-content .btn-icon-addMedia, .ui-state-default .btn-icon-add_disabled, .ui-widget-content .btn-icon-add_disabled, .ui-state-default .btn-icon-addpage, .ui-widget-content .btn-icon-addpage, .ui-state-default .btn-icon-addpage_disabled, .ui-widget-content .btn-icon-addpage_disabled, .ui-state-default .btn-icon-arrow-circle-135-left, .ui-widget-content .btn-icon-arrow-circle-135-left, .ui-state-default .btn-icon-arrow-circle-double, .ui-widget-content .btn-icon-arrow-circle-double, .ui-state-default .btn-icon-back, .ui-widget-content .btn-icon-back, .ui-state-default .btn-icon-back_disabled, .ui-widget-content .btn-icon-back_disabled, .ui-state-default .btn-icon-chain--arrow, .ui-widget-content .btn-icon-chain--arrow, .ui-state-default .btn-icon-chain--exclamation, .ui-widget-content .btn-icon-chain--exclamation, .ui-state-default .btn-icon-chain--minus, .ui-widget-content .btn-icon-chain--minus, .ui-state-default .btn-icon-chain--pencil, .ui-widget-content .btn-icon-chain--pencil, .ui-state-default .btn-icon-chain--plus, .ui-widget-content .btn-icon-chain--plus, .ui-state-default .btn-icon-chain-small, .ui-widget-content .btn-icon-chain-small, .ui-state-default .btn-icon-chain-unchain, .ui-widget-content .btn-icon-chain-unchain, .ui-state-default .btn-icon-chain, .ui-widget-content .btn-icon-chain, .ui-state-default .btn-icon-cross-circle, .ui-widget-content .btn-icon-cross-circle, .ui-state-default .btn-icon-cross-circle_disabled, .ui-widget-content .btn-icon-cross-circle_disabled, .ui-state-default .btn-icon-cross, .ui-widget-content .btn-icon-cross, .ui-state-default .btn-icon-decline, .ui-widget-content .btn-icon-decline, .ui-state-default .btn-icon-decline_disabled, .ui-widget-content .btn-icon-decline_disabled, .ui-state-default .btn-icon-delete, .ui-widget-content .btn-icon-delete, .ui-state-default .btn-icon-deleteLight, .ui-widget-content .btn-icon-deleteLight, .ui-state-default .btn-icon-disk, .ui-widget-content .btn-icon-disk, .ui-state-default .btn-icon-document--pencil, .ui-widget-content .btn-icon-document--pencil, .ui-state-default .btn-icon-download-csv, .ui-widget-content .btn-icon-download-csv, .ui-state-default .btn-icon-drive-upload, .ui-widget-content .btn-icon-drive-upload, .ui-state-default .btn-icon-drive-upload_disabled, .ui-widget-content .btn-icon-drive-upload_disabled, .ui-state-default .btn-icon-grid_print, .ui-widget-content .btn-icon-grid_print, .ui-state-default .btn-icon-information, .ui-widget-content .btn-icon-information, .ui-state-default .btn-icon-magnifier, .ui-widget-content .btn-icon-magnifier, .ui-state-default .btn-icon-minus-circle, .ui-widget-content .btn-icon-minus-circle, .ui-state-default .btn-icon-minus-circle_disabled, .ui-widget-content .btn-icon-minus-circle_disabled, .ui-state-default .btn-icon-navigation, .ui-widget-content .btn-icon-navigation, .ui-state-default .btn-icon-navigation_disabled, .ui-widget-content .btn-icon-navigation_disabled, .ui-state-default .btn-icon-network-cloud, .ui-widget-content .btn-icon-network-cloud, .ui-state-default .btn-icon-network-cloud_disabled, .ui-widget-content .btn-icon-network-cloud_disabled, .ui-state-default .btn-icon-pencil, .ui-widget-content .btn-icon-pencil, .ui-state-default .btn-icon-pencil_disabled, .ui-widget-content .btn-icon-pencil_disabled, .ui-state-default .btn-icon-plug-disconnect-prohibition, .ui-widget-content .btn-icon-plug-disconnect-prohibition, .ui-state-default .btn-icon-plug-disconnect-prohibition_disabled, .ui-widget-content .btn-icon-plug-disconnect-prohibition_disabled, .ui-state-default .btn-icon-preview, .ui-widget-content .btn-icon-preview, .ui-state-default .btn-icon-preview_disabled, .ui-widget-content .btn-icon-preview_disabled, .ui-state-default .btn-icon-settings, .ui-widget-content .btn-icon-settings, .ui-state-default .btn-icon-settings_disabled, .ui-widget-content .btn-icon-settings_disabled, .ui-state-default .btn-icon-unpublish, .ui-widget-content .btn-icon-unpublish, .ui-state-default .btn-icon-unpublish_disabled, .ui-widget-content .btn-icon-unpublish_disabled { background-image: url('../images/btn-icon-scb653ce8a9.png'); background-repeat: no-repeat; }
.ui-state-default .btn-icon-accept, .ui-widget-content .btn-icon-accept { background-position: 0 -96px; }
.ui-state-default .btn-icon-accept_disabled, .ui-widget-content .btn-icon-accept_disabled { background-position: 0 -80px; }
.ui-state-default .btn-icon-add, .ui-widget-content .btn-icon-add { background-position: 0 0; }
.ui-state-default .btn-icon-addMedia, .ui-widget-content .btn-icon-addMedia { background-position: 0 -208px; }
.ui-state-default .btn-icon-add_disabled, .ui-widget-content .btn-icon-add_disabled { background-position: 0 -32px; }
.ui-state-default .btn-icon-addpage, .ui-widget-content .btn-icon-addpage { background-position: 0 -144px; }
.ui-state-default .btn-icon-addpage_disabled, .ui-widget-content .btn-icon-addpage_disabled { background-position: 0 -516px; }
.ui-state-default .btn-icon-arrow-circle-135-left, .ui-widget-content .btn-icon-arrow-circle-135-left { background-position: 0 -356px; }
.ui-state-default .btn-icon-arrow-circle-double, .ui-widget-content .btn-icon-arrow-circle-double { background-position: 0 -340px; }
.ui-state-default .btn-icon-back, .ui-widget-content .btn-icon-back { background-position: 0 -372px; }
.ui-state-default .btn-icon-back_disabled, .ui-widget-content .btn-icon-back_disabled { background-position: 0 -16px; }
.ui-state-default .btn-icon-chain--arrow, .ui-widget-content .btn-icon-chain--arrow { background-position: 0 -740px; }
.ui-state-default .btn-icon-chain--exclamation, .ui-widget-content .btn-icon-chain--exclamation { background-position: 0 -532px; }
.ui-state-default .btn-icon-chain--minus, .ui-widget-content .btn-icon-chain--minus { background-position: 0 -756px; }
.ui-state-default .btn-icon-chain--pencil, .ui-widget-content .btn-icon-chain--pencil { background-position: 0 -692px; }
.ui-state-default .btn-icon-chain--plus, .ui-widget-content .btn-icon-chain--plus { background-position: 0 -724px; }
.ui-state-default .btn-icon-chain-small, .ui-widget-content .btn-icon-chain-small { background-position: 0 -788px; }
.ui-state-default .btn-icon-chain-unchain, .ui-widget-content .btn-icon-chain-unchain { background-position: 0 -500px; }
.ui-state-default .btn-icon-chain, .ui-widget-content .btn-icon-chain { background-position: 0 -772px; }
.ui-state-default .btn-icon-cross-circle, .ui-widget-content .btn-icon-cross-circle { background-position: 0 -468px; }
.ui-state-default .btn-icon-cross-circle_disabled, .ui-widget-content .btn-icon-cross-circle_disabled { background-position: 0 -580px; }
.ui-state-default .btn-icon-cross, .ui-widget-content .btn-icon-cross { background-position: 0 -276px; }
.ui-state-default .btn-icon-decline, .ui-widget-content .btn-icon-decline { background-position: 0 -128px; }
.ui-state-default .btn-icon-decline_disabled, .ui-widget-content .btn-icon-decline_disabled { background-position: 0 -192px; }
.ui-state-default .btn-icon-delete, .ui-widget-content .btn-icon-delete { background-position: 0 -484px; }
.ui-state-default .btn-icon-deleteLight, .ui-widget-content .btn-icon-deleteLight { background-position: 0 -307px; }
.ui-state-default .btn-icon-disk, .ui-widget-content .btn-icon-disk { background-position: 0 -291px; }
.ui-state-default .btn-icon-document--pencil, .ui-widget-content .btn-icon-document--pencil { background-position: 0 -564px; }
.ui-state-default .btn-icon-download-csv, .ui-widget-content .btn-icon-download-csv { background-position: 0 -48px; }
.ui-state-default .btn-icon-drive-upload, .ui-widget-content .btn-icon-drive-upload { background-position: 0 -436px; }
.ui-state-default .btn-icon-drive-upload_disabled, .ui-widget-content .btn-icon-drive-upload_disabled { background-position: 0 -596px; }
.ui-state-default .btn-icon-grid_print, .ui-widget-content .btn-icon-grid_print { background-position: 0 -260px; }
.ui-state-default .btn-icon-information, .ui-widget-content .btn-icon-information { background-position: 0 -388px; }
.ui-state-default .btn-icon-magnifier, .ui-widget-content .btn-icon-magnifier { background-position: 0 -548px; }
.ui-state-default .btn-icon-minus-circle, .ui-widget-content .btn-icon-minus-circle { background-position: 0 -644px; }
.ui-state-default .btn-icon-minus-circle_disabled, .ui-widget-content .btn-icon-minus-circle_disabled { background-position: 0 -660px; }
.ui-state-default .btn-icon-navigation, .ui-widget-content .btn-icon-navigation { background-position: 0 -404px; }
.ui-state-default .btn-icon-navigation_disabled, .ui-widget-content .btn-icon-navigation_disabled { background-position: 0 -452px; }
.ui-state-default .btn-icon-network-cloud, .ui-widget-content .btn-icon-network-cloud { background-position: 0 -628px; }
.ui-state-default .btn-icon-network-cloud_disabled, .ui-widget-content .btn-icon-network-cloud_disabled { background-position: 0 -708px; }
.ui-state-default .btn-icon-pencil, .ui-widget-content .btn-icon-pencil { background-position: 0 -228px; }
.ui-state-default .btn-icon-pencil_disabled, .ui-widget-content .btn-icon-pencil_disabled { background-position: 0 -612px; }
.ui-state-default .btn-icon-plug-disconnect-prohibition, .ui-widget-content .btn-icon-plug-disconnect-prohibition { background-position: 0 -244px; }
.ui-state-default .btn-icon-plug-disconnect-prohibition_disabled, .ui-widget-content .btn-icon-plug-disconnect-prohibition_disabled { background-position: 0 -676px; }
.ui-state-default .btn-icon-preview, .ui-widget-content .btn-icon-preview { background-position: 0 -64px; }
.ui-state-default .btn-icon-preview_disabled, .ui-widget-content .btn-icon-preview_disabled { background-position: 0 -160px; }
.ui-state-default .btn-icon-settings, .ui-widget-content .btn-icon-settings { background-position: 0 -324px; }
.ui-state-default .btn-icon-settings_disabled, .ui-widget-content .btn-icon-settings_disabled { background-position: 0 -420px; }
.ui-state-default .btn-icon-unpublish, .ui-widget-content .btn-icon-unpublish { background-position: 0 -112px; }
.ui-state-default .btn-icon-unpublish_disabled, .ui-widget-content .btn-icon-unpublish_disabled { background-position: 0 -176px; }
.ui-state-default .btn-icon-accept, .ui-widget-content .btn-icon-accept { background-position: 0 0; }
.ui-state-default .btn-icon-accept_disabled, .ui-widget-content .btn-icon-accept_disabled { background-position: 0 -16px; }
.ui-state-default .btn-icon-add, .ui-widget-content .btn-icon-add { background-position: 0 -32px; }
.ui-state-default .btn-icon-addMedia, .ui-widget-content .btn-icon-addMedia { background-position: 0 -48px; }
.ui-state-default .btn-icon-add_disabled, .ui-widget-content .btn-icon-add_disabled { background-position: 0 -68px; }
.ui-state-default .btn-icon-addpage, .ui-widget-content .btn-icon-addpage { background-position: 0 -84px; }
.ui-state-default .btn-icon-addpage_disabled, .ui-widget-content .btn-icon-addpage_disabled { background-position: 0 -100px; }
.ui-state-default .btn-icon-arrow-circle-135-left, .ui-widget-content .btn-icon-arrow-circle-135-left { background-position: 0 -116px; }
.ui-state-default .btn-icon-arrow-circle-double, .ui-widget-content .btn-icon-arrow-circle-double { background-position: 0 -132px; }
.ui-state-default .btn-icon-back, .ui-widget-content .btn-icon-back { background-position: 0 -148px; }
.ui-state-default .btn-icon-back_disabled, .ui-widget-content .btn-icon-back_disabled { background-position: 0 -164px; }
.ui-state-default .btn-icon-chain--arrow, .ui-widget-content .btn-icon-chain--arrow { background-position: 0 -180px; }
.ui-state-default .btn-icon-chain--exclamation, .ui-widget-content .btn-icon-chain--exclamation { background-position: 0 -196px; }
.ui-state-default .btn-icon-chain--minus, .ui-widget-content .btn-icon-chain--minus { background-position: 0 -212px; }
.ui-state-default .btn-icon-chain--pencil, .ui-widget-content .btn-icon-chain--pencil { background-position: 0 -228px; }
.ui-state-default .btn-icon-chain--plus, .ui-widget-content .btn-icon-chain--plus { background-position: 0 -244px; }
.ui-state-default .btn-icon-chain-small, .ui-widget-content .btn-icon-chain-small { background-position: 0 -260px; }
.ui-state-default .btn-icon-chain-unchain, .ui-widget-content .btn-icon-chain-unchain { background-position: 0 -276px; }
.ui-state-default .btn-icon-chain, .ui-widget-content .btn-icon-chain { background-position: 0 -292px; }
.ui-state-default .btn-icon-cross-circle, .ui-widget-content .btn-icon-cross-circle { background-position: 0 -308px; }
.ui-state-default .btn-icon-cross-circle_disabled, .ui-widget-content .btn-icon-cross-circle_disabled { background-position: 0 -324px; }
.ui-state-default .btn-icon-cross, .ui-widget-content .btn-icon-cross { background-position: 0 -340px; }
.ui-state-default .btn-icon-decline, .ui-widget-content .btn-icon-decline { background-position: 0 -355px; }
.ui-state-default .btn-icon-decline_disabled, .ui-widget-content .btn-icon-decline_disabled { background-position: 0 -371px; }
.ui-state-default .btn-icon-delete, .ui-widget-content .btn-icon-delete { background-position: 0 -387px; }
.ui-state-default .btn-icon-deleteLight, .ui-widget-content .btn-icon-deleteLight { background-position: 0 -403px; }
.ui-state-default .btn-icon-disk, .ui-widget-content .btn-icon-disk { background-position: 0 -420px; }
.ui-state-default .btn-icon-document--pencil, .ui-widget-content .btn-icon-document--pencil { background-position: 0 -436px; }
.ui-state-default .btn-icon-download-csv, .ui-widget-content .btn-icon-download-csv { background-position: 0 -452px; }
.ui-state-default .btn-icon-drive-upload, .ui-widget-content .btn-icon-drive-upload { background-position: 0 -468px; }
.ui-state-default .btn-icon-drive-upload_disabled, .ui-widget-content .btn-icon-drive-upload_disabled { background-position: 0 -484px; }
.ui-state-default .btn-icon-grid_print, .ui-widget-content .btn-icon-grid_print { background-position: 0 -500px; }
.ui-state-default .btn-icon-information, .ui-widget-content .btn-icon-information { background-position: 0 -516px; }
.ui-state-default .btn-icon-magnifier, .ui-widget-content .btn-icon-magnifier { background-position: 0 -532px; }
.ui-state-default .btn-icon-minus-circle, .ui-widget-content .btn-icon-minus-circle { background-position: 0 -548px; }
.ui-state-default .btn-icon-minus-circle_disabled, .ui-widget-content .btn-icon-minus-circle_disabled { background-position: 0 -564px; }
.ui-state-default .btn-icon-navigation, .ui-widget-content .btn-icon-navigation { background-position: 0 -580px; }
.ui-state-default .btn-icon-navigation_disabled, .ui-widget-content .btn-icon-navigation_disabled { background-position: 0 -596px; }
.ui-state-default .btn-icon-network-cloud, .ui-widget-content .btn-icon-network-cloud { background-position: 0 -612px; }
.ui-state-default .btn-icon-network-cloud_disabled, .ui-widget-content .btn-icon-network-cloud_disabled { background-position: 0 -628px; }
.ui-state-default .btn-icon-pencil, .ui-widget-content .btn-icon-pencil { background-position: 0 -644px; }
.ui-state-default .btn-icon-pencil_disabled, .ui-widget-content .btn-icon-pencil_disabled { background-position: 0 -660px; }
.ui-state-default .btn-icon-plug-disconnect-prohibition, .ui-widget-content .btn-icon-plug-disconnect-prohibition { background-position: 0 -676px; }
.ui-state-default .btn-icon-plug-disconnect-prohibition_disabled, .ui-widget-content .btn-icon-plug-disconnect-prohibition_disabled { background-position: 0 -692px; }
.ui-state-default .btn-icon-preview, .ui-widget-content .btn-icon-preview { background-position: 0 -708px; }
.ui-state-default .btn-icon-preview_disabled, .ui-widget-content .btn-icon-preview_disabled { background-position: 0 -724px; }
.ui-state-default .btn-icon-settings, .ui-widget-content .btn-icon-settings { background-position: 0 -740px; }
.ui-state-default .btn-icon-settings_disabled, .ui-widget-content .btn-icon-settings_disabled { background-position: 0 -756px; }
.ui-state-default .btn-icon-unpublish, .ui-widget-content .btn-icon-unpublish { background-position: 0 -772px; }
.ui-state-default .btn-icon-unpublish_disabled, .ui-widget-content .btn-icon-unpublish_disabled { background-position: 0 -788px; }
.icon { text-indent: -9999px; border: none; outline: none; }
.icon.icon-24 { width: 24px; height: 24px; background: url('../images/menu-icons/24x24-s391afdd013.png'); }
.icon.icon-24.icon-assetadmin { background-position: 0 -120px; }
.icon.icon-24.icon-cmsmain { background-position: 0 -48px; }
.icon.icon-24.icon-cmspagescontroller { background-position: 0 -216px; }
.icon.icon-24.icon-cmssettingscontroller { background-position: 0 0; }
.icon.icon-24 { width: 24px; height: 24px; background: url('../images/menu-icons/24x24-s0dc15c36f9.png'); }
.icon.icon-24.icon-assetadmin { background-position: 0 -216px; }
.icon.icon-24.icon-cmsmain { background-position: 0 -192px; }
.icon.icon-24.icon-cmspagescontroller { background-position: 0 -168px; }
.icon.icon-24.icon-cmssettingscontroller { background-position: 0 -96px; }
.icon.icon-24.icon-securityadmin { background-position: 0 -24px; }
.icon.icon-24.icon-reportadmin { background-position: 0 -72px; }
.icon.icon-24.icon-commentadmin { background-position: 0 -192px; }
.icon.icon-24.icon-help { background-position: 0 -96px; }
.icon.icon-16 { width: 16px; height: 16px; background: url('../images/menu-icons/16x16-sf5b94bb49b.png'); }
.icon.icon-16.icon-assetadmin { background-position: 0 -80px; }
.icon.icon-16.icon-cmsmain { background-position: 0 -16px; }
.icon.icon-24.icon-reportadmin { background-position: 0 -240px; }
.icon.icon-24.icon-commentadmin { background-position: 0 0; }
.icon.icon-24.icon-help { background-position: 0 -144px; }
.icon.icon-16 { width: 16px; height: 16px; background: url('../images/menu-icons/16x16-s3f4c846209.png'); }
.icon.icon-16.icon-assetadmin { background-position: 0 -144px; }
.icon.icon-16.icon-cmsmain { background-position: 0 -128px; }
.icon.icon-16.icon-cmspagescontroller { background-position: 0 -112px; }
.icon.icon-16.icon-cmssettingscontroller { background-position: 0 0; }
.icon.icon-16.icon-securityadmin { background-position: 0 -48px; }
.icon.icon-16.icon-reportadmin { background-position: 0 -32px; }
.icon.icon-16.icon-commentadmin { background-position: 0 -144px; }
.icon.icon-16.icon-help { background-position: 0 -64px; }
.icon.icon-16.icon-cmssettingscontroller { background-position: 0 -64px; }
.icon.icon-16.icon-securityadmin { background-position: 0 -16px; }
.icon.icon-16.icon-reportadmin { background-position: 0 -160px; }
.icon.icon-16.icon-commentadmin { background-position: 0 0; }
.icon.icon-16.icon-help { background-position: 0 -96px; }
html { overflow: hidden; }

View File

@ -9,7 +9,7 @@
/** ----------------------------------------------- Application Logo (CMS Logo) Must be 24px x 24px ------------------------------------------------ */
.cms .ss-ui-button { background-color: #e6e6e6; }
.cms .ss-ui-button.ui-state-hover { background-color: #f3f3f3; }
.cms .ss-ui-button.ss-ui-action-constructive { background-color: #1f9433; }
.cms .ss-ui-button.ss-ui-action-constructive { background-color: #1F9433; }
.cms .ss-ui-button.ss-ui-action-constructive.ui-state-hover { background-color: #23a93a; }
.cms .ss-ui-button.ss-gridfield-button-filter { background: #55a4d2 url(../../images/icons/filter-icons.png) no-repeat -14px 4px; }
@ -20,7 +20,7 @@
.ss-gridfield-button-filter.ss-ui-button.hover-alike { background-color: #338DC1; background-position: -16px 6px; filter: none; }
.ss-gridfield-button-reset.ss-ui-button { background: #e6e6e6 url(../images/filter-icons.png) no-repeat 8px 5px; filter: none; }
.ss-gridfield-button-reset.ss-ui-button.filtered:hover { background: red url(../images/filter-icons.png) no-repeat 8px -17px; filter: none; }
.ss-gridfield-button-reset.ss-ui-button.filtered:hover { background: #f00 url(../images/filter-icons.png) no-repeat 8px -17px; filter: none; }
.ss-gridfield-button-reset.ss-ui-button.filtered:active { background: #e60000 url(../images/filter-icons.png) no-repeat 9px -16px; filter: none; }
.cms table.ss-gridfield-table tr td { border-right: 1px solid #9a9a9a; }
@ -67,3 +67,7 @@ fieldset.switch-states .switch input.state-name { margin-left: -20px; }
/* fix for actions buttons on edit page content overlapping */
.cms-content-actions .ss-ui-buttonset button { margin-right: 0; }
.tree-holder.filtered-list li > a, .tree-holder.filtered-list li > a:link, .cms-tree.filtered-list li > a, .cms-tree.filtered-list li > a:link { color: #aaa; }
.tree-holder.filtered-list li.filtered-item > a, .tree-holder.filtered-list li.filtered-item > a:link, .cms-tree.filtered-list li.filtered-item > a, .cms-tree.filtered-list li.filtered-item > a:link { color: #0073C1; }
.tree-holder.filtered-list li.disabled > a, .tree-holder.filtered-list li.disabled > a:link, .tree-holder.filtered-list li.edit-disabled > a, .tree-holder.filtered-list li.edit-disabled > a:link, .cms-tree.filtered-list li.disabled > a, .cms-tree.filtered-list li.disabled > a:link, .cms-tree.filtered-list li.edit-disabled > a, .cms-tree.filtered-list li.edit-disabled > a:link { color: #aaa; background: transparent none; cursor: default; }

View File

@ -767,15 +767,12 @@ form.import-form label.left { width: 250px; }
.cms .jstree li, .TreeDropdownField .treedropdownfield-panel .jstree li { min-height: 18px; line-height: 25px; white-space: nowrap; margin-left: 18px; min-width: 18px; }
.cms .jstree li.jstree-open > ul, .TreeDropdownField .treedropdownfield-panel .jstree li.jstree-open > ul { display: block; }
.cms .jstree li.jstree-closed > ul, .TreeDropdownField .treedropdownfield-panel .jstree li.jstree-closed > ul { display: none; }
.cms .jstree li.disabled > a, .TreeDropdownField .treedropdownfield-panel .jstree li.disabled > a { color: #aaaaaa; }
.cms .jstree li.disabled > a:hover, .TreeDropdownField .treedropdownfield-panel .jstree li.disabled > a:hover { background: transparent; cursor: default; }
.cms .jstree li.edit-disabled > a, .TreeDropdownField .treedropdownfield-panel .jstree li.edit-disabled > a { color: #aaaaaa; }
.cms .jstree li > .jstree-icon, .TreeDropdownField .treedropdownfield-panel .jstree li > .jstree-icon { cursor: pointer; }
.cms .jstree ins, .TreeDropdownField .treedropdownfield-panel .jstree ins { display: inline-block; text-decoration: none; width: 18px; height: 18px; margin: 0 0 0 0; padding: 0; float: left; }
.cms .jstree a, .TreeDropdownField .treedropdownfield-panel .jstree a { display: inline-block; line-height: 16px; height: 16px; color: black; white-space: nowrap; text-decoration: none; padding: 1px 2px; margin: 0; border: 1px solid #fff; }
.cms .jstree a:focus, .cms .jstree a:active, .cms .jstree a:hover, .TreeDropdownField .treedropdownfield-panel .jstree a:focus, .TreeDropdownField .treedropdownfield-panel .jstree a:active, .TreeDropdownField .treedropdownfield-panel .jstree a:hover { text-decoration: none; cursor: pointer; text-shadow: 1px 1px 1px white; }
.cms .jstree a > ins, .TreeDropdownField .treedropdownfield-panel .jstree a > ins { height: 16px; width: 12px; }
.cms .jstree a > ins.jstree-checkbox, .TreeDropdownField .treedropdownfield-panel .jstree a > ins.jstree-checkbox { height: 19px; width: 16px; }
.cms .jstree a ins, .TreeDropdownField .treedropdownfield-panel .jstree a ins { height: 16px; width: 12px; }
.cms .jstree a ins.jstree-checkbox, .TreeDropdownField .treedropdownfield-panel .jstree a ins.jstree-checkbox { height: 19px; width: 16px; }
.cms .jstree .jstree-real-checkbox, .TreeDropdownField .treedropdownfield-panel .jstree .jstree-real-checkbox { display: none; }
.cms .jstree .jstree-wholerow-real, .TreeDropdownField .treedropdownfield-panel .jstree .jstree-wholerow-real { position: relative; z-index: 1; }
.cms .jstree .jstree-wholerow-real li, .TreeDropdownField .treedropdownfield-panel .jstree .jstree-wholerow-real li { cursor: pointer; }
@ -835,9 +832,10 @@ form.import-form label.left { width: 250px; }
.tree-holder.jstree li, .cms-tree.jstree li { padding: 0px; clear: left; }
.tree-holder.jstree li.Root strong, .cms-tree.jstree li.Root strong { font-weight: bold; padding-left: 1px; }
.tree-holder.jstree li.Root > a .jstree-icon, .cms-tree.jstree li.Root > a .jstree-icon { background-position: -56px -36px; }
.tree-holder.jstree li.status-deletedonlive .text, .cms-tree.jstree li.status-deletedonlive .text { text-decoration: line-through; }
.tree-holder.jstree li.status-deletedonlive > a .text, .tree-holder.jstree li.status-deletedonlive > a:link .text, .tree-holder.jstree li.status-archived > a .text, .tree-holder.jstree li.status-archived > a:link .text, .cms-tree.jstree li.status-deletedonlive > a .text, .cms-tree.jstree li.status-deletedonlive > a:link .text, .cms-tree.jstree li.status-archived > a .text, .cms-tree.jstree li.status-archived > a:link .text { text-decoration: line-through; }
.tree-holder.jstree li.jstree-checked > a, .tree-holder.jstree li.jstree-checked > a:link, .cms-tree.jstree li.jstree-checked > a, .cms-tree.jstree li.jstree-checked > a:link { background-color: #efe999; }
.tree-holder.jstree li.disabled > a > .jstree-checkbox, .cms-tree.jstree li.disabled > a > .jstree-checkbox { background-position: -57px -54px; }
.tree-holder.jstree li.disabled > a, .tree-holder.jstree li.disabled > a:link, .tree-holder.jstree li.edit-disabled > a, .tree-holder.jstree li.edit-disabled > a:link, .cms-tree.jstree li.disabled > a, .cms-tree.jstree li.disabled > a:link, .cms-tree.jstree li.edit-disabled > a, .cms-tree.jstree li.edit-disabled > a:link { color: #aaa; background-color: transparent; cursor: default; }
.tree-holder.jstree li.disabled > a > .jstree-checkbox, .tree-holder.jstree li.disabled > a:link > .jstree-checkbox, .tree-holder.jstree li.edit-disabled > a > .jstree-checkbox, .tree-holder.jstree li.edit-disabled > a:link > .jstree-checkbox, .cms-tree.jstree li.disabled > a > .jstree-checkbox, .cms-tree.jstree li.disabled > a:link > .jstree-checkbox, .cms-tree.jstree li.edit-disabled > a > .jstree-checkbox, .cms-tree.jstree li.edit-disabled > a:link > .jstree-checkbox { background-position: -57px -54px; }
.tree-holder.jstree li.readonly, .cms-tree.jstree li.readonly { color: #aaa; padding-left: 18px; }
.tree-holder.jstree li.readonly a, .tree-holder.jstree li.readonly a:link, .cms-tree.jstree li.readonly a, .cms-tree.jstree li.readonly a:link { margin: 0; padding: 0; }
.tree-holder.jstree li.readonly .jstree-icon, .cms-tree.jstree li.readonly .jstree-icon { display: none; }
@ -850,8 +848,7 @@ form.import-form label.left { width: 250px; }
.tree-holder.jstree .jstree-hovered, .cms-tree.jstree .jstree-hovered { text-shadow: none; text-decoration: none; }
.tree-holder.jstree .jstree-closed > ins, .cms-tree.jstree .jstree-closed > ins { background-position: 0 0; }
.tree-holder.jstree .jstree-open > ins, .cms-tree.jstree .jstree-open > ins { background-position: -20px 0; }
.tree-holder.filtered-list li.class-Page a, .cms-tree.filtered-list li.class-Page a { color: #aaa; }
.tree-holder.filtered-list li.class-Page.filtered-item a, .cms-tree.filtered-list li.class-Page.filtered-item a { color: #0073C1; }
.tree-holder.filtered-list li:not(.filtered-item) > a, .cms-tree.filtered-list li:not(.filtered-item) > a { color: #aaa; }
.cms-content-fields .cms-tree.jstree .jstree-no-checkboxes li a { padding-left: 15px; }
.cms-content-fields .cms-tree.jstree .jstree-no-checkboxes li .jstree-hovered, .cms-content-fields .cms-tree.jstree .jstree-no-checkboxes li .jstree-clicked, .cms-content-fields .cms-tree.jstree .jstree-no-checkboxes li a:focus { padding-left: 3px; }
@ -872,7 +869,7 @@ li.class-VirtualPage > a .jstree-pageicon { background-position: 0 -32px; }
li.class-ErrorPage > a .jstree-pageicon { background-position: 0 -112px; }
/* tree status icons and labels */
.cms-tree.jstree .status-modified > a .jstree-pageicon:before, .cms-tree.jstree .status-addedtodraft > a .jstree-pageicon:before, .cms-tree.jstree .status-deletedonlive > a .jstree-pageicon:before, .cms-tree.jstree .status-removedfromdraft > a .jstree-pageicon:before, .cms-tree.jstree .status-workflow-approval > a .jstree-pageicon:before { content: ""; display: block; width: 5px; height: 5px; position: absolute; bottom: 0; right: 0; background: #fce2d0; border: 1px solid #ff9344; border-radius: 100px; box-shadow: 0px 0px 0px 1px #fff; }
.cms-tree.jstree .status-modified > a .jstree-pageicon:before, .cms-tree.jstree .status-addedtodraft > a .jstree-pageicon:before, .cms-tree.jstree .status-deletedonlive > a .jstree-pageicon:before, .cms-tree.jstree .status-archived > a .jstree-pageicon:before, .cms-tree.jstree .status-removedfromdraft > a .jstree-pageicon:before, .cms-tree.jstree .status-workflow-approval > a .jstree-pageicon:before { content: ""; display: block; width: 5px; height: 5px; position: absolute; bottom: 0; right: 0; background: #fce2d0; border: 1px solid #ff9344; border-radius: 100px; box-shadow: 0px 0px 0px 1px #fff; }
.jstree .status-modified > .jstree-hovered, .jstree .status-modified > .jstree-clicked, .cms-tree.jstree span.badge.status-modified, .cms-tree.jstree .status-modified > a .jstree-pageicon:before { background-color: #FFF4ED; border-color: #EE6214; }
@ -892,6 +889,12 @@ li.class-ErrorPage > a .jstree-pageicon { background-position: 0 -112px; }
.cms-tree.jstree span.badge.status-deletedonlive { color: #5F7688; }
.jstree .status-archived > .jstree-hovered, .jstree .status-archived > .jstree-clicked, .cms-tree.jstree span.badge.status-archived, .cms-tree.jstree .status-archived > a .jstree-pageicon:before { background-color: #F5F5F5; border-color: #5F7688; }
#cms-content-tools-CMSMain .cms-tree.jstree span.badge.status-archived { box-shadow: 0px 0px 6px 2px #F5F5F5; }
.cms-tree.jstree span.badge.status-archived { color: #5F7688; }
.jstree .status-removedfromdraft > .jstree-hovered, .jstree .status-removedfromdraft > .jstree-clicked, .cms-tree.jstree span.badge.status-removedfromdraft, .cms-tree.jstree .status-removedfromdraft > a .jstree-pageicon:before { background-color: #F5F5F5; border-color: #5F7688; }
#cms-content-tools-CMSMain .cms-tree.jstree span.badge.status-removedfromdraft { box-shadow: 0px 0px 6px 2px #F5F5F5; }

View File

@ -38,12 +38,89 @@
this.serializeFromTree();
}
},
/**
* Register default bulk confirmation dialogs
*/
registerDefault: function() {
// Publish selected pages action
this.register('admin/pages/batchactions/publish', function(ids) {
var confirmed = confirm(
ss.i18n.inject(
ss.i18n._t(
"CMSMAIN.BATCH_PUBLISH_PROMPT",
"You have {num} page(s) selected.\n\nDo you really want to publish?"
),
{'num': ids.length}
)
);
return (confirmed) ? ids : false;
});
// Unpublish selected pages action
this.register('admin/pages/batchactions/unpublish', function(ids) {
var confirmed = confirm(
ss.i18n.inject(
ss.i18n._t(
"CMSMAIN.BATCH_UNPUBLISH_PROMPT",
"You have {num} page(s) selected.\n\nDo you really want to unpublish"
),
{'num': ids.length}
)
);
return (confirmed) ? ids : false;
});
// Delete selected pages action
// @deprecated since 4.0 Use archive instead
this.register('admin/pages/batchactions/delete', function(ids) {
var confirmed = confirm(
ss.i18n.inject(
ss.i18n._t(
"CMSMAIN.BATCH_DELETE_PROMPT",
"You have {num} page(s) selected.\n\nDo you really want to delete?"
),
{'num': ids.length}
)
);
return (confirmed) ? ids : false;
});
// Delete selected pages action
this.register('admin/pages/batchactions/archive', function(ids) {
var confirmed = confirm(
ss.i18n.inject(
ss.i18n._t(
"CMSMAIN.BATCH_ARCHIVE_PROMPT",
"You have {num} page(s) selected.\n\nDo you really want to archive?\n\nThese pages will be removed from both the draft and published sites without discarding the history."
),
{'num': ids.length}
)
);
return (confirmed) ? ids : false;
});
// Delete selected pages from live action
this.register('admin/pages/batchactions/deletefromlive', function(ids) {
var confirmed = confirm(
ss.i18n.inject(
ss.i18n._t(
"CMSMAIN.BATCH_DELETELIVE_PROMPT",
"You have {num} page(s) selected.\n\nDo you really want to delete these pages from live?"
),
{'num': ids.length}
)
);
return (confirmed) ? ids : false;
});
},
/**
* Constructor: onmatch
*/
onadd: function() {
this._updateStateFromViewMode();
this.registerDefault();
this._super();
},
@ -141,7 +218,12 @@
* {Object} rootNode
*/
refreshSelected : function(rootNode) {
var self = this, st = this.getTree(), ids = this.getIDs(), allIds = [];
var self = this,
st = this.getTree(),
ids = this.getIDs(),
allIds = [],
selectedAction = this.find(':input[name=Action]').val();
// Default to refreshing the entire tree
if(rootNode == null) rootNode = st;
@ -149,15 +231,22 @@
$($(st).getNodeByID(idx)).addClass('selected').attr('selected', 'selected');
}
// If no action is selected, enable all nodes
if(selectedAction == -1) {
$(rootNode).find('li').each(function() {
$(this).setEnabled(true);
});
return;
}
// Disable the nodes while the ajax request is being processed
$(rootNode).find('li').each(function() {
allIds.push($(this).data('id'));
// Disable the nodes while the ajax request is being processed
$(this).addClass('treeloading').setEnabled(false);
});
// Post to the server to ask which pages can have this batch action applied
var applicablePagesURL = this.find(':input[name=Action]').val() + '/applicablepages/?csvIDs=' + allIds.join(',');
var applicablePagesURL = selectedAction + '/applicablepages/?csvIDs=' + allIds.join(',');
jQuery.getJSON(applicablePagesURL, function(applicableIDs) {
// Set a CSS class on each tree node indicating which can be batch-actioned and which can't
jQuery(rootNode).find('li').each(function() {
@ -169,6 +258,7 @@
} else {
// De-select the node if it's non-applicable
$(this).removeClass('selected').setEnabled(false);
$(this).prop('selected', false);
}
});
@ -185,10 +275,6 @@
serializeFromTree: function() {
var tree = this.getTree(), ids = tree.getSelectedIDs();
// if no IDs are selected, stop here. This is an implict way for the
// callback to cancel the actions
if(!ids || !ids.length) return false;
// write IDs to the hidden field
this.setIDs(ids);
@ -212,7 +298,11 @@
* {Array}
*/
getIDs: function() {
return this.find(':input[name=csvIDs]').val().split(',');
// Map empty value to empty array
var value = this.find(':input[name=csvIDs]').val();
return value
? value.split(',')
: [];
},
/**
@ -222,17 +312,26 @@
* (Event) e
*/
onsubmit: function(e) {
var self = this, ids = this.getIDs(), tree = this.getTree();
var self = this, ids = this.getIDs(), tree = this.getTree(), actions = this.getActions();
// if no nodes are selected, return with an error
if(!ids || !ids.length) {
alert(ss.i18n._t('CMSMAIN.SELECTONEPAGE'));
alert(ss.i18n._t('CMSMAIN.SELECTONEPAGE', 'Please select at least one page'));
e.preventDefault();
return false;
}
// apply callback, which might modify the IDs
var type = this.find(':input[name=Action]').val();
if(this.getActions()[type]) ids = this.getActions()[type].apply(this, [ids]);
if(actions[type]) {
ids = this.getActions()[type].apply(this, [ids]);
}
// Discontinue processing if there are no further items
if(!ids || !ids.length) {
e.preventDefault();
return false;
}
// write (possibly modified) IDs back into to the hidden field
this.setIDs(ids);
@ -290,6 +389,8 @@
dataType: 'json'
});
// Never process this action; Only invoke via ajax
e.preventDefault();
return false;
}
@ -319,8 +420,10 @@
btn.attr('disabled', 'disabled').button('refresh');
} else {
btn.removeAttr('disabled').button('refresh');
// form.submit();
}
}
// Refresh selected / enabled nodes
$('#Form_BatchActionsForm').refreshSelected();
// TODO Should work by triggering change() along, but doesn't - entwine event bubbling?
this.trigger("liszt:updated");
@ -328,52 +431,6 @@
this._super(e);
}
});
$(document).ready(function() {
/**
* Publish selected pages action
*/
$('#Form_BatchActionsForm').register('admin/batchactions/publish', function(ids) {
var confirmed = confirm(
"You have " + ids.length + " pages selected.\n\n"
+ "Do your really want to publish?"
);
return (confirmed) ? ids : false;
});
/**
* Unpublish selected pages action
*/
$('#Form_BatchActionsForm').register('admin/batchactions/unpublish', function(ids) {
var confirmed = confirm(
"You have " + ids.length + " pages selected.\n\n"
+ "Do your really want to unpublish?"
);
return (confirmed) ? ids : false;
});
/**
* Delete selected pages action
*/
$('#Form_BatchActionsForm').register('admin/batchactions/delete', function(ids) {
var confirmed = confirm(
"You have " + ids.length + " pages selected.\n\n"
+ "Do your really want to delete?"
);
return (confirmed) ? ids : false;
});
/**
* Delete selected pages from live action
*/
$('#Form_BatchActionsForm').register('admin/batchactions/deletefromlive', function(ids) {
var confirmed = confirm(
"You have " + ids.length + " pages selected.\n\n"
+ "Do your really want to delete these pages from live?"
);
return (confirmed) ? ids : false;
});
});
});
})(jQuery);

View File

@ -445,7 +445,13 @@
* (Array)
*/
getSelectedIDs: function() {
return $.map($(this).jstree('get_checked'), function(el, i) {return $(el).data('id');});
return $(this)
.jstree('get_checked')
.not('.disabled')
.map(function() {
return $(this).data('id');
})
.get();
}
});

View File

@ -4,6 +4,12 @@ if(typeof(ss) == 'undefined' || typeof(ss.i18n) == 'undefined') {
if(typeof(console) != 'undefined') console.error('Class ss.i18n not defined');
} else {
ss.i18n.addDictionary('en', {
"CMSMAIN.SELECTONEPAGE": "Please select at least one page",
"CMSMAIN.BATCH_UNPUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to unpublish",
"CMSMAIN.BATCH_PUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to publish?",
"CMSMAIN.BATCH_DELETE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete?",
"CMSMAIN.BATCH_ARCHIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to archive?\n\nThese pages will be removed from both the draft and published sites without discarding the history.",
"CMSMAIN.BATCH_DELETELIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete these pages from live?",
"LeftAndMain.CONFIRMUNSAVED": "Are you sure you want to navigate away from this page?\n\nWARNING: Your changes have not been saved.\n\nPress OK to continue, or Cancel to stay on the current page.",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "WARNING: Your changes have not been saved.",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Do you really want to delete %s groups?",

View File

@ -1,4 +1,10 @@
{
"CMSMAIN.SELECTONEPAGE": "Please select at least one page",
"CMSMAIN.BATCH_UNPUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to unpublish",
"CMSMAIN.BATCH_PUBLISH_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to publish?",
"CMSMAIN.BATCH_DELETE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete?",
"CMSMAIN.BATCH_ARCHIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to archive?\n\nThese pages will be removed from both the draft and published sites without discarding the history.",
"CMSMAIN.BATCH_DELETELIVE_PROMPT": "You have {num} page(s) selected.\n\nDo you really want to delete these pages from live?",
"LeftAndMain.CONFIRMUNSAVED": "Are you sure you want to navigate away from this page?\n\nWARNING: Your changes have not been saved.\n\nPress OK to continue, or Cancel to stay on the current page.",
"LeftAndMain.CONFIRMUNSAVEDSHORT": "WARNING: Your changes have not been saved.",
"SecurityAdmin.BATCHACTIONSDELETECONFIRM": "Do you really want to delete %s groups?",

View File

@ -29,16 +29,6 @@
&.jstree-closed > ul {
display: none;
}
&.disabled > a {
color: #aaaaaa;
&:hover {
background: transparent;
cursor: default;
}
}
&.edit-disabled > a {
color: #aaaaaa;
}
// Expand/collapse arrows
& > .jstree-icon {
cursor: pointer;
@ -70,14 +60,12 @@
cursor: pointer;
text-shadow:1px 1px 1px white;
}
> {
ins {
height: 16px;
width: 12px;
&.jstree-checkbox {
height: 19px; //Larger to help avoid accidental page loads when trying to click checkboxes
width: 16px;
}
ins {
height: 16px;
width: 12px;
&.jstree-checkbox {
height: 19px; //Larger to help avoid accidental page loads when trying to click checkboxes
width: 16px;
}
}
}
@ -443,9 +431,12 @@
background-position: -56px -36px;
}
}
&.status-deletedonlive {
.text {
text-decoration: line-through;
&.status-deletedonlive,
&.status-archived {
> a, > a:link {
.text {
text-decoration: line-through;
}
}
}
&.jstree-checked {
@ -453,9 +444,16 @@
background-color: $color-cms-batchactions-menu-selected-background;
}
}
&.disabled {
> a > .jstree-checkbox {
background-position: -57px -54px;
&.disabled,
&.edit-disabled {
> a, > a:link {
color: $color-text-disabled;
background-color: transparent;
cursor: default;
> .jstree-checkbox {
background-position: -57px -54px;
}
}
}
&.readonly {
@ -553,13 +551,9 @@
// Applied to trees when displaying filter / search results.
&.filtered-list {
li.class-Page a {
li:not(.filtered-item) > a {
color: $color-text-disabled;
}
li.class-Page.filtered-item a {
color: $color-text-blue-link;
}
}
}
@ -678,6 +672,7 @@ a .jstree-pageicon {
@include tree-status-icon('modified', #EE6214, #FFF4ED);
@include tree-status-icon('addedtodraft', #EE6214, #FFF4ED);
@include tree-status-icon('deletedonlive', #5F7688, #F5F5F5);
@include tree-status-icon('archived', #5F7688, #F5F5F5);
@include tree-status-icon('removedfromdraft', #5F7688, #F5F5F5);
@include tree-status-icon('workflow-approval', #0070B4, #E8FAFF);

View File

@ -46,4 +46,35 @@
margin-right: 0;
}
}
}
}
// Fix for lack of support of :not selector
// Applied to trees when displaying filter / search results.
.tree-holder,
.cms-tree {
&.filtered-list li {
> a,
> a:link {
color: $color-text-disabled;
}
// Re-apply default style to filtered items
&.filtered-item {
> a,
> a:link {
color: $color-text-blue-link;
}
}
// Re-re-apply disabled selector from top of _tree.scss
&.disabled,
&.edit-disabled {
> a,
> a:link {
color: $color-text-disabled;
background: transparent none;
cursor: default;
}
}
}
}

View File

@ -17,6 +17,8 @@
* `Mailer` no longer calls `xml2raw` on all email subject line, and now must be passed in via plain text.
* `ErrorControlChain` now supports reload on exceptions
* `FormField::validate` now requires an instance of `Validator`
* Implementation of new "Archive" concept for page removal, which supercedes "delete". Where deletion removed
pages only from draft, archiving removes from both draft and live simultaneously.
#### Deprecated classes/methods removed
@ -367,6 +369,29 @@ After:
Also should be noted is that many functions have been renamed to conform better with
coding conventions. E.g. `DB::requireTable` is now `DB::require_table`
### Revert to legacy CMS page actions
By default "delete from live" and "delete" actions are deprecated in favour of "unpublish" and "archive".
"unpublish" is an existing action which is functionally equivalent to "delete from live", and "archive" is a new
feature which performs both unpublish and deletion simultaneously.
To restore "delete from live" add the following config to your site's config.yml.
:::yml
CMSMain:
enabled_legacy_actions:
- CMSBatchAction_DeleteFromLive
In order to remove the new "archive" action and restore the old "delete" action you can use the following config
:::yml
CMSMain:
enabled_legacy_actions:
- CMSBatchAction_Delete
### Other
* Helper function `DB::placeholders` can be used to generate a comma separated list of placeholders