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)
{
$form = parent::getEditForm($id, $fields);
public function getEditForm($id = null, $fields = null) { $grid=$form->Fields()->dataFieldByName('Subsite');
$form = parent::getEditForm($id, $fields); if ($grid) {
$grid->getConfig()->removeComponentsByType('GridFieldDetailForm');
$grid=$form->Fields()->dataFieldByName('Subsite'); $grid->getConfig()->addComponent(new GridFieldSubsiteDetailForm());
if($grid) { }
$grid->getConfig()->removeComponentsByType('GridFieldDetailForm');
$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() { public function parameterFields()
$subsites = Subsite::accessible_sites('CMS_ACCESS_CMSMain', true); {
$options = $subsites->toDropdownMap('ID', 'Title'); $subsites = Subsite::accessible_sites('CMS_ACCESS_CMSMain', true);
$options = $subsites->toDropdownMap('ID', 'Title');
$subsiteField = new TreeMultiselectField( $subsiteField = new TreeMultiselectField(
'Subsites', 'Subsites',
_t('SubsiteReportWrapper.ReportDropdown', 'Sites'), _t('SubsiteReportWrapper.ReportDropdown', 'Sites'),
$options $options
); );
$subsiteField->setValue(array_keys($options)); $subsiteField->setValue(array_keys($options));
// We don't need to make the field editable if only one subsite is available // We don't need to make the field editable if only one subsite is available
if(sizeof($options) <= 1) { if (sizeof($options) <= 1) {
$subsiteField = $subsiteField->performReadonlyTransformation(); $subsiteField = $subsiteField->performReadonlyTransformation();
} }
$fields = parent::parameterFields(); $fields = parent::parameterFields();
if($fields) { if ($fields) {
$fields->insertBefore($subsiteField, $fields->First()->Name()); $fields->insertBefore($subsiteField, $fields->First()->Name());
} else { } else {
$fields = new FieldList($subsiteField); $fields = new FieldList($subsiteField);
} }
return $fields; return $fields;
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Columns // Columns
function columns() { public function columns()
$columns = parent::columns(); {
$columns['Subsite.Title'] = "Subsite"; $columns = parent::columns();
return $columns; $columns['Subsite.Title'] = "Subsite";
} return $columns;
}
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Querying // Querying
function beforeQuery($params) { public function beforeQuery($params)
// The user has select a few specific sites {
if(!empty($params['Subsites'])) { // The user has select a few specific sites
Subsite::$force_subsite = $params['Subsites']; if (!empty($params['Subsites'])) {
Subsite::$force_subsite = $params['Subsites'];
// Default: restrict to all accessible sites
} else {
$subsites = Subsite::accessible_sites('CMS_ACCESS_CMSMain');
$options = $subsites->toDropdownMap('ID', 'Title');
Subsite::$force_subsite = join(',', array_keys($options));
}
}
function afterQuery() {
// Manually manage the subsite filtering
Subsite::$force_subsite = null;
}
// 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'
);
private static $db = array( public function getCMSFields()
'CustomMetaTitle' => 'Varchar(255)', {
'CustomMetaKeywords' => 'Varchar(255)', $fields = parent::getCMSFields();
'CustomMetaDescription' => 'Text',
'CustomExtraMeta' => 'HTMLText'
);
public function getCMSFields() { $subsites = DataObject::get('Subsite');
$fields = parent::getCMSFields(); if (!$subsites) {
$subsites = new ArrayList();
} else {
$subsites=ArrayList::create($subsites->toArray());
}
$subsites = DataObject::get('Subsite'); $subsites->push(new ArrayData(array('Title' => 'Main site', 'ID' => 0)));
if(!$subsites) {
$subsites = new ArrayList();
}else {
$subsites=ArrayList::create($subsites->toArray());
}
$subsites->push(new ArrayData(array('Title' => 'Main site', 'ID' => 0))); $fields->addFieldToTab(
'Root.Main',
DropdownField::create(
"CopyContentFromID_SubsiteID",
_t('SubsitesVirtualPage.SubsiteField', "Subsite"),
$subsites->map('ID', 'Title')
)->addExtraClass('subsitestreedropdownfield-chooser no-change-track'),
'CopyContentFromID'
);
$fields->addFieldToTab( // Setup the linking to the original page.
'Root.Main', $pageSelectionField = new SubsitesTreeDropdownField(
DropdownField::create( "CopyContentFromID",
"CopyContentFromID_SubsiteID", _t('VirtualPage.CHOOSE', "Choose a page to link to"),
_t('SubsitesVirtualPage.SubsiteField',"Subsite"), "SiteTree",
$subsites->map('ID', 'Title') "ID",
)->addExtraClass('subsitestreedropdownfield-chooser no-change-track'), "MenuTitle"
'CopyContentFromID' );
);
// Setup the linking to the original page. if (Controller::has_curr() && Controller::curr()->getRequest()) {
$pageSelectionField = new SubsitesTreeDropdownField( $subsiteID = Controller::curr()->getRequest()->requestVar('CopyContentFromID_SubsiteID');
"CopyContentFromID", $pageSelectionField->setSubsiteID($subsiteID);
_t('VirtualPage.CHOOSE', "Choose a page to link to"), }
"SiteTree", $fields->replaceField('CopyContentFromID', $pageSelectionField);
"ID",
"MenuTitle"
);
if(Controller::has_curr() && Controller::curr()->getRequest()) { // Create links back to the original object in the CMS
$subsiteID = Controller::curr()->getRequest()->requestVar('CopyContentFromID_SubsiteID'); if ($this->CopyContentFromID) {
$pageSelectionField->setSubsiteID($subsiteID); $editLink = "admin/pages/edit/show/$this->CopyContentFromID/?SubsiteID=" . $this->CopyContentFrom()->SubsiteID;
} $linkToContent = "
$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\">" . <a class=\"cmsEditlink\" href=\"$editLink\">" .
_t('VirtualPage.EDITCONTENT', 'Click here to edit the content') . _t('VirtualPage.EDITCONTENT', 'Click here to edit the content') .
"</a>"; "</a>";
$fields->removeByName("VirtualPageContentLinkLabel"); $fields->removeByName("VirtualPageContentLinkLabel");
$fields->addFieldToTab( $fields->addFieldToTab(
"Root.Main", "Root.Main",
$linkToContentLabelField = new LabelField('VirtualPageContentLinkLabel', $linkToContent), $linkToContentLabelField = new LabelField('VirtualPageContentLinkLabel', $linkToContent),
'Title' 'Title'
); );
$linkToContentLabelField->setAllowHTML(true); $linkToContentLabelField->setAllowHTML(true);
} }
$fields->addFieldToTab( $fields->addFieldToTab(
'Root.Main', 'Root.Main',
TextField::create( TextField::create(
'CustomMetaTitle', 'CustomMetaTitle',
$this->fieldLabel('CustomMetaTitle') $this->fieldLabel('CustomMetaTitle')
)->setDescription(_t('SubsitesVirtualPage.OverrideNote', 'Overrides inherited value from the source')), )->setDescription(_t('SubsitesVirtualPage.OverrideNote', 'Overrides inherited value from the source')),
'MetaTitle' 'MetaTitle'
); );
$fields->addFieldToTab( $fields->addFieldToTab(
'Root.Main', 'Root.Main',
TextareaField::create( TextareaField::create(
'CustomMetaKeywords', 'CustomMetaKeywords',
$this->fieldLabel('CustomMetaTitle') $this->fieldLabel('CustomMetaTitle')
)->setDescription(_t('SubsitesVirtualPage.OverrideNote')), )->setDescription(_t('SubsitesVirtualPage.OverrideNote')),
'MetaKeywords' 'MetaKeywords'
); );
$fields->addFieldToTab( $fields->addFieldToTab(
'Root.Main', 'Root.Main',
TextareaField::create( TextareaField::create(
'CustomMetaDescription', 'CustomMetaDescription',
$this->fieldLabel('CustomMetaTitle') $this->fieldLabel('CustomMetaTitle')
)->setDescription(_t('SubsitesVirtualPage.OverrideNote')), )->setDescription(_t('SubsitesVirtualPage.OverrideNote')),
'MetaDescription' 'MetaDescription'
); );
$fields->addFieldToTab( $fields->addFieldToTab(
'Root.Main', 'Root.Main',
TextField::create( TextField::create(
'CustomExtraMeta', 'CustomExtraMeta',
$this->fieldLabel('CustomMetaTitle') $this->fieldLabel('CustomMetaTitle')
)->setDescription(_t('SubsitesVirtualPage.OverrideNote')), )->setDescription(_t('SubsitesVirtualPage.OverrideNote')),
'ExtraMeta' 'ExtraMeta'
); );
return $fields; return $fields;
} }
public function fieldLabels($includerelations = true) { public function fieldLabels($includerelations = true)
$labels = parent::fieldLabels($includerelations); {
$labels['CustomMetaTitle'] = _t('Subsite.CustomMetaTitle','Title'); $labels = parent::fieldLabels($includerelations);
$labels['CustomMetaKeywords'] = _t('Subsite.CustomMetaKeywords','Keywords'); $labels['CustomMetaTitle'] = _t('Subsite.CustomMetaTitle', 'Title');
$labels['CustomMetaDescription'] = _t('Subsite.CustomMetaDescription','Description'); $labels['CustomMetaKeywords'] = _t('Subsite.CustomMetaKeywords', 'Keywords');
$labels['CustomExtraMeta'] = _t('Subsite.CustomExtraMeta','Custom Meta Tags'); $labels['CustomMetaDescription'] = _t('Subsite.CustomMetaDescription', 'Description');
$labels['CustomExtraMeta'] = _t('Subsite.CustomExtraMeta', 'Custom Meta Tags');
return $labels; return $labels;
} }
public function getCopyContentFromID_SubsiteID() { public function getCopyContentFromID_SubsiteID()
return ($this->CopyContentFromID) ? (int)$this->CopyContentFrom()->SubsiteID : (int)Session::get('SubsiteID'); {
} return ($this->CopyContentFromID) ? (int)$this->CopyContentFrom()->SubsiteID : (int)Session::get('SubsiteID');
}
public function getVirtualFields() { public function getVirtualFields()
$fields = parent::getVirtualFields(); {
foreach($fields as $k => $v) { $fields = parent::getVirtualFields();
if($v == 'SubsiteID') unset($fields[$k]); foreach ($fields as $k => $v) {
} if ($v == 'SubsiteID') {
unset($fields[$k]);
}
}
foreach(self::$db as $field => $type) if (in_array($field, $fields)) unset($fields[array_search($field, $fields)]); foreach (self::$db as $field => $type) {
if (in_array($field, $fields)) {
unset($fields[array_search($field, $fields)]);
}
}
return $fields; return $fields;
} }
public function syncLinkTracking() { public function syncLinkTracking()
$oldState = Subsite::$disable_subsite_filter; {
Subsite::$disable_subsite_filter = true; $oldState = Subsite::$disable_subsite_filter;
if ($this->CopyContentFromID) $this->HasBrokenLink = DataObject::get_by_id('SiteTree', $this->CopyContentFromID) ? false : true; Subsite::$disable_subsite_filter = true;
Subsite::$disable_subsite_filter = $oldState; if ($this->CopyContentFromID) {
} $this->HasBrokenLink = DataObject::get_by_id('SiteTree', $this->CopyContentFromID) ? false : true;
}
Subsite::$disable_subsite_filter = $oldState;
}
public function onBeforeWrite() { public function onBeforeWrite()
parent::onBeforeWrite(); {
parent::onBeforeWrite();
if($this->CustomMetaTitle) $this->MetaTitle = $this->CustomMetaTitle; if ($this->CustomMetaTitle) {
else { $this->MetaTitle = $this->CustomMetaTitle;
$this->MetaTitle = $this->ContentSource()->MetaTitle ? $this->ContentSource()->MetaTitle : $this->MetaTitle; } else {
} $this->MetaTitle = $this->ContentSource()->MetaTitle ? $this->ContentSource()->MetaTitle : $this->MetaTitle;
if($this->CustomMetaKeywords) $this->MetaKeywords = $this->CustomMetaKeywords; }
else { if ($this->CustomMetaKeywords) {
$this->MetaKeywords = $this->ContentSource()->MetaKeywords ? $this->ContentSource()->MetaKeywords : $this->MetaKeywords; $this->MetaKeywords = $this->CustomMetaKeywords;
} } else {
if($this->CustomMetaDescription) $this->MetaDescription = $this->CustomMetaDescription; $this->MetaKeywords = $this->ContentSource()->MetaKeywords ? $this->ContentSource()->MetaKeywords : $this->MetaKeywords;
else { }
$this->MetaDescription = $this->ContentSource()->MetaDescription ? $this->ContentSource()->MetaDescription : $this->MetaDescription; if ($this->CustomMetaDescription) {
} $this->MetaDescription = $this->CustomMetaDescription;
if($this->CustomExtraMeta) $this->ExtraMeta = $this->CustomExtraMeta; } else {
else { $this->MetaDescription = $this->ContentSource()->MetaDescription ? $this->ContentSource()->MetaDescription : $this->MetaDescription;
$this->ExtraMeta = $this->ContentSource()->ExtraMeta ? $this->ContentSource()->ExtraMeta : $this->ExtraMeta; }
} if ($this->CustomExtraMeta) {
} $this->ExtraMeta = $this->CustomExtraMeta;
} else {
$this->ExtraMeta = $this->ContentSource()->ExtraMeta ? $this->ContentSource()->ExtraMeta : $this->ExtraMeta;
}
}
public function validURLSegment() { public function validURLSegment()
$isValid = parent::validURLSegment(); {
$isValid = parent::validURLSegment();
// Veto the validation rules if its false. In this case, some logic // Veto the validation rules if its false. In this case, some logic
// needs to be duplicated from parent to find out the exact reason the validation failed. // needs to be duplicated from parent to find out the exact reason the validation failed.
if(!$isValid) { if (!$isValid) {
$IDFilter = ($this->ID) ? "AND \"SiteTree\".\"ID\" <> $this->ID" : null; $IDFilter = ($this->ID) ? "AND \"SiteTree\".\"ID\" <> $this->ID" : null;
$parentFilter = null; $parentFilter = null;
if(Config::inst()->get('SiteTree', 'nested_urls')) { if (Config::inst()->get('SiteTree', 'nested_urls')) {
if($this->ParentID) { if ($this->ParentID) {
$parentFilter = " AND \"SiteTree\".\"ParentID\" = $this->ParentID"; $parentFilter = " AND \"SiteTree\".\"ParentID\" = $this->ParentID";
} else { } else {
$parentFilter = ' AND "SiteTree"."ParentID" = 0'; $parentFilter = ' AND "SiteTree"."ParentID" = 0';
} }
} }
$origDisableSubsiteFilter = Subsite::$disable_subsite_filter; $origDisableSubsiteFilter = Subsite::$disable_subsite_filter;
Subsite::$disable_subsite_filter = true; Subsite::$disable_subsite_filter = true;
$existingPage = DataObject::get_one( $existingPage = DataObject::get_one(
'SiteTree', 'SiteTree',
"\"URLSegment\" = '$this->URLSegment' $IDFilter $parentFilter", "\"URLSegment\" = '$this->URLSegment' $IDFilter $parentFilter",
false // disable cache, it doesn't include subsite status in the key false // disable cache, it doesn't include subsite status in the key
); );
Subsite::$disable_subsite_filter = $origDisableSubsiteFilter; Subsite::$disable_subsite_filter = $origDisableSubsiteFilter;
$existingPageInSubsite = DataObject::get_one( $existingPageInSubsite = DataObject::get_one(
'SiteTree', 'SiteTree',
"\"URLSegment\" = '$this->URLSegment' $IDFilter $parentFilter", "\"URLSegment\" = '$this->URLSegment' $IDFilter $parentFilter",
false // disable cache, it doesn't include subsite status in the key false // disable cache, it doesn't include subsite status in the key
); );
// If URL has been vetoed because of an existing page, // If URL has been vetoed because of an existing page,
// be more specific and allow same URLSegments in different subsites // be more specific and allow same URLSegments in different subsites
$isValid = !($existingPage && $existingPageInSubsite); $isValid = !($existingPage && $existingPageInSubsite);
} }
return $isValid; return $isValid;
} }
} }
class SubsitesVirtualPage_Controller extends VirtualPage_Controller { class SubsitesVirtualPage_Controller extends VirtualPage_Controller
{
public function reloadContent()
{
$this->failover->copyFrom($this->failover->CopyContentFrom());
$this->failover->write();
return;
}
public function reloadContent() { public function init()
$this->failover->copyFrom($this->failover->CopyContentFrom()); {
$this->failover->write(); $origDisableSubsiteFilter = Subsite::$disable_subsite_filter;
return; Subsite::$disable_subsite_filter = true;
}
public function init(){ parent::init();
$origDisableSubsiteFilter = Subsite::$disable_subsite_filter;
Subsite::$disable_subsite_filter = true;
parent::init(); Subsite::$disable_subsite_filter = $origDisableSubsiteFilter;
}
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(){ public function CurrentSubsite()
if($subsite = Subsite::currentSubsite()){ {
return $subsite; 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
*
* @see Error::get_filepath_for_errorcode()
*
* FIXME since {@link Subsite::currentSubsite()} partly relies on Session, viewing other sub-site (including main site) between
* opening ErrorPage in the CMS and publish ErrorPage causes static error page to get generated incorrectly.
*/
public function alternateFilepathForErrorcode($statusCode, $locale = null)
{
$static_filepath = Config::inst()->get($this->owner->ClassName, 'static_filepath');
$subdomainPart = "";
/** // Try to get current subsite from session
* Alter file path to generated a static (static) error page file to handle error page template on different sub-sites $subsite = Subsite::currentSubsite(false);
*
* @see Error::get_filepath_for_errorcode()
*
* FIXME since {@link Subsite::currentSubsite()} partly relies on Session, viewing other sub-site (including main site) between
* opening ErrorPage in the CMS and publish ErrorPage causes static error page to get generated incorrectly.
*/
function alternateFilepathForErrorcode($statusCode, $locale = null) {
$static_filepath = Config::inst()->get($this->owner->ClassName, 'static_filepath');
$subdomainPart = "";
// Try to get current subsite from session // since this function is called from Page class before the controller is created, we have to get subsite from domain instead
$subsite = Subsite::currentSubsite(false); if (!$subsite) {
$subsiteID = Subsite::getSubsiteIDForDomain();
if ($subsiteID != 0) {
$subsite = DataObject::get_by_id("Subsite", $subsiteID);
} else {
$subsite = null;
}
}
// since this function is called from Page class before the controller is created, we have to get subsite from domain instead if ($subsite) {
if(!$subsite) { $subdomain = $subsite->domain();
$subsiteID = Subsite::getSubsiteIDForDomain(); $subdomainPart = "-{$subdomain}";
if($subsiteID != 0) $subsite = DataObject::get_by_id("Subsite", $subsiteID); }
else $subsite = null;
}
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
// considered 'global', unless set otherwise
public static $default_root_folders_global = false;
// If this is set to true, all folders created will be default be private static $has_one=array(
// considered 'global', unless set otherwise 'Subsite' => 'Subsite',
static $default_root_folders_global = false; );
private static $has_one=array( /**
'Subsite' => 'Subsite', * Amends the CMS tree title for folders in the Files & Images section.
); * Prefixes a '* ' to the folders that are accessible from all subsites.
*/
public function alternateTreeTitle()
{
if ($this->owner->SubsiteID == 0) {
return " * " . $this->owner->Title;
} else {
return $this->owner->Title;
}
}
/** /**
* Amends the CMS tree title for folders in the Files & Images section. * Add subsites-specific fields to the folder editor.
* Prefixes a '* ' to the folders that are accessible from all subsites. */
*/ public function updateCMSFields(FieldList $fields)
function alternateTreeTitle() { {
if($this->owner->SubsiteID == 0) return " * " . $this->owner->Title; if ($this->owner instanceof Folder) {
else return $this->owner->Title; $sites = Subsite::accessible_sites('CMS_ACCESS_AssetAdmin');
} $values = array();
$values[0] = _t('FileSubsites.AllSitesDropdownOpt', 'All sites');
foreach ($sites as $site) {
$values[$site->ID] = $site->Title;
}
ksort($values);
if ($sites) {
//Dropdown needed to move folders between subsites
$dropdown = new DropdownField(
'SubsiteID',
_t('FileSubsites.SubsiteFieldLabel', 'Subsite'),
$values
);
$dropdown->addExtraClass('subsites-move-dropdown');
$fields->push($dropdown);
$fields->push(new LiteralField(
'Message',
'<p class="message notice">'.
_t('ASSETADMIN.SUBSITENOTICE', 'Folders and files created in the main site are accessible by all subsites.')
.'</p>'
));
}
}
}
/** /**
* Add subsites-specific fields to the folder editor. * Update any requests to limit the results to the current site
*/ */
function updateCMSFields(FieldList $fields) { public function augmentSQL(SQLQuery &$query)
if($this->owner instanceof Folder) { {
$sites = Subsite::accessible_sites('CMS_ACCESS_AssetAdmin'); if (Subsite::$disable_subsite_filter) {
$values = array(); return;
$values[0] = _t('FileSubsites.AllSitesDropdownOpt','All sites'); }
foreach ($sites as $site) {
$values[$site->ID] = $site->Title;
}
ksort($values);
if($sites){
//Dropdown needed to move folders between subsites
$dropdown = new DropdownField(
'SubsiteID',
_t('FileSubsites.SubsiteFieldLabel','Subsite'),
$values
);
$dropdown->addExtraClass('subsites-move-dropdown');
$fields->push($dropdown);
$fields->push(new LiteralField(
'Message',
'<p class="message notice">'.
_t('ASSETADMIN.SUBSITENOTICE', 'Folders and files created in the main site are accessible by all subsites.')
.'</p>'
));
}
}
}
/** // If you're querying by ID, ignore the sub-site - this is a bit ugly... (but it was WAYYYYYYYYY worse)
* Update any requests to limit the results to the current site //@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
*/
public function augmentSQL(SQLQuery &$query) {
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) $from = $query->getFrom();
//@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 if (isset($from['SiteTree_ImageTracking']) || $query->filtersOnID()) {
return;
}
$from = $query->getFrom(); $subsiteID = (int) Subsite::currentSubsiteID();
if(isset($from['SiteTree_ImageTracking']) || $query->filtersOnID()) return;
$subsiteID = (int) Subsite::currentSubsiteID(); // The foreach is an ugly way of getting the first key :-)
foreach ($query->getFrom() as $tableName => $info) {
$where = "\"$tableName\".\"SubsiteID\" IN (0, $subsiteID)";
$query->addWhere($where);
break;
}
// The foreach is an ugly way of getting the first key :-) $sect=array_values($query->getSelect());
foreach($query->getFrom() as $tableName => $info) { $isCounting = strpos($sect[0], 'COUNT') !== false;
$where = "\"$tableName\".\"SubsiteID\" IN (0, $subsiteID)";
$query->addWhere($where);
break;
}
$sect=array_values($query->getSelect()); // Ordering when deleting or counting doesn't apply
$isCounting = strpos($sect[0], 'COUNT') !== false; if (!$isCounting) {
$query->addOrderBy("\"SubsiteID\"");
}
}
// Ordering when deleting or counting doesn't apply public function onBeforeWrite()
if(!$isCounting) { {
$query->addOrderBy("\"SubsiteID\""); if (!$this->owner->ID && !$this->owner->SubsiteID) {
} if (self::$default_root_folders_global) {
} $this->owner->SubsiteID = 0;
} else {
$this->owner->SubsiteID = Subsite::currentSubsiteID();
}
}
}
function onBeforeWrite() { public function onAfterUpload()
if (!$this->owner->ID && !$this->owner->SubsiteID) { {
if (self::$default_root_folders_global) { // If we have a parent, use it's subsite as our subsite
$this->owner->SubsiteID = 0; 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();
}
function onAfterUpload() { public function canEdit($member = null)
// If we have a parent, use it's subsite as our subsite {
if ($this->owner->Parent()) { // Check the CMS_ACCESS_SecurityAdmin privileges on the subsite that owns this group
$this->owner->SubsiteID = $this->owner->Parent()->SubsiteID; $subsiteID = Session::get('SubsiteID');
} else { if ($subsiteID&&$subsiteID == $this->owner->SubsiteID) {
$this->owner->SubsiteID = Subsite::currentSubsiteID(); return true;
} } else {
$this->owner->write(); Session::set('SubsiteID', $this->owner->SubsiteID);
} $access = Permission::check(array('CMS_ACCESS_AssetAdmin', 'CMS_ACCESS_LeftAndMain'));
Session::set('SubsiteID', $subsiteID);
function canEdit($member = null) { return $access;
// Check the CMS_ACCESS_SecurityAdmin privileges on the subsite that owns this group }
$subsiteID = Session::get('SubsiteID'); }
if($subsiteID&&$subsiteID == $this->owner->SubsiteID) {
return true;
} else {
Session::set('SubsiteID', $this->owner->SubsiteID);
$access = Permission::check(array('CMS_ACCESS_AssetAdmin', 'CMS_ACCESS_LeftAndMain'));
Session::set('SubsiteID', $subsiteID);
return $access;
}
}
/**
* Return a piece of text to keep DataObject cache keys appropriately specific
*/
function cacheKeyComponent() {
return 'subsite-'.Subsite::currentSubsiteID();
}
/**
* Return a piece of text to keep DataObject cache keys appropriately specific
*/
public function cacheKeyComponent()
{
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()
{
// Migration for Group.SubsiteID data from when Groups only had a single subsite
$groupFields = DB::field_list('Group');
/** // Detection of SubsiteID field is the trigger for old-style-subsiteID migration
* Migrations for GroupSubsites data. if (isset($groupFields['SubsiteID'])) {
*/ // Migrate subsite-specific data
function requireDefaultRecords() { DB::query('INSERT INTO "Group_Subsites" ("GroupID", "SubsiteID")
// Migration for Group.SubsiteID data from when Groups only had a single subsite
$groupFields = DB::field_list('Group');
// Detection of SubsiteID field is the trigger for old-style-subsiteID migration
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)
} {
} if ($this->owner->canEdit()) {
// i18n tab
$fields->findOrMakeTab('Root.Subsites', _t('GroupSubsites.SECURITYTABTITLE', 'Subsites'));
function updateCMSFields(FieldList $fields) { $subsites = Subsite::accessible_sites(array('ADMIN', 'SECURITY_SUBSITE_GROUP'), true);
if($this->owner->canEdit() ){ $subsiteMap = $subsites->map();
// i18n tab
$fields->findOrMakeTab('Root.Subsites',_t('GroupSubsites.SECURITYTABTITLE','Subsites'));
$subsites = Subsite::accessible_sites(array('ADMIN', 'SECURITY_SUBSITE_GROUP'), true); // Prevent XSS injection
$subsiteMap = $subsites->map(); $subsiteMap = Convert::raw2xml($subsiteMap);
// Prevent XSS injection // Interface is different if you have the rights to modify subsite group values on
$subsiteMap = Convert::raw2xml($subsiteMap); // all subsites
if (isset($subsiteMap[0])) {
$fields->addFieldToTab("Root.Subsites", new OptionsetField("AccessAllSubsites",
_t('GroupSubsites.ACCESSRADIOTITLE', 'Give this group access to'),
array(
1 => _t('GroupSubsites.ACCESSALL', "All subsites"),
0 => _t('GroupSubsites.ACCESSONLY', "Only these subsites"),
)
));
// Interface is different if you have the rights to modify subsite group values on unset($subsiteMap[0]);
// all subsites $fields->addFieldToTab("Root.Subsites", new CheckboxSetField("Subsites", "",
if(isset($subsiteMap[0])) { $subsiteMap));
$fields->addFieldToTab("Root.Subsites", new OptionsetField("AccessAllSubsites", } else {
_t('GroupSubsites.ACCESSRADIOTITLE', 'Give this group access to'), if (sizeof($subsiteMap) <= 1) {
array( $fields->addFieldToTab("Root.Subsites", new ReadonlyField("SubsitesHuman",
1 => _t('GroupSubsites.ACCESSALL', "All subsites"), _t('GroupSubsites.ACCESSRADIOTITLE', 'Give this group access to'),
0 => _t('GroupSubsites.ACCESSONLY', "Only these subsites"), reset($subsiteMap)));
) } else {
)); $fields->addFieldToTab("Root.Subsites", new CheckboxSetField("Subsites",
_t('GroupSubsites.ACCESSRADIOTITLE', 'Give this group access to'),
$subsiteMap));
}
}
}
}
unset($subsiteMap[0]); /**
$fields->addFieldToTab("Root.Subsites", new CheckboxSetField("Subsites", "", * If this group belongs to a subsite,
$subsiteMap)); * append the subsites title to the group title
* to make it easy to distinguish in the tree-view
* of the security admin interface.
*/
public function alternateTreeTitle()
{
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>";
}
}
} else { /**
if (sizeof($subsiteMap) <= 1) { * Update any requests to limit the results to the current site
$fields->addFieldToTab("Root.Subsites", new ReadonlyField("SubsitesHuman", */
_t('GroupSubsites.ACCESSRADIOTITLE', 'Give this group access to'), public function augmentSQL(SQLQuery &$query)
reset($subsiteMap))); {
} else { if (Subsite::$disable_subsite_filter) {
$fields->addFieldToTab("Root.Subsites", new CheckboxSetField("Subsites", return;
_t('GroupSubsites.ACCESSRADIOTITLE', 'Give this group access to'), }
$subsiteMap)); if (Cookie::get('noSubsiteFilter') == 'true') {
} return;
} }
}
}
/** // If you're querying by ID, ignore the sub-site - this is a bit ugly...
* If this group belongs to a subsite, if (!$query->filtersOnID()) {
* append the subsites title to the group title
* to make it easy to distinguish in the tree-view
* of the security admin interface.
*/
function alternateTreeTitle() {
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($context = DataObject::context_obj()) $subsiteID = (int)$context->SubsiteID;
* Update any requests to limit the results to the current site else */$subsiteID = (int)Subsite::currentSubsiteID();
*/
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... // Don't filter by Group_Subsites if we've already done that
if(!$query->filtersOnID()) { $hasGroupSubsites = false;
foreach ($query->getFrom() as $item) {
if ((is_array($item) && strpos($item['table'], 'Group_Subsites')!==false) || (!is_array($item) && strpos($item, 'Group_Subsites')!==false)) {
$hasGroupSubsites = true;
break;
}
}
/*if($context = DataObject::context_obj()) $subsiteID = (int)$context->SubsiteID; if (!$hasGroupSubsites) {
else */$subsiteID = (int)Subsite::currentSubsiteID(); if ($subsiteID) {
$query->addLeftJoin("Group_Subsites", "\"Group_Subsites\".\"GroupID\"
// Don't filter by Group_Subsites if we've already done that
$hasGroupSubsites = false;
foreach($query->getFrom() as $item) {
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\"
= \"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() { public function onAfterWrite()
// New record test approximated by checking whether the ID has changed. {
// Note also that the after write test is only used when we're on a subsite // New record test approximated by checking whether the ID has changed.
if($this->owner->isChanged('ID') && $currentSubsiteID = Subsite::currentSubsiteID()) { // Note also that the after write test is only used when we're on a subsite
$subsites = $this->owner->Subsites(); if ($this->owner->isChanged('ID') && $currentSubsiteID = Subsite::currentSubsiteID()) {
$subsites->add($currentSubsiteID); $subsites = $this->owner->Subsites();
} $subsites->add($currentSubsiteID);
} }
}
function alternateCanEdit() { 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)
* Set the title of the CMS tree {
*/ $fields->push(new HiddenField('SubsiteID', 'SubsiteID', Subsite::currentSubsiteID()));
function getCMSTreeTitle() { }
$subsite = Subsite::currentSubSite();
return $subsite ? Convert::raw2xml($subsite->Title) : _t('LeftAndMain.SITECONTENTLEFT');
}
function updatePageOptions(&$fields) { /**
$fields->push(new HiddenField('SubsiteID', 'SubsiteID', Subsite::currentSubsiteID())); * Find all subsites accessible for current user on this controller.
} *
* @return ArrayList of {@link Subsite} instances.
*/
public function sectionSites($includeMainSite = true, $mainSiteTitle = "Main site", $member = null)
{
if ($mainSiteTitle == 'Main site') {
$mainSiteTitle = _t('Subsites.MainSiteTitle', 'Main site');
}
/** // 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' : '';
foreach($list as $subsite) { $output->push(new ArrayData(array(
$CurrentState = $subsite->ID == $currentSubsiteID ? 'selected' : ''; 'CurrentState' => $CurrentState,
'ID' => $subsite->ID,
'Title' => Convert::raw2xml($subsite->Title)
)));
}
$output->push(new ArrayData(array( return $output;
'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
);
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;
}
/** // 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()) {
*/ return;
public function augmentSQL(SQLQuery &$query) { }
if(Subsite::$disable_subsite_filter) return; $regexp = '/^(.*\.)?("|`)?SubsiteID("|`)?\s?=/';
foreach ($query->getWhereParameterised($parameters) as $predicate) {
if (preg_match($regexp, $predicate)) {
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;
}
function isMainSite() { /**
if($this->owner->SubsiteID == 0) return true; * Update any requests to limit the results to the current site
return false; */
} 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...
* Update any requests to limit the results to the current site // 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()) {
public function augmentSQL(SQLQuery &$query, DataQuery &$dataQuery = null) { return;
if(Subsite::$disable_subsite_filter) return; }
if($dataQuery->getQueryParam('Subsite.filter') === false) return;
// If you're querying by ID, ignore the sub-site - this is a bit ugly... if (Subsite::$force_subsite) {
// 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)) { $subsiteID = Subsite::$force_subsite;
if($query->filtersOnID()) return; } else {
/*if($context = DataObject::context_obj()) $subsiteID = (int)$context->SubsiteID;
else */$subsiteID = (int)Subsite::currentSubsiteID();
}
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;
}
}
// The foreach is an ugly way of getting the first key :-) public function onBeforeWrite()
foreach($query->getFrom() as $tableName => $info) { {
// The tableName should be SiteTree or SiteTree_Live... if (!$this->owner->ID && !$this->owner->SubsiteID) {
if(strpos($tableName,'SiteTree') === false) break; $this->owner->SubsiteID = Subsite::currentSubsiteID();
$query->addWhere("\"$tableName\".\"SubsiteID\" IN ($subsiteID)"); }
break;
}
}
function onBeforeWrite() { parent::onBeforeWrite();
if(!$this->owner->ID && !$this->owner->SubsiteID) $this->owner->SubsiteID = Subsite::currentSubsiteID(); }
parent::onBeforeWrite(); public function updateCMSFields(FieldList $fields)
} {
$subsites = Subsite::accessible_sites("CMS_ACCESS_CMSMain");
$subsitesMap = array();
if ($subsites && $subsites->Count()) {
$subsitesMap = $subsites->map('ID', 'Title');
unset($subsitesMap[$this->owner->SubsiteID]);
}
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, );
''
)
);
$fields->addFieldToTab(
'Root.Main',
$copyAction = new InlineFormAction(
"copytosubsite",
_t('SiteTreeSubsites.CopyAction', "Copy")
)
);
$copyAction->includeDefaultJS(false);
}
// replace readonly link prefix $urlsegment = $fields->dataFieldByName('URLSegment');
$subsite = $this->owner->Subsite(); $urlsegment->setURLPrefix($baseLink);
$nested_urls_enabled = Config::inst()->get('SiteTree', 'nested_urls'); }
if($subsite && $subsite->ID) { }
$baseUrl = Director::protocol() . $subsite->domain() . '/';
$baseLink = Controller::join_links (
$baseUrl,
($nested_urls_enabled && $this->owner->ParentID ? $this->owner->Parent()->RelativeLink(true) : null)
);
$urlsegment = $fields->dataFieldByName('URLSegment'); public function alternateSiteConfig()
$urlsegment->setURLPrefix($baseLink); {
} 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;
}
function alternateSiteConfig() { /**
if(!$this->owner->SubsiteID) return false; * Only allow editing of a page if the member satisfies one of the following conditions:
$sc = DataObject::get_one('SiteConfig', '"SubsiteID" = ' . $this->owner->SubsiteID); * - Is in a group which has access to the subsite this page belongs to
if(!$sc) { * - Is in a group with edit permissions on the "main site"
$sc = new SiteConfig(); *
$sc->SubsiteID = $this->owner->SubsiteID; * @return boolean
$sc->Title = _t('Subsite.SiteConfigTitle','Your Site Name'); */
$sc->Tagline = _t('Subsite.SiteConfigSubtitle','Your tagline here'); public function canEdit($member = null)
$sc->write(); {
} if (!$member) {
return $sc; $member = Member::currentUser();
} }
/** // Find the sites that this user has access to
* Only allow editing of a page if the member satisfies one of the following conditions: $goodSites = Subsite::accessible_sites('CMS_ACCESS_CMSMain', true, 'all', $member)->column('ID');
* - 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(); if (!is_null($this->owner->SubsiteID)) {
$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();
}
// Find the sites that this user has access to // Return true if they have access to this object's site
$goodSites = Subsite::accessible_sites('CMS_ACCESS_CMSMain',true,'all',$member)->column('ID'); if (!(in_array(0, $goodSites) || in_array($subsiteID, $goodSites))) {
return false;
}
}
if (!is_null($this->owner->SubsiteID)) { /**
$subsiteID = $this->owner->SubsiteID; * @return boolean
} else { */
// The relationships might not be available during the record creation when using a GridField. public function canDelete($member = null)
// In this case the related objects will have empty fields, and SubsiteID will not be available. {
// if (!$member && $member !== false) {
// We do the second best: fetch the likely SubsiteID from the session. The drawback is this might $member = Member::currentUser();
// 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 return $this->canEdit($member);
if(!(in_array(0, $goodSites) || in_array($subsiteID, $goodSites))) return false; }
}
/** /**
* @return boolean * @return boolean
*/ */
function canDelete($member = null) { public function canAddChildren($member = null)
if(!$member && $member !== FALSE) $member = Member::currentUser(); {
if (!$member && $member !== false) {
$member = Member::currentUser();
}
return $this->canEdit($member); return $this->canEdit($member);
} }
/** /**
* @return boolean * @return boolean
*/ */
function canAddChildren($member = null) { public function canPublish($member = null)
if(!$member && $member !== FALSE) $member = Member::currentUser(); {
if (!$member && $member !== false) {
$member = Member::currentUser();
}
return $this->canEdit($member); return $this->canEdit($member);
} }
/** /**
* @return boolean * Create a duplicate of this page and save it to another subsite
*/ * @param $subsiteID int|Subsite The Subsite to copy to, or its ID
function canPublish($member = null) { */
if(!$member && $member !== FALSE) $member = Member::currentUser(); public function duplicateToSubsite($subsiteID = null)
{
if (is_object($subsiteID)) {
$subsite = $subsiteID;
$subsiteID = $subsite->ID;
} else {
$subsite = DataObject::get_by_id('Subsite', $subsiteID);
}
return $this->canEdit($member); $oldSubsite=Subsite::currentSubsiteID();
} if ($subsiteID) {
Subsite::changeSubsite($subsiteID);
} else {
$subsiteID=$oldSubsite;
}
/** $page = $this->owner->duplicate(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) {
if(is_object($subsiteID)) {
$subsite = $subsiteID;
$subsiteID = $subsite->ID;
} else $subsite = DataObject::get_by_id('Subsite', $subsiteID);
$oldSubsite=Subsite::currentSubsiteID(); $page->CheckedPublicationDifferences = $page->AddedToStage = true;
if($subsiteID) { $subsiteID = ($subsiteID ? $subsiteID : $oldSubsite);
Subsite::changeSubsite($subsiteID); $page->SubsiteID = $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";
}
return $tags; 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);
function augmentSyncLinkTracking() { $subsiteID = Subsite::getSubsiteIDForDomain($domain);
// Set LinkTracking appropriately if ($subsiteID == 0) {
$links = HTTP::getLinksIn($this->owner->Content); continue;
$linkedPages = array(); } // We have no idea what the domain for the main site is, so cant track links to it
if($links) foreach($links as $link) { $origDisableSubsiteFilter = Subsite::$disable_subsite_filter;
if(substr($link, 0, strlen('http://')) == 'http://') { Subsite::disable_subsite_filter(true);
$withoutHttp = substr($link, strlen('http://')); $candidatePage = DataObject::get_one("SiteTree", "\"URLSegment\" = '" . Convert::raw2sql(urldecode($rest)) . "' AND \"SubsiteID\" = " . $subsiteID, false);
if(strpos($withoutHttp, '/') && strpos($withoutHttp, '/') < strlen($withoutHttp)) { Subsite::disable_subsite_filter($origDisableSubsiteFilter);
$domain = substr($withoutHttp, 0, strpos($withoutHttp, '/'));
$rest = substr($withoutHttp, strpos($withoutHttp, '/') + 1);
$subsiteID = Subsite::getSubsiteIDForDomain($domain); if ($candidatePage) {
if($subsiteID == 0) continue; // We have no idea what the domain for the main site is, so cant track links to it $linkedPages[] = $candidatePage->ID;
} else {
$this->owner->HasBrokenLink = true;
}
}
}
}
}
$origDisableSubsiteFilter = Subsite::$disable_subsite_filter; $this->owner->CrossSubsiteLinkTracking()->setByIDList($linkedPages);
Subsite::disable_subsite_filter(true); }
$candidatePage = DataObject::get_one("SiteTree", "\"URLSegment\" = '" . Convert::raw2sql(urldecode( $rest)) . "' AND \"SubsiteID\" = " . $subsiteID, false);
Subsite::disable_subsite_filter($origDisableSubsiteFilter);
if($candidatePage) { /**
$linkedPages[] = $candidatePage->ID; * Return a piece of text to keep DataObject cache keys appropriately specific
} else { */
$this->owner->HasBrokenLink = true; public function cacheKeyComponent()
} {
} return 'subsite-'.Subsite::currentSubsiteID();
} }
}
$this->owner->CrossSubsiteLinkTracking()->setByIDList($linkedPages); /**
} * @param Member
* @return boolean|null
/** */
* Return a piece of text to keep DataObject cache keys appropriately specific public function canCreate($member = null)
*/ {
function cacheKeyComponent() { // Typically called on a singleton, so we're not using the Subsite() relation
return 'subsite-'.Subsite::currentSubsiteID(); $subsite = Subsite::currentSubsite();
} if ($subsite && $subsite->exists() && $subsite->PageTypeBlacklist) {
$blacklisted = explode(',', $subsite->PageTypeBlacklist);
/** // All subclasses need to be listed explicitly
* @param Member if (in_array($this->owner->class, $blacklisted)) {
* @return boolean|null return false;
*/ }
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) {
* Builds an item edit form. The arguments to getCMSFields() are the popupController and $templates = Subsite::get()->sort('Title');
* popupFormName, however this is an experimental API and may change. $templateArray = array();
* if ($templates) {
* @todo In the future, we will probably need to come up with a tigher object representing a partially $templateArray = $templates->map('ID', 'Title');
* 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()
*/
function ItemEditForm() {
$form=parent::ItemEditForm();
if($this->record->ID == 0) { $templateDropdown = new DropdownField('TemplateID', _t('Subsite.COPYSTRUCTURE', 'Copy structure from:'), $templateArray);
$templates = Subsite::get()->sort('Title'); $templateDropdown->setEmptyString('(' . _t('Subsite.NOTEMPLATE', 'No template') . ')');
$templateArray = array(); $form->Fields()->addFieldToTab('Root.Configuration', $templateDropdown);
if($templates) { }
$templateArray = $templates->map('ID', 'Title');
}
$templateDropdown = new DropdownField('TemplateID', _t('Subsite.COPYSTRUCTURE', 'Copy structure from:'), $templateArray); return $form;
$templateDropdown->setEmptyString('(' . _t('Subsite.NOTEMPLATE', 'No template') . ')'); }
$form->Fields()->addFieldToTab('Root.Configuration', $templateDropdown);
}
return $form; public function doSave($data, $form)
} {
$new_record = $this->record->ID == 0;
if ($new_record && isset($data['TemplateID']) && !empty($data['TemplateID'])) {
$template = Subsite::get()->byID(intval($data['TemplateID']));
if ($template) {
$this->record = $template->duplicate();
}
}
function doSave($data, $form) { return parent::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(
'tree'
);
private static $allowed_actions = array( protected $subsiteID = 0;
'tree'
);
protected $subsiteID = 0; protected $extraClasses = array('SubsitesTreeDropdownField');
protected $extraClasses = array('SubsitesTreeDropdownField'); public function Field($properties = array())
{
$html = parent::Field($properties);
function Field($properties = array()) { Requirements::javascript('subsites/javascript/SubsitesTreeDropdownField.js');
$html = parent::Field($properties);
Requirements::javascript('subsites/javascript/SubsitesTreeDropdownField.js'); return $html;
}
return $html; public function setSubsiteID($id)
} {
$this->subsiteID = $id;
}
function setSubsiteID($id) { public function getSubsiteID()
$this->subsiteID = $id; {
} return $this->subsiteID;
}
function getSubsiteID() { public function tree(SS_HTTPRequest $request)
return $this->subsiteID; {
} $oldSubsiteID = Session::get('SubsiteID');
Session::set('SubsiteID', $this->subsiteID);
function tree(SS_HTTPRequest $request) { $results = parent::tree($request);
$oldSubsiteID = Session::get('SubsiteID');
Session::set('SubsiteID', $this->subsiteID);
$results = parent::tree($request); Session::set('SubsiteID', $oldSubsiteID);
Session::set('SubsiteID', $oldSubsiteID); return $results;
}
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 * @var $use_session_subsiteid Boolean Set to TRUE when using the CMS and FALSE
* when browsing the frontend of a website. * when browsing the frontend of a website.
* *
* @todo Remove flag once the Subsite CMS works without session state, * @todo Remove flag once the Subsite CMS works without session state,
* similarly to the Translatable module. * similarly to the Translatable module.
*/ */
public static $use_session_subsiteid = false; public static $use_session_subsiteid = false;
/** /**
* @var boolean $disable_subsite_filter If enabled, bypasses the query decoration * @var boolean $disable_subsite_filter If enabled, bypasses the query decoration
* to limit DataObject::get*() calls to a specific subsite. Useful for debugging. * to limit DataObject::get*() calls to a specific subsite. Useful for debugging.
*/ */
public static $disable_subsite_filter = false; public static $disable_subsite_filter = false;
/** /**
* Allows you to force a specific subsite ID, or comma separated list of IDs. * Allows you to force a specific subsite ID, or comma separated list of IDs.
* Only works for reading. An object cannot be written to more than 1 subsite. * Only works for reading. An object cannot be written to more than 1 subsite.
*/ */
public static $force_subsite = null; public static $force_subsite = null;
/** /**
* *
* @var boolean * @var boolean
*/ */
public static $write_hostmap = true; public static $write_hostmap = true;
/** /**
* Memory cache of accessible sites * Memory cache of accessible sites
* *
* @array * @array
*/ */
private static $_cache_accessible_sites = array(); private static $_cache_accessible_sites = array();
/** /**
* Memory cache of subsite id for domains * Memory cache of subsite id for domains
* *
* @var array * @var array
*/ */
private static $_cache_subsite_for_domain = array(); private static $_cache_subsite_for_domain = array();
/** /**
* @var array $allowed_themes Numeric array of all themes which are allowed to be selected for all subsites. * @var array $allowed_themes Numeric array of all themes which are allowed to be selected for all subsites.
* Corresponds to subfolder names within the /themes folder. By default, all themes contained in this folder * Corresponds to subfolder names within the /themes folder. By default, all themes contained in this folder
* are listed. * are listed.
*/ */
private static $allowed_themes = array(); private static $allowed_themes = array();
/** /**
* @var Boolean If set to TRUE, don't assume 'www.example.com' and 'example.com' are the same. * @var Boolean If set to TRUE, don't assume 'www.example.com' and 'example.com' are the same.
* Doesn't affect wildcard matching, so '*.example.com' will match 'www.example.com' (but not 'example.com') * Doesn't affect wildcard matching, so '*.example.com' will match 'www.example.com' (but not 'example.com')
* in both TRUE or FALSE setting. * in both TRUE or FALSE setting.
*/ */
public static $strict_subdomain_matching = false; public static $strict_subdomain_matching = false;
/** /**
* @var boolean Respects the IsPublic flag when retrieving subsites * @var boolean Respects the IsPublic flag when retrieving subsites
*/ */
public static $check_is_public = true; public static $check_is_public = true;
/** /**
* Set allowed themes * Set allowed themes
* *
* @param array $themes - Numeric array of all themes which are allowed to be selected for all subsites. * @param array $themes - Numeric array of all themes which are allowed to be selected for all subsites.
*/ */
public static function set_allowed_themes($themes) { public static function set_allowed_themes($themes)
self::$allowed_themes = $themes; {
} self::$allowed_themes = $themes;
}
/**
* Gets the subsite currently set in the session. /**
* * Gets the subsite currently set in the session.
* @uses ControllerSubsites->controllerAugmentInit() *
* @return Subsite * @uses ControllerSubsites->controllerAugmentInit()
*/ * @return Subsite
public static function currentSubsite() { */
// get_by_id handles caching so we don't have to public static function currentSubsite()
return DataObject::get_by_id('Subsite', self::currentSubsiteID()); {
} // get_by_id handles caching so we don't have to
return DataObject::get_by_id('Subsite', self::currentSubsiteID());
/** }
* This function gets the current subsite ID from the session. It used in the backend so Ajax requests
* use the correct subsite. The frontend handles subsites differently. It calls getSubsiteIDForDomain /**
* directly from ModelAsController::getNestedController. Only gets Subsite instances which have their * This function gets the current subsite ID from the session. It used in the backend so Ajax requests
* {@link IsPublic} flag set to TRUE. * use the correct subsite. The frontend handles subsites differently. It calls getSubsiteIDForDomain
* * directly from ModelAsController::getNestedController. Only gets Subsite instances which have their
* You can simulate subsite access without creating virtual hosts by appending ?SubsiteID=<ID> to the request. * {@link IsPublic} flag set to TRUE.
* *
* @todo Pass $request object from controller so we don't have to rely on $_GET * You can simulate subsite access without creating virtual hosts by appending ?SubsiteID=<ID> to the request.
* *
* @param boolean $cache * @todo Pass $request object from controller so we don't have to rely on $_GET
* @return int ID of the current subsite instance *
*/ * @param boolean $cache
public static function currentSubsiteID() { * @return int ID of the current subsite instance
$id = NULL; */
public static function currentSubsiteID()
if(isset($_GET['SubsiteID'])) { {
$id = (int)$_GET['SubsiteID']; $id = null;
} else if (Subsite::$use_session_subsiteid) {
$id = Session::get('SubsiteID'); if (isset($_GET['SubsiteID'])) {
} $id = (int)$_GET['SubsiteID'];
} elseif (Subsite::$use_session_subsiteid) {
if($id === NULL) { $id = Session::get('SubsiteID');
$id = self::getSubsiteIDForDomain(); }
}
if ($id === null) {
return (int)$id; $id = self::getSubsiteIDForDomain();
} }
/** return (int)$id;
* Switch to another subsite through storing the subsite identifier in the current PHP session. }
* Only takes effect when {@link Subsite::$use_session_subsiteid} is set to TRUE.
* /**
* @param int|Subsite $subsite Either the ID of the subsite, or the subsite object itself * 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.
public static function changeSubsite($subsite) { *
// Session subsite change only meaningful if the session is active. * @param int|Subsite $subsite Either the ID of the subsite, or the subsite object itself
// Otherwise we risk setting it to wrong value, e.g. if we rely on currentSubsiteID. */
if (!Subsite::$use_session_subsiteid) return; public static function changeSubsite($subsite)
{
if(is_object($subsite)) $subsiteID = $subsite->ID; // Session subsite change only meaningful if the session is active.
else $subsiteID = $subsite; // Otherwise we risk setting it to wrong value, e.g. if we rely on currentSubsiteID.
if (!Subsite::$use_session_subsiteid) {
Session::set('SubsiteID', (int)$subsiteID); return;
}
// Set locale
if (is_object($subsite) && $subsite->Language != '') { if (is_object($subsite)) {
$locale = i18n::get_locale_from_lang($subsite->Language); $subsiteID = $subsite->ID;
if($locale) { } else {
i18n::set_locale($locale); $subsiteID = $subsite;
} }
}
Session::set('SubsiteID', (int)$subsiteID);
Permission::flush_permission_cache();
} // Set locale
if (is_object($subsite) && $subsite->Language != '') {
/** $locale = i18n::get_locale_from_lang($subsite->Language);
* Get a matching subsite for the given host, or for the current HTTP_HOST. if ($locale) {
* Supports "fuzzy" matching of domains by placing an asterisk at the start of end of the string, i18n::set_locale($locale);
* 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. Permission::flush_permission_cache();
* @return int Subsite ID }
*/
public static function getSubsiteIDForDomain($host = null, $checkPermissions = true) { /**
if($host == null && isset($_SERVER['HTTP_HOST'])) { * Get a matching subsite for the given host, or for the current HTTP_HOST.
$host = $_SERVER['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.
$matchingDomains = null; *
$cacheKey = null; * @param $host The host to find the subsite for. If not specified, $_SERVER['HTTP_HOST'] is used.
if ($host) { * @return int Subsite ID
if(!self::$strict_subdomain_matching) $host = preg_replace('/^www\./', '', $host); */
public static function getSubsiteIDForDomain($host = null, $checkPermissions = true)
$cacheKey = implode('_', array($host, Member::currentUserID(), self::$check_is_public)); {
if(isset(self::$_cache_subsite_for_domain[$cacheKey])) return self::$_cache_subsite_for_domain[$cacheKey]; if ($host == null && isset($_SERVER['HTTP_HOST'])) {
$host = $_SERVER['HTTP_HOST'];
$SQL_host = Convert::raw2sql($host); }
$matchingDomains = DataObject::get(
"SubsiteDomain", $matchingDomains = null;
"'$SQL_host' LIKE replace(\"SubsiteDomain\".\"Domain\",'*','%')", $cacheKey = null;
"\"IsPrimary\" DESC" if ($host) {
)->innerJoin('Subsite', "\"Subsite\".\"ID\" = \"SubsiteDomain\".\"SubsiteID\" AND \"Subsite\".\"IsPublic\"=1"); if (!self::$strict_subdomain_matching) {
} $host = preg_replace('/^www\./', '', $host);
}
if($matchingDomains && $matchingDomains->Count()) {
$subsiteIDs = array_unique($matchingDomains->column('SubsiteID')); $cacheKey = implode('_', array($host, Member::currentUserID(), self::$check_is_public));
$subsiteDomains = array_unique($matchingDomains->column('Domain')); if (isset(self::$_cache_subsite_for_domain[$cacheKey])) {
if(sizeof($subsiteIDs) > 1) { return self::$_cache_subsite_for_domain[$cacheKey];
throw new UnexpectedValueException(sprintf( }
"Multiple subsites match on '%s': %s",
$host, $SQL_host = Convert::raw2sql($host);
implode(',', $subsiteDomains) $matchingDomains = DataObject::get(
)); "SubsiteDomain",
} "'$SQL_host' LIKE replace(\"SubsiteDomain\".\"Domain\",'*','%')",
"\"IsPrimary\" DESC"
$subsiteID = $subsiteIDs[0]; )->innerJoin('Subsite', "\"Subsite\".\"ID\" = \"SubsiteDomain\".\"SubsiteID\" AND \"Subsite\".\"IsPublic\"=1");
} else if($default = DataObject::get_one('Subsite', "\"DefaultSite\" = 1")) { }
// Check for a 'default' subsite
$subsiteID = $default->ID; if ($matchingDomains && $matchingDomains->Count()) {
} else { $subsiteIDs = array_unique($matchingDomains->column('SubsiteID'));
// Default subsite id = 0, the main site $subsiteDomains = array_unique($matchingDomains->column('Domain'));
$subsiteID = 0; if (sizeof($subsiteIDs) > 1) {
} throw new UnexpectedValueException(sprintf(
"Multiple subsites match on '%s': %s",
if ($cacheKey) { $host,
self::$_cache_subsite_for_domain[$cacheKey] = $subsiteID; implode(',', $subsiteDomains)
} ));
}
return $subsiteID;
} $subsiteID = $subsiteIDs[0];
} elseif ($default = DataObject::get_one('Subsite', "\"DefaultSite\" = 1")) {
/** // Check for a 'default' subsite
* $subsiteID = $default->ID;
* @param string $className } else {
* @param string $filter // Default subsite id = 0, the main site
* @param string $sort $subsiteID = 0;
* @param string $join }
* @param string $limit
* @return DataList if ($cacheKey) {
*/ self::$_cache_subsite_for_domain[$cacheKey] = $subsiteID;
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 $subsiteID;
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
self::$disable_subsite_filter = $disabled; * @param string $join
} * @param string $limit
* @return DataList
/** */
* Flush caches on database reset public static function get_from_all_subsites($className, $filter = "", $sort = "", $join = "", $limit = "")
*/ {
public static function on_db_reset() { $result = DataObject::get($className, $filter, $sort, $join, $limit);
self::$_cache_accessible_sites = array(); $result = $result->setDataQueryParam('Subsite.filter', false);
self::$_cache_subsite_for_domain = array(); return $result;
} }
/** /**
* Return all subsites, regardless of permissions (augmented with main site). * Disable the sub-site filtering; queries will select from all subsites
* */
* @return SS_List List of {@link Subsite} objects (DataList or ArrayList). public static function disable_subsite_filter($disabled = true)
*/ {
public static function all_sites($includeMainSite = true, $mainSiteTitle = "Main site") { self::$disable_subsite_filter = $disabled;
$subsites = Subsite::get(); }
if($includeMainSite) { /**
$subsites = $subsites->toArray(); * Flush caches on database reset
*/
$mainSite = new Subsite(); public static function on_db_reset()
$mainSite->Title = $mainSiteTitle; {
array_unshift($subsites, $mainSite); self::$_cache_accessible_sites = array();
self::$_cache_subsite_for_domain = array();
$subsites = ArrayList::create($subsites); }
}
/**
return $subsites; * Return all subsites, regardless of permissions (augmented with main site).
} *
* @return SS_List List of {@link Subsite} objects (DataList or ArrayList).
/* */
* Returns an ArrayList of the subsites accessible to the current user. public static function all_sites($includeMainSite = true, $mainSiteTitle = "Main site")
* It's enough for any section to be accessible for the site to be included. {
* $subsites = Subsite::get();
* @return ArrayList of {@link Subsite} instances.
*/ if ($includeMainSite) {
public static function all_accessible_sites($includeMainSite = true, $mainSiteTitle = "Main site", $member = null) { $subsites = $subsites->toArray();
// Rationalise member arguments
if(!$member) $member = Member::currentUser(); $mainSite = new Subsite();
if(!$member) return new ArrayList(); $mainSite->Title = $mainSiteTitle;
if(!is_object($member)) $member = DataObject::get_by_id('Member', $member); array_unshift($subsites, $mainSite);
$subsites = new ArrayList(); $subsites = ArrayList::create($subsites);
}
// Collect subsites for all sections.
$menu = CMSMenu::get_viewable_menu_items(); return $subsites;
foreach($menu as $candidate) { }
if ($candidate->controller) {
$accessibleSites = singleton($candidate->controller)->sectionSites( /*
$includeMainSite, * Returns an ArrayList of the subsites accessible to the current user.
$mainSiteTitle, * It's enough for any section to be accessible for the site to be included.
$member *
); * @return ArrayList of {@link Subsite} instances.
*/
// Replace existing keys so no one site appears twice. public static function all_accessible_sites($includeMainSite = true, $mainSiteTitle = "Main site", $member = null)
$subsites->merge($accessibleSites); {
} // Rationalise member arguments
} if (!$member) {
$member = Member::currentUser();
$subsites->removeDuplicates(); }
if (!$member) {
return $subsites; return new ArrayList();
} }
if (!is_object($member)) {
/** $member = DataObject::get_by_id('Member', $member);
* Return the subsites that the current user can access by given permission. }
* Sites will only be included if they have a Title.
* $subsites = new ArrayList();
* @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. // Collect subsites for all sections.
* @param $mainSiteTitle The label to give to the main site $menu = CMSMenu::get_viewable_menu_items();
* @param $member foreach ($menu as $candidate) {
* @return DataList of {@link Subsite} instances if ($candidate->controller) {
*/ $accessibleSites = singleton($candidate->controller)->sectionSites(
public static function accessible_sites($permCode, $includeMainSite = true, $mainSiteTitle = "Main site", $member = null) { $includeMainSite,
// Rationalise member arguments $mainSiteTitle,
if(!$member) $member = Member::currentUser(); $member
if(!$member) return new ArrayList(); );
if(!is_object($member)) $member = DataObject::get_by_id('Member', $member);
// Replace existing keys so no one site appears twice.
// Rationalise permCode argument $subsites->merge($accessibleSites);
if(is_array($permCode)) $SQL_codes = "'" . implode("', '", Convert::raw2sql($permCode)) . "'"; }
else $SQL_codes = "'" . Convert::raw2sql($permCode) . "'"; }
// Cache handling $subsites->removeDuplicates();
$cacheKey = $SQL_codes . '-' . $member->ID . '-' . $includeMainSite . '-' . $mainSiteTitle;
if(isset(self::$_cache_accessible_sites[$cacheKey])) { return $subsites;
return self::$_cache_accessible_sites[$cacheKey]; }
}
/**
$subsites = DataList::create('Subsite') * Return the subsites that the current user can access by given permission.
->where("\"Subsite\".\"Title\" != ''") * Sites will only be included if they have a Title.
->leftJoin('Group_Subsites', "\"Group_Subsites\".\"SubsiteID\" = \"Subsite\".\"ID\"") *
->innerJoin('Group', "\"Group\".\"ID\" = \"Group_Subsites\".\"GroupID\" OR \"Group\".\"AccessAllSubsites\" = 1") * @param $permCode array|string Either a single permission code or an array of permission codes.
->innerJoin('Group_Members', "\"Group_Members\".\"GroupID\"=\"Group\".\"ID\" AND \"Group_Members\".\"MemberID\" = $member->ID") * @param $includeMainSite If true, the main site will be included if appropriate.
->innerJoin('Permission', "\"Group\".\"ID\"=\"Permission\".\"GroupID\" AND \"Permission\".\"Code\" IN ($SQL_codes, 'CMS_ACCESS_LeftAndMain', 'ADMIN')"); * @param $mainSiteTitle The label to give to the main site
* @param $member
if(!$subsites) $subsites = new ArrayList(); * @return DataList of {@link Subsite} instances
*/
$rolesSubsites = DataList::create('Subsite') public static function accessible_sites($permCode, $includeMainSite = true, $mainSiteTitle = "Main site", $member = null)
->where("\"Subsite\".\"Title\" != ''") {
->leftJoin('Group_Subsites', "\"Group_Subsites\".\"SubsiteID\" = \"Subsite\".\"ID\"") // Rationalise member arguments
->innerJoin('Group', "\"Group\".\"ID\" = \"Group_Subsites\".\"GroupID\" OR \"Group\".\"AccessAllSubsites\" = 1") if (!$member) {
->innerJoin('Group_Members', "\"Group_Members\".\"GroupID\"=\"Group\".\"ID\" AND \"Group_Members\".\"MemberID\" = $member->ID") $member = Member::currentUser();
->innerJoin('Group_Roles', "\"Group_Roles\".\"GroupID\"=\"Group\".\"ID\"") }
->innerJoin('PermissionRole', "\"Group_Roles\".\"PermissionRoleID\"=\"PermissionRole\".\"ID\"") if (!$member) {
->innerJoin('PermissionRoleCode', "\"PermissionRole\".\"ID\"=\"PermissionRoleCode\".\"RoleID\" AND \"PermissionRoleCode\".\"Code\" IN ($SQL_codes, 'CMS_ACCESS_LeftAndMain', 'ADMIN')"); return new ArrayList();
}
if(!$subsites && $rolesSubsites) return $rolesSubsites; if (!is_object($member)) {
$member = DataObject::get_by_id('Member', $member);
$subsites = new ArrayList($subsites->toArray()); }
if($rolesSubsites) foreach($rolesSubsites as $subsite) { // Rationalise permCode argument
if(!$subsites->find('ID', $subsite->ID)) { if (is_array($permCode)) {
$subsites->push($subsite); $SQL_codes = "'" . implode("', '", Convert::raw2sql($permCode)) . "'";
} } else {
} $SQL_codes = "'" . Convert::raw2sql($permCode) . "'";
}
if($includeMainSite) {
if(!is_array($permCode)) $permCode = array($permCode); // Cache handling
if(self::hasMainSitePermission($member, $permCode)) { $cacheKey = $SQL_codes . '-' . $member->ID . '-' . $includeMainSite . '-' . $mainSiteTitle;
$subsites=$subsites->toArray(); if (isset(self::$_cache_accessible_sites[$cacheKey])) {
return self::$_cache_accessible_sites[$cacheKey];
$mainSite = new Subsite(); }
$mainSite->Title = $mainSiteTitle;
array_unshift($subsites, $mainSite); $subsites = DataList::create('Subsite')
$subsites=ArrayList::create($subsites); ->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")
self::$_cache_accessible_sites[$cacheKey] = $subsites; ->innerJoin('Permission', "\"Group\".\"ID\"=\"Permission\".\"GroupID\" AND \"Permission\".\"Code\" IN ($SQL_codes, 'CMS_ACCESS_LeftAndMain', 'ADMIN')");
return $subsites; if (!$subsites) {
} $subsites = new ArrayList();
}
/**
* Write a host->domain map to subsites/host-map.php $rolesSubsites = DataList::create('Subsite')
* ->where("\"Subsite\".\"Title\" != ''")
* This is used primarily when using subsites in conjunction with StaticPublisher ->leftJoin('Group_Subsites', "\"Group_Subsites\".\"SubsiteID\" = \"Subsite\".\"ID\"")
* ->innerJoin('Group', "\"Group\".\"ID\" = \"Group_Subsites\".\"GroupID\" OR \"Group\".\"AccessAllSubsites\" = 1")
* @param string $file - filepath of the host map to be written ->innerJoin('Group_Members', "\"Group_Members\".\"GroupID\"=\"Group\".\"ID\" AND \"Group_Members\".\"MemberID\" = $member->ID")
* @return void ->innerJoin('Group_Roles', "\"Group_Roles\".\"GroupID\"=\"Group\".\"ID\"")
*/ ->innerJoin('PermissionRole', "\"Group_Roles\".\"PermissionRoleID\"=\"PermissionRole\".\"ID\"")
public static function writeHostMap($file = null) { ->innerJoin('PermissionRoleCode', "\"PermissionRole\".\"ID\"=\"PermissionRoleCode\".\"RoleID\" AND \"PermissionRoleCode\".\"Code\" IN ($SQL_codes, 'CMS_ACCESS_LeftAndMain', 'ADMIN')");
if (!self::$write_hostmap) return;
if (!$subsites && $rolesSubsites) {
if (!$file) $file = Director::baseFolder().'/subsites/host-map.php'; return $rolesSubsites;
$hostmap = array(); }
$subsites = DataObject::get('Subsite'); $subsites = new ArrayList($subsites->toArray());
if ($subsites) foreach($subsites as $subsite) { if ($rolesSubsites) {
$domains = $subsite->Domains(); foreach ($rolesSubsites as $subsite) {
if ($domains) foreach($domains as $domain) { if (!$subsites->find('ID', $subsite->ID)) {
$domainStr = $domain->Domain; $subsites->push($subsite);
if(!self::$strict_subdomain_matching) $domainStr = preg_replace('/^www\./', '', $domainStr); }
$hostmap[$domainStr] = $subsite->domain(); }
} }
if ($subsite->DefaultSite) $hostmap['default'] = $subsite->domain();
} if ($includeMainSite) {
if (!is_array($permCode)) {
$data = "<?php \n"; $permCode = array($permCode);
$data .= "// Generated by Subsite::writeHostMap() on " . date('d/M/y') . "\n"; }
$data .= '$subsiteHostmap = ' . var_export($hostmap, true) . ';'; if (self::hasMainSitePermission($member, $permCode)) {
$subsites=$subsites->toArray();
if (is_writable(dirname($file)) || is_writable($file)) {
file_put_contents($file, $data); $mainSite = new Subsite();
} $mainSite->Title = $mainSiteTitle;
} array_unshift($subsites, $mainSite);
$subsites=ArrayList::create($subsites);
/** }
* 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. self::$_cache_accessible_sites[$cacheKey] = $subsites;
* If more than one permission is passed to the method, at least one of them must
* be granted for if to return TRUE. return $subsites;
* }
* @todo Allow permission inheritance through group hierarchy.
* /**
* @param Member Member to check against. Defaults to currently logged in member * Write a host->domain map to subsites/host-map.php
* @param Array Permission code strings. Defaults to "ADMIN". *
* @return boolean * This is used primarily when using subsites in conjunction with StaticPublisher
*/ *
public static function hasMainSitePermission($member = null, $permissionCodes = array('ADMIN')) { * @param string $file - filepath of the host map to be written
if(!is_array($permissionCodes)) * @return void
user_error('Permissions must be passed to Subsite::hasMainSitePermission as an array', E_USER_ERROR); */
public static function writeHostMap($file = null)
if(!$member && $member !== FALSE) $member = Member::currentUser(); {
if (!self::$write_hostmap) {
if(!$member) return false; return;
}
if(!in_array("ADMIN", $permissionCodes)) $permissionCodes[] = "ADMIN";
if (!$file) {
$SQLa_perm = Convert::raw2sql($permissionCodes); $file = Director::baseFolder().'/subsites/host-map.php';
$SQL_perms = join("','", $SQLa_perm); }
$memberID = (int)$member->ID; $hostmap = array();
// Count this user's groups which can access the main site $subsites = DataObject::get('Subsite');
$groupCount = DB::query("
if ($subsites) {
foreach ($subsites as $subsite) {
$domains = $subsite->Domains();
if ($domains) {
foreach ($domains as $domain) {
$domainStr = $domain->Domain;
if (!self::$strict_subdomain_matching) {
$domainStr = preg_replace('/^www\./', '', $domainStr);
}
$hostmap[$domainStr] = $subsite->domain();
}
}
if ($subsite->DefaultSite) {
$hostmap['default'] = $subsite->domain();
}
}
}
$data = "<?php \n";
$data .= "// Generated by Subsite::writeHostMap() on " . date('d/M/y') . "\n";
$data .= '$subsiteHostmap = ' . var_export($hostmap, true) . ';';
if (is_writable(dirname($file)) || is_writable($file)) {
file_put_contents($file, $data);
}
}
/**
* Checks if a member can be granted certain permissions, regardless of the subsite context.
* Similar logic to {@link Permission::checkMember()}, but only returns TRUE
* if the member is part of a group with the "AccessAllSubsites" flag set.
* If more than one permission is passed to the method, at least one of them must
* be granted for if to return TRUE.
*
* @todo Allow permission inheritance through group hierarchy.
*
* @param Member Member to check against. Defaults to currently logged in member
* @param Array Permission code strings. Defaults to "ADMIN".
* @return boolean
*/
public static function hasMainSitePermission($member = null, $permissionCodes = 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";
}
$SQLa_perm = Convert::raw2sql($permissionCodes);
$SQL_perms = join("','", $SQLa_perm);
$memberID = (int)$member->ID;
// Count this user's groups which can access the main site
$groupCount = DB::query("
SELECT COUNT(\"Permission\".\"ID\") SELECT COUNT(\"Permission\".\"ID\")
FROM \"Permission\" FROM \"Permission\"
INNER JOIN \"Group\" ON \"Group\".\"ID\" = \"Permission\".\"GroupID\" AND \"Group\".\"AccessAllSubsites\" = 1 INNER JOIN \"Group\" ON \"Group\".\"ID\" = \"Permission\".\"GroupID\" AND \"Group\".\"AccessAllSubsites\" = 1
@ -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( $languageSelector = new DropdownField(
'Language', 'Language',
$this->fieldLabel('Language'), $this->fieldLabel('Language'),
i18n::get_common_locales() i18n::get_common_locales()
); );
$pageTypeMap = array(); $pageTypeMap = array();
$pageTypes = SiteTree::page_type_classes(); $pageTypes = SiteTree::page_type_classes();
foreach($pageTypes as $pageType) { foreach ($pageTypes as $pageType) {
$pageTypeMap[$pageType] = singleton($pageType)->i18n_singular_name(); $pageTypeMap[$pageType] = singleton($pageType)->i18n_singular_name();
} }
asort($pageTypeMap); asort($pageTypeMap);
$fields = new FieldList( $fields = new FieldList(
$subsiteTabs = new TabSet('Root', $subsiteTabs = new TabSet('Root',
new Tab( new Tab(
'Configuration', 'Configuration',
_t('Subsite.TabTitleConfig', 'Configuration'), _t('Subsite.TabTitleConfig', 'Configuration'),
new HeaderField($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() { public function allowedThemes()
if($themes = $this->stat('allowed_themes')) { {
return ArrayLib::valuekey($themes); if ($themes = $this->stat('allowed_themes')) {
} else { return ArrayLib::valuekey($themes);
$themes = array(); } else {
if(is_dir('../themes/')) { $themes = array();
foreach(scandir('../themes/') as $theme) { if (is_dir('../themes/')) {
if($theme[0] == '.') continue; foreach (scandir('../themes/') as $theme) {
$theme = strtok($theme,'_'); if ($theme[0] == '.') {
$themes[$theme] = $theme; continue;
} }
ksort($themes); $theme = strtok($theme, '_');
} $themes[$theme] = $theme;
return $themes; }
} ksort($themes);
} }
return $themes;
}
}
/** /**
* @return string Current locale of the subsite * @return string Current locale of the subsite
*/ */
public function getLanguage() { public function getLanguage()
if($this->getField('Language')) { {
return $this->getField('Language'); if ($this->getField('Language')) {
} else { return $this->getField('Language');
return i18n::get_locale(); } else {
} return i18n::get_locale();
} }
}
/** /**
* *
* @return ValidationResult * @return ValidationResult
*/ */
public function validate() { public function validate()
$result = parent::validate(); {
if(!$this->Title) { $result = parent::validate();
$result->error(_t('Subsite.ValidateTitle', 'Please add a "Title"')); if (!$this->Title) {
} $result->error(_t('Subsite.ValidateTitle', 'Please add a "Title"'));
return $result; }
} return $result;
}
/** /**
* Whenever a Subsite is written, rewrite the hostmap * Whenever a Subsite is written, rewrite the hostmap
* *
* @return void * @return void
*/ */
public function onAfterWrite() { public function onAfterWrite()
Subsite::writeHostMap(); {
parent::onAfterWrite(); Subsite::writeHostMap();
} parent::onAfterWrite();
}
/** /**
* Return the primary domain of this site. Tries to "normalize" the domain name, * Return the primary domain of this site. Tries to "normalize" the domain name,
* by replacing potential wildcards. * by replacing potential wildcards.
* *
* @return string The full domain name of this subsite (without protocol prefix) * @return string The full domain name of this subsite (without protocol prefix)
*/ */
public function domain() { public function domain()
if($this->ID) { {
$domains = DataObject::get("SubsiteDomain", "\"SubsiteID\" = $this->ID", "\"IsPrimary\" DESC","", 1); if ($this->ID) {
if($domains && $domains->Count()>0) { $domains = DataObject::get("SubsiteDomain", "\"SubsiteID\" = $this->ID", "\"IsPrimary\" DESC", "", 1);
$domain = $domains->First()->Domain; if ($domains && $domains->Count()>0) {
// If there are wildcards in the primary domain (not recommended), make some $domain = $domains->First()->Domain;
// educated guesses about what to replace them with: // If there are wildcards in the primary domain (not recommended), make some
$domain = preg_replace('/\.\*$/',".$_SERVER[HTTP_HOST]", $domain); // educated guesses about what to replace them with:
// Default to "subsite." prefix for first wildcard $domain = preg_replace('/\.\*$/', ".$_SERVER[HTTP_HOST]", $domain);
// TODO Whats the significance of "subsite" in this context?! // Default to "subsite." prefix for first wildcard
$domain = preg_replace('/^\*\./',"subsite.", $domain); // TODO Whats the significance of "subsite" in this context?!
// *Only* removes "intermediate" subdomains, so 'subdomain.www.domain.com' becomes 'subdomain.domain.com' $domain = preg_replace('/^\*\./', "subsite.", $domain);
$domain = str_replace('.www.','.', $domain); // *Only* removes "intermediate" subdomains, so 'subdomain.www.domain.com' becomes 'subdomain.domain.com'
$domain = str_replace('.www.', '.', $domain);
return $domain; return $domain;
} }
// SubsiteID = 0 is often used to refer to the main site, just return $_SERVER['HTTP_HOST'] // SubsiteID = 0 is often used to refer to the main site, just return $_SERVER['HTTP_HOST']
} else { } else {
return $_SERVER['HTTP_HOST']; return $_SERVER['HTTP_HOST'];
} }
} }
/** /**
* *
* @return string - The full domain name of this subsite (without protocol prefix) * @return string - The full domain name of this subsite (without protocol prefix)
*/ */
public function getPrimaryDomain() { public function getPrimaryDomain()
return $this->domain(); {
} return $this->domain();
}
/** /**
* *
* @return string * @return string
*/ */
public function absoluteBaseURL() { public function absoluteBaseURL()
return "http://" . $this->domain() . Director::baseURL(); {
} return "http://" . $this->domain() . Director::baseURL();
}
/** /**
* @todo getClassName is redundant, already stored as a database field? * @todo getClassName is redundant, already stored as a database field?
*/ */
public function getClassName() { public function getClassName()
return $this->class; {
} return $this->class;
}
/** /**
* Javascript admin action to duplicate this subsite * Javascript admin action to duplicate this subsite
* *
* @return string - javascript * @return string - javascript
*/ */
public function adminDuplicate() { public function adminDuplicate()
$newItem = $this->duplicate(); {
$message = _t( $newItem = $this->duplicate();
'Subsite.CopyMessage', $message = _t(
'Created a copy of {title}', 'Subsite.CopyMessage',
array('title' => Convert::raw2js($this->Title)) 'Created a copy of {title}',
); 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
*/
public function duplicate($doWrite = true)
{
$duplicate = parent::duplicate($doWrite);
/** $oldSubsiteID = Session::get('SubsiteID');
* Duplicate this subsite self::changeSubsite($this->ID);
*/
public function duplicate($doWrite = true) {
$duplicate = parent::duplicate($doWrite);
$oldSubsiteID = Session::get('SubsiteID'); /*
self::changeSubsite($this->ID); * Copy data from this object to the given subsite. Does this using an iterative depth-first search.
* 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('Page', 'Live', "\"ParentID\" = $sourceParentID", '');
/* if ($children) {
* Copy data from this object to the given subsite. Does this using an iterative depth-first search. foreach ($children as $child) {
* This will make sure that the new parents on the new subsite are correct, and there are no funny self::changeSubsite($duplicate->ID); //Change to destination subsite
* 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('Page', 'Live', "\"ParentID\" = $sourceParentID", '');
if($children) { $childClone = $child->duplicateToSubsite($duplicate, false);
foreach($children as $child) { $childClone->ParentID = $destParentID;
self::changeSubsite($duplicate->ID); //Change to destination subsite $childClone->writeToStage('Stage');
$childClone->publish('Stage', 'Live');
$childClone = $child->duplicateToSubsite($duplicate, false); self::changeSubsite($this->ID); //Change Back to this subsite
$childClone->ParentID = $destParentID;
$childClone->writeToStage('Stage');
$childClone->publish('Stage', 'Live');
self::changeSubsite($this->ID); //Change Back to this subsite array_push($stack, array($child->ID, $childClone->ID));
}
}
}
array_push($stack, array($child->ID, $childClone->ID)); self::changeSubsite($oldSubsiteID);
}
}
}
self::changeSubsite($oldSubsiteID); return $duplicate;
}
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();
}
/** /**
* Whenever a Subsite Domain is written, rewrite the hostmap *
* * @return \FieldList
* @return void */
*/ public function getCMSFields()
public function onAfterWrite() { {
Subsite::writeHostMap(); $fields = new FieldList(
} new TextField('Domain', $this->fieldLabel('Domain'), null, 255),
new CheckboxField('IsPrimary', $this->fieldLabel('IsPrimary'))
);
/** $this->extend('updateCMSFields', $fields);
* return $fields;
* @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 $title = 'Copy pages to different subsite'; protected $description = '';
protected $description = ''; public function run($request)
{
$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);
}
if($children) { $childClone->ParentID = $destParentID;
foreach($children as $child) { $childClone->writeToStage('Stage');
if($useVirtualPages) { $childClone->publish('Stage', 'Live');
$childClone = new SubsitesVirtualPage(); array_push($stack, array($child->ID, $childClone->ID));
$childClone->writeToStage('Stage');
$childClone->CopyContentFromID = $child->ID;
$childClone->SubsiteID = $subsiteTo->ID;
} else {
$childClone = $child->duplicateToSubsite($subsiteTo->ID, true);
}
$childClone->ParentID = $destParentID; $this->log(sprintf('Copied "%s" (#%d, %s)', $child->Title, $child->ID, $child->Link()));
$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() { public function testTrivialFeatures()
$this->assertTrue(is_array(singleton('FileSubsites')->extraStatics())); {
$file = new File(); $this->assertTrue(is_array(singleton('FileSubsites')->extraStatics()));
$file->Name = 'FileTitle'; $file = new File();
$file->Title = 'FileTitle'; $file->Name = 'FileTitle';
$this->assertEquals(' * FileTitle', $file->alternateTreeTitle()); $file->Title = 'FileTitle';
$file->SubsiteID = $this->objFromFixture('Subsite', 'domaintest1')->ID; $this->assertEquals(' * FileTitle', $file->alternateTreeTitle());
$this->assertEquals('FileTitle', $file->getTreeTitle()); $file->SubsiteID = $this->objFromFixture('Subsite', 'domaintest1')->ID;
$this->assertTrue(singleton('Folder')->getCMSFields() instanceof FieldList); $this->assertEquals('FileTitle', $file->getTreeTitle());
Subsite::changeSubsite(1); $this->assertTrue(singleton('Folder')->getCMSFields() instanceof FieldList);
$this->assertEquals($file->cacheKeyComponent(), 'subsite-1'); Subsite::changeSubsite(1);
} $this->assertEquals($file->cacheKeyComponent(), 'subsite-1');
}
function testWritingSubsiteID() { public function testWritingSubsiteID()
$this->objFromFixture('Member', 'admin')->logIn(); {
$this->objFromFixture('Member', 'admin')->logIn();
$subsite = $this->objFromFixture('Subsite', 'domaintest1'); $subsite = $this->objFromFixture('Subsite', 'domaintest1');
FileSubsites::$default_root_folders_global = true; FileSubsites::$default_root_folders_global = true;
Subsite::changeSubsite(0); Subsite::changeSubsite(0);
$file = new File(); $file = new File();
$file->write(); $file->write();
$file->onAfterUpload(); $file->onAfterUpload();
$this->assertEquals((int)$file->SubsiteID, 0); $this->assertEquals((int)$file->SubsiteID, 0);
Subsite::changeSubsite($subsite->ID); Subsite::changeSubsite($subsite->ID);
$this->assertTrue($file->canEdit()); $this->assertTrue($file->canEdit());
$file = new File(); $file = new File();
$file->write(); $file->write();
$this->assertEquals((int)$file->SubsiteID, 0); $this->assertEquals((int)$file->SubsiteID, 0);
$this->assertTrue($file->canEdit()); $this->assertTrue($file->canEdit());
FileSubsites::$default_root_folders_global = false; FileSubsites::$default_root_folders_global = false;
Subsite::changeSubsite($subsite->ID); Subsite::changeSubsite($subsite->ID);
$file = new File(); $file = new File();
$file->write(); $file->write();
$this->assertEquals($file->SubsiteID, $subsite->ID); $this->assertEquals($file->SubsiteID, $subsite->ID);
// Test inheriting from parent folder // Test inheriting from parent folder
$folder = new Folder(); $folder = new Folder();
$folder->write(); $folder->write();
$this->assertEquals($folder->SubsiteID, $subsite->ID); $this->assertEquals($folder->SubsiteID, $subsite->ID);
FileSubsites::$default_root_folders_global = true; FileSubsites::$default_root_folders_global = true;
$file = new File(); $file = new File();
$file->ParentID = $folder->ID; $file->ParentID = $folder->ID;
$file->onAfterUpload(); $file->onAfterUpload();
$this->assertEquals($folder->SubsiteID, $file->SubsiteID); $this->assertEquals($folder->SubsiteID, $file->SubsiteID);
} }
function testSubsitesFolderDropdown() { 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(
'Main site',
'Template',
'Subsite1 Template',
'Subsite2 Template',
'Test 1',
'Test 2',
'Test 3'
), $source);
}
$this->assertEquals(array(
'Main site',
'Template',
'Subsite1 Template',
'Subsite2 Template',
'Test 1',
'Test 2',
'Test 3'
), $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() { public function testTrivialFeatures()
$this->assertTrue(is_array(singleton('GroupSubsites')->extraStatics())); {
$this->assertTrue(is_array(singleton('GroupSubsites')->providePermissions())); $this->assertTrue(is_array(singleton('GroupSubsites')->extraStatics()));
$this->assertTrue(singleton('Group')->getCMSFields() instanceof FieldList); $this->assertTrue(is_array(singleton('GroupSubsites')->providePermissions()));
} $this->assertTrue(singleton('Group')->getCMSFields() instanceof FieldList);
}
function testAlternateTreeTitle() { public function testAlternateTreeTitle()
$group = new Group(); {
$group->Title = 'The A Team'; $group = new Group();
$group->AccessAllSubsites = true; $group->Title = 'The A Team';
$this->assertEquals($group->getTreeTitle(), 'The A Team <i>(global group)</i>'); $group->AccessAllSubsites = true;
$group->AccessAllSubsites = false; $this->assertEquals($group->getTreeTitle(), 'The A Team <i>(global group)</i>');
$group->write(); $group->AccessAllSubsites = false;
$group->Subsites()->add($this->objFromFixture('Subsite', 'domaintest1')); $group->write();
$group->Subsites()->add($this->objFromFixture('Subsite', 'domaintest2')); $group->Subsites()->add($this->objFromFixture('Subsite', 'domaintest1'));
$this->assertEquals($group->getTreeTitle(), 'The A Team <i>(Test 1, Test 2)</i>'); $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
{
public static $fixture_file = 'subsites/tests/SubsiteTest.yml';
static $fixture_file = 'subsites/tests/SubsiteTest.yml'; /**
* Avoid subsites filtering on fixture fetching.
*/
public function objFromFixture($class, $id)
{
Subsite::disable_subsite_filter(true);
$obj = parent::objFromFixture($class, $id);
Subsite::disable_subsite_filter(false);
/** return $obj;
* 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; public function testSectionSites()
} {
$member = $this->objFromFixture('Member', 'subsite1member');
function testSectionSites() { $cmsmain = singleton('CMSMain');
$member = $this->objFromFixture('Member', 'subsite1member'); $subsites = $cmsmain->sectionSites(true, "Main site", $member);
$this->assertDOSEquals(array(
array('Title' =>'Subsite1 Template')
), $subsites, 'Lists member-accessible sites for the accessible controller.');
$cmsmain = singleton('CMSMain'); $assetadmin = singleton('AssetAdmin');
$subsites = $cmsmain->sectionSites(true, "Main site", $member); $subsites = $assetadmin->sectionSites(true, "Main site", $member);
$this->assertDOSEquals(array( $this->assertDOSEquals(array(), $subsites, 'Does not list any sites for forbidden controller.');
array('Title' =>'Subsite1 Template')
), $subsites, 'Lists member-accessible sites for the accessible controller.');
$assetadmin = singleton('AssetAdmin'); $member = $this->objFromFixture('Member', 'editor');
$subsites = $assetadmin->sectionSites(true, "Main site", $member);
$this->assertDOSEquals(array(), $subsites, 'Does not list any sites for forbidden controller.');
$member = $this->objFromFixture('Member', 'editor'); $cmsmain = singleton('CMSMain');
$subsites = $cmsmain->sectionSites(true, "Main site", $member);
$this->assertDOSContains(array(
array('Title' =>'Main site')
), $subsites, 'Includes the main site for members who can access all sites.');
}
$cmsmain = singleton('CMSMain'); public function testAccessChecksDontChangeCurrentSubsite()
$subsites = $cmsmain->sectionSites(true, "Main site", $member); {
$this->assertDOSContains(array( $admin = $this->objFromFixture("Member", "admin");
array('Title' =>'Main site') $this->loginAs($admin);
), $subsites, 'Includes the main site for members who can access all sites.'); $ids = array();
}
function testAccessChecksDontChangeCurrentSubsite() { $subsite1 = $this->objFromFixture('Subsite', 'domaintest1');
$admin = $this->objFromFixture("Member","admin"); $subsite2 = $this->objFromFixture('Subsite', 'domaintest2');
$this->loginAs($admin); $subsite3 = $this->objFromFixture('Subsite', 'domaintest3');
$ids = array();
$subsite1 = $this->objFromFixture('Subsite', 'domaintest1'); $ids[] = $subsite1->ID;
$subsite2 = $this->objFromFixture('Subsite', 'domaintest2'); $ids[] = $subsite2->ID;
$subsite3 = $this->objFromFixture('Subsite', 'domaintest3'); $ids[] = $subsite3->ID;
$ids[] = 0;
$ids[] = $subsite1->ID; // Enable session-based subsite tracking.
$ids[] = $subsite2->ID; Subsite::$use_session_subsiteid = true;
$ids[] = $subsite3->ID;
$ids[] = 0;
// Enable session-based subsite tracking. foreach ($ids as $id) {
Subsite::$use_session_subsiteid = true; Subsite::changeSubsite($id);
$this->assertEquals($id, Subsite::currentSubsiteID());
foreach($ids as $id) { $left = new LeftAndMain();
Subsite::changeSubsite($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.");
}
}
$left = new LeftAndMain(); public function testShouldChangeSubsite()
$this->assertTrue($left->canView(), "Admin user can view subsites LeftAndMain with id = '$id'"); {
$this->assertEquals($id, Subsite::currentSubsiteID(), $l = new LeftAndMain();
"The current subsite has not been changed in the process of checking permissions for admin user."); Config::inst()->nest();
}
} Config::inst()->update('CMSPageEditController', 'treats_subsite_0_as_global', false);
$this->assertTrue($l->shouldChangeSubsite('CMSPageEditController', 0, 5));
$this->assertFalse($l->shouldChangeSubsite('CMSPageEditController', 0, 0));
$this->assertTrue($l->shouldChangeSubsite('CMSPageEditController', 1, 5));
$this->assertFalse($l->shouldChangeSubsite('CMSPageEditController', 1, 1));
function testShouldChangeSubsite() { Config::inst()->update('CMSPageEditController', 'treats_subsite_0_as_global', true);
$l = new LeftAndMain(); $this->assertFalse($l->shouldChangeSubsite('CMSPageEditController', 0, 5));
Config::inst()->nest(); $this->assertFalse($l->shouldChangeSubsite('CMSPageEditController', 0, 0));
$this->assertTrue($l->shouldChangeSubsite('CMSPageEditController', 1, 5));
Config::inst()->update('CMSPageEditController', 'treats_subsite_0_as_global', false); $this->assertFalse($l->shouldChangeSubsite('CMSPageEditController', 1, 1));
$this->assertTrue($l->shouldChangeSubsite('CMSPageEditController', 0, 5));
$this->assertFalse($l->shouldChangeSubsite('CMSPageEditController', 0, 0));
$this->assertTrue($l->shouldChangeSubsite('CMSPageEditController', 1, 5));
$this->assertFalse($l->shouldChangeSubsite('CMSPageEditController', 1, 1));
Config::inst()->update('CMSPageEditController', 'treats_subsite_0_as_global', true);
$this->assertFalse($l->shouldChangeSubsite('CMSPageEditController', 0, 5));
$this->assertFalse($l->shouldChangeSubsite('CMSPageEditController', 0, 0));
$this->assertTrue($l->shouldChangeSubsite('CMSPageEditController', 1, 5));
$this->assertFalse($l->shouldChangeSubsite('CMSPageEditController', 1, 1));
Config::inst()->unnest();
}
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() { public function testEachSubsiteHasAUniqueSiteConfig()
$subsite1 = $this->objFromFixture('Subsite', 'domaintest1'); {
$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';
static $fixture_file = 'subsites/tests/SubsiteTest.yml'; protected $extraDataObjects = array(
'SiteTreeSubsitesTest_ClassA',
'SiteTreeSubsitesTest_ClassB'
);
protected $extraDataObjects = array( protected $illegalExtensions = array(
'SiteTreeSubsitesTest_ClassA', 'SiteTree' => array('Translatable')
'SiteTreeSubsitesTest_ClassB' );
);
protected $illegalExtensions = array( public function testPagesInDifferentSubsitesCanShareURLSegment()
'SiteTree' => array('Translatable') {
); $subsiteMain = $this->objFromFixture('Subsite', 'main');
$subsite1 = $this->objFromFixture('Subsite', 'subsite1');
function testPagesInDifferentSubsitesCanShareURLSegment() { $pageMain = new SiteTree();
$subsiteMain = $this->objFromFixture('Subsite', 'main'); $pageMain->URLSegment = 'testpage';
$subsite1 = $this->objFromFixture('Subsite', 'subsite1'); $pageMain->write();
$pageMain->publish('Stage', 'Live');
$pageMain = new SiteTree(); $pageMainOther = new SiteTree();
$pageMain->URLSegment = 'testpage'; $pageMainOther->URLSegment = 'testpage';
$pageMain->write(); $pageMainOther->write();
$pageMain->publish('Stage', 'Live'); $pageMainOther->publish('Stage', 'Live');
$pageMainOther = new SiteTree(); $this->assertNotEquals($pageMain->URLSegment, $pageMainOther->URLSegment,
$pageMainOther->URLSegment = 'testpage'; 'Pages in same subsite cant share the same URL'
$pageMainOther->write(); );
$pageMainOther->publish('Stage', 'Live');
$this->assertNotEquals($pageMain->URLSegment, $pageMainOther->URLSegment, Subsite::changeSubsite($subsite1->ID);
'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');
$pageSubsite1 = new SiteTree(); $this->assertEquals($pageMain->URLSegment, $pageSubsite1->URLSegment,
$pageSubsite1->URLSegment = 'testpage'; 'Pages in different subsites can share the same URL'
$pageSubsite1->write(); );
$pageSubsite1->publish('Stage', 'Live'); }
$this->assertEquals($pageMain->URLSegment, $pageSubsite1->URLSegment, public function testBasicSanity()
'Pages in different subsites can share the same URL' {
); $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 testBasicSanity() { public function testErrorPageLocations()
$this->assertTrue(singleton('SiteTree')->getSiteConfig() instanceof SiteConfig); {
// The following assert is breaking in Translatable. $subsite1 = $this->objFromFixture('Subsite', 'domaintest1');
$this->assertTrue(singleton('SiteTree')->getCMSFields() instanceof FieldList);
$this->assertTrue(singleton('SubsitesVirtualPage')->getCMSFields() instanceof FieldList);
$this->assertTrue(is_array(singleton('SiteTreeSubsites')->extraStatics()));
}
function testErrorPageLocations() { Subsite::changeSubsite($subsite1->ID);
$subsite1 = $this->objFromFixture('Subsite', 'domaintest1'); $path = ErrorPage::get_filepath_for_errorcode(500);
Subsite::changeSubsite($subsite1->ID); $static_path = Config::inst()->get('ErrorPage', 'static_filepath');
$path = ErrorPage::get_filepath_for_errorcode(500); $expected_path = $static_path . '/error-500-'.$subsite1->domain().'.html';
$this->assertEquals($expected_path, $path);
}
$static_path = Config::inst()->get('ErrorPage', 'static_filepath'); public function testCanEditSiteTree()
$expected_path = $static_path . '/error-500-'.$subsite1->domain().'.html'; {
$this->assertEquals($expected_path, $path); $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');
function testCanEditSiteTree() { // Cant pass member as arguments to canEdit() because of GroupSubsites
$admin = $this->objFromFixture('Member', 'admin'); Session::set("loggedInAs", $admin->ID);
$subsite1member = $this->objFromFixture('Member', 'subsite1member'); $this->assertTrue(
$subsite2member = $this->objFromFixture('Member', 'subsite2member'); (bool)$subsite1page->canEdit(),
$mainpage = $this->objFromFixture('Page', 'home'); 'Administrators can edit all subsites'
$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 // @todo: Workaround because GroupSubsites->augmentSQL() is relying on session state
Session::set("loggedInAs", $admin->ID); Subsite::changeSubsite($subsite1);
$this->assertTrue(
(bool)$subsite1page->canEdit(),
'Administrators can edit all subsites'
);
// @todo: Workaround because GroupSubsites->augmentSQL() is relying on session state Session::set("loggedInAs", $subsite1member->ID);
Subsite::changeSubsite($subsite1); $this->assertTrue(
(bool)$subsite1page->canEdit(),
'Members can edit pages on a subsite if they are in a group belonging to this subsite'
);
Session::set("loggedInAs", $subsite1member->ID); Session::set("loggedInAs", $subsite2member->ID);
$this->assertTrue( $this->assertFalse(
(bool)$subsite1page->canEdit(), (bool)$subsite1page->canEdit(),
'Members can edit pages on a subsite if they are in a group belonging to this subsite' 'Members cant edit pages on a subsite if they are not in a group belonging to this subsite'
); );
Session::set("loggedInAs", $subsite2member->ID); // @todo: Workaround because GroupSubsites->augmentSQL() is relying on session state
$this->assertFalse( Subsite::changeSubsite(0);
(bool)$subsite1page->canEdit(), $this->assertFalse(
'Members cant edit pages on a subsite if they are not in a group belonging to this subsite' $mainpage->canEdit(),
); 'Members cant edit pages on the main site if they are not in a group allowing this'
);
}
// @todo: Workaround because GroupSubsites->augmentSQL() is relying on session state /**
Subsite::changeSubsite(0); * Similar to {@link SubsitesVirtualPageTest->testSubsiteVirtualPageCanHaveSameUrlsegmentAsOtherSubsite()}.
$this->assertFalse( */
$mainpage->canEdit(), public function testTwoPagesWithSameURLOnDifferentSubsites()
'Members cant edit pages on the main site if they are not in a group allowing this' {
); // 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();
* Similar to {@link SubsitesVirtualPageTest->testSubsiteVirtualPageCanHaveSameUrlsegmentAsOtherSubsite()}. $p1->Title = $p1->URLSegment = "test-page";
*/ $p1->SubsiteID = $s1->ID;
function testTwoPagesWithSameURLOnDifferentSubsites() { $p1->write();
// 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(); $p2 = new SiteTree();
$p1->Title = $p1->URLSegment = "test-page"; $p2->Title = $p1->URLSegment = "test-page";
$p1->SubsiteID = $s1->ID; $p2->SubsiteID = $s2->ID;
$p1->write(); $p2->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 the URLs weren't modified in our set-up // Check that if we switch between the different subsites, we receive the correct pages
$this->assertEquals($p1->URLSegment, 'test-page'); Subsite::changeSubsite($s1);
$this->assertEquals($p2->URLSegment, 'test-page'); $this->assertEquals($p1->ID, SiteTree::get_by_link('test-page')->ID);
// Check that if we switch between the different subsites, we receive the correct pages Subsite::changeSubsite($s2);
Subsite::changeSubsite($s1); $this->assertEquals($p2->ID, SiteTree::get_by_link('test-page')->ID);
$this->assertEquals($p1->ID, SiteTree::get_by_link('test-page')->ID); }
Subsite::changeSubsite($s2); public function testPageTypesBlacklistInClassDropdown()
$this->assertEquals($p2->ID, SiteTree::get_by_link('test-page')->ID); {
} $editor = $this->objFromFixture('Member', 'editor');
Session::set("loggedInAs", $editor->ID);
function testPageTypesBlacklistInClassDropdown() { $s1 = $this->objFromFixture('Subsite', 'domaintest1');
$editor = $this->objFromFixture('Member', 'editor'); $s2 = $this->objFromFixture('Subsite', 'domaintest2');
Session::set("loggedInAs", $editor->ID); $page = singleton('SiteTree');
$s1 = $this->objFromFixture('Subsite','domaintest1'); $s1->PageTypeBlacklist = 'SiteTreeSubsitesTest_ClassA,ErrorPage';
$s2 = $this->objFromFixture('Subsite','domaintest2'); $s1->write();
$page = singleton('SiteTree');
$s1->PageTypeBlacklist = 'SiteTreeSubsitesTest_ClassA,ErrorPage'; Subsite::changeSubsite($s1);
$s1->write(); $settingsFields = $page->getSettingsFields()->dataFieldByName('ClassName')->getSource();
Subsite::changeSubsite($s1); $this->assertArrayNotHasKey('ErrorPage',
$settingsFields = $page->getSettingsFields()->dataFieldByName('ClassName')->getSource(); $settingsFields
);
$this->assertArrayNotHasKey('SiteTreeSubsitesTest_ClassA',
$settingsFields
);
$this->assertArrayHasKey('SiteTreeSubsitesTest_ClassB',
$settingsFields
);
$this->assertArrayNotHasKey('ErrorPage', Subsite::changeSubsite($s2);
$settingsFields $settingsFields = $page->getSettingsFields()->dataFieldByName('ClassName')->getSource();
); $this->assertArrayHasKey('ErrorPage',
$this->assertArrayNotHasKey('SiteTreeSubsitesTest_ClassA', $settingsFields
$settingsFields );
); $this->assertArrayHasKey('SiteTreeSubsitesTest_ClassA',
$this->assertArrayHasKey('SiteTreeSubsitesTest_ClassB', $settingsFields
$settingsFields );
); $this->assertArrayHasKey('SiteTreeSubsitesTest_ClassB',
$settingsFields
);
}
Subsite::changeSubsite($s2); public function testPageTypesBlacklistInCMSMain()
$settingsFields = $page->getSettingsFields()->dataFieldByName('ClassName')->getSource(); {
$this->assertArrayHasKey('ErrorPage', $editor = $this->objFromFixture('Member', 'editor');
$settingsFields Session::set("loggedInAs", $editor->ID);
);
$this->assertArrayHasKey('SiteTreeSubsitesTest_ClassA',
$settingsFields
);
$this->assertArrayHasKey('SiteTreeSubsitesTest_ClassB',
$settingsFields
);
}
function testPageTypesBlacklistInCMSMain() { $cmsmain = new CMSMain();
$editor = $this->objFromFixture('Member', 'editor');
Session::set("loggedInAs", $editor->ID);
$cmsmain = new CMSMain(); $s1 = $this->objFromFixture('Subsite', 'domaintest1');
$s2 = $this->objFromFixture('Subsite', 'domaintest2');
$s1 = $this->objFromFixture('Subsite','domaintest1'); $s1->PageTypeBlacklist = 'SiteTreeSubsitesTest_ClassA,ErrorPage';
$s2 = $this->objFromFixture('Subsite','domaintest2'); $s1->write();
$s1->PageTypeBlacklist = 'SiteTreeSubsitesTest_ClassA,ErrorPage'; Subsite::changeSubsite($s1);
$s1->write(); $hints = Convert::json2array($cmsmain->SiteTreeHints());
$classes = $hints['Root']['disallowedChildren'];
Subsite::changeSubsite($s1); $this->assertContains('ErrorPage', $classes);
$hints = Convert::json2array($cmsmain->SiteTreeHints()); $this->assertContains('SiteTreeSubsitesTest_ClassA', $classes);
$classes = $hints['Root']['disallowedChildren']; $this->assertNotContains('SiteTreeSubsitesTest_ClassB', $classes);
$this->assertContains('ErrorPage', $classes);
$this->assertContains('SiteTreeSubsitesTest_ClassA', $classes);
$this->assertNotContains('SiteTreeSubsitesTest_ClassB', $classes);
Subsite::changeSubsite($s2);
$hints = Convert::json2array($cmsmain->SiteTreeHints());
$classes = $hints['Root']['disallowedChildren'];
$this->assertNotContains('ErrorPage', $classes);
$this->assertNotContains('SiteTreeSubsitesTest_ClassA', $classes);
$this->assertNotContains('SiteTreeSubsitesTest_ClassB', $classes);
}
Subsite::changeSubsite($s2);
$hints = Convert::json2array($cmsmain->SiteTreeHints());
$classes = $hints['Root']['disallowedChildren'];
$this->assertNotContains('ErrorPage', $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->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');
} }
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'); $mainSubsitePage = $this->objFromFixture('Page', 'mainSubsitePage');
$subsite1Home = $this->objFromFixture('Page', 'subsite1_home'); $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) {
$ids[$subsite->ID] = true;
}
$this->assertArrayHasKey(0, $ids, "Main site accessible");
$this->assertArrayHasKey($this->idFromFixture('Subsite','main'), $ids, "Site with no groups inaccesible");
$this->assertArrayHasKey($this->idFromFixture('Subsite','subsite1'), $ids, "Subsite1 Template inaccessible");
$this->assertArrayHasKey($this->idFromFixture('Subsite','subsite2'), $ids, "Subsite2 Template inaccessible");
}
$cmsMain = new CMSMain();
foreach ($cmsMain->Subsites() as $subsite) {
$ids[$subsite->ID] = true;
}
$this->assertArrayHasKey(0, $ids, "Main site accessible");
$this->assertArrayHasKey($this->idFromFixture('Subsite', 'main'), $ids, "Site with no groups inaccesible");
$this->assertArrayHasKey($this->idFromFixture('Subsite', 'subsite1'), $ids, "Subsite1 Template inaccessible");
$this->assertArrayHasKey($this->idFromFixture('Subsite', 'subsite2'), $ids, "Subsite2 Template inaccessible");
}
} }

View File

@ -1,362 +1,380 @@
<?php <?php
class SubsiteTest extends BaseSubsiteTest { class SubsiteTest extends BaseSubsiteTest
{
public static $fixture_file = 'subsites/tests/SubsiteTest.yml';
static $fixture_file = 'subsites/tests/SubsiteTest.yml'; public function setUp()
{
parent::setUp();
function setUp() { $this->origStrictSubdomainMatching = Subsite::$strict_subdomain_matching;
parent::setUp(); Subsite::$strict_subdomain_matching = false;
}
$this->origStrictSubdomainMatching = Subsite::$strict_subdomain_matching; public function tearDown()
Subsite::$strict_subdomain_matching = false; {
} parent::tearDown();
function tearDown() { Subsite::$strict_subdomain_matching = $this->origStrictSubdomainMatching;
parent::tearDown(); }
Subsite::$strict_subdomain_matching = $this->origStrictSubdomainMatching; /**
} * Create a new subsite from the template and verify that all the template's pages are copied
*/
public function testSubsiteCreation()
{
Subsite::$write_hostmap = false;
/** // Create the instance
* Create a new subsite from the template and verify that all the template's pages are copied $template = $this->objFromFixture('Subsite', 'main');
*/
function testSubsiteCreation() {
Subsite::$write_hostmap = false;
// Create the instance // Test that changeSubsite is working
$template = $this->objFromFixture('Subsite', 'main'); Subsite::changeSubsite($template->ID);
$tmplStaff = $this->objFromFixture('Page', 'staff');
$tmplHome = DataObject::get_one('Page', "\"URLSegment\" = 'home'");
// Test that changeSubsite is working // Publish all the pages in the template, testing that DataObject::get only returns pages from the chosen subsite
Subsite::changeSubsite($template->ID); $pages = DataObject::get("SiteTree");
$tmplStaff = $this->objFromFixture('Page','staff'); $totalPages = $pages->Count();
$tmplHome = DataObject::get_one('Page', "\"URLSegment\" = 'home'"); foreach ($pages as $page) {
$this->assertEquals($template->ID, $page->SubsiteID);
$page->publish('Stage', 'Live');
}
// Publish all the pages in the template, testing that DataObject::get only returns pages from the chosen subsite // Create a new site
$pages = DataObject::get("SiteTree"); $subsite = $template->duplicate();
$totalPages = $pages->Count();
foreach($pages as $page) {
$this->assertEquals($template->ID, $page->SubsiteID);
$page->publish('Stage', 'Live');
}
// Create a new site // Check title
$subsite = $template->duplicate(); $this->assertEquals($subsite->Title, $template->Title);
// Check title // Another test that changeSubsite is working
$this->assertEquals($subsite->Title, $template->Title); $subsite->activate();
// Another test that changeSubsite is working $siteHome = DataObject::get_one('Page', "\"URLSegment\" = 'home'");
$subsite->activate(); $this->assertNotEquals($siteHome, false, 'Home Page for subsite not found');
$this->assertEquals($subsite->ID, $siteHome->SubsiteID,
'createInstance() copies existing pages retaining the same URLSegment'
);
$siteHome = DataObject::get_one('Page', "\"URLSegment\" = 'home'"); Subsite::changeSubsite(0);
$this->assertNotEquals($siteHome, false, 'Home Page for subsite not found'); }
$this->assertEquals($subsite->ID, $siteHome->SubsiteID,
'createInstance() copies existing pages retaining the same URLSegment'
);
Subsite::changeSubsite(0); /**
} * 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
* Confirm that domain lookup is working $subsite1 = $this->createSubsiteWithDomains(array(
*/ 'one.example.org' => true,
function testDomainLookup() { 'one.*' => false,
// Clear existing fixtures ));
foreach(DataObject::get('Subsite') as $subsite) $subsite->delete(); $subsite2 = $this->createSubsiteWithDomains(array(
foreach(DataObject::get('SubsiteDomain') as $domain) $domain->delete(); '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,
));
// Much more expressive than YML in this case $this->assertEquals(
$subsite1 = $this->createSubsiteWithDomains(array( $subsite3->ID,
'one.example.org' => true, Subsite::getSubsiteIDForDomain('subdomain.unique.com'),
'one.*' => false, 'Full unique match'
)); );
$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( $this->assertEquals(
$subsite3->ID, $subsite1->ID,
Subsite::getSubsiteIDForDomain('subdomain.unique.com'), Subsite::getSubsiteIDForDomain('one.example.org'),
'Full unique match' 'Full match, doesn\'t complain about multiple matches within a single subsite'
); );
$this->assertEquals( $failed = false;
$subsite1->ID, try {
Subsite::getSubsiteIDForDomain('one.example.org'), Subsite::getSubsiteIDForDomain('subdomain.onmultiplesubsites.com');
'Full match, doesn\'t complain about multiple matches within a single subsite' } catch (UnexpectedValueException $e) {
); $failed = true;
}
$this->assertTrue(
$failed,
'Fails on multiple matches with wildcard vs. www across multiple subsites'
);
$failed = false; $this->assertEquals(
try { $subsite1->ID,
Subsite::getSubsiteIDForDomain('subdomain.onmultiplesubsites.com'); Subsite::getSubsiteIDForDomain('one.unique.com'),
} catch(UnexpectedValueException $e) { 'Fuzzy match suffixed with wildcard (rule "one.*")'
$failed = true; );
}
$this->assertTrue(
$failed,
'Fails on multiple matches with wildcard vs. www across multiple subsites'
);
$this->assertEquals( $this->assertEquals(
$subsite1->ID, $subsite2->ID,
Subsite::getSubsiteIDForDomain('one.unique.com'), Subsite::getSubsiteIDForDomain('two.mysite.com'),
'Fuzzy match suffixed with wildcard (rule "one.*")' 'Matches correct subsite for rule'
); );
$this->assertEquals( $this->assertEquals(
$subsite2->ID, $subsite2->ID,
Subsite::getSubsiteIDForDomain('two.mysite.com'), Subsite::getSubsiteIDForDomain('other.mysite.com'),
'Matches correct subsite for rule' 'Fuzzy match prefixed with wildcard (rule "*.mysite.com")'
); );
$this->assertEquals( $this->assertEquals(
$subsite2->ID, 0,
Subsite::getSubsiteIDForDomain('other.mysite.com'), Subsite::getSubsiteIDForDomain('unknown.madeup.com'),
'Fuzzy match prefixed with wildcard (rule "*.mysite.com")' "Doesn't match unknown subsite"
); );
}
$this->assertEquals( public function testStrictSubdomainMatching()
0, {
Subsite::getSubsiteIDForDomain('unknown.madeup.com'), // Clear existing fixtures
"Doesn't match unknown subsite" 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(
'example.org' => true,
'example.com' => false,
'*.wildcard.com' => false,
));
$subsite2 = $this->createSubsiteWithDomains(array(
'www.example.org' => true,
'www.wildcard.com' => false,
));
function testStrictSubdomainMatching() { Subsite::$strict_subdomain_matching = false;
// 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 $this->assertEquals(
$subsite1 = $this->createSubsiteWithDomains(array( $subsite1->ID,
'example.org' => true, Subsite::getSubsiteIDForDomain('example.org'),
'example.com' => false, 'Exact matches without strict checking when not using www prefix'
'*.wildcard.com' => false, );
)); $this->assertEquals(
$subsite2 = $this->createSubsiteWithDomains(array( $subsite1->ID,
'www.example.org' => true, Subsite::getSubsiteIDForDomain('www.example.org'),
'www.wildcard.com' => false, '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 = false; Subsite::$strict_subdomain_matching = true;
$this->assertEquals( $this->assertEquals(
$subsite1->ID, $subsite1->ID,
Subsite::getSubsiteIDForDomain('example.org'), Subsite::getSubsiteIDForDomain('example.org'),
'Exact matches without strict checking when not using www prefix' 'Matches with strict checking when not using www prefix'
); );
$this->assertEquals( $this->assertEquals(
$subsite1->ID, $subsite2->ID, // not 1
Subsite::getSubsiteIDForDomain('www.example.org'), Subsite::getSubsiteIDForDomain('www.example.org'),
'Matches without strict checking when using www prefix, still matching first domain regardless of www prefix (falling back to subsite primary key ordering)' 'Matches with strict checking when using www prefix'
); );
$this->assertEquals( $this->assertEquals(
$subsite1->ID, 0,
Subsite::getSubsiteIDForDomain('www.example.com'), Subsite::getSubsiteIDForDomain('www.example.com'),
'Fuzzy matches without strict checking with www prefix' 'Doesn\'t fuzzy match with strict checking when using www prefix'
); );
$this->assertEquals( $failed = false;
0, try {
Subsite::getSubsiteIDForDomain('www.wildcard.com'), Subsite::getSubsiteIDForDomain('www.wildcard.com');
'Doesn\'t match www prefix without strict check, even if a wildcard subdomain is in place' } catch (UnexpectedValueException $e) {
); $failed = true;
}
$this->assertTrue(
$failed,
'Fails on multiple matches with strict checking and wildcard vs. www'
);
}
Subsite::$strict_subdomain_matching = true; 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();
}
$this->assertEquals( return $subsite;
$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'
);
} /**
* Test the Subsite->domain() method
*/
public function testDefaultDomain()
{
$this->assertEquals('one.example.org',
$this->objFromFixture('Subsite', 'domaintest1')->domain());
protected function createSubsiteWithDomains($domains) { $this->assertEquals('two.mysite.com',
$subsite = new Subsite(array( $this->objFromFixture('Subsite', 'domaintest2')->domain());
'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; $originalHTTPHost = $_SERVER['HTTP_HOST'];
}
/** $_SERVER['HTTP_HOST'] = "www.example.org";
* Test the Subsite->domain() method $this->assertEquals('three.example.org',
*/ $this->objFromFixture('Subsite', 'domaintest3')->domain());
function testDefaultDomain() {
$this->assertEquals('one.example.org',
$this->objFromFixture('Subsite','domaintest1')->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']; $this->assertEquals($_SERVER['HTTP_HOST'], singleton('Subsite')->PrimaryDomain);
$this->assertEquals('http://'.$_SERVER['HTTP_HOST'].Director::baseURL(), singleton('Subsite')->absoluteBaseURL());
$_SERVER['HTTP_HOST'] = "www.example.org"; $_SERVER['HTTP_HOST'] = $originalHTTPHost;
$this->assertEquals('three.example.org', }
$this->objFromFixture('Subsite','domaintest3')->domain());
$_SERVER['HTTP_HOST'] = "mysite.example.org"; public function testAllSites()
$this->assertEquals('three.mysite.example.org', {
$this->objFromFixture('Subsite','domaintest3')->domain()); $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');
}
$this->assertEquals($_SERVER['HTTP_HOST'], singleton('Subsite')->PrimaryDomain); public function testAllAccessibleSites()
$this->assertEquals('http://'.$_SERVER['HTTP_HOST'].Director::baseURL(), singleton('Subsite')->absoluteBaseURL()); {
$member = $this->objFromFixture('Member', 'subsite1member');
$_SERVER['HTTP_HOST'] = $originalHTTPHost; $subsites = Subsite::all_accessible_sites(true, 'Main site', $member);
} $this->assertDOSEquals(array(
array('Title' =>'Subsite1 Template')
), $subsites, 'Lists member-accessible sites.');
}
function testAllSites() { /**
$subsites = Subsite::all_sites(); * Test Subsite::accessible_sites()
$this->assertDOSEquals(array( */
array('Title' =>'Main site'), public function testAccessibleSites()
array('Title' =>'Template'), {
array('Title' =>'Subsite1 Template'), $member1Sites = Subsite::accessible_sites("CMS_ACCESS_CMSMain", false, null,
array('Title' =>'Subsite2 Template'), $this->objFromFixture('Member', 'subsite1member'));
array('Title' =>'Test 1'), $member1SiteTitles = $member1Sites->column("Title");
array('Title' =>'Test 2'), sort($member1SiteTitles);
array('Title' =>'Test 3') $this->assertEquals('Subsite1 Template', $member1SiteTitles[0], 'Member can get to a subsite via a group');
), $subsites, 'Lists all subsites');
}
function testAllAccessibleSites() { $adminSites = Subsite::accessible_sites("CMS_ACCESS_CMSMain", false, null,
$member = $this->objFromFixture('Member', 'subsite1member'); $this->objFromFixture('Member', 'admin'));
$adminSiteTitles = $adminSites->column("Title");
sort($adminSiteTitles);
$this->assertEquals(array(
'Subsite1 Template',
'Subsite2 Template',
'Template',
'Test 1',
'Test 2',
'Test 3',
), $adminSiteTitles);
$subsites = Subsite::all_accessible_sites(true, 'Main site', $member); $member2Sites = Subsite::accessible_sites("CMS_ACCESS_CMSMain", false, null,
$this->assertDOSEquals(array( $this->objFromFixture('Member', 'subsite1member2'));
array('Title' =>'Subsite1 Template') $member2SiteTitles = $member2Sites->column("Title");
), $subsites, 'Lists member-accessible sites.'); sort($member2SiteTitles);
} $this->assertEquals('Subsite1 Template', $member2SiteTitles[0], 'Member can get to subsite via a group role');
}
/** public function testhasMainSitePermission()
* Test Subsite::accessible_sites() {
*/ $admin = $this->objFromFixture('Member', 'admin');
function testAccessibleSites() { $subsite1member = $this->objFromFixture('Member', 'subsite1member');
$member1Sites = Subsite::accessible_sites("CMS_ACCESS_CMSMain", false, null, $subsite1admin = $this->objFromFixture('Member', 'subsite1admin');
$this->objFromFixture('Member', 'subsite1member')); $allsubsitesauthor = $this->objFromFixture('Member', 'allsubsitesauthor');
$member1SiteTitles = $member1Sites->column("Title");
sort($member1SiteTitles);
$this->assertEquals('Subsite1 Template', $member1SiteTitles[0], 'Member can get to a subsite via a group');
$adminSites = Subsite::accessible_sites("CMS_ACCESS_CMSMain", false, null, $this->assertTrue(
$this->objFromFixture('Member', 'admin')); Subsite::hasMainSitePermission($admin),
$adminSiteTitles = $adminSites->column("Title"); 'Default permissions granted for super-admin'
sort($adminSiteTitles); );
$this->assertEquals(array( $this->assertTrue(
'Subsite1 Template', Subsite::hasMainSitePermission($admin, array("ADMIN")),
'Subsite2 Template', 'ADMIN permissions granted for super-admin'
'Template', );
'Test 1', $this->assertFalse(
'Test 2', Subsite::hasMainSitePermission($subsite1admin, array("ADMIN")),
'Test 3', 'ADMIN permissions (on main site) denied for subsite1 admin'
), $adminSiteTitles); );
$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';
$page1->write();
$page1->doPublish();
$this->assertEquals($page1->SubsiteID, $subsite1->ID);
function testhasMainSitePermission() { // duplicate
$admin = $this->objFromFixture('Member', 'admin'); $subsite2 = $subsite1->duplicate();
$subsite1member = $this->objFromFixture('Member', 'subsite1member'); $subsite2->activate();
$subsite1admin = $this->objFromFixture('Member', 'subsite1admin'); // change content on dupe
$allsubsitesauthor = $this->objFromFixture('Member', 'allsubsitesauthor'); $page2 = DataObject::get_one('Page', "\"Title\" = 'MyAwesomePage'");
$page2->Title = 'MyNewAwesomePage';
$page2->write();
$page2->doPublish();
$this->assertTrue( // check change & check change has not affected subiste1
Subsite::hasMainSitePermission($admin), $subsite1->activate();
'Default permissions granted for super-admin' $this->assertEquals('MyAwesomePage', DataObject::get_by_id('Page', $page1->ID)->Title);
); $subsite2->activate();
$this->assertTrue( $this->assertEquals('MyNewAwesomePage', DataObject::get_by_id('Page', $page2->ID)->Title);
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'
);
}
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() { public function setUp()
parent::setUp(); {
$this->logInWithPermission('ADMIN'); parent::setUp();
$this->logInWithPermission('ADMIN');
$fh = fopen(Director::baseFolder() . '/assets/testscript-test-file.pdf', "w"); $fh = fopen(Director::baseFolder() . '/assets/testscript-test-file.pdf', "w");
fwrite($fh, str_repeat('x',1000000)); fwrite($fh, str_repeat('x', 1000000));
fclose($fh); 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 // Attempt to bring main:linky to subsite2:linky
function testVirtualPageFromAnotherSubsite() { public function testVirtualPageFromAnotherSubsite()
Subsite::$write_hostmap = false; {
Subsite::$write_hostmap = false;
$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;
$linky = $this->objFromFixture('Page', 'linky'); $linky = $this->objFromFixture('Page', 'linky');
$svp = new SubsitesVirtualPage(); $svp = new SubsitesVirtualPage();
$svp->CopyContentFromID = $linky->ID; $svp->CopyContentFromID = $linky->ID;
$svp->SubsiteID = $subsite->ID; $svp->SubsiteID = $subsite->ID;
$svp->URLSegment = 'linky'; $svp->URLSegment = 'linky';
$svp->write(); $svp->write();
$this->assertEquals($svp->SubsiteID, $subsite->ID); $this->assertEquals($svp->SubsiteID, $subsite->ID);
$this->assertEquals($svp->Title, $linky->Title); $this->assertEquals($svp->Title, $linky->Title);
} }
function testFileLinkRewritingOnVirtualPages() { 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 // Go to main site, get parent page
$subsite = $this->objFromFixture('Subsite', 'main'); $subsite = $this->objFromFixture('Subsite', 'main');
Subsite::changeSubsite($subsite->ID); Subsite::changeSubsite($subsite->ID);
$page = $this->objFromFixture('Page', 'importantpage'); $page = $this->objFromFixture('Page', 'importantpage');
// Create two SVPs on other subsites // Create two SVPs on other subsites
$subsite = $this->objFromFixture('Subsite', 'subsite1'); $subsite = $this->objFromFixture('Subsite', 'subsite1');
Subsite::changeSubsite($subsite->ID); Subsite::changeSubsite($subsite->ID);
$vp1 = new SubsitesVirtualPage(); $vp1 = new SubsitesVirtualPage();
$vp1->CopyContentFromID = $page->ID; $vp1->CopyContentFromID = $page->ID;
$vp1->write(); $vp1->write();
$vp1->doPublish(); $vp1->doPublish();
$subsite = $this->objFromFixture('Subsite', 'subsite2'); $subsite = $this->objFromFixture('Subsite', 'subsite2');
Subsite::changeSubsite($subsite->ID); Subsite::changeSubsite($subsite->ID);
$vp2 = new SubsitesVirtualPage(); $vp2 = new SubsitesVirtualPage();
$vp2->CopyContentFromID = $page->ID; $vp2->CopyContentFromID = $page->ID;
$vp2->write(); $vp2->write();
$vp2->doPublish(); $vp2->doPublish();
// Switch back to main site, unpublish source // Switch back to main site, unpublish source
$subsite = $this->objFromFixture('Subsite', 'main'); $subsite = $this->objFromFixture('Subsite', 'main');
Subsite::changeSubsite($subsite->ID); Subsite::changeSubsite($subsite->ID);
$page = $this->objFromFixture('Page', 'importantpage'); $page = $this->objFromFixture('Page', 'importantpage');
$page->doUnpublish(); $page->doUnpublish();
Subsite::changeSubsite($vp1->SubsiteID); Subsite::changeSubsite($vp1->SubsiteID);
$onLive = Versioned::get_one_by_stage('SubsitesVirtualPage', 'Live', "\"SiteTree_Live\".\"ID\" = ".$vp1->ID); $onLive = Versioned::get_one_by_stage('SubsitesVirtualPage', 'Live', "\"SiteTree_Live\".\"ID\" = ".$vp1->ID);
$this->assertNull($onLive, 'SVP has been removed from live'); $this->assertNull($onLive, 'SVP has been removed from live');
$subsite = $this->objFromFixture('Subsite', 'subsite2'); $subsite = $this->objFromFixture('Subsite', 'subsite2');
Subsite::changeSubsite($vp2->SubsiteID); Subsite::changeSubsite($vp2->SubsiteID);
$onLive = Versioned::get_one_by_stage('SubsitesVirtualPage', 'Live', "\"SiteTree_Live\".\"ID\" = ".$vp2->ID); $onLive = Versioned::get_one_by_stage('SubsitesVirtualPage', 'Live', "\"SiteTree_Live\".\"ID\" = ".$vp2->ID);
$this->assertNull($onLive, 'SVP has been removed from live'); $this->assertNull($onLive, 'SVP has been removed from live');
} }
/** /**
* Similar to {@link SiteTreeSubsitesTest->testTwoPagesWithSameURLOnDifferentSubsites()} * Similar to {@link SiteTreeSubsitesTest->testTwoPagesWithSameURLOnDifferentSubsites()}
* and {@link SiteTreeSubsitesTest->testPagesInDifferentSubsitesCanShareURLSegment()}. * and {@link SiteTreeSubsitesTest->testPagesInDifferentSubsitesCanShareURLSegment()}.
*/ */
function testSubsiteVirtualPageCanHaveSameUrlsegmentAsOtherSubsite() { public function testSubsiteVirtualPageCanHaveSameUrlsegmentAsOtherSubsite()
Subsite::$write_hostmap = false; {
$subsite1 = $this->objFromFixture('Subsite', 'subsite1'); Subsite::$write_hostmap = false;
$subsite2 = $this->objFromFixture('Subsite', 'subsite2'); $subsite1 = $this->objFromFixture('Subsite', 'subsite1');
Subsite::changeSubsite($subsite1->ID); $subsite2 = $this->objFromFixture('Subsite', 'subsite2');
Subsite::changeSubsite($subsite1->ID);
$subsite1Page = $this->objFromFixture('Page', 'subsite1_staff'); $subsite1Page = $this->objFromFixture('Page', 'subsite1_staff');
$subsite1Page->URLSegment = 'staff'; $subsite1Page->URLSegment = 'staff';
$subsite1Page->write(); $subsite1Page->write();
// saving on subsite1, and linking to subsite1 // saving on subsite1, and linking to subsite1
$subsite1Vp = new SubsitesVirtualPage(); $subsite1Vp = new SubsitesVirtualPage();
$subsite1Vp->CopyContentFromID = $subsite1Page->ID; $subsite1Vp->CopyContentFromID = $subsite1Page->ID;
$subsite1Vp->SubsiteID = $subsite1->ID; $subsite1Vp->SubsiteID = $subsite1->ID;
$subsite1Vp->write(); $subsite1Vp->write();
$this->assertNotEquals( $this->assertNotEquals(
$subsite1Vp->URLSegment, $subsite1Vp->URLSegment,
$subsite1Page->URLSegment, $subsite1Page->URLSegment,
"Doesn't allow explicit URLSegment overrides when already existing in same subsite" "Doesn't allow explicit URLSegment overrides when already existing in same subsite"
); );
//Change to subsite 2 //Change to subsite 2
Subsite::changeSubsite($subsite2->ID); Subsite::changeSubsite($subsite2->ID);
// saving in subsite2 (which already has a page with URLSegment 'contact-us'), // saving in subsite2 (which already has a page with URLSegment 'contact-us'),
// but linking to a page in subsite1 // but linking to a page in subsite1
$subsite2Vp = new SubsitesVirtualPage(); $subsite2Vp = new SubsitesVirtualPage();
$subsite2Vp->CopyContentFromID = $subsite1Page->ID; $subsite2Vp->CopyContentFromID = $subsite1Page->ID;
$subsite2Vp->SubsiteID = $subsite2->ID; $subsite2Vp->SubsiteID = $subsite2->ID;
$subsite2Vp->write(); $subsite2Vp->write();
$this->assertEquals( $this->assertEquals(
$subsite2Vp->URLSegment, $subsite2Vp->URLSegment,
$subsite1Page->URLSegment, $subsite1Page->URLSegment,
"Does allow explicit URLSegment overrides when only existing in a different subsite" "Does allow explicit URLSegment overrides when only existing in a different subsite"
); );
} }
function fixVersionNumberCache($page) {
$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,25 +53,28 @@ class FeatureContext extends SilverStripeContext {
// Use blueprints to set user name from identifier // Use blueprints to set user name from identifier
$factory = $fixtureContext->getFixtureFactory(); $factory = $fixtureContext->getFixtureFactory();
$blueprint = \Injector::inst()->create('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']);
} }
} }
@ -76,15 +82,17 @@ class FeatureContext extends SilverStripeContext {
/** /**
* @return FixtureFactory * @return FixtureFactory
*/ */
public function getFixtureFactory() { public function getFixtureFactory()
if(!$this->fixtureFactory) { {
if (!$this->fixtureFactory) {
$this->fixtureFactory = \Injector::inst()->create('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;
} }