reformat code and tests

This commit is contained in:
Werner M. Krauß 2017-05-24 15:26:28 +02:00
parent 2295501587
commit c5f507b3f9
30 changed files with 3683 additions and 3282 deletions

View File

@ -13,30 +13,32 @@ use SilverStripe\Subsites\Model\Subsite;
* *
* @package subsites * @package subsites
*/ */
class SubsiteAdmin extends ModelAdmin { class SubsiteAdmin extends ModelAdmin
{
private static $managed_models = array(Subsite::class); private static $managed_models = [Subsite::class];
private static $url_segment = 'subsites'; private static $url_segment = 'subsites';
private static $menu_title = "Subsites"; private static $menu_title = "Subsites";
private static $menu_icon = "subsites/images/subsites.png"; private static $menu_icon = "subsites/images/subsites.png";
public $showImportForm=false; public $showImportForm = false;
private static $tree_class = Subsite::class; private static $tree_class = Subsite::class;
public function getEditForm($id = null, $fields = null) { public function getEditForm($id = null, $fields = null)
$form = parent::getEditForm($id, $fields); {
$form = parent::getEditForm($id, $fields);
$grid=$form->Fields()->dataFieldByName(Subsite::class); $grid = $form->Fields()->dataFieldByName(Subsite::class);
if($grid) { if ($grid) {
$grid->getConfig()->removeComponentsByType('SilverStripe\\Forms\\GridField\\GridFieldDetailForm'); $grid->getConfig()->removeComponentsByType('SilverStripe\\Forms\\GridField\\GridFieldDetailForm');
$grid->getConfig()->addComponent(new GridFieldSubsiteDetailForm()); $grid->getConfig()->addComponent(new GridFieldSubsiteDetailForm());
} }
return $form; return $form;
} }
} }

View File

@ -10,43 +10,54 @@ use SilverStripe\Subsites\Model\Subsite;
/** /**
* Section-agnostic PJAX controller. * Section-agnostic PJAX controller.
*/ */
class SubsiteXHRController extends LeftAndMain { class SubsiteXHRController extends LeftAndMain
{
/** /**
* Relax the access permissions, so anyone who has access to any CMS subsite can access this controller. * Relax the access permissions, so anyone who has access to any CMS subsite can access this controller.
*/ */
public function canView($member = null) { public function canView($member = null)
if (parent::canView()) return true; {
if (parent::canView()) {
return true;
}
if (Subsite::all_accessible_sites()->count()>0) return true; if (Subsite::all_accessible_sites()->count() > 0) {
return true;
}
return false; return false;
} }
/** /**
* Similar as above, but for the LeftAndMainSubsites - allow access if user allowed into the CMS at all. * Similar as above, but for the LeftAndMainSubsites - allow access if user allowed into the CMS at all.
*/ */
public function canAccess() { public function canAccess()
if (Subsite::all_accessible_sites()->count()>0) return true; {
} if (Subsite::all_accessible_sites()->count() > 0) {
return true;
}
}
public function getResponseNegotiator() { public function getResponseNegotiator()
$negotiator = parent::getResponseNegotiator(); {
$self = $this; $negotiator = parent::getResponseNegotiator();
$self = $this;
// Register a new callback // Register a new callback
$negotiator->setCallback('SubsiteList', function() use(&$self) { $negotiator->setCallback('SubsiteList', function () use (&$self) {
return $self->SubsiteList(); return $self->SubsiteList();
}); });
return $negotiator; return $negotiator;
} }
/** /**
* Provide the list of available subsites as a cms-section-agnostic PJAX handler. * Provide the list of available subsites as a cms-section-agnostic PJAX handler.
*/ */
public function SubsiteList() { public function SubsiteList()
return $this->renderWith('Includes/SubsiteList'); {
} return $this->renderWith('Includes/SubsiteList');
}
} }

View File

@ -3,14 +3,16 @@
namespace SilverStripe\Subsites\Extensions; namespace SilverStripe\Subsites\Extensions;
use SilverStripe\Forms\HiddenField;
use SilverStripe\Core\Extension; use SilverStripe\Core\Extension;
use SilverStripe\Forms\HiddenField;
use SilverStripe\Subsites\Model\Subsite; use SilverStripe\Subsites\Model\Subsite;
class CMSPageAddControllerExtension extends Extension { class CMSPageAddControllerExtension extends Extension
{
function updatePageOptions(&$fields) { function updatePageOptions(&$fields)
$fields->push(new HiddenField('SubsiteID', 'SubsiteID', Subsite::currentSubsiteID())); {
} $fields->push(new HiddenField('SubsiteID', 'SubsiteID', Subsite::currentSubsiteID()));
}
} }

View File

@ -3,26 +3,30 @@
namespace SilverStripe\Subsites\Extensions; namespace SilverStripe\Subsites\Extensions;
use SilverStripe\View\SSViewer;
use SilverStripe\Core\Extension; use SilverStripe\Core\Extension;
use SilverStripe\Subsites\Model\Subsite; use SilverStripe\Subsites\Model\Subsite;
use SilverStripe\View\SSViewer;
/** /**
* @package subsites * @package subsites
*/ */
class ControllerSubsites extends Extension { class ControllerSubsites extends Extension
function controllerAugmentInit(){ {
if($subsite = Subsite::currentSubsite()){ function controllerAugmentInit()
if($theme = $subsite->Theme) {
SSViewer::set_theme($theme); if ($subsite = Subsite::currentSubsite()) {
} if ($theme = $subsite->Theme) {
} SSViewer::set_theme($theme);
}
}
}
function CurrentSubsite(){ function CurrentSubsite()
if($subsite = Subsite::currentSubsite()){ {
return $subsite; if ($subsite = Subsite::currentSubsite()) {
} return $subsite;
} }
}
} }
?> ?>

View File

@ -3,44 +3,46 @@
namespace SilverStripe\Subsites\Extensions; namespace SilverStripe\Subsites\Extensions;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\DataExtension; use SilverStripe\ORM\DataExtension;
use SilverStripe\ORM\DataObject;
use SilverStripe\Subsites\Model\Subsite; use SilverStripe\Subsites\Model\Subsite;
class ErrorPageSubsite extends DataExtension { class ErrorPageSubsite extends DataExtension
{
/** /**
* Alter file path to generated a static (static) error page file to handle error page template on different sub-sites * Alter file path to generated a static (static) error page file to handle error page template on different sub-sites
* *
* {@see Error::get_error_filename()} * {@see Error::get_error_filename()}
* *
* FIXME since {@link Subsite::currentSubsite()} partly relies on Session, viewing other sub-site (including main site) between * FIXME since {@link Subsite::currentSubsite()} partly relies on Session, viewing other sub-site (including main site) between
* opening ErrorPage in the CMS and publish ErrorPage causes static error page to get generated incorrectly. * opening ErrorPage in the CMS and publish ErrorPage causes static error page to get generated incorrectly.
* *
* @param string $name Filename to write to * @param string $name Filename to write to
* @param int $statusCode Integer error code * @param int $statusCode Integer error code
*/ */
public function updateErrorFilename(&$name, $statusCode) { public function updateErrorFilename(&$name, $statusCode)
{
// Try to get current subsite from session // Try to get current subsite from session
$subsite = Subsite::currentSubsite(false); $subsite = Subsite::currentSubsite(false);
// since this function is called from Page class before the controller is created, we have to get subsite from domain instead // since this function is called from Page class before the controller is created, we have to get subsite from domain instead
if(!$subsite) { if (!$subsite) {
$subsiteID = Subsite::getSubsiteIDForDomain(); $subsiteID = Subsite::getSubsiteIDForDomain();
if($subsiteID != 0) { if ($subsiteID != 0) {
$subsite = DataObject::get_by_id(Subsite::class, $subsiteID); $subsite = DataObject::get_by_id(Subsite::class, $subsiteID);
} }
} }
// Without subsite, don't rewrite // Without subsite, don't rewrite
if($subsite) { if ($subsite) {
// Add subdomain to end of filename, just before .html // Add subdomain to end of filename, just before .html
// This should preserve translatable locale in the filename as well // This should preserve translatable locale in the filename as well
$subdomain = $subsite->domain(); $subdomain = $subsite->domain();
$name = substr($name, 0, -5) . "-{$subdomain}.html"; $name = substr($name, 0, -5) . "-{$subdomain}.html";
} }
} }
} }

View File

@ -3,15 +3,15 @@
namespace SilverStripe\Subsites\Extensions; namespace SilverStripe\Subsites\Extensions;
use SilverStripe\Forms\FieldList;
use SilverStripe\Assets\Folder; use SilverStripe\Assets\Folder;
use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\LiteralField;
use SilverStripe\ORM\Queries\SQLSelect;
use SilverStripe\ORM\DataQuery;
use SilverStripe\Control\Session; use SilverStripe\Control\Session;
use SilverStripe\Security\Permission; use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\LiteralField;
use SilverStripe\ORM\DataExtension; use SilverStripe\ORM\DataExtension;
use SilverStripe\ORM\DataQuery;
use SilverStripe\ORM\Queries\SQLSelect;
use SilverStripe\Security\Permission;
use SilverStripe\Subsites\Model\Subsite; use SilverStripe\Subsites\Model\Subsite;
@ -20,126 +20,142 @@ use SilverStripe\Subsites\Model\Subsite;
* *
* @package subsites * @package subsites
*/ */
class FileSubsites extends DataExtension { class FileSubsites extends DataExtension
{
// If this is set to true, all folders created will be default be // If this is set to true, all folders created will be default be
// considered 'global', unless set otherwise // considered 'global', unless set otherwise
static $default_root_folders_global = false; static $default_root_folders_global = false;
private static $has_one=array( private static $has_one = [
'Subsite' => Subsite::class, 'Subsite' => Subsite::class,
); ];
/** /**
* Amends the CMS tree title for folders in the Files & Images section. * Amends the CMS tree title for folders in the Files & Images section.
* Prefixes a '* ' to the folders that are accessible from all subsites. * Prefixes a '* ' to the folders that are accessible from all subsites.
*/ */
function alternateTreeTitle() { function alternateTreeTitle()
if($this->owner->SubsiteID == 0) return " * " . $this->owner->Title; {
else return $this->owner->Title; if ($this->owner->SubsiteID == 0) {
} return " * " . $this->owner->Title;
} else {
return $this->owner->Title;
}
}
/** /**
* Add subsites-specific fields to the folder editor. * Add subsites-specific fields to the folder editor.
*/ */
function updateCMSFields(FieldList $fields) { function updateCMSFields(FieldList $fields)
if($this->owner instanceof Folder) { {
$sites = Subsite::accessible_sites('CMS_ACCESS_AssetAdmin'); if ($this->owner instanceof Folder) {
$values = array(); $sites = Subsite::accessible_sites('CMS_ACCESS_AssetAdmin');
$values[0] = _t('FileSubsites.AllSitesDropdownOpt','All sites'); $values = [];
foreach ($sites as $site) { $values[0] = _t('FileSubsites.AllSitesDropdownOpt', 'All sites');
$values[$site->ID] = $site->Title; foreach ($sites as $site) {
} $values[$site->ID] = $site->Title;
ksort($values); }
if($sites){ ksort($values);
//Dropdown needed to move folders between subsites if ($sites) {
$dropdown = new DropdownField( //Dropdown needed to move folders between subsites
'SubsiteID', $dropdown = new DropdownField(
_t('FileSubsites.SubsiteFieldLabel',Subsite::class), 'SubsiteID',
$values _t('FileSubsites.SubsiteFieldLabel', Subsite::class),
); $values
$dropdown->addExtraClass('subsites-move-dropdown'); );
$fields->push($dropdown); $dropdown->addExtraClass('subsites-move-dropdown');
$fields->push(new LiteralField( $fields->push($dropdown);
'Message', $fields->push(new LiteralField(
'<p class="message notice">'. 'Message',
_t('ASSETADMIN.SUBSITENOTICE', 'Folders and files created in the main site are accessible by all subsites.') '<p class="message notice">' .
.'</p>' _t('ASSETADMIN.SUBSITENOTICE',
)); 'Folders and files created in the main site are accessible by all subsites.')
} . '</p>'
} ));
} }
}
}
/** /**
* Update any requests to limit the results to the current site * Update any requests to limit the results to the current site
*/ */
public function augmentSQL(SQLSelect $query, DataQuery $dataQuery = null) { public function augmentSQL(SQLSelect $query, DataQuery $dataQuery = null)
if(Subsite::$disable_subsite_filter) return; {
if (Subsite::$disable_subsite_filter) {
return;
}
// If you're querying by ID, ignore the sub-site - this is a bit ugly... (but it was WAYYYYYYYYY worse) // If you're querying by ID, ignore the sub-site - this is a bit ugly... (but it was WAYYYYYYYYY worse)
//@TODO I don't think excluding if SiteTree_ImageTracking is a good idea however because of the SS 3.0 api and ManyManyList::removeAll() changing the from table after this function is called there isn't much of a choice //@TODO I don't think excluding if SiteTree_ImageTracking is a good idea however because of the SS 3.0 api and ManyManyList::removeAll() changing the from table after this function is called there isn't much of a choice
$from = $query->getFrom(); $from = $query->getFrom();
if(isset($from['SiteTree_ImageTracking']) || $query->filtersOnID()) return; if (isset($from['SiteTree_ImageTracking']) || $query->filtersOnID()) {
return;
}
$subsiteID = (int) Subsite::currentSubsiteID(); $subsiteID = (int)Subsite::currentSubsiteID();
// The foreach is an ugly way of getting the first key :-) // The foreach is an ugly way of getting the first key :-)
foreach($query->getFrom() as $tableName => $info) { foreach ($query->getFrom() as $tableName => $info) {
$where = "\"$tableName\".\"SubsiteID\" IN (0, $subsiteID)"; $where = "\"$tableName\".\"SubsiteID\" IN (0, $subsiteID)";
$query->addWhere($where); $query->addWhere($where);
break; break;
} }
$sect=array_values($query->getSelect()); $sect = array_values($query->getSelect());
$isCounting = strpos($sect[0], 'COUNT') !== false; $isCounting = strpos($sect[0], 'COUNT') !== false;
// Ordering when deleting or counting doesn't apply // Ordering when deleting or counting doesn't apply
if(!$isCounting) { if (!$isCounting) {
$query->addOrderBy("\"SubsiteID\""); $query->addOrderBy("\"SubsiteID\"");
} }
} }
function onBeforeWrite() { function onBeforeWrite()
if (!$this->owner->ID && !$this->owner->SubsiteID) { {
if (self::$default_root_folders_global) { if (!$this->owner->ID && !$this->owner->SubsiteID) {
$this->owner->SubsiteID = 0; if (self::$default_root_folders_global) {
} else { $this->owner->SubsiteID = 0;
$this->owner->SubsiteID = Subsite::currentSubsiteID(); } else {
} $this->owner->SubsiteID = Subsite::currentSubsiteID();
} }
} }
}
function onAfterUpload() { function onAfterUpload()
// If we have a parent, use it's subsite as our subsite {
if ($this->owner->Parent()) { // If we have a parent, use it's subsite as our subsite
$this->owner->SubsiteID = $this->owner->Parent()->SubsiteID; if ($this->owner->Parent()) {
} else { $this->owner->SubsiteID = $this->owner->Parent()->SubsiteID;
$this->owner->SubsiteID = Subsite::currentSubsiteID(); } else {
} $this->owner->SubsiteID = Subsite::currentSubsiteID();
$this->owner->write(); }
} $this->owner->write();
}
function canEdit($member = null) { function canEdit($member = null)
// Check the CMS_ACCESS_SecurityAdmin privileges on the subsite that owns this group {
$subsiteID = Session::get('SubsiteID'); // Check the CMS_ACCESS_SecurityAdmin privileges on the subsite that owns this group
if($subsiteID&&$subsiteID == $this->owner->SubsiteID) { $subsiteID = Session::get('SubsiteID');
return true; if ($subsiteID && $subsiteID == $this->owner->SubsiteID) {
} else { return true;
Session::set('SubsiteID', $this->owner->SubsiteID); } else {
$access = Permission::check(array('CMS_ACCESS_AssetAdmin', 'CMS_ACCESS_LeftAndMain')); Session::set('SubsiteID', $this->owner->SubsiteID);
Session::set('SubsiteID', $subsiteID); $access = Permission::check(['CMS_ACCESS_AssetAdmin', 'CMS_ACCESS_LeftAndMain']);
Session::set('SubsiteID', $subsiteID);
return $access; return $access;
} }
} }
/** /**
* Return a piece of text to keep DataObject cache keys appropriately specific * Return a piece of text to keep DataObject cache keys appropriately specific
*/ */
function cacheKeyComponent() { function cacheKeyComponent()
return 'subsite-'.Subsite::currentSubsiteID(); {
} return 'subsite-' . Subsite::currentSubsiteID();
}
} }

View File

@ -3,19 +3,19 @@
namespace SilverStripe\Subsites\Extensions; namespace SilverStripe\Subsites\Extensions;
use SilverStripe\ORM\DB;
use SilverStripe\Forms\FieldList;
use SilverStripe\Core\Convert;
use SilverStripe\Forms\OptionsetField;
use SilverStripe\Forms\CheckboxSetField;
use SilverStripe\Forms\ReadonlyField;
use SilverStripe\ORM\Queries\SQLSelect;
use SilverStripe\ORM\DataQuery;
use SilverStripe\Control\Cookie; use SilverStripe\Control\Cookie;
use SilverStripe\Core\Convert;
use SilverStripe\Forms\CheckboxSetField;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\OptionsetField;
use SilverStripe\Forms\ReadonlyField;
use SilverStripe\ORM\DataExtension; use SilverStripe\ORM\DataExtension;
use SilverStripe\ORM\DataQuery;
use SilverStripe\ORM\DB;
use SilverStripe\ORM\Queries\SQLSelect;
use SilverStripe\Security\Group;
use SilverStripe\Security\PermissionProvider; use SilverStripe\Security\PermissionProvider;
use SilverStripe\Subsites\Model\Subsite; use SilverStripe\Subsites\Model\Subsite;
use SilverStripe\Security\Group;
/** /**
@ -23,185 +23,206 @@ use SilverStripe\Security\Group;
* *
* @package subsites * @package subsites
*/ */
class GroupSubsites extends DataExtension implements PermissionProvider { class GroupSubsites extends DataExtension implements PermissionProvider
{
private static $db = array( private static $db = [
'AccessAllSubsites' => 'Boolean' 'AccessAllSubsites' => 'Boolean'
); ];
private static $many_many = array( private static $many_many = [
'Subsites' => Subsite::class 'Subsites' => Subsite::class
); ];
private static $defaults = array( private static $defaults = [
'AccessAllSubsites' => true 'AccessAllSubsites' => true
); ];
/** /**
* Migrations for GroupSubsites data. * Migrations for GroupSubsites data.
*/ */
function requireDefaultRecords() { function requireDefaultRecords()
// Migration for Group.SubsiteID data from when Groups only had a single subsite {
// Migration for Group.SubsiteID data from when Groups only had a single subsite
$schema = $this->owner->getSchema(); $schema = $this->owner->getSchema();
$groupFields = DB::field_list($schema->tableName(Group::class)); $groupFields = DB::field_list($schema->tableName(Group::class));
// Detection of SubsiteID field is the trigger for old-style-subsiteID migration // Detection of SubsiteID field is the trigger for old-style-subsiteID migration
if(isset($groupFields['SubsiteID'])) { if (isset($groupFields['SubsiteID'])) {
// Migrate subsite-specific data // Migrate subsite-specific data
DB::query('INSERT INTO "Group_Subsites" ("GroupID", "SubsiteID") DB::query('INSERT INTO "Group_Subsites" ("GroupID", "SubsiteID")
SELECT "ID", "SubsiteID" FROM "Group" WHERE "SubsiteID" > 0'); SELECT "ID", "SubsiteID" FROM "Group" WHERE "SubsiteID" > 0');
// Migrate global-access data // Migrate global-access data
DB::query('UPDATE "Group" SET "AccessAllSubsites" = 1 WHERE "SubsiteID" = 0'); DB::query('UPDATE "Group" SET "AccessAllSubsites" = 1 WHERE "SubsiteID" = 0');
// Move the field out of the way so that this migration doesn't get executed again // Move the field out of the way so that this migration doesn't get executed again
DB::get_schema()->renameField(Group::class, 'SubsiteID', '_obsolete_SubsiteID'); DB::get_schema()->renameField(Group::class, 'SubsiteID', '_obsolete_SubsiteID');
// No subsite access on anything means that we've just installed the subsites module. // No subsite access on anything means that we've just installed the subsites module.
// Make all previous groups global-access groups // Make all previous groups global-access groups
} else if(!DB::query('SELECT "Group"."ID" FROM "Group" } else {
if (!DB::query('SELECT "Group"."ID" FROM "Group"
LEFT JOIN "Group_Subsites" ON "Group_Subsites"."GroupID" = "Group"."ID" AND "Group_Subsites"."SubsiteID" > 0 LEFT JOIN "Group_Subsites" ON "Group_Subsites"."GroupID" = "Group"."ID" AND "Group_Subsites"."SubsiteID" > 0
WHERE "AccessAllSubsites" = 1 WHERE "AccessAllSubsites" = 1
OR "Group_Subsites"."GroupID" IS NOT NULL ')->value()) { OR "Group_Subsites"."GroupID" IS NOT NULL ')->value()
) {
DB::query('UPDATE "Group" SET "AccessAllSubsites" = 1'); DB::query('UPDATE "Group" SET "AccessAllSubsites" = 1');
} }
} }
}
function updateCMSFields(FieldList $fields) { function updateCMSFields(FieldList $fields)
if($this->owner->canEdit() ){ {
// i18n tab if ($this->owner->canEdit()) {
$fields->findOrMakeTab('Root.Subsites',_t('GroupSubsites.SECURITYTABTITLE','Subsites')); // i18n tab
$fields->findOrMakeTab('Root.Subsites', _t('GroupSubsites.SECURITYTABTITLE', 'Subsites'));
$subsites = Subsite::accessible_sites(array('ADMIN', 'SECURITY_SUBSITE_GROUP'), true); $subsites = Subsite::accessible_sites(['ADMIN', 'SECURITY_SUBSITE_GROUP'], true);
$subsiteMap = $subsites->map(); $subsiteMap = $subsites->map();
// Prevent XSS injection // Prevent XSS injection
$subsiteMap = Convert::raw2xml($subsiteMap); $subsiteMap = Convert::raw2xml($subsiteMap);
// Interface is different if you have the rights to modify subsite group values on // Interface is different if you have the rights to modify subsite group values on
// all subsites // all subsites
if(isset($subsiteMap[0])) { if (isset($subsiteMap[0])) {
$fields->addFieldToTab("Root.Subsites", new OptionsetField("AccessAllSubsites", $fields->addFieldToTab("Root.Subsites", new OptionsetField("AccessAllSubsites",
_t('GroupSubsites.ACCESSRADIOTITLE', 'Give this group access to'), _t('GroupSubsites.ACCESSRADIOTITLE', 'Give this group access to'),
array( [
1 => _t('GroupSubsites.ACCESSALL', "All subsites"), 1 => _t('GroupSubsites.ACCESSALL', "All subsites"),
0 => _t('GroupSubsites.ACCESSONLY', "Only these subsites"), 0 => _t('GroupSubsites.ACCESSONLY', "Only these subsites"),
) ]
)); ));
unset($subsiteMap[0]); unset($subsiteMap[0]);
$fields->addFieldToTab("Root.Subsites", new CheckboxSetField("Subsites", "", $fields->addFieldToTab("Root.Subsites", new CheckboxSetField("Subsites", "",
$subsiteMap)); $subsiteMap));
} else { } else {
if (sizeof($subsiteMap) <= 1) { if (sizeof($subsiteMap) <= 1) {
$fields->addFieldToTab("Root.Subsites", new ReadonlyField("SubsitesHuman", $fields->addFieldToTab("Root.Subsites", new ReadonlyField("SubsitesHuman",
_t('GroupSubsites.ACCESSRADIOTITLE', 'Give this group access to'), _t('GroupSubsites.ACCESSRADIOTITLE', 'Give this group access to'),
reset($subsiteMap))); reset($subsiteMap)));
} else { } else {
$fields->addFieldToTab("Root.Subsites", new CheckboxSetField("Subsites", $fields->addFieldToTab("Root.Subsites", new CheckboxSetField("Subsites",
_t('GroupSubsites.ACCESSRADIOTITLE', 'Give this group access to'), _t('GroupSubsites.ACCESSRADIOTITLE', 'Give this group access to'),
$subsiteMap)); $subsiteMap));
} }
} }
} }
} }
/** /**
* If this group belongs to a subsite, * If this group belongs to a subsite,
* append the subsites title to the group title * append the subsites title to the group title
* to make it easy to distinguish in the tree-view * to make it easy to distinguish in the tree-view
* of the security admin interface. * of the security admin interface.
*/ */
function alternateTreeTitle() { function alternateTreeTitle()
if($this->owner->AccessAllSubsites) { {
$title = _t('GroupSubsites.GlobalGroup', 'global group'); if ($this->owner->AccessAllSubsites) {
return htmlspecialchars($this->owner->Title, ENT_QUOTES) . ' <i>(' . $title . ')</i>'; $title = _t('GroupSubsites.GlobalGroup', 'global group');
} else { return htmlspecialchars($this->owner->Title, ENT_QUOTES) . ' <i>(' . $title . ')</i>';
$subsites = Convert::raw2xml(implode(", ", $this->owner->Subsites()->column('Title'))); } else {
return htmlspecialchars($this->owner->Title) . " <i>($subsites)</i>"; $subsites = Convert::raw2xml(implode(", ", $this->owner->Subsites()->column('Title')));
} return htmlspecialchars($this->owner->Title) . " <i>($subsites)</i>";
} }
}
/** /**
* Update any requests to limit the results to the current site * Update any requests to limit the results to the current site
*/ */
public function augmentSQL(SQLSelect $query, DataQuery $dataQuery = null) { public function augmentSQL(SQLSelect $query, DataQuery $dataQuery = null)
if(Subsite::$disable_subsite_filter) return; {
if(Cookie::get('noSubsiteFilter') == 'true') return; if (Subsite::$disable_subsite_filter) {
return;
}
if (Cookie::get('noSubsiteFilter') == 'true') {
return;
}
// If you're querying by ID, ignore the sub-site - this is a bit ugly... // If you're querying by ID, ignore the sub-site - this is a bit ugly...
if(!$query->filtersOnID()) { if (!$query->filtersOnID()) {
/*if($context = DataObject::context_obj()) $subsiteID = (int)$context->SubsiteID; /*if($context = DataObject::context_obj()) $subsiteID = (int)$context->SubsiteID;
else */$subsiteID = (int)Subsite::currentSubsiteID(); else */
$subsiteID = (int)Subsite::currentSubsiteID();
// Don't filter by Group_Subsites if we've already done that // Don't filter by Group_Subsites if we've already done that
$hasGroupSubsites = false; $hasGroupSubsites = false;
foreach($query->getFrom() as $item) { foreach ($query->getFrom() as $item) {
if((is_array($item) && strpos($item['table'], 'Group_Subsites')!==false) || (!is_array($item) && strpos($item, 'Group_Subsites')!==false)) { if ((is_array($item) && strpos($item['table'],
$hasGroupSubsites = true; 'Group_Subsites') !== false) || (!is_array($item) && strpos($item,
break; 'Group_Subsites') !== false)
} ) {
} $hasGroupSubsites = true;
break;
}
}
if(!$hasGroupSubsites) { if (!$hasGroupSubsites) {
if($subsiteID) { if ($subsiteID) {
$query->addLeftJoin("Group_Subsites", "\"Group_Subsites\".\"GroupID\" $query->addLeftJoin("Group_Subsites", "\"Group_Subsites\".\"GroupID\"
= \"Group\".\"ID\" AND \"Group_Subsites\".\"SubsiteID\" = $subsiteID"); = \"Group\".\"ID\" AND \"Group_Subsites\".\"SubsiteID\" = $subsiteID");
$query->addWhere("(\"Group_Subsites\".\"SubsiteID\" IS NOT NULL OR $query->addWhere("(\"Group_Subsites\".\"SubsiteID\" IS NOT NULL OR
\"Group\".\"AccessAllSubsites\" = 1)"); \"Group\".\"AccessAllSubsites\" = 1)");
} else { } else {
$query->addWhere("\"Group\".\"AccessAllSubsites\" = 1"); $query->addWhere("\"Group\".\"AccessAllSubsites\" = 1");
} }
} }
// WORKAROUND for databases that complain about an ORDER BY when the column wasn't selected (e.g. SQL Server) // WORKAROUND for databases that complain about an ORDER BY when the column wasn't selected (e.g. SQL Server)
$select=$query->getSelect(); $select = $query->getSelect();
if(isset($select[0]) && !$select[0] == 'COUNT(*)') { if (isset($select[0]) && !$select[0] == 'COUNT(*)') {
$query->orderby = "\"AccessAllSubsites\" DESC" . ($query->orderby ? ', ' : '') . $query->orderby; $query->orderby = "\"AccessAllSubsites\" DESC" . ($query->orderby ? ', ' : '') . $query->orderby;
} }
} }
} }
function onBeforeWrite() { function onBeforeWrite()
// New record test approximated by checking whether the ID has changed. {
// Note also that the after write test is only used when we're *not* on a subsite // New record test approximated by checking whether the ID has changed.
if($this->owner->isChanged('ID') && !Subsite::currentSubsiteID()) { // Note also that the after write test is only used when we're *not* on a subsite
$this->owner->AccessAllSubsites = 1; if ($this->owner->isChanged('ID') && !Subsite::currentSubsiteID()) {
} $this->owner->AccessAllSubsites = 1;
} }
}
function onAfterWrite() { function onAfterWrite()
// New record test approximated by checking whether the ID has changed. {
// Note also that the after write test is only used when we're on a subsite // New record test approximated by checking whether the ID has changed.
if($this->owner->isChanged('ID') && $currentSubsiteID = Subsite::currentSubsiteID()) { // Note also that the after write test is only used when we're on a subsite
$subsites = $this->owner->Subsites(); if ($this->owner->isChanged('ID') && $currentSubsiteID = Subsite::currentSubsiteID()) {
$subsites->add($currentSubsiteID); $subsites = $this->owner->Subsites();
} $subsites->add($currentSubsiteID);
} }
}
function alternateCanEdit() { function alternateCanEdit()
// Find the sites that this group belongs to and the sites where we have appropriate perm. {
$accessibleSites = Subsite::accessible_sites('CMS_ACCESS_SecurityAdmin')->column('ID'); // Find the sites that this group belongs to and the sites where we have appropriate perm.
$linkedSites = $this->owner->Subsites()->column('ID'); $accessibleSites = Subsite::accessible_sites('CMS_ACCESS_SecurityAdmin')->column('ID');
$linkedSites = $this->owner->Subsites()->column('ID');
// We are allowed to access this site if at we have CMS_ACCESS_SecurityAdmin permission on // We are allowed to access this site if at we have CMS_ACCESS_SecurityAdmin permission on
// at least one of the sites // at least one of the sites
return (bool)array_intersect($accessibleSites, $linkedSites); return (bool)array_intersect($accessibleSites, $linkedSites);
} }
function providePermissions() { function providePermissions()
return array( {
'SECURITY_SUBSITE_GROUP' => array( return [
'name' => _t('GroupSubsites.MANAGE_SUBSITES', 'Manage subsites for groups'), 'SECURITY_SUBSITE_GROUP' => [
'category' => _t('Permissions.PERMISSIONS_CATEGORY', 'Roles and access permissions'), 'name' => _t('GroupSubsites.MANAGE_SUBSITES', 'Manage subsites for groups'),
'help' => _t('GroupSubsites.MANAGE_SUBSITES_HELP', 'Ability to limit the permissions for a group to one or more subsites.'), 'category' => _t('Permissions.PERMISSIONS_CATEGORY', 'Roles and access permissions'),
'sort' => 200 'help' => _t('GroupSubsites.MANAGE_SUBSITES_HELP',
) 'Ability to limit the permissions for a group to one or more subsites.'),
); 'sort' => 200
} ]
];
}
} }

View File

