Merge pull request #1583 from open-sausages/pulls/4.0/namespace-admin

Namespace Admin module
This commit is contained in:
Daniel Hensby 2016-08-17 15:33:55 +01:00 committed by GitHub
commit ef46dee37f
61 changed files with 1457 additions and 1155 deletions

View File

@ -1,5 +1,7 @@
<?php
use SilverStripe\Admin\CMSMenu;
/**
* - CMS_DIR: Path relative to webroot, e.g. "cms"
* - CMS_PATH: Absolute filepath, e.g. "/var/www/my-webroot/cms"
@ -23,5 +25,3 @@ CMSMenu::remove_menu_class('SilverStripe\\CMS\\Controllers\\CMSPageEditControlle
CMSMenu::remove_menu_class('SilverStripe\\CMS\\Controllers\\CMSPageSettingsController');
CMSMenu::remove_menu_class('SilverStripe\\CMS\\Controllers\\CMSPageHistoryController');
CMSMenu::remove_menu_class('SilverStripe\\CMS\\Controllers\\CMSPageAddController');
CMSMenu::remove_menu_item("SiteConfigLeftAndMain");

View File

@ -1,2 +1,2 @@
AdminRootController:
SilverStripe\Admin\AdminRootController:
default_panel: 'SilverStripe\CMS\Controllers\CMSPagesController'

View File

@ -1,4 +1,4 @@
LeftAndMain:
SilverStripe\Admin\LeftAndMain:
extensions:
- SilverStripe\CMS\Controllers\LeftAndMainPageIconsExtension
Controller:

View File

@ -3,4 +3,4 @@ Name: cmslegacy
---
SilverStripe\ORM\DatabaseAdmin:
classname_value_remapping:
SiteTree: 'SilverStripe\CMS\Model\SiteTree'
SiteTree: 'SilverStripe\CMS\Model\SiteTree'

View File

@ -0,0 +1,34 @@
<?php
namespace SilverStripe\CMS\BatchActions;
use SilverStripe\Admin\CMSBatchAction;
use SilverStripe\ORM\SS_List;
/**
* Archives a page, removing it from both live and stage
*
* @package cms
* @subpackage batchaction
*/
class CMSBatchAction_Archive extends CMSBatchAction
{
public function getActionTitle()
{
return _t('CMSBatchActions.ARCHIVE', 'Archive');
}
public function run(SS_List $pages)
{
return $this->batchaction($pages, 'doArchive',
_t('CMSBatchActions.ARCHIVED_PAGES', 'Archived %d pages')
);
}
public function applicablePages($ids)
{
return $this->applicablePagesHelper($ids, 'canArchive', true, true);
}
}

View File

