Reformat for psr-2

This commit is contained in:
Damian Mooyman 2015-11-23 16:53:45 +13:00
parent 5f02e0c406
commit a0ede56c0e
30 changed files with 3602 additions and 3290 deletions

View File

@ -25,4 +25,3 @@ CMSMain::add_extension('SubsiteMenuExtension');
CMSPagesController::add_extension('SubsiteMenuExtension'); CMSPagesController::add_extension('SubsiteMenuExtension');
SubsiteAdmin::add_extension('SubsiteMenuExtension'); SubsiteAdmin::add_extension('SubsiteMenuExtension');
CMSSettingsController::add_extension('SubsiteMenuExtension'); CMSSettingsController::add_extension('SubsiteMenuExtension');

View File

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

View File

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

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

View File

@ -1,212 +1,233 @@
<?php <?php
class SubsitesVirtualPage extends VirtualPage { class SubsitesVirtualPage extends VirtualPage
{
private static $description = 'Displays the content of a page on another subsite';
private static $description = 'Displays the content of a page on another subsite'; private static $db = array(
'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)));
private static $db = array( $fields->addFieldToTab(
'CustomMetaTitle' => 'Varchar(255)', 'Root.Main',
'CustomMetaKeywords' => 'Varchar(255)', DropdownField::create(
'CustomMetaDescription' => 'Text', "CopyContentFromID_SubsiteID",
'CustomExtraMeta' => 'HTMLText' _t('SubsitesVirtualPage.SubsiteField', "Subsite"),
); $subsites->map('ID', 'Title')
)->addExtraClass('subsitestreedropdownfield-chooser no-change-track'),
public function getCMSFields() { 'CopyContentFromID'
$fields = parent::getCMSFields(); );
$subsites = DataObject::get('Subsite'); // Setup the linking to the original page.
if(!$subsites) { $pageSelectionField = new SubsitesTreeDropdownField(
$subsites = new ArrayList(); "CopyContentFromID",
}else { _t('VirtualPage.CHOOSE', "Choose a page to link to"),
$subsites=ArrayList::create($subsites->toArray()); "SiteTree",
} "ID",
"MenuTitle"
$subsites->push(new ArrayData(array('Title' => 'Main site', 'ID' => 0))); );
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;
}
$fields->addFieldToTab( public function fieldLabels($includerelations = true)
'Root.Main', {
DropdownField::create( $labels = parent::fieldLabels($includerelations);
"CopyContentFromID_SubsiteID", $labels['CustomMetaTitle'] = _t('Subsite.CustomMetaTitle', 'Title');
_t('SubsitesVirtualPage.SubsiteField',"Subsite"), $labels['CustomMetaKeywords'] = _t('Subsite.CustomMetaKeywords', 'Keywords');
$subsites->map('ID', 'Title') $labels['CustomMetaDescription'] = _t('Subsite.CustomMetaDescription', 'Description');
)->addExtraClass('subsitestreedropdownfield-chooser no-change-track'), $labels['CustomExtraMeta'] = _t('Subsite.CustomExtraMeta', 'Custom Meta Tags');
'CopyContentFromID'
);
// Setup the linking to the original page.
$pageSelectionField = new SubsitesTreeDropdownField(
"CopyContentFromID",
_t('VirtualPage.CHOOSE', "Choose a page to link to"),
"SiteTree",
"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) { return $labels;
$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)]);
}
}
public function getCopyContentFromID_SubsiteID() { return $fields;
return ($this->CopyContentFromID) ? (int)$this->CopyContentFrom()->SubsiteID : (int)Session::get('SubsiteID'); }
}
public function syncLinkTracking()
public function getVirtualFields() { {
$fields = parent::getVirtualFields(); $oldState = Subsite::$disable_subsite_filter;
foreach($fields as $k => $v) { Subsite::$disable_subsite_filter = true;
if($v == 'SubsiteID') unset($fields[$k]); if ($this->CopyContentFromID) {
} $this->HasBrokenLink = DataObject::get_by_id('SiteTree', $this->CopyContentFromID) ? false : true;
}
foreach(self::$db as $field => $type) if (in_array($field, $fields)) unset($fields[array_search($field, $fields)]); Subsite::$disable_subsite_filter = $oldState;
}
return $fields; public function onBeforeWrite()
} {
parent::onBeforeWrite();
public function syncLinkTracking() {
$oldState = Subsite::$disable_subsite_filter; if ($this->CustomMetaTitle) {
Subsite::$disable_subsite_filter = true; $this->MetaTitle = $this->CustomMetaTitle;
if ($this->CopyContentFromID) $this->HasBrokenLink = DataObject::get_by_id('SiteTree', $this->CopyContentFromID) ? false : true; } else {
Subsite::$disable_subsite_filter = $oldState; $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;
}
}
public function validURLSegment()
{
$isValid = parent::validURLSegment();
// Veto the validation rules if its false. In this case, some logic
// needs to be duplicated from parent to find out the exact reason the validation failed.
if (!$isValid) {
$IDFilter = ($this->ID) ? "AND \"SiteTree\".\"ID\" <> $this->ID" : null;
$parentFilter = null;
public function onBeforeWrite() { if (Config::inst()->get('SiteTree', 'nested_urls')) {
parent::onBeforeWrite(); if ($this->ParentID) {
$parentFilter = " AND \"SiteTree\".\"ParentID\" = $this->ParentID";
if($this->CustomMetaTitle) $this->MetaTitle = $this->CustomMetaTitle; } else {
else { $parentFilter = ' AND "SiteTree"."ParentID" = 0';
$this->MetaTitle = $this->ContentSource()->MetaTitle ? $this->ContentSource()->MetaTitle : $this->MetaTitle; }
} }
if($this->CustomMetaKeywords) $this->MetaKeywords = $this->CustomMetaKeywords;
else { $origDisableSubsiteFilter = Subsite::$disable_subsite_filter;
$this->MetaKeywords = $this->ContentSource()->MetaKeywords ? $this->ContentSource()->MetaKeywords : $this->MetaKeywords; Subsite::$disable_subsite_filter = true;
} $existingPage = DataObject::get_one(
if($this->CustomMetaDescription) $this->MetaDescription = $this->CustomMetaDescription; 'SiteTree',
else { "\"URLSegment\" = '$this->URLSegment' $IDFilter $parentFilter",
$this->MetaDescription = $this->ContentSource()->MetaDescription ? $this->ContentSource()->MetaDescription : $this->MetaDescription; false // disable cache, it doesn't include subsite status in the key
} );
if($this->CustomExtraMeta) $this->ExtraMeta = $this->CustomExtraMeta; Subsite::$disable_subsite_filter = $origDisableSubsiteFilter;
else { $existingPageInSubsite = DataObject::get_one(
$this->ExtraMeta = $this->ContentSource()->ExtraMeta ? $this->ContentSource()->ExtraMeta : $this->ExtraMeta; 'SiteTree',
} "\"URLSegment\" = '$this->URLSegment' $IDFilter $parentFilter",
} false // disable cache, it doesn't include subsite status in the key
);
public function validURLSegment() {
$isValid = parent::validURLSegment();
// Veto the validation rules if its false. In this case, some logic
// needs to be duplicated from parent to find out the exact reason the validation failed.
if(!$isValid) {
$IDFilter = ($this->ID) ? "AND \"SiteTree\".\"ID\" <> $this->ID" : null;
$parentFilter = null;
if(Config::inst()->get('SiteTree', 'nested_urls')) { // If URL has been vetoed because of an existing page,
if($this->ParentID) { // be more specific and allow same URLSegments in different subsites
$parentFilter = " AND \"SiteTree\".\"ParentID\" = $this->ParentID"; $isValid = !($existingPage && $existingPageInSubsite);
} else { }
$parentFilter = ' AND "SiteTree"."ParentID" = 0';
} return $isValid;
} }
$origDisableSubsiteFilter = Subsite::$disable_subsite_filter;
Subsite::$disable_subsite_filter = true;
$existingPage = DataObject::get_one(
'SiteTree',
"\"URLSegment\" = '$this->URLSegment' $IDFilter $parentFilter",
false // disable cache, it doesn't include subsite status in the key
);
Subsite::$disable_subsite_filter = $origDisableSubsiteFilter;
$existingPageInSubsite = DataObject::get_one(
'SiteTree',
"\"URLSegment\" = '$this->URLSegment' $IDFilter $parentFilter",
false // disable cache, it doesn't include subsite status in the key
);
// If URL has been vetoed because of an existing page,
// be more specific and allow same URLSegments in different subsites
$isValid = !($existingPage && $existingPageInSubsite);
}
return $isValid;
}
} }
class SubsitesVirtualPage_Controller extends VirtualPage_Controller { class SubsitesVirtualPage_Controller extends VirtualPage_Controller
{
public function reloadContent() { public function reloadContent()
$this->failover->copyFrom($this->failover->CopyContentFrom()); {
$this->failover->write(); $this->failover->copyFrom($this->failover->CopyContentFrom());
return; $this->failover->write();
} return;
}
public function init(){
$origDisableSubsiteFilter = Subsite::$disable_subsite_filter; public function init()
Subsite::$disable_subsite_filter = true; {
$origDisableSubsiteFilter = Subsite::$disable_subsite_filter;
parent::init(); Subsite::$disable_subsite_filter = true;
Subsite::$disable_subsite_filter = $origDisableSubsiteFilter; parent::init();
}
Subsite::$disable_subsite_filter = $origDisableSubsiteFilter;
}
} }

