Merge pull request #289 from wernerkrauss/fix-ss-4-master

SilverStripe 4 Compatibility
This commit is contained in:
Damian Mooyman 2017-08-29 16:47:29 +12:00 committed by GitHub
commit 60725e7b5c
55 changed files with 2028 additions and 1237 deletions

View File

@ -1,6 +1,6 @@
# For more information about the properties used in this file, # For more information about the properties used in
# please see the EditorConfig documentation: # this file, please see the EditorConfig documentation:
# http://editorconfig.org # http://editorconfig.org/
[*] [*]
charset = utf-8 charset = utf-8
@ -10,8 +10,14 @@ indent_style = space
insert_final_newline = true insert_final_newline = true
trim_trailing_whitespace = true trim_trailing_whitespace = true
[{*.yml,package.json}] [*.md]
trim_trailing_whitespace = false
[*.yml]
indent_size = 2 indent_size = 2
# The indent size used in the package.json file cannot be changed: [{.travis.yml,package.json,composer.json}]
# The indent size used in the `package.json` file cannot be changed
# https://github.com/npm/npm/pull/3180#issuecomment-16336516 # https://github.com/npm/npm/pull/3180#issuecomment-16336516
indent_size = 2
indent_style = space

View File

@ -4,6 +4,8 @@ language: php
sudo: false sudo: false
sudo: false
php: php:
- 5.5 - 5.5

20
.upgrade.yml Normal file
View File

@ -0,0 +1,20 @@
mappings:
SubsiteAdmin: SilverStripe\Subsites\Admin\SubsiteAdmin
SubsiteXHRController: SilverStripe\Subsites\Controller\SubsiteXHRController
CMSPageAddControllerExtension: SilverStripe\Subsites\Extensions\CMSPageAddControllerExtension
ControllerSubsites: SilverStripe\Subsites\Extensions\ControllerSubsites
ErrorPageSubsite: SilverStripe\Subsites\Extensions\ErrorPageSubsite
FileSubsites: SilverStripe\Subsites\Extensions\FileSubsites
GroupSubsites: SilverStripe\Subsites\Extensions\GroupSubsites
LeftAndMainSubsites: SilverStripe\Subsites\Extensions\LeftAndMainSubsites
SiteConfigSubsites: SilverStripe\Subsites\Extensions\SiteConfigSubsites
SiteTreeSubsites: SilverStripe\Subsites\Extensions\SiteTreeSubsites
SubsiteMenuExtension: SilverStripe\Subsites\Extensions\SubsiteMenuExtension
GridFieldSubsiteDetailForm: SilverStripe\Subsites\Forms\GridFieldSubsiteDetailForm
GridFieldSubsiteDetailForm_ItemRequest: SilverStripe\Subsites\Forms\GridFieldSubsiteDetailForm_ItemRequest
SubsitesTreeDropdownField: SilverStripe\Subsites\Forms\SubsitesTreeDropdownField
Subsite: SilverStripe\Subsites\Model\Subsite
SubsiteDomain: SilverStripe\Subsites\Model\SubsiteDomain
SubsitesVirtualPage: SilverStripe\Subsites\Pages\SubsitesVirtualPage
SubsiteReportWrapper: SilverStripe\Subsites\Reports\SubsiteReportWrapper
SubsiteCopyPagesTask: SilverStripe\Subsites\Tasks\SubsiteCopyPagesTask

View File

@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/). This project adheres to [Semantic Versioning](http://semver.org/).
## [2.0.0 (unreleased)]
* Updating to be compatible with SilverStripe 4
* Subsite specific theme is now added to default theme, as themes are now cascadable
## [1.2.3] ## [1.2.3]
* BUG Fix issue with urlsegment being renamed in subsites * BUG Fix issue with urlsegment being renamed in subsites

View File

@ -1,27 +1 @@
<?php <?php
/**
* The subsites module modifies the behaviour of the CMS - in the SiteTree and Group databases - to store information
* about a number of sub-sites, rather than a single site.
*/
SiteTree::add_extension('SiteTreeSubsites');
ContentController::add_extension('ControllerSubsites');
CMSPageAddController::add_extension('CMSPageAddControllerExtension');
LeftAndMain::add_extension('LeftAndMainSubsites');
LeftAndMain::add_extension('ControllerSubsites');
Group::add_extension('GroupSubsites');
File::add_extension('FileSubsites');
ErrorPage::add_extension('ErrorPageSubsite');
SiteConfig::add_extension('SiteConfigSubsites');
SS_Report::add_excluded_reports('SubsiteReportWrapper');
//Display in cms menu
AssetAdmin::add_extension('SubsiteMenuExtension');
SecurityAdmin::add_extension('SubsiteMenuExtension');
CMSMain::add_extension('SubsiteMenuExtension');
CMSPagesController::add_extension('SubsiteMenuExtension');
SubsiteAdmin::add_extension('SubsiteMenuExtension');
CMSSettingsController::add_extension('SubsiteMenuExtension');

View File

@ -1,12 +1,15 @@
--- ---
Name: subsiteconfig Name: subsiteconfig
After: After:
- 'framework/*' - 'framework/*'
- 'cms/*'
--- ---
AssetAdmin: SilverStripe\AssetAdmin\Controller\AssetAdmin:
treats_subsite_0_as_global: true treats_subsite_0_as_global: true
Director: Director:
rules: rules:
'SubsiteXHRController': 'SubsiteXHRController' SubsiteXHRController: SilverStripe\Subsites\Controller\SubsiteXHRController
SilverStripe\Reports\Report:
excluded_reports:
- SilverStripe\Subsites\Reports\SubsiteReportWrapper

61
_config/extensions.yml Normal file
View File

@ -0,0 +1,61 @@
---
Name: subsiteextensions
After:
- 'framework/*'
---
SilverStripe\CMS\Model\SiteTree:
extensions:
- SilverStripe\Subsites\Extensions\SiteTreeSubsites
SilverStripe\CMS\Controllers\ContentController:
extensions:
- SilverStripe\Subsites\Extensions\ControllerSubsites
SilverStripe\CMS\Controllers\CMSPageAddController:
extensions:
- SilverStripe\Subsites\Extensions\CMSPageAddControllerExtension
SilverStripe\Admin\LeftAndMain:
extensions:
- SilverStripe\Subsites\Extensions\LeftAndMainSubsites
- SilverStripe\Subsites\Extensions\ControllerSubsites
SilverStripe\Security\Group:
extensions:
- SilverStripe\Subsites\Extensions\GroupSubsites
SilverStripe\Assets\File:
extensions:
- SilverStripe\Subsites\Extensions\FileSubsites
SilverStripe\CMS\Model\ErrorPage:
extensions:
- SilverStripe\Subsites\Extensions\ErrorPageSubsite
SilverStripe\SiteConfig\SiteConfig:
extensions:
- SilverStripe\Subsites\Extensions\SiteConfigSubsites
SilverStripe\AssetAdmin\Controller\AssetAdmin:
extensions:
- SilverStripe\Subsites\Extensions\SubsiteMenuExtension
SilverStripe\Admin\SecurityAdmin:
extensions:
- SilverStripe\Subsites\Extensions\SubsiteMenuExtension
SilverStripe\CMS\Controllers\CMSMain:
extensions:
- SilverStripe\Subsites\Extensions\SubsiteMenuExtension
SilverStripe\CMS\Controllers\CMSPagesController:
extensions:
- SilverStripe\Subsites\Extensions\SubsiteMenuExtension
SilverStripe\Subsites\SubsiteAdmin:
extensions:
- SilverStripe\Subsites\Extensions\SubsiteMenuExtension
SilverStripe\CMS\Controllers\CMSPageSettingsController:
extensions:
- SilverStripe\Subsites\Extensions\SubsiteMenuExtension

9
_config/legacy.yml Normal file
View File

@ -0,0 +1,9 @@
---
Name: subsites-legacy
---
SilverStripe\ORM\DatabaseAdmin:
classname_value_remapping:
Subsite: SilverStripe\Subsites\Model\Subsite
SubsiteDomain: SilverStripe\Subsites\Model\SubsiteDomain
SubsitesVirtualPage: SilverStripe\Subsites\Pages\SubsitesVirtualPage

View File

@ -1,33 +0,0 @@
<?php
/**
* Admin interface to manage and create {@link Subsite} instances.
*
* @package subsites
*/
class SubsiteAdmin extends ModelAdmin
{
private static $managed_models = array('Subsite');
private static $url_segment = 'subsites';
private static $menu_title = "Subsites";
private static $menu_icon = "subsites/images/subsites.png";
public $showImportForm=false;
private static $tree_class = 'Subsite';
public function getEditForm($id = null, $fields = null)
{
$form = parent::getEditForm($id, $fields);
$grid=$form->Fields()->dataFieldByName('Subsite');
if ($grid) {
$grid->getConfig()->removeComponentsByType('GridFieldDetailForm');
$grid->getConfig()->addComponent(new GridFieldSubsiteDetailForm());
}
return $form;
}
}

View File

@ -0,0 +1,42 @@
<?php
namespace SilverStripe\Subsites\Admin;
use SilverStripe\Admin\ModelAdmin;
use SilverStripe\Subsites\Forms\GridFieldSubsiteDetailForm;
use SilverStripe\Subsites\Model\Subsite;
/**
* Admin interface to manage and create {@link Subsite} instances.
*
* @package subsites
*/
class SubsiteAdmin extends ModelAdmin
{
private static $managed_models = [Subsite::class];
private static $url_segment = 'subsites';
private static $menu_title = 'Subsites';
private static $menu_icon_class = 'font-icon-tree';
public $showImportForm = false;
private static $tree_class = Subsite::class;
public function getEditForm($id = null, $fields = null)
{
$form = parent::getEditForm($id, $fields);
$grid = $form->Fields()->dataFieldByName(Subsite::class);
if ($grid) {
$grid->getConfig()->removeComponentsByType(GridFieldDetailForm::class);
$grid->getConfig()->addComponent(new GridFieldSubsiteDetailForm());
}
return $form;
}
}

View File

@ -1,12 +1,28 @@
<?php <?php
namespace SilverStripe\Subsites\Controller;
use SilverStripe\Admin\LeftAndMain;
use SilverStripe\Security\Permission;
use SilverStripe\Subsites\Model\Subsite;
/** /**
* Section-agnostic PJAX controller. * Section-agnostic PJAX controller.
*/ */
class SubsiteXHRController extends LeftAndMain class SubsiteXHRController extends LeftAndMain
{ {
/**
* @todo Temporary addition due to new requirements for LeftAndMain
* descendants in SS4. Consider alternate implementation.
*/
private static $url_segment = 'subsite_xhr';
/** /**
* 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.
* @param null $member
* @return bool
*/ */
public function canView($member = null) public function canView($member = null)
{ {
@ -14,7 +30,7 @@ class SubsiteXHRController extends LeftAndMain
return true; return true;
} }
if (Subsite::all_accessible_sites()->count()>0) { if (Subsite::all_accessible_sites()->count() > 0) {
return true; return true;
} }
@ -26,11 +42,11 @@ class SubsiteXHRController extends LeftAndMain
*/ */
public function canAccess() public function canAccess()
{ {
// Allow if any cms access is available // Allow if any cms access is available
return Permission::check(array( return Permission::check([
'CMS_ACCESS', // Supported by 3.1.14 and up 'CMS_ACCESS', // Supported by 3.1.14 and up
'CMS_ACCESS_LeftAndMain' 'CMS_ACCESS_LeftAndMain'
)); ]);
} }
public function getResponseNegotiator() public function getResponseNegotiator()
@ -51,6 +67,7 @@ class SubsiteXHRController extends LeftAndMain
*/ */
public function SubsiteList() public function SubsiteList()
{ {
return $this->renderWith('SubsiteList'); return $this->renderWith('Includes/SubsiteList');
} }
} }

View File

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

View File

@ -1,4 +1,10 @@
<?php <?php
namespace SilverStripe\Subsites\Extensions;
use SilverStripe\Core\Extension;
use SilverStripe\Subsites\Model\Subsite;
use SilverStripe\View\SSViewer;
/** /**
* @package subsites * @package subsites
*/ */
@ -8,11 +14,11 @@ class ControllerSubsites extends Extension
{ {
if ($subsite = Subsite::currentSubsite()) { if ($subsite = Subsite::currentSubsite()) {
if ($theme = $subsite->Theme) { if ($theme = $subsite->Theme) {
SSViewer::set_theme($theme); SSViewer::set_themes([$theme, SSViewer::DEFAULT_THEME]);
} }
} }
} }
public function CurrentSubsite() public function CurrentSubsite()
{ {
if ($subsite = Subsite::currentSubsite()) { if ($subsite = Subsite::currentSubsite()) {

View File

@ -1,37 +1,55 @@
<?php <?php
namespace SilverStripe\Subsites\Extensions;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Core\Config\Config;
use SilverStripe\ORM\DataExtension;
use SilverStripe\ORM\DataObject;
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_filepath_for_errorcode()
* *
* 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 $statusCode
* @param string $name Filename to write to * @param null $locale
* @param int $statusCode Integer error code * @return string
*/ */
public function updateErrorFilename(&$name, $statusCode) public function alternateFilepathForErrorcode($statusCode, $locale = null)
{ {
// Try to get current subsite from session $static_filepath = Config::inst()->get($this->owner->ClassName, 'static_filepath');
$subsite = Subsite::currentSubsite(false); $subdomainPart = '';
// since this function is called from Page class before the controller is created, we have to get subsite from domain instead // Try to get current subsite from session
if (!$subsite) { $subsite = Subsite::currentSubsite();
$subsiteID = Subsite::getSubsiteIDForDomain();
if ($subsiteID != 0) {
$subsite = DataObject::get_by_id("Subsite", $subsiteID);
}
}
// Without subsite, don't rewrite // 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) {
// Add subdomain to end of filename, just before .html $subsiteID = Subsite::getSubsiteIDForDomain();
// This should preserve translatable locale in the filename as well if ($subsiteID != 0) {
$subdomain = $subsite->domain(); $subsite = DataObject::get_by_id(Subsite::class, $subsiteID);
$name = substr($name, 0, -5) . "-{$subdomain}.html"; } else {
} $subsite = null;
} }
}
if ($subsite) {
$subdomain = $subsite->domain();
$subdomainPart = "-{$subdomain}";
}
if (singleton(SiteTree::class)->hasExtension('Translatable') && $locale && $locale != Translatable::default_locale()) {
$filepath = $static_filepath . "/error-{$statusCode}-{$locale}{$subdomainPart}.html";
} else {
$filepath = $static_filepath . "/error-{$statusCode}{$subdomainPart}.html";
}
return $filepath;
}
} }

View File

@ -1,4 +1,18 @@
<?php <?php
namespace SilverStripe\Subsites\Extensions;
use SilverStripe\Assets\Folder;
use SilverStripe\Control\Session;
use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\LiteralField;
use SilverStripe\ORM\DataExtension;
use SilverStripe\ORM\DataQuery;
use SilverStripe\ORM\Queries\SQLSelect;
use SilverStripe\Security\Permission;
use SilverStripe\Subsites\Model\Subsite;
/** /**
* Extension for the File object to add subsites support * Extension for the File object to add subsites support
* *
@ -10,9 +24,9 @@ class FileSubsites extends DataExtension
// considered 'global', unless set otherwise // considered 'global', unless set otherwise
public static $default_root_folders_global = false; public static $default_root_folders_global = false;
private static $has_one=array( private static $has_one = [
'Subsite' => 'Subsite', '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.
@ -21,20 +35,21 @@ class FileSubsites extends DataExtension
public function alternateTreeTitle() public function alternateTreeTitle()
{ {
if ($this->owner->SubsiteID == 0) { if ($this->owner->SubsiteID == 0) {
return " * " . $this->owner->Title; return ' * ' . $this->owner->Title;
} else {
return $this->owner->Title;
} }
return $this->owner->Title;
} }
/** /**
* Add subsites-specific fields to the folder editor. * Add subsites-specific fields to the folder editor.
* @param FieldList $fields
*/ */
public function updateCMSFields(FieldList $fields) public function updateCMSFields(FieldList $fields)
{ {
if ($this->owner instanceof Folder) { if ($this->owner instanceof Folder) {
$sites = Subsite::accessible_sites('CMS_ACCESS_AssetAdmin'); $sites = Subsite::accessible_sites('CMS_ACCESS_AssetAdmin');
$values = array(); $values = [];
$values[0] = _t('FileSubsites.AllSitesDropdownOpt', 'All sites'); $values[0] = _t('FileSubsites.AllSitesDropdownOpt', 'All sites');
foreach ($sites as $site) { foreach ($sites as $site) {
$values[$site->ID] = $site->Title; $values[$site->ID] = $site->Title;
@ -44,16 +59,17 @@ class FileSubsites extends DataExtension
//Dropdown needed to move folders between subsites //Dropdown needed to move folders between subsites
$dropdown = new DropdownField( $dropdown = new DropdownField(
'SubsiteID', 'SubsiteID',
_t('FileSubsites.SubsiteFieldLabel', 'Subsite'), _t('FileSubsites.SubsiteFieldLabel', Subsite::class),
$values $values
); );
$dropdown->addExtraClass('subsites-move-dropdown'); $dropdown->addExtraClass('subsites-move-dropdown');
$fields->push($dropdown); $fields->push($dropdown);
$fields->push(new LiteralField( $fields->push(new LiteralField(
'Message', 'Message',
'<p class="message notice">'. '<p class="message notice">' .
_t('ASSETADMIN.SUBSITENOTICE', 'Folders and files created in the main site are accessible by all subsites.') _t('ASSETADMIN.SUBSITENOTICE',
.'</p>' 'Folders and files created in the main site are accessible by all subsites.')
. '</p>'
)); ));
} }
} }
@ -61,6 +77,8 @@ class FileSubsites extends DataExtension
/** /**
* Update any requests to limit the results to the current site * Update any requests to limit the results to the current site
* @param SQLSelect $query
* @param DataQuery|null $dataQuery
*/ */
public function augmentSQL(SQLSelect $query, DataQuery $dataQuery = null) public function augmentSQL(SQLSelect $query, DataQuery $dataQuery = null)
{ {
@ -76,7 +94,7 @@ class FileSubsites extends DataExtension
return; 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) {
@ -85,12 +103,12 @@ class FileSubsites extends DataExtension
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"');
} }
} }
@ -120,22 +138,24 @@ class FileSubsites extends DataExtension
{ {
// Check the CMS_ACCESS_SecurityAdmin privileges on the subsite that owns this group // Check the CMS_ACCESS_SecurityAdmin privileges on the subsite that owns this group
$subsiteID = Session::get('SubsiteID'); $subsiteID = Session::get('SubsiteID');
if ($subsiteID&&$subsiteID == $this->owner->SubsiteID) { if ($subsiteID && $subsiteID == $this->owner->SubsiteID) {
return true; return true;
} else {
Session::set('SubsiteID', $this->owner->SubsiteID);
$access = Permission::check(array('CMS_ACCESS_AssetAdmin', 'CMS_ACCESS_LeftAndMain'));
Session::set('SubsiteID', $subsiteID);
return $access;
} }
Session::set('SubsiteID', $this->owner->SubsiteID);
$access = Permission::check(['CMS_ACCESS_AssetAdmin', 'CMS_ACCESS_LeftAndMain']);
Session::set('SubsiteID', $subsiteID);
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
*
* @return string
*/ */
public function cacheKeyComponent() public function cacheKeyComponent()
{ {
return 'subsite-'.Subsite::currentSubsiteID(); return 'subsite-' . Subsite::currentSubsiteID();
} }
} }

View File

