mirror of
https://github.com/silverstripe/silverstripe-cms
synced 2024-10-22 06:05:56 +00:00
Merge remote-tracking branch 'origin/3'
This commit is contained in:
commit
3b7abb09ed
@ -54,6 +54,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
'treeview',
|
||||
'listview',
|
||||
'ListViewForm',
|
||||
'childfilter',
|
||||
);
|
||||
|
||||
public function init() {
|
||||
@ -409,60 +410,38 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
|
||||
// Contains all possible classes to support UI controls listing them all,
|
||||
// such as the "add page here" context menu.
|
||||
$def['All'] = array();
|
||||
$def['All'] = array();
|
||||
|
||||
// Identify disallows and set globals
|
||||
$globalDisallowed = array();
|
||||
foreach($classes as $class) {
|
||||
$obj = singleton($class);
|
||||
$needsPerm = $obj->stat('need_permission');
|
||||
|
||||
if(!($obj instanceof HiddenClass)) {
|
||||
$def['All'][$class] = array(
|
||||
'title' => $obj->i18n_singular_name()
|
||||
);
|
||||
}
|
||||
|
||||
if(!$obj->stat('can_be_root')) {
|
||||
$def['Root']['disallowedChildren'][] = $class;
|
||||
}
|
||||
|
||||
if(
|
||||
($obj instanceof HiddenClass)
|
||||
|| (!array_key_exists($class, $cacheCanCreate) || !$cacheCanCreate[$class])
|
||||
|| ($needsPerm && !$this->can($needsPerm))
|
||||
) {
|
||||
$globalDisallowed[] = $class;
|
||||
$def['Root']['disallowedChildren'][] = $class;
|
||||
}
|
||||
}
|
||||
|
||||
// Set disallows by class
|
||||
foreach($classes as $class) {
|
||||
$obj = singleton($class);
|
||||
if($obj instanceof HiddenClass) continue;
|
||||
|
||||
// Name item
|
||||
$def['All'][$class] = array(
|
||||
'title' => $obj->i18n_singular_name()
|
||||
);
|
||||
|
||||
// Check if can be created at the root
|
||||
$needsPerm = $obj->stat('need_permission');
|
||||
if(
|
||||
!$obj->stat('can_be_root')
|
||||
|| (!array_key_exists($class, $cacheCanCreate) || !$cacheCanCreate[$class])
|
||||
|| ($needsPerm && !$this->can($needsPerm))
|
||||
) {
|
||||
$def['Root']['disallowedChildren'][] = $class;
|
||||
}
|
||||
|
||||
// Hint data specific to the class
|
||||
$def[$class] = array();
|
||||
|
||||
$allowed = $obj->allowedChildren();
|
||||
if($pos = array_search('SiteTree', $allowed)) unset($allowed[$pos]);
|
||||
|
||||
// Start by disallowing all classes which aren't specifically allowed,
|
||||
// then add the ones which are globally disallowed.
|
||||
$disallowed = array_diff($classes, (array)$allowed);
|
||||
$disallowed = array_unique(array_merge($disallowed, $globalDisallowed));
|
||||
// Re-index the array for JSON non sequential key issue
|
||||
if($disallowed) $def[$class]['disallowedChildren'] = array_values($disallowed);
|
||||
|
||||
$defaultChild = $obj->defaultChild();
|
||||
if($defaultChild != 'Page' && $defaultChild != null) {
|
||||
if($defaultChild !== 'Page' && $defaultChild !== null) {
|
||||
$def[$class]['defaultChild'] = $defaultChild;
|
||||
}
|
||||
|
||||
$defaultParent = $obj->defaultParent();
|
||||
$parent = SiteTree::get_by_link($defaultParent);
|
||||
$id = $parent ? $parent->id : null;
|
||||
if ($defaultParent != 1 && $defaultParent != null) {
|
||||
if ($defaultParent !== 1 && $defaultParent !== null) {
|
||||
$def[$class]['defaultParent'] = $defaultParent;
|
||||
}
|
||||
}
|
||||
@ -491,8 +470,6 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
|
||||
if($instance instanceof HiddenClass) continue;
|
||||
|
||||
if(!$instance->canCreate()) continue;
|
||||
|
||||
// skip this type if it is restricted
|
||||
if($instance->stat('need_permission') && !$this->can(singleton($class)->stat('need_permission'))) continue;
|
||||
|
||||
@ -705,6 +682,39 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
||||
public function listview($request) {
|
||||
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);
|
||||
return $this
|
||||
->response
|
||||
->addHeader('Content-Type', 'application/json; charset=utf-8')
|
||||
->setBody(Convert::raw2json($disallowedChildren));
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely reconstruct a selected filter from a given set of query parameters
|
||||
|
@ -16,7 +16,7 @@ class CMSPageAddController extends CMSPageEditController {
|
||||
/**
|
||||
* @return Form
|
||||
*/
|
||||
function AddForm() {
|
||||
public function AddForm() {
|
||||
$pageTypes = array();
|
||||
foreach($this->PageTypes() as $type) {
|
||||
$html = sprintf('<span class="page-icon class-%s"></span><strong class="title">%s</strong><span class="description">%s</span>',
|
||||
@ -38,11 +38,6 @@ class CMSPageAddController extends CMSPageEditController {
|
||||
$childTitle = _t('CMSPageAddController.ParentMode_child', 'Under another page');
|
||||
|
||||
$fields = new FieldList(
|
||||
// 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>', Convert::raw2xml($this->SiteTreeHints()))
|
||||
),
|
||||
new LiteralField('PageModeHeader', sprintf($numericLabelTmpl, 1, _t('CMSMain.ChoosePageParentMode', 'Choose where to create this page'))),
|
||||
$parentModeField = new SelectionGroup(
|
||||
"ParentModeField",
|
||||
@ -68,7 +63,7 @@ class CMSPageAddController extends CMSPageEditController {
|
||||
$typeField = new OptionsetField(
|
||||
"PageType",
|
||||
sprintf($numericLabelTmpl, 2, _t('CMSMain.ChoosePageType', 'Choose page type')),
|
||||
$pageTypes,
|
||||
$pageTypes,
|
||||
'Page'
|
||||
),
|
||||
new LiteralField(
|
||||
@ -78,7 +73,7 @@ class CMSPageAddController extends CMSPageEditController {
|
||||
_t(
|
||||
'CMSMain.AddPageRestriction',
|
||||
'Note: Some page types are not allowed for this selection'
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
@ -122,6 +117,9 @@ class CMSPageAddController extends CMSPageEditController {
|
||||
$form = CMSForm::create(
|
||||
$this, "AddForm", $fields, $actions
|
||||
)->setHTMLID('Form_AddForm');
|
||||
$form->setAttribute('data-hints', $this->SiteTreeHints());
|
||||
$form->setAttribute('data-childfilter', $this->Link('childfilter'));
|
||||
|
||||
$form->setResponseNegotiator($this->getResponseNegotiator());
|
||||
$form->addExtraClass('cms-add-form stacked cms-content center cms-edit-form ' . $this->BaseCSSClasses());
|
||||
$form->setTemplate($this->getTemplatesWithSuffix('_EditForm'));
|
||||
@ -145,12 +143,8 @@ class CMSPageAddController extends CMSPageEditController {
|
||||
|
||||
if(!$parentObj || !$parentObj->ID) $parentID = 0;
|
||||
|
||||
if($parentObj) {
|
||||
if(!$parentObj->canAddChildren()) return Security::permissionFailure($this);
|
||||
if(!singleton($className)->canCreate()) return Security::permissionFailure($this);
|
||||
} else {
|
||||
if(!SiteConfig::current_site_config()->canCreateTopLevel())
|
||||
return Security::permissionFailure($this);
|
||||
if(!singleton($className)->canCreate(Member::currentUser(), array('Parent' => $parentObj))) {
|
||||
return Security::permissionFailure($this);
|
||||
}
|
||||
|
||||
$record = $this->getNewItem("new-$className-$parentID".$suffix, false);
|
||||
|
@ -227,34 +227,34 @@ class ErrorPage extends Page {
|
||||
public function doPublish() {
|
||||
parent::doPublish();
|
||||
|
||||
return $this->writeStaticPage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write out the published version of the page to the filesystem
|
||||
*
|
||||
* @return mixed Either true, or an error
|
||||
*/
|
||||
public function writeStaticPage() {
|
||||
// Run the page (reset the theme, it might've been disabled by LeftAndMain::init())
|
||||
$oldEnabled = Config::inst()->get('SSViewer', 'theme_enabled');
|
||||
Config::inst()->update('SSViewer', 'theme_enabled', true);
|
||||
|
||||
$response = Director::test(Director::makeRelative($this->Link()));
|
||||
Config::inst()->update('SSViewer', 'theme_enabled', $oldEnabled);
|
||||
|
||||
$errorContent = $response->getBody();
|
||||
|
||||
// Make the base tag dynamic.
|
||||
// $errorContent = preg_replace('/<base[^>]+href="' . str_replace('/','\\/', Director::absoluteBaseURL()) . '"[^>]*>/i', '<base href="$BaseURL" />', $errorContent);
|
||||
|
||||
|
||||
// Check we have an assets base directory, creating if it we don't
|
||||
if(!file_exists(ASSETS_PATH)) {
|
||||
mkdir(ASSETS_PATH, 02775);
|
||||
}
|
||||
|
||||
|
||||
// if the page is published in a language other than default language,
|
||||
// write a specific language version of the HTML page
|
||||
$filePath = self::get_filepath_for_errorcode($this->ErrorCode, $this->Locale);
|
||||
if($fh = fopen($filePath, "w")) {
|
||||
fwrite($fh, $errorContent);
|
||||
fclose($fh);
|
||||
} else {
|
||||
if (!file_put_contents($filePath, $errorContent)) {
|
||||
$fileErrorText = _t(
|
||||
"ErrorPage.ERRORFILEPROBLEM",
|
||||
"Error opening file \"{filename}\" for writing. Please check file permissions.",
|
||||
'ErrorPage.ERRORFILEPROBLEM',
|
||||
'Error opening file "{filename}" for writing. Please check file permissions.',
|
||||
array('filename' => $errorFile)
|
||||
);
|
||||
$this->response->addHeader('X-Status', rawurlencode($fileErrorText));
|
||||
|
@ -934,7 +934,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
||||
// check for inherit
|
||||
if($this->CanViewType == 'Inherit') {
|
||||
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
|
||||
@ -1013,12 +1013,12 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
||||
|
||||
/**
|
||||
* 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
|
||||
* application.
|
||||
* pages of this class, regardless of context. It can be overloaded
|
||||
* to customise the security model for an application.
|
||||
*
|
||||
* Denies permission if any of the following conditions is TRUE:
|
||||
* - canCreate() returns FALSE on any extension
|
||||
* - $can_create is set to FALSE and the site is not in "dev mode"
|
||||
* By default, permission to create at the root level is based on the SiteConfig
|
||||
* configuration, and permission to create beneath a parent is based on the
|
||||
* ability to edit that parent page.
|
||||
*
|
||||
* Use {@link canAddChildren()} to control behaviour of creating children under this page.
|
||||
*
|
||||
@ -1026,6 +1026,9 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
||||
* @uses DataExtension->canCreate()
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
public function canCreate($member = null) {
|
||||
@ -1033,15 +1036,30 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
||||
$member = Member::currentUserID();
|
||||
}
|
||||
|
||||
if($member && Permission::checkMember($member, "ADMIN")) return true;
|
||||
|
||||
// Standard mechanism for accepting permission changes from extensions
|
||||
$extended = $this->extendedCan('canCreate', $member);
|
||||
if($extended !== null) return $extended;
|
||||
|
||||
return $this->stat('can_create') != false || Director::isDev();
|
||||
}
|
||||
// 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;
|
||||
|
||||
// Standard mechanism for accepting permission changes from extensions
|
||||
$results = $this->extend('canCreate', $member, $parent);
|
||||
if(is_array($results) && ($results = array_filter($results, function($v) {return $v !== null;}))) {
|
||||
return min($results);
|
||||
}
|
||||
|
||||
// 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
|
||||
@ -1083,7 +1101,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
||||
|
||||
// Default for unsaved pages
|
||||
} else {
|
||||
return $this->getSiteConfig()->canEdit($member);
|
||||
return $this->getSiteConfig()->canEditPages($member);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1289,7 +1307,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
||||
if(empty(self::$cache_permissions[$cacheKey])) self::$cache_permissions[$cacheKey] = array();
|
||||
self::$cache_permissions[$cacheKey] = $combinedStageResult + self::$cache_permissions[$cacheKey];
|
||||
|
||||
return $combinedStageResult;
|
||||
return $combinedStageResult;
|
||||
} else {
|
||||
return array();
|
||||
}
|
||||
@ -1305,7 +1323,7 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
||||
* page can be edited.
|
||||
*/
|
||||
static public function can_edit_multiple($ids, $memberID, $useCached = true) {
|
||||
return self::batch_permission_check($ids, $memberID, 'CanEditType', 'SiteTree_EditorGroups', 'canEdit', null, $useCached);
|
||||
return self::batch_permission_check($ids, $memberID, 'CanEditType', 'SiteTree_EditorGroups', 'canEditPages', null, $useCached);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2740,9 +2758,20 @@ class SiteTree extends DataObject implements PermissionProvider,i18nEntityProvid
|
||||
* @return string a html string ready to be directly used in a template
|
||||
*/
|
||||
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();
|
||||
$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))
|
||||
);
|
||||
foreach($flags as $class => $data) {
|
||||
|
@ -10,13 +10,41 @@ class SiteTreeFileExtension extends DataExtension {
|
||||
);
|
||||
|
||||
public function updateCMSFields(FieldList $fields) {
|
||||
$fields->insertAfter(new ReadonlyField('BackLinkCount',
|
||||
_t('AssetTableField.BACKLINKCOUNT', 'Used on:'),
|
||||
$this->BackLinkTracking()->Count() . ' ' . _t('AssetTableField.PAGES', 'page(s)')),
|
||||
$fields->insertAfter(
|
||||
ReadonlyField::create(
|
||||
'BackLinkCount',
|
||||
_t('AssetTableField.BACKLINKCOUNT', 'Used on:'),
|
||||
$this->BackLinkTracking()->Count() . ' ' . _t('AssetTableField.PAGES', 'page(s)'))
|
||||
->addExtraClass('cms-description-toggle')
|
||||
->setDescription($this->BackLinkHTMLList()),
|
||||
'LastEdited'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an HTML list which provides links to where a file is used.
|
||||
*
|
||||
* @return String
|
||||
*/
|
||||
public function BackLinkHTMLList() {
|
||||
$html = '<em>' . _t('SiteTreeFileExtension.BACKLINK_LIST_DESCRIPTION', 'This list shows all pages where the file has been added through a WYSIWYG editor.') . '</em>';
|
||||
$html .= '<ul>';
|
||||
|
||||
foreach ($this->BackLinkTracking() as $backLink) {
|
||||
$listItem = '<li>';
|
||||
|
||||
// Add the page link
|
||||
$listItem .= '<a href="' . $backLink->Link() . '" target="_blank">' . Convert::raw2xml($backLink->MenuTitle) . '</a> – ';
|
||||
|
||||
// Add the CMS link
|
||||
$listItem .= '<a href="' . $backLink->CMSEditLink() . '">' . _t('SiteTreeFileExtension.EDIT', 'Edit') . '</a>';
|
||||
|
||||
$html .= $listItem . '</li>';
|
||||
}
|
||||
|
||||
return $html .= '</ul>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend through {@link updateBackLinkTracking()} in your own {@link Extension}.
|
||||
*
|
||||
@ -26,13 +54,31 @@ class SiteTreeFileExtension extends DataExtension {
|
||||
* @param string $limit
|
||||
* @return ManyManyList
|
||||
*/
|
||||
public function BackLinkTracking($filter = "", $sort = "", $join = "", $limit = "") {
|
||||
public function BackLinkTracking($filter = null, $sort = null, $join = null, $limit = null) {
|
||||
if($filter !== null || $sort !== null || $join !== null || $limit !== null) {
|
||||
Deprecation::notice('3.2', 'The $filter, $sort, $join and $limit parameters for
|
||||
SiteTreeFileExtension::BackLinkTracking() have been deprecated.
|
||||
Please manipluate the returned list directly.', Deprecation::SCOPE_GLOBAL);
|
||||
}
|
||||
|
||||
if(class_exists("Subsite")){
|
||||
$rememberSubsiteFilter = Subsite::$disable_subsite_filter;
|
||||
Subsite::disable_subsite_filter(true);
|
||||
}
|
||||
|
||||
if($filter || $sort || $join || $limit) {
|
||||
Deprecation::notice('3.2', 'The $filter, $sort, $join and $limit parameters for
|
||||
SiteTreeFileExtension::BackLinkTracking() have been deprecated.
|
||||
Please manipluate the returned list directly.', Deprecation::SCOPE_GLOBAL);
|
||||
}
|
||||
|
||||
$links = $this->owner->getManyManyComponents('BackLinkTracking', $filter, $sort, $join, $limit);
|
||||
$links = $this->owner->getManyManyComponents('BackLinkTracking');
|
||||
if($this->owner->ID) {
|
||||
$links = $links
|
||||
->where($filter)
|
||||
->sort($sort)
|
||||
->limit($limit);
|
||||
}
|
||||
$this->owner->extend('updateBackLinkTracking', $links);
|
||||
|
||||
if(class_exists("Subsite")){
|
||||
|
@ -27,7 +27,7 @@ class SiteTreeFolderExtension extends DataExtension {
|
||||
$ids = $query->execute()->column();
|
||||
if(!count($ids)) continue;
|
||||
|
||||
foreach(singleton($className)->has_one() as $relName => $joinClass) {
|
||||
foreach(singleton($className)->hasOne() as $relName => $joinClass) {
|
||||
if($joinClass == 'Image' || $joinClass == 'File') {
|
||||
$fieldName = $relName .'ID';
|
||||
$query = DataList::create($className)->where("$fieldName > 0");
|
||||
|
@ -109,7 +109,7 @@ class SiteTreeLinkTracking extends DataExtension {
|
||||
}
|
||||
|
||||
// Update the "LinkTracking" many_many
|
||||
if($record->ID && $record->many_many('LinkTracking') && $tracker = $record->LinkTracking()) {
|
||||
if($record->ID && $record->manyManyComponent('LinkTracking') && $tracker = $record->LinkTracking()) {
|
||||
$tracker->removeByFilter(sprintf(
|
||||
'"FieldName" = \'%s\' AND "%s" = %d',
|
||||
$fieldName,
|
||||
@ -123,7 +123,7 @@ class SiteTreeLinkTracking extends DataExtension {
|
||||
}
|
||||
|
||||
// Update the "ImageTracking" many_many
|
||||
if($record->ID && $record->many_many('ImageTracking') && $tracker = $record->ImageTracking()) {
|
||||
if($record->ID && $record->manyManyComponent('ImageTracking') && $tracker = $record->ImageTracking()) {
|
||||
$tracker->removeByFilter(sprintf(
|
||||
'"FieldName" = \'%s\' AND "%s" = %d',
|
||||
$fieldName,
|
||||
|
@ -54,7 +54,7 @@ class VirtualPage extends Page {
|
||||
$record = $this->CopyContentFrom();
|
||||
|
||||
$allFields = $record->db();
|
||||
if($hasOne = $record->has_one()) foreach($hasOne as $link) $allFields[$link . 'ID'] = "Int";
|
||||
if($hasOne = $record->hasOne()) foreach($hasOne as $link) $allFields[$link . 'ID'] = "Int";
|
||||
$virtualFields = array();
|
||||
foreach($allFields as $field => $type) {
|
||||
if(!in_array($field, $nonVirtualFields)) $virtualFields[] = $field;
|
||||
@ -458,6 +458,22 @@ class VirtualPage extends Page {
|
||||
if(parent::hasMethod($method)) return true;
|
||||
return $this->copyContentFrom()->hasMethod($method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the "casting helper" (a piece of PHP code that when evaluated creates a casted value object) for a field
|
||||
* on this object.
|
||||
*
|
||||
* @param string $field
|
||||
* @return string
|
||||
*/
|
||||
public function castingHelper($field) {
|
||||
if($this->copyContentFrom()) {
|
||||
return $this->copyContentFrom()->castingHelper($field);
|
||||
} else {
|
||||
return parent::castingHelper($field);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -20,7 +20,7 @@
|
||||
"composer/installers": "*",
|
||||
"silverstripe/framework": "4.0.x-dev",
|
||||
"silverstripe/reports": "*",
|
||||
"silverstripe/siteconfig": "*"
|
||||
"silverstripe/siteconfig": "3.2.x-dev"
|
||||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
|
@ -15,43 +15,114 @@
|
||||
});
|
||||
|
||||
$(".cms-add-form").entwine({
|
||||
ParentID: 0, // Last selected parentID
|
||||
ParentCache: {}, // Cache allowed children for each selected page
|
||||
onadd: function() {
|
||||
var self = this;
|
||||
this.find('#Form_AddForm_ParentID_Holder .TreeDropdownField').bind('change', function() {
|
||||
self.updateTypeList();
|
||||
});
|
||||
this.find(".SelectionGroup.parent-mode").bind('change', function() {
|
||||
self.updateTypeList();
|
||||
});
|
||||
this.updateTypeList();
|
||||
},
|
||||
|
||||
loadCachedChildren: function(parentID) {
|
||||
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.
|
||||
*/
|
||||
updateTypeList: function() {
|
||||
var hints = this.find('.hints').data('hints'),
|
||||
metadata = this.find('#Form_AddForm_ParentID_Holder .TreeDropdownField').data('metadata'),
|
||||
id = this.find('#Form_AddForm_ParentID_Holder .TreeDropdownField').getValue(),
|
||||
newClassName = (id && metadata) ? metadata.ClassName : null,
|
||||
hintKey = (newClassName) ? newClassName : 'Root',
|
||||
hint = (typeof hints[hintKey] != 'undefined') ? hints[hintKey] : null,
|
||||
allAllowed = true;
|
||||
|
||||
var disallowedChildren = (hint && typeof hint.disallowedChildren != 'undefined') ? hint.disallowedChildren : [],
|
||||
defaultChildClass = (hint && typeof hint.defaultChild != 'undefined') ? hint.defaultChild : null;
|
||||
|
||||
var hints = this.data('hints'),
|
||||
parentTree = this.find('#Form_AddForm_ParentID_Holder .TreeDropdownField'),
|
||||
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,
|
||||
hintKey = (newClassName && parentMode === 'child')
|
||||
? newClassName
|
||||
: 'Root',
|
||||
hint = (typeof hints[hintKey] !== 'undefined') ? hints[hintKey] : null,
|
||||
self = this,
|
||||
defaultChildClass = (hint && typeof hint.defaultChild !== 'undefined')
|
||||
? hint.defaultChild
|
||||
: null,
|
||||
disallowedChildren = [];
|
||||
|
||||
if(id) {
|
||||
// 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
|
||||
var allAllowed = null; // troolian
|
||||
this.find('#Form_AddForm_PageType_Holder li').each(function() {
|
||||
var className = $(this).find('input').val(),
|
||||
isAllowed = ($.inArray(className, disallowedChildren) == -1);
|
||||
isAllowed = ($.inArray(className, disallowedChildren) === -1);
|
||||
|
||||
$(this).setEnabled(isAllowed);
|
||||
if(!isAllowed) $(this).setSelected(false);
|
||||
allAllowed = allAllowed && isAllowed;
|
||||
if(allAllowed === null) allAllowed = isAllowed;
|
||||
else allAllowed = allAllowed && isAllowed;
|
||||
});
|
||||
|
||||
// Set default child selection, or fall back to first available option
|
||||
if(defaultChildClass) {
|
||||
var selectedEl = this.find('#Form_AddForm_PageType_Holder li input[value=' + defaultChildClass + ']').parents('li:first');
|
||||
var selectedEl = this
|
||||
.find('#Form_AddForm_PageType_Holder li input[value=' + defaultChildClass + ']')
|
||||
.parents('li:first');
|
||||
} else {
|
||||
var selectedEl = this.find('#Form_AddForm_PageType_Holder li:not(.disabled):first');
|
||||
}
|
||||
@ -59,7 +130,9 @@
|
||||
selectedEl.siblings().setSelected(false);
|
||||
|
||||
// Disable the "Create" button if none of the pagetypes are available
|
||||
var buttonState = (this.find('#Form_AddForm_PageType_Holder li:not(.disabled)').length) ? 'enable' : 'disable';
|
||||
var buttonState = this.find('#Form_AddForm_PageType_Holder li:not(.disabled)').length
|
||||
? 'enable'
|
||||
: 'disable';
|
||||
this.find('button[name=action_doAdd]').button(buttonState);
|
||||
|
||||
this.find('.message-restricted')[allAllowed ? 'hide' : 'show']();
|
||||
@ -72,10 +145,13 @@
|
||||
},
|
||||
setSelected: function(bool) {
|
||||
var input = this.find('input');
|
||||
this.toggleClass('selected', bool);
|
||||
if(bool && !input.is(':disabled')) {
|
||||
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) {
|
||||
|
@ -118,7 +118,7 @@
|
||||
// update button
|
||||
updateURLFromTitle = $('<button />', {
|
||||
'class': 'update ss-ui-button-small',
|
||||
'text': 'Update URL',
|
||||
'text': ss.i18n._t('URLSEGMENT.UpdateURL'),
|
||||
'click': function(e) {
|
||||
e.preventDefault();
|
||||
self.updateURLSegment(self.val());
|
||||
|
@ -68,27 +68,15 @@
|
||||
// Build a list for allowed children as submenu entries
|
||||
var pagetype = node.data('pagetype'),
|
||||
id = node.data('id'),
|
||||
disallowedChildren = (typeof hints[pagetype] != 'undefined') ? hints[pagetype].disallowedChildren : null,
|
||||
allowedChildren = $.extend(true, {}, hints['All']), // clone
|
||||
disallowedClass,
|
||||
allowedChildren = node.find('>a .item').data('allowedchildren'),
|
||||
menuAllowedChildren = {},
|
||||
hasAllowedChildren = false;
|
||||
|
||||
// Filter allowed
|
||||
if(disallowedChildren) {
|
||||
for(var i=0; i<disallowedChildren.length; i++) {
|
||||
disallowedClass = disallowedChildren[i];
|
||||
if(allowedChildren[disallowedClass]) {
|
||||
delete allowedChildren[disallowedClass];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convert to menu entries
|
||||
$.each(allowedChildren, function(klass, klassData){
|
||||
$.each(allowedChildren, function(klass, title){
|
||||
hasAllowedChildren = true;
|
||||
menuAllowedChildren["allowedchildren-" + klass ] = {
|
||||
'label': '<span class="jstree-pageicon"></span>' + klassData.title,
|
||||
'label': '<span class="jstree-pageicon"></span>' + title,
|
||||
'_class': 'class-' + klass,
|
||||
'action': function(obj) {
|
||||
$('.cms-container').entwine('.ss').loadPanel(
|
||||
|
@ -40,7 +40,8 @@ if(typeof(ss) == 'undefined' || typeof(ss.i18n) == 'undefined') {
|
||||
"CMSMain.RollbackToVersion": "Do you really want to roll back to version #%s of this page?",
|
||||
"URLSEGMENT.Edit": "Edit",
|
||||
"URLSEGMENT.OK": "OK",
|
||||
"URLSEGMENT.Cancel": "Cancel"
|
||||
"URLSEGMENT.Cancel": "Cancel",
|
||||
"URLSEGMENT.UpdateURL": "Update URL"
|
||||
}
|
||||
);
|
||||
}
|
@ -35,5 +35,6 @@
|
||||
"CMSMain.RollbackToVersion": "Do you really want to roll back to version #%s of this page?",
|
||||
"URLSEGMENT.Edit": "Edit",
|
||||
"URLSEGMENT.OK": "OK",
|
||||
"URLSEGMENT.Cancel": "Cancel"
|
||||
"URLSEGMENT.Cancel": "Cancel",
|
||||
"URLSEGMENT.UpdateURL": "Update URL"
|
||||
}
|
||||
|
@ -26,14 +26,14 @@
|
||||
"AssetAdmin.ConfirmDelete": "Vill du verkligen radera denna mapp och alla filer i den?",
|
||||
"Folder.Name": "Mappnamn",
|
||||
"Tree.AddSubPage": "Lägg till ny sida här",
|
||||
"Tree.Duplicate": "Duplicate",
|
||||
"Tree.EditPage": "Editera",
|
||||
"Tree.ThisPageOnly": "This page only",
|
||||
"Tree.ThisPageAndSubpages": "This page and subpages",
|
||||
"Tree.ShowAsList": "Show children as list",
|
||||
"Tree.Duplicate": "Duplicera",
|
||||
"Tree.EditPage": "Redigera",
|
||||
"Tree.ThisPageOnly": "Endast denna sida",
|
||||
"Tree.ThisPageAndSubpages": "Denna sida och undersidor",
|
||||
"Tree.ShowAsList": "Visa undersidor som lista",
|
||||
"CMSMain.ConfirmRestoreFromLive": "Vill du verkligen kopiera det publicerade innehållet till utkastsajten?",
|
||||
"CMSMain.RollbackToVersion": "Vill du verkligen gå tillbaka till version %s av denna sida?",
|
||||
"URLSEGMENT.Edit": "Edit",
|
||||
"URLSEGMENT.Edit": "Redigera",
|
||||
"URLSEGMENT.OK": "OK",
|
||||
"URLSEGMENT.Cancel": "Cancel"
|
||||
"URLSEGMENT.Cancel": "Avbryt"
|
||||
}
|
@ -31,15 +31,15 @@ if(typeof(ss) == 'undefined' || typeof(ss.i18n) == 'undefined') {
|
||||
"AssetAdmin.ConfirmDelete": "Vill du verkligen radera denna mapp och alla filer i den?",
|
||||
"Folder.Name": "Mappnamn",
|
||||
"Tree.AddSubPage": "Lägg till ny sida här",
|
||||
"Tree.Duplicate": "Duplicate",
|
||||
"Tree.EditPage": "Editera",
|
||||
"Tree.ThisPageOnly": "This page only",
|
||||
"Tree.ThisPageAndSubpages": "This page and subpages",
|
||||
"Tree.ShowAsList": "Show children as list",
|
||||
"Tree.Duplicate": "Duplicera",
|
||||
"Tree.EditPage": "Redigera",
|
||||
"Tree.ThisPageOnly": "Endast denna sida",
|
||||
"Tree.ThisPageAndSubpages": "Denna sida och undersidor",
|
||||
"Tree.ShowAsList": "Visa undersidor som lista",
|
||||
"CMSMain.ConfirmRestoreFromLive": "Vill du verkligen kopiera det publicerade innehållet till utkastsajten?",
|
||||
"CMSMain.RollbackToVersion": "Vill du verkligen gå tillbaka till version %s av denna sida?",
|
||||
"URLSEGMENT.Edit": "Edit",
|
||||
"URLSEGMENT.Edit": "Redigera",
|
||||
"URLSEGMENT.OK": "OK",
|
||||
"URLSEGMENT.Cancel": "Cancel"
|
||||
"URLSEGMENT.Cancel": "Avbryt"
|
||||
});
|
||||
}
|
26
lang/sv.yml
26
lang/sv.yml
@ -5,7 +5,9 @@ sv:
|
||||
AppCategoryArchive: Arkivera
|
||||
AppCategoryAudio: Ljud
|
||||
AppCategoryDocument: Dokument
|
||||
AppCategoryFlash: Flash
|
||||
AppCategoryImage: Bild
|
||||
AppCategoryVideo: Video
|
||||
BackToFolder: 'Tillbaka till mappen'
|
||||
CREATED: Datum
|
||||
CurrentFolderOnly: 'Begränsa till aktuell mapp?'
|
||||
@ -43,6 +45,7 @@ sv:
|
||||
ColumnDateLastModified: 'Datum vid senaste modifiering'
|
||||
ColumnDateLastPublished: 'Datum vid senaste publicering'
|
||||
ColumnProblemType: 'Problemtyp'
|
||||
ColumnURL: URL
|
||||
HasBrokenFile: 'har trasig fil'
|
||||
HasBrokenLink: 'har trasig länk'
|
||||
HasBrokenLinkAndFile: 'har trasig länk och fil'
|
||||
@ -72,6 +75,7 @@ sv:
|
||||
AddNew: 'Skapa ny sida'
|
||||
AddNewButton: 'Skapa ny'
|
||||
AddPageRestriction: 'OBS: Vissa sidtyper är inte tillåtna här'
|
||||
Cancel: Avbryt
|
||||
ChoosePageParentMode: 'Välj var du vill skapa denna sida'
|
||||
ChoosePageType: 'Välj sidtyp'
|
||||
Create: Skapa
|
||||
@ -124,6 +128,7 @@ sv:
|
||||
SHOWUNPUBLISHED: 'Visa opublicerade versioner'
|
||||
SHOWVERSION: 'Visa version'
|
||||
VIEW: Titta på
|
||||
VIEWINGLATEST: 'Nu visas den senaste versionen.'
|
||||
VIEWINGVERSION: 'Nu visas version {version}.'
|
||||
MENUTITLE: Historia
|
||||
CMSPageHistoryController_versions_ss:
|
||||
@ -139,6 +144,8 @@ sv:
|
||||
TreeView: 'Trädvy'
|
||||
CMSPagesController_ContentToolbar_ss:
|
||||
MULTISELECT: Flerval
|
||||
CMSPagesController_Tools_ss:
|
||||
FILTER: Filter
|
||||
CMSSearch:
|
||||
FILTERDATEFROM: Från
|
||||
FILTERDATEHEADING: Datum
|
||||
@ -155,6 +162,7 @@ sv:
|
||||
ContentController:
|
||||
ARCHIVEDSITE: 'Utkast version'
|
||||
ARCHIVEDSITEFROM: 'Arkiverad sajt från'
|
||||
CMS: CMS
|
||||
DRAFT: Utkast
|
||||
DRAFTSITE: 'Utkast'
|
||||
DRAFT_SITE_ACCESS_RESTRICTION: 'Du måste logga med ditt CMS-lösenord för att kunna se utkast och arkiverat material. <a href="%s">Klicka här för att gå tillbaks till den publicerade sajten.</a>'
|
||||
@ -192,6 +200,8 @@ sv:
|
||||
415: '415 - Mediatypen stöds inte'
|
||||
416: '416 - Det efterfrågade intervallet går inte att leverera'
|
||||
417: '417 - Förväntningen gick inte att infria'
|
||||
422: '422 - Obehandlingsbar entitet'
|
||||
429: '429 - För många anrop'
|
||||
500: '500 - Internt serverfel'
|
||||
501: '501 - Inte implementerad'
|
||||
502: '502 - Felaktig gateway'
|
||||
@ -262,6 +272,7 @@ sv:
|
||||
SilverStripeNavigator:
|
||||
ARCHIVED: Arkiverad
|
||||
SilverStripeNavigatorLink:
|
||||
ShareInstructions: 'Kopiera och klistra in länken nedan för att dela den här sidan.'
|
||||
ShareLink: 'Dela länk'
|
||||
SilverStripeNavigatorLinkl:
|
||||
CloseLink: Stäng
|
||||
@ -310,6 +321,7 @@ sv:
|
||||
DEPENDENT_NOTE: 'Följande sidor berörs av den här sidan. Inklusive virtuella sidor, omdirigeringssidor, och sidor med innehållslänkar.'
|
||||
DESCRIPTION: 'Generisk innehållssida'
|
||||
DependtPageColumnLinkType: 'Länktyp'
|
||||
DependtPageColumnURL: URL
|
||||
EDITANYONE: 'Alla som kan logga in'
|
||||
EDITHEADER: 'Vem kan redigera den här sidan?'
|
||||
EDITONLYTHESE: 'Bara de här (välj från listan)'
|
||||
@ -332,6 +344,7 @@ sv:
|
||||
METAEXTRAHELP: 'HTML taggar för övrig meta information. Till exempel <meta name="Namn" content="innehållet kommer här" />'
|
||||
MODIFIEDONDRAFTHELP: 'Sidan har ej publicerade ändringar'
|
||||
MODIFIEDONDRAFTSHORT: Ändrad
|
||||
MetadataToggle: Metadata
|
||||
MoreOptions: 'Fler alternativ'
|
||||
NOTPUBLISHED: 'Ej publicerad'
|
||||
OBSOLETECLASS: 'Denna sida är den föråldrade typen {type}. Att spara kommer att återställa dess typ och du kan förlora data'
|
||||
@ -375,12 +388,19 @@ sv:
|
||||
SiteTreeURLSegmentField:
|
||||
EMPTY: 'Ange ett URL-segment eller klicka på avbryt'
|
||||
HelpChars: 'Specialtecken konverteras eller tas bort'
|
||||
URLSegmentField:
|
||||
Cancel: Avbryt
|
||||
Edit: Redigera
|
||||
OK: OK
|
||||
ViewArchivedEmail_ss:
|
||||
CANACCESS: 'Du kan komma åt den arkiverade sajten med denna länk:'
|
||||
HAVEASKED: 'Du har efterfrågat att se innehållet på vår sajt på'
|
||||
VirtualPage:
|
||||
CHOOSE: 'Länkad sida'
|
||||
DESCRIPTION: 'Visar innehåll från en annan sida'
|
||||
EditLink: redigera
|
||||
HEADER: 'Det här är en virutell sida'
|
||||
HEADERWITHLINK: 'Det här är en virtuell sida som kopierar innehållet från "{title}" ({link})'
|
||||
PLURALNAME: 'Virtuella sidor'
|
||||
PageTypNotAllowedOnRoot: 'Ursprungliga sidan av typ "{type}" tillåts inte på rotnivå för denna virtuella sida'
|
||||
SINGULARNAME: 'Virtuell sida'
|
||||
@ -392,3 +412,9 @@ sv:
|
||||
MENUTITLE: 'Redigera sida'
|
||||
CMSSettingsController:
|
||||
MENUTITLE: Inställningar
|
||||
CMSSiteTreeFilter_StatusDeletedPages:
|
||||
Title: 'Raderade sidor'
|
||||
CMSSiteTreeFilter_StatusDraftPages:
|
||||
Title: 'Ej publicerade utkast'
|
||||
CMSSiteTreeFilter_StatusRemovedFromDraftPages:
|
||||
Title: 'Live men borttagen från utkast'
|
||||
|
@ -19,7 +19,7 @@ $ExtraTreeTools
|
||||
</div>
|
||||
<% end_if %>
|
||||
|
||||
<div class="cms-tree" data-url-tree="$LinkWithSearch($Link(getsubtree))" data-url-savetreenode="$Link(savetreenode)" data-url-updatetreenodes="$Link(updatetreenodes)" data-url-addpage="{$LinkPageAdd('AddForm/?action_doAdd=1', 'ParentID=%s&PageType=%s')}" data-url-editpage="$LinkPageEdit('%s')" data-url-duplicate="{$Link('duplicate/%s')}" data-url-duplicatewithchildren="{$Link('duplicatewithchildren/%s')}" data-url-listview="{$Link('?view=list')}" data-hints="$SiteTreeHints.XML" data-extra-params="SecurityID=$SecurityID">
|
||||
<div class="cms-tree" data-url-tree="$LinkWithSearch($Link(getsubtree))" data-url-savetreenode="$Link(savetreenode)" data-url-updatetreenodes="$Link(updatetreenodes)" data-url-addpage="{$LinkPageAdd('AddForm/?action_doAdd=1', 'ParentID=%s&PageType=%s')}" data-url-editpage="$LinkPageEdit('%s')" data-url-duplicate="{$Link('duplicate/%s')}" data-url-duplicatewithchildren="{$Link('duplicatewithchildren/%s')}" data-url-listview="{$Link('?view=list')}" data-hints="$SiteTreeHints.XML" data-childfilter="$Link('childfilter')" data-extra-params="SecurityID=$SecurityID">
|
||||
$SiteTreeAsUL
|
||||
</div>
|
||||
</div>
|
||||
|
@ -11,6 +11,9 @@ class CMSMainTest extends FunctionalTest {
|
||||
|
||||
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();
|
||||
@ -46,23 +49,36 @@ class CMSMainTest extends FunctionalTest {
|
||||
$hints['Root']['disallowedChildren'],
|
||||
'Limits root classes'
|
||||
);
|
||||
$this->assertNotContains(
|
||||
'CMSMainTest_ClassA',
|
||||
// Lenient checks because other modules might influence state
|
||||
(array)@$hints['Page']['disallowedChildren'],
|
||||
'Does not limit types on unlimited parent'
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
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',
|
||||
$hints['CMSMainTest_ClassA']['disallowedChildren'],
|
||||
$children,
|
||||
'Limited parent lists disallowed classes'
|
||||
);
|
||||
|
||||
// But it can create a ClassB
|
||||
$this->assertNotContains(
|
||||
'CMSMainTest_ClassB',
|
||||
$hints['CMSMainTest_ClassA']['disallowedChildren'],
|
||||
$children,
|
||||
'Limited parent omits explicitly allowed classes in disallowedChildren'
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -302,11 +318,7 @@ class CMSMainTest extends FunctionalTest {
|
||||
'admin/pages/add/AddForm',
|
||||
array('ParentID' => $newPageId, 'PageType' => 'Page', 'Locale' => 'en_US', 'action_doAdd' => 1)
|
||||
);
|
||||
$this->assertFalse($response->isError());
|
||||
$this->assertContains(
|
||||
htmlentities(_t('SiteTree.PageTypeNotAllowed', array('type' => 'Page'))),
|
||||
$response->getBody()
|
||||
);
|
||||
$this->assertEquals(403, $response->getStatusCode(), 'Add disallowed child should fail');
|
||||
|
||||
$this->session()->inst_set('loggedInAs', NULL);
|
||||
|
||||
|
@ -20,24 +20,31 @@ class SiteTreeTest extends SapphireTest {
|
||||
'SiteTreeTest_NotRoot',
|
||||
'SiteTreeTest_StageStatusInherit',
|
||||
);
|
||||
|
||||
/**
|
||||
* Ensure any current member is logged out
|
||||
*/
|
||||
public function logOut() {
|
||||
if($member = Member::currentUser()) $member->logOut();
|
||||
}
|
||||
|
||||
public function testCreateDefaultpages() {
|
||||
$remove = SiteTree::get();
|
||||
if($remove) foreach($remove as $page) $page->delete();
|
||||
// Make sure the table is empty
|
||||
$this->assertEquals(DB::query('SELECT COUNT("ID") FROM "SiteTree"')->value(), 0);
|
||||
|
||||
|
||||
// Disable the creation
|
||||
SiteTree::config()->create_default_pages = false;
|
||||
singleton('SiteTree')->requireDefaultRecords();
|
||||
|
||||
|
||||
// The table should still be empty
|
||||
$this->assertEquals(DB::query('SELECT COUNT("ID") FROM "SiteTree"')->value(), 0);
|
||||
|
||||
|
||||
// Enable the creation
|
||||
SiteTree::config()->create_default_pages = true;
|
||||
singleton('SiteTree')->requireDefaultRecords();
|
||||
|
||||
|
||||
// The table should now have three rows (home, about-us, contact-us)
|
||||
$this->assertEquals(DB::query('SELECT COUNT("ID") FROM "SiteTree"')->value(), 3);
|
||||
}
|
||||
@ -63,64 +70,64 @@ class SiteTreeTest extends SapphireTest {
|
||||
'controller' => 'controller-2',
|
||||
'numericonly' => '1930',
|
||||
);
|
||||
|
||||
|
||||
foreach($expectedURLs as $fixture => $urlSegment) {
|
||||
$obj = $this->objFromFixture('Page', $fixture);
|
||||
$this->assertEquals($urlSegment, $obj->URLSegment);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test that publication copies data to SiteTree_Live
|
||||
*/
|
||||
public function testPublishCopiesToLiveTable() {
|
||||
$obj = $this->objFromFixture('Page','about');
|
||||
$obj->publish('Stage', 'Live');
|
||||
|
||||
|
||||
$createdID = DB::query("SELECT \"ID\" FROM \"SiteTree_Live\" WHERE \"URLSegment\" = '$obj->URLSegment'")->value();
|
||||
$this->assertEquals($obj->ID, $createdID);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test that field which are set and then cleared are also transferred to the published site.
|
||||
*/
|
||||
public function testPublishDeletedFields() {
|
||||
$this->logInWithPermission('ADMIN');
|
||||
|
||||
|
||||
$obj = $this->objFromFixture('Page', 'about');
|
||||
$obj->Title = "asdfasdf";
|
||||
$obj->write();
|
||||
$this->assertTrue($obj->doPublish());
|
||||
|
||||
|
||||
$this->assertEquals('asdfasdf', DB::query("SELECT \"Title\" FROM \"SiteTree_Live\" WHERE \"ID\" = '$obj->ID'")->value());
|
||||
|
||||
|
||||
$obj->Title = null;
|
||||
$obj->write();
|
||||
$this->assertTrue($obj->doPublish());
|
||||
|
||||
|
||||
$this->assertNull(DB::query("SELECT \"Title\" FROM \"SiteTree_Live\" WHERE \"ID\" = '$obj->ID'")->value());
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function testParentNodeCachedInMemory() {
|
||||
$parent = new SiteTree();
|
||||
$parent->Title = 'Section Title';
|
||||
$child = new SiteTree();
|
||||
$child->Title = 'Page Title';
|
||||
$child->setParent($parent);
|
||||
|
||||
|
||||
$this->assertInstanceOf("SiteTree", $child->Parent);
|
||||
$this->assertEquals("Section Title", $child->Parent->Title);
|
||||
}
|
||||
|
||||
|
||||
public function testParentModelReturnType() {
|
||||
$parent = new SiteTreeTest_PageNode();
|
||||
$child = new SiteTreeTest_PageNode();
|
||||
|
||||
|
||||
$child->setParent($parent);
|
||||
$this->assertInstanceOf('SiteTreeTest_PageNode', $child->Parent);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Confirm that DataObject::get_one() gets records from SiteTree_Live
|
||||
*/
|
||||
@ -132,43 +139,43 @@ class SiteTreeTest extends SapphireTest {
|
||||
$s->publish("Stage", "Live");
|
||||
$s->Title = "V2";
|
||||
$s->write();
|
||||
|
||||
|
||||
$oldMode = Versioned::get_reading_mode();
|
||||
Versioned::reading_stage('Live');
|
||||
|
||||
|
||||
$checkSiteTree = DataObject::get_one("SiteTree", array(
|
||||
'"SiteTree"."URLSegment"' => 'get-one-test-page'
|
||||
));
|
||||
$this->assertEquals("V1", $checkSiteTree->Title);
|
||||
|
||||
|
||||
Versioned::set_reading_mode($oldMode);
|
||||
}
|
||||
|
||||
|
||||
public function testChidrenOfRootAreTopLevelPages() {
|
||||
$pages = SiteTree::get();
|
||||
foreach($pages as $page) $page->publish('Stage', 'Live');
|
||||
unset($pages);
|
||||
|
||||
|
||||
/* If we create a new SiteTree object with ID = 0 */
|
||||
$obj = new SiteTree();
|
||||
/* Then its children should be the top-level pages */
|
||||
$stageChildren = $obj->stageChildren()->map('ID','Title');
|
||||
$liveChildren = $obj->liveChildren()->map('ID','Title');
|
||||
$allChildren = $obj->AllChildrenIncludingDeleted()->map('ID','Title');
|
||||
|
||||
|
||||
$this->assertContains('Home', $stageChildren);
|
||||
$this->assertContains('Products', $stageChildren);
|
||||
$this->assertNotContains('Staff', $stageChildren);
|
||||
|
||||
|
||||
$this->assertContains('Home', $liveChildren);
|
||||
$this->assertContains('Products', $liveChildren);
|
||||
$this->assertNotContains('Staff', $liveChildren);
|
||||
|
||||
|
||||
$this->assertContains('Home', $allChildren);
|
||||
$this->assertContains('Products', $allChildren);
|
||||
$this->assertNotContains('Staff', $allChildren);
|
||||
}
|
||||
|
||||
|
||||
public function testCanSaveBlankToHasOneRelations() {
|
||||
/* DataObject::write() should save to a has_one relationship if you set a field called (relname)ID */
|
||||
$page = new SiteTree();
|
||||
@ -176,13 +183,13 @@ class SiteTreeTest extends SapphireTest {
|
||||
$page->ParentID = $parentID;
|
||||
$page->write();
|
||||
$this->assertEquals($parentID, DB::query("SELECT \"ParentID\" FROM \"SiteTree\" WHERE \"ID\" = $page->ID")->value());
|
||||
|
||||
|
||||
/* You should then be able to save a null/0/'' value to the relation */
|
||||
$page->ParentID = null;
|
||||
$page->write();
|
||||
$this->assertEquals(0, DB::query("SELECT \"ParentID\" FROM \"SiteTree\" WHERE \"ID\" = $page->ID")->value());
|
||||
}
|
||||
|
||||
|
||||
public function testStageStates() {
|
||||
// newly created page
|
||||
$createdPage = new SiteTree();
|
||||
@ -190,15 +197,15 @@ class SiteTreeTest extends SapphireTest {
|
||||
$this->assertFalse($createdPage->IsDeletedFromStage);
|
||||
$this->assertTrue($createdPage->IsAddedToStage);
|
||||
$this->assertTrue($createdPage->IsModifiedOnStage);
|
||||
|
||||
// published page
|
||||
|
||||
// published page
|
||||
$publishedPage = new SiteTree();
|
||||
$publishedPage->write();
|
||||
$publishedPage->publish('Stage','Live');
|
||||
$this->assertFalse($publishedPage->IsDeletedFromStage);
|
||||
$this->assertFalse($publishedPage->IsAddedToStage);
|
||||
$this->assertFalse($publishedPage->IsModifiedOnStage);
|
||||
|
||||
$this->assertFalse($publishedPage->IsModifiedOnStage);
|
||||
|
||||
// published page, deleted from stage
|
||||
$deletedFromDraftPage = new SiteTree();
|
||||
$deletedFromDraftPage->write();
|
||||
@ -208,7 +215,7 @@ class SiteTreeTest extends SapphireTest {
|
||||
$this->assertTrue($deletedFromDraftPage->IsDeletedFromStage);
|
||||
$this->assertFalse($deletedFromDraftPage->IsAddedToStage);
|
||||
$this->assertFalse($deletedFromDraftPage->IsModifiedOnStage);
|
||||
|
||||
|
||||
// published page, deleted from live
|
||||
$deletedFromLivePage = new SiteTree();
|
||||
$deletedFromLivePage->write();
|
||||
@ -218,7 +225,7 @@ class SiteTreeTest extends SapphireTest {
|
||||
$this->assertTrue($deletedFromLivePage->IsDeletedFromStage);
|
||||
$this->assertFalse($deletedFromLivePage->IsAddedToStage);
|
||||
$this->assertFalse($deletedFromLivePage->IsModifiedOnStage);
|
||||
|
||||
|
||||
// published page, modified
|
||||
$modifiedOnDraftPage = new SiteTree();
|
||||
$modifiedOnDraftPage->write();
|
||||
@ -229,7 +236,7 @@ class SiteTreeTest extends SapphireTest {
|
||||
$this->assertFalse($modifiedOnDraftPage->IsAddedToStage);
|
||||
$this->assertTrue($modifiedOnDraftPage->IsModifiedOnStage);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test that a page can be completely deleted and restored to the stage site
|
||||
*/
|
||||
@ -238,23 +245,23 @@ class SiteTreeTest extends SapphireTest {
|
||||
$pageID = $page->ID;
|
||||
$page->delete();
|
||||
$this->assertTrue(!DataObject::get_by_id("Page", $pageID));
|
||||
|
||||
|
||||
$deletedPage = Versioned::get_latest_version('SiteTree', $pageID);
|
||||
$resultPage = $deletedPage->doRestoreToStage();
|
||||
|
||||
|
||||
$requeriedPage = DataObject::get_by_id("Page", $pageID);
|
||||
|
||||
|
||||
$this->assertEquals($pageID, $resultPage->ID);
|
||||
$this->assertEquals($pageID, $requeriedPage->ID);
|
||||
$this->assertEquals('About Us', $requeriedPage->Title);
|
||||
$this->assertEquals('Page', $requeriedPage->class);
|
||||
|
||||
|
||||
|
||||
|
||||
$page2 = $this->objFromFixture('Page', 'products');
|
||||
$page2ID = $page2->ID;
|
||||
$page2->doUnpublish();
|
||||
$page2->delete();
|
||||
|
||||
|
||||
// Check that if we restore while on the live site that the content still gets pushed to
|
||||
// stage
|
||||
Versioned::reading_stage('Live');
|
||||
@ -266,39 +273,39 @@ class SiteTreeTest extends SapphireTest {
|
||||
$requeriedPage = DataObject::get_by_id("Page", $page2ID);
|
||||
$this->assertEquals('Products', $requeriedPage->Title);
|
||||
$this->assertEquals('Page', $requeriedPage->class);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function testGetByLink() {
|
||||
$home = $this->objFromFixture('Page', 'home');
|
||||
$about = $this->objFromFixture('Page', 'about');
|
||||
$staff = $this->objFromFixture('Page', 'staff');
|
||||
$product = $this->objFromFixture('Page', 'product1');
|
||||
$notFound = $this->objFromFixture('ErrorPage', '404');
|
||||
|
||||
|
||||
SiteTree::config()->nested_urls = false;
|
||||
|
||||
|
||||
$this->assertEquals($home->ID, SiteTree::get_by_link('/', false)->ID);
|
||||
$this->assertEquals($home->ID, SiteTree::get_by_link('/home/', false)->ID);
|
||||
$this->assertEquals($about->ID, SiteTree::get_by_link($about->Link(), false)->ID);
|
||||
$this->assertEquals($staff->ID, SiteTree::get_by_link($staff->Link(), false)->ID);
|
||||
$this->assertEquals($product->ID, SiteTree::get_by_link($product->Link(), false)->ID);
|
||||
$this->assertEquals($notFound->ID, SiteTree::get_by_link($notFound->Link(), false)->ID);
|
||||
|
||||
|
||||
Config::inst()->update('SiteTree', 'nested_urls', true);
|
||||
|
||||
|
||||
$this->assertEquals($home->ID, SiteTree::get_by_link('/', false)->ID);
|
||||
$this->assertEquals($home->ID, SiteTree::get_by_link('/home/', false)->ID);
|
||||
$this->assertEquals($about->ID, SiteTree::get_by_link($about->Link(), false)->ID);
|
||||
$this->assertEquals($staff->ID, SiteTree::get_by_link($staff->Link(), false)->ID);
|
||||
$this->assertEquals($product->ID, SiteTree::get_by_link($product->Link(), false)->ID);
|
||||
$this->assertEquals($notFound->ID, SiteTree::get_by_link($notFound->Link(), false)->ID);
|
||||
|
||||
|
||||
$this->assertEquals (
|
||||
$staff->ID, SiteTree::get_by_link('/my-staff/', false)->ID, 'Assert a unique URLSegment can be used for b/c.'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function testRelativeLink() {
|
||||
$about = $this->objFromFixture('Page', 'about');
|
||||
$staff = $this->objFromFixture('Page', 'staff');
|
||||
@ -315,7 +322,7 @@ class SiteTreeTest extends SapphireTest {
|
||||
$parent = $this->objFromFixture('Page', 'about');
|
||||
$child = $this->objFromFixture('Page', 'staff');
|
||||
|
||||
Config::inst()->update('SiteTree', 'nested_urls', true);
|
||||
Config::inst()->update('SiteTree', 'nested_urls', true);
|
||||
|
||||
$child->publish('Stage', 'Live');
|
||||
$parent->URLSegment = 'changed-on-live';
|
||||
@ -323,52 +330,52 @@ class SiteTreeTest extends SapphireTest {
|
||||
$parent->publish('Stage', 'Live');
|
||||
$parent->URLSegment = 'changed-on-draft';
|
||||
$parent->write();
|
||||
|
||||
|
||||
$this->assertStringEndsWith('changed-on-live/my-staff/', $child->getAbsoluteLiveLink(false));
|
||||
$this->assertStringEndsWith('changed-on-live/my-staff/?stage=Live', $child->getAbsoluteLiveLink());
|
||||
}
|
||||
|
||||
|
||||
public function testDeleteFromStageOperatesRecursively() {
|
||||
Config::inst()->update('SiteTree', 'enforce_strict_hierarchy', false);
|
||||
$pageAbout = $this->objFromFixture('Page', 'about');
|
||||
$pageStaff = $this->objFromFixture('Page', 'staff');
|
||||
$pageStaffDuplicate = $this->objFromFixture('Page', 'staffduplicate');
|
||||
|
||||
|
||||
$pageAbout->delete();
|
||||
|
||||
|
||||
$this->assertFalse(DataObject::get_by_id('Page', $pageAbout->ID));
|
||||
$this->assertTrue(DataObject::get_by_id('Page', $pageStaff->ID) instanceof Page);
|
||||
$this->assertTrue(DataObject::get_by_id('Page', $pageStaffDuplicate->ID) instanceof Page);
|
||||
Config::inst()->update('SiteTree', 'enforce_strict_hierarchy', true);
|
||||
}
|
||||
|
||||
|
||||
public function testDeleteFromStageOperatesRecursivelyStrict() {
|
||||
$pageAbout = $this->objFromFixture('Page', 'about');
|
||||
$pageStaff = $this->objFromFixture('Page', 'staff');
|
||||
$pageStaffDuplicate = $this->objFromFixture('Page', 'staffduplicate');
|
||||
|
||||
|
||||
$pageAbout->delete();
|
||||
|
||||
|
||||
$this->assertFalse(DataObject::get_by_id('Page', $pageAbout->ID));
|
||||
$this->assertFalse(DataObject::get_by_id('Page', $pageStaff->ID));
|
||||
$this->assertFalse(DataObject::get_by_id('Page', $pageStaffDuplicate->ID));
|
||||
}
|
||||
|
||||
|
||||
public function testDeleteFromLiveOperatesRecursively() {
|
||||
Config::inst()->update('SiteTree', 'enforce_strict_hierarchy', false);
|
||||
$this->logInWithPermission('ADMIN');
|
||||
|
||||
|
||||
$pageAbout = $this->objFromFixture('Page', 'about');
|
||||
$pageAbout->doPublish();
|
||||
$pageStaff = $this->objFromFixture('Page', 'staff');
|
||||
$pageStaff->doPublish();
|
||||
$pageStaffDuplicate = $this->objFromFixture('Page', 'staffduplicate');
|
||||
$pageStaffDuplicate->doPublish();
|
||||
|
||||
|
||||
$parentPage = $this->objFromFixture('Page', 'about');
|
||||
|
||||
$parentPage->doDeleteFromLive();
|
||||
|
||||
|
||||
Versioned::reading_stage('Live');
|
||||
|
||||
$this->assertFalse(DataObject::get_by_id('Page', $pageAbout->ID));
|
||||
@ -377,21 +384,21 @@ class SiteTreeTest extends SapphireTest {
|
||||
Versioned::reading_stage('Stage');
|
||||
Config::inst()->update('SiteTree', 'enforce_strict_hierarchy', true);
|
||||
}
|
||||
|
||||
|
||||
public function testUnpublishDoesNotDeleteChildrenWithLooseHierachyOn() {
|
||||
Config::inst()->update('SiteTree', 'enforce_strict_hierarchy', false);
|
||||
$this->logInWithPermission('ADMIN');
|
||||
|
||||
|
||||
$pageAbout = $this->objFromFixture('Page', 'about');
|
||||
$pageAbout->doPublish();
|
||||
$pageStaff = $this->objFromFixture('Page', 'staff');
|
||||
$pageStaff->doPublish();
|
||||
$pageStaffDuplicate = $this->objFromFixture('Page', 'staffduplicate');
|
||||
$pageStaffDuplicate->doPublish();
|
||||
|
||||
|
||||
$parentPage = $this->objFromFixture('Page', 'about');
|
||||
$parentPage->doUnpublish();
|
||||
|
||||
|
||||
Versioned::reading_stage('Live');
|
||||
$this->assertFalse(DataObject::get_by_id('Page', $pageAbout->ID));
|
||||
$this->assertTrue(DataObject::get_by_id('Page', $pageStaff->ID) instanceof Page);
|
||||
@ -399,28 +406,28 @@ class SiteTreeTest extends SapphireTest {
|
||||
Versioned::reading_stage('Stage');
|
||||
Config::inst()->update('SiteTree', 'enforce_strict_hierarchy', true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public function testDeleteFromLiveOperatesRecursivelyStrict() {
|
||||
$this->logInWithPermission('ADMIN');
|
||||
|
||||
|
||||
$pageAbout = $this->objFromFixture('Page', 'about');
|
||||
$pageAbout->doPublish();
|
||||
$pageStaff = $this->objFromFixture('Page', 'staff');
|
||||
$pageStaff->doPublish();
|
||||
$pageStaffDuplicate = $this->objFromFixture('Page', 'staffduplicate');
|
||||
$pageStaffDuplicate->doPublish();
|
||||
|
||||
|
||||
$parentPage = $this->objFromFixture('Page', 'about');
|
||||
$parentPage->doDeleteFromLive();
|
||||
|
||||
|
||||
Versioned::reading_stage('Live');
|
||||
$this->assertFalse(DataObject::get_by_id('Page', $pageAbout->ID));
|
||||
$this->assertFalse(DataObject::get_by_id('Page', $pageStaff->ID));
|
||||
$this->assertFalse(DataObject::get_by_id('Page', $pageStaffDuplicate->ID));
|
||||
Versioned::reading_stage('Stage');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Simple test to confirm that querying from a particular archive date doesn't throw
|
||||
* an error
|
||||
@ -437,24 +444,29 @@ class SiteTreeTest extends SapphireTest {
|
||||
'Archive.'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public function testEditPermissions() {
|
||||
$editor = $this->objFromFixture("Member", "editor");
|
||||
|
||||
|
||||
$home = $this->objFromFixture("Page", "home");
|
||||
$staff = $this->objFromFixture("Page", "staff");
|
||||
$products = $this->objFromFixture("Page", "products");
|
||||
$product1 = $this->objFromFixture("Page", "product1");
|
||||
$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
|
||||
$this->assertFalse($home->canEdit($editor));
|
||||
|
||||
|
||||
// Can edit a page that is locked to editors
|
||||
$this->assertTrue($products->canEdit($editor));
|
||||
|
||||
|
||||
// Can edit a child of that page that inherits
|
||||
$this->assertTrue($product1->canEdit($editor));
|
||||
|
||||
|
||||
// Can't edit a child of that page that has its permissions overridden
|
||||
$this->assertFalse($product4->canEdit($editor));
|
||||
}
|
||||
@ -468,6 +480,33 @@ class SiteTreeTest extends SapphireTest {
|
||||
$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() {
|
||||
// Create an inherit-permission page
|
||||
@ -476,17 +515,17 @@ class SiteTreeTest extends SapphireTest {
|
||||
$page->CanEditType = "Inherit";
|
||||
$page->doPublish();
|
||||
$pageID = $page->ID;
|
||||
|
||||
|
||||
// Lock down the site config
|
||||
$sc = $page->SiteConfig;
|
||||
$sc->CanEditType = 'OnlyTheseUsers';
|
||||
$sc->EditorGroups()->add($this->idFromFixture('Group', 'admins'));
|
||||
$sc->write();
|
||||
|
||||
|
||||
// Confirm that Member.editor can't edit the page
|
||||
$this->objFromFixture('Member','editor')->logIn();
|
||||
$this->assertFalse($page->canEdit());
|
||||
|
||||
|
||||
// Change the page to be editable by Group.editors, but do not publish
|
||||
$this->objFromFixture('Member','admin')->logIn();
|
||||
$page->CanEditType = 'OnlyTheseUsers';
|
||||
@ -494,25 +533,25 @@ class SiteTreeTest extends SapphireTest {
|
||||
$page->write();
|
||||
// Clear permission cache
|
||||
SiteTree::on_db_reset();
|
||||
|
||||
|
||||
// Confirm that Member.editor can now edit the page
|
||||
$this->objFromFixture('Member','editor')->logIn();
|
||||
$this->assertTrue($page->canEdit());
|
||||
|
||||
|
||||
// Publish the changes to the page
|
||||
$this->objFromFixture('Member','admin')->logIn();
|
||||
$page->doPublish();
|
||||
|
||||
|
||||
// Confirm that Member.editor can still edit the page
|
||||
$this->objFromFixture('Member','editor')->logIn();
|
||||
$this->assertTrue($page->canEdit());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function testCompareVersions() {
|
||||
// Necessary to avoid
|
||||
$oldCleanerClass = Diff::$html_cleaner_class;
|
||||
Diff::$html_cleaner_class = 'SiteTreeTest_NullHtmlCleaner';
|
||||
|
||||
|
||||
$page = new Page();
|
||||
$page->write();
|
||||
$this->assertEquals(1, $page->Version);
|
||||
@ -523,14 +562,14 @@ class SiteTreeTest extends SapphireTest {
|
||||
$page->Content = "<span>This is a test</span>";
|
||||
$page->write();
|
||||
$this->assertEquals(2, $page->Version);
|
||||
|
||||
|
||||
$diff = $page->compareVersions(1, 2);
|
||||
|
||||
|
||||
$processedContent = trim($diff->Content);
|
||||
$processedContent = preg_replace('/\s*</','<',$processedContent);
|
||||
$processedContent = preg_replace('/>\s*/','>',$processedContent);
|
||||
$this->assertEquals("<ins><span>This is a test</span></ins>", $processedContent);
|
||||
|
||||
|
||||
Diff::$html_cleaner_class = $oldCleanerClass;
|
||||
}
|
||||
|
||||
@ -548,52 +587,52 @@ class SiteTreeTest extends SapphireTest {
|
||||
$about = $this->objFromFixture('Page','about');
|
||||
$about->Title = "Another title";
|
||||
$about->write();
|
||||
|
||||
|
||||
// Check the version created
|
||||
$savedVersion = DB::query("SELECT \"AuthorID\", \"PublisherID\" FROM \"SiteTree_versions\"
|
||||
$savedVersion = DB::query("SELECT \"AuthorID\", \"PublisherID\" FROM \"SiteTree_versions\"
|
||||
WHERE \"RecordID\" = $about->ID ORDER BY \"Version\" DESC")->first();
|
||||
$this->assertEquals($memberID, $savedVersion['AuthorID']);
|
||||
$this->assertEquals(0, $savedVersion['PublisherID']);
|
||||
|
||||
|
||||
// Publish the page
|
||||
$about->doPublish();
|
||||
$publishedVersion = DB::query("SELECT \"AuthorID\", \"PublisherID\" FROM \"SiteTree_versions\"
|
||||
$publishedVersion = DB::query("SELECT \"AuthorID\", \"PublisherID\" FROM \"SiteTree_versions\"
|
||||
WHERE \"RecordID\" = $about->ID ORDER BY \"Version\" DESC")->first();
|
||||
|
||||
|
||||
// Check the version created
|
||||
$this->assertEquals($memberID, $publishedVersion['AuthorID']);
|
||||
$this->assertEquals($memberID, $publishedVersion['PublisherID']);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function testLinkShortcodeHandler() {
|
||||
$aboutPage = $this->objFromFixture('Page', 'about');
|
||||
$errorPage = $this->objFromFixture('ErrorPage', '404');
|
||||
$redirectPage = $this->objFromFixture('RedirectorPage', 'external');
|
||||
|
||||
|
||||
$parser = new ShortcodeParser();
|
||||
$parser->register('sitetree_link', array('SiteTree', 'link_shortcode_handler'));
|
||||
|
||||
|
||||
$aboutShortcode = sprintf('[sitetree_link,id=%d]', $aboutPage->ID);
|
||||
$aboutEnclosed = sprintf('[sitetree_link,id=%d]Example Content[/sitetree_link]', $aboutPage->ID);
|
||||
|
||||
|
||||
$aboutShortcodeExpected = $aboutPage->Link();
|
||||
$aboutEnclosedExpected = sprintf('<a href="%s">Example Content</a>', $aboutPage->Link());
|
||||
|
||||
|
||||
$this->assertEquals($aboutShortcodeExpected, $parser->parse($aboutShortcode), 'Test that simple linking works.');
|
||||
$this->assertEquals($aboutEnclosedExpected, $parser->parse($aboutEnclosed), 'Test enclosed content is linked.');
|
||||
|
||||
|
||||
$aboutPage->delete();
|
||||
|
||||
|
||||
$this->assertEquals($aboutShortcodeExpected, $parser->parse($aboutShortcode), 'Test that deleted pages still link.');
|
||||
$this->assertEquals($aboutEnclosedExpected, $parser->parse($aboutEnclosed));
|
||||
|
||||
|
||||
$aboutShortcode = '[sitetree_link,id="-1"]';
|
||||
$aboutEnclosed = '[sitetree_link,id="-1"]Example Content[/sitetree_link]';
|
||||
|
||||
|
||||
$aboutShortcodeExpected = $errorPage->Link();
|
||||
$aboutEnclosedExpected = sprintf('<a href="%s">Example Content</a>', $errorPage->Link());
|
||||
|
||||
|
||||
$this->assertEquals($aboutShortcodeExpected, $parser->parse($aboutShortcode), 'Test link to 404 page if no suitable matches.');
|
||||
$this->assertEquals($aboutEnclosedExpected, $parser->parse($aboutEnclosed));
|
||||
|
||||
@ -603,24 +642,24 @@ class SiteTreeTest extends SapphireTest {
|
||||
|
||||
$this->assertEquals($redirectExpected, $parser->parse($redirectShortcode));
|
||||
$this->assertEquals(sprintf('<a href="%s">Example Content</a>', $redirectExpected), $parser->parse($redirectEnclosed));
|
||||
|
||||
|
||||
$this->assertEquals('', $parser->parse('[sitetree_link]'), 'Test that invalid ID attributes are not parsed.');
|
||||
$this->assertEquals('', $parser->parse('[sitetree_link,id="text"]'));
|
||||
$this->assertEquals('', $parser->parse('[sitetree_link]Example Content[/sitetree_link]'));
|
||||
}
|
||||
|
||||
|
||||
public function testIsCurrent() {
|
||||
$aboutPage = $this->objFromFixture('Page', 'about');
|
||||
$errorPage = $this->objFromFixture('ErrorPage', '404');
|
||||
|
||||
|
||||
Director::set_current_page($aboutPage);
|
||||
$this->assertTrue($aboutPage->isCurrent(), 'Assert that basic isSection checks works.');
|
||||
$this->assertFalse($errorPage->isCurrent());
|
||||
|
||||
|
||||
Director::set_current_page($errorPage);
|
||||
$this->assertTrue($errorPage->isCurrent(), 'Assert isSection works on error pages.');
|
||||
$this->assertFalse($aboutPage->isCurrent());
|
||||
|
||||
|
||||
Director::set_current_page($aboutPage);
|
||||
$this->assertTrue (
|
||||
DataObject::get_one('SiteTree', array(
|
||||
@ -628,91 +667,91 @@ class SiteTreeTest extends SapphireTest {
|
||||
))->isCurrent(),
|
||||
'Assert that isCurrent works on another instance with the same ID.'
|
||||
);
|
||||
|
||||
|
||||
Director::set_current_page($newPage = new SiteTree());
|
||||
$this->assertTrue($newPage->isCurrent(), 'Assert that isCurrent works on unsaved pages.');
|
||||
}
|
||||
|
||||
|
||||
public function testIsSection() {
|
||||
$about = $this->objFromFixture('Page', 'about');
|
||||
$staff = $this->objFromFixture('Page', 'staff');
|
||||
$ceo = $this->objFromFixture('Page', 'ceo');
|
||||
|
||||
|
||||
Director::set_current_page($about);
|
||||
$this->assertTrue($about->isSection());
|
||||
$this->assertFalse($staff->isSection());
|
||||
$this->assertFalse($ceo->isSection());
|
||||
|
||||
|
||||
Director::set_current_page($staff);
|
||||
$this->assertTrue($about->isSection());
|
||||
$this->assertTrue($staff->isSection());
|
||||
$this->assertFalse($ceo->isSection());
|
||||
|
||||
|
||||
Director::set_current_page($ceo);
|
||||
$this->assertTrue($about->isSection());
|
||||
$this->assertTrue($staff->isSection());
|
||||
$this->assertTrue($ceo->isSection());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @covers SiteTree::validURLSegment
|
||||
*/
|
||||
public function testValidURLSegmentURLSegmentConflicts() {
|
||||
$sitetree = new SiteTree();
|
||||
SiteTree::config()->nested_urls = false;
|
||||
|
||||
|
||||
$sitetree->URLSegment = 'home';
|
||||
$this->assertFalse($sitetree->validURLSegment(), 'URLSegment conflicts are recognised');
|
||||
$sitetree->URLSegment = 'home-noconflict';
|
||||
$this->assertTrue($sitetree->validURLSegment());
|
||||
|
||||
|
||||
$sitetree->ParentID = $this->idFromFixture('Page', 'about');
|
||||
$sitetree->URLSegment = 'home';
|
||||
$this->assertFalse($sitetree->validURLSegment(), 'Conflicts are still recognised with a ParentID value');
|
||||
|
||||
|
||||
Config::inst()->update('SiteTree', 'nested_urls', true);
|
||||
|
||||
|
||||
$sitetree->ParentID = 0;
|
||||
$sitetree->URLSegment = 'home';
|
||||
$this->assertFalse($sitetree->validURLSegment(), 'URLSegment conflicts are recognised');
|
||||
|
||||
|
||||
$sitetree->ParentID = $this->idFromFixture('Page', 'about');
|
||||
$this->assertTrue($sitetree->validURLSegment(), 'URLSegments can be the same across levels');
|
||||
|
||||
|
||||
$sitetree->URLSegment = 'my-staff';
|
||||
$this->assertFalse($sitetree->validURLSegment(), 'Nested URLSegment conflicts are recognised');
|
||||
$sitetree->URLSegment = 'my-staff-noconflict';
|
||||
$this->assertTrue($sitetree->validURLSegment());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @covers SiteTree::validURLSegment
|
||||
*/
|
||||
public function testValidURLSegmentClassNameConflicts() {
|
||||
$sitetree = new SiteTree();
|
||||
$sitetree->URLSegment = 'Controller';
|
||||
|
||||
|
||||
$this->assertFalse($sitetree->validURLSegment(), 'Class name conflicts are recognised');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @covers SiteTree::validURLSegment
|
||||
*/
|
||||
public function testValidURLSegmentControllerConflicts() {
|
||||
Config::inst()->update('SiteTree', 'nested_urls', true);
|
||||
|
||||
|
||||
$sitetree = new SiteTree();
|
||||
$sitetree->ParentID = $this->idFromFixture('SiteTreeTest_Conflicted', 'parent');
|
||||
|
||||
|
||||
$sitetree->URLSegment = 'index';
|
||||
$this->assertFalse($sitetree->validURLSegment(), 'index is not a valid URLSegment');
|
||||
|
||||
|
||||
$sitetree->URLSegment = 'conflicted-action';
|
||||
$this->assertFalse($sitetree->validURLSegment(), 'allowed_actions conflicts are recognised');
|
||||
|
||||
|
||||
$sitetree->URLSegment = 'conflicted-template';
|
||||
$this->assertFalse($sitetree->validURLSegment(), 'Action-specific template conflicts are recognised');
|
||||
|
||||
|
||||
$sitetree->URLSegment = 'valid';
|
||||
$this->assertTrue($sitetree->validURLSegment(), 'Valid URLSegment values are allowed');
|
||||
}
|
||||
@ -748,13 +787,13 @@ class SiteTreeTest extends SapphireTest {
|
||||
|
||||
Config::inst()->update('URLSegmentFilter', 'default_allow_multibyte', $origAllow);
|
||||
}
|
||||
|
||||
|
||||
public function testVersionsAreCreated() {
|
||||
$p = new Page();
|
||||
$p->Content = "one";
|
||||
$p->write();
|
||||
$this->assertEquals(1, $p->Version);
|
||||
|
||||
|
||||
// No changes don't bump version
|
||||
$p->write();
|
||||
$this->assertEquals(1, $p->Version);
|
||||
@ -775,45 +814,45 @@ class SiteTreeTest extends SapphireTest {
|
||||
$this->assertEquals(3, $p->Version);
|
||||
|
||||
}
|
||||
|
||||
|
||||
public function testPageTypeClasses() {
|
||||
$classes = SiteTree::page_type_classes();
|
||||
$this->assertNotContains('SiteTree', $classes, 'Page types do not include base class');
|
||||
$this->assertContains('Page', $classes, 'Page types do contain subclasses');
|
||||
}
|
||||
|
||||
|
||||
public function testAllowedChildren() {
|
||||
$page = new SiteTree();
|
||||
$this->assertContains(
|
||||
'VirtualPage',
|
||||
'VirtualPage',
|
||||
$page->allowedChildren(),
|
||||
'Includes core subclasses by default'
|
||||
);
|
||||
|
||||
|
||||
$classA = new SiteTreeTest_ClassA();
|
||||
$this->assertEquals(
|
||||
array('SiteTreeTest_ClassB'),
|
||||
array('SiteTreeTest_ClassB'),
|
||||
$classA->allowedChildren(),
|
||||
'Direct setting of allowed children'
|
||||
);
|
||||
|
||||
|
||||
$classB = new SiteTreeTest_ClassB();
|
||||
$this->assertEquals(
|
||||
array('SiteTreeTest_ClassC', 'SiteTreeTest_ClassCext'),
|
||||
array('SiteTreeTest_ClassC', 'SiteTreeTest_ClassCext'),
|
||||
$classB->allowedChildren(),
|
||||
'Includes subclasses'
|
||||
);
|
||||
|
||||
|
||||
$classD = new SiteTreeTest_ClassD();
|
||||
$this->assertEquals(
|
||||
array('SiteTreeTest_ClassC'),
|
||||
array('SiteTreeTest_ClassC'),
|
||||
$classD->allowedChildren(),
|
||||
'Excludes subclasses if class is prefixed by an asterisk'
|
||||
);
|
||||
|
||||
|
||||
$classC = new SiteTreeTest_ClassC();
|
||||
$this->assertEquals(
|
||||
array(),
|
||||
array(),
|
||||
$classC->allowedChildren(),
|
||||
'Null setting'
|
||||
);
|
||||
@ -832,11 +871,11 @@ class SiteTreeTest extends SapphireTest {
|
||||
$classD->write();
|
||||
$classCext = new SiteTreeTest_ClassCext();
|
||||
$classCext->write();
|
||||
|
||||
|
||||
$classB->ParentID = $page->ID;
|
||||
$valid = $classB->validate();
|
||||
$this->assertTrue($valid->valid(), "Does allow children on unrestricted parent");
|
||||
|
||||
|
||||
$classB->ParentID = $classA->ID;
|
||||
$valid = $classB->validate();
|
||||
$this->assertTrue($valid->valid(), "Does allow child specifically allowed by parent");
|
||||
@ -844,20 +883,20 @@ class SiteTreeTest extends SapphireTest {
|
||||
$classC->ParentID = $classA->ID;
|
||||
$valid = $classC->validate();
|
||||
$this->assertFalse($valid->valid(), "Doesnt allow child on parents specifically restricting children");
|
||||
|
||||
|
||||
$classB->ParentID = $classC->ID;
|
||||
$valid = $classB->validate();
|
||||
$this->assertFalse($valid->valid(), "Doesnt allow child on parents disallowing all children");
|
||||
|
||||
|
||||
$classB->ParentID = $classC->ID;
|
||||
$valid = $classB->validate();
|
||||
$this->assertFalse($valid->valid(), "Doesnt allow child on parents disallowing all children");
|
||||
|
||||
|
||||
$classCext->ParentID = $classD->ID;
|
||||
$valid = $classCext->validate();
|
||||
$this->assertFalse($valid->valid(), "Doesnt allow child where only parent class is allowed on parent node, and asterisk prefixing is used");
|
||||
}
|
||||
|
||||
|
||||
public function testClassDropdown() {
|
||||
$sitetree = new SiteTree();
|
||||
$method = new ReflectionMethod($sitetree, 'getClassDropdown');
|
||||
@ -865,13 +904,13 @@ class SiteTreeTest extends SapphireTest {
|
||||
|
||||
Session::set("loggedInAs", null);
|
||||
$this->assertArrayNotHasKey('SiteTreeTest_ClassA', $method->invoke($sitetree));
|
||||
|
||||
|
||||
$this->loginWithPermission('ADMIN');
|
||||
$this->assertArrayHasKey('SiteTreeTest_ClassA', $method->invoke($sitetree));
|
||||
|
||||
|
||||
$this->loginWithPermission('CMS_ACCESS_CMSMain');
|
||||
$this->assertArrayHasKey('SiteTreeTest_ClassA', $method->invoke($sitetree));
|
||||
|
||||
|
||||
Session::set("loggedInAs", null);
|
||||
}
|
||||
|
||||
@ -884,14 +923,14 @@ class SiteTreeTest extends SapphireTest {
|
||||
$notRootPage->ParentID = 0;
|
||||
$isDetected = false;
|
||||
try {
|
||||
$notRootPage->write();
|
||||
$notRootPage->write();
|
||||
} catch(ValidationException $e) {
|
||||
$this->assertContains('is not allowed on the root level', $e->getMessage());
|
||||
$isDetected = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!$isDetected) $this->fail('Fails validation with $can_be_root=false');
|
||||
}
|
||||
}
|
||||
|
||||
public function testModifyStatusFlagByInheritance(){
|
||||
$node = new SiteTreeTest_StageStatusInherit();
|
||||
@ -905,7 +944,7 @@ class SiteTreeTest extends SapphireTest {
|
||||
$page->Title = 'orig';
|
||||
$page->MenuTitle = 'orig';
|
||||
$page->write();
|
||||
|
||||
|
||||
// change menu title
|
||||
$page->MenuTitle = 'changed';
|
||||
$page->write();
|
||||
@ -976,7 +1015,7 @@ class SiteTreeTest extends SapphireTest {
|
||||
public function testMetaTags() {
|
||||
$this->logInWithPermission('ADMIN');
|
||||
$page = $this->objFromFixture('Page', 'metapage');
|
||||
|
||||
|
||||
// Test with title
|
||||
$meta = $page->MetaTags();
|
||||
$charset = Config::inst()->get('ContentNegotiator', 'encoding');
|
||||
@ -986,18 +1025,18 @@ class SiteTreeTest extends SapphireTest {
|
||||
$this->assertContains('<meta name="x-page-id" content="'.$page->ID.'"', $meta);
|
||||
$this->assertContains('<meta name="x-cms-edit-link" content="'.$page->CMSEditLink().'" />', $meta);
|
||||
$this->assertContains('<title>HTML & XML</title>', $meta);
|
||||
|
||||
|
||||
// Test without title
|
||||
$meta = $page->MetaTags(false);
|
||||
$this->assertNotContains('<title>', $meta);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test that orphaned pages are handled correctly
|
||||
*/
|
||||
public function testOrphanedPages() {
|
||||
$origStage = Versioned::get_reading_mode();
|
||||
|
||||
|
||||
// Setup user who can view draft content, but lacks cms permission.
|
||||
// To users such as this, orphaned pages should be inaccessible. canView for these pages is only
|
||||
// necessary for admin / cms users, who require this permission to edit / rearrange these pages.
|
||||
@ -1010,7 +1049,7 @@ class SiteTreeTest extends SapphireTest {
|
||||
$member->Email = 'someguy@example.com';
|
||||
$member->write();
|
||||
$member->Groups()->add($group);
|
||||
|
||||
|
||||
// both pages are viewable in stage
|
||||
Versioned::reading_stage('Stage');
|
||||
$about = $this->objFromFixture('Page', 'about');
|
||||
@ -1019,7 +1058,7 @@ class SiteTreeTest extends SapphireTest {
|
||||
$this->assertFalse($staff->isOrphaned());
|
||||
$this->assertTrue($about->canView($member));
|
||||
$this->assertTrue($staff->canView($member));
|
||||
|
||||
|
||||
// Publishing only the child page to live should orphan the live record, but not the staging one
|
||||
$staff->publish('Stage', 'Live');
|
||||
$this->assertFalse($staff->isOrphaned());
|
||||
@ -1028,7 +1067,7 @@ class SiteTreeTest extends SapphireTest {
|
||||
$staff = $this->objFromFixture('Page', 'staff'); // Live copy of page
|
||||
$this->assertTrue($staff->isOrphaned()); // because parent isn't published
|
||||
$this->assertFalse($staff->canView($member));
|
||||
|
||||
|
||||
// Publishing the parent page should restore visibility
|
||||
Versioned::reading_stage('Stage');
|
||||
$about = $this->objFromFixture('Page', 'about');
|
||||
@ -1037,19 +1076,19 @@ class SiteTreeTest extends SapphireTest {
|
||||
$staff = $this->objFromFixture('Page', 'staff');
|
||||
$this->assertFalse($staff->isOrphaned());
|
||||
$this->assertTrue($staff->canView($member));
|
||||
|
||||
|
||||
// Removing staging page should not prevent live page being visible
|
||||
$about->deleteFromStage('Stage');
|
||||
$staff->deleteFromStage('Stage');
|
||||
$staff = $this->objFromFixture('Page', 'staff');
|
||||
$this->assertFalse($staff->isOrphaned());
|
||||
$this->assertTrue($staff->canView($member));
|
||||
|
||||
|
||||
// Cleanup
|
||||
Versioned::set_reading_mode($origStage);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**#@+
|
||||
|
@ -1,3 +1,11 @@
|
||||
SiteConfig:
|
||||
default:
|
||||
Title: My test site
|
||||
Tagline: Default site config
|
||||
CanViewType: Anyone
|
||||
CanEditType: LoggedInUsers
|
||||
CanCreateTopLevelType: LoggedInUsers
|
||||
|
||||
Group:
|
||||
editors:
|
||||
Title: Editors
|
||||
|
@ -591,6 +591,19 @@ class VirtualPageTest extends SapphireTest {
|
||||
'No field copying from previous original after page type changed'
|
||||
);
|
||||
}
|
||||
|
||||
public function testVirtualPageFindsCorrectCasting() {
|
||||
$page = new VirtualPageTest_ClassA();
|
||||
$page->CastingTest = "Some content";
|
||||
$page->write();
|
||||
$virtual = new VirtualPage();
|
||||
$virtual->CopyContentFromID = $page->ID;
|
||||
$virtual->write();
|
||||
|
||||
$this->assertEquals('VirtualPageTest_TestDBField', $virtual->castingHelper('CastingTest'));
|
||||
$this->assertEquals('SOME CONTENT', $virtual->obj('CastingTest')->forTemplate());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class VirtualPageTest_ClassA extends Page implements TestOnly {
|
||||
@ -599,6 +612,7 @@ class VirtualPageTest_ClassA extends Page implements TestOnly {
|
||||
'MyInitiallyCopiedField' => 'Text',
|
||||
'MyVirtualField' => 'Text',
|
||||
'MyNonVirtualField' => 'Text',
|
||||
'CastingTest' => 'VirtualPageTest_TestDBField'
|
||||
);
|
||||
|
||||
private static $allowed_children = array('VirtualPageTest_ClassB');
|
||||
@ -616,6 +630,12 @@ class VirtualPageTest_NotRoot extends Page implements TestOnly {
|
||||
private static $can_be_root = false;
|
||||
}
|
||||
|
||||
class VirtualPageTest_TestDBField extends Varchar implements TestOnly {
|
||||
public function forTemplate() {
|
||||
return strtoupper($this->XML());
|
||||
}
|
||||
}
|
||||
|
||||
class VirtualPageTest_VirtualPageSub extends VirtualPage implements TestOnly {
|
||||
private static $db = array(
|
||||
'MyProperty' => 'Varchar',
|
||||
|
Loading…
x
Reference in New Issue
Block a user