@ -0,0 +1,55 @@
<?php
namespace SilverStripe\CMS\BatchActions;
use SilverStripe\ORM\SS_List;
use SilverStripe\ORM\Versioning\Versioned;
use SilverStripe\Admin\CMSBatchAction;
/**
* Delete items batch action.
*
* @package cms
* @subpackage batchaction
*/
class CMSBatchAction_Delete extends CMSBatchAction {
public function getActionTitle() {
return _t('CMSBatchActions.DELETE_DRAFT_PAGES', 'Delete from draft site');
}
public function run(SS_List $pages) {
$status = array(
'modified'=>array(),
'deleted'=>array(),
'error'=>array()
);
foreach($pages as $page) {
$id = $page->ID;
// Perform the action
if($page->canDelete()) $page->delete();
else $status['error'][$page->ID] = true;
// check to see if the record exists on the live site,
// if it doesn't remove the tree node
$liveRecord = Versioned::get_one_by_stage( 'SilverStripe\\CMS\\Model\\SiteTree', 'Live', array(
'"SiteTree"."ID"' => $id
));
if($liveRecord) {
$status['modified'][$liveRecord->ID] = array(
'TreeTitle' => $liveRecord->TreeTitle,
);
} else {
$status['deleted'][$id] = array();
}
}
return $this->response(_t('CMSBatchActions.DELETED_DRAFT_PAGES', 'Deleted %d pages from draft site, %d failures'), $status);
}
public function applicablePages($ids) {
return $this->applicablePagesHelper($ids, 'canDelete', true, false);
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace SilverStripe\CMS\BatchActions;
use SilverStripe\Admin\CMSBatchAction;
use SilverStripe\ORM\SS_List;
/**
* Publish items batch action.
*
* @package cms
* @subpackage batchaction
*/
class CMSBatchAction_Publish extends CMSBatchAction
{
public function getActionTitle()
{
return _t('CMSBatchActions.PUBLISH_PAGES', 'Publish');
}
public function run(SS_List $pages)
{
return $this->batchaction($pages, 'publishRecursive',
_t('CMSBatchActions.PUBLISHED_PAGES', 'Published %d pages, %d failures')
);
}
public function applicablePages($ids)
{
return $this->applicablePagesHelper($ids, 'canPublish', true, false);
}
}

View File

@ -0,0 +1,62 @@
<?php
namespace SilverStripe\CMS\BatchActions;
use SilverStripe\Admin\CMSBatchAction;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\ORM\ArrayList;
use SilverStripe\ORM\SS_List;
use SilverStripe\ORM\Versioning\Versioned;
use SilverStripe\Security\Permission;
/**
* Batch restore of pages
* @package cms
* @subpackage batchaction
*/
class CMSBatchAction_Restore extends CMSBatchAction
{
public function getActionTitle()
{
return _t('CMSBatchActions.RESTORE', 'Restore');
}
public function run(SS_List $pages)
{
// Sort pages by depth
$pageArray = $pages->toArray();
// because of https://bugs.php.net/bug.php?id=50688
/** @var SiteTree $page */
foreach ($pageArray as $page) {
$page->getPageLevel();
}
usort($pageArray, function (SiteTree $a, SiteTree $b) {
return $a->getPageLevel() - $b->getPageLevel();
});
$pages = new ArrayList($pageArray);
// Restore
return $this->batchaction($pages, 'doRestoreToStage',
_t('CMSBatchActions.RESTORED_PAGES', 'Restored %d pages')
);
}
/**
* {@see SiteTree::canEdit()}
*
* @param array $ids
* @return array
*/
public function applicablePages($ids)
{
// Basic permission check based on SiteTree::canEdit
if (!Permission::check(array("ADMIN", "SITETREE_EDIT_ALL"))) {
return array();
}
// Get pages that exist in stage and remove them from the restore-able set
$stageIDs = Versioned::get_by_stage($this->managedClass, 'Stage')->column('ID');
return array_values(array_diff($ids, $stageIDs));
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace SilverStripe\CMS\BatchActions;
use SilverStripe\Admin\CMSBatchAction;
use SilverStripe\ORM\SS_List;
/**
* Unpublish items batch action.
*
* @package cms
* @subpackage batchaction
*/
class CMSBatchAction_Unpublish extends CMSBatchAction
{
public function getActionTitle()
{
return _t('CMSBatchActions.UNPUBLISH_PAGES', 'Unpublish');
}
public function run(SS_List $pages)
{
return $this->batchaction($pages, 'doUnpublish',
_t('CMSBatchActions.UNPUBLISHED_PAGES', 'Unpublished %d pages')
);
}
public function applicablePages($ids)
{
return $this->applicablePagesHelper($ids, 'canUnpublish', false, true);
}
}

View File

@ -1,174 +0,0 @@
<?php
namespace SilverStripe\CMS\BatchActions;
use SilverStripe\ORM\SS_List;
use SilverStripe\ORM\ArrayList;
use SilverStripe\ORM\Versioning\Versioned;
use SilverStripe\Security\Permission;
use CMSBatchAction;
/**
* Publish items batch action.
*
* @package cms
* @subpackage batchaction
*/
class CMSBatchAction_Publish extends CMSBatchAction {
public function getActionTitle() {
return _t('CMSBatchActions.PUBLISH_PAGES', 'Publish');
}
public function run(SS_List $pages) {
return $this->batchaction($pages, 'publishRecursive',
_t('CMSBatchActions.PUBLISHED_PAGES', 'Published %d pages, %d failures')
);
}
public function applicablePages($ids) {
return $this->applicablePagesHelper($ids, 'canPublish', true, false);
}
}
/**
* Unpublish items batch action.
*
* @package cms
* @subpackage batchaction
*/
class CMSBatchAction_Unpublish extends CMSBatchAction {
public function getActionTitle() {
return _t('CMSBatchActions.UNPUBLISH_PAGES', 'Unpublish');
}
public function run(SS_List $pages) {
return $this->batchaction($pages, 'doUnpublish',
_t('CMSBatchActions.UNPUBLISHED_PAGES', 'Unpublished %d pages')
);
}
public function applicablePages($ids) {
return $this->applicablePagesHelper($ids, 'canUnpublish', false, true);
}
}
/**
* Archives a page, removing it from both live and stage
*
* @package cms
* @subpackage batchaction
*/
class CMSBatchAction_Archive extends CMSBatchAction {
public function getActionTitle() {
return _t('CMSBatchActions.ARCHIVE', 'Archive');
}
public function run(SS_List $pages) {
return $this->batchaction($pages, 'doArchive',
_t('CMSBatchActions.ARCHIVED_PAGES', 'Archived %d pages')
);
}
public function applicablePages($ids) {
return $this->applicablePagesHelper($ids, 'canArchive', true, true);
}
}
/**
* Batch restore of pages
* @package cms
* @subpackage batchaction
*/
class CMSBatchAction_Restore extends CMSBatchAction {
public function getActionTitle() {
return _t('CMSBatchActions.RESTORE', 'Restore');
}
public function run(SS_List $pages) {
// Sort pages by depth
$pageArray = $pages->toArray();
// because of https://bugs.php.net/bug.php?id=50688
foreach($pageArray as $page) {
$page->getPageLevel();
}
usort($pageArray, function($a, $b) {
return $a->getPageLevel() - $b->getPageLevel();
});
$pages = new ArrayList($pageArray);
// Restore
return $this->batchaction($pages, 'doRestoreToStage',
_t('CMSBatchActions.RESTORED_PAGES', 'Restored %d pages')
);
}
/**
* {@see SiteTree::canEdit()}
*
* @param array $ids
* @return bool
*/
public function applicablePages($ids) {
// Basic permission check based on SiteTree::canEdit
if(!Permission::check(array("ADMIN", "SITETREE_EDIT_ALL"))) {
return array();
}
// Get pages that exist in stage and remove them from the restore-able set
$stageIDs = Versioned::get_by_stage($this->managedClass, 'Stage')->column('ID');
return array_values(array_diff($ids, $stageIDs));
}
}
/**
* Delete items batch action.
*
* @package cms
* @subpackage batchaction
*/
class CMSBatchAction_Delete extends CMSBatchAction {
public function getActionTitle() {
return _t('CMSBatchActions.DELETE_DRAFT_PAGES', 'Delete from draft site');
}
public function run(SS_List $pages) {
$status = array(
'modified'=>array(),
'deleted'=>array(),
'error'=>array()
);
foreach($pages as $page) {
$id = $page->ID;
// Perform the action
if($page->canDelete()) $page->delete();
else $status['error'][$page->ID] = true;
// check to see if the record exists on the live site,
// if it doesn't remove the tree node
$liveRecord = Versioned::get_one_by_stage( 'SilverStripe\\CMS\\Model\\SiteTree', 'Live', array(
'"SiteTree"."ID"' => $id
));
if($liveRecord) {
$status['modified'][$liveRecord->ID] = array(
'TreeTitle' => $liveRecord->TreeTitle,
);
} else {
$status['deleted'][$id] = array();
}
}
return $this->response(_t('CMSBatchActions.DELETED_DRAFT_PAGES', 'Deleted %d pages from draft site, %d failures'), $status);
}
public function applicablePages($ids) {
return $this->applicablePagesHelper($ids, 'canDelete', true, false);
}
}

View File

@ -2,17 +2,22 @@
namespace SilverStripe\CMS\Controllers;
use SearchContext;
use SearchFilter;
use SilverStripe\Filesystem\Storage\AssetNameGenerator;
use SilverStripe\ORM\ArrayList;
use SilverStripe\ORM\DataList;
use SilverStripe\ORM\FieldType\DBHTMLText;
use SilverStripe\ORM\Versioning\Versioned;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\SS_List;
use SilverStripe\Security\Security;
use SilverStripe\Security\PermissionProvider;
use LeftAndMain;
use SilverStripe\Admin\CMSBatchAction;
use SilverStripe\Admin\CMSBatchActionHandler;
use SilverStripe\Admin\LeftAndMain;
use Session;
use Requirements;
use CMSBatchActionHandler;
use File;
use DateField;
use HiddenField;
@ -29,6 +34,7 @@ use GridFieldLevelup;
use GridField;
use Controller;
use LiteralField;
use SS_HTTPRequest;
use TabSet;
use Tab;
use CompositeField;
@ -50,7 +56,7 @@ use Folder;
use Injector;
use Director;
use ArrayData;
use CMSBatchAction;
@ -94,6 +100,8 @@ class AssetAdmin extends LeftAndMain implements PermissionProvider{
'getsubtree'
);
private static $required_permission_codes = 'CMS_ACCESS_AssetAdmin';
/**
* Return fake-ID "root" if no ID is found (needed to upload files into the root-folder)
*/
@ -136,6 +144,7 @@ class AssetAdmin extends LeftAndMain implements PermissionProvider{
// Overwrite name filter to search both Name and Title attributes
$context->removeFilterByName('Name');
$params = $this->getRequest()->requestVar('q');
/** @var DataList $list */
$list = $context->getResults($params);
// Don't filter list when a detail view is requested,
@ -216,6 +225,7 @@ class AssetAdmin extends LeftAndMain implements PermissionProvider{
);
$gridField = GridField::create('File', $title, $this->getList(), $gridFieldConfig);
/** @var GridFieldDataColumns $columns */
$columns = $gridField->getConfig()->getComponentByType('GridFieldDataColumns');
$columns->setDisplayFields(array(
'StripThumbnail' => '',
@ -253,14 +263,14 @@ class AssetAdmin extends LeftAndMain implements PermissionProvider{
// Move existing fields to a "details" tab, unless they've already been tabbed out through extensions.
// Required to keep Folder->getCMSFields() simple and reuseable,
// without any dependencies into AssetAdmin (e.g. useful for "add folder" views).
if(!$fields->hasTabset()) {
if(!$fields->hasTabSet()) {
$tabs = new TabSet('Root',
$tabList = new Tab('ListView', _t('AssetAdmin.ListView', 'List View')),
$tabTree = new Tab('TreeView', _t('AssetAdmin.TreeView', 'Tree View'))
);
$tabList->addExtraClass("content-listview cms-tabset-icon list");
$tabTree->addExtraClass("content-treeview cms-tabset-icon tree");
if($fields->Count() && $folder && $folder->isInDB()) {
if($fields->count() && $folder && $folder->isInDB()) {
$tabs->push($tabDetails = new Tab('DetailsView', _t('AssetAdmin.DetailsView', 'Details')));
$tabDetails->addExtraClass("content-galleryview cms-tabset-icon edit");
foreach($fields as $field) {
@ -370,6 +380,10 @@ class AssetAdmin extends LeftAndMain implements PermissionProvider{
return $form;
}
/**
* @param SS_HTTPRequest $request
* @return DBHTMLText
*/
public function addfolder($request) {
$obj = $this->customise(array(
'EditForm' => $this->AddForm()
@ -410,12 +424,14 @@ class AssetAdmin extends LeftAndMain implements PermissionProvider{
* @return SearchContext
*/
public function getSearchContext() {
$context = singleton('File')->getDefaultSearchContext();
$context = File::singleton()->getDefaultSearchContext();
// Namespace fields, for easier detection if a search is present
/** @var FormField $field */
foreach($context->getFields() as $field) {
$field->setName(sprintf('q[%s]', $field->getName()));
}
/** @var SearchFilter $filter */
foreach($context->getFilters() as $filter) {
$filter->setFullName(sprintf('q[%s]', $filter->getFullName()));
}
@ -526,6 +542,10 @@ class AssetAdmin extends LeftAndMain implements PermissionProvider{
* Add a new group and return its details suitable for ajax.
*
* @todo Move logic into Folder class, and use LeftAndMain->doAdd() default implementation.
*
* @param array $data
* @param Form $form
* @return SS_HTTPResponse|string
*/
public function doAdd($data, $form) {
$class = $this->stat('tree_class');
@ -536,6 +556,7 @@ class AssetAdmin extends LeftAndMain implements PermissionProvider{
}
// check addchildren permissions
/** @var File $parentRecord */
if(
singleton($class)->hasExtension('SilverStripe\ORM\Hierarchy\Hierarchy')
&& isset($data['ParentID'])
@ -676,37 +697,3 @@ class AssetAdmin extends LeftAndMain implements PermissionProvider{
}
}
/**
* Delete multiple {@link Folder} records (and the associated filesystem nodes).
* Usually used through the {@link AssetAdmin} interface.
*
* @package cms
* @subpackage batchactions
*/
class AssetAdmin_DeleteBatchAction extends CMSBatchAction {
public function getActionTitle() {
// _t('AssetAdmin_left_ss.SELECTTODEL','Select the folders that you want to delete and then click the button below')
return _t('AssetAdmin_DeleteBatchAction.TITLE', 'Delete folders');
}
public function run(SS_List $records) {
$status = array(
'modified'=>array(),
'deleted'=>array()
);
foreach($records as $record) {
$id = $record->ID;
// Perform the action
if($record->canDelete()) $record->delete();
$status['deleted'][$id] = array();
$record->destroy();
unset($record);
}
return Convert::raw2json($status);
}
}

View File

@ -0,0 +1,47 @@
<?php
namespace SilverStripe\CMS\Controllers;
use Convert;
use SilverStripe\Admin\CMSBatchAction;
use SilverStripe\ORM\SS_List;
/**
* Delete multiple {@link Folder} records (and the associated filesystem nodes).
* Usually used through the {@link AssetAdmin} interface.
*
* @package cms
* @subpackage batchactions
*/
class AssetAdmin_DeleteBatchAction extends CMSBatchAction
{
public function getActionTitle()
{
// _t('AssetAdmin_left_ss.SELECTTODEL','Select the folders that you want to delete and then click the button below')
return _t('AssetAdmin_DeleteBatchAction.TITLE', 'Delete folders');
}
public function run(SS_List $records)
{
$status = array(
'modified' => array(),
'deleted' => array()
);
foreach ($records as $record) {
$id = $record->ID;
// Perform the action
if ($record->canDelete()) {
$record->delete();
}
$status['deleted'][$id] = array();
$record->destroy();
unset($record);
}
return Convert::raw2json($status);
}
}

View File

@ -2,6 +2,8 @@
namespace SilverStripe\CMS\Controllers;
use FormField;
use Injector;
use ResetFormAction;
use SilverStripe\ORM\FieldType\DBHTMLText;
use SilverStripe\ORM\SS_List;
@ -16,33 +18,33 @@ use SilverStripe\Security\Security;
use SilverStripe\Security\SecurityToken;
use SilverStripe\Security\Permission;
use SilverStripe\Security\PermissionProvider;
use LeftAndMain;
use SilverStripe\Admin\AdminRootController;
use SilverStripe\Admin\LeftAndMain;
use SilverStripe\Admin\CMSBatchActionHandler;
use SilverStripe\Admin\CMSPreviewable;
use SilverStripe\Admin\AddToCampaignHandler;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\CMS\Model\RedirectorPage;
use SilverStripe\CMS\Model\CurrentPageIdentifier;
use SS_HTTPRequest;
use TabSet;
use Translatable;
use Requirements;
use CMSBatchActionHandler;
use Controller;
use AdminRootController;
use Director;
use Page;
use TextField;
use HeaderField;
use DateField;
use DropdownField;
use FieldGroup;
use FieldList;
use FormAction;
use Object;
use Form;
use SS_Cache;
use Zend_Cache;
use Convert;
use ArrayData;
use HiddenField;
use CMSPreviewable;
use LiteralField;
use RequiredFields;
use LabelField;
@ -55,16 +57,7 @@ use GridFieldLevelup;
use GridField;
use SS_HTTPResponse_Exception;
use Session;
use AddToCampaignHandler;
use HTMLEditorField;
use SS_HTTPResponse;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\CMS\Model\RedirectorPage;
use SilverStripe\CMS\Model\CurrentPageIdentifier;
/**
* The main "content" area of the CMS.
@ -75,6 +68,8 @@ use SilverStripe\CMS\Model\CurrentPageIdentifier;
* @package cms
* @subpackage controller
* @todo Create some base classes to contain the generic functionality that will be replicated.
*
* @mixin LeftAndMainPageIconsExtension
*/
class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionProvider {
@ -96,6 +91,8 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
private static $session_namespace = 'SilverStripe\\CMS\\Controllers\\CMSMain';
private static $required_permission_codes = 'CMS_ACCESS_CMSMain';
/**
* Amount of results showing on a single page.
*
@ -172,7 +169,9 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
public function index($request) {
// In case we're not showing a specific record, explicitly remove any session state,
// to avoid it being highlighted in the tree, and causing an edit form to show.
if(!$request->param('Action')) $this->setCurrentPageId(null);
if(!$request->param('Action')) {
$this->setCurrentPageID(null);
}
return parent::index($request);
}
@ -199,6 +198,9 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
/**
* Overloads the LeftAndMain::ShowView. Allows to pass a page as a parameter, so we are able
* to switch view also for archived versions.
*
* @param SiteTree $page
* @return array
*/
public function SwitchView($page = null) {
if(!$page) {
@ -226,7 +228,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
public function Link($action = null) {
$link = Controller::join_links(
AdminRootController::admin_url(),
$this->stat('url_segment', true), // in case we want to change the segment
$this->stat('url_segment'), // in case we want to change the segment
'/', // trailing slash needed if $action is null!
"$action"
);
@ -400,7 +402,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
$dateFrom,
$dateTo
);
$dateGroup->setTitle('Last Edited', _t('CMSSearch.PAGEFILTERDATEHEADING', 'Last edited'));
$dateGroup->setTitle(_t('CMSSearch.PAGEFILTERDATEHEADING', 'Last edited'));
// Create the Field list
$fields = new FieldList(
@ -418,6 +420,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
);
// Use <button> to allow full jQuery UI styling on the all of the Actions
/** @var FormAction $action */
foreach($actions->dataFields() as $action) {
/** @var FormAction $action */
$action->setUseButtonTag(true);
@ -482,17 +485,18 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
* @return string Serialized JSON
*/
public function SiteTreeHints() {
$json = '';
$classes = SiteTree::page_type_classes();
$cacheCanCreate = array();
foreach($classes as $class) $cacheCanCreate[$class] = singleton($class)->canCreate();
$cacheCanCreate = array();
foreach($classes as $class) $cacheCanCreate[$class] = singleton($class)->canCreate();
// Generate basic cache key. Too complex to encompass all variations
$cache = SS_Cache::factory('CMSMain_SiteTreeHints');
$cacheKey = md5(implode('_', array(Member::currentUserID(), implode(',', $cacheCanCreate), implode(',', $classes))));
if($this->getRequest()->getVar('flush')) $cache->clean(Zend_Cache::CLEANING_MODE_ALL);
$json = $cache->load($cacheKey);
// Generate basic cache key. Too complex to encompass all variations
$cache = SS_Cache::factory('CMSMain_SiteTreeHints');
$cacheKey = md5(implode('_', array(Member::currentUserID(), implode(',', $cacheCanCreate), implode(',', $classes))));
if($this->getRequest()->getVar('flush')) {
$cache->clean(Zend_Cache::CLEANING_MODE_ALL);
}
$json = $cache->load($cacheKey);
if(!$json) {
$def['Root'] = array();
$def['Root']['disallowedChildren'] = array();
@ -706,7 +710,8 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
$actions = $record->getCMSActions();
// Find and remove action menus that have no actions.
if ($actions && $actions->Count()) {
if ($actions && $actions->count()) {
/** @var TabSet $tabset */
$tabset = $actions->fieldByName('ActionMenus');
if ($tabset) {
foreach ($tabset->getChildren() as $tab) {
@ -720,7 +725,12 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
// Use <button> to allow full jQuery UI styling
$actionsFlattened = $actions->dataFields();
if($actionsFlattened) foreach($actionsFlattened as $action) $action->setUseButtonTag(true);
if($actionsFlattened) {
/** @var FormAction $action */
foreach($actionsFlattened as $action) {
$action->setUseButtonTag(true);
}
}
if($record->hasMethod('getCMSValidator')) {
$validator = $record->getCMSValidator();
@ -734,7 +744,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
$form->setValidationExemptActions(array('restore', 'revert', 'deletefromlive', 'delete', 'unpublish', 'rollback', 'doRollback'));
// Announce the capability so the frontend can decide whether to allow preview or not.
if(in_array('CMSPreviewable', class_implements($record))) {
if ($record instanceof CMSPreviewable) {
$form->addExtraClass('cms-previewable');
}
@ -840,6 +850,9 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
}
}
/**
* @return Form
*/
public function ListViewForm() {
$params = $this->getRequest()->requestVar('q');
$list = $this->getList($params, $parentID = $this->getRequest()->requestVar('ParentID'));
@ -856,6 +869,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
);
}
$gridField = new GridField('Page','Pages', $list, $gridFieldConfig);
/** @var GridFieldDataColumns $columns */
$columns = $gridField->getConfig()->getComponentByType('GridFieldDataColumns');
// Don't allow navigating into children nodes on filtered lists
@ -864,7 +878,9 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
'singular_name' => _t('SiteTree.PAGETYPE'),
'LastEdited' => _t('SiteTree.LASTUPDATED', 'Last Updated'),
);
$gridField->getConfig()->getComponentByType('GridFieldSortableHeader')->setFieldSorting(array('getTreeTitle' => 'Title'));
/** @var GridFieldSortableHeader $sortableHeader */
$sortableHeader = $gridField->getConfig()->getComponentByType('GridFieldSortableHeader');
$sortableHeader->setFieldSorting(array('getTreeTitle' => 'Title'));
$gridField->getState()->ParentID = $parentID;
if(!$params) {
@ -881,6 +897,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
$controller = $this;
$columns->setFieldFormatting(array(
'listChildrenLink' => function($value, &$item) use($controller) {
/** @var SiteTree $item */
$num = $item ? $item->numChildren() : null;
if($num) {
return sprintf(
@ -1025,12 +1042,17 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
/**
* @uses LeftAndMainExtension->augmentNewSiteTreeItem()
*
* @param int|string $id
* @param bool $setID
* @return mixed|DataObject
* @throws SS_HTTPResponse_Exception
*/
public function getNewItem($id, $setID = true) {
$parentClass = $this->stat('tree_class');
list($dummy, $className, $parentID, $suffix) = array_pad(explode('-',$id),4,null);
if(!is_subclass_of($className, $parentClass) && strcasecmp($className, $parentClass) != 0) {
if (!is_a($className, $parentClass, true)) {
$response = Security::permissionFailure($this);
if (!$response) {
$response = $this->getResponse();
@ -1038,8 +1060,8 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
throw new SS_HTTPResponse_Exception($response);
}
$newItem = new $className();
/** @var SiteTree $newItem */
$newItem = Injector::inst()->create($className);
if( !$suffix ) {
$sessionTag = "NewItems." . $parentID . "." . $className;
if(Session::get($sessionTag)) {
@ -1232,7 +1254,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
}
/**
* @return array
* @return SS_HTTPResponse
*/
public function rollback() {
return $this->doRollback(array(
@ -1314,6 +1336,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
$SNG_action = singleton($batchAction);
if ($SNG_action->canView() && $fieldset = $SNG_action->getParameterFields()) {
$formHtml = '';
/** @var FormField $field */
foreach($fieldset as $field) {
$formHtml .= $field->Field();
}
@ -1349,6 +1372,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
$pages = SiteTree::get()->limit("$start,30");
$count = 0;
while($pages) {
/** @var SiteTree $page */
foreach($pages as $page) {
if($page && !$page->canPublish()) {
return Security::permissionFailure($this);
@ -1373,7 +1397,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
$token = SecurityToken::inst();
$fields = new FieldList();
$token->updateFieldSet($fields);
$tokenField = $fields->First();
$tokenField = $fields->first();
$tokenHtml = ($tokenField) ? $tokenField->FieldHolder() : '';
$response .= '<h1>' . _t('CMSMain.PUBALLFUN','"Publish All" functionality') . '</h1>
<p>' . _t('CMSMain.PUBALLFUN2', 'Pressing this button will do the equivalent of going to every page and pressing "publish". It\'s
@ -1391,6 +1415,10 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
/**
* Restore a completely deleted page from the SiteTree_versions table.
*
* @param array $data
* @param Form $form
* @return SS_HTTPResponse
*/
public function restore($data, $form) {
if(!isset($data['ID']) || !is_numeric($data['ID'])) {
@ -1398,8 +1426,11 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
}
$id = (int)$data['ID'];
/** @var SiteTree $restoredPage */
$restoredPage = Versioned::get_latest_version("SilverStripe\\CMS\\Model\\SiteTree", $id);
if(!$restoredPage) return new SS_HTTPResponse("SiteTree #$id not found", 400);
if(!$restoredPage) {
return new SS_HTTPResponse("SiteTree #$id not found", 400);
}
$restoredPage = $restoredPage->doRestoreToStage();
@ -1420,6 +1451,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
if(!SecurityToken::inst()->checkRequest($request)) return $this->httpError(400);
if(($id = $this->urlParams['ID']) && is_numeric($id)) {
/** @var SiteTree $page */
$page = SiteTree::get()->byID($id);
if($page && (!$page->canEdit() || !$page->canCreate(null, array('Parent' => $page->Parent())))) {
return Security::permissionFailure($this);
@ -1458,6 +1490,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
if(!SecurityToken::inst()->checkRequest($request)) return $this->httpError(400);
increase_time_limit_to();
if(($id = $this->urlParams['ID']) && is_numeric($id)) {
/** @var SiteTree $page */
$page = SiteTree::get()->byID($id);
if($page && (!$page->canEdit() || !$page->canCreate(null, array('Parent' => $page->Parent())))) {
return Security::permissionFailure($this);

View File

@ -11,11 +11,11 @@ use FieldList;
use LiteralField;
use SelectionGroup;
use SelectionGroup_Item;
use SS_HTTPResponse;
use TreeDropdownField;
use OptionsetField;
use FormAction;
use Form;
use Session;
use Controller;
use SilverStripe\CMS\Model\SiteTree;
@ -151,8 +151,8 @@ class CMSPageAddController extends CMSPageEditController {
$form->setValidationResponseCallback(function() use ($negotiator, $form) {
$request = $this->getRequest();
if($request->isAjax() && $negotiator) {
$this->setupFormErrors();
$result = $this->forTemplate();
$form->setupFormErrors();
$result = $form->forTemplate();
return $negotiator->respond($request, array(
'CurrentForm' => function() use($result) {
@ -167,6 +167,11 @@ class CMSPageAddController extends CMSPageEditController {
return $form;
}
/**
* @param array $data
* @param Form $form
* @return SS_HTTPResponse
*/
public function doAdd($data, $form) {
$className = isset($data['PageType']) ? $data['PageType'] : "Page";
$parentID = isset($data['ParentID']) ? (int)$data['ParentID'] : 0;

View File

@ -403,6 +403,7 @@ class CMSPageHistoryController extends CMSMain {
/** @var SiteTree $page */
$page = SiteTree::get()->byID($id);
$record = null;
if($page && $page->exists()) {
if(!$page->canView()) {
return Security::permissionFailure($this);

View File

@ -7,9 +7,10 @@ use SilverStripe\ORM\DataList;
use SilverStripe\ORM\SS_List;
use SilverStripe\ORM\Versioning\Versioned;
use Object;
use LeftAndMain_SearchFilter;
use ClassInfo;
use DateField;
use SilverStripe\Admin\LeftAndMain_SearchFilter;
/**
* Base class for filtering the subtree for certain node statuses.
@ -239,220 +240,3 @@ abstract class CMSSiteTreeFilter extends Object implements LeftAndMain_SearchFil
return $ids;
}
}
/**
* This filter will display the SiteTree as a site visitor might see the site, i.e only the
* pages that is currently published.
*
* Note that this does not check canView permissions that might hide pages from certain visitors
*
* @package cms
* @subpackage content
*/
class CMSSiteTreeFilter_PublishedPages extends CMSSiteTreeFilter {
/**
* @return string
*/
static public function title() {
return _t('CMSSIteTreeFilter_PublishedPages.Title', "Published pages");
}
/**
* @var string
*/
protected $childrenMethod = "AllHistoricalChildren";
/**
* @var string
*/
protected $numChildrenMethod = 'numHistoricalChildren';
/**
* Filters out all pages who's status who's status that doesn't exist on live
*
* @see {@link SiteTree::getStatusFlags()}
* @return SS_List
*/
public function getFilteredPages() {
$pages = Versioned::get_including_deleted('SilverStripe\\CMS\\Model\\SiteTree');
$pages = $this->applyDefaultFilters($pages);
$pages = $pages->filterByCallback(function(SiteTree $page) {
return $page->getExistsOnLive();
});
return $pages;
}
}
/**
* Works a bit different than the other filters:
* Shows all pages *including* those deleted from stage and live.
* It does not filter out pages still existing in the different stages.
*
* @package cms
* @subpackage content
*/
class CMSSiteTreeFilter_DeletedPages extends CMSSiteTreeFilter {
/**
* @var string
*/
protected $childrenMethod = "AllHistoricalChildren";
/**
* @var string
*/
protected $numChildrenMethod = 'numHistoricalChildren';
static public function title() {
return _t('CMSSiteTreeFilter_DeletedPages.Title', "All pages, including archived");
}
public function getFilteredPages() {
$pages = Versioned::get_including_deleted('SilverStripe\\CMS\\Model\\SiteTree');
$pages = $this->applyDefaultFilters($pages);
return $pages;
}
}
/**
* Gets all pages which have changed on stage.
*
* @package cms
* @subpackage content
*/
class CMSSiteTreeFilter_ChangedPages extends CMSSiteTreeFilter {
static public function title() {
return _t('CMSSiteTreeFilter_ChangedPages.Title', "Modified pages");
}
public function getFilteredPages() {
$pages = Versioned::get_by_stage('SilverStripe\\CMS\\Model\\SiteTree', 'Stage');
$pages = $this->applyDefaultFilters($pages)
->leftJoin('SiteTree_Live', '"SiteTree_Live"."ID" = "SiteTree"."ID"')
->where('"SiteTree"."Version" <> "SiteTree_Live"."Version"');
return $pages;
}
}
/**
* Filters pages which have a status "Removed from Draft".
*
* @package cms
* @subpackage content
*/
class CMSSiteTreeFilter_StatusRemovedFromDraftPages extends CMSSiteTreeFilter {
static public function title() {
return _t('CMSSiteTreeFilter_StatusRemovedFromDraftPages.Title', 'Live but removed from draft');
}
/**
* Filters out all pages who's status is set to "Removed from draft".
*
* @return SS_List
*/
public function getFilteredPages() {
$pages = Versioned::get_including_deleted('SilverStripe\\CMS\\Model\\SiteTree');
$pages = $this->applyDefaultFilters($pages);
$pages = $pages->filterByCallback(function(SiteTree $page) {
// If page is removed from stage but not live
return $page->getIsDeletedFromStage() && $page->getExistsOnLive();
});
return $pages;
}
}
/**
* Filters pages which have a status "Draft".
*
* @package cms
* @subpackage content
*/
class CMSSiteTreeFilter_StatusDraftPages extends CMSSiteTreeFilter {
static public function title() {
return _t('CMSSiteTreeFilter_StatusDraftPages.Title', 'Draft pages');
}
/**
* Filters out all pages who's status is set to "Draft".
*
* @see {@link SiteTree::getStatusFlags()}
* @return SS_List
*/
public function getFilteredPages() {
$pages = Versioned::get_by_stage('SilverStripe\\CMS\\Model\\SiteTree', 'Stage');
$pages = $this->applyDefaultFilters($pages);
$pages = $pages->filterByCallback(function(SiteTree $page) {
// If page exists on stage but not on live
return (!$page->getIsDeletedFromStage() && $page->getIsAddedToStage());
});
return $pages;
}
}
/**
* Filters pages which have a status "Deleted".
*
* @package cms
* @subpackage content
*/
class CMSSiteTreeFilter_StatusDeletedPages extends CMSSiteTreeFilter {
/**
* @var string
*/
protected $childrenMethod = "AllHistoricalChildren";
/**
* @var string
*/
protected $numChildrenMethod = 'numHistoricalChildren';
static public function title() {
return _t('CMSSiteTreeFilter_StatusDeletedPages.Title', 'Archived pages');
}
/**
* Filters out all pages who's status is set to "Deleted".
*
* @see {@link SiteTree::getStatusFlags()}
* @return SS_List
*/
public function getFilteredPages() {
$pages = Versioned::get_including_deleted('SilverStripe\\CMS\\Model\\SiteTree');
$pages = $this->applyDefaultFilters($pages);
$pages = $pages->filterByCallback(function(SiteTree $page) {
// Doesn't exist on either stage or live
return $page->getIsDeletedFromStage() && !$page->getExistsOnLive();
});
return $pages;
}
}
/**
* @package cms
* @subpackage content
*/
class CMSSiteTreeFilter_Search extends CMSSiteTreeFilter {
static public function title() {
return _t('CMSSiteTreeFilter_Search.Title', "All pages");
}
/**
* Retun an array of maps containing the keys, 'ID' and 'ParentID' for each page to be displayed
* in the search.
*
* @return SS_List
*/
public function getFilteredPages() {
// Filter default records
$pages = Versioned::get_by_stage('SilverStripe\\CMS\\Model\\SiteTree', 'Stage');
$pages = $this->applyDefaultFilters($pages);
return $pages;
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace SilverStripe\CMS\Controllers;
use SilverStripe\ORM\Versioning\Versioned;
/**
* Gets all pages which have changed on stage.
*
* @package cms
* @subpackage content
*/
class CMSSiteTreeFilter_ChangedPages extends CMSSiteTreeFilter
{
static public function title()
{
return _t('CMSSiteTreeFilter_ChangedPages.Title', "Modified pages");
}
public function getFilteredPages()
{
$pages = Versioned::get_by_stage('SilverStripe\\CMS\\Model\\SiteTree', 'Stage');
$pages = $this->applyDefaultFilters($pages)
->leftJoin('SiteTree_Live', '"SiteTree_Live"."ID" = "SiteTree"."ID"')
->where('"SiteTree"."Version" <> "SiteTree_Live"."Version"');
return $pages;
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace SilverStripe\CMS\Controllers;
use SilverStripe\ORM\Versioning\Versioned;
/**
* Works a bit different than the other filters:
* Shows all pages *including* those deleted from stage and live.
* It does not filter out pages still existing in the different stages.
*
* @package cms
* @subpackage content
*/
class CMSSiteTreeFilter_DeletedPages extends CMSSiteTreeFilter
{
/**
* @var string
*/
protected $childrenMethod = "AllHistoricalChildren";
/**
* @var string
*/
protected $numChildrenMethod = 'numHistoricalChildren';
static public function title()
{
return _t('CMSSiteTreeFilter_DeletedPages.Title', "All pages, including archived");
}
public function getFilteredPages()
{
$pages = Versioned::get_including_deleted('SilverStripe\\CMS\\Model\\SiteTree');
$pages = $this->applyDefaultFilters($pages);
return $pages;
}
}

View File

@ -0,0 +1,54 @@
<?php
namespace SilverStripe\CMS\Controllers;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\ORM\SS_List;
use SilverStripe\ORM\Versioning\Versioned;
/**
* This filter will display the SiteTree as a site visitor might see the site, i.e only the
* pages that is currently published.
*
* Note that this does not check canView permissions that might hide pages from certain visitors
*
* @package cms
* @subpackage content
*/
class CMSSiteTreeFilter_PublishedPages extends CMSSiteTreeFilter
{
/**
* @return string
*/
static public function title()
{
return _t('CMSSIteTreeFilter_PublishedPages.Title', "Published pages");
}
/**
* @var string
*/
protected $childrenMethod = "AllHistoricalChildren";
/**
* @var string
*/
protected $numChildrenMethod = 'numHistoricalChildren';
/**
* Filters out all pages who's status who's status that doesn't exist on live
*
* @see {@link SiteTree::getStatusFlags()}
* @return SS_List
*/
public function getFilteredPages()
{
$pages = Versioned::get_including_deleted('SilverStripe\\CMS\\Model\\SiteTree');
$pages = $this->applyDefaultFilters($pages);
$pages = $pages->filterByCallback(function (SiteTree $page) {
return $page->getExistsOnLive();
});
return $pages;
}
}

View File

@ -0,0 +1,32 @@
<?php
namespace SilverStripe\CMS\Controllers;
use SilverStripe\ORM\SS_List;
use SilverStripe\ORM\Versioning\Versioned;
/**
* @package cms
* @subpackage content
*/
class CMSSiteTreeFilter_Search extends CMSSiteTreeFilter
{
static public function title()
{
return _t('CMSSiteTreeFilter_Search.Title', "All pages");
}
/**
* Retun an array of maps containing the keys, 'ID' and 'ParentID' for each page to be displayed
* in the search.
*
* @return SS_List
*/
public function getFilteredPages()
{
// Filter default records
$pages = Versioned::get_by_stage('SilverStripe\\CMS\\Model\\SiteTree', 'Stage');
$pages = $this->applyDefaultFilters($pages);
return $pages;
}
}

View File

@ -0,0 +1,50 @@
<?php
namespace SilverStripe\CMS\Controllers;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\ORM\SS_List;
use SilverStripe\ORM\Versioning\Versioned;
/**
* Filters pages which have a status "Deleted".
*
* @package cms
* @subpackage content
*/
class CMSSiteTreeFilter_StatusDeletedPages extends CMSSiteTreeFilter
{
/**
* @var string
*/
protected $childrenMethod = "AllHistoricalChildren";
/**
* @var string
*/
protected $numChildrenMethod = 'numHistoricalChildren';
static public function title()
{
return _t('CMSSiteTreeFilter_StatusDeletedPages.Title', 'Archived pages');
}
/**
* Filters out all pages who's status is set to "Deleted".
*
* @see {@link SiteTree::getStatusFlags()}
* @return SS_List
*/
public function getFilteredPages()
{
$pages = Versioned::get_including_deleted('SilverStripe\\CMS\\Model\\SiteTree');
$pages = $this->applyDefaultFilters($pages);
$pages = $pages->filterByCallback(function (SiteTree $page) {
// Doesn't exist on either stage or live
return $page->getIsDeletedFromStage() && !$page->getExistsOnLive();
});
return $pages;
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace SilverStripe\CMS\Controllers;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\ORM\SS_List;
use SilverStripe\ORM\Versioning\Versioned;
/**
* Filters pages which have a status "Draft".
*
* @package cms
* @subpackage content
*/
class CMSSiteTreeFilter_StatusDraftPages extends CMSSiteTreeFilter
{
static public function title()
{
return _t('CMSSiteTreeFilter_StatusDraftPages.Title', 'Draft pages');
}
/**
* Filters out all pages who's status is set to "Draft".
*
* @see {@link SiteTree::getStatusFlags()}
* @return SS_List
*/
public function getFilteredPages()
{
$pages = Versioned::get_by_stage('SilverStripe\\CMS\\Model\\SiteTree', 'Stage');
$pages = $this->applyDefaultFilters($pages);
$pages = $pages->filterByCallback(function (SiteTree $page) {
// If page exists on stage but not on live
return (!$page->getIsDeletedFromStage() && $page->getIsAddedToStage());
});
return $pages;
}
}

View File

@ -0,0 +1,38 @@
<?php
namespace SilverStripe\CMS\Controllers;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\ORM\SS_List;
use SilverStripe\ORM\Versioning\Versioned;
/**
* Filters pages which have a status "Removed from Draft".
*
* @package cms
* @subpackage content
*/
class CMSSiteTreeFilter_StatusRemovedFromDraftPages extends CMSSiteTreeFilter
{
static public function title()
{
return _t('CMSSiteTreeFilter_StatusRemovedFromDraftPages.Title', 'Live but removed from draft');
}
/**
* Filters out all pages who's status is set to "Removed from draft".
*
* @return SS_List
*/
public function getFilteredPages()
{
$pages = Versioned::get_including_deleted('SilverStripe\\CMS\\Model\\SiteTree');
$pages = $this->applyDefaultFilters($pages);
$pages = $pages->filterByCallback(function (SiteTree $page) {
// If page is removed from stage but not live
return $page->getIsDeletedFromStage() && $page->getExistsOnLive();
});
return $pages;
}
}

View File

@ -17,9 +17,7 @@ use SilverStripe\Security\Member;
use SilverStripe\Security\Permission;
use Controller;
use Page;
use SiteConfig;
use Config;
use SS_HTTPRequest;
use Translatable;
use i18n;

View File

@ -4,10 +4,8 @@ namespace SilverStripe\CMS\Controllers;
use SilverStripe\ORM\DataExtension;
use SilverStripe\ORM\DataObject;
use SilverStripe\CMS\Model\ErrorPage;
/**
* Decorates {@see File} with ErrorPage support
*/

View File

@ -7,7 +7,6 @@ use SilverStripe\ORM\DataModel;
use SilverStripe\ORM\DB;
use SilverStripe\ORM\DataObject;
use Controller;
use ClassInfo;
use Injector;
use SS_HTTPRequest;

View File

@ -11,7 +11,6 @@ use SS_HTTPResponse;
use Controller;
use SS_HTTPResponse_Exception;
class OldPageRedirector extends Extension {
/**

View File

@ -14,7 +14,6 @@ use SS_HTTPRequest;
use ClassInfo;
use Director;
/**
* @package cms
* @subpackage control

View File

@ -5,17 +5,10 @@ namespace SilverStripe\CMS\Controllers;
use SilverStripe\ORM\ArrayList;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\SS_List;
use SilverStripe\ORM\Versioning\Versioned;
use SilverStripe\ORM\FieldType\DBField;
use SilverStripe\Security\Member;
use ViewableData;
use CMSPreviewable;
use ClassInfo;
use LeftAndMain;
use Controller;
use SilverStripe\Admin\CMSPreviewable;
use SiteTreeFutureState;
use SilverStripe\CMS\Model\RedirectorPage;
/**
@ -108,309 +101,3 @@ class SilverStripeNavigator extends ViewableData {
);
}
}
/**
* Navigator items are links that appear in the $SilverStripeNavigator bar.
* To add an item, extend this class - it will be automatically picked up.
* When instanciating items manually, please ensure to call {@link canView()}.
*
* @package cms
* @subpackage content
*/
abstract class SilverStripeNavigatorItem extends ViewableData {
/**
* @param DataObject|CMSPreviewable
*/
protected $record;
/** @var string */
protected $recordLink;
/**
* @param DataObject|CMSPreviewable $record
*/
public function __construct(CMSPreviewable $record) {
parent::__construct();
$this->record = $record;
}
/**
* @return string HTML, mostly a link - but can be more complex as well.
* For example, a "future state" item might show a date selector.
*/
abstract public function getHTML();
/**
* @return string
* Get the Title of an item
*/
abstract public function getTitle();
/**
* Machine-friendly name.
*
* @return string
*/
public function getName() {
return substr(get_class($this), strpos(get_class($this), '_')+1);
}
/**
* Optional link to a specific view of this record.
* Not all items are simple links, please use {@link getHTML()}
* to represent an item in markup unless you know what you're doing.
*
* @return string
*/
public function getLink() {}
/**
* @return string
*/
public function getMessage() {}
/**
* @return DataObject
*/
public function getRecord() {
return $this->record;
}
/**
* @return int
*/
public function getPriority() {
return $this->stat('priority');
}
/**
* As items might convey different record states like a "stage" or "live" table,
* an item can be active (showing the record in this state).
*
* @return boolean
*/
public function isActive() {
return false;
}
/**
* Filters items based on member permissions or other criteria,
* such as if a state is generally available for the current record.
*
* @param Member $member
* @return Boolean
*/
public function canView($member = null) {
return true;
}
/**
* Counts as "archived" if the current record is a different version from both live and draft.
*
* @return boolean
*/
public function isArchived() {
if(!$this->record->hasExtension('SilverStripe\ORM\Versioning\Versioned')) return false;
if(!isset($this->record->_cached_isArchived)) {
$baseClass = $this->record->baseClass();
$currentDraft = Versioned::get_by_stage($baseClass, Versioned::DRAFT)->byID($this->record->ID);
$currentLive = Versioned::get_by_stage($baseClass, Versioned::LIVE)->byID($this->record->ID);
$this->record->_cached_isArchived = (
(!$currentDraft || ($currentDraft && $this->record->Version != $currentDraft->Version))
&& (!$currentLive || ($currentLive && $this->record->Version != $currentLive->Version))
);
}
return $this->record->_cached_isArchived;
}
}
/**
* @package cms
* @subpackage content
*/
class SilverStripeNavigatorItem_CMSLink extends SilverStripeNavigatorItem {
/** @config */
private static $priority = 10;
public function getHTML() {
return sprintf(
'<a href="%s">%s</a>',
$this->record->CMSEditLink(),
_t('ContentController.CMS', 'CMS')
);
}
public function getTitle() {
return _t('ContentController.CMS', 'CMS', 'Used in navigation. Should be a short label');
}
public function getLink() {
return $this->record->CMSEditLink();
}
public function isActive() {
return (Controller::curr() instanceof LeftAndMain);
}
public function canView($member = null) {
return (
// Don't show in CMS
!(Controller::curr() instanceof LeftAndMain)
// Don't follow redirects in preview, they break the CMS editing form
&& !($this->record instanceof RedirectorPage)
);
}
}
/**
* @package cms
* @subpackage content
*/
class SilverStripeNavigatorItem_StageLink extends SilverStripeNavigatorItem {
/** @config */
private static $priority = 20;
public function getHTML() {
$draftPage = $this->getDraftPage();
if($draftPage) {
$this->recordLink = Controller::join_links($draftPage->AbsoluteLink(), "?stage=Stage");
return "<a ". ($this->isActive() ? 'class="current" ' : '') ."href=\"$this->recordLink\">". _t('ContentController.DRAFTSITE', 'Draft Site') ."</a>";
}
}
public function getTitle() {
return _t('ContentController.DRAFT', 'Draft', 'Used for the Switch between draft and published view mode. Needs to be a short label');
}
public function getMessage() {
return "<div id=\"SilverStripeNavigatorMessage\" title=\"". _t('ContentControl.NOTEWONTBESHOWN', 'Note: this message will not be shown to your visitors') ."\">". _t('ContentController.DRAFTSITE', 'Draft Site') ."</div>";
}
public function getLink() {
$date = Versioned::current_archived_date();
return Controller::join_links(
$this->record->PreviewLink(),
'?stage=Stage',
$date ? '?archiveDate=' . $date : null
);
}
public function canView($member = null) {
return (
$this->record->hasExtension('SilverStripe\ORM\Versioning\Versioned')
&& $this->getDraftPage()
// Don't follow redirects in preview, they break the CMS editing form
&& !($this->record instanceof RedirectorPage)
);
}
public function isActive() {
return (
Versioned::get_stage() == 'Stage'
&& !(ClassInfo::exists('SiteTreeFutureState') && SiteTreeFutureState::get_future_datetime())
&& !$this->isArchived()
);
}
protected function getDraftPage() {
$baseClass = $this->record->baseClass();
return Versioned::get_by_stage($baseClass, Versioned::DRAFT)->byID($this->record->ID);
}
}
/**
* @package cms
* @subpackage content
*/
class SilverStripeNavigatorItem_LiveLink extends SilverStripeNavigatorItem {
/** @config */
private static $priority = 30;
public function getHTML() {
$livePage = $this->getLivePage();
if($livePage) {
$this->recordLink = Controller::join_links($livePage->AbsoluteLink(), "?stage=Live");
return "<a ". ($this->isActive() ? 'class="current" ' : '') ."href=\"$this->recordLink\">". _t('ContentController.PUBLISHEDSITE', 'Published Site') ."</a>";
}
}
public function getTitle() {
return _t('ContentController.PUBLISHED', 'Published', 'Used for the Switch between draft and published view mode. Needs to be a short label');
}
public function getMessage() {
return "<div id=\"SilverStripeNavigatorMessage\" title=\"". _t('ContentControl.NOTEWONTBESHOWN', 'Note: this message will not be shown to your visitors') ."\">". _t('ContentController.PUBLISHEDSITE', 'Published Site') ."</div>";
}
public function getLink() {
return Controller::join_links($this->record->PreviewLink(), '?stage=Live');
}
public function canView($member = null) {
return (
$this->record->hasExtension('SilverStripe\ORM\Versioning\Versioned')
&& $this->getLivePage()
// Don't follow redirects in preview, they break the CMS editing form
&& !($this->record instanceof RedirectorPage)
);
}
public function isActive() {
return (
(!Versioned::get_stage() || Versioned::get_stage() == 'Live')
&& !$this->isArchived()
);
}
protected function getLivePage() {
$baseClass = $this->record->baseClass();
return Versioned::get_by_stage($baseClass, Versioned::LIVE)->byID($this->record->ID);
}
}
/**
* @package cms
* @subpackage content
*/
class SilverStripeNavigatorItem_ArchiveLink extends SilverStripeNavigatorItem {
/** @config */
private static $priority = 40;
public function getHTML() {
$this->recordLink = $this->record->AbsoluteLink();
return "<a class=\"ss-ui-button". ($this->isActive() ? ' current' : '') ."\" href=\"$this->recordLink?archiveDate={$this->record->LastEdited}\" target=\"_blank\">". _t('ContentController.ARCHIVEDSITE', 'Preview version') ."</a>";
}
public function getTitle() {
return _t('SilverStripeNavigator.ARCHIVED', 'Archived');
}
public function getMessage() {
if($date = Versioned::current_archived_date()) {
$dateObj = DBField::create_field('Datetime', $date);
return "<div id=\"SilverStripeNavigatorMessage\" title=\"". _t('ContentControl.NOTEWONTBESHOWN', 'Note: this message will not be shown to your visitors') ."\">". _t('ContentController.ARCHIVEDSITEFROM', 'Archived site from') ."<br>" . $dateObj->Nice() . "</div>";
}
}
public function getLink() {
return $this->record->PreviewLink() . '?archiveDate=' . urlencode($this->record->LastEdited);
}
public function canView($member = null) {
return (
$this->record->hasExtension('SilverStripe\ORM\Versioning\Versioned')
&& $this->isArchived()
// Don't follow redirects in preview, they break the CMS editing form
&& !($this->record instanceof RedirectorPage)
);
}
public function isActive() {
return $this->isArchived();
}
}

View File

@ -0,0 +1,141 @@
<?php
namespace SilverStripe\CMS\Controllers;
use SilverStripe\Admin\CMSPreviewable;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\Versioning\Versioned;
use SilverStripe\Security\Member;
use ViewableData;
/**
* Navigator items are links that appear in the $SilverStripeNavigator bar.
* To add an item, extend this class - it will be automatically picked up.
* When instanciating items manually, please ensure to call {@link canView()}.
*
* @package cms
* @subpackage content
*/
abstract class SilverStripeNavigatorItem extends ViewableData
{
/**
* @param DataObject|CMSPreviewable
*/
protected $record;
/** @var string */
protected $recordLink;
/**
* @param DataObject|CMSPreviewable $record
*/
public function __construct(CMSPreviewable $record)
{
parent::__construct();
$this->record = $record;
}
/**
* @return string HTML, mostly a link - but can be more complex as well.
* For example, a "future state" item might show a date selector.
*/
abstract public function getHTML();
/**
* @return string
* Get the Title of an item
*/
abstract public function getTitle();
/**
* Machine-friendly name.
*
* @return string
*/
public function getName()
{
return substr(get_class($this), strpos(get_class($this), '_') + 1);
}
/**
* Optional link to a specific view of this record.
* Not all items are simple links, please use {@link getHTML()}
* to represent an item in markup unless you know what you're doing.
*
* @return string
*/
public function getLink()
{
}
/**
* @return string
*/
public function getMessage()
{
}
/**
* @return DataObject
*/
public function getRecord()
{
return $this->record;
}
/**
* @return int
*/
public function getPriority()
{
return $this->stat('priority');
}
/**
* As items might convey different record states like a "stage" or "live" table,
* an item can be active (showing the record in this state).
*
* @return boolean
*/
public function isActive()
{
return false;
}
/**
* Filters items based on member permissions or other criteria,
* such as if a state is generally available for the current record.
*
* @param Member $member
* @return Boolean
*/
public function canView($member = null)
{
return true;
}
/**
* Counts as "archived" if the current record is a different version from both live and draft.
*
* @return boolean
*/
public function isArchived()
{
if (!$this->record->hasExtension('SilverStripe\ORM\Versioning\Versioned')) {
return false;
}
if (!isset($this->record->_cached_isArchived)) {
$baseClass = $this->record->baseClass();
$currentDraft = Versioned::get_by_stage($baseClass, Versioned::DRAFT)->byID($this->record->ID);
$currentLive = Versioned::get_by_stage($baseClass, Versioned::LIVE)->byID($this->record->ID);
$this->record->_cached_isArchived = (
(!$currentDraft || ($currentDraft && $this->record->Version != $currentDraft->Version))
&& (!$currentLive || ($currentLive && $this->record->Version != $currentLive->Version))
);
}
return $this->record->_cached_isArchived;
}
}

View File

@ -0,0 +1,60 @@
<?php
namespace SilverStripe\CMS\Controllers;
use SilverStripe\CMS\Model\RedirectorPage;
use SilverStripe\ORM\FieldType\DBDatetime;
use SilverStripe\ORM\FieldType\DBField;
use SilverStripe\ORM\Versioning\Versioned;
/**
* @package cms
* @subpackage content
*/
class SilverStripeNavigatorItem_ArchiveLink extends SilverStripeNavigatorItem
{
/** @config */
private static $priority = 40;
public function getHTML()
{
$this->recordLink = $this->record->AbsoluteLink();
return "<a class=\"ss-ui-button" . ($this->isActive() ? ' current' : '') . "\" href=\"$this->recordLink?archiveDate={$this->record->LastEdited}\" target=\"_blank\">" . _t('ContentController.ARCHIVEDSITE',
'Preview version') . "</a>";
}
public function getTitle()
{
return _t('SilverStripeNavigator.ARCHIVED', 'Archived');
}
public function getMessage()
{
if ($date = Versioned::current_archived_date()) {
/** @var DBDatetime $dateObj */
$dateObj = DBField::create_field('Datetime', $date);
return "<div id=\"SilverStripeNavigatorMessage\" title=\"" . _t('ContentControl.NOTEWONTBESHOWN',
'Note: this message will not be shown to your visitors') . "\">" . _t('ContentController.ARCHIVEDSITEFROM',
'Archived site from') . "<br>" . $dateObj->Nice() . "</div>";
}
}
public function getLink()
{
return $this->record->PreviewLink() . '?archiveDate=' . urlencode($this->record->LastEdited);
}
public function canView($member = null)
{
return (
$this->record->hasExtension('SilverStripe\ORM\Versioning\Versioned')
&& $this->isArchived()
// Don't follow redirects in preview, they break the CMS editing form
&& !($this->record instanceof RedirectorPage)
);
}
public function isActive()
{
return $this->isArchived();
}
}

View File

@ -0,0 +1,51 @@
<?php
namespace SilverStripe\CMS\Controllers;
use Controller;
use SilverStripe\Admin\LeftAndMain;
use SilverStripe\CMS\Model\RedirectorPage;
/**
* @package cms
* @subpackage content
*/
class SilverStripeNavigatorItem_CMSLink extends SilverStripeNavigatorItem
{
/** @config */
private static $priority = 10;
public function getHTML()
{
return sprintf(
'<a href="%s">%s</a>',
$this->record->CMSEditLink(),
_t('ContentController.CMS', 'CMS')
);
}
public function getTitle()
{
return _t('ContentController.CMS', 'CMS', 'Used in navigation. Should be a short label');
}
public function getLink()
{
return $this->record->CMSEditLink();
}
public function isActive()
{
return (Controller::curr() instanceof LeftAndMain);
}
public function canView($member = null)
{
return (
// Don't show in CMS
!(Controller::curr() instanceof LeftAndMain)
// Don't follow redirects in preview, they break the CMS editing form
&& !($this->record instanceof RedirectorPage)
);
}
}

View File

@ -0,0 +1,68 @@
<?php
namespace SilverStripe\CMS\Controllers;
use Controller;
use SilverStripe\CMS\Model\RedirectorPage;
use SilverStripe\ORM\Versioning\Versioned;
/**
* @package cms
* @subpackage content
*/
class SilverStripeNavigatorItem_LiveLink extends SilverStripeNavigatorItem
{
/** @config */
private static $priority = 30;
public function getHTML()
{
$livePage = $this->getLivePage();
if ($livePage) {
$this->recordLink = Controller::join_links($livePage->AbsoluteLink(), "?stage=Live");
return "<a " . ($this->isActive() ? 'class="current" ' : '') . "href=\"$this->recordLink\">" . _t('ContentController.PUBLISHEDSITE',
'Published Site') . "</a>";
}
}
public function getTitle()
{
return _t('ContentController.PUBLISHED', 'Published',
'Used for the Switch between draft and published view mode. Needs to be a short label');
}
public function getMessage()
{
return "<div id=\"SilverStripeNavigatorMessage\" title=\"" . _t('ContentControl.NOTEWONTBESHOWN',
'Note: this message will not be shown to your visitors') . "\">" . _t('ContentController.PUBLISHEDSITE',
'Published Site') . "</div>";
}
public function getLink()
{
return Controller::join_links($this->record->PreviewLink(), '?stage=Live');
}
public function canView($member = null)
{
return (
$this->record->hasExtension('SilverStripe\ORM\Versioning\Versioned')
&& $this->getLivePage()
// Don't follow redirects in preview, they break the CMS editing form
&& !($this->record instanceof RedirectorPage)
);
}
public function isActive()
{
return (
(!Versioned::get_stage() || Versioned::get_stage() == 'Live')
&& !$this->isArchived()
);
}
protected function getLivePage()
{
$baseClass = $this->record->baseClass();
return Versioned::get_by_stage($baseClass, Versioned::LIVE)->byID($this->record->ID);
}
}

View File

@ -0,0 +1,76 @@
<?php
namespace SilverStripe\CMS\Controllers;
use ClassInfo;
use Controller;
use SilverStripe\CMS\Model\RedirectorPage;
use SilverStripe\ORM\Versioning\Versioned;
use SiteTreeFutureState;
/**
* @package cms
* @subpackage content
*/
class SilverStripeNavigatorItem_StageLink extends SilverStripeNavigatorItem
{
/** @config */
private static $priority = 20;
public function getHTML()
{
$draftPage = $this->getDraftPage();
if ($draftPage) {
$this->recordLink = Controller::join_links($draftPage->AbsoluteLink(), "?stage=Stage");
return "<a " . ($this->isActive() ? 'class="current" ' : '') . "href=\"$this->recordLink\">" . _t('ContentController.DRAFTSITE',
'Draft Site') . "</a>";
}
}
public function getTitle()
{
return _t('ContentController.DRAFT', 'Draft',
'Used for the Switch between draft and published view mode. Needs to be a short label');
}
public function getMessage()
{
return "<div id=\"SilverStripeNavigatorMessage\" title=\"" . _t('ContentControl.NOTEWONTBESHOWN',
'Note: this message will not be shown to your visitors') . "\">" . _t('ContentController.DRAFTSITE',
'Draft Site') . "</div>";
}
public function getLink()
{
$date = Versioned::current_archived_date();
return Controller::join_links(
$this->record->PreviewLink(),
'?stage=Stage',
$date ? '?archiveDate=' . $date : null
);
}
public function canView($member = null)
{
return (
$this->record->hasExtension('SilverStripe\ORM\Versioning\Versioned')
&& $this->getDraftPage()
// Don't follow redirects in preview, they break the CMS editing form
&& !($this->record instanceof RedirectorPage)
);
}
public function isActive()
{
return (
Versioned::get_stage() == 'Stage'
&& !(ClassInfo::exists('SiteTreeFutureState') && SiteTreeFutureState::get_future_datetime())
&& !$this->isArchived()
);
}
protected function getDraftPage()
{
$baseClass = $this->record->baseClass();
return Versioned::get_by_stage($baseClass, Versioned::DRAFT)->byID($this->record->ID);
}
}

View File

@ -158,18 +158,3 @@ class SiteTreeURLSegmentField extends TextField {
return $newInst;
}
}
/**
* Readonly version of a site tree URL segment field
*
* @package forms
* @subpackage fields-basic
*/
class SiteTreeURLSegmentField_Readonly extends SiteTreeURLSegmentField {
protected $readonly = true;
public function performReadonlyTransformation() {
return clone $this;
}
}

View File

@ -0,0 +1,18 @@
<?php
namespace SilverStripe\CMS\Forms;
/**
* Readonly version of a site tree URL segment field
*
* @package forms
* @subpackage fields-basic
*/
class SiteTreeURLSegmentField_Readonly extends SiteTreeURLSegmentField
{
protected $readonly = true;
public function performReadonlyTransformation()
{
return clone $this;
}
}

View File

@ -383,27 +383,3 @@ class ErrorPage extends Page {
}
}
/**
* Controller for ErrorPages.
*
* @package cms
*/
class ErrorPage_Controller extends Page_Controller {
/**
* Overload the provided {@link Controller::handleRequest()} to append the
* correct status code post request since otherwise permission related error
* pages such as 401 and 403 pages won't be rendered due to
* {@link SS_HTTPResponse::isFinished() ignoring the response body.
*
* @param SS_HTTPRequest $request
* @param DataModel $model
* @return SS_HTTPResponse
*/
public function handleRequest(SS_HTTPRequest $request, DataModel $model = NULL) {
$response = parent::handleRequest($request, $model);
$response->setStatusCode($this->ErrorCode);
return $response;
}
}

View File

@ -0,0 +1,33 @@
<?php
namespace SilverStripe\CMS\Model;
use Page_Controller;
use SilverStripe\ORM\DataModel;
use SS_HTTPRequest;
use SS_HTTPResponse;
/**
* Controller for ErrorPages.
*
* @package cms
*/
class ErrorPage_Controller extends Page_Controller
{
/**
* Overload the provided {@link Controller::handleRequest()} to append the
* correct status code post request since otherwise permission related error
* pages such as 401 and 403 pages won't be rendered due to
* {@link SS_HTTPResponse::isFinished() ignoring the response body.
*
* @param SS_HTTPRequest $request
* @param DataModel $model
* @return SS_HTTPResponse
*/
public function handleRequest(SS_HTTPRequest $request, DataModel $model = null)
{
$response = parent::handleRequest($request, $model);
$response->setStatusCode($this->ErrorCode);
return $response;
}
}

View File

@ -180,31 +180,3 @@ class RedirectorPage extends Page {
return array();
}
}
/**
* Controller for the {@link RedirectorPage}.
* @package cms
* @subpackage content
*/
class RedirectorPage_Controller extends Page_Controller {
public function init() {
parent::init();
// Check we don't already have a redirect code set
/** @var RedirectorPage $page */
$page = $this->data();
if(!$this->getResponse()->isFinished() && $link = $page->redirectionLink()) {
$this->redirect($link, 301);
}
}
/**
* If we ever get this far, it means that the redirection failed.
*/
public function Content() {
return "<p class=\"message-setupWithoutRedirect\">" .
_t('RedirectorPage.HASBEENSETUP', 'A redirector page has been set up without anywhere to redirect to.') .
"</p>";
}
}

View File

@ -0,0 +1,35 @@
<?php
namespace SilverStripe\CMS\Model;
use Page_Controller;
/**
* Controller for the {@link RedirectorPage}.
* @package cms
* @subpackage content
*/
class RedirectorPage_Controller extends Page_Controller
{
public function init()
{
parent::init();
// Check we don't already have a redirect code set
/** @var RedirectorPage $page */
$page = $this->data();
if (!$this->getResponse()->isFinished() && $link = $page->redirectionLink()) {
$this->redirect($link, 301);
}
}
/**
* If we ever get this far, it means that the redirection failed.
*/
public function Content()
{
return "<p class=\"message-setupWithoutRedirect\">" .
_t('RedirectorPage.HASBEENSETUP', 'A redirector page has been set up without anywhere to redirect to.') .
"</p>";
}
}

View File

@ -19,7 +19,6 @@ use SilverStripe\Security\Permission;
use SilverStripe\Security\Group;
use SilverStripe\Security\PermissionProvider;
use i18nEntityProvider;
use CMSPreviewable;
use Director;
use SilverStripe\CMS\Controllers\RootURLController;
use ClassInfo;
@ -52,9 +51,11 @@ use TreeDropdownField;
use FieldGroup;
use CheckboxField;
use ListboxField;
use AddToCampaignHandler_FormAction;
use FormAction;
use i18n;
use SilverStripe\Admin\AddToCampaignHandler_FormAction;
use SilverStripe\Admin\CMSPreviewable;
/**
* Basic data-object representing all pages within the site tree. All page types that live within the hierarchy should
@ -81,8 +82,8 @@ use i18n;
* @property string CanViewType Type of restriction for viewing this object.
* @property string CanEditType Type of restriction for editing this object.
*
* @method ManyManyList ViewerGroups List of groups that can view this object.
* @method ManyManyList EditorGroups List of groups that can edit this object.
* @method ManyManyList ViewerGroups() List of groups that can view this object.
* @method ManyManyList EditorGroups() List of groups that can edit this object.
* @method SiteTree Parent()
*
* @mixin Hierarchy
@ -352,13 +353,13 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
foreach($alternatives as $alternative) {
if($alternative) {
$sitetree = $alternative;
}
}
}
}
if(!$sitetree) {
return null;
}
}
}
// Check if we have any more URL parts to parse.
@ -402,8 +403,10 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
public static function page_type_classes() {
$classes = ClassInfo::getValidSubClasses();
$baseClassIndex = array_search('SilverStripe\\CMS\\Model\\SiteTree', $classes);
if($baseClassIndex !== FALSE) unset($classes[$baseClassIndex]);
$baseClassIndex = array_search(__CLASS__, $classes);
if($baseClassIndex !== false) {
unset($classes[$baseClassIndex]);
}
$kill_ancestors = array();
@ -437,8 +440,8 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
/**
* Replace a "[sitetree_link id=n]" shortcode with a link to the page with the corresponding ID.
*
* @param array $arguments
* @param string $content
* @param array $arguments
* @param string $content
* @param ShortcodeParser $parser
* @return string
*/
@ -447,6 +450,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
return null;
}
/** @var SiteTree $page */
if (
!($page = DataObject::get_by_id(__CLASS__, $arguments['id'])) // Get the current page by ID.
&& !($page = Versioned::get_latest_version(__CLASS__, $arguments['id'])) // Attempt link to old version.
@ -609,7 +613,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
$currentPage = Director::get_current_page();
if ($currentPage instanceof ContentController) {
$currentPage = $currentPage->data();
}
}
if($currentPage instanceof SiteTree) {
return $currentPage === $this || $currentPage->ID === $this->ID;
}
@ -1467,7 +1471,6 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
}
if(Permission::check('CMS_ACCESS_CMSMain')
&& in_array('CMSPreviewable', class_implements($this))
&& !$this instanceof ErrorPage
&& $this->ID > 0
) {
@ -1552,7 +1555,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
DB::alteration_message('Contact Us page created', 'created');
}
}
}
}
protected function onBeforeWrite() {
parent::onBeforeWrite();
@ -1877,10 +1880,10 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
if($parentPage && $parentPage->exists()) {
$link = Convert::raw2att($parentPage->CMSEditLink());
$title = Convert::raw2xml($parentPage->Title);
} else {
} else {
$link = CMSPageEditController::singleton()->Link('show');
$title = _t('SiteTree.TOPLEVEL', 'Site Content (Top Level)');
}
}
$parentPageLinks[] = "<a class=\"cmsEditlink\" href=\"{$link}\">{$title}</a>";
}
@ -1925,9 +1928,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
$dependentPages
);
/** @var GridFieldDataColumns $dataColumns */
$dataColumns = $dependentTable
->getConfig()
->getComponentByType('GridFieldDataColumns');
$dataColumns = $dependentTable->getConfig()->getComponentByType('GridFieldDataColumns');
$dataColumns
->setDisplayFields($dependentColumns)
->setFieldFormatting(array(

View File

@ -230,126 +230,3 @@ class SiteTreeLinkTracking extends DataExtension {
}
}
}
/**
* A helper object for extracting information about links.
*/
class SiteTreeLinkTracking_Parser {
/**
* Finds the links that are of interest for the link tracking automation. Checks for brokenness and attaches
* extracted metadata so consumers can decide what to do with the DOM element (provided as DOMReference).
*
* @param SS_HTMLValue $htmlValue Object to parse the links from.
* @return array Associative array containing found links with the following field layout:
* Type: string, name of the link type
* Target: any, a reference to the target object, depends on the Type
* Anchor: string, anchor part of the link
* DOMReference: DOMElement, reference to the link to apply changes.
* Broken: boolean, a flag highlighting whether the link should be treated as broken.
*/
public function process(SS_HTMLValue $htmlValue) {
$results = array();
$links = $htmlValue->getElementsByTagName('a');
if(!$links) {
return $results;
}
foreach($links as $link) {
if (!$link->hasAttribute('href')) {
continue;
}
$href = Director::makeRelative($link->getAttribute('href'));
// Definitely broken links.
if($href == '' || $href[0] == '/') {
$results[] = array(
'Type' => 'broken',
'Target' => null,
'Anchor' => null,
'DOMReference' => $link,
'Broken' => true
);
continue;
}
// Link to a page on this site.
$matches = array();
if(preg_match('/\[sitetree_link(?:\s*|%20|,)?id=(?<id>[0-9]+)\](#(?<anchor>.*))?/i', $href, $matches)) {
$page = DataObject::get_by_id('SilverStripe\\CMS\\Model\\SiteTree', $matches['id']);
$broken = false;
if (!$page) {
// Page doesn't exist.
$broken = true;
} else if (!empty($matches['anchor'])) {
$anchor = preg_quote($matches['anchor'], '/');
if (!preg_match("/(name|id)=\"{$anchor}\"/", $page->Content)) {
// Broken anchor on the target page.
$broken = true;
}
}
$results[] = array(
'Type' => 'sitetree',
'Target' => $matches['id'],
'Anchor' => empty($matches['anchor']) ? null : $matches['anchor'],
'DOMReference' => $link,
'Broken' => $broken
);
continue;
}
// Link to a file on this site.
$matches = array();
if(preg_match('/\[file_link(?:\s*|%20|,)?id=(?<id>[0-9]+)/i', $href, $matches)) {
$results[] = array(
'Type' => 'file',
'Target' => $matches['id'],
'Anchor' => null,
'DOMReference' => $link,
'Broken' => !DataObject::get_by_id('File', $matches['id'])
);
continue;
}
// Local anchor.
$matches = array();
if(preg_match('/^#(.*)/i', $href, $matches)) {
$anchor = preg_quote($matches[1], '#');
$results[] = array(
'Type' => 'localanchor',
'Target' => null,
'Anchor' => $matches[1],
'DOMReference' => $link,
'Broken' => !preg_match("#(name|id)=\"{$anchor}\"#", $htmlValue->getContent())
);
continue;
}
}
// Find all [image ] shortcodes (will be inline, not inside attributes)
$content = $htmlValue->getContent();
if(preg_match_all('/\[image([^\]]+)\bid=(["])?(?<id>\d+)\D/i', $content, $matches)) {
foreach($matches['id'] as $id) {
$results[] = array(
'Type' => 'image',
'Target' => (int)$id,
'Anchor' => null,
'DOMReference' => null,
'Broken' => !DataObject::get_by_id('Image', (int)$id)
);
}
}
return $results;
}
}

View File

@ -0,0 +1,133 @@
<?php
namespace SilverStripe\CMS\Model;
use Director;
use SilverStripe\ORM\DataObject;
use SS_HTMLValue;
/**
* A helper object for extracting information about links.
*/
class SiteTreeLinkTracking_Parser
{
/**
* Finds the links that are of interest for the link tracking automation. Checks for brokenness and attaches
* extracted metadata so consumers can decide what to do with the DOM element (provided as DOMReference).
*
* @param SS_HTMLValue $htmlValue Object to parse the links from.
* @return array Associative array containing found links with the following field layout:
* Type: string, name of the link type
* Target: any, a reference to the target object, depends on the Type
* Anchor: string, anchor part of the link
* DOMReference: DOMElement, reference to the link to apply changes.
* Broken: boolean, a flag highlighting whether the link should be treated as broken.
*/
public function process(SS_HTMLValue $htmlValue)
{
$results = array();
$links = $htmlValue->getElementsByTagName('a');
if (!$links) {
return $results;
}
foreach ($links as $link) {
if (!$link->hasAttribute('href')) {
continue;
}
$href = Director::makeRelative($link->getAttribute('href'));
// Definitely broken links.
if ($href == '' || $href[0] == '/') {
$results[] = array(
'Type' => 'broken',
'Target' => null,
'Anchor' => null,
'DOMReference' => $link,
'Broken' => true
);
continue;
}
// Link to a page on this site.
$matches = array();
if (preg_match('/\[sitetree_link(?:\s*|%20|,)?id=(?<id>[0-9]+)\](#(?<anchor>.*))?/i', $href, $matches)) {
$page = DataObject::get_by_id('SilverStripe\\CMS\\Model\\SiteTree', $matches['id']);
$broken = false;
if (!$page) {
// Page doesn't exist.
$broken = true;
} else {
if (!empty($matches['anchor'])) {
$anchor = preg_quote($matches['anchor'], '/');
if (!preg_match("/(name|id)=\"{$anchor}\"/", $page->Content)) {
// Broken anchor on the target page.
$broken = true;
}
}
}
$results[] = array(
'Type' => 'sitetree',
'Target' => $matches['id'],
'Anchor' => empty($matches['anchor']) ? null : $matches['anchor'],
'DOMReference' => $link,
'Broken' => $broken
);
continue;
}
// Link to a file on this site.
$matches = array();
if (preg_match('/\[file_link(?:\s*|%20|,)?id=(?<id>[0-9]+)/i', $href, $matches)) {
$results[] = array(
'Type' => 'file',
'Target' => $matches['id'],
'Anchor' => null,
'DOMReference' => $link,
'Broken' => !DataObject::get_by_id('File', $matches['id'])
);
continue;
}
// Local anchor.
$matches = array();
if (preg_match('/^#(.*)/i', $href, $matches)) {
$anchor = preg_quote($matches[1], '#');
$results[] = array(
'Type' => 'localanchor',
'Target' => null,
'Anchor' => $matches[1],
'DOMReference' => $link,
'Broken' => !preg_match("#(name|id)=\"{$anchor}\"#", $htmlValue->getContent())
);
continue;
}
}
// Find all [image ] shortcodes (will be inline, not inside attributes)
$content = $htmlValue->getContent();
if (preg_match_all('/\[image([^\]]+)\bid=(["])?(?<id>\d+)\D/i', $content, $matches)) {
foreach ($matches['id'] as $id) {
$results[] = array(
'Type' => 'image',
'Target' => (int)$id,
'Anchor' => null,
'DOMReference' => null,
'Broken' => !DataObject::get_by_id('Image', (int)$id)
);
}
}
return $results;
}
}

View File

@ -455,106 +455,3 @@ class VirtualPage extends Page {
}
}
/**
* Controller for the virtual page.
* @package cms
*/
class VirtualPage_Controller extends Page_Controller {
private static $allowed_actions = array(
'loadcontentall' => 'ADMIN',
);
/**
* Backup of virtualised controller
*
* @var ContentController
*/
protected $virtualController = null;
/**
* Get virtual controller
*
* @return ContentController
*/
protected function getVirtualisedController() {
if($this->virtualController) {
return $this->virtualController;
}
// Validate virtualised model
/** @var VirtualPage $page */
$page = $this->data();
$virtualisedPage = $page->CopyContentFrom();
if (!$virtualisedPage || !$virtualisedPage->exists()) {
return null;
}
// Create controller using standard mechanism
$this->virtualController = ModelAsController::controller_for($virtualisedPage);
return $this->virtualController;
}
public function getViewer($action) {
$controller = $this->getVirtualisedController() ?: $this;
return $controller->getViewer($action);
}
/**
* When the virtualpage is loaded, check to see if the versions are the same
* if not, reload the content.
* NOTE: Virtual page must have a container object of subclass of sitetree.
* We can't load the content without an ID or record to copy it from.
*/
public function init(){
parent::init();
$this->__call('init', array());
}
/**
* Also check the original object's original controller for the method
*
* @param string $method
* @return bool
*/
public function hasMethod($method) {
if(parent::hasMethod($method)) {
return true;
};
// Fallback
$controller = $this->getVirtualisedController();
return $controller && $controller->hasMethod($method);
}
/**
* Pass unrecognized method calls on to the original controller
*
* @param string $method
* @param string $args
* @return mixed
*
* @throws Exception Any error other than a 'no method' error.
*/
public function __call($method, $args)
{
// Check if we can safely call this method before passing it back
// to custom methods.
if($this->getExtraMethodConfig($method)) {
return parent::__call($method, $args);
}
// Pass back to copied page
$controller = $this->getVirtualisedController();
if(!$controller) {
return null;
}
// Ensure request/response data is available on virtual controller
$controller->setRequest($this->getRequest());
$controller->setResponse($this->getResponse());
return call_user_func_array(array($controller, $method), $args);
}
}

View File

@ -0,0 +1,115 @@
<?php
namespace SilverStripe\CMS\Model;
use Exception;
use Page_Controller;
use SilverStripe\CMS\Controllers\ContentController;
use SilverStripe\CMS\Controllers\ModelAsController;
/**
* Controller for the virtual page.
* @package cms
*/
class VirtualPage_Controller extends Page_Controller
{
private static $allowed_actions = array(
'loadcontentall' => 'ADMIN',
);
/**
* Backup of virtualised controller
*
* @var ContentController
*/
protected $virtualController = null;
/**
* Get virtual controller
*
* @return ContentController
*/
protected function getVirtualisedController()
{
if ($this->virtualController) {
return $this->virtualController;
}
// Validate virtualised model
/** @var VirtualPage $page */
$page = $this->data();
$virtualisedPage = $page->CopyContentFrom();
if (!$virtualisedPage || !$virtualisedPage->exists()) {
return null;
}
// Create controller using standard mechanism
$this->virtualController = ModelAsController::controller_for($virtualisedPage);
return $this->virtualController;
}
public function getViewer($action)
{
$controller = $this->getVirtualisedController() ?: $this;
return $controller->getViewer($action);
}
/**
* When the virtualpage is loaded, check to see if the versions are the same
* if not, reload the content.
* NOTE: Virtual page must have a container object of subclass of sitetree.
* We can't load the content without an ID or record to copy it from.
*/
public function init()
{
parent::init();
$this->__call('init', array());
}
/**
* Also check the original object's original controller for the method
*
* @param string $method
* @return bool
*/
public function hasMethod($method)
{
if (parent::hasMethod($method)) {
return true;
};
// Fallback
$controller = $this->getVirtualisedController();
return $controller && $controller->hasMethod($method);
}
/**
* Pass unrecognized method calls on to the original controller
*
* @param string $method
* @param string $args
* @return mixed
*
* @throws Exception Any error other than a 'no method' error.
*/
public function __call($method, $args)
{
// Check if we can safely call this method before passing it back
// to custom methods.
if ($this->getExtraMethodConfig($method)) {
return parent::__call($method, $args);
}
// Pass back to copied page
$controller = $this->getVirtualisedController();
if (!$controller) {
return null;
}
// Ensure request/response data is available on virtual controller
$controller->setRequest($this->getRequest());
$controller->setResponse($this->getResponse());
return call_user_func_array(array($controller, $method), $args);
}
}

View File

@ -1,37 +1,40 @@
{
"name": "silverstripe/cms",
"type": "silverstripe-module",
"description": "The SilverStripe Content Management System",
"homepage": "http://silverstripe.org",
"license": "BSD-3-Clause",
"keywords": ["silverstripe", "cms"],
"authors": [
{
"name": "SilverStripe",
"homepage": "http://silverstripe.com"
},
{
"name": "The SilverStripe Community",
"homepage": "http://silverstripe.org"
}
],
"require": {
"php": ">=5.5.0",
"composer/installers": "*",
"silverstripe/framework": "^4.0",
"silverstripe/reports": "^4.0",
"silverstripe/siteconfig": "^4.0"
},
"require-dev": {
"phpunit/PHPUnit": "~4.8"
},
"extra": {
"branch-alias": {
"dev-master": "4.0.x-dev"
}
},
"autoload": {
"classmap": ["tests/behat/"]
},
"minimum-stability": "dev"
"name": "silverstripe/cms",
"type": "silverstripe-module",
"description": "The SilverStripe Content Management System",
"homepage": "http://silverstripe.org",
"license": "BSD-3-Clause",
"keywords": ["silverstripe", "cms"],
"authors": [
{
"name": "SilverStripe",
"homepage": "http://silverstripe.com"
},
{
"name": "The SilverStripe Community",
"homepage": "http://silverstripe.org"
}
],
"require": {
"php": ">=5.5.0",
"composer/installers": "*",
"silverstripe/framework": "^4.0",
"silverstripe/reports": "^4.0",
"silverstripe/siteconfig": "^4.0"
},
"require-dev": {
"phpunit/PHPUnit": "~4.8"
},
"extra": {
"branch-alias": {
"dev-master": "4.0.x-dev"
}
},
"autoload": {
"psr-4": {
"SilverStripe\\CMS\\": "code/"
},
"classmap": ["tests/behat/"]
},
"minimum-stability": "dev"
}

View File

@ -4,7 +4,7 @@
<div class="cms-content-header north">
<div class="cms-content-header-info">
<% with $Controller %>
<% include CMSBreadcrumbs %>
<% include SilverStripe\\Admin\\CMSBreadcrumbs %>
<% end_with %>
</div>

View File

@ -1,24 +1,24 @@
<div id="pages-controller-cms-content" class="has-panel cms-content center cms-tabset $BaseCSSClasses" data-layout-type="border" data-pjax-fragment="Content" data-ignore-tab-state="true">
<div class="cms-content-header north">
<div class="cms-content-header-nav">
<% include CMSBreadcrumbs %>
<% include SilverStripe\\Admin\\CMSBreadcrumbs %>
<div class="cms-content-header-tabs">
<ul class="cms-tabset-nav-primary">
<li class="content-treeview<% if class == 'CMSPageEditController' %> ui-tabs-active<% end_if %>">
<li class="content-treeview<% if $class == 'SilverStripe\\CMS\\Controllers\\CMSPageEditController' %> ui-tabs-active<% end_if %>">
<a href="$LinkPageEdit" class="cms-panel-link" title="Form_EditForm" data-href="$LinkPageEdit">
<% _t('CMSMain.TabContent', 'Content') %>
</a>
</li>
<li class="content-listview<% if class == 'CMSPageSettingsController' %> ui-tabs-active<% end_if %>">
<li class="content-listview<% if $class == 'SilverStripe\\CMS\\Controllers\\CMSPageSettingsController' %> ui-tabs-active<% end_if %>">
<a href="$LinkPageSettings" class="cms-panel-link" title="Form_EditForm" data-href="$LinkPageSettings">
<% _t('CMSMain.TabSettings', 'Settings') %>
</a>
</li>
<li class="content-listview<% if class == 'CMSPageHistoryController' %> ui-tabs-active<% end_if %>">
<li class="content-listview<% if $class == 'SilverStripe\\CMS\\Controllers\\CMSPageHistoryController' %> ui-tabs-active<% end_if %>">
<a href="$LinkPageHistory" class="cms-panel-link" title="Form_EditForm" data-href="$LinkPageHistory">
<% _t('CMSMain.TabHistory', 'History') %>
</a>
@ -29,7 +29,7 @@
<div class="cms-content-header-info">
<div class="section-heading">
<% include CMSSectionIcon %>
<% include SilverStripe\\Admin\\CMSSectionIcon %>
<span class="section-label"><a href="$LinkPages">{$MenuCurrentItem.Title}</a></span>
</div>

View File

@ -28,7 +28,7 @@
</a>
<% end_if %>
<% include LeftAndMain_ViewModeSelector SelectID="preview-mode-dropdown-in-content" %>
<% include SilverStripe\\Admin\\LeftAndMain_ViewModeSelector SelectID="preview-mode-dropdown-in-content" %>
</div>
<% end_if %>
</div>

View File

@ -2,7 +2,7 @@
<div class="cms-content-header north">
<div class="cms-content-header-info">
<% include CMSBreadcrumbs %>
<% include SilverStripe\\Admin\\CMSBreadcrumbs %>
</div>
<div class="cms-content-header-tabs">

View File

@ -6,6 +6,8 @@ use SilverStripe\ORM\Versioning\Versioned;
use SilverStripe\ORM\HiddenClass;
use SilverStripe\CMS\Controllers\CMSMain;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Admin\CMSBatchActionHandler;
/**