@ -1,4 +1,21 @@
<?php <?php
namespace SilverStripe\Subsites\Extensions;
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\DataQuery;
use SilverStripe\ORM\DB;
use SilverStripe\ORM\Queries\SQLSelect;
use SilverStripe\Security\Group;
use SilverStripe\Security\PermissionProvider;
use SilverStripe\Subsites\Model\Subsite;
/** /**
* Extension for the Group object to add subsites support * Extension for the Group object to add subsites support
* *
@ -6,25 +23,30 @@
*/ */
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' 'Subsites' => Subsite::class
); ];
private static $defaults = array( private static $defaults = [
'AccessAllSubsites' => true 'AccessAllSubsites' => true
); ];
/** /**
* Migrations for GroupSubsites data. * Migrations for GroupSubsites data.
*/ */
public function requireDefaultRecords() public function requireDefaultRecords()
{ {
if (!$this->owner) {
return;
}
// 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
$groupFields = DB::field_list('Group'); $ownerClass = get_class($this->owner);
$schema = $ownerClass::getSchema();
$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'])) {
@ -36,51 +58,55 @@ class GroupSubsites extends DataExtension implements PermissionProvider
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', '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
} elseif (!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');
} }
}
}
public function updateCMSFields(FieldList $fields) public function updateCMSFields(FieldList $fields)
{ {
if ($this->owner->canEdit()) { if ($this->owner->canEdit()) {
// i18n tab // i18n tab
$fields->findOrMakeTab('Root.Subsites', _t('GroupSubsites.SECURITYTABTITLE', 'Subsites')); $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->toArray());
// 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));
} }
@ -88,7 +114,7 @@ class GroupSubsites extends DataExtension implements PermissionProvider
} }
} }
/** /**
* 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
@ -99,16 +125,18 @@ class GroupSubsites extends DataExtension implements PermissionProvider
if ($this->owner->AccessAllSubsites) { if ($this->owner->AccessAllSubsites) {
$title = _t('GroupSubsites.GlobalGroup', 'global group'); $title = _t('GroupSubsites.GlobalGroup', 'global group');
return htmlspecialchars($this->owner->Title, ENT_QUOTES) . ' <i>(' . $title . ')</i>'; return htmlspecialchars($this->owner->Title, ENT_QUOTES) . ' <i>(' . $title . ')</i>';
} else {
$subsites = Convert::raw2xml(implode(", ", $this->owner->Subsites()->column('Title')));
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
*/ * @param SQLSelect $query
public function augmentSQL(SQLSelect $query, DataQuery $dataQuery = null) * @param DataQuery|null $dataQuery
*/
public function augmentSQL(SQLSelect $query, DataQuery $dataQuery = null)
{ {
if (Subsite::$disable_subsite_filter) { if (Subsite::$disable_subsite_filter) {
return; return;
@ -121,12 +149,17 @@ class GroupSubsites extends DataExtension implements PermissionProvider
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'],
'Group_Subsites') !== false) || (!is_array($item) && strpos($item,
'Group_Subsites') !== false)
) {
$hasGroupSubsites = true; $hasGroupSubsites = true;
break; break;
} }
@ -134,19 +167,19 @@ class GroupSubsites extends DataExtension implements PermissionProvider
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->addOrderBy('AccessAllSubsites', 'DESC');
} }
} }
} }
@ -183,13 +216,14 @@ class GroupSubsites extends DataExtension implements PermissionProvider
public function providePermissions() public function providePermissions()
{ {
return array( return [
'SECURITY_SUBSITE_GROUP' => array( 'SECURITY_SUBSITE_GROUP' => [
'name' => _t('GroupSubsites.MANAGE_SUBSITES', 'Manage subsites for groups'), 'name' => _t('GroupSubsites.MANAGE_SUBSITES', 'Manage subsites for groups'),
'category' => _t('Permissions.PERMISSIONS_CATEGORY', 'Roles and access permissions'), 'category' => _t('Permissions.PERMISSIONS_CATEGORY', 'Roles and access permissions'),
'help' => _t('GroupSubsites.MANAGE_SUBSITES_HELP', 'Ability to limit the permissions for a group to one or more subsites.'), 'help' => _t('GroupSubsites.MANAGE_SUBSITES_HELP',
'Ability to limit the permissions for a group to one or more subsites.'),
'sort' => 200 'sort' => 200
) ]
); ];
} }
} }

View File

@ -1,4 +1,25 @@
<?php <?php
namespace SilverStripe\Subsites\Extensions;
use SilverStripe\Admin\CMSMenu;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Control\Controller;
use SilverStripe\Control\Session;
use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Convert;
use SilverStripe\Core\Extension;
use SilverStripe\Forms\HiddenField;
use SilverStripe\ORM\ArrayList;
use SilverStripe\ORM\DataObject;
use SilverStripe\Security\Member;
use SilverStripe\Security\Permission;
use SilverStripe\Security\Security;
use SilverStripe\Subsites\Controller\SubsiteXHRController;
use SilverStripe\Subsites\Model\Subsite;
use SilverStripe\View\ArrayData;
use SilverStripe\View\Requirements;
/** /**
* Decorator designed to add subsites support to LeftAndMain * Decorator designed to add subsites support to LeftAndMain
* *
@ -6,7 +27,7 @@
*/ */
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".
@ -27,7 +48,7 @@ class LeftAndMainSubsites extends Extension
*/ */
public function getCMSTreeTitle() public function getCMSTreeTitle()
{ {
$subsite = Subsite::currentSubSite(); $subsite = Subsite::currentSubsite();
return $subsite ? Convert::raw2xml($subsite->Title) : _t('LeftAndMain.SITECONTENTLEFT'); return $subsite ? Convert::raw2xml($subsite->Title) : _t('LeftAndMain.SITECONTENTLEFT');
} }
@ -39,9 +60,13 @@ class LeftAndMainSubsites extends Extension
/** /**
* 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. * @param bool $includeMainSite
* @param string $mainSiteTitle
* @param null $member
* @return ArrayList of <a href='psi_element://Subsite'>Subsite</a> instances.
* instances.
*/ */
public function sectionSites($includeMainSite = true, $mainSiteTitle = "Main site", $member = null) public function sectionSites($includeMainSite = true, $mainSiteTitle = 'Main site', $member = null)
{ {
if ($mainSiteTitle == 'Main site') { if ($mainSiteTitle == 'Main site') {
$mainSiteTitle = _t('Subsites.MainSiteTitle', 'Main site'); $mainSiteTitle = _t('Subsites.MainSiteTitle', 'Main site');
@ -55,12 +80,12 @@ class LeftAndMainSubsites extends Extension
return new ArrayList(); return new ArrayList();
} }
if (!is_object($member)) { if (!is_object($member)) {
$member = DataObject::get_by_id('Member', $member); $member = DataObject::get_by_id(Member::class, $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) { if ($extraCodes) {
@ -74,8 +99,8 @@ class LeftAndMainSubsites extends Extension
} }
// 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) {
@ -90,7 +115,7 @@ class LeftAndMainSubsites extends Extension
// 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]);
} }
} }
@ -118,7 +143,7 @@ class LeftAndMainSubsites extends Extension
$list = $this->Subsites(); $list = $this->Subsites();
$currentSubsiteID = Subsite::currentSubsiteID(); $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;
} }
@ -129,11 +154,11 @@ class LeftAndMainSubsites extends Extension
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;
@ -145,37 +170,41 @@ class LeftAndMainSubsites extends Extension
return false; return false;
} }
// Don't display SubsiteXHRController
if ($controllerName == SubsiteXHRController::class) {
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 {
$controller = singleton($controllerName);
if ($controller->hasMethod('subsiteCMSShowInMenu') && $controller->subsiteCMSShowInMenu()) {
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.
$controller = singleton($controllerName);
return false; return $controller->hasMethod('subsiteCMSShowInMenu') && $controller->subsiteCMSShowInMenu();
} }
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.
* @param $adminClass
* @param $recordSubsiteID
* @param $currentSubsiteID
* @return bool
*/ */
public function shouldChangeSubsite($adminClass, $recordSubsiteID, $currentSubsiteID) public function shouldChangeSubsite($adminClass, $recordSubsiteID, $currentSubsiteID)
{ {
if (Config::inst()->get($adminClass, 'treats_subsite_0_as_global') && $recordSubsiteID==0) { if (Config::inst()->get($adminClass, 'treats_subsite_0_as_global') && $recordSubsiteID == 0) {
return false; return false;
} }
if ($recordSubsiteID!=$currentSubsiteID) { if ($recordSubsiteID != $currentSubsiteID) {
return true; return true;
} }
return false; return false;
@ -197,13 +226,8 @@ class LeftAndMainSubsites extends Extension
} }
// 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())) { return $accessibleSites->count() && $accessibleSites->find('ID', Subsite::currentSubsiteID());
// Current section can be accessed on the current site, all good.
return true;
}
return false;
} }
/** /**
@ -230,13 +254,15 @@ class LeftAndMainSubsites extends Extension
// We are accessing the CMS, so we need to let Subsites know we will be using the session. // We are accessing the CMS, so we need to let Subsites know we will be using the session.
Subsite::$use_session_subsiteid = true; Subsite::$use_session_subsiteid = true;
$session = Controller::curr()->getRequest()->getSession();
// 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
@ -254,18 +280,20 @@ class LeftAndMainSubsites extends Extension
// 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
if ($this->shouldChangeSubsite($this->owner->class, $record->SubsiteID, Subsite::currentSubsiteID())) { && isset($record->SubsiteID, $this->owner->urlParams['ID'])
// Update current subsite in session && is_numeric($record->SubsiteID)
Subsite::changeSubsite($record->SubsiteID); && $this->shouldChangeSubsite($this->owner->class, $record->SubsiteID, Subsite::currentSubsiteID())
) {
// Update current subsite in session
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
return $this->owner->redirect('admin/');
} }
//Redirect to the default CMS section
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.
@ -276,7 +304,7 @@ class LeftAndMainSubsites extends Extension
// 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.
@ -312,7 +340,8 @@ class LeftAndMainSubsites extends Extension
public function onAfterSave($record) public function onAfterSave($record)
{ {
if ($record->hasMethod('NormalRelated') && ($record->NormalRelated() || $record->ReverseRelated())) { if ($record->hasMethod('NormalRelated') && ($record->NormalRelated() || $record->ReverseRelated())) {
$this->owner->response->addHeader('X-Status', rawurlencode(_t('LeftAndMainSubsites.Saved', 'Saved, please update related pages.'))); $this->owner->response->addHeader('X-Status',
rawurlencode(_t('LeftAndMainSubsites.Saved', 'Saved, please update related pages.')));
} }
} }

View File

@ -1,16 +1,28 @@
<?php <?php
namespace SilverStripe\Subsites\Extensions;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\HiddenField;
use SilverStripe\ORM\DataExtension;
use SilverStripe\ORM\DataQuery;
use SilverStripe\ORM\Queries\SQLSelect;
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', // 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
* @param SQLSelect $query
* @param DataQuery|null $dataQuery
*/ */
public function augmentSQL(SQLSelect $query, DataQuery $dataQuery = null) public function augmentSQL(SQLSelect $query, DataQuery $dataQuery = null)
{ {
@ -29,13 +41,12 @@ class SiteConfigSubsites extends DataExtension
} }
} }
/*if($context = DataObject::context_obj()) $subsiteID = (int)$context->SubsiteID; $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') { if ($tableName !== SiteConfig::getSchema()->tableName(SiteConfig::class)) {
return; return;
} }
$query->addWhere("\"$tableName\".\"SubsiteID\" IN ($subsiteID)"); $query->addWhere("\"$tableName\".\"SubsiteID\" IN ($subsiteID)");
@ -53,7 +64,7 @@ class SiteConfigSubsites extends DataExtension
*/ */
public function cacheKeyComponent() public function cacheKeyComponent()
{ {
return 'subsite-'.Subsite::currentSubsiteID(); return 'subsite-' . Subsite::currentSubsiteID();
} }
public function updateCMSFields(FieldList $fields) public function updateCMSFields(FieldList $fields)

View File