@ -3,21 +3,21 @@
namespace SilverStripe\Subsites\Extensions; namespace SilverStripe\Subsites\Extensions;
use SilverStripe\View\Requirements; use SilverStripe\Admin\CMSMenu;
use SilverStripe\Control\Controller;
use SilverStripe\Control\Session;
use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Convert; use SilverStripe\Core\Convert;
use SilverStripe\Core\Extension;
use SilverStripe\Forms\HiddenField; use SilverStripe\Forms\HiddenField;
use SilverStripe\Security\Member;
use SilverStripe\ORM\ArrayList; use SilverStripe\ORM\ArrayList;
use SilverStripe\ORM\DataObject; use SilverStripe\ORM\DataObject;
use SilverStripe\Core\Config\Config; use SilverStripe\Security\Member;
use SilverStripe\View\ArrayData;
use SilverStripe\Security\Permission; use SilverStripe\Security\Permission;
use SilverStripe\Control\Session;
use SilverStripe\Admin\CMSMenu;
use SilverStripe\Security\Security; use SilverStripe\Security\Security;
use SilverStripe\Control\Controller;
use SilverStripe\Core\Extension;
use SilverStripe\Subsites\Model\Subsite; use SilverStripe\Subsites\Model\Subsite;
use SilverStripe\View\ArrayData;
use SilverStripe\View\Requirements;
/** /**
@ -25,303 +25,335 @@ use SilverStripe\Subsites\Model\Subsite;
* *
* @package subsites * @package subsites
*/ */
class LeftAndMainSubsites extends Extension { class LeftAndMainSubsites extends Extension
{
private static $allowed_actions = array('CopyToSubsite'); private static $allowed_actions = ['CopyToSubsite'];
/** /**
* Normally SubsiteID=0 on a DataObject means it is only accessible from the special "main site". * Normally SubsiteID=0 on a DataObject means it is only accessible from the special "main site".
* However in some situations SubsiteID=0 will be understood as a "globally accessible" object in which * However in some situations SubsiteID=0 will be understood as a "globally accessible" object in which
* case this property is set to true (i.e. in AssetAdmin). * case this property is set to true (i.e. in AssetAdmin).
*/ */
private static $treats_subsite_0_as_global = false; private static $treats_subsite_0_as_global = false;
function init() { function init()
Requirements::css('subsites/css/LeftAndMain_Subsites.css'); {
Requirements::javascript('subsites/javascript/LeftAndMain_Subsites.js'); Requirements::css('subsites/css/LeftAndMain_Subsites.css');
Requirements::javascript('subsites/javascript/VirtualPage_Subsites.js'); Requirements::javascript('subsites/javascript/LeftAndMain_Subsites.js');
} Requirements::javascript('subsites/javascript/VirtualPage_Subsites.js');
}
/** /**
* Set the title of the CMS tree * Set the title of the CMS tree
*/ */
function getCMSTreeTitle() { function getCMSTreeTitle()
$subsite = Subsite::currentSubSite(); {
return $subsite ? Convert::raw2xml($subsite->Title) : _t('LeftAndMain.SITECONTENTLEFT'); $subsite = Subsite::currentSubSite();
} return $subsite ? Convert::raw2xml($subsite->Title) : _t('LeftAndMain.SITECONTENTLEFT');
}
function updatePageOptions(&$fields) { function updatePageOptions(&$fields)
$fields->push(new HiddenField('SubsiteID', 'SubsiteID', Subsite::currentSubsiteID())); {
} $fields->push(new HiddenField('SubsiteID', 'SubsiteID', Subsite::currentSubsiteID()));
}
/** /**
* Find all subsites accessible for current user on this controller. * Find all subsites accessible for current user on this controller.
* *
* @return ArrayList of {@link Subsite} instances. * @return ArrayList of {@link Subsite} instances.
*/ */
function sectionSites($includeMainSite = true, $mainSiteTitle = "Main site", $member = null) { function sectionSites($includeMainSite = true, $mainSiteTitle = "Main site", $member = null)
if($mainSiteTitle == 'Main site') { {
$mainSiteTitle = _t('Subsites.MainSiteTitle', 'Main site'); if ($mainSiteTitle == 'Main site') {
} $mainSiteTitle = _t('Subsites.MainSiteTitle', 'Main site');
}
// Rationalise member arguments // Rationalise member arguments
if(!$member) $member = Member::currentUser(); if (!$member) {
if(!$member) return new ArrayList(); $member = Member::currentUser();
if(!is_object($member)) $member = DataObject::get_by_id('SilverStripe\\Security\\Member', $member); }
if (!$member) {
return new ArrayList();
}
if (!is_object($member)) {
$member = DataObject::get_by_id('SilverStripe\\Security\\Member', $member);
}
// Collect permissions - honour the LeftAndMain::required_permission_codes, current model requires // Collect permissions - honour the LeftAndMain::required_permission_codes, current model requires
// us to check if the user satisfies ALL permissions. Code partly copied from LeftAndMain::canView. // us to check if the user satisfies ALL permissions. Code partly copied from LeftAndMain::canView.
$codes = array(); $codes = [];
$extraCodes = Config::inst()->get($this->owner->class, 'required_permission_codes'); $extraCodes = Config::inst()->get($this->owner->class, 'required_permission_codes');
if($extraCodes !== false) { if ($extraCodes !== false) {
if($extraCodes) $codes = array_merge($codes, (array)$extraCodes); if ($extraCodes) {
else $codes[] = "CMS_ACCESS_{$this->owner->class}"; $codes = array_merge($codes, (array)$extraCodes);
} else { } else {
// Check overriden - all subsites accessible. $codes[] = "CMS_ACCESS_{$this->owner->class}";
return Subsite::all_sites(); }
} } else {
// Check overriden - all subsites accessible.
return Subsite::all_sites();
}
// Find subsites satisfying all permissions for the Member. // Find subsites satisfying all permissions for the Member.
$codesPerSite = array(); $codesPerSite = [];
$sitesArray = array(); $sitesArray = [];
foreach ($codes as $code) { foreach ($codes as $code) {
$sites = Subsite::accessible_sites($code, $includeMainSite, $mainSiteTitle, $member); $sites = Subsite::accessible_sites($code, $includeMainSite, $mainSiteTitle, $member);
foreach ($sites as $site) { foreach ($sites as $site) {
// Build the structure for checking how many codes match. // Build the structure for checking how many codes match.
$codesPerSite[$site->ID][$code] = true; $codesPerSite[$site->ID][$code] = true;
// Retain Subsite objects for later. // Retain Subsite objects for later.
$sitesArray[$site->ID] = $site; $sitesArray[$site->ID] = $site;
} }
} }
// Find sites that satisfy all codes conjuncitvely. // Find sites that satisfy all codes conjuncitvely.
$accessibleSites = new ArrayList(); $accessibleSites = new ArrayList();
foreach ($codesPerSite as $siteID => $siteCodes) { foreach ($codesPerSite as $siteID => $siteCodes) {
if (count($siteCodes)==count($codes)) { if (count($siteCodes) == count($codes)) {
$accessibleSites->push($sitesArray[$siteID]); $accessibleSites->push($sitesArray[$siteID]);
} }
} }
return $accessibleSites; return $accessibleSites;
} }
/* /*
* Returns a list of the subsites accessible to the current user. * Returns a list of the subsites accessible to the current user.
* It's enough for any section to be accessible for the section to be included. * It's enough for any section to be accessible for the section to be included.
*/ */
public function Subsites() { public function Subsites()
return Subsite::all_accessible_sites(); {
} return Subsite::all_accessible_sites();
}
/* /*
* Generates a list of subsites with the data needed to * Generates a list of subsites with the data needed to
* produce a dropdown site switcher * produce a dropdown site switcher
* @return ArrayList * @return ArrayList
*/ */
public function ListSubsites(){ public function ListSubsites()
$list = $this->Subsites(); {
$currentSubsiteID = Subsite::currentSubsiteID(); $list = $this->Subsites();
$currentSubsiteID = Subsite::currentSubsiteID();
if($list == null || $list->Count() == 1 && $list->First()->DefaultSite == true){ if ($list == null || $list->Count() == 1 && $list->First()->DefaultSite == true) {
return false; return false;
} }
Requirements::javascript('subsites/javascript/LeftAndMain_Subsites.js'); Requirements::javascript('subsites/javascript/LeftAndMain_Subsites.js');
$output = new ArrayList(); $output = new ArrayList();
foreach($list as $subsite) { foreach ($list as $subsite) {
$CurrentState = $subsite->ID == $currentSubsiteID ? 'selected' : ''; $CurrentState = $subsite->ID == $currentSubsiteID ? 'selected' : '';
$output->push(new ArrayData(array( $output->push(new ArrayData([
'CurrentState' => $CurrentState, 'CurrentState' => $CurrentState,
'ID' => $subsite->ID, 'ID' => $subsite->ID,
'Title' => Convert::raw2xml($subsite->Title) 'Title' => Convert::raw2xml($subsite->Title)
))); ]));
} }
return $output; return $output;
} }
public function alternateMenuDisplayCheck($controllerName) { public function alternateMenuDisplayCheck($controllerName)
if(!class_exists($controllerName)){ {
return false; if (!class_exists($controllerName)) {
} return false;
}
// Check subsite support. // Check subsite support.
if(Subsite::currentSubsiteID() == 0){ if (Subsite::currentSubsiteID() == 0) {
// Main site always supports everything. // Main site always supports everything.
return true; return true;
} else { } else {
$controller = singleton($controllerName); $controller = singleton($controllerName);
if($controller->hasMethod('subsiteCMSShowInMenu') && $controller->subsiteCMSShowInMenu()){ if ($controller->hasMethod('subsiteCMSShowInMenu') && $controller->subsiteCMSShowInMenu()) {
return true; return true;
} }
} }
// It's not necessary to check access permissions here. Framework calls canView on the controller, // It's not necessary to check access permissions here. Framework calls canView on the controller,
// which in turn uses the Permission API which is augmented by our GroupSubsites. // which in turn uses the Permission API which is augmented by our GroupSubsites.
return false; return false;
} }
public function CanAddSubsites() { public function CanAddSubsites()
return Permission::check("ADMIN", "any", null, "all"); {
} return Permission::check("ADMIN", "any", null, "all");
}
/** /**
* Helper for testing if the subsite should be adjusted. * Helper for testing if the subsite should be adjusted.
*/ */
public function shouldChangeSubsite($adminClass, $recordSubsiteID, $currentSubsiteID) { public function shouldChangeSubsite($adminClass, $recordSubsiteID, $currentSubsiteID)
if (Config::inst()->get($adminClass, 'treats_subsite_0_as_global') && $recordSubsiteID==0) return false; {
if ($recordSubsiteID!=$currentSubsiteID) return true; if (Config::inst()->get($adminClass, 'treats_subsite_0_as_global') && $recordSubsiteID == 0) {
return false; return false;
} }
if ($recordSubsiteID != $currentSubsiteID) {
return true;
}
return false;
}
/** /**
* Check if the current controller is accessible for this user on this subsite. * Check if the current controller is accessible for this user on this subsite.
*/ */
function canAccess() { function canAccess()
// Admin can access everything, no point in checking. {
$member = Member::currentUser(); // Admin can access everything, no point in checking.
if($member && $member = Member::currentUser();
( if ($member &&
Permission::checkMember($member, 'ADMIN') || // 'Full administrative rights' in SecurityAdmin (
Permission::checkMember($member, 'CMS_ACCESS_LeftAndMain') // 'Access to all CMS sections' in SecurityAdmin Permission::checkMember($member, 'ADMIN') || // 'Full administrative rights' in SecurityAdmin
)) { Permission::checkMember($member,
return true; 'CMS_ACCESS_LeftAndMain') // 'Access to all CMS sections' in SecurityAdmin
} )
) {
return true;
}
// Check if we have access to current section on the current subsite. // Check if we have access to current section on the current subsite.
$accessibleSites = $this->owner->sectionSites(true, "Main site", $member); $accessibleSites = $this->owner->sectionSites(true, "Main site", $member);
if ($accessibleSites->count() && $accessibleSites->find('ID', Subsite::currentSubsiteID())) { if ($accessibleSites->count() && $accessibleSites->find('ID', Subsite::currentSubsiteID())) {
// Current section can be accessed on the current site, all good. // Current section can be accessed on the current site, all good.
return true; return true;
} }
return false; return false;
} }
/** /**
* Prevent accessing disallowed resources. This happens after onBeforeInit has executed, * Prevent accessing disallowed resources. This happens after onBeforeInit has executed,
* so all redirections should've already taken place. * so all redirections should've already taken place.
*/ */
public function alternateAccessCheck() { public function alternateAccessCheck()
return $this->owner->canAccess(); {
} return $this->owner->canAccess();
}
/** /**
* Redirect the user to something accessible if the current section/subsite is forbidden. * Redirect the user to something accessible if the current section/subsite is forbidden.
* *
* This is done via onBeforeInit as it needs to be done before the LeftAndMain::init has a * This is done via onBeforeInit as it needs to be done before the LeftAndMain::init has a
* chance to forbids access via alternateAccessCheck. * chance to forbids access via alternateAccessCheck.
* *
* If we need to change the subsite we force the redirection to /admin/ so the frontend is * If we need to change the subsite we force the redirection to /admin/ so the frontend is
* fully re-synchronised with the internal session. This is better than risking some panels * fully re-synchronised with the internal session. This is better than risking some panels
* showing data from another subsite. * showing data from another subsite.
*/ */
public function onBeforeInit() { public function onBeforeInit()
// We are accessing the CMS, so we need to let Subsites know we will be using the session. {
Subsite::$use_session_subsiteid = true; // We are accessing the CMS, so we need to let Subsites know we will be using the session.
Subsite::$use_session_subsiteid = true;
// FIRST, check if we need to change subsites due to the URL. // FIRST, check if we need to change subsites due to the URL.
// Catch forced subsite changes that need to cause CMS reloads. // Catch forced subsite changes that need to cause CMS reloads.
if(isset($_GET['SubsiteID'])) { if (isset($_GET['SubsiteID'])) {
// Clear current page when subsite changes (or is set for the first time) // Clear current page when subsite changes (or is set for the first time)
if(!Session::get('SubsiteID') || $_GET['SubsiteID'] != Session::get('SubsiteID')) { if (!Session::get('SubsiteID') || $_GET['SubsiteID'] != Session::get('SubsiteID')) {
Session::clear("{$this->owner->class}.currentPage"); Session::clear("{$this->owner->class}.currentPage");
} }
// Update current subsite in session // Update current subsite in session
Subsite::changeSubsite($_GET['SubsiteID']); Subsite::changeSubsite($_GET['SubsiteID']);
//Redirect to clear the current page //Redirect to clear the current page
if ($this->owner->canView(Member::currentUser())) { if ($this->owner->canView(Member::currentUser())) {
//Redirect to clear the current page //Redirect to clear the current page
return $this->owner->redirect($this->owner->Link()); return $this->owner->redirect($this->owner->Link());
} }
//Redirect to the default CMS section //Redirect to the default CMS section
return $this->owner->redirect('admin/'); return $this->owner->redirect('admin/');
} }
// Automatically redirect the session to appropriate subsite when requesting a record. // Automatically redirect the session to appropriate subsite when requesting a record.
// This is needed to properly initialise the session in situations where someone opens the CMS via a link. // This is needed to properly initialise the session in situations where someone opens the CMS via a link.
$record = $this->owner->currentPage(); $record = $this->owner->currentPage();
if($record && isset($record->SubsiteID) && is_numeric($record->SubsiteID) && isset($this->owner->urlParams['ID'])) { if ($record && isset($record->SubsiteID) && is_numeric($record->SubsiteID) && isset($this->owner->urlParams['ID'])) {
if ($this->shouldChangeSubsite($this->owner->class, $record->SubsiteID, Subsite::currentSubsiteID())) { if ($this->shouldChangeSubsite($this->owner->class, $record->SubsiteID, Subsite::currentSubsiteID())) {
// Update current subsite in session // Update current subsite in session
Subsite::changeSubsite($record->SubsiteID); Subsite::changeSubsite($record->SubsiteID);
if ($this->owner->canView(Member::currentUser())) { if ($this->owner->canView(Member::currentUser())) {
//Redirect to clear the current page //Redirect to clear the current page
return $this->owner->redirect($this->owner->Link()); return $this->owner->redirect($this->owner->Link());
} }
//Redirect to the default CMS section //Redirect to the default CMS section
return $this->owner->redirect('admin/'); return $this->owner->redirect('admin/');
} }
} }
// SECOND, check if we need to change subsites due to lack of permissions. // SECOND, check if we need to change subsites due to lack of permissions.
if (!$this->owner->canAccess()) { if (!$this->owner->canAccess()) {
$member = Member::currentUser(); $member = Member::currentUser();
// Current section is not accessible, try at least to stick to the same subsite. // Current section is not accessible, try at least to stick to the same subsite.
$menu = CMSMenu::get_menu_items(); $menu = CMSMenu::get_menu_items();
foreach($menu as $candidate) { foreach ($menu as $candidate) {
if($candidate->controller && $candidate->controller!=$this->owner->class) { if ($candidate->controller && $candidate->controller != $this->owner->class) {
$accessibleSites = singleton($candidate->controller)->sectionSites(true, 'Main site', $member); $accessibleSites = singleton($candidate->controller)->sectionSites(true, 'Main site', $member);
if ($accessibleSites->count() && $accessibleSites->find('ID', Subsite::currentSubsiteID())) { if ($accessibleSites->count() && $accessibleSites->find('ID', Subsite::currentSubsiteID())) {
// Section is accessible, redirect there. // Section is accessible, redirect there.
return $this->owner->redirect(singleton($candidate->controller)->Link()); return $this->owner->redirect(singleton($candidate->controller)->Link());
} }
} }
} }
// If no section is available, look for other accessible subsites. // If no section is available, look for other accessible subsites.
foreach($menu as $candidate) { foreach ($menu as $candidate) {
if($candidate->controller) { if ($candidate->controller) {
$accessibleSites = singleton($candidate->controller)->sectionSites(true, 'Main site', $member); $accessibleSites = singleton($candidate->controller)->sectionSites(true, 'Main site', $member);
if ($accessibleSites->count()) { if ($accessibleSites->count()) {
Subsite::changeSubsite($accessibleSites->First()->ID); Subsite::changeSubsite($accessibleSites->First()->ID);
return $this->owner->redirect(singleton($candidate->controller)->Link()); return $this->owner->redirect(singleton($candidate->controller)->Link());
} }
} }
} }
// We have not found any accessible section or subsite. User should be denied access. // We have not found any accessible section or subsite. User should be denied access.
return Security::permissionFailure($this->owner); return Security::permissionFailure($this->owner);
} }
// Current site is accessible. Allow through. // Current site is accessible. Allow through.
return; return;
} }
function augmentNewSiteTreeItem(&$item) { function augmentNewSiteTreeItem(&$item)
$item->SubsiteID = isset($_POST['SubsiteID']) ? $_POST['SubsiteID'] : Subsite::currentSubsiteID(); {
} $item->SubsiteID = isset($_POST['SubsiteID']) ? $_POST['SubsiteID'] : Subsite::currentSubsiteID();
}
function onAfterSave($record) { function onAfterSave($record)
if($record->hasMethod('NormalRelated') && ($record->NormalRelated() || $record->ReverseRelated())) { {
$this->owner->response->addHeader('X-Status', rawurlencode(_t('LeftAndMainSubsites.Saved', 'Saved, please update related pages.'))); if ($record->hasMethod('NormalRelated') && ($record->NormalRelated() || $record->ReverseRelated())) {
} $this->owner->response->addHeader('X-Status',
} rawurlencode(_t('LeftAndMainSubsites.Saved', 'Saved, please update related pages.')));
}
}
function copytosubsite($data, $form) { function copytosubsite($data, $form)
$page = DataObject::get_by_id('SilverStripe\\CMS\\Model\\SiteTree', $data['ID']); {
$subsite = DataObject::get_by_id(Subsite::class, $data['CopyToSubsiteID']); $page = DataObject::get_by_id('SilverStripe\\CMS\\Model\\SiteTree', $data['ID']);
$newPage = $page->duplicateToSubsite($subsite->ID, true); $subsite = DataObject::get_by_id(Subsite::class, $data['CopyToSubsiteID']);
$response = $this->owner->getResponse(); $newPage = $page->duplicateToSubsite($subsite->ID, true);
$response->addHeader('X-Reload', true); $response = $this->owner->getResponse();
return $this->owner->redirect(Controller::join_links($this->owner->Link('show'), $newPage->ID)); $response->addHeader('X-Reload', true);
} return $this->owner->redirect(Controller::join_links($this->owner->Link('show'), $newPage->ID));
}
} }

View File

@ -3,60 +3,75 @@
namespace SilverStripe\Subsites\Extensions; namespace SilverStripe\Subsites\Extensions;
use SilverStripe\ORM\Queries\SQLSelect;
use SilverStripe\ORM\DataQuery;
use SilverStripe\Forms\FieldList; use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\HiddenField; use SilverStripe\Forms\HiddenField;
use SilverStripe\ORM\DataExtension; use SilverStripe\ORM\DataExtension;
use SilverStripe\Subsites\Model\Subsite; use SilverStripe\ORM\DataQuery;
use SilverStripe\ORM\Queries\SQLSelect;
use SilverStripe\SiteConfig\SiteConfig; use SilverStripe\SiteConfig\SiteConfig;
use SilverStripe\Subsites\Model\Subsite;
/** /**
* Extension for the SiteConfig object to add subsites support * Extension for the SiteConfig object to add subsites support
*/ */
class SiteConfigSubsites extends DataExtension { class SiteConfigSubsites extends DataExtension
{
private static $has_one = array( private static $has_one = [
'Subsite' => Subsite::class, // The subsite that this page belongs to 'Subsite' => Subsite::class, // The subsite that this page belongs to
); ];
/** /**
* Update any requests to limit the results to the current site * Update any requests to limit the results to the current site
*/ */
public function augmentSQL(SQLSelect $query, DataQuery $dataQuery = null) { public function augmentSQL(SQLSelect $query, DataQuery $dataQuery = null)
if(Subsite::$disable_subsite_filter) return; {
if (Subsite::$disable_subsite_filter) {
return;
}
// If you're querying by ID, ignore the sub-site - this is a bit ugly... // If you're querying by ID, ignore the sub-site - this is a bit ugly...
if($query->filtersOnID()) return; if ($query->filtersOnID()) {
$regexp = '/^(.*\.)?("|`)?SubsiteID("|`)?\s?=/'; return;
foreach($query->getWhereParameterised($parameters) as $predicate) { }
if(preg_match($regexp, $predicate)) return; $regexp = '/^(.*\.)?("|`)?SubsiteID("|`)?\s?=/';
} foreach ($query->getWhereParameterised($parameters) as $predicate) {
if (preg_match($regexp, $predicate)) {
return;
}
}
/*if($context = DataObject::context_obj()) $subsiteID = (int)$context->SubsiteID; /*if($context = DataObject::context_obj()) $subsiteID = (int)$context->SubsiteID;
else */$subsiteID = (int)Subsite::currentSubsiteID(); else */
$subsiteID = (int)Subsite::currentSubsiteID();
$froms=$query->getFrom(); $froms = $query->getFrom();
$froms=array_keys($froms); $froms = array_keys($froms);
$tableName = array_shift($froms); $tableName = array_shift($froms);
if($tableName != SiteConfig::class) return; if ($tableName != SiteConfig::class) {
$query->addWhere("\"$tableName\".\"SubsiteID\" IN ($subsiteID)"); return;
} }
$query->addWhere("\"$tableName\".\"SubsiteID\" IN ($subsiteID)");
}
function onBeforeWrite() { function onBeforeWrite()
if((!is_numeric($this->owner->ID) || !$this->owner->ID) && !$this->owner->SubsiteID) $this->owner->SubsiteID = Subsite::currentSubsiteID(); {
} if ((!is_numeric($this->owner->ID) || !$this->owner->ID) && !$this->owner->SubsiteID) {
$this->owner->SubsiteID = Subsite::currentSubsiteID();
}
}
/** /**
* Return a piece of text to keep DataObject cache keys appropriately specific * Return a piece of text to keep DataObject cache keys appropriately specific
*/ */
function cacheKeyComponent() { function cacheKeyComponent()
return 'subsite-'.Subsite::currentSubsiteID(); {
} return 'subsite-' . Subsite::currentSubsiteID();
}
function updateCMSFields(FieldList $fields) { function updateCMSFields(FieldList $fields)
$fields->push(new HiddenField('SubsiteID','SubsiteID', Subsite::currentSubsiteID())); {
} $fields->push(new HiddenField('SubsiteID', 'SubsiteID', Subsite::currentSubsiteID()));
}
} }

View File

@ -3,364 +3,418 @@
namespace SilverStripe\Subsites\Extensions; namespace SilverStripe\Subsites\Extensions;
use SilverStripe\ORM\Queries\SQLSelect;
use SilverStripe\ORM\DataQuery;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\InlineFormAction;
use SilverStripe\Core\Config\Config;
use SilverStripe\Control\Director;
use SilverStripe\Control\Controller;
use SilverStripe\ORM\DataObject;
use SilverStripe\SiteConfig\SiteConfig;
use SilverStripe\Security\Member;
use SilverStripe\Control\HTTP;
use SilverStripe\Core\Convert;
use SilverStripe\ORM\DataExtension;
use SilverStripe\Subsites\Model\Subsite;
use Page; use Page;
use SilverStripe\CMS\Model\SiteTree; use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Control\Controller;
use SilverStripe\Control\Director;
use SilverStripe\Control\HTTP;
use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Convert;
use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\InlineFormAction;
use SilverStripe\ORM\DataExtension;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\DataQuery;
use SilverStripe\ORM\Queries\SQLSelect;
use SilverStripe\Security\Member;
use SilverStripe\SiteConfig\SiteConfig;
use SilverStripe\Subsites\Model\Subsite;
/** /**
* Extension for the SiteTree object to add subsites support * Extension for the SiteTree object to add subsites support
*/ */
class SiteTreeSubsites extends DataExtension { class SiteTreeSubsites extends DataExtension
{
private static $has_one = array( private static $has_one = [
'Subsite' => Subsite::class, // The subsite that this page belongs to 'Subsite' => Subsite::class, // The subsite that this page belongs to
); ];
private static $many_many = array( private static $many_many = [
'CrossSubsiteLinkTracking' => 'SilverStripe\\CMS\\Model\\SiteTree' // Stored separately, as the logic for URL rewriting is different 'CrossSubsiteLinkTracking' => 'SilverStripe\\CMS\\Model\\SiteTree'
); // Stored separately, as the logic for URL rewriting is different
];
private static $many_many_extraFields = array( private static $many_many_extraFields = [
"CrossSubsiteLinkTracking" => array("FieldName" => "Varchar") "CrossSubsiteLinkTracking" => ["FieldName" => "Varchar"]
); ];
function isMainSite() { function isMainSite()
if($this->owner->SubsiteID == 0) return true; {
return false; if ($this->owner->SubsiteID == 0) {
} return true;
}
return false;
}
/** /**
* Update any requests to limit the results to the current site * Update any requests to limit the results to the current site
*/ */
public function augmentSQL(SQLSelect $query, DataQuery $dataQuery = null) { public function augmentSQL(SQLSelect $query, DataQuery $dataQuery = null)
if(Subsite::$disable_subsite_filter) return; {
if($dataQuery->getQueryParam('Subsite.filter') === false) return; if (Subsite::$disable_subsite_filter) {
return;
}
if ($dataQuery->getQueryParam('Subsite.filter') === false) {
return;
}
// If you're querying by ID, ignore the sub-site - this is a bit ugly... // If you're querying by ID, ignore the sub-site - this is a bit ugly...
// if(!$query->where || (strpos($query->where[0], ".\"ID\" = ") === false && strpos($query->where[0], ".`ID` = ") === false && strpos($query->where[0], ".ID = ") === false && strpos($query->where[0], "ID = ") !== 0)) { // if(!$query->where || (strpos($query->where[0], ".\"ID\" = ") === false && strpos($query->where[0], ".`ID` = ") === false && strpos($query->where[0], ".ID = ") === false && strpos($query->where[0], "ID = ") !== 0)) {
if($query->filtersOnID()) return; if ($query->filtersOnID()) {
return;
}
if (Subsite::$force_subsite) $subsiteID = Subsite::$force_subsite; if (Subsite::$force_subsite) {
else { $subsiteID = Subsite::$force_subsite;
/*if($context = DataObject::context_obj()) $subsiteID = (int)$context->SubsiteID; } else {
else */$subsiteID = (int)Subsite::currentSubsiteID(); /*if($context = DataObject::context_obj()) $subsiteID = (int)$context->SubsiteID;
} else */
$subsiteID = (int)Subsite::currentSubsiteID();
}
// The foreach is an ugly way of getting the first key :-) // The foreach is an ugly way of getting the first key :-)
foreach($query->getFrom() as $tableName => $info) { foreach ($query->getFrom() as $tableName => $info) {
// The tableName should be SiteTree or SiteTree_Live... // The tableName should be SiteTree or SiteTree_Live...
if(strpos($tableName,SiteTree::class) === false) break; if (strpos($tableName, SiteTree::class) === false) {
$query->addWhere("\"$tableName\".\"SubsiteID\" IN ($subsiteID)"); break;
break; }
} $query->addWhere("\"$tableName\".\"SubsiteID\" IN ($subsiteID)");
} break;
}
}
function onBeforeWrite() { function onBeforeWrite()
if(!$this->owner->ID && !$this->owner->SubsiteID) $this->owner->SubsiteID = Subsite::currentSubsiteID(); {
if (!$this->owner->ID && !$this->owner->SubsiteID) {
$this->owner->SubsiteID = Subsite::currentSubsiteID();
}
parent::onBeforeWrite(); parent::onBeforeWrite();
} }
function updateCMSFields(FieldList $fields) { function updateCMSFields(FieldList $fields)
$subsites = Subsite::accessible_sites("CMS_ACCESS_CMSMain"); {
$subsitesMap = array(); $subsites = Subsite::accessible_sites("CMS_ACCESS_CMSMain");
if($subsites && $subsites->Count()) { $subsitesMap = [];
$subsitesMap = $subsites->map('ID', 'Title')->toArray(); if ($subsites && $subsites->Count()) {
unset($subsitesMap[$this->owner->SubsiteID]); $subsitesMap = $subsites->map('ID', 'Title')->toArray();
} unset($subsitesMap[$this->owner->SubsiteID]);
}
// Master page edit field (only allowed from default subsite to avoid inconsistent relationships) // Master page edit field (only allowed from default subsite to avoid inconsistent relationships)
$isDefaultSubsite = $this->owner->SubsiteID == 0 || $this->owner->Subsite()->DefaultSite; $isDefaultSubsite = $this->owner->SubsiteID == 0 || $this->owner->Subsite()->DefaultSite;
if($isDefaultSubsite && $subsitesMap) { if ($isDefaultSubsite && $subsitesMap) {
$fields->addFieldToTab( $fields->addFieldToTab(
'Root.Main', 'Root.Main',
new DropdownField( new DropdownField(
"CopyToSubsiteID", "CopyToSubsiteID",
_t('SiteTreeSubsites.CopyToSubsite', "Copy page to subsite"), _t('SiteTreeSubsites.CopyToSubsite', "Copy page to subsite"),
$subsitesMap, $subsitesMap,
'' ''
) )
); );
$fields->addFieldToTab( $fields->addFieldToTab(
'Root.Main', 'Root.Main',
$copyAction = new InlineFormAction( $copyAction = new InlineFormAction(
"copytosubsite", "copytosubsite",
_t('SiteTreeSubsites.CopyAction', "Copy") _t('SiteTreeSubsites.CopyAction', "Copy")
) )
); );
} }
// replace readonly link prefix // replace readonly link prefix
$subsite = $this->owner->Subsite(); $subsite = $this->owner->Subsite();
$nested_urls_enabled = Config::inst()->get('SilverStripe\\CMS\\Model\\SiteTree', 'nested_urls'); $nested_urls_enabled = Config::inst()->get('SilverStripe\\CMS\\Model\\SiteTree', 'nested_urls');
if($subsite && $subsite->ID) { if ($subsite && $subsite->ID) {
$baseUrl = Director::protocol() . $subsite->domain() . '/'; $baseUrl = Director::protocol() . $subsite->domain() . '/';
$baseLink = Controller::join_links ( $baseLink = Controller::join_links(
$baseUrl, $baseUrl,
($nested_urls_enabled && $this->owner->ParentID ? $this->owner->Parent()->RelativeLink(true) : null) ($nested_urls_enabled && $this->owner->ParentID ? $this->owner->Parent()->RelativeLink(true) : null)
); );
$urlsegment = $fields->dataFieldByName('URLSegment'); $urlsegment = $fields->dataFieldByName('URLSegment');
$urlsegment->setURLPrefix($baseLink); $urlsegment->setURLPrefix($baseLink);
} }
} }
function alternateSiteConfig() { function alternateSiteConfig()
if(!$this->owner->SubsiteID) return false; {
$sc = DataObject::get_one('SilverStripe\\SiteConfig\\SiteConfig', '"SubsiteID" = ' . $this->owner->SubsiteID); if (!$this->owner->SubsiteID) {
if(!$sc) { return false;
$sc = new SiteConfig(); }
$sc->SubsiteID = $this->owner->SubsiteID; $sc = DataObject::get_one('SilverStripe\\SiteConfig\\SiteConfig', '"SubsiteID" = ' . $this->owner->SubsiteID);
$sc->Title = _t('Subsite.SiteConfigTitle','Your Site Name'); if (!$sc) {
$sc->Tagline = _t('Subsite.SiteConfigSubtitle','Your tagline here'); $sc = new SiteConfig();
$sc->write(); $sc->SubsiteID = $this->owner->SubsiteID;
} $sc->Title = _t('Subsite.SiteConfigTitle', 'Your Site Name');
return $sc; $sc->Tagline = _t('Subsite.SiteConfigSubtitle', 'Your tagline here');
} $sc->write();
}
return $sc;
}
/** /**
* Only allow editing of a page if the member satisfies one of the following conditions: * Only allow editing of a page if the member satisfies one of the following conditions:
* - Is in a group which has access to the subsite this page belongs to * - Is in a group which has access to the subsite this page belongs to
* - Is in a group with edit permissions on the "main site" * - Is in a group with edit permissions on the "main site"
* *
* @return boolean * @return boolean
*/ */
function canEdit($member = null) { function canEdit($member = null)
{
if(!$member) $member = Member::currentUser(); if (!$member) {
$member = Member::currentUser();
}
// Find the sites that this user has access to // Find the sites that this user has access to
$goodSites = Subsite::accessible_sites('CMS_ACCESS_CMSMain',true,'all',$member)->column('ID'); $goodSites = Subsite::accessible_sites('CMS_ACCESS_CMSMain', true, 'all', $member)->column('ID');
if (!is_null($this->owner->SubsiteID)) { if (!is_null($this->owner->SubsiteID)) {
$subsiteID = $this->owner->SubsiteID; $subsiteID = $this->owner->SubsiteID;
} else { } else {
// The relationships might not be available during the record creation when using a GridField. // The relationships might not be available during the record creation when using a GridField.
// In this case the related objects will have empty fields, and SubsiteID will not be available. // In this case the related objects will have empty fields, and SubsiteID will not be available.
// //
// We do the second best: fetch the likely SubsiteID from the session. The drawback is this might // We do the second best: fetch the likely SubsiteID from the session. The drawback is this might
// make it possible to force relations to point to other (forbidden) subsites. // make it possible to force relations to point to other (forbidden) subsites.
$subsiteID = Subsite::currentSubsiteID(); $subsiteID = Subsite::currentSubsiteID();
} }
// Return true if they have access to this object's site // Return true if they have access to this object's site
if(!(in_array(0, $goodSites) || in_array($subsiteID, $goodSites))) return false; if (!(in_array(0, $goodSites) || in_array($subsiteID, $goodSites))) {
} return false;
}
}
/** /**
* @return boolean * @return boolean
*/ */
function canDelete($member = null) { function canDelete($member = null)
if(!$member && $member !== FALSE) $member = Member::currentUser(); {
if (!$member && $member !== FALSE) {
$member = Member::currentUser();
}
return $this->canEdit($member); return $this->canEdit($member);
} }
/** /**
* @return boolean * @return boolean
*/ */
function canAddChildren($member = null) { function canAddChildren($member = null)
if(!$member && $member !== FALSE) $member = Member::currentUser(); {
if (!$member && $member !== FALSE) {
$member = Member::currentUser();
}
return $this->canEdit($member); return $this->canEdit($member);
} }
/** /**
* @return boolean * @return boolean
*/ */
function canPublish($member = null) { function canPublish($member = null)
if(!$member && $member !== FALSE) $member = Member::currentUser(); {
if (!$member && $member !== FALSE) {
$member = Member::currentUser();
}
return $this->canEdit($member); return $this->canEdit($member);
} }
/** /**
* Does the basic duplication, but doesn't write anything * Does the basic duplication, but doesn't write anything
* this means we can subclass this easier and do more complex * this means we can subclass this easier and do more complex
* relation duplication. * relation duplication.
*/ */
public function duplicateToSubsitePrep($subsiteID) { public function duplicateToSubsitePrep($subsiteID)
if (is_object($subsiteID)) { {
$subsiteID = $subsiteID->ID; if (is_object($subsiteID)) {
} $subsiteID = $subsiteID->ID;
}
$oldSubsite = Subsite::currentSubsiteID(); $oldSubsite = Subsite::currentSubsiteID();
if ($subsiteID) { if ($subsiteID) {
Subsite::changeSubsite($subsiteID); Subsite::changeSubsite($subsiteID);
} else { } else {
$subsiteID = $oldSubsite; $subsiteID = $oldSubsite;
} }
// doesn't write as we need to reset the SubsiteID, ParentID etc // doesn't write as we need to reset the SubsiteID, ParentID etc
$clone = $this->owner->duplicate(false); $clone = $this->owner->duplicate(false);
$clone->CheckedPublicationDifferences = $clone->AddedToStage = true; $clone->CheckedPublicationDifferences = $clone->AddedToStage = true;
$subsiteID = ($subsiteID ? $subsiteID : $oldSubsite); $subsiteID = ($subsiteID ? $subsiteID : $oldSubsite);
$clone->SubsiteID = $subsiteID; $clone->SubsiteID = $subsiteID;
// We have no idea what the parentID should be, so as a workaround use the url-segment and subsite ID // We have no idea what the parentID should be, so as a workaround use the url-segment and subsite ID
if ($this->owner->Parent()) { if ($this->owner->Parent()) {
$parentSeg = $this->owner->Parent()->URLSegment; $parentSeg = $this->owner->Parent()->URLSegment;
$newParentPage = Page::get()->filter('URLSegment', $parentSeg)->first(); $newParentPage = Page::get()->filter('URLSegment', $parentSeg)->first();
if ($newParentPage) { if ($newParentPage) {
$clone->ParentID = $newParentPage->ID; $clone->ParentID = $newParentPage->ID;
} else { } else {
// reset it to the top level, so the user can decide where to put it // reset it to the top level, so the user can decide where to put it
$clone->ParentID = 0; $clone->ParentID = 0;
} }
} }
// MasterPageID is here for legacy purposes, to satisfy the subsites_relatedpages module // MasterPageID is here for legacy purposes, to satisfy the subsites_relatedpages module
$clone->MasterPageID = $this->owner->ID; $clone->MasterPageID = $this->owner->ID;
return $clone; return $clone;
} }
/** /**
* Create a duplicate of this page and save it to another subsite * Create a duplicate of this page and save it to another subsite
* @param $subsiteID int|Subsite The Subsite to copy to, or its ID * @param $subsiteID int|Subsite The Subsite to copy to, or its ID
*/ */
public function duplicateToSubsite($subsiteID = null) { public function duplicateToSubsite($subsiteID = null)
$clone = $this->owner->duplicateToSubsitePrep($subsiteID); {
$clone->invokeWithExtensions('onBeforeDuplicateToSubsite', $this->owner); $clone = $this->owner->duplicateToSubsitePrep($subsiteID);
$clone->write(); $clone->invokeWithExtensions('onBeforeDuplicateToSubsite', $this->owner);
$clone->duplicateSubsiteRelations($this->owner); $clone->write();
// new extension hooks which happens after write, $clone->duplicateSubsiteRelations($this->owner);
// onAfterDuplicate isn't reliable due to // new extension hooks which happens after write,
// https://github.com/silverstripe/silverstripe-cms/issues/1253 // onAfterDuplicate isn't reliable due to
$clone->invokeWithExtensions('onAfterDuplicateToSubsite', $this->owner); // https://github.com/silverstripe/silverstripe-cms/issues/1253
return $clone; $clone->invokeWithExtensions('onAfterDuplicateToSubsite', $this->owner);
} return $clone;
}
/** /**
* Duplicate relations using a static property to define * Duplicate relations using a static property to define
* which ones we want to duplicate * which ones we want to duplicate
* *
* It may be that some relations are not diostinct to sub site so can stay * It may be that some relations are not diostinct to sub site so can stay
* whereas others may need to be duplicated * whereas others may need to be duplicated
* *
*/ */
public function duplicateSubsiteRelations($originalPage) { public function duplicateSubsiteRelations($originalPage)
$thisClass = $originalPage->ClassName; {
$relations = Config::inst()->get($thisClass, 'duplicate_to_subsite_relations'); $thisClass = $originalPage->ClassName;
$relations = Config::inst()->get($thisClass, 'duplicate_to_subsite_relations');
if($relations && !empty($relations)) { if ($relations && !empty($relations)) {
foreach($relations as $relation) { foreach ($relations as $relation) {
$items = $originalPage->$relation(); $items = $originalPage->$relation();
foreach($items as $item) { foreach ($items as $item) {
$duplicateItem = $item->duplicate(false); $duplicateItem = $item->duplicate(false);
$duplicateItem->{$thisClass.'ID'} = $this->owner->ID; $duplicateItem->{$thisClass . 'ID'} = $this->owner->ID;
$duplicateItem->write(); $duplicateItem->write();
} }
} }
} }
} }
/** /**
* Called by ContentController::init(); * Called by ContentController::init();
*/ */
static function contentcontrollerInit($controller) { static function contentcontrollerInit($controller)
$subsite = Subsite::currentSubsite(); {
$subsite = Subsite::currentSubsite();
if($subsite && $subsite->Theme){ if ($subsite && $subsite->Theme) {
Config::modify()->set('SilverStripe\\View\\SSViewer', 'theme', Subsite::currentSubsite()->Theme); Config::modify()->set('SilverStripe\\View\\SSViewer', 'theme', Subsite::currentSubsite()->Theme);
} }
} }
function alternateAbsoluteLink() { function alternateAbsoluteLink()
// Generate the existing absolute URL and replace the domain with the subsite domain. {
// This helps deal with Link() returning an absolute URL. // Generate the existing absolute URL and replace the domain with the subsite domain.
$url = Director::absoluteURL($this->owner->Link()); // This helps deal with Link() returning an absolute URL.
if($this->owner->SubsiteID) { $url = Director::absoluteURL($this->owner->Link());
$url = preg_replace('/\/\/[^\/]+\//', '//' . $this->owner->Subsite()->domain() . '/', $url); if ($this->owner->SubsiteID) {
} $url = preg_replace('/\/\/[^\/]+\//', '//' . $this->owner->Subsite()->domain() . '/', $url);
return $url; }
} return $url;
}
/** /**
* Use the CMS domain for iframed CMS previews to prevent single-origin violations * Use the CMS domain for iframed CMS previews to prevent single-origin violations
* and SSL cert problems. * and SSL cert problems.
*/ */
function alternatePreviewLink($action = null) { function alternatePreviewLink($action = null)
$url = Director::absoluteURL($this->owner->Link()); {
if($this->owner->SubsiteID) { $url = Director::absoluteURL($this->owner->Link());
$url = HTTP::setGetVar('SubsiteID', $this->owner->SubsiteID, $url); if ($this->owner->SubsiteID) {
} $url = HTTP::setGetVar('SubsiteID', $this->owner->SubsiteID, $url);
return $url; }
} return $url;
}
/** /**
* Inject the subsite ID into the content so it can be used by frontend scripts. * Inject the subsite ID into the content so it can be used by frontend scripts.
*/ */
function MetaTags(&$tags) { function MetaTags(&$tags)
if($this->owner->SubsiteID) { {
$tags .= "<meta name=\"x-subsite-id\" content=\"" . $this->owner->SubsiteID . "\" />\n"; if ($this->owner->SubsiteID) {
} $tags .= "<meta name=\"x-subsite-id\" content=\"" . $this->owner->SubsiteID . "\" />\n";
}
return $tags; return $tags;
} }
function augmentSyncLinkTracking() { function augmentSyncLinkTracking()
// Set LinkTracking appropriately {
$links = HTTP::getLinksIn($this->owner->Content); // Set LinkTracking appropriately
$linkedPages = array(); $links = HTTP::getLinksIn($this->owner->Content);
$linkedPages = [];
if($links) foreach($links as $link) { if ($links) {
if(substr($link, 0, strlen('http://')) == 'http://') { foreach ($links as $link) {
$withoutHttp = substr($link, strlen('http://')); if (substr($link, 0, strlen('http://')) == 'http://') {
if(strpos($withoutHttp, '/') && strpos($withoutHttp, '/') < strlen($withoutHttp)) { $withoutHttp = substr($link, strlen('http://'));
$domain = substr($withoutHttp, 0, strpos($withoutHttp, '/')); if (strpos($withoutHttp, '/') && strpos($withoutHttp, '/') < strlen($withoutHttp)) {
$rest = substr($withoutHttp, strpos($withoutHttp, '/') + 1); $domain = substr($withoutHttp, 0, strpos($withoutHttp, '/'));
$rest = substr($withoutHttp, strpos($withoutHttp, '/') + 1);
$subsiteID = Subsite::getSubsiteIDForDomain($domain); $subsiteID = Subsite::getSubsiteIDForDomain($domain);
if($subsiteID == 0) continue; // We have no idea what the domain for the main site is, so cant track links to it if ($subsiteID == 0) {
continue;
} // We have no idea what the domain for the main site is, so cant track links to it
$origDisableSubsiteFilter = Subsite::$disable_subsite_filter; $origDisableSubsiteFilter = Subsite::$disable_subsite_filter;
Subsite::disable_subsite_filter(true); Subsite::disable_subsite_filter(true);
$candidatePage = DataObject::get_one("SilverStripe\\CMS\\Model\\SiteTree", "\"URLSegment\" = '" . Convert::raw2sql(urldecode( $rest)) . "' AND \"SubsiteID\" = " . $subsiteID, false); $candidatePage = DataObject::get_one("SilverStripe\\CMS\\Model\\SiteTree",
Subsite::disable_subsite_filter($origDisableSubsiteFilter); "\"URLSegment\" = '" . Convert::raw2sql(urldecode($rest)) . "' AND \"SubsiteID\" = " . $subsiteID,
false);
Subsite::disable_subsite_filter($origDisableSubsiteFilter);
if($candidatePage) { if ($candidatePage) {
$linkedPages[] = $candidatePage->ID; $linkedPages[] = $candidatePage->ID;
} else { } else {
$this->owner->HasBrokenLink = true; $this->owner->HasBrokenLink = true;
} }
} }
} }
} }
}
$this->owner->CrossSubsiteLinkTracking()->setByIDList($linkedPages); $this->owner->CrossSubsiteLinkTracking()->setByIDList($linkedPages);
} }
/** /**
* Return a piece of text to keep DataObject cache keys appropriately specific * Return a piece of text to keep DataObject cache keys appropriately specific
*/ */
function cacheKeyComponent() { function cacheKeyComponent()
return 'subsite-'.Subsite::currentSubsiteID(); {
} return 'subsite-' . Subsite::currentSubsiteID();
}
/** /**
* @param Member * @param Member
* @return boolean|null * @return boolean|null
*/ */
function canCreate($member = null) { function canCreate($member = null)
// Typically called on a singleton, so we're not using the Subsite() relation {
$subsite = Subsite::currentSubsite(); // Typically called on a singleton, so we're not using the Subsite() relation
if($subsite && $subsite->exists() && $subsite->PageTypeBlacklist) { $subsite = Subsite::currentSubsite();
$blacklisted = explode(',', $subsite->PageTypeBlacklist); if ($subsite && $subsite->exists() && $subsite->PageTypeBlacklist) {
// All subclasses need to be listed explicitly $blacklisted = explode(',', $subsite->PageTypeBlacklist);
if(in_array($this->owner->class, $blacklisted)) return false; // All subclasses need to be listed explicitly
} if (in_array($this->owner->class, $blacklisted)) {
} return false;
}
}
}
} }

