mirror of
https://github.com/silverstripe/silverstripe-cms
synced 2024-10-22 08:05:56 +02:00
Merge pull request #1177 from tractorcow/pulls/3.0/fix-page-create
BUG Fix SiteTree / SiteConfig permissions (3.0 backport version)
This commit is contained in:
commit
581cd3179b
@ -46,6 +46,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
'treeview',
|
'treeview',
|
||||||
'listview',
|
'listview',
|
||||||
'ListViewForm',
|
'ListViewForm',
|
||||||
|
'childfilter',
|
||||||
);
|
);
|
||||||
|
|
||||||
public function init() {
|
public function init() {
|
||||||
@ -379,55 +380,49 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
$json = $cache->load($cacheKey);
|
$json = $cache->load($cacheKey);
|
||||||
if(!$json) {
|
if(!$json) {
|
||||||
$def['Root'] = array();
|
$def['Root'] = array();
|
||||||
$def['Root']['disallowedParents'] = array();
|
$def['Root']['disallowedChildren'] = array();
|
||||||
|
|
||||||
|
// Contains all possible classes to support UI controls listing them all,
|
||||||
|
// such as the "add page here" context menu.
|
||||||
|
$def['All'] = array();
|
||||||
|
|
||||||
|
// Identify disallows and set globals
|
||||||
foreach($classes as $class) {
|
foreach($classes as $class) {
|
||||||
$obj = singleton($class);
|
$obj = singleton($class);
|
||||||
if($obj instanceof HiddenClass) continue;
|
if($obj instanceof HiddenClass) continue;
|
||||||
|
|
||||||
$allowedChildren = $obj->allowedChildren();
|
// Name item
|
||||||
|
$def['All'][$class] = array(
|
||||||
|
'title' => $obj->i18n_singular_name()
|
||||||
|
);
|
||||||
|
|
||||||
// SiteTree::allowedChildren() returns null rather than an empty array if SiteTree::allowed_chldren == 'none'
|
// Check if can be created at the root
|
||||||
if($allowedChildren == null) $allowedChildren = array();
|
$needsPerm = $obj->stat('need_permission');
|
||||||
|
if(
|
||||||
// Exclude SiteTree from possible Children
|
!$obj->stat('can_be_root')
|
||||||
$possibleChildren = array_diff($allowedChildren, array("SiteTree"));
|
|| (!array_key_exists($class, $cacheCanCreate) || !$cacheCanCreate[$class])
|
||||||
|
|| ($needsPerm && !$this->can($needsPerm))
|
||||||
// Find i18n - names and build allowed children array
|
) {
|
||||||
foreach($possibleChildren as $child) {
|
$def['Root']['disallowedChildren'][] = $class;
|
||||||
$instance = singleton($child);
|
|
||||||
|
|
||||||
if($instance instanceof HiddenClass) continue;
|
|
||||||
|
|
||||||
if(!array_key_exists($child, $cacheCanCreate) || !$cacheCanCreate[$child]) continue;
|
|
||||||
|
|
||||||
// skip this type if it is restricted
|
|
||||||
if($instance->stat('need_permission') && !$this->can(singleton($class)->stat('need_permission'))) continue;
|
|
||||||
|
|
||||||
$title = $instance->i18n_singular_name();
|
|
||||||
|
|
||||||
$def[$class]['allowedChildren'][] = array("ssclass" => $child, "ssname" => $title);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$allowedChildren = array_keys(array_diff($classes, $allowedChildren));
|
// Hint data specific to the class
|
||||||
if($allowedChildren) $def[$class]['disallowedChildren'] = $allowedChildren;
|
$def[$class] = array();
|
||||||
|
|
||||||
$defaultChild = $obj->defaultChild();
|
$defaultChild = $obj->defaultChild();
|
||||||
if($defaultChild != 'Page' && $defaultChild != null) $def[$class]['defaultChild'] = $defaultChild;
|
if($defaultChild !== 'Page' && $defaultChild !== null) {
|
||||||
$defaultParent = $obj->defaultParent();
|
$def[$class]['defaultChild'] = $defaultChild;
|
||||||
$parent = SiteTree::get_by_link($defaultParent);
|
|
||||||
$id = $parent ? $parent->id : null;
|
|
||||||
if ($defaultParent != 1 && $defaultParent != null) $def[$class]['defaultParent'] = $defaultParent;
|
|
||||||
if(isset($def[$class]['disallowedChildren'])) {
|
|
||||||
foreach($def[$class]['disallowedChildren'] as $disallowedChild) {
|
|
||||||
$def[$disallowedChild]['disallowedParents'][] = $class;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Are any classes allowed to be parents of root?
|
$defaultParent = $obj->defaultParent();
|
||||||
$def['Root']['disallowedParents'][] = $class;
|
if ($defaultParent !== 1 && $defaultParent !== null) {
|
||||||
|
$def[$class]['defaultParent'] = $defaultParent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$json = Convert::raw2xml(Convert::raw2json($def));
|
$this->extend('updateSiteTreeHints', $def);
|
||||||
|
|
||||||
|
$json = Convert::raw2json($def);
|
||||||
$cache->save($json, $cacheKey);
|
$cache->save($json, $cacheKey);
|
||||||
}
|
}
|
||||||
return $json;
|
return $json;
|
||||||
@ -490,8 +485,6 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
|
|
||||||
if($instance instanceof HiddenClass) continue;
|
if($instance instanceof HiddenClass) continue;
|
||||||
|
|
||||||
if(!$instance->canCreate()) continue;
|
|
||||||
|
|
||||||
// skip this type if it is restricted
|
// skip this type if it is restricted
|
||||||
if($instance->stat('need_permission') && !$this->can(singleton($class)->stat('need_permission'))) continue;
|
if($instance->stat('need_permission') && !$this->can(singleton($class)->stat('need_permission'))) continue;
|
||||||
|
|
||||||
@ -675,6 +668,38 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
return $this->renderWith($this->getTemplatesWithSuffix('_ListView'));
|
return $this->renderWith($this->getTemplatesWithSuffix('_ListView'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback to request the list of page types allowed under a given page instance.
|
||||||
|
* Provides a slower but more precise response over SiteTreeHints
|
||||||
|
*
|
||||||
|
* @param SS_HTTPRequest $request
|
||||||
|
* @return SS_HTTPResponse
|
||||||
|
*/
|
||||||
|
public function childfilter($request) {
|
||||||
|
// Check valid parent specified
|
||||||
|
$parentID = $request->requestVar('ParentID');
|
||||||
|
$parent = SiteTree::get()->byID($parentID);
|
||||||
|
if(!$parent || !$parent->exists()) return $this->httpError(404);
|
||||||
|
|
||||||
|
// Build hints specific to this class
|
||||||
|
// Identify disallows and set globals
|
||||||
|
$classes = SiteTree::page_type_classes();
|
||||||
|
$disallowedChildren = array();
|
||||||
|
foreach($classes as $class) {
|
||||||
|
$obj = singleton($class);
|
||||||
|
if($obj instanceof HiddenClass) continue;
|
||||||
|
|
||||||
|
if(!$obj->canCreate(null, array('Parent' => $parent))) {
|
||||||
|
$disallowedChildren[] = $class;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->extend('updateChildFilter', $disallowedChildren, $parentID);
|
||||||
|
$this->response->addHeader('Content-Type', 'application/json; charset=utf-8');
|
||||||
|
$this->response->setBody(Convert::raw2json($disallowedChildren));
|
||||||
|
return $this->response;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the pages meet a certain criteria as {@see CMSSiteTreeFilter} or the subpages of a parent page
|
* Returns the pages meet a certain criteria as {@see CMSSiteTreeFilter} or the subpages of a parent page
|
||||||
* defaulting to no filter and show all pages in first level.
|
* defaulting to no filter and show all pages in first level.
|
||||||
|
@ -21,7 +21,7 @@ class CMSPageAddController extends CMSPageEditController {
|
|||||||
$pageTypes = array();
|
$pageTypes = array();
|
||||||
foreach($this->PageTypes() as $type) {
|
foreach($this->PageTypes() as $type) {
|
||||||
$html = sprintf('<span class="page-icon class-%s"></span><strong class="title">%s</strong><span class="description">%s</span>',
|
$html = sprintf('<span class="page-icon class-%s"></span><strong class="title">%s</strong><span class="description">%s</span>',
|
||||||
$type->getField('Title'),
|
$type->getField('ClassName'),
|
||||||
$type->getField('AddAction'),
|
$type->getField('AddAction'),
|
||||||
$type->getField('Description')
|
$type->getField('Description')
|
||||||
);
|
);
|
||||||
@ -39,9 +39,6 @@ class CMSPageAddController extends CMSPageEditController {
|
|||||||
$childTitle = _t('CMSPageAddController.ParentMode_child', 'Under another page');
|
$childTitle = _t('CMSPageAddController.ParentMode_child', 'Under another page');
|
||||||
|
|
||||||
$fields = new FieldList(
|
$fields = new FieldList(
|
||||||
// new HiddenField("ParentID", false, ($this->parentRecord) ? $this->parentRecord->ID : null),
|
|
||||||
// TODO Should be part of the form attribute, but not possible in current form API
|
|
||||||
$hintsField = new LiteralField('Hints', sprintf('<span class="hints" data-hints="%s"></span>', $this->SiteTreeHints())),
|
|
||||||
new LiteralField('PageModeHeader', sprintf($numericLabelTmpl, 1, _t('CMSMain.ChoosePageParentMode', 'Choose where to create this page'))),
|
new LiteralField('PageModeHeader', sprintf($numericLabelTmpl, 1, _t('CMSMain.ChoosePageParentMode', 'Choose where to create this page'))),
|
||||||
|
|
||||||
$parentModeField = new SelectionGroup(
|
$parentModeField = new SelectionGroup(
|
||||||
@ -87,6 +84,8 @@ class CMSPageAddController extends CMSPageEditController {
|
|||||||
$this->extend('updatePageOptions', $fields);
|
$this->extend('updatePageOptions', $fields);
|
||||||
|
|
||||||
$form = new Form($this, "AddForm", $fields, $actions);
|
$form = new Form($this, "AddForm", $fields, $actions);
|
||||||
|
$form->setAttribute('data-hints', $this->SiteTreeHints());
|
||||||
|
$form->setAttribute('data-childfilter', $this->Link('childfilter'));
|
||||||
$form->addExtraClass('cms-add-form stacked cms-content center cms-edit-form ' . $this->BaseCSSClasses());
|
$form->addExtraClass('cms-add-form stacked cms-content center cms-edit-form ' . $this->BaseCSSClasses());
|
||||||
$form->setTemplate($this->getTemplatesWithSuffix('_EditForm'));
|
$form->setTemplate($this->getTemplatesWithSuffix('_EditForm'));
|
||||||
|
|
||||||
@ -113,12 +112,8 @@ class CMSPageAddController extends CMSPageEditController {
|
|||||||
|
|
||||||
if(!$parentObj || !$parentObj->ID) $parentID = 0;
|
if(!$parentObj || !$parentObj->ID) $parentID = 0;
|
||||||
|
|
||||||
if($parentObj) {
|
if(!singleton($className)->canCreate(Member::currentUser(), array('Parent' => $parentObj))) {
|
||||||
if(!$parentObj->canAddChildren()) return Security::permissionFailure($this);
|
return Security::permissionFailure($this);
|
||||||
if(!singleton($className)->canCreate()) return Security::permissionFailure($this);
|
|
||||||
} else {
|
|
||||||
if(!SiteConfig::current_site_config()->canCreateTopLevel())
|
|
||||||
return Security::permissionFailure($this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$record = $this->getNewItem("new-$className-$parentID".$suffix, false);
|
$record = $this->getNewItem("new-$className-$parentID".$suffix, false);
|
||||||
|
@ -22,8 +22,21 @@ class SiteConfig extends DataObject implements PermissionProvider {
|
|||||||
"CreateTopLevelGroups" => "Group"
|
"CreateTopLevelGroups" => "Group"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
static $defaults = array(
|
||||||
|
"CanViewType" => "Anyone",
|
||||||
|
"CanEditType" => "LoggedInUsers",
|
||||||
|
"CanCreateTopLevelType" => "LoggedInUsers",
|
||||||
|
);
|
||||||
|
|
||||||
protected static $disabled_themes = array();
|
protected static $disabled_themes = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default permission to check for 'LoggedInUsers' to create or edit pages
|
||||||
|
*
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
static $required_permission = array('CMS_ACCESS_CMSMain', 'CMS_ACCESS_LeftAndMain');
|
||||||
|
|
||||||
static public function disable_theme($theme) {
|
static public function disable_theme($theme) {
|
||||||
self::$disabled_themes[$theme] = $theme;
|
self::$disabled_themes[$theme] = $theme;
|
||||||
}
|
}
|
||||||
@ -187,27 +200,47 @@ class SiteConfig extends DataObject implements PermissionProvider {
|
|||||||
return $config;
|
return $config;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Can a user view this SiteConfig instance?
|
||||||
|
*
|
||||||
|
* @param Member $member
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function canView($member = null) {
|
||||||
|
if(!$member) $member = Member::currentUserID();
|
||||||
|
if($member && is_numeric($member)) $member = DataObject::get_by_id('Member', $member);
|
||||||
|
|
||||||
|
$extended = $this->extendedCan('canView', $member);
|
||||||
|
if($extended !== null) return $extended;
|
||||||
|
|
||||||
|
// Assuming all that can edit this object can also view it
|
||||||
|
return $this->canEdit($member);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Can a user view pages on this site? This method is only
|
* Can a user view pages on this site? This method is only
|
||||||
* called if a page is set to Inherit, but there is nothing
|
* called if a page is set to Inherit, but there is nothing
|
||||||
* to inherit from.
|
* to inherit from.
|
||||||
*
|
*
|
||||||
* @param mixed $member
|
* @param Member $member
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public function canView($member = null) {
|
public function canViewPages($member = null) {
|
||||||
if(!$member) $member = Member::currentUserID();
|
if(!$member) $member = Member::currentUserID();
|
||||||
if($member && is_numeric($member)) $member = DataObject::get_by_id('Member', $member);
|
if($member && is_numeric($member)) $member = DataObject::get_by_id('Member', $member);
|
||||||
|
|
||||||
if ($member && Permission::checkMember($member, "ADMIN")) return true;
|
if ($member && Permission::checkMember($member, "ADMIN")) return true;
|
||||||
|
|
||||||
|
$extended = $this->extendedCan('canViewPages', $member);
|
||||||
|
if($extended !== null) return $extended;
|
||||||
|
|
||||||
if (!$this->CanViewType || $this->CanViewType == 'Anyone') return true;
|
if (!$this->CanViewType || $this->CanViewType == 'Anyone') return true;
|
||||||
|
|
||||||
// check for any logged-in users
|
// check for any logged-in users
|
||||||
if($this->CanViewType == 'LoggedInUsers' && $member) return true;
|
if($this->CanViewType === 'LoggedInUsers' && $member) return true;
|
||||||
|
|
||||||
// check for specific groups
|
// check for specific groups
|
||||||
if($this->CanViewType == 'OnlyTheseUsers' && $member && $member->inGroups($this->ViewerGroups())) return true;
|
if($this->CanViewType === 'OnlyTheseUsers' && $member && $member->inGroups($this->ViewerGroups())) return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -215,26 +248,45 @@ class SiteConfig extends DataObject implements PermissionProvider {
|
|||||||
/**
|
/**
|
||||||
* Can a user edit pages on this site? This method is only
|
* Can a user edit pages on this site? This method is only
|
||||||
* called if a page is set to Inherit, but there is nothing
|
* called if a page is set to Inherit, but there is nothing
|
||||||
* to inherit from.
|
* to inherit from, or on new records without a parent.
|
||||||
*
|
*
|
||||||
* @param mixed $member
|
* @param Member $member
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public function canEdit($member = null) {
|
public function canEditPages($member = null) {
|
||||||
if(!$member) $member = Member::currentUserID();
|
if(!$member) $member = Member::currentUserID();
|
||||||
if($member && is_numeric($member)) $member = DataObject::get_by_id('Member', $member);
|
if($member && is_numeric($member)) $member = DataObject::get_by_id('Member', $member);
|
||||||
|
|
||||||
if ($member && Permission::checkMember($member, "ADMIN")) return true;
|
if ($member && Permission::checkMember($member, "ADMIN")) return true;
|
||||||
|
|
||||||
// check for any logged-in users
|
$extended = $this->extendedCan('canEditPages', $member);
|
||||||
if(!$this->CanEditType || $this->CanEditType == 'LoggedInUsers' && $member) return true;
|
if($extended !== null) return $extended;
|
||||||
|
|
||||||
|
// check for any logged-in users with CMS access
|
||||||
|
if( $this->CanEditType === 'LoggedInUsers'
|
||||||
|
&& Permission::checkMember($member, $this->config()->required_permission)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// check for specific groups
|
// check for specific groups
|
||||||
if($this->CanEditType == 'OnlyTheseUsers' && $member && $member->inGroups($this->EditorGroups())) return true;
|
if($this->CanEditType === 'OnlyTheseUsers' && $member && $member->inGroups($this->EditorGroups())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function canEdit($member = null) {
|
||||||
|
if(!$member) $member = Member::currentUserID();
|
||||||
|
if($member && is_numeric($member)) $member = DataObject::get_by_id('Member', $member);
|
||||||
|
|
||||||
|
$extended = $this->extendedCan('canEdit', $member);
|
||||||
|
if($extended !== null) return $extended;
|
||||||
|
|
||||||
|
return Permission::checkMember($member, "EDIT_SITECONFIG");
|
||||||
|
}
|
||||||
|
|
||||||
public function providePermissions() {
|
public function providePermissions() {
|
||||||
return array(
|
return array(
|
||||||
'EDIT_SITECONFIG' => array(
|
'EDIT_SITECONFIG' => array(
|
||||||
@ -249,25 +301,32 @@ class SiteConfig extends DataObject implements PermissionProvider {
|
|||||||
/**
|
/**
|
||||||
* Can a user create pages in the root of this site?
|
* Can a user create pages in the root of this site?
|
||||||
*
|
*
|
||||||
* @param mixed $member
|
* @param Member $member
|
||||||
* @return boolean
|
* @return boolean
|
||||||
*/
|
*/
|
||||||
public function canCreateTopLevel($member = null) {
|
public function canCreateTopLevel($member = null) {
|
||||||
if(!$member || !(is_a($member, 'Member')) || is_numeric($member)) {
|
if(!$member) $member = Member::currentUserID();
|
||||||
$member = Member::currentUserID();
|
if($member && is_numeric($member)) $member = DataObject::get_by_id('Member', $member);
|
||||||
}
|
|
||||||
|
|
||||||
if (Permission::check('ADMIN')) return true;
|
|
||||||
|
|
||||||
if ($member && Permission::checkMember($member, "ADMIN")) return true;
|
if ($member && Permission::checkMember($member, "ADMIN")) return true;
|
||||||
|
|
||||||
// check for any logged-in users
|
$extended = $this->extendedCan('canCreateTopLevel', $member);
|
||||||
if($this->CanCreateTopLevelType == 'LoggedInUsers' && $member) return true;
|
if($extended !== null) return $extended;
|
||||||
|
|
||||||
|
// check for any logged-in users with CMS permission
|
||||||
|
if( $this->CanCreateTopLevelType === 'LoggedInUsers'
|
||||||
|
&& Permission::checkMember($member, $this->config()->required_permission)
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// check for specific groups
|
// check for specific groups
|
||||||
if($member && is_numeric($member)) $member = DataObject::get_by_id('Member', $member);
|
if( $this->CanCreateTopLevelType === 'OnlyTheseUsers'
|
||||||
if($this->CanCreateTopLevelType == 'OnlyTheseUsers' && $member && $member->inGroups($this->CreateTopLevelGroups())) return true;
|
&& $member
|
||||||
|
&& $member->inGroups($this->CreateTopLevelGroups())
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -815,7 +815,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
|||||||
// check for inherit
|
// check for inherit
|
||||||
if($this->CanViewType == 'Inherit') {
|
if($this->CanViewType == 'Inherit') {
|
||||||
if($this->ParentID) return $this->Parent()->canView($member);
|
if($this->ParentID) return $this->Parent()->canView($member);
|
||||||
else return $this->getSiteConfig()->canView($member);
|
else return $this->getSiteConfig()->canViewPages($member);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for any logged-in users
|
// check for any logged-in users
|
||||||
@ -896,12 +896,12 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This function should return true if the current user can create new
|
* This function should return true if the current user can create new
|
||||||
* pages of this class. It can be overloaded to customise the security model for an
|
* pages of this class, regardless of context. It can be overloaded
|
||||||
* application.
|
* to customise the security model for an application.
|
||||||
*
|
*
|
||||||
* Denies permission if any of the following conditions is TRUE:
|
* By default, permission to create at the root level is based on the SiteConfig
|
||||||
* - canCreate() returns FALSE on any extension
|
* configuration, and permission to create beneath a parent is based on the
|
||||||
* - $can_create is set to FALSE and the site is not in "dev mode"
|
* ability to edit that parent page.
|
||||||
*
|
*
|
||||||
* Use {@link canAddChildren()} to control behaviour of creating children under this page.
|
* Use {@link canAddChildren()} to control behaviour of creating children under this page.
|
||||||
*
|
*
|
||||||
@ -909,6 +909,9 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
|||||||
* @uses DataExtension->canCreate()
|
* @uses DataExtension->canCreate()
|
||||||
*
|
*
|
||||||
* @param Member $member
|
* @param Member $member
|
||||||
|
* @param array $context Optional array which may contain array('Parent' => $parentObj)
|
||||||
|
* If a parent page is known, it will be checked for validity.
|
||||||
|
* If omitted, it will be assumed this is to be created as a top level page.
|
||||||
* @return boolean True if the current user can create pages on this class.
|
* @return boolean True if the current user can create pages on this class.
|
||||||
*/
|
*/
|
||||||
public function canCreate($member = null) {
|
public function canCreate($member = null) {
|
||||||
@ -916,16 +919,31 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
|||||||
$member = Member::currentUserID();
|
$member = Member::currentUserID();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check parent (custom canCreate option for SiteTree)
|
||||||
|
// Block children not allowed for this parent type
|
||||||
|
$context = func_num_args() > 1 ? func_get_arg(1) : array();
|
||||||
|
$parent = isset($context['Parent']) ? $context['Parent'] : null;
|
||||||
|
if($parent && !in_array(get_class($this), $parent->allowedChildren())) return false;
|
||||||
|
|
||||||
|
// Check permission
|
||||||
if($member && Permission::checkMember($member, "ADMIN")) return true;
|
if($member && Permission::checkMember($member, "ADMIN")) return true;
|
||||||
|
|
||||||
// Standard mechanism for accepting permission changes from extensions
|
// Standard mechanism for accepting permission changes from extensions
|
||||||
$extended = $this->extendedCan('canCreate', $member);
|
$results = $this->extend('canCreate', $member, $parent);
|
||||||
if($extended !== null) return $extended;
|
if(is_array($results) && ($results = array_filter($results, function($v) {return $v !== null;}))) {
|
||||||
|
return min($results);
|
||||||
|
}
|
||||||
|
|
||||||
return $this->stat('can_create') != false || Director::isDev();
|
// Fall over to inherited permissions
|
||||||
|
if($parent) {
|
||||||
|
return $parent->canAddChildren($member);
|
||||||
|
} else {
|
||||||
|
// This doesn't necessarily mean we are creating a root page, but that
|
||||||
|
// we don't know if there is a parent, so default to this permission
|
||||||
|
return SiteConfig::current_site_config()->canCreateTopLevel($member);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function should return true if the current user can edit this
|
* This function should return true if the current user can edit this
|
||||||
* page. It can be overloaded to customise the security model for an
|
* page. It can be overloaded to customise the security model for an
|
||||||
@ -966,7 +984,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
|||||||
|
|
||||||
// Default for unsaved pages
|
// Default for unsaved pages
|
||||||
} else {
|
} else {
|
||||||
return $this->getSiteConfig()->canEdit($member);
|
return $this->getSiteConfig()->canEditPages($member);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1207,7 +1225,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
|||||||
* page can be edited.
|
* page can be edited.
|
||||||
*/
|
*/
|
||||||
static public function can_edit_multiple($ids, $memberID, $useCached = true) {
|
static public function can_edit_multiple($ids, $memberID, $useCached = true) {
|
||||||
return self::batch_permission_check($ids, $memberID, 'CanEditType', 'SiteTree_EditorGroups', 'canEdit', 'CMS_ACCESS_CMSMain', $useCached);
|
return self::batch_permission_check($ids, $memberID, 'CanEditType', 'SiteTree_EditorGroups', 'canEditPages', 'CMS_ACCESS_CMSMain', $useCached);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -2574,9 +2592,20 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
|||||||
* @return string a html string ready to be directly used in a template
|
* @return string a html string ready to be directly used in a template
|
||||||
*/
|
*/
|
||||||
public function getTreeTitle() {
|
public function getTreeTitle() {
|
||||||
|
// Build the list of candidate children
|
||||||
|
$children = array();
|
||||||
|
$candidates = static::page_type_classes();
|
||||||
|
foreach($this->allowedChildren() as $childClass) {
|
||||||
|
if(!in_array($childClass, $candidates)) continue;
|
||||||
|
$child = singleton($childClass);
|
||||||
|
if($child->canCreate(null, array('Parent' => $this))) {
|
||||||
|
$children[$childClass] = $child->i18n_singular_name();
|
||||||
|
}
|
||||||
|
}
|
||||||
$flags = $this->getStatusFlags();
|
$flags = $this->getStatusFlags();
|
||||||
$treeTitle = sprintf(
|
$treeTitle = sprintf(
|
||||||
"<span class=\"jstree-pageicon\"></span><span class=\"item\">%s</span>",
|
"<span class=\"jstree-pageicon\"></span><span class=\"item\" data-allowedchildren=\"%s\">%s</span>",
|
||||||
|
Convert::raw2att(Convert::raw2json($children)),
|
||||||
Convert::raw2xml(str_replace(array("\n","\r"),"",$this->MenuTitle))
|
Convert::raw2xml(str_replace(array("\n","\r"),"",$this->MenuTitle))
|
||||||
);
|
);
|
||||||
foreach($flags as $class => $data) {
|
foreach($flags as $class => $data) {
|
||||||
|
@ -15,37 +15,107 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
$(".cms-add-form").entwine({
|
$(".cms-add-form").entwine({
|
||||||
onmatch: function() {
|
ParentID: 0, // Last selected parentID
|
||||||
|
ParentCache: {}, // Cache allowed children for each selected page
|
||||||
|
onadd: function() {
|
||||||
var self = this;
|
var self = this;
|
||||||
this.find('#ParentID .TreeDropdownField').bind('change', function() {
|
this.find('#ParentID .TreeDropdownField').bind('change', function() {
|
||||||
self.updateTypeList();
|
self.updateTypeList();
|
||||||
});
|
});
|
||||||
|
this.find(".SelectionGroup.parent-mode").bind('change', function() {
|
||||||
|
self.updateTypeList();
|
||||||
|
});
|
||||||
this.updateTypeList();
|
this.updateTypeList();
|
||||||
this._super();
|
|
||||||
},
|
},
|
||||||
onunmatch: function() {
|
loadCachedChildren: function(parentID) {
|
||||||
this._super();
|
var cache = this.getParentCache();
|
||||||
|
if(typeof cache[parentID] !== 'undefined') return cache[parentID];
|
||||||
|
else return null;
|
||||||
|
},
|
||||||
|
saveCachedChildren: function(parentID, children) {
|
||||||
|
var cache = this.getParentCache();
|
||||||
|
cache[parentID] = children;
|
||||||
|
this.setParentCache(cache);
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Limit page type selection based on parent class.
|
* Limit page type selection based on parent selection.
|
||||||
|
* Select of root classes is pre-computed, but selections with a given parent
|
||||||
|
* are updated on-demand.
|
||||||
* Similar implementation to LeftAndMain.Tree.js.
|
* Similar implementation to LeftAndMain.Tree.js.
|
||||||
*/
|
*/
|
||||||
updateTypeList: function() {
|
updateTypeList: function() {
|
||||||
var hints = this.find('.hints').data('hints'),
|
var hints = this.data('hints'),
|
||||||
metadata = this.find('#ParentID .TreeDropdownField').data('metadata'),
|
parentTree = this.find('#ParentID .TreeDropdownField'),
|
||||||
id = this.find('#ParentID .TreeDropdownField').getValue(),
|
parentMode = this.find("input[name=ParentModeField]:checked").val(),
|
||||||
|
metadata = parentTree.data('metadata'),
|
||||||
|
id = (metadata && parentMode === 'child')
|
||||||
|
? (parentTree.getValue() || this.getParentID())
|
||||||
|
: null,
|
||||||
newClassName = metadata ? metadata.ClassName : null,
|
newClassName = metadata ? metadata.ClassName : null,
|
||||||
hintKey = newClassName ? newClassName : 'Root',
|
hintKey = (newClassName && parentMode === 'child')
|
||||||
hint = (typeof hints[hintKey] != 'undefined') ? hints[hintKey] : null;
|
? newClassName
|
||||||
|
: 'Root',
|
||||||
|
hint = (typeof hints[hintKey] !== 'undefined') ? hints[hintKey] : null,
|
||||||
|
self = this,
|
||||||
|
defaultChildClass = (hint && typeof hint.defaultChild !== 'undefined')
|
||||||
|
? hint.defaultChild
|
||||||
|
: null,
|
||||||
|
disallowedChildren = [];
|
||||||
|
|
||||||
var disallowedChildren = (hint && typeof hint.disallowedChildren != 'undefined') ? hint.disallowedChildren : [],
|
if(id) {
|
||||||
defaultChildClass = (hint && typeof hint.defaultChild != 'undefined') ? hint.defaultChild : null;
|
// Prevent interface operations
|
||||||
|
if(this.hasClass('loading')) return;
|
||||||
|
this.addClass('loading');
|
||||||
|
|
||||||
|
// Enable last parent ID to be re-selected from memory
|
||||||
|
this.setParentID(id);
|
||||||
|
if(!parentTree.getValue()) parentTree.setValue(id);
|
||||||
|
|
||||||
|
// Use cached data if available
|
||||||
|
disallowedChildren = this.loadCachedChildren(id);
|
||||||
|
if(disallowedChildren !== null) {
|
||||||
|
this.updateSelectionFilter(disallowedChildren, defaultChildClass);
|
||||||
|
this.removeClass('loading');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$.ajax({
|
||||||
|
url: self.data('childfilter'),
|
||||||
|
data: {'ParentID': id},
|
||||||
|
success: function(data) {
|
||||||
|
// reload current form and tree
|
||||||
|
self.saveCachedChildren(id, data);
|
||||||
|
self.updateSelectionFilter(data, defaultChildClass);
|
||||||
|
},
|
||||||
|
complete: function() {
|
||||||
|
self.removeClass('loading');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
disallowedChildren = (hint && typeof hint.disallowedChildren !== 'undefined')
|
||||||
|
? hint.disallowedChildren
|
||||||
|
: [],
|
||||||
|
this.updateSelectionFilter(disallowedChildren, defaultChildClass);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Update the selection filter with the given blacklist and default selection
|
||||||
|
*
|
||||||
|
* @param array disallowedChildren
|
||||||
|
* @param string defaultChildClass
|
||||||
|
*/
|
||||||
|
updateSelectionFilter: function(disallowedChildren, defaultChildClass) {
|
||||||
// Limit selection
|
// Limit selection
|
||||||
|
var allAllowed = null; // troolian
|
||||||
this.find('#PageType li').each(function() {
|
this.find('#PageType li').each(function() {
|
||||||
var className = $(this).find('input').val(), isAllowed = ($.inArray(className, disallowedChildren) == -1);
|
var className = $(this).find('input').val(),
|
||||||
|
isAllowed = ($.inArray(className, disallowedChildren) === -1);
|
||||||
|
|
||||||
$(this).setEnabled(isAllowed);
|
$(this).setEnabled(isAllowed);
|
||||||
|
if(!isAllowed) $(this).setSelected(false);
|
||||||
|
if(allAllowed === null) allAllowed = isAllowed;
|
||||||
|
else allAllowed = allAllowed && isAllowed;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set default child selection, or fall back to first available option
|
// Set default child selection, or fall back to first available option
|
||||||
@ -69,10 +139,13 @@
|
|||||||
},
|
},
|
||||||
setSelected: function(bool) {
|
setSelected: function(bool) {
|
||||||
var input = this.find('input');
|
var input = this.find('input');
|
||||||
this.toggleClass('selected', bool);
|
|
||||||
if(bool && !input.is(':disabled')) {
|
if(bool && !input.is(':disabled')) {
|
||||||
this.siblings().setSelected(false);
|
this.siblings().setSelected(false);
|
||||||
input.attr('checked', 'checked');
|
this.toggleClass('selected', true);
|
||||||
|
input.prop('checked', true);
|
||||||
|
} else {
|
||||||
|
this.toggleClass('selected', false);
|
||||||
|
input.prop('checked', false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setEnabled: function(bool) {
|
setEnabled: function(bool) {
|
||||||
|
@ -9,23 +9,23 @@
|
|||||||
'items': function(node) {
|
'items': function(node) {
|
||||||
|
|
||||||
// Build a list for allowed children as submenu entries
|
// Build a list for allowed children as submenu entries
|
||||||
var pagetype = node.data('pagetype');
|
|
||||||
var id = node.data('id');
|
var id = node.data('id');
|
||||||
|
|
||||||
|
var allowedChildrenClasses = node.find('>a .item').data('allowedchildren');
|
||||||
var allowedChildren = new Object;
|
var allowedChildren = new Object;
|
||||||
$(hints[pagetype].allowedChildren).each(
|
var hasAllowedChildren = false;
|
||||||
function(key, val){
|
$.each(allowedChildrenClasses, function(klass, title) {
|
||||||
allowedChildren["allowedchildren-" + key ] = {
|
hasAllowedChildren = true;
|
||||||
'label': '<span class="jstree-pageicon"></span>' + val.ssname,
|
allowedChildren["allowedchildren-" + klass ] = {
|
||||||
'_class': 'class-' + val.ssclass,
|
'label': '<span class="jstree-pageicon"></span>' + title,
|
||||||
'action': function(obj) {
|
'_class': 'class-' + klass,
|
||||||
$('.cms-container').entwine('.ss').loadPanel(ss.i18n.sprintf(
|
'action': function(obj) {
|
||||||
self.data('urlAddpage'), id, val.ssclass
|
$('.cms-container').entwine('.ss').loadPanel(ss.i18n.sprintf(
|
||||||
));
|
self.data('urlAddpage'), id, klass
|
||||||
}
|
));
|
||||||
};
|
}
|
||||||
}
|
};
|
||||||
);
|
});
|
||||||
var menuitems =
|
var menuitems =
|
||||||
{
|
{
|
||||||
'edit': {
|
'edit': {
|
||||||
@ -38,7 +38,7 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Test if there are any allowed Children and thus the possibility of adding some
|
// Test if there are any allowed Children and thus the possibility of adding some
|
||||||
if(allowedChildren.hasOwnProperty('allowedchildren-0')) {
|
if(hasAllowedChildren) {
|
||||||
menuitems['addsubpage'] = {
|
menuitems['addsubpage'] = {
|
||||||
'label': ss.i18n._t('Tree.AddSubPage', 'Add page under this page', 100, 'Used in the context menu when right-clicking on a page node in the CMS tree'),
|
'label': ss.i18n._t('Tree.AddSubPage', 'Add page under this page', 100, 'Used in the context menu when right-clicking on a page node in the CMS tree'),
|
||||||
'submenu': allowedChildren
|
'submenu': allowedChildren
|
||||||
|
@ -19,7 +19,7 @@ $ExtraTreeTools
|
|||||||
</div>
|
</div>
|
||||||
<% end_if %>
|
<% end_if %>
|
||||||
|
|
||||||
<div class="cms-tree" data-url-tree="$Link(getsubtree)" data-url-savetreenode="$Link(savetreenode)" data-url-updatetreenodes="$Link(updatetreenodes)" data-url-addpage="{$LinkPageAdd('AddForm/?action_doAdd=1')}&ParentID=%s&PageType=%s&SecurityID=$SecurityID" data-url-editpage="$LinkPageEdit('%s')" data-hints="$SiteTreeHints">
|
<div class="cms-tree" data-url-tree="$Link(getsubtree)" data-url-savetreenode="$Link(savetreenode)" data-url-updatetreenodes="$Link(updatetreenodes)" data-url-addpage="{$LinkPageAdd('AddForm/?action_doAdd=1')}&ParentID=%s&PageType=%s&SecurityID=$SecurityID" data-url-editpage="$LinkPageEdit('%s')" data-hints="$SiteTreeHints.ATT" data-childfilter="$Link('childfilter')">
|
||||||
$SiteTreeAsUL
|
$SiteTreeAsUL
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -26,6 +26,78 @@ class CMSMainTest extends FunctionalTest {
|
|||||||
parent::tearDownOnce();
|
parent::tearDownOnce();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function testSiteTreeHints() {
|
||||||
|
$cache = SS_Cache::factory('CMSMain_SiteTreeHints');
|
||||||
|
// Login as user with root creation privileges
|
||||||
|
$user = $this->objFromFixture('Member', 'rootedituser');
|
||||||
|
$user->logIn();
|
||||||
|
$cache->clean(Zend_Cache::CLEANING_MODE_ALL);
|
||||||
|
|
||||||
|
$rawHints = singleton('CMSMain')->SiteTreeHints();
|
||||||
|
$this->assertNotNull($rawHints);
|
||||||
|
|
||||||
|
$rawHints = preg_replace('/^"(.*)"$/', '$1', Convert::xml2raw($rawHints));
|
||||||
|
$hints = Convert::json2array($rawHints);
|
||||||
|
|
||||||
|
$this->assertArrayHasKey('Root', $hints);
|
||||||
|
$this->assertArrayHasKey('Page', $hints);
|
||||||
|
$this->assertArrayHasKey('All', $hints);
|
||||||
|
|
||||||
|
$this->assertArrayHasKey(
|
||||||
|
'CMSMainTest_ClassA',
|
||||||
|
$hints['All'],
|
||||||
|
'Global list shows allowed classes'
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertArrayNotHasKey(
|
||||||
|
'CMSMainTest_HiddenClass',
|
||||||
|
$hints['All'],
|
||||||
|
'Global list does not list hidden classes'
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertNotContains(
|
||||||
|
'CMSMainTest_ClassA',
|
||||||
|
$hints['Root']['disallowedChildren'],
|
||||||
|
'Limits root classes'
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->assertContains(
|
||||||
|
'CMSMainTest_NotRoot',
|
||||||
|
$hints['Root']['disallowedChildren'],
|
||||||
|
'Limits root classes'
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testChildFilter() {
|
||||||
|
$this->logInWithPermission('ADMIN');
|
||||||
|
|
||||||
|
// Check page A
|
||||||
|
$pageA = new CMSMainTest_ClassA();
|
||||||
|
$pageA->write();
|
||||||
|
$pageB = new CMSMainTest_ClassB();
|
||||||
|
$pageB->write();
|
||||||
|
|
||||||
|
// Check query
|
||||||
|
$response = $this->get('CMSMain/childfilter?ParentID='.$pageA->ID);
|
||||||
|
$children = json_decode($response->getBody());
|
||||||
|
$this->assertFalse($response->isError());
|
||||||
|
|
||||||
|
// Page A can't have unrelated children
|
||||||
|
$this->assertContains(
|
||||||
|
'Page',
|
||||||
|
$children,
|
||||||
|
'Limited parent lists disallowed classes'
|
||||||
|
);
|
||||||
|
|
||||||
|
// But it can create a ClassB
|
||||||
|
$this->assertNotContains(
|
||||||
|
'CMSMainTest_ClassB',
|
||||||
|
$children,
|
||||||
|
'Limited parent omits explicitly allowed classes in disallowedChildren'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @todo Test the results of a publication better
|
* @todo Test the results of a publication better
|
||||||
*/
|
*/
|
||||||
@ -256,11 +328,7 @@ class CMSMainTest extends FunctionalTest {
|
|||||||
'admin/pages/add/AddForm',
|
'admin/pages/add/AddForm',
|
||||||
array('ParentID' => $newPageId, 'PageType' => 'Page', 'Locale' => 'en_US', 'action_doAdd' => 1)
|
array('ParentID' => $newPageId, 'PageType' => 'Page', 'Locale' => 'en_US', 'action_doAdd' => 1)
|
||||||
);
|
);
|
||||||
$this->assertFalse($response->isError());
|
$this->assertEquals(403, $response->getStatusCode(), 'Add disallowed child should fail');
|
||||||
$this->assertContains(
|
|
||||||
htmlentities(_t('SiteTree.PageTypeNotAllowed', array('type' => 'Page'))),
|
|
||||||
$response->getBody()
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->session()->inst_set('loggedInAs', NULL);
|
$this->session()->inst_set('loggedInAs', NULL);
|
||||||
|
|
||||||
@ -314,3 +382,11 @@ class CMSMainTest_ClassA extends Page implements TestOnly {
|
|||||||
class CMSMainTest_ClassB extends Page implements TestOnly {
|
class CMSMainTest_ClassB extends Page implements TestOnly {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CMSMainTest_NotRoot extends Page implements TestOnly {
|
||||||
|
static $can_be_root = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
class CMSMainTest_HiddenClass extends Page implements TestOnly, HiddenClass {
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -8,12 +8,14 @@
|
|||||||
*/
|
*/
|
||||||
class SiteConfigTest extends SapphireTest {
|
class SiteConfigTest extends SapphireTest {
|
||||||
|
|
||||||
|
static $fixture_file = 'SiteConfigTest.yml';
|
||||||
|
|
||||||
protected $illegalExtensions = array(
|
protected $illegalExtensions = array(
|
||||||
'SiteTree' => array('SiteTreeSubsites')
|
'SiteTree' => array('SiteTreeSubsites')
|
||||||
);
|
);
|
||||||
|
|
||||||
public function testAvailableThemes() {
|
public function testAvailableThemes() {
|
||||||
$config = SiteConfig::current_site_config();
|
$config = $this->objFromFixture('SiteConfig', 'default');
|
||||||
$ds = DIRECTORY_SEPARATOR;
|
$ds = DIRECTORY_SEPARATOR;
|
||||||
$testThemeBaseDir = TEMP_FOLDER . $ds . 'test-themes';
|
$testThemeBaseDir = TEMP_FOLDER . $ds . 'test-themes';
|
||||||
|
|
||||||
@ -35,4 +37,49 @@ class SiteConfigTest extends SapphireTest {
|
|||||||
Filesystem::removeFolder($testThemeBaseDir);
|
Filesystem::removeFolder($testThemeBaseDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testCanCreateRootPages() {
|
||||||
|
$config = $this->objFromFixture('SiteConfig', 'default');
|
||||||
|
|
||||||
|
// Log in without pages admin access
|
||||||
|
$this->logInWithPermission('CMS_ACCESS_AssetAdmin');
|
||||||
|
$this->assertFalse($config->canCreateTopLevel());
|
||||||
|
|
||||||
|
// Login with necessary edit permission
|
||||||
|
$perms = SiteConfig::config()->required_permission;
|
||||||
|
$this->logInWithPermission(reset($perms));
|
||||||
|
$this->assertTrue($config->canCreateTopLevel());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCanViewPages() {
|
||||||
|
$config = $this->objFromFixture('SiteConfig', 'default');
|
||||||
|
$this->assertTrue($config->canViewPages());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCanEdit() {
|
||||||
|
$config = $this->objFromFixture('SiteConfig', 'default');
|
||||||
|
|
||||||
|
// Unrelated permissions don't allow siteconfig
|
||||||
|
$this->logInWithPermission('CMS_ACCESS_AssetAdmin');
|
||||||
|
$this->assertFalse($config->canEdit());
|
||||||
|
|
||||||
|
// Only those with edit permission can do this
|
||||||
|
$this->logInWithPermission('EDIT_SITECONFIG');
|
||||||
|
$this->assertTrue($config->canEdit());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCanEditPages() {
|
||||||
|
$config = $this->objFromFixture('SiteConfig', 'default');
|
||||||
|
|
||||||
|
// Log in without pages admin access
|
||||||
|
$this->logInWithPermission('CMS_ACCESS_AssetAdmin');
|
||||||
|
$this->assertFalse($config->canEditPages());
|
||||||
|
|
||||||
|
// Login with necessary edit permission
|
||||||
|
$perms = SiteConfig::config()->required_permission;
|
||||||
|
$this->logInWithPermission(reset($perms));
|
||||||
|
$this->assertTrue($config->canEditPages());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
7
tests/model/SiteConfigTest.yml
Normal file
7
tests/model/SiteConfigTest.yml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
SiteConfig:
|
||||||
|
default:
|
||||||
|
Title: My test site
|
||||||
|
Tagline: Default site config
|
||||||
|
CanViewType: Anyone
|
||||||
|
CanEditType: LoggedInUsers
|
||||||
|
CanCreateTopLevelType: LoggedInUsers
|
@ -20,6 +20,13 @@ class SiteTreeTest extends SapphireTest {
|
|||||||
'SiteTreeTest_StageStatusInherit',
|
'SiteTreeTest_StageStatusInherit',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure any current member is logged out
|
||||||
|
*/
|
||||||
|
public function logOut() {
|
||||||
|
if($member = Member::currentUser()) $member->logOut();
|
||||||
|
}
|
||||||
|
|
||||||
public function testCreateDefaultpages() {
|
public function testCreateDefaultpages() {
|
||||||
$remove = DataObject::get('SiteTree');
|
$remove = DataObject::get('SiteTree');
|
||||||
if($remove) foreach($remove as $page) $page->delete();
|
if($remove) foreach($remove as $page) $page->delete();
|
||||||
@ -98,7 +105,6 @@ class SiteTreeTest extends SapphireTest {
|
|||||||
$this->assertTrue($obj->doPublish());
|
$this->assertTrue($obj->doPublish());
|
||||||
|
|
||||||
$this->assertNull(DB::query("SELECT \"MetaTitle\" FROM \"SiteTree_Live\" WHERE \"ID\" = '$obj->ID'")->value());
|
$this->assertNull(DB::query("SELECT \"MetaTitle\" FROM \"SiteTree_Live\" WHERE \"ID\" = '$obj->ID'")->value());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testParentNodeCachedInMemory() {
|
public function testParentNodeCachedInMemory() {
|
||||||
@ -434,10 +440,15 @@ class SiteTreeTest extends SapphireTest {
|
|||||||
$editor = $this->objFromFixture("Member", "editor");
|
$editor = $this->objFromFixture("Member", "editor");
|
||||||
|
|
||||||
$home = $this->objFromFixture("Page", "home");
|
$home = $this->objFromFixture("Page", "home");
|
||||||
|
$staff = $this->objFromFixture("Page", "staff");
|
||||||
$products = $this->objFromFixture("Page", "products");
|
$products = $this->objFromFixture("Page", "products");
|
||||||
$product1 = $this->objFromFixture("Page", "product1");
|
$product1 = $this->objFromFixture("Page", "product1");
|
||||||
$product4 = $this->objFromFixture("Page", "product4");
|
$product4 = $this->objFromFixture("Page", "product4");
|
||||||
|
|
||||||
|
// Test logged out users cannot edit
|
||||||
|
$this->logOut();
|
||||||
|
$this->assertFalse($staff->canEdit());
|
||||||
|
|
||||||
// Can't edit a page that is locked to admins
|
// Can't edit a page that is locked to admins
|
||||||
$this->assertFalse($home->canEdit($editor));
|
$this->assertFalse($home->canEdit($editor));
|
||||||
|
|
||||||
@ -451,6 +462,44 @@ class SiteTreeTest extends SapphireTest {
|
|||||||
$this->assertFalse($product4->canEdit($editor));
|
$this->assertFalse($product4->canEdit($editor));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testCanEditWithAccessToAllSections() {
|
||||||
|
$page = new Page();
|
||||||
|
$page->write();
|
||||||
|
$allSectionMember = $this->objFromFixture('Member', 'allsections');
|
||||||
|
$securityAdminMember = $this->objFromFixture('Member', 'securityadmin');
|
||||||
|
|
||||||
|
$this->assertTrue(SiteConfig::current_site_config()->canEditPages($allSectionMember));
|
||||||
|
$this->assertTrue($page->canEdit($allSectionMember));
|
||||||
|
$this->assertFalse($page->canEdit($securityAdminMember));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testCreatePermissions() {
|
||||||
|
// Test logged out users cannot create
|
||||||
|
$this->logOut();
|
||||||
|
$this->assertFalse(singleton('SiteTree')->canCreate());
|
||||||
|
|
||||||
|
// Login with another permission
|
||||||
|
$this->logInWithPermission('DUMMY');
|
||||||
|
$this->assertFalse(singleton('SiteTree')->canCreate());
|
||||||
|
|
||||||
|
// Login with basic CMS permission
|
||||||
|
$perms = SiteConfig::config()->required_permission;
|
||||||
|
$this->logInWithPermission(reset($perms));
|
||||||
|
$this->assertTrue(singleton('SiteTree')->canCreate());
|
||||||
|
|
||||||
|
// Test creation underneath a parent which this user doesn't have access to
|
||||||
|
$parent = $this->objFromFixture('Page', 'about');
|
||||||
|
$this->assertFalse(singleton('SiteTree')->canCreate(null, array('Parent' => $parent)));
|
||||||
|
|
||||||
|
// Test creation underneath a parent which doesn't allow a certain child
|
||||||
|
$parentB = new SiteTreeTest_ClassB();
|
||||||
|
$parentB->Title = 'Only Allows SiteTreeTest_ClassC';
|
||||||
|
$parentB->write();
|
||||||
|
$this->assertTrue(singleton('SiteTreeTest_ClassA')->canCreate(null));
|
||||||
|
$this->assertFalse(singleton('SiteTreeTest_ClassA')->canCreate(null, array('Parent' => $parentB)));
|
||||||
|
$this->assertTrue(singleton('SiteTreeTest_ClassC')->canCreate(null, array('Parent' => $parentB)));
|
||||||
|
}
|
||||||
|
|
||||||
public function testEditPermissionsOnDraftVsLive() {
|
public function testEditPermissionsOnDraftVsLive() {
|
||||||
// Create an inherit-permission page
|
// Create an inherit-permission page
|
||||||
$page = new Page();
|
$page = new Page();
|
||||||
@ -488,7 +537,7 @@ class SiteTreeTest extends SapphireTest {
|
|||||||
// Confirm that Member.editor can still edit the page
|
// Confirm that Member.editor can still edit the page
|
||||||
$this->objFromFixture('Member','editor')->logIn();
|
$this->objFromFixture('Member','editor')->logIn();
|
||||||
$this->assertTrue($page->canEdit());
|
$this->assertTrue($page->canEdit());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testCompareVersions() {
|
public function testCompareVersions() {
|
||||||
// Necessary to avoid
|
// Necessary to avoid
|
||||||
@ -879,7 +928,7 @@ class SiteTreeTest extends SapphireTest {
|
|||||||
$this->assertEquals(null, $page->getField('MenuTitle'));
|
$this->assertEquals(null, $page->getField('MenuTitle'));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**#@+
|
/**#@+
|
||||||
* @ignore
|
* @ignore
|
||||||
|
@ -1,82 +1,104 @@
|
|||||||
|
SiteConfig:
|
||||||
|
default:
|
||||||
|
Title: My test site
|
||||||
|
Tagline: Default site config
|
||||||
|
CanViewType: Anyone
|
||||||
|
CanEditType: LoggedInUsers
|
||||||
|
CanCreateTopLevelType: LoggedInUsers
|
||||||
|
|
||||||
Group:
|
Group:
|
||||||
editors:
|
editors:
|
||||||
Title: Editors
|
Title: Editors
|
||||||
admins:
|
admins:
|
||||||
Title: Administrators
|
Title: Administrators
|
||||||
|
allsections:
|
||||||
|
Title: All Section Editors
|
||||||
|
securityadmins:
|
||||||
|
Title: Security Admins
|
||||||
|
|
||||||
Permission:
|
Permission:
|
||||||
admins:
|
admins:
|
||||||
Code: ADMIN
|
Code: ADMIN
|
||||||
Group: =>Group.admins
|
Group: =>Group.admins
|
||||||
editors:
|
editors:
|
||||||
Code: CMS_ACCESS_CMSMain
|
Code: CMS_ACCESS_CMSMain
|
||||||
Group: =>Group.editors
|
Group: =>Group.editors
|
||||||
|
allsections:
|
||||||
|
Code: CMS_ACCESS_CMSMain
|
||||||
|
Group: =>Group.allsections
|
||||||
|
securityadmins:
|
||||||
|
Code: CMS_ACCESS_SecurityAdmin
|
||||||
|
Group: =>Group.securityadmins
|
||||||
|
|
||||||
Member:
|
Member:
|
||||||
editor:
|
editor:
|
||||||
FirstName: Test
|
FirstName: Test
|
||||||
Surname: Editor
|
Surname: Editor
|
||||||
Groups: =>Group.editors
|
Groups: =>Group.editors
|
||||||
admin:
|
admin:
|
||||||
FirstName: Test
|
FirstName: Test
|
||||||
Surname: Administrator
|
Surname: Administrator
|
||||||
Groups: =>Group.admins
|
Groups: =>Group.admins
|
||||||
|
allsections:
|
||||||
|
Groups: =>Group.allsections
|
||||||
|
securityadmin:
|
||||||
|
Groups: =>Group.securityadmins
|
||||||
|
|
||||||
Page:
|
Page:
|
||||||
home:
|
home:
|
||||||
Title: Home
|
Title: Home
|
||||||
CanEditType: OnlyTheseUsers
|
CanEditType: OnlyTheseUsers
|
||||||
EditorGroups: =>Group.admins
|
EditorGroups: =>Group.admins
|
||||||
about:
|
about:
|
||||||
Title: About Us
|
Title: About Us
|
||||||
CanEditType: OnlyTheseUsers
|
CanEditType: OnlyTheseUsers
|
||||||
EditorGroups: =>Group.admins
|
EditorGroups: =>Group.admins
|
||||||
staff:
|
staff:
|
||||||
Title: Staff
|
Title: Staff
|
||||||
URLSegment: my-staff
|
URLSegment: my-staff
|
||||||
Parent: =>Page.about
|
Parent: =>Page.about
|
||||||
ceo:
|
ceo:
|
||||||
Title: CEO
|
Title: CEO
|
||||||
Parent: =>Page.staff
|
Parent: =>Page.staff
|
||||||
staffduplicate:
|
staffduplicate:
|
||||||
Title: Staff
|
Title: Staff
|
||||||
URLSegment: my-staff
|
URLSegment: my-staff
|
||||||
Parent: =>Page.about
|
Parent: =>Page.about
|
||||||
products:
|
products:
|
||||||
Title: Products
|
Title: Products
|
||||||
CanEditType: OnlyTheseUsers
|
CanEditType: OnlyTheseUsers
|
||||||
EditorGroups: =>Group.editors
|
EditorGroups: =>Group.editors
|
||||||
product1:
|
product1:
|
||||||
Title: 1.1 Test Product
|
Title: 1.1 Test Product
|
||||||
Parent: =>Page.products
|
Parent: =>Page.products
|
||||||
CanEditType: Inherit
|
CanEditType: Inherit
|
||||||
product2:
|
product2:
|
||||||
Title: Another Product
|
Title: Another Product
|
||||||
Parent: =>Page.products
|
Parent: =>Page.products
|
||||||
CanEditType: Inherit
|
CanEditType: Inherit
|
||||||
product3:
|
product3:
|
||||||
Title: Another Product
|
Title: Another Product
|
||||||
Parent: =>Page.products
|
Parent: =>Page.products
|
||||||
CanEditType: Inherit
|
CanEditType: Inherit
|
||||||
product4:
|
product4:
|
||||||
Title: Another Product
|
Title: Another Product
|
||||||
Parent: =>Page.products
|
Parent: =>Page.products
|
||||||
CanEditType: OnlyTheseUsers
|
CanEditType: OnlyTheseUsers
|
||||||
EditorGroups: =>Group.admins
|
EditorGroups: =>Group.admins
|
||||||
contact:
|
contact:
|
||||||
Title: Contact Us
|
Title: Contact Us
|
||||||
object:
|
object:
|
||||||
Title: Object
|
Title: Object
|
||||||
controller:
|
controller:
|
||||||
Title: Controller
|
Title: Controller
|
||||||
numericonly:
|
numericonly:
|
||||||
Title: 1930
|
Title: 1930
|
||||||
|
|
||||||
SiteTreeTest_Conflicted:
|
SiteTreeTest_Conflicted:
|
||||||
parent:
|
parent:
|
||||||
Title: Parent
|
Title: Parent
|
||||||
|
|
||||||
ErrorPage:
|
ErrorPage:
|
||||||
404:
|
404:
|
||||||
Title: Page not Found
|
Title: Page not Found
|
||||||
ErrorCode: 404
|
ErrorCode: 404
|
Loading…
Reference in New Issue
Block a user