@ -1,34 +1,55 @@
<?php <?php
namespace SilverStripe\Subsites\Extensions;
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\CheckboxField;
use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\FormAction;
use SilverStripe\Forms\ToggleCompositeField;
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;
use SilverStripe\View\SSViewer;
/** /**
* 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', // 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' => 'SiteTree' // Stored separately, as the logic for URL rewriting is different 'CrossSubsiteLinkTracking' => SiteTree::class // 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']
); ];
public function isMainSite() public function isMainSite()
{ {
if ($this->owner->SubsiteID == 0) { return $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
* @param SQLSelect $query
* @param DataQuery $dataQuery
*/ */
public function augmentSQL(SQLQuery &$query, DataQuery &$dataQuery = null) public function augmentSQL(SQLSelect $query, DataQuery $dataQuery = null)
{ {
if (Subsite::$disable_subsite_filter) { if (Subsite::$disable_subsite_filter) {
return; return;
@ -47,13 +68,15 @@ class SiteTreeSubsites extends DataExtension
$subsiteID = Subsite::$force_subsite; $subsiteID = Subsite::$force_subsite;
} else { } else {
/*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();
} }
// 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') === false) { $siteTreeTableName = SiteTree::getSchema()->tableName(SiteTree::class);
if (strpos($tableName, $siteTreeTableName) === false) {
break; break;
} }
$query->addWhere("\"$tableName\".\"SubsiteID\" IN ($subsiteID)"); $query->addWhere("\"$tableName\".\"SubsiteID\" IN ($subsiteID)");
@ -72,38 +95,41 @@ class SiteTreeSubsites extends DataExtension
public function updateCMSFields(FieldList $fields) public function updateCMSFields(FieldList $fields)
{ {
$subsites = Subsite::accessible_sites("CMS_ACCESS_CMSMain"); $subsites = Subsite::accessible_sites('CMS_ACCESS_CMSMain');
$subsitesMap = array(); $subsitesMap = [];
if ($subsites && $subsites->Count()) { if ($subsites && $subsites->count()) {
$subsitesMap = $subsites->map('ID', 'Title'); $subsitesToMap = $subsites->exclude('ID', $this->owner->SubsiteID);
unset($subsitesMap[$this->owner->SubsiteID]); $subsitesMap = $subsitesToMap->map('ID', 'Title');
} }
// 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->addFieldsToTab( $fields->addFieldToTab(
'Root.Main', 'Root.Main',
ToggleCompositeField::create('SubsiteOperations', ToggleCompositeField::create('SubsiteOperations',
_t('SiteTreeSubsites.SubsiteOperations', 'Subsite Operations'), _t('SiteTreeSubsites.SubsiteOperations', 'Subsite Operations'),
array( [
new DropdownField("CopyToSubsiteID", _t('SiteTreeSubsites.CopyToSubsite', "Copy page to subsite"), $subsitesMap), new DropdownField('CopyToSubsiteID', _t('SiteTreeSubsites.CopyToSubsite',
new CheckboxField("CopyToSubsiteWithChildren", _t('SiteTreeSubsites.CopyToSubsiteWithChildren', 'Include children pages?')), 'Copy page to subsite'), $subsitesMap),
$copyAction = new InlineFormAction( new CheckboxField('CopyToSubsiteWithChildren',
"copytosubsite", _t('SiteTreeSubsites.CopyToSubsiteWithChildren', 'Include children pages?')),
_t('SiteTreeSubsites.CopyAction', "Copy") $copyAction = new FormAction(
'copytosubsite',
_t('SiteTreeSubsites.CopyAction', 'Copy')
) )
) ]
)->setHeadingLevel(4) )->setHeadingLevel(4)
); );
$copyAction->includeDefaultJS(false);
// $copyAction->includeDefaultJS(false);
} }
// replace readonly link prefix // replace readonly link prefix
$subsite = $this->owner->Subsite(); $subsite = $this->owner->Subsite();
$nested_urls_enabled = Config::inst()->get('SiteTree', 'nested_urls'); $nested_urls_enabled = Config::inst()->get(SiteTree::class, 'nested_urls');
if ($subsite && $subsite->exists()) { if ($subsite && $subsite->exists()) {
// Use baseurl from domain // Use baseurl from domain
$baseLink = $subsite->absoluteBaseURL(); $baseLink = $subsite->absoluteBaseURL();
@ -209,7 +235,7 @@ class SiteTreeSubsites extends DataExtension
if (!$this->owner->SubsiteID) { if (!$this->owner->SubsiteID) {
return false; return false;
} }
$sc = DataObject::get_one('SiteConfig', '"SubsiteID" = ' . $this->owner->SubsiteID); $sc = DataObject::get_one(SiteConfig::class, '"SubsiteID" = ' . $this->owner->SubsiteID);
if (!$sc) { if (!$sc) {
$sc = new SiteConfig(); $sc = new SiteConfig();
$sc->SubsiteID = $this->owner->SubsiteID; $sc->SubsiteID = $this->owner->SubsiteID;
@ -225,7 +251,8 @@ class SiteTreeSubsites extends DataExtension
* - 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 * @param null $member
* @return bool
*/ */
public function canEdit($member = null) public function canEdit($member = null)
{ {
@ -254,7 +281,8 @@ class SiteTreeSubsites extends DataExtension
} }
/** /**
* @return boolean * @param null $member
* @return bool
*/ */
public function canDelete($member = null) public function canDelete($member = null)
{ {
@ -266,7 +294,8 @@ class SiteTreeSubsites extends DataExtension
} }
/** /**
* @return boolean * @param null $member
* @return bool
*/ */
public function canAddChildren($member = null) public function canAddChildren($member = null)
{ {
@ -278,7 +307,8 @@ class SiteTreeSubsites extends DataExtension
} }
/** /**
* @return boolean * @param null $member
* @return bool
*/ */
public function canPublish($member = null) public function canPublish($member = null)
{ {
@ -289,61 +319,16 @@ class SiteTreeSubsites extends DataExtension
return $this->canEdit($member); return $this->canEdit($member);
} }
/**
* Create a duplicate of this page and save it to another subsite
*
* @param int|Subsite $subsiteID The Subsite to copy to, or its ID
* @param bool $includeChildren Recursively copy child Pages.
* @param int $parentID Where to place the Page in the SiteTree's structure.
*
* @return SiteTree duplicated page
*/
public function duplicateToSubsite($subsiteID = null, $includeChildren = false, $parentID = 0)
{
if ($subsiteID instanceof Subsite) {
$subsiteID = $subsiteID->ID;
}
$oldSubsite = Subsite::currentSubsiteID();
if ($subsiteID) {
Subsite::changeSubsite($subsiteID);
} else {
$subsiteID = $oldSubsite;
}
$page = $this->owner->duplicate(false);
$page->CheckedPublicationDifferences = $page->AddedToStage = true;
$subsiteID = ($subsiteID ? $subsiteID : $oldSubsite);
$page->SubsiteID = $subsiteID;
$page->ParentID = $parentID;
// MasterPageID is here for legacy purposes, to satisfy the subsites_relatedpages module
$page->MasterPageID = $this->owner->ID;
$page->write();
Subsite::changeSubsite($oldSubsite);
if ($includeChildren) {
foreach ($this->owner->AllChildren() as $child) {
$child->duplicateToSubsite($subsiteID, $includeChildren, $page->ID);
}
}
return $page;
}
/** /**
* Called by ContentController::init(); * Called by ContentController::init();
* @param $controller
*/ */
public static function contentcontrollerInit($controller) public static function contentcontrollerInit($controller)
{ {
$subsite = Subsite::currentSubsite(); $subsite = Subsite::currentSubsite();
if ($subsite && $subsite->Theme) { if ($subsite && $subsite->Theme) {
Config::inst()->update('SSViewer', 'theme', Subsite::currentSubsite()->Theme); SSViewer::set_themes(array_merge([$subsite->Theme], SSViewer::get_themes()));
} }
} }
@ -353,7 +338,7 @@ class SiteTreeSubsites extends DataExtension
// This helps deal with Link() returning an absolute URL. // This helps deal with Link() returning an absolute URL.
$url = Director::absoluteURL($this->owner->Link()); $url = Director::absoluteURL($this->owner->Link());
if ($this->owner->SubsiteID) { if ($this->owner->SubsiteID) {
$url = preg_replace('/\/\/[^\/]+\//', '//' . $this->owner->Subsite()->domain() . '/', $url); $url = preg_replace('/\/\/[^\/]+\//', '//' . $this->owner->Subsite()->domain() . '/', $url);
} }
return $url; return $url;
} }
@ -361,6 +346,8 @@ class SiteTreeSubsites extends DataExtension
/** /**
* 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.
* @param null $action
* @return string
*/ */
public function alternatePreviewLink($action = null) public function alternatePreviewLink($action = null)
{ {
@ -373,11 +360,13 @@ class SiteTreeSubsites extends DataExtension
/** /**
* 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.
* @param $tags
* @return string
*/ */
public function MetaTags(&$tags) public function MetaTags(&$tags)
{ {
if ($this->owner->SubsiteID) { if ($this->owner->SubsiteID) {
$tags .= "<meta name=\"x-subsite-id\" content=\"" . $this->owner->SubsiteID . "\" />\n"; $tags .= '<meta name="x-subsite-id" content="' . $this->owner->SubsiteID . "\" />\n";
} }
return $tags; return $tags;
@ -387,7 +376,7 @@ class SiteTreeSubsites extends DataExtension
{ {
// Set LinkTracking appropriately // Set LinkTracking appropriately
$links = HTTP::getLinksIn($this->owner->Content); $links = HTTP::getLinksIn($this->owner->Content);
$linkedPages = array(); $linkedPages = [];
if ($links) { if ($links) {
foreach ($links as $link) { foreach ($links as $link) {
@ -404,7 +393,9 @@ class SiteTreeSubsites extends DataExtension
$origDisableSubsiteFilter = Subsite::$disable_subsite_filter; $origDisableSubsiteFilter = Subsite::$disable_subsite_filter;
Subsite::disable_subsite_filter(true); Subsite::disable_subsite_filter(true);
$candidatePage = DataObject::get_one("SiteTree", "\"URLSegment\" = '" . Convert::raw2sql(urldecode($rest)) . "' AND \"SubsiteID\" = " . $subsiteID, false); $candidatePage = DataObject::get_one(SiteTree::class,
"\"URLSegment\" = '" . Convert::raw2sql(urldecode($rest)) . "' AND \"SubsiteID\" = " . $subsiteID,
false);
Subsite::disable_subsite_filter($origDisableSubsiteFilter); Subsite::disable_subsite_filter($origDisableSubsiteFilter);
if ($candidatePage) { if ($candidatePage) {
@ -452,7 +443,7 @@ class SiteTreeSubsites extends DataExtension
*/ */
public function cacheKeyComponent() public function cacheKeyComponent()
{ {
return 'subsite-'.Subsite::currentSubsiteID(); return 'subsite-' . Subsite::currentSubsiteID();
} }
/** /**

View File

@ -1,8 +1,14 @@
<?php <?php
namespace SilverStripe\Subsites\Extensions;
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
* this class to your admin in config. eg: * this class to your admin in config. eg:
* *
* MyAdmin::add_extension('SubsiteMenuExtension'); * MyAdmin::add_extension('SubsiteMenuExtension');

View File

@ -1,55 +1,10 @@
<?php <?php
namespace SilverStripe\Subsites\Forms;
use SilverStripe\Forms\GridField\GridFieldDetailForm;
class GridFieldSubsiteDetailForm extends GridFieldDetailForm class GridFieldSubsiteDetailForm extends GridFieldDetailForm
{ {
protected $itemRequestClass='GridFieldSubsiteDetailForm_ItemRequest'; protected $itemRequestClass = 'GridFieldSubsiteDetailForm_ItemRequest';
}
class GridFieldSubsiteDetailForm_ItemRequest extends GridFieldDetailForm_ItemRequest
{
private static $allowed_actions = array(
'ItemEditForm',
);
/**
* Builds an item edit form. The arguments to getCMSFields() are the popupController and
* 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
* 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.
*
* @return Form
* @see GridFieldDetailForm_ItemRequest::ItemEditForm()
*/
public function ItemEditForm()
{
$form=parent::ItemEditForm();
if ($this->record->ID == 0) {
$templates = Subsite::get()->sort('Title');
$templateArray = array();
if ($templates) {
$templateArray = $templates->map('ID', 'Title');
}
$templateDropdown = new DropdownField('TemplateID', _t('Subsite.COPYSTRUCTURE', 'Copy structure from:'), $templateArray);
$templateDropdown->setEmptyString('(' . _t('Subsite.NOTEMPLATE', 'No template') . ')');
$form->Fields()->addFieldToTab('Root.Configuration', $templateDropdown);
}
return $form;
}
public function doSave($data, $form)
{
$new_record = $this->record->ID == 0;
if ($new_record && isset($data['TemplateID']) && !empty($data['TemplateID'])) {
$template = Subsite::get()->byID(intval($data['TemplateID']));
if ($template) {
$this->record = $template->duplicate();
}
}
return parent::doSave($data, $form);
}
} }

View File

@ -0,0 +1,61 @@
<?php
namespace SilverStripe\Subsites\Forms;
use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\Form;
use SilverStripe\Forms\GridField\GridFieldDetailForm_ItemRequest;
use SilverStripe\Subsites\Model\Subsite;
class GridFieldSubsiteDetailForm_ItemRequest extends GridFieldDetailForm_ItemRequest
{
private static $allowed_actions = [
'ItemEditForm',
];
/**
* Builds an item edit form. The arguments to getCMSFields() are the popupController and
* 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
* 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.
*
* @return Form
* @see GridFieldDetailForm_ItemRequest::ItemEditForm()
*/
public function ItemEditForm()
{
$form = parent::ItemEditForm();
if ($this->record->ID == 0) {
$templates = Subsite::get()->sort('Title');
$templateArray = [];
if ($templates) {
$templateArray = $templates->map('ID', 'Title');
}
$templateDropdown = new DropdownField('TemplateID', _t('Subsite.COPYSTRUCTURE', 'Copy structure from:'),
$templateArray);
$templateDropdown->setEmptyString('(' . _t('Subsite.NOTEMPLATE', 'No template') . ')');
$form->Fields()->addFieldToTab('Root.Configuration', $templateDropdown);
}
return $form;
}
public function doSave($data, $form)
{
$new_record = $this->record->ID == 0;
if ($new_record && isset($data['TemplateID']) && !empty($data['TemplateID'])) {
$template = Subsite::get()->byID(intval($data['TemplateID']));
if ($template) {
$this->record = $template->duplicate();
}
}
return parent::doSave($data, $form);
}
}

View File

@ -1,48 +1,61 @@
<?php <?php
namespace SilverStripe\Subsites\Forms;
use SilverStripe\Control\Controller;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Control\Session;
use SilverStripe\Forms\TreeDropdownField;
use SilverStripe\View\Requirements;
/** /**
* Wraps around a TreedropdownField to add ability for temporary * Wraps around a TreedropdownField to add ability for temporary
* switching of subsite sessions. * switching of subsite sessions.
* *
* @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'); protected $extraClasses = ['SubsitesTreeDropdownField'];
public function Field($properties = array()) public 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;
} }
public function setSubsiteID($id) public function setSubsiteID($id)
{ {
$this->subsiteID = $id; $this->subsiteID = $id;
} }
public function getSubsiteID() public function getSubsiteID()
{ {
return $this->subsiteID; return $this->subsiteID;
} }
public function tree(SS_HTTPRequest $request) public function tree(HTTPRequest $request)
{ {
$oldSubsiteID = Session::get('SubsiteID'); $session = Controller::curr()->getRequest()->getSession();
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

@ -1,5 +1,7 @@
<?php <?php
namespace SilverStripe\Subsites\Forms;
use SilverStripe\Forms\TextField;
/** /**
* A text field that accepts only valid domain names, but allows the wildcard (*) character * A text field that accepts only valid domain names, but allows the wildcard (*) character
*/ */
@ -19,8 +21,8 @@ class WildcardDomainField extends TextField
$validator->validationError( $validator->validationError(
$this->getName(), $this->getName(),
_t("DomainNameField.INVALID_DOMAIN", "Invalid domain name"), _t('DomainNameField.INVALID_DOMAIN', 'Invalid domain name'),
"validation" 'validation'
); );
return false; return false;
} }

View File

@ -1,4 +1,41 @@
<?php <?php
namespace SilverStripe\Subsites\Model;
use SilverStripe\Admin\CMSMenu;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Control\Controller;
use SilverStripe\Control\Director;
use SilverStripe\Control\Session;
use SilverStripe\Core\Convert;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Forms\CheckboxField;
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\TabSet;
use SilverStripe\Forms\TextField;
use SilverStripe\i18n\Data\Intl\IntlLocales;
use SilverStripe\i18n\i18n;
use SilverStripe\ORM\ArrayLib;
use SilverStripe\ORM\ArrayList;
use SilverStripe\ORM\DataList;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\DB;
use SilverStripe\ORM\SS_List;
use SilverStripe\Security\Group;
use SilverStripe\Security\Member;
use SilverStripe\Security\Permission;
use SilverStripe\Versioned\Versioned;
use UnexpectedValueException;
/** /**
* A dynamically created subsite. SiteTree objects can now belong to a subsite. * A dynamically created subsite. SiteTree objects can now belong to a subsite.
* You can simulate subsite access without setting up virtual hosts by appending ?SubsiteID=<ID> to the request. * You can simulate subsite access without setting up virtual hosts by appending ?SubsiteID=<ID> to the request.
@ -7,6 +44,9 @@
*/ */
class Subsite extends DataObject class Subsite extends DataObject
{ {
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.
@ -39,21 +79,21 @@ class Subsite extends DataObject
* *
* @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.
@ -67,14 +107,13 @@ class Subsite extends DataObject
*/ */
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
@ -90,12 +129,11 @@ class Subsite extends DataObject
* Gets the subsite currently set in the session. * Gets the subsite currently set in the session.
* *
* @uses ControllerSubsites->controllerAugmentInit() * @uses ControllerSubsites->controllerAugmentInit()
* @return Subsite * @return DataObject The current Subsite
*/ */
public static function currentSubsite() public static function currentSubsite()
{ {
// get_by_id handles caching so we don't have to return Subsite::get()->byID(self::currentSubsiteID());
return DataObject::get_by_id('Subsite', self::currentSubsiteID());
} }
/** /**
@ -117,7 +155,7 @@ class Subsite extends DataObject
if (isset($_GET['SubsiteID'])) { if (isset($_GET['SubsiteID'])) {
$id = (int)$_GET['SubsiteID']; $id = (int)$_GET['SubsiteID'];
} elseif (Subsite::$use_session_subsiteid) { } elseif (Subsite::$use_session_subsiteid) {
$id = Session::get('SubsiteID'); $id = Controller::curr()->getRequest()->getSession()->get('SubsiteID');
} }
if ($id === null) { if ($id === null) {
@ -147,17 +185,17 @@ class Subsite extends DataObject
$subsiteID = $subsite; $subsiteID = $subsite;
} }
Session::set('SubsiteID', (int)$subsiteID); Controller::curr()->getRequest()->getSession()->set('SubsiteID', (int)$subsiteID);
// Set locale // Set locale
if (is_object($subsite) && $subsite->Language != '') { if (is_object($subsite) && $subsite->Language !== '') {
$locale = i18n::get_locale_from_lang($subsite->Language); $locale = (new IntlLocales())->localeFromLang($subsite->Language);
if ($locale) { if ($locale) {
i18n::set_locale($locale); i18n::set_locale($locale);
} }
} }
Permission::flush_permission_cache(); Permission::reset();
} }
/** /**
@ -166,7 +204,8 @@ class Subsite extends DataObject
* for example matching all subdomains on *.example.com with one subsite, * for example matching all subdomains on *.example.com with one subsite,
* and all subdomains on *.example.org on another. * and all subdomains on *.example.org on another.
* *
* @param $host The host to find the subsite for. If not specified, $_SERVER['HTTP_HOST'] is used. * @param $host string The host to find the subsite for. If not specified, $_SERVER['HTTP_HOST'] is used.
* @param bool $checkPermissions
* @return int Subsite ID * @return int Subsite ID
*/ */
public static function getSubsiteIDForDomain($host = null, $checkPermissions = true) public static function getSubsiteIDForDomain($host = null, $checkPermissions = true)
@ -182,20 +221,21 @@ class Subsite extends DataObject
$host = preg_replace('/^www\./', '', $host); $host = preg_replace('/^www\./', '', $host);
} }
$cacheKey = implode('_', array($host, Member::currentUserID(), self::$check_is_public)); $cacheKey = implode('_', [$host, Member::currentUserID(), self::$check_is_public]);
if (isset(self::$_cache_subsite_for_domain[$cacheKey])) { if (isset(self::$_cache_subsite_for_domain[$cacheKey])) {
return self::$_cache_subsite_for_domain[$cacheKey]; return self::$_cache_subsite_for_domain[$cacheKey];
} }
$SQL_host = Convert::raw2sql($host); $SQL_host = Convert::raw2sql($host);
$matchingDomains = DataObject::get( $matchingDomains = DataObject::get(
"SubsiteDomain", SubsiteDomain::class,
"'$SQL_host' LIKE replace(\"SubsiteDomain\".\"Domain\",'*','%')", "'$SQL_host' LIKE replace(\"SubsiteDomain\".\"Domain\",'*','%')",
"\"IsPrimary\" DESC" '"IsPrimary" DESC'
)->innerJoin('Subsite', "\"Subsite\".\"ID\" = \"SubsiteDomain\".\"SubsiteID\" AND \"Subsite\".\"IsPublic\"=1"); )->innerJoin('Subsite',
'"Subsite"."ID" = "SubsiteDomain"."SubsiteID" AND "Subsite"."IsPublic"=1');
} }
if ($matchingDomains && $matchingDomains->Count()) { if ($matchingDomains && $matchingDomains->count()) {
$subsiteIDs = array_unique($matchingDomains->column('SubsiteID')); $subsiteIDs = array_unique($matchingDomains->column('SubsiteID'));
$subsiteDomains = array_unique($matchingDomains->column('Domain')); $subsiteDomains = array_unique($matchingDomains->column('Domain'));
if (sizeof($subsiteIDs) > 1) { if (sizeof($subsiteIDs) > 1) {
@ -207,12 +247,14 @@ class Subsite extends DataObject
} }
$subsiteID = $subsiteIDs[0]; $subsiteID = $subsiteIDs[0];
} elseif ($default = Subsite::get()->filter('DefaultSite', 1)->setQueriedColumns(array('ID'))->first()) {
// Check for a 'default' subsite
$subsiteID = $default->ID;
} else { } else {
// Default subsite id = 0, the main site if ($default = DataObject::get_one(Subsite::class, '"DefaultSite" = 1')) {
$subsiteID = 0; // Check for a 'default' subsite
$subsiteID = $default->ID;
} else {
// Default subsite id = 0, the main site
$subsiteID = 0;
}
} }
if ($cacheKey) { if ($cacheKey) {
@ -231,7 +273,7 @@ class Subsite extends DataObject
* @param string $limit * @param string $limit
* @return DataList * @return DataList
*/ */
public static function get_from_all_subsites($className, $filter = "", $sort = "", $join = "", $limit = "") public static function get_from_all_subsites($className, $filter = '', $sort = '', $join = '', $limit = '')
{ {
$result = DataObject::get($className, $filter, $sort, $join, $limit); $result = DataObject::get($className, $filter, $sort, $join, $limit);
$result = $result->setDataQueryParam('Subsite.filter', false); $result = $result->setDataQueryParam('Subsite.filter', false);
@ -240,6 +282,7 @@ class Subsite extends DataObject
/** /**
* Disable the sub-site filtering; queries will select from all subsites * Disable the sub-site filtering; queries will select from all subsites
* @param bool $disabled
*/ */
public static function disable_subsite_filter($disabled = true) public static function disable_subsite_filter($disabled = true)
{ {
@ -251,16 +294,19 @@ class Subsite extends DataObject
*/ */
public static function on_db_reset() public static function on_db_reset()
{ {
self::$_cache_accessible_sites = array(); self::$_cache_accessible_sites = [];
self::$_cache_subsite_for_domain = array(); self::$_cache_subsite_for_domain = [];
} }
/** /**
* Return all subsites, regardless of permissions (augmented with main site). * Return all subsites, regardless of permissions (augmented with main site).
* *
* @return SS_List List of {@link Subsite} objects (DataList or ArrayList). * @param bool $includeMainSite
* @param string $mainSiteTitle
* @return SS_List List of <a href='psi_element://Subsite'>Subsite</a> objects (DataList or ArrayList).
* objects (DataList or ArrayList).
*/ */
public static function all_sites($includeMainSite = true, $mainSiteTitle = "Main site") public static function all_sites($includeMainSite = true, $mainSiteTitle = 'Main site')
{ {
$subsites = Subsite::get(); $subsites = Subsite::get();
@ -283,7 +329,7 @@ class Subsite extends DataObject
* *
* @return ArrayList of {@link Subsite} instances. * @return ArrayList of {@link Subsite} instances.
*/ */
public static function all_accessible_sites($includeMainSite = true, $mainSiteTitle = "Main site", $member = null) public static function all_accessible_sites($includeMainSite = true, $mainSiteTitle = 'Main site', $member = null)
{ {
// Rationalise member arguments // Rationalise member arguments
if (!$member) { if (!$member) {
@ -293,7 +339,7 @@ class Subsite extends DataObject
return new ArrayList(); return new ArrayList();
} }
if (!is_object($member)) { if (!is_object($member)) {
$member = DataObject::get_by_id('Member', $member); $member = DataObject::get_by_id(Member::class, $member);
} }
$subsites = new ArrayList(); $subsites = new ArrayList();
@ -323,12 +369,17 @@ class Subsite extends DataObject
* Sites will only be included if they have a Title. * Sites will only be included if they have a Title.
* *
* @param $permCode array|string Either a single permission code or an array of permission codes. * @param $permCode array|string Either a single permission code or an array of permission codes.
* @param $includeMainSite If true, the main site will be included if appropriate. * @param $includeMainSite bool If true, the main site will be included if appropriate.
* @param $mainSiteTitle The label to give to the main site * @param $mainSiteTitle string The label to give to the main site
* @param $member * @param $member int|Member The member attempting to access the sites
* @return DataList of {@link Subsite} instances * @return DataList|ArrayList of {@link Subsite} instances
*/ */
public static function accessible_sites($permCode, $includeMainSite = true, $mainSiteTitle = "Main site", $member = null) public static function accessible_sites(
$permCode,
$includeMainSite = true,
$mainSiteTitle = 'Main site',
$member = null
)
{ {
// Rationalise member arguments // Rationalise member arguments
if (!$member) { if (!$member) {
@ -338,7 +389,7 @@ class Subsite extends DataObject
return new ArrayList(); return new ArrayList();
} }
if (!is_object($member)) { if (!is_object($member)) {
$member = DataObject::get_by_id('Member', $member); $member = DataObject::get_by_id(Member::class, $member);
} }
// Rationalise permCode argument // Rationalise permCode argument
@ -354,25 +405,32 @@ class Subsite extends DataObject
return self::$_cache_accessible_sites[$cacheKey]; return self::$_cache_accessible_sites[$cacheKey];
} }
$subsites = DataList::create('Subsite') $subsites = DataList::create(Subsite::class)
->where("\"Subsite\".\"Title\" != ''") ->where("\"Subsite\".\"Title\" != ''")
->leftJoin('Group_Subsites', "\"Group_Subsites\".\"SubsiteID\" = \"Subsite\".\"ID\"") ->leftJoin('Group_Subsites', '"Group_Subsites"."SubsiteID" = "Subsite"."ID"')
->innerJoin('Group', "\"Group\".\"ID\" = \"Group_Subsites\".\"GroupID\" OR \"Group\".\"AccessAllSubsites\" = 1") ->innerJoin('Group',
->innerJoin('Group_Members', "\"Group_Members\".\"GroupID\"=\"Group\".\"ID\" AND \"Group_Members\".\"MemberID\" = $member->ID") '"Group"."ID" = "Group_Subsites"."GroupID" OR "Group"."AccessAllSubsites" = 1')
->innerJoin('Permission', "\"Group\".\"ID\"=\"Permission\".\"GroupID\" AND \"Permission\".\"Code\" IN ($SQL_codes, 'CMS_ACCESS_LeftAndMain', 'ADMIN')"); ->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')");
if (!$subsites) { if (!$subsites) {
$subsites = new ArrayList(); $subsites = new ArrayList();
} }
$rolesSubsites = DataList::create('Subsite') /** @var DataList $rolesSubsites */
$rolesSubsites = DataList::create(Subsite::class)
->where("\"Subsite\".\"Title\" != ''") ->where("\"Subsite\".\"Title\" != ''")
->leftJoin('Group_Subsites', "\"Group_Subsites\".\"SubsiteID\" = \"Subsite\".\"ID\"") ->leftJoin('Group_Subsites', '"Group_Subsites"."SubsiteID" = "Subsite"."ID"')
->innerJoin('Group', "\"Group\".\"ID\" = \"Group_Subsites\".\"GroupID\" OR \"Group\".\"AccessAllSubsites\" = 1") ->innerJoin('Group',
->innerJoin('Group_Members', "\"Group_Members\".\"GroupID\"=\"Group\".\"ID\" AND \"Group_Members\".\"MemberID\" = $member->ID") '"Group"."ID" = "Group_Subsites"."GroupID" OR "Group"."AccessAllSubsites" = 1')
->innerJoin('Group_Roles', "\"Group_Roles\".\"GroupID\"=\"Group\".\"ID\"") ->innerJoin('Group_Members',
->innerJoin('PermissionRole', "\"Group_Roles\".\"PermissionRoleID\"=\"PermissionRole\".\"ID\"") "\"Group_Members\".\"GroupID\"=\"Group\".\"ID\" AND \"Group_Members\".\"MemberID\" = $member->ID")
->innerJoin('PermissionRoleCode', "\"PermissionRole\".\"ID\"=\"PermissionRoleCode\".\"RoleID\" AND \"PermissionRoleCode\".\"Code\" IN ($SQL_codes, 'CMS_ACCESS_LeftAndMain', 'ADMIN')"); ->innerJoin('Group_Roles', '"Group_Roles"."GroupID"="Group"."ID"')
->innerJoin('PermissionRole', '"Group_Roles"."PermissionRoleID"="PermissionRole"."ID"')
->innerJoin('PermissionRoleCode',
"\"PermissionRole\".\"ID\"=\"PermissionRoleCode\".\"RoleID\" AND \"PermissionRoleCode\".\"Code\" IN ($SQL_codes, 'CMS_ACCESS_LeftAndMain', 'ADMIN')");
if (!$subsites && $rolesSubsites) { if (!$subsites && $rolesSubsites) {
return $rolesSubsites; return $rolesSubsites;
@ -390,15 +448,15 @@ class Subsite extends DataObject
if ($includeMainSite) { if ($includeMainSite) {
if (!is_array($permCode)) { if (!is_array($permCode)) {
$permCode = array($permCode); $permCode = [$permCode];
} }
if (self::hasMainSitePermission($member, $permCode)) { if (self::hasMainSitePermission($member, $permCode)) {
$subsites=$subsites->toArray(); $subsites = $subsites->toArray();
$mainSite = new Subsite(); $mainSite = new Subsite();
$mainSite->Title = $mainSiteTitle; $mainSite->Title = $mainSiteTitle;
array_unshift($subsites, $mainSite); array_unshift($subsites, $mainSite);
$subsites=ArrayList::create($subsites); $subsites = ArrayList::create($subsites);
} }
} }
@ -422,11 +480,11 @@ class Subsite extends DataObject
} }
if (!$file) { if (!$file) {
$file = Director::baseFolder().'/subsites/host-map.php'; $file = Director::baseFolder() . '/subsites/host-map.php';
} }
$hostmap = array(); $hostmap = [];
$subsites = DataObject::get('Subsite'); $subsites = DataObject::get(Subsite::class);
if ($subsites) { if ($subsites) {
foreach ($subsites as $subsite) { foreach ($subsites as $subsite) {
@ -465,10 +523,10 @@ class Subsite extends DataObject
* @todo Allow permission inheritance through group hierarchy. * @todo Allow permission inheritance through group hierarchy.
* *
* @param Member Member to check against. Defaults to currently logged in member * @param Member Member to check against. Defaults to currently logged in member
* @param Array Permission code strings. Defaults to "ADMIN". * @param array $permissionCodes
* @return boolean * @return bool
*/ */
public static function hasMainSitePermission($member = null, $permissionCodes = array('ADMIN')) public static function hasMainSitePermission($member = null, $permissionCodes = ['ADMIN'])
{ {
if (!is_array($permissionCodes)) { if (!is_array($permissionCodes)) {
user_error('Permissions must be passed to Subsite::hasMainSitePermission as an array', E_USER_ERROR); user_error('Permissions must be passed to Subsite::hasMainSitePermission as an array', E_USER_ERROR);
@ -482,8 +540,8 @@ class Subsite extends DataObject
return false; return false;
} }
if (!in_array("ADMIN", $permissionCodes)) { if (!in_array('ADMIN', $permissionCodes)) {
$permissionCodes[] = "ADMIN"; $permissionCodes[] = 'ADMIN';
} }
$SQLa_perm = Convert::raw2sql($permissionCodes); $SQLa_perm = Convert::raw2sql($permissionCodes);
@ -518,10 +576,9 @@ class Subsite extends DataObject
} }
/** /**
*
* @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',
@ -534,51 +591,47 @@ class Subsite extends DataObject
// 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', 'Domains' => SubsiteDomain::class,
); ];
/** /**
*
* @var array * @var array
*/ */
private static $belongs_many_many = array( private static $belongs_many_many = [
"Groups" => "Group", 'Groups' => Group::class,
); ];
/** /**
*
* @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 * @param bool $member
* @return bool
*/ */
public function canEdit($member = false) public function canEdit($member = false)
{ {
@ -593,42 +646,44 @@ class Subsite extends DataObject
public function getCMSFields() public function getCMSFields()
{ {
if ($this->ID != 0) { if ($this->ID != 0) {
$domainTable = GridField::create( $domainTable = new GridField(
"Domains", 'Domains',
_t('Subsite.DomainsListTitle', "Domains"), _t('Subsite.DomainsListTitle', 'Domains'),
$this->Domains(), $this->Domains(),
GridFieldConfig_RecordEditor::create(10) GridFieldConfig_RecordEditor::create(10)
); );
} else { } else {
$domainTable = LiteralField::create( $domainTable = new LiteralField(
'Domains', 'Domains',
'<p>'._t('Subsite.DOMAINSAVEFIRST', 'You can only add domains after saving for the first time').'</p>' '<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'),
i18n::get_common_locales() Injector::inst()->get(IntlLocales::class)->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 = FieldList::create( $fields = new FieldList(
$subsiteTabs = TabSet::create('Root', $subsiteTabs = new TabSet('Root',
Tab::create( new Tab(
'Configuration', 'Configuration',
_t('Subsite.TabTitleConfig', 'Configuration'), _t('Subsite.TabTitleConfig', 'Configuration'),
HeaderField::create($this->getClassName() . ' configuration', 2), HeaderField::create('ConfigForSubsiteHeaderField', 'Subsite Configuration'),
TextField::create('Title', $this->fieldLabel('Title'), $this->Title), TextField::create('Title', $this->fieldLabel('Title'), $this->Title),
HeaderField::create( HeaderField::create(
_t('Subsite.DomainsHeadline', "Domains for this subsite") 'DomainsForSubsiteHeaderField',
_t('Subsite.DomainsHeadline', 'Domains for this subsite')
), ),
$domainTable, $domainTable,
$languageSelector, $languageSelector,
@ -658,7 +713,8 @@ class Subsite extends DataObject
$themes = $this->allowedThemes(); $themes = $this->allowedThemes();
if (!empty($themes)) { if (!empty($themes)) {
$fields->addFieldToTab('Root.Configuration', $fields->addFieldToTab('Root.Configuration',
DropdownField::create('Theme', $this->fieldLabel('Theme'), $this->allowedThemes(), $this->Theme)->setEmptyString(_t('Subsite.ThemeFieldEmptyString', '')), 'PageTypeBlacklistToggle'); DropdownField::create('Theme', $this->fieldLabel('Theme'), $this->allowedThemes(), $this->Theme)
->setEmptyString(_t('Subsite.ThemeFieldEmptyString', '-')), 'PageTypeBlacklistToggle');
} }
$subsiteTabs->addExtraClass('subsite-model'); $subsiteTabs->addExtraClass('subsite-model');
@ -697,20 +753,20 @@ class Subsite extends DataObject
{ {
if ($themes = $this->stat('allowed_themes')) { if ($themes = $this->stat('allowed_themes')) {
return ArrayLib::valuekey($themes); return ArrayLib::valuekey($themes);
} else {
$themes = array();
if (is_dir(THEMES_PATH)) {
foreach (scandir(THEMES_PATH) as $theme) {
if ($theme[0] == '.') {
continue;
}
$theme = strtok($theme, '_');
$themes[$theme] = $theme;
}
ksort($themes);
}
return $themes;
} }
$themes = [];
if (is_dir(THEMES_PATH)) {
foreach (scandir(THEMES_PATH) as $theme) {
if ($theme[0] == '.') {
continue;
}
$theme = strtok($theme, '_');
$themes[$theme] = $theme;
}
ksort($themes);
}
return $themes;
} }
/** /**
@ -720,20 +776,20 @@ class Subsite extends DataObject
{ {
if ($this->getField('Language')) { if ($this->getField('Language')) {
return $this->getField('Language'); return $this->getField('Language');
} else {
return i18n::get_locale();
} }
return i18n::get_locale();
} }
/** /**
* *
* @return ValidationResult * @return \SilverStripe\ORM\ValidationResult
*/ */
public function validate() public function validate()
{ {
$result = parent::validate(); $result = parent::validate();
if (!$this->Title) { if (!$this->Title) {
$result->error(_t('Subsite.ValidateTitle', 'Please add a "Title"')); $result->addError(_t('Subsite.ValidateTitle', 'Please add a "Title"'));
} }
return $result; return $result;
} }
@ -805,14 +861,6 @@ class Subsite extends DataObject
return Director::absoluteBaseURL(); return Director::absoluteBaseURL();
} }
/**
* @todo getClassName is redundant, already stored as a database field?
*/
public function getClassName()
{
return $this->class;
}
/** /**
* Javascript admin action to duplicate this subsite * Javascript admin action to duplicate this subsite
* *
@ -824,7 +872,7 @@ class Subsite extends DataObject
$message = _t( $message = _t(
'Subsite.CopyMessage', 'Subsite.CopyMessage',
'Created a copy of {title}', 'Created a copy of {title}',
array('title' => Convert::raw2js($this->Title)) ['title' => Convert::raw2js($this->Title)]
); );
return <<<JS return <<<JS
@ -846,7 +894,7 @@ JS;
* @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)) { if (!is_array($permissionCodes)) {
user_error('Permissions must be passed to Subsite::getMembersByPermission as an array', E_USER_ERROR); user_error('Permissions must be passed to Subsite::getMembersByPermission as an array', E_USER_ERROR);
@ -856,19 +904,22 @@ JS;
$SQL_permissionCodes = join("','", $SQL_permissionCodes); $SQL_permissionCodes = join("','", $SQL_permissionCodes);
return DataObject::get( return DataObject::get(
'Member', Member::class,
"\"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
* @param bool $doWrite
* @param string $manyMany
* @return DataObject
*/ */
public function duplicate($doWrite = true) public function duplicate($doWrite = true, $manyMany = 'many_many')
{ {
$duplicate = parent::duplicate($doWrite); $duplicate = parent::duplicate($doWrite);
@ -881,7 +932,7 @@ JS;
* 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", '');
@ -893,11 +944,11 @@ JS;
$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->copyVersionToStage('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]);
} }
} }
} }

View File

@ -1,5 +1,16 @@
<?php <?php
namespace SilverStripe\Subsites\Model;
use SilverStripe\Control\Controller;
use SilverStripe\Control\Director;
use SilverStripe\Forms\CheckboxField;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\OptionsetField;
use SilverStripe\ORM\DataObject;
use SilverStripe\Subsites\Forms\WildcardDomainField;
/** /**
* @property string $Domain domain name of this subsite. Can include wildcards. Do not include the URL scheme here * @property string $Domain domain name of this subsite. Can include wildcards. Do not include the URL scheme here
* @property string $Protocol Required protocol (http or https) if only one is supported. 'automatic' implies * @property string $Protocol Required protocol (http or https) if only one is supported. 'automatic' implies
@ -10,21 +21,23 @@
*/ */
class SubsiteDomain extends DataObject class SubsiteDomain extends DataObject
{ {
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)',
"Protocol" => "Enum('http,https,automatic','automatic')", 'Protocol' => "Enum('http,https,automatic','automatic')",
"IsPrimary" => "Boolean", 'IsPrimary' => 'Boolean',
); ];
/** /**
* Specifies that this subsite is http only * Specifies that this subsite is http only
@ -55,50 +68,49 @@ class SubsiteDomain extends DataObject
* *
* @var array * @var array
*/ */
private static $has_one = array( private static $has_one = [
"Subsite" => "Subsite", 'Subsite' => Subsite::class,
); ];
/**
*
* @var array
*/
private static $summary_fields=array(
'Domain',
'IsPrimary',
);
/** /**
* @config * @config
* @var array * @var array
*/ */
private static $casting = array( private static $summary_fields = [
'Domain',
'IsPrimary',
];
/*** @config
* @var array
*/
private static $casting = [
'SubstitutedDomain' => 'Varchar', 'SubstitutedDomain' => 'Varchar',
'FullProtocol' => 'Varchar', 'FullProtocol' => 'Varchar',
'AbsoluteLink' => 'Varchar', 'AbsoluteLink' => 'Varchar',
); ];
/** /**
* 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()
{ {
$protocols = array( $protocols = [
self::PROTOCOL_HTTP => _t('SubsiteDomain.PROTOCOL_HTTP', 'http://'), self::PROTOCOL_HTTP => _t('SubsiteDomain.PROTOCOL_HTTP', 'http://'),
self::PROTOCOL_HTTPS => _t('SubsiteDomain.PROTOCOL_HTTPS', 'https://'), self::PROTOCOL_HTTPS => _t('SubsiteDomain.PROTOCOL_HTTPS', 'https://'),
self::PROTOCOL_AUTOMATIC => _t('SubsiteDomain.PROTOCOL_AUTOMATIC', 'Automatic') self::PROTOCOL_AUTOMATIC => _t('SubsiteDomain.PROTOCOL_AUTOMATIC', 'Automatic')
); ];
$fields = new FieldList( $fields = new FieldList(
WildcardDomainField::create('Domain', $this->fieldLabel('Domain'), null, 255) WildcardDomainField::create('Domain', $this->fieldLabel('Domain'), null, 255)
->setDescription(_t( ->setDescription(_t(
@ -155,16 +167,13 @@ class SubsiteDomain extends DataObject
public function getFullProtocol() public function getFullProtocol()
{ {
switch ($this->Protocol) { switch ($this->Protocol) {
case self::PROTOCOL_HTTPS: case self::PROTOCOL_HTTPS: {
{
return 'https://'; return 'https://';
} }
case self::PROTOCOL_HTTP: case self::PROTOCOL_HTTP: {
{
return 'http://'; return 'http://';
} }
default: default: {
{
return Director::protocol(); return Director::protocol();
} }
} }

View File

@ -1,33 +1,59 @@
<?php <?php
namespace SilverStripe\Subsites\Pages;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\CMS\Model\VirtualPage;
use SilverStripe\Control\Controller;
use SilverStripe\Control\Session;
use SilverStripe\Core\Config\Config;
use SilverStripe\Forms\DropdownField;
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\Model\Subsite;
use SilverStripe\View\ArrayData;
class SubsitesVirtualPage extends VirtualPage class SubsitesVirtualPage extends VirtualPage
{ {
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'
); ];
private static $non_virtual_fields = [
'SubsiteID'
];
public function getCMSFields() public function getCMSFields()
{ {
$fields = parent::getCMSFields(); $fields = parent::getCMSFields();
$subsites = DataObject::get('Subsite'); $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"), _t('SubsitesVirtualPage.SubsiteField', 'Subsite'),
$subsites->map('ID', 'Title') $subsites->map('ID', 'Title')
)->addExtraClass('subsitestreedropdownfield-chooser no-change-track'), )->addExtraClass('subsitestreedropdownfield-chooser no-change-track'),
'CopyContentFromID' 'CopyContentFromID'
@ -35,11 +61,11 @@ class SubsitesVirtualPage extends VirtualPage
// 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'),
"SiteTree", "SilverStripe\\CMS\\Model\\SiteTree",
"ID", 'ID',
"MenuTitle" 'MenuTitle'
); );
if (Controller::has_curr() && Controller::curr()->getRequest()) { if (Controller::has_curr() && Controller::curr()->getRequest()) {
@ -54,10 +80,10 @@ class SubsitesVirtualPage extends VirtualPage
$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'
); );
@ -78,7 +104,7 @@ class SubsitesVirtualPage extends VirtualPage
TextareaField::create( TextareaField::create(
'CustomMetaKeywords', 'CustomMetaKeywords',
$this->fieldLabel('CustomMetaTitle') $this->fieldLabel('CustomMetaTitle')
)->setDescription(_t('SubsitesVirtualPage.OverrideNote')), )->setDescription(_t('SubsitesVirtualPage.OverrideNote', 'Overrides inherited value from the source')),
'MetaKeywords' 'MetaKeywords'
); );
$fields->addFieldToTab( $fields->addFieldToTab(
@ -86,7 +112,7 @@ class SubsitesVirtualPage extends VirtualPage
TextareaField::create( TextareaField::create(
'CustomMetaDescription', 'CustomMetaDescription',
$this->fieldLabel('CustomMetaTitle') $this->fieldLabel('CustomMetaTitle')
)->setDescription(_t('SubsitesVirtualPage.OverrideNote')), )->setDescription(_t('SubsitesVirtualPage.OverrideNote', 'Overrides inherited value from the source')),
'MetaDescription' 'MetaDescription'
); );
$fields->addFieldToTab( $fields->addFieldToTab(
@ -94,7 +120,7 @@ class SubsitesVirtualPage extends VirtualPage
TextField::create( TextField::create(
'CustomExtraMeta', 'CustomExtraMeta',
$this->fieldLabel('CustomMetaTitle') $this->fieldLabel('CustomMetaTitle')
)->setDescription(_t('SubsitesVirtualPage.OverrideNote')), )->setDescription(_t('SubsitesVirtualPage.OverrideNote', 'Overrides inherited value from the source')),
'ExtraMeta' 'ExtraMeta'
); );
@ -114,7 +140,7 @@ class SubsitesVirtualPage extends VirtualPage
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()
@ -140,7 +166,7 @@ class SubsitesVirtualPage extends VirtualPage
$oldState = Subsite::$disable_subsite_filter; $oldState = Subsite::$disable_subsite_filter;
Subsite::$disable_subsite_filter = true; Subsite::$disable_subsite_filter = true;
if ($this->CopyContentFromID) { if ($this->CopyContentFromID) {
$this->HasBrokenLink = DataObject::get_by_id('SiteTree', $this->CopyContentFromID) ? false : true; $this->HasBrokenLink = DataObject::get_by_id(SiteTree::class, $this->CopyContentFromID) ? false : true;
} }
Subsite::$disable_subsite_filter = $oldState; Subsite::$disable_subsite_filter = $oldState;
} }
@ -170,24 +196,44 @@ class SubsitesVirtualPage extends VirtualPage
$this->ExtraMeta = $this->ContentSource()->ExtraMeta ? $this->ContentSource()->ExtraMeta : $this->ExtraMeta; $this->ExtraMeta = $this->ContentSource()->ExtraMeta ? $this->ContentSource()->ExtraMeta : $this->ExtraMeta;
} }
} }
}
class SubsitesVirtualPage_Controller extends VirtualPage_Controller public function validURLSegment()
{
public function reloadContent()
{ {
$this->failover->copyFrom($this->failover->CopyContentFrom()); $isValid = parent::validURLSegment();
$this->failover->write();
return;
}
public function init() // 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.
$origDisableSubsiteFilter = Subsite::$disable_subsite_filter; if (!$isValid) {
Subsite::$disable_subsite_filter = true; $IDFilter = $this->ID ? "AND \"SiteTree\".\"ID\" <> $this->ID" : null;
$parentFilter = null;
parent::init(); if (Config::inst()->get(SiteTree::class, 'nested_urls')) {
if ($this->ParentID) {
$parentFilter = " AND \"SiteTree\".\"ParentID\" = $this->ParentID";
} else {
$parentFilter = ' AND "SiteTree"."ParentID" = 0';
}
}
Subsite::$disable_subsite_filter = $origDisableSubsiteFilter; $origDisableSubsiteFilter = Subsite::$disable_subsite_filter;
Subsite::$disable_subsite_filter = true;
$existingPage = DataObject::get_one(
'SilverStripe\\CMS\\Model\\SiteTree',
"\"URLSegment\" = '$this->URLSegment' $IDFilter $parentFilter",
false // disable cache, it doesn't include subsite status in the key
);
Subsite::$disable_subsite_filter = $origDisableSubsiteFilter;
$existingPageInSubsite = DataObject::get_one(
'SilverStripe\\CMS\\Model\\SiteTree',
"\"URLSegment\" = '$this->URLSegment' $IDFilter $parentFilter",
false // disable cache, it doesn't include subsite status in the key
);
// If URL has been vetoed because of an existing page,
// be more specific and allow same URLSegments in different subsites
$isValid = !($existingPage && $existingPageInSubsite);
}
return $isValid;
} }
} }

View File

@ -1,19 +1,29 @@
<?php <?php
namespace SilverStripe\Subsites\Reports;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\TreeMultiselectField;
use SilverStripe\Reports\ReportWrapper;
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 SS_ReportWrapper class SubsiteReportWrapper extends ReportWrapper
{ {
///////////////////////////////////////////////////////////////////////////////////////////
// Filtering
/**
* @return FieldList
*/
public function parameterFields() public function parameterFields()
{ {
$subsites = Subsite::accessible_sites('CMS_ACCESS_CMSMain', true); $subsites = Subsite::accessible_sites('CMS_ACCESS_CMSMain', true);
$options = $subsites->toDropdownMap('ID', 'Title'); $options = $subsites->toDropdownMap('ID', 'Title');
$subsiteField = new TreeMultiselectField( $subsiteField = new TreeMultiselectField(
'Subsites', 'Subsites',
_t('SubsiteReportWrapper.ReportDropdown', 'Sites'), _t('SubsiteReportWrapper.ReportDropdown', 'Sites'),
@ -25,7 +35,7 @@ class SubsiteReportWrapper extends SS_ReportWrapper
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());
@ -35,35 +45,44 @@ class SubsiteReportWrapper extends SS_ReportWrapper
return $fields; return $fields;
} }
/////////////////////////////////////////////////////////////////////////////////////////// /**
// Columns * @return array
*/
public function columns() public function columns()
{ {
$columns = parent::columns(); $columns = parent::columns();
$columns['Subsite.Title'] = "Subsite"; $columns['Subsite.Title'] = Subsite::class;
return $columns; return $columns;
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Querying // Querying
/**
* @param arary $params
* @return void
*/
public function beforeQuery($params) public function beforeQuery($params)
{ {
// The user has select a few specific sites // The user has select a few specific sites
if (!empty($params['Subsites'])) { if (!empty($params['Subsites'])) {
Subsite::$force_subsite = $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));
} }
} }
/**
* @return void
*/
public function afterQuery() public function afterQuery()
{ {
// Manually manage the subsite filtering // Manually manage the subsite filtering
Subsite::$force_subsite = null; Subsite::$force_subsite = null;
} }
} }

View File

@ -1,8 +1,21 @@
<?php <?php
namespace SilverStripe\Subsites\Tasks;
use InvalidArgumentException;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Dev\BuildTask;
use SilverStripe\ORM\DataObject;
use SilverStripe\Subsites\Model\Subsite;
use SilverStripe\Subsites\Pages\SubsitesVirtualPage;
use SilverStripe\Versioned\Versioned;
/** /**
* Handy alternative to copying pages when creating a subsite through the UI. * Handy alternative to copying pages when creating a subsite through the UI.
* *
* Can be used to batch-add new pages after subsite creation, or simply to * Can be used to batch-add new pages after subsite creation, or simply to
* process a large site outside of the UI. * process a large site outside of the UI.
* *
* Example: sake dev/tasks/SubsiteCopyPagesTask from=<subsite-source> to=<subsite-target> * Example: sake dev/tasks/SubsiteCopyPagesTask from=<subsite-source> to=<subsite-target>
@ -12,7 +25,6 @@
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 = '';
public function run($request) public function run($request)
@ -21,7 +33,7 @@ class SubsiteCopyPagesTask extends BuildTask
if (!is_numeric($subsiteFromId)) { if (!is_numeric($subsiteFromId)) {
throw new InvalidArgumentException('Missing "from" parameter'); throw new InvalidArgumentException('Missing "from" parameter');
} }
$subsiteFrom = DataObject::get_by_id('Subsite', $subsiteFromId); $subsiteFrom = DataObject::get_by_id(Subsite::class, $subsiteFromId);
if (!$subsiteFrom) { if (!$subsiteFrom) {
throw new InvalidArgumentException('Subsite not found'); throw new InvalidArgumentException('Subsite not found');
} }
@ -30,7 +42,7 @@ class SubsiteCopyPagesTask extends BuildTask
if (!is_numeric($subsiteToId)) { if (!is_numeric($subsiteToId)) {
throw new InvalidArgumentException('Missing "to" parameter'); throw new InvalidArgumentException('Missing "to" parameter');
} }
$subsiteTo = DataObject::get_by_id('Subsite', $subsiteToId); $subsiteTo = DataObject::get_by_id(Subsite::class, $subsiteToId);
if (!$subsiteTo) { if (!$subsiteTo) {
throw new InvalidArgumentException('Subsite not found'); throw new InvalidArgumentException('Subsite not found');
} }
@ -43,11 +55,11 @@ class SubsiteCopyPagesTask extends BuildTask
// 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('SiteTree', 'Live', "\"ParentID\" = $sourceParentID", ''); $children = Versioned::get_by_stage(SiteTree::class, 'Live', "\"ParentID\" = $sourceParentID", '');
if ($children) { if ($children) {
foreach ($children as $child) { foreach ($children as $child) {
@ -59,11 +71,11 @@ class SubsiteCopyPagesTask extends BuildTask
} 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->copyVersionToStage('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()));
} }

View File

@ -11,16 +11,22 @@
} }
], ],
"require": { "require": {
"silverstripe/framework": "^4.0@dev", "silverstripe/framework": "~4.0",
"silverstripe/cms": "^4.0@dev" "silverstripe/cms": "~4.0",
"silverstripe/asset-admin": "~1.0.0"
}, },
"require-dev": { "require-dev": {
"phpunit/PHPUnit": "~4.8@stable" "phpunit/PHPUnit": "~4.8@stable"
}, },
"autoload": {
"psr-4": {
"SilverStripe\\Subsites\\": "code/",
"SilverStripe\\Subsites\\Tests\\": "tests/php/"
}
},
"extra": { "extra": {
"branch-alias": { "branch-alias": {
"dev-master": "2.0.x-dev" "dev-master": "2.0.x-dev"
} }
}, }
"license": "BSD-3-Clause"
} }

View File

@ -2,95 +2,101 @@
* Styling for the subsite actions section in the CMS * Styling for the subsite actions section in the CMS
*/ */
#SubsiteActions { #SubsiteActions {
position: absolute; position: absolute;
padding: 0; padding: 0;
margin: 0; margin: 0;
right: 0; right: 0;
top: 0; top: 0;
width: 350px; width: 350px;
text-align: right; text-align: right;
margin-right: 130px; margin-right: 130px;
height: 51px; height: 51px;
border-bottom: 3px solid #d4d0c8; border-bottom: 3px solid #d4d0c8;
color: #fff; color: #fff;
} }
#SubsiteActions fieldset {
padding: 3px;
border-style: none;
margin-top: 1px;
background: none;
}
#SubsiteActions fieldset span {
padding: 3px;
}
.cms-menu .cms-subsites{ #SubsiteActions fieldset {
padding:3px 0px 15px; padding: 3px;
border-style: none;
margin-top: 1px;
background: none;
} }
.cms-menu .cms-subsites .field.dropdown{
padding-bottom:0; #SubsiteActions fieldset span {
margin-bottom:0; padding: 3px;
} }
.cms-menu .cms-subsites {
padding: 3px 0 15px;
}
.cms-menu .cms-subsites .field.dropdown {
margin: 0 10px;
padding-bottom: 0;
}
.cms-menu.collapsed .cms-subsites { .cms-menu.collapsed .cms-subsites {
display: none; display: none;
} }
/* Custom chzn styles for dark blue background */ /* Custom chzn styles for dark blue background */
.cms-subsites .chzn-container-single .chzn-single, .cms-subsites .chzn-container-single .chzn-single,
.cms-subsites .chzn-container-active .chzn-single { .cms-subsites .chzn-container-active .chzn-single {
filter: none; /* Fix for IE9 */ filter: none; /* Fix for IE9 */
border: 1px solid #152338; border: 1px solid #152338;
background:#213557; background: #213557;
-webkit-box-shadow: inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); -webkit-box-shadow: inset 1px 0 0 rgba(255, 255, 255, .125), inset 0 1px 0 rgba(255, 255, 255, .2), 0 1px 2px rgba(0, 0, 0, .05);
box-shadow: inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05); box-shadow: inset 1px 0 0 rgba(255, 255, 255, .125), inset 0 1px 0 rgba(255, 255, 255, .2), 0 1px 2px rgba(0, 0, 0, .05);
} }
.cms-menu .cms-subsites .dropdown span{ .cms-menu .cms-subsites .dropdown span {
padding-left:5px; padding-left: 5px;
} color: black;
.cms-subsites .chzn-container-single .chzn-single div b{
background: url(../images/chosen-sprite-light.png) 3px 0 no-repeat;
}
.cms-subsites .chzn-container .chzn-drop{
padding-left:5px;
background:#213557;
border: 1px solid #152338;
border-top:0;
color:#fff;
-webkit-box-shadow: inset 1px 0 0 rgba(255,255,255,.125);
box-shadow: inset 1px 0 0 rgba(255,255,255,.125);
} }
.cms-subsites .chzn-container-single .chzn-single div b {
background: url(../images/chosen-sprite-light.png) 3px 0 no-repeat;
}
.cms-subsites .chzn-container .chzn-drop {
padding-left: 5px;
background: #213557;
border: 1px solid #152338;
border-top: 0;
color: #fff;
-webkit-box-shadow: inset 1px 0 0 rgba(255, 255, 255, .125);
box-shadow: inset 1px 0 0 rgba(255, 255, 255, .125);
}
#AddSubsiteLink { #AddSubsiteLink {
display: block; display: block;
font-size: 80%; font-size: 80%;
margin-left: 3px; margin-left: 3px;
} }
#Form_AddSubsiteForm .field { #Form_AddSubsiteForm .field {
margin-left: 100px; margin-left: 100px;
} }
#Form_AddSubsiteForm label.left { #Form_AddSubsiteForm label.left {
float: left; float: left;
width: 100px; width: 100px;
margin-left: -100px; margin-left: -100px;
} }
body.SubsiteAdmin .right form #URL .fieldgroup * { body.SubsiteAdmin .right form #URL .fieldgroup * {
font-size: 11px; font-size: 11px;
} }
.cms-add-form #PageType li .class-SubsitesVirtualPage { .cms-add-form #PageType li .class-SubsitesVirtualPage {
background-position: 0 -32px !important; background-position: 0 -32px !important;
} }
.subsites-move-dropdown{ .subsites-move-dropdown {
display:none; display: none;
} }
#Root_DetailsView .subsites-move-dropdown, #Root_DetailsView .subsites-move-dropdown,
#Form_ItemEditForm .subsites-move-dropdown { #Form_ItemEditForm .subsites-move-dropdown {
display:block; display: block;
} }

7
phpunit.xml.dist Normal file
View File

@ -0,0 +1,7 @@
<phpunit bootstrap="framework/tests/bootstrap.php" colors="true">
<testsuite name="Default">
<directory>tests/php</directory>
</testsuite>
</phpunit>

View File

@ -0,0 +1,22 @@
<div class="cms-mobile-menu-toggle-wrapper"></div>
<div class="fill-height cms-menu cms-panel cms-panel-layout" id="cms-menu" data-layout-type="border" aria-expanded="false">
<div class="cms-menu__header">
<% include SilverStripe\\Admin\\LeftAndMain_MenuLogo %>
<% include SilverStripe\\Admin\\LeftAndMain_MenuStatus %>
<% if $ListSubsites %>
<% include SubsiteList %>
<% end_if %>
</div>
<div class="flexbox-area-grow panel--scrollable panel--triple-toolbar cms-panel-content">
<% include SilverStripe\\Admin\\LeftAndMain_MenuList %>
</div>
<div class="toolbar toolbar--south cms-panel-toggle">
<% include SilverStripe\\Admin\\LeftAndMain_MenuToggle %>
</div>
</div>
<button class="fill-height fill-width cms-menu-mobile-overlay" aria-controls="cms-menu" aria-expanded="false"></button>

View File

@ -1,28 +0,0 @@
<?php
class GroupSubsitesTest extends BaseSubsiteTest
{
public static $fixture_file = 'subsites/tests/SubsiteTest.yml';
protected $requireDefaultRecordsFrom = array('GroupSubsites');
public function testTrivialFeatures()
{
$this->assertTrue(is_array(singleton('GroupSubsites')->extraStatics()));
$this->assertTrue(is_array(singleton('GroupSubsites')->providePermissions()));
$this->assertTrue(singleton('Group')->getCMSFields() instanceof FieldList);
}
public function testAlternateTreeTitle()
{
$group = new Group();
$group->Title = 'The A Team';
$group->AccessAllSubsites = true;
$this->assertEquals($group->getTreeTitle(), 'The A Team <i>(global group)</i>');
$group->AccessAllSubsites = false;
$group->write();
$group->Subsites()->add($this->objFromFixture('Subsite', 'domaintest1'));
$group->Subsites()->add($this->objFromFixture('Subsite', 'domaintest2'));
$this->assertEquals($group->getTreeTitle(), 'The A Team <i>(Test 1, Test 2)</i>');
}
}

View File

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

View File

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

View File

@ -1,51 +0,0 @@
<?php
class SubsiteAdminTest extends BaseSubsiteTest
{
public static $fixture_file = 'subsites/tests/SubsiteTest.yml';
public function adminLoggedInSession()
{
return new Session(array(
'loggedInAs' => $this->idFromFixture('Member', 'admin')
));
}
/**
* Test generation of the view
*/
public function testBasicView()
{
Subsite::$write_hostmap = false;
$subsite1ID = $this->objFromFixture('Subsite', 'domaintest1')->ID;
// Open the admin area logged in as admin
$response1 = Director::test('admin/subsites/', null, $this->adminLoggedInSession());
// 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());
$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");
}
/**
* 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.
*/
public function testMainsiteAdminCanAccessAllSubsites()
{
$member = $this->objFromFixture('Member', 'admin');
Session::set("loggedInAs", $member->ID);
$cmsMain = new CMSMain();
foreach ($cmsMain->Subsites() as $subsite) {
$ids[$subsite->ID] = true;
}
$this->assertArrayHasKey(0, $ids, "Main site accessible");
$this->assertArrayHasKey($this->idFromFixture('Subsite', 'main'), $ids, "Site with no groups inaccesible");
$this->assertArrayHasKey($this->idFromFixture('Subsite', 'subsite1'), $ids, "Subsite1 Template inaccessible");
$this->assertArrayHasKey($this->idFromFixture('Subsite', 'subsite2'), $ids, "Subsite2 Template inaccessible");
}
}

View File

@ -1,18 +1,24 @@
<?php <?php
namespace Subsites\Test\Behaviour; namespace SilverStripe\Subsites\Tests\Behaviour;
if (!class_exists('SilverStripe\BehatExtension\Context\SilverStripeContext')) { if (!class_exists('SilverStripe\BehatExtension\Context\SilverStripeContext')) {
return; return;
} }
use SilverStripe\BehatExtension\Context\SilverStripeContext;
use SilverStripe\BehatExtension\Context\BasicContext; use SilverStripe\BehatExtension\Context\BasicContext;
use SilverStripe\BehatExtension\Context\LoginContext;
use SilverStripe\BehatExtension\Context\FixtureContext; use SilverStripe\BehatExtension\Context\FixtureContext;
use SilverStripe\BehatExtension\Context\LoginContext;
use SilverStripe\BehatExtension\Context\SilverStripeContext;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Core\ClassInfo;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Dev\BehatFixtureFactory;
use SilverStripe\Dev\FixtureBlueprint;
use SilverStripe\Dev\FixtureFactory;
use SilverStripe\Framework\Test\Behaviour\CmsFormsContext; use SilverStripe\Framework\Test\Behaviour\CmsFormsContext;
use SilverStripe\Framework\Test\Behaviour\CmsUiContext; use SilverStripe\Framework\Test\Behaviour\CmsUiContext;
use SilverStripe\Cms\Test\Behaviour; use SilverStripe\Security\Member;
// PHPUnit // PHPUnit
require_once 'PHPUnit/Autoload.php'; require_once 'PHPUnit/Autoload.php';
@ -52,19 +58,19 @@ 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('FixtureBlueprint', 'Member'); $blueprint = Injector::inst()->create(FixtureBlueprint::class, Member::class);
$blueprint->addCallback('beforeCreate', function ($identifier, &$data, &$fixtures) { $blueprint->addCallback('beforeCreate', function ($identifier, &$data, &$fixtures) {
if (!isset($data['FirstName'])) { if (!isset($data['FirstName'])) {
$data['FirstName'] = $identifier; $data['FirstName'] = $identifier;
} }
}); });
$factory->define('Member', $blueprint); $factory->define(Member::class, $blueprint);
// Auto-publish pages // Auto-publish pages
foreach (\ClassInfo::subclassesFor('SiteTree') as $id => $class) { foreach (ClassInfo::subclassesFor(SiteTree::class) as $id => $class) {
$blueprint = \Injector::inst()->create('FixtureBlueprint', $class); $blueprint = Injector::inst()->create(FixtureBlueprint::class, $class);
$blueprint->addCallback('afterCreate', function ($obj, $identifier, &$data, &$fixtures) { $blueprint->addCallback('afterCreate', function ($obj, $identifier, &$data, &$fixtures) {
$obj->publish('Stage', 'Live'); $obj->copyVersionToStage('Stage', 'Live');
}); });
$factory->define($class, $blueprint); $factory->define($class, $blueprint);
} }
@ -73,7 +79,6 @@ class FeatureContext extends SilverStripeContext
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']);
} }
@ -85,7 +90,7 @@ class FeatureContext extends SilverStripeContext
public function getFixtureFactory() public function getFixtureFactory()
{ {
if (!$this->fixtureFactory) { if (!$this->fixtureFactory) {
$this->fixtureFactory = \Injector::inst()->create('BehatFixtureFactory'); $this->fixtureFactory = Injector::inst()->create(BehatFixtureFactory::class);
} }
return $this->fixtureFactory; return $this->fixtureFactory;

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"

View File

@ -1,16 +1,26 @@
<?php <?php
namespace SilverStripe\Subsites\Tests;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\Subsites\Model\Subsite;
class BaseSubsiteTest extends SapphireTest class BaseSubsiteTest extends SapphireTest
{ {
public function setUp() public function setUp()
{ {
parent::setUp(); parent::setUp();
Subsite::$use_session_subsiteid = true; Subsite::$use_session_subsiteid = true;
Subsite::$force_subsite = null; Subsite::$force_subsite = null;
} }
/** /**
* Avoid subsites filtering on fixture fetching. * Avoid subsites filtering on fixture fetching.
* @param string $class
* @param string $id
* @return \SilverStripe\ORM\DataObject
*/ */
public function objFromFixture($class, $id) public function objFromFixture($class, $id)
{ {

View File

@ -1,43 +1,53 @@
<?php <?php
namespace SilverStripe\Subsites\Tests;
use SilverStripe\Assets\File;
use SilverStripe\Assets\Folder;
use SilverStripe\Forms\FieldList;
use SilverStripe\Security\Member;
use SilverStripe\Subsites\Extensions\FileSubsites;
use SilverStripe\Subsites\Model\Subsite;
class FileSubsitesTest extends BaseSubsiteTest class FileSubsitesTest extends BaseSubsiteTest
{ {
public static $fixture_file = 'subsites/tests/SubsiteTest.yml';
public static $fixture_file = 'subsites/tests/php/SubsiteTest.yml';
/** /**
* Disable other file extensions * Disable other file extensions
* *
* @var array * @var array
*/ */
protected $illegalExtensions = array( protected $illegalExtensions = [
'File' => array( 'File' => [
'SecureFileExtension', 'SecureFileExtension',
'VersionedFileExtension' 'VersionedFileExtension'
), ],
'SiteTree' => array( 'SiteTree' => [
'Translatable', 'Translatable',
) ]
); ];
public function testTrivialFeatures() public function testTrivialFeatures()
{ {
$this->assertTrue(is_array(singleton('FileSubsites')->extraStatics())); $this->assertTrue(is_array(singleton(FileSubsites::class)->extraStatics()));
$file = new File(); $file = new File();
$file->Name = 'FileTitle'; $file->Name = 'FileTitle';
$file->Title = 'FileTitle'; $file->Title = 'FileTitle';
$this->assertEquals(' * FileTitle', $file->alternateTreeTitle()); $this->assertEquals(' * FileTitle', $file->alternateTreeTitle());
$file->SubsiteID = $this->objFromFixture('Subsite', 'domaintest1')->ID; $file->SubsiteID = $this->objFromFixture(Subsite::class, 'domaintest1')->ID;
$this->assertEquals('FileTitle', $file->getTreeTitle()); $this->assertEquals('FileTitle', $file->getTreeTitle());
$this->assertTrue(singleton('Folder')->getCMSFields() instanceof FieldList); $this->assertInstanceOf(FieldList::class, singleton(Folder::class)->getCMSFields());
Subsite::changeSubsite(1); Subsite::changeSubsite(1);
$this->assertEquals($file->cacheKeyComponent(), 'subsite-1'); $this->assertEquals('subsite-1', $file->getExtensionInstance(FileSubsites::class)->cacheKeyComponent());
} }
public function testWritingSubsiteID() public function testWritingSubsiteID()
{ {
$this->objFromFixture('Member', 'admin')->logIn(); $this->objFromFixture(Member::class, 'admin')->logIn();
$subsite = $this->objFromFixture('Subsite', '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);
@ -74,14 +84,14 @@ class FileSubsitesTest extends BaseSubsiteTest
public function testSubsitesFolderDropdown() public function testSubsitesFolderDropdown()
{ {
$this->objFromFixture('Member', 'admin')->logIn(); $this->objFromFixture(Member::class, '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',
'Subsite1 Template', 'Subsite1 Template',
'Subsite2 Template', 'Subsite2 Template',
@ -91,6 +101,6 @@ class FileSubsitesTest extends BaseSubsiteTest
'Test 3', 'Test 3',
'Test Non-SSL', 'Test Non-SSL',
'Test SSL', 'Test SSL',
), array_values($source)); ], array_values($source));
} }
} }

View File

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

View File

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

View File

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

View File

@ -1,33 +1,55 @@
<?php <?php
namespace SilverStripe\Subsites\Tests;
use Page;
use SilverStripe\CMS\Controllers\CMSMain;
use SilverStripe\CMS\Controllers\ModelAsController;
use SilverStripe\CMS\Model\ErrorPage;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Control\Director;
use SilverStripe\Control\Session;
use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Convert;
use SilverStripe\Dev\TestOnly;
use SilverStripe\Forms\FieldList;
use SilverStripe\Security\Member;
use SilverStripe\SiteConfig\SiteConfig;
use SilverStripe\Subsites\Extensions\SiteTreeSubsites;
use SilverStripe\Subsites\Model\Subsite;
use SilverStripe\Subsites\Pages\SubsitesVirtualPage;
use SilverStripe\Versioned\Versioned;
use SilverStripe\View\SSViewer;
class SiteTreeSubsitesTest extends BaseSubsiteTest class SiteTreeSubsitesTest extends BaseSubsiteTest
{ {
protected static $fixture_file = 'subsites/tests/SubsiteTest.yml';
protected $extraDataObjects = array( public static $fixture_file = 'subsites/tests/php/SubsiteTest.yml';
protected $extraDataObjects = [
'SiteTreeSubsitesTest_ClassA', 'SiteTreeSubsitesTest_ClassA',
'SiteTreeSubsitesTest_ClassB', 'SiteTreeSubsitesTest_ClassB',
'SiteTreeSubsitesTest_ErrorPage' 'SiteTreeSubsitesTest_ErrorPage'
); ];
protected $illegalExtensions = array( protected $illegalExtensions = [
'SiteTree' => array('Translatable') SiteTree::class => ['Translatable']
); ];
public function testPagesInDifferentSubsitesCanShareURLSegment() public function testPagesInDifferentSubsitesCanShareURLSegment()
{ {
$subsiteMain = $this->objFromFixture('Subsite', 'main'); $subsiteMain = $this->objFromFixture(Subsite::class, 'main');
$subsite1 = $this->objFromFixture('Subsite', 'subsite1'); $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->copyVersionToStage('Stage', 'Live');
$pageMainOther = new SiteTree(); $pageMainOther = new SiteTree();
$pageMainOther->URLSegment = 'testpage'; $pageMainOther->URLSegment = 'testpage';
$pageMainOther->write(); $pageMainOther->write();
$pageMainOther->publish('Stage', 'Live'); $pageMainOther->copyVersionToStage('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'
@ -38,7 +60,7 @@ class SiteTreeSubsitesTest extends BaseSubsiteTest
$pageSubsite1 = new SiteTree(); $pageSubsite1 = new SiteTree();
$pageSubsite1->URLSegment = 'testpage'; $pageSubsite1->URLSegment = 'testpage';
$pageSubsite1->write(); $pageSubsite1->write();
$pageSubsite1->publish('Stage', 'Live'); $pageSubsite1->copyVersionToStage('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'
@ -47,37 +69,40 @@ class SiteTreeSubsitesTest extends BaseSubsiteTest
public function testBasicSanity() public function testBasicSanity()
{ {
$this->assertTrue(singleton('SiteTree')->getSiteConfig() instanceof SiteConfig); $this->assertInstanceOf(SiteConfig::class, singleton(SiteTree::class)->getSiteConfig());
// The following assert is breaking in Translatable. // The following assert is breaking in Translatable.
$this->assertTrue(singleton('SiteTree')->getCMSFields() instanceof FieldList); $this->assertInstanceOf(FieldList::class, singleton(SiteTree::class)->getCMSFields());
$this->assertTrue(singleton('SubsitesVirtualPage')->getCMSFields() instanceof FieldList); $this->assertInstanceOf(FieldList::class, singleton(SubsitesVirtualPage::class)->getCMSFields());
$this->assertTrue(is_array(singleton('SiteTreeSubsites')->extraStatics())); $this->assertTrue(is_array(singleton(SiteTreeSubsites::class)->extraStatics()));
} }
public function testErrorPageLocations() public function testErrorPageLocations()
{ {
$subsite1 = $this->objFromFixture('Subsite', 'domaintest1'); $this->markTestSkipped('needs refactoring');
$subsite1 = $this->objFromFixture(Subsite::class, 'domaintest1');
Subsite::changeSubsite($subsite1->ID); Subsite::changeSubsite($subsite1->ID);
$path = SiteTreeSubsitesTest_ErrorPage::get_error_filename_spy(500); $path = ErrorPage::get_filepath_for_errorcode(500);
$expected_path = 'error-500-'.$subsite1->domain().'.html'; $static_path = Config::inst()->get(ErrorPage::class, 'static_filepath');
$expected_path = $static_path . '/error-500-' . $subsite1->domain() . '.html';
$this->assertEquals($expected_path, $path); $this->assertEquals($expected_path, $path);
} }
public function testCanEditSiteTree() public function testCanEditSiteTree()
{ {
$admin = $this->objFromFixture('Member', 'admin'); $admin = $this->objFromFixture(Member::class, 'admin');
$subsite1member = $this->objFromFixture('Member', 'subsite1member'); $subsite1member = $this->objFromFixture(Member::class, 'subsite1member');
$subsite2member = $this->objFromFixture('Member', 'subsite2member'); $subsite2member = $this->objFromFixture(Member::class, 'subsite2member');
$mainpage = $this->objFromFixture('Page', 'home'); $mainpage = $this->objFromFixture('Page', 'home');
$subsite1page = $this->objFromFixture('Page', 'subsite1_home'); $subsite1page = $this->objFromFixture('Page', 'subsite1_home');
$subsite2page = $this->objFromFixture('Page', 'subsite2_home'); $subsite2page = $this->objFromFixture('Page', 'subsite2_home');
$subsite1 = $this->objFromFixture('Subsite', 'subsite1'); $subsite1 = $this->objFromFixture(Subsite::class, 'subsite1');
$subsite2 = $this->objFromFixture('Subsite', 'subsite2'); $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'
@ -86,13 +111,13 @@ class SiteTreeSubsitesTest extends BaseSubsiteTest
// @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'
@ -112,16 +137,16 @@ class SiteTreeSubsitesTest extends BaseSubsiteTest
public function testTwoPagesWithSameURLOnDifferentSubsites() public function testTwoPagesWithSameURLOnDifferentSubsites()
{ {
// Set up a couple of pages with the same URL on different subsites // Set up a couple of pages with the same URL on different subsites
$s1 = $this->objFromFixture('Subsite', 'domaintest1'); $s1 = $this->objFromFixture(Subsite::class, 'domaintest1');
$s2 = $this->objFromFixture('Subsite', 'domaintest2'); $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();
@ -139,38 +164,38 @@ class SiteTreeSubsitesTest extends BaseSubsiteTest
public function testPageTypesBlacklistInClassDropdown() public function testPageTypesBlacklistInClassDropdown()
{ {
$editor = $this->objFromFixture('Member', 'editor'); $editor = $this->objFromFixture(Member::class, 'editor');
Session::set("loggedInAs", $editor->ID); Session::set('loggedInAs', $editor->ID);
$s1 = $this->objFromFixture('Subsite', 'domaintest1'); $s1 = $this->objFromFixture(Subsite::class, 'domaintest1');
$s2 = $this->objFromFixture('Subsite', 'domaintest2'); $s2 = $this->objFromFixture(Subsite::class, 'domaintest2');
$page = singleton('SiteTree'); $page = singleton(SiteTree::class);
$s1->PageTypeBlacklist = 'SiteTreeSubsitesTest_ClassA,ErrorPage'; $s1->PageTypeBlacklist = implode(',', [SiteTreeSubsitesTest_ClassA::class, ErrorPage::class]);
$s1->write(); $s1->write();
Subsite::changeSubsite($s1); Subsite::changeSubsite($s1);
$settingsFields = $page->getSettingsFields()->dataFieldByName('ClassName')->getSource(); $settingsFields = $page->getSettingsFields()->dataFieldByName('ClassName')->getSource();
$this->assertArrayNotHasKey('ErrorPage', $this->assertArrayNotHasKey(ErrorPage::class,
$settingsFields $settingsFields
); );
$this->assertArrayNotHasKey('SiteTreeSubsitesTest_ClassA', $this->assertArrayNotHasKey(SiteTreeSubsitesTest_ClassA::class,
$settingsFields $settingsFields
); );
$this->assertArrayHasKey('SiteTreeSubsitesTest_ClassB', $this->assertArrayHasKey(SiteTreeSubsitesTest_ClassB::class,
$settingsFields $settingsFields
); );
Subsite::changeSubsite($s2); Subsite::changeSubsite($s2);
$settingsFields = $page->getSettingsFields()->dataFieldByName('ClassName')->getSource(); $settingsFields = $page->getSettingsFields()->dataFieldByName('ClassName')->getSource();
$this->assertArrayHasKey('ErrorPage', $this->assertArrayHasKey(ErrorPage::class,
$settingsFields $settingsFields
); );
$this->assertArrayHasKey('SiteTreeSubsitesTest_ClassA', $this->assertArrayHasKey(SiteTreeSubsitesTest_ClassA::class,
$settingsFields $settingsFields
); );
$this->assertArrayHasKey('SiteTreeSubsitesTest_ClassB', $this->assertArrayHasKey(SiteTreeSubsitesTest_ClassB::class,
$settingsFields $settingsFields
); );
} }
@ -178,10 +203,10 @@ class SiteTreeSubsitesTest extends BaseSubsiteTest
public function testCopyToSubsite() public function testCopyToSubsite()
{ {
// Remove baseurl if testing in subdir // Remove baseurl if testing in subdir
Config::inst()->update('Director', 'alternate_base_url', '/'); Config::modify()->set(Director::class, 'alternate_base_url', '/');
/** @var Subsite $otherSubsite */ /** @var Subsite $otherSubsite */
$otherSubsite = $this->objFromFixture('Subsite', 'subsite1'); $otherSubsite = $this->objFromFixture(Subsite::class, 'subsite1');
$staffPage = $this->objFromFixture('Page', 'staff'); // nested page $staffPage = $this->objFromFixture('Page', 'staff'); // nested page
$contactPage = $this->objFromFixture('Page', 'contact'); // top level page $contactPage = $this->objFromFixture('Page', 'contact'); // top level page
@ -199,36 +224,36 @@ class SiteTreeSubsitesTest extends BaseSubsiteTest
// Staff is shifted to top level and given a unique url segment // Staff is shifted to top level and given a unique url segment
$domain = $otherSubsite->domain(); $domain = $otherSubsite->domain();
$this->assertEquals('http://'.$domain.'/staff-2/', $staffPage2->AbsoluteLink()); $this->assertEquals('http://' . $domain . '/staff-2/', $staffPage2->AbsoluteLink());
$this->assertEquals('http://'.$domain.'/contact-us-2/', $contactPage2->AbsoluteLink()); $this->assertEquals('http://' . $domain . '/contact-us-2/', $contactPage2->AbsoluteLink());
} }
public function testPageTypesBlacklistInCMSMain() public function testPageTypesBlacklistInCMSMain()
{ {
$editor = $this->objFromFixture('Member', 'editor'); $editor = $this->objFromFixture(Member::class, 'editor');
Session::set("loggedInAs", $editor->ID); Session::set('loggedInAs', $editor->ID);
$cmsmain = new CMSMain(); $cmsmain = new CMSMain();
$s1 = $this->objFromFixture('Subsite', 'domaintest1'); $s1 = $this->objFromFixture(Subsite::class, 'domaintest1');
$s2 = $this->objFromFixture('Subsite', 'domaintest2'); $s2 = $this->objFromFixture(Subsite::class, 'domaintest2');
$s1->PageTypeBlacklist = 'SiteTreeSubsitesTest_ClassA,ErrorPage'; $s1->PageTypeBlacklist = implode(',', [SiteTreeSubsitesTest_ClassA::class, ErrorPage::class]);
$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('ErrorPage', $classes); static::assertContains(ErrorPage::class, $classes);
$this->assertContains('SiteTreeSubsitesTest_ClassA', $classes); static::assertContains(SiteTreeSubsitesTest_ClassA::class, $classes);
$this->assertNotContains('SiteTreeSubsitesTest_ClassB', $classes); static::assertNotContains(SiteTreeSubsitesTest_ClassB::class, $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('ErrorPage', $classes); static::assertNotContains(ErrorPage::class, $classes);
$this->assertNotContains('SiteTreeSubsitesTest_ClassA', $classes); static::assertNotContains(SiteTreeSubsitesTest_ClassA::class, $classes);
$this->assertNotContains('SiteTreeSubsitesTest_ClassB', $classes); static::assertNotContains(SiteTreeSubsitesTest_ClassB::class, $classes);
} }
/** /**
@ -239,7 +264,7 @@ class SiteTreeSubsitesTest extends BaseSubsiteTest
$this->logInWithPermission('ADMIN'); $this->logInWithPermission('ADMIN');
// Saving existing page in the same subsite doesn't change urls // Saving existing page in the same subsite doesn't change urls
$mainHome = $this->objFromFixture('Page', 'home'); $mainHome = $this->objFromFixture('Page', 'home');
$mainSubsiteID = $this->idFromFixture('Subsite', 'main'); $mainSubsiteID = $this->idFromFixture(Subsite::class, 'main');
Subsite::changeSubsite($mainSubsiteID); Subsite::changeSubsite($mainSubsiteID);
$mainHome->Content = '<p>Some new content</p>'; $mainHome->Content = '<p>Some new content</p>';
$mainHome->write(); $mainHome->write();
@ -255,11 +280,12 @@ class SiteTreeSubsitesTest extends BaseSubsiteTest
$subsite1Home->write(); $subsite1Home->write();
$this->assertEquals('home', $subsite1Home->URLSegment); $this->assertEquals('home', $subsite1Home->URLSegment);
$subsite1Home->doPublish(); $subsite1Home->doPublish();
$subsite1HomeLive = Versioned::get_one_by_stage('Page', 'Live', sprintf('"SiteTree"."ID" = \'%d\'', $subsite1Home->ID)); $subsite1HomeLive = Versioned::get_one_by_stage('Page', 'Live',
sprintf('"SiteTree"."ID" = \'%d\'', $subsite1Home->ID));
$this->assertEquals('home', $subsite1HomeLive->URLSegment); $this->assertEquals('home', $subsite1HomeLive->URLSegment);
// Creating a new page in a subsite doesn't conflict with urls in other subsites // Creating a new page in a subsite doesn't conflict with urls in other subsites
$subsite1ID = $this->idFromFixture('Subsite', 'subsite1'); $subsite1ID = $this->idFromFixture(Subsite::class, 'subsite1');
Subsite::changeSubsite($subsite1ID); Subsite::changeSubsite($subsite1ID);
$subsite1NewPage = new Page(); $subsite1NewPage = new Page();
$subsite1NewPage->SubsiteID = $subsite1ID; $subsite1NewPage->SubsiteID = $subsite1ID;
@ -268,7 +294,8 @@ class SiteTreeSubsitesTest extends BaseSubsiteTest
$subsite1NewPage->write(); $subsite1NewPage->write();
$this->assertEquals('important-page', $subsite1NewPage->URLSegment); $this->assertEquals('important-page', $subsite1NewPage->URLSegment);
$subsite1NewPage->doPublish(); $subsite1NewPage->doPublish();
$subsite1NewPageLive = Versioned::get_one_by_stage('Page', 'Live', sprintf('"SiteTree"."ID" = \'%d\'', $subsite1NewPage->ID)); $subsite1NewPageLive = Versioned::get_one_by_stage('Page', 'Live',
sprintf('"SiteTree"."ID" = \'%d\'', $subsite1NewPage->ID));
$this->assertEquals('important-page', $subsite1NewPageLive->URLSegment); $this->assertEquals('important-page', $subsite1NewPageLive->URLSegment);
// Creating a new page in a subsite DOES conflict with urls in the same subsite // Creating a new page in a subsite DOES conflict with urls in the same subsite
@ -279,7 +306,8 @@ class SiteTreeSubsitesTest extends BaseSubsiteTest
$subsite1NewPage2->write(); $subsite1NewPage2->write();
$this->assertEquals('important-page-2', $subsite1NewPage2->URLSegment); $this->assertEquals('important-page-2', $subsite1NewPage2->URLSegment);
$subsite1NewPage2->doPublish(); $subsite1NewPage2->doPublish();
$subsite1NewPage2Live = Versioned::get_one_by_stage('Page', 'Live', sprintf('"SiteTree"."ID" = \'%d\'', $subsite1NewPage2->ID)); $subsite1NewPage2Live = Versioned::get_one_by_stage('Page', 'Live',
sprintf('"SiteTree"."ID" = \'%d\'', $subsite1NewPage2->ID));
$this->assertEquals('important-page-2', $subsite1NewPage2Live->URLSegment); $this->assertEquals('important-page-2', $subsite1NewPage2Live->URLSegment);
// Original page is left un-modified // Original page is left un-modified
@ -291,25 +319,55 @@ class SiteTreeSubsitesTest extends BaseSubsiteTest
$this->assertEquals('important-page', $mainSubsiteImportantPage->URLSegment); $this->assertEquals('important-page', $mainSubsiteImportantPage->URLSegment);
} }
function testCopySubsiteWithChildren() { public function testCopySubsiteWithChildren()
{
$page = $this->objFromFixture('Page', 'about'); $page = $this->objFromFixture('Page', 'about');
$newSubsite = $this->objFromFixture('Subsite', 'subsite1'); $newSubsite = $this->objFromFixture(Subsite::class, 'subsite1');
$moved = $page->duplicateToSubsite($newSubsite->ID, true); $moved = $page->duplicateToSubsite($newSubsite->ID, true);
$this->assertEquals($moved->SubsiteID, $newSubsite->ID, 'Ensure returned records are on new subsite'); $this->assertEquals($moved->SubsiteID, $newSubsite->ID, 'Ensure returned records are on new subsite');
$this->assertEquals($moved->AllChildren()->count(), $page->AllChildren()->count(), 'All pages are copied across'); $this->assertEquals($moved->AllChildren()->count(), $page->AllChildren()->count(),
'All pages are copied across');
} }
function testCopySubsiteWithoutChildren() { public function testCopySubsiteWithoutChildren()
{
$page = $this->objFromFixture('Page', 'about'); $page = $this->objFromFixture('Page', 'about');
$newSubsite = $this->objFromFixture('Subsite', 'subsite2'); $newSubsite = $this->objFromFixture(Subsite::class, 'subsite2');
$moved = $page->duplicateToSubsite($newSubsite->ID, false); $moved = $page->duplicateToSubsite($newSubsite->ID, false);
$this->assertEquals($moved->SubsiteID, $newSubsite->ID, 'Ensure returned records are on new subsite'); $this->assertEquals($moved->SubsiteID, $newSubsite->ID, 'Ensure returned records are on new subsite');
$this->assertEquals($moved->AllChildren()->count(), 0, 'All pages are copied across'); $this->assertEquals($moved->AllChildren()->count(), 0, 'All pages are copied across');
} }
/**
* @todo: move to a functional test?
*/
public function testIfSubsiteThemeIsSetToThemeList()
{
$defaultThemes = ['default'];
SSViewer::set_themes($defaultThemes);
$subsitePage = $this->objFromFixture(Page::class, 'home');
Subsite::changeSubsite($subsitePage->SubsiteID);
$controller = ModelAsController::controller_for($subsitePage);
SiteTree::singleton()->extend('contentcontrollerInit', $controller);
$this->assertEquals(SSViewer::get_themes(), $defaultThemes,
'Themes should not be modified when Subsite has no theme defined');
$pageWithTheme = $this->objFromFixture(Page::class, 'subsite1_home');
Subsite::changeSubsite($pageWithTheme->SubsiteID);
$controller = ModelAsController::controller_for($pageWithTheme);
SiteTree::singleton()->extend('contentcontrollerInit', $controller);
$subsiteTheme = $pageWithTheme->Subsite()->Theme;
$this->assertEquals(SSViewer::get_themes(), array_merge([$subsiteTheme], $defaultThemes),
'Themes should be modified when Subsite has theme defined');
}
} }
class SiteTreeSubsitesTest_ClassA extends SiteTree implements TestOnly class SiteTreeSubsitesTest_ClassA extends SiteTree implements TestOnly
{ {
} }
@ -320,14 +378,14 @@ class SiteTreeSubsitesTest_ClassB extends SiteTree implements TestOnly
class SiteTreeSubsitesTest_ErrorPage extends ErrorPage implements TestOnly class SiteTreeSubsitesTest_ErrorPage extends ErrorPage implements TestOnly
{ {
/** /**
* Helper method to call protected members * Helper method to call protected members
* *
* @param int $statusCode * @param int $statusCode
* @return string * @return string
*/ */
public static function get_error_filename_spy($statusCode) public static function get_error_filename_spy($statusCode)
{ {
return self::get_error_filename($statusCode); return self::get_error_filename($statusCode);
} }
} }

View File

@ -1,14 +1,26 @@
<?php <?php
namespace SilverStripe\Subsites\Tests;
use SilverStripe\CMS\Controllers\CMSPageEditController;
use SilverStripe\Control\Session;
use SilverStripe\Core\Config\Config;
use SilverStripe\Dev\FunctionalTest;
use SilverStripe\Security\Member;
use SilverStripe\Subsites\Controller\SubsiteXHRController;
use SilverStripe\Subsites\Model\Subsite;
class SubsiteAdminFunctionalTest extends FunctionalTest class SubsiteAdminFunctionalTest extends FunctionalTest
{ {
public static $fixture_file = 'subsites/tests/SubsiteTest.yml'; public static $fixture_file = 'subsites/tests/php/SubsiteTest.yml';
public static $use_draft_site = true; public 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.
* @param $url
* @return \SilverStripe\Control\HTTPResponse
*/ */
public function getAndFollowAll($url) public function getAndFollowAll($url)
{ {
@ -29,7 +41,7 @@ class SubsiteAdminFunctionalTest extends FunctionalTest
$response = $this->getAndFollowAll('admin/pages/?SubsiteID=0'); $response = $this->getAndFollowAll('admin/pages/?SubsiteID=0');
$this->assertRegExp('#^Security/login.*#', $this->mainSession->lastUrl(), 'Admin is disallowed'); $this->assertRegExp('#^Security/login.*#', $this->mainSession->lastUrl(), 'Admin is disallowed');
$subsite1 = $this->objFromFixture('Subsite', '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');
@ -43,50 +55,53 @@ class SubsiteAdminFunctionalTest extends FunctionalTest
*/ */
public function testAdminCanAccessAllSubsites() public function testAdminCanAccessAllSubsites()
{ {
$member = $this->objFromFixture('Member', 'admin'); $member = $this->objFromFixture(Member::class, 'admin');
Session::set("loggedInAs", $member->ID); 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', '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'); $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');
} }
public function testAdminIsRedirectedToObjectsSubsite() public function testAdminIsRedirectedToObjectsSubsite()
{ {
$member = $this->objFromFixture('Member', 'admin'); $member = $this->objFromFixture(Member::class, 'admin');
Session::set("loggedInAs", $member->ID); 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::nest();
Config::inst()->update('CMSPageEditController', 'treats_subsite_0_as_global', false); Config::modify()->set(CMSPageEditController::class, 'treats_subsite_0_as_global', false);
Subsite::changeSubsite(0); Subsite::changeSubsite(0);
$this->getAndFollowAll("admin/pages/edit/show/$subsite1Home->ID"); $this->getAndFollowAll("admin/pages/edit/show/$subsite1Home->ID");
$this->assertEquals(Subsite::currentSubsiteID(), $subsite1Home->SubsiteID, 'Loading an object switches the subsite'); $this->assertEquals(Subsite::currentSubsiteID(), $subsite1Home->SubsiteID,
$this->assertRegExp("#^admin/pages.*#", $this->mainSession->lastUrl(), 'Lands on the correct section'); 'Loading an object switches the subsite');
$this->assertRegExp('#^admin/pages.*#', $this->mainSession->lastUrl(), 'Lands on the correct section');
Config::inst()->update('CMSPageEditController', 'treats_subsite_0_as_global', true); Config::modify()->set(CMSPageEditController::class, 'treats_subsite_0_as_global', true);
Subsite::changeSubsite(0); Subsite::changeSubsite(0);
$this->getAndFollowAll("admin/pages/edit/show/$subsite1Home->ID"); $this->getAndFollowAll("admin/pages/edit/show/$subsite1Home->ID");
$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->assertEquals(Subsite::currentSubsiteID(), $subsite1Home->SubsiteID,
$this->assertRegExp("#^admin/pages.*#", $this->mainSession->lastUrl(), 'Lands on the correct section'); '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::unnest();
} }
/** /**
@ -95,14 +110,14 @@ class SubsiteAdminFunctionalTest extends FunctionalTest
*/ */
public function testEditorCanAccessAllSubsites() public function testEditorCanAccessAllSubsites()
{ {
$member = $this->objFromFixture('Member', 'editor'); $member = $this->objFromFixture(Member::class, 'editor');
Session::set("loggedInAs", $member->ID); 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', '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');
@ -117,10 +132,10 @@ class SubsiteAdminFunctionalTest extends FunctionalTest
*/ */
public function testSubsiteAdmin() public function testSubsiteAdmin()
{ {
$member = $this->objFromFixture('Member', 'subsite1member'); $member = $this->objFromFixture(Member::class, 'subsite1member');
Session::set("loggedInAs", $member->ID); Session::set('loggedInAs', $member->ID);
$subsite1 = $this->objFromFixture('Subsite', '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}");
@ -134,16 +149,16 @@ class SubsiteAdminFunctionalTest extends FunctionalTest
'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'); $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

@ -0,0 +1,67 @@
<?php
namespace SilverStripe\Subsites\Tests;
use SilverStripe\CMS\Controllers\CMSMain;
use SilverStripe\Control\Director;
use SilverStripe\Control\Session;
use SilverStripe\Security\Member;
use SilverStripe\Subsites\Model\Subsite;
class SubsiteAdminTest extends BaseSubsiteTest
{
public static $fixture_file = 'subsites/tests/php/SubsiteTest.yml';
public function adminLoggedInSession()
{
return new Session([
'loggedInAs' => $this->idFromFixture(Member::class, 'admin')
]);
}
/**
* Test generation of the view
*/
public function testBasicView()
{
Subsite::$write_hostmap = false;
$subsite1ID = $this->objFromFixture(Subsite::class, 'domaintest1')->ID;
// Open the admin area logged in as admin
$response1 = Director::test('admin/subsites/', null, $this->adminLoggedInSession());
// Confirm that this URL gets you the entire page, with the edit form loaded
$response2 = Director::test("admin/subsites/SilverStripe-Subsites-Model-Subsite/EditForm/field/SilverStripe-Subsites-Model-Subsite/item/$subsite1ID/edit",
null,
$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');
}
/**
* 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.
*/
public function testMainsiteAdminCanAccessAllSubsites()
{
$member = $this->objFromFixture(Member::class, 'admin');
Session::set('loggedInAs', $member->ID);
$cmsMain = new CMSMain();
foreach ($cmsMain->Subsites() as $subsite) {
$ids[$subsite->ID] = true;
}
$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, 'subsite1'), $ids,
'Subsite1 Template inaccessible');
$this->assertArrayHasKey($this->idFromFixture(Subsite::class, 'subsite2'), $ids,
'Subsite2 Template inaccessible');
}
}

View File

@ -0,0 +1,38 @@
<?php
namespace subsites\tests\php;
use Page;
use SilverStripe\Dev\FunctionalTest;
use SilverStripe\Subsites\Model\Subsite;
use SilverStripe\View\SSViewer;
class SubsiteFunctionalTest extends FunctionalTest
{
public static $fixture_file = 'subsites/tests/php/SubsiteTest.yml';
/**
* @todo: remove test from SiteTreeSubsitesTest when this one works. Seems domain lookup is broken atm
*/
public function testIfSubsiteThemeIsSetToThemeList()
{
$this->markTestSkipped('doesn\'t work somehow - refactor when domain lookup is working');
$defaultThemes = ['default'];
SSViewer::set_themes($defaultThemes);
$subsitePage = $this->objFromFixture(Page::class, 'contact');
$this->get($subsitePage->AbsoluteLink());
$this->assertEquals($subsitePage->SubsiteID, Subsite::currentSubsiteID(), 'Subsite should be changed');
$this->assertEquals(SSViewer::get_themes(), $defaultThemes,
'Themes should not be modified when Subsite has no theme defined');
$pageWithTheme = $this->objFromFixture(Page::class, 'subsite1_contactus');
$this->get($pageWithTheme->AbsoluteLink());
$subsiteTheme = $pageWithTheme->Subsite()->Theme;
$this->assertEquals($pageWithTheme->SubsiteID, Subsite::currentSubsiteID(), 'Subsite should be changed');
$this->assertEquals(SSViewer::get_themes(), array_merge([$subsiteTheme], $defaultThemes),
'Themes should be modified when Subsite has theme defined');
}
}

View File

@ -1,8 +1,19 @@
<?php <?php
namespace SilverStripe\Subsites\Tests;
use Page;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Control\Director;
use SilverStripe\Core\Config\Config;
use SilverStripe\ORM\DataObject;
use SilverStripe\Security\Member;
use SilverStripe\Subsites\Model\Subsite;
use SilverStripe\Subsites\Model\SubsiteDomain;
class SubsiteTest extends BaseSubsiteTest class SubsiteTest extends BaseSubsiteTest
{ {
public static $fixture_file = 'subsites/tests/SubsiteTest.yml'; public static $fixture_file = 'subsites/tests/php/SubsiteTest.yml';
/** /**
* Original value of {@see SubSite::$strict_subdomain_matching} * Original value of {@see SubSite::$strict_subdomain_matching}
@ -16,13 +27,13 @@ class SubsiteTest extends BaseSubsiteTest
* *
* @var array * @var array
*/ */
protected $origServer = array(); protected $origServer = [];
public function setUp() public function setUp()
{ {
parent::setUp(); parent::setUp();
Config::inst()->update('Director', 'alternate_base_url', '/'); Config::modify()->set(Director::class, 'alternate_base_url', '/');
$this->origStrictSubdomainMatching = Subsite::$strict_subdomain_matching; $this->origStrictSubdomainMatching = Subsite::$strict_subdomain_matching;
$this->origServer = $_SERVER; $this->origServer = $_SERVER;
Subsite::$strict_subdomain_matching = false; Subsite::$strict_subdomain_matching = false;
@ -44,20 +55,20 @@ class SubsiteTest extends BaseSubsiteTest
Subsite::$write_hostmap = false; Subsite::$write_hostmap = false;
// Create the instance // Create the instance
$template = $this->objFromFixture('Subsite', '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);
$this->assertEquals($template->ID, Subsite::currentSubsiteID()); $this->assertEquals($template->ID, Subsite::currentSubsiteID());
$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("SiteTree"); $pages = DataObject::get(SiteTree::class);
$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->copyVersionToStage('Stage', 'Live');
} }
// Create a new site // Create a new site
@ -84,28 +95,28 @@ class SubsiteTest extends BaseSubsiteTest
public function testDomainLookup() public function testDomainLookup()
{ {
// Clear existing fixtures // Clear existing fixtures
foreach (DataObject::get('Subsite') as $subsite) { foreach (DataObject::get(Subsite::class) as $subsite) {
$subsite->delete(); $subsite->delete();
} }
foreach (DataObject::get('SubsiteDomain') as $domain) { foreach (DataObject::get(SubsiteDomain::class) as $domain) {
$domain->delete(); $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,
@ -158,23 +169,23 @@ class SubsiteTest extends BaseSubsiteTest
public function testStrictSubdomainMatching() public function testStrictSubdomainMatching()
{ {
// Clear existing fixtures // Clear existing fixtures
foreach (DataObject::get('Subsite') as $subsite) { foreach (DataObject::get(Subsite::class) as $subsite) {
$subsite->delete(); $subsite->delete();
} }
foreach (DataObject::get('SubsiteDomain') as $domain) { foreach (DataObject::get(SubsiteDomain::class) as $domain) {
$domain->delete(); $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;
@ -230,16 +241,16 @@ class SubsiteTest extends BaseSubsiteTest
protected function createSubsiteWithDomains($domains) protected function createSubsiteWithDomains($domains)
{ {
$subsite = new Subsite(array( $subsite = new Subsite([
'Title' => 'My Subsite' 'Title' => 'My Subsite'
)); ]);
$subsite->write(); $subsite->write();
foreach ($domains as $domainStr => $isPrimary) { foreach ($domains as $domainStr => $isPrimary) {
$domain = new SubsiteDomain(array( $domain = new SubsiteDomain([
'Domain' => $domainStr, 'Domain' => $domainStr,
'IsPrimary' => $isPrimary, 'IsPrimary' => $isPrimary,
'SubsiteID' => $subsite->ID 'SubsiteID' => $subsite->ID
)); ]);
$domain->write(); $domain->write();
} }
@ -252,40 +263,42 @@ class SubsiteTest extends BaseSubsiteTest
public function testDefaultDomain() public function testDefaultDomain()
{ {
$this->assertEquals('one.example.org', $this->assertEquals('one.example.org',
$this->objFromFixture('Subsite', 'domaintest1')->domain()); $this->objFromFixture(Subsite::class, 'domaintest1')->domain());
$this->assertEquals('two.mysite.com', $this->assertEquals('two.mysite.com',
$this->objFromFixture('Subsite', 'domaintest2')->domain()); $this->objFromFixture(Subsite::class, 'domaintest2')->domain());
$_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', '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', 'domaintest3')->domain()); $this->objFromFixture(Subsite::class, 'domaintest3')->domain());
$this->assertEquals($_SERVER['HTTP_HOST'], singleton('Subsite')->PrimaryDomain); $this->assertEquals($_SERVER['HTTP_HOST'], singleton(Subsite::class)->PrimaryDomain);
$this->assertEquals('http://'.$_SERVER['HTTP_HOST'].Director::baseURL(), singleton('Subsite')->absoluteBaseURL()); $this->assertEquals('http://' . $_SERVER['HTTP_HOST'] . Director::baseURL(),
singleton(Subsite::class)->absoluteBaseURL());
} }
/** /**
* Tests that Subsite and SubsiteDomain both respect http protocol correctly * Tests that Subsite and SubsiteDomain both respect http protocol correctly
*/ */
public function testDomainProtocol() { public function testDomainProtocol()
{
// domaintest2 has 'protocol' // domaintest2 has 'protocol'
$subsite2 = $this->objFromFixture('Subsite', 'domaintest2'); $subsite2 = $this->objFromFixture(Subsite::class, 'domaintest2');
$domain2a = $this->objFromFixture('SubsiteDomain', 'dt2a'); $domain2a = $this->objFromFixture(SubsiteDomain::class, 'dt2a');
$domain2b = $this->objFromFixture('SubsiteDomain', 'dt2b'); $domain2b = $this->objFromFixture(SubsiteDomain::class, 'dt2b');
// domaintest4 is 'https' (primary only) // domaintest4 is 'https' (primary only)
$subsite4 = $this->objFromFixture('Subsite', 'domaintest4'); $subsite4 = $this->objFromFixture(Subsite::class, 'domaintest4');
$domain4a = $this->objFromFixture('SubsiteDomain', 'dt4a'); $domain4a = $this->objFromFixture(SubsiteDomain::class, 'dt4a');
$domain4b = $this->objFromFixture('SubsiteDomain', 'dt4b'); // secondary domain is http only though $domain4b = $this->objFromFixture(SubsiteDomain::class, 'dt4b'); // secondary domain is http only though
// domaintest5 is 'http' // domaintest5 is 'http'
$subsite5 = $this->objFromFixture('Subsite', 'domaintest5'); $subsite5 = $this->objFromFixture(Subsite::class, 'domaintest5');
$domain5a = $this->objFromFixture('SubsiteDomain', 'dt5'); $domain5a = $this->objFromFixture(SubsiteDomain::class, 'dt5');
// Check protocol when current protocol is http:// // Check protocol when current protocol is http://
$_SERVER['HTTP_HOST'] = 'www.mysite.com'; $_SERVER['HTTP_HOST'] = 'www.mysite.com';
@ -317,27 +330,27 @@ class SubsiteTest extends BaseSubsiteTest
public function testAllSites() public function testAllSites()
{ {
$subsites = Subsite::all_sites(); $subsites = Subsite::all_sites();
$this->assertDOSEquals(array( $this->assertDOSEquals([
array('Title' =>'Main site'), ['Title' => 'Main site'],
array('Title' =>'Template'), ['Title' => 'Template'],
array('Title' =>'Subsite1 Template'), ['Title' => 'Subsite1 Template'],
array('Title' =>'Subsite2 Template'), ['Title' => 'Subsite2 Template'],
array('Title' =>'Test 1'), ['Title' => 'Test 1'],
array('Title' =>'Test 2'), ['Title' => 'Test 2'],
array('Title' =>'Test 3'), ['Title' => 'Test 3'],
array('Title' => 'Test Non-SSL'), ['Title' => 'Test Non-SSL'],
array('Title' => 'Test SSL') ['Title' => 'Test SSL']
), $subsites, 'Lists all subsites'); ], $subsites, 'Lists all subsites');
} }
public function testAllAccessibleSites() public function testAllAccessibleSites()
{ {
$member = $this->objFromFixture('Member', 'subsite1member'); $member = $this->objFromFixture(Member::class, '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.');
} }
/** /**
@ -345,17 +358,17 @@ class SubsiteTest extends BaseSubsiteTest
*/ */
public function testAccessibleSites() public function testAccessibleSites()
{ {
$member1Sites = Subsite::accessible_sites("CMS_ACCESS_CMSMain", false, null, $member1Sites = Subsite::accessible_sites('CMS_ACCESS_CMSMain', false, null,
$this->objFromFixture('Member', 'subsite1member')); $this->objFromFixture(Member::class, 'subsite1member'));
$member1SiteTitles = $member1Sites->column("Title"); $member1SiteTitles = $member1Sites->column('Title');
sort($member1SiteTitles); sort($member1SiteTitles);
$this->assertEquals('Subsite1 Template', $member1SiteTitles[0], 'Member can get to a subsite via a group'); $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('Member', 'admin')); $this->objFromFixture(Member::class, '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',
@ -364,54 +377,54 @@ class SubsiteTest extends BaseSubsiteTest
'Test 3', 'Test 3',
'Test Non-SSL', 'Test Non-SSL',
'Test SSL' 'Test SSL'
), array_values($adminSiteTitles)); ], array_values($adminSiteTitles));
$member2Sites = Subsite::accessible_sites( $member2Sites = Subsite::accessible_sites(
"CMS_ACCESS_CMSMain", false, null, 'CMS_ACCESS_CMSMain', false, null,
$this->objFromFixture('Member', 'subsite1member2') $this->objFromFixture(Member::class, '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');
} }
public function testhasMainSitePermission() public function testhasMainSitePermission()
{ {
$admin = $this->objFromFixture('Member', 'admin'); $admin = $this->objFromFixture(Member::class, 'admin');
$subsite1member = $this->objFromFixture('Member', 'subsite1member'); $subsite1member = $this->objFromFixture(Member::class, 'subsite1member');
$subsite1admin = $this->objFromFixture('Member', 'subsite1admin'); $subsite1admin = $this->objFromFixture(Member::class, 'subsite1admin');
$allsubsitesauthor = $this->objFromFixture('Member', 'allsubsitesauthor'); $allsubsitesauthor = $this->objFromFixture(Member::class, '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'
); );
} }
@ -419,7 +432,7 @@ class SubsiteTest extends BaseSubsiteTest
public function testDuplicateSubsite() public function testDuplicateSubsite()
{ {
// get subsite1 & create page // get subsite1 & create page
$subsite1 = $this->objFromFixture('Subsite', 'domaintest1'); $subsite1 = $this->objFromFixture(Subsite::class, 'domaintest1');
$subsite1->activate(); $subsite1->activate();
$page1 = new Page(); $page1 = new Page();
$page1->Title = 'MyAwesomePage'; $page1->Title = 'MyAwesomePage';

View File

@ -1,115 +1,116 @@
Subsite: SilverStripe\Subsites\Model\Subsite:
main: main:
Title: 'Template' Title: Template
subsite1: subsite1:
Title: 'Subsite1 Template' Title: Subsite1 Template
Theme: subsiteTheme
subsite2: subsite2:
Title: 'Subsite2 Template' Title: Subsite2 Template
domaintest1: domaintest1:
Title: 'Test 1' Title: Test 1
domaintest2: domaintest2:
Title: 'Test 2' Title: Test 2
domaintest3: domaintest3:
Title: 'Test 3' Title: Test 3
domaintest4: domaintest4:
Title: 'Test SSL' Title: 'Test SSL'
domaintest5: domaintest5:
Title: 'Test Non-SSL' Title: 'Test Non-SSL'
SubsiteDomain: SilverStripe\Subsites\Model\SubsiteDomain:
subsite1: subsite1:
SubsiteID: =>Subsite.subsite1 SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsite1
Domain: 'subsite1.*' Domain: subsite1.*
Protocol: automatic Protocol: automatic
subsite2: subsite2:
SubsiteID: =>Subsite.subsite2 SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsite2
Domain: 'subsite2.*' Domain: subsite2.*
Protocol: automatic Protocol: automatic
dt1a: dt1a:
SubsiteID: =>Subsite.domaintest1 SubsiteID: =>SilverStripe\Subsites\Model\Subsite.domaintest1
Domain: 'one.example.org' Domain: one.example.org
Protocol: automatic Protocol: automatic
IsPrimary: 1 IsPrimary: 1
dt1b: dt1b:
SubsiteID: =>Subsite.domaintest1 SubsiteID: =>SilverStripe\Subsites\Model\Subsite.domaintest1
Domain: 'one.*' Domain: one.*
Protocol: automatic Protocol: automatic
dt2a: dt2a:
SubsiteID: =>Subsite.domaintest2 SubsiteID: =>SilverStripe\Subsites\Model\Subsite.domaintest2
Domain: 'two.mysite.com' Domain: two.mysite.com
Protocol: automatic Protocol: automatic
IsPrimary: 1 IsPrimary: 1
dt2b: dt2b:
SubsiteID: =>Subsite.domaintest2 SubsiteID: =>SilverStripe\Subsites\Model\Subsite.domaintest2
Domain: '*.mysite.com' Domain: '*.mysite.com'
Protocol: automatic Protocol: automatic
dt3: dt3:
SubsiteID: =>Subsite.domaintest3 SubsiteID: =>SilverStripe\Subsites\Model\Subsite.domaintest3
Domain: 'three.*' Domain: three.*
Protocol: automatic Protocol: automatic
IsPrimary: 1 IsPrimary: 1
dt4a: dt4a:
SubsiteID: =>Subsite.domaintest4 SubsiteID: =>SilverStripe\Subsites\Model\Subsite.domaintest4
Domain: www.primary.com Domain: www.primary.com
Protocol: https Protocol: https
dt4b: dt4b:
SubsiteID: =>Subsite.domaintest4 SubsiteID: =>SilverStripe\Subsites\Model\Subsite.domaintest4
Domain: www.secondary.com Domain: www.secondary.com
Protocol: http Protocol: http
dt5: dt5:
SubsiteID: =>Subsite.domaintest5 SubsiteID: =>SilverStripe\Subsites\Model\Subsite.domaintest5
Domain: www.tertiary.com Domain: www.tertiary.com
Protocol: http Protocol: http
IsPrimary: 1 IsPrimary: 1
Page: Page:
mainSubsitePage: mainSubsitePage:
Title: 'MainSubsitePage' Title: 'MainSubsitePage'
SubsiteID: 0 SubsiteID: 0
URLSegment: mainsubsitepage URLSegment: mainsubsitepage
home: home:
Title: 'Home' Title: 'Home'
SubsiteID: =>Subsite.main SubsiteID: =>SilverStripe\Subsites\Model\Subsite.main
URLSegment: home URLSegment: home
about: about:
Title: 'About' Title: 'About'
SubsiteID: =>Subsite.main SubsiteID: =>SilverStripe\Subsites\Model\Subsite.main
URLSegment: about URLSegment: about
linky: linky:
Title: 'Linky' Title: 'Linky'
SubsiteID: =>Subsite.main SubsiteID: =>SilverStripe\Subsites\Model\Subsite.main
URLSegment: linky URLSegment: linky
staff: staff:
Title: 'Staff' Title: 'Staff'
ParentID: =>Page.about ParentID: =>Page.about
SubsiteID: =>Subsite.main SubsiteID: =>SilverStripe\Subsites\Model\Subsite.main
URLSegment: staff URLSegment: staff
contact: contact:
Title: 'Contact Us' Title: 'Contact Us'
SubsiteID: =>Subsite.main SubsiteID: =>SilverStripe\Subsites\Model\Subsite.main
URLSegment: contact-us URLSegment: contact-us
importantpage: importantpage:
Title: 'Important Page' Title: 'Important Page'
SubsiteID: =>Subsite.main SubsiteID: =>SilverStripe\Subsites\Model\Subsite.main
URLSegment: important-page URLSegment: important-page
subsite1_home: subsite1_home:
Title: 'Home (Subsite 1)' Title: 'Home (Subsite 1)'
SubsiteID: =>Subsite.subsite1 SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsite1
URLSegment: home URLSegment: home
subsite1_contactus: subsite1_contactus:
Title: 'Contact Us (Subsite 1)' Title: 'Contact Us (Subsite 1)'
SubsiteID: =>Subsite.subsite1 SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsite1
URLSegment: contact-us URLSegment: contact-us
subsite1_staff: subsite1_staff:
Title: 'Staff' Title: 'Staff'
SubsiteID: =>Subsite.subsite1 SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsite1
URLSegment: staff URLSegment: staff
subsite2_home: subsite2_home:
Title: 'Home (Subsite 2)' Title: 'Home (Subsite 2)'
SubsiteID: =>Subsite.subsite2 SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsite2
URLSegment: home URLSegment: home
subsite2_contactus: subsite2_contactus:
Title: 'Contact Us (Subsite 2)' Title: 'Contact Us (Subsite 2)'
SubsiteID: =>Subsite.subsite2 SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsite2
URLSegment: contact-us URLSegment: contact-us
PermissionRoleCode: PermissionRoleCode:
roleCode1: roleCode1:
@ -131,17 +132,17 @@ Group:
Title: subsite1_group Title: subsite1_group
Code: subsite1_group Code: subsite1_group
AccessAllSubsites: 0 AccessAllSubsites: 0
Subsites: =>Subsite.subsite1 Subsites: =>SilverStripe\Subsites\Model\Subsite.subsite1
subsite2_group: subsite2_group:
Title: subsite2_group Title: subsite2_group
Code: subsite2_group Code: subsite2_group
AccessAllSubsites: 0 AccessAllSubsites: 0
Subsites: =>Subsite.subsite2 Subsites: =>SilverStripe\Subsites\Model\Subsite.subsite2
subsite1admins: subsite1admins:
Title: subsite1admins Title: subsite1admins
Code: subsite1admins Code: subsite1admins
AccessAllSubsites: 0 AccessAllSubsites: 0
Subsites: =>Subsite.subsite1 Subsites: =>SilverStripe\Subsites\Model\Subsite.subsite1
allsubsitesauthors: allsubsitesauthors:
Title: allsubsitesauthors Title: allsubsitesauthors
Code: allsubsitesauthors Code: allsubsitesauthors

View File

@ -0,0 +1,50 @@
<?php
namespace SilverStripe\Subsites\Tests;
use SilverStripe\Control\Session;
use SilverStripe\Dev\FunctionalTest;
/**
* Created by PhpStorm.
* User: dmooyman
* Date: 27/05/16
* Time: 3:10 PM
*/
class SubsiteXHRControllerTest extends FunctionalTest
{
protected static $fixture_file = 'SubsiteTest.yml';
public function testCanView()
{
// Test unauthenticated access
Session::clear('MemberID');
$result = $this->get('SubsiteXHRController', null, [
'X-Pjax' => 'SubsiteList',
'X-Requested-With' => 'XMLHttpRequest'
]);
$this->assertEquals(403, $result->getStatusCode());
// Login with NO permissions
$this->logInWithPermission('NOT_CMS_PERMISSION');
$result = $this->get('SubsiteXHRController', null, [
'X-Pjax' => 'SubsiteList',
'X-Requested-With' => 'XMLHttpRequest'
]);
$this->assertEquals(403, $result->getStatusCode());
// Test cms user
$this->logInWithPermission('CMS_ACCESS_CMSMain');
$result = $this->get('SubsiteXHRController', null, [
'X-Pjax' => 'SubsiteList',
'X-Requested-With' => 'XMLHttpRequest'
]);
$this->assertEquals(200, $result->getStatusCode());
$this->assertEquals('text/json', $result->getHeader('Content-Type'));
$body = $result->getBody();
static::assertContains('Main site', $body);
static::assertContains('Test 1', $body);
static::assertContains('Test 2', $body);
static::assertContains('Test 3', $body);
}
}

View File

@ -1,44 +1,62 @@
<?php <?php
namespace SilverStripe\Subsites\Tests;
use Page;
use SilverStripe\Assets\File;
use SilverStripe\Assets\Filesystem;
use SilverStripe\Assets\Tests\Storage\AssetStoreTest\TestAssetStore;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Control\Director;
use SilverStripe\Core\Config\Config;
use SilverStripe\ORM\DB;
use SilverStripe\Subsites\Model\Subsite;
use SilverStripe\Subsites\Pages\SubsitesVirtualPage;
use SilverStripe\Versioned\Versioned;
class SubsitesVirtualPageTest extends BaseSubsiteTest class SubsitesVirtualPageTest extends BaseSubsiteTest
{ {
protected static $fixture_file = array( public static $fixture_file = [
'SubsiteTest.yml', 'subsites/tests/php/SubsiteTest.yml',
'SubsitesVirtualPageTest.yml', 'subsites/tests/php/SubsitesVirtualPageTest.yml',
); ];
public function setUp() protected $illegalExtensions = [
'SiteTree' => ['Translatable']
];
public function setUp()
{ {
parent::setUp(); parent::setUp();
// Set backend root to /DataDifferencerTest // Set backend root to /DataDifferencerTest
AssetStoreTest_SpyStore::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('File', 'file1'); $file = $this->objFromFixture(File::class, 'file1');
$page = $this->objFromFixture('SiteTree', 'page1'); $page = $this->objFromFixture(SiteTree::class, 'page1');
$fromPath = __DIR__ . '/testscript-test-file.pdf'; $fromPath = __DIR__ . '/testscript-test-file.pdf';
$destPath = AssetStoreTest_SpyStore::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()
{ {
AssetStoreTest_SpyStore::reset(); TestAssetStore::reset();
parent::tearDown(); parent::tearDown();
} }
// Attempt to bring main:linky to subsite2:linky // Attempt to bring main:linky to subsite2:linky
public function testVirtualPageFromAnotherSubsite() public function testVirtualPageFromAnotherSubsite()
{ {
Subsite::$write_hostmap = false; Subsite::$write_hostmap = false;
$subsite = $this->objFromFixture('Subsite', '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;
@ -56,13 +74,15 @@ class SubsitesVirtualPageTest extends BaseSubsiteTest
$this->assertEquals($svp->Title, $linky->Title); $this->assertEquals($svp->Title, $linky->Title);
} }
public function testFileLinkRewritingOnVirtualPages() public function testFileLinkRewritingOnVirtualPages()
{ {
$this->markTestSkipped('File handling needs update');
// File setup // File setup
$this->logInWithPermission('ADMIN'); $this->logInWithPermission('ADMIN');
touch(Director::baseFolder() . '/assets/testscript-test-file.pdf');
// Publish the source page // Publish the source page
$page = $this->objFromFixture('SiteTree', 'page1'); $page = $this->objFromFixture(SiteTree::class, '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
@ -72,22 +92,23 @@ class SubsitesVirtualPageTest extends BaseSubsiteTest
$svp->doPublish(); $svp->doPublish();
// Rename the file // Rename the file
$file = $this->objFromFixture('File', 'file1'); $file = $this->objFromFixture(File::class, '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"', static::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"', static::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());
} }
public function testSubsiteVirtualPagesArentInappropriatelyPublished() public function testSubsiteVirtualPagesArentInappropriatelyPublished()
{ {
$this->markTestSkipped('Needs some update or refactoring');
// Fixture // Fixture
$p = new Page(); $p = new Page();
$p->Content = "test content"; $p->Content = 'test content';
$p->write(); $p->write();
$vp = new SubsitesVirtualPage(); $vp = new SubsitesVirtualPage();
$vp->CopyContentFromID = $p->ID; $vp->CopyContentFromID = $p->ID;
@ -108,7 +129,7 @@ class SubsitesVirtualPageTest extends BaseSubsiteTest
$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);
@ -122,7 +143,7 @@ class SubsitesVirtualPageTest extends BaseSubsiteTest
$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);
@ -136,7 +157,7 @@ class SubsitesVirtualPageTest extends BaseSubsiteTest
$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,
@ -144,16 +165,18 @@ class SubsitesVirtualPageTest extends BaseSubsiteTest
*/ */
public function testPublishedSubsiteVirtualPagesUpdateIfTargetPageUpdates() public function testPublishedSubsiteVirtualPagesUpdateIfTargetPageUpdates()
{ {
$this->markTestSkipped('Needs some update or refactoring');
// 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->copyVersionToStage('Stage', 'Live');
$this->assertTrue($p->ExistsOnLive); $this->assertTrue($p->ExistsOnLive);
// change to subsite // change to subsite
$subsite = $this->objFromFixture('Subsite', '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;
@ -162,7 +185,7 @@ class SubsitesVirtualPageTest extends BaseSubsiteTest
$svp->CopyContentFromID = $p->ID; $svp->CopyContentFromID = $p->ID;
$svp->write(); $svp->write();
$svp->writeToStage('Stage'); $svp->writeToStage('Stage');
$svp->publish('Stage', 'Live'); $svp->copyVersionToStage('Stage', 'Live');
$this->assertEquals($svp->SubsiteID, $subsite->ID); $this->assertEquals($svp->SubsiteID, $subsite->ID);
$this->assertTrue($svp->ExistsOnLive); $this->assertTrue($svp->ExistsOnLive);
@ -173,7 +196,7 @@ class SubsitesVirtualPageTest extends BaseSubsiteTest
$p->Title = 'New Title'; $p->Title = 'New Title';
// "save & publish" // "save & publish"
$p->writeToStage('Stage'); $p->writeToStage('Stage');
$p->publish('Stage', 'Live'); $p->copyVersionToStage('Stage', 'Live');
$this->assertNotEquals($p->SubsiteID, $subsite->ID); $this->assertNotEquals($p->SubsiteID, $subsite->ID);
// reload SVP from database // reload SVP from database
@ -184,43 +207,43 @@ class SubsitesVirtualPageTest extends BaseSubsiteTest
$this->assertEquals($svpdb->Title, $p->Title); $this->assertEquals($svpdb->Title, $p->Title);
} }
public function testUnpublishingParentPageUnpublishesSubsiteVirtualPages() public function testUnpublishingParentPageUnpublishesSubsiteVirtualPages()
{ {
Config::inst()->update('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', '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', '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->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
$subsite = $this->objFromFixture('Subsite', '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->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
// Switch back to main site, unpublish source // Switch back to main site, unpublish source
$subsite = $this->objFromFixture('Subsite', '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', 'Live', "\"SiteTree_Live\".\"ID\" = ".$vp1->ID); $onLive = Versioned::get_one_by_stage(SubsitesVirtualPage::class, 'Live', '"SiteTree_Live"."ID" = ' . $vp1->ID);
$this->assertNull($onLive, 'SVP has been removed from live'); $this->assertNull($onLive, 'SVP has been removed from live');
$subsite = $this->objFromFixture('Subsite', 'subsite2'); $subsite = $this->objFromFixture(Subsite::class, 'subsite2');
Subsite::changeSubsite($vp2->SubsiteID); Subsite::changeSubsite($vp2->SubsiteID);
$onLive = Versioned::get_one_by_stage('SubsitesVirtualPage', 'Live', "\"SiteTree_Live\".\"ID\" = ".$vp2->ID); $onLive = Versioned::get_one_by_stage(SubsitesVirtualPage::class, 'Live', '"SiteTree_Live"."ID" = ' . $vp2->ID);
$this->assertNull($onLive, 'SVP has been removed from live'); $this->assertNull($onLive, 'SVP has been removed from live');
} }
@ -231,8 +254,8 @@ class SubsitesVirtualPageTest extends BaseSubsiteTest
public function testSubsiteVirtualPageCanHaveSameUrlsegmentAsOtherSubsite() public function testSubsiteVirtualPageCanHaveSameUrlsegmentAsOtherSubsite()
{ {
Subsite::$write_hostmap = false; Subsite::$write_hostmap = false;
$subsite1 = $this->objFromFixture('Subsite', 'subsite1'); $subsite1 = $this->objFromFixture(Subsite::class, 'subsite1');
$subsite2 = $this->objFromFixture('Subsite', 'subsite2'); $subsite2 = $this->objFromFixture(Subsite::class, 'subsite2');
Subsite::changeSubsite($subsite1->ID); Subsite::changeSubsite($subsite1->ID);
$subsite1Page = $this->objFromFixture('Page', 'subsite1_staff'); $subsite1Page = $this->objFromFixture('Page', 'subsite1_staff');
@ -262,7 +285,7 @@ class SubsitesVirtualPageTest extends BaseSubsiteTest
$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'
); );
// When changing subsites and re-saving this page, it doesn't trigger a change // When changing subsites and re-saving this page, it doesn't trigger a change
@ -280,8 +303,8 @@ class SubsitesVirtualPageTest extends BaseSubsiteTest
{ {
$pages = func_get_args(); $pages = func_get_args();
foreach ($pages as $p) { foreach ($pages as $p) {
Versioned::prepopulate_versionnumber_cache('SiteTree', 'Stage', array($p->ID)); Versioned::prepopulate_versionnumber_cache(SiteTree::class, 'Stage', [$p->ID]);
Versioned::prepopulate_versionnumber_cache('SiteTree', 'Live', array($p->ID)); Versioned::prepopulate_versionnumber_cache(SiteTree::class, 'Live', [$p->ID]);
} }
} }
} }

View File

@ -0,0 +1,87 @@
<?php
namespace SilverStripe\Subsites\Tests;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\Subsites\Forms\WildcardDomainField;
/**
* Tests {@see WildcardDomainField}
*/
class WildcardDomainFieldTest extends SapphireTest
{
/**
* Check that valid domains are accepted
*
* @dataProvider validDomains
* @param $domain
*/
public function testValidDomains($domain)
{
$field = new WildcardDomainField('DomainField');
$this->assertTrue($field->checkHostname($domain), "Validate that {$domain} is a valid domain name");
}
/**
* Check that valid domains are accepted
*
* @dataProvider invalidDomains
* @param $domain
*/
public function testInvalidDomains($domain)
{
$field = new WildcardDomainField('DomainField');
$this->assertFalse($field->checkHostname($domain), "Validate that {$domain} is an invalid domain name");
}
/**
* Check that valid domains are accepted
*
* @dataProvider validWildcards
* @param $domain
*/
public function testValidWildcards($domain)
{
$field = new WildcardDomainField('DomainField');
$this->assertTrue($field->checkHostname($domain), "Validate that {$domain} is a valid domain wildcard");
}
public function validDomains()
{
return [
['www.mysite.com'],
['domain7'],
['mysite.co.n-z'],
['subdomain.my-site.com'],
['subdomain.mysite']
];
}
public function invalidDomains()
{
return [
['-mysite'],
['.mysite'],
['mys..ite'],
['mysite-'],
['mysite.'],
['-mysite.*'],
['.mysite.*'],
['mys..ite.*'],
['*.mysite-'],
['*.mysite.']
];
}
public function validWildcards()
{
return [
['*.mysite.com'],
['mys*ite.com'],
['*.my-site.*'],
['*']
];
}
}