View File

@ -6,7 +6,6 @@ namespace SilverStripe\Subsites\Extensions;
use SilverStripe\Core\Extension; use SilverStripe\Core\Extension;
/* /*
* Simple extension to show admins in the menu of subsites. * Simple extension to show admins in the menu of subsites.
* If an admin area should be available to a subsite, you can attach * If an admin area should be available to a subsite, you can attach
@ -17,10 +16,12 @@ use SilverStripe\Core\Extension;
* Or you can include the subsiteCMSShowInMenu function in your admin class and have it return true * Or you can include the subsiteCMSShowInMenu function in your admin class and have it return true
*/ */
class SubsiteMenuExtension extends Extension{ class SubsiteMenuExtension extends Extension
{
public function subsiteCMSShowInMenu(){ public function subsiteCMSShowInMenu()
return true; {
} return true;
}
} }

View File

@ -3,61 +3,65 @@
namespace SilverStripe\Subsites\Forms; namespace SilverStripe\Subsites\Forms;
use SilverStripe\Forms\GridField\GridFieldDetailForm;
use SilverStripe\Forms\DropdownField; use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\GridField\GridFieldDetailForm;
use SilverStripe\Forms\GridField\GridFieldDetailForm_ItemRequest; use SilverStripe\Forms\GridField\GridFieldDetailForm_ItemRequest;
use SilverStripe\Subsites\Model\Subsite; use SilverStripe\Subsites\Model\Subsite;
class GridFieldSubsiteDetailForm extends GridFieldDetailForm { class GridFieldSubsiteDetailForm extends GridFieldDetailForm
protected $itemRequestClass=GridFieldSubsiteDetailForm_ItemRequest::class; {
protected $itemRequestClass = GridFieldSubsiteDetailForm_ItemRequest::class;
} }
class GridFieldSubsiteDetailForm_ItemRequest extends GridFieldDetailForm_ItemRequest { class GridFieldSubsiteDetailForm_ItemRequest extends GridFieldDetailForm_ItemRequest
{
private static $allowed_actions = array( private static $allowed_actions = [
'ItemEditForm', 'ItemEditForm',
); ];
/** /**
* Builds an item edit form. The arguments to getCMSFields() are the popupController and * Builds an item edit form. The arguments to getCMSFields() are the popupController and
* popupFormName, however this is an experimental API and may change. * popupFormName, however this is an experimental API and may change.
* *
* @todo In the future, we will probably need to come up with a tigher object representing a partially * @todo In the future, we will probably need to come up with a tigher object representing a partially
* complete controller with gaps for extra functionality. This, for example, would be a better way * complete controller with gaps for extra functionality. This, for example, would be a better way
* of letting Security/login put its log-in form inside a UI specified elsewhere. * of letting Security/login put its log-in form inside a UI specified elsewhere.
* *
* @return Form * @return Form
* @see GridFieldDetailForm_ItemRequest::ItemEditForm() * @see GridFieldDetailForm_ItemRequest::ItemEditForm()
*/ */
function ItemEditForm() { function ItemEditForm()
$form=parent::ItemEditForm(); {
$form = parent::ItemEditForm();
if($this->record->ID == 0) { if ($this->record->ID == 0) {
$templates = Subsite::get()->sort('Title'); $templates = Subsite::get()->sort('Title');
$templateArray = array(); $templateArray = [];
if($templates) { if ($templates) {
$templateArray = $templates->map('ID', 'Title'); $templateArray = $templates->map('ID', 'Title');
} }
$templateDropdown = new DropdownField('TemplateID', _t('Subsite.COPYSTRUCTURE', 'Copy structure from:'), $templateArray); $templateDropdown = new DropdownField('TemplateID', _t('Subsite.COPYSTRUCTURE', 'Copy structure from:'),
$templateDropdown->setEmptyString('(' . _t('Subsite.NOTEMPLATE', 'No template') . ')'); $templateArray);
$form->Fields()->addFieldToTab('Root.Configuration', $templateDropdown); $templateDropdown->setEmptyString('(' . _t('Subsite.NOTEMPLATE', 'No template') . ')');
} $form->Fields()->addFieldToTab('Root.Configuration', $templateDropdown);
}
return $form; return $form;
} }
function doSave($data, $form) { function doSave($data, $form)
$new_record = $this->record->ID == 0; {
if($new_record && isset($data['TemplateID']) && !empty($data['TemplateID'])) { $new_record = $this->record->ID == 0;
$template = Subsite::get()->byID(intval($data['TemplateID'])); if ($new_record && isset($data['TemplateID']) && !empty($data['TemplateID'])) {
if($template) { $template = Subsite::get()->byID(intval($data['TemplateID']));
$this->record = $template->duplicate(); if ($template) {
} $this->record = $template->duplicate();
} }
}
return parent::doSave($data, $form); return parent::doSave($data, $form);
} }
} }

View File

@ -3,11 +3,10 @@
namespace SilverStripe\Subsites\Forms; namespace SilverStripe\Subsites\Forms;
use SilverStripe\View\Requirements;
use SilverStripe\Control\HTTPRequest; use SilverStripe\Control\HTTPRequest;
use SilverStripe\Control\Session; use SilverStripe\Control\Session;
use SilverStripe\Forms\TreeDropdownField; use SilverStripe\Forms\TreeDropdownField;
use SilverStripe\View\Requirements;
/** /**
@ -16,40 +15,45 @@ use SilverStripe\Forms\TreeDropdownField;
* *
* @package subsites * @package subsites
*/ */
class SubsitesTreeDropdownField extends TreeDropdownField { class SubsitesTreeDropdownField extends TreeDropdownField
{
private static $allowed_actions = array( private static $allowed_actions = [
'tree' 'tree'
); ];
protected $subsiteID = 0; protected $subsiteID = 0;
protected $extraClasses = array(SubsitesTreeDropdownField::class); protected $extraClasses = [SubsitesTreeDropdownField::class];
function Field($properties = array()) { function Field($properties = [])
$html = parent::Field($properties); {
$html = parent::Field($properties);
Requirements::javascript('subsites/javascript/SubsitesTreeDropdownField.js'); Requirements::javascript('subsites/javascript/SubsitesTreeDropdownField.js');
return $html; return $html;
} }
function setSubsiteID($id) { function setSubsiteID($id)
$this->subsiteID = $id; {
} $this->subsiteID = $id;
}
function getSubsiteID() { function getSubsiteID()
return $this->subsiteID; {
} return $this->subsiteID;
}
function tree(HTTPRequest $request) { function tree(HTTPRequest $request)
$oldSubsiteID = Session::get('SubsiteID'); {
Session::set('SubsiteID', $this->subsiteID); $oldSubsiteID = Session::get('SubsiteID');
Session::set('SubsiteID', $this->subsiteID);
$results = parent::tree($request); $results = parent::tree($request);
Session::set('SubsiteID', $oldSubsiteID); Session::set('SubsiteID', $oldSubsiteID);
return $results; return $results;
} }
} }

View File

