mirror of
https://github.com/silverstripe/silverstripe-subsites
synced 2024-10-22 11:05:55 +02:00
Merge branch '2.2' into 2.3
This commit is contained in:
commit
5d015c7a96
50
README.md
50
README.md
@ -96,7 +96,55 @@ In some Browsers the SubsiteID is visible if you hover over the "Edit" link in t
|
|||||||
|
|
||||||
### Subsite-specific themes
|
### Subsite-specific themes
|
||||||
|
|
||||||
Download a second theme from http://www.silverstripe.com/themes/ and put it in your themes folder. Open admin/subsites?flush=1 and select one of your subsites from the menu on the bottom-left. You should see a Theme dropdown in the subsite details, and it should list both your original theme and the new theme. Select the new theme in the dropdown. Now, this subsite will use a different theme from the main site.
|
Download a second theme from http://www.silverstripe.com/themes/ and put it in your themes folder. Open
|
||||||
|
admin/subsites?flush=1 and select one of your subsites from the menu on the bottom-left. You should see a
|
||||||
|
Theme dropdown in the subsite details, and it should list both your original theme and the new theme. Select the new
|
||||||
|
theme in the dropdown. Now, this subsite will use a different theme from the main site.
|
||||||
|
|
||||||
|
#### Cascading themes
|
||||||
|
|
||||||
|
In SilverStripe 4 themes will resolve theme files by looking through a list of themes (see the documentation on
|
||||||
|
[creating your own theme](https://docs.silverstripe.org/en/4/developer_guides/templates/themes/#developing-your-own-theme)).
|
||||||
|
Subsites will inherit this configuration for the order of themes. Choosing a theme for a Subsite will set the list of
|
||||||
|
themes to that chosen theme, and all themes that are defined below the chosen theme in priority. For example, with a
|
||||||
|
theme configuration as follows:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
SilverStripe\View\SSViewer:
|
||||||
|
themes:
|
||||||
|
- '$public'
|
||||||
|
- 'my-theme'
|
||||||
|
- 'watea'
|
||||||
|
- 'starter'
|
||||||
|
- '$default'
|
||||||
|
```
|
||||||
|
|
||||||
|
Choosing `watea` in your Subsite will create a cascading config as follows:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
themes:
|
||||||
|
- 'watea'
|
||||||
|
- '$public'
|
||||||
|
- 'starter'
|
||||||
|
- '$default'
|
||||||
|
```
|
||||||
|
|
||||||
|
You may also completely define your own cascading theme lists for CMS users to choose as theme options for their
|
||||||
|
subsite:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
SilverStripe\Subsites\Service\ThemeResolver:
|
||||||
|
theme_options:
|
||||||
|
normal:
|
||||||
|
- '$public'
|
||||||
|
- 'watea'
|
||||||
|
- 'starter'
|
||||||
|
- '$default'
|
||||||
|
special:
|
||||||
|
- 'my-theme'
|
||||||
|
- 'starter'
|
||||||
|
- '$default'
|
||||||
|
```
|
||||||
|
|
||||||
### Limit available themes for a subsite
|
### Limit available themes for a subsite
|
||||||
|
|
||||||
|
@ -26,6 +26,7 @@ use SilverStripe\Security\Member;
|
|||||||
use SilverStripe\Security\Security;
|
use SilverStripe\Security\Security;
|
||||||
use SilverStripe\SiteConfig\SiteConfig;
|
use SilverStripe\SiteConfig\SiteConfig;
|
||||||
use SilverStripe\Subsites\Model\Subsite;
|
use SilverStripe\Subsites\Model\Subsite;
|
||||||
|
use SilverStripe\Subsites\Service\ThemeResolver;
|
||||||
use SilverStripe\Subsites\State\SubsiteState;
|
use SilverStripe\Subsites\State\SubsiteState;
|
||||||
use SilverStripe\View\SSViewer;
|
use SilverStripe\View\SSViewer;
|
||||||
|
|
||||||
@ -390,10 +391,11 @@ class SiteTreeSubsites extends DataExtension
|
|||||||
*/
|
*/
|
||||||
public static function contentcontrollerInit($controller)
|
public static function contentcontrollerInit($controller)
|
||||||
{
|
{
|
||||||
|
/** @var Subsite $subsite */
|
||||||
$subsite = Subsite::currentSubsite();
|
$subsite = Subsite::currentSubsite();
|
||||||
|
|
||||||
if ($subsite && $subsite->Theme) {
|
if ($subsite && $subsite->Theme) {
|
||||||
SSViewer::add_themes([$subsite->Theme]);
|
SSViewer::set_themes(ThemeResolver::singleton()->getThemeList($subsite));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($subsite && i18n::getData()->validate($subsite->Language)) {
|
if ($subsite && i18n::getData()->validate($subsite->Language)) {
|
||||||
|
@ -28,6 +28,7 @@ use SilverStripe\Security\Group;
|
|||||||
use SilverStripe\Security\Member;
|
use SilverStripe\Security\Member;
|
||||||
use SilverStripe\Security\Permission;
|
use SilverStripe\Security\Permission;
|
||||||
use SilverStripe\Security\Security;
|
use SilverStripe\Security\Security;
|
||||||
|
use SilverStripe\Subsites\Service\ThemeResolver;
|
||||||
use SilverStripe\Subsites\State\SubsiteState;
|
use SilverStripe\Subsites\State\SubsiteState;
|
||||||
use SilverStripe\Versioned\Versioned;
|
use SilverStripe\Versioned\Versioned;
|
||||||
use UnexpectedValueException;
|
use UnexpectedValueException;
|
||||||
@ -183,7 +184,6 @@ class Subsite extends DataObject
|
|||||||
/**
|
/**
|
||||||
* Gets the subsite currently set in the session.
|
* Gets the subsite currently set in the session.
|
||||||
*
|
*
|
||||||
* @uses ControllerSubsites->controllerAugmentInit()
|
|
||||||
* @return DataObject The current Subsite
|
* @return DataObject The current Subsite
|
||||||
*/
|
*/
|
||||||
public static function currentSubsite()
|
public static function currentSubsite()
|
||||||
@ -788,7 +788,7 @@ class Subsite extends DataObject
|
|||||||
*/
|
*/
|
||||||
public function allowedThemes()
|
public function allowedThemes()
|
||||||
{
|
{
|
||||||
if ($themes = self::$allowed_themes) {
|
if (($themes = self::$allowed_themes) || ($themes = ThemeResolver::singleton()->getCustomThemeOptions())) {
|
||||||
return ArrayLib::valuekey($themes);
|
return ArrayLib::valuekey($themes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -875,7 +875,7 @@ class Subsite extends DataObject
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If there are no objects, default to the current hostname
|
// If there are no objects, default to the current hostname
|
||||||
return $_SERVER['HTTP_HOST'];
|
return Director::host();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -186,7 +186,7 @@ class SubsiteDomain extends DataObject
|
|||||||
*/
|
*/
|
||||||
public function getSubstitutedDomain()
|
public function getSubstitutedDomain()
|
||||||
{
|
{
|
||||||
$currentHost = $_SERVER['HTTP_HOST'];
|
$currentHost = Director::host();
|
||||||
|
|
||||||
// If there are wildcards in the primary domain (not recommended), make some
|
// If there are wildcards in the primary domain (not recommended), make some
|
||||||
// educated guesses about what to replace them with:
|
// educated guesses about what to replace them with:
|
||||||
|
99
src/Service/ThemeResolver.php
Normal file
99
src/Service/ThemeResolver.php
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\Subsites\Service;
|
||||||
|
|
||||||
|
use SilverStripe\Core\Config\Configurable;
|
||||||
|
use SilverStripe\Core\Injector\Injectable;
|
||||||
|
use SilverStripe\Subsites\Model\Subsite;
|
||||||
|
use SilverStripe\View\SSViewer;
|
||||||
|
|
||||||
|
class ThemeResolver
|
||||||
|
{
|
||||||
|
use Injectable;
|
||||||
|
use Configurable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cascading definitions for themes, keyed by the name they should appear under in the CMS. For example:
|
||||||
|
*
|
||||||
|
* [
|
||||||
|
* 'theme-1' => [
|
||||||
|
* '$public',
|
||||||
|
* 'starter',
|
||||||
|
* '$default',
|
||||||
|
* ],
|
||||||
|
* 'theme-2' => [
|
||||||
|
* 'custom',
|
||||||
|
* 'watea',
|
||||||
|
* 'starter',
|
||||||
|
* '$public',
|
||||||
|
* '$default',
|
||||||
|
* ]
|
||||||
|
* ]
|
||||||
|
*
|
||||||
|
* @config
|
||||||
|
* @var null|array[]
|
||||||
|
*/
|
||||||
|
private static $theme_options;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the list of themes for the given sub site that can be given to SSViewer::set_themes
|
||||||
|
*
|
||||||
|
* @param Subsite $site
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getThemeList(Subsite $site)
|
||||||
|
{
|
||||||
|
$themes = array_values(SSViewer::get_themes());
|
||||||
|
$siteTheme = $site->Theme;
|
||||||
|
|
||||||
|
if (!$siteTheme) {
|
||||||
|
return $themes;
|
||||||
|
}
|
||||||
|
|
||||||
|
$customOptions = $this->config()->get('theme_options');
|
||||||
|
if ($customOptions && isset($customOptions[$siteTheme])) {
|
||||||
|
return $customOptions[$siteTheme];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure themes don't cascade "up" the list
|
||||||
|
$index = array_search($siteTheme, $themes);
|
||||||
|
|
||||||
|
if ($index > 0) {
|
||||||
|
// 4.0 didn't have support for themes in the public webroot
|
||||||
|
$constant = SSViewer::class . '::PUBLIC_THEME';
|
||||||
|
$publicConstantDefined = defined($constant);
|
||||||
|
|
||||||
|
// Check if the default is public themes
|
||||||
|
$publicDefault = $publicConstantDefined && $themes[0] === SSViewer::PUBLIC_THEME;
|
||||||
|
|
||||||
|
// Take only those that appear after theme chosen (non-inclusive)
|
||||||
|
$themes = array_slice($themes, $index + 1);
|
||||||
|
|
||||||
|
// Add back in public
|
||||||
|
if ($publicDefault) {
|
||||||
|
array_unshift($themes, SSViewer::PUBLIC_THEME);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add our theme
|
||||||
|
array_unshift($themes, $siteTheme);
|
||||||
|
|
||||||
|
return $themes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of custom cascading theme definitions if available
|
||||||
|
*
|
||||||
|
* @return null|array
|
||||||
|
*/
|
||||||
|
public function getCustomThemeOptions()
|
||||||
|
{
|
||||||
|
$config = $this->config()->get('theme_options');
|
||||||
|
|
||||||
|
if (!$config) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_keys($config);
|
||||||
|
}
|
||||||
|
}
|
143
tests/php/Service/ThemeResolverTest.php
Normal file
143
tests/php/Service/ThemeResolverTest.php
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\Subsites\Tests\Service;
|
||||||
|
|
||||||
|
use SilverStripe\Core\Config\Config;
|
||||||
|
use SilverStripe\Dev\SapphireTest;
|
||||||
|
use SilverStripe\Subsites\Model\Subsite;
|
||||||
|
use SilverStripe\Subsites\Service\ThemeResolver;
|
||||||
|
use SilverStripe\View\SSViewer;
|
||||||
|
|
||||||
|
class ThemeResolverTest extends SapphireTest
|
||||||
|
{
|
||||||
|
protected $themeList = [
|
||||||
|
'$public',
|
||||||
|
'custom',
|
||||||
|
'main',
|
||||||
|
'backup',
|
||||||
|
SSViewer::DEFAULT_THEME,
|
||||||
|
];
|
||||||
|
|
||||||
|
protected function setUp()
|
||||||
|
{
|
||||||
|
parent::setUp();
|
||||||
|
|
||||||
|
// Setup known theme config
|
||||||
|
Config::modify()->set(SSViewer::class, 'themes', $this->themeList);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSubsiteWithoutThemeReturnsDefaultThemeList()
|
||||||
|
{
|
||||||
|
$subsite = new Subsite();
|
||||||
|
$resolver = new ThemeResolver();
|
||||||
|
|
||||||
|
$this->assertSame($this->themeList, $resolver->getThemeList($subsite));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSubsiteWithCustomThemePrependsToList()
|
||||||
|
{
|
||||||
|
$subsite = new Subsite();
|
||||||
|
$subsite->Theme = 'subsite';
|
||||||
|
|
||||||
|
$resolver = new ThemeResolver();
|
||||||
|
|
||||||
|
$expected = array_merge(['subsite'], $this->themeList);
|
||||||
|
|
||||||
|
$this->assertSame($expected, $resolver->getThemeList($subsite));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSubsiteWithCustomThemeDoesNotCascadeUpTheList()
|
||||||
|
{
|
||||||
|
$subsite = new Subsite();
|
||||||
|
$subsite->Theme = 'main';
|
||||||
|
|
||||||
|
$resolver = new ThemeResolver();
|
||||||
|
|
||||||
|
$expected = [
|
||||||
|
'main', // 'main' is moved to the top
|
||||||
|
'$public', // $public is preserved
|
||||||
|
// Anything above 'main' is removed
|
||||||
|
'backup',
|
||||||
|
SSViewer::DEFAULT_THEME,
|
||||||
|
];
|
||||||
|
|
||||||
|
$this->assertSame($expected, $resolver->getThemeList($subsite));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider customThemeDefinitionsAreRespectedProvider
|
||||||
|
*/
|
||||||
|
public function testCustomThemeDefinitionsAreRespected($themeOptions, $siteTheme, $expected)
|
||||||
|
{
|
||||||
|
Config::modify()->set(ThemeResolver::class, 'theme_options', $themeOptions);
|
||||||
|
|
||||||
|
$subsite = new Subsite();
|
||||||
|
$subsite->Theme = $siteTheme;
|
||||||
|
|
||||||
|
$resolver = new ThemeResolver();
|
||||||
|
|
||||||
|
$this->assertSame($expected, $resolver->getThemeList($subsite));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function customThemeDefinitionsAreRespectedProvider()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
// Simple
|
||||||
|
[
|
||||||
|
['test' => $expected = [
|
||||||
|
'subsite',
|
||||||
|
'backup',
|
||||||
|
'$public',
|
||||||
|
SSViewer::DEFAULT_THEME,
|
||||||
|
]],
|
||||||
|
'test',
|
||||||
|
$expected
|
||||||
|
],
|
||||||
|
// Many options
|
||||||
|
[
|
||||||
|
[
|
||||||
|
'aye' => [
|
||||||
|
'aye',
|
||||||
|
'thing',
|
||||||
|
SSViewer::DEFAULT_THEME,
|
||||||
|
],
|
||||||
|
'bee' => $expected = [
|
||||||
|
'subsite',
|
||||||
|
'backup',
|
||||||
|
'$public',
|
||||||
|
SSViewer::DEFAULT_THEME,
|
||||||
|
],
|
||||||
|
'sea' => [
|
||||||
|
'mer',
|
||||||
|
'ocean',
|
||||||
|
SSViewer::DEFAULT_THEME,
|
||||||
|
],
|
||||||
|
],
|
||||||
|
'bee',
|
||||||
|
$expected
|
||||||
|
],
|
||||||
|
// Conflicting with root definitions
|
||||||
|
[
|
||||||
|
['main' => $expected = [
|
||||||
|
'subsite',
|
||||||
|
'backup',
|
||||||
|
'$public',
|
||||||
|
SSViewer::DEFAULT_THEME,
|
||||||
|
]],
|
||||||
|
'main',
|
||||||
|
$expected
|
||||||
|
],
|
||||||
|
// Declaring a theme specifically should still work
|
||||||
|
[
|
||||||
|
['test' => [
|
||||||
|
'subsite',
|
||||||
|
'backup',
|
||||||
|
'$public',
|
||||||
|
SSViewer::DEFAULT_THEME,
|
||||||
|
]],
|
||||||
|
'other',
|
||||||
|
array_merge(['other'], $this->themeList)
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,8 @@ use SilverStripe\CMS\Forms\SiteTreeURLSegmentField;
|
|||||||
use SilverStripe\CMS\Model\SiteTree;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
use SilverStripe\Control\Director;
|
use SilverStripe\Control\Director;
|
||||||
use SilverStripe\Core\Config\Config;
|
use SilverStripe\Core\Config\Config;
|
||||||
|
use SilverStripe\Core\Convert;
|
||||||
|
use SilverStripe\Core\Injector\Injector;
|
||||||
use SilverStripe\ErrorPage\ErrorPage;
|
use SilverStripe\ErrorPage\ErrorPage;
|
||||||
use SilverStripe\Forms\FieldList;
|
use SilverStripe\Forms\FieldList;
|
||||||
use SilverStripe\Security\Member;
|
use SilverStripe\Security\Member;
|
||||||
@ -16,6 +18,7 @@ use SilverStripe\SiteConfig\SiteConfig;
|
|||||||
use SilverStripe\Subsites\Extensions\SiteTreeSubsites;
|
use SilverStripe\Subsites\Extensions\SiteTreeSubsites;
|
||||||
use SilverStripe\Subsites\Model\Subsite;
|
use SilverStripe\Subsites\Model\Subsite;
|
||||||
use SilverStripe\Subsites\Pages\SubsitesVirtualPage;
|
use SilverStripe\Subsites\Pages\SubsitesVirtualPage;
|
||||||
|
use SilverStripe\Subsites\Service\ThemeResolver;
|
||||||
use SilverStripe\Subsites\Tests\SiteTreeSubsitesTest\TestClassA;
|
use SilverStripe\Subsites\Tests\SiteTreeSubsitesTest\TestClassA;
|
||||||
use SilverStripe\Subsites\Tests\SiteTreeSubsitesTest\TestClassB;
|
use SilverStripe\Subsites\Tests\SiteTreeSubsitesTest\TestClassB;
|
||||||
use SilverStripe\Subsites\Tests\SiteTreeSubsitesTest\TestErrorPage;
|
use SilverStripe\Subsites\Tests\SiteTreeSubsitesTest\TestErrorPage;
|
||||||
@ -399,41 +402,25 @@ class SiteTreeSubsitesTest extends BaseSubsiteTest
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testIfSubsiteThemeIsSetToThemeList()
|
public function testThemeResolverIsUsedForSettingThemeList()
|
||||||
{
|
{
|
||||||
$defaultThemes = ['default'];
|
$firstResolver = $this->createMock(ThemeResolver::class);
|
||||||
SSViewer::set_themes($defaultThemes);
|
$firstResolver->expects($this->never())->method('getThemeList');
|
||||||
|
Injector::inst()->registerService($firstResolver, ThemeResolver::class);
|
||||||
|
|
||||||
$subsitePage = $this->objFromFixture(Page::class, 'home');
|
$subsitePage = $this->objFromFixture(Page::class, 'home');
|
||||||
Subsite::changeSubsite($subsitePage->SubsiteID);
|
Subsite::changeSubsite($subsitePage->SubsiteID);
|
||||||
$controller = ModelAsController::controller_for($subsitePage);
|
$controller = ModelAsController::controller_for($subsitePage);
|
||||||
SiteTree::singleton()->extend('contentcontrollerInit', $controller);
|
SiteTree::singleton()->extend('contentcontrollerInit', $controller);
|
||||||
|
|
||||||
$this->assertEquals(
|
$secondResolver = $this->createMock(ThemeResolver::class);
|
||||||
SSViewer::get_themes(),
|
$secondResolver->expects($this->once())->method('getThemeList');
|
||||||
$defaultThemes,
|
Injector::inst()->registerService($secondResolver, ThemeResolver::class);
|
||||||
'Themes should not be modified when Subsite has no theme defined'
|
|
||||||
);
|
|
||||||
|
|
||||||
$pageWithTheme = $this->objFromFixture(Page::class, 'subsite1_home');
|
$subsitePage = $this->objFromFixture(Page::class, 'subsite1_home');
|
||||||
Subsite::changeSubsite($pageWithTheme->SubsiteID);
|
Subsite::changeSubsite($subsitePage->SubsiteID);
|
||||||
$controller = ModelAsController::controller_for($pageWithTheme);
|
$controller = ModelAsController::controller_for($subsitePage);
|
||||||
SiteTree::singleton()->extend('contentcontrollerInit', $controller);
|
SiteTree::singleton()->extend('contentcontrollerInit', $controller);
|
||||||
$subsiteTheme = $pageWithTheme->Subsite()->Theme;
|
|
||||||
|
|
||||||
$allThemes = SSViewer::get_themes();
|
|
||||||
|
|
||||||
$this->assertContains(
|
|
||||||
$subsiteTheme,
|
|
||||||
$allThemes,
|
|
||||||
'Themes should be modified when Subsite has theme defined'
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->assertEquals(
|
|
||||||
$subsiteTheme,
|
|
||||||
array_shift($allThemes),
|
|
||||||
'Subsite theme should be prepeded to theme list'
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function provideAlternateAbsoluteLink()
|
public function provideAlternateAbsoluteLink()
|
||||||
|
Loading…
Reference in New Issue
Block a user