View File

@ -1,8 +1,8 @@
<?php <?php
class CMSPageAddControllerExtension extends Extension { class CMSPageAddControllerExtension extends Extension
{
function updatePageOptions(&$fields) { public function updatePageOptions(&$fields)
$fields->push(new HiddenField('SubsiteID', 'SubsiteID', Subsite::currentSubsiteID())); {
} $fields->push(new HiddenField('SubsiteID', 'SubsiteID', Subsite::currentSubsiteID()));
}
} }

View File

@ -2,19 +2,21 @@
/** /**
* @package subsites * @package subsites
*/ */
class ControllerSubsites extends Extension { class ControllerSubsites extends Extension
function controllerAugmentInit(){ {
if($subsite = Subsite::currentSubsite()){ public function controllerAugmentInit()
if($theme = $subsite->Theme) {
SSViewer::set_theme($theme); if ($subsite = Subsite::currentSubsite()) {
} if ($theme = $subsite->Theme) {
} SSViewer::set_theme($theme);
}
function CurrentSubsite(){ }
if($subsite = Subsite::currentSubsite()){ }
return $subsite;
} public function CurrentSubsite()
} {
if ($subsite = Subsite::currentSubsite()) {
return $subsite;
}
}
} }
?>

View File

@ -1,40 +1,43 @@
<?php <?php
class ErrorPageSubsite extends DataExtension { class ErrorPageSubsite extends DataExtension
{
/** /**
* Alter file path to generated a static (static) error page file to handle error page template on different sub-sites * Alter file path to generated a static (static) error page file to handle error page template on different sub-sites
* *
* @see Error::get_filepath_for_errorcode() * @see Error::get_filepath_for_errorcode()
* *
* FIXME since {@link Subsite::currentSubsite()} partly relies on Session, viewing other sub-site (including main site) between * FIXME since {@link Subsite::currentSubsite()} partly relies on Session, viewing other sub-site (including main site) between
* opening ErrorPage in the CMS and publish ErrorPage causes static error page to get generated incorrectly. * opening ErrorPage in the CMS and publish ErrorPage causes static error page to get generated incorrectly.
*/ */
function alternateFilepathForErrorcode($statusCode, $locale = null) { public function alternateFilepathForErrorcode($statusCode, $locale = null)
$static_filepath = Config::inst()->get($this->owner->ClassName, 'static_filepath'); {
$subdomainPart = ""; $static_filepath = Config::inst()->get($this->owner->ClassName, 'static_filepath');
$subdomainPart = "";
// Try to get current subsite from session
$subsite = Subsite::currentSubsite(false); // Try to get current subsite from session
$subsite = Subsite::currentSubsite(false);
// since this function is called from Page class before the controller is created, we have to get subsite from domain instead
if(!$subsite) { // since this function is called from Page class before the controller is created, we have to get subsite from domain instead
$subsiteID = Subsite::getSubsiteIDForDomain(); if (!$subsite) {
if($subsiteID != 0) $subsite = DataObject::get_by_id("Subsite", $subsiteID); $subsiteID = Subsite::getSubsiteIDForDomain();
else $subsite = null; if ($subsiteID != 0) {
} $subsite = DataObject::get_by_id("Subsite", $subsiteID);
} else {
if($subsite) { $subsite = null;
$subdomain = $subsite->domain(); }
$subdomainPart = "-{$subdomain}"; }
}
if ($subsite) {
if(singleton('SiteTree')->hasExtension('Translatable') && $locale && $locale != Translatable::default_locale()) { $subdomain = $subsite->domain();
$filepath = $static_filepath . "/error-{$statusCode}-{$locale}{$subdomainPart}.html"; $subdomainPart = "-{$subdomain}";
} else { }
$filepath = $static_filepath . "/error-{$statusCode}{$subdomainPart}.html";
} if (singleton('SiteTree')->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; return $filepath;
} }
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -10,10 +10,10 @@
* Or you can include the subsiteCMSShowInMenu function in your admin class and have it return true * Or you can include the subsiteCMSShowInMenu function in your admin class and have it return true
*/ */
class SubsiteMenuExtension extends Extension{ class SubsiteMenuExtension extends Extension
{
public function subsiteCMSShowInMenu(){ public function subsiteCMSShowInMenu()
return true; {
} return true;
}
} }

View File

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

View File

@ -5,40 +5,44 @@
* *
* @package subsites * @package subsites
*/ */
class SubsitesTreeDropdownField extends TreeDropdownField { class SubsitesTreeDropdownField extends TreeDropdownField
{
private static $allowed_actions = array( private static $allowed_actions = array(
'tree' 'tree'
); );
protected $subsiteID = 0; protected $subsiteID = 0;
protected $extraClasses = array('SubsitesTreeDropdownField'); protected $extraClasses = array('SubsitesTreeDropdownField');
function Field($properties = array()) { public function Field($properties = array())
$html = parent::Field($properties); {
$html = parent::Field($properties);
Requirements::javascript('subsites/javascript/SubsitesTreeDropdownField.js');
Requirements::javascript('subsites/javascript/SubsitesTreeDropdownField.js');
return $html;
} return $html;
}
function setSubsiteID($id) {
$this->subsiteID = $id; public function setSubsiteID($id)
} {
$this->subsiteID = $id;
function getSubsiteID() { }
return $this->subsiteID;
} public function getSubsiteID()
{
function tree(SS_HTTPRequest $request) { return $this->subsiteID;
$oldSubsiteID = Session::get('SubsiteID'); }
Session::set('SubsiteID', $this->subsiteID);
public function tree(SS_HTTPRequest $request)
$results = parent::tree($request); {
$oldSubsiteID = Session::get('SubsiteID');
Session::set('SubsiteID', $oldSubsiteID); Session::set('SubsiteID', $this->subsiteID);
return $results; $results = parent::tree($request);
}
} Session::set('SubsiteID', $oldSubsiteID);
return $results;
}
}

View File

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

View File

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

View File

@ -6,62 +6,72 @@
* *
* Example: sake dev/tasks/SubsiteCopyPagesTask from=<subsite-source> to=<subsite-target> * Example: sake dev/tasks/SubsiteCopyPagesTask from=<subsite-source> to=<subsite-target>
*/ */
class SubsiteCopyPagesTask extends BuildTask { class SubsiteCopyPagesTask extends BuildTask
{
protected $title = 'Copy pages to different subsite';
protected $description = '';
protected $title = 'Copy pages to different subsite'; public function run($request)
{
protected $description = ''; $subsiteFromId = $request->getVar('from');
if (!is_numeric($subsiteFromId)) {
throw new InvalidArgumentException('Missing "from" parameter');
}
$subsiteFrom = DataObject::get_by_id('Subsite', $subsiteFromId);
if (!$subsiteFrom) {
throw new InvalidArgumentException('Subsite not found');
}
function run($request) { $subsiteToId = $request->getVar('to');
$subsiteFromId = $request->getVar('from'); if (!is_numeric($subsiteToId)) {
if(!is_numeric($subsiteFromId)) throw new InvalidArgumentException('Missing "from" parameter'); throw new InvalidArgumentException('Missing "to" parameter');
$subsiteFrom = DataObject::get_by_id('Subsite', $subsiteFromId); }
if(!$subsiteFrom) throw new InvalidArgumentException('Subsite not found'); $subsiteTo = DataObject::get_by_id('Subsite', $subsiteToId);
if (!$subsiteTo) {
throw new InvalidArgumentException('Subsite not found');
}
$subsiteToId = $request->getVar('to'); $useVirtualPages = (bool)$request->getVar('virtual');
if(!is_numeric($subsiteToId)) throw new InvalidArgumentException('Missing "to" parameter');
$subsiteTo = DataObject::get_by_id('Subsite', $subsiteToId);
if(!$subsiteTo) throw new InvalidArgumentException('Subsite not found');
$useVirtualPages = (bool)$request->getVar('virtual'); Subsite::changeSubsite($subsiteFrom);
Subsite::changeSubsite($subsiteFrom); // Copy data from this template to the given subsite. Does this using an iterative depth-first search.
// 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 = array(array(0,0));
while (count($stack) > 0) {
list($sourceParentID, $destParentID) = array_pop($stack);
// Copy data from this template to the given subsite. Does this using an iterative depth-first search. $children = Versioned::get_by_stage('SiteTree', 'Live', "\"ParentID\" = $sourceParentID", '');
// 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 = array(array(0,0));
while(count($stack) > 0) {
list($sourceParentID, $destParentID) = array_pop($stack);
$children = Versioned::get_by_stage('SiteTree', 'Live', "\"ParentID\" = $sourceParentID", ''); if ($children) {
foreach ($children as $child) {
if ($useVirtualPages) {
$childClone = new SubsitesVirtualPage();
$childClone->writeToStage('Stage');
$childClone->CopyContentFromID = $child->ID;
$childClone->SubsiteID = $subsiteTo->ID;
} else {
$childClone = $child->duplicateToSubsite($subsiteTo->ID, true);
}
$childClone->ParentID = $destParentID;
$childClone->writeToStage('Stage');
$childClone->publish('Stage', 'Live');
array_push($stack, array($child->ID, $childClone->ID));
if($children) { $this->log(sprintf('Copied "%s" (#%d, %s)', $child->Title, $child->ID, $child->Link()));
foreach($children as $child) { }
if($useVirtualPages) { }
$childClone = new SubsitesVirtualPage();
$childClone->writeToStage('Stage');
$childClone->CopyContentFromID = $child->ID;
$childClone->SubsiteID = $subsiteTo->ID;
} else {
$childClone = $child->duplicateToSubsite($subsiteTo->ID, true);
}
$childClone->ParentID = $destParentID;
$childClone->writeToStage('Stage');
$childClone->publish('Stage', 'Live');
array_push($stack, array($child->ID, $childClone->ID));
$this->log(sprintf('Copied "%s" (#%d, %s)', $child->Title, $child->ID, $child->Link())); unset($children);
} }
} }
unset($children); public function log($msg)
} {
} echo $msg . "\n";
}
function log($msg) { }
echo $msg . "\n";
}
}

View File

@ -1,28 +1,30 @@
<?php <?php
class BaseSubsiteTest extends SapphireTest { class BaseSubsiteTest extends SapphireTest
{
public function setUp()
{
parent::setUp();
function setUp() { Subsite::$use_session_subsiteid = true;
parent::setUp(); }
Subsite::$use_session_subsiteid = true; /**
} * 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;
* 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);
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

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

View File

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

View File

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

View File

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

View File

@ -1,203 +1,213 @@
<?php <?php
class SiteTreeSubsitesTest extends BaseSubsiteTest { class SiteTreeSubsitesTest extends BaseSubsiteTest
{
public static $fixture_file = 'subsites/tests/SubsiteTest.yml';
protected $extraDataObjects = array(
'SiteTreeSubsitesTest_ClassA',
'SiteTreeSubsitesTest_ClassB'
);
static $fixture_file = 'subsites/tests/SubsiteTest.yml'; protected $illegalExtensions = array(
'SiteTree' => array('Translatable')
protected $extraDataObjects = array( );
'SiteTreeSubsitesTest_ClassA',
'SiteTreeSubsitesTest_ClassB' public function testPagesInDifferentSubsitesCanShareURLSegment()
); {
$subsiteMain = $this->objFromFixture('Subsite', 'main');
$subsite1 = $this->objFromFixture('Subsite', 'subsite1');
$pageMain = new SiteTree();
$pageMain->URLSegment = 'testpage';
$pageMain->write();
$pageMain->publish('Stage', 'Live');
$pageMainOther = new SiteTree();
$pageMainOther->URLSegment = 'testpage';
$pageMainOther->write();
$pageMainOther->publish('Stage', 'Live');
$this->assertNotEquals($pageMain->URLSegment, $pageMainOther->URLSegment,
'Pages in same subsite cant share the same URL'
);
Subsite::changeSubsite($subsite1->ID);
$pageSubsite1 = new SiteTree();
$pageSubsite1->URLSegment = 'testpage';
$pageSubsite1->write();
$pageSubsite1->publish('Stage', 'Live');
$this->assertEquals($pageMain->URLSegment, $pageSubsite1->URLSegment,
'Pages in different subsites can share the same URL'
);
}
public function testBasicSanity()
{
$this->assertTrue(singleton('SiteTree')->getSiteConfig() instanceof SiteConfig);
// The following assert is breaking in Translatable.
$this->assertTrue(singleton('SiteTree')->getCMSFields() instanceof FieldList);
$this->assertTrue(singleton('SubsitesVirtualPage')->getCMSFields() instanceof FieldList);
$this->assertTrue(is_array(singleton('SiteTreeSubsites')->extraStatics()));
}
public function testErrorPageLocations()
{
$subsite1 = $this->objFromFixture('Subsite', 'domaintest1');
Subsite::changeSubsite($subsite1->ID);
$path = ErrorPage::get_filepath_for_errorcode(500);
$static_path = Config::inst()->get('ErrorPage', 'static_filepath');
$expected_path = $static_path . '/error-500-'.$subsite1->domain().'.html';
$this->assertEquals($expected_path, $path);
}
public function testCanEditSiteTree()
{
$admin = $this->objFromFixture('Member', 'admin');
$subsite1member = $this->objFromFixture('Member', 'subsite1member');
$subsite2member = $this->objFromFixture('Member', 'subsite2member');
$mainpage = $this->objFromFixture('Page', 'home');
$subsite1page = $this->objFromFixture('Page', 'subsite1_home');
$subsite2page = $this->objFromFixture('Page', 'subsite2_home');
$subsite1 = $this->objFromFixture('Subsite', 'subsite1');
$subsite2 = $this->objFromFixture('Subsite', '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'
);
// @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", $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'
);
}
/**
* 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', 'domaintest1');
$s2 = $this->objFromFixture('Subsite', 'domaintest2');
$p1 = new SiteTree();
$p1->Title = $p1->URLSegment = "test-page";
$p1->SubsiteID = $s1->ID;
$p1->write();
protected $illegalExtensions = array( $p2 = new SiteTree();
'SiteTree' => array('Translatable') $p2->Title = $p1->URLSegment = "test-page";
); $p2->SubsiteID = $s2->ID;
$p2->write();
function testPagesInDifferentSubsitesCanShareURLSegment() {
$subsiteMain = $this->objFromFixture('Subsite', 'main');
$subsite1 = $this->objFromFixture('Subsite', 'subsite1');
$pageMain = new SiteTree();
$pageMain->URLSegment = 'testpage';
$pageMain->write();
$pageMain->publish('Stage', 'Live');
$pageMainOther = new SiteTree();
$pageMainOther->URLSegment = 'testpage';
$pageMainOther->write();
$pageMainOther->publish('Stage', 'Live');
$this->assertNotEquals($pageMain->URLSegment, $pageMainOther->URLSegment,
'Pages in same subsite cant share the same URL'
);
Subsite::changeSubsite($subsite1->ID);
$pageSubsite1 = new SiteTree();
$pageSubsite1->URLSegment = 'testpage';
$pageSubsite1->write();
$pageSubsite1->publish('Stage', 'Live');
$this->assertEquals($pageMain->URLSegment, $pageSubsite1->URLSegment,
'Pages in different subsites can share the same URL'
);
}
function testBasicSanity() {
$this->assertTrue(singleton('SiteTree')->getSiteConfig() instanceof SiteConfig);
// The following assert is breaking in Translatable.
$this->assertTrue(singleton('SiteTree')->getCMSFields() instanceof FieldList);
$this->assertTrue(singleton('SubsitesVirtualPage')->getCMSFields() instanceof FieldList);
$this->assertTrue(is_array(singleton('SiteTreeSubsites')->extraStatics()));
}
function testErrorPageLocations() {
$subsite1 = $this->objFromFixture('Subsite', 'domaintest1');
Subsite::changeSubsite($subsite1->ID);
$path = ErrorPage::get_filepath_for_errorcode(500);
$static_path = Config::inst()->get('ErrorPage', 'static_filepath');
$expected_path = $static_path . '/error-500-'.$subsite1->domain().'.html';
$this->assertEquals($expected_path, $path);
}
function testCanEditSiteTree() {
$admin = $this->objFromFixture('Member', 'admin');
$subsite1member = $this->objFromFixture('Member', 'subsite1member');
$subsite2member = $this->objFromFixture('Member', 'subsite2member');
$mainpage = $this->objFromFixture('Page', 'home');
$subsite1page = $this->objFromFixture('Page', 'subsite1_home');
$subsite2page = $this->objFromFixture('Page', 'subsite2_home');
$subsite1 = $this->objFromFixture('Subsite', 'subsite1');
$subsite2 = $this->objFromFixture('Subsite', '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'
);
// @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", $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'
);
}
/**
* Similar to {@link SubsitesVirtualPageTest->testSubsiteVirtualPageCanHaveSameUrlsegmentAsOtherSubsite()}.
*/
function testTwoPagesWithSameURLOnDifferentSubsites() {
// Set up a couple of pages with the same URL on different subsites
$s1 = $this->objFromFixture('Subsite','domaintest1');
$s2 = $this->objFromFixture('Subsite','domaintest2');
$p1 = new SiteTree();
$p1->Title = $p1->URLSegment = "test-page";
$p1->SubsiteID = $s1->ID;
$p1->write();
$p2 = new SiteTree(); // Check that the URLs weren't modified in our set-up
$p2->Title = $p1->URLSegment = "test-page"; $this->assertEquals($p1->URLSegment, 'test-page');
$p2->SubsiteID = $s2->ID; $this->assertEquals($p2->URLSegment, 'test-page');
$p2->write();
// Check that if we switch between the different subsites, we receive the correct pages
Subsite::changeSubsite($s1);
$this->assertEquals($p1->ID, SiteTree::get_by_link('test-page')->ID);
// Check that the URLs weren't modified in our set-up Subsite::changeSubsite($s2);
$this->assertEquals($p1->URLSegment, 'test-page'); $this->assertEquals($p2->ID, SiteTree::get_by_link('test-page')->ID);
$this->assertEquals($p2->URLSegment, 'test-page'); }
// Check that if we switch between the different subsites, we receive the correct pages public function testPageTypesBlacklistInClassDropdown()
Subsite::changeSubsite($s1); {
$this->assertEquals($p1->ID, SiteTree::get_by_link('test-page')->ID); $editor = $this->objFromFixture('Member', 'editor');
Session::set("loggedInAs", $editor->ID);
$s1 = $this->objFromFixture('Subsite', 'domaintest1');
$s2 = $this->objFromFixture('Subsite', 'domaintest2');
$page = singleton('SiteTree');
$s1->PageTypeBlacklist = 'SiteTreeSubsitesTest_ClassA,ErrorPage';
$s1->write();
Subsite::changeSubsite($s1);
$settingsFields = $page->getSettingsFields()->dataFieldByName('ClassName')->getSource();
$this->assertArrayNotHasKey('ErrorPage',
$settingsFields
);
$this->assertArrayNotHasKey('SiteTreeSubsitesTest_ClassA',
$settingsFields
);
$this->assertArrayHasKey('SiteTreeSubsitesTest_ClassB',
$settingsFields
);
Subsite::changeSubsite($s2); Subsite::changeSubsite($s2);
$this->assertEquals($p2->ID, SiteTree::get_by_link('test-page')->ID); $settingsFields = $page->getSettingsFields()->dataFieldByName('ClassName')->getSource();
} $this->assertArrayHasKey('ErrorPage',
$settingsFields
function testPageTypesBlacklistInClassDropdown() { );
$editor = $this->objFromFixture('Member', 'editor'); $this->assertArrayHasKey('SiteTreeSubsitesTest_ClassA',
Session::set("loggedInAs", $editor->ID); $settingsFields
);
$s1 = $this->objFromFixture('Subsite','domaintest1'); $this->assertArrayHasKey('SiteTreeSubsitesTest_ClassB',
$s2 = $this->objFromFixture('Subsite','domaintest2'); $settingsFields
$page = singleton('SiteTree'); );
}
$s1->PageTypeBlacklist = 'SiteTreeSubsitesTest_ClassA,ErrorPage';
$s1->write(); public function testPageTypesBlacklistInCMSMain()
{
Subsite::changeSubsite($s1); $editor = $this->objFromFixture('Member', 'editor');
$settingsFields = $page->getSettingsFields()->dataFieldByName('ClassName')->getSource(); Session::set("loggedInAs", $editor->ID);
$this->assertArrayNotHasKey('ErrorPage', $cmsmain = new CMSMain();
$settingsFields
); $s1 = $this->objFromFixture('Subsite', 'domaintest1');
$this->assertArrayNotHasKey('SiteTreeSubsitesTest_ClassA', $s2 = $this->objFromFixture('Subsite', 'domaintest2');
$settingsFields
); $s1->PageTypeBlacklist = 'SiteTreeSubsitesTest_ClassA,ErrorPage';
$this->assertArrayHasKey('SiteTreeSubsitesTest_ClassB', $s1->write();
$settingsFields
);
Subsite::changeSubsite($s2); Subsite::changeSubsite($s1);
$settingsFields = $page->getSettingsFields()->dataFieldByName('ClassName')->getSource(); $hints = Convert::json2array($cmsmain->SiteTreeHints());
$this->assertArrayHasKey('ErrorPage', $classes = $hints['Root']['disallowedChildren'];
$settingsFields $this->assertContains('ErrorPage', $classes);
); $this->assertContains('SiteTreeSubsitesTest_ClassA', $classes);
$this->assertArrayHasKey('SiteTreeSubsitesTest_ClassA', $this->assertNotContains('SiteTreeSubsitesTest_ClassB', $classes);
$settingsFields
);
$this->assertArrayHasKey('SiteTreeSubsitesTest_ClassB',
$settingsFields
);
}
function testPageTypesBlacklistInCMSMain() {
$editor = $this->objFromFixture('Member', 'editor');
Session::set("loggedInAs", $editor->ID);
$cmsmain = new CMSMain();
$s1 = $this->objFromFixture('Subsite','domaintest1');
$s2 = $this->objFromFixture('Subsite','domaintest2');
$s1->PageTypeBlacklist = 'SiteTreeSubsitesTest_ClassA,ErrorPage';
$s1->write();
Subsite::changeSubsite($s1); Subsite::changeSubsite($s2);
$hints = Convert::json2array($cmsmain->SiteTreeHints()); $hints = Convert::json2array($cmsmain->SiteTreeHints());
$classes = $hints['Root']['disallowedChildren']; $classes = $hints['Root']['disallowedChildren'];
$this->assertContains('ErrorPage', $classes); $this->assertNotContains('ErrorPage', $classes);
$this->assertContains('SiteTreeSubsitesTest_ClassA', $classes); $this->assertNotContains('SiteTreeSubsitesTest_ClassA', $classes);
$this->assertNotContains('SiteTreeSubsitesTest_ClassB', $classes); $this->assertNotContains('SiteTreeSubsitesTest_ClassB', $classes);
}
Subsite::changeSubsite($s2);
$hints = Convert::json2array($cmsmain->SiteTreeHints());
$classes = $hints['Root']['disallowedChildren'];
$this->assertNotContains('ErrorPage', $classes);
$this->assertNotContains('SiteTreeSubsitesTest_ClassA', $classes);
$this->assertNotContains('SiteTreeSubsitesTest_ClassB', $classes);
}
} }
class SiteTreeSubsitesTest_ClassA extends SiteTree implements TestOnly {} class SiteTreeSubsitesTest_ClassA extends SiteTree implements TestOnly
{
}
class SiteTreeSubsitesTest_ClassB extends SiteTree implements TestOnly {} class SiteTreeSubsitesTest_ClassB extends SiteTree implements TestOnly
{
}

View File

@ -1,143 +1,150 @@
<?php <?php
class SubsiteAdminFunctionalTest extends FunctionalTest { 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. * 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')) { $response = $this->get($url);
$response = $this->mainSession->followRedirection(); while ($location = $response->getHeader('Location')) {
} $response = $this->mainSession->followRedirection();
echo $response->getHeader('Location'); }
echo $response->getHeader('Location');
return $response; return $response;
} }
/** /**
* Anonymous user cannot access anything. * Anonymous user cannot access anything.
*/ */
function testAnonymousIsForbiddenAdminAccess() { public function testAnonymousIsForbiddenAdminAccess()
$response = $this->getAndFollowAll('admin/pages/?SubsiteID=0'); {
$this->assertRegExp('#^Security/login.*#', $this->mainSession->lastUrl(), 'Admin is disallowed'); $response = $this->getAndFollowAll('admin/pages/?SubsiteID=0');
$this->assertRegExp('#^Security/login.*#', $this->mainSession->lastUrl(), 'Admin is disallowed');
$subsite1 = $this->objFromFixture('Subsite', 'subsite1'); $subsite1 = $this->objFromFixture('Subsite', 'subsite1');
$response = $this->getAndFollowAll("admin/pages/?SubsiteID={$subsite1->ID}"); $response = $this->getAndFollowAll("admin/pages/?SubsiteID={$subsite1->ID}");
$this->assertRegExp('#^Security/login.*#', $this->mainSession->lastUrl(), 'Admin is disallowed'); $this->assertRegExp('#^Security/login.*#', $this->mainSession->lastUrl(), 'Admin is disallowed');
$response = $this->getAndFollowAll('SubsiteXHRController'); $response = $this->getAndFollowAll('SubsiteXHRController');
$this->assertRegExp('#^Security/login.*#', $this->mainSession->lastUrl(), $this->assertRegExp('#^Security/login.*#', $this->mainSession->lastUrl(),
'SubsiteXHRController is disallowed'); 'SubsiteXHRController is disallowed');
} }
/** /**
* Admin should be able to access all subsites and the main site * Admin should be able to access all subsites and the main site
*/ */
function testAdminCanAccessAllSubsites() { public function testAdminCanAccessAllSubsites()
$member = $this->objFromFixture('Member', 'admin'); {
Session::set("loggedInAs", $member->ID); $member = $this->objFromFixture('Member', 'admin');
Session::set("loggedInAs", $member->ID);
$this->getAndFollowAll('admin/pages/?SubsiteID=0');
$this->assertEquals(Subsite::currentSubsiteID(), '0', 'Can access main site.'); $this->getAndFollowAll('admin/pages/?SubsiteID=0');
$this->assertRegExp('#^admin/pages.*#', $this->mainSession->lastUrl(), 'Lands on the correct section'); $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', 'subsite1'); $subsite1 = $this->objFromFixture('Subsite', 'subsite1');
$this->getAndFollowAll("admin/pages/?SubsiteID={$subsite1->ID}"); $this->getAndFollowAll("admin/pages/?SubsiteID={$subsite1->ID}");
$this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Can access other subsite.'); $this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Can access other subsite.');
$this->assertRegExp('#^admin/pages.*#', $this->mainSession->lastUrl(), 'Lands on the correct section'); $this->assertRegExp('#^admin/pages.*#', $this->mainSession->lastUrl(), 'Lands on the correct section');
$response = $this->getAndFollowAll('SubsiteXHRController'); $response = $this->getAndFollowAll('SubsiteXHRController');
$this->assertNotRegExp('#^Security/login.*#', $this->mainSession->lastUrl(), $this->assertNotRegExp('#^Security/login.*#', $this->mainSession->lastUrl(),
'SubsiteXHRController is reachable'); 'SubsiteXHRController is reachable');
} }
function testAdminIsRedirectedToObjectsSubsite() { public function testAdminIsRedirectedToObjectsSubsite()
$member = $this->objFromFixture('Member', 'admin'); {
Session::set("loggedInAs", $member->ID); $member = $this->objFromFixture('Member', 'admin');
Session::set("loggedInAs", $member->ID);
$mainSubsitePage = $this->objFromFixture('Page', 'mainSubsitePage');
$subsite1Home = $this->objFromFixture('Page', 'subsite1_home'); $mainSubsitePage = $this->objFromFixture('Page', 'mainSubsitePage');
$subsite1Home = $this->objFromFixture('Page', 'subsite1_home');
Config::inst()->nest(); Config::inst()->nest();
Config::inst()->update('CMSPageEditController', 'treats_subsite_0_as_global', false); Config::inst()->update('CMSPageEditController', 'treats_subsite_0_as_global', false);
Subsite::changeSubsite(0); Subsite::changeSubsite(0);
$this->getAndFollowAll("admin/pages/edit/show/$subsite1Home->ID"); $this->getAndFollowAll("admin/pages/edit/show/$subsite1Home->ID");
$this->assertEquals(Subsite::currentSubsiteID(), $subsite1Home->SubsiteID, 'Loading an object switches the subsite'); $this->assertEquals(Subsite::currentSubsiteID(), $subsite1Home->SubsiteID, 'Loading an object switches the subsite');
$this->assertRegExp("#^admin/pages.*#", $this->mainSession->lastUrl(), 'Lands on the correct section'); $this->assertRegExp("#^admin/pages.*#", $this->mainSession->lastUrl(), 'Lands on the correct section');
Config::inst()->update('CMSPageEditController', 'treats_subsite_0_as_global', true); Config::inst()->update('CMSPageEditController', 'treats_subsite_0_as_global', true);
Subsite::changeSubsite(0); Subsite::changeSubsite(0);
$this->getAndFollowAll("admin/pages/edit/show/$subsite1Home->ID"); $this->getAndFollowAll("admin/pages/edit/show/$subsite1Home->ID");
$this->assertEquals(Subsite::currentSubsiteID(), $subsite1Home->SubsiteID, 'Loading a non-main-site object still switches the subsite if configured with treats_subsite_0_as_global'); $this->assertEquals(Subsite::currentSubsiteID(), $subsite1Home->SubsiteID, '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->assertRegExp("#^admin/pages.*#", $this->mainSession->lastUrl(), 'Lands on the correct section');
$this->getAndFollowAll("admin/pages/edit/show/$mainSubsitePage->ID"); $this->getAndFollowAll("admin/pages/edit/show/$mainSubsitePage->ID");
$this->assertNotEquals(Subsite::currentSubsiteID(), $mainSubsitePage->SubsiteID, 'Loading a main-site object does not change the subsite if configured with treats_subsite_0_as_global'); $this->assertNotEquals(Subsite::currentSubsiteID(), $mainSubsitePage->SubsiteID, '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'); $this->assertRegExp("#^admin/pages.*#", $this->mainSession->lastUrl(), 'Lands on the correct section');
Config::inst()->unnest(); Config::inst()->unnest();
} }
/** /**
* User which has AccessAllSubsites set to 1 should be able to access all subsites and main site, * User which has AccessAllSubsites set to 1 should be able to access all subsites and main site,
* even though he does not have the ADMIN permission. * even though he does not have the ADMIN permission.
*/ */
function testEditorCanAccessAllSubsites() { public function testEditorCanAccessAllSubsites()
$member = $this->objFromFixture('Member', 'editor'); {
Session::set("loggedInAs", $member->ID); $member = $this->objFromFixture('Member', 'editor');
Session::set("loggedInAs", $member->ID);
$this->getAndFollowAll('admin/pages/?SubsiteID=0'); $this->getAndFollowAll('admin/pages/?SubsiteID=0');
$this->assertEquals(Subsite::currentSubsiteID(), '0', 'Can access main site.'); $this->assertEquals(Subsite::currentSubsiteID(), '0', 'Can access main site.');
$this->assertRegExp('#^admin/pages.*#', $this->mainSession->lastUrl(), 'Lands on the correct section'); $this->assertRegExp('#^admin/pages.*#', $this->mainSession->lastUrl(), 'Lands on the correct section');
$subsite1 = $this->objFromFixture('Subsite', 'subsite1'); $subsite1 = $this->objFromFixture('Subsite', 'subsite1');
$this->getAndFollowAll("admin/pages/?SubsiteID={$subsite1->ID}"); $this->getAndFollowAll("admin/pages/?SubsiteID={$subsite1->ID}");
$this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Can access other subsite.'); $this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Can access other subsite.');
$this->assertRegExp('#^admin/pages.*#', $this->mainSession->lastUrl(), 'Lands on the correct section'); $this->assertRegExp('#^admin/pages.*#', $this->mainSession->lastUrl(), 'Lands on the correct section');
$response = $this->getAndFollowAll('SubsiteXHRController'); $response = $this->getAndFollowAll('SubsiteXHRController');
$this->assertNotRegExp('#^Security/login.*#', $this->mainSession->lastUrl(), $this->assertNotRegExp('#^Security/login.*#', $this->mainSession->lastUrl(),
'SubsiteXHRController is reachable'); 'SubsiteXHRController is reachable');
} }
/** /**
* Test a member who only has access to one subsite (subsite1) and only some sections (pages and security). * Test a member who only has access to one subsite (subsite1) and only some sections (pages and security).
*/ */
function testSubsiteAdmin() { public function testSubsiteAdmin()
$member = $this->objFromFixture('Member', 'subsite1member'); {
Session::set("loggedInAs", $member->ID); $member = $this->objFromFixture('Member', 'subsite1member');
Session::set("loggedInAs", $member->ID);
$subsite1 = $this->objFromFixture('Subsite', 'subsite1'); $subsite1 = $this->objFromFixture('Subsite', 'subsite1');
// Check allowed URL. // Check allowed URL.
$this->getAndFollowAll("admin/pages/?SubsiteID={$subsite1->ID}"); $this->getAndFollowAll("admin/pages/?SubsiteID={$subsite1->ID}");
$this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Can access own subsite.'); $this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Can access own subsite.');
$this->assertRegExp('#^admin/pages.*#', $this->mainSession->lastUrl(), 'Can access permitted section.'); $this->assertRegExp('#^admin/pages.*#', $this->mainSession->lastUrl(), 'Can access permitted section.');
// Check forbidden section in allowed subsite. // Check forbidden section in allowed subsite.
$this->getAndFollowAll("admin/assets/?SubsiteID={$subsite1->ID}"); $this->getAndFollowAll("admin/assets/?SubsiteID={$subsite1->ID}");
$this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Is redirected within subsite.'); $this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Is redirected within subsite.');
$this->assertNotRegExp('#^admin/assets/.*#', $this->mainSession->lastUrl(), $this->assertNotRegExp('#^admin/assets/.*#', $this->mainSession->lastUrl(),
'Is redirected away from forbidden section'); 'Is redirected away from forbidden section');
// Check forbidden site, on a section that's allowed on another subsite // Check forbidden site, on a section that's allowed on another subsite
$this->getAndFollowAll("admin/pages/?SubsiteID=0"); $this->getAndFollowAll("admin/pages/?SubsiteID=0");
$this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Is redirected to permitted subsite.'); $this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Is redirected to permitted subsite.');
// Check forbidden site, on a section that's not allowed on any other subsite // Check forbidden site, on a section that's not allowed on any other subsite
$this->getAndFollowAll("admin/assets/?SubsiteID=0"); $this->getAndFollowAll("admin/assets/?SubsiteID=0");
$this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Is redirected to first permitted subsite.'); $this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Is redirected to first permitted subsite.');
$this->assertNotRegExp('#^Security/login.*#', $this->mainSession->lastUrl(), 'Is not denied access'); $this->assertNotRegExp('#^Security/login.*#', $this->mainSession->lastUrl(), 'Is not denied access');
// Check the standalone XHR controller. // Check the standalone XHR controller.
$response = $this->getAndFollowAll('SubsiteXHRController'); $response = $this->getAndFollowAll('SubsiteXHRController');
$this->assertNotRegExp('#^Security/login.*#', $this->mainSession->lastUrl(), $this->assertNotRegExp('#^Security/login.*#', $this->mainSession->lastUrl(),
'SubsiteXHRController is reachable'); 'SubsiteXHRController is reachable');
} }
} }