@ -3,37 +3,34 @@
namespace SilverStripe\Subsites\Model; namespace SilverStripe\Subsites\Model;
use SilverStripe\i18n\Data\Intl\IntlLocales;
use SilverStripe\ORM\DataObject;
use SilverStripe\Control\Session;
use SilverStripe\i18n\i18n;
use SilverStripe\Security\Permission;
use SilverStripe\Security\Member;
use SilverStripe\Core\Convert;
use SilverStripe\ORM\ArrayList;
use SilverStripe\Admin\CMSMenu; use SilverStripe\Admin\CMSMenu;
use SilverStripe\ORM\DataList;
use SilverStripe\Control\Director;
use SilverStripe\ORM\DB;
use SilverStripe\Forms\GridField\GridFieldConfig_RecordEditor;
use SilverStripe\Forms\GridField\GridField;
use SilverStripe\Forms\LiteralField;
use SilverStripe\Forms\DropdownField;
use SilverStripe\CMS\Model\SiteTree; use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Forms\HeaderField; use SilverStripe\Control\Director;
use SilverStripe\Forms\TextField; use SilverStripe\Control\Session;
use SilverStripe\Core\Convert;
use SilverStripe\Forms\CheckboxField; use SilverStripe\Forms\CheckboxField;
use SilverStripe\Forms\CheckboxSetField; use SilverStripe\Forms\CheckboxSetField;
use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\GridField\GridField;
use SilverStripe\Forms\GridField\GridFieldConfig_RecordEditor;
use SilverStripe\Forms\HeaderField;
use SilverStripe\Forms\HiddenField;
use SilverStripe\Forms\LiteralField;
use SilverStripe\Forms\Tab; use SilverStripe\Forms\Tab;
use SilverStripe\Forms\TabSet; use SilverStripe\Forms\TabSet;
use SilverStripe\Forms\HiddenField; use SilverStripe\Forms\TextField;
use SilverStripe\Forms\FieldList; use SilverStripe\i18n\Data\Intl\IntlLocales;
use SilverStripe\i18n\i18n;
use SilverStripe\ORM\ArrayLib; use SilverStripe\ORM\ArrayLib;
use SilverStripe\ORM\ArrayList;
use SilverStripe\ORM\DataList;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\DB;
use SilverStripe\Security\Member;
use SilverStripe\Security\Permission;
use SilverStripe\Versioned\Versioned; use SilverStripe\Versioned\Versioned;
use UnexpectedValueException; use UnexpectedValueException;
use SilverStripe\Security\Group;
use SilverStripe\Security\PermissionRole;
use SilverStripe\Security\PermissionRoleCode;
/** /**
@ -42,432 +39,513 @@ use SilverStripe\Security\PermissionRoleCode;
* *
* @package subsites * @package subsites
*/ */
class Subsite extends DataObject { class Subsite extends DataObject
{
private static $table_name = 'Subsite'; private static $table_name = 'Subsite';
/** /**
* @var $use_session_subsiteid Boolean Set to TRUE when using the CMS and FALSE * @var $use_session_subsiteid Boolean Set to TRUE when using the CMS and FALSE
* when browsing the frontend of a website. * when browsing the frontend of a website.
* *
* @todo Remove flag once the Subsite CMS works without session state, * @todo Remove flag once the Subsite CMS works without session state,
* similarly to the Translatable module. * similarly to the Translatable module.
*/ */
public static $use_session_subsiteid = false; public static $use_session_subsiteid = false;
/** /**
* @var boolean $disable_subsite_filter If enabled, bypasses the query decoration * @var boolean $disable_subsite_filter If enabled, bypasses the query decoration
* to limit DataObject::get*() calls to a specific subsite. Useful for debugging. * to limit DataObject::get*() calls to a specific subsite. Useful for debugging.
*/ */
public static $disable_subsite_filter = false; public static $disable_subsite_filter = false;
/** /**
* Allows you to force a specific subsite ID, or comma separated list of IDs. * Allows you to force a specific subsite ID, or comma separated list of IDs.
* Only works for reading. An object cannot be written to more than 1 subsite. * Only works for reading. An object cannot be written to more than 1 subsite.
*/ */
public static $force_subsite = null; public static $force_subsite = null;
/** /**
* *
* @var boolean * @var boolean
*/ */
public static $write_hostmap = true; public static $write_hostmap = true;
/** /**
* Memory cache of accessible sites * Memory cache of accessible sites
* *
* @array * @array
*/ */
private static $_cache_accessible_sites = array(); private static $_cache_accessible_sites = [];
/** /**
* Memory cache of subsite id for domains * Memory cache of subsite id for domains
* *
* @var array * @var array
*/ */
private static $_cache_subsite_for_domain = array(); private static $_cache_subsite_for_domain = [];
/** /**
* @var array $allowed_themes Numeric array of all themes which are allowed to be selected for all subsites. * @var array $allowed_themes Numeric array of all themes which are allowed to be selected for all subsites.
* Corresponds to subfolder names within the /themes folder. By default, all themes contained in this folder * Corresponds to subfolder names within the /themes folder. By default, all themes contained in this folder
* are listed. * are listed.
*/ */
private static $allowed_themes = array(); private static $allowed_themes = [];
/** /**
* @var Boolean If set to TRUE, don't assume 'www.example.com' and 'example.com' are the same. * @var Boolean If set to TRUE, don't assume 'www.example.com' and 'example.com' are the same.
* Doesn't affect wildcard matching, so '*.example.com' will match 'www.example.com' (but not 'example.com') * Doesn't affect wildcard matching, so '*.example.com' will match 'www.example.com' (but not 'example.com')
* in both TRUE or FALSE setting. * in both TRUE or FALSE setting.
*/ */
public static $strict_subdomain_matching = false; public static $strict_subdomain_matching = false;
/** /**
* @var boolean Respects the IsPublic flag when retrieving subsites * @var boolean Respects the IsPublic flag when retrieving subsites
*/ */
public static $check_is_public = true; public static $check_is_public = true;
/** /**
* @return array * @return array
*/ */
private static $summary_fields = array( private static $summary_fields = [
'Title', 'Title',
'PrimaryDomain', 'PrimaryDomain',
'IsPublic' 'IsPublic'
); ];
/** /**
* Set allowed themes * Set allowed themes
* *
* @param array $themes - Numeric array of all themes which are allowed to be selected for all subsites. * @param array $themes - Numeric array of all themes which are allowed to be selected for all subsites.
*/ */
public static function set_allowed_themes($themes) { public static function set_allowed_themes($themes)
self::$allowed_themes = $themes; {
} self::$allowed_themes = $themes;
}
/**
* Gets the subsite currently set in the session. /**
* * Gets the subsite currently set in the session.
* @uses ControllerSubsites->controllerAugmentInit() *
* @return Subsite * @uses ControllerSubsites->controllerAugmentInit()
*/ * @return Subsite
public static function currentSubsite() { */
// get_by_id handles caching so we don't have to public static function currentSubsite()
return DataObject::get_by_id(Subsite::class, self::currentSubsiteID()); {
} // get_by_id handles caching so we don't have to
return DataObject::get_by_id(Subsite::class, self::currentSubsiteID());
/** }
* This function gets the current subsite ID from the session. It used in the backend so Ajax requests
* use the correct subsite. The frontend handles subsites differently. It calls getSubsiteIDForDomain /**
* directly from ModelAsController::getNestedController. Only gets Subsite instances which have their * This function gets the current subsite ID from the session. It used in the backend so Ajax requests
* {@link IsPublic} flag set to TRUE. * use the correct subsite. The frontend handles subsites differently. It calls getSubsiteIDForDomain
* * directly from ModelAsController::getNestedController. Only gets Subsite instances which have their
* You can simulate subsite access without creating virtual hosts by appending ?SubsiteID=<ID> to the request. * {@link IsPublic} flag set to TRUE.
* *
* @todo Pass $request object from controller so we don't have to rely on $_GET * You can simulate subsite access without creating virtual hosts by appending ?SubsiteID=<ID> to the request.
* *
* @param boolean $cache * @todo Pass $request object from controller so we don't have to rely on $_GET
* @return int ID of the current subsite instance *
*/ * @param boolean $cache
public static function currentSubsiteID() { * @return int ID of the current subsite instance
$id = NULL; */
public static function currentSubsiteID()
if(isset($_GET['SubsiteID'])) { {
$id = (int)$_GET['SubsiteID']; $id = NULL;
} else if (Subsite::$use_session_subsiteid) {
$id = Session::get('SubsiteID'); if (isset($_GET['SubsiteID'])) {
} $id = (int)$_GET['SubsiteID'];
} else {
if($id === NULL) { if (Subsite::$use_session_subsiteid) {
$id = self::getSubsiteIDForDomain(); $id = Session::get('SubsiteID');
} }
}
return (int)$id;
} if ($id === NULL) {
$id = self::getSubsiteIDForDomain();
/** }
* Switch to another subsite through storing the subsite identifier in the current PHP session.
* Only takes effect when {@link Subsite::$use_session_subsiteid} is set to TRUE. return (int)$id;
* }
* @param int|Subsite $subsite Either the ID of the subsite, or the subsite object itself
*/ /**
public static function changeSubsite($subsite) { * Switch to another subsite through storing the subsite identifier in the current PHP session.
// Session subsite change only meaningful if the session is active. * Only takes effect when {@link Subsite::$use_session_subsiteid} is set to TRUE.
// Otherwise we risk setting it to wrong value, e.g. if we rely on currentSubsiteID. *
if (!Subsite::$use_session_subsiteid) return; * @param int|Subsite $subsite Either the ID of the subsite, or the subsite object itself
*/
if(is_object($subsite)) $subsiteID = $subsite->ID; public static function changeSubsite($subsite)
else $subsiteID = $subsite; {
// Session subsite change only meaningful if the session is active.
Session::set('SubsiteID', (int)$subsiteID); // Otherwise we risk setting it to wrong value, e.g. if we rely on currentSubsiteID.
if (!Subsite::$use_session_subsiteid) {
// Set locale return;
if (is_object($subsite) && $subsite->Language != '') { }
$locale = i18n::get_locale_from_lang($subsite->Language);
if($locale) { if (is_object($subsite)) {
i18n::set_locale($locale); $subsiteID = $subsite->ID;
} } else {
} $subsiteID = $subsite;
}
Permission::flush_permission_cache();
} Session::set('SubsiteID', (int)$subsiteID);
/** // Set locale
* Get a matching subsite for the given host, or for the current HTTP_HOST. if (is_object($subsite) && $subsite->Language != '') {
* Supports "fuzzy" matching of domains by placing an asterisk at the start of end of the string, $locale = i18n::get_locale_from_lang($subsite->Language);
* for example matching all subdomains on *.example.com with one subsite, if ($locale) {
* and all subdomains on *.example.org on another. i18n::set_locale($locale);
* }
* @param $host The host to find the subsite for. If not specified, $_SERVER['HTTP_HOST'] is used. }
* @return int Subsite ID
*/ Permission::flush_permission_cache();
public static function getSubsiteIDForDomain($host = null, $checkPermissions = true) { }
if($host == null && isset($_SERVER['HTTP_HOST'])) {
$host = $_SERVER['HTTP_HOST']; /**
} * Get a matching subsite for the given host, or for the current HTTP_HOST.
* Supports "fuzzy" matching of domains by placing an asterisk at the start of end of the string,
$matchingDomains = null; * for example matching all subdomains on *.example.com with one subsite,
$cacheKey = null; * and all subdomains on *.example.org on another.
if ($host) { *
if(!self::$strict_subdomain_matching) $host = preg_replace('/^www\./', '', $host); * @param $host The host to find the subsite for. If not specified, $_SERVER['HTTP_HOST'] is used.
* @return int Subsite ID
$cacheKey = implode('_', array($host, Member::currentUserID(), self::$check_is_public)); */
if(isset(self::$_cache_subsite_for_domain[$cacheKey])) return self::$_cache_subsite_for_domain[$cacheKey]; public static function getSubsiteIDForDomain($host = null, $checkPermissions = true)
{
$SQL_host = Convert::raw2sql($host); if ($host == null && isset($_SERVER['HTTP_HOST'])) {
$matchingDomains = DataObject::get( $host = $_SERVER['HTTP_HOST'];
SubsiteDomain::class, }
"'$SQL_host' LIKE replace(\"SubsiteDomain\".\"Domain\",'*','%')",
"\"IsPrimary\" DESC" $matchingDomains = null;
)->innerJoin('Subsite', "\"Subsite\".\"ID\" = \"SubsiteDomain\".\"SubsiteID\" AND \"Subsite\".\"IsPublic\"=1"); $cacheKey = null;
} if ($host) {
if (!self::$strict_subdomain_matching) {
if($matchingDomains && $matchingDomains->Count()) { $host = preg_replace('/^www\./', '', $host);
$subsiteIDs = array_unique($matchingDomains->column('SubsiteID')); }
$subsiteDomains = array_unique($matchingDomains->column('Domain'));
if(sizeof($subsiteIDs) > 1) { $cacheKey = implode('_', [$host, Member::currentUserID(), self::$check_is_public]);
throw new UnexpectedValueException(sprintf( if (isset(self::$_cache_subsite_for_domain[$cacheKey])) {
"Multiple subsites match on '%s': %s", return self::$_cache_subsite_for_domain[$cacheKey];
$host, }
implode(',', $subsiteDomains)
)); $SQL_host = Convert::raw2sql($host);
} $matchingDomains = DataObject::get(
SubsiteDomain::class,
$subsiteID = $subsiteIDs[0]; "'$SQL_host' LIKE replace(\"SubsiteDomain\".\"Domain\",'*','%')",
} else if($default = DataObject::get_one(Subsite::class, "\"DefaultSite\" = 1")) { "\"IsPrimary\" DESC"
// Check for a 'default' subsite )->innerJoin('Subsite',
$subsiteID = $default->ID; "\"Subsite\".\"ID\" = \"SubsiteDomain\".\"SubsiteID\" AND \"Subsite\".\"IsPublic\"=1");
} else { }
// Default subsite id = 0, the main site
$subsiteID = 0; if ($matchingDomains && $matchingDomains->Count()) {
} $subsiteIDs = array_unique($matchingDomains->column('SubsiteID'));
$subsiteDomains = array_unique($matchingDomains->column('Domain'));
if ($cacheKey) { if (sizeof($subsiteIDs) > 1) {
self::$_cache_subsite_for_domain[$cacheKey] = $subsiteID; throw new UnexpectedValueException(sprintf(
} "Multiple subsites match on '%s': %s",
$host,
return $subsiteID; implode(',', $subsiteDomains)
} ));
}
/**
* $subsiteID = $subsiteIDs[0];
* @param string $className } else {
* @param string $filter if ($default = DataObject::get_one(Subsite::class, "\"DefaultSite\" = 1")) {
* @param string $sort // Check for a 'default' subsite
* @param string $join $subsiteID = $default->ID;
* @param string $limit } else {
* @return DataList // Default subsite id = 0, the main site
*/ $subsiteID = 0;
public static function get_from_all_subsites($className, $filter = "", $sort = "", $join = "", $limit = "") { }
$result = DataObject::get($className, $filter, $sort, $join, $limit); }
$result = $result->setDataQueryParam('Subsite.filter', false);
return $result; if ($cacheKey) {
} self::$_cache_subsite_for_domain[$cacheKey] = $subsiteID;
}
/**
* Disable the sub-site filtering; queries will select from all subsites return $subsiteID;
*/ }
public static function disable_subsite_filter($disabled = true) {
self::$disable_subsite_filter = $disabled; /**
} *
* @param string $className
/** * @param string $filter
* Flush caches on database reset * @param string $sort
*/ * @param string $join
public static function on_db_reset() { * @param string $limit
self::$_cache_accessible_sites = array(); * @return DataList
self::$_cache_subsite_for_domain = array(); */
} public static function get_from_all_subsites($className, $filter = "", $sort = "", $join = "", $limit = "")
{
/** $result = DataObject::get($className, $filter, $sort, $join, $limit);
* Return all subsites, regardless of permissions (augmented with main site). $result = $result->setDataQueryParam('Subsite.filter', false);
* return $result;
* @return SS_List List of {@link Subsite} objects (DataList or ArrayList). }
*/
public static function all_sites($includeMainSite = true, $mainSiteTitle = "Main site") { /**
$subsites = Subsite::get(); * Disable the sub-site filtering; queries will select from all subsites
*/
if($includeMainSite) { public static function disable_subsite_filter($disabled = true)
$subsites = $subsites->toArray(); {
self::$disable_subsite_filter = $disabled;
$mainSite = new Subsite(); }
$mainSite->Title = $mainSiteTitle;
array_unshift($subsites, $mainSite); /**
* Flush caches on database reset
$subsites = ArrayList::create($subsites); */
} public static function on_db_reset()
{
return $subsites; self::$_cache_accessible_sites = [];
} self::$_cache_subsite_for_domain = [];
}
/*
* Returns an ArrayList of the subsites accessible to the current user. /**
* It's enough for any section to be accessible for the site to be included. * Return all subsites, regardless of permissions (augmented with main site).
* *
* @return ArrayList of {@link Subsite} instances. * @return SS_List List of {@link Subsite} objects (DataList or ArrayList).
*/ */
public static function all_accessible_sites($includeMainSite = true, $mainSiteTitle = "Main site", $member = null) { public static function all_sites($includeMainSite = true, $mainSiteTitle = "Main site")
// Rationalise member arguments {
if(!$member) $member = Member::currentUser(); $subsites = Subsite::get();
if(!$member) return new ArrayList();
if(!is_object($member)) $member = DataObject::get_by_id('SilverStripe\\Security\\Member', $member); if ($includeMainSite) {
$subsites = $subsites->toArray();
$subsites = new ArrayList();
$mainSite = new Subsite();
// Collect subsites for all sections. $mainSite->Title = $mainSiteTitle;
$menu = CMSMenu::get_viewable_menu_items(); array_unshift($subsites, $mainSite);
foreach($menu as $candidate) {
if ($candidate->controller) { $subsites = ArrayList::create($subsites);
$accessibleSites = singleton($candidate->controller)->sectionSites( }
$includeMainSite,
$mainSiteTitle, return $subsites;
$member }
);
/*
// Replace existing keys so no one site appears twice. * Returns an ArrayList of the subsites accessible to the current user.
$subsites->merge($accessibleSites); * It's enough for any section to be accessible for the site to be included.
} *
} * @return ArrayList of {@link Subsite} instances.
*/
$subsites->removeDuplicates(); public static function all_accessible_sites($includeMainSite = true, $mainSiteTitle = "Main site", $member = null)
{
return $subsites; // Rationalise member arguments
} if (!$member) {
$member = Member::currentUser();
/** }
* Return the subsites that the current user can access by given permission. if (!$member) {
* Sites will only be included if they have a Title. return new ArrayList();
* }
* @param $permCode array|string Either a single permission code or an array of permission codes. if (!is_object($member)) {
* @param $includeMainSite If true, the main site will be included if appropriate. $member = DataObject::get_by_id('SilverStripe\\Security\\Member', $member);
* @param $mainSiteTitle The label to give to the main site }
* @param $member
* @return DataList of {@link Subsite} instances $subsites = new ArrayList();
*/
public static function accessible_sites($permCode, $includeMainSite = true, $mainSiteTitle = "Main site", $member = null) { // Collect subsites for all sections.
// Rationalise member arguments $menu = CMSMenu::get_viewable_menu_items();
if(!$member) $member = Member::currentUser(); foreach ($menu as $candidate) {
if(!$member) return new ArrayList(); if ($candidate->controller) {
if(!is_object($member)) $member = DataObject::get_by_id('SilverStripe\\Security\\Member', $member); $accessibleSites = singleton($candidate->controller)->sectionSites(
$includeMainSite,
// Rationalise permCode argument $mainSiteTitle,
if(is_array($permCode)) $SQL_codes = "'" . implode("', '", Convert::raw2sql($permCode)) . "'"; $member
else $SQL_codes = "'" . Convert::raw2sql($permCode) . "'"; );
// Cache handling // Replace existing keys so no one site appears twice.
$cacheKey = $SQL_codes . '-' . $member->ID . '-' . $includeMainSite . '-' . $mainSiteTitle; $subsites->merge($accessibleSites);
if(isset(self::$_cache_accessible_sites[$cacheKey])) { }
return self::$_cache_accessible_sites[$cacheKey]; }
}
$subsites->removeDuplicates();
$subsites = DataList::create(Subsite::class)
->where("\"Subsite\".\"Title\" != ''") return $subsites;
->leftJoin('Group_Subsites', "\"Group_Subsites\".\"SubsiteID\" = \"Subsite\".\"ID\"") }
->innerJoin('Group', "\"Group\".\"ID\" = \"Group_Subsites\".\"GroupID\" OR \"Group\".\"AccessAllSubsites\" = 1")
->innerJoin('Group_Members', "\"Group_Members\".\"GroupID\"=\"Group\".\"ID\" AND \"Group_Members\".\"MemberID\" = $member->ID") /**
->innerJoin('Permission', "\"Group\".\"ID\"=\"Permission\".\"GroupID\" AND \"Permission\".\"Code\" IN ($SQL_codes, 'CMS_ACCESS_LeftAndMain', 'ADMIN')"); * Return the subsites that the current user can access by given permission.
* Sites will only be included if they have a Title.
if(!$subsites) $subsites = new ArrayList(); *
* @param $permCode array|string Either a single permission code or an array of permission codes.
$rolesSubsites = DataList::create(Subsite::class) * @param $includeMainSite If true, the main site will be included if appropriate.
->where("\"Subsite\".\"Title\" != ''") * @param $mainSiteTitle The label to give to the main site
->leftJoin('Group_Subsites', "\"Group_Subsites\".\"SubsiteID\" = \"Subsite\".\"ID\"") * @param $member
->innerJoin('Group', "\"Group\".\"ID\" = \"Group_Subsites\".\"GroupID\" OR \"Group\".\"AccessAllSubsites\" = 1") * @return DataList of {@link Subsite} instances
->innerJoin('Group_Members', "\"Group_Members\".\"GroupID\"=\"Group\".\"ID\" AND \"Group_Members\".\"MemberID\" = $member->ID") */
->innerJoin('Group_Roles', "\"Group_Roles\".\"GroupID\"=\"Group\".\"ID\"") public static function accessible_sites(
->innerJoin('PermissionRole', "\"Group_Roles\".\"PermissionRoleID\"=\"PermissionRole\".\"ID\"") $permCode,
->innerJoin('PermissionRoleCode', "\"PermissionRole\".\"ID\"=\"PermissionRoleCode\".\"RoleID\" AND \"PermissionRoleCode\".\"Code\" IN ($SQL_codes, 'CMS_ACCESS_LeftAndMain', 'ADMIN')"); $includeMainSite = true,
$mainSiteTitle = "Main site",
if(!$subsites && $rolesSubsites) return $rolesSubsites; $member = null
)
$subsites = new ArrayList($subsites->toArray()); {
// Rationalise member arguments
if($rolesSubsites) foreach($rolesSubsites as $subsite) { if (!$member) {
if(!$subsites->find('ID', $subsite->ID)) { $member = Member::currentUser();
$subsites->push($subsite); }
} if (!$member) {
} return new ArrayList();
}
if($includeMainSite) { if (!is_object($member)) {
if(!is_array($permCode)) $permCode = array($permCode); $member = DataObject::get_by_id('SilverStripe\\Security\\Member', $member);
if(self::hasMainSitePermission($member, $permCode)) { }
$subsites=$subsites->toArray();
// Rationalise permCode argument
$mainSite = new Subsite(); if (is_array($permCode)) {
$mainSite->Title = $mainSiteTitle; $SQL_codes = "'" . implode("', '", Convert::raw2sql($permCode)) . "'";
array_unshift($subsites, $mainSite); } else {
$subsites=ArrayList::create($subsites); $SQL_codes = "'" . Convert::raw2sql($permCode) . "'";
} }
}
// Cache handling
self::$_cache_accessible_sites[$cacheKey] = $subsites; $cacheKey = $SQL_codes . '-' . $member->ID . '-' . $includeMainSite . '-' . $mainSiteTitle;
if (isset(self::$_cache_accessible_sites[$cacheKey])) {
return $subsites; return self::$_cache_accessible_sites[$cacheKey];
} }
/** $subsites = DataList::create(Subsite::class)
* Write a host->domain map to subsites/host-map.php ->where("\"Subsite\".\"Title\" != ''")
* ->leftJoin('Group_Subsites', "\"Group_Subsites\".\"SubsiteID\" = \"Subsite\".\"ID\"")
* This is used primarily when using subsites in conjunction with StaticPublisher ->innerJoin('Group',
* "\"Group\".\"ID\" = \"Group_Subsites\".\"GroupID\" OR \"Group\".\"AccessAllSubsites\" = 1")
* @param string $file - filepath of the host map to be written ->innerJoin('Group_Members',
* @return void "\"Group_Members\".\"GroupID\"=\"Group\".\"ID\" AND \"Group_Members\".\"MemberID\" = $member->ID")
*/ ->innerJoin('Permission',
public static function writeHostMap($file = null) { "\"Group\".\"ID\"=\"Permission\".\"GroupID\" AND \"Permission\".\"Code\" IN ($SQL_codes, 'CMS_ACCESS_LeftAndMain', 'ADMIN')");
if (!self::$write_hostmap) return;
if (!$subsites) {
if (!$file) $file = Director::baseFolder().'/subsites/host-map.php'; $subsites = new ArrayList();
$hostmap = array(); }
$subsites = DataObject::get(Subsite::class); $rolesSubsites = DataList::create(Subsite::class)
->where("\"Subsite\".\"Title\" != ''")
if ($subsites) foreach($subsites as $subsite) { ->leftJoin('Group_Subsites', "\"Group_Subsites\".\"SubsiteID\" = \"Subsite\".\"ID\"")
$domains = $subsite->Domains(); ->innerJoin('Group',
if ($domains) foreach($domains as $domain) { "\"Group\".\"ID\" = \"Group_Subsites\".\"GroupID\" OR \"Group\".\"AccessAllSubsites\" = 1")
$domainStr = $domain->Domain; ->innerJoin('Group_Members',
if(!self::$strict_subdomain_matching) $domainStr = preg_replace('/^www\./', '', $domainStr); "\"Group_Members\".\"GroupID\"=\"Group\".\"ID\" AND \"Group_Members\".\"MemberID\" = $member->ID")
$hostmap[$domainStr] = $subsite->domain(); ->innerJoin('Group_Roles', "\"Group_Roles\".\"GroupID\"=\"Group\".\"ID\"")
} ->innerJoin('PermissionRole', "\"Group_Roles\".\"PermissionRoleID\"=\"PermissionRole\".\"ID\"")
if ($subsite->DefaultSite) $hostmap['default'] = $subsite->domain(); ->innerJoin('PermissionRoleCode',
} "\"PermissionRole\".\"ID\"=\"PermissionRoleCode\".\"RoleID\" AND \"PermissionRoleCode\".\"Code\" IN ($SQL_codes, 'CMS_ACCESS_LeftAndMain', 'ADMIN')");
$data = "<?php \n"; if (!$subsites && $rolesSubsites) {
$data .= "// Generated by Subsite::writeHostMap() on " . date('d/M/y') . "\n"; return $rolesSubsites;
$data .= '$subsiteHostmap = ' . var_export($hostmap, true) . ';'; }
if (is_writable(dirname($file)) || is_writable($file)) { $subsites = new ArrayList($subsites->toArray());
file_put_contents($file, $data);
} if ($rolesSubsites) {
} foreach ($rolesSubsites as $subsite) {
if (!$subsites->find('ID', $subsite->ID)) {
/** $subsites->push($subsite);
* Checks if a member can be granted certain permissions, regardless of the subsite context. }
* Similar logic to {@link Permission::checkMember()}, but only returns TRUE }
* if the member is part of a group with the "AccessAllSubsites" flag set. }
* If more than one permission is passed to the method, at least one of them must
* be granted for if to return TRUE. if ($includeMainSite) {
* if (!is_array($permCode)) {
* @todo Allow permission inheritance through group hierarchy. $permCode = [$permCode];
* }
* @param Member Member to check against. Defaults to currently logged in member if (self::hasMainSitePermission($member, $permCode)) {
* @param Array Permission code strings. Defaults to "ADMIN". $subsites = $subsites->toArray();
* @return boolean
*/ $mainSite = new Subsite();
public static function hasMainSitePermission($member = null, $permissionCodes = array('ADMIN')) { $mainSite->Title = $mainSiteTitle;
if(!is_array($permissionCodes)) array_unshift($subsites, $mainSite);
user_error('Permissions must be passed to Subsite::hasMainSitePermission as an array', E_USER_ERROR); $subsites = ArrayList::create($subsites);
}
if(!$member && $member !== FALSE) $member = Member::currentUser(); }
if(!$member) return false; self::$_cache_accessible_sites[$cacheKey] = $subsites;
if(!in_array("ADMIN", $permissionCodes)) $permissionCodes[] = "ADMIN"; return $subsites;
}
$SQLa_perm = Convert::raw2sql($permissionCodes);
$SQL_perms = join("','", $SQLa_perm); /**
$memberID = (int)$member->ID; * Write a host->domain map to subsites/host-map.php
*
// Count this user's groups which can access the main site * This is used primarily when using subsites in conjunction with StaticPublisher
$groupCount = DB::query(" *
* @param string $file - filepath of the host map to be written
* @return void
*/
public static function writeHostMap($file = null)
{
if (!self::$write_hostmap) {
return;
}
if (!$file) {
$file = Director::baseFolder() . '/subsites/host-map.php';
}
$hostmap = [];
$subsites = DataObject::get(Subsite::class);
if ($subsites) {
foreach ($subsites as $subsite) {
$domains = $subsite->Domains();
if ($domains) {
foreach ($domains as $domain) {
$domainStr = $domain->Domain;
if (!self::$strict_subdomain_matching) {
$domainStr = preg_replace('/^www\./', '', $domainStr);
}
$hostmap[$domainStr] = $subsite->domain();
}
}
if ($subsite->DefaultSite) {
$hostmap['default'] = $subsite->domain();
}
}
}
$data = "<?php \n";
$data .= "// Generated by Subsite::writeHostMap() on " . date('d/M/y') . "\n";
$data .= '$subsiteHostmap = ' . var_export($hostmap, true) . ';';
if (is_writable(dirname($file)) || is_writable($file)) {
file_put_contents($file, $data);
}
}
/**
* Checks if a member can be granted certain permissions, regardless of the subsite context.
* Similar logic to {@link Permission::checkMember()}, but only returns TRUE
* if the member is part of a group with the "AccessAllSubsites" flag set.
* If more than one permission is passed to the method, at least one of them must
* be granted for if to return TRUE.
*
* @todo Allow permission inheritance through group hierarchy.
*
* @param Member Member to check against. Defaults to currently logged in member
* @param Array Permission code strings. Defaults to "ADMIN".
* @return boolean
*/
public static function hasMainSitePermission($member = null, $permissionCodes = ['ADMIN'])
{
if (!is_array($permissionCodes)) {
user_error('Permissions must be passed to Subsite::hasMainSitePermission as an array', E_USER_ERROR);
}
if (!$member && $member !== FALSE) {
$member = Member::currentUser();
}
if (!$member) {
return false;
}
if (!in_array("ADMIN", $permissionCodes)) {
$permissionCodes[] = "ADMIN";
}
$SQLa_perm = Convert::raw2sql($permissionCodes);
$SQL_perms = join("','", $SQLa_perm);
$memberID = (int)$member->ID;
// Count this user's groups which can access the main site
$groupCount = DB::query("
SELECT COUNT(\"Permission\".\"ID\") SELECT COUNT(\"Permission\".\"ID\")
FROM \"Permission\" FROM \"Permission\"
INNER JOIN \"Group\" ON \"Group\".\"ID\" = \"Permission\".\"GroupID\" AND \"Group\".\"AccessAllSubsites\" = 1 INNER JOIN \"Group\" ON \"Group\".\"ID\" = \"Permission\".\"GroupID\" AND \"Group\".\"AccessAllSubsites\" = 1
@ -476,8 +554,8 @@ class Subsite extends DataObject {
AND \"MemberID\" = {$memberID} AND \"MemberID\" = {$memberID}
")->value(); ")->value();
// Count this user's groups which have a role that can access the main site // Count this user's groups which have a role that can access the main site
$roleCount = DB::query(" $roleCount = DB::query("
SELECT COUNT(\"PermissionRoleCode\".\"ID\") SELECT COUNT(\"PermissionRoleCode\".\"ID\")
FROM \"Group\" FROM \"Group\"
INNER JOIN \"Group_Members\" ON \"Group_Members\".\"GroupID\" = \"Group\".\"ID\" INNER JOIN \"Group_Members\" ON \"Group_Members\".\"GroupID\" = \"Group\".\"ID\"
@ -489,369 +567,388 @@ class Subsite extends DataObject {
AND \"MemberID\" = {$memberID} AND \"MemberID\" = {$memberID}
")->value(); ")->value();
// There has to be at least one that allows access. // There has to be at least one that allows access.
return ($groupCount + $roleCount > 0); return ($groupCount + $roleCount > 0);
} }
/** /**
* *
* @var array * @var array
*/ */
private static $db = array( private static $db = [
'Title' => 'Varchar(255)', 'Title' => 'Varchar(255)',
'RedirectURL' => 'Varchar(255)', 'RedirectURL' => 'Varchar(255)',
'DefaultSite' => 'Boolean', 'DefaultSite' => 'Boolean',
'Theme' => 'Varchar', 'Theme' => 'Varchar',
'Language' => 'Varchar(6)', 'Language' => 'Varchar(6)',
// Used to hide unfinished/private subsites from public view. // Used to hide unfinished/private subsites from public view.
// If unset, will default to true // If unset, will default to true
'IsPublic' => 'Boolean', 'IsPublic' => 'Boolean',
// Comma-separated list of disallowed page types // Comma-separated list of disallowed page types
'PageTypeBlacklist' => 'Text', 'PageTypeBlacklist' => 'Text',
); ];
/** /**
* *
* @var array * @var array
*/ */
private static $has_many = array( private static $has_many = [
'Domains' => SubsiteDomain::class, 'Domains' => SubsiteDomain::class,
); ];
/** /**
* *
* @var array * @var array
*/ */
private static $belongs_many_many = array( private static $belongs_many_many = [
"Groups" => "SilverStripe\\Security\\Group", "Groups" => "SilverStripe\\Security\\Group",
); ];
/** /**
* *
* @var array * @var array
*/ */
private static $defaults = array( private static $defaults = [
'IsPublic' => 1 'IsPublic' => 1
); ];
/** /**
* *
* @var array * @var array
*/ */
private static $searchable_fields = array( private static $searchable_fields = [
'Title', 'Title',
'Domains.Domain', 'Domains.Domain',
'IsPublic', 'IsPublic',
); ];
/** /**
* *
* @var string * @var string
*/ */
private static $default_sort = "\"Title\" ASC"; private static $default_sort = "\"Title\" ASC";
/** /**
* @todo Possible security issue, don't grant edit permissions to everybody. * @todo Possible security issue, don't grant edit permissions to everybody.
* @return boolean * @return boolean
*/ */
public function canEdit($member = false) { public function canEdit($member = false)
return true; {
} return true;
}
/** /**
* Show the configuration fields for each subsite * Show the configuration fields for each subsite
* *
* @return FieldList * @return FieldList
*/ */
public function getCMSFields() { public function getCMSFields()
if($this->ID!=0) { {
$domainTable = new GridField( if ($this->ID != 0) {
"Domains", $domainTable = new GridField(
_t('Subsite.DomainsListTitle',"Domains"), "Domains",
$this->Domains(), _t('Subsite.DomainsListTitle', "Domains"),
GridFieldConfig_RecordEditor::create(10) $this->Domains(),
); GridFieldConfig_RecordEditor::create(10)
}else { );
$domainTable = new LiteralField( } else {
'Domains', $domainTable = new LiteralField(
'<p>'._t('Subsite.DOMAINSAVEFIRST', 'You can only add domains after saving for the first time').'</p>' 'Domains',
); '<p>' . _t('Subsite.DOMAINSAVEFIRST',
} 'You can only add domains after saving for the first time') . '</p>'
);
}
$languageSelector = new DropdownField( $languageSelector = new DropdownField(
'Language', 'Language',
$this->fieldLabel('Language'), $this->fieldLabel('Language'),
(new IntlLocales)->getLocales() (new IntlLocales)->getLocales()
); );
$pageTypeMap = array(); $pageTypeMap = [];
$pageTypes = SiteTree::page_type_classes(); $pageTypes = SiteTree::page_type_classes();
foreach($pageTypes as $pageType) { foreach ($pageTypes as $pageType) {
$pageTypeMap[$pageType] = singleton($pageType)->i18n_singular_name(); $pageTypeMap[$pageType] = singleton($pageType)->i18n_singular_name();
} }
asort($pageTypeMap); asort($pageTypeMap);
$fields = new FieldList( $fields = new FieldList(
$subsiteTabs = new TabSet('Root', $subsiteTabs = new TabSet('Root',
new Tab( new Tab(
'Configuration', 'Configuration',
_t('Subsite.TabTitleConfig', 'Configuration'), _t('Subsite.TabTitleConfig', 'Configuration'),
new HeaderField('ConfigurationHeader', $this->getClassName() . ' configuration', 2), new HeaderField('ConfigurationHeader', $this->getClassName() . ' configuration', 2),
new TextField('Title', $this->fieldLabel('Title'), $this->Title), new TextField('Title', $this->fieldLabel('Title'), $this->Title),
new HeaderField( new HeaderField(
'DomainsHeader', 'DomainsHeader',
_t('Subsite.DomainsHeadline', "Domains for this subsite") _t('Subsite.DomainsHeadline', "Domains for this subsite")
), ),
$domainTable, $domainTable,
$languageSelector, $languageSelector,
// new TextField('RedirectURL', 'Redirect to URL', $this->RedirectURL), // new TextField('RedirectURL', 'Redirect to URL', $this->RedirectURL),
new CheckboxField('DefaultSite', $this->fieldLabel('DefaultSite'), $this->DefaultSite), new CheckboxField('DefaultSite', $this->fieldLabel('DefaultSite'), $this->DefaultSite),
new CheckboxField('IsPublic', $this->fieldLabel('IsPublic'), $this->IsPublic), new CheckboxField('IsPublic', $this->fieldLabel('IsPublic'), $this->IsPublic),
new DropdownField('Theme',$this->fieldLabel('Theme'), $this->allowedThemes(), $this->Theme), new DropdownField('Theme', $this->fieldLabel('Theme'), $this->allowedThemes(), $this->Theme),
new LiteralField( new LiteralField(
'PageTypeBlacklistToggle', 'PageTypeBlacklistToggle',
sprintf( sprintf(
'<div class="field"><a href="#" id="PageTypeBlacklistToggle">%s</a></div>', '<div class="field"><a href="#" id="PageTypeBlacklistToggle">%s</a></div>',
_t('Subsite.PageTypeBlacklistField', 'Disallow page types?') _t('Subsite.PageTypeBlacklistField', 'Disallow page types?')
) )
), ),
new CheckboxSetField( new CheckboxSetField(
'PageTypeBlacklist', 'PageTypeBlacklist',
false, false,
$pageTypeMap $pageTypeMap
) )
) )
), ),
new HiddenField('ID', '', $this->ID), new HiddenField('ID', '', $this->ID),
new HiddenField('IsSubsite', '', 1) new HiddenField('IsSubsite', '', 1)
); );
$subsiteTabs->addExtraClass('subsite-model'); $subsiteTabs->addExtraClass('subsite-model');
$this->extend('updateCMSFields', $fields); $this->extend('updateCMSFields', $fields);
return $fields; return $fields;
} }
/** /**
* *
* @param boolean $includerelations * @param boolean $includerelations
* @return array * @return array
*/ */
public function fieldLabels($includerelations = true) { public function fieldLabels($includerelations = true)
$labels = parent::fieldLabels($includerelations); {
$labels['Title'] = _t('Subsites.TitleFieldLabel', 'Subsite Name'); $labels = parent::fieldLabels($includerelations);
$labels['RedirectURL'] = _t('Subsites.RedirectURLFieldLabel', 'Redirect URL'); $labels['Title'] = _t('Subsites.TitleFieldLabel', 'Subsite Name');
$labels['DefaultSite'] = _t('Subsites.DefaultSiteFieldLabel', 'Default site'); $labels['RedirectURL'] = _t('Subsites.RedirectURLFieldLabel', 'Redirect URL');
$labels['Theme'] = _t('Subsites.ThemeFieldLabel', 'Theme'); $labels['DefaultSite'] = _t('Subsites.DefaultSiteFieldLabel', 'Default site');
$labels['Language'] = _t('Subsites.LanguageFieldLabel', 'Language'); $labels['Theme'] = _t('Subsites.ThemeFieldLabel', 'Theme');
$labels['IsPublic'] = _t('Subsites.IsPublicFieldLabel', 'Enable public access'); $labels['Language'] = _t('Subsites.LanguageFieldLabel', 'Language');
$labels['PageTypeBlacklist'] = _t('Subsites.PageTypeBlacklistFieldLabel', 'Page Type Blacklist'); $labels['IsPublic'] = _t('Subsites.IsPublicFieldLabel', 'Enable public access');
$labels['Domains.Domain'] = _t('Subsites.DomainFieldLabel', 'Domain'); $labels['PageTypeBlacklist'] = _t('Subsites.PageTypeBlacklistFieldLabel', 'Page Type Blacklist');
$labels['PrimaryDomain'] = _t('Subsites.PrimaryDomainFieldLabel', 'Primary Domain'); $labels['Domains.Domain'] = _t('Subsites.DomainFieldLabel', 'Domain');
$labels['PrimaryDomain'] = _t('Subsites.PrimaryDomainFieldLabel', 'Primary Domain');
return $labels; return $labels;
} }
/** /**
* Return the themes that can be used with this subsite, as an array of themecode => description * Return the themes that can be used with this subsite, as an array of themecode => description
* *
* @return array * @return array
*/ */
public function allowedThemes() { public function allowedThemes()
if($themes = $this->stat('allowed_themes')) { {
return ArrayLib::valuekey($themes); if ($themes = $this->stat('allowed_themes')) {
} else { return ArrayLib::valuekey($themes);
$themes = array(); } else {
if(is_dir('../themes/')) { $themes = [];
foreach(scandir('../themes/') as $theme) { if (is_dir('../themes/')) {
if($theme[0] == '.') continue; foreach (scandir('../themes/') as $theme) {
$theme = strtok($theme,'_'); if ($theme[0] == '.') {
$themes[$theme] = $theme; continue;
} }
ksort($themes); $theme = strtok($theme, '_');
} $themes[$theme] = $theme;
return $themes; }
} ksort($themes);
} }
return $themes;
}
}
/** /**
* @return string Current locale of the subsite * @return string Current locale of the subsite
*/ */
public function getLanguage() { public function getLanguage()
if($this->getField('Language')) { {
return $this->getField('Language'); if ($this->getField('Language')) {
} else { return $this->getField('Language');
return i18n::get_locale(); } else {
} return i18n::get_locale();
} }
}
/** /**
* *
* @return ValidationResult * @return ValidationResult
*/ */
public function validate() { public function validate()
$result = parent::validate(); {
if(!$this->Title) { $result = parent::validate();
$result->error(_t('Subsite.ValidateTitle', 'Please add a "Title"')); if (!$this->Title) {
} $result->error(_t('Subsite.ValidateTitle', 'Please add a "Title"'));
return $result; }
} return $result;
}
/** /**
* Whenever a Subsite is written, rewrite the hostmap * Whenever a Subsite is written, rewrite the hostmap
* *
* @return void * @return void
*/ */
public function onAfterWrite() { public function onAfterWrite()
Subsite::writeHostMap(); {
parent::onAfterWrite(); Subsite::writeHostMap();
} parent::onAfterWrite();
}
/** /**
* Return the primary domain of this site. Tries to "normalize" the domain name, * Return the primary domain of this site. Tries to "normalize" the domain name,
* by replacing potential wildcards. * by replacing potential wildcards.
* *
* @return string The full domain name of this subsite (without protocol prefix) * @return string The full domain name of this subsite (without protocol prefix)
*/ */
public function domain() { public function domain()
if($this->ID) { {
$domains = DataObject::get(SubsiteDomain::class, "\"SubsiteID\" = $this->ID", "\"IsPrimary\" DESC","", 1); if ($this->ID) {
if($domains && $domains->Count()>0) { $domains = DataObject::get(SubsiteDomain::class, "\"SubsiteID\" = $this->ID", "\"IsPrimary\" DESC", "", 1);
$domain = $domains->First()->Domain; if ($domains && $domains->Count() > 0) {
// If there are wildcards in the primary domain (not recommended), make some $domain = $domains->First()->Domain;
// educated guesses about what to replace them with: // If there are wildcards in the primary domain (not recommended), make some
$domain = preg_replace('/\.\*$/',".$_SERVER[HTTP_HOST]", $domain); // educated guesses about what to replace them with:
// Default to "subsite." prefix for first wildcard $domain = preg_replace('/\.\*$/', ".$_SERVER[HTTP_HOST]", $domain);
// TODO Whats the significance of "subsite" in this context?! // Default to "subsite." prefix for first wildcard
$domain = preg_replace('/^\*\./',"subsite.", $domain); // TODO Whats the significance of "subsite" in this context?!
// *Only* removes "intermediate" subdomains, so 'subdomain.www.domain.com' becomes 'subdomain.domain.com' $domain = preg_replace('/^\*\./', "subsite.", $domain);
$domain = str_replace('.www.','.', $domain); // *Only* removes "intermediate" subdomains, so 'subdomain.www.domain.com' becomes 'subdomain.domain.com'
$domain = str_replace('.www.', '.', $domain);
return $domain; return $domain;
} }
// SubsiteID = 0 is often used to refer to the main site, just return $_SERVER['HTTP_HOST'] // SubsiteID = 0 is often used to refer to the main site, just return $_SERVER['HTTP_HOST']
} else { } else {
return $_SERVER['HTTP_HOST']; return $_SERVER['HTTP_HOST'];
} }
} }
/** /**
* *
* @return string - The full domain name of this subsite (without protocol prefix) * @return string - The full domain name of this subsite (without protocol prefix)
*/ */
public function getPrimaryDomain() { public function getPrimaryDomain()
return $this->domain(); {
} return $this->domain();
}
/** /**
* *
* @return string * @return string
*/ */
public function absoluteBaseURL() { public function absoluteBaseURL()
return "http://" . $this->domain() . Director::baseURL(); {
} return "http://" . $this->domain() . Director::baseURL();
}
/** /**
* @todo getClassName is redundant, already stored as a database field? * @todo getClassName is redundant, already stored as a database field?
*/ */
public function getClassName() { public function getClassName()
return $this->class; {
} return $this->class;
}
/** /**
* Javascript admin action to duplicate this subsite * Javascript admin action to duplicate this subsite
* *
* @return string - javascript * @return string - javascript
*/ */
public function adminDuplicate() { public function adminDuplicate()
$newItem = $this->duplicate(); {
$message = _t( $newItem = $this->duplicate();
'Subsite.CopyMessage', $message = _t(
'Created a copy of {title}', 'Subsite.CopyMessage',
array('title' => Convert::raw2js($this->Title)) 'Created a copy of {title}',
); ['title' => Convert::raw2js($this->Title)]
);
return <<<JS return <<<JS
statusMessage($message, 'good'); statusMessage($message, 'good');
$('Form_EditForm').loadURLFromServer('admin/subsites/show/$newItem->ID'); $('Form_EditForm').loadURLFromServer('admin/subsites/show/$newItem->ID');
JS; JS;
} }
/** /**
* Make this subsite the current one * Make this subsite the current one
*/ */
public function activate() { public function activate()
Subsite::changeSubsite($this); {
} Subsite::changeSubsite($this);
}
/** /**
* *
* @param array $permissionCodes * @param array $permissionCodes
* @return DataList * @return DataList
*/ */
public function getMembersByPermission($permissionCodes = array('ADMIN')){ public function getMembersByPermission($permissionCodes = ['ADMIN'])
if(!is_array($permissionCodes)) {
user_error('Permissions must be passed to Subsite::getMembersByPermission as an array', E_USER_ERROR); if (!is_array($permissionCodes)) {
$SQL_permissionCodes = Convert::raw2sql($permissionCodes); user_error('Permissions must be passed to Subsite::getMembersByPermission as an array', E_USER_ERROR);
}
$SQL_permissionCodes = Convert::raw2sql($permissionCodes);
$SQL_permissionCodes = join("','", $SQL_permissionCodes); $SQL_permissionCodes = join("','", $SQL_permissionCodes);
return DataObject::get( return DataObject::get(
'SilverStripe\\Security\\Member', 'SilverStripe\\Security\\Member',
"\"Group\".\"SubsiteID\" = $this->ID AND \"Permission\".\"Code\" IN ('$SQL_permissionCodes')", "\"Group\".\"SubsiteID\" = $this->ID AND \"Permission\".\"Code\" IN ('$SQL_permissionCodes')",
'', '',
"LEFT JOIN \"Group_Members\" ON \"Member\".\"ID\" = \"Group_Members\".\"MemberID\" "LEFT JOIN \"Group_Members\" ON \"Member\".\"ID\" = \"Group_Members\".\"MemberID\"
LEFT JOIN \"Group\" ON \"Group\".\"ID\" = \"Group_Members\".\"GroupID\" LEFT JOIN \"Group\" ON \"Group\".\"ID\" = \"Group_Members\".\"GroupID\"
LEFT JOIN \"Permission\" ON \"Permission\".\"GroupID\" = \"Group\".\"ID\"" LEFT JOIN \"Permission\" ON \"Permission\".\"GroupID\" = \"Group\".\"ID\""
); );
} }
/** /**
* Duplicate this subsite * Duplicate this subsite
*/ */
public function duplicate($doWrite = true) { public function duplicate($doWrite = true)
$duplicate = parent::duplicate($doWrite); {
$duplicate = parent::duplicate($doWrite);
$oldSubsiteID = Session::get('SubsiteID'); $oldSubsiteID = Session::get('SubsiteID');
self::changeSubsite($this->ID); self::changeSubsite($this->ID);
/* /*
* Copy data from this object to the given subsite. Does this using an iterative depth-first search. * Copy data from this object to the given subsite. Does this using an iterative depth-first search.
* This will make sure that the new parents on the new subsite are correct, and there are no funny * This will make sure that the new parents on the new subsite are correct, and there are no funny
* issues with having to check whether or not the new parents have been added to the site tree * issues with having to check whether or not the new parents have been added to the site tree
* when a page, etc, is duplicated * when a page, etc, is duplicated
*/ */
$stack = array(array(0,0)); $stack = [[0, 0]];
while(count($stack) > 0) { while (count($stack) > 0) {
list($sourceParentID, $destParentID) = array_pop($stack); list($sourceParentID, $destParentID) = array_pop($stack);
$children = Versioned::get_by_stage('Page', 'Live', "\"ParentID\" = $sourceParentID", ''); $children = Versioned::get_by_stage('Page', 'Live', "\"ParentID\" = $sourceParentID", '');
if($children) { if ($children) {
foreach($children as $child) { foreach ($children as $child) {
self::changeSubsite($duplicate->ID); //Change to destination subsite self::changeSubsite($duplicate->ID); //Change to destination subsite
$childClone = $child->duplicateToSubsite($duplicate, false); $childClone = $child->duplicateToSubsite($duplicate, false);
$childClone->ParentID = $destParentID; $childClone->ParentID = $destParentID;
$childClone->writeToStage('Stage'); $childClone->writeToStage('Stage');
$childClone->publish('Stage', 'Live'); $childClone->publish('Stage', 'Live');
self::changeSubsite($this->ID); //Change Back to this subsite self::changeSubsite($this->ID); //Change Back to this subsite
array_push($stack, array($child->ID, $childClone->ID)); array_push($stack, [$child->ID, $childClone->ID]);
} }
} }
} }
self::changeSubsite($oldSubsiteID); self::changeSubsite($oldSubsiteID);
return $duplicate; return $duplicate;
} }
} }

View File

@ -3,98 +3,102 @@
namespace SilverStripe\Subsites\Model; namespace SilverStripe\Subsites\Model;
use SilverStripe\Forms\TextField; use SilverStripe\Core\Convert;
use SilverStripe\Forms\CheckboxField; use SilverStripe\Forms\CheckboxField;
use SilverStripe\Forms\FieldList; use SilverStripe\Forms\FieldList;
use SilverStripe\Core\Convert; use SilverStripe\Forms\TextField;
use SilverStripe\ORM\DataObject; use SilverStripe\ORM\DataObject;
/** /**
* @property text Domain domain name of this subsite. Do not include the URL scheme here * @property text Domain domain name of this subsite. Do not include the URL scheme here
* @property bool IsPrimary Is this the primary subdomain? * @property bool IsPrimary Is this the primary subdomain?
*/ */
class SubsiteDomain extends DataObject { class SubsiteDomain extends DataObject
{
private static $table_name = 'SubsiteDomain'; private static $table_name = 'SubsiteDomain';
/** /**
* *
* @var string * @var string
*/ */
private static $default_sort = "\"IsPrimary\" DESC"; private static $default_sort = "\"IsPrimary\" DESC";
/** /**
* *
* @var array * @var array
*/ */
private static $db = array( private static $db = [
"Domain" => "Varchar(255)", "Domain" => "Varchar(255)",
"IsPrimary" => "Boolean", "IsPrimary" => "Boolean",
); ];
/** /**
* *
* @var array * @var array
*/ */
private static $has_one = array( private static $has_one = [
"Subsite" => Subsite::class, "Subsite" => Subsite::class,
); ];
/** /**
* *
* @var array * @var array
*/ */
private static $summary_fields=array( private static $summary_fields = [
'Domain', 'Domain',
'IsPrimary', 'IsPrimary',
); ];
/** /**
* Whenever a Subsite Domain is written, rewrite the hostmap * Whenever a Subsite Domain is written, rewrite the hostmap
* *
* @return void * @return void
*/ */
public function onAfterWrite() { public function onAfterWrite()
Subsite::writeHostMap(); {
} Subsite::writeHostMap();
}
/** /**
* *
* @return \FieldList * @return \FieldList
*/ */
public function getCMSFields() { public function getCMSFields()
$fields = new FieldList( {
new TextField('Domain', $this->fieldLabel('Domain'), null, 255), $fields = new FieldList(
new CheckboxField('IsPrimary', $this->fieldLabel('IsPrimary')) new TextField('Domain', $this->fieldLabel('Domain'), null, 255),
); new CheckboxField('IsPrimary', $this->fieldLabel('IsPrimary'))
);
$this->extend('updateCMSFields', $fields); $this->extend('updateCMSFields', $fields);
return $fields; return $fields;
} }
/** /**
* *
* @param bool $includerelations * @param bool $includerelations
* @return array * @return array
*/ */
public function fieldLabels($includerelations = true) { public function fieldLabels($includerelations = true)
$labels = parent::fieldLabels($includerelations); {
$labels['Domain'] = _t('SubsiteDomain.DOMAIN', 'Domain'); $labels = parent::fieldLabels($includerelations);
$labels['IsPrimary'] = _t('SubsiteDomain.IS_PRIMARY', 'Is Primary Domain'); $labels['Domain'] = _t('SubsiteDomain.DOMAIN', 'Domain');
$labels['IsPrimary'] = _t('SubsiteDomain.IS_PRIMARY', 'Is Primary Domain');
return $labels; return $labels;
} }
/** /**
* Before writing the Subsite Domain, strip out any HTML the user has entered. * Before writing the Subsite Domain, strip out any HTML the user has entered.
* @return void * @return void
*/ */
public function onBeforeWrite() { public function onBeforeWrite()
parent::onBeforeWrite(); {
parent::onBeforeWrite();
//strip out any HTML to avoid XSS attacks //strip out any HTML to avoid XSS attacks
$this->Domain = Convert::html2raw($this->Domain); $this->Domain = Convert::html2raw($this->Domain);
} }
} }

View File

@ -3,213 +3,234 @@
namespace SilverStripe\Subsites\Pages; namespace SilverStripe\Subsites\Pages;
use SilverStripe\ORM\DataObject; use SilverStripe\CMS\Model\VirtualPage;
use SilverStripe\ORM\ArrayList;
use SilverStripe\View\ArrayData;
use SilverStripe\Forms\DropdownField;
use SilverStripe\Control\Controller; use SilverStripe\Control\Controller;
use SilverStripe\Forms\LabelField;
use SilverStripe\Forms\TextField;
use SilverStripe\Forms\TextareaField;
use SilverStripe\Control\Session; use SilverStripe\Control\Session;
use SilverStripe\Core\Config\Config; use SilverStripe\Core\Config\Config;
use SilverStripe\CMS\Model\VirtualPage; use SilverStripe\Forms\DropdownField;
use SilverStripe\Subsites\Model\Subsite; use SilverStripe\Forms\LabelField;
use SilverStripe\Forms\TextareaField;
use SilverStripe\Forms\TextField;
use SilverStripe\ORM\ArrayList;
use SilverStripe\ORM\DataObject;
use SilverStripe\Subsites\Forms\SubsitesTreeDropdownField; use SilverStripe\Subsites\Forms\SubsitesTreeDropdownField;
use SilverStripe\Subsites\Model\Subsite;
use SilverStripe\View\ArrayData;
class SubsitesVirtualPage extends VirtualPage { class SubsitesVirtualPage extends VirtualPage
{
private static $table_name = 'SubsitesVirtualPage'; private static $table_name = 'SubsitesVirtualPage';
private static $description = 'Displays the content of a page on another subsite'; private static $description = 'Displays the content of a page on another subsite';
private static $db = array( private static $db = [
'CustomMetaTitle' => 'Varchar(255)', 'CustomMetaTitle' => 'Varchar(255)',
'CustomMetaKeywords' => 'Varchar(255)', 'CustomMetaKeywords' => 'Varchar(255)',
'CustomMetaDescription' => 'Text', 'CustomMetaDescription' => 'Text',
'CustomExtraMeta' => 'HTMLText' 'CustomExtraMeta' => 'HTMLText'
); ];
public function getCMSFields() { public function getCMSFields()
$fields = parent::getCMSFields(); {
$fields = parent::getCMSFields();
$subsites = DataObject::get(Subsite::class); $subsites = DataObject::get(Subsite::class);
if(!$subsites) { if (!$subsites) {
$subsites = new ArrayList(); $subsites = new ArrayList();
}else { } else {
$subsites=ArrayList::create($subsites->toArray()); $subsites = ArrayList::create($subsites->toArray());
} }
$subsites->push(new ArrayData(array('Title' => 'Main site', 'ID' => 0))); $subsites->push(new ArrayData(['Title' => 'Main site', 'ID' => 0]));
$fields->addFieldToTab( $fields->addFieldToTab(
'Root.Main', 'Root.Main',
DropdownField::create( DropdownField::create(
"CopyContentFromID_SubsiteID", "CopyContentFromID_SubsiteID",
_t('SubsitesVirtualPage.SubsiteField',Subsite::class), _t('SubsitesVirtualPage.SubsiteField', Subsite::class),
$subsites->map('ID', 'Title') $subsites->map('ID', 'Title')
)->addExtraClass('subsitestreedropdownfield-chooser no-change-track'), )->addExtraClass('subsitestreedropdownfield-chooser no-change-track'),
'CopyContentFromID' 'CopyContentFromID'
); );
// Setup the linking to the original page. // Setup the linking to the original page.
$pageSelectionField = new SubsitesTreeDropdownField( $pageSelectionField = new SubsitesTreeDropdownField(
"CopyContentFromID", "CopyContentFromID",
_t('VirtualPage.CHOOSE', "Choose a page to link to"), _t('VirtualPage.CHOOSE', "Choose a page to link to"),
"SilverStripe\\CMS\\Model\\SiteTree", "SilverStripe\\CMS\\Model\\SiteTree",
"ID", "ID",
"MenuTitle" "MenuTitle"
); );
if(Controller::has_curr() && Controller::curr()->getRequest()) { if (Controller::has_curr() && Controller::curr()->getRequest()) {
$subsiteID = Controller::curr()->getRequest()->requestVar('CopyContentFromID_SubsiteID'); $subsiteID = Controller::curr()->getRequest()->requestVar('CopyContentFromID_SubsiteID');
$pageSelectionField->setSubsiteID($subsiteID); $pageSelectionField->setSubsiteID($subsiteID);
} }
$fields->replaceField('CopyContentFromID', $pageSelectionField); $fields->replaceField('CopyContentFromID', $pageSelectionField);
// Create links back to the original object in the CMS // Create links back to the original object in the CMS
if($this->CopyContentFromID) { if ($this->CopyContentFromID) {
$editLink = "admin/pages/edit/show/$this->CopyContentFromID/?SubsiteID=" . $this->CopyContentFrom()->SubsiteID; $editLink = "admin/pages/edit/show/$this->CopyContentFromID/?SubsiteID=" . $this->CopyContentFrom()->SubsiteID;
$linkToContent = " $linkToContent = "
<a class=\"cmsEditlink\" href=\"$editLink\">" . <a class=\"cmsEditlink\" href=\"$editLink\">" .
_t('VirtualPage.EDITCONTENT', 'Click here to edit the content') . _t('VirtualPage.EDITCONTENT', 'Click here to edit the content') .
"</a>"; "</a>";
$fields->removeByName("VirtualPageContentLinkLabel"); $fields->removeByName("VirtualPageContentLinkLabel");
$fields->addFieldToTab( $fields->addFieldToTab(
"Root.Main", "Root.Main",
$linkToContentLabelField = new LabelField('VirtualPageContentLinkLabel', $linkToContent), $linkToContentLabelField = new LabelField('VirtualPageContentLinkLabel', $linkToContent),
'Title' 'Title'
); );
$linkToContentLabelField->setAllowHTML(true); $linkToContentLabelField->setAllowHTML(true);
} }
$fields->addFieldToTab( $fields->addFieldToTab(
'Root.Main', 'Root.Main',
TextField::create( TextField::create(
'CustomMetaTitle', 'CustomMetaTitle',
$this->fieldLabel('CustomMetaTitle') $this->fieldLabel('CustomMetaTitle')
)->setDescription(_t('SubsitesVirtualPage.OverrideNote', 'Overrides inherited value from the source')), )->setDescription(_t('SubsitesVirtualPage.OverrideNote', 'Overrides inherited value from the source')),
'MetaTitle' 'MetaTitle'
); );
$fields->addFieldToTab( $fields->addFieldToTab(
'Root.Main', 'Root.Main',
TextareaField::create( TextareaField::create(
'CustomMetaKeywords', 'CustomMetaKeywords',
$this->fieldLabel('CustomMetaTitle') $this->fieldLabel('CustomMetaTitle')
)->setDescription(_t('SubsitesVirtualPage.OverrideNote')), )->setDescription(_t('SubsitesVirtualPage.OverrideNote')),
'MetaKeywords' 'MetaKeywords'
); );
$fields->addFieldToTab( $fields->addFieldToTab(
'Root.Main', 'Root.Main',
TextareaField::create( TextareaField::create(
'CustomMetaDescription', 'CustomMetaDescription',
$this->fieldLabel('CustomMetaTitle') $this->fieldLabel('CustomMetaTitle')
)->setDescription(_t('SubsitesVirtualPage.OverrideNote')), )->setDescription(_t('SubsitesVirtualPage.OverrideNote')),
'MetaDescription' 'MetaDescription'
); );
$fields->addFieldToTab( $fields->addFieldToTab(
'Root.Main', 'Root.Main',
TextField::create( TextField::create(
'CustomExtraMeta', 'CustomExtraMeta',
$this->fieldLabel('CustomMetaTitle') $this->fieldLabel('CustomMetaTitle')
)->setDescription(_t('SubsitesVirtualPage.OverrideNote')), )->setDescription(_t('SubsitesVirtualPage.OverrideNote')),
'ExtraMeta' 'ExtraMeta'
); );
return $fields; return $fields;
} }
public function fieldLabels($includerelations = true) { public function fieldLabels($includerelations = true)
$labels = parent::fieldLabels($includerelations); {
$labels['CustomMetaTitle'] = _t('Subsite.CustomMetaTitle','Title'); $labels = parent::fieldLabels($includerelations);
$labels['CustomMetaKeywords'] = _t('Subsite.CustomMetaKeywords','Keywords'); $labels['CustomMetaTitle'] = _t('Subsite.CustomMetaTitle', 'Title');
$labels['CustomMetaDescription'] = _t('Subsite.CustomMetaDescription','Description'); $labels['CustomMetaKeywords'] = _t('Subsite.CustomMetaKeywords', 'Keywords');
$labels['CustomExtraMeta'] = _t('Subsite.CustomExtraMeta','Custom Meta Tags'); $labels['CustomMetaDescription'] = _t('Subsite.CustomMetaDescription', 'Description');
$labels['CustomExtraMeta'] = _t('Subsite.CustomExtraMeta', 'Custom Meta Tags');
return $labels; return $labels;
} }
public function getCopyContentFromID_SubsiteID() { public function getCopyContentFromID_SubsiteID()
return ($this->CopyContentFromID) ? (int)$this->CopyContentFrom()->SubsiteID : (int)Session::get('SubsiteID'); {
} return ($this->CopyContentFromID) ? (int)$this->CopyContentFrom()->SubsiteID : (int)Session::get('SubsiteID');
}
public function getVirtualFields() { public function getVirtualFields()
$fields = parent::getVirtualFields(); {
foreach($fields as $k => $v) { $fields = parent::getVirtualFields();
if($v == 'SubsiteID') unset($fields[$k]); foreach ($fields as $k => $v) {
} if ($v == 'SubsiteID') {
unset($fields[$k]);
}
}
foreach(self::$db as $field => $type) if (in_array($field, $fields)) unset($fields[array_search($field, $fields)]); foreach (self::$db as $field => $type) {
if (in_array($field, $fields)) {
unset($fields[array_search($field, $fields)]);
}
}
return $fields; return $fields;
} }
public function syncLinkTracking() { public function syncLinkTracking()
$oldState = Subsite::$disable_subsite_filter; {
Subsite::$disable_subsite_filter = true; $oldState = Subsite::$disable_subsite_filter;
if ($this->CopyContentFromID) $this->HasBrokenLink = DataObject::get_by_id('SilverStripe\\CMS\\Model\\SiteTree', $this->CopyContentFromID) ? false : true; Subsite::$disable_subsite_filter = true;
Subsite::$disable_subsite_filter = $oldState; if ($this->CopyContentFromID) {
} $this->HasBrokenLink = DataObject::get_by_id('SilverStripe\\CMS\\Model\\SiteTree',
$this->CopyContentFromID) ? false : true;
}
Subsite::$disable_subsite_filter = $oldState;
}
public function onBeforeWrite() { public function onBeforeWrite()
parent::onBeforeWrite(); {
parent::onBeforeWrite();
if($this->CustomMetaTitle) $this->MetaTitle = $this->CustomMetaTitle; if ($this->CustomMetaTitle) {
else { $this->MetaTitle = $this->CustomMetaTitle;
$this->MetaTitle = $this->ContentSource()->MetaTitle ? $this->ContentSource()->MetaTitle : $this->MetaTitle; } else {
} $this->MetaTitle = $this->ContentSource()->MetaTitle ? $this->ContentSource()->MetaTitle : $this->MetaTitle;
if($this->CustomMetaKeywords) $this->MetaKeywords = $this->CustomMetaKeywords; }
else { if ($this->CustomMetaKeywords) {
$this->MetaKeywords = $this->ContentSource()->MetaKeywords ? $this->ContentSource()->MetaKeywords : $this->MetaKeywords; $this->MetaKeywords = $this->CustomMetaKeywords;
} } else {
if($this->CustomMetaDescription) $this->MetaDescription = $this->CustomMetaDescription; $this->MetaKeywords = $this->ContentSource()->MetaKeywords ? $this->ContentSource()->MetaKeywords : $this->MetaKeywords;
else { }
$this->MetaDescription = $this->ContentSource()->MetaDescription ? $this->ContentSource()->MetaDescription : $this->MetaDescription; if ($this->CustomMetaDescription) {
} $this->MetaDescription = $this->CustomMetaDescription;
if($this->CustomExtraMeta) $this->ExtraMeta = $this->CustomExtraMeta; } else {
else { $this->MetaDescription = $this->ContentSource()->MetaDescription ? $this->ContentSource()->MetaDescription : $this->MetaDescription;
$this->ExtraMeta = $this->ContentSource()->ExtraMeta ? $this->ContentSource()->ExtraMeta : $this->ExtraMeta; }
} if ($this->CustomExtraMeta) {
} $this->ExtraMeta = $this->CustomExtraMeta;
} else {
$this->ExtraMeta = $this->ContentSource()->ExtraMeta ? $this->ContentSource()->ExtraMeta : $this->ExtraMeta;
}
}
public function validURLSegment() { public function validURLSegment()
$isValid = parent::validURLSegment(); {
$isValid = parent::validURLSegment();
// Veto the validation rules if its false. In this case, some logic // Veto the validation rules if its false. In this case, some logic
// needs to be duplicated from parent to find out the exact reason the validation failed. // needs to be duplicated from parent to find out the exact reason the validation failed.
if(!$isValid) { if (!$isValid) {
$IDFilter = ($this->ID) ? "AND \"SiteTree\".\"ID\" <> $this->ID" : null; $IDFilter = ($this->ID) ? "AND \"SiteTree\".\"ID\" <> $this->ID" : null;
$parentFilter = null; $parentFilter = null;
if(Config::inst()->get('SilverStripe\\CMS\\Model\\SiteTree', 'nested_urls')) { if (Config::inst()->get('SilverStripe\\CMS\\Model\\SiteTree', 'nested_urls')) {
if($this->ParentID) { if ($this->ParentID) {
$parentFilter = " AND \"SiteTree\".\"ParentID\" = $this->ParentID"; $parentFilter = " AND \"SiteTree\".\"ParentID\" = $this->ParentID";
} else { } else {
$parentFilter = ' AND "SiteTree"."ParentID" = 0'; $parentFilter = ' AND "SiteTree"."ParentID" = 0';
} }
} }
$origDisableSubsiteFilter = Subsite::$disable_subsite_filter; $origDisableSubsiteFilter = Subsite::$disable_subsite_filter;
Subsite::$disable_subsite_filter = true; Subsite::$disable_subsite_filter = true;
$existingPage = DataObject::get_one( $existingPage = DataObject::get_one(
'SilverStripe\\CMS\\Model\\SiteTree', 'SilverStripe\\CMS\\Model\\SiteTree',
"\"URLSegment\" = '$this->URLSegment' $IDFilter $parentFilter", "\"URLSegment\" = '$this->URLSegment' $IDFilter $parentFilter",
false // disable cache, it doesn't include subsite status in the key false // disable cache, it doesn't include subsite status in the key
); );
Subsite::$disable_subsite_filter = $origDisableSubsiteFilter; Subsite::$disable_subsite_filter = $origDisableSubsiteFilter;
$existingPageInSubsite = DataObject::get_one( $existingPageInSubsite = DataObject::get_one(
'SilverStripe\\CMS\\Model\\SiteTree', 'SilverStripe\\CMS\\Model\\SiteTree',
"\"URLSegment\" = '$this->URLSegment' $IDFilter $parentFilter", "\"URLSegment\" = '$this->URLSegment' $IDFilter $parentFilter",
false // disable cache, it doesn't include subsite status in the key false // disable cache, it doesn't include subsite status in the key
); );
// If URL has been vetoed because of an existing page, // If URL has been vetoed because of an existing page,
// be more specific and allow same URLSegments in different subsites // be more specific and allow same URLSegments in different subsites
$isValid = !($existingPage && $existingPageInSubsite); $isValid = !($existingPage && $existingPageInSubsite);
} }
return $isValid; return $isValid;
} }
} }

View File

@ -3,73 +3,78 @@
namespace SilverStripe\Subsites\Reports; namespace SilverStripe\Subsites\Reports;
use SilverStripe\Forms\TreeMultiselectField;
use SilverStripe\Forms\FieldList; use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\TreeMultiselectField;
use SilverStripe\Reports\ReportWrapper; use SilverStripe\Reports\ReportWrapper;
use SilverStripe\Subsites\Model\Subsite; use SilverStripe\Subsites\Model\Subsite;
/** /**
* Creates a subsite-aware version of another report. * Creates a subsite-aware version of another report.
* Pass another report (or its classname) into the constructor. * Pass another report (or its classname) into the constructor.
*/ */
class SubsiteReportWrapper extends ReportWrapper { class SubsiteReportWrapper extends ReportWrapper
/////////////////////////////////////////////////////////////////////////////////////////// {
// Filtering ///////////////////////////////////////////////////////////////////////////////////////////
// Filtering
function parameterFields() { function parameterFields()
$subsites = Subsite::accessible_sites('CMS_ACCESS_CMSMain', true); {
$options = $subsites->toDropdownMap('ID', 'Title'); $subsites = Subsite::accessible_sites('CMS_ACCESS_CMSMain', true);
$options = $subsites->toDropdownMap('ID', 'Title');
$subsiteField = new TreeMultiselectField( $subsiteField = new TreeMultiselectField(
'Subsites', 'Subsites',
_t('SubsiteReportWrapper.ReportDropdown', 'Sites'), _t('SubsiteReportWrapper.ReportDropdown', 'Sites'),
$options $options
); );
$subsiteField->setValue(array_keys($options)); $subsiteField->setValue(array_keys($options));
// We don't need to make the field editable if only one subsite is available // We don't need to make the field editable if only one subsite is available
if(sizeof($options) <= 1) { if (sizeof($options) <= 1) {
$subsiteField = $subsiteField->performReadonlyTransformation(); $subsiteField = $subsiteField->performReadonlyTransformation();
} }
$fields = parent::parameterFields(); $fields = parent::parameterFields();
if($fields) { if ($fields) {
$fields->insertBefore($subsiteField, $fields->First()->Name()); $fields->insertBefore($subsiteField, $fields->First()->Name());
} else { } else {
$fields = new FieldList($subsiteField); $fields = new FieldList($subsiteField);
} }
return $fields; return $fields;
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Columns // Columns
function columns() { function columns()
$columns = parent::columns(); {
$columns['Subsite.Title'] = Subsite::class; $columns = parent::columns();
return $columns; $columns['Subsite.Title'] = Subsite::class;
} return $columns;
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Querying // Querying
function beforeQuery($params) { function beforeQuery($params)
// The user has select a few specific sites {
if(!empty($params['Subsites'])) { // The user has select a few specific sites
Subsite::$force_subsite = $params['Subsites']; if (!empty($params['Subsites'])) {
Subsite::$force_subsite = $params['Subsites'];
// Default: restrict to all accessible sites // Default: restrict to all accessible sites
} else { } else {
$subsites = Subsite::accessible_sites('CMS_ACCESS_CMSMain'); $subsites = Subsite::accessible_sites('CMS_ACCESS_CMSMain');
$options = $subsites->toDropdownMap('ID', 'Title'); $options = $subsites->toDropdownMap('ID', 'Title');
Subsite::$force_subsite = join(',', array_keys($options)); Subsite::$force_subsite = join(',', array_keys($options));
} }
} }
function afterQuery() {
// Manually manage the subsite filtering function afterQuery()
Subsite::$force_subsite = null; {
} // Manually manage the subsite filtering
Subsite::$force_subsite = null;
}
} }

View File

@ -3,12 +3,12 @@
namespace SilverStripe\Subsites\Tasks; namespace SilverStripe\Subsites\Tasks;
use SilverStripe\ORM\DataObject;
use SilverStripe\Dev\BuildTask;
use InvalidArgumentException; use InvalidArgumentException;
use SilverStripe\Dev\BuildTask;
use SilverStripe\ORM\DataObject;
use SilverStripe\Subsites\Model\Subsite; use SilverStripe\Subsites\Model\Subsite;
use SilverStripe\Versioned\Versioned;
use SilverStripe\Subsites\Pages\SubsitesVirtualPage; use SilverStripe\Subsites\Pages\SubsitesVirtualPage;
use SilverStripe\Versioned\Versioned;
/** /**
@ -18,62 +18,74 @@ use SilverStripe\Subsites\Pages\SubsitesVirtualPage;
* *
* Example: sake dev/tasks/SubsiteCopyPagesTask from=<subsite-source> to=<subsite-target> * Example: sake dev/tasks/SubsiteCopyPagesTask from=<subsite-source> to=<subsite-target>
*/ */
class SubsiteCopyPagesTask extends BuildTask { class SubsiteCopyPagesTask extends BuildTask
{
protected $title = 'Copy pages to different subsite'; protected $title = 'Copy pages to different subsite';
protected $description = ''; protected $description = '';
function run($request) { function run($request)
$subsiteFromId = $request->getVar('from'); {
if(!is_numeric($subsiteFromId)) throw new InvalidArgumentException('Missing "from" parameter'); $subsiteFromId = $request->getVar('from');
$subsiteFrom = DataObject::get_by_id(Subsite::class, $subsiteFromId); if (!is_numeric($subsiteFromId)) {
if(!$subsiteFrom) throw new InvalidArgumentException('Subsite not found'); throw new InvalidArgumentException('Missing "from" parameter');
}
$subsiteFrom = DataObject::get_by_id(Subsite::class, $subsiteFromId);
if (!$subsiteFrom) {
throw new InvalidArgumentException('Subsite not found');
}
$subsiteToId = $request->getVar('to'); $subsiteToId = $request->getVar('to');
if(!is_numeric($subsiteToId)) throw new InvalidArgumentException('Missing "to" parameter'); if (!is_numeric($subsiteToId)) {
$subsiteTo = DataObject::get_by_id(Subsite::class, $subsiteToId); throw new InvalidArgumentException('Missing "to" parameter');
if(!$subsiteTo) throw new InvalidArgumentException('Subsite not found'); }
$subsiteTo = DataObject::get_by_id(Subsite::class, $subsiteToId);
if (!$subsiteTo) {
throw new InvalidArgumentException('Subsite not found');
}
$useVirtualPages = (bool)$request->getVar('virtual'); $useVirtualPages = (bool)$request->getVar('virtual');
Subsite::changeSubsite($subsiteFrom); Subsite::changeSubsite($subsiteFrom);
// Copy data from this template to the given subsite. Does this using an iterative depth-first search. // Copy data from this template to the given subsite. Does this using an iterative depth-first search.
// This will make sure that the new parents on the new subsite are correct, and there are no funny // This will make sure that the new parents on the new subsite are correct, and there are no funny
// issues with having to check whether or not the new parents have been added to the site tree // issues with having to check whether or not the new parents have been added to the site tree
// when a page, etc, is duplicated // when a page, etc, is duplicated
$stack = array(array(0,0)); $stack = [[0, 0]];
while(count($stack) > 0) { while (count($stack) > 0) {
list($sourceParentID, $destParentID) = array_pop($stack); list($sourceParentID, $destParentID) = array_pop($stack);
$children = Versioned::get_by_stage('SilverStripe\\CMS\\Model\\SiteTree', 'Live', "\"ParentID\" = $sourceParentID", ''); $children = Versioned::get_by_stage('SilverStripe\\CMS\\Model\\SiteTree', 'Live',
"\"ParentID\" = $sourceParentID", '');
if($children) { if ($children) {
foreach($children as $child) { foreach ($children as $child) {
if($useVirtualPages) { if ($useVirtualPages) {
$childClone = new SubsitesVirtualPage(); $childClone = new SubsitesVirtualPage();
$childClone->writeToStage('Stage'); $childClone->writeToStage('Stage');
$childClone->CopyContentFromID = $child->ID; $childClone->CopyContentFromID = $child->ID;
$childClone->SubsiteID = $subsiteTo->ID; $childClone->SubsiteID = $subsiteTo->ID;
} else { } else {
$childClone = $child->duplicateToSubsite($subsiteTo->ID, true); $childClone = $child->duplicateToSubsite($subsiteTo->ID, true);
} }
$childClone->ParentID = $destParentID; $childClone->ParentID = $destParentID;
$childClone->writeToStage('Stage'); $childClone->writeToStage('Stage');
$childClone->publish('Stage', 'Live'); $childClone->publish('Stage', 'Live');
array_push($stack, array($child->ID, $childClone->ID)); array_push($stack, [$child->ID, $childClone->ID]);
$this->log(sprintf('Copied "%s" (#%d, %s)', $child->Title, $child->ID, $child->Link())); $this->log(sprintf('Copied "%s" (#%d, %s)', $child->Title, $child->ID, $child->Link()));
} }
} }
unset($children); unset($children);
} }
} }
function log($msg) { function log($msg)
echo $msg . "\n"; {
} echo $msg . "\n";
}
} }

View File

@ -3,30 +3,34 @@
use SilverStripe\Dev\SapphireTest; use SilverStripe\Dev\SapphireTest;
use SilverStripe\Subsites\Model\Subsite; use SilverStripe\Subsites\Model\Subsite;
class BaseSubsiteTest extends SapphireTest { class BaseSubsiteTest extends SapphireTest
{
function setUp() { function setUp()
parent::setUp(); {
parent::setUp();
Subsite::$use_session_subsiteid = true; Subsite::$use_session_subsiteid = true;
} }
/** /**
* Avoid subsites filtering on fixture fetching. * Avoid subsites filtering on fixture fetching.
*/ */
function objFromFixture($class, $id) { function objFromFixture($class, $id)
Subsite::disable_subsite_filter(true); {
$obj = parent::objFromFixture($class, $id); Subsite::disable_subsite_filter(true);
Subsite::disable_subsite_filter(false); $obj = parent::objFromFixture($class, $id);
Subsite::disable_subsite_filter(false);
return $obj; return $obj;
} }
/** /**
* Tests the initial state of disable_subsite_filter * Tests the initial state of disable_subsite_filter
*/ */
function testDisableSubsiteFilter() { function testDisableSubsiteFilter()
$this->assertFalse(Subsite::$disable_subsite_filter); {
} $this->assertFalse(Subsite::$disable_subsite_filter);
}
} }

View File

@ -1,83 +1,87 @@
<?php <?php
use SilverStripe\Assets\File; use SilverStripe\Assets\File;
use SilverStripe\Forms\FieldList;
use SilverStripe\Assets\Folder; use SilverStripe\Assets\Folder;
use SilverStripe\Forms\FieldList;
use SilverStripe\Subsites\Extensions\FileSubsites; use SilverStripe\Subsites\Extensions\FileSubsites;
use SilverStripe\Subsites\Model\Subsite; use SilverStripe\Subsites\Model\Subsite;
class FileSubsitesTest extends BaseSubsiteTest { class FileSubsitesTest extends BaseSubsiteTest
static $fixture_file = 'subsites/tests/SubsiteTest.yml'; {
static $fixture_file = 'subsites/tests/SubsiteTest.yml';
function testTrivialFeatures() { function testTrivialFeatures()
$this->assertTrue(is_array(singleton(FileSubsites::class)->extraStatics())); {
$file = new File(); $this->assertTrue(is_array(singleton(FileSubsites::class)->extraStatics()));
$file->Name = 'FileTitle'; $file = new File();
$file->Title = 'FileTitle'; $file->Name = 'FileTitle';
$this->assertEquals(' * FileTitle', $file->alternateTreeTitle()); $file->Title = 'FileTitle';
$file->SubsiteID = $this->objFromFixture(Subsite::class, 'domaintest1')->ID; $this->assertEquals(' * FileTitle', $file->alternateTreeTitle());
$this->assertEquals('FileTitle', $file->getTreeTitle()); $file->SubsiteID = $this->objFromFixture(Subsite::class, 'domaintest1')->ID;
$this->assertTrue(singleton('SilverStripe\\Assets\\Folder')->getCMSFields() instanceof FieldList); $this->assertEquals('FileTitle', $file->getTreeTitle());
Subsite::changeSubsite(1); $this->assertTrue(singleton('SilverStripe\\Assets\\Folder')->getCMSFields() instanceof FieldList);
$this->assertEquals($file->cacheKeyComponent(), 'subsite-1'); Subsite::changeSubsite(1);
} $this->assertEquals($file->cacheKeyComponent(), 'subsite-1');
}
function testWritingSubsiteID() { function testWritingSubsiteID()
$this->objFromFixture('SilverStripe\\Security\\Member', 'admin')->logIn(); {
$this->objFromFixture('SilverStripe\\Security\\Member', 'admin')->logIn();
$subsite = $this->objFromFixture(Subsite::class, 'domaintest1'); $subsite = $this->objFromFixture(Subsite::class, 'domaintest1');
FileSubsites::$default_root_folders_global = true; FileSubsites::$default_root_folders_global = true;
Subsite::changeSubsite(0); Subsite::changeSubsite(0);
$file = new File(); $file = new File();
$file->write(); $file->write();
$file->onAfterUpload(); $file->onAfterUpload();
$this->assertEquals((int)$file->SubsiteID, 0); $this->assertEquals((int)$file->SubsiteID, 0);
Subsite::changeSubsite($subsite->ID); Subsite::changeSubsite($subsite->ID);
$this->assertTrue($file->canEdit()); $this->assertTrue($file->canEdit());
$file = new File(); $file = new File();
$file->write(); $file->write();
$this->assertEquals((int)$file->SubsiteID, 0); $this->assertEquals((int)$file->SubsiteID, 0);
$this->assertTrue($file->canEdit()); $this->assertTrue($file->canEdit());
FileSubsites::$default_root_folders_global = false; FileSubsites::$default_root_folders_global = false;
Subsite::changeSubsite($subsite->ID); Subsite::changeSubsite($subsite->ID);
$file = new File(); $file = new File();
$file->write(); $file->write();
$this->assertEquals($file->SubsiteID, $subsite->ID); $this->assertEquals($file->SubsiteID, $subsite->ID);
// Test inheriting from parent folder // Test inheriting from parent folder
$folder = new Folder(); $folder = new Folder();
$folder->write(); $folder->write();
$this->assertEquals($folder->SubsiteID, $subsite->ID); $this->assertEquals($folder->SubsiteID, $subsite->ID);
FileSubsites::$default_root_folders_global = true; FileSubsites::$default_root_folders_global = true;
$file = new File(); $file = new File();
$file->ParentID = $folder->ID; $file->ParentID = $folder->ID;
$file->onAfterUpload(); $file->onAfterUpload();
$this->assertEquals($folder->SubsiteID, $file->SubsiteID); $this->assertEquals($folder->SubsiteID, $file->SubsiteID);
} }
function testSubsitesFolderDropdown() { function testSubsitesFolderDropdown()
$this->objFromFixture('SilverStripe\\Security\\Member', 'admin')->logIn(); {
$this->objFromFixture('SilverStripe\\Security\\Member', 'admin')->logIn();
$file = new Folder(); $file = new Folder();
$source = array_values($file->getCMSFields()->dataFieldByName('SubsiteID')->getSource()); $source = array_values($file->getCMSFields()->dataFieldByName('SubsiteID')->getSource());
asort($source); asort($source);
$this->assertEquals(array( $this->assertEquals([
'Main site', 'Main site',
'Template', 'Template',
'Subsite1 Template', 'Subsite1 Template',
'Subsite2 Template', 'Subsite2 Template',
'Test 1', 'Test 1',
'Test 2', 'Test 2',
'Test 3' 'Test 3'
), $source); ], $source);
} }
} }

View File

@ -6,26 +6,29 @@ use SilverStripe\Subsites\Extensions\GroupSubsites;
use SilverStripe\Subsites\Model\Subsite; use SilverStripe\Subsites\Model\Subsite;
class GroupSubsitesTest extends BaseSubsiteTest { class GroupSubsitesTest extends BaseSubsiteTest
static $fixture_file = 'subsites/tests/SubsiteTest.yml'; {
static $fixture_file = 'subsites/tests/SubsiteTest.yml';
protected $requireDefaultRecordsFrom = array(GroupSubsites::class); protected $requireDefaultRecordsFrom = [GroupSubsites::class];
function testTrivialFeatures() { function testTrivialFeatures()
$this->assertTrue(is_array(singleton(GroupSubsites::class)->extraStatics())); {
$this->assertTrue(is_array(singleton(GroupSubsites::class)->providePermissions())); $this->assertTrue(is_array(singleton(GroupSubsites::class)->extraStatics()));
$this->assertTrue(singleton('SilverStripe\\Security\\Group')->getCMSFields() instanceof FieldList); $this->assertTrue(is_array(singleton(GroupSubsites::class)->providePermissions()));
} $this->assertTrue(singleton('SilverStripe\\Security\\Group')->getCMSFields() instanceof FieldList);
}
function testAlternateTreeTitle() { function testAlternateTreeTitle()
$group = new Group(); {
$group->Title = 'The A Team'; $group = new Group();
$group->AccessAllSubsites = true; $group->Title = 'The A Team';
$this->assertEquals($group->getTreeTitle(), 'The A Team <i>(global group)</i>'); $group->AccessAllSubsites = true;
$group->AccessAllSubsites = false; $this->assertEquals($group->getTreeTitle(), 'The A Team <i>(global group)</i>');
$group->write(); $group->AccessAllSubsites = false;
$group->Subsites()->add($this->objFromFixture(Subsite::class, 'domaintest1')); $group->write();
$group->Subsites()->add($this->objFromFixture(Subsite::class, 'domaintest2')); $group->Subsites()->add($this->objFromFixture(Subsite::class, 'domaintest1'));
$this->assertEquals($group->getTreeTitle(), 'The A Team <i>(Test 1, Test 2)</i>'); $group->Subsites()->add($this->objFromFixture(Subsite::class, 'domaintest2'));
} $this->assertEquals($group->getTreeTitle(), 'The A Team <i>(Test 1, Test 2)</i>');
}
} }

View File

@ -1,95 +1,102 @@
<?php <?php
use SilverStripe\Admin\LeftAndMain; use SilverStripe\Admin\LeftAndMain;
use SilverStripe\AssetAdmin\Controller\AssetAdmin;
use SilverStripe\Core\Config\Config; use SilverStripe\Core\Config\Config;
use SilverStripe\Dev\FunctionalTest; use SilverStripe\Dev\FunctionalTest;
use SilverStripe\Subsites\Model\Subsite; use SilverStripe\Subsites\Model\Subsite;
use SilverStripe\AssetAdmin\Controller\AssetAdmin;
class LeftAndMainSubsitesTest extends FunctionalTest { class LeftAndMainSubsitesTest extends FunctionalTest
{
static $fixture_file = 'subsites/tests/SubsiteTest.yml'; static $fixture_file = 'subsites/tests/SubsiteTest.yml';
/** /**
* Avoid subsites filtering on fixture fetching. * Avoid subsites filtering on fixture fetching.
*/ */
function objFromFixture($class, $id) { function objFromFixture($class, $id)
Subsite::disable_subsite_filter(true); {
$obj = parent::objFromFixture($class, $id); Subsite::disable_subsite_filter(true);
Subsite::disable_subsite_filter(false); $obj = parent::objFromFixture($class, $id);
Subsite::disable_subsite_filter(false);
return $obj; return $obj;
} }
function testSectionSites() { function testSectionSites()
$member = $this->objFromFixture('SilverStripe\\Security\\Member', 'subsite1member'); {
$member = $this->objFromFixture('SilverStripe\\Security\\Member', 'subsite1member');
$cmsmain = singleton('SilverStripe\\CMS\\Controllers\\CMSMain'); $cmsmain = singleton('SilverStripe\\CMS\\Controllers\\CMSMain');
$subsites = $cmsmain->sectionSites(true, "Main site", $member); $subsites = $cmsmain->sectionSites(true, "Main site", $member);
$this->assertDOSEquals(array( $this->assertDOSEquals([
array('Title' =>'Subsite1 Template') ['Title' => 'Subsite1 Template']
), $subsites, 'Lists member-accessible sites for the accessible controller.'); ], $subsites, 'Lists member-accessible sites for the accessible controller.');
$assetadmin = singleton(AssetAdmin::class); $assetadmin = singleton(AssetAdmin::class);
$subsites = $assetadmin->sectionSites(true, "Main site", $member); $subsites = $assetadmin->sectionSites(true, "Main site", $member);
$this->assertDOSEquals(array(), $subsites, 'Does not list any sites for forbidden controller.'); $this->assertDOSEquals([], $subsites, 'Does not list any sites for forbidden controller.');
$member = $this->objFromFixture('SilverStripe\\Security\\Member', 'editor'); $member = $this->objFromFixture('SilverStripe\\Security\\Member', 'editor');
$cmsmain = singleton('SilverStripe\\CMS\\Controllers\\CMSMain'); $cmsmain = singleton('SilverStripe\\CMS\\Controllers\\CMSMain');
$subsites = $cmsmain->sectionSites(true, "Main site", $member); $subsites = $cmsmain->sectionSites(true, "Main site", $member);
$this->assertDOSContains(array( $this->assertDOSContains([
array('Title' =>'Main site') ['Title' => 'Main site']
), $subsites, 'Includes the main site for members who can access all sites.'); ], $subsites, 'Includes the main site for members who can access all sites.');
} }
function testAccessChecksDontChangeCurrentSubsite() { function testAccessChecksDontChangeCurrentSubsite()
$admin = $this->objFromFixture("SilverStripe\\Security\\Member","admin"); {
$this->loginAs($admin); $admin = $this->objFromFixture("SilverStripe\\Security\\Member", "admin");
$ids = array(); $this->loginAs($admin);
$ids = [];
$subsite1 = $this->objFromFixture(Subsite::class, 'domaintest1'); $subsite1 = $this->objFromFixture(Subsite::class, 'domaintest1');
$subsite2 = $this->objFromFixture(Subsite::class, 'domaintest2'); $subsite2 = $this->objFromFixture(Subsite::class, 'domaintest2');
$subsite3 = $this->objFromFixture(Subsite::class, 'domaintest3'); $subsite3 = $this->objFromFixture(Subsite::class, 'domaintest3');
$ids[] = $subsite1->ID; $ids[] = $subsite1->ID;
$ids[] = $subsite2->ID; $ids[] = $subsite2->ID;
$ids[] = $subsite3->ID; $ids[] = $subsite3->ID;
$ids[] = 0; $ids[] = 0;
// Enable session-based subsite tracking. // Enable session-based subsite tracking.
Subsite::$use_session_subsiteid = true; Subsite::$use_session_subsiteid = true;
foreach($ids as $id) { foreach ($ids as $id) {
Subsite::changeSubsite($id); Subsite::changeSubsite($id);
$this->assertEquals($id, Subsite::currentSubsiteID()); $this->assertEquals($id, Subsite::currentSubsiteID());
$left = new LeftAndMain(); $left = new LeftAndMain();
$this->assertTrue($left->canView(), "Admin user can view subsites LeftAndMain with id = '$id'"); $this->assertTrue($left->canView(), "Admin user can view subsites LeftAndMain with id = '$id'");
$this->assertEquals($id, Subsite::currentSubsiteID(), $this->assertEquals($id, Subsite::currentSubsiteID(),
"The current subsite has not been changed in the process of checking permissions for admin user."); "The current subsite has not been changed in the process of checking permissions for admin user.");
} }
} }
function testShouldChangeSubsite() { function testShouldChangeSubsite()
$l = new LeftAndMain(); {
Config::inst()->nest(); $l = new LeftAndMain();
Config::inst()->nest();
Config::modify()->set('SilverStripe\\CMS\\Controllers\\CMSPageEditController', 'treats_subsite_0_as_global', false); Config::modify()->set('SilverStripe\\CMS\\Controllers\\CMSPageEditController', 'treats_subsite_0_as_global',
$this->assertTrue($l->shouldChangeSubsite('SilverStripe\\CMS\\Controllers\\CMSPageEditController', 0, 5)); false);
$this->assertFalse($l->shouldChangeSubsite('SilverStripe\\CMS\\Controllers\\CMSPageEditController', 0, 0)); $this->assertTrue($l->shouldChangeSubsite('SilverStripe\\CMS\\Controllers\\CMSPageEditController', 0, 5));
$this->assertTrue($l->shouldChangeSubsite('SilverStripe\\CMS\\Controllers\\CMSPageEditController', 1, 5)); $this->assertFalse($l->shouldChangeSubsite('SilverStripe\\CMS\\Controllers\\CMSPageEditController', 0, 0));
$this->assertFalse($l->shouldChangeSubsite('SilverStripe\\CMS\\Controllers\\CMSPageEditController', 1, 1)); $this->assertTrue($l->shouldChangeSubsite('SilverStripe\\CMS\\Controllers\\CMSPageEditController', 1, 5));
$this->assertFalse($l->shouldChangeSubsite('SilverStripe\\CMS\\Controllers\\CMSPageEditController', 1, 1));
Config::modify()->set('SilverStripe\\CMS\\Controllers\\CMSPageEditController', 'treats_subsite_0_as_global', true); Config::modify()->set('SilverStripe\\CMS\\Controllers\\CMSPageEditController', 'treats_subsite_0_as_global',
$this->assertFalse($l->shouldChangeSubsite('SilverStripe\\CMS\\Controllers\\CMSPageEditController', 0, 5)); true);
$this->assertFalse($l->shouldChangeSubsite('SilverStripe\\CMS\\Controllers\\CMSPageEditController', 0, 0)); $this->assertFalse($l->shouldChangeSubsite('SilverStripe\\CMS\\Controllers\\CMSPageEditController', 0, 5));
$this->assertTrue($l->shouldChangeSubsite('SilverStripe\\CMS\\Controllers\\CMSPageEditController', 1, 5)); $this->assertFalse($l->shouldChangeSubsite('SilverStripe\\CMS\\Controllers\\CMSPageEditController', 0, 0));
$this->assertFalse($l->shouldChangeSubsite('SilverStripe\\CMS\\Controllers\\CMSPageEditController', 1, 1)); $this->assertTrue($l->shouldChangeSubsite('SilverStripe\\CMS\\Controllers\\CMSPageEditController', 1, 5));
$this->assertFalse($l->shouldChangeSubsite('SilverStripe\\CMS\\Controllers\\CMSPageEditController', 1, 1));
Config::inst()->unnest(); Config::inst()->unnest();
} }
} }

View File

@ -1,43 +1,45 @@
<?php <?php
use SilverStripe\SiteConfig\SiteConfig; use SilverStripe\SiteConfig\SiteConfig;
use SilverStripe\Subsites\Model\Subsite;
use SilverStripe\Subsites\Extensions\SiteConfigSubsites; use SilverStripe\Subsites\Extensions\SiteConfigSubsites;
use SilverStripe\Subsites\Model\Subsite;
class SiteConfigSubsitesTest extends BaseSubsiteTest { class SiteConfigSubsitesTest extends BaseSubsiteTest
static $fixture_file = 'subsites/tests/SubsiteTest.yml'; {
static $fixture_file = 'subsites/tests/SubsiteTest.yml';
function testEachSubsiteHasAUniqueSiteConfig() { function testEachSubsiteHasAUniqueSiteConfig()
$subsite1 = $this->objFromFixture(Subsite::class, 'domaintest1'); {
$subsite2 = $this->objFromFixture(Subsite::class, 'domaintest2'); $subsite1 = $this->objFromFixture(Subsite::class, 'domaintest1');
$subsite2 = $this->objFromFixture(Subsite::class, 'domaintest2');
$this->assertTrue(is_array(singleton(SiteConfigSubsites::class)->extraStatics())); $this->assertTrue(is_array(singleton(SiteConfigSubsites::class)->extraStatics()));
Subsite::changeSubsite(0); Subsite::changeSubsite(0);
$sc = SiteConfig::current_site_config(); $sc = SiteConfig::current_site_config();
$sc->Title = 'RootSite'; $sc->Title = 'RootSite';
$sc->write(); $sc->write();
Subsite::changeSubsite($subsite1->ID); Subsite::changeSubsite($subsite1->ID);
$sc = SiteConfig::current_site_config(); $sc = SiteConfig::current_site_config();
$sc->Title = 'Subsite1'; $sc->Title = 'Subsite1';
$sc->write(); $sc->write();
Subsite::changeSubsite($subsite2->ID); Subsite::changeSubsite($subsite2->ID);
$sc = SiteConfig::current_site_config(); $sc = SiteConfig::current_site_config();
$sc->Title = 'Subsite2'; $sc->Title = 'Subsite2';
$sc->write(); $sc->write();
Subsite::changeSubsite(0); Subsite::changeSubsite(0);
$this->assertEquals(SiteConfig::current_site_config()->Title, 'RootSite'); $this->assertEquals(SiteConfig::current_site_config()->Title, 'RootSite');
Subsite::changeSubsite($subsite1->ID); Subsite::changeSubsite($subsite1->ID);
$this->assertEquals(SiteConfig::current_site_config()->Title, 'Subsite1'); $this->assertEquals(SiteConfig::current_site_config()->Title, 'Subsite1');
Subsite::changeSubsite($subsite2->ID); Subsite::changeSubsite($subsite2->ID);
$this->assertEquals(SiteConfig::current_site_config()->Title, 'Subsite2'); $this->assertEquals(SiteConfig::current_site_config()->Title, 'Subsite2');
$keys = SiteConfig::current_site_config()->extend('cacheKeyComponent'); $keys = SiteConfig::current_site_config()->extend('cacheKeyComponent');
$this->assertContains('subsite-' . $subsite2->ID, $keys); $this->assertContains('subsite-' . $subsite2->ID, $keys);
} }
} }

View File

@ -1,229 +1,243 @@
<?php <?php
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\SiteConfig\SiteConfig;
use SilverStripe\Forms\FieldList;
use SilverStripe\Control\Session;
use SilverStripe\CMS\Controllers\CMSMain; use SilverStripe\CMS\Controllers\CMSMain;
use SilverStripe\CMS\Model\ErrorPage;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Control\Session;
use SilverStripe\Core\Convert; use SilverStripe\Core\Convert;
use SilverStripe\Dev\TestOnly; use SilverStripe\Dev\TestOnly;
use SilverStripe\CMS\Model\ErrorPage; use SilverStripe\Forms\FieldList;
use SilverStripe\SiteConfig\SiteConfig;
use SilverStripe\Subsites\Extensions\SiteTreeSubsites;
use SilverStripe\Subsites\Model\Subsite; use SilverStripe\Subsites\Model\Subsite;
use SilverStripe\Subsites\Pages\SubsitesVirtualPage; use SilverStripe\Subsites\Pages\SubsitesVirtualPage;
use SilverStripe\Subsites\Extensions\SiteTreeSubsites;
class SiteTreeSubsitesTest extends BaseSubsiteTest { class SiteTreeSubsitesTest extends BaseSubsiteTest
{
static $fixture_file = 'subsites/tests/SubsiteTest.yml'; static $fixture_file = 'subsites/tests/SubsiteTest.yml';
protected $extraDataObjects = array( protected $extraDataObjects = [
'SiteTreeSubsitesTest_ClassA', 'SiteTreeSubsitesTest_ClassA',
'SiteTreeSubsitesTest_ClassB', 'SiteTreeSubsitesTest_ClassB',
'SiteTreeSubsitesTest_ErrorPage' 'SiteTreeSubsitesTest_ErrorPage'
); ];
protected $illegalExtensions = array( protected $illegalExtensions = [
'SilverStripe\CMS\Model\SiteTree' => array('Translatable') 'SilverStripe\CMS\Model\SiteTree' => ['Translatable']
); ];
function testPagesInDifferentSubsitesCanShareURLSegment() { function testPagesInDifferentSubsitesCanShareURLSegment()
$subsiteMain = $this->objFromFixture(Subsite::class, 'main'); {
$subsite1 = $this->objFromFixture(Subsite::class, 'subsite1'); $subsiteMain = $this->objFromFixture(Subsite::class, 'main');
$subsite1 = $this->objFromFixture(Subsite::class, 'subsite1');
$pageMain = new SiteTree(); $pageMain = new SiteTree();
$pageMain->URLSegment = 'testpage'; $pageMain->URLSegment = 'testpage';
$pageMain->write(); $pageMain->write();
$pageMain->publish('Stage', 'Live'); $pageMain->publish('Stage', 'Live');
$pageMainOther = new SiteTree(); $pageMainOther = new SiteTree();
$pageMainOther->URLSegment = 'testpage'; $pageMainOther->URLSegment = 'testpage';
$pageMainOther->write(); $pageMainOther->write();
$pageMainOther->publish('Stage', 'Live'); $pageMainOther->publish('Stage', 'Live');
$this->assertNotEquals($pageMain->URLSegment, $pageMainOther->URLSegment, $this->assertNotEquals($pageMain->URLSegment, $pageMainOther->URLSegment,
'Pages in same subsite cant share the same URL' 'Pages in same subsite cant share the same URL'
); );
Subsite::changeSubsite($subsite1->ID); Subsite::changeSubsite($subsite1->ID);
$pageSubsite1 = new SiteTree(); $pageSubsite1 = new SiteTree();
$pageSubsite1->URLSegment = 'testpage'; $pageSubsite1->URLSegment = 'testpage';
$pageSubsite1->write(); $pageSubsite1->write();
$pageSubsite1->publish('Stage', 'Live'); $pageSubsite1->publish('Stage', 'Live');
$this->assertEquals($pageMain->URLSegment, $pageSubsite1->URLSegment, $this->assertEquals($pageMain->URLSegment, $pageSubsite1->URLSegment,
'Pages in different subsites can share the same URL' 'Pages in different subsites can share the same URL'
); );
} }
function testBasicSanity() { function testBasicSanity()
$this->assertTrue(singleton('SilverStripe\\CMS\\Model\\SiteTree')->getSiteConfig() instanceof SiteConfig); {
// The following assert is breaking in Translatable. $this->assertTrue(singleton('SilverStripe\\CMS\\Model\\SiteTree')->getSiteConfig() instanceof SiteConfig);
$this->assertTrue(singleton('SilverStripe\\CMS\\Model\\SiteTree')->getCMSFields() instanceof FieldList); // The following assert is breaking in Translatable.
$this->assertTrue(singleton(SubsitesVirtualPage::class)->getCMSFields() instanceof FieldList); $this->assertTrue(singleton('SilverStripe\\CMS\\Model\\SiteTree')->getCMSFields() instanceof FieldList);
$this->assertTrue(is_array(singleton(SiteTreeSubsites::class)->extraStatics())); $this->assertTrue(singleton(SubsitesVirtualPage::class)->getCMSFields() instanceof FieldList);
} $this->assertTrue(is_array(singleton(SiteTreeSubsites::class)->extraStatics()));
}
function testErrorPageLocations() { function testErrorPageLocations()
$subsite1 = $this->objFromFixture(Subsite::class, 'domaintest1'); {
$subsite1 = $this->objFromFixture(Subsite::class, 'domaintest1');
Subsite::changeSubsite($subsite1->ID); Subsite::changeSubsite($subsite1->ID);
$path = SiteTreeSubsitesTest_ErrorPage::get_error_filename_spy(500); $path = SiteTreeSubsitesTest_ErrorPage::get_error_filename_spy(500);
$expected_path = 'error-500-'.$subsite1->domain().'.html'; $expected_path = 'error-500-' . $subsite1->domain() . '.html';
$this->assertEquals($expected_path, $path); $this->assertEquals($expected_path, $path);
} }
function testCanEditSiteTree() { function testCanEditSiteTree()
$admin = $this->objFromFixture('SilverStripe\\Security\\Member', 'admin'); {
$subsite1member = $this->objFromFixture('SilverStripe\\Security\\Member', 'subsite1member'); $admin = $this->objFromFixture('SilverStripe\\Security\\Member', 'admin');
$subsite2member = $this->objFromFixture('SilverStripe\\Security\\Member', 'subsite2member'); $subsite1member = $this->objFromFixture('SilverStripe\\Security\\Member', 'subsite1member');
$mainpage = $this->objFromFixture('Page', 'home'); $subsite2member = $this->objFromFixture('SilverStripe\\Security\\Member', 'subsite2member');
$subsite1page = $this->objFromFixture('Page', 'subsite1_home'); $mainpage = $this->objFromFixture('Page', 'home');
$subsite2page = $this->objFromFixture('Page', 'subsite2_home'); $subsite1page = $this->objFromFixture('Page', 'subsite1_home');
$subsite1 = $this->objFromFixture(Subsite::class, 'subsite1'); $subsite2page = $this->objFromFixture('Page', 'subsite2_home');
$subsite2 = $this->objFromFixture(Subsite::class, 'subsite2'); $subsite1 = $this->objFromFixture(Subsite::class, 'subsite1');
$subsite2 = $this->objFromFixture(Subsite::class, 'subsite2');
// Cant pass member as arguments to canEdit() because of GroupSubsites // Cant pass member as arguments to canEdit() because of GroupSubsites
Session::set("loggedInAs", $admin->ID); Session::set("loggedInAs", $admin->ID);
$this->assertTrue( $this->assertTrue(
(bool)$subsite1page->canEdit(), (bool)$subsite1page->canEdit(),
'Administrators can edit all subsites' 'Administrators can edit all subsites'
); );
// @todo: Workaround because GroupSubsites->augmentSQL() is relying on session state // @todo: Workaround because GroupSubsites->augmentSQL() is relying on session state
Subsite::changeSubsite($subsite1); Subsite::changeSubsite($subsite1);
Session::set("loggedInAs", $subsite1member->ID); Session::set("loggedInAs", $subsite1member->ID);
$this->assertTrue( $this->assertTrue(
(bool)$subsite1page->canEdit(), (bool)$subsite1page->canEdit(),
'Members can edit pages on a subsite if they are in a group belonging to this subsite' 'Members can edit pages on a subsite if they are in a group belonging to this subsite'
); );
Session::set("loggedInAs", $subsite2member->ID); Session::set("loggedInAs", $subsite2member->ID);
$this->assertFalse( $this->assertFalse(
(bool)$subsite1page->canEdit(), (bool)$subsite1page->canEdit(),
'Members cant edit pages on a subsite if they are not in a group belonging to this subsite' 'Members cant edit pages on a subsite if they are not in a group belonging to this subsite'
); );
// @todo: Workaround because GroupSubsites->augmentSQL() is relying on session state // @todo: Workaround because GroupSubsites->augmentSQL() is relying on session state
Subsite::changeSubsite(0); Subsite::changeSubsite(0);
$this->assertFalse( $this->assertFalse(
$mainpage->canEdit(), $mainpage->canEdit(),
'Members cant edit pages on the main site if they are not in a group allowing this' 'Members cant edit pages on the main site if they are not in a group allowing this'
); );
} }
/** /**
* Similar to {@link SubsitesVirtualPageTest->testSubsiteVirtualPageCanHaveSameUrlsegmentAsOtherSubsite()}. * Similar to {@link SubsitesVirtualPageTest->testSubsiteVirtualPageCanHaveSameUrlsegmentAsOtherSubsite()}.
*/ */
function testTwoPagesWithSameURLOnDifferentSubsites() { function testTwoPagesWithSameURLOnDifferentSubsites()
// Set up a couple of pages with the same URL on different subsites {
$s1 = $this->objFromFixture(Subsite::class,'domaintest1'); // Set up a couple of pages with the same URL on different subsites
$s2 = $this->objFromFixture(Subsite::class,'domaintest2'); $s1 = $this->objFromFixture(Subsite::class, 'domaintest1');
$s2 = $this->objFromFixture(Subsite::class, 'domaintest2');
$p1 = new SiteTree(); $p1 = new SiteTree();
$p1->Title = $p1->URLSegment = "test-page"; $p1->Title = $p1->URLSegment = "test-page";
$p1->SubsiteID = $s1->ID; $p1->SubsiteID = $s1->ID;
$p1->write(); $p1->write();
$p2 = new SiteTree(); $p2 = new SiteTree();
$p2->Title = $p1->URLSegment = "test-page"; $p2->Title = $p1->URLSegment = "test-page";
$p2->SubsiteID = $s2->ID; $p2->SubsiteID = $s2->ID;
$p2->write(); $p2->write();
// Check that the URLs weren't modified in our set-up // Check that the URLs weren't modified in our set-up
$this->assertEquals($p1->URLSegment, 'test-page'); $this->assertEquals($p1->URLSegment, 'test-page');
$this->assertEquals($p2->URLSegment, 'test-page'); $this->assertEquals($p2->URLSegment, 'test-page');
// Check that if we switch between the different subsites, we receive the correct pages // Check that if we switch between the different subsites, we receive the correct pages
Subsite::changeSubsite($s1); Subsite::changeSubsite($s1);
$this->assertEquals($p1->ID, SiteTree::get_by_link('test-page')->ID); $this->assertEquals($p1->ID, SiteTree::get_by_link('test-page')->ID);
Subsite::changeSubsite($s2); Subsite::changeSubsite($s2);
$this->assertEquals($p2->ID, SiteTree::get_by_link('test-page')->ID); $this->assertEquals($p2->ID, SiteTree::get_by_link('test-page')->ID);
} }
function testPageTypesBlacklistInClassDropdown() { function testPageTypesBlacklistInClassDropdown()
$editor = $this->objFromFixture('SilverStripe\\Security\\Member', 'editor'); {
Session::set("loggedInAs", $editor->ID); $editor = $this->objFromFixture('SilverStripe\\Security\\Member', 'editor');
Session::set("loggedInAs", $editor->ID);
$s1 = $this->objFromFixture(Subsite::class,'domaintest1'); $s1 = $this->objFromFixture(Subsite::class, 'domaintest1');
$s2 = $this->objFromFixture(Subsite::class,'domaintest2'); $s2 = $this->objFromFixture(Subsite::class, 'domaintest2');
$page = singleton('SilverStripe\\CMS\\Model\\SiteTree'); $page = singleton('SilverStripe\\CMS\\Model\\SiteTree');
$s1->PageTypeBlacklist = 'SiteTreeSubsitesTest_ClassA,ErrorPage'; $s1->PageTypeBlacklist = 'SiteTreeSubsitesTest_ClassA,ErrorPage';
$s1->write(); $s1->write();
Subsite::changeSubsite($s1); Subsite::changeSubsite($s1);
$settingsFields = $page->getSettingsFields()->dataFieldByName('ClassName')->getSource(); $settingsFields = $page->getSettingsFields()->dataFieldByName('ClassName')->getSource();
$this->assertArrayNotHasKey('SilverStripe\\CMS\\Model\\ErrorPage', $this->assertArrayNotHasKey('SilverStripe\\CMS\\Model\\ErrorPage',
$settingsFields $settingsFields
); );
$this->assertArrayNotHasKey('SiteTreeSubsitesTest_ClassA', $this->assertArrayNotHasKey('SiteTreeSubsitesTest_ClassA',
$settingsFields $settingsFields
); );
$this->assertArrayHasKey('SiteTreeSubsitesTest_ClassB', $this->assertArrayHasKey('SiteTreeSubsitesTest_ClassB',
$settingsFields $settingsFields
); );
Subsite::changeSubsite($s2); Subsite::changeSubsite($s2);
$settingsFields = $page->getSettingsFields()->dataFieldByName('ClassName')->getSource(); $settingsFields = $page->getSettingsFields()->dataFieldByName('ClassName')->getSource();
$this->assertArrayHasKey('SilverStripe\\CMS\\Model\\ErrorPage', $this->assertArrayHasKey('SilverStripe\\CMS\\Model\\ErrorPage',
$settingsFields $settingsFields
); );
$this->assertArrayHasKey('SiteTreeSubsitesTest_ClassA', $this->assertArrayHasKey('SiteTreeSubsitesTest_ClassA',
$settingsFields $settingsFields
); );
$this->assertArrayHasKey('SiteTreeSubsitesTest_ClassB', $this->assertArrayHasKey('SiteTreeSubsitesTest_ClassB',
$settingsFields $settingsFields
); );
} }
function testPageTypesBlacklistInCMSMain() { function testPageTypesBlacklistInCMSMain()
$editor = $this->objFromFixture('SilverStripe\\Security\\Member', 'editor'); {
Session::set("loggedInAs", $editor->ID); $editor = $this->objFromFixture('SilverStripe\\Security\\Member', 'editor');
Session::set("loggedInAs", $editor->ID);
$cmsmain = new CMSMain(); $cmsmain = new CMSMain();
$s1 = $this->objFromFixture(Subsite::class,'domaintest1'); $s1 = $this->objFromFixture(Subsite::class, 'domaintest1');
$s2 = $this->objFromFixture(Subsite::class,'domaintest2'); $s2 = $this->objFromFixture(Subsite::class, 'domaintest2');
$s1->PageTypeBlacklist = 'SiteTreeSubsitesTest_ClassA,ErrorPage'; $s1->PageTypeBlacklist = 'SiteTreeSubsitesTest_ClassA,ErrorPage';
$s1->write(); $s1->write();
Subsite::changeSubsite($s1); Subsite::changeSubsite($s1);
$hints = Convert::json2array($cmsmain->SiteTreeHints()); $hints = Convert::json2array($cmsmain->SiteTreeHints());
$classes = $hints['Root']['disallowedChildren']; $classes = $hints['Root']['disallowedChildren'];
$this->assertContains('SilverStripe\\CMS\\Model\\ErrorPage', $classes); $this->assertContains('SilverStripe\\CMS\\Model\\ErrorPage', $classes);
$this->assertContains('SiteTreeSubsitesTest_ClassA', $classes); $this->assertContains('SiteTreeSubsitesTest_ClassA', $classes);
$this->assertNotContains('SiteTreeSubsitesTest_ClassB', $classes); $this->assertNotContains('SiteTreeSubsitesTest_ClassB', $classes);
Subsite::changeSubsite($s2); Subsite::changeSubsite($s2);
$hints = Convert::json2array($cmsmain->SiteTreeHints()); $hints = Convert::json2array($cmsmain->SiteTreeHints());
$classes = $hints['Root']['disallowedChildren']; $classes = $hints['Root']['disallowedChildren'];
$this->assertNotContains('SilverStripe\\CMS\\Model\\ErrorPage', $classes); $this->assertNotContains('SilverStripe\\CMS\\Model\\ErrorPage', $classes);
$this->assertNotContains('SiteTreeSubsitesTest_ClassA', $classes); $this->assertNotContains('SiteTreeSubsitesTest_ClassA', $classes);
$this->assertNotContains('SiteTreeSubsitesTest_ClassB', $classes); $this->assertNotContains('SiteTreeSubsitesTest_ClassB', $classes);
} }
} }
class SiteTreeSubsitesTest_ClassA extends SiteTree implements TestOnly {} class SiteTreeSubsitesTest_ClassA extends SiteTree implements TestOnly
{
class SiteTreeSubsitesTest_ClassB extends SiteTree implements TestOnly {} }
class SiteTreeSubsitesTest_ErrorPage extends ErrorPage implements TestOnly { class SiteTreeSubsitesTest_ClassB extends SiteTree implements TestOnly
{
/** }
* Helper method to call protected members
* class SiteTreeSubsitesTest_ErrorPage extends ErrorPage implements TestOnly
* @param int $statusCode {
* @return string
*/ /**
public static function get_error_filename_spy($statusCode) { * Helper method to call protected members
return self::get_error_filename($statusCode); *
} * @param int $statusCode
* @return string
*/
public static function get_error_filename_spy($statusCode)
{
return self::get_error_filename($statusCode);
}
} }

View File

@ -3,148 +3,160 @@
use SilverStripe\Control\Session; use SilverStripe\Control\Session;
use SilverStripe\Core\Config\Config; use SilverStripe\Core\Config\Config;
use SilverStripe\Dev\FunctionalTest; use SilverStripe\Dev\FunctionalTest;
use SilverStripe\Subsites\Model\Subsite;
use SilverStripe\Subsites\Controller\SubsiteXHRController; use SilverStripe\Subsites\Controller\SubsiteXHRController;
use SilverStripe\Subsites\Model\Subsite;
class SubsiteAdminFunctionalTest extends FunctionalTest { class SubsiteAdminFunctionalTest extends FunctionalTest
static $fixture_file = 'subsites/tests/SubsiteTest.yml'; {
static $use_draft_site = true; static $fixture_file = 'subsites/tests/SubsiteTest.yml';
static $use_draft_site = true;
protected $autoFollowRedirection = false; protected $autoFollowRedirection = false;
/** /**
* Helper: FunctionalTest is only able to follow redirection once, we want to go all the way. * Helper: FunctionalTest is only able to follow redirection once, we want to go all the way.
*/ */
function getAndFollowAll($url) { function getAndFollowAll($url)
$response = $this->get($url); {
while ($location = $response->getHeader('Location')) { $response = $this->get($url);
$response = $this->mainSession->followRedirection(); while ($location = $response->getHeader('Location')) {
} $response = $this->mainSession->followRedirection();
echo $response->getHeader('Location'); }
echo $response->getHeader('Location');
return $response; return $response;
} }
/** /**
* Anonymous user cannot access anything. * Anonymous user cannot access anything.
*/ */
function testAnonymousIsForbiddenAdminAccess() { function testAnonymousIsForbiddenAdminAccess()
$response = $this->getAndFollowAll('admin/pages/?SubsiteID=0'); {
$this->assertRegExp('#^Security/login.*#', $this->mainSession->lastUrl(), 'Admin is disallowed'); $response = $this->getAndFollowAll('admin/pages/?SubsiteID=0');
$this->assertRegExp('#^Security/login.*#', $this->mainSession->lastUrl(), 'Admin is disallowed');
$subsite1 = $this->objFromFixture(Subsite::class, 'subsite1'); $subsite1 = $this->objFromFixture(Subsite::class, 'subsite1');
$response = $this->getAndFollowAll("admin/pages/?SubsiteID={$subsite1->ID}"); $response = $this->getAndFollowAll("admin/pages/?SubsiteID={$subsite1->ID}");
$this->assertRegExp('#^Security/login.*#', $this->mainSession->lastUrl(), 'Admin is disallowed'); $this->assertRegExp('#^Security/login.*#', $this->mainSession->lastUrl(), 'Admin is disallowed');
$response = $this->getAndFollowAll(SubsiteXHRController::class); $response = $this->getAndFollowAll(SubsiteXHRController::class);
$this->assertRegExp('#^Security/login.*#', $this->mainSession->lastUrl(), $this->assertRegExp('#^Security/login.*#', $this->mainSession->lastUrl(),
'SubsiteXHRController is disallowed'); 'SubsiteXHRController is disallowed');
} }
/** /**
* Admin should be able to access all subsites and the main site * Admin should be able to access all subsites and the main site
*/ */
function testAdminCanAccessAllSubsites() { function testAdminCanAccessAllSubsites()
$member = $this->objFromFixture('SilverStripe\\Security\\Member', 'admin'); {
Session::set("loggedInAs", $member->ID); $member = $this->objFromFixture('SilverStripe\\Security\\Member', 'admin');
Session::set("loggedInAs", $member->ID);
$this->getAndFollowAll('admin/pages/?SubsiteID=0'); $this->getAndFollowAll('admin/pages/?SubsiteID=0');
$this->assertEquals(Subsite::currentSubsiteID(), '0', 'Can access main site.'); $this->assertEquals(Subsite::currentSubsiteID(), '0', 'Can access main site.');
$this->assertRegExp('#^admin/pages.*#', $this->mainSession->lastUrl(), 'Lands on the correct section'); $this->assertRegExp('#^admin/pages.*#', $this->mainSession->lastUrl(), 'Lands on the correct section');
$subsite1 = $this->objFromFixture(Subsite::class, 'subsite1'); $subsite1 = $this->objFromFixture(Subsite::class, 'subsite1');
$this->getAndFollowAll("admin/pages/?SubsiteID={$subsite1->ID}"); $this->getAndFollowAll("admin/pages/?SubsiteID={$subsite1->ID}");
$this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Can access other subsite.'); $this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Can access other subsite.');
$this->assertRegExp('#^admin/pages.*#', $this->mainSession->lastUrl(), 'Lands on the correct section'); $this->assertRegExp('#^admin/pages.*#', $this->mainSession->lastUrl(), 'Lands on the correct section');
$response = $this->getAndFollowAll(SubsiteXHRController::class); $response = $this->getAndFollowAll(SubsiteXHRController::class);
$this->assertNotRegExp('#^Security/login.*#', $this->mainSession->lastUrl(), $this->assertNotRegExp('#^Security/login.*#', $this->mainSession->lastUrl(),
'SubsiteXHRController is reachable'); 'SubsiteXHRController is reachable');
} }
function testAdminIsRedirectedToObjectsSubsite() { function testAdminIsRedirectedToObjectsSubsite()
$member = $this->objFromFixture('SilverStripe\\Security\\Member', 'admin'); {
Session::set("loggedInAs", $member->ID); $member = $this->objFromFixture('SilverStripe\\Security\\Member', 'admin');
Session::set("loggedInAs", $member->ID);
$mainSubsitePage = $this->objFromFixture('Page', 'mainSubsitePage'); $mainSubsitePage = $this->objFromFixture('Page', 'mainSubsitePage');
$subsite1Home = $this->objFromFixture('Page', 'subsite1_home'); $subsite1Home = $this->objFromFixture('Page', 'subsite1_home');
Config::inst()->nest(); Config::inst()->nest();
Config::modify()->set('SilverStripe\\CMS\\Controllers\\CMSPageEditController', 'treats_subsite_0_as_global', false); Config::modify()->set('SilverStripe\\CMS\\Controllers\\CMSPageEditController', 'treats_subsite_0_as_global',
Subsite::changeSubsite(0); false);
$this->getAndFollowAll("admin/pages/edit/show/$subsite1Home->ID"); Subsite::changeSubsite(0);
$this->assertEquals(Subsite::currentSubsiteID(), $subsite1Home->SubsiteID, 'Loading an object switches the subsite'); $this->getAndFollowAll("admin/pages/edit/show/$subsite1Home->ID");
$this->assertRegExp("#^admin/pages.*#", $this->mainSession->lastUrl(), 'Lands on the correct section'); $this->assertEquals(Subsite::currentSubsiteID(), $subsite1Home->SubsiteID,
'Loading an object switches the subsite');
$this->assertRegExp("#^admin/pages.*#", $this->mainSession->lastUrl(), 'Lands on the correct section');
Config::modify()->set('SilverStripe\\CMS\\Controllers\\CMSPageEditController', 'treats_subsite_0_as_global', true); Config::modify()->set('SilverStripe\\CMS\\Controllers\\CMSPageEditController', 'treats_subsite_0_as_global',
Subsite::changeSubsite(0); true);
$this->getAndFollowAll("admin/pages/edit/show/$subsite1Home->ID"); Subsite::changeSubsite(0);
$this->assertEquals(Subsite::currentSubsiteID(), $subsite1Home->SubsiteID, 'Loading a non-main-site object still switches the subsite if configured with treats_subsite_0_as_global'); $this->getAndFollowAll("admin/pages/edit/show/$subsite1Home->ID");
$this->assertRegExp("#^admin/pages.*#", $this->mainSession->lastUrl(), 'Lands on the correct section'); $this->assertEquals(Subsite::currentSubsiteID(), $subsite1Home->SubsiteID,
'Loading a non-main-site object still switches the subsite if configured with treats_subsite_0_as_global');
$this->assertRegExp("#^admin/pages.*#", $this->mainSession->lastUrl(), 'Lands on the correct section');
$this->getAndFollowAll("admin/pages/edit/show/$mainSubsitePage->ID"); $this->getAndFollowAll("admin/pages/edit/show/$mainSubsitePage->ID");
$this->assertNotEquals(Subsite::currentSubsiteID(), $mainSubsitePage->SubsiteID, 'Loading a main-site object does not change the subsite if configured with treats_subsite_0_as_global'); $this->assertNotEquals(Subsite::currentSubsiteID(), $mainSubsitePage->SubsiteID,
$this->assertRegExp("#^admin/pages.*#", $this->mainSession->lastUrl(), 'Lands on the correct section'); 'Loading a main-site object does not change the subsite if configured with treats_subsite_0_as_global');
$this->assertRegExp("#^admin/pages.*#", $this->mainSession->lastUrl(), 'Lands on the correct section');
Config::inst()->unnest(); Config::inst()->unnest();
} }
/** /**
* User which has AccessAllSubsites set to 1 should be able to access all subsites and main site, * User which has AccessAllSubsites set to 1 should be able to access all subsites and main site,
* even though he does not have the ADMIN permission. * even though he does not have the ADMIN permission.
*/ */
function testEditorCanAccessAllSubsites() { function testEditorCanAccessAllSubsites()
$member = $this->objFromFixture('SilverStripe\\Security\\Member', 'editor'); {
Session::set("loggedInAs", $member->ID); $member = $this->objFromFixture('SilverStripe\\Security\\Member', 'editor');
Session::set("loggedInAs", $member->ID);
$this->getAndFollowAll('admin/pages/?SubsiteID=0'); $this->getAndFollowAll('admin/pages/?SubsiteID=0');
$this->assertEquals(Subsite::currentSubsiteID(), '0', 'Can access main site.'); $this->assertEquals(Subsite::currentSubsiteID(), '0', 'Can access main site.');
$this->assertRegExp('#^admin/pages.*#', $this->mainSession->lastUrl(), 'Lands on the correct section'); $this->assertRegExp('#^admin/pages.*#', $this->mainSession->lastUrl(), 'Lands on the correct section');
$subsite1 = $this->objFromFixture(Subsite::class, 'subsite1'); $subsite1 = $this->objFromFixture(Subsite::class, 'subsite1');
$this->getAndFollowAll("admin/pages/?SubsiteID={$subsite1->ID}"); $this->getAndFollowAll("admin/pages/?SubsiteID={$subsite1->ID}");
$this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Can access other subsite.'); $this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Can access other subsite.');
$this->assertRegExp('#^admin/pages.*#', $this->mainSession->lastUrl(), 'Lands on the correct section'); $this->assertRegExp('#^admin/pages.*#', $this->mainSession->lastUrl(), 'Lands on the correct section');
$response = $this->getAndFollowAll(SubsiteXHRController::class); $response = $this->getAndFollowAll(SubsiteXHRController::class);
$this->assertNotRegExp('#^Security/login.*#', $this->mainSession->lastUrl(), $this->assertNotRegExp('#^Security/login.*#', $this->mainSession->lastUrl(),
'SubsiteXHRController is reachable'); 'SubsiteXHRController is reachable');
} }
/** /**
* Test a member who only has access to one subsite (subsite1) and only some sections (pages and security). * Test a member who only has access to one subsite (subsite1) and only some sections (pages and security).
*/ */
function testSubsiteAdmin() { function testSubsiteAdmin()
$member = $this->objFromFixture('SilverStripe\\Security\\Member', 'subsite1member'); {
Session::set("loggedInAs", $member->ID); $member = $this->objFromFixture('SilverStripe\\Security\\Member', 'subsite1member');
Session::set("loggedInAs", $member->ID);
$subsite1 = $this->objFromFixture(Subsite::class, 'subsite1'); $subsite1 = $this->objFromFixture(Subsite::class, 'subsite1');
// Check allowed URL. // Check allowed URL.
$this->getAndFollowAll("admin/pages/?SubsiteID={$subsite1->ID}"); $this->getAndFollowAll("admin/pages/?SubsiteID={$subsite1->ID}");
$this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Can access own subsite.'); $this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Can access own subsite.');
$this->assertRegExp('#^admin/pages.*#', $this->mainSession->lastUrl(), 'Can access permitted section.'); $this->assertRegExp('#^admin/pages.*#', $this->mainSession->lastUrl(), 'Can access permitted section.');
// Check forbidden section in allowed subsite. // Check forbidden section in allowed subsite.
$this->getAndFollowAll("admin/assets/?SubsiteID={$subsite1->ID}"); $this->getAndFollowAll("admin/assets/?SubsiteID={$subsite1->ID}");
$this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Is redirected within subsite.'); $this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Is redirected within subsite.');
$this->assertNotRegExp('#^admin/assets/.*#', $this->mainSession->lastUrl(), $this->assertNotRegExp('#^admin/assets/.*#', $this->mainSession->lastUrl(),
'Is redirected away from forbidden section'); 'Is redirected away from forbidden section');
// Check forbidden site, on a section that's allowed on another subsite // Check forbidden site, on a section that's allowed on another subsite
$this->getAndFollowAll("admin/pages/?SubsiteID=0"); $this->getAndFollowAll("admin/pages/?SubsiteID=0");
$this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Is redirected to permitted subsite.'); $this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Is redirected to permitted subsite.');
// Check forbidden site, on a section that's not allowed on any other subsite // Check forbidden site, on a section that's not allowed on any other subsite
$this->getAndFollowAll("admin/assets/?SubsiteID=0"); $this->getAndFollowAll("admin/assets/?SubsiteID=0");
$this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Is redirected to first permitted subsite.'); $this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Is redirected to first permitted subsite.');
$this->assertNotRegExp('#^Security/login.*#', $this->mainSession->lastUrl(), 'Is not denied access'); $this->assertNotRegExp('#^Security/login.*#', $this->mainSession->lastUrl(), 'Is not denied access');
// Check the standalone XHR controller. // Check the standalone XHR controller.
$response = $this->getAndFollowAll(SubsiteXHRController::class); $response = $this->getAndFollowAll(SubsiteXHRController::class);
$this->assertNotRegExp('#^Security/login.*#', $this->mainSession->lastUrl(), $this->assertNotRegExp('#^Security/login.*#', $this->mainSession->lastUrl(),
'SubsiteXHRController is reachable'); 'SubsiteXHRController is reachable');
} }
} }

View File

@ -1,55 +1,63 @@
<?php <?php
use SilverStripe\Control\Session;
use SilverStripe\Control\Director;
use SilverStripe\CMS\Controllers\CMSMain; use SilverStripe\CMS\Controllers\CMSMain;
use SilverStripe\Control\Director;
use SilverStripe\Control\Session;
use SilverStripe\Subsites\Model\Subsite; use SilverStripe\Subsites\Model\Subsite;
class SubsiteAdminTest extends BaseSubsiteTest { class SubsiteAdminTest extends BaseSubsiteTest
static $fixture_file = 'subsites/tests/SubsiteTest.yml'; {
static $fixture_file = 'subsites/tests/SubsiteTest.yml';
function adminLoggedInSession() { function adminLoggedInSession()
return new Session(array( {
return new Session([
'loggedInAs' => $this->idFromFixture('SilverStripe\\Security\\Member', 'admin') 'loggedInAs' => $this->idFromFixture('SilverStripe\\Security\\Member', 'admin')
)); ]);
} }
/** /**
* Test generation of the view * Test generation of the view
*/ */
function testBasicView() { function testBasicView()
Subsite::$write_hostmap = false; {
$subsite1ID = $this->objFromFixture(Subsite::class,'domaintest1')->ID; Subsite::$write_hostmap = false;
$subsite1ID = $this->objFromFixture(Subsite::class, 'domaintest1')->ID;
// Open the admin area logged in as admin // Open the admin area logged in as admin
$response1 = Director::test('admin/subsites/', null, $this->adminLoggedInSession()); $response1 = Director::test('admin/subsites/', null, $this->adminLoggedInSession());
// Confirm that this URL gets you the entire page, with the edit form loaded // Confirm that this URL gets you the entire page, with the edit form loaded
$response2 = Director::test("admin/subsites/Subsite/EditForm/field/Subsite/item/$subsite1ID/edit", null, $this->adminLoggedInSession()); $response2 = Director::test("admin/subsites/Subsite/EditForm/field/Subsite/item/$subsite1ID/edit", null,
$this->assertTrue(strpos($response2->getBody(), 'id="Form_ItemEditForm_ID"') !== false, "Testing Form_ItemEditForm_ID exists"); $this->adminLoggedInSession());
$this->assertTrue(strpos($response2->getBody(), 'id="Form_ItemEditForm_ID"') !== false,
"Testing Form_ItemEditForm_ID exists");
$this->assertTrue(strpos($response2->getBody(), '<head') !== false, "Testing <head> exists"); $this->assertTrue(strpos($response2->getBody(), '<head') !== false, "Testing <head> exists");
} }
/** /**
* Test that the main-site user with ADMIN permissions can access all subsites, regardless * Test that the main-site user with ADMIN permissions can access all subsites, regardless
* of whether he is in a subsite-specific group or not. * of whether he is in a subsite-specific group or not.
*/ */
function testMainsiteAdminCanAccessAllSubsites() { function testMainsiteAdminCanAccessAllSubsites()
$member = $this->objFromFixture('SilverStripe\\Security\\Member', 'admin'); {
Session::set("loggedInAs", $member->ID); $member = $this->objFromFixture('SilverStripe\\Security\\Member', 'admin');
Session::set("loggedInAs", $member->ID);
$cmsMain = new CMSMain(); $cmsMain = new CMSMain();
foreach($cmsMain->Subsites() as $subsite) { foreach ($cmsMain->Subsites() as $subsite) {
$ids[$subsite->ID] = true; $ids[$subsite->ID] = true;
} }
$this->assertArrayHasKey(0, $ids, "Main site accessible"); $this->assertArrayHasKey(0, $ids, "Main site accessible");
$this->assertArrayHasKey($this->idFromFixture(Subsite::class,'main'), $ids, "Site with no groups inaccesible"); $this->assertArrayHasKey($this->idFromFixture(Subsite::class, 'main'), $ids, "Site with no groups inaccesible");
$this->assertArrayHasKey($this->idFromFixture(Subsite::class,'subsite1'), $ids, "Subsite1 Template inaccessible"); $this->assertArrayHasKey($this->idFromFixture(Subsite::class, 'subsite1'), $ids,
$this->assertArrayHasKey($this->idFromFixture(Subsite::class,'subsite2'), $ids, "Subsite2 Template inaccessible"); "Subsite1 Template inaccessible");
} $this->assertArrayHasKey($this->idFromFixture(Subsite::class, 'subsite2'), $ids,
"Subsite2 Template inaccessible");
}
} }

View File

@ -1,368 +1,390 @@
<?php <?php
use SilverStripe\ORM\DataObject;
use SilverStripe\Control\Director; use SilverStripe\Control\Director;
use SilverStripe\ORM\DataObject;
use SilverStripe\Subsites\Model\Subsite; use SilverStripe\Subsites\Model\Subsite;
use SilverStripe\Subsites\Model\SubsiteDomain; use SilverStripe\Subsites\Model\SubsiteDomain;
class SubsiteTest extends BaseSubsiteTest { class SubsiteTest extends BaseSubsiteTest
{
static $fixture_file = 'subsites/tests/SubsiteTest.yml'; static $fixture_file = 'subsites/tests/SubsiteTest.yml';
function setUp() { function setUp()
parent::setUp(); {
parent::setUp();
$this->origStrictSubdomainMatching = Subsite::$strict_subdomain_matching; $this->origStrictSubdomainMatching = Subsite::$strict_subdomain_matching;
Subsite::$strict_subdomain_matching = false; Subsite::$strict_subdomain_matching = false;
} }
function tearDown() { function tearDown()
parent::tearDown(); {
parent::tearDown();
Subsite::$strict_subdomain_matching = $this->origStrictSubdomainMatching; Subsite::$strict_subdomain_matching = $this->origStrictSubdomainMatching;
} }
/** /**
* Create a new subsite from the template and verify that all the template's pages are copied * Create a new subsite from the template and verify that all the template's pages are copied
*/ */
function testSubsiteCreation() { function testSubsiteCreation()
Subsite::$write_hostmap = false; {
Subsite::$write_hostmap = false;
// Create the instance // Create the instance
$template = $this->objFromFixture(Subsite::class, 'main'); $template = $this->objFromFixture(Subsite::class, 'main');
// Test that changeSubsite is working // Test that changeSubsite is working
Subsite::changeSubsite($template->ID); Subsite::changeSubsite($template->ID);
$tmplStaff = $this->objFromFixture('Page','staff'); $tmplStaff = $this->objFromFixture('Page', 'staff');
$tmplHome = DataObject::get_one('Page', "\"URLSegment\" = 'home'"); $tmplHome = DataObject::get_one('Page', "\"URLSegment\" = 'home'");
// Publish all the pages in the template, testing that DataObject::get only returns pages from the chosen subsite // Publish all the pages in the template, testing that DataObject::get only returns pages from the chosen subsite
$pages = DataObject::get("SilverStripe\\CMS\\Model\\SiteTree"); $pages = DataObject::get("SilverStripe\\CMS\\Model\\SiteTree");
$totalPages = $pages->Count(); $totalPages = $pages->Count();
foreach($pages as $page) { foreach ($pages as $page) {
$this->assertEquals($template->ID, $page->SubsiteID); $this->assertEquals($template->ID, $page->SubsiteID);
$page->publish('Stage', 'Live'); $page->publish('Stage', 'Live');
} }
// Create a new site // Create a new site
$subsite = $template->duplicate(); $subsite = $template->duplicate();
// Check title // Check title
$this->assertEquals($subsite->Title, $template->Title); $this->assertEquals($subsite->Title, $template->Title);
// Another test that changeSubsite is working // Another test that changeSubsite is working
$subsite->activate(); $subsite->activate();
$siteHome = DataObject::get_one('Page', "\"URLSegment\" = 'home'"); $siteHome = DataObject::get_one('Page', "\"URLSegment\" = 'home'");
$this->assertNotEquals($siteHome, false, 'Home Page for subsite not found'); $this->assertNotEquals($siteHome, false, 'Home Page for subsite not found');
$this->assertEquals($subsite->ID, $siteHome->SubsiteID, $this->assertEquals($subsite->ID, $siteHome->SubsiteID,
'createInstance() copies existing pages retaining the same URLSegment' 'createInstance() copies existing pages retaining the same URLSegment'
); );
Subsite::changeSubsite(0); Subsite::changeSubsite(0);
} }
/** /**
* Confirm that domain lookup is working * Confirm that domain lookup is working
*/ */
function testDomainLookup() { function testDomainLookup()
// Clear existing fixtures {
foreach(DataObject::get(Subsite::class) as $subsite) $subsite->delete(); // Clear existing fixtures
foreach(DataObject::get(SubsiteDomain::class) as $domain) $domain->delete(); foreach (DataObject::get(Subsite::class) as $subsite) {
$subsite->delete();
}
foreach (DataObject::get(SubsiteDomain::class) as $domain) {
$domain->delete();
}
// Much more expressive than YML in this case // Much more expressive than YML in this case
$subsite1 = $this->createSubsiteWithDomains(array( $subsite1 = $this->createSubsiteWithDomains([
'one.example.org' => true, 'one.example.org' => true,
'one.*' => false, 'one.*' => false,
)); ]);
$subsite2 = $this->createSubsiteWithDomains(array( $subsite2 = $this->createSubsiteWithDomains([
'two.mysite.com' => true, 'two.mysite.com' => true,
'*.mysite.com' => false, '*.mysite.com' => false,
'subdomain.onmultiplesubsites.com' => false, 'subdomain.onmultiplesubsites.com' => false,
)); ]);
$subsite3 = $this->createSubsiteWithDomains(array( $subsite3 = $this->createSubsiteWithDomains([
'three.*' => true, // wildcards in primary domain are not recommended 'three.*' => true, // wildcards in primary domain are not recommended
'subdomain.unique.com' => false, 'subdomain.unique.com' => false,
'*.onmultiplesubsites.com' => false, '*.onmultiplesubsites.com' => false,
)); ]);
$this->assertEquals( $this->assertEquals(
$subsite3->ID, $subsite3->ID,
Subsite::getSubsiteIDForDomain('subdomain.unique.com'), Subsite::getSubsiteIDForDomain('subdomain.unique.com'),
'Full unique match' 'Full unique match'
); );
$this->assertEquals( $this->assertEquals(
$subsite1->ID, $subsite1->ID,
Subsite::getSubsiteIDForDomain('one.example.org'), Subsite::getSubsiteIDForDomain('one.example.org'),
'Full match, doesn\'t complain about multiple matches within a single subsite' 'Full match, doesn\'t complain about multiple matches within a single subsite'
); );
$failed = false; $failed = false;
try { try {
Subsite::getSubsiteIDForDomain('subdomain.onmultiplesubsites.com'); Subsite::getSubsiteIDForDomain('subdomain.onmultiplesubsites.com');
} catch(UnexpectedValueException $e) { } catch (UnexpectedValueException $e) {
$failed = true; $failed = true;
} }
$this->assertTrue( $this->assertTrue(
$failed, $failed,
'Fails on multiple matches with wildcard vs. www across multiple subsites' 'Fails on multiple matches with wildcard vs. www across multiple subsites'
); );
$this->assertEquals( $this->assertEquals(
$subsite1->ID, $subsite1->ID,
Subsite::getSubsiteIDForDomain('one.unique.com'), Subsite::getSubsiteIDForDomain('one.unique.com'),
'Fuzzy match suffixed with wildcard (rule "one.*")' 'Fuzzy match suffixed with wildcard (rule "one.*")'
); );
$this->assertEquals( $this->assertEquals(
$subsite2->ID, $subsite2->ID,
Subsite::getSubsiteIDForDomain('two.mysite.com'), Subsite::getSubsiteIDForDomain('two.mysite.com'),
'Matches correct subsite for rule' 'Matches correct subsite for rule'
); );
$this->assertEquals( $this->assertEquals(
$subsite2->ID, $subsite2->ID,
Subsite::getSubsiteIDForDomain('other.mysite.com'), Subsite::getSubsiteIDForDomain('other.mysite.com'),
'Fuzzy match prefixed with wildcard (rule "*.mysite.com")' 'Fuzzy match prefixed with wildcard (rule "*.mysite.com")'
); );
$this->assertEquals( $this->assertEquals(
0, 0,
Subsite::getSubsiteIDForDomain('unknown.madeup.com'), Subsite::getSubsiteIDForDomain('unknown.madeup.com'),
"Doesn't match unknown subsite" "Doesn't match unknown subsite"
); );
} }
function testStrictSubdomainMatching() { function testStrictSubdomainMatching()
// Clear existing fixtures {
foreach(DataObject::get(Subsite::class) as $subsite) $subsite->delete(); // Clear existing fixtures
foreach(DataObject::get(SubsiteDomain::class) as $domain) $domain->delete(); foreach (DataObject::get(Subsite::class) as $subsite) {
$subsite->delete();
}
foreach (DataObject::get(SubsiteDomain::class) as $domain) {
$domain->delete();
}
// Much more expressive than YML in this case // Much more expressive than YML in this case
$subsite1 = $this->createSubsiteWithDomains(array( $subsite1 = $this->createSubsiteWithDomains([
'example.org' => true, 'example.org' => true,
'example.com' => false, 'example.com' => false,
'*.wildcard.com' => false, '*.wildcard.com' => false,
)); ]);
$subsite2 = $this->createSubsiteWithDomains(array( $subsite2 = $this->createSubsiteWithDomains([
'www.example.org' => true, 'www.example.org' => true,
'www.wildcard.com' => false, 'www.wildcard.com' => false,
)); ]);
Subsite::$strict_subdomain_matching = false; Subsite::$strict_subdomain_matching = false;
$this->assertEquals( $this->assertEquals(
$subsite1->ID, $subsite1->ID,
Subsite::getSubsiteIDForDomain('example.org'), Subsite::getSubsiteIDForDomain('example.org'),
'Exact matches without strict checking when not using www prefix' 'Exact matches without strict checking when not using www prefix'
); );
$this->assertEquals( $this->assertEquals(
$subsite1->ID, $subsite1->ID,
Subsite::getSubsiteIDForDomain('www.example.org'), Subsite::getSubsiteIDForDomain('www.example.org'),
'Matches without strict checking when using www prefix, still matching first domain regardless of www prefix (falling back to subsite primary key ordering)' 'Matches without strict checking when using www prefix, still matching first domain regardless of www prefix (falling back to subsite primary key ordering)'
); );
$this->assertEquals( $this->assertEquals(
$subsite1->ID, $subsite1->ID,
Subsite::getSubsiteIDForDomain('www.example.com'), Subsite::getSubsiteIDForDomain('www.example.com'),
'Fuzzy matches without strict checking with www prefix' 'Fuzzy matches without strict checking with www prefix'
); );
$this->assertEquals( $this->assertEquals(
0, 0,
Subsite::getSubsiteIDForDomain('www.wildcard.com'), Subsite::getSubsiteIDForDomain('www.wildcard.com'),
'Doesn\'t match www prefix without strict check, even if a wildcard subdomain is in place' 'Doesn\'t match www prefix without strict check, even if a wildcard subdomain is in place'
); );
Subsite::$strict_subdomain_matching = true; Subsite::$strict_subdomain_matching = true;
$this->assertEquals( $this->assertEquals(
$subsite1->ID, $subsite1->ID,
Subsite::getSubsiteIDForDomain('example.org'), Subsite::getSubsiteIDForDomain('example.org'),
'Matches with strict checking when not using www prefix' 'Matches with strict checking when not using www prefix'
); );
$this->assertEquals( $this->assertEquals(
$subsite2->ID, // not 1 $subsite2->ID, // not 1
Subsite::getSubsiteIDForDomain('www.example.org'), Subsite::getSubsiteIDForDomain('www.example.org'),
'Matches with strict checking when using www prefix' 'Matches with strict checking when using www prefix'
); );
$this->assertEquals( $this->assertEquals(
0, 0,
Subsite::getSubsiteIDForDomain('www.example.com'), Subsite::getSubsiteIDForDomain('www.example.com'),
'Doesn\'t fuzzy match with strict checking when using www prefix' 'Doesn\'t fuzzy match with strict checking when using www prefix'
); );
$failed = false; $failed = false;
try { try {
Subsite::getSubsiteIDForDomain('www.wildcard.com'); Subsite::getSubsiteIDForDomain('www.wildcard.com');
} catch(UnexpectedValueException $e) { } catch (UnexpectedValueException $e) {
$failed = true; $failed = true;
} }
$this->assertTrue( $this->assertTrue(
$failed, $failed,
'Fails on multiple matches with strict checking and wildcard vs. www' 'Fails on multiple matches with strict checking and wildcard vs. www'
); );
} }
protected function createSubsiteWithDomains($domains) { protected function createSubsiteWithDomains($domains)
$subsite = new Subsite(array( {
'Title' => 'My Subsite' $subsite = new Subsite([
)); 'Title' => 'My Subsite'
$subsite->write(); ]);
foreach($domains as $domainStr => $isPrimary) { $subsite->write();
$domain = new SubsiteDomain(array( foreach ($domains as $domainStr => $isPrimary) {
'Domain' => $domainStr, $domain = new SubsiteDomain([
'IsPrimary' => $isPrimary, 'Domain' => $domainStr,
'SubsiteID' => $subsite->ID 'IsPrimary' => $isPrimary,
)); 'SubsiteID' => $subsite->ID
$domain->write(); ]);
} $domain->write();
}
return $subsite; return $subsite;
} }
/** /**
* Test the Subsite->domain() method * Test the Subsite->domain() method
*/ */
function testDefaultDomain() { function testDefaultDomain()
$this->assertEquals('one.example.org', {
$this->objFromFixture(Subsite::class,'domaintest1')->domain()); $this->assertEquals('one.example.org',
$this->objFromFixture(Subsite::class, 'domaintest1')->domain());
$this->assertEquals('two.mysite.com', $this->assertEquals('two.mysite.com',
$this->objFromFixture(Subsite::class,'domaintest2')->domain()); $this->objFromFixture(Subsite::class, 'domaintest2')->domain());
$originalHTTPHost = $_SERVER['HTTP_HOST']; $originalHTTPHost = $_SERVER['HTTP_HOST'];
$_SERVER['HTTP_HOST'] = "www.example.org"; $_SERVER['HTTP_HOST'] = "www.example.org";
$this->assertEquals('three.example.org', $this->assertEquals('three.example.org',
$this->objFromFixture(Subsite::class,'domaintest3')->domain()); $this->objFromFixture(Subsite::class, 'domaintest3')->domain());
$_SERVER['HTTP_HOST'] = "mysite.example.org"; $_SERVER['HTTP_HOST'] = "mysite.example.org";
$this->assertEquals('three.mysite.example.org', $this->assertEquals('three.mysite.example.org',
$this->objFromFixture(Subsite::class,'domaintest3')->domain()); $this->objFromFixture(Subsite::class, 'domaintest3')->domain());
$this->assertEquals($_SERVER['HTTP_HOST'], singleton(Subsite::class)->PrimaryDomain); $this->assertEquals($_SERVER['HTTP_HOST'], singleton(Subsite::class)->PrimaryDomain);
$this->assertEquals('http://'.$_SERVER['HTTP_HOST'].Director::baseURL(), singleton(Subsite::class)->absoluteBaseURL()); $this->assertEquals('http://' . $_SERVER['HTTP_HOST'] . Director::baseURL(),
singleton(Subsite::class)->absoluteBaseURL());
$_SERVER['HTTP_HOST'] = $originalHTTPHost; $_SERVER['HTTP_HOST'] = $originalHTTPHost;
} }
function testAllSites() { function testAllSites()
$subsites = Subsite::all_sites(); {
$this->assertDOSEquals(array( $subsites = Subsite::all_sites();
array('Title' =>'Main site'), $this->assertDOSEquals([
array('Title' =>'Template'), ['Title' => 'Main site'],
array('Title' =>'Subsite1 Template'), ['Title' => 'Template'],
array('Title' =>'Subsite2 Template'), ['Title' => 'Subsite1 Template'],
array('Title' =>'Test 1'), ['Title' => 'Subsite2 Template'],
array('Title' =>'Test 2'), ['Title' => 'Test 1'],
array('Title' =>'Test 3') ['Title' => 'Test 2'],
), $subsites, 'Lists all subsites'); ['Title' => 'Test 3']
} ], $subsites, 'Lists all subsites');
}
function testAllAccessibleSites() { function testAllAccessibleSites()
$member = $this->objFromFixture('SilverStripe\\Security\\Member', 'subsite1member'); {
$member = $this->objFromFixture('SilverStripe\\Security\\Member', 'subsite1member');
$subsites = Subsite::all_accessible_sites(true, 'Main site', $member); $subsites = Subsite::all_accessible_sites(true, 'Main site', $member);
$this->assertDOSEquals(array( $this->assertDOSEquals([
array('Title' =>'Subsite1 Template') ['Title' => 'Subsite1 Template']
), $subsites, 'Lists member-accessible sites.'); ], $subsites, 'Lists member-accessible sites.');
} }
/** /**
* Test Subsite::accessible_sites() * Test Subsite::accessible_sites()
*/ */
function testAccessibleSites() { function testAccessibleSites()
$member1Sites = Subsite::accessible_sites("CMS_ACCESS_CMSMain", false, null, {
$this->objFromFixture('SilverStripe\\Security\\Member', 'subsite1member')); $member1Sites = Subsite::accessible_sites("CMS_ACCESS_CMSMain", false, null,
$member1SiteTitles = $member1Sites->column("Title"); $this->objFromFixture('SilverStripe\\Security\\Member', 'subsite1member'));
sort($member1SiteTitles); $member1SiteTitles = $member1Sites->column("Title");
$this->assertEquals('Subsite1 Template', $member1SiteTitles[0], 'Member can get to a subsite via a group'); sort($member1SiteTitles);
$this->assertEquals('Subsite1 Template', $member1SiteTitles[0], 'Member can get to a subsite via a group');
$adminSites = Subsite::accessible_sites("CMS_ACCESS_CMSMain", false, null, $adminSites = Subsite::accessible_sites("CMS_ACCESS_CMSMain", false, null,
$this->objFromFixture('SilverStripe\\Security\\Member', 'admin')); $this->objFromFixture('SilverStripe\\Security\\Member', 'admin'));
$adminSiteTitles = $adminSites->column("Title"); $adminSiteTitles = $adminSites->column("Title");
sort($adminSiteTitles); sort($adminSiteTitles);
$this->assertEquals(array( $this->assertEquals([
'Subsite1 Template', 'Subsite1 Template',
'Subsite2 Template', 'Subsite2 Template',
'Template', 'Template',
'Test 1', 'Test 1',
'Test 2', 'Test 2',
'Test 3', 'Test 3',
), $adminSiteTitles); ], $adminSiteTitles);
$member2Sites = Subsite::accessible_sites("CMS_ACCESS_CMSMain", false, null, $member2Sites = Subsite::accessible_sites("CMS_ACCESS_CMSMain", false, null,
$this->objFromFixture('SilverStripe\\Security\\Member', 'subsite1member2')); $this->objFromFixture('SilverStripe\\Security\\Member', 'subsite1member2'));
$member2SiteTitles = $member2Sites->column("Title"); $member2SiteTitles = $member2Sites->column("Title");
sort($member2SiteTitles); sort($member2SiteTitles);
$this->assertEquals('Subsite1 Template', $member2SiteTitles[0], 'Member can get to subsite via a group role'); $this->assertEquals('Subsite1 Template', $member2SiteTitles[0], 'Member can get to subsite via a group role');
} }
function testhasMainSitePermission() { function testhasMainSitePermission()
$admin = $this->objFromFixture('SilverStripe\\Security\\Member', 'admin'); {
$subsite1member = $this->objFromFixture('SilverStripe\\Security\\Member', 'subsite1member'); $admin = $this->objFromFixture('SilverStripe\\Security\\Member', 'admin');
$subsite1admin = $this->objFromFixture('SilverStripe\\Security\\Member', 'subsite1admin'); $subsite1member = $this->objFromFixture('SilverStripe\\Security\\Member', 'subsite1member');
$allsubsitesauthor = $this->objFromFixture('SilverStripe\\Security\\Member', 'allsubsitesauthor'); $subsite1admin = $this->objFromFixture('SilverStripe\\Security\\Member', 'subsite1admin');
$allsubsitesauthor = $this->objFromFixture('SilverStripe\\Security\\Member', 'allsubsitesauthor');
$this->assertTrue( $this->assertTrue(
Subsite::hasMainSitePermission($admin), Subsite::hasMainSitePermission($admin),
'Default permissions granted for super-admin' 'Default permissions granted for super-admin'
); );
$this->assertTrue( $this->assertTrue(
Subsite::hasMainSitePermission($admin, array("ADMIN")), Subsite::hasMainSitePermission($admin, ["ADMIN"]),
'ADMIN permissions granted for super-admin' 'ADMIN permissions granted for super-admin'
); );
$this->assertFalse( $this->assertFalse(
Subsite::hasMainSitePermission($subsite1admin, array("ADMIN")), Subsite::hasMainSitePermission($subsite1admin, ["ADMIN"]),
'ADMIN permissions (on main site) denied for subsite1 admin' 'ADMIN permissions (on main site) denied for subsite1 admin'
); );
$this->assertFalse( $this->assertFalse(
Subsite::hasMainSitePermission($subsite1admin, array("CMS_ACCESS_CMSMain")), Subsite::hasMainSitePermission($subsite1admin, ["CMS_ACCESS_CMSMain"]),
'CMS_ACCESS_CMSMain (on main site) denied for subsite1 admin' 'CMS_ACCESS_CMSMain (on main site) denied for subsite1 admin'
); );
$this->assertFalse( $this->assertFalse(
Subsite::hasMainSitePermission($allsubsitesauthor, array("ADMIN")), Subsite::hasMainSitePermission($allsubsitesauthor, ["ADMIN"]),
'ADMIN permissions (on main site) denied for CMS author with edit rights on all subsites' 'ADMIN permissions (on main site) denied for CMS author with edit rights on all subsites'
); );
$this->assertTrue( $this->assertTrue(
Subsite::hasMainSitePermission($allsubsitesauthor, array("CMS_ACCESS_CMSMain")), Subsite::hasMainSitePermission($allsubsitesauthor, ["CMS_ACCESS_CMSMain"]),
'CMS_ACCESS_CMSMain (on main site) granted for CMS author with edit rights on all subsites' 'CMS_ACCESS_CMSMain (on main site) granted for CMS author with edit rights on all subsites'
); );
$this->assertFalse( $this->assertFalse(
Subsite::hasMainSitePermission($subsite1member, array("ADMIN")), Subsite::hasMainSitePermission($subsite1member, ["ADMIN"]),
'ADMIN (on main site) denied for subsite1 subsite1 cms author' 'ADMIN (on main site) denied for subsite1 subsite1 cms author'
); );
$this->assertFalse( $this->assertFalse(
Subsite::hasMainSitePermission($subsite1member, array("CMS_ACCESS_CMSMain")), Subsite::hasMainSitePermission($subsite1member, ["CMS_ACCESS_CMSMain"]),
'CMS_ACCESS_CMSMain (on main site) denied for subsite1 cms author' 'CMS_ACCESS_CMSMain (on main site) denied for subsite1 cms author'
); );
} }
function testDuplicateSubsite() { function testDuplicateSubsite()
// get subsite1 & create page {
$subsite1 = $this->objFromFixture(Subsite::class,'domaintest1'); // get subsite1 & create page
$subsite1->activate(); $subsite1 = $this->objFromFixture(Subsite::class, 'domaintest1');
$page1 = new Page(); $subsite1->activate();
$page1->Title = 'MyAwesomePage'; $page1 = new Page();
$page1->write(); $page1->Title = 'MyAwesomePage';
$page1->doPublish(); $page1->write();
$this->assertEquals($page1->SubsiteID, $subsite1->ID); $page1->doPublish();
$this->assertEquals($page1->SubsiteID, $subsite1->ID);
// duplicate // duplicate
$subsite2 = $subsite1->duplicate(); $subsite2 = $subsite1->duplicate();
$subsite2->activate(); $subsite2->activate();
// change content on dupe // change content on dupe
$page2 = DataObject::get_one('Page', "\"Title\" = 'MyAwesomePage'"); $page2 = DataObject::get_one('Page', "\"Title\" = 'MyAwesomePage'");
$page2->Title = 'MyNewAwesomePage'; $page2->Title = 'MyNewAwesomePage';
$page2->write(); $page2->write();
$page2->doPublish(); $page2->doPublish();
// check change & check change has not affected subiste1 // check change & check change has not affected subiste1
$subsite1->activate(); $subsite1->activate();
$this->assertEquals('MyAwesomePage', DataObject::get_by_id('Page', $page1->ID)->Title); $this->assertEquals('MyAwesomePage', DataObject::get_by_id('Page', $page1->ID)->Title);
$subsite2->activate(); $subsite2->activate();
$this->assertEquals('MyNewAwesomePage', DataObject::get_by_id('Page', $page2->ID)->Title); $this->assertEquals('MyNewAwesomePage', DataObject::get_by_id('Page', $page2->ID)->Title);
} }
} }

View File

@ -1,279 +1,289 @@
<?php <?php
use SilverStripe\Assets\Filesystem; use SilverStripe\Assets\Filesystem;
use SilverStripe\ORM\DB;
use SilverStripe\Core\Config\Config;
use SilverStripe\Assets\Tests\Storage\AssetStoreTest\TestAssetStore; use SilverStripe\Assets\Tests\Storage\AssetStoreTest\TestAssetStore;
use SilverStripe\Core\Config\Config;
use SilverStripe\ORM\DB;
use SilverStripe\Subsites\Model\Subsite; use SilverStripe\Subsites\Model\Subsite;
use SilverStripe\Subsites\Pages\SubsitesVirtualPage; use SilverStripe\Subsites\Pages\SubsitesVirtualPage;
use SilverStripe\Versioned\Versioned; use SilverStripe\Versioned\Versioned;
class SubsitesVirtualPageTest extends BaseSubsiteTest { class SubsitesVirtualPageTest extends BaseSubsiteTest
static $fixture_file = array( {
'subsites/tests/SubsiteTest.yml', static $fixture_file = [
'subsites/tests/SubsitesVirtualPageTest.yml', 'subsites/tests/SubsiteTest.yml',
); 'subsites/tests/SubsitesVirtualPageTest.yml',
];
public function setUp() { public function setUp()
parent::setUp(); {
parent::setUp();
// Set backend root to /DataDifferencerTest // Set backend root to /DataDifferencerTest
TestAssetStore::activate('SubsitesVirtualPageTest'); TestAssetStore::activate('SubsitesVirtualPageTest');
// Create a test files for each of the fixture references // Create a test files for each of the fixture references
$file = $this->objFromFixture('SilverStripe\\Assets\\File', 'file1'); $file = $this->objFromFixture('SilverStripe\\Assets\\File', 'file1');
$page = $this->objFromFixture('SilverStripe\\CMS\\Model\\SiteTree', 'page1'); $page = $this->objFromFixture('SilverStripe\\CMS\\Model\\SiteTree', 'page1');
$fromPath = __DIR__ . '/testscript-test-file.pdf'; $fromPath = __DIR__ . '/testscript-test-file.pdf';
$destPath = TestAssetStore::getLocalPath($file); $destPath = TestAssetStore::getLocalPath($file);
Filesystem::makeFolder(dirname($destPath)); Filesystem::makeFolder(dirname($destPath));
copy($fromPath, $destPath); copy($fromPath, $destPath);
// Hack in site link tracking after the fact // Hack in site link tracking after the fact
$page->Content = '<p><img src="'. $file->getURL(). '" data-fileid="' . $file->ID . '" /></p>'; $page->Content = '<p><img src="' . $file->getURL() . '" data-fileid="' . $file->ID . '" /></p>';
$page->write(); $page->write();
} }
public function tearDown() { public function tearDown()
TestAssetStore::reset(); {
parent::tearDown(); TestAssetStore::reset();
} parent::tearDown();
}
// Attempt to bring main:linky to subsite2:linky // Attempt to bring main:linky to subsite2:linky
function testVirtualPageFromAnotherSubsite() { function testVirtualPageFromAnotherSubsite()
Subsite::$write_hostmap = false; {
Subsite::$write_hostmap = false;
$subsite = $this->objFromFixture(Subsite::class, 'subsite2'); $subsite = $this->objFromFixture(Subsite::class, 'subsite2');
Subsite::changeSubsite($subsite->ID); Subsite::changeSubsite($subsite->ID);
Subsite::$disable_subsite_filter = false; Subsite::$disable_subsite_filter = false;
$linky = $this->objFromFixture('Page', 'linky'); $linky = $this->objFromFixture('Page', 'linky');
$svp = new SubsitesVirtualPage(); $svp = new SubsitesVirtualPage();
$svp->CopyContentFromID = $linky->ID; $svp->CopyContentFromID = $linky->ID;
$svp->SubsiteID = $subsite->ID; $svp->SubsiteID = $subsite->ID;
$svp->URLSegment = 'linky'; $svp->URLSegment = 'linky';
$svp->write(); $svp->write();
$this->assertEquals($svp->SubsiteID, $subsite->ID); $this->assertEquals($svp->SubsiteID, $subsite->ID);
$this->assertEquals($svp->Title, $linky->Title); $this->assertEquals($svp->Title, $linky->Title);
} }
function testFileLinkRewritingOnVirtualPages() { function testFileLinkRewritingOnVirtualPages()
// File setup {
$this->logInWithPermission('ADMIN'); // File setup
$this->logInWithPermission('ADMIN');
// Publish the source page // Publish the source page
$page = $this->objFromFixture('SilverStripe\\CMS\\Model\\SiteTree', 'page1'); $page = $this->objFromFixture('SilverStripe\\CMS\\Model\\SiteTree', 'page1');
$this->assertTrue($page->doPublish()); $this->assertTrue($page->doPublish());
// Create a virtual page from it, and publish that // Create a virtual page from it, and publish that
$svp = new SubsitesVirtualPage(); $svp = new SubsitesVirtualPage();
$svp->CopyContentFromID = $page->ID; $svp->CopyContentFromID = $page->ID;
$svp->write(); $svp->write();
$svp->doPublish(); $svp->doPublish();
// Rename the file // Rename the file
$file = $this->objFromFixture('SilverStripe\\Assets\\File', 'file1'); $file = $this->objFromFixture('SilverStripe\\Assets\\File', 'file1');
$file->Name = 'renamed-test-file.pdf'; $file->Name = 'renamed-test-file.pdf';
$file->write(); $file->write();
// Verify that the draft and publish virtual pages both have the corrected link // Verify that the draft and publish virtual pages both have the corrected link
$this->assertContains('<img src="/assets/SubsitesVirtualPageTest/464dedb70a/renamed-test-file.pdf"', $this->assertContains('<img src="/assets/SubsitesVirtualPageTest/464dedb70a/renamed-test-file.pdf"',
DB::query("SELECT \"Content\" FROM \"SiteTree\" WHERE \"ID\" = $svp->ID")->value()); DB::query("SELECT \"Content\" FROM \"SiteTree\" WHERE \"ID\" = $svp->ID")->value());
$this->assertContains('<img src="/assets/SubsitesVirtualPageTest/464dedb70a/renamed-test-file.pdf"', $this->assertContains('<img src="/assets/SubsitesVirtualPageTest/464dedb70a/renamed-test-file.pdf"',
DB::query("SELECT \"Content\" FROM \"SiteTree_Live\" WHERE \"ID\" = $svp->ID")->value()); DB::query("SELECT \"Content\" FROM \"SiteTree_Live\" WHERE \"ID\" = $svp->ID")->value());
} }
function testSubsiteVirtualPagesArentInappropriatelyPublished() { function testSubsiteVirtualPagesArentInappropriatelyPublished()
// Fixture {
$p = new Page(); // Fixture
$p->Content = "test content"; $p = new Page();
$p->write(); $p->Content = "test content";
$vp = new SubsitesVirtualPage(); $p->write();
$vp->CopyContentFromID = $p->ID; $vp = new SubsitesVirtualPage();
$vp->write(); $vp->CopyContentFromID = $p->ID;
$vp->write();
// VP is oragne // VP is oragne
$this->assertTrue($vp->IsAddedToStage); $this->assertTrue($vp->IsAddedToStage);
// VP is still orange after we publish // VP is still orange after we publish
$p->doPublish(); $p->doPublish();
$this->fixVersionNumberCache($vp); $this->fixVersionNumberCache($vp);
$this->assertTrue($vp->IsAddedToStage); $this->assertTrue($vp->IsAddedToStage);
// A new VP created after P's initial construction // A new VP created after P's initial construction
$vp2 = new SubsitesVirtualPage(); $vp2 = new SubsitesVirtualPage();
$vp2->CopyContentFromID = $p->ID; $vp2->CopyContentFromID = $p->ID;
$vp2->write(); $vp2->write();
$this->assertTrue($vp2->IsAddedToStage); $this->assertTrue($vp2->IsAddedToStage);
// Also remains orange after a republish // Also remains orange after a republish
$p->Content = "new content"; $p->Content = "new content";
$p->write(); $p->write();
$p->doPublish(); $p->doPublish();
$this->fixVersionNumberCache($vp2); $this->fixVersionNumberCache($vp2);
$this->assertTrue($vp2->IsAddedToStage); $this->assertTrue($vp2->IsAddedToStage);
// VP is now published // VP is now published
$vp->doPublish(); $vp->doPublish();
$this->fixVersionNumberCache($vp); $this->fixVersionNumberCache($vp);
$this->assertTrue($vp->ExistsOnLive); $this->assertTrue($vp->ExistsOnLive);
$this->assertFalse($vp->IsModifiedOnStage); $this->assertFalse($vp->IsModifiedOnStage);
// P edited, VP and P both go green // P edited, VP and P both go green
$p->Content = "third content"; $p->Content = "third content";
$p->write(); $p->write();
$this->fixVersionNumberCache($vp, $p); $this->fixVersionNumberCache($vp, $p);
$this->assertTrue($p->IsModifiedOnStage); $this->assertTrue($p->IsModifiedOnStage);
$this->assertTrue($vp->IsModifiedOnStage); $this->assertTrue($vp->IsModifiedOnStage);
// Publish, VP goes black // Publish, VP goes black
$p->doPublish(); $p->doPublish();
$this->fixVersionNumberCache($vp); $this->fixVersionNumberCache($vp);
$this->assertTrue($vp->ExistsOnLive); $this->assertTrue($vp->ExistsOnLive);
$this->assertFalse($vp->IsModifiedOnStage); $this->assertFalse($vp->IsModifiedOnStage);
} }
/** /**
* This test ensures published Subsites Virtual Pages immediately reflect updates * This test ensures published Subsites Virtual Pages immediately reflect updates
* to their published target pages. Note - this has to happen when the virtual page * to their published target pages. Note - this has to happen when the virtual page
* is in a different subsite to the page you are editing and republishing, * is in a different subsite to the page you are editing and republishing,
* otherwise the test will pass falsely due to current subsite ID being the same. * otherwise the test will pass falsely due to current subsite ID being the same.
*/ */
function testPublishedSubsiteVirtualPagesUpdateIfTargetPageUpdates() function testPublishedSubsiteVirtualPagesUpdateIfTargetPageUpdates()
{ {
// create page // create page
$p = new Page(); $p = new Page();
$p->Content = 'Content'; $p->Content = 'Content';
$p->Title = 'Title'; $p->Title = 'Title';
$p->writeToStage('Stage'); $p->writeToStage('Stage');
$p->publish('Stage', 'Live'); $p->publish('Stage', 'Live');
$this->assertTrue($p->ExistsOnLive); $this->assertTrue($p->ExistsOnLive);
// change to subsite // change to subsite
$subsite = $this->objFromFixture(Subsite::class, 'subsite2'); $subsite = $this->objFromFixture(Subsite::class, 'subsite2');
Subsite::changeSubsite($subsite->ID); Subsite::changeSubsite($subsite->ID);
Subsite::$disable_subsite_filter = false; Subsite::$disable_subsite_filter = false;
// create svp in subsite // create svp in subsite
$svp = new SubsitesVirtualPage(); $svp = new SubsitesVirtualPage();
$svp->CopyContentFromID = $p->ID; $svp->CopyContentFromID = $p->ID;
$svp->write(); $svp->write();
$svp->writeToStage('Stage'); $svp->writeToStage('Stage');
$svp->publish('Stage', 'Live'); $svp->publish('Stage', 'Live');
$this->assertEquals($svp->SubsiteID, $subsite->ID); $this->assertEquals($svp->SubsiteID, $subsite->ID);
$this->assertTrue($svp->ExistsOnLive); $this->assertTrue($svp->ExistsOnLive);
// change back to original subsite ("Main site") // change back to original subsite ("Main site")
Subsite::changeSubsite(0); Subsite::changeSubsite(0);
// update original page // update original page
$p->Title = 'New Title'; $p->Title = 'New Title';
// "save & publish" // "save & publish"
$p->writeToStage('Stage'); $p->writeToStage('Stage');
$p->publish('Stage', 'Live'); $p->publish('Stage', 'Live');
$this->assertNotEquals($p->SubsiteID, $subsite->ID); $this->assertNotEquals($p->SubsiteID, $subsite->ID);
// reload SVP from database // reload SVP from database
// can't use DO::get by id because caches. // can't use DO::get by id because caches.
$svpdb = $svp->get()->byID($svp->ID); $svpdb = $svp->get()->byID($svp->ID);
// ensure title changed // ensure title changed
$this->assertEquals($svpdb->Title, $p->Title); $this->assertEquals($svpdb->Title, $p->Title);
} }
function testUnpublishingParentPageUnpublishesSubsiteVirtualPages() { function testUnpublishingParentPageUnpublishesSubsiteVirtualPages()
Config::modify()->set('StaticPublisher', 'disable_realtime', true); {
Config::modify()->set('StaticPublisher', 'disable_realtime', true);
// Go to main site, get parent page // Go to main site, get parent page
$subsite = $this->objFromFixture(Subsite::class, 'main'); $subsite = $this->objFromFixture(Subsite::class, 'main');
Subsite::changeSubsite($subsite->ID); Subsite::changeSubsite($subsite->ID);
$page = $this->objFromFixture('Page', 'importantpage'); $page = $this->objFromFixture('Page', 'importantpage');
// Create two SVPs on other subsites // Create two SVPs on other subsites
$subsite = $this->objFromFixture(Subsite::class, 'subsite1'); $subsite = $this->objFromFixture(Subsite::class, 'subsite1');
Subsite::changeSubsite($subsite->ID); Subsite::changeSubsite($subsite->ID);
$vp1 = new SubsitesVirtualPage(); $vp1 = new SubsitesVirtualPage();
$vp1->CopyContentFromID = $page->ID; $vp1->CopyContentFromID = $page->ID;
$vp1->write(); $vp1->write();
$vp1->doPublish(); $vp1->doPublish();
$subsite = $this->objFromFixture(Subsite::class, 'subsite2'); $subsite = $this->objFromFixture(Subsite::class, 'subsite2');
Subsite::changeSubsite($subsite->ID); Subsite::changeSubsite($subsite->ID);
$vp2 = new SubsitesVirtualPage(); $vp2 = new SubsitesVirtualPage();
$vp2->CopyContentFromID = $page->ID; $vp2->CopyContentFromID = $page->ID;
$vp2->write(); $vp2->write();
$vp2->doPublish(); $vp2->doPublish();
// Switch back to main site, unpublish source // Switch back to main site, unpublish source
$subsite = $this->objFromFixture(Subsite::class, 'main'); $subsite = $this->objFromFixture(Subsite::class, 'main');
Subsite::changeSubsite($subsite->ID); Subsite::changeSubsite($subsite->ID);
$page = $this->objFromFixture('Page', 'importantpage'); $page = $this->objFromFixture('Page', 'importantpage');
$page->doUnpublish(); $page->doUnpublish();
Subsite::changeSubsite($vp1->SubsiteID); Subsite::changeSubsite($vp1->SubsiteID);
$onLive = Versioned::get_one_by_stage(SubsitesVirtualPage::class, 'Live', "\"SiteTree_Live\".\"ID\" = ".$vp1->ID); $onLive = Versioned::get_one_by_stage(SubsitesVirtualPage::class, 'Live',
$this->assertNull($onLive, 'SVP has been removed from live'); "\"SiteTree_Live\".\"ID\" = " . $vp1->ID);
$this->assertNull($onLive, 'SVP has been removed from live');
$subsite = $this->objFromFixture(Subsite::class, 'subsite2'); $subsite = $this->objFromFixture(Subsite::class, 'subsite2');
Subsite::changeSubsite($vp2->SubsiteID); Subsite::changeSubsite($vp2->SubsiteID);
$onLive = Versioned::get_one_by_stage(SubsitesVirtualPage::class, 'Live', "\"SiteTree_Live\".\"ID\" = ".$vp2->ID); $onLive = Versioned::get_one_by_stage(SubsitesVirtualPage::class, 'Live',
$this->assertNull($onLive, 'SVP has been removed from live'); "\"SiteTree_Live\".\"ID\" = " . $vp2->ID);
} $this->assertNull($onLive, 'SVP has been removed from live');
}
/** /**
* Similar to {@link SiteTreeSubsitesTest->testTwoPagesWithSameURLOnDifferentSubsites()} * Similar to {@link SiteTreeSubsitesTest->testTwoPagesWithSameURLOnDifferentSubsites()}
* and {@link SiteTreeSubsitesTest->testPagesInDifferentSubsitesCanShareURLSegment()}. * and {@link SiteTreeSubsitesTest->testPagesInDifferentSubsitesCanShareURLSegment()}.
*/ */
function testSubsiteVirtualPageCanHaveSameUrlsegmentAsOtherSubsite() { function testSubsiteVirtualPageCanHaveSameUrlsegmentAsOtherSubsite()
Subsite::$write_hostmap = false; {
$subsite1 = $this->objFromFixture(Subsite::class, 'subsite1'); Subsite::$write_hostmap = false;
$subsite2 = $this->objFromFixture(Subsite::class, 'subsite2'); $subsite1 = $this->objFromFixture(Subsite::class, 'subsite1');
Subsite::changeSubsite($subsite1->ID); $subsite2 = $this->objFromFixture(Subsite::class, 'subsite2');
Subsite::changeSubsite($subsite1->ID);
$subsite1Page = $this->objFromFixture('Page', 'subsite1_staff'); $subsite1Page = $this->objFromFixture('Page', 'subsite1_staff');
$subsite1Page->URLSegment = 'staff'; $subsite1Page->URLSegment = 'staff';
$subsite1Page->write(); $subsite1Page->write();
// saving on subsite1, and linking to subsite1 // saving on subsite1, and linking to subsite1
$subsite1Vp = new SubsitesVirtualPage(); $subsite1Vp = new SubsitesVirtualPage();
$subsite1Vp->CopyContentFromID = $subsite1Page->ID; $subsite1Vp->CopyContentFromID = $subsite1Page->ID;
$subsite1Vp->SubsiteID = $subsite1->ID; $subsite1Vp->SubsiteID = $subsite1->ID;
$subsite1Vp->write(); $subsite1Vp->write();
$this->assertNotEquals( $this->assertNotEquals(
$subsite1Vp->URLSegment, $subsite1Vp->URLSegment,
$subsite1Page->URLSegment, $subsite1Page->URLSegment,
"Doesn't allow explicit URLSegment overrides when already existing in same subsite" "Doesn't allow explicit URLSegment overrides when already existing in same subsite"
); );
//Change to subsite 2 //Change to subsite 2
Subsite::changeSubsite($subsite2->ID); Subsite::changeSubsite($subsite2->ID);
// saving in subsite2 (which already has a page with URLSegment 'contact-us'), // saving in subsite2 (which already has a page with URLSegment 'contact-us'),
// but linking to a page in subsite1 // but linking to a page in subsite1
$subsite2Vp = new SubsitesVirtualPage(); $subsite2Vp = new SubsitesVirtualPage();
$subsite2Vp->CopyContentFromID = $subsite1Page->ID; $subsite2Vp->CopyContentFromID = $subsite1Page->ID;
$subsite2Vp->SubsiteID = $subsite2->ID; $subsite2Vp->SubsiteID = $subsite2->ID;
$subsite2Vp->write(); $subsite2Vp->write();
$this->assertEquals( $this->assertEquals(
$subsite2Vp->URLSegment, $subsite2Vp->URLSegment,
$subsite1Page->URLSegment, $subsite1Page->URLSegment,
"Does allow explicit URLSegment overrides when only existing in a different subsite" "Does allow explicit URLSegment overrides when only existing in a different subsite"
); );
} }
function fixVersionNumberCache($page) { function fixVersionNumberCache($page)
$pages = func_get_args(); {
foreach($pages as $p) { $pages = func_get_args();
Versioned::prepopulate_versionnumber_cache('SilverStripe\\CMS\\Model\\SiteTree', 'Stage', array($p->ID)); foreach ($pages as $p) {
Versioned::prepopulate_versionnumber_cache('SilverStripe\\CMS\\Model\\SiteTree', 'Live', array($p->ID)); Versioned::prepopulate_versionnumber_cache('SilverStripe\\CMS\\Model\\SiteTree', 'Stage', [$p->ID]);
} Versioned::prepopulate_versionnumber_cache('SilverStripe\\CMS\\Model\\SiteTree', 'Live', [$p->ID]);
} }
}
} }

