mirror of
https://github.com/silverstripe/silverstripe-cms
synced 2024-10-22 06:05:56 +00:00
Merge pull request #2048 from open-sausages/pulls/4.0/sitetree-can-create-cache
ENHANCEMENT: Cache canCreate in CMSMain
This commit is contained in:
commit
6b0863d362
@ -8,3 +8,8 @@ SilverStripe\Core\Injector\Injector:
|
|||||||
factory: SilverStripe\Core\Cache\CacheFactory
|
factory: SilverStripe\Core\Cache\CacheFactory
|
||||||
constructor:
|
constructor:
|
||||||
namespace: "CMSMain_SiteTreeHints"
|
namespace: "CMSMain_SiteTreeHints"
|
||||||
|
Psr\SimpleCache\CacheInterface.SiteTree_CreatableChildren:
|
||||||
|
factory: SilverStripe\Core\Cache\CacheFactory
|
||||||
|
constructor:
|
||||||
|
namespace: "SiteTree_CreatableChildren"
|
||||||
|
|
||||||
|
@ -16,3 +16,5 @@ SilverStripe\Core\Injector\Injector:
|
|||||||
properties:
|
properties:
|
||||||
Services:
|
Services:
|
||||||
- '%$SilverStripe\Security\PermissionChecker.sitetree'
|
- '%$SilverStripe\Security\PermissionChecker.sitetree'
|
||||||
|
- '%$SilverStripe\CMS\Controllers\CMSMain'
|
||||||
|
- '%$SilverStripe\CMS\Model\SiteTree'
|
||||||
|
@ -25,8 +25,6 @@ use SilverStripe\Core\Config\Config;
|
|||||||
use SilverStripe\Core\Convert;
|
use SilverStripe\Core\Convert;
|
||||||
use SilverStripe\Core\Environment;
|
use SilverStripe\Core\Environment;
|
||||||
use SilverStripe\Core\Injector\Injector;
|
use SilverStripe\Core\Injector\Injector;
|
||||||
use SilverStripe\Core\Manifest\ModuleResource;
|
|
||||||
use SilverStripe\Core\Manifest\ModuleResourceLoader;
|
|
||||||
use SilverStripe\Forms\DateField;
|
use SilverStripe\Forms\DateField;
|
||||||
use SilverStripe\Forms\DropdownField;
|
use SilverStripe\Forms\DropdownField;
|
||||||
use SilverStripe\Forms\FieldGroup;
|
use SilverStripe\Forms\FieldGroup;
|
||||||
@ -53,7 +51,6 @@ use SilverStripe\ORM\DataObject;
|
|||||||
use SilverStripe\ORM\DB;
|
use SilverStripe\ORM\DB;
|
||||||
use SilverStripe\ORM\FieldType\DBHTMLText;
|
use SilverStripe\ORM\FieldType\DBHTMLText;
|
||||||
use SilverStripe\ORM\HiddenClass;
|
use SilverStripe\ORM\HiddenClass;
|
||||||
use SilverStripe\ORM\Hierarchy;
|
|
||||||
use SilverStripe\ORM\Hierarchy\MarkedSet;
|
use SilverStripe\ORM\Hierarchy\MarkedSet;
|
||||||
use SilverStripe\ORM\SS_List;
|
use SilverStripe\ORM\SS_List;
|
||||||
use SilverStripe\ORM\ValidationResult;
|
use SilverStripe\ORM\ValidationResult;
|
||||||
@ -69,6 +66,8 @@ use SilverStripe\Versioned\ChangeSetItem;
|
|||||||
use SilverStripe\Versioned\Versioned;
|
use SilverStripe\Versioned\Versioned;
|
||||||
use SilverStripe\View\ArrayData;
|
use SilverStripe\View\ArrayData;
|
||||||
use SilverStripe\View\Requirements;
|
use SilverStripe\View\Requirements;
|
||||||
|
use SilverStripe\Core\Flushable;
|
||||||
|
use SilverStripe\Core\Cache\MemberCacheFlusher;
|
||||||
use Translatable;
|
use Translatable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -81,7 +80,7 @@ use Translatable;
|
|||||||
*
|
*
|
||||||
* @mixin LeftAndMainPageIconsExtension
|
* @mixin LeftAndMainPageIconsExtension
|
||||||
*/
|
*/
|
||||||
class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionProvider
|
class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionProvider, Flushable, MemberCacheFlusher
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Unique ID for page icons CSS block
|
* Unique ID for page icons CSS block
|
||||||
@ -162,6 +161,15 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
'SiteTreeAsUL' => 'HTMLFragment',
|
'SiteTreeAsUL' => 'HTMLFragment',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
private static $dependencies = [
|
||||||
|
'HintsCache' => '%$' . CacheInterface::class . '.CMSMain_SiteTreeHints',
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var CacheInterface
|
||||||
|
*/
|
||||||
|
protected $hintsCache;
|
||||||
|
|
||||||
protected function init()
|
protected function init()
|
||||||
{
|
{
|
||||||
// set reading lang
|
// set reading lang
|
||||||
@ -381,6 +389,33 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
return 'edit';
|
return 'edit';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param CacheInterface $cache
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setHintsCache(CacheInterface $cache)
|
||||||
|
{
|
||||||
|
$this->hintsCache = $cache;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return CacheInterface $cache
|
||||||
|
*/
|
||||||
|
public function getHintsCache()
|
||||||
|
{
|
||||||
|
return $this->hintsCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clears all dependent cache backends
|
||||||
|
*/
|
||||||
|
public function clearCache()
|
||||||
|
{
|
||||||
|
$this->getHintsCache()->clear();
|
||||||
|
}
|
||||||
|
|
||||||
public function LinkWithSearch($link)
|
public function LinkWithSearch($link)
|
||||||
{
|
{
|
||||||
// Whitelist to avoid side effects
|
// Whitelist to avoid side effects
|
||||||
@ -977,26 +1012,26 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
public function SiteTreeHints()
|
public function SiteTreeHints()
|
||||||
{
|
{
|
||||||
$classes = SiteTree::page_type_classes();
|
$classes = SiteTree::page_type_classes();
|
||||||
|
$memberID = Security::getCurrentUser() ? Security::getCurrentUser()->ID : 0;
|
||||||
$cacheCanCreate = array();
|
$cache = $this->getHintsCache();
|
||||||
foreach ($classes as $class) {
|
$cacheKey = $this->generateHintsCacheKey($memberID);
|
||||||
$cacheCanCreate[$class] = singleton($class)->canCreate();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generate basic cache key. Too complex to encompass all variations
|
|
||||||
$cache = Injector::inst()->get(CacheInterface::class . '.CMSMain_SiteTreeHints');
|
|
||||||
$cacheKey = md5(implode('_', array(Security::getCurrentUser()->ID, implode(',', $cacheCanCreate), implode(',', $classes))));
|
|
||||||
if ($this->getRequest()->getVar('flush')) {
|
|
||||||
$cache->clear();
|
|
||||||
}
|
|
||||||
$json = $cache->get($cacheKey);
|
$json = $cache->get($cacheKey);
|
||||||
if (!$json) {
|
|
||||||
$def['Root'] = array();
|
if ($json) {
|
||||||
$def['Root']['disallowedChildren'] = array();
|
return $json;
|
||||||
|
}
|
||||||
|
|
||||||
|
$canCreate = [];
|
||||||
|
foreach ($classes as $class) {
|
||||||
|
$canCreate[$class] = singleton($class)->canCreate();
|
||||||
|
}
|
||||||
|
|
||||||
|
$def['Root'] = [];
|
||||||
|
$def['Root']['disallowedChildren'] = [];
|
||||||
|
|
||||||
// Contains all possible classes to support UI controls listing them all,
|
// Contains all possible classes to support UI controls listing them all,
|
||||||
// such as the "add page here" context menu.
|
// such as the "add page here" context menu.
|
||||||
$def['All'] = array();
|
$def['All'] = [];
|
||||||
|
|
||||||
// Identify disallows and set globals
|
// Identify disallows and set globals
|
||||||
foreach ($classes as $class) {
|
foreach ($classes as $class) {
|
||||||
@ -1006,21 +1041,21 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Name item
|
// Name item
|
||||||
$def['All'][$class] = array(
|
$def['All'][$class] = [
|
||||||
'title' => $obj->i18n_singular_name()
|
'title' => $obj->i18n_singular_name()
|
||||||
);
|
];
|
||||||
|
|
||||||
// Check if can be created at the root
|
// Check if can be created at the root
|
||||||
$needsPerm = $obj->config()->get('need_permission');
|
$needsPerm = $obj->config()->get('need_permission');
|
||||||
if (!$obj->config()->get('can_be_root')
|
if (!$obj->config()->get('can_be_root')
|
||||||
|| (!array_key_exists($class, $cacheCanCreate) || !$cacheCanCreate[$class])
|
|| (!array_key_exists($class, $canCreate) || !$canCreate[$class])
|
||||||
|| ($needsPerm && !$this->can($needsPerm))
|
|| ($needsPerm && !$this->can($needsPerm))
|
||||||
) {
|
) {
|
||||||
$def['Root']['disallowedChildren'][] = $class;
|
$def['Root']['disallowedChildren'][] = $class;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hint data specific to the class
|
// Hint data specific to the class
|
||||||
$def[$class] = array();
|
$def[$class] = [];
|
||||||
|
|
||||||
$defaultChild = $obj->defaultChild();
|
$defaultChild = $obj->defaultChild();
|
||||||
if ($defaultChild !== 'Page' && $defaultChild !== null) {
|
if ($defaultChild !== 'Page' && $defaultChild !== null) {
|
||||||
@ -1037,7 +1072,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
|
|
||||||
$json = Convert::raw2json($def);
|
$json = Convert::raw2json($def);
|
||||||
$cache->set($cacheKey, $json);
|
$cache->set($cacheKey, $json);
|
||||||
}
|
|
||||||
return $json;
|
return $json;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1477,7 +1512,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
$gridField = new GridField('Page', 'Pages', $list, $gridFieldConfig);
|
$gridField = new GridField('Page', 'Pages', $list, $gridFieldConfig);
|
||||||
$gridField->setAttribute('cms-loading-ignore-url-params', true);
|
$gridField->setAttribute('cms-loading-ignore-url-params', true);
|
||||||
/** @var GridFieldDataColumns $columns */
|
/** @var GridFieldDataColumns $columns */
|
||||||
$columns = $gridField->getConfig()->getComponentByType('SilverStripe\\Forms\\GridField\\GridFieldDataColumns');
|
$columns = $gridField->getConfig()->getComponentByType(GridFieldDataColumns::class);
|
||||||
|
|
||||||
// Don't allow navigating into children nodes on filtered lists
|
// Don't allow navigating into children nodes on filtered lists
|
||||||
$fields = array(
|
$fields = array(
|
||||||
@ -1486,7 +1521,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
'LastEdited' => _t('SilverStripe\\CMS\\Model\\SiteTree.LASTUPDATED', 'Last Updated'),
|
'LastEdited' => _t('SilverStripe\\CMS\\Model\\SiteTree.LASTUPDATED', 'Last Updated'),
|
||||||
);
|
);
|
||||||
/** @var GridFieldSortableHeader $sortableHeader */
|
/** @var GridFieldSortableHeader $sortableHeader */
|
||||||
$sortableHeader = $gridField->getConfig()->getComponentByType('SilverStripe\\Forms\\GridField\\GridFieldSortableHeader');
|
$sortableHeader = $gridField->getConfig()->getComponentByType(GridFieldSortableHeader::class);
|
||||||
$sortableHeader->setFieldSorting(array('getTreeTitle' => 'Title'));
|
$sortableHeader->setFieldSorting(array('getTreeTitle' => 'Title'));
|
||||||
$gridField->getState()->ParentID = $parentID;
|
$gridField->getState()->ParentID = $parentID;
|
||||||
|
|
||||||
@ -1630,13 +1665,13 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
if ($doPublish) {
|
if ($doPublish) {
|
||||||
$record->publishRecursive();
|
$record->publishRecursive();
|
||||||
$message = _t(
|
$message = _t(
|
||||||
'SilverStripe\\CMS\\Controllers\\CMSMain.PUBLISHED',
|
__CLASS__ . '.PUBLISHED',
|
||||||
"Published '{title}' successfully.",
|
"Published '{title}' successfully.",
|
||||||
['title' => $record->Title]
|
['title' => $record->Title]
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
$message = _t(
|
$message = _t(
|
||||||
'SilverStripe\\CMS\\Controllers\\CMSMain.SAVED',
|
__CLASS__ . '.SAVED',
|
||||||
"Saved '{title}' successfully.",
|
"Saved '{title}' successfully.",
|
||||||
['title' => $record->Title]
|
['title' => $record->Title]
|
||||||
);
|
);
|
||||||
@ -1670,7 +1705,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
/** @var SiteTree $newItem */
|
/** @var SiteTree $newItem */
|
||||||
$newItem = Injector::inst()->create($className);
|
$newItem = Injector::inst()->create($className);
|
||||||
$newItem->Title = _t(
|
$newItem->Title = _t(
|
||||||
'SilverStripe\\CMS\\Controllers\\CMSMain.NEWPAGE',
|
__CLASS__ . '.NEWPAGE',
|
||||||
"New {pagetype}",
|
"New {pagetype}",
|
||||||
'followed by a page type title',
|
'followed by a page type title',
|
||||||
array('pagetype' => singleton($className)->i18n_singular_name())
|
array('pagetype' => singleton($className)->i18n_singular_name())
|
||||||
@ -1756,7 +1791,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
$this->getResponse()->addHeader(
|
$this->getResponse()->addHeader(
|
||||||
'X-Status',
|
'X-Status',
|
||||||
rawurlencode(_t(
|
rawurlencode(_t(
|
||||||
'SilverStripe\\CMS\\Controllers\\CMSMain.RESTORED',
|
__CLASS__ . '.RESTORED',
|
||||||
"Restored '{title}' successfully",
|
"Restored '{title}' successfully",
|
||||||
'Param {title} is a title',
|
'Param {title} is a title',
|
||||||
array('title' => $record->Title)
|
array('title' => $record->Title)
|
||||||
@ -1863,7 +1898,11 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
|
|
||||||
$this->getResponse()->addHeader(
|
$this->getResponse()->addHeader(
|
||||||
'X-Status',
|
'X-Status',
|
||||||
rawurlencode(_t('SilverStripe\\CMS\\Controllers\\CMSMain.REMOVEDPAGE', "Removed '{title}' from the published site", array('title' => $record->Title)))
|
rawurlencode(_t(
|
||||||
|
__CLASS__ . '.REMOVEDPAGE',
|
||||||
|
"Removed '{title}' from the published site",
|
||||||
|
['title' => $record->Title]
|
||||||
|
))
|
||||||
);
|
);
|
||||||
|
|
||||||
return $this->getResponseNegotiator()->respond($this->getRequest());
|
return $this->getResponseNegotiator()->respond($this->getRequest());
|
||||||
@ -2006,7 +2045,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$response .= _t('SilverStripe\\CMS\\Controllers\\CMSMain.PUBPAGES', "Done: Published {count} pages", array('count' => $count));
|
$response .= _t(__CLASS__ . '.PUBPAGES', "Done: Published {count} pages", array('count' => $count));
|
||||||
} else {
|
} else {
|
||||||
$token = SecurityToken::inst();
|
$token = SecurityToken::inst();
|
||||||
$fields = new FieldList();
|
$fields = new FieldList();
|
||||||
@ -2014,16 +2053,16 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
$tokenField = $fields->first();
|
$tokenField = $fields->first();
|
||||||
$tokenHtml = ($tokenField) ? $tokenField->FieldHolder() : '';
|
$tokenHtml = ($tokenField) ? $tokenField->FieldHolder() : '';
|
||||||
$publishAllDescription = _t(
|
$publishAllDescription = _t(
|
||||||
'SilverStripe\\CMS\\Controllers\\CMSMain.PUBALLFUN2',
|
__CLASS__ . '.PUBALLFUN2',
|
||||||
'Pressing this button will do the equivalent of going to every page and pressing "publish". '
|
'Pressing this button will do the equivalent of going to every page and pressing "publish". '
|
||||||
. 'It\'s intended to be used after there have been massive edits of the content, such as when '
|
. 'It\'s intended to be used after there have been massive edits of the content, such as when '
|
||||||
. 'the site was first built.'
|
. 'the site was first built.'
|
||||||
);
|
);
|
||||||
$response .= '<h1>' . _t('SilverStripe\\CMS\\Controllers\\CMSMain.PUBALLFUN', '"Publish All" functionality') . '</h1>
|
$response .= '<h1>' . _t(__CLASS__ . '.PUBALLFUN', '"Publish All" functionality') . '</h1>
|
||||||
<p>' . $publishAllDescription . '</p>
|
<p>' . $publishAllDescription . '</p>
|
||||||
<form method="post" action="publishall">
|
<form method="post" action="publishall">
|
||||||
<input type="submit" name="confirm" value="'
|
<input type="submit" name="confirm" value="'
|
||||||
. _t('SilverStripe\\CMS\\Controllers\\CMSMain.PUBALLCONFIRM', "Please publish every page in the site, copying content stage to live", 'Confirmation button') .'" />'
|
. _t(__CLASS__ . '.PUBALLCONFIRM', "Please publish every page in the site, copying content stage to live", 'Confirmation button') .'" />'
|
||||||
. $tokenHtml .
|
. $tokenHtml .
|
||||||
'</form>';
|
'</form>';
|
||||||
}
|
}
|
||||||
@ -2056,7 +2095,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
$this->getResponse()->addHeader(
|
$this->getResponse()->addHeader(
|
||||||
'X-Status',
|
'X-Status',
|
||||||
rawurlencode(_t(
|
rawurlencode(_t(
|
||||||
'SilverStripe\\CMS\\Controllers\\CMSMain.RESTORED',
|
__CLASS__ . '.RESTORED',
|
||||||
"Restored '{title}' successfully",
|
"Restored '{title}' successfully",
|
||||||
array('title' => $restoredPage->Title)
|
array('title' => $restoredPage->Title)
|
||||||
))
|
))
|
||||||
@ -2093,7 +2132,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
$this->getResponse()->addHeader(
|
$this->getResponse()->addHeader(
|
||||||
'X-Status',
|
'X-Status',
|
||||||
rawurlencode(_t(
|
rawurlencode(_t(
|
||||||
'SilverStripe\\CMS\\Controllers\\CMSMain.DUPLICATED',
|
__CLASS__ . '.DUPLICATED',
|
||||||
"Duplicated '{title}' successfully",
|
"Duplicated '{title}' successfully",
|
||||||
array('title' => $newPage->Title)
|
array('title' => $newPage->Title)
|
||||||
))
|
))
|
||||||
@ -2131,7 +2170,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
$this->getResponse()->addHeader(
|
$this->getResponse()->addHeader(
|
||||||
'X-Status',
|
'X-Status',
|
||||||
rawurlencode(_t(
|
rawurlencode(_t(
|
||||||
'SilverStripe\\CMS\\Controllers\\CMSMain.DUPLICATEDWITHCHILDREN',
|
__CLASS__ . '.DUPLICATEDWITHCHILDREN',
|
||||||
"Duplicated '{title}' and children successfully",
|
"Duplicated '{title}' and children successfully",
|
||||||
array('title' => $newPage->Title)
|
array('title' => $newPage->Title)
|
||||||
))
|
))
|
||||||
@ -2152,10 +2191,10 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
$title = CMSPagesController::menu_title();
|
$title = CMSPagesController::menu_title();
|
||||||
return array(
|
return array(
|
||||||
"CMS_ACCESS_CMSMain" => array(
|
"CMS_ACCESS_CMSMain" => array(
|
||||||
'name' => _t('SilverStripe\\CMS\\Controllers\\CMSMain.ACCESS', "Access to '{title}' section", array('title' => $title)),
|
'name' => _t(__CLASS__ . '.ACCESS', "Access to '{title}' section", array('title' => $title)),
|
||||||
'category' => _t('SilverStripe\\Security\\Permission.CMS_ACCESS_CATEGORY', 'CMS Access'),
|
'category' => _t('SilverStripe\\Security\\Permission.CMS_ACCESS_CATEGORY', 'CMS Access'),
|
||||||
'help' => _t(
|
'help' => _t(
|
||||||
'SilverStripe\\CMS\\Controllers\\CMSMain.ACCESS_HELP',
|
__CLASS__ . '.ACCESS_HELP',
|
||||||
'Allow viewing of the section containing page tree and content. View and edit permissions can be handled through page specific dropdowns, as well as the separate "Content permissions".'
|
'Allow viewing of the section containing page tree and content. View and edit permissions can be handled through page specific dropdowns, as well as the separate "Content permissions".'
|
||||||
),
|
),
|
||||||
'sort' => -99 // below "CMS_ACCESS_LeftAndMain", but above everything else
|
'sort' => -99 // below "CMS_ACCESS_LeftAndMain", but above everything else
|
||||||
@ -2174,4 +2213,43 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
$this->extend('updateCMSTreeTitle', $rootTitle);
|
$this->extend('updateCMSTreeTitle', $rootTitle);
|
||||||
return $rootTitle;
|
return $rootTitle;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache key for SiteTreeHints() method
|
||||||
|
*
|
||||||
|
* @param $memberID
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function generateHintsCacheKey($memberID)
|
||||||
|
{
|
||||||
|
return md5($memberID . '_' . __CLASS__);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the cache on ?flush
|
||||||
|
*/
|
||||||
|
public static function flush()
|
||||||
|
{
|
||||||
|
CMSMain::singleton()->clearCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flush the hints cache for a specific member
|
||||||
|
*
|
||||||
|
* @param array $memberIDs
|
||||||
|
*/
|
||||||
|
public function flushMemberCache($memberIDs = null)
|
||||||
|
{
|
||||||
|
$cache = $this->getHintsCache();
|
||||||
|
|
||||||
|
if (!$memberIDs) {
|
||||||
|
$cache->clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($memberIDs as $memberID) {
|
||||||
|
$key = $this->generateHintsCacheKey($memberID);
|
||||||
|
$cache->delete($key);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace SilverStripe\CMS\Model;
|
namespace SilverStripe\CMS\Model;
|
||||||
|
|
||||||
use Page;
|
use Page;
|
||||||
|
use Psr\SimpleCache\CacheInterface;
|
||||||
use SilverStripe\CampaignAdmin\AddToCampaignHandler_FormAction;
|
use SilverStripe\CampaignAdmin\AddToCampaignHandler_FormAction;
|
||||||
use SilverStripe\CMS\Controllers\CMSPageEditController;
|
use SilverStripe\CMS\Controllers\CMSPageEditController;
|
||||||
use SilverStripe\CMS\Controllers\ContentController;
|
use SilverStripe\CMS\Controllers\ContentController;
|
||||||
@ -16,6 +17,7 @@ use SilverStripe\Control\RequestHandler;
|
|||||||
use SilverStripe\Core\ClassInfo;
|
use SilverStripe\Core\ClassInfo;
|
||||||
use SilverStripe\Core\Config\Config;
|
use SilverStripe\Core\Config\Config;
|
||||||
use SilverStripe\Core\Convert;
|
use SilverStripe\Core\Convert;
|
||||||
|
use SilverStripe\Core\Flushable;
|
||||||
use SilverStripe\Core\Injector\Injector;
|
use SilverStripe\Core\Injector\Injector;
|
||||||
use SilverStripe\Core\Manifest\ModuleResource;
|
use SilverStripe\Core\Manifest\ModuleResource;
|
||||||
use SilverStripe\Core\Manifest\ModuleResourceLoader;
|
use SilverStripe\Core\Manifest\ModuleResourceLoader;
|
||||||
@ -30,7 +32,6 @@ use SilverStripe\Forms\FormAction;
|
|||||||
use SilverStripe\Forms\GridField\GridField;
|
use SilverStripe\Forms\GridField\GridField;
|
||||||
use SilverStripe\Forms\GridField\GridFieldDataColumns;
|
use SilverStripe\Forms\GridField\GridFieldDataColumns;
|
||||||
use SilverStripe\Forms\HTMLEditor\HTMLEditorField;
|
use SilverStripe\Forms\HTMLEditor\HTMLEditorField;
|
||||||
use SilverStripe\Forms\ListboxField;
|
|
||||||
use SilverStripe\Forms\LiteralField;
|
use SilverStripe\Forms\LiteralField;
|
||||||
use SilverStripe\Forms\OptionsetField;
|
use SilverStripe\Forms\OptionsetField;
|
||||||
use SilverStripe\Forms\Tab;
|
use SilverStripe\Forms\Tab;
|
||||||
@ -66,6 +67,7 @@ use SilverStripe\View\HTML;
|
|||||||
use SilverStripe\View\Parsers\ShortcodeParser;
|
use SilverStripe\View\Parsers\ShortcodeParser;
|
||||||
use SilverStripe\View\Parsers\URLSegmentFilter;
|
use SilverStripe\View\Parsers\URLSegmentFilter;
|
||||||
use SilverStripe\View\SSViewer;
|
use SilverStripe\View\SSViewer;
|
||||||
|
use SilverStripe\Core\Cache\MemberCacheFlusher;
|
||||||
use Subsite;
|
use Subsite;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -100,7 +102,7 @@ use Subsite;
|
|||||||
* @mixin SiteTreeLinkTracking
|
* @mixin SiteTreeLinkTracking
|
||||||
* @mixin InheritedPermissionsExtension
|
* @mixin InheritedPermissionsExtension
|
||||||
*/
|
*/
|
||||||
class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvider, CMSPreviewable, Resettable
|
class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvider, CMSPreviewable, Resettable, Flushable, MemberCacheFlusher
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -343,6 +345,18 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
|||||||
*/
|
*/
|
||||||
private static $base_description = 'Generic content page';
|
private static $base_description = 'Generic content page';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array
|
||||||
|
*/
|
||||||
|
private static $dependencies = [
|
||||||
|
'creatableChildrenCache' => '%$' . CacheInterface::class . '.SiteTree_CreatableChildren'
|
||||||
|
];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var CacheInterface
|
||||||
|
*/
|
||||||
|
protected $creatableChildrenCache;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetches the {@link SiteTree} object that maps to a link.
|
* Fetches the {@link SiteTree} object that maps to a link.
|
||||||
*
|
*
|
||||||
@ -904,6 +918,25 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param CacheInterface $cache
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setCreatableChildrenCache(CacheInterface $cache)
|
||||||
|
{
|
||||||
|
$this->creatableChildrenCache = $cache;
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return CacheInterface $cache
|
||||||
|
*/
|
||||||
|
public function getCreatableChildrenCache()
|
||||||
|
{
|
||||||
|
return $this->creatableChildrenCache;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a string of the form "parent - page" or "grandparent - parent - page" using page titles
|
* Return a string of the form "parent - page" or "grandparent - parent - page" using page titles
|
||||||
*
|
*
|
||||||
@ -1508,6 +1541,26 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
|||||||
$this->_cache_statusFlags = null;
|
$this->_cache_statusFlags = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flushes the member specific cache for creatable children
|
||||||
|
*
|
||||||
|
* @param array $memberIDs
|
||||||
|
*/
|
||||||
|
public function flushMemberCache($memberIDs = null)
|
||||||
|
{
|
||||||
|
$cache = SiteTree::singleton()->getCreatableChildrenCache();
|
||||||
|
|
||||||
|
if (!$memberIDs) {
|
||||||
|
$cache->clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($memberIDs as $memberID) {
|
||||||
|
$key = $this->generateChildrenCacheKey($memberID);
|
||||||
|
$cache->delete($key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function validate()
|
public function validate()
|
||||||
{
|
{
|
||||||
$result = parent::validate();
|
$result = parent::validate();
|
||||||
@ -2528,6 +2581,32 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
|||||||
return $allowedChildren;
|
return $allowedChildren;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a list of the page types that can be created under this specific page
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function creatableChildren()
|
||||||
|
{
|
||||||
|
// Build the list of candidate children
|
||||||
|
$cache = SiteTree::singleton()->getCreatableChildrenCache();
|
||||||
|
$cacheKey = $this->generateChildrenCacheKey(Security::getCurrentUser() ? Security::getCurrentUser()->ID : 0);
|
||||||
|
$children = $cache->get($cacheKey, []);
|
||||||
|
if (!$children || !isset($children[$this->ID])) {
|
||||||
|
$children[$this->ID] = [];
|
||||||
|
$candidates = static::page_type_classes();
|
||||||
|
foreach ($candidates as $childClass) {
|
||||||
|
$child = singleton($childClass);
|
||||||
|
if ($child->canCreate(null, ['Parent' => $this])) {
|
||||||
|
$children[$this->ID][$childClass] = $child->i18n_singular_name();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$cache->set($cacheKey, $children);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $children[$this->ID];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the class name of the default class for children of this page.
|
* Returns the class name of the default class for children of this page.
|
||||||
*
|
*
|
||||||
@ -2644,18 +2723,7 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
|||||||
*/
|
*/
|
||||||
public function getTreeTitle()
|
public function getTreeTitle()
|
||||||
{
|
{
|
||||||
// Build the list of candidate children
|
$children = $this->creatableChildren();
|
||||||
$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 page-icon class-%s\"></span><span class=\"item\" data-allowedchildren=\"%s\">%s</span>",
|
"<span class=\"jstree-pageicon page-icon class-%s\"></span><span class=\"item\" data-allowedchildren=\"%s\">%s</span>",
|
||||||
@ -2956,6 +3024,15 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the creatableChildren cache on flush
|
||||||
|
*/
|
||||||
|
public static function flush()
|
||||||
|
{
|
||||||
|
Injector::inst()->get(CacheInterface::class . '.SiteTree_CreatableChildren')
|
||||||
|
->clear();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update dependant pages
|
* Update dependant pages
|
||||||
*/
|
*/
|
||||||
@ -2973,4 +3050,15 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cache key for creatableChildren() method
|
||||||
|
*
|
||||||
|
* @param int $memberID
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function generateChildrenCacheKey($memberID)
|
||||||
|
{
|
||||||
|
return md5($memberID . '_' . __CLASS__);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,17 +147,6 @@ class CMSMainTest extends FunctionalTest
|
|||||||
$this->assertEquals(1, $dsCount, "Published page has no duplicate version records: it has " . $dsCount . " for version " . $latestID);
|
$this->assertEquals(1, $dsCount, "Published page has no duplicate version records: it has " . $dsCount . " for version " . $latestID);
|
||||||
|
|
||||||
$this->session()->clear('loggedInAs');
|
$this->session()->clear('loggedInAs');
|
||||||
|
|
||||||
//$this->assertRegexp('/Done: Published 4 pages/', $response->getBody())
|
|
||||||
|
|
||||||
/*
|
|
||||||
$response = Director::test("admin/pages/publishitems", array(
|
|
||||||
'ID' => ''
|
|
||||||
'Title' => ''
|
|
||||||
'action_publish' => 'Save and publish',
|
|
||||||
), $session);
|
|
||||||
$this->assertRegexp('/Done: Published 4 pages/', $response->getBody())
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -577,4 +566,56 @@ class CMSMainTest extends FunctionalTest
|
|||||||
$this->assertEquals(CMSMainTest_ClassB::class, $newPage->ClassName);
|
$this->assertEquals(CMSMainTest_ClassB::class, $newPage->ClassName);
|
||||||
$this->assertEquals('Class A', $newPage->Title);
|
$this->assertEquals('Class A', $newPage->Title);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testSiteTreeHintsCache()
|
||||||
|
{
|
||||||
|
$cms = CMSMain::create();
|
||||||
|
/** @var Member $user */
|
||||||
|
$user = $this->objFromFixture(Member::class, 'rootedituser');
|
||||||
|
Security::setCurrentUser($user);
|
||||||
|
$pageClass = array_values(SiteTree::page_type_classes())[0];
|
||||||
|
$mockPageMissesCache = $this->getMockBuilder($pageClass)
|
||||||
|
->setMethods(['canCreate'])
|
||||||
|
->getMock();
|
||||||
|
$mockPageMissesCache
|
||||||
|
->expects($this->exactly(3))
|
||||||
|
->method('canCreate');
|
||||||
|
|
||||||
|
$mockPageHitsCache = $this->getMockBuilder($pageClass)
|
||||||
|
->setMethods(['canCreate'])
|
||||||
|
->getMock();
|
||||||
|
$mockPageHitsCache
|
||||||
|
->expects($this->never())
|
||||||
|
->method('canCreate');
|
||||||
|
|
||||||
|
|
||||||
|
// Initially, cache misses (1)
|
||||||
|
Injector::inst()->registerService($mockPageMissesCache, $pageClass);
|
||||||
|
$hints = $cms->SiteTreeHints();
|
||||||
|
$this->assertNotNull($hints);
|
||||||
|
|
||||||
|
// Now it hits
|
||||||
|
Injector::inst()->registerService($mockPageHitsCache, $pageClass);
|
||||||
|
$hints = $cms->SiteTreeHints();
|
||||||
|
$this->assertNotNull($hints);
|
||||||
|
|
||||||
|
// Mutating member record invalidates cache. Misses (2)
|
||||||
|
$user->FirstName = 'changed';
|
||||||
|
$user->write();
|
||||||
|
Injector::inst()->registerService($mockPageMissesCache, $pageClass);
|
||||||
|
$hints = $cms->SiteTreeHints();
|
||||||
|
$this->assertNotNull($hints);
|
||||||
|
|
||||||
|
// Now it hits again
|
||||||
|
Injector::inst()->registerService($mockPageHitsCache, $pageClass);
|
||||||
|
$hints = $cms->SiteTreeHints();
|
||||||
|
$this->assertNotNull($hints);
|
||||||
|
|
||||||
|
// Different user. Misses. (3)
|
||||||
|
$user = $this->objFromFixture(Member::class, 'allcmssectionsuser');
|
||||||
|
Security::setCurrentUser($user);
|
||||||
|
Injector::inst()->registerService($mockPageMissesCache, $pageClass);
|
||||||
|
$hints = $cms->SiteTreeHints();
|
||||||
|
$this->assertNotNull($hints);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,6 +26,7 @@ use SilverStripe\Versioned\Versioned;
|
|||||||
use SilverStripe\View\Parsers\Diff;
|
use SilverStripe\View\Parsers\Diff;
|
||||||
use SilverStripe\View\Parsers\ShortcodeParser;
|
use SilverStripe\View\Parsers\ShortcodeParser;
|
||||||
use SilverStripe\View\Parsers\URLSegmentFilter;
|
use SilverStripe\View\Parsers\URLSegmentFilter;
|
||||||
|
use SilverStripe\Core\Injector\Injector;
|
||||||
use LogicException;
|
use LogicException;
|
||||||
|
|
||||||
class SiteTreeTest extends SapphireTest
|
class SiteTreeTest extends SapphireTest
|
||||||
@ -1506,4 +1507,56 @@ class SiteTreeTest extends SapphireTest
|
|||||||
$class = new SiteTreeTest_LegacyControllerName;
|
$class = new SiteTreeTest_LegacyControllerName;
|
||||||
$this->assertEquals(SiteTreeTest_LegacyControllerName_Controller::class, $class->getControllerName());
|
$this->assertEquals(SiteTreeTest_LegacyControllerName_Controller::class, $class->getControllerName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testTreeTitleCache()
|
||||||
|
{
|
||||||
|
$siteTree = SiteTree::create();
|
||||||
|
$user = $this->objFromFixture(Member::class, 'allsections');
|
||||||
|
Security::setCurrentUser($user);
|
||||||
|
$pageClass = array_values(SiteTree::page_type_classes())[0];
|
||||||
|
|
||||||
|
$mockPageMissesCache = $this->getMockBuilder($pageClass)
|
||||||
|
->setMethods(['canCreate'])
|
||||||
|
->getMock();
|
||||||
|
$mockPageMissesCache
|
||||||
|
->expects($this->exactly(3))
|
||||||
|
->method('canCreate');
|
||||||
|
|
||||||
|
$mockPageHitsCache = $this->getMockBuilder($pageClass)
|
||||||
|
->setMethods(['canCreate'])
|
||||||
|
->getMock();
|
||||||
|
$mockPageHitsCache
|
||||||
|
->expects($this->never())
|
||||||
|
->method('canCreate');
|
||||||
|
|
||||||
|
// Initially, cache misses (1)
|
||||||
|
Injector::inst()->registerService($mockPageMissesCache, $pageClass);
|
||||||
|
$title = $siteTree->getTreeTitle();
|
||||||
|
$this->assertNotNull($title);
|
||||||
|
|
||||||
|
// Now it hits
|
||||||
|
Injector::inst()->registerService($mockPageHitsCache, $pageClass);
|
||||||
|
$title = $siteTree->getTreeTitle();
|
||||||
|
$this->assertNotNull($title);
|
||||||
|
|
||||||
|
|
||||||
|
// Mutating member record invalidates cache. Misses (2)
|
||||||
|
$user->FirstName = 'changed';
|
||||||
|
$user->write();
|
||||||
|
Injector::inst()->registerService($mockPageMissesCache, $pageClass);
|
||||||
|
$title = $siteTree->getTreeTitle();
|
||||||
|
$this->assertNotNull($title);
|
||||||
|
|
||||||
|
// Now it hits again
|
||||||
|
Injector::inst()->registerService($mockPageHitsCache, $pageClass);
|
||||||
|
$title = $siteTree->getTreeTitle();
|
||||||
|
$this->assertNotNull($title);
|
||||||
|
|
||||||
|
// Different user. Misses. (3)
|
||||||
|
$user = $this->objFromFixture(Member::class, 'editor');
|
||||||
|
Security::setCurrentUser($user);
|
||||||
|
Injector::inst()->registerService($mockPageMissesCache, $pageClass);
|
||||||
|
$title = $siteTree->getTreeTitle();
|
||||||
|
$this->assertNotNull($title);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user