View File

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

View File

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

View File

@ -1,276 +1,288 @@
<?php <?php
class SubsitesVirtualPageTest extends BaseSubsiteTest { class SubsitesVirtualPageTest extends BaseSubsiteTest
static $fixture_file = array( {
'subsites/tests/SubsiteTest.yml', public static $fixture_file = array(
'subsites/tests/SubsitesVirtualPageTest.yml', 'subsites/tests/SubsiteTest.yml',
); 'subsites/tests/SubsitesVirtualPageTest.yml',
);
function setUp() {
parent::setUp(); public function setUp()
$this->logInWithPermission('ADMIN'); {
parent::setUp();
$fh = fopen(Director::baseFolder() . '/assets/testscript-test-file.pdf', "w"); $this->logInWithPermission('ADMIN');
fwrite($fh, str_repeat('x',1000000));
fclose($fh); $fh = fopen(Director::baseFolder() . '/assets/testscript-test-file.pdf', "w");
} fwrite($fh, str_repeat('x', 1000000));
fclose($fh);
}
function tearDown() { public function tearDown()
parent::tearDown(); {
$testFiles = array( parent::tearDown();
'/assets/testscript-test-file.pdf', $testFiles = array(
'/assets/renamed-test-file.pdf', '/assets/testscript-test-file.pdf',
'/assets/renamed-test-file-second-time.pdf', '/assets/renamed-test-file.pdf',
); '/assets/renamed-test-file-second-time.pdf',
foreach($testFiles as $file) { );
if(file_exists(Director::baseFolder().$file)) unlink(Director::baseFolder().$file); foreach ($testFiles as $file) {
} if (file_exists(Director::baseFolder().$file)) {
} unlink(Director::baseFolder().$file);
}
// Attempt to bring main:linky to subsite2:linky }
function testVirtualPageFromAnotherSubsite() { }
Subsite::$write_hostmap = false;
// Attempt to bring main:linky to subsite2:linky
$subsite = $this->objFromFixture('Subsite', 'subsite2'); public function testVirtualPageFromAnotherSubsite()
{
Subsite::changeSubsite($subsite->ID); Subsite::$write_hostmap = false;
Subsite::$disable_subsite_filter = false;
$subsite = $this->objFromFixture('Subsite', 'subsite2');
$linky = $this->objFromFixture('Page', 'linky');
Subsite::changeSubsite($subsite->ID);
$svp = new SubsitesVirtualPage(); Subsite::$disable_subsite_filter = false;
$svp->CopyContentFromID = $linky->ID;
$svp->SubsiteID = $subsite->ID; $linky = $this->objFromFixture('Page', 'linky');
$svp->URLSegment = 'linky';
$svp = new SubsitesVirtualPage();
$svp->write(); $svp->CopyContentFromID = $linky->ID;
$svp->SubsiteID = $subsite->ID;
$this->assertEquals($svp->SubsiteID, $subsite->ID); $svp->URLSegment = 'linky';
$this->assertEquals($svp->Title, $linky->Title);
} $svp->write();
$this->assertEquals($svp->SubsiteID, $subsite->ID);
$this->assertEquals($svp->Title, $linky->Title);
}
function testFileLinkRewritingOnVirtualPages() { public function testFileLinkRewritingOnVirtualPages()
// File setup {
$this->logInWithPermission('ADMIN'); // File setup
touch(Director::baseFolder() . '/assets/testscript-test-file.pdf'); $this->logInWithPermission('ADMIN');
touch(Director::baseFolder() . '/assets/testscript-test-file.pdf');
// Publish the source page // Publish the source page
$page = $this->objFromFixture('SiteTree', 'page1'); $page = $this->objFromFixture('SiteTree', 'page1');
$this->assertTrue($page->doPublish()); $this->assertTrue($page->doPublish());
// Create a virtual page from it, and publish that // Create a virtual page from it, and publish that
$svp = new SubsitesVirtualPage(); $svp = new SubsitesVirtualPage();
$svp->CopyContentFromID = $page->ID; $svp->CopyContentFromID = $page->ID;
$svp->write(); $svp->write();
$svp->doPublish(); $svp->doPublish();
// Rename the file // Rename the file
$file = $this->objFromFixture('File', 'file1'); $file = $this->objFromFixture('File', 'file1');
$file->Name = 'renamed-test-file.pdf'; $file->Name = 'renamed-test-file.pdf';
$file->write(); $file->write();
// Verify that the draft and publish virtual pages both have the corrected link // Verify that the draft and publish virtual pages both have the corrected link
$this->assertContains('<img src="assets/renamed-test-file.pdf"', $this->assertContains('<img src="assets/renamed-test-file.pdf"',
DB::query("SELECT \"Content\" FROM \"SiteTree\" WHERE \"ID\" = $svp->ID")->value()); DB::query("SELECT \"Content\" FROM \"SiteTree\" WHERE \"ID\" = $svp->ID")->value());
$this->assertContains('<img src="assets/renamed-test-file.pdf"', $this->assertContains('<img src="assets/renamed-test-file.pdf"',
DB::query("SELECT \"Content\" FROM \"SiteTree_Live\" WHERE \"ID\" = $svp->ID")->value()); DB::query("SELECT \"Content\" FROM \"SiteTree_Live\" WHERE \"ID\" = $svp->ID")->value());
// File teardown // File teardown
$testFiles = array( $testFiles = array(
'/assets/testscript-test-file.pdf', '/assets/testscript-test-file.pdf',
'/assets/renamed-test-file.pdf', '/assets/renamed-test-file.pdf',
); );
foreach($testFiles as $file) { foreach ($testFiles as $file) {
if(file_exists(Director::baseFolder().$file)) unlink(Director::baseFolder().$file); if (file_exists(Director::baseFolder().$file)) {
} unlink(Director::baseFolder().$file);
} }
}
}
function testSubsiteVirtualPagesArentInappropriatelyPublished() { public function testSubsiteVirtualPagesArentInappropriatelyPublished()
// Fixture {
$p = new Page(); // Fixture
$p->Content = "test content"; $p = new Page();
$p->write(); $p->Content = "test content";
$vp = new SubsitesVirtualPage(); $p->write();
$vp->CopyContentFromID = $p->ID; $vp = new SubsitesVirtualPage();
$vp->write(); $vp->CopyContentFromID = $p->ID;
$vp->write();
// VP is oragne // VP is oragne
$this->assertTrue($vp->IsAddedToStage); $this->assertTrue($vp->IsAddedToStage);
// VP is still orange after we publish // VP is still orange after we publish
$p->doPublish(); $p->doPublish();
$this->fixVersionNumberCache($vp); $this->fixVersionNumberCache($vp);
$this->assertTrue($vp->IsAddedToStage); $this->assertTrue($vp->IsAddedToStage);
// A new VP created after P's initial construction // A new VP created after P's initial construction
$vp2 = new SubsitesVirtualPage(); $vp2 = new SubsitesVirtualPage();
$vp2->CopyContentFromID = $p->ID; $vp2->CopyContentFromID = $p->ID;
$vp2->write(); $vp2->write();
$this->assertTrue($vp2->IsAddedToStage); $this->assertTrue($vp2->IsAddedToStage);
// Also remains orange after a republish // Also remains orange after a republish
$p->Content = "new content"; $p->Content = "new content";
$p->write(); $p->write();
$p->doPublish(); $p->doPublish();
$this->fixVersionNumberCache($vp2); $this->fixVersionNumberCache($vp2);
$this->assertTrue($vp2->IsAddedToStage); $this->assertTrue($vp2->IsAddedToStage);
// VP is now published // VP is now published
$vp->doPublish(); $vp->doPublish();
$this->fixVersionNumberCache($vp); $this->fixVersionNumberCache($vp);
$this->assertTrue($vp->ExistsOnLive); $this->assertTrue($vp->ExistsOnLive);
$this->assertFalse($vp->IsModifiedOnStage); $this->assertFalse($vp->IsModifiedOnStage);
// P edited, VP and P both go green // P edited, VP and P both go green
$p->Content = "third content"; $p->Content = "third content";
$p->write(); $p->write();
$this->fixVersionNumberCache($vp, $p); $this->fixVersionNumberCache($vp, $p);
$this->assertTrue($p->IsModifiedOnStage); $this->assertTrue($p->IsModifiedOnStage);
$this->assertTrue($vp->IsModifiedOnStage); $this->assertTrue($vp->IsModifiedOnStage);
// Publish, VP goes black // Publish, VP goes black
$p->doPublish(); $p->doPublish();
$this->fixVersionNumberCache($vp); $this->fixVersionNumberCache($vp);
$this->assertTrue($vp->ExistsOnLive); $this->assertTrue($vp->ExistsOnLive);
$this->assertFalse($vp->IsModifiedOnStage); $this->assertFalse($vp->IsModifiedOnStage);
} }
/** /**
* This test ensures published Subsites Virtual Pages immediately reflect updates * This test ensures published Subsites Virtual Pages immediately reflect updates
* to their published target pages. Note - this has to happen when the virtual page * to their published target pages. Note - this has to happen when the virtual page
* is in a different subsite to the page you are editing and republishing, * is in a different subsite to the page you are editing and republishing,
* otherwise the test will pass falsely due to current subsite ID being the same. * otherwise the test will pass falsely due to current subsite ID being the same.
*/ */
function testPublishedSubsiteVirtualPagesUpdateIfTargetPageUpdates() public function testPublishedSubsiteVirtualPagesUpdateIfTargetPageUpdates()
{ {
// create page // create page
$p = new Page(); $p = new Page();
$p->Content = 'Content'; $p->Content = 'Content';
$p->Title = 'Title'; $p->Title = 'Title';
$p->writeToStage('Stage'); $p->writeToStage('Stage');
$p->publish('Stage', 'Live'); $p->publish('Stage', 'Live');
$this->assertTrue($p->ExistsOnLive); $this->assertTrue($p->ExistsOnLive);
// change to subsite // change to subsite
$subsite = $this->objFromFixture('Subsite', 'subsite2'); $subsite = $this->objFromFixture('Subsite', 'subsite2');
Subsite::changeSubsite($subsite->ID); Subsite::changeSubsite($subsite->ID);
Subsite::$disable_subsite_filter = false; Subsite::$disable_subsite_filter = false;
// create svp in subsite // create svp in subsite
$svp = new SubsitesVirtualPage(); $svp = new SubsitesVirtualPage();
$svp->CopyContentFromID = $p->ID; $svp->CopyContentFromID = $p->ID;
$svp->write(); $svp->write();
$svp->writeToStage('Stage'); $svp->writeToStage('Stage');
$svp->publish('Stage', 'Live'); $svp->publish('Stage', 'Live');
$this->assertEquals($svp->SubsiteID, $subsite->ID); $this->assertEquals($svp->SubsiteID, $subsite->ID);
$this->assertTrue($svp->ExistsOnLive); $this->assertTrue($svp->ExistsOnLive);
// change back to original subsite ("Main site") // change back to original subsite ("Main site")
Subsite::changeSubsite(0); Subsite::changeSubsite(0);
// update original page // update original page
$p->Title = 'New Title'; $p->Title = 'New Title';
// "save & publish" // "save & publish"
$p->writeToStage('Stage'); $p->writeToStage('Stage');
$p->publish('Stage', 'Live'); $p->publish('Stage', 'Live');
$this->assertNotEquals($p->SubsiteID, $subsite->ID); $this->assertNotEquals($p->SubsiteID, $subsite->ID);
// reload SVP from database // reload SVP from database
// can't use DO::get by id because caches. // can't use DO::get by id because caches.
$svpdb = $svp->get()->byID($svp->ID); $svpdb = $svp->get()->byID($svp->ID);
// ensure title changed // ensure title changed
$this->assertEquals($svpdb->Title, $p->Title); $this->assertEquals($svpdb->Title, $p->Title);
} }
function testUnpublishingParentPageUnpublishesSubsiteVirtualPages() { public function testUnpublishingParentPageUnpublishesSubsiteVirtualPages()
Config::inst()->update('StaticPublisher', 'disable_realtime', true); {
Config::inst()->update('StaticPublisher', 'disable_realtime', true);
// Go to main site, get parent page
$subsite = $this->objFromFixture('Subsite', 'main'); // Go to main site, get parent page
Subsite::changeSubsite($subsite->ID); $subsite = $this->objFromFixture('Subsite', 'main');
$page = $this->objFromFixture('Page', 'importantpage'); Subsite::changeSubsite($subsite->ID);
$page = $this->objFromFixture('Page', 'importantpage');
// Create two SVPs on other subsites
$subsite = $this->objFromFixture('Subsite', 'subsite1'); // Create two SVPs on other subsites
Subsite::changeSubsite($subsite->ID); $subsite = $this->objFromFixture('Subsite', 'subsite1');
$vp1 = new SubsitesVirtualPage(); Subsite::changeSubsite($subsite->ID);
$vp1->CopyContentFromID = $page->ID; $vp1 = new SubsitesVirtualPage();
$vp1->write(); $vp1->CopyContentFromID = $page->ID;
$vp1->doPublish(); $vp1->write();
$vp1->doPublish();
$subsite = $this->objFromFixture('Subsite', 'subsite2');
Subsite::changeSubsite($subsite->ID); $subsite = $this->objFromFixture('Subsite', 'subsite2');
$vp2 = new SubsitesVirtualPage(); Subsite::changeSubsite($subsite->ID);
$vp2->CopyContentFromID = $page->ID; $vp2 = new SubsitesVirtualPage();
$vp2->write(); $vp2->CopyContentFromID = $page->ID;
$vp2->doPublish(); $vp2->write();
$vp2->doPublish();
// Switch back to main site, unpublish source
$subsite = $this->objFromFixture('Subsite', 'main'); // Switch back to main site, unpublish source
Subsite::changeSubsite($subsite->ID); $subsite = $this->objFromFixture('Subsite', 'main');
$page = $this->objFromFixture('Page', 'importantpage'); Subsite::changeSubsite($subsite->ID);
$page->doUnpublish(); $page = $this->objFromFixture('Page', 'importantpage');
$page->doUnpublish();
Subsite::changeSubsite($vp1->SubsiteID);
$onLive = Versioned::get_one_by_stage('SubsitesVirtualPage', 'Live', "\"SiteTree_Live\".\"ID\" = ".$vp1->ID); Subsite::changeSubsite($vp1->SubsiteID);
$this->assertNull($onLive, 'SVP has been removed from live'); $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', 'subsite2');
Subsite::changeSubsite($vp2->SubsiteID); $subsite = $this->objFromFixture('Subsite', 'subsite2');
$onLive = Versioned::get_one_by_stage('SubsitesVirtualPage', 'Live', "\"SiteTree_Live\".\"ID\" = ".$vp2->ID); Subsite::changeSubsite($vp2->SubsiteID);
$this->assertNull($onLive, 'SVP has been removed from live'); $onLive = Versioned::get_one_by_stage('SubsitesVirtualPage', 'Live', "\"SiteTree_Live\".\"ID\" = ".$vp2->ID);
} $this->assertNull($onLive, 'SVP has been removed from live');
}
/**
* Similar to {@link SiteTreeSubsitesTest->testTwoPagesWithSameURLOnDifferentSubsites()} /**
* and {@link SiteTreeSubsitesTest->testPagesInDifferentSubsitesCanShareURLSegment()}. * Similar to {@link SiteTreeSubsitesTest->testTwoPagesWithSameURLOnDifferentSubsites()}
*/ * and {@link SiteTreeSubsitesTest->testPagesInDifferentSubsitesCanShareURLSegment()}.
function testSubsiteVirtualPageCanHaveSameUrlsegmentAsOtherSubsite() { */
Subsite::$write_hostmap = false; public function testSubsiteVirtualPageCanHaveSameUrlsegmentAsOtherSubsite()
$subsite1 = $this->objFromFixture('Subsite', 'subsite1'); {
$subsite2 = $this->objFromFixture('Subsite', 'subsite2'); Subsite::$write_hostmap = false;
Subsite::changeSubsite($subsite1->ID); $subsite1 = $this->objFromFixture('Subsite', 'subsite1');
$subsite2 = $this->objFromFixture('Subsite', 'subsite2');
$subsite1Page = $this->objFromFixture('Page', 'subsite1_staff'); Subsite::changeSubsite($subsite1->ID);
$subsite1Page->URLSegment = 'staff';
$subsite1Page->write(); $subsite1Page = $this->objFromFixture('Page', 'subsite1_staff');
$subsite1Page->URLSegment = 'staff';
// saving on subsite1, and linking to subsite1 $subsite1Page->write();
$subsite1Vp = new SubsitesVirtualPage();
$subsite1Vp->CopyContentFromID = $subsite1Page->ID; // saving on subsite1, and linking to subsite1
$subsite1Vp->SubsiteID = $subsite1->ID; $subsite1Vp = new SubsitesVirtualPage();
$subsite1Vp->write(); $subsite1Vp->CopyContentFromID = $subsite1Page->ID;
$this->assertNotEquals( $subsite1Vp->SubsiteID = $subsite1->ID;
$subsite1Vp->URLSegment, $subsite1Vp->write();
$subsite1Page->URLSegment, $this->assertNotEquals(
"Doesn't allow explicit URLSegment overrides when already existing in same subsite" $subsite1Vp->URLSegment,
); $subsite1Page->URLSegment,
"Doesn't allow explicit URLSegment overrides when already existing in same subsite"
//Change to subsite 2 );
Subsite::changeSubsite($subsite2->ID);
//Change to subsite 2
Subsite::changeSubsite($subsite2->ID);
// saving in subsite2 (which already has a page with URLSegment 'contact-us'), // saving in subsite2 (which already has a page with URLSegment 'contact-us'),
// but linking to a page in subsite1 // but linking to a page in subsite1
$subsite2Vp = new SubsitesVirtualPage(); $subsite2Vp = new SubsitesVirtualPage();
$subsite2Vp->CopyContentFromID = $subsite1Page->ID; $subsite2Vp->CopyContentFromID = $subsite1Page->ID;
$subsite2Vp->SubsiteID = $subsite2->ID; $subsite2Vp->SubsiteID = $subsite2->ID;
$subsite2Vp->write(); $subsite2Vp->write();
$this->assertEquals( $this->assertEquals(
$subsite2Vp->URLSegment, $subsite2Vp->URLSegment,
$subsite1Page->URLSegment, $subsite1Page->URLSegment,
"Does allow explicit URLSegment overrides when only existing in a different subsite" "Does allow explicit URLSegment overrides when only existing in a different subsite"
); );
} }
function fixVersionNumberCache($page) {
$pages = func_get_args();
foreach($pages as $p) {
Versioned::prepopulate_versionnumber_cache('SiteTree', 'Stage', array($p->ID));
Versioned::prepopulate_versionnumber_cache('SiteTree', 'Live', array($p->ID));
}
}
public function fixVersionNumberCache($page)
{
$pages = func_get_args();
foreach ($pages as $p) {
Versioned::prepopulate_versionnumber_cache('SiteTree', 'Stage', array($p->ID));
Versioned::prepopulate_versionnumber_cache('SiteTree', 'Live', array($p->ID));
}
}
} }

View File

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