View File

@ -2,17 +2,18 @@
namespace Subsites\Test\Behaviour; namespace Subsites\Test\Behaviour;
if(!class_exists('SilverStripe\BehatExtension\Context\SilverStripeContext')) return; if (!class_exists('SilverStripe\BehatExtension\Context\SilverStripeContext')) {
return;
}
use SilverStripe\BehatExtension\Context\SilverStripeContext, use SilverStripe\BehatExtension\Context\BasicContext;
SilverStripe\BehatExtension\Context\BasicContext, use SilverStripe\BehatExtension\Context\FixtureContext;
SilverStripe\BehatExtension\Context\LoginContext, use SilverStripe\BehatExtension\Context\LoginContext;
SilverStripe\BehatExtension\Context\FixtureContext, use SilverStripe\BehatExtension\Context\SilverStripeContext;
SilverStripe\Framework\Test\Behaviour\CmsFormsContext,
SilverStripe\Framework\Test\Behaviour\CmsUiContext,
SilverStripe\Cms\Test\Behaviour;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Core\ClassInfo; use SilverStripe\Core\ClassInfo;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Framework\Test\Behaviour\CmsFormsContext;
use SilverStripe\Framework\Test\Behaviour\CmsUiContext;
// PHPUnit // PHPUnit
@ -25,7 +26,8 @@ require_once 'PHPUnit/Framework/Assert/Functions.php';
* Context automatically loaded by Behat. * Context automatically loaded by Behat.
* Uses subcontexts to extend functionality. * Uses subcontexts to extend functionality.
*/ */
class FeatureContext extends SilverStripeContext { class FeatureContext extends SilverStripeContext
{
/** /**
* @var FixtureFactory * @var FixtureFactory
@ -38,7 +40,8 @@ class FeatureContext extends SilverStripeContext {
* *
* @param array $parameters context parameters (set them up through behat.yml) * @param array $parameters context parameters (set them up through behat.yml)
*/ */
public function __construct(array $parameters) { public function __construct(array $parameters)
{
parent::__construct($parameters); parent::__construct($parameters);
$this->useContext('BasicContext', new BasicContext($parameters)); $this->useContext('BasicContext', new BasicContext($parameters));
@ -53,25 +56,28 @@ class FeatureContext extends SilverStripeContext {
// Use blueprints to set user name from identifier // Use blueprints to set user name from identifier
$factory = $fixtureContext->getFixtureFactory(); $factory = $fixtureContext->getFixtureFactory();
$blueprint = Injector::inst()->create('SilverStripe\\Dev\\FixtureBlueprint', 'SilverStripe\\Security\\Member'); $blueprint = Injector::inst()->create('SilverStripe\\Dev\\FixtureBlueprint', 'SilverStripe\\Security\\Member');
$blueprint->addCallback('beforeCreate', function($identifier, &$data, &$fixtures) { $blueprint->addCallback('beforeCreate', function ($identifier, &$data, &$fixtures) {
if(!isset($data['FirstName'])) $data['FirstName'] = $identifier; if (!isset($data['FirstName'])) {
$data['FirstName'] = $identifier;
}
}); });
$factory->define('SilverStripe\\Security\\Member', $blueprint); $factory->define('SilverStripe\\Security\\Member', $blueprint);
// Auto-publish pages // Auto-publish pages
foreach(ClassInfo::subclassesFor('SilverStripe\\CMS\\Model\\SiteTree') as $id => $class) { foreach (ClassInfo::subclassesFor('SilverStripe\\CMS\\Model\\SiteTree') as $id => $class) {
$blueprint = Injector::inst()->create('SilverStripe\\Dev\\FixtureBlueprint', $class); $blueprint = Injector::inst()->create('SilverStripe\\Dev\\FixtureBlueprint', $class);
$blueprint->addCallback('afterCreate', function($obj, $identifier, &$data, &$fixtures) { $blueprint->addCallback('afterCreate', function ($obj, $identifier, &$data, &$fixtures) {
$obj->publish('Stage', 'Live'); $obj->publish('Stage', 'Live');
}); });
$factory->define($class, $blueprint); $factory->define($class, $blueprint);
} }
} }
public function setMinkParameters(array $parameters) { public function setMinkParameters(array $parameters)
{
parent::setMinkParameters($parameters); parent::setMinkParameters($parameters);
if(isset($parameters['files_path'])) { if (isset($parameters['files_path'])) {
$this->getSubcontext('FixtureContext')->setFilesPath($parameters['files_path']); $this->getSubcontext('FixtureContext')->setFilesPath($parameters['files_path']);
} }
} }
@ -79,15 +85,17 @@ class FeatureContext extends SilverStripeContext {
/** /**
* @return FixtureFactory * @return FixtureFactory
*/ */
public function getFixtureFactory() { public function getFixtureFactory()
if(!$this->fixtureFactory) { {
if (!$this->fixtureFactory) {
$this->fixtureFactory = Injector::inst()->create('SilverStripe\\Dev\\BehatFixtureFactory'); $this->fixtureFactory = Injector::inst()->create('SilverStripe\\Dev\\BehatFixtureFactory');
} }
return $this->fixtureFactory; return $this->fixtureFactory;
} }
public function setFixtureFactory(FixtureFactory $factory) { public function setFixtureFactory(FixtureFactory $factory)
{
$this->fixtureFactory = $factory; $this->fixtureFactory = $factory;
} }

View File

@ -1,26 +1,26 @@
Feature: Preview navigation Feature: Preview navigation
As a CMS user As a CMS user
I can navigate a subsite in the preview pane I can navigate a subsite in the preview pane
In order to preview my content In order to preview my content
Background: Background:
Given a "subsite" "My subsite" Given a "subsite" "My subsite"
And a "page" "My page" with "URLSegment"="my-page", "Content"="My page content <a name='aname'>aname</a><a href='other-page'>ahref</a>" and "Subsite"="=>Subsite.My subsite" And a "page" "My page" with "URLSegment"="my-page", "Content"="My page content <a name='aname'>aname</a><a href='other-page'>ahref</a>" and "Subsite"="=>Subsite.My subsite"
And a "page" "Other page" with "URLSegment"="other-page", "Content"="Other page content <form action='my-page'><input type='submit' value='Submit my form'></form>" and "Subsite"="=>Subsite.My subsite" And a "page" "Other page" with "URLSegment"="other-page", "Content"="Other page content <form action='my-page'><input type='submit' value='Submit my form'></form>" and "Subsite"="=>Subsite.My subsite"
Given a "member" "Joe" belonging to "Admin Group" with "Email"="joe@test.com" and "Password"="Password1" Given a "member" "Joe" belonging to "Admin Group" with "Email"="joe@test.com" and "Password"="Password1"
And the "group" "Admin Group" has permissions "Full administrative rights" And the "group" "Admin Group" has permissions "Full administrative rights"
And I log in with "joe@test.com" and "Password1" And I log in with "joe@test.com" and "Password1"
Scenario: I can navigate the subsite preview Scenario: I can navigate the subsite preview
When I go to "admin" When I go to "admin"
And I select "My subsite" from "SubsitesSelect" And I select "My subsite" from "SubsitesSelect"
And I go to "admin/pages" And I go to "admin/pages"
And I click on "My page" in the tree And I click on "My page" in the tree
And I wait for 3 seconds And I wait for 3 seconds
And I set the CMS mode to "Preview mode" And I set the CMS mode to "Preview mode"
And I follow "ahref" in preview And I follow "ahref" in preview
Then the preview contains "Other page content" Then the preview contains "Other page content"
# We are already on the second page, submit the form to return to first one. # We are already on the second page, submit the form to return to first one.
When I wait for 3 seconds When I wait for 3 seconds
And I press "Submit my form" in preview And I press "Submit my form" in preview
Then the preview contains "My page content" Then the preview contains "My page content"