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
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 .
*/
2007-09-05 06:47:05 +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 .
*/
static $force_subsite = null ;
2009-05-04 07:03:44 +02:00
2010-03-01 03:58:13 +01:00
static $write_hostmap = true ;
2011-09-09 11:59:46 +02:00
2010-05-06 05:27:02 +02:00
static $default_sort = " \" Title \" ASC " ;
2009-05-04 07:03:44 +02:00
2007-08-18 13:38:11 +02:00
static $db = array (
'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
2008-06-19 02:46:51 +02:00
static $has_one = array (
);
2010-03-01 03:53:00 +01:00
static $has_many = array (
'Domains' => 'SubsiteDomain' ,
);
2010-03-01 22:37:56 +01:00
static $belongs_many_many = array (
" Groups " => " Group " ,
);
2009-05-04 07:03:44 +02:00
2008-06-24 04:31:26 +02:00
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
2010-03-01 03:53:00 +01:00
static $searchable_fields = array (
'Title' => array (
'title' => 'Subsite Name'
),
'Domains.Domain' => array (
'title' => 'Domain name'
),
'IsPublic' => array (
'title' => 'Active subsite' ,
),
);
static $summary_fields = array (
'Title' => 'Subsite Name' ,
'PrimaryDomain' => 'Primary Domain' ,
'IsPublic' => 'Active subsite' ,
);
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
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 .
*/
2009-05-04 07:03:44 +02:00
protected 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 .
*/
static $strict_subdomain_matching = false ;
2009-05-04 07:03:44 +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 ();
}
}
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 ) {
$domainTable = new GridField ( " Domains " , " Domains " , $this -> Domains (), GridFieldConfig_RecordEditor :: create ( 10 ));
} else {
$domainTable = new LiteralField ( 'Domains' , '<p>' . _t ( 'Subsite.DOMAINSAVEFIRST' , '_You can only add domains after saving for the first time' ) . '</p>' );
}
2010-03-01 04:00:14 +01:00
2010-03-01 22:26:07 +01:00
$languageSelector = new DropdownField ( 'Language' , '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 (
2007-08-18 13:38:11 +02:00
new TabSet ( 'Root' ,
new Tab ( 'Configuration' ,
new HeaderField ( $this -> getClassName () . ' configuration' , 2 ),
new TextField ( 'Title' , 'Name of subsite:' , $this -> Title ),
2010-03-01 03:48:45 +01:00
new HeaderField ( " Domains for this subsite " ),
$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),
2008-11-24 05:56:47 +01:00
new CheckboxField ( 'DefaultSite' , 'Default site' , $this -> DefaultSite ),
new CheckboxField ( 'IsPublic' , 'Enable public access' , $this -> IsPublic ),
2008-06-24 04:31:26 +02:00
2011-08-30 18:58:36 +02:00
new DropdownField ( 'Theme' , 'Theme' , $this -> allowedThemes (), $this -> Theme ),
new LiteralField (
'PageTypeBlacklistToggle' ,
sprintf (
'<div class="field"><a href="#" id="PageTypeBlacklistToggle">%s</a></div>' ,
_t ( 'Subsite.PageTypeBlacklistField' , 'Disallow page types?' )
)
),
new CheckboxSetField (
'PageTypeBlacklist' ,
false ,
$pageTypeMap
)
2007-08-18 13:38:11 +02:00
)
),
new HiddenField ( 'ID' , '' , $this -> ID ),
new HiddenField ( 'IsSubsite' , '' , 1 )
);
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
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 (
2012-07-10 15:43:53 +02:00
new FormAction ( 'callPageMethod' , " 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 ();
$JS_title = Convert :: raw2js ( $this -> Title );
return <<< JS
statusMessage ( 'Created a copy of $JS_title' , 'good' );
$ ( '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
*
2008-11-30 23:31:08 +01:00
* @ todo Pass $request object from controller so we don ' t have to rely on $_REQUEST
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 () {
2010-03-30 23:02:44 +02:00
if ( isset ( $_REQUEST [ 'SubsiteID' ])) $id = ( int ) $_REQUEST [ 'SubsiteID' ];
else $id = Session :: get ( 'SubsiteID' );
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
Session :: set ( 'SubsiteID' , $id );
}
2012-07-10 15:43:53 +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
/**
2008-11-24 07:35:51 +01:00
* Switch to another subsite .
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 != '' ) {
if ( isset ( i18n :: $likely_subtags [ $subsite -> Language ])) {
i18n :: set_locale ( i18n :: $likely_subtags [ $subsite -> Language ]);
}
}
2010-03-01 03:56:58 +01:00
// Only bother flushing caches if we've actually changed
2010-03-30 23:02:44 +02:00
if ( $subsiteID != self :: currentSubsiteID ()) 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
*/
2010-03-01 04:15:07 +01:00
static function getSubsiteIDForDomain ( $host = null , $returnMainIfNotFound = true ) {
2010-03-01 03:48:45 +01:00
if ( $host == null ) $host = $_SERVER [ 'HTTP_HOST' ];
2009-11-02 02:59:15 +01:00
2011-09-09 11:59:46 +02:00
if ( ! Subsite :: $strict_subdomain_matching ) $host = preg_replace ( '/^www\./' , '' , $host );
2010-03-01 03:48:45 +01:00
$SQL_host = Convert :: raw2sql ( $host );
2009-05-04 07:03:44 +02:00
2010-03-09 02:09:25 +01:00
$matchingDomains = DataObject :: get ( " SubsiteDomain " , " ' $SQL_host ' LIKE replace( \" SubsiteDomain \" . \" Domain \" ,'*','%') " ,
2012-03-25 18:35:01 +02:00
" \" IsPrimary \" DESC " ) -> innerJoin ( 'Subsite' , " \" Subsite \" . \" ID \" = \" SubsiteDomain \" . \" SubsiteID \" AND
2010-03-09 02:09:25 +01:00
\ " Subsite \" . \" IsPublic \" =1 " );
2010-03-01 03:48:45 +01:00
2012-03-25 18:35:01 +02:00
if ( $matchingDomains && $matchingDomains -> Count () > 0 ) {
$subsiteIDs = array_unique ( $matchingDomains -> map ( 'SubsiteID' ) -> keys ());
$subsiteDomains = array_unique ( $matchingDomains -> map ( 'Domain' ) -> keys ());
2011-09-09 11:59:46 +02:00
if ( sizeof ( $subsiteIDs ) > 1 ) {
throw new UnexpectedValueException ( sprintf (
" Multiple subsites match on '%s': %s " ,
$host ,
implode ( ',' , $subsiteDomains )
));
}
2010-03-01 03:48:45 +01:00
return $subsiteIDs [ 0 ];
2007-08-18 13:38:11 +02:00
}
2010-03-01 03:56:58 +01:00
2010-03-01 22:49:22 +01:00
// Check for a 'default' subsite
2010-03-01 22:54:36 +01:00
if ( $default = DataObject :: get_one ( 'Subsite' , " \" DefaultSite \" = 1 " )) {
2010-03-01 22:49:22 +01:00
return $default -> ID ;
}
2010-03-01 03:56:58 +01:00
// Default subsite id = 0, the main site
return 0 ;
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
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
2008-11-26 04:43:12 +01:00
return ( $groupCount > 0 );
2010-03-01 23:05:55 +01:00
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 ) {
$newTemplate = 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
/*
* 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 ));
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 );
2009-05-04 07:03:44 +02:00
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 ) {
$childClone = $child -> duplicateToSubsite ( $newTemplate , false );
$childClone -> ParentID = $destParentID ;
$childClone -> writeToStage ( 'Stage' );
$childClone -> publish ( 'Stage' , 'Live' );
array_push ( $stack , array ( $child -> ID , $childClone -> ID ));
}
}
}
self :: changeSubsite ( $oldSubsiteID );
2009-05-04 07:03:44 +02:00
2007-08-29 00:29:44 +02:00
return $newTemplate ;
2007-08-31 02:29:25 +02:00
}
2009-05-04 07:03:44 +02:00
2007-08-31 02:29:25 +02:00
/**
* Return the subsites that the current user can access .
* Look for one of the given permission codes on the site .
2009-05-04 07:03:44 +02:00
*
2010-03-01 03:49:35 +01:00
* Sites and Templates 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
2009-02-24 23:09:15 +01:00
$templateClassList = " ' " . implode ( " ', ' " , ClassInfo :: subclassesFor ( " Subsite_Template " )) . " ' " ;
2007-08-31 02:29:25 +02: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') " );
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
2010-03-01 04:10:43 +01:00
if ( $rolesSubsites ) foreach ( $rolesSubsites as $subsite ) {
if ( ! $subsites -> containsIDs ( array ( $subsite -> ID ))) {
$subsites -> push ( $subsite );
}
}
2010-03-01 23:12:58 +01:00
2010-03-01 22:32:37 +01:00
// Include the main site
2012-03-25 18:35:01 +02:00
if ( ! $subsites ) $subsites = new ArrayList ();
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
2012-07-10 15:43:53 +02:00
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 ;
if ( ! Subsite :: $strict_subdomain_matching ) $domainStr = preg_replace ( '/^www\./' , '' , $domainStr );
$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-18 13:38:11 +02:00
/**
2012-03-25 18:35:01 +02:00
* Return the FieldList that will build the search form in the CMS
2007-08-18 13:38:11 +02:00
*/
function adminSearchFields () {
2012-03-25 18:35:01 +02:00
return new FieldList (
2007-08-18 13:38:11 +02:00
new TextField ( 'Name' , 'Sub-site name' )
2009-05-04 07:03:44 +02:00
);
2007-08-18 13:38:11 +02:00
}
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 = " " ) {
2010-03-01 22:41:59 +01:00
$oldState = self :: $disable_subsite_filter ;
2007-09-05 06:47:05 +02:00
self :: $disable_subsite_filter = true ;
$result = DataObject :: get ( $className , $filter , $sort , $join , $limit );
2010-03-01 22:41:59 +01:00
self :: $disable_subsite_filter = $oldState ;
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 ();
}
2007-08-18 13:38:11 +02:00
}
/**
* An instance of subsite that can be duplicated to provide a quick way to create new subsites .
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
*/
class Subsite_Template extends Subsite {
/**
2010-03-01 03:49:35 +01:00
* Create an instance of this template , with the given title & domain
2007-08-18 13:38:11 +02:00
*/
2010-03-01 03:53:00 +01:00
function createInstance ( $title , $domain = null ) {
2007-08-18 13:38:11 +02:00
$intranet = Object :: create ( 'Subsite' );
$intranet -> Title = $title ;
$intranet -> TemplateID = $this -> ID ;
$intranet -> write ();
2010-03-01 03:48:45 +01:00
2010-03-01 03:53:00 +01:00
if ( $domain ) {
$intranetDomain = Object :: create ( 'SubsiteDomain' );
$intranetDomain -> SubsiteID = $intranet -> ID ;
$intranetDomain -> Domain = $domain ;
$intranetDomain -> write ();
}
2009-05-04 07:03:44 +02:00
2007-08-18 13:38:11 +02:00
$oldSubsiteID = Session :: get ( 'SubsiteID' );
self :: changeSubsite ( $this -> ID );
2009-05-04 07:03:44 +02:00
2007-08-18 13:38:11 +02:00
/*
2009-02-24 23:09:15 +01:00
* Copy site content from this template to the given subsite . Does this using an iterative depth - first search .
2007-08-18 13:38:11 +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-18 13:38:11 +02:00
list ( $sourceParentID , $destParentID ) = array_pop ( $stack );
2009-05-04 07:03:44 +02:00
2010-03-09 02:09:25 +01:00
$children = Versioned :: get_by_stage ( 'SiteTree' , 'Live' , " \" ParentID \" = $sourceParentID " , '' );
2009-05-04 07:03:44 +02:00
2007-08-18 13:38:11 +02:00
if ( $children ) {
foreach ( $children as $child ) {
2007-08-29 00:29:44 +02:00
$childClone = $child -> duplicateToSubsite ( $intranet );
2012-07-16 01:14:13 +02:00
//Change to destination subsite
self :: changeSubsite ( $intranet -> ID );
$newTemplate = parent :: duplicate ( $doWrite );
$childClone = $child -> duplicateToSubsite ( $newTemplate , false );
2007-08-18 13:38:11 +02:00
$childClone -> ParentID = $destParentID ;
$childClone -> writeToStage ( 'Stage' );
$childClone -> publish ( 'Stage' , 'Live' );
2012-07-16 01:14:13 +02:00
//Change Back to this subsite
self :: changeSubsite ( $this -> ID );
2007-08-18 13:38:11 +02:00
array_push ( $stack , array ( $child -> ID , $childClone -> ID ));
}
}
}
2009-05-04 07:03:44 +02:00
2007-08-18 13:38:11 +02:00
self :: changeSubsite ( $oldSubsiteID );
2009-05-04 07:03:44 +02:00
2007-08-18 13:38:11 +02:00
return $intranet ;
}
}
2012-07-16 01:14:13 +02:00
?>