From 9a556a31f9f1ade92de156d95515e10e0265ff07 Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Mon, 1 Mar 2010 02:48:45 +0000 Subject: [PATCH] --- code/Subsite.php | 158 ++++++++++++------------------------- code/SubsiteAdmin.php | 19 +++-- code/SubsiteDomain.php | 11 +++ tests/SubsiteAdminTest.php | 2 +- tests/SubsiteTest.php | 48 ++++++++++- tests/SubsiteTest.yml | 36 ++++++++- 6 files changed, 153 insertions(+), 121 deletions(-) create mode 100644 code/SubsiteDomain.php diff --git a/code/Subsite.php b/code/Subsite.php index ac5157a..99edf20 100644 --- a/code/Subsite.php +++ b/code/Subsite.php @@ -23,12 +23,11 @@ class Subsite extends DataObject implements PermissionProvider { static $use_domain = false; static $db = array( - 'Subdomain' => 'Varchar', 'Title' => 'Varchar(255)', 'RedirectURL' => 'Varchar(255)', 'DefaultSite' => 'Boolean', 'Theme' => 'Varchar', - 'Domain' => 'Varchar', + // Used to hide unfinished/private subsites from public view. // If unset, will default to 'IsPublic' => 'Boolean' @@ -37,37 +36,15 @@ class Subsite extends DataObject implements PermissionProvider { static $has_one = array( ); - static $indexes = array( - 'Subdomain' => true, - 'Domain' => true - ); - static $defaults = array( 'IsPublic' => 1, ); - /** - * @var string $base_domain If {@link Domain} is not set for this subsite instance, - * default to this domain (without subdomain or protocol prefix). - */ - static $base_domain; - - /** - * @var string $default_subdomain If {@link Subdomain} is not set for this subsite instance, - * default to this domain (without domain or protocol prefix). - */ - static $default_subdomain; - /** * @var Subsite $cached_subsite Internal cache used by {@link currentSubsite()}. */ protected static $cached_subsite = null; - /** - * @var array $allowed_domains Numeric array of all domains which are selectable for (without their subdomain-parts or http:// prefix) - */ - public static $allowed_domains = array(); - /** * @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 @@ -76,29 +53,8 @@ class Subsite extends DataObject implements PermissionProvider { protected static $allowed_themes = array(); static function set_allowed_domains($domain){ - if(is_array($domain)){ - foreach($domain as $do){ - self::set_allowed_domains($do); - } - }else{ - self::$allowed_domains[] = $domain; - } - } - - /** - * Returns all domains (without their subdomain parts) - * which are allowed to be combined to the full URL - * (subdomain.domain). If no custom domains are set through - * {@link set_allowed_domains()}, will fall back to the {@link base_domain()}. - * - * @return array - */ - static function allowed_domains() { - if(self::$allowed_domains && count(self::$allowed_domains)) { - return self::$allowed_domains; - } else { - return array(self::base_domain()); - } + user_error('Subsite::set_allowed_domains() is deprecated; it is no longer necessary ' + . 'because users can now enter any domain name', E_USER_NOTICE); } static function set_allowed_themes($themes) { @@ -125,39 +81,25 @@ class Subsite extends DataObject implements PermissionProvider { } } - /** - * Return the base domain for this set of subsites. - * You can set this by setting Subsite::$base_domain, otherwise it defaults to HTTP_HOST - * - * @return string Domain name (without protocol prefix). - */ - static function base_domain() { - if(self::$base_domain) return self::$base_domain; - else return $_SERVER['HTTP_HOST']; - } - - /** - * Return the default domain of this set of subsites. Generally this will be the base domain, - * but you can also set Subsite::$default_subdomain to add a default prefix to this. - * - * @return string Domain name (without protocol prefix). - */ - static function default_domain() { - if(self::$default_subdomain) return self::$default_subdomain . '.' . self::base_domain(); - else return self::base_domain(); - } - /** * Return the domain of this site * * @return string Domain name including subdomain (without protocol prefix) */ function domain() { - $base = $this->Domain ? $this->Domain : self::base_domain(); - $sub = $this->Subdomain ? $this->Subdomain : self::$default_subdomain; - - if($sub) return "$sub.$base"; - else return $base; + if($this->ID) { + $domains = DataObject::get("SubsiteDomain", "SubsiteID = $this->ID", "IsPrimary DESC", + "", 1); + if($domains) { + $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); + $domain = preg_replace("/^\\*\\./","subsite.", $domain); + $domain = str_replace('.www.','.', $domain); + return $domain; + } + } } function absoluteBaseURL() { @@ -168,15 +110,23 @@ class Subsite extends DataObject implements PermissionProvider { * Show the configuration fields for each subsite */ function getCMSFields() { + $domainTable = new TableField("Domains", "SubsiteDomain", + array("Domain" => "Domain (use * as a wildcard)", "IsPrimary" => "Primary domain?"), + array("Domain" => "TextField", "IsPrimary" => "CheckboxField"), + null, "SubsiteDomain.SubsiteID", $this->ID); + + $domainTable->setExtraData(array( + 'SubsiteID' => $this->ID ? $this->ID : '$RecordID', + )); + $fields = new FieldSet( new TabSet('Root', new Tab('Configuration', new HeaderField($this->getClassName() . ' configuration', 2), new TextField('Title', 'Name of subsite:', $this->Title), - new FieldGroup('URL', - new TextField('Subdomain',"Subdomain (without domain or protocol)", $this->Subdomain), - new DropdownField('Domain','.', ArrayLib::valuekey(self::allowed_domains()), $this->Domain) - ), + + new HeaderField("Domains for this subsite"), + $domainTable, // new TextField('RedirectURL', 'Redirect to URL', $this->RedirectURL), new CheckboxField('DefaultSite', 'Default site', $this->DefaultSite), new CheckboxField('IsPublic', 'Enable public access', $this->IsPublic), @@ -303,37 +253,27 @@ JS; } /** - * Get a matching subsite for the domain defined in HTTP_HOST. + * Get a matching subsite for the given host, or for the current HTTP_HOST. + * + * @param $host The host to find the subsite for. If not specified, $_SERVER['HTTP_HOST'] + * is used. * * @return int Subsite ID */ - static function getSubsiteIDForDomain() { - $domainNameParts = explode('.', $_SERVER['HTTP_HOST']); - - if($domainNameParts[0] == 'www') array_shift($domainNameParts); - - $SQL_subdomain = Convert::raw2sql(array_shift($domainNameParts)); - $SQL_domain = join('.', Convert::raw2sql($domainNameParts)); + static function getSubsiteIDForDomain($host = null) { + if($host == null) $host = $_SERVER['HTTP_HOST']; - if(defined('DB::USE_ANSI_SQL')) - $q="\""; - else $q='`'; - - $subsite = null; - if(self::$use_domain) { - $subsite = DataObject::get_one('Subsite', "{$q}Subdomain{$q} = '$SQL_subdomain' AND {$q}Domain{$q} = '$SQL_domain' AND {$q}IsPublic{$q} = 1"); - } - if(!$subsite) { - $subsite = DataObject::get_one('Subsite', "{$q}Subdomain{$q} = '$SQL_subdomain' AND {$q}IsPublic{$q} = 1"); - } - if(!$subsite) { - $subsite = DataObject::get_one('Subsite', "{$q}DefaultSite{$q} = 1 AND {$q}IsPublic{$q} = 1"); - } + $host = str_replace('www.','',$host); + $SQL_host = Convert::raw2sql($host); - if($subsite) { - // This will need to be updated to use the current theme system - // SSViewer::setCurrentTheme($subsite->Theme); - return $subsite->ID; + $matchingDomains = DataObject::get("SubsiteDomain", "'$SQL_host' LIKE replace({$q}SubsiteDomain{$q}.{$q}Domain{$q},'*','%')", + "{$q}IsPrimary{$q} DESC", "INNER JOIN {$q}Subsite{$q} ON {$q}Subsite{$q}.{$q}ID{$q} = {$q}SubsiteDomain{$q}.{$q}SubsiteID{$q} AND + {$q}Subsite{$q}.{$q}IsPublic{$q}"); + + if($matchingDomains) { + $subsiteIDs = array_unique($matchingDomains->column('SubsiteID')); + if(sizeof($subsiteIDs) > 1) user_error("Multiple subsites match '$host'", E_USER_WARNING); + return $subsiteIDs[0]; } } @@ -543,14 +483,16 @@ class Subsite_Template extends Subsite { /** * Create an instance of this template, with the given title & subdomain */ - function createInstance($title, $subdomain) { + function createInstance($title, $domain) { $intranet = Object::create('Subsite'); $intranet->Title = $title; - $intranet->Domain = $this->Domain; - $intranet->Subdomain = $subdomain; $intranet->TemplateID = $this->ID; $intranet->write(); - + + $intranetDomain = Object::create('SubsiteDomain'); + $intranetDomain->SubsiteID = $intranet->ID; + $intranetDomain->Domain = $domain; + $intranetDomain->write(); $oldSubsiteID = Session::get('SubsiteID'); self::changeSubsite($this->ID); diff --git a/code/SubsiteAdmin.php b/code/SubsiteAdmin.php index e1c8e97..7bd358e 100644 --- a/code/SubsiteAdmin.php +++ b/code/SubsiteAdmin.php @@ -68,7 +68,7 @@ class SubsiteAdmin extends GenericDataAdmin { $numIntranets++; $evenOdd = ($numIntranets % 2) ? 'odd':'even'; $prefix = ($intranet instanceof Subsite_Template) ? " * " : ""; - $html .= "ID}\">$prefix{$intranet->Title}ID}\">{$intranet->Subdomain}.{$intranet->Domain}"; + $html .= "ID}\">$prefix{$intranet->Title}ID}\">{$intranet->domain()}"; } $html .= ""; return $html; @@ -88,7 +88,7 @@ class SubsiteAdmin extends GenericDataAdmin { return new Form($this, 'AddSubsiteForm', new FieldSet( new TextField('Name', 'Name:'), - new TextField('Subdomain', 'Subdomain:'), + new TextField('Domain', 'Domain name:'), new DropdownField('Type', 'Type', array( 'subsite' => 'New site', 'template' => 'New template', @@ -111,13 +111,13 @@ class SubsiteAdmin extends GenericDataAdmin { } function addintranet($data, $form) { - if($data['Name'] && ($data['Subdomain'] || $data['Type'] == 'template')) { + if($data['Name'] && ($data['Domain'] || $data['Type'] == 'template')) { if(isset($data['TemplateID']) && $data['TemplateID']) { $template = DataObject::get_by_id('Subsite_Template', $data['TemplateID']); } else { $template = null; } - + // Create intranet from existing template switch($data['Type']) { case 'template': @@ -130,12 +130,17 @@ class SubsiteAdmin extends GenericDataAdmin { case 'subsite': default: - if($template) $intranet = $template->createInstance($data['Name'], $data['Subdomain']); + if($template) $intranet = $template->createInstance($data['Name'], $data['Domain']); else { $intranet = new Subsite(); $intranet->Title = $data['Name']; - $intranet->Subdomain = $data['Subdomain']; $intranet->write(); + + $newSubsiteDomain = new SubsiteDomain(); + $newSubsiteDomain->SubsiteID = $intranet->ID; + $newSubsiteDomain->write(); + $newSubsiteDomain->Domain = $data['Domain']; + $newSubsiteDomain->write(); } break; } @@ -143,7 +148,7 @@ class SubsiteAdmin extends GenericDataAdmin { Director::redirect('admin/subsites/show/' . $intranet->ID); } else { if($data['Type'] == 'template') echo "You must provide a name for your new template."; - else echo "You must provide a name and subdomain for your new site."; + else echo "You must provide a name and domain for your new site."; } } diff --git a/code/SubsiteDomain.php b/code/SubsiteDomain.php new file mode 100644 index 0000000..4db7483 --- /dev/null +++ b/code/SubsiteDomain.php @@ -0,0 +1,11 @@ + "Varchar(255)", + "IsPrimary" => "Boolean", + ); + static $has_one = array( + "Subsite" => "Subsite", + ); +} \ No newline at end of file diff --git a/tests/SubsiteAdminTest.php b/tests/SubsiteAdminTest.php index b734258..c9a1df7 100644 --- a/tests/SubsiteAdminTest.php +++ b/tests/SubsiteAdminTest.php @@ -78,7 +78,7 @@ class SubsiteAdminTest extends SapphireTest { $response = $form->testSubmission('addintranet', array( 'Name' => 'Test Intranet', - 'Subdomain' => 'Test', + 'Domain' => 'test.example.com', 'TemplateID' => 1, 'AdminEmail' => '', 'AdminName' => '', diff --git a/tests/SubsiteTest.php b/tests/SubsiteTest.php index 340a67e..ffc0639 100644 --- a/tests/SubsiteTest.php +++ b/tests/SubsiteTest.php @@ -58,13 +58,13 @@ class SubsiteTest extends SapphireTest { } // Create a new site - $subsite = $template->createInstance('My Site', 'something'); + $subsite = $template->createInstance('My Site', 'something.test.com'); // Check title $this->assertEquals($subsite->Title, 'My Site'); // Check that domain generation is working - $this->assertEquals($subsite->domain(), 'something.test.com'); + $this->assertEquals('something.test.com', $subsite->domain()); // Another test that changeSubsite is working Subsite::changeSubsite($subsite->ID); @@ -85,6 +85,50 @@ class SubsiteTest extends SapphireTest { } + /** + * Confirm that domain lookup is working + */ + function testDomainLookup() { + $this->assertEquals($this->idFromFixture('Subsite','domaintest1'), + Subsite::getSubsiteIDForDomain('one.example.org')); + $this->assertEquals($this->idFromFixture('Subsite','domaintest1'), + Subsite::getSubsiteIDForDomain('one.localhost')); + + $this->assertEquals($this->idFromFixture('Subsite','domaintest2'), + Subsite::getSubsiteIDForDomain('two.mysite.com')); + $this->assertEquals($this->idFromFixture('Subsite','domaintest2'), + Subsite::getSubsiteIDForDomain('other.mysite.com')); + + $this->assertNull(Subsite::getSubsiteIDForDomain('other.example.com')); + $this->assertNull(Subsite::getSubsiteIDForDomain('two.example.com')); + } + + /** + * Test the Subsite->domain() method + */ + function testDefaultDomain() { + $this->assertEquals('one.example.org', + $this->objFromFixture('Subsite','domaintest1')->domain()); + + $this->assertEquals('two.mysite.com', + $this->objFromFixture('Subsite','domaintest2')->domain()); + + $originalHTTPHost = $_SERVER['HTTP_HOST']; + + $_SERVER['HTTP_HOST'] = "www.example.org"; + $this->assertEquals('three.example.org', + $this->objFromFixture('Subsite','domaintest3')->domain()); + + $_SERVER['HTTP_HOST'] = "mysite.example.org"; + $this->assertEquals('three.mysite.example.org', + $this->objFromFixture('Subsite','domaintest3')->domain()); + + + $_SERVER['HTTP_HOST'] = $originalHTTPHost; + + } + + /** * Only the published content from the template should publish. */ diff --git a/tests/SubsiteTest.yml b/tests/SubsiteTest.yml index 4f13228..df816d4 100644 --- a/tests/SubsiteTest.yml +++ b/tests/SubsiteTest.yml @@ -1,13 +1,43 @@ Subsite_Template: main: Title: Template - Domain: test.com subsite1: Title: Subsite1 Template - Subdomain: subsite1 subsite2: Title: Subsite2 Template - Subdomain: subsite2 +Subsite: + domaintest1: + Title: Test 1 + domaintest2: + Title: Test 2 + domaintest3: + Title: Test 3 +SubsiteDomain: + subsite1: + SubsiteID: =>Subsite_Template.subsite1 + Domain: subsite1.* + subsite2: + SubsiteID: =>Subsite_Template.subsite2 + Domain: subsite2.* + dt1a: + SubsiteID: =>Subsite.domaintest1 + Domain: one.example.org + IsPrimary: 1 + dt1b: + SubsiteID: =>Subsite.domaintest1 + Domain: one.* + dt2a: + SubsiteID: =>Subsite.domaintest2 + Domain: two.mysite.com + IsPrimary: 1 + dt2b: + SubsiteID: =>Subsite.domaintest2 + Domain: *.mysite.com + dt3: + SubsiteID: =>Subsite.domaintest3 + Domain: three.* + IsPrimary: 1 + SiteTree: home: Title: Home