2007-08-18 13:38:11 +02:00
< ? php
2016-09-22 16:38:29 +02:00
2017-05-24 12:32:05 +02:00
namespace SilverStripe\Subsites\Model ;
2017-05-24 15:09:13 +02:00
use SilverStripe\i18n\Data\Intl\IntlLocales ;
2016-09-22 16:38:29 +02:00
use SilverStripe\ORM\DataObject ;
use SilverStripe\Control\Session ;
use SilverStripe\i18n\i18n ;
use SilverStripe\Security\Permission ;
use SilverStripe\Security\Member ;
use SilverStripe\Core\Convert ;
use SilverStripe\ORM\ArrayList ;
use SilverStripe\Admin\CMSMenu ;
use SilverStripe\ORM\DataList ;
use SilverStripe\Control\Director ;
use SilverStripe\ORM\DB ;
use SilverStripe\Forms\GridField\GridFieldConfig_RecordEditor ;
use SilverStripe\Forms\GridField\GridField ;
use SilverStripe\Forms\LiteralField ;
use SilverStripe\Forms\DropdownField ;
use SilverStripe\CMS\Model\SiteTree ;
use SilverStripe\Forms\HeaderField ;
use SilverStripe\Forms\TextField ;
use SilverStripe\Forms\CheckboxField ;
use SilverStripe\Forms\CheckboxSetField ;
use SilverStripe\Forms\Tab ;
use SilverStripe\Forms\TabSet ;
use SilverStripe\Forms\HiddenField ;
use SilverStripe\Forms\FieldList ;
use SilverStripe\ORM\ArrayLib ;
2017-05-24 12:32:05 +02:00
use SilverStripe\Versioned\Versioned ;
use UnexpectedValueException ;
2017-05-24 13:36:04 +02:00
use SilverStripe\Security\Group ;
use SilverStripe\Security\PermissionRole ;
use SilverStripe\Security\PermissionRoleCode ;
2017-05-24 12:32:05 +02:00
2007-08-18 13:38:11 +02:00
/**
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
*/
2014-08-18 01:20:38 +02:00
class Subsite extends DataObject {
2007-09-05 06:47:05 +02:00
2017-05-24 14:31:56 +02:00
private static $table_name = 'Subsite' ;
2013-07-05 03:57:58 +02:00
/**
* @ var $use_session_subsiteid Boolean Set to TRUE when using the CMS and FALSE
2016-09-22 16:38:29 +02:00
* when browsing the frontend of a website .
*
2013-07-05 03:57:58 +02:00
* @ 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-11-10 23:55:00 +01:00
public static $disable_subsite_filter = false ;
2016-09-22 16:38:29 +02:00
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-11-10 23:55:00 +01:00
public static $force_subsite = null ;
2009-05-04 07:03:44 +02:00
2013-11-10 23:55:00 +01:00
/**
*
* @ var boolean
*/
public static $write_hostmap = true ;
2016-09-22 16:38:29 +02:00
2011-09-22 17:40:26 +02:00
/**
* Memory cache of accessible sites
2016-09-22 16:38:29 +02:00
*
2013-11-10 23:55:00 +01:00
* @ array
2011-09-22 17:40:26 +02:00
*/
private static $_cache_accessible_sites = array ();
2010-03-01 03:53:00 +01:00
2013-11-10 23:55:00 +01:00
/**
* Memory cache of subsite id for domains
*
* @ var array
*/
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 ();
2016-09-22 16:38:29 +02:00
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-11-10 23:55:00 +01:00
public 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-11-10 23:55:00 +01:00
public static $check_is_public = true ;
2012-08-15 11:11:22 +02:00
2015-10-20 21:14:39 +02:00
/**
* @ return array
*/
private static $summary_fields = array (
'Title' ,
'PrimaryDomain' ,
'IsPublic'
);
2013-11-10 23:55:00 +01:00
/**
* Set allowed themes
2016-09-22 16:38:29 +02:00
*
2013-11-10 23:55:00 +01:00
* @ param array $themes - Numeric array of all themes which are allowed to be selected for all subsites .
*/
public static function set_allowed_themes ( $themes ) {
2008-06-24 04:31:26 +02:00
self :: $allowed_themes = $themes ;
}
2016-09-22 16:38:29 +02:00
2008-11-20 00:25:43 +01:00
/**
2013-11-11 00:09:27 +01:00
* Gets the subsite currently set in the session .
*
* @ uses ControllerSubsites -> controllerAugmentInit ()
* @ return Subsite
2008-11-20 00:25:43 +01:00
*/
2013-11-11 00:09:27 +01:00
public static function currentSubsite () {
// get_by_id handles caching so we don't have to
2017-05-24 13:36:04 +02:00
return DataObject :: get_by_id ( Subsite :: class , self :: currentSubsiteID ());
2008-11-20 00:25:43 +01:00
}
2009-05-04 07:03:44 +02:00
2013-11-10 23:55:00 +01:00
/**
2013-11-11 00:09:27 +01: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
* directly from ModelAsController :: getNestedController . Only gets Subsite instances which have their
* { @ link IsPublic } flag set to TRUE .
*
* You can simulate subsite access without creating virtual hosts by appending ? SubsiteID =< ID > to the request .
*
* @ todo Pass $request object from controller so we don ' t have to rely on $_GET
*
* @ param boolean $cache
* @ return int ID of the current subsite instance
2013-11-10 23:55:00 +01:00
*/
2013-11-11 00:09:27 +01:00
public static function currentSubsiteID () {
$id = NULL ;
if ( isset ( $_GET [ 'SubsiteID' ])) {
$id = ( int ) $_GET [ 'SubsiteID' ];
} else if ( Subsite :: $use_session_subsiteid ) {
$id = Session :: get ( 'SubsiteID' );
2010-08-23 02:30:34 +02:00
}
2013-11-11 00:09:27 +01:00
if ( $id === NULL ) {
$id = self :: getSubsiteIDForDomain ();
2013-07-10 16:15:34 +02:00
}
2013-11-11 00:09:27 +01:00
return ( int ) $id ;
}
2016-09-22 16:38:29 +02:00
2010-03-01 03:53:34 +01:00
/**
2013-11-11 00:09:27 +01: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 .
2010-03-01 03:53:34 +01:00
*
2013-11-11 00:09:27 +01:00
* @ param int | Subsite $subsite Either the ID of the subsite , or the subsite object itself
2010-03-01 03:53:34 +01:00
*/
2013-11-11 00:09:27 +01:00
public static function changeSubsite ( $subsite ) {
2013-11-26 00:56:59 +01:00
// Session subsite change only meaningful if the session is active.
// Otherwise we risk setting it to wrong value, e.g. if we rely on currentSubsiteID.
if ( ! Subsite :: $use_session_subsiteid ) return ;
2013-11-11 00:09:27 +01:00
if ( is_object ( $subsite )) $subsiteID = $subsite -> ID ;
else $subsiteID = $subsite ;
2013-11-26 00:56:59 +01:00
2013-11-11 00:09:27 +01:00
Session :: set ( 'SubsiteID' , ( int ) $subsiteID );
2013-11-26 00:56:59 +01:00
2013-11-11 00:09:27 +01:00
// Set locale
if ( is_object ( $subsite ) && $subsite -> Language != '' ) {
$locale = i18n :: get_locale_from_lang ( $subsite -> Language );
if ( $locale ) {
i18n :: set_locale ( $locale );
}
}
2013-11-26 00:56:59 +01:00
Permission :: flush_permission_cache ();
2010-03-01 03:53:34 +01:00
}
2016-09-22 16:38:29 +02:00
2007-08-18 13:38:11 +02:00
/**
2013-11-11 00:09:27 +01:00
* Get a matching subsite for the given host , or for the current HTTP_HOST .
* Supports " fuzzy " matching of domains by placing an asterisk at the start of end of the string ,
* for example matching all subdomains on *. example . com with one subsite ,
* and all subdomains on *. example . org on another .
2016-09-22 16:38:29 +02:00
*
2013-11-11 00:09:27 +01:00
* @ param $host The host to find the subsite for . If not specified , $_SERVER [ 'HTTP_HOST' ] is used .
* @ return int Subsite ID
2007-08-18 13:38:11 +02:00
*/
2013-11-11 00:09:27 +01:00
public static function getSubsiteIDForDomain ( $host = null , $checkPermissions = true ) {
2014-03-27 05:11:50 +01:00
if ( $host == null && isset ( $_SERVER [ 'HTTP_HOST' ])) {
$host = $_SERVER [ 'HTTP_HOST' ];
}
$matchingDomains = null ;
$cacheKey = null ;
if ( $host ) {
if ( ! self :: $strict_subdomain_matching ) $host = preg_replace ( '/^www\./' , '' , $host );
2013-11-11 00:09:27 +01:00
2014-03-27 05:11:50 +01:00
$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 ];
2013-11-11 00:09:27 +01:00
2014-03-27 05:11:50 +01:00
$SQL_host = Convert :: raw2sql ( $host );
$matchingDomains = DataObject :: get (
2017-05-24 13:36:04 +02:00
SubsiteDomain :: class ,
2014-03-27 05:11:50 +01:00
" ' $SQL_host ' LIKE replace( \" SubsiteDomain \" . \" Domain \" ,'*','%') " ,
" \" IsPrimary \" DESC "
2017-05-24 14:55:03 +02:00
) -> innerJoin ( 'Subsite' , " \" Subsite \" . \" ID \" = \" SubsiteDomain \" . \" SubsiteID \" AND \" Subsite \" . \" IsPublic \" =1 " );
2014-03-27 05:11:50 +01:00
}
2013-11-11 00:09:27 +01:00
if ( $matchingDomains && $matchingDomains -> Count ()) {
$subsiteIDs = array_unique ( $matchingDomains -> column ( 'SubsiteID' ));
$subsiteDomains = array_unique ( $matchingDomains -> column ( 'Domain' ));
if ( sizeof ( $subsiteIDs ) > 1 ) {
throw new UnexpectedValueException ( sprintf (
" Multiple subsites match on '%s': %s " ,
$host ,
implode ( ',' , $subsiteDomains )
));
2010-03-01 03:48:45 +01:00
}
2014-03-27 05:11:50 +01:00
2013-11-11 00:09:27 +01:00
$subsiteID = $subsiteIDs [ 0 ];
2017-05-24 13:36:04 +02:00
} else if ( $default = DataObject :: get_one ( Subsite :: class , " \" DefaultSite \" = 1 " )) {
2013-11-11 00:09:27 +01:00
// Check for a 'default' subsite
$subsiteID = $default -> ID ;
2010-03-01 03:53:15 +01:00
} else {
2013-11-11 00:09:27 +01:00
// Default subsite id = 0, the main site
$subsiteID = 0 ;
2010-03-01 03:48:45 +01:00
}
2014-03-27 05:11:50 +01:00
if ( $cacheKey ) {
self :: $_cache_subsite_for_domain [ $cacheKey ] = $subsiteID ;
}
2013-11-11 00:09:27 +01:00
return $subsiteID ;
2010-03-01 03:53:00 +01:00
}
2008-12-04 22:36:06 +01:00
2013-11-11 00:09:27 +01:00
/**
2016-09-22 16:38:29 +02:00
*
2013-11-11 00:09:27 +01:00
* @ param string $className
* @ param string $filter
* @ param string $sort
* @ param string $join
* @ param string $limit
* @ return DataList
*/
public static function get_from_all_subsites ( $className , $filter = " " , $sort = " " , $join = " " , $limit = " " ) {
$result = DataObject :: get ( $className , $filter , $sort , $join , $limit );
$result = $result -> setDataQueryParam ( 'Subsite.filter' , false );
return $result ;
2008-12-04 22:36:06 +01:00
}
2009-05-04 07:03:44 +02:00
2008-11-24 05:56:47 +01:00
/**
2013-11-11 00:09:27 +01:00
* Disable the sub - site filtering ; queries will select from all subsites
2008-11-24 05:56:47 +01:00
*/
2013-11-11 00:09:27 +01:00
public static function disable_subsite_filter ( $disabled = true ) {
self :: $disable_subsite_filter = $disabled ;
}
2016-09-22 16:38:29 +02:00
2013-11-11 00:09:27 +01:00
/**
* Flush caches on database reset
*/
public static function on_db_reset () {
self :: $_cache_accessible_sites = array ();
self :: $_cache_subsite_for_domain = array ();
}
2016-09-22 16:38:29 +02:00
2013-11-11 00:09:27 +01: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 .
*
* @ 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 ();
2016-09-22 16:38:29 +02:00
if ( ! is_object ( $member )) $member = DataObject :: get_by_id ( 'SilverStripe\\Security\\Member' , $member );
2013-11-11 00:09:27 +01:00
$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 .
*
* @ param $permCode array | string Either a single permission code or an array of permission codes .
* @ param $includeMainSite If true , the main site will be included if appropriate .
* @ param $mainSiteTitle The label to give to the main site
* @ param $member
* @ return DataList of { @ link Subsite } instances
*/
public static function accessible_sites ( $permCode , $includeMainSite = true , $mainSiteTitle = " Main site " , $member = null ) {
// Rationalise member arguments
if ( ! $member ) $member = Member :: currentUser ();
if ( ! $member ) return new ArrayList ();
2016-09-22 16:38:29 +02:00
if ( ! is_object ( $member )) $member = DataObject :: get_by_id ( 'SilverStripe\\Security\\Member' , $member );
2013-11-11 00:09:27 +01:00
2016-09-22 16:38:29 +02:00
// Rationalise permCode argument
2013-11-11 00:09:27 +01:00
if ( is_array ( $permCode )) $SQL_codes = " ' " . implode ( " ', ' " , Convert :: raw2sql ( $permCode )) . " ' " ;
else $SQL_codes = " ' " . Convert :: raw2sql ( $permCode ) . " ' " ;
2016-09-22 16:38:29 +02:00
2013-11-11 00:09:27 +01:00
// Cache handling
$cacheKey = $SQL_codes . '-' . $member -> ID . '-' . $includeMainSite . '-' . $mainSiteTitle ;
if ( isset ( self :: $_cache_accessible_sites [ $cacheKey ])) {
return self :: $_cache_accessible_sites [ $cacheKey ];
}
2017-05-24 13:36:04 +02:00
$subsites = DataList :: create ( Subsite :: class )
2013-11-11 00:09:27 +01:00
-> where ( " \" Subsite \" . \" Title \" != '' " )
-> leftJoin ( 'Group_Subsites' , " \" Group_Subsites \" . \" SubsiteID \" = \" Subsite \" . \" ID \" " )
2017-05-24 14:55:03 +02:00
-> innerJoin ( 'Group' , " \" Group \" . \" ID \" = \" Group_Subsites \" . \" GroupID \" OR \" Group \" . \" AccessAllSubsites \" = 1 " )
2013-11-11 00:09:27 +01:00
-> innerJoin ( 'Group_Members' , " \" Group_Members \" . \" GroupID \" = \" Group \" . \" ID \" AND \" Group_Members \" . \" MemberID \" = $member->ID " )
2017-05-24 14:55:03 +02:00
-> innerJoin ( 'Permission' , " \" Group \" . \" ID \" = \" Permission \" . \" GroupID \" AND \" Permission \" . \" Code \" IN ( $SQL_codes , 'CMS_ACCESS_LeftAndMain', 'ADMIN') " );
2013-11-11 00:09:27 +01:00
if ( ! $subsites ) $subsites = new ArrayList ();
2017-05-24 13:36:04 +02:00
$rolesSubsites = DataList :: create ( Subsite :: class )
2013-11-11 00:09:27 +01:00
-> where ( " \" Subsite \" . \" Title \" != '' " )
-> leftJoin ( 'Group_Subsites' , " \" Group_Subsites \" . \" SubsiteID \" = \" Subsite \" . \" ID \" " )
2017-05-24 14:55:03 +02:00
-> innerJoin ( 'Group' , " \" Group \" . \" ID \" = \" Group_Subsites \" . \" GroupID \" OR \" Group \" . \" AccessAllSubsites \" = 1 " )
2013-11-11 00:09:27 +01:00
-> innerJoin ( 'Group_Members' , " \" Group_Members \" . \" GroupID \" = \" Group \" . \" ID \" AND \" Group_Members \" . \" MemberID \" = $member->ID " )
-> innerJoin ( 'Group_Roles' , " \" Group_Roles \" . \" GroupID \" = \" Group \" . \" ID \" " )
2017-05-24 14:55:03 +02:00
-> innerJoin ( 'PermissionRole' , " \" Group_Roles \" . \" PermissionRoleID \" = \" PermissionRole \" . \" ID \" " )
-> innerJoin ( 'PermissionRoleCode' , " \" PermissionRole \" . \" ID \" = \" PermissionRoleCode \" . \" RoleID \" AND \" PermissionRoleCode \" . \" Code \" IN ( $SQL_codes , 'CMS_ACCESS_LeftAndMain', 'ADMIN') " );
2013-11-11 00:09:27 +01:00
if ( ! $subsites && $rolesSubsites ) return $rolesSubsites ;
$subsites = new ArrayList ( $subsites -> toArray ());
if ( $rolesSubsites ) foreach ( $rolesSubsites as $subsite ) {
if ( ! $subsites -> find ( 'ID' , $subsite -> ID )) {
$subsites -> push ( $subsite );
}
}
if ( $includeMainSite ) {
if ( ! is_array ( $permCode )) $permCode = array ( $permCode );
if ( self :: hasMainSitePermission ( $member , $permCode )) {
$subsites = $subsites -> toArray ();
2016-09-22 16:38:29 +02:00
2013-11-11 00:09:27 +01:00
$mainSite = new Subsite ();
$mainSite -> Title = $mainSiteTitle ;
array_unshift ( $subsites , $mainSite );
$subsites = ArrayList :: create ( $subsites );
}
}
2016-09-22 16:38:29 +02:00
2013-11-11 00:09:27 +01:00
self :: $_cache_accessible_sites [ $cacheKey ] = $subsites ;
return $subsites ;
}
2016-09-22 16:38:29 +02:00
2013-11-11 00:09:27 +01:00
/**
* Write a host -> domain map to subsites / host - map . php
*
* This is used primarily when using subsites in conjunction with StaticPublisher
*
* @ param string $file - filepath of the host map to be written
* @ return void
*/
public static function writeHostMap ( $file = null ) {
if ( ! self :: $write_hostmap ) return ;
2016-09-22 16:38:29 +02:00
2013-11-11 00:09:27 +01:00
if ( ! $file ) $file = Director :: baseFolder () . '/subsites/host-map.php' ;
$hostmap = array ();
2016-09-22 16:38:29 +02:00
2017-05-24 13:36:04 +02:00
$subsites = DataObject :: get ( Subsite :: class );
2016-09-22 16:38:29 +02:00
2013-11-11 00:09:27 +01:00
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 );
2016-09-22 16:38:29 +02:00
$hostmap [ $domainStr ] = $subsite -> domain ();
2013-11-11 00:09:27 +01:00
}
if ( $subsite -> DefaultSite ) $hostmap [ 'default' ] = $subsite -> domain ();
}
2016-09-22 16:38:29 +02:00
2013-11-11 00:09:27 +01:00
$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 );
}
}
2016-09-22 16:38:29 +02:00
2013-11-11 00:09:27 +01: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 .
2016-09-22 16:38:29 +02:00
*
2013-11-11 00:09:27 +01:00
* @ todo Allow permission inheritance through group hierarchy .
2016-09-22 16:38:29 +02:00
*
2013-11-11 00:09:27 +01:00
* @ 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 ;
2016-09-22 16:38:29 +02:00
2013-11-11 00:09:27 +01:00
if ( ! in_array ( " ADMIN " , $permissionCodes )) $permissionCodes [] = " ADMIN " ;
$SQLa_perm = Convert :: raw2sql ( $permissionCodes );
$SQL_perms = join ( " ',' " , $SQLa_perm );
$memberID = ( int ) $member -> ID ;
2016-09-22 16:38:29 +02:00
2013-11-11 00:09:27 +01:00
// Count this user's groups which can access the main site
$groupCount = DB :: query ( "
SELECT COUNT ( \ " Permission \" . \" ID \" )
FROM \ " Permission \"
INNER JOIN \ " Group \" ON \" Group \" . \" ID \" = \" Permission \" . \" GroupID \" AND \" Group \" . \" AccessAllSubsites \" = 1
INNER JOIN \ " Group_Members \" ON \" Group_Members \" . \" GroupID \" = \" Permission \" . \" GroupID \"
WHERE \ " Permission \" . \" Code \" IN (' $SQL_perms ')
AND \ " MemberID \" = { $memberID }
" )->value();
// 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();
// There has to be at least one that allows access.
return ( $groupCount + $roleCount > 0 );
}
2016-09-22 16:38:29 +02:00
2013-11-11 00:09:27 +01:00
/**
*
* @ var array
*/
private static $db = array (
'Title' => 'Varchar(255)' ,
'RedirectURL' => 'Varchar(255)' ,
'DefaultSite' => 'Boolean' ,
'Theme' => 'Varchar' ,
'Language' => 'Varchar(6)' ,
// Used to hide unfinished/private subsites from public view.
// If unset, will default to true
'IsPublic' => 'Boolean' ,
2016-09-22 16:38:29 +02:00
2013-11-11 00:09:27 +01:00
// Comma-separated list of disallowed page types
'PageTypeBlacklist' => 'Text' ,
);
/**
*
* @ var array
*/
private static $has_many = array (
2017-05-24 13:36:04 +02:00
'Domains' => SubsiteDomain :: class ,
2013-11-11 00:09:27 +01:00
);
2016-09-22 16:38:29 +02:00
2013-11-11 00:09:27 +01:00
/**
*
* @ var array
*/
private static $belongs_many_many = array (
2016-09-22 16:38:29 +02:00
" Groups " => " SilverStripe \\ Security \\ Group " ,
2013-11-11 00:09:27 +01:00
);
/**
*
* @ var array
*/
private static $defaults = array (
'IsPublic' => 1
);
/**
*
* @ var array
*/
private static $searchable_fields = array (
'Title' ,
'Domains.Domain' ,
2016-09-22 16:38:29 +02:00
'IsPublic' ,
2013-11-11 00:09:27 +01:00
);
2016-09-22 16:38:29 +02:00
2013-11-11 00:09:27 +01:00
/**
*
* @ var string
*/
private static $default_sort = " \" Title \" ASC " ;
2016-09-22 16:38:29 +02:00
2013-11-11 00:09:27 +01:00
/**
* @ todo Possible security issue , don ' t grant edit permissions to everybody .
* @ return boolean
*/
public function canEdit ( $member = false ) {
return true ;
}
2014-08-18 01:20:38 +02:00
2013-11-11 00:09:27 +01:00
/**
* Show the configuration fields for each subsite
2016-09-22 16:38:29 +02:00
*
2013-11-11 00:09:27 +01:00
* @ return FieldList
*/
public function getCMSFields () {
if ( $this -> ID != 0 ) {
$domainTable = new GridField (
2016-09-22 16:38:29 +02:00
" Domains " ,
_t ( 'Subsite.DomainsListTitle' , " Domains " ),
$this -> Domains (),
2013-11-11 00:09:27 +01:00
GridFieldConfig_RecordEditor :: create ( 10 )
);
} else {
$domainTable = new LiteralField (
2016-09-22 16:38:29 +02:00
'Domains' ,
2013-11-11 00:09:27 +01:00
'<p>' . _t ( 'Subsite.DOMAINSAVEFIRST' , 'You can only add domains after saving for the first time' ) . '</p>'
);
}
2016-09-22 16:38:29 +02:00
2013-11-11 00:09:27 +01:00
$languageSelector = new DropdownField (
2016-09-22 16:38:29 +02:00
'Language' ,
2013-11-11 00:09:27 +01:00
$this -> fieldLabel ( 'Language' ),
2017-05-24 15:09:13 +02:00
( new IntlLocales ) -> getLocales ()
2013-11-11 00:09:27 +01:00
);
2016-09-22 16:38:29 +02:00
2013-11-11 00:09:27 +01:00
$pageTypeMap = array ();
$pageTypes = SiteTree :: page_type_classes ();
foreach ( $pageTypes as $pageType ) {
2011-08-30 18:58:36 +02:00
$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' ),
2016-09-22 16:38:29 +02:00
new HeaderField ( 'ConfigurationHeader' , $this -> getClassName () . ' configuration' , 2 ),
2013-10-30 13:43:59 +01:00
new TextField ( 'Title' , $this -> fieldLabel ( 'Title' ), $this -> Title ),
2016-09-22 16:38:29 +02:00
2013-10-30 13:43:59 +01:00
new HeaderField (
2016-09-22 16:38:29 +02:00
'DomainsHeader' ,
_t ( 'Subsite.DomainsHeadline' , " Domains for this subsite " )
2013-10-30 13:43:59 +01:00
),
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 ),
2016-09-22 16:38:29 +02:00
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 (
2016-09-22 16:38:29 +02:00
'PageTypeBlacklist' ,
2011-08-30 18:58:36 +02:00
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 ;
}
2016-09-22 16:38:29 +02:00
2013-11-10 23:55:00 +01:00
/**
2016-09-22 16:38:29 +02:00
*
2013-11-10 23:55:00 +01:00
* @ param boolean $includerelations
* @ return array
*/
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 ;
}
2016-09-22 16:38:29 +02:00
2008-11-24 05:56:47 +01:00
/**
2013-11-11 00:09:27 +01:00
* Return the themes that can be used with this subsite , as an array of themecode => description
2016-09-22 16:38:29 +02:00
*
2013-11-11 00:09:27 +01:00
* @ return array
2008-11-24 05:56:47 +01:00
*/
2013-11-11 00:09:27 +01:00
public function allowedThemes () {
if ( $themes = $this -> stat ( 'allowed_themes' )) {
return ArrayLib :: valuekey ( $themes );
} else {
$themes = array ();
if ( is_dir ( '../themes/' )) {
foreach ( scandir ( '../themes/' ) as $theme ) {
if ( $theme [ 0 ] == '.' ) continue ;
$theme = strtok ( $theme , '_' );
$themes [ $theme ] = $theme ;
}
ksort ( $themes );
}
return $themes ;
}
2007-08-18 13:38:11 +02:00
}
2009-05-04 07:03:44 +02:00
2013-11-10 23:55:00 +01:00
/**
2013-11-11 00:09:27 +01:00
* @ return string Current locale of the subsite
2013-11-10 23:55:00 +01:00
*/
2013-11-11 00:09:27 +01:00
public function getLanguage () {
if ( $this -> getField ( 'Language' )) {
return $this -> getField ( 'Language' );
} else {
return i18n :: get_locale ();
}
2007-08-18 13:38:11 +02:00
}
2009-05-04 07:03:44 +02:00
2013-11-10 23:55:00 +01:00
/**
2016-09-22 16:38:29 +02:00
*
2013-11-11 00:09:27 +01:00
* @ return ValidationResult
2013-11-10 23:55:00 +01:00
*/
2013-11-11 00:09:27 +01:00
public function validate () {
$result = parent :: validate ();
if ( ! $this -> Title ) {
$result -> error ( _t ( 'Subsite.ValidateTitle' , 'Please add a "Title"' ));
}
return $result ;
2007-08-29 00:29:44 +02:00
}
2009-05-04 07:03:44 +02:00
2008-11-24 05:56:47 +01:00
/**
2013-11-11 00:09:27 +01:00
* Whenever a Subsite is written , rewrite the hostmap
2009-05-04 07:03:44 +02:00
*
2013-11-11 00:09:27 +01:00
* @ return void
2008-11-24 05:56:47 +01:00
*/
2013-11-11 00:09:27 +01:00
public function onAfterWrite () {
Subsite :: writeHostMap ();
parent :: onAfterWrite ();
2007-08-18 13:38:11 +02:00
}
2016-09-22 16:38:29 +02:00
2007-08-18 13:38:11 +02:00
/**
2013-11-11 00:09:27 +01:00
* Return the primary domain of this site . Tries to " normalize " the domain name ,
* by replacing potential wildcards .
2016-09-22 16:38:29 +02:00
*
2013-11-11 00:09:27 +01:00
* @ return string The full domain name of this subsite ( without protocol prefix )
2007-08-18 13:38:11 +02:00
*/
2013-11-11 00:09:27 +01:00
public function domain () {
if ( $this -> ID ) {
2017-05-24 13:36:04 +02:00
$domains = DataObject :: get ( SubsiteDomain :: class , " \" SubsiteID \" = $this->ID " , " \" IsPrimary \" DESC " , " " , 1 );
2013-11-11 00:09:27 +01:00
if ( $domains && $domains -> Count () > 0 ) {
$domain = $domains -> First () -> Domain ;
// If there are wildcards in the primary domain (not recommended), make some
// educated guesses about what to replace them with:
$domain = preg_replace ( '/\.\*$/' , " . $_SERVER[HTTP_HOST] " , $domain );
// Default to "subsite." prefix for first wildcard
// TODO Whats the significance of "subsite" in this context?!
$domain = preg_replace ( '/^\*\./' , " subsite. " , $domain );
// *Only* removes "intermediate" subdomains, so 'subdomain.www.domain.com' becomes 'subdomain.domain.com'
$domain = str_replace ( '.www.' , '.' , $domain );
2016-09-22 16:38:29 +02:00
2013-11-11 00:09:27 +01:00
return $domain ;
}
2016-09-22 16:38:29 +02:00
2013-11-11 00:09:27 +01:00
// SubsiteID = 0 is often used to refer to the main site, just return $_SERVER['HTTP_HOST']
} else {
return $_SERVER [ 'HTTP_HOST' ];
2008-11-30 23:31:08 +01:00
}
2007-08-18 13:38:11 +02:00
}
2016-09-22 16:38:29 +02:00
2007-08-18 13:38:11 +02:00
/**
2016-09-22 16:38:29 +02:00
*
2013-11-11 00:09:27 +01:00
* @ return string - The full domain name of this subsite ( without protocol prefix )
2007-08-18 13:38:11 +02:00
*/
2013-11-11 00:09:27 +01:00
public function getPrimaryDomain () {
return $this -> domain ();
2007-08-18 13:38:11 +02:00
}
2009-05-04 07:03:44 +02:00
2007-08-31 02:29:25 +02:00
/**
2016-09-22 16:38:29 +02:00
*
* @ return string
2007-08-31 02:29:25 +02:00
*/
2013-11-11 00:09:27 +01:00
public function absoluteBaseURL () {
return " http:// " . $this -> domain () . Director :: baseURL ();
2007-08-31 02:29:25 +02:00
}
2009-05-04 07:03:44 +02:00
2008-11-24 05:56:47 +01:00
/**
2013-11-11 00:09:27 +01:00
* @ todo getClassName is redundant , already stored as a database field ?
2008-11-24 05:56:47 +01:00
*/
2013-11-11 00:09:27 +01:00
public function getClassName () {
return $this -> class ;
2007-08-18 13:38:11 +02:00
}
2009-05-04 07:03:44 +02:00
2008-11-24 05:56:47 +01:00
/**
2013-11-11 00:09:27 +01:00
* Javascript admin action to duplicate this subsite
2016-09-22 16:38:29 +02:00
*
* @ return string - javascript
2008-11-24 05:56:47 +01:00
*/
2013-11-11 00:09:27 +01:00
public function adminDuplicate () {
$newItem = $this -> duplicate ();
$message = _t (
'Subsite.CopyMessage' ,
'Created a copy of {title}' ,
array ( 'title' => Convert :: raw2js ( $this -> Title ))
);
2016-09-22 16:38:29 +02:00
2013-11-11 00:09:27 +01:00
return <<< JS
statusMessage ( $message , 'good' );
$ ( 'Form_EditForm' ) . loadURLFromServer ( 'admin/subsites/show/$newItem->ID' );
JS ;
2007-08-18 13:38:11 +02:00
}
2009-05-04 07:03:44 +02:00
2013-11-10 23:55:00 +01:00
/**
2013-11-11 00:09:27 +01:00
* Make this subsite the current one
2013-11-10 23:55:00 +01:00
*/
2013-11-11 00:09:27 +01:00
public function activate () {
Subsite :: changeSubsite ( $this );
2007-08-18 13:38:11 +02:00
}
2009-05-04 07:03:44 +02:00
2011-09-05 17:24:48 +02:00
/**
2016-09-22 16:38:29 +02:00
*
2013-11-11 00:09:27 +01:00
* @ param array $permissionCodes
* @ return DataList
2011-09-05 17:24:48 +02:00
*/
2013-11-11 00:09:27 +01:00
public function getMembersByPermission ( $permissionCodes = array ( 'ADMIN' )){
2007-08-18 13:38:11 +02:00
if ( ! is_array ( $permissionCodes ))
2013-11-11 00:09:27 +01:00
user_error ( 'Permissions must be passed to Subsite::getMembersByPermission as an array' , E_USER_ERROR );
$SQL_permissionCodes = Convert :: raw2sql ( $permissionCodes );
2010-03-01 23:05:55 +01:00
2013-11-11 00:09:27 +01:00
$SQL_permissionCodes = join ( " ',' " , $SQL_permissionCodes );
2010-03-01 23:05:55 +01:00
2013-11-11 00:09:27 +01:00
return DataObject :: get (
2016-09-22 16:38:29 +02:00
'SilverStripe\\Security\\Member' ,
2013-11-11 00:09:27 +01:00
" \" Group \" . \" SubsiteID \" = $this->ID AND \" Permission \" . \" Code \" IN (' $SQL_permissionCodes ') " ,
'' ,
" LEFT JOIN \" Group_Members \" ON \" Member \" . \" ID \" = \" Group_Members \" . \" MemberID \"
LEFT JOIN \ " Group \" ON \" Group \" . \" ID \" = \" Group_Members \" . \" GroupID \"
LEFT JOIN \ " Permission \" ON \" Permission \" . \" GroupID \" = \" Group \" . \" ID \" "
);
2016-09-22 16:38:29 +02: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
*/
2013-11-10 23:55:00 +01:00
public 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
2016-09-22 16:38:29 +02:00
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
}
2007-08-18 13:38:11 +02:00
}