merge ss4 fixes by cheddam

This commit is contained in:
Werner M. Krauß 2017-05-29 13:42:42 +02:00
parent d1e829697f
commit 9862cf5ea6
71 changed files with 2727 additions and 1518 deletions

View File

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

View File

39
CHANGELOG.md Normal file
View File

@ -0,0 +1,39 @@
# Changelog
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).
## [1.2.3]
* BUG Fix issue with urlsegment being renamed in subsites
## [1.2.2]
* Update translations.
* Added attributes to menu link
## [1.2.1]
* BUG: The move to subsite folder dropdown in files is gone
* Update templates for 3.3 compatibility
* Update userhelp documentation
* Fix Subsite module does not picks up themes
* Update translations
## [1.2.0]
* API Add option to specify http / https on subsite domains
## [1.1.0]
* Changelog added.
* Fixes #135: LeftAndMain switching between subsites
* BUG Fix incompatibility with framework 3.2
* Adjusted tests to new SiteTree->canCreate() logic in 3.1.11+
* Fix subsites to use correct permissions
* Wrong edit link in SubsitesVirtualPage
* Added missing route to `SubsiteXHRController` for SilverStripe 3.2 compatibility.
* Add sticky nav toggle button
* BUG Subsites selection on SubsitesVirtualPage (fixes #45 and #47)
* Update translations

View File

@ -21,8 +21,8 @@ permissions" will imply that the person will likely be able to escalate his/her
For user documentation please see:
1. [Setting up subsites](docs/en/set_up.md)
1. [Working with subsites](docs/en/working_with.md)
1. [Setting up subsites](docs/en/userguide/set_up.md)
1. [Working with subsites](docs/en/userguide/working_with.md)
## Features & limitations
@ -41,7 +41,7 @@ For user documentation please see:
* Each subsite domain name has to be set up on the server first, and DNS records need to be updated as appropriate.
* A subsite cannot use a different codebase as the main site, they are intrinsically tied
* However, you can remove page types from a subsite when creating the subsite - [see the setup documentation for further details](set_up.md)
* However, you can remove page types from a subsite when creating the subsite - [see the setup documentation for further details](docs/en/userguide/set_up.md)
* The only code a developer can edit between subsites is the theme
* The separation between subsites in the CMS needs to be seen as cosmetic, and mostly applicable to the "Pages" and "Files" sections of the CMS.
* All subsites run in the same process space and data set. Therefore if an outage affects one subsite it will affect all subsites, and if bad code or hardware corrupts one subsite's data, it's very likely that it has corrupted all subsite data.

36
code/SubsiteAdmin.php Normal file
View File

@ -0,0 +1,36 @@
<?php
use SilverStripe\Forms\GridField\GridFieldDetailForm;
use SilverStripe\Admin\ModelAdmin;
/**
* 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::class);
$grid->getConfig()->addComponent(new GridFieldSubsiteDetailForm());
}
return $form;
}
}

View File

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

View File

@ -0,0 +1,59 @@
<?php
use SilverStripe\Security\Permission;
use SilverStripe\Admin\LeftAndMain;
/**
* Section-agnostic PJAX controller.
*/
class SubsiteXHRController extends LeftAndMain
{
/**
* Relax the access permissions, so anyone who has access to any CMS subsite can access this controller.
*/
public function canView($member = null)
{
if (parent::canView()) {
return true;
}
if (Subsite::all_accessible_sites()->count()>0) {
return true;
}
return false;
}
/**
* Allow access if user allowed into the CMS at all.
*/
public function canAccess()
{
// Allow if any cms access is available
return Permission::check(array(
'CMS_ACCESS', // Supported by 3.1.14 and up
'CMS_ACCESS_LeftAndMain'
));
}
public function getResponseNegotiator()
{
$negotiator = parent::getResponseNegotiator();
$self = $this;
// Register a new callback
$negotiator->setCallback('SubsiteList', function () use (&$self) {
return $self->SubsiteList();
});
return $negotiator;
}
/**
* Provide the list of available subsites as a cms-section-agnostic PJAX handler.
*/
public function SubsiteList()
{
return $this->renderWith('SubsiteList');
}
}

View File

@ -0,0 +1,206 @@
<?php
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\ArrayList;
use SilverStripe\View\ArrayData;
use SilverStripe\Forms\DropdownField;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Control\Controller;
use SilverStripe\Forms\LabelField;
use SilverStripe\Forms\TextField;
use SilverStripe\Forms\TextareaField;
use SilverStripe\Control\Session;
use SilverStripe\CMS\Model\VirtualPage;
class SubsitesVirtualPage extends VirtualPage
{
private static $description = 'Displays the content of a page on another subsite';
private static $db = array(
'CustomMetaTitle' => 'Varchar(255)',
'CustomMetaKeywords' => 'Varchar(255)',
'CustomMetaDescription' => 'Text',
'CustomExtraMeta' => 'HTMLText'
);
public function getCMSFields()
{
$fields = parent::getCMSFields();
$subsites = DataObject::get('Subsite');
if (!$subsites) {
$subsites = new ArrayList();
} else {
$subsites=ArrayList::create($subsites->toArray());
}
$subsites->push(new ArrayData(array('Title' => 'Main site', 'ID' => 0)));
$fields->addFieldToTab(
'Root.Main',
DropdownField::create(
"CopyContentFromID_SubsiteID",
_t('SubsitesVirtualPage.SubsiteField', "Subsite"),
$subsites->map('ID', 'Title')
)->addExtraClass('subsitestreedropdownfield-chooser no-change-track'),
'CopyContentFromID'
);
// Setup the linking to the original page.
$pageSelectionField = new SubsitesTreeDropdownField(
"CopyContentFromID",
_t('VirtualPage.CHOOSE', "Choose a page to link to"),
SiteTree::class,
"ID",
"MenuTitle"
);
if (Controller::has_curr() && Controller::curr()->getRequest()) {
$subsiteID = Controller::curr()->getRequest()->requestVar('CopyContentFromID_SubsiteID');
$pageSelectionField->setSubsiteID($subsiteID);
}
$fields->replaceField('CopyContentFromID', $pageSelectionField);
// Create links back to the original object in the CMS
if ($this->CopyContentFromID) {
$editLink = "admin/pages/edit/show/$this->CopyContentFromID/?SubsiteID=" . $this->CopyContentFrom()->SubsiteID;
$linkToContent = "
<a class=\"cmsEditlink\" href=\"$editLink\">" .
_t('VirtualPage.EDITCONTENT', 'Click here to edit the content') .
"</a>";
$fields->removeByName("VirtualPageContentLinkLabel");
$fields->addFieldToTab(
"Root.Main",
$linkToContentLabelField = new LabelField('VirtualPageContentLinkLabel', $linkToContent),
'Title'
);
$linkToContentLabelField->setAllowHTML(true);
}
$fields->addFieldToTab(
'Root.Main',
TextField::create(
'CustomMetaTitle',
$this->fieldLabel('CustomMetaTitle')
)->setDescription(_t('SubsitesVirtualPage.OverrideNote', 'Overrides inherited value from the source')),
'MetaTitle'
);
$fields->addFieldToTab(
'Root.Main',
TextareaField::create(
'CustomMetaKeywords',
$this->fieldLabel('CustomMetaTitle')
)->setDescription(_t('SubsitesVirtualPage.OverrideNote')),
'MetaKeywords'
);
$fields->addFieldToTab(
'Root.Main',
TextareaField::create(
'CustomMetaDescription',
$this->fieldLabel('CustomMetaTitle')
)->setDescription(_t('SubsitesVirtualPage.OverrideNote')),
'MetaDescription'
);
$fields->addFieldToTab(
'Root.Main',
TextField::create(
'CustomExtraMeta',
$this->fieldLabel('CustomMetaTitle')
)->setDescription(_t('SubsitesVirtualPage.OverrideNote')),
'ExtraMeta'
);
return $fields;
}
public function fieldLabels($includerelations = true)
{
$labels = parent::fieldLabels($includerelations);
$labels['CustomMetaTitle'] = _t('Subsite.CustomMetaTitle', 'Title');
$labels['CustomMetaKeywords'] = _t('Subsite.CustomMetaKeywords', 'Keywords');
$labels['CustomMetaDescription'] = _t('Subsite.CustomMetaDescription', 'Description');
$labels['CustomExtraMeta'] = _t('Subsite.CustomExtraMeta', 'Custom Meta Tags');
return $labels;
}
public function getCopyContentFromID_SubsiteID()
{
return ($this->CopyContentFromID) ? (int)$this->CopyContentFrom()->SubsiteID : (int)Session::get('SubsiteID');
}
public function getVirtualFields()
{
$fields = parent::getVirtualFields();
foreach ($fields as $k => $v) {
if ($v == 'SubsiteID') {
unset($fields[$k]);
}
}
foreach (self::$db as $field => $type) {
if (in_array($field, $fields)) {
unset($fields[array_search($field, $fields)]);
}
}
return $fields;
}
public function syncLinkTracking()
{
$oldState = Subsite::$disable_subsite_filter;
Subsite::$disable_subsite_filter = true;
if ($this->CopyContentFromID) {
$this->HasBrokenLink = DataObject::get_by_id(SiteTree::class, $this->CopyContentFromID) ? false : true;
}
Subsite::$disable_subsite_filter = $oldState;
}
public function onBeforeWrite()
{
parent::onBeforeWrite();
if ($this->CustomMetaTitle) {
$this->MetaTitle = $this->CustomMetaTitle;
} else {
$this->MetaTitle = $this->ContentSource()->MetaTitle ? $this->ContentSource()->MetaTitle : $this->MetaTitle;
}
if ($this->CustomMetaKeywords) {
$this->MetaKeywords = $this->CustomMetaKeywords;
} else {
$this->MetaKeywords = $this->ContentSource()->MetaKeywords ? $this->ContentSource()->MetaKeywords : $this->MetaKeywords;
}
if ($this->CustomMetaDescription) {
$this->MetaDescription = $this->CustomMetaDescription;
} else {
$this->MetaDescription = $this->ContentSource()->MetaDescription ? $this->ContentSource()->MetaDescription : $this->MetaDescription;
}
if ($this->CustomExtraMeta) {
$this->ExtraMeta = $this->CustomExtraMeta;
} else {
$this->ExtraMeta = $this->ContentSource()->ExtraMeta ? $this->ContentSource()->ExtraMeta : $this->ExtraMeta;
}
}
}
//class SubsitesVirtualPage_Controller extends VirtualPage_Controller
//{
// public function reloadContent()
// {
// $this->failover->copyFrom($this->failover->CopyContentFrom());
// $this->failover->write();
// return;
// }
//
// public function init()
// {
// $origDisableSubsiteFilter = Subsite::$disable_subsite_filter;
// Subsite::$disable_subsite_filter = true;
//
// parent::init();
//
// Subsite::$disable_subsite_filter = $origDisableSubsiteFilter;
// }
//}

View File

@ -12,7 +12,7 @@ use SilverStripe\View\SSViewer;
*/
class ControllerSubsites extends Extension
{
function controllerAugmentInit()
public function controllerAugmentInit()
{
if ($subsite = Subsite::currentSubsite()) {
if ($theme = $subsite->Theme) {
@ -21,12 +21,10 @@ class ControllerSubsites extends Extension
}
}
function CurrentSubsite()
public function CurrentSubsite()
{
if ($subsite = Subsite::currentSubsite()) {
return $subsite;
}
}
}
?>

View File

@ -2,28 +2,26 @@
namespace SilverStripe\Subsites\Extensions;
use SilverStripe\ORM\DataExtension;
use SilverStripe\Core\Config\Config;
use SilverStripe\ORM\DataObject;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\ORM\DataExtension;
use SilverStripe\Subsites\Model\Subsite;
class ErrorPageSubsite extends DataExtension
{
/**
* 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
* opening ErrorPage in the CMS and publish ErrorPage causes static error page to get generated incorrectly.
*
* @param string $name Filename to write to
* @param int $statusCode Integer error code
*/
public function updateErrorFilename(&$name, $statusCode)
public function alternateFilepathForErrorcode($statusCode, $locale = null)
{
$static_filepath = Config::inst()->get($this->owner->ClassName, 'static_filepath');
$subdomainPart = "";
// Try to get current subsite from session
$subsite = Subsite::currentSubsite(false);
@ -32,17 +30,23 @@ class ErrorPageSubsite extends DataExtension
if (!$subsite) {
$subsiteID = Subsite::getSubsiteIDForDomain();
if ($subsiteID != 0) {
$subsite = DataObject::get_by_id(Subsite::class, $subsiteID);
$subsite = DataObject::get_by_id("Subsite", $subsiteID);
} else {
$subsite = null;
}
}
// Without subsite, don't rewrite
if ($subsite) {
// Add subdomain to end of filename, just before .html
// This should preserve translatable locale in the filename as well
$subdomain = $subsite->domain();
$name = substr($name, 0, -5) . "-{$subdomain}.html";
$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

@ -21,61 +21,59 @@ use SilverStripe\Subsites\Model\Subsite;
* @package subsites
*/
class FileSubsites extends DataExtension
{
{
// If this is set to true, all folders created will be default be
// considered 'global', unless set otherwise
public static $default_root_folders_global = false;
// If this is set to true, all folders created will be default be
// considered 'global', unless set otherwise
static $default_root_folders_global = false;
private static $has_one=[
'Subsite' => Subsite::class,
];
private static $has_one = [
'Subsite' => Subsite::class,
];
/**
* Amends the CMS tree title for folders in the Files & Images section.
* Prefixes a '* ' to the folders that are accessible from all subsites.
*/
function alternateTreeTitle()
/**
* Amends the CMS tree title for folders in the Files & Images section.
* Prefixes a '* ' to the folders that are accessible from all subsites.
*/
public function alternateTreeTitle()
{
if ($this->owner->SubsiteID == 0) {
return " * " . $this->owner->Title;
} else {
return $this->owner->Title;
}
return $this->owner->Title;
}
/**
* Add subsites-specific fields to the folder editor.
*/
function updateCMSFields(FieldList $fields)
/**
* Add subsites-specific fields to the folder editor.
*/
public function updateCMSFields(FieldList $fields)
{
if ($this->owner instanceof Folder) {
$sites = Subsite::accessible_sites('CMS_ACCESS_AssetAdmin');
$values = [];
$values[0] = _t('FileSubsites.AllSitesDropdownOpt', 'All sites');
foreach ($sites as $site) {
$values[$site->ID] = $site->Title;
}
ksort($values);
if ($sites) {
//Dropdown needed to move folders between subsites
$dropdown = new DropdownField(
'SubsiteID',
_t('FileSubsites.SubsiteFieldLabel', Subsite::class),
$values
);
$dropdown->addExtraClass('subsites-move-dropdown');
$fields->push($dropdown);
$fields->push(new LiteralField(
'Message',
'<p class="message notice">' .
_t('ASSETADMIN.SUBSITENOTICE',
'Folders and files created in the main site are accessible by all subsites.')
. '</p>'
));
}
}
}
if($this->owner instanceof Folder) {
$sites = Subsite::accessible_sites('CMS_ACCESS_AssetAdmin');
$values = [];
$values[0] = _t('FileSubsites.AllSitesDropdownOpt','All sites');
foreach ($sites as $site) {
$values[$site->ID] = $site->Title;
}
ksort($values);
if($sites){
//Dropdown needed to move folders between subsites
$dropdown = new DropdownField(
'SubsiteID',
_t('FileSubsites.SubsiteFieldLabel',Subsite::class),
$values
);
$dropdown->addExtraClass('subsites-move-dropdown');
$fields->push($dropdown);
$fields->push(new LiteralField(
'Message',
'<p class="message notice">'.
_t('ASSETADMIN.SUBSITENOTICE', 'Folders and files created in the main site are accessible by all subsites.')
.'</p>'
));
}
}
}
/**
* Update any requests to limit the results to the current site
@ -112,50 +110,49 @@ class FileSubsites extends DataExtension
}
}
function onBeforeWrite()
public function onBeforeWrite()
{
if (!$this->owner->ID && !$this->owner->SubsiteID) {
if (self::$default_root_folders_global) {
$this->owner->SubsiteID = 0;
} else {
$this->owner->SubsiteID = Subsite::currentSubsiteID();
}
}
}
if (!$this->owner->ID && !$this->owner->SubsiteID) {
if (self::$default_root_folders_global) {
$this->owner->SubsiteID = 0;
} else {
$this->owner->SubsiteID = Subsite::currentSubsiteID();
}
}
}
function onAfterUpload()
public function onAfterUpload()
{
// If we have a parent, use it's subsite as our subsite
if ($this->owner->Parent()) {
$this->owner->SubsiteID = $this->owner->Parent()->SubsiteID;
} else {
$this->owner->SubsiteID = Subsite::currentSubsiteID();
}
$this->owner->write();
}
if ($this->owner->Parent()) {
$this->owner->SubsiteID = $this->owner->Parent()->SubsiteID;
} else {
$this->owner->SubsiteID = Subsite::currentSubsiteID();
}
$this->owner->write();
}
function canEdit($member = null)
public function canEdit($member = null)
{
// Check the CMS_ACCESS_SecurityAdmin privileges on the subsite that owns this group
$subsiteID = Session::get('SubsiteID');
if ($subsiteID && $subsiteID == $this->owner->SubsiteID) {
return true;
} else {
Session::set('SubsiteID', $this->owner->SubsiteID);
$access = Permission::check(['CMS_ACCESS_AssetAdmin', 'CMS_ACCESS_LeftAndMain']);
Session::set('SubsiteID', $subsiteID);
$subsiteID = Session::get('SubsiteID');
if($subsiteID&&$subsiteID == $this->owner->SubsiteID) {
return true;
}
return $access;
}
}
Session::set('SubsiteID', $this->owner->SubsiteID);
$access = Permission::check(['CMS_ACCESS_AssetAdmin', 'CMS_ACCESS_LeftAndMain']);
Session::set('SubsiteID', $subsiteID);
/**
* Return a piece of text to keep DataObject cache keys appropriately specific
*/
function cacheKeyComponent()
return $access;
}
/**
* Return a piece of text to keep DataObject cache keys appropriately specific
*/
public function cacheKeyComponent()
{
return 'subsite-' . Subsite::currentSubsiteID();
}
return 'subsite-'.Subsite::currentSubsiteID();
}
}

View File

@ -25,23 +25,22 @@ use SilverStripe\Subsites\Model\Subsite;
*/
class GroupSubsites extends DataExtension implements PermissionProvider
{
private static $db = [
'AccessAllSubsites' => 'Boolean'
];
private static $db = [
'AccessAllSubsites' => 'Boolean'
];
private static $many_many = [
'Subsites' => Subsite::class
];
private static $many_many = [
'Subsites' => Subsite::class
];
private static $defaults = [
'AccessAllSubsites' => true
];
private static $defaults = [
'AccessAllSubsites' => true
];
/**
* Migrations for GroupSubsites data.
*/
function requireDefaultRecords()
public function requireDefaultRecords()
{
// Migration for Group.SubsiteID data from when Groups only had a single subsite
$schema = $this->owner->getSchema();
@ -65,15 +64,13 @@ class GroupSubsites extends DataExtension implements PermissionProvider
if (!DB::query('SELECT "Group"."ID" FROM "Group"
LEFT JOIN "Group_Subsites" ON "Group_Subsites"."GroupID" = "Group"."ID" AND "Group_Subsites"."SubsiteID" > 0
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');
}
}
}
function updateCMSFields(FieldList $fields)
public function updateCMSFields(FieldList $fields)
{
if ($this->owner->canEdit()) {
// i18n tab
@ -82,37 +79,37 @@ class GroupSubsites extends DataExtension implements PermissionProvider
$subsites = Subsite::accessible_sites(['ADMIN', 'SECURITY_SUBSITE_GROUP'], true);
$subsiteMap = $subsites->map();
// Prevent XSS injection
$subsiteMap = Convert::raw2xml($subsiteMap);
// Prevent XSS injection
$subsiteMap = Convert::raw2xml($subsiteMap->toArray());
// Interface is different if you have the rights to modify subsite group values on
// all subsites
if (isset($subsiteMap[0])) {
$fields->addFieldToTab("Root.Subsites", new OptionsetField("AccessAllSubsites",
_t('GroupSubsites.ACCESSRADIOTITLE', 'Give this group access to'),
[
1 => _t('GroupSubsites.ACCESSALL', "All subsites"),
0 => _t('GroupSubsites.ACCESSONLY', "Only these subsites"),
]
));
// Interface is different if you have the rights to modify subsite group values on
// all subsites
if(isset($subsiteMap[0])) {
$fields->addFieldToTab("Root.Subsites", new OptionsetField("AccessAllSubsites",
_t('GroupSubsites.ACCESSRADIOTITLE', 'Give this group access to'),
[
1 => _t('GroupSubsites.ACCESSALL', "All subsites"),
0 => _t('GroupSubsites.ACCESSONLY', "Only these subsites"),
]
));
unset($subsiteMap[0]);
$fields->addFieldToTab("Root.Subsites", new CheckboxSetField("Subsites", "",
$subsiteMap));
unset($subsiteMap[0]);
$fields->addFieldToTab("Root.Subsites", new CheckboxSetField("Subsites", "",
$subsiteMap));
} else {
if (sizeof($subsiteMap) <= 1) {
$fields->addFieldToTab("Root.Subsites", new ReadonlyField("SubsitesHuman",
_t('GroupSubsites.ACCESSRADIOTITLE', 'Give this group access to'),
reset($subsiteMap)));
} else {
$fields->addFieldToTab("Root.Subsites", new CheckboxSetField("Subsites",
_t('GroupSubsites.ACCESSRADIOTITLE', 'Give this group access to'),
$subsiteMap));
}
}
}
}
} else {
if (sizeof($subsiteMap) <= 1) {
$fields->addFieldToTab("Root.Subsites", new ReadonlyField("SubsitesHuman",
_t('GroupSubsites.ACCESSRADIOTITLE', 'Give this group access to'),
reset($subsiteMap)));
} else {
$fields->addFieldToTab("Root.Subsites", new CheckboxSetField("Subsites",
_t('GroupSubsites.ACCESSRADIOTITLE', 'Give this group access to'),
$subsiteMap));
}
}
}
}
/**
* If this group belongs to a subsite,
@ -120,7 +117,7 @@ class GroupSubsites extends DataExtension implements PermissionProvider
* to make it easy to distinguish in the tree-view
* of the security admin interface.
*/
function alternateTreeTitle()
public function alternateTreeTitle()
{
if ($this->owner->AccessAllSubsites) {
$title = _t('GroupSubsites.GlobalGroup', 'global group');
@ -176,12 +173,12 @@ class GroupSubsites extends DataExtension implements PermissionProvider
// WORKAROUND for databases that complain about an ORDER BY when the column wasn't selected (e.g. SQL Server)
$select = $query->getSelect();
if (isset($select[0]) && !$select[0] == 'COUNT(*)') {
$query->orderby = "\"AccessAllSubsites\" DESC" . ($query->orderby ? ', ' : '') . $query->orderby;
$query->addOrderBy("AccessAllSubsites", "DESC");
}
}
}
function onBeforeWrite()
public function onBeforeWrite()
{
// New record test approximated by checking whether the ID has changed.
// Note also that the after write test is only used when we're *not* on a subsite
@ -190,7 +187,7 @@ class GroupSubsites extends DataExtension implements PermissionProvider
}
}
function onAfterWrite()
public function onAfterWrite()
{
// New record test approximated by checking whether the ID has changed.
// Note also that the after write test is only used when we're on a subsite
@ -200,7 +197,7 @@ class GroupSubsites extends DataExtension implements PermissionProvider
}
}
function alternateCanEdit()
public function alternateCanEdit()
{
// Find the sites that this group belongs to and the sites where we have appropriate perm.
$accessibleSites = Subsite::accessible_sites('CMS_ACCESS_SecurityAdmin')->column('ID');
@ -211,7 +208,7 @@ class GroupSubsites extends DataExtension implements PermissionProvider
return (bool)array_intersect($accessibleSites, $linkedSites);
}
function providePermissions()
public function providePermissions()
{
return [
'SECURITY_SUBSITE_GROUP' => [
@ -225,5 +222,3 @@ class GroupSubsites extends DataExtension implements PermissionProvider
}
}
?>

View File

@ -1,25 +1,20 @@
<?php
namespace SilverStripe\Subsites\Extensions;
use SilverStripe\Admin\CMSMenu;
use SilverStripe\Control\Controller;
use SilverStripe\Control\Session;
use SilverStripe\Core\Config\Config;
use SilverStripe\View\Requirements;
use SilverStripe\Core\Convert;
use SilverStripe\Core\Extension;
use SilverStripe\Forms\HiddenField;
use SilverStripe\Security\Member;
use SilverStripe\ORM\ArrayList;
use SilverStripe\ORM\DataObject;
use SilverStripe\Security\Member;
use SilverStripe\Security\Permission;
use SilverStripe\Security\Security;
use SilverStripe\Subsites\Model\Subsite;
use SilverStripe\Core\Config\Config;
use SilverStripe\View\ArrayData;
use SilverStripe\View\Requirements;
use SilverStripe\Security\Permission;
use SilverStripe\Control\Session;
use SilverStripe\Admin\CMSMenu;
use SilverStripe\Security\Security;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Control\Controller;
use SilverStripe\Core\Extension;
/**
* Decorator designed to add subsites support to LeftAndMain
*
@ -27,8 +22,7 @@ use SilverStripe\View\Requirements;
*/
class LeftAndMainSubsites extends Extension
{
private static $allowed_actions = ['CopyToSubsite'];
private static $allowed_actions = array('CopyToSubsite');
/**
* Normally SubsiteID=0 on a DataObject means it is only accessible from the special "main site".
@ -37,7 +31,7 @@ class LeftAndMainSubsites extends Extension
*/
private static $treats_subsite_0_as_global = false;
function init()
public function init()
{
Requirements::css('subsites/css/LeftAndMain_Subsites.css');
Requirements::javascript('subsites/javascript/LeftAndMain_Subsites.js');
@ -47,13 +41,13 @@ class LeftAndMainSubsites extends Extension
/**
* Set the title of the CMS tree
*/
function getCMSTreeTitle()
public function getCMSTreeTitle()
{
$subsite = Subsite::currentSubSite();
return $subsite ? Convert::raw2xml($subsite->Title) : _t('LeftAndMain.SITECONTENTLEFT');
}
function updatePageOptions(&$fields)
public function updatePageOptions(&$fields)
{
$fields->push(new HiddenField('SubsiteID', 'SubsiteID', Subsite::currentSubsiteID()));
}
@ -63,7 +57,7 @@ class LeftAndMainSubsites extends Extension
*
* @return ArrayList of {@link Subsite} instances.
*/
function sectionSites($includeMainSite = true, $mainSiteTitle = "Main site", $member = null)
public function sectionSites($includeMainSite = true, $mainSiteTitle = "Main site", $member = null)
{
if ($mainSiteTitle == 'Main site') {
$mainSiteTitle = _t('Subsites.MainSiteTitle', 'Main site');
@ -77,12 +71,12 @@ class LeftAndMainSubsites extends Extension
return new ArrayList();
}
if (!is_object($member)) {
$member = DataObject::get_by_id('SilverStripe\\Security\\Member', $member);
$member = DataObject::get_by_id(Member::class, $member);
}
// 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.
$codes = [];
$codes = array();
$extraCodes = Config::inst()->get($this->owner->class, 'required_permission_codes');
if ($extraCodes !== false) {
if ($extraCodes) {
@ -96,8 +90,8 @@ class LeftAndMainSubsites extends Extension
}
// Find subsites satisfying all permissions for the Member.
$codesPerSite = [];
$sitesArray = [];
$codesPerSite = array();
$sitesArray = array();
foreach ($codes as $code) {
$sites = Subsite::accessible_sites($code, $includeMainSite, $mainSiteTitle, $member);
foreach ($sites as $site) {
@ -112,7 +106,7 @@ class LeftAndMainSubsites extends Extension
// Find sites that satisfy all codes conjuncitvely.
$accessibleSites = new ArrayList();
foreach ($codesPerSite as $siteID => $siteCodes) {
if (count($siteCodes) == count($codes)) {
if (count($siteCodes)==count($codes)) {
$accessibleSites->push($sitesArray[$siteID]);
}
}
@ -151,11 +145,11 @@ class LeftAndMainSubsites extends Extension
foreach ($list as $subsite) {
$CurrentState = $subsite->ID == $currentSubsiteID ? 'selected' : '';
$output->push(new ArrayData([
$output->push(new ArrayData(array(
'CurrentState' => $CurrentState,
'ID' => $subsite->ID,
'Title' => Convert::raw2xml($subsite->Title)
]));
)));
}
return $output;
@ -194,10 +188,10 @@ class LeftAndMainSubsites extends Extension
*/
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;
}
if ($recordSubsiteID != $currentSubsiteID) {
if ($recordSubsiteID!=$currentSubsiteID) {
return true;
}
return false;
@ -206,17 +200,15 @@ class LeftAndMainSubsites extends Extension
/**
* Check if the current controller is accessible for this user on this subsite.
*/
function canAccess()
public function canAccess()
{
// Admin can access everything, no point in checking.
$member = Member::currentUser();
if ($member &&
(
Permission::checkMember($member, 'ADMIN') || // 'Full administrative rights' in SecurityAdmin
Permission::checkMember($member,
'CMS_ACCESS_LeftAndMain') // 'Access to all CMS sections' in SecurityAdmin
)
) {
(
Permission::checkMember($member, 'ADMIN') || // 'Full administrative rights' in SecurityAdmin
Permission::checkMember($member, 'CMS_ACCESS_LeftAndMain') // 'Access to all CMS sections' in SecurityAdmin
)) {
return true;
}
@ -279,7 +271,6 @@ class LeftAndMainSubsites extends Extension
// This is needed to properly initialise the session in situations where someone opens the CMS via a link.
$record = $this->owner->currentPage();
if ($record && isset($record->SubsiteID) && is_numeric($record->SubsiteID) && isset($this->owner->urlParams['ID'])) {
if ($this->shouldChangeSubsite($this->owner->class, $record->SubsiteID, Subsite::currentSubsiteID())) {
// Update current subsite in session
Subsite::changeSubsite($record->SubsiteID);
@ -291,20 +282,17 @@ class LeftAndMainSubsites extends Extension
//Redirect to the default CMS section
return $this->owner->redirect('admin/');
}
}
// SECOND, check if we need to change subsites due to lack of permissions.
if (!$this->owner->canAccess()) {
$member = Member::currentUser();
// Current section is not accessible, try at least to stick to the same subsite.
$menu = CMSMenu::get_menu_items();
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);
if ($accessibleSites->count() && $accessibleSites->find('ID', Subsite::currentSubsiteID())) {
// Section is accessible, redirect there.
@ -326,34 +314,41 @@ class LeftAndMainSubsites extends Extension
// We have not found any accessible section or subsite. User should be denied access.
return Security::permissionFailure($this->owner);
}
// Current site is accessible. Allow through.
return;
}
function augmentNewSiteTreeItem(&$item)
public function augmentNewSiteTreeItem(&$item)
{
$item->SubsiteID = isset($_POST['SubsiteID']) ? $_POST['SubsiteID'] : Subsite::currentSubsiteID();
}
function onAfterSave($record)
public function onAfterSave($record)
{
if ($record->hasMethod('NormalRelated') && ($record->NormalRelated() || $record->ReverseRelated())) {
$this->owner->response->addHeader('X-Status',
rawurlencode(_t('LeftAndMainSubsites.Saved', 'Saved, please update related pages.')));
$this->owner->response->addHeader('X-Status', rawurlencode(_t('LeftAndMainSubsites.Saved', 'Saved, please update related pages.')));
}
}
function copytosubsite($data, $form)
/**
* @param array $data
* @param Form $form
*/
public function copytosubsite($data, $form)
{
$page = DataObject::get_by_id('SilverStripe\\CMS\\Model\\SiteTree', $data['ID']);
$subsite = DataObject::get_by_id(Subsite::class, $data['CopyToSubsiteID']);
$newPage = $page->duplicateToSubsite($subsite->ID, true);
$page = DataObject::get_by_id(SiteTree::class, $data['ID']);
$subsite = DataObject::get_by_id('Subsite', $data['CopyToSubsiteID']);
$includeChildren = (isset($data['CopyToSubsiteWithChildren'])) ? $data['CopyToSubsiteWithChildren'] : false;
$newPage = $page->duplicateToSubsite($subsite->ID, $includeChildren);
$response = $this->owner->getResponse();
$response->addHeader('X-Reload', true);
return $this->owner->redirect(Controller::join_links($this->owner->Link('show'), $newPage->ID));
}
return $this->owner->redirect(Controller::join_links(
$this->owner->Link('show'),
$newPage->ID
));
}
}

View File

@ -17,19 +17,15 @@ use SilverStripe\Subsites\Model\Subsite;
*/
class SiteConfigSubsites extends DataExtension
{
private static $has_one = [
'Subsite' => Subsite::class, // The subsite that this page belongs to
];
private static $has_one = [
'Subsite' => Subsite::class, // The subsite that this page belongs to
];
/**
* Update any requests to limit the results to the current site
*/
public function augmentSQL(SQLSelect $query, DataQuery $dataQuery = null)
{
if (Subsite::$disable_subsite_filter) {
return;
}
/**
* Update any requests to limit the results to the current site
*/
public function augmentSQL(SQLSelect$query, DataQuery $dataQuery = null) {
if(Subsite::$disable_subsite_filter) {return;}
// If you're querying by ID, ignore the sub-site - this is a bit ugly...
if ($query->filtersOnID()) {
@ -42,20 +38,16 @@ class SiteConfigSubsites extends DataExtension
}
}
/*if($context = DataObject::context_obj()) $subsiteID = (int)$context->SubsiteID;
else */
$subsiteID = (int)Subsite::currentSubsiteID();
$subsiteID = (int)Subsite::currentSubsiteID();
$froms = $query->getFrom();
$froms = array_keys($froms);
$tableName = array_shift($froms);
if ($tableName != SiteConfig::class) {
return;
}
$query->addWhere("\"$tableName\".\"SubsiteID\" IN ($subsiteID)");
}
$froms=$query->getFrom();
$froms=array_keys($froms);
$tableName = array_shift($froms);
if($tableName != SiteConfig::class) { return;}
$query->addWhere("\"$tableName\".\"SubsiteID\" IN ($subsiteID)");
}
function onBeforeWrite()
public function onBeforeWrite()
{
if ((!is_numeric($this->owner->ID) || !$this->owner->ID) && !$this->owner->SubsiteID) {
$this->owner->SubsiteID = Subsite::currentSubsiteID();
@ -65,12 +57,12 @@ class SiteConfigSubsites extends DataExtension
/**
* Return a piece of text to keep DataObject cache keys appropriately specific
*/
function cacheKeyComponent()
public function cacheKeyComponent()
{
return 'subsite-' . Subsite::currentSubsiteID();
return 'subsite-'.Subsite::currentSubsiteID();
}
function updateCMSFields(FieldList $fields)
public function updateCMSFields(FieldList $fields)
{
$fields->push(new HiddenField('SubsiteID', 'SubsiteID', Subsite::currentSubsiteID()));
}

View File

@ -1,47 +1,42 @@
<?php
namespace SilverStripe\Subsites\Extensions;
use Page;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\ORM\DataQuery;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\CheckboxField;
use SilverStripe\Forms\InlineFormAction;
use SilverStripe\Forms\ToggleCompositeField;
use SilverStripe\Core\Config\Config;
use SilverStripe\Control\Controller;
use SilverStripe\ORM\Queries\SQLSelect;
use SilverStripe\SiteConfig\SiteConfig;
use SilverStripe\ORM\DataObject;
use SilverStripe\Security\Member;
use SilverStripe\View\SSViewer;
use SilverStripe\Control\Director;
use SilverStripe\Control\HTTP;
use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Convert;
use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\InlineFormAction;
use SilverStripe\ORM\DataExtension;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\DataQuery;
use SilverStripe\ORM\Queries\SQLSelect;
use SilverStripe\Security\Member;
use SilverStripe\SiteConfig\SiteConfig;
use SilverStripe\Subsites\Model\Subsite;
/**
* Extension for the SiteTree object to add subsites support
*/
class SiteTreeSubsites extends DataExtension
{
private static $has_one = array(
'Subsite' => 'Subsite', // The subsite that this page belongs to
);
private static $has_one = [
'Subsite' => Subsite::class, // The subsite that this page belongs to
];
private static $many_many = array(
'CrossSubsiteLinkTracking' => SiteTree::class // Stored separately, as the logic for URL rewriting is different
);
private static $many_many = [
'CrossSubsiteLinkTracking' => 'SilverStripe\\CMS\\Model\\SiteTree'
// Stored separately, as the logic for URL rewriting is different
];
private static $many_many_extraFields = array(
"CrossSubsiteLinkTracking" => array("FieldName" => "Varchar")
);
private static $many_many_extraFields = [
"CrossSubsiteLinkTracking" => ["FieldName" => "Varchar"]
];
function isMainSite()
public function isMainSite()
{
if ($this->owner->SubsiteID == 0) {
return true;
@ -49,15 +44,17 @@ class SiteTreeSubsites extends DataExtension
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(SQLSelect $query, DataQuery $dataQuery = null)
{
if (Subsite::$disable_subsite_filter) {
return;
}
if ($dataQuery->getQueryParam('Subsite.filter') === false) {
if ($dataQuery && $dataQuery->getQueryParam('Subsite.filter') === false) {
return;
}
@ -71,8 +68,7 @@ class SiteTreeSubsites extends DataExtension
$subsiteID = Subsite::$force_subsite;
} else {
/*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 :-)
@ -86,7 +82,7 @@ class SiteTreeSubsites extends DataExtension
}
}
function onBeforeWrite()
public function onBeforeWrite()
{
if (!$this->owner->ID && !$this->owner->SubsiteID) {
$this->owner->SubsiteID = Subsite::currentSubsiteID();
@ -95,57 +91,67 @@ class SiteTreeSubsites extends DataExtension
parent::onBeforeWrite();
}
function updateCMSFields(FieldList $fields)
public function updateCMSFields(FieldList $fields)
{
$subsites = Subsite::accessible_sites("CMS_ACCESS_CMSMain");
$subsitesMap = [];
$subsitesMap = array();
if ($subsites && $subsites->Count()) {
$subsitesMap = $subsites->map('ID', 'Title')->toArray();
unset($subsitesMap[$this->owner->SubsiteID]);
$subsitesToMap = $subsites->exclude('ID', $this->owner->SubsiteID);
$subsitesMap = $subsitesToMap->map('ID', 'Title');
}
// Master page edit field (only allowed from default subsite to avoid inconsistent relationships)
$isDefaultSubsite = $this->owner->SubsiteID == 0 || $this->owner->Subsite()->DefaultSite;
if ($isDefaultSubsite && $subsitesMap) {
$fields->addFieldToTab(
$fields->addFieldsToTab(
'Root.Main',
new DropdownField(
"CopyToSubsiteID",
_t('SiteTreeSubsites.CopyToSubsite', "Copy page to subsite"),
$subsitesMap,
''
)
);
$fields->addFieldToTab(
'Root.Main',
$copyAction = new InlineFormAction(
"copytosubsite",
_t('SiteTreeSubsites.CopyAction', "Copy")
)
ToggleCompositeField::create('SubsiteOperations',
_t('SiteTreeSubsites.SubsiteOperations', 'Subsite Operations'),
array(
new DropdownField("CopyToSubsiteID", _t('SiteTreeSubsites.CopyToSubsite', "Copy page to subsite"), $subsitesMap),
new CheckboxField("CopyToSubsiteWithChildren", _t('SiteTreeSubsites.CopyToSubsiteWithChildren', 'Include children pages?')),
$copyAction = new InlineFormAction(
"copytosubsite",
_t('SiteTreeSubsites.CopyAction', "Copy")
)
)
)->setHeadingLevel(4)
);
// $copyAction->includeDefaultJS(false);
}
// replace readonly link prefix
$subsite = $this->owner->Subsite();
$nested_urls_enabled = Config::inst()->get('SilverStripe\\CMS\\Model\\SiteTree', 'nested_urls');
if ($subsite && $subsite->ID) {
$baseUrl = Director::protocol() . $subsite->domain() . '/';
$baseLink = Controller::join_links(
$baseUrl,
($nested_urls_enabled && $this->owner->ParentID ? $this->owner->Parent()->RelativeLink(true) : null)
);
$nested_urls_enabled = Config::inst()->get(SiteTree::class, 'nested_urls');
if ($subsite && $subsite->exists()) {
// Use baseurl from domain
$baseLink = $subsite->absoluteBaseURL();
// Add parent page if enabled
if($nested_urls_enabled && $this->owner->ParentID) {
$baseLink = Controller::join_links(
$baseLink,
$this->owner->Parent()->RelativeLink(true)
);
}
$urlsegment = $fields->dataFieldByName('URLSegment');
$urlsegment->setURLPrefix($baseLink);
}
}
function alternateSiteConfig()
/**
* @return SiteConfig
*/
public function alternateSiteConfig()
{
if (!$this->owner->SubsiteID) {
return false;
}
$sc = DataObject::get_one('SilverStripe\\SiteConfig\\SiteConfig', '"SubsiteID" = ' . $this->owner->SubsiteID);
$sc = DataObject::get_one(SiteConfig::class, '"SubsiteID" = ' . $this->owner->SubsiteID);
if (!$sc) {
$sc = new SiteConfig();
$sc->SubsiteID = $this->owner->SubsiteID;
@ -163,9 +169,8 @@ class SiteTreeSubsites extends DataExtension
*
* @return boolean
*/
function canEdit($member = null)
public function canEdit($member = null)
{
if (!$member) {
$member = Member::currentUser();
}
@ -193,9 +198,9 @@ class SiteTreeSubsites extends DataExtension
/**
* @return boolean
*/
function canDelete($member = null)
public function canDelete($member = null)
{
if (!$member && $member !== FALSE) {
if (!$member && $member !== false) {
$member = Member::currentUser();
}
@ -205,9 +210,9 @@ class SiteTreeSubsites extends DataExtension
/**
* @return boolean
*/
function canAddChildren($member = null)
public function canAddChildren($member = null)
{
if (!$member && $member !== FALSE) {
if (!$member && $member !== false) {
$member = Member::currentUser();
}
@ -217,9 +222,9 @@ class SiteTreeSubsites extends DataExtension
/**
* @return boolean
*/
function canPublish($member = null)
public function canPublish($member = null)
{
if (!$member && $member !== FALSE) {
if (!$member && $member !== false) {
$member = Member::currentUser();
}
@ -227,104 +232,70 @@ class SiteTreeSubsites extends DataExtension
}
/**
* Does the basic duplication, but doesn't write anything
* this means we can subclass this easier and do more complex
* relation duplication.
* 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 duplicateToSubsitePrep($subsiteID)
public function duplicateToSubsite($subsiteID = null, $includeChildren = false, $parentID = 0)
{
if (is_object($subsiteID)) {
if ($subsiteID instanceof Subsite) {
$subsiteID = $subsiteID->ID;
}
$oldSubsite = Subsite::currentSubsiteID();
if ($subsiteID) {
Subsite::changeSubsite($subsiteID);
} else {
$subsiteID = $oldSubsite;
}
// doesn't write as we need to reset the SubsiteID, ParentID etc
$clone = $this->owner->duplicate(false);
$clone->CheckedPublicationDifferences = $clone->AddedToStage = true;
$page = $this->owner->duplicate(false);
$page->CheckedPublicationDifferences = $page->AddedToStage = true;
$subsiteID = ($subsiteID ? $subsiteID : $oldSubsite);
$clone->SubsiteID = $subsiteID;
// We have no idea what the parentID should be, so as a workaround use the url-segment and subsite ID
if ($this->owner->Parent()) {
$parentSeg = $this->owner->Parent()->URLSegment;
$newParentPage = Page::get()->filter('URLSegment', $parentSeg)->first();
if ($newParentPage) {
$clone->ParentID = $newParentPage->ID;
} else {
// reset it to the top level, so the user can decide where to put it
$clone->ParentID = 0;
}
}
$page->SubsiteID = $subsiteID;
$page->ParentID = $parentID;
// MasterPageID is here for legacy purposes, to satisfy the subsites_relatedpages module
$clone->MasterPageID = $this->owner->ID;
return $clone;
}
$page->MasterPageID = $this->owner->ID;
$page->write();
/**
* Create a duplicate of this page and save it to another subsite
* @param $subsiteID int|Subsite The Subsite to copy to, or its ID
*/
public function duplicateToSubsite($subsiteID = null)
{
$clone = $this->owner->duplicateToSubsitePrep($subsiteID);
$clone->invokeWithExtensions('onBeforeDuplicateToSubsite', $this->owner);
$clone->write();
$clone->duplicateSubsiteRelations($this->owner);
// new extension hooks which happens after write,
// onAfterDuplicate isn't reliable due to
// https://github.com/silverstripe/silverstripe-cms/issues/1253
$clone->invokeWithExtensions('onAfterDuplicateToSubsite', $this->owner);
return $clone;
}
Subsite::changeSubsite($oldSubsite);
/**
* Duplicate relations using a static property to define
* which ones we want to duplicate
*
* It may be that some relations are not diostinct to sub site so can stay
* whereas others may need to be duplicated
*
*/
public function duplicateSubsiteRelations($originalPage)
{
$thisClass = $originalPage->ClassName;
$relations = Config::inst()->get($thisClass, 'duplicate_to_subsite_relations');
if($includeChildren) {
foreach($this->owner->AllChildren() as $child) {
$child->duplicateToSubsite($subsiteID, $includeChildren, $page->ID);
}
}
if ($relations && !empty($relations)) {
foreach ($relations as $relation) {
$items = $originalPage->$relation();
foreach ($items as $item) {
$duplicateItem = $item->duplicate(false);
$duplicateItem->{$thisClass . 'ID'} = $this->owner->ID;
$duplicateItem->write();
}
}
}
return $page;
}
/**
* Called by ContentController::init();
*/
static function contentcontrollerInit($controller)
public static function contentcontrollerInit($controller)
{
$subsite = Subsite::currentSubsite();
if ($subsite && $subsite->Theme) {
Config::modify()->set('SilverStripe\\View\\SSViewer', 'theme', Subsite::currentSubsite()->Theme);
Config::inst()->update(SSViewer::class, 'theme', Subsite::currentSubsite()->Theme);
}
}
function alternateAbsoluteLink()
public function alternateAbsoluteLink()
{
// Generate the existing absolute URL and replace the domain with the subsite domain.
// This helps deal with Link() returning an absolute URL.
$url = Director::absoluteURL($this->owner->Link());
if ($this->owner->SubsiteID) {
$url = preg_replace('/\/\/[^\/]+\//', '//' . $this->owner->Subsite()->domain() . '/', $url);
$url = preg_replace('/\/\/[^\/]+\//', '//' . $this->owner->Subsite()->domain() . '/', $url);
}
return $url;
}
@ -333,7 +304,7 @@ class SiteTreeSubsites extends DataExtension
* Use the CMS domain for iframed CMS previews to prevent single-origin violations
* and SSL cert problems.
*/
function alternatePreviewLink($action = null)
public function alternatePreviewLink($action = null)
{
$url = Director::absoluteURL($this->owner->Link());
if ($this->owner->SubsiteID) {
@ -345,7 +316,7 @@ class SiteTreeSubsites extends DataExtension
/**
* Inject the subsite ID into the content so it can be used by frontend scripts.
*/
function MetaTags(&$tags)
public function MetaTags(&$tags)
{
if ($this->owner->SubsiteID) {
$tags .= "<meta name=\"x-subsite-id\" content=\"" . $this->owner->SubsiteID . "\" />\n";
@ -354,11 +325,11 @@ class SiteTreeSubsites extends DataExtension
return $tags;
}
function augmentSyncLinkTracking()
public function augmentSyncLinkTracking()
{
// Set LinkTracking appropriately
$links = HTTP::getLinksIn($this->owner->Content);
$linkedPages = [];
$linkedPages = array();
if ($links) {
foreach ($links as $link) {
@ -373,11 +344,9 @@ class SiteTreeSubsites extends DataExtension
continue;
} // We have no idea what the domain for the main site is, so cant track links to it
$origDisableSubsiteFilter = Subsite::$disable_subsite_filter;
$origDisableSubsiteFilter = Subsite::$disable_subsite_filter;
Subsite::disable_subsite_filter(true);
$candidatePage = DataObject::get_one("SilverStripe\\CMS\\Model\\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);
if ($candidatePage) {
@ -393,19 +362,46 @@ class SiteTreeSubsites extends DataExtension
$this->owner->CrossSubsiteLinkTracking()->setByIDList($linkedPages);
}
/**
* Ensure that valid url segments are checked within the correct subsite of the owner object,
* even if the current subsiteID is set to some other subsite.
*
* @return null|bool Either true or false, or null to not influence result
*/
public function augmentValidURLSegment()
{
// If this page is being filtered in the current subsite, then no custom validation query is required.
$subsite = Subsite::$force_subsite ?: Subsite::currentSubsiteID();
if (empty($this->owner->SubsiteID) || $subsite == $this->owner->SubsiteID) {
return null;
}
// Backup forced subsite
$prevForceSubsite = Subsite::$force_subsite;
Subsite::$force_subsite = $this->owner->SubsiteID;
// Repeat validation in the correct subsite
$isValid = $this->owner->validURLSegment();
// Restore
Subsite::$force_subsite = $prevForceSubsite;
return (bool)$isValid;
}
/**
* Return a piece of text to keep DataObject cache keys appropriately specific
*/
function cacheKeyComponent()
public function cacheKeyComponent()
{
return 'subsite-' . Subsite::currentSubsiteID();
return 'subsite-'.Subsite::currentSubsiteID();
}
/**
* @param Member
* @return boolean|null
*/
function canCreate($member = null)
public function canCreate($member = null)
{
// Typically called on a singleton, so we're not using the Subsite() relation
$subsite = Subsite::currentSubsite();

View File

@ -16,10 +16,9 @@ class GridFieldSubsiteDetailForm extends GridFieldDetailForm
class GridFieldSubsiteDetailForm_ItemRequest extends GridFieldDetailForm_ItemRequest
{
private static $allowed_actions = [
'ItemEditForm',
];
private static $allowed_actions = [
'ItemEditForm',
];
/**
* Builds an item edit form. The arguments to getCMSFields() are the popupController and
@ -32,7 +31,7 @@ class GridFieldSubsiteDetailForm_ItemRequest extends GridFieldDetailForm_ItemReq
* @return Form
* @see GridFieldDetailForm_ItemRequest::ItemEditForm()
*/
function ItemEditForm()
public function ItemEditForm()
{
$form = parent::ItemEditForm();
@ -52,7 +51,7 @@ class GridFieldSubsiteDetailForm_ItemRequest extends GridFieldDetailForm_ItemReq
return $form;
}
function doSave($data, $form)
public function doSave($data, $form)
{
$new_record = $this->record->ID == 0;
if ($new_record && isset($data['TemplateID']) && !empty($data['TemplateID'])) {

View File

@ -17,16 +17,15 @@ use SilverStripe\View\Requirements;
*/
class SubsitesTreeDropdownField extends TreeDropdownField
{
private static $allowed_actions = [
private static $allowed_actions = array(
'tree'
];
);
protected $subsiteID = 0;
protected $extraClasses = [SubsitesTreeDropdownField::class];
protected $extraClasses = array('SubsitesTreeDropdownField');
function Field($properties = [])
public function Field($properties = array())
{
$html = parent::Field($properties);
@ -35,17 +34,17 @@ class SubsitesTreeDropdownField extends TreeDropdownField
return $html;
}
function setSubsiteID($id)
public function setSubsiteID($id)
{
$this->subsiteID = $id;
}
function getSubsiteID()
public function getSubsiteID()
{
return $this->subsiteID;
}
function tree(HTTPRequest $request)
public function tree(HTTPRequest $request)
{
$oldSubsiteID = Session::get('SubsiteID');
Session::set('SubsiteID', $this->subsiteID);

View File

@ -0,0 +1,45 @@
<?php
use SilverStripe\Forms\TextField;
/**
* A text field that accepts only valid domain names, but allows the wildcard (*) character
*/
class WildcardDomainField extends TextField
{
/**
* Validate this field as a valid hostname
*
* @param Validator $validator
* @return bool
*/
public function validate($validator)
{
if ($this->checkHostname($this->Value())) {
return true;
}
$validator->validationError(
$this->getName(),
_t("DomainNameField.INVALID_DOMAIN", "Invalid domain name"),
"validation"
);
return false;
}
/**
* Check if the given hostname is valid.
*
* @param string $hostname
* @return bool True if this hostname is valid
*/
public function checkHostname($hostname)
{
return (bool)preg_match('/^([a-z0-9\*]+[\-\.])*([a-z0-9\*]+)$/', $hostname);
}
public function Type()
{
return 'text wildcarddomain';
}
}

View File

@ -8,6 +8,7 @@ use SilverStripe\CMS\Model\SiteTree;
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;
@ -29,6 +30,7 @@ use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\DB;
use SilverStripe\Security\Member;
use SilverStripe\Security\Permission;
use SilverStripe\Security\Group;
use SilverStripe\Versioned\Versioned;
use UnexpectedValueException;
@ -104,8 +106,7 @@ class Subsite extends DataObject
*/
public static $check_is_public = true;
/**
* @return array
/*** @return array
*/
private static $summary_fields = [
'Title',
@ -114,172 +115,154 @@ class Subsite extends DataObject
];
/**
* Set allowed themes
*
* @param array $themes - Numeric array of all themes which are allowed to be selected for all subsites.
*/
public static function set_allowed_themes($themes)
{
self::$allowed_themes = $themes;
}
* Set allowed themes
*
* @param array $themes - Numeric array of all themes which are allowed to be selected for all subsites.
*/
public static function set_allowed_themes($themes)
{self::$allowed_themes = $themes;
}
/**
* Gets the subsite currently set in the session.
*
* @uses ControllerSubsites->controllerAugmentInit()
* @return Subsite
*/
public static function currentSubsite()
{
// get_by_id handles caching so we don't have to
return DataObject::get_by_id(Subsite::class, self::currentSubsiteID());
}
/**
* Gets the subsite currently set in the session.
*
* @uses ControllerSubsites->controllerAugmentInit()
* @return Subsite
*/
public static function currentSubsite()
{// get_by_id handles caching so we don't have to
return DataObject::get_by_id(Subsite::class, self::currentSubsiteID());
}
/**
* This function gets the current subsite ID from the session. It used in the backend so Ajax requests
* use the correct subsite. The frontend handles subsites differently. It calls getSubsiteIDForDomain
* directly from ModelAsController::getNestedController. Only gets Subsite instances which have their
* {@link IsPublic} flag set to TRUE.
*
* You can simulate subsite access without creating virtual hosts by appending ?SubsiteID=<ID> to the request.
*
* @todo Pass $request object from controller so we don't have to rely on $_GET
*
* @param boolean $cache
* @return int ID of the current subsite instance
*/
public static function currentSubsiteID()
{
$id = NULL;
/**
* This function gets the current subsite ID from the session. It used in the backend so Ajax requests
* use the correct subsite. The frontend handles subsites differently. It calls getSubsiteIDForDomain
* directly from ModelAsController::getNestedController. Only gets Subsite instances which have their
* {@link IsPublic} flag set to TRUE.
*
* You can simulate subsite access without creating virtual hosts by appending ?SubsiteID=<ID> to the request.
*
* @todo Pass $request object from controller so we don't have to rely on $_GET
*
* @return int ID of the current subsite instance
*/
public static function currentSubsiteID()
{$id = null;
if (isset($_GET['SubsiteID'])) {
$id = (int)$_GET['SubsiteID'];
} else {
if (Subsite::$use_session_subsiteid) {
$id = Session::get('SubsiteID');
}
} elseif (Subsite::$use_session_subsiteid) {
$id = Session::get('SubsiteID');
}
if ($id === NULL) {
$id = self::getSubsiteIDForDomain();
}
if($id === null) {
$id = self::getSubsiteIDForDomain();
}
return (int)$id;
}
return (int)$id;
}
/**
* Switch to another subsite through storing the subsite identifier in the current PHP session.
* Only takes effect when {@link Subsite::$use_session_subsiteid} is set to TRUE.
*
* @param int|Subsite $subsite Either the ID of the subsite, or the subsite object itself
*/
public static function changeSubsite($subsite)
{
// Session subsite change only meaningful if the session is active.
// Otherwise we risk setting it to wrong value, e.g. if we rely on currentSubsiteID.
if (!Subsite::$use_session_subsiteid) {
return;
}
/**
* Switch to another subsite through storing the subsite identifier in the current PHP session.
* Only takes effect when {@link Subsite::$use_session_subsiteid} is set to TRUE.
*
* @param int|Subsite $subsite Either the ID of the subsite, or the subsite object itself
*/
public static function changeSubsite($subsite) {
// Session subsite change only meaningful if the session is active.
// Otherwise we risk setting it to wrong value, e.g. if we rely on currentSubsiteID.
if (!Subsite::$use_session_subsiteid) return;
if (is_object($subsite)) {
$subsiteID = $subsite->ID;
} else {
$subsiteID = $subsite;
}
if(is_object($subsite)) $subsiteID = $subsite->ID;
else $subsiteID = $subsite;
Session::set('SubsiteID', (int)$subsiteID);
Session::set('SubsiteID', (int)$subsiteID);
// Set locale
if (is_object($subsite) && $subsite->Language != '') {
$locale = i18n::get_locale_from_lang($subsite->Language);
if ($locale) {
i18n::set_locale($locale);
}
}
// Set locale
if (is_object($subsite) && $subsite->Language != '') {
$locale = i18n::get_locale_from_lang($subsite->Language);
if($locale) {
i18n::set_locale($locale);
}
}
Permission::flush_permission_cache();
}
Permission::flush_permission_cache();
}
/**
* Get a matching subsite for the given host, or for the current HTTP_HOST.
* Supports "fuzzy" matching of domains by placing an asterisk at the start of end of the string,
* for example matching all subdomains on *.example.com with one subsite,
* 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.
* @return int Subsite ID
*/
public static function getSubsiteIDForDomain($host = null, $checkPermissions = true)
{
if ($host == null && isset($_SERVER['HTTP_HOST'])) {
$host = $_SERVER['HTTP_HOST'];
}
/**
* Get a matching subsite for the given host, or for the current HTTP_HOST.
* Supports "fuzzy" matching of domains by placing an asterisk at the start of end of the string,
* for example matching all subdomains on *.example.com with one subsite,
* 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.
* @return int Subsite ID
*/
public static function getSubsiteIDForDomain($host = null, $checkPermissions = true) {
if($host == null && isset($_SERVER['HTTP_HOST'])) {
$host = $_SERVER['HTTP_HOST'];
}
$matchingDomains = null;
$cacheKey = null;
if ($host) {
if (!self::$strict_subdomain_matching) {
$host = preg_replace('/^www\./', '', $host);
}
$matchingDomains = null;
$cacheKey = null;
if ($host) {
if(!self::$strict_subdomain_matching) $host = preg_replace('/^www\./', '', $host);
$cacheKey = implode('_', [$host, Member::currentUserID(), self::$check_is_public]);
if (isset(self::$_cache_subsite_for_domain[$cacheKey])) {
return self::$_cache_subsite_for_domain[$cacheKey];
}
$SQL_host = Convert::raw2sql($host);
$matchingDomains = DataObject::get(
SubsiteDomain::class,
"'$SQL_host' LIKE replace(\"SubsiteDomain\".\"Domain\",'*','%')",
"\"IsPrimary\" DESC"
)->innerJoin('Subsite',
"\"Subsite\".\"ID\" = \"SubsiteDomain\".\"SubsiteID\" AND \"Subsite\".\"IsPublic\"=1");
}
$SQL_host = Convert::raw2sql($host);
$matchingDomains = DataObject::get(
SubsiteDomain::class,
"'$SQL_host' LIKE replace(\"SubsiteDomain\".\"Domain\",'*','%')",
"\"IsPrimary\" DESC"
)->innerJoin('Subsite', "\"Subsite\".\"ID\" = \"SubsiteDomain\".\"SubsiteID\" AND \"Subsite\".\"IsPublic\"=1");
}
if ($matchingDomains && $matchingDomains->Count()) {
$subsiteIDs = array_unique($matchingDomains->column('SubsiteID'));
$subsiteDomains = array_unique($matchingDomains->column('Domain'));
if (sizeof($subsiteIDs) > 1) {
throw new UnexpectedValueException(sprintf(
"Multiple subsites match on '%s': %s",
$host,
implode(',', $subsiteDomains)
));
}
if($matchingDomains && $matchingDomains->Count()) {
$subsiteIDs = array_unique($matchingDomains->column('SubsiteID'));
$subsiteDomains = array_unique($matchingDomains->column('Domain'));
if(sizeof($subsiteIDs) > 1) {
throw new UnexpectedValueException(sprintf(
"Multiple subsites match on '%s': %s",
$host,
implode(',', $subsiteDomains)
));
}
$subsiteID = $subsiteIDs[0];
} else {
if ($default = DataObject::get_one(Subsite::class, "\"DefaultSite\" = 1")) {
// Check for a 'default' subsite
$subsiteID = $default->ID;
} else {
// Default subsite id = 0, the main site
$subsiteID = 0;
}
}
$subsiteID = $subsiteIDs[0];
} else {
if($default = DataObject::get_one(Subsite::class, "\"DefaultSite\" = 1")) {
// Check for a 'default' subsite
$subsiteID = $default->ID;
} else {
// Default subsite id = 0, the main site
$subsiteID = 0;
}}
if ($cacheKey) {
self::$_cache_subsite_for_domain[$cacheKey] = $subsiteID;
}
if ($cacheKey) {
self::$_cache_subsite_for_domain[$cacheKey] = $subsiteID;
}
return $subsiteID;
}
return $subsiteID;
}
/**
*
* @param string $className
* @param string $filter
* @param string $sort
* @param string $join
* @param string $limit
* @return DataList
*/
public static function get_from_all_subsites($className, $filter = "", $sort = "", $join = "", $limit = "")
{
$result = DataObject::get($className, $filter, $sort, $join, $limit);
$result = $result->setDataQueryParam('Subsite.filter', false);
return $result;
}
/**
*
* @param string $className
* @param string $filter
* @param string $sort
* @param string $join
* @param string $limit
* @return DataList
*/
public static function get_from_all_subsites($className, $filter = "", $sort = "", $join = "", $limit = "") {
$result = DataObject::get($className, $filter, $sort, $join, $limit);
$result = $result->setDataQueryParam('Subsite.filter', false);
return $result;
}
/**
* Disable the sub-site filtering; queries will select from all subsites
@ -307,18 +290,18 @@ class Subsite extends DataObject
{
$subsites = Subsite::get();
if ($includeMainSite) {
$subsites = $subsites->toArray();
if($includeMainSite) {
$subsites = $subsites->toArray();
$mainSite = new Subsite();
$mainSite->Title = $mainSiteTitle;
array_unshift($subsites, $mainSite);
$mainSite = new Subsite();
$mainSite->Title = $mainSiteTitle;
array_unshift($subsites, $mainSite);
$subsites = ArrayList::create($subsites);
}
$subsites = ArrayList::create($subsites);
}
return $subsites;
}
return $subsites;
}
/*
* Returns an ArrayList of the subsites accessible to the current user.
@ -336,30 +319,30 @@ class Subsite extends DataObject
return new ArrayList();
}
if (!is_object($member)) {
$member = DataObject::get_by_id('SilverStripe\\Security\\Member', $member);
$member = DataObject::get_by_id(Member::class, $member);
}
$subsites = new ArrayList();
$subsites = new ArrayList();
// Collect subsites for all sections.
$menu = CMSMenu::get_viewable_menu_items();
foreach ($menu as $candidate) {
if ($candidate->controller) {
$accessibleSites = singleton($candidate->controller)->sectionSites(
$includeMainSite,
$mainSiteTitle,
$member
);
// Collect subsites for all sections.
$menu = CMSMenu::get_viewable_menu_items();
foreach($menu as $candidate) {
if ($candidate->controller) {
$accessibleSites = singleton($candidate->controller)->sectionSites(
$includeMainSite,
$mainSiteTitle,
$member
);
// Replace existing keys so no one site appears twice.
$subsites->merge($accessibleSites);
}
}
// Replace existing keys so no one site appears twice.
$subsites->merge($accessibleSites);
}
}
$subsites->removeDuplicates();
$subsites->removeDuplicates();
return $subsites;
}
return $subsites;
}
/**
* Return the subsites that the current user can access by given permission.
@ -371,12 +354,7 @@ class Subsite extends DataObject
* @param $member
* @return DataList 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
if (!$member) {
@ -386,21 +364,18 @@ class Subsite extends DataObject
return new ArrayList();
}
if (!is_object($member)) {
$member = DataObject::get_by_id('SilverStripe\\Security\\Member', $member);
$member = DataObject::get_by_id(Member::class, $member);
}
// Rationalise permCode argument
if (is_array($permCode)) {
$SQL_codes = "'" . implode("', '", Convert::raw2sql($permCode)) . "'";
} else {
$SQL_codes = "'" . Convert::raw2sql($permCode) . "'";
}
// Rationalise permCode argument
if(is_array($permCode)) $SQL_codes = "'" . implode("', '", Convert::raw2sql($permCode)) . "'";
else $SQL_codes = "'" . Convert::raw2sql($permCode) . "'";
// Cache handling
$cacheKey = $SQL_codes . '-' . $member->ID . '-' . $includeMainSite . '-' . $mainSiteTitle;
if (isset(self::$_cache_accessible_sites[$cacheKey])) {
return self::$_cache_accessible_sites[$cacheKey];
}
// Cache handling
$cacheKey = $SQL_codes . '-' . $member->ID . '-' . $includeMainSite . '-' . $mainSiteTitle;
if(isset(self::$_cache_accessible_sites[$cacheKey])) {
return self::$_cache_accessible_sites[$cacheKey];
}
$subsites = DataList::create(Subsite::class)
->where("\"Subsite\".\"Title\" != ''")
@ -412,9 +387,7 @@ class Subsite extends DataObject
->innerJoin('Permission',
"\"Group\".\"ID\"=\"Permission\".\"GroupID\" AND \"Permission\".\"Code\" IN ($SQL_codes, 'CMS_ACCESS_LeftAndMain', 'ADMIN')");
if (!$subsites) {
$subsites = new ArrayList();
}
if(!$subsites) $subsites = new ArrayList();
$rolesSubsites = DataList::create(Subsite::class)
->where("\"Subsite\".\"Title\" != ''")
@ -428,19 +401,15 @@ class Subsite extends DataObject
->innerJoin('PermissionRoleCode',
"\"PermissionRole\".\"ID\"=\"PermissionRoleCode\".\"RoleID\" AND \"PermissionRoleCode\".\"Code\" IN ($SQL_codes, 'CMS_ACCESS_LeftAndMain', 'ADMIN')");
if (!$subsites && $rolesSubsites) {
return $rolesSubsites;
}
if(!$subsites && $rolesSubsites) return $rolesSubsites;
$subsites = new ArrayList($subsites->toArray());
$subsites = new ArrayList($subsites->toArray());
if ($rolesSubsites) {
foreach ($rolesSubsites as $subsite) {
if (!$subsites->find('ID', $subsite->ID)) {
$subsites->push($subsite);
}
}
}
if($rolesSubsites) foreach($rolesSubsites as $subsite) {
if(!$subsites->find('ID', $subsite->ID)) {
$subsites->push($subsite);
}
}
if ($includeMainSite) {
if (!is_array($permCode)) {
@ -528,24 +497,20 @@ class Subsite extends DataObject
user_error('Permissions must be passed to Subsite::hasMainSitePermission as an array', E_USER_ERROR);
}
if (!$member && $member !== FALSE) {
if (!$member && $member !== false) {
$member = Member::currentUser();
}
if (!$member) {
return false;
}
if(!$member) return false;
if (!in_array("ADMIN", $permissionCodes)) {
$permissionCodes[] = "ADMIN";
}
if(!in_array("ADMIN", $permissionCodes)) $permissionCodes[] = "ADMIN";
$SQLa_perm = Convert::raw2sql($permissionCodes);
$SQL_perms = join("','", $SQLa_perm);
$memberID = (int)$member->ID;
$SQLa_perm = Convert::raw2sql($permissionCodes);
$SQL_perms = join("','", $SQLa_perm);
$memberID = (int)$member->ID;
// Count this user's groups which can access the main site
$groupCount = DB::query("
// Count this user's groups which can access the main site
$groupCount = DB::query("
SELECT COUNT(\"Permission\".\"ID\")
FROM \"Permission\"
INNER JOIN \"Group\" ON \"Group\".\"ID\" = \"Permission\".\"GroupID\" AND \"Group\".\"AccessAllSubsites\" = 1
@ -554,8 +519,8 @@ class Subsite extends DataObject
AND \"MemberID\" = {$memberID}
")->value();
// Count this user's groups which have a role that can access the main site
$roleCount = DB::query("
// Count this user's groups which have a role that can access the main site
$roleCount = DB::query("
SELECT COUNT(\"PermissionRoleCode\".\"ID\")
FROM \"Group\"
INNER JOIN \"Group_Members\" ON \"Group_Members\".\"GroupID\" = \"Group\".\"ID\"
@ -582,13 +547,13 @@ class Subsite extends DataObject
'Theme' => 'Varchar',
'Language' => 'Varchar(6)',
// Used to hide unfinished/private subsites from public view.
// If unset, will default to true
'IsPublic' => 'Boolean',
// Used to hide unfinished/private subsites from public view.
// If unset, will default to true
'IsPublic' => 'Boolean',
// Comma-separated list of disallowed page types
'PageTypeBlacklist' => 'Text',
];
// Comma-separated list of disallowed page types
'PageTypeBlacklist' => 'Text',
];
/**
*
@ -603,41 +568,40 @@ class Subsite extends DataObject
* @var array
*/
private static $belongs_many_many = [
"Groups" => "SilverStripe\\Security\\Group",
"Groups" => Group::class,
];
/**
*
* @var array
*/
private static $defaults = [
'IsPublic' => 1
];
/**
*
* @var array
*/
private static $defaults = [
'IsPublic' => 1
];
/**
*
* @var array
*/
private static $searchable_fields = [
'Title',
'Domains.Domain',
'IsPublic',
];
/**
*
* @var array
*/
private static $searchable_fields = [
'Title',
'Domains.Domain',
'IsPublic',
];
/**
*
* @var string
*/
private static $default_sort = "\"Title\" ASC";
/**
*
* @var string
*/
private static $default_sort = "\"Title\" ASC";
/**
* @todo Possible security issue, don't grant edit permissions to everybody.
* @return boolean
*/
public function canEdit($member = false)
{
return true;
}
/**
* @todo Possible security issue, don't grant edit permissions to everybody.
* @return boolean
*/
public function canEdit($member = false)
{return true;
}
/**
* Show the configuration fields for each subsite
@ -646,7 +610,7 @@ class Subsite extends DataObject
*/
public function getCMSFields()
{
if ($this->ID != 0) {
if ($this->ID!=0) {
$domainTable = new GridField(
"Domains",
_t('Subsite.DomainsListTitle', "Domains"),
@ -656,18 +620,17 @@ class Subsite extends DataObject
} else {
$domainTable = new LiteralField(
'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(
'Language',
$this->fieldLabel('Language'),
(new IntlLocales)->getLocales()
Injector::inst()->get(IntlLocales::class)->getLocales()
);
$pageTypeMap = [];
$pageTypeMap = array();
$pageTypes = SiteTree::page_type_classes();
foreach ($pageTypes as $pageType) {
$pageTypeMap[$pageType] = singleton($pageType)->i18n_singular_name();
@ -679,11 +642,11 @@ class Subsite extends DataObject
new Tab(
'Configuration',
_t('Subsite.TabTitleConfig', 'Configuration'),
new HeaderField('ConfigurationHeader', $this->getClassName() . ' configuration', 2),
new HeaderField('ConfigForSubsiteHeaderField', $this->getClassName() . ' configuration'),
new TextField('Title', $this->fieldLabel('Title'), $this->Title),
new HeaderField(
'DomainsHeader',
'DomainsForSubsiteHeaderField',
_t('Subsite.DomainsHeadline', "Domains for this subsite")
),
$domainTable,
@ -692,103 +655,98 @@ class Subsite extends DataObject
new CheckboxField('DefaultSite', $this->fieldLabel('DefaultSite'), $this->DefaultSite),
new CheckboxField('IsPublic', $this->fieldLabel('IsPublic'), $this->IsPublic),
new DropdownField('Theme', $this->fieldLabel('Theme'), $this->allowedThemes(), $this->Theme),
new DropdownField('Theme',$this->fieldLabel('Theme'), $this->allowedThemes(), $this->Theme),
new LiteralField(
'PageTypeBlacklistToggle',
sprintf(
'<div class="field"><a href="#" id="PageTypeBlacklistToggle">%s</a></div>',
_t('Subsite.PageTypeBlacklistField', 'Disallow page types?')
)
),
new CheckboxSetField(
'PageTypeBlacklist',
false,
$pageTypeMap
)
)
),
new HiddenField('ID', '', $this->ID),
new HiddenField('IsSubsite', '', 1)
);
new LiteralField(
'PageTypeBlacklistToggle',
sprintf(
'<div class="field"><a href="#" id="PageTypeBlacklistToggle">%s</a></div>',
_t('Subsite.PageTypeBlacklistField', 'Disallow page types?')
)
),
new CheckboxSetField(
'PageTypeBlacklist',
false,
$pageTypeMap
)
)
),
new HiddenField('ID', '', $this->ID),
new HiddenField('IsSubsite', '', 1)
);
$subsiteTabs->addExtraClass('subsite-model');
$subsiteTabs->addExtraClass('subsite-model');
$this->extend('updateCMSFields', $fields);
return $fields;
}
$this->extend('updateCMSFields', $fields);
return $fields;
}
/**
*
* @param boolean $includerelations
* @return array
*/
public function fieldLabels($includerelations = true)
{
$labels = parent::fieldLabels($includerelations);
$labels['Title'] = _t('Subsites.TitleFieldLabel', 'Subsite Name');
$labels['RedirectURL'] = _t('Subsites.RedirectURLFieldLabel', 'Redirect URL');
$labels['DefaultSite'] = _t('Subsites.DefaultSiteFieldLabel', 'Default site');
$labels['Theme'] = _t('Subsites.ThemeFieldLabel', 'Theme');
$labels['Language'] = _t('Subsites.LanguageFieldLabel', 'Language');
$labels['IsPublic'] = _t('Subsites.IsPublicFieldLabel', 'Enable public access');
$labels['PageTypeBlacklist'] = _t('Subsites.PageTypeBlacklistFieldLabel', 'Page Type Blacklist');
$labels['Domains.Domain'] = _t('Subsites.DomainFieldLabel', 'Domain');
$labels['PrimaryDomain'] = _t('Subsites.PrimaryDomainFieldLabel', 'Primary Domain');
/**
*
* @param boolean $includerelations
* @return array
*/
public function fieldLabels($includerelations = true) {
$labels = parent::fieldLabels($includerelations);
$labels['Title'] = _t('Subsites.TitleFieldLabel', 'Subsite Name');
$labels['RedirectURL'] = _t('Subsites.RedirectURLFieldLabel', 'Redirect URL');
$labels['DefaultSite'] = _t('Subsites.DefaultSiteFieldLabel', 'Default site');
$labels['Theme'] = _t('Subsites.ThemeFieldLabel', 'Theme');
$labels['Language'] = _t('Subsites.LanguageFieldLabel', 'Language');
$labels['IsPublic'] = _t('Subsites.IsPublicFieldLabel', 'Enable public access');
$labels['PageTypeBlacklist'] = _t('Subsites.PageTypeBlacklistFieldLabel', 'Page Type Blacklist');
$labels['Domains.Domain'] = _t('Subsites.DomainFieldLabel', 'Domain');
$labels['PrimaryDomain'] = _t('Subsites.PrimaryDomainFieldLabel', 'Primary Domain');
return $labels;
}
return $labels;
}
/**
* Return the themes that can be used with this subsite, as an array of themecode => description
*
* @return array
*/
public function allowedThemes()
{
if ($themes = $this->stat('allowed_themes')) {
return ArrayLib::valuekey($themes);
} else {
$themes = [];
if (is_dir('../themes/')) {
foreach (scandir('../themes/') as $theme) {
if ($theme[0] == '.') {
continue;
}
$theme = strtok($theme, '_');
$themes[$theme] = $theme;
}
ksort($themes);
}
return $themes;
}
}
/**
/**
* @return string Current locale of the subsite
*/
public function getLanguage()
{
if ($this->getField('Language')) {
return $this->getField('Language');
} else {
return i18n::get_locale();
}
}
* Return the themes that can be used with this subsite, as an array of themecode => description
*
* @return array
*/
public function allowedThemes()
{if($themes = $this->stat('allowed_themes')) {
return ArrayLib::valuekey($themes);
} else {
$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;
}
}
/**
*
* @return ValidationResult
*/
public function validate()
{
$result = parent::validate();
if (!$this->Title) {
$result->error(_t('Subsite.ValidateTitle', 'Please add a "Title"'));
}
return $result;
}
/**
* @return string Current locale of the subsite
*/
public function getLanguage() {
if($this->getField('Language')) {
return $this->getField('Language');
} else {
return i18n::get_locale();
}
}
/**
*
* @return ValidationResult
*/
public function validate() {
$result = parent::validate();
if(!$this->Title) {
$result->error(_t('Subsite.ValidateTitle', 'Please add a "Title"'));
}
return $result;
}
/**
* Whenever a Subsite is written, rewrite the hostmap
@ -809,26 +767,27 @@ class Subsite extends DataObject
*/
public function domain()
{
if ($this->ID) {
$domains = DataObject::get(SubsiteDomain::class, "\"SubsiteID\" = $this->ID", "\"IsPrimary\" DESC", "", 1);
if ($domains && $domains->Count() > 0) {
$domain = $domains->First()->Domain;
// If there are wildcards in the primary domain (not recommended), make some
// educated guesses about what to replace them with:
$domain = preg_replace('/\.\*$/', ".$_SERVER[HTTP_HOST]", $domain);
// Default to "subsite." prefix for first wildcard
// TODO Whats the significance of "subsite" in this context?!
$domain = preg_replace('/^\*\./', "subsite.", $domain);
// *Only* removes "intermediate" subdomains, so 'subdomain.www.domain.com' becomes 'subdomain.domain.com'
$domain = str_replace('.www.', '.', $domain);
return $domain;
}
// SubsiteID = 0 is often used to refer to the main site, just return $_SERVER['HTTP_HOST']
} else {
return $_SERVER['HTTP_HOST'];
// Get best SubsiteDomain object
$domainObject = $this->getPrimarySubsiteDomain();
if ($domainObject) {
return $domainObject->SubstitutedDomain;
}
// If there are no objects, default to the current hostname
return $_SERVER['HTTP_HOST'];
}
/**
* Finds the primary {@see SubsiteDomain} object for this subsite
*
* @return SubsiteDomain
*/
public function getPrimarySubsiteDomain()
{
return $this
->Domains()
->sort('"IsPrimary" DESC')
->first();
}
/**
@ -841,49 +800,53 @@ class Subsite extends DataObject
}
/**
*
* Get the absolute URL for this subsite
* @return string
*/
public function absoluteBaseURL()
{
return "http://" . $this->domain() . Director::baseURL();
// Get best SubsiteDomain object
$domainObject = $this->getPrimarySubsiteDomain();
if ($domainObject) {
return $domainObject->absoluteBaseURL();
}
// Fall back to the current base url
return Director::absoluteBaseURL();
}
/**
* @todo getClassName is redundant, already stored as a database field?
*/
public function getClassName()
{
return $this->class;
}
/**
* @todo getClassName is redundant, already stored as a database field?
*/
public function getClassName() {
return $this->class;
}
/**
* Javascript admin action to duplicate this subsite
*
* @return string - javascript
*/
public function adminDuplicate()
{
$newItem = $this->duplicate();
$message = _t(
'Subsite.CopyMessage',
'Created a copy of {title}',
['title' => Convert::raw2js($this->Title)]
);
/**
* Javascript admin action to duplicate this subsite
*
* @return string - javascript
*/
public function adminDuplicate()
{$newItem = $this->duplicate();
$message = _t(
'Subsite.CopyMessage',
'Created a copy of {title}',
['title' => Convert::raw2js($this->Title)]
);
return <<<JS
return <<<JS
statusMessage($message, 'good');
$('Form_EditForm').loadURLFromServer('admin/subsites/show/$newItem->ID');
JS;
}
}
/**
* Make this subsite the current one
*/
public function activate()
{
Subsite::changeSubsite($this);
}
/**
* Make this subsite the current one
*/
public function activate() {
Subsite::changeSubsite($this);
}
/**
*
@ -897,28 +860,26 @@ JS;
}
$SQL_permissionCodes = Convert::raw2sql($permissionCodes);
$SQL_permissionCodes = join("','", $SQL_permissionCodes);
$SQL_permissionCodes = join("','", $SQL_permissionCodes);
return DataObject::get(
'SilverStripe\\Security\\Member',
Member::class,
"\"Group\".\"SubsiteID\" = $this->ID AND \"Permission\".\"Code\" IN ('$SQL_permissionCodes')",
'',
"LEFT JOIN \"Group_Members\" ON \"Member\".\"ID\" = \"Group_Members\".\"MemberID\"
LEFT JOIN \"Group\" ON \"Group\".\"ID\" = \"Group_Members\".\"GroupID\"
LEFT JOIN \"Permission\" ON \"Permission\".\"GroupID\" = \"Group\".\"ID\""
);
);
}
}
/**
* Duplicate this subsite
*/
public function duplicate($doWrite = true) {
$duplicate = parent::duplicate($doWrite);
/**
* Duplicate this subsite
*/
public function duplicate($doWrite = true)
{
$duplicate = parent::duplicate($doWrite);
$oldSubsiteID = Session::get('SubsiteID');
self::changeSubsite($this->ID);
$oldSubsiteID = Session::get('SubsiteID');
self::changeSubsite($this->ID);
/*
* Copy data from this object to the given subsite. Does this using an iterative depth-first search.
@ -931,24 +892,24 @@ JS;
list($sourceParentID, $destParentID) = array_pop($stack);
$children = Versioned::get_by_stage('Page', 'Live', "\"ParentID\" = $sourceParentID", '');
if ($children) {
foreach ($children as $child) {
self::changeSubsite($duplicate->ID); //Change to destination subsite
if($children) {
foreach($children as $child) {
self::changeSubsite($duplicate->ID); //Change to destination subsite
$childClone = $child->duplicateToSubsite($duplicate, false);
$childClone->ParentID = $destParentID;
$childClone->writeToStage('Stage');
$childClone->publish('Stage', 'Live');
$childClone = $child->duplicateToSubsite($duplicate, false);
$childClone->ParentID = $destParentID;
$childClone->writeToStage('Stage');
$childClone->publish('Stage', 'Live');
self::changeSubsite($this->ID); //Change Back to this subsite
self::changeSubsite($this->ID); //Change Back to this subsite
array_push($stack, [$child->ID, $childClone->ID]);
}
}
}
self::changeSubsite($oldSubsiteID);
self::changeSubsite($oldSubsiteID);
return $duplicate;
}
return $duplicate;
}
}

View File

@ -2,22 +2,26 @@
namespace SilverStripe\Subsites\Model;
use SilverStripe\Core\Convert;
use SilverStripe\Control\Director;
use SilverStripe\Control\Controller;
use SilverStripe\Forms\CheckboxField;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\TextField;
use SilverStripe\Forms\OptionsetField;
use SilverStripe\ORM\DataObject;
/**
* @property text Domain domain name of this subsite. Do not include the URL scheme here
* @property bool IsPrimary Is this the primary subdomain?
* @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
* that any links to this subsite should use the current protocol, and that both are supported.
* @property string $SubstitutedDomain Domain name with all wildcards filled in
* @property string $FullProtocol Full protocol including ://
* @property bool $IsPrimary Is this the primary subdomain?
*/
class SubsiteDomain extends DataObject
{
private static $table_name = 'SubsiteDomain';
private static $table_name = 'SubsiteDomain';
/**
*
@ -25,52 +29,100 @@ class SubsiteDomain extends DataObject
*/
private static $default_sort = "\"IsPrimary\" DESC";
/**
*
* @var array
*/
private static $db = [
"Domain" => "Varchar(255)",
/** *
* @var array
*/
private static $db = [
"Domain" => "Varchar(255)",
"Protocol" => "Enum('http,https,automatic','automatic')",
"IsPrimary" => "Boolean",
];
];
/*** Specifies that this subsite is http only
*/
const PROTOCOL_HTTP = 'http';
/**
*
* @var array
* Specifies that this subsite is https only
*/
private static $has_one = [
"Subsite" => Subsite::class,
];
const PROTOCOL_HTTPS = 'https';
/**
*
* @var array
* Specifies that this subsite supports both http and https
*/
private static $summary_fields = [
'Domain',
'IsPrimary',
];
const PROTOCOL_AUTOMATIC = 'automatic';
/**
* Whenever a Subsite Domain is written, rewrite the hostmap
* Get the descriptive title for this domain
*
* @return void
* @return string
*/
public function onAfterWrite()
public function getTitle()
{
Subsite::writeHostMap();
return $this->Domain;
}
/**
*
* @return \FieldList
*
* @var array
*/
private static $has_one = [
"Subsite" => Subsite::class,
];
/**
*@config
* @var array
*/
private static $summary_fields=[
'Domain',
'IsPrimary',
];
/*** @config
* @var array
*/
public function getCMSFields()
{
$fields = new FieldList(
new TextField('Domain', $this->fieldLabel('Domain'), null, 255),
new CheckboxField('IsPrimary', $this->fieldLabel('IsPrimary'))
);
private static $casting = array(
'SubstitutedDomain' => 'Varchar',
'FullProtocol' => 'Varchar',
'AbsoluteLink' => 'Varchar',
);
/**
* Whenever a Subsite Domain is written, rewrite the hostmap
*
* @return void
*/
public function onAfterWrite()
{Subsite::writeHostMap();
}
/**
*
* @return \FieldList
*/
public function getCMSFields()
{
$protocols = array(
self::PROTOCOL_HTTP => _t('SubsiteDomain.PROTOCOL_HTTP', 'http://'),
self::PROTOCOL_HTTPS => _t('SubsiteDomain.PROTOCOL_HTTPS', 'https://'),
self::PROTOCOL_AUTOMATIC => _t('SubsiteDomain.PROTOCOL_AUTOMATIC', 'Automatic')
);$fields = new FieldList(
WildcardDomainField::create('Domain', $this->fieldLabel('Domain'), null, 255)
->setDescription(_t(
'SubsiteDomain.DOMAIN_DESCRIPTION',
'Hostname of this subsite (exclude protocol). Allows wildcards (*).'
)),
OptionsetField::create('Protocol', $this->fieldLabel('Protocol'), $protocols)
->setDescription(_t(
'SubsiteDomain.PROTOCOL_DESCRIPTION',
'When generating links to this subsite, use the selected protocol. <br />' .
'Selecting \'Automatic\' means subsite links will default to the current protocol.'
)), CheckboxField::create('IsPrimary', $this->fieldLabel('IsPrimary'))
->setDescription(_t(
'SubsiteDomain.PROTOCOL_DESCRIPTION',
'Mark this as the default domain for this subsite'
)));
$this->extend('updateCMSFields', $fields);
return $fields;
@ -85,7 +137,8 @@ class SubsiteDomain extends DataObject
{
$labels = parent::fieldLabels($includerelations);
$labels['Domain'] = _t('SubsiteDomain.DOMAIN', 'Domain');
$labels['IsPrimary'] = _t('SubsiteDomain.IS_PRIMARY', 'Is Primary Domain');
$labels['Protocol'] = _t('SubsiteDomain.Protocol', 'Protocol');
$labels['IsPrimary'] = _t('SubsiteDomain.IS_PRIMARY', 'Is Primary Domain?');
return $labels;
}
@ -97,8 +150,85 @@ class SubsiteDomain extends DataObject
public function onBeforeWrite()
{
parent::onBeforeWrite();
//strip out any HTML to avoid XSS attacks
$this->Domain = Convert::html2raw($this->Domain);
}
/**
* Get the link to this subsite
*
* @return string
*/
public function Link()
{
return $this->getFullProtocol() . $this->Domain;
}
/**
* Gets the full protocol (including ://) for this domain
*
* @return string
*/
public function getFullProtocol()
{
switch ($this->Protocol) {
case self::PROTOCOL_HTTPS:
{
return 'https://';
}
case self::PROTOCOL_HTTP:
{
return 'http://';
}
default:
{
return Director::protocol();
}
}
}
/**
* Retrieves domain name with wildcards substituted with actual values
*
* @todo Refactor domains into separate wildcards / primary domains
*
* @return string
*/
public function getSubstitutedDomain()
{
$currentHost = $_SERVER['HTTP_HOST'];
// If there are wildcards in the primary domain (not recommended), make some
// educated guesses about what to replace them with:
$domain = preg_replace('/\.\*$/', ".{$currentHost}", $this->Domain );
// Default to "subsite." prefix for first wildcard
// TODO Whats the significance of "subsite" inthiscontext?!
$domain = preg_replace('/^\*\./', "subsite.", $domain);
// *Only* removes "intermediate" subdomains, so 'subdomain.www.domain.com' becomes 'subdomain.domain.com'
$domain = str_replace('.www.', '.', $domain);
return $domain;
}
/**
* Get absolute link for this domain
*
* @return string
*/
public function getAbsoluteLink()
{
return $this->getFullProtocol() . $this->getSubstitutedDomain();
}
/**
* Get absolute baseURL for this domain
*
* @return string
*/
public function absoluteBaseURL()
{
return Controller::join_links(
$this->getAbsoluteLink(),
Director::baseURL());
}
}

View File

@ -13,25 +13,27 @@ use SilverStripe\Versioned\Versioned;
/**
* 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 process a large site outside of the UI.
*
* Can be used to batch-add new pages after subsite creation, or simply to
* process a large site outside of the UI.
*
* Example: sake dev/tasks/SubsiteCopyPagesTask from=<subsite-source> to=<subsite-target>
*
* @package subsites
*/
class SubsiteCopyPagesTask extends BuildTask
{
protected $title = 'Copy pages to different subsite';
protected $description = '';
function run($request)
public function run($request)
{
$subsiteFromId = $request->getVar('from');
if (!is_numeric($subsiteFromId)) {
throw new InvalidArgumentException('Missing "from" parameter');
}
$subsiteFrom = DataObject::get_by_id(Subsite::class, $subsiteFromId);
$subsiteFrom = DataObject::get_by_id('Subsite', $subsiteFromId);
if (!$subsiteFrom) {
throw new InvalidArgumentException('Subsite not found');
}
@ -40,7 +42,7 @@ class SubsiteCopyPagesTask extends BuildTask
if (!is_numeric($subsiteToId)) {
throw new InvalidArgumentException('Missing "to" parameter');
}
$subsiteTo = DataObject::get_by_id(Subsite::class, $subsiteToId);
$subsiteTo = DataObject::get_by_id('Subsite', $subsiteToId);
if (!$subsiteTo) {
throw new InvalidArgumentException('Subsite not found');
}
@ -53,12 +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
// issues with having to check whether or not the new parents have been added to the site tree
// when a page, etc, is duplicated
$stack = [[0, 0]];
$stack = array(array(0,0));
while (count($stack) > 0) {
list($sourceParentID, $destParentID) = array_pop($stack);
$children = Versioned::get_by_stage('SilverStripe\\CMS\\Model\\SiteTree', 'Live',
"\"ParentID\" = $sourceParentID", '');
$children = Versioned::get_by_stage(SiteTree::class, 'Live', "\"ParentID\" = $sourceParentID", '');
if ($children) {
foreach ($children as $child) {
@ -74,7 +75,7 @@ class SubsiteCopyPagesTask extends BuildTask
$childClone->ParentID = $destParentID;
$childClone->writeToStage('Stage');
$childClone->publish('Stage', 'Live');
array_push($stack, [$child->ID, $childClone->ID]);
array_push($stack, array($child->ID, $childClone->ID));
$this->log(sprintf('Copied "%s" (#%d, %s)', $child->Title, $child->ID, $child->Link()));
}
@ -84,7 +85,7 @@ class SubsiteCopyPagesTask extends BuildTask
}
}
function log($msg)
public function log($msg)
{
echo $msg . "\n";
}

View File

@ -1,6 +1,7 @@
{
"name": "silverstripe/subsites",
"description": "Run multiple sites from a single SilverStripe install.",
"license": "BSD-3-Clause",
"type": "silverstripe-module",
"keywords": ["silverstripe", "subsites", "multisite"],
"authors": [

View File

@ -95,6 +95,7 @@ body.SubsiteAdmin .right form #URL .fieldgroup * {
display:none;
}
#Root_DetailsView .subsites-move-dropdown{
#Root_DetailsView .subsites-move-dropdown,
#Form_ItemEditForm .subsites-move-dropdown {
display:block;
}

View File

@ -1,84 +1,9 @@
## Introduction
# Subsites
Subsites is a module to allow you manage multiple related sites from a single CMS interface.
When Subsites is installed your existing site is defined as the main site, you will be then be able to create related subsites under the main site.
One of the benefits of subsites is that it is easy to copy pages between the subsites and you have access to all of
the assets across all of the subsites.
Subsites is not for running unrelated websites on a single SilverStripe instance so if 2 sites have different vhosts
you will not be able to run them with Subsites on a single SilverStripe instance.
With Subsites you can set up users to have access to all subsites or just a selection of subsites.
## When to use the Subsites module
* You have a clear parent/main website and a set of subsites associated with it.
* You have a group of simple, commonly themed websites
* Your websites contain some overlapping content, and you want to avoid the redundancy of making the same update to content that exists on multiple CMS installations.
* You have simple content websites that use the same templates and modules, and are managed by the same team, but have different themes.
* Content editing of the subsites is through the same team, so there is no problem around editors having access to all the files for each subsite, nor around having CMS admin(s) who control access to all the sites.
## When to avoid the Subsites module
* You have constraints on what content CMS editors can and cannot view between subsites.
* You have a business critical website, and adding complexity around changes and releases for subsites creates unwanted risk.
* There is a significant security risk around putting the websites into the same CMS installation. This could be the risk of content bleeding between sites, or the risk of an editor on one site gaining access to content on the other site.
* Websites that are distinct and use customised modules and/or bespoke code
* Websites that are owned by different business units and/or managed by different development and web teams. (not technical, it just organisational complexity).
It is important to remember that the only unique trait each site can have is its theme (look and feel), whilst all the other building blocks of the site — the code, database, and modules — are shared. Therefore, one of the biggest drawbacks to using subsites is exposure to a single point of failure. If there is a bug in code that is only used on one subsite, it nevertheless affects all other subsites, because they share a codebase. Similarly, if the database becomes corrupt, all subsites are affected. Further, it is not possible to create or restore backups of any given subsite. Backups must represent and replace the entire collection of sites.
## Access
Access to certain subsites can be limited to administrators based on the groups they are in.
So for example if you had a couple of subsites you could create a group for each subsite and then specify that the
group had access to all subsites or just a specific subsites.
To access this functionality go to
Security -> Groups
![alt text](_images/subsite-admin-security-group.png "Groups")
Select the group you want to modify and then go to the Subsites tab
You can also limit the page types that are available for a subsite (but not the main site).
This can be done via accessing the particular subsite you want to amend via the Subsite admin section, underneath the
Subsite theme will be a link called 'Disallow page types?' clicking on that link will display a list of checkboxes for
all of the page types which can be selected to disable that page type for the subsite you are editing.
This is useful when you create a content editor and you do not want them to be able to add certain page types.
## Theme
A theme is group of templates, images and CSS for the look of a website.
When you are using Subsites you may have different themes installed for your site so you could apply different
themes for each subsite.
## Page types
Page types refer to the type of pages that can be set up on a site.
A page type will have certain features and functionality some examples on SilverStripe would be 'Page', 'HomePage'
and 'ErrorPage' these all differ to each other in what they would be used for so you would use Page for any pages
underneath the HomePage.
You would only have one HomePage for your site and you may have some logic to only allow you to create one of these
pages, ErrorPage would only be used for error pages and would be designed to be very minimal to work in situations
where the site is experiencing difficulties like no DB access.
You can set up a Subsite to only work with certain page types so you may have a page type with a contact form for a
particular department so you may set up a new subsite and not allow that page type to be used on a particular subsite.
You will not be able to filter Page Types for the main site.
## Assets
Assets are files that have been uploaded via the CMS.
It is suggested to use a naming convention for files designated to be used on a particular subsite or to create folders
for each subsite to help organise them.
[User guide](userguide/index.md)
## FAQ
### How can I restrict a content author to a particular subsite?

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

View File

Before

Width:  |  Height:  |  Size: 93 KiB

After

Width:  |  Height:  |  Size: 93 KiB

View File

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 61 KiB

View File

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

View File

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

View File

Before

Width:  |  Height:  |  Size: 153 KiB

After

Width:  |  Height:  |  Size: 153 KiB

View File

Before

Width:  |  Height:  |  Size: 9.5 KiB

After

Width:  |  Height:  |  Size: 9.5 KiB

View File

Before

Width:  |  Height:  |  Size: 176 KiB

After

Width:  |  Height:  |  Size: 176 KiB

View File

Before

Width:  |  Height:  |  Size: 128 KiB

After

Width:  |  Height:  |  Size: 128 KiB

View File

@ -0,0 +1,48 @@
title: Working with multiple websites
summary: Setting up and editing multiple websites using SilverStripe
# Working with multiple sites
## In this section:
* Understand subsites
* Learn how to create and delete subsites
* Learn how to manage subsite permissions
* Enable/Disable public access to subsites
* Learn how to create and use subsite templates
* Learn how to edit existing subsites
* Sharing content between the main site and subsites
## Before we begin:
* Make sure you have the SilverStripe [Subsites](http://addons.silverstripe.org/add-ons/silverstripe/subsites) module installed.
* Make sure you are in the "Subsites" section on the Navigation Tabs.
* Make sure you have full administrative rights on your site.
## Understanding subsites
Subsites is a module to allow you manage multiple related sites from a single CMS interface. Because all sites run on a single installation of SilverStripe, they can share users, content and assets. They can all use the same templates, or each use different ones.
When Subsites is installed your existing site is defined as the main site, you will be then be able to create related subsites under the main site.
So for example you may have an international presence and you want to create a subsite for a country where you do business which is geared just for that market. You could create a subsite for this and have all information related to that country kept under this subsite, you can also set up a subdomain for this site.
One of the benefits of subsites is that it is easy to copy pages between the subsites and you have access to all of the assets across all of the subsites.
Subsites is not for running unrelated websites on a single SilverStripe instance so if two sites have different vhosts you will not be able to run them with Subsites on a single SilverStripe instance.
With Subsites you can set up users to have access to all subsites or just a selection of subsites.
## Common subsite uses
Subsites can be used for various different reasons here are some of the common ones:
* Setting up a subsite for a small campaign so for example a clothing company may set up a summer or winter subsite to market just that season of clothing.
* Locking down a particular subsite you may create a particular department like recruitment who would have access to create and edit pages for their particular subsite but they would not be able to modify the main website.
* Running sub-domains on a single SilverStripe instance, with subsites if a sub-domain is pointing to the same instance and has been setup correctly you can manage this via a single CMS instance.
* Subsites can not be used to run multiple websites on a single instance. Subsites does not allow you to run multiple domains/vhosts on a single instance.
## Documentation
* [Set up](set_up.md)
* [Working with subsites](working_with.md)

View File

@ -2,44 +2,42 @@
## Creating subsites
To view, edit and create subsites, go to the *Subsite* section of the CMS. Click *Search* to view a list of all
existing subsites.
To view, edit and create subsites, go to the *Subsite* section of the CMS. Click *Search* to view a list of all existing subsites.
![View subsites](_images/view-subsites.jpg)
Click on a subsite name to edit its details. Click the red X to delete a subsite (you will be asked for confirmation.)
To create a new subsite, click *Add Subsite*. This opens the *Subsite configuration* subsection.
Click on a subsite name to edit its details. To create a new subsite, click *Add Subsite*. This opens the *Subsite configuration* subsection.
## Deleting a subsite
Click the red X to delete a subsite (you will be asked for confirmation.)
## Subsite configuration
![Subsite configuration](_images/subsite-configuration.jpg)
You can configure the following details for a subsite:
* *Name of subsite:* This is the display name for the site in the CMS (not public-facing)
* *Domains for this subsite:* Lets you add one or more domains for this subsite, e.g., subsite.co.nz, subsite.org.nz,
subsite.com
* *Domains for this subsite:* Lets you add one or more domains for this subsite, e.g., subsite.co.nz, subsite.org.nz, subsite.com
* *Language:* Sets the language for the subsite. This affects the spellchecker (not the CMS interface language)
* *Default site:* If your site is accessed by a domain not listed in any subsites, this subsite is shown as default.
* *Enable public access:* Enables/disables the subsite. Corresponds to the *Active Subsite* column.
* *Theme:* Shows the list of available themes that exist in the themes directory. The subsite will use the templates
and styles from the selected theme.
* *Disallow page types:* Lets you mark some page types to prevent being used within this subsite.
* *Copy structure from:* Gives you the option to select an existing subsite from which to copy pages and files (see
"Copying subsites" for more information about this)
* *Theme:* Shows the list of available themes that exist in the themes directory. The subsite will use the templates and styles from the selected theme.
* *Disallow page types:* Lets you mark some page types to prevent them being used within this subsite (but not the main site). Clicking on that link will display a list of checkboxes for all of the page types which can be selected to disable that page type for the subsite you are editing. This is useful when you create a content editor and you do not want them to be able to add certain page types.
* *Copy structure from:* Gives you the option to select an existing subsite from which to copy pages and files (see "Copying subsites" for more information about this)
## Roles
When creating roles, you can assign the following subsite-specific permissions:
* *Access to 'Subsites' section:* Shows the *Subsite* section in the CMS, allowing you to manage subsites for your
site (ie, create, edit, view.)
* *Access to 'Subsites' section:* Shows the *Subsite* section in the CMS, allowing you to manage subsites for your site (ie, create, edit, view.)
* *Manage subsites for groups:* Ability to limit the permissions for a group to one or more subsites.
* *Manage assets for subsites:* Ability to select the subsite to which an asset folder belongs (also requires access to
*Files* section)
* *Manage assets for subsites:* Ability to select the subsite to which an asset folder belongs (also requires access to *Files* section)
## Groups
Groups can have access to all sites, or to one or more specific subsites. If you have different staff in charge of each
subsite, you probably want to create a separate group for each subsite.
Groups can have access to all sites, or to one or more specific subsites. If you have different staff in charge of each subsite, you probably want to [create](#creating-groups) a separate group for each subsite.
The dropdown in the upper left corner of the CMS indicates which subsite you are currently on.
@ -50,7 +48,19 @@ these subsites* to reveal a checklist of all available sites.
![Group subsites access](_images/group-subsites-access.png)
### Creating groups
Access to certain subsites can be limited to administrators based on the groups they are in.
So for example if you had a couple of subsites you could create a group for each subsite and then specify that the group had access to all subsites or just a specific subsites.
To access this functionality go to Security -> Groups
![Creating Groups](_images/subsite-admin-security-group.png "Groups")
Select the group you want to modify and then go to the Subsites tab
## Copying subsites
Duplicating subsites can be useful if you want to create several subsites based on the same general site structure. You can set up a collection of pages, files, and images and use it as a template. When you create a new subsite, instead of starting from scratch you can copy it all from your existing subsite. This will copy all pages, files and images from that subsite into your new subsite.
To create a new subsite template, create a new subsite described above under "Creating subsites" but don't add any domains. Add a name that will make it easy to see that it is a template. Select the new template from the subsites dropdown in the upper right and create the pages and add the files and images you'd like to become part of the
@ -60,7 +70,14 @@ When you create a new subsite, you can now choose to Copy structure from your te
![Copy subsite structure](_images/copy-structure.jpg)
## Disable particular page types from a subsite
## Page types
Page types refer to the type of pages that can be set up on a site. A page type will have certain features and functionality some examples on SilverStripe would be 'Page', 'HomePage' and 'ErrorPage' these all differ to each other in what they would be used for so you would use Page for any pages
underneath the HomePage.
You would only have one HomePage for your site and you may have some logic to only allow you to create one of these pages, ErrorPage would only be used for error pages and would be designed to be very minimal to work in situations where the site is experiencing difficulties (like no database access).
### Disable particular page types from a subsite
Sometimes, you will have two or more websites that are very similar, but have some small differences. For example, a head office and its 3 satellite offices may have 4 subsites, but only the head office site will have a "Company News" section on the site. In this instance, you can still use the subsites module, and use the 'Disallow page types' ability to remove certain page types from being created on subsites.
1. Create a new subsite as you normally would via the Subsites section in the CMS
@ -69,7 +86,7 @@ Sometimes, you will have two or more websites that are very similar, but have so
4. Select the page types that you wish to forbid from this subsite.
5. Click the Save button at the bottom of the section.
Note that this process is exactly the same when editing an existing subsite, you just select the subsite you want to remove page types from instead of creating a new subsite.
*Note:* This process is exactly the same when editing an existing subsite, you just select the subsite you want to remove page types from instead of creating a new subsite. Also you cannot not filter Page Types for the main site.
![Disallow page types screenshot](_images/disallow-page-types.png)
@ -79,4 +96,10 @@ Now, whenever someone wants to create a new page on the subsite (the 'London Bra
The page type blacklist applies only to future pages, not existing ones. If an administrator blacklists a defined page type on a subsite, existing pages of that type will remain on the subsite. The administrator (or another user) can later change it to a type that is not blacklisted after the page is published; however, once a user does that, that "grandfathered" pagetype will no longer be available to the published page.
For example, say a subsite user publishes a new Company Page before it was forbidden by an administrator. Later, a user with full administrative rights decides to disallow Company Pages from being created on the subsite. This restriction will only apply to all future pages that are published, but not to existing Company Pages. The full administrator (or any user with sufficient publishing rights) will have to manually convert any published Company Pages on the subsite to that of another allowed type.
For example, say a subsite user publishes a new Company Page before it was forbidden by an administrator. Later, a user with full administrative rights decides to disallow Company Pages from being created on the subsite. This restriction will only apply to all future pages that are published, but not to existing Company Pages. The full administrator (or any user with sufficient publishing rights) will have to manually convert any published Company Pages on the subsite to that of another allowed type.
## Themes
A theme is group of templates, images and CSS for the look of a website. When you are using Subsites you may have different themes installed for your site so you could apply different themes for each subsite.
## Assets
Assets are files that have been uploaded via the CMS. It is suggested to use a naming convention for files designated to be used on a particular subsite or to create folders for each subsite to help organise them.

View File

@ -2,11 +2,11 @@
## Managing content across subsites
Select a subsite from the dropdown in the upper left to display the content for that site in the site tree.
To edit a particular subsite, choose the subsite from the dropdown menu in the left-hand menu.
![Subsites dropdown](_images/subsites-dropdown.png)
## Subsites virtual pages
### Subsites virtual pages
You can pull in the content from a page that resides on another subsite by creating a page of the type **Subsites
Virtual Page**. Pick the subsite from which you want to pull the content, then select the page. As with regular virtual
@ -15,7 +15,7 @@ the original content changes.
![Subsites virtual page](_images/subsites-virtual-page.jpg)
## Duplicating pages from the main site
### Duplicating pages from the main site
If you have an existing page on the main site that you would like to copy to a subsite, all you need to do is:

View File

@ -1,13 +1,9 @@
ar:
SubsiteAdmin:
MENUTITLE: مواقع فرعية
SubsiteXHRController:
MENUTITLE: المراقب XHR للموقع الفرعي
ASSETADMIN:
SUBSITENOTICE: 'يمكن الوصول إلى المجلدات والملفات التي تم إنشاؤها في الموقع الرئيسي من طرف كل المواقع الفرعية.'
FileSubsites:
AllSitesDropdownOpt: 'كافة المواقع'
SubsiteFieldLabel: الموقع الفرعي
SubsiteFieldLabel: 'الموقع الفرعي'
GridFieldAddFromTemplateButton:
AddFromTemplate: 'اضف جديد من القالب'
GroupSubsites:
@ -17,7 +13,7 @@ ar:
GlobalGroup: 'المجموعة العامة'
MANAGE_SUBSITES: 'إدارة مواقع فرعية للمجموعات'
MANAGE_SUBSITES_HELP: 'القدرة على الحد من أذونات مجموعة ما على موقع فرعي واحدة أو أكثر.'
SECURITYTABTITLE: مواقع فرعية
SECURITYTABTITLE: 'مواقع فرعية'
LeftAndMainSubsites:
Saved: 'تمّ الحفظ، يرجى تحديث الصفحات ذات الصلة.'
SiteTreeSubsites:
@ -28,27 +24,30 @@ ar:
CopyMessage: 'إنشاء نسخة من {title}'
CustomExtraMeta: 'العلامات الوصفية المخصصة'
CustomMetaDescription: الوصف
CustomMetaKeywords: كلمات البحث
CustomMetaKeywords: 'كلمات البحث'
CustomMetaTitle: عنوان
DOMAINSAVEFIRST: 'لا يمكنك إضافة النطاقات إلا بعد القيام بالحفظ لأول مرة'
DomainsHeadline: 'نطاقات هذا الموقع الفرعي'
DomainsListTitle: النطاقات
IsPublicHeaderField: 'موقع فرعي نشط'
NOTEMPLATE: 'بدون قالب'
PLURALNAME: المواقع الفرعية
PLURALNAME: 'المواقع الفرعية'
PageTypeBlacklistField: 'عدم السماح بأصناف الصفحات؟'
SINGULARNAME: الموقع الفرعي
SINGULARNAME: 'الموقع الفرعي'
SiteConfigSubtitle: 'هنا سطر الوصف الخاص بك'
SiteConfigTitle: 'اسم موقعك'
TabTitleConfig: المواصفات
TabTitleConfig: 'المواصفات'
ValidateTitle: 'الرجاء إضافة "عنوان"'
SubsiteAdmin:
MENUTITLE: 'مواقع فرعية'
SubsiteDomain:
DOMAIN: نطاق
IS_PRIMARY: 'هو نطاق أساسي'
PLURALNAME: 'نطاقات موقع فرعي'
SINGULARNAME: 'نطاق موقع فرعي'
SubsiteReportWrapper:
ReportDropdown: المواقع
ReportDropdown: 'المواقع'
SubsiteXHRController:
MENUTITLE: 'المراقب XHR للموقع الفرعي'
Subsites:
DefaultSiteFieldLabel: 'الموقع الافتراضي'
DomainFieldLabel: النطاق
@ -58,10 +57,9 @@ ar:
PageTypeBlacklistFieldLabel: 'نوع الصفحة قائمة سوداء'
PrimaryDomainFieldLabel: 'النطاق الأساسي'
RedirectURLFieldLabel: 'إعادة توجيه عنوان موقع الويب'
ThemeFieldLabel: المحور
ThemeFieldLabel: 'المحور'
TitleFieldLabel: 'اسم الموقع الفرعي'
SubsitesVirtualPage:
DESCRIPTION: 'يعرض محتوى صفحة على موقع فرعي آخر'
PLURALNAME: 'قاعدة الصفحات'
SINGULARNAME: 'الصفحة الإفتراضية للمواقع الفرعية'
SubsiteField: الموقع الفرعي
SubsiteField: 'الموقع الفرعي'

34
lang/cs.yml Normal file
View File

@ -0,0 +1,34 @@
cs:
LeftAndMain_Menu:
Hello: Ahoj
LOGOUT: 'Odhlásit se'
SiteTreeSubsites:
CopyAction: Kopírovat
Subsite:
CustomExtraMeta: 'Vlastní meta tagy'
CustomMetaDescription: Popis
CustomMetaKeywords: 'Klíčová slova'
CustomMetaTitle: Název
DOMAINSAVEFIRST: 'Domény můžete přidávat až po uložení'
DomainsHeadline: 'Domény pro tento web'
DomainsListTitle: Domény
SiteConfigSubtitle: 'Slogan Vašeho webu'
SiteConfigTitle: 'Název Vašeho webu'
TabTitleConfig: Konfigurace
ValidateTitle: 'Prosím vložte "Název"'
SubsiteAdmin:
MENUTITLE: Subsites
SubsiteDomain:
DOMAIN: Doména
PLURALNAME: 'Domény webů'
SINGULARNAME: 'Doména webu'
SubsiteReportWrapper:
ReportDropdown: Weby
Subsites:
DefaultSiteFieldLabel: 'Výchozí web'
DomainFieldLabel: Doména
LanguageFieldLabel: Jazyk
ThemeFieldLabel: Téma
TitleFieldLabel: 'Název subsite'
SubsitesVirtualPage:
PLURALNAME: 'Základní stránky'

View File

@ -1,10 +1,8 @@
de:
SubsiteAdmin:
MENUTITLE: Subsites
SubsiteXHRController:
MENUTITLE: SubsiteXHRController
ASSETADMIN:
SUBSITENOTICE: 'Auf Ordner und Dateien der Hauptseite kann von allen Subsites zugegriffen werden.'
DomainNameField:
INVALID_DOMAIN: 'Ungültige Domain'
FileSubsites:
AllSitesDropdownOpt: 'Alle Subseiten'
SubsiteFieldLabel: Subseite
@ -19,7 +17,10 @@ de:
MANAGE_SUBSITES_HELP: 'Möglichkeit, die Berechtigungen einer Gruppe auf bestimmte Subsites zu beschränken.'
SECURITYTABTITLE: Subsites
LeftAndMainSubsites:
Saved: 'Gespeichert.'
Saved: Gespeichert.
LeftAndMain_Menu:
Hello: Hallo
LOGOUT: Abmelden
SiteTreeSubsites:
CopyAction: Kopieren
CopyToSubsite: 'Seite auf Subseite Kopieren'
@ -42,26 +43,37 @@ de:
SiteConfigTitle: 'Name Ihrer Website'
TabTitleConfig: Einstellungen
ValidateTitle: 'Bitte geben Sie einen Titel an'
SubsiteAdmin:
MENUTITLE: Subsites
SubsiteDomain:
DOMAIN: Domain
IS_PRIMARY: 'Ist primäre Domain'
DOMAIN_DESCRIPTION: 'Hostname dieser Subsite (ohne Protokol). Joker (*) ist erlaubt.'
IS_PRIMARY: 'Ist Hauptdomain?'
PLURALNAME: 'Subsite Domains'
PROTOCOL_AUTOMATIC: Automatisch
PROTOCOL_HTTP: 'http://'
PROTOCOL_HTTPS: 'https://'
Protocol: Protokoll
SINGULARNAME: 'Subsite Domain'
SubsiteReportWrapper:
ReportDropdown: Seiten
SubsiteXHRController:
MENUTITLE: SubsiteXHRController
Subsites:
DefaultSiteFieldLabel: 'Standard Seite'
DomainFieldLabel: Domäne
IsPublicFieldLabel: 'Öffentlich zugänglich'
LanguageFieldLabel: Sprache
MainSiteTitle: 'Hauptsite'
MainSiteTitle: Hauptsite
PageTypeBlacklistFieldLabel: 'Ausgeschlossene Seitentypen'
PrimaryDomainFieldLabel: 'Primäre Domain'
RedirectURLFieldLabel: 'Weierleitungs-URL'
RedirectURLFieldLabel: Weierleitungs-URL
ThemeFieldLabel: Theme
TitleFieldLabel: 'Name der Subsite'
SubsitesVirtualPage:
DESCRIPTION: 'Zeigt den Inhalt einer anderen Seite von einer anderen Subsite an'
PLURALNAME: 'Subsites Virtuelle Seiten'
PLURALNAME: 'Basis Seiten'
SINGULARNAME: 'Subsites Virtuelle Seite'
SubsiteField: Subsite
VirtualPage:
EDITCONTENT: 'Klicken Sie hier, um den Inhalt zu bearbeiten'

View File

@ -1,6 +1,8 @@
en:
ASSETADMIN:
SUBSITENOTICE: 'Folders and files created in the main site are accessible by all subsites.'
DomainNameField:
INVALID_DOMAIN: 'Invalid domain name'
FileSubsites:
AllSitesDropdownOpt: 'All sites'
SubsiteFieldLabel: Subsite
@ -22,6 +24,8 @@ en:
SiteTreeSubsites:
CopyAction: Copy
CopyToSubsite: 'Copy page to subsite'
CopyToSubsiteWithChildren: 'Include children pages?'
SubsiteOperations: 'Subsite Operations'
Subsite:
COPYSTRUCTURE: 'Copy structure from:'
CopyMessage: 'Created a copy of {title}'
@ -45,8 +49,14 @@ en:
MENUTITLE: Subsites
SubsiteDomain:
DOMAIN: Domain
IS_PRIMARY: 'Is Primary Domain'
DOMAIN_DESCRIPTION: 'Hostname of this subsite (exclude protocol). Allows wildcards (*).'
IS_PRIMARY: 'Is Primary Domain?'
PLURALNAME: 'Subsite Domains'
PROTOCOL_AUTOMATIC: Automatic
PROTOCOL_DESCRIPTION: 'Mark this as the default domain for this subsite'
PROTOCOL_HTTP: 'http://'
PROTOCOL_HTTPS: 'https://'
Protocol: Protocol
SINGULARNAME: 'Subsite Domain'
SubsiteReportWrapper:
ReportDropdown: Sites

View File

@ -1,10 +1,8 @@
eo:
SubsiteAdmin:
MENUTITLE: Subretejoj
SubsiteXHRController:
MENUTITLE: SubsiteXHRController
ASSETADMIN:
SUBSITENOTICE: 'Dosierujoj kaj dosieroj kreitaj en la ĉefa retejo estas alireblaj de ĉiuj retejoj'
DomainNameField:
INVALID_DOMAIN: 'Nevalida domajna nomo'
FileSubsites:
AllSitesDropdownOpt: 'Ĉiuj retejoj'
SubsiteFieldLabel: Subretejo
@ -20,9 +18,14 @@ eo:
SECURITYTABTITLE: Subsites
LeftAndMainSubsites:
Saved: 'Konservita, bonvole ĝisdatigi rilatajn paĝojn.'
LeftAndMain_Menu:
Hello: Saluton
LOGOUT: Elsaluti
SiteTreeSubsites:
CopyAction: Kopio
CopyToSubsite: 'Kopii paĝon al subretejo'
CopyToSubsiteWithChildren: 'Ĉu inkluzivi paĝidojn?'
SubsiteOperations: 'Subretejaj operacioj'
Subsite:
COPYSTRUCTURE: 'Kopii strukturon de:'
CopyMessage: 'Kreis kopion de {title}'
@ -42,13 +45,23 @@ eo:
SiteConfigTitle: 'Nomo de via retejo'
TabTitleConfig: Agordaro
ValidateTitle: 'Bonvole aldonu "Titolon"'
SubsiteAdmin:
MENUTITLE: Subretejoj
SubsiteDomain:
DOMAIN: Domajno
IS_PRIMARY: 'Estas unuaranga domajno'
DOMAIN_DESCRIPTION: 'Gastiga nomo de ĉi tiu subretejo (ellasu protokolon). Permesas ĵokerojn (*).'
IS_PRIMARY: 'Ĉu unuaranga domajno?'
PLURALNAME: 'Subretejaj domajnoj'
PROTOCOL_AUTOMATIC: Aŭtomata
PROTOCOL_DESCRIPTION: 'Marki ĉi tion kiel la aprioran domajnon por ĉi tiu subretejo'
PROTOCOL_HTTP: 'http://'
PROTOCOL_HTTPS: 'https://'
Protocol: Protokolo
SINGULARNAME: 'Subreteja domajno'
SubsiteReportWrapper:
ReportDropdown: Retejoj
SubsiteXHRController:
MENUTITLE: SubsiteXHRController
Subsites:
DefaultSiteFieldLabel: 'Apriora retejo'
DomainFieldLabel: Domajno
@ -62,6 +75,8 @@ eo:
TitleFieldLabel: 'Nomo de subretejo'
SubsitesVirtualPage:
DESCRIPTION: 'Vidigas la enhavon de paĝo en alia subretejo'
PLURALNAME: 'Virtualaj paĝoj de subretejoj'
PLURALNAME: 'Bazaj paĝoj'
SINGULARNAME: 'Virtuala paĝo de subretejoj'
SubsiteField: Subretejo
VirtualPage:
EDITCONTENT: 'Alklaku ĉi tie por redakti la enhavon'

46
lang/fa_IR.yml Normal file
View File

@ -0,0 +1,46 @@
fa_IR:
DomainNameField:
INVALID_DOMAIN: 'نام دامنه نامعتبر'
FileSubsites:
AllSitesDropdownOpt: 'تمامی سایت ها'
SubsiteFieldLabel: 'زیر سایت ها'
GroupSubsites:
ACCESSALL: 'تمامی زیر سایت ها'
ACCESSONLY: 'فقط این زیر سایت ها'
SECURITYTABTITLE: 'زیر سایت ها'
LeftAndMain_Menu:
Hello: 'سلام'
LOGOUT: خروج
Subsite:
CustomMetaKeywords: 'کلید واژه ها'
CustomMetaTitle: عنوان
DomainsHeadline: 'دامنه های این زیر سایت'
DomainsListTitle: 'دامنه ها'
IsPublicHeaderField: 'زیر سایت فعال'
NOTEMPLATE: 'بدون قالب'
PLURALNAME: 'زیر سایت ها'
SINGULARNAME: 'زیر سایت'
SiteConfigTitle: 'نام سایت شما'
TabTitleConfig: پیکربندی
SubsiteAdmin:
MENUTITLE: 'زیر سایت ها'
SubsiteDomain:
DOMAIN: 'دامنه'
PLURALNAME: 'دامنه های زیر سایت'
PROTOCOL_AUTOMATIC: 'به صورت خودکار'
Protocol: پروتکل
SINGULARNAME: 'دمنه زیرسایت'
SubsiteReportWrapper:
ReportDropdown: 'سایت ها'
Subsites:
DefaultSiteFieldLabel: 'سایت پیش فرض'
DomainFieldLabel: 'دامنه'
LanguageFieldLabel: زبان
PrimaryDomainFieldLabel: 'دامنه اولیه'
ThemeFieldLabel: پوسته
TitleFieldLabel: 'نام زیر سایت'
SubsitesVirtualPage:
PLURALNAME: 'صفحه اصلی'
SubsiteField: 'زیر سایت'
VirtualPage:
EDITCONTENT: 'برای ویرایش محتوا اینجا را کلیک کنید'

View File

@ -1,8 +1,4 @@
fi:
SubsiteAdmin:
MENUTITLE: Alasivustot
SubsiteXHRController:
MENUTITLE: SubsiteXHRController
ASSETADMIN:
SUBSITENOTICE: 'Kansiot ja tiedostot, jotka on luotu pääsivustolla, ovat käytettävissä kaikissa alisivustoissa.'
FileSubsites:
@ -14,7 +10,7 @@ fi:
ACCESSALL: 'Kaikki alasivustot'
ACCESSONLY: 'Vain nämä alasivustot'
ACCESSRADIOTITLE: 'Anna tälle ryhmälle pääsy kohteeseen'
GlobalGroup: 'Globaaliryhmä'
GlobalGroup: Globaaliryhmä
MANAGE_SUBSITES: 'Hallinnoi ryhmien alisivustoja'
MANAGE_SUBSITES_HELP: 'Mahdollisuus rajoittaa ryhmän oikeuksia yhdelle tai useammalle alisivustolle.'
SECURITYTABTITLE: Alisivustot
@ -38,30 +34,33 @@ fi:
PLURALNAME: Alasivustot
PageTypeBlacklistField: 'Kiellä sivutyyppien käyttö?'
SINGULARNAME: Alisivusto
SiteConfigSubtitle: 'Iskulauseesi'
SiteConfigSubtitle: Iskulauseesi
SiteConfigTitle: 'Sivuston nimi'
TabTitleConfig: Asetukset
ValidateTitle: 'Lisää "Otsikko"'
SubsiteAdmin:
MENUTITLE: Alasivustot
SubsiteDomain:
DOMAIN: Domain
IS_PRIMARY: 'On päädomain'
PLURALNAME: 'Alisivuston domain-osoitteet'
SINGULARNAME: 'Alisivuston domain-osoite'
SubsiteReportWrapper:
ReportDropdown: Sivustot
SubsiteXHRController:
MENUTITLE: SubsiteXHRController
Subsites:
DefaultSiteFieldLabel: 'Oletussivusto'
DefaultSiteFieldLabel: Oletussivusto
DomainFieldLabel: Domain
IsPublicFieldLabel: 'Aktivoi julkinen pääsy'
LanguageFieldLabel: Kieli
MainSiteTitle: 'Pääsivusto'
MainSiteTitle: Pääsivusto
PageTypeBlacklistFieldLabel: 'Sivutyyppien mustalista'
PrimaryDomainFieldLabel: 'Oletusdomain'
PrimaryDomainFieldLabel: Oletusdomain
RedirectURLFieldLabel: 'Edelleenohjaus URL'
ThemeFieldLabel: Teema
TitleFieldLabel: 'Alisivuston nimi'
SubsitesVirtualPage:
DESCRIPTION: 'Näyttää sisällön toisen alisivuston sivulta'
PLURALNAME: 'Alisivustojen virtuaaliset sivut'
PLURALNAME: Pohjasivut
SINGULARNAME: 'Alisivuston Virtuaalisivu'
SubsiteField: Alisivu

80
lang/hr.yml Normal file
View File

@ -0,0 +1,80 @@
hr:
ASSETADMIN:
SUBSITENOTICE: 'Direktoriji i datoteke kreirane u glavnom sajtu su dostupne svim podsajtovima.'
DomainNameField:
INVALID_DOMAIN: 'Netočan naziv domene'
FileSubsites:
AllSitesDropdownOpt: 'Svi sajtovi'
SubsiteFieldLabel: Podsajt
GridFieldAddFromTemplateButton:
AddFromTemplate: 'Dodaj novi iz predloška'
GroupSubsites:
ACCESSALL: 'Svi podsajtovi'
ACCESSONLY: 'Samo ovi podsajtovi'
ACCESSRADIOTITLE: 'Dodijeli ovoj grupi pristup za'
GlobalGroup: 'globalna grupa'
MANAGE_SUBSITES: 'Upravljaj podsajtove za grupe'
MANAGE_SUBSITES_HELP: 'Mogućnost limitiranja prava za grupu za jedan ili više podsajtova.'
SECURITYTABTITLE: Podsajtovi
LeftAndMainSubsites:
Saved: 'Spremljeno, molimo osvježite povezane stranice.'
LeftAndMain_Menu:
Hello: Pozdrav
LOGOUT: Odjava
SiteTreeSubsites:
CopyAction: Kopiraj
CopyToSubsite: 'Kopiraj stranicu u podsajt'
Subsite:
COPYSTRUCTURE: 'Kopiraj strukturu od:'
CopyMessage: 'Kreirana kopija od {title}'
CustomExtraMeta: 'Prilagođeni Meta tagovi'
CustomMetaDescription: Opis
CustomMetaKeywords: 'Ključne riječi'
CustomMetaTitle: Naslov
DOMAINSAVEFIRST: 'Možete dodati domene nakon prvog spremanja'
DomainsHeadline: 'Domene za ove podsajtove'
DomainsListTitle: Domene
IsPublicHeaderField: 'Aktivni podsajtovi'
NOTEMPLATE: 'Nema predloška'
PLURALNAME: Podsajtovi
PageTypeBlacklistField: 'Ne dopuštaj tipove stranica?'
SINGULARNAME: Podsajt
SiteConfigSubtitle: 'vaš slogan ovdje'
SiteConfigTitle: 'Naziv vašeg weba'
TabTitleConfig: Konfiguracija
ValidateTitle: 'Molimo dodajte "Naslov"'
SubsiteAdmin:
MENUTITLE: Podsajtovi
SubsiteDomain:
DOMAIN: Domena
DOMAIN_DESCRIPTION: 'Hostname ovog podsajta (bez protokola). Omogućava wildcard (*).'
IS_PRIMARY: 'Da li je glavna domena?'
PLURALNAME: 'Domene podsajtova'
PROTOCOL_AUTOMATIC: Automatsko
PROTOCOL_DESCRIPTION: 'Označi kao zadanu domenu za ovu podstranicu'
PROTOCOL_HTTP: 'http://'
PROTOCOL_HTTPS: 'https://'
Protocol: Protokol
SINGULARNAME: 'Domena podsajta'
SubsiteReportWrapper:
ReportDropdown: Sajtovi
SubsiteXHRController:
MENUTITLE: SubsiteXHRController
Subsites:
DefaultSiteFieldLabel: 'Zadani sajt'
DomainFieldLabel: Domena
IsPublicFieldLabel: 'Omogućni javni pristup'
LanguageFieldLabel: Jezik
MainSiteTitle: 'Glavni sajt'
PageTypeBlacklistFieldLabel: 'Crna lista tipova stranica'
PrimaryDomainFieldLabel: 'Glavna domena'
RedirectURLFieldLabel: 'Link preusmjeravanja'
ThemeFieldLabel: Tema
TitleFieldLabel: 'Naziv podsajta'
SubsitesVirtualPage:
DESCRIPTION: 'Prikazuje sadržaj stranice na drugom podsajtu'
PLURALNAME: 'Bazna stranica'
SINGULARNAME: 'Virtualna stranica podsajta'
SubsiteField: Podsajt
VirtualPage:
EDITCONTENT: 'Klikni ovdje za uređivanje sadržaja'

View File

@ -1,16 +1,16 @@
id:
SubsiteAdmin:
MENUTITLE: Subsitus
FileSubsites:
SubsiteFieldLabel: Subsitus
GroupSubsites:
SECURITYTABTITLE: Subsitus
Subsite:
CustomMetaDescription: Deskripsi
CustomMetaKeywords: Kata kunci
CustomMetaKeywords: 'Kata kunci'
CustomMetaTitle: Judul
PLURALNAME: Subsitus
SINGULARNAME: Subsitus
SubsiteAdmin:
MENUTITLE: Subsitus
SubsiteReportWrapper:
ReportDropdown: Situs
Subsites:

View File

@ -1,17 +1,16 @@
ja:
SubsiteAdmin:
MENUTITLE: サブサイト
GridFieldAddFromTemplateButton:
AddFromTemplate: 'テンプレートから新しく追加'
GroupSubsites:
ACCESSALL: '全てのサブサイト'
ACCESSONLY: 'これらのサブサイトのみ'
ACCESSONLY: これらのサブサイトのみ
ACCESSRADIOTITLE: 'このグループに選択先へのアクセス権を与える'
SECURITYTABTITLE: サブサイト
SubsiteAdmin:
MENUTITLE: サブサイト
SubsiteDomain:
DOMAIN: ドメイン
IS_PRIMARY: 'プライマリドメイン'
PLURALNAME: 'サブサイトのドメイン'
SINGULARNAME: 'サブサイトのドメイン'
PLURALNAME: サブサイトのドメイン
SINGULARNAME: サブサイトのドメイン
SubsitesVirtualPage:
SINGULARNAME: 'サブサイトの仮想ページ'
SINGULARNAME: サブサイトの仮想ページ

45
lang/lt.yml Normal file
View File

@ -0,0 +1,45 @@
lt:
FileSubsites:
AllSitesDropdownOpt: 'Visos svetainės'
GridFieldAddFromTemplateButton:
AddFromTemplate: 'Sukurti naują pagal šabloną'
LeftAndMainSubsites:
Saved: 'Išsaugota, prašome atnaujinti susijusius puslapius'
LeftAndMain_Menu:
Hello: Sveiki
LOGOUT: Atsijungti
SiteTreeSubsites:
CopyAction: Kopijuoti
CopyToSubsite: 'Kopijuoti puslapį į kitą svetainę'
Subsite:
COPYSTRUCTURE: 'Kopijuoti struktūrą iš:'
CopyMessage: 'Sukurta {title} kopija'
CustomExtraMeta: 'Kitos meta žymės'
CustomMetaDescription: Aprašymas
CustomMetaKeywords: Raktažodžiai
CustomMetaTitle: Pavadinimas
DomainsHeadline: 'Šio puslapio domenai'
DomainsListTitle: Domenai
NOTEMPLATE: 'Nėra šablono'
PageTypeBlacklistField: 'Neleidžiami puslapių tipai'
SiteConfigSubtitle: 'Jūsų svetainės šūkis'
SiteConfigTitle: 'Jūsų svetainės pavadinimas'
TabTitleConfig: Nustatymai
ValidateTitle: 'Prašome įvesti "Pavadinimą"'
SubsiteDomain:
DOMAIN: Domenas
SubsiteReportWrapper:
ReportDropdown: Svetainės
SubsiteXHRController:
MENUTITLE: SubsiteXHRController
Subsites:
DefaultSiteFieldLabel: 'Pagrindinė svetainė'
DomainFieldLabel: Domenas
IsPublicFieldLabel: 'Leisti pasiekti visiems'
LanguageFieldLabel: Kalba
MainSiteTitle: 'Pagrindinis puslapis'
PrimaryDomainFieldLabel: 'Pagrindinis domenas'
RedirectURLFieldLabel: 'Nukreipimo nuoroda'
ThemeFieldLabel: Tema
SubsitesVirtualPage:
PLURALNAME: 'Baziniai puslapiai'

View File

@ -1,13 +1,9 @@
mi:
SubsiteAdmin:
MENUTITLE: Ngā pae iti
SubsiteXHRController:
MENUTITLE: ManaXHRPaeiti
ASSETADMIN:
SUBSITENOTICE: 'Ka taea ngā kōpaki me ngā kōnae kua hangaia i te pae matua te uru mā ngā pae iti katoa.'
FileSubsites:
AllSitesDropdownOpt: 'Ngā pae katoa'
SubsiteFieldLabel: Pae iti
SubsiteFieldLabel: 'Pae iti'
GridFieldAddFromTemplateButton:
AddFromTemplate: 'Tāpiri Hōu mai i te Tātauira'
GroupSubsites:
@ -17,7 +13,7 @@ mi:
GlobalGroup: 'Rōpū Hurinoa'
MANAGE_SUBSITES: 'Whakahaere pae iti mō ngā rōpū'
MANAGE_SUBSITES_HELP: 'Te āheinga ki te whakawhāiti whakaaetanga mō tētahi rōpū ki tētahi neke atu rānei o ngā pae iti.'
SECURITYTABTITLE: Ngā pae iti
SECURITYTABTITLE: 'Ngā pae iti'
LeftAndMainSubsites:
Saved: 'Kua tiakina, whakahoutia ngā whārangi pāhono.'
SiteTreeSubsites:
@ -28,27 +24,30 @@ mi:
CopyMessage: 'I hangaia he tārua o {title}'
CustomExtraMeta: 'Ngā Tūtohu Meta Ritenga'
CustomMetaDescription: Whakaahuatanga
CustomMetaKeywords: Ngā kupumatua
CustomMetaKeywords: 'Ngā kupumatua'
CustomMetaTitle: Taitara
DOMAINSAVEFIRST: 'Ka taea noa iho te tāpiri rohe i muri i te tiakinga tuatahitanga'
DomainsHeadline: 'Ngā rohe mō tēnei pae iti'
DomainsListTitle: Ngā Rohe
DomainsListTitle: 'Ngā Rohe'
IsPublicHeaderField: 'Pae iti hohe'
NOTEMPLATE: 'Kāore he tātauira'
PLURALNAME: Ngā pae iti
PLURALNAME: 'Ngā pae iti'
PageTypeBlacklistField: 'Me whakakāhore ngā momo whārangi?'
SINGULARNAME: Pae iti
SINGULARNAME: 'Pae iti'
SiteConfigSubtitle: 'Tō rārangi tautuhinga ki konei'
SiteConfigTitle: 'Tō Ingoa Pae'
TabTitleConfig: Whirihoranga
ValidateTitle: 'Tāurua he "Taitara"'
SubsiteAdmin:
MENUTITLE: 'Ngā pae iti'
SubsiteDomain:
DOMAIN: Rohe
IS_PRIMARY: 'Ko Te Rohe Matua'
PLURALNAME: 'Ngā Rohe Pae Iti'
SINGULARNAME: 'Rohe Pae Iti'
SubsiteReportWrapper:
ReportDropdown: Ngā Pae
ReportDropdown: 'Ngā Pae'
SubsiteXHRController:
MENUTITLE: ManaXHRPaeiti
Subsites:
DefaultSiteFieldLabel: 'Pae taunoa'
DomainFieldLabel: Rohe
@ -62,6 +61,5 @@ mi:
TitleFieldLabel: 'Ingoa Pae Iti'
SubsitesVirtualPage:
DESCRIPTION: 'Ka whakaatu i ngā ihirangi o tētahi whārangi ki tētahi atu pae iti'
PLURALNAME: 'Ngā Whārangi Taketake'
SINGULARNAME: 'Whārangi Mariko Pae Iti'
SubsiteField: Pae iti
SubsiteField: 'Pae iti'

View File

@ -1,6 +1,4 @@
nb_NO:
SubsiteAdmin:
MENUTITLE: Underdomener
GridFieldAddFromTemplateButton:
AddFromTemplate: 'Add New from Template'
GroupSubsites:
@ -8,9 +6,10 @@ nb_NO:
ACCESSONLY: 'Only these subsites'
ACCESSRADIOTITLE: 'Give this group access to'
SECURITYTABTITLE: subdomener
SubsiteAdmin:
MENUTITLE: Underdomener
SubsiteDomain:
DOMAIN: Domain
IS_PRIMARY: 'Is Primary Domain'
PLURALNAME: 'Subsite Domains'
SINGULARNAME: 'Subsite Domain'
SubsitesVirtualPage:

View File

@ -1,6 +1,4 @@
nl:
SubsiteAdmin:
MENUTITLE: Subsites
FileSubsites:
SubsiteFieldLabel: Subsite
GridFieldAddFromTemplateButton:
@ -17,9 +15,10 @@ nl:
SINGULARNAME: Subsite
SiteConfigTitle: 'Jouw Site Naam'
TabTitleConfig: Configuratie
SubsiteAdmin:
MENUTITLE: Subsites
SubsiteDomain:
DOMAIN: Domein
IS_PRIMARY: 'Is Primaire domein'
PLURALNAME: 'Subsite Domeinen'
SINGULARNAME: 'Subsite Domein'
Subsites:
@ -29,6 +28,5 @@ nl:
ThemeFieldLabel: Thema
TitleFieldLabel: 'Subsite naam'
SubsitesVirtualPage:
PLURALNAME: 'Subsites Virtuele pagina'
SINGULARNAME: 'Subsites Virtuele pagina'
SubsiteField: Subsite

View File

@ -1,6 +1,4 @@
pl_PL:
SubsiteAdmin:
MENUTITLE: Podwitryny
GridFieldAddFromTemplateButton:
AddFromTemplate: 'Dodaj nową na podstawie szablonu'
GroupSubsites:
@ -8,9 +6,10 @@ pl_PL:
ACCESSONLY: 'Tylko te podwitryny'
ACCESSRADIOTITLE: 'Daj tej grupie dostęp do'
SECURITYTABTITLE: Podwitryny
SubsiteAdmin:
MENUTITLE: Podwitryny
SubsiteDomain:
DOMAIN: Domena
IS_PRIMARY: 'Ma priorytet?'
PLURALNAME: 'Domeny podwitryny'
SINGULARNAME: 'Domena podwitryny'
SubsitesVirtualPage:

82
lang/ru.yml Normal file
View File

@ -0,0 +1,82 @@
ru:
ASSETADMIN:
SUBSITENOTICE: 'Папки и файлы созданные на основном сайте так же доступны для под-сайтов.'
DomainNameField:
INVALID_DOMAIN: 'Неверный домен'
FileSubsites:
AllSitesDropdownOpt: 'Все сайты'
SubsiteFieldLabel: Подсайт
GridFieldAddFromTemplateButton:
AddFromTemplate: 'Добавить из шаблона'
GroupSubsites:
ACCESSALL: 'Все подсайты'
ACCESSONLY: 'Только эти подсайты'
ACCESSRADIOTITLE: 'Разрешить этой группе доступ к'
GlobalGroup: 'глобальная группа'
MANAGE_SUBSITES: 'Управление подсайтами для групп'
MANAGE_SUBSITES_HELP: 'Возможность ограничить права доступа для группы к одному и более подсайтам'
SECURITYTABTITLE: Подсайты
LeftAndMainSubsites:
Saved: 'Сохранено, пожалуйста обновите связанные группы'
LeftAndMain_Menu:
Hello: Здравствуйте
LOGOUT: 'Выход'
SiteTreeSubsites:
CopyAction: Копировать
CopyToSubsite: 'Копировать страницу на подсайт'
CopyToSubsiteWithChildren: 'Включая под-страницы?'
SubsiteOperations: 'Операции над подсайтами'
Subsite:
COPYSTRUCTURE: 'Скопировать структуры из:'
CopyMessage: 'Создана копия {title}'
CustomExtraMeta: 'Пользовательские мета-тэги'
CustomMetaDescription: Описание
CustomMetaKeywords: 'Ключевые слова'
CustomMetaTitle: Заголовок
DOMAINSAVEFIRST: 'Вы можете добавлять домены только после первого сохранения'
DomainsHeadline: 'Домены для этого подсайта'
DomainsListTitle: Домены
IsPublicHeaderField: 'Активный подсайт'
NOTEMPLATE: 'Нет шаблона'
PLURALNAME: Подсайты
PageTypeBlacklistField: 'Запретить данные типы страниц?'
SINGULARNAME: Подсайт
SiteConfigSubtitle: 'ваш слоган здесь'
SiteConfigTitle: 'Название сайта'
TabTitleConfig: Конфигурация
ValidateTitle: 'Пожалуйста, добавьте "Заголовок"'
SubsiteAdmin:
MENUTITLE: Подсайты
SubsiteDomain:
DOMAIN: Домен
DOMAIN_DESCRIPTION: 'Домен подсайта (без указания протоколов: http:// и https://). Разрешены (*) для обозначения поддоменов.'
IS_PRIMARY: 'Это основной домен?'
PLURALNAME: 'Домены подсайтов'
PROTOCOL_AUTOMATIC: Автоматически
PROTOCOL_DESCRIPTION: 'Отметить как основной домен для данного подсайта'
PROTOCOL_HTTP: 'http://'
PROTOCOL_HTTPS: 'https://'
Protocol: Протокол
SINGULARNAME: 'Домен подсайта'
SubsiteReportWrapper:
ReportDropdown: Сайты
SubsiteXHRController:
MENUTITLE: SubsiteXHRController
Subsites:
DefaultSiteFieldLabel: 'Основной сайт'
DomainFieldLabel: Домен
IsPublicFieldLabel: 'Разрешить публичный доступ'
LanguageFieldLabel: Язык
MainSiteTitle: 'Основной Сайт'
PageTypeBlacklistFieldLabel: 'Запрещённые типы страниц'
PrimaryDomainFieldLabel: 'Основной Домен'
RedirectURLFieldLabel: 'Ссылка для перенаправления'
ThemeFieldLabel: Оформление
TitleFieldLabel: 'Название Подсайта'
SubsitesVirtualPage:
DESCRIPTION: 'Отображает содержимое выбранной страницы на другом подсайте'
PLURALNAME: 'Базовые страницы'
SINGULARNAME: 'Виртуальная страница подсайта'
SubsiteField: Подсайт
VirtualPage:
EDITCONTENT: 'Нажмите для изменения содержимого'

View File

@ -1,16 +1,15 @@
tr_TR:
SubsiteAdmin:
MENUTITLE: Alt Siteler
GridFieldAddFromTemplateButton:
AddFromTemplate: 'Add New from Template'
GroupSubsites:
ACCESSALL: 'All subsites'
ACCESSONLY: 'Only these subsites'
ACCESSRADIOTITLE: 'Give this group access to'
SECURITYTABTITLE: Alt Siteler
SECURITYTABTITLE: 'Alt Siteler'
SubsiteAdmin:
MENUTITLE: 'Alt Siteler'
SubsiteDomain:
DOMAIN: Domain
IS_PRIMARY: 'Is Primary Domain'
PLURALNAME: 'Subsite Domains'
SINGULARNAME: 'Subsite Domain'
SubsitesVirtualPage:

View File

@ -1,67 +1,65 @@
zh:
SubsiteAdmin:
MENUTITLE: 多个子网站
SubsiteXHRController:
MENUTITLE: 子网站 XHR 控制器
ASSETADMIN:
SUBSITENOTICE: '主网站上创建的文件夹和文件可以被所有子网站访问。'
SUBSITENOTICE: 主网站上创建的文件夹和文件可以被所有子网站访问。
FileSubsites:
AllSitesDropdownOpt: '所有网站'
AllSitesDropdownOpt: 所有网站
SubsiteFieldLabel: 子网站
GridFieldAddFromTemplateButton:
AddFromTemplate: '从模板中取出新加入'
GroupSubsites:
ACCESSALL: '所有子网站'
ACCESSALL: 所有子网站
ACCESSONLY: '仅这些子网站'
ACCESSRADIOTITLE: '准许该群进入'
GlobalGroup: '全局小组'
MANAGE_SUBSITES: '管理小组的子网站'
MANAGE_SUBSITES_HELP: '能够将权限限制在一个小组、一个或多个子网站。'
MANAGE_SUBSITES: 管理小组的子网站
MANAGE_SUBSITES_HELP: 能够将权限限制在一个小组、一个或多个子网站。
SECURITYTABTITLE: 多个子网站
LeftAndMainSubsites:
Saved: '已保存,请更新相关的页面。'
SiteTreeSubsites:
CopyAction: 复制
CopyToSubsite: '将页面复制到子网站'
CopyToSubsite: 将页面复制到子网站
Subsite:
COPYSTRUCTURE: '复制结构来自:'
COPYSTRUCTURE: 复制结构来自:
CopyMessage: '已创建一个 {title} 的副本'
CustomExtraMeta: '自定义 Meta 标签'
CustomMetaDescription: 说明
CustomMetaKeywords: 关键词
CustomMetaTitle: 标题
CustomMetaKeywords: '关键词'
CustomMetaTitle: '标题'
DOMAINSAVEFIRST: '只有当您第一次保存后,才能添加域名'
DomainsHeadline: '这个子网站的域名'
DomainsHeadline: 这个子网站的域名
DomainsListTitle: 域名
IsPublicHeaderField: '活跃的子网站'
NOTEMPLATE: '没有模板'
IsPublicHeaderField: 活跃的子网站
NOTEMPLATE: 没有模板
PLURALNAME: 多个子网站
PageTypeBlacklistField: '禁止页面类型?'
PageTypeBlacklistField: 禁止页面类型?
SINGULARNAME: 子网站
SiteConfigSubtitle: '您的标语在这里'
SiteConfigTitle: '您的网站名称'
TabTitleConfig: 配置
SiteConfigTitle: 您的网站名称
TabTitleConfig: '配置'
ValidateTitle: '请添加一个“标题”'
SubsiteAdmin:
MENUTITLE: 多个子网站
SubsiteDomain:
DOMAIN: 域名
IS_PRIMARY: '是主域名'
PLURALNAME: '多个子网站域名'
SINGULARNAME: '子网站域名'
PLURALNAME: 多个子网站域名
SINGULARNAME: 子网站域名
SubsiteReportWrapper:
ReportDropdown: 网站
SubsiteXHRController:
MENUTITLE: '子网站 XHR 控制器'
Subsites:
DefaultSiteFieldLabel: '默认网站'
DefaultSiteFieldLabel: 默认网站
DomainFieldLabel: 域名
IsPublicFieldLabel: '启用公共访问权'
LanguageFieldLabel: 语言
MainSiteTitle: '主网站'
PageTypeBlacklistFieldLabel: '页面类型黑名单'
PrimaryDomainFieldLabel: '主域名'
MainSiteTitle: 主网站
PageTypeBlacklistFieldLabel: 页面类型黑名单
PrimaryDomainFieldLabel: 主域名
RedirectURLFieldLabel: '重定向 URL'
ThemeFieldLabel: 主题
TitleFieldLabel: '子网站名称'
TitleFieldLabel: 子网站名称
SubsitesVirtualPage:
DESCRIPTION: '显示另一个子网站上一个页面的内容'
PLURALNAME: '基本页面'
SINGULARNAME: '子网站虚拟页面'
SINGULARNAME: 子网站虚拟页面
SubsiteField: 子网站

View File

@ -0,0 +1,45 @@
<div class="cms-menu cms-panel cms-panel-layout west" id="cms-menu" data-layout-type="border">
<div class="cms-logo-header north">
<div class="cms-logo">
<a href="$ApplicationLink" target="_blank" title="$ApplicationName (Version - $CMSVersion)">
$ApplicationName <% if $CMSVersion %><abbr class="version">$CMSVersion</abbr><% end_if %>
</a>
<span><% if $SiteConfig %>$SiteConfig.Title<% else %>$ApplicationName<% end_if %></span>
</div>
<div class="cms-login-status">
<a href="$LogoutURL" class="logout-link font-icon-logout" title="<%t LeftAndMain_Menu_ss.LOGOUT 'Log out' %>"></a>
<% with $CurrentMember %>
<span>
<%t LeftAndMain_Menu_ss.Hello 'Hi' %>
<a href="{$AbsoluteBaseURL}admin/myprofile" class="profile-link">
<% if $FirstName && $Surname %>$FirstName $Surname<% else_if $FirstName %>$FirstName<% else %>$Email<% end_if %>
</a>
</span>
<% end_with %>
</div>
<% if $ListSubsites %>
<% include SubsiteList %>
<% end_if %>
</div>
<div class="cms-panel-content center">
<ul class="cms-menu-list">
<% loop $MainMenu %>
<li class="$LinkingMode $FirstLast <% if $LinkingMode == 'link' %><% else %>opened<% end_if %>" id="Menu-$Code" title="$Title.ATT">
<a href="$Link" $AttributesHTML>
<span class="icon icon-16 icon-{$Code.LowerCase}">&nbsp;</span>
<span class="text">$Title</span>
</a>
</li>
<% end_loop %>
</ul>
</div>
<div class="cms-panel-toggle south">
<button class="sticky-toggle" type="button" title="Sticky nav">Sticky nav</button>
<span class="sticky-status-indicator">auto</span>
<a class="toggle-expand" href="#"><span>&raquo;</span></a>
<a class="toggle-collapse" href="#"><span>&laquo;</span></a>
</div>
</div>

View File

@ -6,31 +6,28 @@ use SilverStripe\Subsites\Model\Subsite;
class BaseSubsiteTest extends SapphireTest
{
function setUp()
public function setUp()
{
parent::setUp();
Subsite::$use_session_subsiteid = true;
}
Subsite::$use_session_subsiteid = true;
Subsite::$force_subsite = null;}
/**
* Avoid subsites filtering on fixture fetching.
*/
function objFromFixture($class, $id)
{
Subsite::disable_subsite_filter(true);
$obj = parent::objFromFixture($class, $id);
Subsite::disable_subsite_filter(false);
/**
* 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;
}
/**
* Tests the initial state of disable_subsite_filter
*/
function testDisableSubsiteFilter()
{
$this->assertFalse(Subsite::$disable_subsite_filter);
}
/**
* Tests the initial state of disable_subsite_filter
*/
public function testDisableSubsiteFilter()
{$this->assertFalse(Subsite::$disable_subsite_filter);
}
}

View File

@ -3,33 +3,47 @@
use SilverStripe\Assets\File;
use SilverStripe\Assets\Folder;
use SilverStripe\Forms\FieldList;
use SilverStripe\Subsites\Extensions\FileSubsites;
use SilverStripe\Security\Member;
use SilverStripe\Subsites\Model\Subsite;
class FileSubsitesTest extends BaseSubsiteTest
{
static $fixture_file = 'subsites/tests/SubsiteTest.yml';
public static $fixture_file = 'subsites/tests/SubsiteTest.yml';
function testTrivialFeatures()
/**
* Disable other file extensions
*
* @var array
*/
protected $illegalExtensions = array(
'File' => array(
'SecureFileExtension',
'VersionedFileExtension'
),
'SiteTree' => array(
'Translatable',
)
);
public function testTrivialFeatures()
{
$this->assertTrue(is_array(singleton(FileSubsites::class)->extraStatics()));
$this->assertTrue(is_array(singleton('FileSubsites')->extraStatics()));
$file = new File();
$file->Name = 'FileTitle';
$file->Title = 'FileTitle';
$this->assertEquals(' * FileTitle', $file->alternateTreeTitle());
$file->SubsiteID = $this->objFromFixture(Subsite::class, 'domaintest1')->ID;
$file->SubsiteID = $this->objFromFixture('Subsite', 'domaintest1')->ID;
$this->assertEquals('FileTitle', $file->getTreeTitle());
$this->assertTrue(singleton('SilverStripe\\Assets\\Folder')->getCMSFields() instanceof FieldList);
$this->assertTrue(singleton(Folder::class)->getCMSFields() instanceof FieldList);
Subsite::changeSubsite(1);
$this->assertEquals($file->cacheKeyComponent(), 'subsite-1');
}
function testWritingSubsiteID()
public function testWritingSubsiteID()
{
$this->objFromFixture('SilverStripe\\Security\\Member', 'admin')->logIn();
$this->objFromFixture(Member::class, 'admin')->logIn();
$subsite = $this->objFromFixture(Subsite::class, 'domaintest1');
$subsite = $this->objFromFixture('Subsite', 'domaintest1');
FileSubsites::$default_root_folders_global = true;
Subsite::changeSubsite(0);
@ -64,24 +78,25 @@ class FileSubsitesTest extends BaseSubsiteTest
$this->assertEquals($folder->SubsiteID, $file->SubsiteID);
}
function testSubsitesFolderDropdown()
public function testSubsitesFolderDropdown()
{
$this->objFromFixture('SilverStripe\\Security\\Member', 'admin')->logIn();
$this->objFromFixture(Member::class, 'admin')->logIn();
$file = new Folder();
$source = array_values($file->getCMSFields()->dataFieldByName('SubsiteID')->getSource());
asort($source);
$this->assertEquals([
$this->assertEquals(array(
'Main site',
'Template',
'Subsite1 Template',
'Subsite2 Template',
'Template',
'Test 1',
'Test 2',
'Test 3'
], $source);
'Test 3',
'Test Non-SSL',
'Test SSL',
), array_values($source));
}
}

View File

@ -1,25 +1,24 @@
<?php
use SilverStripe\Forms\FieldList;
use SilverStripe\Security\Group;
use SilverStripe\Forms\FieldList;
use SilverStripe\Subsites\Extensions\GroupSubsites;
use SilverStripe\Subsites\Model\Subsite;
class GroupSubsitesTest extends BaseSubsiteTest
{
static $fixture_file = 'subsites/tests/SubsiteTest.yml';
public static $fixture_file = 'subsites/tests/SubsiteTest.yml';
protected $requireDefaultRecordsFrom = [GroupSubsites::class];
function testTrivialFeatures()
public function testTrivialFeatures()
{
$this->assertTrue(is_array(singleton(GroupSubsites::class)->extraStatics()));
$this->assertTrue(is_array(singleton(GroupSubsites::class)->providePermissions()));
$this->assertTrue(singleton('SilverStripe\\Security\\Group')->getCMSFields() instanceof FieldList);
$this->assertTrue(is_array(singleton('GroupSubsites')->extraStatics()));
$this->assertTrue(is_array(singleton('GroupSubsites')->providePermissions()));
$this->assertTrue(singleton(Group::class)->getCMSFields() instanceof FieldList);
}
function testAlternateTreeTitle()
public function testAlternateTreeTitle()
{
$group = new Group();
$group->Title = 'The A Team';

View File

@ -1,21 +1,21 @@
<?php
use SilverStripe\Security\Member;
use SilverStripe\CMS\Controllers\CMSMain;
use SilverStripe\Admin\LeftAndMain;
use SilverStripe\AssetAdmin\Controller\AssetAdmin;
use SilverStripe\Core\Config\Config;
use SilverStripe\CMS\Controllers\CMSPageEditController;
use SilverStripe\Dev\FunctionalTest;
use SilverStripe\Subsites\Model\Subsite;
class LeftAndMainSubsitesTest extends FunctionalTest
{
static $fixture_file = 'subsites/tests/SubsiteTest.yml';
public static $fixture_file = 'subsites/tests/SubsiteTest.yml';
/**
* Avoid subsites filtering on fixture fetching.
*/
function objFromFixture($class, $id)
public function objFromFixture($class, $id)
{
Subsite::disable_subsite_filter(true);
$obj = parent::objFromFixture($class, $id);
@ -24,34 +24,34 @@ class LeftAndMainSubsitesTest extends FunctionalTest
return $obj;
}
function testSectionSites()
public function testSectionSites()
{
$member = $this->objFromFixture('SilverStripe\\Security\\Member', 'subsite1member');
$member = $this->objFromFixture(Member::class, 'subsite1member');
$cmsmain = singleton('SilverStripe\\CMS\\Controllers\\CMSMain');
$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.');
$this->assertDOSEquals(array(
array('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('SilverStripe\\Security\\Member', 'editor');
$member = $this->objFromFixture(Member::class, 'editor');
$cmsmain = singleton('SilverStripe\\CMS\\Controllers\\CMSMain');
$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.');
$this->assertDOSContains(array(
array('Title' =>'Main site')
), $subsites, 'Includes the main site for members who can access all sites.');
}
function testAccessChecksDontChangeCurrentSubsite()
public function testAccessChecksDontChangeCurrentSubsite()
{
$admin = $this->objFromFixture("SilverStripe\\Security\\Member", "admin");
$admin = $this->objFromFixture(Member::class, "admin");
$this->loginAs($admin);
$ids = [];
$ids = array();
$subsite1 = $this->objFromFixture(Subsite::class, 'domaintest1');
$subsite2 = $this->objFromFixture(Subsite::class, 'domaintest2');
@ -69,34 +69,31 @@ class LeftAndMainSubsitesTest extends FunctionalTest
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.");
}
$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.");
}
}
}
function testShouldChangeSubsite()
public function testShouldChangeSubsite()
{
$l = new LeftAndMain();
Config::inst()->nest();
Config::modify()->set('SilverStripe\\CMS\\Controllers\\CMSPageEditController', 'treats_subsite_0_as_global',
false);
$this->assertTrue($l->shouldChangeSubsite('SilverStripe\\CMS\\Controllers\\CMSPageEditController', 0, 5));
$this->assertFalse($l->shouldChangeSubsite('SilverStripe\\CMS\\Controllers\\CMSPageEditController', 0, 0));
$this->assertTrue($l->shouldChangeSubsite('SilverStripe\\CMS\\Controllers\\CMSPageEditController', 1, 5));
$this->assertFalse($l->shouldChangeSubsite('SilverStripe\\CMS\\Controllers\\CMSPageEditController', 1, 1));
Config::inst()->update(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('SilverStripe\\CMS\\Controllers\\CMSPageEditController', 'treats_subsite_0_as_global',
true);
$this->assertFalse($l->shouldChangeSubsite('SilverStripe\\CMS\\Controllers\\CMSPageEditController', 0, 5));
$this->assertFalse($l->shouldChangeSubsite('SilverStripe\\CMS\\Controllers\\CMSPageEditController', 0, 0));
$this->assertTrue($l->shouldChangeSubsite('SilverStripe\\CMS\\Controllers\\CMSPageEditController', 1, 5));
$this->assertFalse($l->shouldChangeSubsite('SilverStripe\\CMS\\Controllers\\CMSPageEditController', 1, 1));
Config::inst()->update(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::inst()->unnest();
}
}

View File

@ -14,32 +14,31 @@ class SiteConfigSubsitesTest extends BaseSubsiteTest
$subsite1 = $this->objFromFixture(Subsite::class, 'domaintest1');
$subsite2 = $this->objFromFixture(Subsite::class, 'domaintest2');
$this->assertTrue(is_array(singleton(SiteConfigSubsites::class)->extraStatics()));
$this->assertTrue(is_array(singleton(SiteConfigSubsites::class)->extraStatics()));
Subsite::changeSubsite(0);
$sc = SiteConfig::current_site_config();
$sc->Title = 'RootSite';
$sc->write();
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($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($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');
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,13 +1,17 @@
<?php
use SilverStripe\CMS\Controllers\CMSMain;
use SilverStripe\CMS\Model\ErrorPage;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Control\Session;
use SilverStripe\Core\Convert;
use SilverStripe\Dev\TestOnly;
use SilverStripe\Forms\FieldList;
use SilverStripe\SiteConfig\SiteConfig;
use SilverStripe\Forms\FieldList;
use SilverStripe\CMS\Model\ErrorPage;
use SilverStripe\Core\Config\Config;
use SilverStripe\Security\Member;
use SilverStripe\Control\Session;
use SilverStripe\Control\Director;
use SilverStripe\CMS\Controllers\CMSMain;
use SilverStripe\Core\Convert;
use SilverStripe\Versioned\Versioned;
use SilverStripe\Dev\TestOnly;
use SilverStripe\Subsites\Extensions\SiteTreeSubsites;
use SilverStripe\Subsites\Model\Subsite;
use SilverStripe\Subsites\Pages\SubsitesVirtualPage;
@ -16,7 +20,7 @@ use SilverStripe\Subsites\Pages\SubsitesVirtualPage;
class SiteTreeSubsitesTest extends BaseSubsiteTest
{
static $fixture_file = 'subsites/tests/SubsiteTest.yml';
public static $fixture_file = 'subsites/tests/SubsiteTest.yml';
protected $extraDataObjects = [
'SiteTreeSubsitesTest_ClassA',
@ -25,11 +29,10 @@ class SiteTreeSubsitesTest extends BaseSubsiteTest
];
protected $illegalExtensions = [
'SilverStripe\CMS\Model\SiteTree' => ['Translatable']
SiteTree::class => ['Translatable']
];
function testPagesInDifferentSubsitesCanShareURLSegment()
{
public function testPagesInDifferentSubsitesCanShareURLSegment() {
$subsiteMain = $this->objFromFixture(Subsite::class, 'main');
$subsite1 = $this->objFromFixture(Subsite::class, 'subsite1');
@ -57,82 +60,80 @@ class SiteTreeSubsitesTest extends BaseSubsiteTest
$this->assertEquals($pageMain->URLSegment, $pageSubsite1->URLSegment,
'Pages in different subsites can share the same URL'
);
}
}
function testBasicSanity()
{
$this->assertTrue(singleton('SilverStripe\\CMS\\Model\\SiteTree')->getSiteConfig() instanceof SiteConfig);
public function testBasicSanity() {
$this->assertTrue(singleton(SiteTree::class)->getSiteConfig() instanceof SiteConfig);
// The following assert is breaking in Translatable.
$this->assertTrue(singleton('SilverStripe\\CMS\\Model\\SiteTree')->getCMSFields() instanceof FieldList);
$this->assertTrue(singleton(SiteTree::class)->getCMSFields() instanceof FieldList);
$this->assertTrue(singleton(SubsitesVirtualPage::class)->getCMSFields() instanceof FieldList);
$this->assertTrue(is_array(singleton(SiteTreeSubsites::class)->extraStatics()));
}
function testErrorPageLocations()
public function testErrorPageLocations() {
$subsite1 = $this->objFromFixture(Subsite::class, 'domaintest1');
Subsite::changeSubsite($subsite1->ID);
$path = ErrorPage::get_filepath_for_errorcode(500);
$static_path = Config::inst()->get(ErrorPage::class, 'static_filepath');
$expected_path = $static_path . '/error-500-'.$subsite1->domain().'.html';
$this->assertEquals($expected_path, $path);
}
public function testCanEditSiteTree()
{
$subsite1 = $this->objFromFixture(Subsite::class, 'domaintest1');
Subsite::changeSubsite($subsite1->ID);
$path = SiteTreeSubsitesTest_ErrorPage::get_error_filename_spy(500);
$expected_path = 'error-500-' . $subsite1->domain() . '.html';
$this->assertEquals($expected_path, $path);
}
function testCanEditSiteTree()
{
$admin = $this->objFromFixture('SilverStripe\\Security\\Member', 'admin');
$subsite1member = $this->objFromFixture('SilverStripe\\Security\\Member', 'subsite1member');
$subsite2member = $this->objFromFixture('SilverStripe\\Security\\Member', 'subsite2member');
$admin = $this->objFromFixture(Member::class, 'admin');
$subsite1member = $this->objFromFixture(Member::class, 'subsite1member');
$subsite2member = $this->objFromFixture(Member::class, 'subsite2member');
$mainpage = $this->objFromFixture('Page', 'home');
$subsite1page = $this->objFromFixture('Page', 'subsite1_home');
$subsite2page = $this->objFromFixture('Page', 'subsite2_home');
$subsite1 = $this->objFromFixture(Subsite::class, 'subsite1');
$subsite2 = $this->objFromFixture(Subsite::class, 'subsite2');
// Cant pass member as arguments to canEdit() because of GroupSubsites
Session::set("loggedInAs", $admin->ID);
$this->assertTrue(
(bool)$subsite1page->canEdit(),
'Administrators can edit all subsites'
);
// Cant pass member as arguments to canEdit() because of GroupSubsites
Session::set("loggedInAs", $admin->ID);
$this->assertTrue(
(bool)$subsite1page->canEdit(),
'Administrators can edit all subsites'
);
// @todo: Workaround because GroupSubsites->augmentSQL() is relying on session state
Subsite::changeSubsite($subsite1);
// @todo: Workaround because GroupSubsites->augmentSQL() is relying on session state
Subsite::changeSubsite($subsite1);
Session::set("loggedInAs", $subsite1member->ID);
$this->assertTrue(
(bool)$subsite1page->canEdit(),
'Members can edit pages on a subsite if they are in a group belonging to this subsite'
);
Session::set("loggedInAs", $subsite1member->ID);
$this->assertTrue(
(bool)$subsite1page->canEdit(),
'Members can edit pages on a subsite if they are in a group belonging to this subsite'
);
Session::set("loggedInAs", $subsite2member->ID);
$this->assertFalse(
(bool)$subsite1page->canEdit(),
'Members cant edit pages on a subsite if they are not in a group belonging to this subsite'
);
Session::set("loggedInAs", $subsite2member->ID);
$this->assertFalse(
(bool)$subsite1page->canEdit(),
'Members cant edit pages on a subsite if they are not in a group belonging to this subsite'
);
// @todo: Workaround because GroupSubsites->augmentSQL() is relying on session state
Subsite::changeSubsite(0);
$this->assertFalse(
$mainpage->canEdit(),
'Members cant edit pages on the main site if they are not in a group allowing this'
);
}
// @todo: Workaround because GroupSubsites->augmentSQL() is relying on session state
Subsite::changeSubsite(0);
$this->assertFalse(
$mainpage->canEdit(),
'Members cant edit pages on the main site if they are not in a group allowing this'
);
}
/**
* Similar to {@link SubsitesVirtualPageTest->testSubsiteVirtualPageCanHaveSameUrlsegmentAsOtherSubsite()}.
*/
function testTwoPagesWithSameURLOnDifferentSubsites()
{
// Set up a couple of pages with the same URL on different subsites
/**
* Similar to {@link SubsitesVirtualPageTest->testSubsiteVirtualPageCanHaveSameUrlsegmentAsOtherSubsite()}.
*/
public function testTwoPagesWithSameURLOnDifferentSubsites() {
// Set up a couple of pages with the same URL on different subsites
$s1 = $this->objFromFixture(Subsite::class, 'domaintest1');
$s2 = $this->objFromFixture(Subsite::class, 'domaintest2');
$p1 = new SiteTree();
$p1->Title = $p1->URLSegment = "test-page";
$p1->SubsiteID = $s1->ID;
$p1->write();
$p1 = new SiteTree();
$p1->Title = $p1->URLSegment = "test-page";
$p1->SubsiteID = $s1->ID;
$p1->write();
$p2 = new SiteTree();
$p2->Title = $p1->URLSegment = "test-page";
@ -147,82 +148,182 @@ class SiteTreeSubsitesTest extends BaseSubsiteTest
Subsite::changeSubsite($s1);
$this->assertEquals($p1->ID, SiteTree::get_by_link('test-page')->ID);
Subsite::changeSubsite($s2);
$this->assertEquals($p2->ID, SiteTree::get_by_link('test-page')->ID);
}
Subsite::changeSubsite($s2);
$this->assertEquals($p2->ID, SiteTree::get_by_link('test-page')->ID);
}
function testPageTypesBlacklistInClassDropdown()
{
$editor = $this->objFromFixture('SilverStripe\\Security\\Member', 'editor');
Session::set("loggedInAs", $editor->ID);
public function testPageTypesBlacklistInClassDropdown() {
$editor = $this->objFromFixture(Member::class, 'editor');
Session::set("loggedInAs", $editor->ID);
$s1 = $this->objFromFixture(Subsite::class, 'domaintest1');
$s2 = $this->objFromFixture(Subsite::class, 'domaintest2');
$page = singleton('SilverStripe\\CMS\\Model\\SiteTree');
$page = singleton('SiteTree');
$s1->PageTypeBlacklist = 'SiteTreeSubsitesTest_ClassA,ErrorPage';
$s1->write();
$s1->PageTypeBlacklist = 'SiteTreeSubsitesTest_ClassA,ErrorPage';
$s1->write();
Subsite::changeSubsite($s1);
$settingsFields = $page->getSettingsFields()->dataFieldByName('ClassName')->getSource();
Subsite::changeSubsite($s1);
$settingsFields = $page->getSettingsFields()->dataFieldByName('ClassName')->getSource();
$this->assertArrayNotHasKey('SilverStripe\\CMS\\Model\\ErrorPage',
$settingsFields
);
$this->assertArrayNotHasKey('SiteTreeSubsitesTest_ClassA',
$settingsFields
);
$this->assertArrayHasKey('SiteTreeSubsitesTest_ClassB',
$settingsFields
);
$this->assertArrayNotHasKey(ErrorPage::class,
$settingsFields
);
$this->assertArrayNotHasKey('SiteTreeSubsitesTest_ClassA',
$settingsFields
);
$this->assertArrayHasKey('SiteTreeSubsitesTest_ClassB',
$settingsFields
);
Subsite::changeSubsite($s2);
$settingsFields = $page->getSettingsFields()->dataFieldByName('ClassName')->getSource();
$this->assertArrayHasKey('SilverStripe\\CMS\\Model\\ErrorPage',
$settingsFields
);
$this->assertArrayHasKey('SiteTreeSubsitesTest_ClassA',
$settingsFields
);
$this->assertArrayHasKey('SiteTreeSubsitesTest_ClassB',
$settingsFields
);
Subsite::changeSubsite($s2);
$settingsFields = $page->getSettingsFields()->dataFieldByName('ClassName')->getSource();
$this->assertArrayHasKey(ErrorPage::class,
$settingsFields
);
$this->assertArrayHasKey('SiteTreeSubsitesTest_ClassA',
$settingsFields
);
$this->assertArrayHasKey('SiteTreeSubsitesTest_ClassB',
$settingsFields
);
}
public function testCopyToSubsite() {
// Remove baseurl if testing in subdir
Config::inst()->update(Director::class, 'alternate_base_url', '/');
/** @var Subsite $otherSubsite */
$otherSubsite = $this->objFromFixture(Subsite::class, 'subsite1');
$staffPage = $this->objFromFixture('Page', 'staff'); // nested page
$contactPage = $this->objFromFixture('Page', 'contact'); // top level page
$staffPage2 = $staffPage->duplicateToSubsite($otherSubsite->ID);
$contactPage2 = $contactPage->duplicateToSubsite($otherSubsite->ID);
$this->assertNotEquals($staffPage->ID, $staffPage2->ID);
$this->assertNotEquals($staffPage->SubsiteID, $staffPage2->SubsiteID);
$this->assertNotEquals($contactPage->ID, $contactPage2->ID);
$this->assertNotEquals($contactPage->SubsiteID, $contactPage2->SubsiteID);
$this->assertEmpty($staffPage2->ParentID);
$this->assertEmpty($contactPage2->ParentID);
$this->assertNotEmpty($staffPage->ParentID);
$this->assertEmpty($contactPage->ParentID);
// Staff is shifted to top level and given a unique url segment
$domain = $otherSubsite->domain();
$this->assertEquals('http://'.$domain.'/staff-2/', $staffPage2->AbsoluteLink());
$this->assertEquals('http://'.$domain.'/contact-us-2/', $contactPage2->AbsoluteLink());
}
function testPageTypesBlacklistInCMSMain()
{
$editor = $this->objFromFixture('SilverStripe\\Security\\Member', 'editor');
Session::set("loggedInAs", $editor->ID);
public function testPageTypesBlacklistInCMSMain() {
$editor = $this->objFromFixture('Member', 'editor');
Session::set("loggedInAs", $editor->ID);
$cmsmain = new CMSMain();
$cmsmain = new CMSMain();
$s1 = $this->objFromFixture(Subsite::class, 'domaintest1');
$s2 = $this->objFromFixture(Subsite::class, 'domaintest2');
$s1 = $this->objFromFixture(Subsite::class,'domaintest1');
$s2 = $this->objFromFixture(Subsite::class,'domaintest2');
$s1->PageTypeBlacklist = 'SiteTreeSubsitesTest_ClassA,ErrorPage';
$s1->write();
$s1->PageTypeBlacklist = 'SiteTreeSubsitesTest_ClassA,ErrorPage';
$s1->write();
Subsite::changeSubsite($s1);
$hints = Convert::json2array($cmsmain->SiteTreeHints());
$classes = $hints['Root']['disallowedChildren'];
$this->assertContains('SilverStripe\\CMS\\Model\\ErrorPage', $classes);
$this->assertContains('SiteTreeSubsitesTest_ClassA', $classes);
$this->assertNotContains('SiteTreeSubsitesTest_ClassB', $classes);
Subsite::changeSubsite($s1);
$hints = Convert::json2array($cmsmain->SiteTreeHints());
$classes = $hints['Root']['disallowedChildren'];
$this->assertContains(ErrorPage::class, $classes);
$this->assertContains('SiteTreeSubsitesTest_ClassA', $classes);
$this->assertNotContains('SiteTreeSubsitesTest_ClassB', $classes);
Subsite::changeSubsite($s2);
$hints = Convert::json2array($cmsmain->SiteTreeHints());
$classes = $hints['Root']['disallowedChildren'];
$this->assertNotContains('SilverStripe\\CMS\\Model\\ErrorPage', $classes);
$this->assertNotContains('SiteTreeSubsitesTest_ClassA', $classes);
$this->assertNotContains('SiteTreeSubsitesTest_ClassB', $classes);
Subsite::changeSubsite($s2);
$hints = Convert::json2array($cmsmain->SiteTreeHints());
$classes = $hints['Root']['disallowedChildren'];
$this->assertNotContains(ErrorPage::class, $classes);
$this->assertNotContains('SiteTreeSubsitesTest_ClassA', $classes);
$this->assertNotContains('SiteTreeSubsitesTest_ClassB', $classes);
}
/**
* Tests that url segments between subsites don't conflict, but do conflict within them
*/
public function testValidateURLSegment() {
$this->logInWithPermission('ADMIN');
// Saving existing page in the same subsite doesn't change urls
$mainHome = $this->objFromFixture('Page', 'home');
$mainSubsiteID = $this->idFromFixture(Subsite::class, 'main');
Subsite::changeSubsite($mainSubsiteID);
$mainHome->Content = '<p>Some new content</p>';
$mainHome->write();
$this->assertEquals('home', $mainHome->URLSegment);
$mainHome->doPublish();
$mainHomeLive = Versioned::get_one_by_stage('Page', 'Live', sprintf('"SiteTree"."ID" = \'%d\'', $mainHome->ID));
$this->assertEquals('home', $mainHomeLive->URLSegment);
// Saving existing page in another subsite doesn't change urls
Subsite::changeSubsite($mainSubsiteID);
$subsite1Home = $this->objFromFixture('Page', 'subsite1_home');
$subsite1Home->Content = '<p>In subsite 1</p>';
$subsite1Home->write();
$this->assertEquals('home', $subsite1Home->URLSegment);
$subsite1Home->doPublish();
$subsite1HomeLive = Versioned::get_one_by_stage('Page', 'Live', sprintf('"SiteTree"."ID" = \'%d\'', $subsite1Home->ID));
$this->assertEquals('home', $subsite1HomeLive->URLSegment);
// Creating a new page in a subsite doesn't conflict with urls in other subsites
$subsite1ID = $this->idFromFixture(Subsite::class, 'subsite1');
Subsite::changeSubsite($subsite1ID);
$subsite1NewPage = new Page();
$subsite1NewPage->SubsiteID = $subsite1ID;
$subsite1NewPage->Title = 'Important Page (Subsite 1)';
$subsite1NewPage->URLSegment = 'important-page'; // Also exists in main subsite
$subsite1NewPage->write();
$this->assertEquals('important-page', $subsite1NewPage->URLSegment);
$subsite1NewPage->doPublish();
$subsite1NewPageLive = Versioned::get_one_by_stage('Page', 'Live', sprintf('"SiteTree"."ID" = \'%d\'', $subsite1NewPage->ID));
$this->assertEquals('important-page', $subsite1NewPageLive->URLSegment);
// Creating a new page in a subsite DOES conflict with urls in the same subsite
$subsite1NewPage2 = new Page();
$subsite1NewPage2->SubsiteID = $subsite1ID;
$subsite1NewPage2->Title = 'Important Page (Subsite 1)';
$subsite1NewPage2->URLSegment = 'important-page'; // Also exists in main subsite
$subsite1NewPage2->write();
$this->assertEquals('important-page-2', $subsite1NewPage2->URLSegment);
$subsite1NewPage2->doPublish();
$subsite1NewPage2Live = Versioned::get_one_by_stage('Page', 'Live', sprintf('"SiteTree"."ID" = \'%d\'', $subsite1NewPage2->ID));
$this->assertEquals('important-page-2', $subsite1NewPage2Live->URLSegment);
// Original page is left un-modified
$mainSubsiteImportantPageID = $this->idFromFixture('Page', 'importantpage');
$mainSubsiteImportantPage = Page::get()->byID($mainSubsiteImportantPageID);
$this->assertEquals('important-page', $mainSubsiteImportantPage->URLSegment);
$mainSubsiteImportantPage->Content = '<p>New Important Page Content</p>';
$mainSubsiteImportantPage->write();
$this->assertEquals('important-page', $mainSubsiteImportantPage->URLSegment);
}
public function testCopySubsiteWithChildren() {
$page = $this->objFromFixture('Page', 'about');
$newSubsite = $this->objFromFixture(Subsite::class, 'subsite1');
$moved = $page->duplicateToSubsite($newSubsite->ID, true);
$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');
}
public function testCopySubsiteWithoutChildren() {
$page = $this->objFromFixture('Page', 'about');
$newSubsite = $this->objFromFixture(Subsite::class, 'subsite2');
$moved = $page->duplicateToSubsite($newSubsite->ID, false);
$this->assertEquals($moved->SubsiteID, $newSubsite->ID, 'Ensure returned records are on new subsite');
$this->assertEquals($moved->AllChildren()->count(), 0, 'All pages are copied across');
}
}
class SiteTreeSubsitesTest_ClassA extends SiteTree implements TestOnly
{
}
class SiteTreeSubsitesTest_ClassB extends SiteTree implements TestOnly
{
}

View File

@ -1,23 +1,24 @@
<?php
use SilverStripe\Security\Member;
use SilverStripe\Control\Session;
use SilverStripe\Core\Config\Config;
use SilverStripe\CMS\Controllers\CMSPageEditController;
use SilverStripe\Dev\FunctionalTest;
use SilverStripe\Subsites\Controller\SubsiteXHRController;
use SilverStripe\Subsites\Model\Subsite;
class SubsiteAdminFunctionalTest extends FunctionalTest
{
static $fixture_file = 'subsites/tests/SubsiteTest.yml';
static $use_draft_site = true;
public static $fixture_file = 'subsites/tests/SubsiteTest.yml';
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.
*/
function getAndFollowAll($url)
public function getAndFollowAll($url)
{
$response = $this->get($url);
while ($location = $response->getHeader('Location')) {
@ -25,13 +26,13 @@ class SubsiteAdminFunctionalTest extends FunctionalTest
}
echo $response->getHeader('Location');
return $response;
}
return $response;
}
/**
* Anonymous user cannot access anything.
*/
function testAnonymousIsForbiddenAdminAccess()
public function testAnonymousIsForbiddenAdminAccess()
{
$response = $this->getAndFollowAll('admin/pages/?SubsiteID=0');
$this->assertRegExp('#^Security/login.*#', $this->mainSession->lastUrl(), 'Admin is disallowed');
@ -40,7 +41,7 @@ class SubsiteAdminFunctionalTest extends FunctionalTest
$response = $this->getAndFollowAll("admin/pages/?SubsiteID={$subsite1->ID}");
$this->assertRegExp('#^Security/login.*#', $this->mainSession->lastUrl(), 'Admin is disallowed');
$response = $this->getAndFollowAll(SubsiteXHRController::class);
$response = $this->getAndFollowAll('SubsiteXHRController');
$this->assertRegExp('#^Security/login.*#', $this->mainSession->lastUrl(),
'SubsiteXHRController is disallowed');
}
@ -48,9 +49,9 @@ class SubsiteAdminFunctionalTest extends FunctionalTest
/**
* Admin should be able to access all subsites and the main site
*/
function testAdminCanAccessAllSubsites()
public function testAdminCanAccessAllSubsites()
{
$member = $this->objFromFixture('SilverStripe\\Security\\Member', 'admin');
$member = $this->objFromFixture(Member::class, 'admin');
Session::set("loggedInAs", $member->ID);
$this->getAndFollowAll('admin/pages/?SubsiteID=0');
@ -67,59 +68,54 @@ class SubsiteAdminFunctionalTest extends FunctionalTest
'SubsiteXHRController is reachable');
}
function testAdminIsRedirectedToObjectsSubsite()
public function testAdminIsRedirectedToObjectsSubsite()
{
$member = $this->objFromFixture('SilverStripe\\Security\\Member', 'admin');
$member = $this->objFromFixture(Member::class, 'admin');
Session::set("loggedInAs", $member->ID);
$mainSubsitePage = $this->objFromFixture('Page', 'mainSubsitePage');
$subsite1Home = $this->objFromFixture('Page', 'subsite1_home');
Config::inst()->nest();
Config::inst()->nest();
Config::modify()->set('SilverStripe\\CMS\\Controllers\\CMSPageEditController', 'treats_subsite_0_as_global',
false);
Config::inst()->update(CMSPageEditController::class, 'treats_subsite_0_as_global', false);
Subsite::changeSubsite(0);
$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, 'Loading an object switches the subsite');
$this->assertRegExp("#^admin/pages.*#", $this->mainSession->lastUrl(), 'Lands on the correct section');
Config::modify()->set('SilverStripe\\CMS\\Controllers\\CMSPageEditController', 'treats_subsite_0_as_global',
true);
Config::inst()->update(CMSPageEditController::class, 'treats_subsite_0_as_global', true);
Subsite::changeSubsite(0);
$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, '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->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, 'Loading a main-site object does not change the subsite if configured with treats_subsite_0_as_global');
$this->assertRegExp("#^admin/pages.*#", $this->mainSession->lastUrl(), 'Lands on the correct section');
Config::inst()->unnest();
}
Config::inst()->unnest();
}
/**
* User which has AccessAllSubsites set to 1 should be able to access all subsites and main site,
* even though he does not have the ADMIN permission.
*/
function testEditorCanAccessAllSubsites()
public function testEditorCanAccessAllSubsites()
{
$member = $this->objFromFixture('SilverStripe\\Security\\Member', 'editor');
$member = $this->objFromFixture(Member::class, 'editor');
Session::set("loggedInAs", $member->ID);
$this->getAndFollowAll('admin/pages/?SubsiteID=0');
$this->assertEquals(Subsite::currentSubsiteID(), '0', 'Can access main site.');
$this->assertRegExp('#^admin/pages.*#', $this->mainSession->lastUrl(), 'Lands on the correct section');
$this->getAndFollowAll('admin/pages/?SubsiteID=0');
$this->assertEquals(Subsite::currentSubsiteID(), '0', 'Can access main site.');
$this->assertRegExp('#^admin/pages.*#', $this->mainSession->lastUrl(), 'Lands on the correct section');
$subsite1 = $this->objFromFixture(Subsite::class, 'subsite1');
$this->getAndFollowAll("admin/pages/?SubsiteID={$subsite1->ID}");
$this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Can access other subsite.');
$this->assertRegExp('#^admin/pages.*#', $this->mainSession->lastUrl(), 'Lands on the correct section');
$response = $this->getAndFollowAll(SubsiteXHRController::class);
$response = $this->getAndFollowAll('SubsiteXHRController');
$this->assertNotRegExp('#^Security/login.*#', $this->mainSession->lastUrl(),
'SubsiteXHRController is reachable');
}
@ -127,32 +123,32 @@ class SubsiteAdminFunctionalTest extends FunctionalTest
/**
* Test a member who only has access to one subsite (subsite1) and only some sections (pages and security).
*/
function testSubsiteAdmin()
public function testSubsiteAdmin()
{
$member = $this->objFromFixture('SilverStripe\\Security\\Member', 'subsite1member');
$member = $this->objFromFixture(Member::class, 'subsite1member');
Session::set("loggedInAs", $member->ID);
$subsite1 = $this->objFromFixture(Subsite::class, 'subsite1');
// Check allowed URL.
$this->getAndFollowAll("admin/pages/?SubsiteID={$subsite1->ID}");
$this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Can access own subsite.');
$this->assertRegExp('#^admin/pages.*#', $this->mainSession->lastUrl(), 'Can access permitted section.');
// Check allowed URL.
$this->getAndFollowAll("admin/pages/?SubsiteID={$subsite1->ID}");
$this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Can access own subsite.');
$this->assertRegExp('#^admin/pages.*#', $this->mainSession->lastUrl(), 'Can access permitted section.');
// Check forbidden section in allowed subsite.
$this->getAndFollowAll("admin/assets/?SubsiteID={$subsite1->ID}");
$this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Is redirected within subsite.');
$this->assertNotRegExp('#^admin/assets/.*#', $this->mainSession->lastUrl(),
'Is redirected away from forbidden section');
// Check forbidden section in allowed subsite.
$this->getAndFollowAll("admin/assets/?SubsiteID={$subsite1->ID}");
$this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Is redirected within subsite.');
$this->assertNotRegExp('#^admin/assets/.*#', $this->mainSession->lastUrl(),
'Is redirected away from forbidden section');
// Check forbidden site, on a section that's allowed on another subsite
$this->getAndFollowAll("admin/pages/?SubsiteID=0");
$this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Is redirected to permitted subsite.');
// Check forbidden site, on a section that's allowed on another subsite
$this->getAndFollowAll("admin/pages/?SubsiteID=0");
$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
$this->getAndFollowAll("admin/assets/?SubsiteID=0");
$this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Is redirected to first permitted subsite.');
$this->assertNotRegExp('#^Security/login.*#', $this->mainSession->lastUrl(), 'Is not denied access');
// Check forbidden site, on a section that's not allowed on any other subsite
$this->getAndFollowAll("admin/assets/?SubsiteID=0");
$this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Is redirected to first permitted subsite.');
$this->assertNotRegExp('#^Security/login.*#', $this->mainSession->lastUrl(), 'Is not denied access');
// Check the standalone XHR controller.
$response = $this->getAndFollowAll(SubsiteXHRController::class);

View File

@ -1,26 +1,26 @@
<?php
use SilverStripe\CMS\Controllers\CMSMain;
use SilverStripe\Control\Director;
use SilverStripe\Security\Member;
use SilverStripe\Control\Session;
use SilverStripe\Control\Director;
use SilverStripe\CMS\Controllers\CMSMain;
use SilverStripe\Subsites\Model\Subsite;
class SubsiteAdminTest extends BaseSubsiteTest
{
static $fixture_file = 'subsites/tests/SubsiteTest.yml';
public static $fixture_file = 'subsites/tests/SubsiteTest.yml';
function adminLoggedInSession()
public function adminLoggedInSession()
{
return new Session([
'loggedInAs' => $this->idFromFixture('SilverStripe\\Security\\Member', 'admin')
]);
return new Session(array(
'loggedInAs' => $this->idFromFixture(Member::class, 'admin')
));
}
/**
* Test generation of the view
*/
function testBasicView()
public function testBasicView()
{
Subsite::$write_hostmap = false;
$subsite1ID = $this->objFromFixture(Subsite::class, 'domaintest1')->ID;
@ -41,9 +41,9 @@ class SubsiteAdminTest extends BaseSubsiteTest
* 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.
*/
function testMainsiteAdminCanAccessAllSubsites()
public function testMainsiteAdminCanAccessAllSubsites()
{
$member = $this->objFromFixture('SilverStripe\\Security\\Member', 'admin');
$member = $this->objFromFixture(Member::class, 'admin');
Session::set("loggedInAs", $member->ID);
$cmsMain = new CMSMain();
@ -51,13 +51,11 @@ class SubsiteAdminTest extends BaseSubsiteTest
$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");
}
$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

@ -1,35 +1,53 @@
<?php
use SilverStripe\Core\Config\Config;
use SilverStripe\Control\Director;
use SilverStripe\ORM\DataObject;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Security\Member;
use SilverStripe\Subsites\Model\Subsite;
use SilverStripe\Subsites\Model\SubsiteDomain;
class SubsiteTest extends BaseSubsiteTest
{
public static $fixture_file = 'subsites/tests/SubsiteTest.yml';
static $fixture_file = 'subsites/tests/SubsiteTest.yml';
/**
* Original value of {@see SubSite::$strict_subdomain_matching}
*
* @var bool
*/
protected $origStrictSubdomainMatching = null;
function setUp()
/**
* Original value of $_REQUEST
*
* @var array
*/
protected $origServer = array();
public function setUp()
{
parent::setUp();
Config::inst()->update(Director::class, 'alternate_base_url', '/');
$this->origStrictSubdomainMatching = Subsite::$strict_subdomain_matching;
$this->origServer = $_SERVER;
Subsite::$strict_subdomain_matching = false;
}
function tearDown()
public function tearDown()
{
parent::tearDown();
$_SERVER = $this->origServer;
Subsite::$strict_subdomain_matching = $this->origStrictSubdomainMatching;
parent::tearDown();
}
/**
* Create a new subsite from the template and verify that all the template's pages are copied
*/
function testSubsiteCreation()
public function testSubsiteCreation()
{
Subsite::$write_hostmap = false;
@ -38,11 +56,12 @@ class SubsiteTest extends BaseSubsiteTest
// Test that changeSubsite is working
Subsite::changeSubsite($template->ID);
$this->assertEquals($template->ID, Subsite::currentSubsiteID());
$tmplStaff = $this->objFromFixture('Page', 'staff');
$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
$pages = DataObject::get("SilverStripe\\CMS\\Model\\SiteTree");
$pages = DataObject::get(SiteTree::class);
$totalPages = $pages->Count();
foreach ($pages as $page) {
$this->assertEquals($template->ID, $page->SubsiteID);
@ -70,7 +89,7 @@ class SubsiteTest extends BaseSubsiteTest
/**
* Confirm that domain lookup is working
*/
function testDomainLookup()
public function testDomainLookup()
{
// Clear existing fixtures
foreach (DataObject::get(Subsite::class) as $subsite) {
@ -81,20 +100,20 @@ class SubsiteTest extends BaseSubsiteTest
}
// Much more expressive than YML in this case
$subsite1 = $this->createSubsiteWithDomains([
$subsite1 = $this->createSubsiteWithDomains(array(
'one.example.org' => true,
'one.*' => false,
]);
$subsite2 = $this->createSubsiteWithDomains([
));
$subsite2 = $this->createSubsiteWithDomains(array(
'two.mysite.com' => true,
'*.mysite.com' => false,
'subdomain.onmultiplesubsites.com' => false,
]);
$subsite3 = $this->createSubsiteWithDomains([
));
$subsite3 = $this->createSubsiteWithDomains(array(
'three.*' => true, // wildcards in primary domain are not recommended
'subdomain.unique.com' => false,
'*.onmultiplesubsites.com' => false,
]);
));
$this->assertEquals(
$subsite3->ID,
@ -142,10 +161,9 @@ class SubsiteTest extends BaseSubsiteTest
Subsite::getSubsiteIDForDomain('unknown.madeup.com'),
"Doesn't match unknown subsite"
);
}
function testStrictSubdomainMatching()
public function testStrictSubdomainMatching()
{
// Clear existing fixtures
foreach (DataObject::get(Subsite::class) as $subsite) {
@ -156,15 +174,15 @@ class SubsiteTest extends BaseSubsiteTest
}
// Much more expressive than YML in this case
$subsite1 = $this->createSubsiteWithDomains([
$subsite1 = $this->createSubsiteWithDomains(array(
'example.org' => true,
'example.com' => false,
'*.wildcard.com' => false,
]);
$subsite2 = $this->createSubsiteWithDomains([
));
$subsite2 = $this->createSubsiteWithDomains(array(
'www.example.org' => true,
'www.wildcard.com' => false,
]);
));
Subsite::$strict_subdomain_matching = false;
@ -216,21 +234,20 @@ class SubsiteTest extends BaseSubsiteTest
$failed,
'Fails on multiple matches with strict checking and wildcard vs. www'
);
}
protected function createSubsiteWithDomains($domains)
{
$subsite = new Subsite([
$subsite = new Subsite(array(
'Title' => 'My Subsite'
]);
));
$subsite->write();
foreach ($domains as $domainStr => $isPrimary) {
$domain = new SubsiteDomain([
$domain = new SubsiteDomain(array(
'Domain' => $domainStr,
'IsPrimary' => $isPrimary,
'SubsiteID' => $subsite->ID
]);
));
$domain->write();
}
@ -240,7 +257,7 @@ class SubsiteTest extends BaseSubsiteTest
/**
* Test the Subsite->domain() method
*/
function testDefaultDomain()
public function testDefaultDomain()
{
$this->assertEquals('one.example.org',
$this->objFromFixture(Subsite::class, 'domaintest1')->domain());
@ -248,8 +265,6 @@ class SubsiteTest extends BaseSubsiteTest
$this->assertEquals('two.mysite.com',
$this->objFromFixture(Subsite::class, 'domaintest2')->domain());
$originalHTTPHost = $_SERVER['HTTP_HOST'];
$_SERVER['HTTP_HOST'] = "www.example.org";
$this->assertEquals('three.example.org',
$this->objFromFixture(Subsite::class, 'domaintest3')->domain());
@ -259,109 +274,157 @@ class SubsiteTest extends BaseSubsiteTest
$this->objFromFixture(Subsite::class, 'domaintest3')->domain());
$this->assertEquals($_SERVER['HTTP_HOST'], singleton(Subsite::class)->PrimaryDomain);
$this->assertEquals('http://' . $_SERVER['HTTP_HOST'] . Director::baseURL(),
singleton(Subsite::class)->absoluteBaseURL());
$_SERVER['HTTP_HOST'] = $originalHTTPHost;
$this->assertEquals('http://'.$_SERVER['HTTP_HOST'].Director::baseURL(), singleton(Subsite::class)->absoluteBaseURL());
}
function testAllSites()
/**
* Tests that Subsite and SubsiteDomain both respect http protocol correctly
*/
public function testDomainProtocol() {
// domaintest2 has 'protocol'
$subsite2 = $this->objFromFixture(Subsite::class, 'domaintest2');
$domain2a = $this->objFromFixture(SubsiteDomain::class, 'dt2a');
$domain2b = $this->objFromFixture(SubsiteDomain::class, 'dt2b');
// domaintest4 is 'https' (primary only)
$subsite4 = $this->objFromFixture(Subsite::class, 'domaintest4');
$domain4a = $this->objFromFixture(SubsiteDomain::class, 'dt4a');
$domain4b = $this->objFromFixture(SubsiteDomain::class, 'dt4b'); // secondary domain is http only though
// domaintest5 is 'http'
$subsite5 = $this->objFromFixture(Subsite::class, 'domaintest5');
$domain5a = $this->objFromFixture(SubsiteDomain::class, 'dt5');
// Check protocol when current protocol is http://
$_SERVER['HTTP_HOST'] = 'www.mysite.com';
$_SERVER['HTTPS'] = '';
$this->assertEquals('http://two.mysite.com/', $subsite2->absoluteBaseURL());
$this->assertEquals('http://two.mysite.com/', $domain2a->absoluteBaseURL());
$this->assertEquals('http://subsite.mysite.com/', $domain2b->absoluteBaseURL());
$this->assertEquals('https://www.primary.com/', $subsite4->absoluteBaseURL());
$this->assertEquals('https://www.primary.com/', $domain4a->absoluteBaseURL());
$this->assertEquals('http://www.secondary.com/', $domain4b->absoluteBaseURL());
$this->assertEquals('http://www.tertiary.com/', $subsite5->absoluteBaseURL());
$this->assertEquals('http://www.tertiary.com/', $domain5a->absoluteBaseURL());
// Check protocol when current protocol is https://
$_SERVER['HTTP_HOST'] = 'www.mysite.com';
$_SERVER['HTTPS'] = 'ON';
$this->assertEquals('https://two.mysite.com/', $subsite2->absoluteBaseURL());
$this->assertEquals('https://two.mysite.com/', $domain2a->absoluteBaseURL());
$this->assertEquals('https://subsite.mysite.com/', $domain2b->absoluteBaseURL());
$this->assertEquals('https://www.primary.com/', $subsite4->absoluteBaseURL());
$this->assertEquals('https://www.primary.com/', $domain4a->absoluteBaseURL());
$this->assertEquals('http://www.secondary.com/', $domain4b->absoluteBaseURL());
$this->assertEquals('http://www.tertiary.com/', $subsite5->absoluteBaseURL());
$this->assertEquals('http://www.tertiary.com/', $domain5a->absoluteBaseURL());
}
public function testAllSites()
{
$subsites = Subsite::all_sites();
$this->assertDOSEquals([
['Title' => 'Main site'],
['Title' => 'Template'],
['Title' => 'Subsite1 Template'],
['Title' => 'Subsite2 Template'],
['Title' => 'Test 1'],
['Title' => 'Test 2'],
['Title' => 'Test 3']
], $subsites, 'Lists all subsites');
$this->assertDOSEquals(array(
array('Title' =>'Main site'),
array('Title' =>'Template'),
array('Title' =>'Subsite1 Template'),
array('Title' =>'Subsite2 Template'),
array('Title' =>'Test 1'),
array('Title' =>'Test 2'),
array('Title' =>'Test 3'),
array('Title' => 'Test Non-SSL'),
array('Title' => 'Test SSL')
), $subsites, 'Lists all subsites');
}
function testAllAccessibleSites()
public function testAllAccessibleSites()
{
$member = $this->objFromFixture('SilverStripe\\Security\\Member', 'subsite1member');
$member = $this->objFromFixture(Member::class, 'subsite1member');
$subsites = Subsite::all_accessible_sites(true, 'Main site', $member);
$this->assertDOSEquals([
['Title' => 'Subsite1 Template']
], $subsites, 'Lists member-accessible sites.');
$this->assertDOSEquals(array(
array('Title' =>'Subsite1 Template')
), $subsites, 'Lists member-accessible sites.');
}
/**
* Test Subsite::accessible_sites()
*/
function testAccessibleSites()
public function testAccessibleSites()
{
$member1Sites = Subsite::accessible_sites("CMS_ACCESS_CMSMain", false, null,
$this->objFromFixture('SilverStripe\\Security\\Member', 'subsite1member'));
$this->objFromFixture(Member::class, 'subsite1member'));
$member1SiteTitles = $member1Sites->column("Title");
sort($member1SiteTitles);
$this->assertEquals('Subsite1 Template', $member1SiteTitles[0], 'Member can get to a subsite via a group');
$adminSites = Subsite::accessible_sites("CMS_ACCESS_CMSMain", false, null,
$this->objFromFixture('SilverStripe\\Security\\Member', 'admin'));
$this->objFromFixture(Member::class, 'admin'));
$adminSiteTitles = $adminSites->column("Title");
sort($adminSiteTitles);
$this->assertEquals([
$this->assertEquals(array(
'Subsite1 Template',
'Subsite2 Template',
'Template',
'Test 1',
'Test 2',
'Test 3',
], $adminSiteTitles);
'Test Non-SSL',
'Test SSL'
), array_values($adminSiteTitles));
$member2Sites = Subsite::accessible_sites("CMS_ACCESS_CMSMain", false, null,
$this->objFromFixture('SilverStripe\\Security\\Member', 'subsite1member2'));
$member2Sites = Subsite::accessible_sites(
"CMS_ACCESS_CMSMain", false, null,
$this->objFromFixture(Member::class, 'subsite1member2')
);
$member2SiteTitles = $member2Sites->column("Title");
sort($member2SiteTitles);
$this->assertEquals('Subsite1 Template', $member2SiteTitles[0], 'Member can get to subsite via a group role');
}
function testhasMainSitePermission()
public function testhasMainSitePermission()
{
$admin = $this->objFromFixture('SilverStripe\\Security\\Member', 'admin');
$subsite1member = $this->objFromFixture('SilverStripe\\Security\\Member', 'subsite1member');
$subsite1admin = $this->objFromFixture('SilverStripe\\Security\\Member', 'subsite1admin');
$allsubsitesauthor = $this->objFromFixture('SilverStripe\\Security\\Member', 'allsubsitesauthor');
$admin = $this->objFromFixture(Member::class, 'admin');
$subsite1member = $this->objFromFixture(Member::class, 'subsite1member');
$subsite1admin = $this->objFromFixture(Member::class, 'subsite1admin');
$allsubsitesauthor = $this->objFromFixture(Member::class, 'allsubsitesauthor');
$this->assertTrue(
Subsite::hasMainSitePermission($admin),
'Default permissions granted for super-admin'
);
$this->assertTrue(
Subsite::hasMainSitePermission($admin, ["ADMIN"]),
Subsite::hasMainSitePermission($admin, array("ADMIN")),
'ADMIN permissions granted for super-admin'
);
$this->assertFalse(
Subsite::hasMainSitePermission($subsite1admin, ["ADMIN"]),
Subsite::hasMainSitePermission($subsite1admin, array("ADMIN")),
'ADMIN permissions (on main site) denied for subsite1 admin'
);
$this->assertFalse(
Subsite::hasMainSitePermission($subsite1admin, ["CMS_ACCESS_CMSMain"]),
Subsite::hasMainSitePermission($subsite1admin, array("CMS_ACCESS_CMSMain")),
'CMS_ACCESS_CMSMain (on main site) denied for subsite1 admin'
);
$this->assertFalse(
Subsite::hasMainSitePermission($allsubsitesauthor, ["ADMIN"]),
Subsite::hasMainSitePermission($allsubsitesauthor, array("ADMIN")),
'ADMIN permissions (on main site) denied for CMS author with edit rights on all subsites'
);
$this->assertTrue(
Subsite::hasMainSitePermission($allsubsitesauthor, ["CMS_ACCESS_CMSMain"]),
Subsite::hasMainSitePermission($allsubsitesauthor, array("CMS_ACCESS_CMSMain")),
'CMS_ACCESS_CMSMain (on main site) granted for CMS author with edit rights on all subsites'
);
$this->assertFalse(
Subsite::hasMainSitePermission($subsite1member, ["ADMIN"]),
Subsite::hasMainSitePermission($subsite1member, array("ADMIN")),
'ADMIN (on main site) denied for subsite1 subsite1 cms author'
);
$this->assertFalse(
Subsite::hasMainSitePermission($subsite1member, ["CMS_ACCESS_CMSMain"]),
Subsite::hasMainSitePermission($subsite1member, array("CMS_ACCESS_CMSMain")),
'CMS_ACCESS_CMSMain (on main site) denied for subsite1 cms author'
);
}
function testDuplicateSubsite()
public function testDuplicateSubsite()
{
// get subsite1 & create page
$subsite1 = $this->objFromFixture(Subsite::class, 'domaintest1');

View File

@ -1,79 +1,103 @@
SilverStripe\Subsites\Model\Subsite:
main:
Title: 'Template'
Title: Template
subsite1:
Title: 'Subsite1 Template'
Title: Subsite1 Template
subsite2:
Title: 'Subsite2 Template'
Title: Subsite2 Template
domaintest1:
Title: 'Test 1'
Title: Test 1
domaintest2:
Title: 'Test 2'
Title: Test 2
domaintest3:
Title: 'Test 3'
Title: Test 3
domaintest4:
Title: 'Test SSL'
domaintest5:
Title: 'Test Non-SSL'
SilverStripe\Subsites\Model\SubsiteDomain:
subsite1:
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsite1
Domain: 'subsite1.*'
Domain: subsite1.*
Protocol: automatic
subsite2:
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsite2
Domain: 'subsite2.*'
Domain: subsite2.*
Protocol: automatic
dt1a:
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.domaintest1
Domain: 'one.example.org'
Domain: one.example.org
Protocol: automatic
IsPrimary: 1
dt1b:
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.domaintest1
Domain: 'one.*'
Domain: one.*
Protocol: automatic
dt2a:
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.domaintest2
Domain: 'two.mysite.com'
Domain: two.mysite.com
Protocol: automatic
IsPrimary: 1
dt2b:
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.domaintest2
Domain: '*.mysite.com'
Domain: *.mysite.com
Protocol: automatic
dt3:
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.domaintest3
Domain: 'three.*'
Domain: three.*
Protocol: automatic
IsPrimary: 1
dt4a:
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.domaintest4
Domain: www.primary.com
Protocol: https
dt4b:
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.domaintest4
Domain: www.secondary.com
Protocol: http
dt5:
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.domaintest5
Domain: www.tertiary.com
Protocol: http
IsPrimary: 1
Page:
mainSubsitePage:
Title: 'MainSubsitePage'
SubsiteID: 0
home:
Title: 'Home'
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.main
about:
Title: 'About'
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.main
linky:
Title: 'Linky'
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.main
staff:
Title: 'Staff'
ParentID: =>Page.about
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.main
contact:
Title: 'Contact Us'
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.main
importantpage:
Title: 'Important Page'
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.main
subsite1_home:
Title: 'Home (Subsite 1)'
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsite1
subsite1_contactus:
Title: 'Contact Us (Subsite 1)'
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsite1
subsite1_staff:
Title: 'Staff'
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsite1
subsite2_home:
Title: 'Home (Subsite 2)'
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsite2
subsite2_contactus:
Title: 'Contact Us (Subsite 2)'
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsite2
mainSubsitePage:
Title: 'MainSubsitePage'
SubsiteID: 0
URLSegment: mainsubsitepagehome:
Title: 'Home'
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.main
URLSegment: homeabout:
Title: 'About'
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.main
URLSegment: aboutlinky:
Title: 'Linky'
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.main
URLSegment: linkystaff:
Title: 'Staff'
ParentID: =>Page.about
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.main
URLSegment: staffcontact:
Title: 'Contact Us'
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.main
URLSegment: contact-usimportantpage:
Title: 'Important Page'
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.main
URLSegment: important-pagesubsite1_home:
Title: 'Home (Subsite 1)'
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsite1
URLSegment: homesubsite1_contactus:
Title: 'Contact Us (Subsite 1)'
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsite1
URLSegment: contact-ussubsite1_staff:
Title: 'Staff'
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsite1
URLSegment: staffsubsite2_home:
Title: 'Home (Subsite 2)'
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsite2
URLSegment: homesubsite2_contactus:
Title: 'Contact Us (Subsite 2)'
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsite2URLSegment: contact-us
SilverStripe\Security\PermissionRoleCode:
roleCode1:

View File

@ -0,0 +1,47 @@
<?php
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, array(
'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, array(
'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, array(
'X-Pjax' => 'SubsiteList',
'X-Requested-With' => 'XMLHttpRequest'
));
$this->assertEquals(200, $result->getStatusCode());
$this->assertEquals('text/json', $result->getHeader('Content-Type'));
$body = $result->getBody();
$this->assertContains('Main site', $body);
$this->assertContains('Test 1', $body);
$this->assertContains('Test 2', $body);
$this->assertContains('Test 3', $body);
}
}

View File

@ -2,19 +2,25 @@
use SilverStripe\Assets\Filesystem;
use SilverStripe\Assets\Tests\Storage\AssetStoreTest\TestAssetStore;
use SilverStripe\Core\Config\Config;
use SilverStripe\Control\Director;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Assets\File;
use SilverStripe\ORM\DB;
use SilverStripe\Core\Config\Config;
use SilverStripe\Versioned\Versioned;
use SilverStripe\Subsites\Model\Subsite;
use SilverStripe\Subsites\Pages\SubsitesVirtualPage;
use SilverStripe\Versioned\Versioned;
class SubsitesVirtualPageTest extends BaseSubsiteTest
{
static $fixture_file = [
public static $fixture_file = array(
'subsites/tests/SubsiteTest.yml',
'subsites/tests/SubsitesVirtualPageTest.yml',
];
);
protected $illegalExtensions = array(
'SiteTree' => array('Translatable')
);
public function setUp()
{
@ -24,8 +30,8 @@ class SubsitesVirtualPageTest extends BaseSubsiteTest
TestAssetStore::activate('SubsitesVirtualPageTest');
// Create a test files for each of the fixture references
$file = $this->objFromFixture('SilverStripe\\Assets\\File', 'file1');
$page = $this->objFromFixture('SilverStripe\\CMS\\Model\\SiteTree', 'page1');
$file = $this->objFromFixture(File::class, 'file1');
$page = $this->objFromFixture(SiteTree::class, 'page1');
$fromPath = __DIR__ . '/testscript-test-file.pdf';
$destPath = TestAssetStore::getLocalPath($file);
Filesystem::makeFolder(dirname($destPath));
@ -65,13 +71,14 @@ class SubsitesVirtualPageTest extends BaseSubsiteTest
$this->assertEquals($svp->Title, $linky->Title);
}
function testFileLinkRewritingOnVirtualPages()
public function testFileLinkRewritingOnVirtualPages()
{
// File setup
$this->logInWithPermission('ADMIN');
touch(Director::baseFolder() . '/assets/testscript-test-file.pdf');
// Publish the source page
$page = $this->objFromFixture('SilverStripe\\CMS\\Model\\SiteTree', 'page1');
$page = $this->objFromFixture(SiteTree::class, 'page1');
$this->assertTrue($page->doPublish());
// Create a virtual page from it, and publish that
@ -81,7 +88,7 @@ class SubsitesVirtualPageTest extends BaseSubsiteTest
$svp->doPublish();
// Rename the file
$file = $this->objFromFixture('SilverStripe\\Assets\\File', 'file1');
$file = $this->objFromFixture(File::class, 'file1');
$file->Name = 'renamed-test-file.pdf';
$file->write();
@ -92,7 +99,7 @@ class SubsitesVirtualPageTest extends BaseSubsiteTest
DB::query("SELECT \"Content\" FROM \"SiteTree_Live\" WHERE \"ID\" = $svp->ID")->value());
}
function testSubsiteVirtualPagesArentInappropriatelyPublished()
public function testSubsiteVirtualPagesArentInappropriatelyPublished()
{
// Fixture
$p = new Page();
@ -151,7 +158,7 @@ class SubsitesVirtualPageTest extends BaseSubsiteTest
* is in a different subsite to the page you are editing and republishing,
* otherwise the test will pass falsely due to current subsite ID being the same.
*/
function testPublishedSubsiteVirtualPagesUpdateIfTargetPageUpdates()
public function testPublishedSubsiteVirtualPagesUpdateIfTargetPageUpdates()
{
// create page
$p = new Page();
@ -193,9 +200,9 @@ class SubsitesVirtualPageTest extends BaseSubsiteTest
$this->assertEquals($svpdb->Title, $p->Title);
}
function testUnpublishingParentPageUnpublishesSubsiteVirtualPages()
public function testUnpublishingParentPageUnpublishesSubsiteVirtualPages()
{
Config::modify()->set('StaticPublisher', 'disable_realtime', true);
Config::inst()->update('StaticPublisher', 'disable_realtime', true);
// Go to main site, get parent page
$subsite = $this->objFromFixture(Subsite::class, 'main');
@ -224,14 +231,12 @@ class SubsitesVirtualPageTest extends BaseSubsiteTest
$page->doUnpublish();
Subsite::changeSubsite($vp1->SubsiteID);
$onLive = Versioned::get_one_by_stage(SubsitesVirtualPage::class, 'Live',
"\"SiteTree_Live\".\"ID\" = " . $vp1->ID);
$onLive = Versioned::get_one_by_stage('SubsitesVirtualPage', 'Live', "\"SiteTree_Live\".\"ID\" = ".$vp1->ID);
$this->assertNull($onLive, 'SVP has been removed from live');
$subsite = $this->objFromFixture(Subsite::class, 'subsite2');
Subsite::changeSubsite($vp2->SubsiteID);
$onLive = Versioned::get_one_by_stage(SubsitesVirtualPage::class, 'Live',
"\"SiteTree_Live\".\"ID\" = " . $vp2->ID);
$onLive = Versioned::get_one_by_stage('SubsitesVirtualPage', 'Live', "\"SiteTree_Live\".\"ID\" = ".$vp2->ID);
$this->assertNull($onLive, 'SVP has been removed from live');
}
@ -239,7 +244,7 @@ class SubsitesVirtualPageTest extends BaseSubsiteTest
* Similar to {@link SiteTreeSubsitesTest->testTwoPagesWithSameURLOnDifferentSubsites()}
* and {@link SiteTreeSubsitesTest->testPagesInDifferentSubsitesCanShareURLSegment()}.
*/
function testSubsiteVirtualPageCanHaveSameUrlsegmentAsOtherSubsite()
public function testSubsiteVirtualPageCanHaveSameUrlsegmentAsOtherSubsite()
{
Subsite::$write_hostmap = false;
$subsite1 = $this->objFromFixture(Subsite::class, 'subsite1');
@ -274,16 +279,25 @@ class SubsitesVirtualPageTest extends BaseSubsiteTest
$subsite2Vp->URLSegment,
$subsite1Page->URLSegment,
"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
Subsite::changeSubsite($subsite1->ID);
$subsite1Page->write();
$subsite2Vp->write();
$this->assertEquals(
$subsite2Vp->URLSegment,
$subsite1Page->URLSegment,
"SubsiteVirtualPage doesn't change urls when being written in another subsite"
);
}
function fixVersionNumberCache($page)
public function fixVersionNumberCache($page)
{
$pages = func_get_args();
foreach ($pages as $p) {
Versioned::prepopulate_versionnumber_cache('SilverStripe\\CMS\\Model\\SiteTree', 'Stage', [$p->ID]);
Versioned::prepopulate_versionnumber_cache('SilverStripe\\CMS\\Model\\SiteTree', 'Live', [$p->ID]);
Versioned::prepopulate_versionnumber_cache(SiteTree::class, 'Stage', array($p->ID));
Versioned::prepopulate_versionnumber_cache(SiteTree::class, 'Live', array($p->ID));
}
}
}

View File

@ -0,0 +1,74 @@
<?php
use SilverStripe\Dev\SapphireTest;
/**
* Tests {@see WildcardDomainField}
*/
class WildcardDomainFieldTest extends SapphireTest {
/**
* Check that valid domains are accepted
*
* @dataProvider validDomains
*/
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
*/
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
*/
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 array(
array('www.mysite.com'),
array('domain7'),
array('mysite.co.n-z'),
array('subdomain.my-site.com'),
array('subdomain.mysite')
);
}
public function invalidDomains() {
return array(
array('-mysite'),
array('.mysite'),
array('mys..ite'),
array('mysite-'),
array('mysite.'),
array('-mysite.*'),
array('.mysite.*'),
array('mys..ite.*'),
array('*.mysite-'),
array('*.mysite.')
);
}
public function validWildcards() {
return array(
array('*.mysite.com'),
array('mys*ite.com'),
array('*.my-site.*'),
array('*')
);
}
}

View File

@ -6,15 +6,20 @@ if (!class_exists('SilverStripe\BehatExtension\Context\SilverStripeContext')) {
return;
}
use SilverStripe\BehatExtension\Context\BasicContext;
use SilverStripe\BehatExtension\Context\FixtureContext;
use SilverStripe\BehatExtension\Context\LoginContext;
use SilverStripe\BehatExtension\Context\SilverStripeContext;
use SilverStripe\BehatExtension\Context\BasicContext;
use SilverStripe\BehatExtension\Context\LoginContext;
use SilverStripe\BehatExtension\Context\FixtureContext;
use SilverStripe\Framework\Test\Behaviour\CmsFormsContext;
use SilverStripe\Framework\Test\Behaviour\CmsUiContext;l
use SilverStripe\Cms\Test\Behaviour;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Core\ClassInfo;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Framework\Test\Behaviour\CmsFormsContext;
use SilverStripe\Framework\Test\Behaviour\CmsUiContext;
use SilverStripe\Dev\BehatFixtureFactory;
use SilverStripe\Dev\FixtureBlueprint;
use SilverStripe\Dev\FixtureFactory;
use SilverStripe\Security\Member;
// PHPUnit
require_once 'PHPUnit/Autoload.php';
@ -27,8 +32,7 @@ require_once 'PHPUnit/Framework/Assert/Functions.php';
* Uses subcontexts to extend functionality.
*/
class FeatureContext extends SilverStripeContext
{
{
/**
* @var FixtureFactory
*/
@ -55,17 +59,17 @@ class FeatureContext extends SilverStripeContext
// Use blueprints to set user name from identifier
$factory = $fixtureContext->getFixtureFactory();
$blueprint = Injector::inst()->create('SilverStripe\\Dev\\FixtureBlueprint', 'SilverStripe\\Security\\Member');
$blueprint = Injector::inst()->create(FixtureBlueprint::class, Member::class);
$blueprint->addCallback('beforeCreate', function ($identifier, &$data, &$fixtures) {
if (!isset($data['FirstName'])) {
$data['FirstName'] = $identifier;
}
});
$factory->define('SilverStripe\\Security\\Member', $blueprint);
$factory->define(Member::class, $blueprint);
// Auto-publish pages
foreach (ClassInfo::subclassesFor('SilverStripe\\CMS\\Model\\SiteTree') as $id => $class) {
$blueprint = Injector::inst()->create('SilverStripe\\Dev\\FixtureBlueprint', $class);
foreach (ClassInfo::subclassesFor(SiteTree::class) as $id => $class) {
$blueprint = Injector::inst()->create(FixtureBlueprint::class, $class);
$blueprint->addCallback('afterCreate', function ($obj, $identifier, &$data, &$fixtures) {
$obj->publish('Stage', 'Live');
});
@ -88,7 +92,7 @@ class FeatureContext extends SilverStripeContext
public function getFixtureFactory()
{
if (!$this->fixtureFactory) {
$this->fixtureFactory = Injector::inst()->create('SilverStripe\\Dev\\BehatFixtureFactory');
$this->fixtureFactory = Injector::inst()->create(BehatFixtureFactory::class);
}
return $this->fixtureFactory;