2007-08-18 13:38:11 +02:00
< ? php
/**
2010-03-01 03:49:35 +01:00
* A dynamically created subsite . SiteTree objects can now belong to a subsite .
* You can simulate subsite access without setting up virtual hosts by appending ? SubsiteID =< ID > to the request .
2009-05-04 07:03:44 +02:00
*
2008-11-24 05:56:47 +01:00
* @ package subsites
2007-08-18 13:38:11 +02:00
*/
2007-08-27 07:07:27 +02:00
class Subsite extends DataObject implements PermissionProvider {
2007-09-05 06:47:05 +02:00
2013-07-05 03:57:58 +02:00
/**
* @ var $use_session_subsiteid Boolean Set to TRUE when using the CMS and FALSE
* when browsing the frontend of a website .
*
* @ todo Remove flag once the Subsite CMS works without session state ,
* similarly to the Translatable module .
*/
public static $use_session_subsiteid = false ;
2008-11-24 05:56:47 +01:00
/**
* @ var boolean $disable_subsite_filter If enabled , bypasses the query decoration
* to limit DataObject :: get * () calls to a specific subsite . Useful for debugging .
*/
2013-05-06 12:59:18 +02:00
static $disable_subsite_filter = false ;
2010-03-01 22:56:24 +01:00
/**
* 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 .
*/
2013-05-06 12:59:18 +02:00
static $force_subsite = null ;
2009-05-04 07:03:44 +02:00
2013-05-06 12:59:18 +02:00
static $write_hostmap = true ;
2011-09-09 11:59:46 +02:00
2013-05-06 12:21:09 +02:00
private static $default_sort = " \" Title \" ASC " ;
2009-05-04 07:03:44 +02:00
2013-05-06 12:21:09 +02:00
private static $db = array (
2007-08-18 13:38:11 +02:00
'Title' => 'Varchar(255)' ,
'RedirectURL' => 'Varchar(255)' ,
'DefaultSite' => 'Boolean' ,
'Theme' => 'Varchar' ,
2010-03-01 04:00:14 +01:00
'Language' => 'Varchar(6)' ,
2010-03-01 03:48:45 +01:00
2008-11-24 05:56:47 +01:00
// Used to hide unfinished/private subsites from public view.
2010-03-01 04:00:14 +01:00
// If unset, will default to true
2011-08-30 18:58:36 +02:00
'IsPublic' => 'Boolean' ,
// Comma-separated list of disallowed page types
'PageTypeBlacklist' => 'Text' ,
2007-08-18 13:38:11 +02:00
);
2010-03-01 03:53:00 +01:00
2013-05-06 12:21:09 +02:00
private static $has_one = array (
2008-06-19 02:46:51 +02:00
);
2010-03-01 03:53:00 +01:00
2013-05-06 12:21:09 +02:00
private static $has_many = array (
2010-03-01 03:53:00 +01:00
'Domains' => 'SubsiteDomain' ,
);
2010-03-01 22:37:56 +01:00
2013-05-06 12:21:09 +02:00
private static $belongs_many_many = array (
2010-03-01 22:37:56 +01:00
" Groups " => " Group " ,
);
2009-05-04 07:03:44 +02:00
2013-05-06 12:21:09 +02:00
private static $defaults = array (
2010-08-23 02:30:34 +02:00
'IsPublic' => 1
2008-06-24 04:31:26 +02:00
);
2009-05-04 07:03:44 +02:00
2013-05-06 12:21:09 +02:00
private static $searchable_fields = array (
2013-10-30 13:43:59 +01:00
'Title' ,
'Domains.Domain' ,
'IsPublic' ,
2010-03-01 03:53:00 +01:00
);
2011-09-22 17:40:26 +02:00
/**
* Memory cache of accessible sites
*/
private static $_cache_accessible_sites = array ();
2010-03-01 03:53:00 +01:00
2012-08-15 11:11:22 +02:00
private static $_cache_subsite_for_domain = array ();
2008-11-24 05:56:47 +01:00
/**
* @ 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
* are listed .
*/
2013-05-06 12:44:19 +02:00
private static $allowed_themes = array ();
2011-09-09 11:59:46 +02:00
/**
* @ var Boolean If set to TRUE , don 't assume ' www . example . com ' and ' example . com ' are the same .
* Doesn 't affect wildcard matching, so ' *. example . com ' will match ' www . example . com ' (but not ' example . com ' )
* in both TRUE or FALSE setting .
*/
2013-05-06 12:59:18 +02:00
static $strict_subdomain_matching = false ;
2009-05-04 07:03:44 +02:00
2012-08-15 11:11:22 +02:00
/**
* @ var boolean Respects the IsPublic flag when retrieving subsites
*/
2013-05-06 12:59:18 +02:00
static $check_is_public = true ;
2012-08-15 11:11:22 +02:00
2008-04-22 08:53:35 +02:00
static function set_allowed_domains ( $domain ){
2010-03-01 03:48:45 +01:00
user_error ( 'Subsite::set_allowed_domains() is deprecated; it is no longer necessary '
. 'because users can now enter any domain name' , E_USER_NOTICE );
2008-11-24 05:56:47 +01:00
}
2009-05-04 07:03:44 +02:00
2008-06-24 04:31:26 +02:00
static function set_allowed_themes ( $themes ) {
self :: $allowed_themes = $themes ;
}
2009-05-04 07:03:44 +02:00
2008-11-20 00:25:43 +01:00
/**
* Return the themes that can be used with this subsite , as an array of themecode => description
*/
function allowedThemes () {
if ( $themes = $this -> stat ( 'allowed_themes' )) {
return ArrayLib :: valuekey ( $themes );
} else {
$themes = array ();
2009-02-17 02:08:57 +01:00
if ( is_dir ( '../themes/' )) {
foreach ( scandir ( '../themes/' ) as $theme ) {
if ( $theme [ 0 ] == '.' ) continue ;
$theme = strtok ( $theme , '_' );
$themes [ $theme ] = $theme ;
}
ksort ( $themes );
2008-11-20 00:25:43 +01:00
}
return $themes ;
}
}
2009-05-04 07:03:44 +02:00
2010-08-23 02:30:34 +02:00
public function getLanguage () {
if ( $this -> getField ( 'Language' )) {
return $this -> getField ( 'Language' );
} else {
return i18n :: get_locale ();
}
}
2013-07-10 16:15:34 +02:00
public function validate () {
$result = parent :: validate ();
if ( ! $this -> Title ) {
$result -> error ( _t ( 'Subsite.ValidateTitle' , 'Please add a "Title"' ));
}
return $result ;
}
2010-03-01 03:53:34 +01:00
/**
* Whenever a Subsite is written , rewrite the hostmap
*
* @ return void
*/
public function onAfterWrite () {
Subsite :: writeHostMap ();
2011-08-30 14:43:53 +02:00
parent :: onAfterWrite ();
2010-03-01 03:53:34 +01:00
}
2007-08-18 13:38:11 +02:00
/**
2011-09-09 12:09:10 +02:00
* Return the primary domain of this site . Tries to " normalize " the domain name ,
* by replacing potential wildcards .
*
2010-03-01 03:49:35 +01:00
* @ return string The full domain name of this subsite ( without protocol prefix )
2007-08-18 13:38:11 +02:00
*/
function domain () {
2010-03-01 03:48:45 +01:00
if ( $this -> ID ) {
2011-09-09 12:09:10 +02:00
$domains = DataObject :: get ( " SubsiteDomain " , " \" SubsiteID \" = $this->ID " , " \" IsPrimary \" DESC " , " " , 1 );
2012-03-25 18:35:01 +02:00
if ( $domains && $domains -> Count () > 0 ) {
2010-03-01 03:48:45 +01:00
$domain = $domains -> First () -> Domain ;
// If there are wildcards in the primary domain (not recommended), make some
2011-09-09 12:09:10 +02:00
// educated guesses about what to replace them with:
$domain = preg_replace ( '/\.\*$/' , " . $_SERVER[HTTP_HOST] " , $domain );
// Default to "subsite." prefix for first wildcard
// TODO Whats the significance of "subsite" in this context?!
$domain = preg_replace ( '/^\*\./' , " subsite. " , $domain );
// *Only* removes "intermediate" subdomains, so 'subdomain.www.domain.com' becomes 'subdomain.domain.com'
2010-03-01 03:48:45 +01:00
$domain = str_replace ( '.www.' , '.' , $domain );
2011-09-09 12:09:10 +02:00
2010-03-01 03:48:45 +01:00
return $domain ;
}
2010-03-01 03:53:15 +01:00
// SubsiteID = 0 is often used to refer to the main site, just return $_SERVER['HTTP_HOST']
} else {
return $_SERVER [ 'HTTP_HOST' ];
2010-03-01 03:48:45 +01:00
}
2007-08-18 13:38:11 +02:00
}
2010-03-01 03:53:00 +01:00
function getPrimaryDomain () {
return $this -> domain ();
}
2008-12-04 22:36:06 +01:00
function absoluteBaseURL () {
return " http:// " . $this -> domain () . Director :: baseURL ();
}
2009-05-04 07:03:44 +02:00
2008-11-24 05:56:47 +01:00
/**
* Show the configuration fields for each subsite
*/
2007-08-18 13:38:11 +02:00
function getCMSFields () {
2012-07-10 15:43:53 +02:00
if ( $this -> ID != 0 ) {
2013-10-30 13:43:59 +01:00
$domainTable = new GridField (
" Domains " ,
_t ( 'Subsite.DomainsListTitle' , " Domains " ),
$this -> Domains (),
GridFieldConfig_RecordEditor :: create ( 10 )
);
2012-07-10 15:43:53 +02:00
} else {
2013-10-30 13:43:59 +01:00
$domainTable = new LiteralField (
'Domains' ,
'<p>' . _t ( 'Subsite.DOMAINSAVEFIRST' , 'You can only add domains after saving for the first time' ) . '</p>'
);
2012-07-10 15:43:53 +02:00
}
2010-03-01 04:00:14 +01:00
2013-10-30 13:43:59 +01:00
$languageSelector = new DropdownField (
'Language' ,
$this -> fieldLabel ( 'Language' ),
i18n :: get_common_locales ()
);
2011-08-30 18:58:36 +02:00
$pageTypeMap = array ();
$pageTypes = SiteTree :: page_type_classes ();
foreach ( $pageTypes as $pageType ) {
$pageTypeMap [ $pageType ] = singleton ( $pageType ) -> i18n_singular_name ();
}
asort ( $pageTypeMap );
2010-03-01 03:48:45 +01:00
2012-03-25 18:35:01 +02:00
$fields = new FieldList (
2013-08-22 03:02:46 +02:00
$subsiteTabs = new TabSet ( 'Root' ,
2013-10-30 13:43:59 +01:00
new Tab (
'Configuration' ,
_t ( 'Subsite.TabTitleConfig' , 'Configuration' ),
2007-08-18 13:38:11 +02:00
new HeaderField ( $this -> getClassName () . ' configuration' , 2 ),
2013-10-30 13:43:59 +01:00
new TextField ( 'Title' , $this -> fieldLabel ( 'Title' ), $this -> Title ),
2010-03-01 03:48:45 +01:00
2013-10-30 13:43:59 +01:00
new HeaderField (
_t ( 'Subsite.DomainsHeadline' , " Domains for this subsite " )
),
2010-03-01 03:48:45 +01:00
$domainTable ,
2010-03-01 04:00:14 +01:00
$languageSelector ,
2007-08-18 13:38:11 +02:00
// new TextField('RedirectURL', 'Redirect to URL', $this->RedirectURL),
2013-10-30 13:43:59 +01:00
new CheckboxField ( 'DefaultSite' , $this -> fieldLabel ( 'DefaultSite' ), $this -> DefaultSite ),
new CheckboxField ( 'IsPublic' , $this -> fieldLabel ( 'IsPublic' ), $this -> IsPublic ),
2008-06-24 04:31:26 +02:00
2013-10-30 13:43:59 +01:00
new DropdownField ( 'Theme' , $this -> fieldLabel ( 'Theme' ), $this -> allowedThemes (), $this -> Theme ),
2011-08-30 18:58:36 +02:00
new LiteralField (
'PageTypeBlacklistToggle' ,
sprintf (
'<div class="field"><a href="#" id="PageTypeBlacklistToggle">%s</a></div>' ,
_t ( 'Subsite.PageTypeBlacklistField' , 'Disallow page types?' )
)
),
new CheckboxSetField (
'PageTypeBlacklist' ,
false ,
$pageTypeMap
)
2007-08-18 13:38:11 +02:00
)
),
new HiddenField ( 'ID' , '' , $this -> ID ),
new HiddenField ( 'IsSubsite' , '' , 1 )
);
2013-08-22 03:02:46 +02:00
$subsiteTabs -> addExtraClass ( 'subsite-model' );
2008-07-15 01:48:37 +02:00
$this -> extend ( 'updateCMSFields' , $fields );
2007-08-18 13:38:11 +02:00
return $fields ;
}
2009-05-04 07:03:44 +02:00
2013-10-30 13:43:59 +01:00
public function fieldLabels ( $includerelations = true ) {
$labels = parent :: fieldLabels ( $includerelations );
$labels [ 'Title' ] = _t ( 'Subsites.TitleFieldLabel' , 'Subsite Name' );
$labels [ 'RedirectURL' ] = _t ( 'Subsites.RedirectURLFieldLabel' , 'Redirect URL' );
$labels [ 'DefaultSite' ] = _t ( 'Subsites.DefaultSiteFieldLabel' , 'Default site' );
$labels [ 'Theme' ] = _t ( 'Subsites.ThemeFieldLabel' , 'Theme' );
$labels [ 'Language' ] = _t ( 'Subsites.LanguageFieldLabel' , 'Language' );
$labels [ 'IsPublic' ] = _t ( 'Subsites.IsPublicFieldLabel' , 'Enable public access' );
$labels [ 'PageTypeBlacklist' ] = _t ( 'Subsites.PageTypeBlacklistFieldLabel' , 'Page Type Blacklist' );
$labels [ 'Domains.Domain' ] = _t ( 'Subsites.DomainFieldLabel' , 'Domain' );
$labels [ 'PrimaryDomain' ] = _t ( 'Subsites.PrimaryDomainFieldLabel' , 'Primary Domain' );
return $labels ;
}
public function summaryFields () {
return array (
'Title' => $this -> fieldLabel ( 'Title' ),
'PrimaryDomain' => $this -> fieldLabel ( 'PrimaryDomain' ),
'IsPublic' => _t ( 'Subsite.IsPublicHeaderField' , 'Active subsite' ),
);
}
2008-11-24 05:56:47 +01:00
/**
* @ todo getClassName is redundant , already stored as a database field ?
*/
2007-08-18 13:38:11 +02:00
function getClassName () {
return $this -> class ;
}
2009-05-04 07:03:44 +02:00
2007-08-18 13:38:11 +02:00
function getCMSActions () {
2012-03-25 18:35:01 +02:00
return new FieldList (
2013-10-30 13:43:59 +01:00
new FormAction (
'callPageMethod' ,
_t ( 'Subsite.ButtonLabelCopy' , " Create copy " ),
null ,
'adminDuplicate'
)
2007-08-18 13:38:11 +02:00
);
}
2009-05-04 07:03:44 +02:00
2007-08-29 00:29:44 +02:00
function adminDuplicate () {
$newItem = $this -> duplicate ();
2013-10-30 13:43:59 +01:00
$message = _t (
'Subsite.CopyMessage' ,
'Created a copy of {title}' ,
array ( 'title' => Convert :: raw2js ( $this -> Title ))
);
2007-08-29 00:29:44 +02:00
return <<< JS
2013-10-30 13:43:59 +01:00
statusMessage ( $message , 'good' );
2007-08-29 00:29:44 +02:00
$ ( 'Form_EditForm' ) . loadURLFromServer ( 'admin/subsites/show/$newItem->ID' );
JS ;
}
2009-05-04 07:03:44 +02:00
2008-11-24 05:56:47 +01:00
/**
* Gets the subsite currently set in the session .
2009-05-04 07:03:44 +02:00
*
2008-11-24 05:56:47 +01:00
* @ uses ControllerSubsites -> controllerAugmentInit ()
2010-03-01 03:53:28 +01:00
*
2008-11-24 05:56:47 +01:00
* @ return Subsite
*/
2010-03-01 03:53:28 +01:00
static function currentSubsite () {
// get_by_id handles caching so we don't have to
return DataObject :: get_by_id ( 'Subsite' , self :: currentSubsiteID ());
2007-08-18 13:38:11 +02:00
}
2009-05-04 07:03:44 +02:00
2007-08-18 13:38:11 +02:00
/**
* 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
2008-11-24 05:56:47 +01:00
* directly from ModelAsController :: getNestedController . Only gets Subsite instances which have their
* { @ link IsPublic } flag set to TRUE .
2009-05-04 07:03:44 +02:00
*
2010-03-01 03:49:35 +01:00
* You can simulate subsite access without creating virtual hosts by appending ? SubsiteID =< ID > to the request .
2009-05-04 07:03:44 +02:00
*
2012-10-26 00:53:40 +02:00
* @ todo Pass $request object from controller so we don ' t have to rely on $_GET
2009-05-04 07:03:44 +02:00
*
2008-11-24 05:56:47 +01:00
* @ param boolean $cache
* @ return int ID of the current subsite instance
2007-08-18 13:38:11 +02:00
*/
2010-03-01 03:53:07 +01:00
static function currentSubsiteID () {
2013-07-05 03:57:58 +02:00
$id = NULL ;
if ( isset ( $_GET [ 'SubsiteID' ])) {
$id = ( int ) $_GET [ 'SubsiteID' ];
2013-10-16 02:25:06 +02:00
} else if ( Subsite :: $use_session_subsiteid ) {
2013-07-05 03:57:58 +02:00
$id = Session :: get ( 'SubsiteID' );
2013-10-16 02:25:06 +02:00
}
2009-05-04 07:03:44 +02:00
2010-03-01 03:56:58 +01:00
if ( $id === NULL ) {
2010-03-01 03:53:07 +01:00
$id = self :: getSubsiteIDForDomain ();
2008-11-30 23:31:08 +01:00
}
2009-05-04 07:03:44 +02:00
2007-08-18 13:38:11 +02:00
return ( int ) $id ;
}
2010-03-30 23:02:44 +02:00
2007-08-18 13:38:11 +02:00
/**
2013-07-05 03:57:58 +02:00
* 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 .
2009-05-04 07:03:44 +02:00
*
2008-11-24 07:35:51 +01:00
* @ param int | Subsite $subsite Either the ID of the subsite , or the subsite object itself
2007-08-18 13:38:11 +02:00
*/
static function changeSubsite ( $subsite ) {
2008-11-24 07:35:51 +01:00
if ( is_object ( $subsite )) $subsiteID = $subsite -> ID ;
2008-11-30 22:31:49 +01:00
else $subsiteID = $subsite ;
2010-03-01 03:56:58 +01:00
Session :: set ( 'SubsiteID' , ( int ) $subsiteID );
2010-03-01 04:00:14 +01:00
// Set locale
if ( is_object ( $subsite ) && $subsite -> Language != '' ) {
2013-04-30 05:15:11 +02:00
$locale = i18n :: get_locale_from_lang ( $subsite -> Language );
if ( $locale ) {
i18n :: set_locale ( $locale );
2010-03-01 04:00:14 +01:00
}
}
2011-04-11 00:32:51 +02:00
Permission :: flush_permission_cache ();
2007-08-18 13:38:11 +02:00
}
2009-05-04 07:03:44 +02:00
2007-08-31 02:29:25 +02:00
/**
* Make this subsite the current one
*/
public function activate () {
Subsite :: changeSubsite ( $this );
}
2009-05-04 07:03:44 +02:00
2008-11-24 05:56:47 +01:00
/**
* @ todo Possible security issue , don ' t grant edit permissions to everybody .
*/
2012-07-11 15:32:10 +02:00
function canEdit ( $member = false ) {
2007-08-18 13:38:11 +02:00
return true ;
}
2009-05-04 07:03:44 +02:00
2008-11-24 05:56:47 +01:00
/**
2010-03-01 03:48:45 +01:00
* Get a matching subsite for the given host , or for the current HTTP_HOST .
2011-09-09 10:50:52 +02:00
* 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 .
2010-03-01 03:48:45 +01:00
*
2011-09-09 10:50:52 +02:00
* @ param $host The host to find the subsite for . If not specified , $_SERVER [ 'HTTP_HOST' ] is used .
2008-11-24 05:56:47 +01:00
* @ return int Subsite ID
*/
2012-08-15 11:11:22 +02:00
static function getSubsiteIDForDomain ( $host = null , $checkPermissions = true ) {
2010-03-01 03:48:45 +01:00
if ( $host == null ) $host = $_SERVER [ 'HTTP_HOST' ];
2012-08-15 11:11:22 +02:00
2013-05-06 12:54:23 +02:00
if ( ! self :: $strict_subdomain_matching ) $host = preg_replace ( '/^www\./' , '' , $host );
2009-05-04 07:03:44 +02:00
2013-05-06 12:54:23 +02:00
$cacheKey = implode ( '_' , array ( $host , Member :: currentUserID (), self :: $check_is_public ));
2012-08-15 11:11:22 +02:00
if ( isset ( self :: $_cache_subsite_for_domain [ $cacheKey ])) return self :: $_cache_subsite_for_domain [ $cacheKey ];
$SQL_host = Convert :: raw2sql ( $host );
$joinFilter = self :: $check_is_public ? " AND \" Subsite \" . \" IsPublic \" =1 " : '' ;
$matchingDomains = DataObject :: get (
" SubsiteDomain " ,
" ' $SQL_host ' LIKE replace( \" SubsiteDomain \" . \" Domain \" ,'*','%') " ,
2013-01-03 17:38:51 +01:00
" \" IsPrimary \" DESC "
) -> innerJoin ( 'Subsite' , " \" Subsite \" . \" ID \" = \" SubsiteDomain \" . \" SubsiteID \" AND \" Subsite \" . \" IsPublic \" =1 " );;
2010-03-01 03:48:45 +01:00
2013-01-03 17:38:51 +01:00
if ( $matchingDomains && $matchingDomains -> Count ()) {
2010-03-01 03:48:45 +01:00
$subsiteIDs = array_unique ( $matchingDomains -> column ( 'SubsiteID' ));
2011-09-09 11:59:46 +02:00
$subsiteDomains = array_unique ( $matchingDomains -> column ( 'Domain' ));
if ( sizeof ( $subsiteIDs ) > 1 ) {
throw new UnexpectedValueException ( sprintf (
" Multiple subsites match on '%s': %s " ,
$host ,
implode ( ',' , $subsiteDomains )
));
}
2012-08-15 11:11:22 +02:00
$subsiteID = $subsiteIDs [ 0 ];
} else if ( $default = DataObject :: get_one ( 'Subsite' , " \" DefaultSite \" = 1 " )) {
// Check for a 'default' subsite
$subsiteID = $default -> ID ;
} else {
// Default subsite id = 0, the main site
$subsiteID = 0 ;
2007-08-18 13:38:11 +02:00
}
2010-03-01 03:56:58 +01:00
2012-08-15 11:11:22 +02:00
self :: $_cache_subsite_for_domain [ $cacheKey ] = $subsiteID ;
2010-03-01 22:49:22 +01:00
2012-08-15 11:11:22 +02:00
return $subsiteID ;
2007-08-18 13:38:11 +02:00
}
2009-05-04 07:03:44 +02:00
2007-08-18 13:38:11 +02:00
function getMembersByPermission ( $permissionCodes = array ( 'ADMIN' )){
if ( ! is_array ( $permissionCodes ))
user_error ( 'Permissions must be passed to Subsite::getMembersByPermission as an array' , E_USER_ERROR );
$SQL_permissionCodes = Convert :: raw2sql ( $permissionCodes );
2009-05-04 07:03:44 +02:00
2007-08-18 13:38:11 +02:00
$SQL_permissionCodes = join ( " ',' " , $SQL_permissionCodes );
2009-05-04 07:03:44 +02:00
2008-11-24 07:35:51 +01:00
return DataObject :: get (
2009-05-04 07:03:44 +02:00
'Member' ,
2010-03-09 02:09:25 +01:00
" \" Group \" . \" SubsiteID \" = $this->ID AND \" Permission \" . \" Code \" IN (' $SQL_permissionCodes ') " ,
2009-05-04 07:03:44 +02:00
'' ,
2010-03-09 02:09:25 +01:00
" LEFT JOIN \" Group_Members \" ON \" Member \" . \" ID \" = \" Group_Members \" . \" MemberID \"
LEFT JOIN \ " Group \" ON \" Group \" . \" ID \" = \" Group_Members \" . \" GroupID \"
LEFT JOIN \ " Permission \" ON \" Permission \" . \" GroupID \" = \" Group \" . \" ID \" "
2008-11-24 07:35:51 +01:00
);
2009-10-29 02:40:46 +01:00
2007-08-18 13:38:11 +02:00
}
2009-05-04 07:03:44 +02:00
2011-09-05 17:24:48 +02:00
/**
* 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
*/
2007-08-18 13:38:11 +02:00
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 );
2012-07-11 15:32:10 +02:00
if ( ! $member && $member !== FALSE ) $member = Member :: currentUser ();
2009-05-04 07:03:44 +02:00
2008-12-03 23:31:54 +01:00
if ( ! $member ) return false ;
2010-03-01 22:27:41 +01:00
if ( ! in_array ( " ADMIN " , $permissionCodes )) $permissionCodes [] = " ADMIN " ;
2008-12-01 03:43:57 +01:00
2007-08-18 13:38:11 +02:00
$SQLa_perm = Convert :: raw2sql ( $permissionCodes );
2009-05-04 07:03:44 +02:00
$SQL_perms = join ( " ',' " , $SQLa_perm );
2007-08-18 13:38:11 +02:00
$memberID = ( int ) $member -> ID ;
2010-03-01 22:27:41 +01:00
2011-10-05 04:02:57 +02:00
// Count this user's groups which can access the main site
2009-11-02 02:59:15 +01:00
$groupCount = DB :: query ( "
2010-03-01 22:54:36 +01:00
SELECT COUNT ( \ " Permission \" . \" ID \" )
FROM \ " Permission \"
2010-03-01 23:05:55 +01:00
INNER JOIN \ " Group \" ON \" Group \" . \" ID \" = \" Permission \" . \" GroupID \" AND \" Group \" . \" AccessAllSubsites \" = 1
2010-03-09 02:42:56 +01:00
INNER JOIN \ " Group_Members \" ON \" Group_Members \" . \" GroupID \" = \" Permission \" . \" GroupID \"
WHERE \ " Permission \" . \" Code \" IN (' $SQL_perms ')
2010-03-01 22:54:36 +01:00
AND \ " MemberID \" = { $memberID }
2009-11-02 02:59:15 +01:00
" )->value();
2010-03-01 23:05:55 +01:00
2011-10-05 04:02:57 +02:00
// Count this user's groups which have a role that can access the main site
$roleCount = DB :: query ( "
SELECT COUNT ( \ " PermissionRoleCode \" . \" ID \" )
FROM \ " Group \"
INNER JOIN \ " Group_Members \" ON \" Group_Members \" . \" GroupID \" = \" Group \" . \" ID \"
INNER JOIN \ " Group_Roles \" ON \" Group_Roles \" . \" GroupID \" = \" Group \" . \" ID \"
INNER JOIN \ " PermissionRole \" ON \" Group_Roles \" . \" PermissionRoleID \" = \" PermissionRole \" . \" ID \"
INNER JOIN \ " PermissionRoleCode \" ON \" PermissionRole \" . \" ID \" = \" PermissionRoleCode \" . \" RoleID \"
WHERE \ " PermissionRoleCode \" . \" Code \" IN (' $SQL_perms ')
AND \ " Group \" . \" AccessAllSubsites \" = 1
AND \ " MemberID \" = { $memberID }
" )->value();
2010-03-01 23:05:55 +01:00
2011-10-05 04:02:57 +02:00
// There has to be at least one that allows access.
return ( $groupCount + $roleCount > 0 );
2008-11-26 04:43:12 +01:00
}
2009-05-04 07:03:44 +02:00
2007-08-29 00:29:44 +02:00
/**
* Duplicate this subsite
*/
2012-07-11 15:32:10 +02:00
function duplicate ( $doWrite = true ) {
2013-01-03 17:38:51 +01:00
$duplicate = parent :: duplicate ( $doWrite );
2009-05-04 07:03:44 +02:00
2007-08-29 00:29:44 +02:00
$oldSubsiteID = Session :: get ( 'SubsiteID' );
self :: changeSubsite ( $this -> ID );
2009-05-04 07:03:44 +02:00
2007-08-29 00:29:44 +02:00
/*
2012-08-07 17:37:44 +02:00
* Copy data from this object to the given subsite . Does this using an iterative depth - first search .
2007-08-29 00:29:44 +02:00
* 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 ));
2009-05-04 07:03:44 +02:00
while ( count ( $stack ) > 0 ) {
2007-08-29 00:29:44 +02:00
list ( $sourceParentID , $destParentID ) = array_pop ( $stack );
2010-03-09 02:09:25 +01:00
$children = Versioned :: get_by_stage ( 'Page' , 'Live' , " \" ParentID \" = $sourceParentID " , '' );
2009-05-04 07:03:44 +02:00
2007-08-29 00:29:44 +02:00
if ( $children ) {
foreach ( $children as $child ) {
2013-01-03 17:38:51 +01:00
self :: changeSubsite ( $duplicate -> ID ); //Change to destination subsite
2012-08-07 17:37:44 +02:00
$childClone = $child -> duplicateToSubsite ( $duplicate , false );
2007-08-29 00:29:44 +02:00
$childClone -> ParentID = $destParentID ;
$childClone -> writeToStage ( 'Stage' );
$childClone -> publish ( 'Stage' , 'Live' );
2013-01-03 17:38:51 +01:00
self :: changeSubsite ( $this -> ID ); //Change Back to this subsite
2007-08-29 00:29:44 +02:00
array_push ( $stack , array ( $child -> ID , $childClone -> ID ));
}
}
}
self :: changeSubsite ( $oldSubsiteID );
2009-05-04 07:03:44 +02:00
2012-08-07 17:37:44 +02:00
return $duplicate ;
2007-08-31 02:29:25 +02:00
}
2009-05-04 07:03:44 +02:00
2007-08-31 02:29:25 +02:00
/**
2013-10-16 02:25:06 +02:00
* Return all subsites , regardless of permissions ( augmented with main site ) .
*
* @ return SS_List List of { @ link Subsite } objects ( DataList or ArrayList ) .
*/
public static function all_sites ( $includeMainSite = true , $mainSiteTitle = " Main site " ) {
$subsites = Subsite :: get ();
if ( $includeMainSite ) {
$subsites = $subsites -> toArray ();
$mainSite = new Subsite ();
$mainSite -> Title = $mainSiteTitle ;
array_unshift ( $subsites , $mainSite );
$subsites = ArrayList :: create ( $subsites );
}
return $subsites ;
}
/*
* Returns an ArrayList of the subsites accessible to the current user .
* It ' s enough for any section to be accessible for the site to be included .
2009-05-04 07:03:44 +02:00
*
2013-10-16 02:25:06 +02:00
* @ return ArrayList of { @ link Subsite } instances .
*/
public static function all_accessible_sites ( $includeMainSite = true , $mainSiteTitle = " Main site " , $member = null ) {
// Rationalise member arguments
if ( ! $member ) $member = Member :: currentUser ();
if ( ! $member ) return new ArrayList ();
if ( ! is_object ( $member )) $member = DataObject :: get_by_id ( 'Member' , $member );
$subsites = new ArrayList ();
// Collect subsites for all sections.
$menu = CMSMenu :: get_viewable_menu_items ();
foreach ( $menu as $candidate ) {
if ( $candidate -> controller ) {
$accessibleSites = singleton ( $candidate -> controller ) -> sectionSites (
$includeMainSite ,
$mainSiteTitle ,
$member
);
// Replace existing keys so no one site appears twice.
$subsites -> merge ( $accessibleSites );
}
}
$subsites -> removeDuplicates ();
return $subsites ;
}
/**
* Return the subsites that the current user can access by given permission .
* Sites will only be included if they have a Title .
2009-05-04 07:03:44 +02:00
*
2007-08-31 02:29:25 +02:00
* @ param $permCode array | string Either a single permission code or an array of permission codes .
2010-03-01 22:32:37 +01:00
* @ param $includeMainSite If true , the main site will be included if appropriate .
* @ param $mainSiteTitle The label to give to the main site
2011-09-05 17:24:48 +02:00
* @ param $member
2012-03-25 18:35:01 +02:00
* @ return DataList of { @ link Subsite } instances
2007-08-31 02:29:25 +02:00
*/
2012-07-11 15:32:10 +02:00
public static function accessible_sites ( $permCode , $includeMainSite = true , $mainSiteTitle = " Main site " , $member = null ) {
2010-03-01 22:37:56 +01:00
// Rationalise member arguments
if ( ! $member ) $member = Member :: currentUser ();
2012-03-25 18:35:01 +02:00
if ( ! $member ) return new ArrayList ();
2010-03-01 22:37:56 +01:00
if ( ! is_object ( $member )) $member = DataObject :: get_by_id ( 'Member' , $member );
2009-05-04 07:03:44 +02:00
2011-09-22 17:40:26 +02:00
// Rationalise permCode argument
2007-08-31 02:29:25 +02:00
if ( is_array ( $permCode )) $SQL_codes = " ' " . implode ( " ', ' " , Convert :: raw2sql ( $permCode )) . " ' " ;
else $SQL_codes = " ' " . Convert :: raw2sql ( $permCode ) . " ' " ;
2011-09-22 17:40:26 +02:00
// Cache handling
$cacheKey = $SQL_codes . '-' . $member -> ID . '-' . $includeMainSite . '-' . $mainSiteTitle ;
if ( isset ( self :: $_cache_accessible_sites [ $cacheKey ])) {
return self :: $_cache_accessible_sites [ $cacheKey ];
}
2010-03-01 23:05:55 +01:00
2012-04-25 14:29:32 +02:00
$subsites = DataList :: create ( 'Subsite' )
-> where ( " \" Subsite \" . \" Title \" != '' " )
-> leftJoin ( 'Group_Subsites' , " \" Group_Subsites \" . \" SubsiteID \" = \" Subsite \" . \" ID \" " )
2012-07-10 15:43:53 +02:00
-> innerJoin ( 'Group' , " \" Group \" . \" ID \" = \" Group_Subsites \" . \" GroupID \" OR \" Group \" . \" AccessAllSubsites \" = 1 " )
-> innerJoin ( 'Group_Members' , " \" Group_Members \" . \" GroupID \" = \" Group \" . \" ID \" AND \" Group_Members \" . \" MemberID \" = $member->ID " )
-> innerJoin ( 'Permission' , " \" Group \" . \" ID \" = \" Permission \" . \" GroupID \" AND \" Permission \" . \" Code \" IN ( $SQL_codes , 'ADMIN') " );
2010-03-01 04:10:43 +01:00
2012-03-25 18:35:01 +02:00
if ( ! $subsites ) $subsites = new ArrayList ();
2010-03-01 04:10:43 +01:00
2012-04-25 14:29:32 +02:00
$rolesSubsites = DataList :: create ( 'Subsite' )
-> where ( " \" Subsite \" . \" Title \" != '' " )
-> leftJoin ( 'Group_Subsites' , " \" Group_Subsites \" . \" SubsiteID \" = \" Subsite \" . \" ID \" " )
2012-03-25 18:35:01 +02:00
-> innerJoin ( 'Group' , " \" Group \" . \" ID \" = \" Group_Subsites \" . \" GroupID \" OR \" Group \" . \" AccessAllSubsites \" = 1 " )
-> innerJoin ( 'Group_Members' , " \" Group_Members \" . \" GroupID \" = \" Group \" . \" ID \" AND \" Group_Members \" . \" MemberID \" = $member->ID " )
-> innerJoin ( 'Group_Roles' , " \" Group_Roles \" . \" GroupID \" = \" Group \" . \" ID \" " )
-> innerJoin ( 'PermissionRole' , " \" Group_Roles \" . \" PermissionRoleID \" = \" PermissionRole \" . \" ID \" " )
-> innerJoin ( 'PermissionRoleCode' , " \" PermissionRole \" . \" ID \" = \" PermissionRoleCode \" . \" RoleID \" AND \" PermissionRoleCode \" . \" Code \" IN ( $SQL_codes , 'ADMIN') " );
2010-03-01 04:10:43 +01:00
if ( ! $subsites && $rolesSubsites ) return $rolesSubsites ;
2010-03-01 23:12:58 +01:00
2012-10-12 04:09:38 +02:00
$subsites = new ArrayList ( $subsites -> toArray ());
2010-03-01 04:10:43 +01:00
if ( $rolesSubsites ) foreach ( $rolesSubsites as $subsite ) {
2012-10-12 04:09:38 +02:00
if ( ! $subsites -> find ( 'ID' , $subsite -> ID )) {
2010-03-01 04:10:43 +01:00
$subsites -> push ( $subsite );
}
}
2010-03-01 23:12:58 +01:00
2010-03-01 22:32:37 +01:00
if ( $includeMainSite ) {
if ( ! is_array ( $permCode )) $permCode = array ( $permCode );
if ( self :: hasMainSitePermission ( $member , $permCode )) {
2012-07-10 15:43:53 +02:00
$subsites = $subsites -> toArray ();
2010-03-01 22:32:37 +01:00
$mainSite = new Subsite ();
$mainSite -> Title = $mainSiteTitle ;
2012-03-25 18:35:01 +02:00
array_unshift ( $subsites , $mainSite );
2012-07-10 15:43:53 +02:00
$subsites = ArrayList :: create ( $subsites );
2010-03-01 22:32:37 +01:00
}
}
2011-09-22 17:40:26 +02:00
self :: $_cache_accessible_sites [ $cacheKey ] = $subsites ;
2010-03-01 04:10:43 +01:00
return $subsites ;
2007-08-31 02:29:25 +02:00
}
2010-03-01 03:53:34 +01:00
/**
2010-03-01 03:53:45 +01:00
* Write a host -> domain map to subsites / host - map . php
2010-03-01 03:53:34 +01:00
*
* This is used primarily when using subsites in conjunction with StaticPublisher
*
* @ return void
*/
static function writeHostMap ( $file = null ) {
2010-03-01 03:58:13 +01:00
if ( ! self :: $write_hostmap ) return ;
2010-03-01 03:53:45 +01:00
if ( ! $file ) $file = Director :: baseFolder () . '/subsites/host-map.php' ;
2010-03-01 03:53:34 +01:00
$hostmap = array ();
$subsites = DataObject :: get ( 'Subsite' );
if ( $subsites ) foreach ( $subsites as $subsite ) {
$domains = $subsite -> Domains ();
if ( $domains ) foreach ( $domains as $domain ) {
2011-09-09 11:59:46 +02:00
$domainStr = $domain -> Domain ;
2013-05-06 12:54:23 +02:00
if ( ! self :: $strict_subdomain_matching ) $domainStr = preg_replace ( '/^www\./' , '' , $domainStr );
2011-09-09 11:59:46 +02:00
$hostmap [ $domainStr ] = $subsite -> domain ();
2010-03-01 03:53:34 +01:00
}
2010-03-01 03:53:51 +01:00
if ( $subsite -> DefaultSite ) $hostmap [ 'default' ] = $subsite -> domain ();
2010-03-01 03:53:34 +01:00
}
2010-04-29 08:59:33 +02:00
$data = " <?php \n " ;
$data .= " // Generated by Subsite::writeHostMap() on " . date ( 'd/M/y' ) . " \n " ;
$data .= '$subsiteHostmap = ' . var_export ( $hostmap , true ) . ';' ;
2010-03-01 03:53:51 +01:00
if ( is_writable ( dirname ( $file )) || is_writable ( $file )) {
file_put_contents ( $file , $data );
}
2010-03-01 03:53:34 +01:00
}
2009-05-04 07:03:44 +02:00
2007-08-18 13:38:11 +02:00
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// CMS ADMINISTRATION HELPERS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
2009-05-04 07:03:44 +02:00
2007-08-27 07:07:27 +02:00
function providePermissions () {
return array (
2010-03-01 22:39:48 +01:00
'SUBSITE_ASSETS_CREATE_SUBSITE' => array (
2010-03-01 22:40:26 +01:00
'name' => _t ( 'Subsite.MANAGE_ASSETS' , 'Manage assets for subsites' ),
'category' => _t ( 'Permissions.PERMISSIONS_CATEGORY' , 'Roles and access permissions' ),
'help' => _t ( 'Subsite.MANAGE_ASSETS_HELP' , 'Ability to select the subsite to which an asset folder belongs. Requires "Access to Files & Images."' ),
2010-03-01 22:39:48 +01:00
'sort' => 300
)
2007-08-27 07:07:27 +02:00
);
}
2007-09-05 06:47:05 +02:00
static function get_from_all_subsites ( $className , $filter = " " , $sort = " " , $join = " " , $limit = " " ) {
$result = DataObject :: get ( $className , $filter , $sort , $join , $limit );
2013-09-12 05:23:21 +02:00
$result = $result -> setDataQueryParam ( 'Subsite.filter' , false );
2007-09-05 06:47:05 +02:00
return $result ;
}
2009-05-04 07:03:44 +02:00
2008-08-21 07:50:38 +02:00
/**
* Disable the sub - site filtering ; queries will select from all subsites
2009-05-04 07:03:44 +02:00
*/
2008-08-21 07:50:38 +02:00
static function disable_subsite_filter ( $disabled = true ) {
self :: $disable_subsite_filter = $disabled ;
}
2011-09-22 17:40:26 +02:00
/**
* Flush caches on database reset
*/
static function on_db_reset () {
self :: $_cache_accessible_sites = array ();
2012-08-15 11:11:22 +02:00
self :: $_cache_subsite_for_domain = array ();
2007-08-18 13:38:11 +02:00
}
}