Compare commits

..

222 Commits
2.1.0 ... 2

Author SHA1 Message Date
Steve Boyd
0a3056267c Merge branch '2.8' into 2 2024-07-11 16:16:35 +12:00
Guy Sartorelli
92363c4ca8
Merge pull request #583 from creative-commoners/pulls/2.8/composer
DEP Bump minimum version of framework
2024-07-11 10:00:14 +12:00
Steve Boyd
5025c32fc7 DEP Bump minimum version of framework 2024-07-10 10:48:46 +12:00
github-actions
dcc39392cb Merge branch '2.8' into 2 2024-02-07 11:31:02 +00:00
Guy Sartorelli
d66e723a51
TLN Update translations (#557) 2024-02-07 16:13:02 +13:00
github-actions
23b9db798d Merge branch '2.8' into 2 2023-11-08 11:30:53 +00:00
Guy Sartorelli
83600d758a
Merge pull request #550 from creative-commoners/pulls/2.8/tx-1699241375
TLN Update translations
2023-11-07 11:14:25 +13:00
Steve Boyd
dd3073f41a TLN Update translations 2023-11-06 16:29:35 +13:00
Guy Sartorelli
4e292b8e89
Merge pull request #539 from creative-commoners/pulls/2.8/revert
Revert "Switch to listing views of subsite-filtered sections"
2023-09-07 09:44:32 +12:00
Steve Boyd
e020766095 Revert "Switch to listing views of subsite-filtered sections"
This reverts commit 8ba7070b02.
2023-09-06 17:26:00 +12:00
Guy Sartorelli
4049f7a12d
Merge pull request #490 from micschk/patch-1
Switch to listing views of subsite-filtered sections
2023-09-01 14:58:22 +12:00
github-actions
40ee7c76db Merge branch '2.8' into 2 2023-08-23 11:31:18 +00:00
Guy Sartorelli
178b521c54
ENH Update translations (#534) 2023-08-21 13:16:59 +12:00
Guy Sartorelli
7d1431df5e
Merge pull request #533 from lekoala/3-1
don't trigger permissionFailure when it's not needed
2023-08-18 11:15:48 +12:00
Thomas Portelange
39de47167d don't trigger permissionFailure when it's not needed
(cherry picked from commit a19ed3ed54d4927ef96da8ce96f032dc3b0d897e)
2023-08-17 06:44:29 +02:00
github-actions
3ec16bbe56 Merge branch '2.8' into 2 2023-08-16 11:31:00 +00:00
Guy Sartorelli
6ff4f336f7
Merge pull request #527 from michalkleiner/pulls/element-preview
Add extension to correctly support element preview
2023-06-22 10:51:57 +12:00
Michal Kleiner
a052bfd590 Add extension to correctly support element preview 2023-06-22 01:32:07 +10:00
Steve Boyd
d1d1b139d0 Merge branch '2.8' into 2 2023-06-16 12:10:37 +12:00
Guy Sartorelli
b855a555c8
Merge pull request #525 from creative-commoners/pulls/2.8/tx-1686724870
ENH Update translations
2023-06-15 10:06:14 +12:00
Steve Boyd
c847f3e1d8 ENH Update translations 2023-06-14 18:41:10 +12:00
Steve Boyd
86205a6286 Merge branch '2.8' into 2 2023-03-29 09:59:23 +13:00
Guy Sartorelli
413de7014c
MNT Revert erroneous dependency changes (#519) 2023-03-28 16:50:01 +13:00
Maxime Rainville
b4bf58e132
Merge pull request #518 from creative-commoners/pulls/2.8/no-deprecations
FIX Don't use deprecated API
2023-03-28 11:17:03 +13:00
Guy Sartorelli
a249d46dcc
FIX Don't use deprecated API 2023-03-28 10:22:51 +13:00
Maxime Rainville
701c6cd053
Merge pull request #517 from creative-commoners/pulls/2/dispatch-ci
MNT Use gha-dispatch-ci
2023-03-23 14:19:26 +13:00
Steve Boyd
4006884aa7 MNT Use gha-dispatch-ci 2023-03-21 13:41:59 +13:00
Guy Sartorelli
aa9797a5f1
MNT Update development dependencies 2023-03-10 16:38:46 +13:00
Guy Sartorelli
5e823223d7
MNT Update release dependencies 2023-03-10 16:38:42 +13:00
Guy Sartorelli
04fe468f36
MNT Update development dependencies 2023-03-10 12:21:32 +13:00
Guy Sartorelli
21240a7c83
Merge pull request #513 from creative-commoners/pulls/2/tx-1678080159
ENH Update translations
2023-03-08 10:32:23 +13:00
Steve Boyd
339b0c855b ENH Update translations 2023-03-06 18:22:39 +13:00
Guy Sartorelli
d11124f576
Merge pull request #507 from creative-commoners/pulls/2/depr
API Deprecations
2023-02-08 10:43:16 +13:00
Steve Boyd
412b2709d2 API Deprecations 2023-02-07 17:16:51 +13:00
Guy Sartorelli
7d8909ac6d
Merge pull request #504 from creative-commoners/pulls/2/depr
API Deprecate passing multiple IDs
2023-01-26 14:01:22 +13:00
Steve Boyd
30b1f09af4 API Deprecate passing multiple IDs 2023-01-26 10:39:07 +13:00
Guy Sartorelli
6770dedc2a
Merge branch '2.7' into 2 2022-12-19 03:00:28 +00:00
Guy Sartorelli
67a21914eb
Merge branch '2.7-release' into 2.7 2022-12-19 03:00:24 +00:00
Guy Sartorelli
901dbc8848
Merge branch '2.6' into 2.7-release 2022-12-19 13:52:24 +13:00
Steve Boyd
4308ac4316
Merge pull request #498 from creative-commoners/pulls/2.6/file-permissions
Subsite file permissions
2022-12-19 11:34:52 +13:00
Steve Boyd
73f3d15bfb
[CVE-2022-42949] Subsite file permissions 2022-12-19 11:30:59 +13:00
Guy Sartorelli
a7e9e8dcdc
Merge pull request #496 from creative-commoners/pulls/2.7-release/historyviewer
FIX Do not show copy to subsite buttons in history viewer
2022-12-14 10:54:12 +13:00
Steve Boyd
5f489b1df9 FIX Do not show copy to subsite buttons in history viewer 2022-12-14 10:43:53 +13:00
Sabina Talipova
f45ccead3c
Merge pull request #493 from creative-commoners/pulls/2/stop-using-depr
API Stop using deprecated API
2022-12-05 16:36:37 +13:00
Steve Boyd
416f55ad03 API Stop using deprecated API 2022-11-28 17:49:50 +13:00
Guy Sartorelli
ceaa915b77
Merge pull request #492 from creative-commoners/pulls/2/depr-messages
API Update deprecations
2022-11-21 09:56:31 +13:00
Steve Boyd
aba286d8a3 API Update deprecations 2022-11-16 11:55:02 +13:00
Guy Sartorelli
a1ee94ce61
Update translations 2022-11-10 01:56:21 +00:00
Michael van Schaik
8ba7070b02
Switch to listing views of subsite-filtered sections
Fixes #489
2022-09-27 06:12:48 +02:00
Guy Sartorelli
1e311e8668
Merge pull request #487 from creative-commoners/pulls/2/review-behat-tests
ENH Replace ADMIN permissions with less permissions in Behat test
2022-09-13 10:14:54 +12:00
Sabina Talipova
5eb7e8a7a3 ENH Replace ADMIN permissions with less permissions in Behat test 2022-09-09 15:16:40 +12:00
Maxime Rainville
837ab70a8d
Merge pull request #486 from creative-commoners/pulls/2/fix-userdoc-deploy
MNT Fix github action for deploying userdocs
2022-08-24 11:11:02 +12:00
Guy Sartorelli
800e8dc473
MNT Fix github action for deploying userdocs 2022-08-23 13:55:05 +12:00
Guy Sartorelli
d13d73299a
Merge pull request #485 from creative-commoners/pulls/2/userhelp-fix
DOC Correct title for userhelp
2022-08-22 11:01:31 +12:00
Maxime Rainville
945e2bc370 DOC Correct title for userhelp 2022-08-20 22:00:04 +12:00
Steve Boyd
9ad6c8f97e Merge branch '2.6' into 2 2022-08-02 19:00:38 +12:00
Steve Boyd
f566ab2b2a Merge branch '2.5' into 2.6 2022-08-02 19:00:11 +12:00
Guy Sartorelli
0338d41626
Merge pull request #484 from creative-commoners/pulls/2.5/standardise-modules
MNT Standardise modules
2022-08-02 16:11:26 +12:00
Steve Boyd
193866ec62 MNT Standardise modules 2022-08-01 16:23:25 +12:00
Bart van Irsel
74b6cec374
ENH added config setting to ignore subsite language (#481)
added config setting to ignore subsite language; when using subsites in combination with fluent it picked up wrong yml file
2022-07-28 12:19:33 +12:00
Steve Boyd
b16495cf54 Merge branch '2.6' into 2 2022-07-25 11:39:47 +12:00
Steve Boyd
cdc37de076 Merge branch '2.5' into 2.6 2022-07-25 11:39:25 +12:00
Guy Sartorelli
b9d972b7fd
Merge pull request #483 from creative-commoners/pulls/2.5/behat
FIX Move files to client directory
2022-07-20 09:43:37 +12:00
Steve Boyd
6e1b504ff3 FIX Move files to client directory 2022-07-19 18:35:15 +12:00
Guy Sartorelli
2bab73ee35
Merge pull request #482 from creative-commoners/pulls/2.5/module-standards
MNT Use GitHub Actions CI
2022-07-15 17:22:14 +12:00
Steve Boyd
6969fe06d3 MNT Use GitHub Actions CI 2022-07-05 19:07:52 +12:00
Guy Sartorelli
7860a03180
Merge pull request #478 from creative-commoners/pulls/2/php81
ENH PHP 8.1 compatibility
2022-04-26 17:57:39 +12:00
Steve Boyd
ed4663be9b ENH PHP 8.1 compatibility 2022-04-13 13:49:48 +12:00
Maxime Rainville
acf9715c3b
Merge pull request #475 from creative-commoners/pulls/2/php74
DEP Set PHP 7.4 as the minimum version
2022-02-18 22:09:19 +13:00
Michal Kleiner
5c3d000b9b
Merge pull request #476 from wilr/wilr-patch-1
FIX replace `in_array` check with `hasTable` check
2022-02-15 15:39:46 +13:00
Will Rossiter
dfe1ba5c6a
FIX replace in_array check with hasTable check.
in_array is case-sensitive and may not detect that a table exists when using lowercase format
2022-02-15 15:05:43 +13:00
Steve Boyd
07aefb1982 DEP Set PHP 7.4 as the minimum version 2022-02-10 17:39:05 +13:00
Maxime Rainville
2a16e1eefa
Merge pull request #469 from creative-commoners/pulls/2.4/behat
MNT Update behat tests
2021-11-15 14:31:30 +13:00
Steve Boyd
cae9bd51ec MNT Update behat tests 2021-11-12 12:38:44 +13:00
Maxime Rainville
5049a5e13c
Merge pull request #471 from creative-commoners/pulls/2/sapphire-test-nine
API phpunit 9 support
2021-11-01 22:29:34 +13:00
Steve Boyd
13ab072303 API phpunit 9 support 2021-10-27 18:24:03 +13:00
Steve Boyd
0179176b6c Merge branch '2.4' into 2 2021-09-13 16:00:54 +12:00
Maxime Rainville
3a2ef2b3bc
Merge pull request #467 from creative-commoners/pulls/2.4/fix-test
MNT Fix test to work with session-manager module
2021-09-09 22:48:38 +12:00
Steve Boyd
643b8d436a MNT Fix test to work with session-manager module 2021-09-07 15:36:57 +12:00
Steve Boyd
e306264740
Update build status badge 2021-01-21 16:42:37 +13:00
Steve Boyd
f4ec177065 Merge branch '2.3' into 2 2020-11-11 17:01:38 +13:00
Steve Boyd
bd690ae62b
Merge pull request #458 from creative-commoners/pulls/2.3/fix-travis
FIX yml config, use sminnee/phpunit fork
2020-11-11 11:01:17 +13:00
Serge Latyntcev
3fb2e9a1e0 MNT Define phpcs paths 2020-11-09 16:39:15 +13:00
Serge Latyntcev
08e5af4f2b MNT use shared travis config 2020-11-06 15:34:01 +13:00
Serge Latyntcev
b5bd1e69b5 MNT Fix travis matrix 2020-11-06 13:08:31 +13:00
Serge Latyntcev
017804dbd4 DEP use sminnee/phpunit:5.7 fork 2020-11-06 12:17:07 +13:00
Serge Latyntcev
683e7da208 MNT Fix yaml config syntax 2020-11-06 12:17:05 +13:00
Steve Boyd
212790ae29 Merge branch '2.3' into 2 2020-09-12 18:39:03 +12:00
Steve Boyd
5a4d613d8e
Merge pull request #452 from chrometoasters/bugfix/default-subsite-query
FIX Adjusting query used in getSubsiteIDForDomain()
2020-09-12 18:37:35 +12:00
Mohamed Alsharaf
27bae53017 Adjusting query used in getSubsiteIDForDomain
Prevent unknow database field error when a new DB field added
to Subsite data object.
2020-09-11 16:39:33 +12:00
Dylan Wagstaff
fd10b868ec
Merge pull request #451 from cjsewell/2
Fix "Column 'URLSegment' in where clause is ambiguous" when duplicating pages
2020-01-22 09:01:17 +13:00
Corey Sewell
f1fce6f739 Fix "Column 'URLSegment' in where clause is ambiguous" when duplicating pages 2020-01-17 12:35:08 +13:00
Dylan Wagstaff
2175d44755
Merge pull request #448 from open-sausages/pulls/docs-limit-cross-domain
DOCS Limitation around cross-domain usage
2020-01-14 09:33:42 +13:00
Aaron Carlino
831c3c3cbe
META: Add github action to build docs 2019-12-19 13:50:59 +13:00
Ingo Schommer
67af02c3b2 DOCS Limitation around cross-domain usage 2019-12-17 15:33:28 +13:00
Robbie Averill
b32499ef31 Merge branch '2.3' 2019-09-25 15:01:00 -07:00
Robbie Averill
5d015c7a96 Merge branch '2.2' into 2.3 2019-09-25 15:00:47 -07:00
Robbie Averill
38b356c256 Merge branch '2.1' into 2.2
# Conflicts:
 #	tests/php/SiteTreeSubsitesTest.php
2019-09-25 15:00:35 -07:00
Robbie Averill
5cf44c9d02 Merge branch '2.0' into 2.1 2019-09-25 15:00:00 -07:00
Nik Rolls
b1c1931d5d Detect domains correctly in Director sub-calls
Previously it relied on the PHP-level $_SERVER variable; now it will use
the HTTPRequest so it works correctly in more situations.
2019-08-15 10:16:20 +12:00
Guy Marriott
917640699d
FIX Prevent undefined index notice when trying to determine HTTP… (#440)
FIX Prevent undefined index notice when trying to determine HTTP_HOST during dev/build
2019-07-30 10:36:48 +12:00
Robbie Averill
09abe2b2f2 Use Director::host() over direct $_SERVER access 2019-07-29 10:38:14 +02:00
Robbie Averill
9a7cdbbe2d FIX Prevent undefined index notice when trying to determine HTTP_HOST during dev/build 2019-07-26 09:53:54 +02:00
Robbie Averill
ce63a9ed08
Merge pull request #427 from creative-commoners/pulls/2.0/cascading-themes
FIX Improving support for cascading themes
2019-07-15 12:14:30 +02:00
Guy Marriott
58f89801b0
FIX Ensure constant is accessed correctly 2019-07-12 15:24:10 +12:00
Guy Marriott
9feef185dc
Adding documentation about cascading themes 2019-07-12 13:34:42 +12:00
Guy Marriott
2eb04ffa78
FIX Improving support for cascading themes
- Fixes an issue where themes would cascade "up" the list of themes
- Provides configuration for defining custom theme options with their own sets of cascading themes

Fixes #392
2019-07-12 13:34:42 +12:00
Guy Marriott
e73d622bdb
Update README.md (#435)
Update README.md
2019-07-08 12:19:26 +12:00
Greg808
d0054a1294
Update README.md
Subsite::currentSubsiteID() is deprecated use  SubsiteState::singleton()->getSubsiteId()
2019-07-04 16:52:04 +02:00
Robbie Averill
c4adf556cf
Merge pull request #433 from Greg808/patch-3
Update README.md
2019-06-29 13:32:03 +12:00
Greg808
644b9c8b90
Update README.md
Subsite::currentSubsiteID() is deprecated. class_exists needs a fully-qualified class name to work
2019-06-28 10:49:47 +02:00
Robbie Averill
81e6d0fe59
Merge pull request #432 from Greg808/patch-1
Update README.md
2019-06-28 08:00:10 +12:00
Greg808
9abaca6d48
Update README.md
I am not quite sure if this is needed but i'd expect the code snipets to work with Silverstripe 4 if the requirement say Silverstripe 4. I testet it with SS 4.4.1
Subsite::currentSubsiteID() is deprecated.  class_exists needs namespace to work correctly
2019-06-27 17:32:11 +02:00
Robbie Averill
7d27abf2b1 Update expected json content type in unit test 2019-06-25 16:05:38 +12:00
Robbie Averill
001f44d73b Merge branch '2.3' 2019-06-25 15:45:17 +12:00
Robbie Averill
ca01e2680a Merge branch '2.2' into 2.3 2019-06-25 15:45:08 +12:00
Robbie Averill
e41dc8b018 Merge branch '2.1' into 2.2 2019-06-25 15:44:37 +12:00
Robbie Averill
9655371276 Merge branch '2.0' into 2.1 2019-06-25 15:43:59 +12:00
Robbie Averill
67d10ec0cb Remove SilverStripe 4.0-4.2 from Travis builds 2019-06-25 15:43:50 +12:00
Robbie Averill
4fdf2e24e3 FIX LeftAndMainSubsites::canAccess() now accepts a Member argument and falls back to the session member 2019-06-25 15:42:54 +12:00
Robbie Averill
614819a1d3 Reduce Behat builds to SS 4.3 and update postgres version 2019-06-25 11:12:16 +12:00
Robbie Averill
be3bcab715 Merge branch '2.3' 2019-06-25 10:01:52 +12:00
Robbie Averill
135ae961bf Merge branch '2.2' into 2.3 2019-06-25 10:01:46 +12:00
Robbie Averill
eddbc90524 Remove SilverStripe 4.0-4.2 from Travis builds 2019-06-25 10:01:27 +12:00
Robbie Averill
a4e99a2df5 Merge branch '2.3' 2019-06-25 10:01:06 +12:00
Robbie Averill
77fafe6450 Merge branch '2.2' into 2.3 2019-06-25 10:00:47 +12:00
Robbie Averill
4249fffc0f FIX LeftAndMainSubsites::canAccess() now accepts a Member argument and falls back to the session member 2019-06-25 10:00:13 +12:00
Dylan Wagstaff
b3bd51cb6c
Merge pull request #430 from creative-commoners/pulls/2.3/access-passed-member
FIX LeftAndMainSubsites::canAccess() now accepts a Member argument and falls back to the session member
2019-06-24 11:49:50 +12:00
Robbie Averill
0275bb1eca FIX LeftAndMainSubsites::canAccess() now accepts a Member argument and falls back to the session member 2019-06-24 10:19:58 +12:00
Robbie Averill
483a867289
Merge pull request #428 from harmoney-dev/detect-subsite-by-domain-in-mock-requests
Detect domains correctly in Director sub-calls
2019-06-12 15:42:23 +12:00
Nik Rolls
46a863557b
Detect domains correctly in Director sub-calls
Previously it relied on the PHP-level $_SERVER variable; now it will use
the HTTPRequest so it works correctly in more situations.
2019-06-12 15:14:09 +12:00
Garion Herman
19edb78756
Merge pull request #425 from creative-commoners/pulls/2.2/re-save-virtual-page
FIX Subsites virtual pages now allow you to re-save them when used in conjunction with silverstripe-fluent
2019-06-05 06:18:24 +01:00
Garion Herman
5c4a655106
Merge pull request #418 from creative-commoners/pulls/2.3/fluent-domain-docs
Ensure URL segment field type before using its API, and add docs around subsite and fluent domain compatibility
2019-06-05 05:27:06 +01:00
Robbie Averill
f6503822e8
DOCS Fix typos
[ci skip]

Co-Authored-By: Garion Herman <garion@silverstripe.com>
2019-06-05 15:09:57 +12:00
Robbie Averill
2b26876596 Add test for URLSegment prefix set to primary subsite domain for page 2019-05-31 16:41:36 +12:00
Robbie Averill
900d04d94a Add tests and move logic into the if statement 2019-05-31 16:32:28 +12:00
Robbie Averill
1f51fcd909 FIX Subsites virtual pages now allow you to re-save them when used in conjunction with silverstripe-fluent 2019-05-31 14:41:37 +12:00
Robbie Averill
33244fb430
Merge pull request #422 from creative-commoners/pulls/2.3/noice
Tidy output of IsPublic value in Subsites admin
2019-05-31 13:48:51 +12:00
Robbie Averill
b8576744a1
Merge pull request #419 from creative-commoners/pulls/2.3/fix-sitetree-hints-caching
FIX allowed pagetypes displaying incorrectly when switching subsite
2019-05-31 13:34:37 +12:00
Robbie Averill
1595079156
Merge pull request #421 from creative-commoners/pulls/2.3/subsites-virtual-labels
FIX Field labels for subsites virtual pages are no longer repeated
2019-05-31 13:22:32 +12:00
Robbie Averill
9ee451f706
Merge pull request #417 from creative-commoners/pulls/2.3/default-automatic-protocol
FIX Domains now default to "Automatic" protocol, and have the correct help description
2019-05-31 11:59:54 +12:00
Robbie Averill
2a9f3ac0f6 DOCS Fix phpdoc in summary_fields
[ci skip]
2019-05-31 11:44:09 +12:00
Robbie Averill
fadd42910b
Merge pull request #424 from creative-commoners/pulls/2.3/travis-segfault
Remove code coverage, it is segfaulting on SS 4.4
2019-05-31 11:42:29 +12:00
Robbie Averill
c60acb3190 FIX Field labels for subsites virtual pages are no longer repeated 2019-05-31 11:29:30 +12:00
Garion Herman
4d7641e16a FIX allowed pagetypes displaying incorrectly when switching subsite
This patch depends on an update to the CMS module that provides this
extension point. The code is inert when matched with existing CMS
versions.
2019-05-31 11:28:15 +12:00
Robbie Averill
3b8207d70c Ensure URL segment field type before using its API, and add docs around subsite and fluent domain compatibility 2019-05-31 11:27:58 +12:00
Garion Herman
68c763da3e Tidy output of IsPublic value in Subsites admin 2019-05-31 11:27:47 +12:00
Robbie Averill
1e44e1d4ba FIX Domains now default to "Automatic" protocol, and have the correct help description 2019-05-31 11:26:46 +12:00
Robbie Averill
2644083a2d Remove code coverage, it is segfaulting on SS 4.4 2019-05-31 11:17:51 +12:00
Robbie Averill
ee961594bc Merge branch '2.3' 2019-05-10 11:14:12 +12:00
Robbie Averill
e313f2ed5d FIX Update Behat assertion to use correct label for "Search or choose Page" 2019-05-10 10:03:35 +12:00
Robbie Averill
536420ec68 FIX Update Behat assertion to use correct field label for SilverStripe 4.4 2019-05-10 09:46:21 +12:00
Guy Marriott
f8e4804cc1
Update translations 2019-05-09 15:44:59 +12:00
Robbie Averill
46653e2b07 Merge branch '2.3' 2019-04-15 16:27:59 +12:00
Robbie Averill
fe20bc2907 Merge branch '2.2' into 2.3 2019-04-15 16:27:36 +12:00
Robbie Averill
ec327aee7c Update dependencies for SilverStripe ^4.4 2019-04-15 16:26:53 +12:00
Robbie Averill
d1fc84d15c
Merge pull request #408 from creative-commoners/pulls/2.2/insert-a-link
FIX Content editor group users can now insert links into contents while using subsites
2019-02-10 12:52:33 +03:00
Robbie Averill
f003fb5e74 Add PHP 7.3 to build matrix and move Behat builds to SilverStripe 4.3 2019-02-10 12:41:59 +03:00
Robbie Averill
9dbdd992f7 FIX Content editor group users can now insert links into contents while using subsites 2019-02-10 12:18:19 +03:00
Robbie Averill
1294671086 Merge branch '2.2' 2019-01-28 21:22:31 +02:00
Robbie Averill
fbd98ff402 FIX Disable transactions in SubsiteTest to prevent global state bugs in CWP kitchen sink test suite 2019-01-28 21:22:12 +02:00
Dylan Wagstaff
d9fcaa3319
Merge pull request #403 from creative-commoners/pulls/2.3/bootstrap-notice
NEW Use Bootstrap styled alerts in assets notification
2018-11-28 14:40:49 +13:00
Robbie Averill
da2e8fcc8b NEW Use Bootstrap styled alerts in assets notification
This will be the default in SilverStripe 4.4, and has been partially implemented in 4.3
2018-11-27 16:23:58 +01:00
Dylan Wagstaff
ce9dd1a856
Merge pull request #402 from creative-commoners/pulls/2.2/subsites-is-off-the-table-or-maybe-its-on-it-now-idk
FIX Catching situation where database has no tables but it exists
2018-11-08 12:26:59 +13:00
Guy Marriott
59f6685e2a
FIX Catching situation where database has no tables but it exists 2018-11-08 12:05:36 +13:00
Robbie Averill
e510213c3e Merge branch '2.2' 2018-11-07 16:28:55 +02:00
Robbie Averill
a0ecbdf4b6 Remove obsolete branch alias 2018-11-07 16:28:40 +02:00
Robbie Averill
e4fe534f10 Merge branch '2.1' 2018-11-07 16:28:10 +02:00
Guy Marriott
3afdd01d41
Merge pull request #400 from creative-commoners/pulls/2.1/remove-json-methods
FIX Replace Convert JSON methods with json_* methods, deprecated from SilverStripe 4.4
2018-10-29 11:46:16 +13:00
Robbie Averill
2a35a5c70a FIX Replace Convert JSON methods with json_* methods, deprecated from SilverStripe 4.4 2018-10-28 21:41:32 +00:00
Robbie Averill
1fa549886f Define explode limit when removing port 2018-10-20 23:16:05 +02:00
Daniel Hensby
2bf4812947
Merge pull request #399 from creative-commoners/pulls/2.1/separate-test
Update testDomainProtocol to use a dataProvider
2018-10-19 22:50:43 +01:00
Dylan Wagstaff
810ee63ea6
Merge pull request #398 from creative-commoners/pulls/2.1/ignore-domain-port
FIX Ignore ports when matching domain for subsite
2018-10-20 10:31:10 +13:00
Robbie Averill
5e79abdbbc Update testDomainProtocol to use a dataProvider
This might help with test state leakage
2018-10-19 21:51:32 +02:00
Robbie Averill
ff9997e0f2 FIX Ignore ports when matching domain for subsite 2018-10-19 20:51:43 +02:00
Robbie Averill
bf7dd9c37b Merge branch '2.1'
# Conflicts:
  #	src/Extensions/SiteTreeSubsites.php
2018-10-19 16:28:50 +02:00
Robbie Averill
9199d509d6 Merge branch '2.0' into 2.1 2018-10-19 16:27:38 +02:00
Robbie Averill
bbfb93d50d
Merge pull request #397 from silverstripe/revert-388-pulls/2.1/fix-role-permissions
Revert "FIX CMS permission checks for subsite are now handled in the state context"
2018-10-19 11:27:37 +02:00
Guy Marriott
7cc86199e7
Revert "FIX CMS permission checks for subsite are now handled in the state context" 2018-10-19 12:00:50 +13:00
Robbie Averill
1a5666182e
Merge pull request #396 from open-sausages/pulls/2.0/352-fix-migrate-file-task
BUG: Fix `MigrateFileTask` not migrating files for subsites
2018-10-18 11:37:21 +02:00
Robbie Averill
2a6f7b5dfb Automated linting fix 2018-10-18 11:03:16 +02:00
Robbie Averill
6a8f15a194
Merge pull request #395 from DorsetDigital/patch-1
Change source of admin URL in getIsAdmin()
2018-10-18 11:02:46 +02:00
bergice
e52fe41a23 BUG: Fix MigrateFileTask not migrating files for subsites
Fixes #352
2018-10-18 18:41:07 +13:00
DorsetDigital
1e458ef03d
Change source of admin URL in getIsAdmin()
As per #394 
Change direct call to the AdminRootController config setting, using instead the admin_url() method on the class which provides detection via the Director rules, and the fallback to the config setting.
2018-10-17 23:20:20 +01:00
Dylan Wagstaff
4323db52f0
Merge pull request #391 from creative-commoners/pulls/2.1/duplicate-tabs
FIX Remove duplicate Configuration tabs when creating a new subsite
2018-09-14 19:55:33 +12:00
Robbie Averill
bb226a0652 FIX Remove duplicate Configuration tabs when creating a new subsite 2018-09-14 09:42:29 +02:00
Dylan Wagstaff
fa3f1fa767
Merge pull request #389 from creative-commoners/pulls/2.1/dont-catch-exceptions
FIX Only continue delegation when DB exceptions are caused by no database selected
2018-09-08 18:54:51 +12:00
Robbie Averill
313d22ffca FIX Only continue delegation when DB exceptions are caused by no database selected
This prevents the middleware from interrupting legitimate database exceptions from being
propagated.
2018-09-07 11:06:55 +02:00
Guy Marriott
70dc70f494
Merge pull request #388 from creative-commoners/pulls/2.1/fix-role-permissions
FIX CMS permission checks for subsite are now handled in the state context
2018-08-27 14:16:13 +12:00
Robbie Averill
7681634cb2 Remove irrelevant check for subsites list size, use func_num_args() and add break to loop 2018-08-27 10:04:57 +12:00
Robbie Averill
6af985420f FIX CMS permission checks for subsite are now handled in the state context
We now check the subsite state for the context and validate it against the current member's
group permissions using the SilverStripe ORM relationships instead of using SQL queries.

More granular permission checks e.g. canView etc are still up to data models to define and
handle.
2018-08-24 16:58:36 +12:00
Guy Marriott
039a7a8c84
Merge pull request #387 from creative-commoners/pulls/2.1/fix-cross-subsite-duplication
FIX Pages now correctly duplicate children across subsites
2018-08-24 11:03:56 +12:00
Robbie Averill
e8a72e1c33 FIX Duplicate page's parent IDs are now assumed or zeroed after duplication 2018-08-24 10:30:27 +12:00
Guy Marriott
5b8a0dbf13
Merge pull request #386 from creative-commoners/pulls/2.2/deprecate-duplicate
API Deprecate duplicateSubsiteRelations. Use "cascade_duplicates" config API instead.
2018-08-24 10:27:21 +12:00
Robbie Averill
8af796fa7a API Deprecate duplicateSubsiteRelations. Use "cascade_duplicates" config API instead. 2018-08-24 10:13:22 +12:00
Robbie Averill
7f28c32427 FIX Pages now correctly duplicate children across subsites 2018-08-24 10:12:05 +12:00
Robbie Averill
6747b5ffe8
Merge pull request #381 from lekoala/patch-1
allow disabling filter using queryParam
2018-08-24 08:50:47 +12:00
Robbie Averill
9ba1275b49
Merge pull request #382 from lekoala/patch-2
allow using queryParam to disable filter
2018-08-24 08:50:41 +12:00
Guy Marriott
687e013793
Merge branch '2.1' 2018-08-20 09:58:59 +12:00
Guy Marriott
f24fd60f14
Merge pull request #383 from creative-commoners/pulls/2.1/loosen-json-test
Loosen test assertion on content type for application/json
2018-08-20 09:57:26 +12:00
Robbie Averill
87485e39f4 Loosen test assertion on content type for application/json
See https://github.com/silverstripe/silverstripe-framework/issues/5594

This was changed in Silverstripe 4.3
2018-08-20 09:19:54 +12:00
Thomas Portelange
6e35807dc7
allow using queryParam to disable filter
much better than global state (and should potentially replace cookie usage that is user controlled)
2018-08-16 16:20:48 +02:00
Thomas Portelange
c177a9f640
allow disabling filter using queryParam
It is much better than relying on global state
2018-08-16 16:19:25 +02:00
Robbie Averill
d8088edfa9 Merge branch '2.1' 2018-07-26 15:15:29 +12:00
Robbie Averill
bf2c81dce6
Merge pull request #1 from silverstripe-security/pulls/2.1/ss-2018-016
[SS-2018-016] Group table name is escaped to prevent possibility of SQL injection
2018-07-25 09:55:14 +12:00
Robbie Averill
4b6804eaab [SS-2018-016] Group table name is escaped to prevent possibility of SQL injection 2018-07-16 11:22:58 +12:00
Raissa North
7e1c2eb0aa
Merge pull request #378 from creative-commoners/pulls/2.1/initial-publish-bug
Add subsite switching and page publish behat tests
2018-07-16 10:13:20 +12:00
Robbie Averill
191c63bd9b Remove extra subsite fixture definition 2018-07-13 16:31:10 +12:00
Robbie Averill
f635aa9811 Add subsite switching behat tests and a test that shows a UI bug in publishing 2018-07-13 15:59:37 +12:00
Robbie Averill
c732c0c799
Merge pull request #375 from creative-commoners/pulls/2.2/deprecate-old-preview-url-function
Deprecate alternatePreviewLink function as per CMS
2018-07-05 10:48:37 +12:00
Dylan Wagstaff
ffbcb9a0c8 Deprecate alternatePreviewLink function as per CMS
SilverStripe CMS 4.0.0 issues a deprecation notice before calling
alternatePreviewLink on any page that hasMethod (i.e. is applied via an
extension such as SiteTreeSubsites). Instead of double-issuing a notice,
we will just mark this as deprecated in the next minor version via
docblocks.
2018-07-05 09:55:45 +12:00
Dylan Wagstaff
d78e3c4662 Merge branch '2.1' 2018-07-05 09:50:21 +12:00
Dylan Wagstaff
75b851326d Merge branch '2.0' into 2.1 2018-07-05 09:49:11 +12:00
Robbie Averill
19f0162265
Merge pull request #374 from creative-commoners/pulls/2.0/cms-preview-nonbreakage
FIX apply SubsiteID getVar to CMS Preview fetches
2018-07-04 15:57:32 +12:00
Dylan Wagstaff
5370bc8af6 FIX apply SubsiteID getVar to CMS Preview fetches
A preview link must be loaded on the same domain the CMS is loaded
through, which was previously causing issues when a page (identified
via URLSegment) did not exist on the subsite domain. By _always_
prepending the identifier to the preview link, this should never happen.
2018-07-04 15:21:47 +12:00
Guy
c43abb05c2
Merge branch '2.1' 2018-06-20 17:11:18 +12:00
Dylan Wagstaff
de0bba0df2
Merge pull request #371 from creative-commoners/pulls/2.0/exists
Switch count for exists() check for readability
2018-06-20 09:44:51 +12:00
Robbie Averill
153db12d42 Switch count for exists() check for readability 2018-06-20 09:27:04 +12:00
Dylan Wagstaff
f19b82f029
Merge pull request #369 from creative-commoners/pulls/2.0/fix-edit-check
FIX Do no provide input for canEdit or canPublish if no subsites exist
2018-06-19 17:04:15 +12:00
Robbie Averill
dc9d6de62d FIX Do no provide input for canEdit or canPublish if no subsites exist 2018-06-19 15:43:09 +12:00
Raissa North
9038a166bd
Merge pull request #367 from creative-commoners/pulls/2.0/escaped-menu
FIX Double escaping subsites title in CMS menu
2018-06-19 13:56:02 +12:00
Robbie Averill
315621892d FIX Double escaping subsites title in CMS menu 2018-06-19 13:47:15 +12:00
93 changed files with 2552 additions and 478 deletions

View File

@ -13,7 +13,7 @@ trim_trailing_whitespace = true
[*.md] [*.md]
trim_trailing_whitespace = false trim_trailing_whitespace = false
[*.yml] [*.{yml,feature}]
indent_size = 2 indent_size = 2
[{.travis.yml,package.json,composer.json}] [{.travis.yml,package.json,composer.json}]

11
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,11 @@
name: CI
on:
push:
pull_request:
workflow_dispatch:
jobs:
ci:
name: CI
uses: silverstripe/gha-ci/.github/workflows/ci.yml@v1

View File

@ -0,0 +1,16 @@
name: Deploy Userhelp Docs
on:
push:
branches:
- '3'
- '2'
- '1.1'
paths:
- 'docs/en/userguide/**'
jobs:
deploy:
name: deploy-userhelp-docs
runs-on: ubuntu-latest
steps:
- name: Run build hook
run: curl -X POST -d {} https://api.netlify.com/build_hooks/${{ secrets.NETLIFY_BUILD_HOOK }}

16
.github/workflows/dispatch-ci.yml vendored Normal file
View File

@ -0,0 +1,16 @@
name: Dispatch CI
on:
# At 11:30 AM UTC, only on Saturday and Sunday
schedule:
- cron: '30 11 * * 6,0'
jobs:
dispatch-ci:
name: Dispatch CI
# Only run cron on the silverstripe account
if: (github.event_name == 'schedule' && github.repository_owner == 'silverstripe') || (github.event_name != 'schedule')
runs-on: ubuntu-latest
steps:
- name: Dispatch CI
uses: silverstripe/gha-dispatch-ci@v1

17
.github/workflows/keepalive.yml vendored Normal file
View File

@ -0,0 +1,17 @@
name: Keepalive
on:
workflow_dispatch:
# The 4th of every month at 10:50am UTC
schedule:
- cron: '50 10 4 * *'
jobs:
keepalive:
name: Keepalive
# Only run cron on the silverstripe account
if: (github.event_name == 'schedule' && github.repository_owner == 'silverstripe') || (github.event_name != 'schedule')
runs-on: ubuntu-latest
steps:
- name: Keepalive
uses: silverstripe/gha-keepalive@v1

View File

@ -1,13 +0,0 @@
inherit: true
checks:
php: true
build:
nodes:
analysis:
tests:
override: [php-scrutinizer-run]
filter:
paths: [src/*, tests/*]

View File

@ -1,62 +0,0 @@
language: php
dist: trusty
before_install:
- sudo apt-get update
- sudo apt-get install chromium-chromedriver
env:
global:
- COMPOSER_ROOT_VERSION="2.0.x-dev"
- DISPLAY=":99"
- XVFBARGS=":99 -ac -screen 0 1024x768x16"
- SS_BASE_URL="http://localhost:8080/"
- SS_ENVIRONMENT_TYPE="dev"
matrix:
include:
- php: 5.6
env: DB=MYSQL INSTALLER_VERSION=4.0.x-dev PHPCS_TEST=1 PHPUNIT_TEST=1
- php: 7.0
env: DB=PGSQL INSTALLER_VERSION=4.1.x-dev PHPUNIT_TEST=1
- php: 7.1
env: DB=MYSQL INSTALLER_VERSION=4.1.x-dev BEHAT_TEST=1
- php: 7.2
env: DB=MYSQL INSTALLER_VERSION=4.2.x-dev PHPUNIT_COVERAGE_TEST=1
- php: 7.2
env: DB=MYSQL INSTALLER_VERSION=4.x-dev BEHAT_TEST=1
before_script:
# Extra $PATH
- export PATH=/usr/lib/chromium-browser/:$PATH
# Init PHP
- phpenv rehash
- phpenv config-rm xdebug.ini
- echo 'memory_limit=3G' >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini
# Install composer
- composer validate
- composer require silverstripe/installer:"$INSTALLER_VERSION" silverstripe/recipe-testing:^1 --no-update
- if [[ $DB == PGSQL ]]; then composer require --no-update silverstripe/postgresql:2.0.x-dev; fi
- composer install --prefer-source --no-interaction --no-progress --no-suggest --optimize-autoloader --verbose --profile
# Behat bootstrapping
- if [[ $BEHAT_TEST ]]; then mkdir artifacts; fi
- if [[ $BEHAT_TEST ]]; then cp composer.lock artifacts/; fi
- if [[ $BEHAT_TEST ]]; then sh -e /etc/init.d/xvfb start; sleep 3; fi
- if [[ $BEHAT_TEST ]]; then (chromedriver > artifacts/chromedriver.log 2>&1 &); fi
- if [[ $BEHAT_TEST ]]; then (vendor/bin/serve --bootstrap-file vendor/silverstripe/cms/tests/behat/serve-bootstrap.php &> artifacts/serve.log &); fi
script:
- if [[ $PHPUNIT_TEST ]]; then vendor/bin/phpunit; fi
- if [[ $PHPUNIT_COVERAGE_TEST ]]; then phpdbg -qrr vendor/bin/phpunit --coverage-clover=coverage.xml; fi
- if [[ $PHPCS_TEST ]]; then vendor/bin/phpcs src tests *.php --ignore=host-map.php; fi
- if [[ $BEHAT_TEST ]]; then vendor/bin/behat @subsites; fi
after_success:
- if [[ $PHPUNIT_COVERAGE_TEST ]]; then bash <(curl -s https://codecov.io/bash) -f coverage.xml; fi
after_failure:
- if [[ $BEHAT_TEST ]]; then php ./vendor/silverstripe/framework/tests/behat/travis-upload-artifacts.php --if-env BEHAT_TEST,ARTIFACTS_BUCKET,ARTIFACTS_KEY,ARTIFACTS_SECRET --target-path $TRAVIS_REPO_SLUG/$TRAVIS_BUILD_ID/$TRAVIS_JOB_ID --artifacts-base-url https://s3.amazonaws.com/$ARTIFACTS_BUCKET/ --artifacts-path ./artifacts/; fi

View File

@ -1,8 +1,9 @@
[main] [main]
host = https://www.transifex.com host = https://www.transifex.com
[silverstripe-subsites.master] [o:silverstripe:p:silverstripe-subsites:r:master]
file_filter = lang/<lang>.yml file_filter = lang/<lang>.yml
source_file = lang/en.yml source_file = lang/en.yml
source_lang = en source_lang = en
type = YML type = YML

View File

@ -1,7 +1,7 @@
# Subsites Module # Subsites Module
[![Build Status](https://secure.travis-ci.org/silverstripe/silverstripe-subsites.png?branch=master)](http://travis-ci.org/silverstripe/silverstripe-subsites) [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/silverstripe/silverstripe-subsites/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/silverstripe/silverstripe-subsites/?branch=master) [![codecov](https://codecov.io/gh/silverstripe/silverstripe-subsites/branch/master/graph/badge.svg)](https://codecov.io/gh/silverstripe/silverstripe-subsites) [![CI](https://github.com/silverstripe/silverstripe-subsites/actions/workflows/ci.yml/badge.svg)](https://github.com/silverstripe/silverstripe-subsites/actions/workflows/ci.yml)
[![SilverStripe supported module](https://img.shields.io/badge/silverstripe-supported-0071C4.svg)](https://www.silverstripe.org/software/addons/silverstripe-commercially-supported-module-list/) [![Silverstripe supported module](https://img.shields.io/badge/silverstripe-supported-0071C4.svg)](https://www.silverstripe.org/software/addons/silverstripe-commercially-supported-module-list/)
## Introduction ## Introduction
@ -9,7 +9,7 @@ The subsites module provides a convenient way of running multiple websites from
sharing users, content, and assets between them - the sites will be managed from a single CMS. sharing users, content, and assets between them - the sites will be managed from a single CMS.
A useful way to think of its use is where you have a business with a global headquarters and four branches in various A useful way to think of its use is where you have a business with a global headquarters and four branches in various
countries. The subsites module allows the five offices to use a single SilverStripe installation, and have information countries. The subsites module allows the five offices to use a single Silverstripe installation, and have information
from the headquarters flow down into the branches. The branches can hold information that is individual and the website from the headquarters flow down into the branches. The branches can hold information that is individual and the website
templates can also be different. templates can also be different.
@ -40,6 +40,11 @@ For user documentation please see:
### Limitations: ### Limitations:
* Subsites are usually accessed via their own separate domains.
In order to allow efficient cross-subsite CMS editing,
they can also be accessed via URL parameters rather than domain maps.
This can weaken domain-specific security controls in your environment
such as domain-specific IP whitelists, firewall rules or business logic.
* Each subsite domain name has to be set up on the server first, and DNS records need to be updated as appropriate. * Each subsite domain name has to be set up on the server first, and DNS records need to be updated as appropriate.
* A subsite cannot use a different codebase as the main site, they are intrinsically tied * A subsite cannot use a different codebase as the main site, they are intrinsically tied
* However, you can remove page types from a subsite when creating the subsite - [see the setup documentation for further details](docs/en/userguide/set_up.md) * However, you can remove page types from a subsite when creating the subsite - [see the setup documentation for further details](docs/en/userguide/set_up.md)
@ -54,7 +59,7 @@ If more isolation of code, security, or performance is needed, then consider run
## Requirements ## Requirements
* SilverStripe 4.x * Silverstripe 4.x
## Installation ## Installation
@ -64,7 +69,7 @@ If more isolation of code, security, or performance is needed, then consider run
* Once you've created a subsite, you'll see a "Create Subsite Domain" button, hit that button to enter a domain or subdomain for your subsite. This will determine the URL of your website. For example, if your site is running on `http://localhost/mysite`, and you set the subdomain to "subsite", then your subsite will be accessible on `http://subsite.localhost/mysite` * Once you've created a subsite, you'll see a "Create Subsite Domain" button, hit that button to enter a domain or subdomain for your subsite. This will determine the URL of your website. For example, if your site is running on `http://localhost/mysite`, and you set the subdomain to "subsite", then your subsite will be accessible on `http://subsite.localhost/mysite`
* Go to the "Pages" section of the CMS. In the top-left above the menu, you'll see a dropdown listing the two subsites - "Main site" is the original site that you had before you installed the subsites module. Select your new subsite, and the site content tree will be changed. It should be empty at this stage. * Go to the "Pages" section of the CMS. In the top-left above the menu, you'll see a dropdown listing the two subsites - "Main site" is the original site that you had before you installed the subsites module. Select your new subsite, and the site content tree will be changed. It should be empty at this stage.
* Add a page - change its title to "Home", and its URL Segment will be changed to "home". Save the page. * Add a page - change its title to "Home", and its URL Segment will be changed to "home". Save the page.
* Update your DNS and, if necessary, your webserver configuration, so that your subdomain will point to the SilverStripe installation on your webserver. Visit this new subdomain. You should see the new subsite homepage. * Update your DNS and, if necessary, your webserver configuration, so that your subdomain will point to the Silverstripe installation on your webserver. Visit this new subdomain. You should see the new subsite homepage.
## Usage ## Usage
@ -96,7 +101,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
@ -124,8 +177,8 @@ Include the current SubsiteID as a hidden field on getCMSFields, or updateCMSFie
:::php :::php
public function getCMSFields() { public function getCMSFields() {
$fields = parent::getCMSFields(); $fields = parent::getCMSFields();
if(class_exists('Subsite')){ if(class_exists(Subsite::class)){
$fields->push(new HiddenField('SubsiteID','SubsiteID', Subsite::currentSubsiteID())); $fields->push(new HiddenField('SubsiteID','SubsiteID', SubsiteState::singleton()->getSubsiteId()));
} }
return $fields; return $fields;
} }
@ -139,8 +192,8 @@ To limit your admin gridfields to the current Subsite records, you can do someth
$form = parent::getEditForm($id, $fields); $form = parent::getEditForm($id, $fields);
$gridField = $form->Fields()->fieldByName($this->sanitiseClassName($this->modelClass)); $gridField = $form->Fields()->fieldByName($this->sanitiseClassName($this->modelClass));
if(class_exists('Subsite')){ if(class_exists(Subsite::class)){
$list = $gridField->getList()->filter(array('SubsiteID'=>Subsite::currentSubsiteID())); $list = $gridField->getList()->filter(['SubsiteID'=>SubsiteState::singleton()->getSubsiteId()]);
$gridField->setList($list); $gridField->setList($list);
} }
@ -165,6 +218,14 @@ or by defining the subsiteCMSShowInMenu function in your admin:
return true; return true;
} }
### Using Subsites in combination with Fluent
When using Subsites in combination with Fluent module, the Subsites module sets the i18n locale to the language defined in the current Subsite. When this behaviour is not desired and you need to use the locale in FluentState use the following setting in your yml config file:
```yaml
SilverStripe\Subsites\Extensions\SiteTreeSubsites:
ignore_subsite_locale: true
```
### Public display of a subsite ### Public display of a subsite

View File

@ -1,5 +0,0 @@
<?php
use SilverStripe\Dev\Deprecation;
Deprecation::notification_version('2.0', 'subsites');

View File

@ -50,6 +50,7 @@ SilverStripe\Admin\SecurityAdmin:
SilverStripe\CMS\Controllers\CMSMain: SilverStripe\CMS\Controllers\CMSMain:
extensions: extensions:
- SilverStripe\Subsites\Extensions\HintsCacheKeyExtension
- SilverStripe\Subsites\Extensions\SubsiteMenuExtension - SilverStripe\Subsites\Extensions\SubsiteMenuExtension
SilverStripe\CMS\Controllers\CMSPagesController: SilverStripe\CMS\Controllers\CMSPagesController:
@ -63,3 +64,12 @@ SilverStripe\Subsites\Admin\SubsiteAdmin:
SilverStripe\SiteConfig\SiteConfigLeftAndMain: SilverStripe\SiteConfig\SiteConfigLeftAndMain:
extensions: extensions:
- SilverStripe\Subsites\Extensions\SubsiteMenuExtension - SilverStripe\Subsites\Extensions\SubsiteMenuExtension
---
Name: subsite-preview-elemental
Only:
classexists: DNADesign\Elemental\Models\BaseElement
---
DNADesign\Elemental\Models\BaseElement:
extensions:
- SilverStripe\Subsites\Extensions\BaseElementSubsites

View File

@ -7,4 +7,6 @@ SilverStripe\Core\Injector\Injector:
SilverStripe\Control\Director: SilverStripe\Control\Director:
properties: properties:
Middlewares: Middlewares:
SubsitesStateMiddleware: %$SilverStripe\Subsites\Middleware\InitStateMiddleware SubsitesStateMiddleware: '%$SilverStripe\Subsites\Middleware\InitStateMiddleware'
SilverStripe\Dev\Tasks\MigrateFileTask:
class: SilverStripe\Subsites\Tasks\SubsiteMigrateFileTask

View File

@ -2,7 +2,7 @@ default:
suites: suites:
subsites: subsites:
paths: paths:
- %paths.modules.subsites%/tests/behat/features - '%paths.modules.subsites%/tests/behat/features'
contexts: contexts:
- SilverStripe\Framework\Tests\Behaviour\FeatureContext - SilverStripe\Framework\Tests\Behaviour\FeatureContext
- SilverStripe\Framework\Tests\Behaviour\CmsFormsContext - SilverStripe\Framework\Tests\Behaviour\CmsFormsContext
@ -11,9 +11,10 @@ default:
- SilverStripe\BehatExtension\Context\EmailContext - SilverStripe\BehatExtension\Context\EmailContext
- SilverStripe\CMS\Tests\Behaviour\LoginContext - SilverStripe\CMS\Tests\Behaviour\LoginContext
- SilverStripe\CMS\Tests\Behaviour\ThemeContext - SilverStripe\CMS\Tests\Behaviour\ThemeContext
- SilverStripe\CMS\Tests\Behaviour\FixtureContext: # Using asset-admin for fixture context to get iAttachTheFileToDropzone()
- SilverStripe\AssetAdmin\Tests\Behat\Context\FixtureContext:
# Note: double indent for args is intentional # Note: double indent for args is intentional
- %paths.modules.subsites%/tests/behat/features/files/ - '%paths.modules.subsites%/tests/behat/files/'
extensions: extensions:
SilverStripe\BehatExtension\MinkExtension: SilverStripe\BehatExtension\MinkExtension:
@ -25,5 +26,5 @@ default:
browser_name: chrome browser_name: chrome
SilverStripe\BehatExtension\Extension: SilverStripe\BehatExtension\Extension:
screenshot_path: %paths.base%/artifacts/screenshots screenshot_path: '%paths.base%/artifacts/screenshots'
bootstrap_file: "vendor/silverstripe/cms/tests/behat/serve-bootstrap.php" bootstrap_file: "vendor/silverstripe/cms/tests/behat/serve-bootstrap.php"

View File

@ -101,7 +101,7 @@
/** /**
* Update links and forms with GET/POST SubsiteID param, so we remaing on the current subsite. * Update links and forms with GET/POST SubsiteID param, so we remaing on the current subsite.
* The initial link for the iframe comes from SiteTreeSubsites::alternatePreviewLink. * The initial link for the iframe comes from SiteTreeSubsites::updatePreviewLink.
* *
* This is done so we can use the CMS domain for displaying previews so we prevent single-origin * This is done so we can use the CMS domain for displaying previews so we prevent single-origin
* violations and SSL cert problems that come up when iframing content from a different URL. * violations and SSL cert problems that come up when iframing content from a different URL.

View File

@ -15,15 +15,16 @@
} }
], ],
"require": { "require": {
"silverstripe/framework": "^4", "php": "^7.4 || ^8.0",
"silverstripe/cms": "^4", "silverstripe/framework": "^4.12",
"silverstripe/admin": "^1", "silverstripe/cms": "^4.4@dev",
"silverstripe/asset-admin": "^1", "silverstripe/admin": "^1.4@dev",
"silverstripe/errorpage": "^1", "silverstripe/asset-admin": "^1.4@dev",
"silverstripe/versioned": "^1" "silverstripe/errorpage": "^1.4@dev",
"silverstripe/versioned": "^1.4@dev"
}, },
"require-dev": { "require-dev": {
"phpunit/phpunit": "^5.7", "phpunit/phpunit": "^9.5",
"squizlabs/php_codesniffer": "^3.0" "squizlabs/php_codesniffer": "^3.0"
}, },
"autoload": { "autoload": {
@ -34,8 +35,8 @@
}, },
"extra": { "extra": {
"expose": [ "expose": [
"javascript", "client/javascript",
"css" "client/css"
] ]
}, },
"minimum-stability": "dev", "minimum-stability": "dev",

View File

@ -48,3 +48,14 @@ to speak to your website administrator or hosting provider to facilitate this.
You can simulate subsite access without setting up virtual hosts by appending ?SubsiteID=<ID> to the request. You can simulate subsite access without setting up virtual hosts by appending ?SubsiteID=<ID> to the request.
### How do Subsite domains work with Fluent domains?
The Subsites module and Fluent translation module both provide the concept of defining "domains" and let you
configure the host name for it. This functionality is essentially performing the same duty in both modules.
In the "URL segment" field for CMS pages, both Subsites and Fluent will add their context to the value. If you
have a Subsite domain configured but no Fluent domain, Fluent will respect the existing domain and add its
locale context to the value. If you have a Subsite domain configured and a Fluent domain configured, Fluent will
use its own domain host name value, and the Subsite domain value will be lost. For this reason, you will need
to ensure that you use the same host name in both Subsite and Fluent domain entries.

View File

@ -28,5 +28,5 @@ This method is called when a pages are being copied between the main site or ano
### alternateAbsoluteLink ### alternateAbsoluteLink
This method modifies the absolute link to contain the valid subsite domain This method modifies the absolute link to contain the valid subsite domain
### alternatePreviewLink ### updatePreviewLink
This method modifies the preview link for the CMS. This method modifies the preview link for the CMS.

View File

@ -1,5 +1,7 @@
---
title: Working with multiple websites title: Working with multiple websites
summary: Setting up and editing multiple websites using SilverStripe summary: Setting up and editing multiple websites using SilverStripe
---
# Working with multiple sites # Working with multiple sites

View File

@ -1,3 +1,6 @@
---
title: Setting up
---
# Setting up # Setting up
## Creating subsites ## Creating subsites

View File

@ -1,3 +1,6 @@
---
title: Working with subsites
---
# Working with subsites # Working with subsites
## Managing content across subsites ## Managing content across subsites

View File

@ -1,4 +1,65 @@
ar: ar:
SilverStripe\CMS\Model\VirtualPage:
EDITCONTENT: 'انقر هنا لتحرير المحتوى'
SilverStripe\Subsites\Admin\SubsiteAdmin:
MENUTITLE: 'المواقع الفرعية'
SilverStripe\Subsites\Extensions\FileSubsites:
has_one_Subsite: 'الموقع الفرعي'
SilverStripe\Subsites\Extensions\FolderFormFactoryExtension:
AllSitesDropdownOpt: 'كافة المواقع'
SUBSITENOTICE: 'يمكن الوصول إلى المجلدات والملفات التي تم إنشاؤها في الموقع الرئيسي من طرف كل المواقع الفرعية.'
SubsiteFieldLabel: 'الموقع الفرعي'
SilverStripe\Subsites\Extensions\GroupSubsites:
ACCESSALL: 'كل المواقع الفرعية'
ACCESSONLY: 'فقط هذه المواقع الفرعية'
ACCESSRADIOTITLE: 'امنح هذه المجموعة تصريح الولوج إلى'
GlobalGroup: 'المجموعة العامة'
MANAGE_SUBSITES: 'إدارة مواقع فرعية للمجموعات'
MANAGE_SUBSITES_HELP: 'القدرة على الحد من أذونات مجموعة ما على موقع فرعي واحدة أو أكثر.'
SECURITYTABTITLE: 'المواقع الفرعية'
many_many_Subsites: 'المواقع الفرعية'
SilverStripe\Subsites\Extensions\LeftAndMainSubsites:
Saved: 'تمّ الحفظ، يرجى تحديث الصفحات ذات الصلة.'
SilverStripe\Subsites\Extensions\SiteConfigSubsites:
has_one_Subsite: 'الموقع الفرعي'
SilverStripe\Subsites\Extensions\SiteTreeSubsites:
CopyAction: نسخ
CopyToSubsite: 'نسخ الصفحة في موقع فرعي'
has_one_Subsite: 'الموقع الفرعي'
SilverStripe\Subsites\Model\Subsite:
ConfigurationTab: المواصفات
CopyMessage: 'إنشاء نسخة من {title}'
CustomExtraMeta: 'تخصيص Meta Tags'
CustomMetaDescription: الوصف
CustomMetaKeywords: 'كلمات البحث'
CustomMetaTitle: عنوان
PLURALNAME: 'المواقع الفرعية'
PageTypeBlacklistField: 'عدم السماح بأصناف الصفحات؟'
SINGULARNAME: 'الموقع الفرعي'
SiteConfigSubtitle: 'هنا سطر الوصف الخاص بك'
SiteConfigTitle: 'اسم موقعك'
ValidateTitle: 'الرجاء إضافة "عنوان"'
belongs_many_many_Groups: المجموعات
db_DefaultSite: 'الموقع الافتراضي'
db_Language: لغة
db_RedirectURL: 'إعادة توجيه عنوان موقع الويب'
db_Theme: المحور
db_Title: عنوان
has_many_Domains: النطاقات
SilverStripe\Subsites\Model\SubsiteDomain:
DOMAIN: النطاق
PLURALNAME: 'نطاقات موقع فرعي'
SINGULARNAME: 'نطاق موقع فرعي'
db_Domain: النطاق
has_one_Subsite: 'الموقع الفرعي'
SilverStripe\Subsites\Pages\SubsitesVirtualPage:
DESCRIPTION: 'يعرض محتوى صفحة على موقع فرعي آخر'
PLURALNAME: 'قاعدة الصفحات'
SINGULARNAME: 'الصفحة الإفتراضية للمواقع الفرعية'
SubsiteField: 'الموقع الفرعي'
SilverStripe\Subsites\Reports\SubsiteReportWrapper:
ReportDropdown: المواقع
ReportDropdownSubsite: 'الموقع الفرعي'
Subsite: Subsite:
COPYSTRUCTURE: 'نسخ الهيكل من:' COPYSTRUCTURE: 'نسخ الهيكل من:'
NOTEMPLATE: 'بدون قالب' NOTEMPLATE: 'بدون قالب'

View File

@ -2,6 +2,36 @@ cs:
LeftAndMain_Menu: LeftAndMain_Menu:
Hello: Ahoj Hello: Ahoj
LOGOUT: 'Odhlásit se' LOGOUT: 'Odhlásit se'
SilverStripe\Subsites\Admin\SubsiteAdmin:
MENUTITLE: Subsites
SilverStripe\Subsites\Extensions\GroupSubsites:
SECURITYTABTITLE: Subsites
many_many_Subsites: Subsites
SilverStripe\Subsites\Extensions\SiteTreeSubsites:
CopyAction: Kopírovat
SilverStripe\Subsites\Model\Subsite:
ConfigurationTab: Konfigurace
CustomExtraMeta: 'Vlastní meta tagy'
CustomMetaDescription: Popis
CustomMetaKeywords: 'Klíčová slova'
CustomMetaTitle: Název
PLURALNAME: Subsites
SiteConfigSubtitle: 'Slogan Vašeho webu'
SiteConfigTitle: 'Název Vašeho webu'
ValidateTitle: 'Prosím vložte "Název"'
belongs_many_many_Groups: Skupiny
db_DefaultSite: 'Výchozí web'
db_Language: Jazyk
db_Theme: Téma
db_Title: Název
has_many_Domains: Domény
SilverStripe\Subsites\Model\SubsiteDomain:
DOMAIN: Doména
PLURALNAME: 'Domény webů'
SINGULARNAME: 'Doména webu'
db_Domain: Doména
SilverStripe\Subsites\Reports\SubsiteReportWrapper:
ReportDropdown: Weby
SiteTreeSubsites: SiteTreeSubsites:
CopyAction: Kopírovat CopyAction: Kopírovat
Subsite: Subsite:

View File

@ -1,6 +1,89 @@
de: de:
DomainNameField: DomainNameField:
INVALID_DOMAIN: 'Ungültige Domain' INVALID_DOMAIN: 'Ungültige Domain'
SilverStripe\CMS\Model\VirtualPage:
EDITCONTENT: 'Klicken Sie hier, um den Inhalt zu bearbeiten'
SilverStripe\Subsites\Admin\SubsiteAdmin:
MENUTITLE: Subsites
SilverStripe\Subsites\Extensions\FileSubsites:
has_one_Subsite: Subseite
SilverStripe\Subsites\Extensions\FolderFormFactoryExtension:
AllSitesDropdownOpt: 'Alle Seiten'
SUBSITENOTICE: 'Auf Ordner und Dateien der Hauptseite kann von allen Subsites zugegriffen werden.'
SubsiteFieldLabel: Subseite
SilverStripe\Subsites\Extensions\GroupSubsites:
ACCESSALL: 'Alle Subsites'
ACCESSONLY: 'Nur diese Subsites'
ACCESSRADIOTITLE: 'Dieser Gruppe Zugriff geben auf'
GlobalGroup: 'globale Gruppe'
MANAGE_SUBSITES: 'Subseiten für jede Gruppe bearbeiten'
MANAGE_SUBSITES_HELP: 'Möglichkeit, die Berechtigungen einer Gruppe auf bestimmte Subsites zu beschränken.'
SECURITYTABTITLE: Subsites
many_many_Subsites: Subsites
SilverStripe\Subsites\Extensions\LeftAndMainSubsites:
SITECONTENTLEFT: 'Seiten Inhalt'
Saved: 'Gespeichert, bitte aktualisieren Sie verknüpfte Seiten'
SilverStripe\Subsites\Extensions\SiteConfigSubsites:
has_one_Subsite: Subseite
SilverStripe\Subsites\Extensions\SiteTreeSubsites:
CopyAction: Kopieren
CopyToSubsite: 'Seite auf Subseite Kopieren'
CopyToSubsiteWithChildren: 'Samt Unterseiten?'
SubsiteOperations: 'Subseiten Operationen'
has_one_Subsite: Subseite
SilverStripe\Subsites\Model\Subsite:
ConfigurationTab: Einstellungen
CopyMessage: 'Kopie von {title} erstellt'
CustomExtraMeta: 'Benutzerdefinierte Meta-Tags'
CustomMetaDescription: Beschreibung
CustomMetaKeywords: Schlüsselwörter
CustomMetaTitle: Titel
PLURALNAME: Subsites
PLURALS:
one: 'Eine Subseite'
other: '{count} Subsites'
PageTypeBlacklistField: 'Seitentypen verbieten?'
SINGULARNAME: Subseite
SiteConfigSubtitle: 'Ihr Websiteslogan'
SiteConfigTitle: 'Name Ihrer Website'
ThemeFieldEmptyString: '-'
ValidateTitle: 'Bitte geben Sie einen Titel an'
belongs_many_many_Groups: Gruppe
db_DefaultSite: 'Standard Seite'
db_Language: Sprache
db_RedirectURL: Weierleitungs-URL
db_Theme: Theme
db_Title: Titel
has_many_Domains: Domains
SilverStripe\Subsites\Model\SubsiteDomain:
DOMAIN: Domäne
DOMAIN_DESCRIPTION: 'Hostname dieser Subsite (ohne Protokol). Joker (*) ist erlaubt.'
IS_PRIMARY: 'Ist Hauptdomäne?'
PLURALNAME: 'Domänen der Subseite'
PLURALS:
one: 'Eine Domäne der Subseite'
other: '{count} Domainen der Subseite'
PROTOCOL_AUTOMATIC: Automatisch
PROTOCOL_DESCRIPTION: 'DIes ist die Standarddomäne für diese Subseite'
PROTOCOL_HTTP: 'http://'
PROTOCOL_HTTPS: 'https://'
Protocol: Protokoll
SINGULARNAME: 'Domäne der Subseite'
db_Domain: Domäne
db_Protocol: Protokoll
has_one_Subsite: Subseite
SilverStripe\Subsites\Pages\SubsitesVirtualPage:
DESCRIPTION: 'Zeigt den Inhalt einer anderen Seite von einer anderen Subseite an'
OverrideNote: 'Überschreibt den geerbten Wert der Quelle'
PLURALNAME: 'Subsites Virtuelle Seiten'
PLURALS:
one: 'Eine Subsites Virtuelle Seite'
other: '{count} Subsites Virtuelle Seiten'
SINGULARNAME: 'Subsites Virtuelle Seite'
SubsiteField: Subseite
SilverStripe\Subsites\Reports\SubsiteReportWrapper:
ReportDropdown: Seiten
ReportDropdownSubsite: Subseite
Subsite: Subsite:
COPYSTRUCTURE: 'Struktur kopieren von:' COPYSTRUCTURE: 'Struktur kopieren von:'
NOTEMPLATE: 'Kein Template' NOTEMPLATE: 'Kein Template'
@ -9,9 +92,9 @@ de:
DomainFieldLabel: Domäne DomainFieldLabel: Domäne
IsPublicFieldLabel: 'Öffentlich zugänglich' IsPublicFieldLabel: 'Öffentlich zugänglich'
LanguageFieldLabel: Sprache LanguageFieldLabel: Sprache
MainSiteTitle: 'Hauptsite' MainSiteTitle: Hauptsite
PageTypeBlacklistFieldLabel: 'Ausgeschlossene Seitentypen' PageTypeBlacklistFieldLabel: 'Ausgeschlossene Seitentypen'
PrimaryDomainFieldLabel: 'Primäre Domain' PrimaryDomainFieldLabel: 'Primäre Domain'
RedirectURLFieldLabel: 'Weierleitungs-URL' RedirectURLFieldLabel: Weierleitungs-URL
ThemeFieldLabel: Theme ThemeFieldLabel: Theme
TitleFieldLabel: 'Name der Subsite' TitleFieldLabel: 'Name der Subsite'

View File

@ -7,6 +7,8 @@ en:
MENUTITLE: Subsites MENUTITLE: Subsites
SilverStripe\Subsites\Controller\SubsiteXHRController: SilverStripe\Subsites\Controller\SubsiteXHRController:
MENUTITLE: SilverStripe\Subsites\Controller\SubsiteXHRController MENUTITLE: SilverStripe\Subsites\Controller\SubsiteXHRController
SilverStripe\Subsites\Extensions\FileSubsites:
has_one_Subsite: Subsite
SilverStripe\Subsites\Extensions\FolderFormFactoryExtension: SilverStripe\Subsites\Extensions\FolderFormFactoryExtension:
AllSitesDropdownOpt: 'All sites' AllSitesDropdownOpt: 'All sites'
SUBSITENOTICE: 'Folders and files created in the main site are accessible by all subsites.' SUBSITENOTICE: 'Folders and files created in the main site are accessible by all subsites.'
@ -19,14 +21,20 @@ en:
MANAGE_SUBSITES: 'Manage subsites for groups' MANAGE_SUBSITES: 'Manage subsites for groups'
MANAGE_SUBSITES_HELP: 'Ability to limit the permissions for a group to one or more subsites.' MANAGE_SUBSITES_HELP: 'Ability to limit the permissions for a group to one or more subsites.'
SECURITYTABTITLE: Subsites SECURITYTABTITLE: Subsites
db_AccessAllSubsites: 'Access all subsites'
many_many_Subsites: Subsites
SilverStripe\Subsites\Extensions\LeftAndMainSubsites: SilverStripe\Subsites\Extensions\LeftAndMainSubsites:
SITECONTENTLEFT: 'Site Content' SITECONTENTLEFT: 'Site Content'
Saved: 'Saved, please update related pages.' Saved: 'Saved, please update related pages.'
SilverStripe\Subsites\Extensions\SiteConfigSubsites:
has_one_Subsite: Subsite
SilverStripe\Subsites\Extensions\SiteTreeSubsites: SilverStripe\Subsites\Extensions\SiteTreeSubsites:
CopyAction: Copy CopyAction: Copy
CopyToSubsite: 'Copy page to subsite' CopyToSubsite: 'Copy page to subsite'
CopyToSubsiteWithChildren: 'Include children pages?' CopyToSubsiteWithChildren: 'Include children pages?'
SubsiteOperations: 'Subsite Operations' SubsiteOperations: 'Subsite Operations'
has_one_Subsite: Subsite
many_many_CrossSubsiteLinkTracking: 'Cross subsite link tracking'
SilverStripe\Subsites\Model\Subsite: SilverStripe\Subsites\Model\Subsite:
ConfigurationTab: Configuration ConfigurationTab: Configuration
CopyMessage: 'Created a copy of {title}' CopyMessage: 'Created a copy of {title}'
@ -44,20 +52,34 @@ en:
SiteConfigTitle: 'Your Site Name' SiteConfigTitle: 'Your Site Name'
ThemeFieldEmptyString: '-' ThemeFieldEmptyString: '-'
ValidateTitle: 'Please add a "Title"' ValidateTitle: 'Please add a "Title"'
belongs_many_many_Groups: Groups
db_DefaultSite: 'Default site'
db_IsPublic: 'Is public'
db_Language: Language
db_PageTypeBlacklist: 'Page type blacklist'
db_RedirectURL: 'Redirect URL'
db_Theme: Theme
db_Title: Title
has_many_Domains: Domains
SilverStripe\Subsites\Model\SubsiteDomain: SilverStripe\Subsites\Model\SubsiteDomain:
DOMAIN: Domain DOMAIN: Domain
DOMAIN_DESCRIPTION: 'Hostname of this subsite (exclude protocol). Allows wildcards (*).' DOMAIN_DESCRIPTION: 'Hostname of this subsite (exclude protocol). Allows wildcards (*).'
ISPRIMARY_DESCRIPTION: 'Mark this as the default domain for this subsite'
IS_PRIMARY: 'Is Primary Domain?' IS_PRIMARY: 'Is Primary Domain?'
PLURALNAME: 'Subsite Domains' PLURALNAME: 'Subsite Domains'
PLURALS: PLURALS:
one: 'A Subsite Domain' one: 'A Subsite Domain'
other: '{count} Subsite Domains' other: '{count} Subsite Domains'
PROTOCOL_AUTOMATIC: Automatic PROTOCOL_AUTOMATIC: Automatic
PROTOCOL_DESCRIPTION: 'Mark this as the default domain for this subsite' PROTOCOL_DESCRIPTION: 'When generating links to this subsite, use the selected protocol. <br />Selecting ''Automatic'' means subsite links will default to the current protocol.'
PROTOCOL_HTTP: 'http://' PROTOCOL_HTTP: 'http://'
PROTOCOL_HTTPS: 'https://' PROTOCOL_HTTPS: 'https://'
Protocol: Protocol Protocol: Protocol
SINGULARNAME: 'Subsite Domain' SINGULARNAME: 'Subsite Domain'
db_Domain: Domain
db_IsPrimary: 'Is primary'
db_Protocol: Protocol
has_one_Subsite: Subsite
SilverStripe\Subsites\Pages\SubsitesVirtualPage: SilverStripe\Subsites\Pages\SubsitesVirtualPage:
DESCRIPTION: 'Displays the content of a page on another subsite' DESCRIPTION: 'Displays the content of a page on another subsite'
OverrideNote: 'Overrides inherited value from the source' OverrideNote: 'Overrides inherited value from the source'
@ -67,8 +89,14 @@ en:
other: '{count} Subsites Virtual Pages' other: '{count} Subsites Virtual Pages'
SINGULARNAME: 'Subsites Virtual Page' SINGULARNAME: 'Subsites Virtual Page'
SubsiteField: Subsite SubsiteField: Subsite
db_CustomExtraMeta: 'Custom extra meta'
db_CustomMetaDescription: 'Custom meta description'
db_CustomMetaKeywords: 'Custom meta keywords'
db_CustomMetaTitle: 'Custom meta title'
SilverStripe\Subsites\Reports\SubsiteReportWrapper: SilverStripe\Subsites\Reports\SubsiteReportWrapper:
ReportDropdown: Sites ReportDropdown: Sites
ReportDropdownAll: All
ReportDropdownSubsite: Subsite
Subsite: Subsite:
COPYSTRUCTURE: 'Copy structure from:' COPYSTRUCTURE: 'Copy structure from:'
NOTEMPLATE: 'No template' NOTEMPLATE: 'No template'

View File

@ -7,6 +7,8 @@ eo:
MENUTITLE: Subretejoj MENUTITLE: Subretejoj
SilverStripe\Subsites\Controller\SubsiteXHRController: SilverStripe\Subsites\Controller\SubsiteXHRController:
MENUTITLE: SilverStripe\Subretejoj\Reganto\SubretejaXHRReganto MENUTITLE: SilverStripe\Subretejoj\Reganto\SubretejaXHRReganto
SilverStripe\Subsites\Extensions\FileSubsites:
has_one_Subsite: Subretejo
SilverStripe\Subsites\Extensions\FolderFormFactoryExtension: SilverStripe\Subsites\Extensions\FolderFormFactoryExtension:
AllSitesDropdownOpt: 'Ĉiuj retejoj' AllSitesDropdownOpt: 'Ĉiuj retejoj'
SUBSITENOTICE: 'Dosierujoj kaj dosieroj kreitaj en la ĉefa retejo estas alireblaj de ĉiuj retejoj' SUBSITENOTICE: 'Dosierujoj kaj dosieroj kreitaj en la ĉefa retejo estas alireblaj de ĉiuj retejoj'
@ -19,14 +21,20 @@ eo:
MANAGE_SUBSITES: 'Administri subretejojn por grupoj' MANAGE_SUBSITES: 'Administri subretejojn por grupoj'
MANAGE_SUBSITES_HELP: 'Eblo limigi la permesojn por grupo al unu aŭ pluaj subretejoj.' MANAGE_SUBSITES_HELP: 'Eblo limigi la permesojn por grupo al unu aŭ pluaj subretejoj.'
SECURITYTABTITLE: Subretejoj SECURITYTABTITLE: Subretejoj
db_AccessAllSubsites: 'Aliri ĉiujn subretejojn'
many_many_Subsites: Subretejoj
SilverStripe\Subsites\Extensions\LeftAndMainSubsites: SilverStripe\Subsites\Extensions\LeftAndMainSubsites:
SITECONTENTLEFT: 'Enhavo de retejo' SITECONTENTLEFT: 'Enhavo de retejo'
Saved: 'Konservita, bonvole ĝisdatigi rilatajn paĝojn.' Saved: 'Konservita, bonvole ĝisdatigi rilatajn paĝojn.'
SilverStripe\Subsites\Extensions\SiteConfigSubsites:
has_one_Subsite: Subretejo
SilverStripe\Subsites\Extensions\SiteTreeSubsites: SilverStripe\Subsites\Extensions\SiteTreeSubsites:
CopyAction: Kopio CopyAction: Kopio
CopyToSubsite: 'Kopii paĝon al subretejo' CopyToSubsite: 'Kopii paĝon al subretejo'
CopyToSubsiteWithChildren: 'Ĉu inkluzivi paĝidojn?' CopyToSubsiteWithChildren: 'Ĉu inkluzivi paĝidojn?'
SubsiteOperations: 'Subretejaj operacioj' SubsiteOperations: 'Subretejaj operacioj'
has_one_Subsite: Subretejo
many_many_CrossSubsiteLinkTracking: 'Trans-subreteja ligspurado'
SilverStripe\Subsites\Model\Subsite: SilverStripe\Subsites\Model\Subsite:
ConfigurationTab: Agordaro ConfigurationTab: Agordaro
CopyMessage: 'Kreis kopion de {title}' CopyMessage: 'Kreis kopion de {title}'
@ -44,20 +52,34 @@ eo:
SiteConfigTitle: 'Nomo de via retejo' SiteConfigTitle: 'Nomo de via retejo'
ThemeFieldEmptyString: '-' ThemeFieldEmptyString: '-'
ValidateTitle: 'Bonvole aldoni "Titolon"' ValidateTitle: 'Bonvole aldoni "Titolon"'
belongs_many_many_Groups: Grupoj
db_DefaultSite: 'Apriora retejo'
db_IsPublic: 'Estas publika'
db_Language: Lingvo
db_PageTypeBlacklist: 'Paĝtipa nigra listo'
db_RedirectURL: 'Redirekti je URL'
db_Theme: Etoso
db_Title: Titolo
has_many_Domains: Domajnoj
SilverStripe\Subsites\Model\SubsiteDomain: SilverStripe\Subsites\Model\SubsiteDomain:
DOMAIN: Domajno DOMAIN: Domajno
DOMAIN_DESCRIPTION: 'Gastiga nomo de ĉi tiu subretejo (ellasu protokolon). Permesas ĵokerojn (*).' DOMAIN_DESCRIPTION: 'Gastiga nomo de ĉi tiu subretejo (ellasu protokolon). Permesas ĵokerojn (*).'
ISPRIMARY_DESCRIPTION: 'Marki ĉi tion kiel la aprioran domajnon por ĉi tiu subretejo'
IS_PRIMARY: 'Ĉu unuaranga domajno?' IS_PRIMARY: 'Ĉu unuaranga domajno?'
PLURALNAME: 'Subretejaj domajnoj' PLURALNAME: 'Subretejaj domajnoj'
PLURALS: PLURALS:
one: 'Unu subreteja domajno' one: 'Unu subreteja domajno'
other: '{count} subretejaj domajnoj' other: '{count} subretejaj domajnoj'
PROTOCOL_AUTOMATIC: Aŭtomata PROTOCOL_AUTOMATIC: Aŭtomata
PROTOCOL_DESCRIPTION: 'Marki ĉi tion kiel la aprioran domajnon por ĉi tiu subretejo' PROTOCOL_DESCRIPTION: 'Kiam generante ligilojn al ĉi tiu subretejo, uzu la elektitan protokolon.'
PROTOCOL_HTTP: 'http://' PROTOCOL_HTTP: 'http://'
PROTOCOL_HTTPS: 'https://' PROTOCOL_HTTPS: 'https://'
Protocol: Protokolo Protocol: Protokolo
SINGULARNAME: 'Subreteja domajno' SINGULARNAME: 'Subreteja domajno'
db_Domain: Domajno
db_IsPrimary: 'Estas unuaranga'
db_Protocol: Protokolo
has_one_Subsite: Subretejo
SilverStripe\Subsites\Pages\SubsitesVirtualPage: SilverStripe\Subsites\Pages\SubsitesVirtualPage:
DESCRIPTION: 'Vidigas la enhavon de paĝo en alia subretejo' DESCRIPTION: 'Vidigas la enhavon de paĝo en alia subretejo'
OverrideNote: 'Anstataŭigas hereditan valoron el la fonto' OverrideNote: 'Anstataŭigas hereditan valoron el la fonto'
@ -67,8 +89,14 @@ eo:
other: '{count} virtualaj paĝoj de subretejoj' other: '{count} virtualaj paĝoj de subretejoj'
SINGULARNAME: 'Virtuala paĝo de subretejoj' SINGULARNAME: 'Virtuala paĝo de subretejoj'
SubsiteField: Subretejo SubsiteField: Subretejo
db_CustomExtraMeta: 'Propra kroma meta'
db_CustomMetaDescription: 'Propra metapriskribo'
db_CustomMetaKeywords: 'Propraj meta-ŝlosilvortoj '
db_CustomMetaTitle: 'Propraj meta-titolo'
SilverStripe\Subsites\Reports\SubsiteReportWrapper: SilverStripe\Subsites\Reports\SubsiteReportWrapper:
ReportDropdown: Retejoj ReportDropdown: Retejoj
ReportDropdownAll: Ĉiuj
ReportDropdownSubsite: Subretejo
Subsite: Subsite:
COPYSTRUCTURE: 'Kopii strukturon de:' COPYSTRUCTURE: 'Kopii strukturon de:'
NOTEMPLATE: 'Mankas ŝablono' NOTEMPLATE: 'Mankas ŝablono'

13
lang/es.yml Normal file
View File

@ -0,0 +1,13 @@
es:
SilverStripe\Subsites\Model\Subsite:
CustomExtraMeta: 'Meta Tags Personalizadas'
CustomMetaDescription: Descripción
CustomMetaTitle: Título
SiteConfigTitle: 'Nombre de tu Sitio'
belongs_many_many_Groups: Grupos
db_Language: Lenguaje
db_Theme: Tema
db_Title: Título
Subsites:
LanguageFieldLabel: Lenguaje
ThemeFieldLabel: Tema

13
lang/et_EE.yml Normal file
View File

@ -0,0 +1,13 @@
et_EE:
SilverStripe\Subsites\Model\Subsite:
CustomExtraMeta: 'Kohandatud metamärgendid'
CustomMetaDescription: Kirjeldus
CustomMetaTitle: Pealkiri
SiteConfigTitle: 'Teie saidi nimi'
belongs_many_many_Groups: Grupid
db_Language: Keel
db_Theme: Kujundus
db_Title: Pealkiri
Subsites:
LanguageFieldLabel: Keel
ThemeFieldLabel: Kujundus

View File

@ -1,6 +1,53 @@
fa_IR: fa_IR:
DomainNameField: DomainNameField:
INVALID_DOMAIN: 'نام دامنه نامعتبر' INVALID_DOMAIN: 'نام دامنه نامعتبر'
SilverStripe\CMS\Model\VirtualPage:
EDITCONTENT: 'برای ویرایش محتوا اینجا را کلیک کنید'
SilverStripe\Subsites\Admin\SubsiteAdmin:
MENUTITLE: 'زیر سایت ها'
SilverStripe\Subsites\Extensions\FileSubsites:
has_one_Subsite: 'زیر سایت'
SilverStripe\Subsites\Extensions\FolderFormFactoryExtension:
AllSitesDropdownOpt: 'تمامی سایت ها'
SubsiteFieldLabel: 'زیر سایت'
SilverStripe\Subsites\Extensions\GroupSubsites:
ACCESSALL: 'تمامی زیر سایت ها'
ACCESSONLY: 'فقط این زیر سایت ها'
SECURITYTABTITLE: 'زیر سایت ها'
many_many_Subsites: 'زیر سایت ها'
SilverStripe\Subsites\Extensions\SiteConfigSubsites:
has_one_Subsite: 'زیر سایت'
SilverStripe\Subsites\Extensions\SiteTreeSubsites:
has_one_Subsite: 'زیر سایت'
SilverStripe\Subsites\Model\Subsite:
ConfigurationTab: پیکربندی
CustomExtraMeta: 'متا تگ‌های اختصاصی'
CustomMetaDescription: توضحیات
CustomMetaKeywords: 'کلید واژه ها'
CustomMetaTitle: عنوان
PLURALNAME: 'زیر سایت ها'
SINGULARNAME: 'زیر سایت'
SiteConfigTitle: 'نام سایت شما'
belongs_many_many_Groups: گروه‌ها
db_DefaultSite: 'سایت پیش فرض'
db_Language: زبان
db_Theme: پوسته
db_Title: عنوان
has_many_Domains: 'دامنه ها'
SilverStripe\Subsites\Model\SubsiteDomain:
DOMAIN: دامنه
PLURALNAME: 'دامنه های زیر سایت'
PROTOCOL_AUTOMATIC: 'به صورت خودکار'
Protocol: پروتکل
SINGULARNAME: 'دمنه زیرسایت'
db_Domain: دامنه
db_Protocol: پروتکل
has_one_Subsite: 'زیر سایت'
SilverStripe\Subsites\Pages\SubsitesVirtualPage:
SubsiteField: 'زیر سایت'
SilverStripe\Subsites\Reports\SubsiteReportWrapper:
ReportDropdown: 'سایت ها'
ReportDropdownSubsite: 'زیر سایت'
Subsite: Subsite:
NOTEMPLATE: 'بدون قالب' NOTEMPLATE: 'بدون قالب'
Subsites: Subsites:

View File

@ -1,17 +1,92 @@
fi: fi:
DomainNameField: DomainNameField:
INVALID_DOMAIN: 'Virheellinen domain-nimi' INVALID_DOMAIN: 'Virheellinen domain-nimi'
SilverStripe\CMS\Model\VirtualPage:
EDITCONTENT: 'Napsauta tässä muokataksesi sisältöä'
SilverStripe\Subsites\Admin\SubsiteAdmin:
MENUTITLE: Alasivustot
SilverStripe\Subsites\Extensions\FileSubsites:
has_one_Subsite: Alasivusto
SilverStripe\Subsites\Extensions\FolderFormFactoryExtension:
AllSitesDropdownOpt: 'Kaikki sivustot'
SUBSITENOTICE: 'Kansiot ja tiedostot, jotka on luotu pääsivustolla, ovat käytettävissä kaikissa alisivustoissa.'
SubsiteFieldLabel: Alasivusto
SilverStripe\Subsites\Extensions\GroupSubsites:
ACCESSALL: 'Kaikki alasivustot'
ACCESSONLY: 'Vain nämä alasivustot'
ACCESSRADIOTITLE: 'Anna ryhmän oikeudet »'
GlobalGroup: Globaaliryhmä
MANAGE_SUBSITES: 'Hallinnoi ryhmien alasivustoja'
MANAGE_SUBSITES_HELP: 'Mahdollisuus rajoittaa ryhmän oikeuksia yhdelle tai useammalle alisivustolle.'
SECURITYTABTITLE: Alasivustot
many_many_Subsites: Alasivut
SilverStripe\Subsites\Extensions\LeftAndMainSubsites:
SITECONTENTLEFT: 'Sivuston sisältö'
Saved: 'Tallennettu, ole hyvä ja päivitä liittyvät sivut.'
SilverStripe\Subsites\Extensions\SiteConfigSubsites:
has_one_Subsite: Alasivusto
SilverStripe\Subsites\Extensions\SiteTreeSubsites:
CopyAction: Kopioi
CopyToSubsite: 'Kopioi sivu alasivustolle'
CopyToSubsiteWithChildren: 'Sisällytä alasivut?'
SubsiteOperations: 'Alasivuston toiminnot'
has_one_Subsite: Alasivusto
SilverStripe\Subsites\Model\Subsite:
ConfigurationTab: Asetukset
CopyMessage: 'Luotiin kopio lähteestä {title}'
CustomExtraMeta: 'Omat meta-tagit'
CustomMetaDescription: Kuvaus
CustomMetaKeywords: Avainsanat
CustomMetaTitle: Otsikko
PLURALNAME: Alasivut
PLURALS:
one: Alasivu
other: '{count} alasivua'
PageTypeBlacklistField: 'Kiellä sivutyyppien käyttö?'
SINGULARNAME: Alasivusto
SiteConfigSubtitle: 'Tähän sloganisi'
SiteConfigTitle: 'Sivuston nimi'
ValidateTitle: 'Lisää "Otsikko"'
belongs_many_many_Groups: Ryhmät
db_DefaultSite: Oletussivusto
db_Language: Kieli
db_RedirectURL: 'Edelleenohjaus URL'
db_Theme: Teema
db_Title: Otsikko
has_many_Domains: Domainit
SilverStripe\Subsites\Model\SubsiteDomain:
DOMAIN: Verkkotunnus
DOMAIN_DESCRIPTION: 'Tämä alisivuston isäntänimi (ilman protokollaa). Sallii wildcard-merkit (*).'
ISPRIMARY_DESCRIPTION: 'Merkitse tämä oletusdomainiksi tälle alisivustolle'
IS_PRIMARY: 'On päädomain?'
PLURALNAME: 'Alisivuston domain-osoitteet'
PROTOCOL_AUTOMATIC: Automaattinen
PROTOCOL_HTTP: 'http://'
PROTOCOL_HTTPS: 'https://'
Protocol: Protokolla
SINGULARNAME: 'Alisivuston domain-osoite'
db_Domain: Verkkotunnus
db_Protocol: Protokolla
has_one_Subsite: Alasivusto
SilverStripe\Subsites\Pages\SubsitesVirtualPage:
DESCRIPTION: 'Näyttää sisällön toisen alisivuston sivulta'
PLURALNAME: 'Alisivustojen virtuaaliset sivut'
SINGULARNAME: 'Alisivuston Virtuaalisivu'
SubsiteField: Alasivusto
SilverStripe\Subsites\Reports\SubsiteReportWrapper:
ReportDropdown: Sivustot
ReportDropdownSubsite: Alasivusto
Subsite: Subsite:
COPYSTRUCTURE: 'Kopioi rakenne kohteesta:' COPYSTRUCTURE: 'Kopioi rakenne kohteesta:'
NOTEMPLATE: 'Ei sivupohjaa' NOTEMPLATE: 'Ei sivupohjaa'
Subsites: Subsites:
DefaultSiteFieldLabel: 'Oletussivusto' DefaultSiteFieldLabel: Oletussivusto
DomainFieldLabel: Domain DomainFieldLabel: Domain
IsPublicFieldLabel: 'Aktivoi julkinen pääsy' IsPublicFieldLabel: 'Aktivoi julkinen pääsy'
LanguageFieldLabel: Kieli LanguageFieldLabel: Kieli
MainSiteTitle: 'Pääsivusto' MainSiteTitle: Pääsivusto
PageTypeBlacklistFieldLabel: 'Sivutyyppien mustalista' PageTypeBlacklistFieldLabel: 'Sivutyyppien mustalista'
PrimaryDomainFieldLabel: 'Oletusdomain' PrimaryDomainFieldLabel: Oletusdomain
RedirectURLFieldLabel: 'Edelleenohjaus URL' RedirectURLFieldLabel: 'Edelleenohjaus URL'
ThemeFieldLabel: Teema ThemeFieldLabel: Teema
TitleFieldLabel: 'Alisivuston nimi' TitleFieldLabel: 'Alisivuston nimi'

14
lang/fr.yml Normal file
View File

@ -0,0 +1,14 @@
fr:
SilverStripe\Subsites\Model\Subsite:
ConfigurationTab: Configuration
CustomExtraMeta: 'Balises Méta personnalisées'
CustomMetaDescription: Description
CustomMetaTitle: Titre
SiteConfigTitle: 'Nom du site'
belongs_many_many_Groups: Groupes
db_Language: Langue
db_Theme: Thème
db_Title: Titre
Subsites:
LanguageFieldLabel: Langue
ThemeFieldLabel: Thème

View File

@ -1,6 +1,76 @@
hr: hr:
DomainNameField: DomainNameField:
INVALID_DOMAIN: 'Netočan naziv domene' INVALID_DOMAIN: 'Netočan naziv domene'
SilverStripe\CMS\Model\VirtualPage:
EDITCONTENT: 'Klikni ovdje za uređivanje sadržaja'
SilverStripe\Subsites\Admin\SubsiteAdmin:
MENUTITLE: Podsajtovi
SilverStripe\Subsites\Extensions\FileSubsites:
has_one_Subsite: Podsajt
SilverStripe\Subsites\Extensions\FolderFormFactoryExtension:
AllSitesDropdownOpt: 'Svi sajtovi'
SUBSITENOTICE: 'Direktoriji i datoteke kreirane u glavnom sajtu su dostupne svim podsajtovima.'
SubsiteFieldLabel: Podsajt
SilverStripe\Subsites\Extensions\GroupSubsites:
ACCESSALL: 'Svi podsajtovi'
ACCESSONLY: 'Samo ovi podsajtovi'
ACCESSRADIOTITLE: 'Dodijeli ovoj grupi pristup za'
GlobalGroup: 'globalna grupa'
MANAGE_SUBSITES: 'Upravljaj podsajtove za grupe'
MANAGE_SUBSITES_HELP: 'Mogućnost limitiranja prava za grupu za jedan ili više podsajtova.'
SECURITYTABTITLE: Podsajtovi
many_many_Subsites: Podsajtovi
SilverStripe\Subsites\Extensions\LeftAndMainSubsites:
Saved: 'Spremljeno, molimo osvježite povezane stranice.'
SilverStripe\Subsites\Extensions\SiteConfigSubsites:
has_one_Subsite: Podsajt
SilverStripe\Subsites\Extensions\SiteTreeSubsites:
CopyAction: Kopiraj
CopyToSubsite: 'Kopiraj stranicu u podsajt'
CopyToSubsiteWithChildren: 'Uključi podstranice?'
SubsiteOperations: 'Operacije podređenog prostora'
has_one_Subsite: Podsajt
SilverStripe\Subsites\Model\Subsite:
ConfigurationTab: Konfiguracija
CopyMessage: 'Kreirana kopija od {title}'
CustomExtraMeta: 'Prilagođeni Meta tagovi'
CustomMetaDescription: Opis
CustomMetaKeywords: 'Ključne riječi'
CustomMetaTitle: Naslov
PLURALNAME: Podsajtovi
PageTypeBlacklistField: 'Ne dopuštaj tipove stranica?'
SINGULARNAME: Podsajt
SiteConfigSubtitle: 'vaš slogan ovdje'
SiteConfigTitle: 'Naziv vašeg weba'
ValidateTitle: 'Molimo dodajte "Naslov"'
belongs_many_many_Groups: Grupe
db_DefaultSite: 'Zadani sajt'
db_Language: Jezik
db_RedirectURL: 'Link preusmjeravanja'
db_Theme: Tema
db_Title: Naslov
has_many_Domains: Domene
SilverStripe\Subsites\Model\SubsiteDomain:
DOMAIN: Domena
DOMAIN_DESCRIPTION: 'Hostname ovog podsajta (bez protokola). Omogućava wildcard (*).'
ISPRIMARY_DESCRIPTION: 'Označi kao zadanu domenu za ovu podstranicu'
IS_PRIMARY: 'Da li je glavna domena?'
PLURALNAME: 'Domene podsajtova'
PROTOCOL_AUTOMATIC: Automatsko
PROTOCOL_HTTP: 'http://'
PROTOCOL_HTTPS: 'https://'
Protocol: Protokol
SINGULARNAME: 'Domena podsajta'
db_Domain: Domena
db_Protocol: Protokol
has_one_Subsite: Podsajt
SilverStripe\Subsites\Pages\SubsitesVirtualPage:
DESCRIPTION: 'Prikazuje sadržaj stranice na drugom podsajtu'
SINGULARNAME: 'Virtualna stranica podsajta'
SubsiteField: Podsajt
SilverStripe\Subsites\Reports\SubsiteReportWrapper:
ReportDropdown: Sajtovi
ReportDropdownSubsite: Podsajt
Subsite: Subsite:
COPYSTRUCTURE: 'Kopiraj strukturu od:' COPYSTRUCTURE: 'Kopiraj strukturu od:'
NOTEMPLATE: 'Nema predloška' NOTEMPLATE: 'Nema predloška'

View File

@ -3,6 +3,38 @@ id:
SubsiteFieldLabel: Subsitus SubsiteFieldLabel: Subsitus
GroupSubsites: GroupSubsites:
SECURITYTABTITLE: Subsitus SECURITYTABTITLE: Subsitus
SilverStripe\Subsites\Admin\SubsiteAdmin:
MENUTITLE: Subsitus
SilverStripe\Subsites\Extensions\FileSubsites:
has_one_Subsite: Subsitus
SilverStripe\Subsites\Extensions\FolderFormFactoryExtension:
SubsiteFieldLabel: Subsitus
SilverStripe\Subsites\Extensions\GroupSubsites:
SECURITYTABTITLE: Subsitus
many_many_Subsites: Subsitus
SilverStripe\Subsites\Extensions\SiteConfigSubsites:
has_one_Subsite: Subsitus
SilverStripe\Subsites\Extensions\SiteTreeSubsites:
has_one_Subsite: Subsitus
SilverStripe\Subsites\Model\Subsite:
CustomExtraMeta: 'Penanda Meta'
CustomMetaDescription: Deskripsi
CustomMetaKeywords: 'Kata kunci'
CustomMetaTitle: Judul
PLURALNAME: Subsitus
SINGULARNAME: Subsitus
SiteConfigTitle: 'Nama Situs'
belongs_many_many_Groups: Kelompok
db_Language: Bahasa
db_Theme: Tema
db_Title: Judul
SilverStripe\Subsites\Model\SubsiteDomain:
has_one_Subsite: Subsitus
SilverStripe\Subsites\Pages\SubsitesVirtualPage:
SubsiteField: Subsitus
SilverStripe\Subsites\Reports\SubsiteReportWrapper:
ReportDropdown: Situs
ReportDropdownSubsite: Subsitus
Subsite: Subsite:
CustomMetaDescription: Deskripsi CustomMetaDescription: Deskripsi
CustomMetaKeywords: 'Kata kunci' CustomMetaKeywords: 'Kata kunci'
@ -15,5 +47,6 @@ id:
ReportDropdown: Situs ReportDropdown: Situs
Subsites: Subsites:
LanguageFieldLabel: Bahasa LanguageFieldLabel: Bahasa
ThemeFieldLabel: Tema
SubsitesVirtualPage: SubsitesVirtualPage:
SubsiteField: Subsitus SubsiteField: Subsitus

View File

@ -1,6 +1,76 @@
it: it:
DomainNameField: DomainNameField:
INVALID_DOMAIN: 'Nome a dominio non valido' INVALID_DOMAIN: 'Nome a dominio non valido'
SilverStripe\CMS\Model\VirtualPage:
EDITCONTENT: 'Clicca qui per editare il contenuto'
SilverStripe\Subsites\Admin\SubsiteAdmin:
MENUTITLE: Sottositi
SilverStripe\Subsites\Extensions\FileSubsites:
has_one_Subsite: Sottosito
SilverStripe\Subsites\Extensions\FolderFormFactoryExtension:
AllSitesDropdownOpt: 'Tutti i siti'
SUBSITENOTICE: 'Le cartelle e i files creati nel sito principale sono accessibili da tutti i sottositi.'
SubsiteFieldLabel: Sottosito
SilverStripe\Subsites\Extensions\GroupSubsites:
ACCESSALL: 'Tutti i sottositi'
ACCESSONLY: 'Solo questi sottositi'
ACCESSRADIOTITLE: 'Dai a questo gruppo accesso a'
GlobalGroup: 'Gruppo globale'
MANAGE_SUBSITES: 'Gestisci sottositi per gruppi'
MANAGE_SUBSITES_HELP: 'Abilità di limitare i permessi per un gruppo di uno o più sottositi.'
SECURITYTABTITLE: Sottositi
many_many_Subsites: Sottositi
SilverStripe\Subsites\Extensions\LeftAndMainSubsites:
Saved: 'Salvato, si prega di aggiornare le pagine relative.'
SilverStripe\Subsites\Extensions\SiteConfigSubsites:
has_one_Subsite: Sottosito
SilverStripe\Subsites\Extensions\SiteTreeSubsites:
CopyAction: Copia
CopyToSubsite: 'Copia pagina in sottosito'
CopyToSubsiteWithChildren: 'Includi pagine figlie?'
SubsiteOperations: 'Operazioni sui sottositi'
has_one_Subsite: Sottosito
SilverStripe\Subsites\Model\Subsite:
ConfigurationTab: Configurazione
CopyMessage: 'Creata una copia di {title}'
CustomExtraMeta: 'Meta Tags personalizzati'
CustomMetaDescription: Descrizione
CustomMetaKeywords: Keywords
CustomMetaTitle: Titolo
PLURALNAME: Sottositi
PageTypeBlacklistField: 'Disabilita tipi di pagina?'
SINGULARNAME: Sottosito
SiteConfigSubtitle: 'Lo slogan qui'
SiteConfigTitle: 'Nome del sito'
ValidateTitle: 'Prego aggiungere il "Titolo"'
belongs_many_many_Groups: Gruppi
db_DefaultSite: 'Sito di default'
db_Language: Lingua
db_RedirectURL: 'URL di reindirizzamento'
db_Theme: Tema
db_Title: Titolo
has_many_Domains: Domini
SilverStripe\Subsites\Model\SubsiteDomain:
DOMAIN: Dominio
DOMAIN_DESCRIPTION: 'Nome host di questo sottosito (escluso il protocollo). Permette caratteri wildcard (*).'
ISPRIMARY_DESCRIPTION: 'Segna questo come il dominio di default per questo sottosito'
IS_PRIMARY: 'È il dominio primario?'
PLURALNAME: 'Domini del sottosito'
PROTOCOL_AUTOMATIC: Automatico
PROTOCOL_HTTP: 'http://'
PROTOCOL_HTTPS: 'https://'
Protocol: Protocollo
SINGULARNAME: 'Dominio del sottosito'
db_Domain: Dominio
db_Protocol: Protocollo
has_one_Subsite: Sottosito
SilverStripe\Subsites\Pages\SubsitesVirtualPage:
DESCRIPTION: 'Mostra il contenuto di una pagina appartenente ad un altro sottosito'
SINGULARNAME: 'Pagina virtuale dei sottositi'
SubsiteField: Sottosito
SilverStripe\Subsites\Reports\SubsiteReportWrapper:
ReportDropdown: Siti
ReportDropdownSubsite: Sottosito
Subsite: Subsite:
COPYSTRUCTURE: 'Copia struttura da:' COPYSTRUCTURE: 'Copia struttura da:'
NOTEMPLATE: 'Nessuno schema' NOTEMPLATE: 'Nessuno schema'

View File

@ -6,11 +6,55 @@ ja:
ACCESSONLY: これらのサブサイトのみ ACCESSONLY: これらのサブサイトのみ
ACCESSRADIOTITLE: このグループに選択先へのアクセス権を与える ACCESSRADIOTITLE: このグループに選択先へのアクセス権を与える
SECURITYTABTITLE: サブサイト SECURITYTABTITLE: サブサイト
SilverStripe\CMS\Model\VirtualPage:
EDITCONTENT: ここをクリックしてコンテンツを編集
SilverStripe\Subsites\Admin\SubsiteAdmin:
MENUTITLE: サブサイト
SilverStripe\Subsites\Extensions\FileSubsites:
has_one_Subsite: サブサイト
SilverStripe\Subsites\Extensions\FolderFormFactoryExtension:
SubsiteFieldLabel: サブサイト
SilverStripe\Subsites\Extensions\GroupSubsites:
ACCESSALL: 全てのサブサイト
ACCESSONLY: これらのサブサイトのみ
ACCESSRADIOTITLE: このグループに選択先へのアクセス権を与える
SECURITYTABTITLE: サブサイト
many_many_Subsites: サブサイト
SilverStripe\Subsites\Extensions\SiteConfigSubsites:
has_one_Subsite: サブサイト
SilverStripe\Subsites\Extensions\SiteTreeSubsites:
has_one_Subsite: サブサイト
SilverStripe\Subsites\Model\Subsite:
CustomExtraMeta: メタタグをカスタム
CustomMetaDescription: 説明文
CustomMetaTitle: タイトル
PLURALNAME: サブサイト
SINGULARNAME: サブサイト
SiteConfigTitle: サイト名
belongs_many_many_Groups: グループ
db_Language: 言語
db_Theme: テーマ
db_Title: タイトル
SilverStripe\Subsites\Model\SubsiteDomain:
DOMAIN: ドメイン
PLURALNAME: サブサイトのドメイン
SINGULARNAME: サブサイトのドメイン
db_Domain: ドメイン
has_one_Subsite: サブサイト
SilverStripe\Subsites\Pages\SubsitesVirtualPage:
SINGULARNAME: サブサイトの仮想ページ
SubsiteField: サブサイト
SilverStripe\Subsites\Reports\SubsiteReportWrapper:
ReportDropdownSubsite: サブサイト
SubsiteAdmin: SubsiteAdmin:
MENUTITLE: サブサイト MENUTITLE: サブサイト
SubsiteDomain: SubsiteDomain:
DOMAIN: ドメイン DOMAIN: ドメイン
PLURALNAME: サブサイトのドメイン PLURALNAME: サブサイトのドメイン
SINGULARNAME: サブサイトのドメイン SINGULARNAME: サブサイトのドメイン
Subsites:
DomainFieldLabel: ドメイン
LanguageFieldLabel: 言語
ThemeFieldLabel: テーマ
SubsitesVirtualPage: SubsitesVirtualPage:
SINGULARNAME: サブサイトの仮想ページ SINGULARNAME: サブサイトの仮想ページ

View File

@ -1,4 +1,34 @@
lt: lt:
SilverStripe\Subsites\Extensions\FolderFormFactoryExtension:
AllSitesDropdownOpt: 'Visos svetainės'
SilverStripe\Subsites\Extensions\LeftAndMainSubsites:
Saved: 'Išsaugota, prašome atnaujinti susijusius puslapius'
SilverStripe\Subsites\Extensions\SiteTreeSubsites:
CopyAction: Kopijuoti
CopyToSubsite: 'Kopijuoti puslapį į kitą svetainę'
SilverStripe\Subsites\Model\Subsite:
ConfigurationTab: Nustatymai
CopyMessage: 'Sukurta {title} kopija'
CustomExtraMeta: 'Kitos meta žymės'
CustomMetaDescription: Aprašymas
CustomMetaKeywords: Raktažodžiai
CustomMetaTitle: Pavadinimas
PageTypeBlacklistField: 'Neleidžiami puslapių tipai'
SiteConfigSubtitle: 'Jūsų svetainės šūkis'
SiteConfigTitle: 'Jūsų svetainės pavadinimas'
ValidateTitle: 'Prašome įvesti "Pavadinimą"'
belongs_many_many_Groups: Grupės
db_DefaultSite: 'Pagrindinė svetainė'
db_Language: Kalba
db_RedirectURL: 'Nukreipimo nuoroda'
db_Theme: Tema
db_Title: Pavadinimas
has_many_Domains: Domenai
SilverStripe\Subsites\Model\SubsiteDomain:
DOMAIN: Domenas
db_Domain: Domenas
SilverStripe\Subsites\Reports\SubsiteReportWrapper:
ReportDropdown: Svetainės
Subsite: Subsite:
COPYSTRUCTURE: 'Kopijuoti struktūrą iš:' COPYSTRUCTURE: 'Kopijuoti struktūrą iš:'
NOTEMPLATE: 'Nėra šablono' NOTEMPLATE: 'Nėra šablono'

View File

@ -1,4 +1,65 @@
mi: mi:
SilverStripe\CMS\Model\VirtualPage:
EDITCONTENT: 'Pāwhiri ki konei hei whakatika i ngā ihirangi'
SilverStripe\Subsites\Admin\SubsiteAdmin:
MENUTITLE: 'Ngā pae iti'
SilverStripe\Subsites\Extensions\FileSubsites:
has_one_Subsite: 'Pae iti'
SilverStripe\Subsites\Extensions\FolderFormFactoryExtension:
AllSitesDropdownOpt: 'Ngā pae katoa'
SUBSITENOTICE: 'Ka taea ngā kōpaki me ngā kōnae kua hangaia i te pae matua te uru mā ngā pae iti katoa.'
SubsiteFieldLabel: 'Pae iti'
SilverStripe\Subsites\Extensions\GroupSubsites:
ACCESSALL: 'Ngā pae iti katoa'
ACCESSONLY: 'Ko ēnei pae iti anake'
ACCESSRADIOTITLE: 'Tukuna tēnei rōpū kia uru ki'
GlobalGroup: 'Rōpū Hurinoa'
MANAGE_SUBSITES: 'Whakahaere pae iti mō ngā rōpū'
MANAGE_SUBSITES_HELP: 'Te āheinga ki te whakawhāiti whakaaetanga mō tētahi rōpū ki tētahi neke atu rānei o ngā pae iti.'
SECURITYTABTITLE: 'Ngā pae iti'
many_many_Subsites: 'Ngā pae iti'
SilverStripe\Subsites\Extensions\LeftAndMainSubsites:
Saved: 'Kua tiakina, whakahoutia ngā whārangi pāhono.'
SilverStripe\Subsites\Extensions\SiteConfigSubsites:
has_one_Subsite: 'Pae iti'
SilverStripe\Subsites\Extensions\SiteTreeSubsites:
CopyAction: Tārua
CopyToSubsite: 'Tāruatia te whārangi ki te pae iti'
has_one_Subsite: 'Pae iti'
SilverStripe\Subsites\Model\Subsite:
ConfigurationTab: Whirihoranga
CopyMessage: 'I hangaia he tārua o {title}'
CustomExtraMeta: 'Ngā Tūtohu Meta Ritenga'
CustomMetaDescription: Whakaahuatanga
CustomMetaKeywords: 'Ngā kupumatua'
CustomMetaTitle: Taitara
PLURALNAME: 'Ngā pae iti'
PageTypeBlacklistField: 'Me whakakāhore ngā momo whārangi?'
SINGULARNAME: 'Pae iti'
SiteConfigSubtitle: 'Tō rārangi tautuhinga ki konei'
SiteConfigTitle: 'Tō Ingoa Pae'
ValidateTitle: 'Tāurua he "Taitara"'
belongs_many_many_Groups: 'Ngā Rōpū'
db_DefaultSite: 'Pae taunoa'
db_Language: Reo
db_RedirectURL: 'Tukua anō te URL'
db_Theme: Kaupapa
db_Title: Taitara
has_many_Domains: 'Ngā Rohe'
SilverStripe\Subsites\Model\SubsiteDomain:
DOMAIN: Rohe
PLURALNAME: 'Ngā Rohe Pae Iti'
SINGULARNAME: 'Rohe Pae Iti'
db_Domain: Rohe
has_one_Subsite: 'Pae iti'
SilverStripe\Subsites\Pages\SubsitesVirtualPage:
DESCRIPTION: 'Ka whakaatu i ngā ihirangi o tētahi whārangi ki tētahi atu pae iti'
PLURALNAME: 'Ngā Whārangi Taketake'
SINGULARNAME: 'Whārangi Mariko Pae Iti'
SubsiteField: 'Pae iti'
SilverStripe\Subsites\Reports\SubsiteReportWrapper:
ReportDropdown: 'Ngā Pae'
ReportDropdownSubsite: 'Pae iti'
Subsite: Subsite:
COPYSTRUCTURE: 'Tāurutia te hanganga mai i:' COPYSTRUCTURE: 'Tāurutia te hanganga mai i:'
NOTEMPLATE: 'Kāore he tātauira' NOTEMPLATE: 'Kāore he tātauira'

View File

@ -6,11 +6,46 @@ nb_NO:
ACCESSONLY: 'Only these subsites' ACCESSONLY: 'Only these subsites'
ACCESSRADIOTITLE: 'Give this group access to' ACCESSRADIOTITLE: 'Give this group access to'
SECURITYTABTITLE: subdomener SECURITYTABTITLE: subdomener
SilverStripe\CMS\Model\VirtualPage:
EDITCONTENT: 'klikk her for å endre dette innholdet'
SilverStripe\Subsites\Admin\SubsiteAdmin:
MENUTITLE: subdomener
SilverStripe\Subsites\Extensions\FileSubsites:
has_one_Subsite: Subdomene
SilverStripe\Subsites\Extensions\FolderFormFactoryExtension:
SubsiteFieldLabel: Subdomene
SilverStripe\Subsites\Extensions\GroupSubsites:
ACCESSALL: 'All subsites'
ACCESSONLY: 'Only these subsites'
ACCESSRADIOTITLE: 'Give this group access to'
SECURITYTABTITLE: subdomener
many_many_Subsites: subdomener
SilverStripe\Subsites\Extensions\SiteConfigSubsites:
has_one_Subsite: Subdomene
SilverStripe\Subsites\Extensions\SiteTreeSubsites:
has_one_Subsite: Subdomene
SilverStripe\Subsites\Model\Subsite:
ConfigurationTab: Konfigurasjon
PLURALNAME: subdomener
SINGULARNAME: Subdomene
SilverStripe\Subsites\Model\SubsiteDomain:
DOMAIN: Domain
PLURALNAME: 'Subsite Domains'
SINGULARNAME: 'Subsite Domain'
db_Domain: Domain
has_one_Subsite: Subdomene
SilverStripe\Subsites\Pages\SubsitesVirtualPage:
SINGULARNAME: 'Subdomeners Virtuelle Side'
SubsiteField: Subdomene
SilverStripe\Subsites\Reports\SubsiteReportWrapper:
ReportDropdownSubsite: Subdomene
SubsiteAdmin: SubsiteAdmin:
MENUTITLE: Underdomener MENUTITLE: Underdomener
SubsiteDomain: SubsiteDomain:
DOMAIN: Domain DOMAIN: Domain
PLURALNAME: 'Subsite Domains' PLURALNAME: 'Subsite Domains'
SINGULARNAME: 'Subsite Domain' SINGULARNAME: 'Subsite Domain'
Subsites:
DomainFieldLabel: Domain
SubsitesVirtualPage: SubsitesVirtualPage:
SINGULARNAME: 'Subdomeners Virtuelle Side' SINGULARNAME: 'Subdomeners Virtuelle Side'

View File

@ -1,32 +1,103 @@
nl: nl:
FileSubsites: DomainNameField:
INVALID_DOMAIN: 'Ongeldige domein naam'
SilverStripe\CMS\Model\VirtualPage:
EDITCONTENT: 'Klik hier om inhoud aan te passen'
SilverStripe\Subsites\Admin\SubsiteAdmin:
MENUTITLE: Subsites
SilverStripe\Subsites\Controller\SubsiteXHRController:
MENUTITLE: Subsites
SilverStripe\Subsites\Extensions\FileSubsites:
has_one_Subsite: Subsite
SilverStripe\Subsites\Extensions\FolderFormFactoryExtension:
AllSitesDropdownOpt: 'Alle sites'
SUBSITENOTICE: 'Mappen en bestanden van de Hoofdsite zijn toegankelijk voor alle subsites.'
SubsiteFieldLabel: Subsite SubsiteFieldLabel: Subsite
GridFieldAddFromTemplateButton: SilverStripe\Subsites\Extensions\GroupSubsites:
AddFromTemplate: 'Nieuwe toevegen vanaf template'
GroupSubsites:
ACCESSALL: 'Alle subsites' ACCESSALL: 'Alle subsites'
ACCESSONLY: 'Alleen deze subsites' ACCESSONLY: 'Alleen deze subsites'
ACCESSRADIOTITLE: 'Geef deze groep rechten aan' ACCESSRADIOTITLE: 'Geef deze groep rechten aan'
GlobalGroup: 'globale groep'
MANAGE_SUBSITES: 'Beheer subsites voor groepen'
MANAGE_SUBSITES_HELP: 'Bepaal de toegangsrechten voor groepen per subsite'
SECURITYTABTITLE: Subsites SECURITYTABTITLE: Subsites
Subsite: db_AccessAllSubsites: 'Toegang tot alle subsites'
many_many_Subsites: Subsites
SilverStripe\Subsites\Extensions\LeftAndMainSubsites:
SITECONTENTLEFT: 'Site inhoud'
Saved: 'Opgeslagen, pas onderliggende pagina''s aan.'
SilverStripe\Subsites\Extensions\SiteConfigSubsites:
has_one_Subsite: Subsite
SilverStripe\Subsites\Extensions\SiteTreeSubsites:
CopyAction: Kopieer
CopyToSubsite: 'Kopieer pagina''s naar subsite'
CopyToSubsiteWithChildren: 'Inclusief onderliggende pagina''s?'
SubsiteOperations: 'Subsite Acties'
has_one_Subsite: Subsite
SilverStripe\Subsites\Model\Subsite:
ConfigurationTab: Configuratie
CopyMessage: 'Kopie gemaakt van {title}'
CustomExtraMeta: 'Andere meta tags'
CustomMetaDescription: Omschrijving CustomMetaDescription: Omschrijving
CustomMetaKeywords: Sleutelwoorden
CustomMetaTitle: Titel CustomMetaTitle: Titel
PLURALNAME: Subsites PLURALNAME: Subsites
PLURALS:
one: 'Een Subsite'
other: '{count} Subsites'
PageTypeBlacklistField: 'Niet toegestane paginatypes?'
SINGULARNAME: Subsite SINGULARNAME: Subsite
SiteConfigSubtitle: 'Jouw slagzin hier'
SiteConfigTitle: 'Jouw Site Naam' SiteConfigTitle: 'Jouw Site Naam'
TabTitleConfig: Configuratie ThemeFieldEmptyString: '-'
SubsiteAdmin: ValidateTitle: 'Voeg een "Titel" toe'
MENUTITLE: Subsites belongs_many_many_Groups: Groepen
SubsiteDomain: db_DefaultSite: 'Standaard Site'
db_Language: Taal
db_RedirectURL: 'Redirect URL'
db_Theme: Thema
db_Title: Titel
SilverStripe\Subsites\Model\SubsiteDomain:
DOMAIN: Domein DOMAIN: Domein
DOMAIN_DESCRIPTION: 'Domeinnaam van deze subsite (zonder http), wildcards zijn toegestaan (*).'
ISPRIMARY_DESCRIPTION: 'Dit is de standaard domeinnaam voor deze subsite'
IS_PRIMARY: 'Is Primaire domein'
PLURALNAME: 'Subsite Domeinen' PLURALNAME: 'Subsite Domeinen'
PLURALS:
one: 'Een subsite domein'
other: '{count} subsite domeinen'
PROTOCOL_AUTOMATIC: Automatisch
PROTOCOL_DESCRIPTION: 'Wordt gebruikt bij het genereren van links naar deze subsite. <br />''Automatisch'' houdt in dat het huidige protocol gebruikt zal worden.'
PROTOCOL_HTTP: 'http://'
PROTOCOL_HTTPS: 'https://'
Protocol: Protocol
SINGULARNAME: 'Subsite Domein' SINGULARNAME: 'Subsite Domein'
Subsites: db_Domain: Domein
DefaultSiteFieldLabel: 'Standaard Site' db_Protocol: Protocol
LanguageFieldLabel: Taal has_one_Subsite: Subsite
PrimaryDomainFieldLabel: 'Primaire domein' SilverStripe\Subsites\Pages\SubsitesVirtualPage:
ThemeFieldLabel: Thema DESCRIPTION: 'Toon de inhoud van een pagina op een andere subsite'
TitleFieldLabel: 'Subsite naam' OverrideNote: 'Overschrijft de overgenomen tekst van de gelinkte pagina'
SubsitesVirtualPage: PLURALNAME: 'Subsites Virtuele paginas'
PLURALS:
one: 'Subsites Virtuele pagina'
other: '{count} Subsites Virtuele paginas'
SINGULARNAME: 'Subsites Virtuele pagina' SINGULARNAME: 'Subsites Virtuele pagina'
SubsiteField: Subsite SubsiteField: Subsite
SilverStripe\Subsites\Reports\SubsiteReportWrapper:
ReportDropdown: Sites
ReportDropdownSubsite: Subsite
Subsite:
COPYSTRUCTURE: 'Kopieer structuur vanaf:'
NOTEMPLATE: 'Geen template'
Subsites:
DefaultSiteFieldLabel: 'Standaard Site'
DomainFieldLabel: Domein
IsPublicFieldLabel: 'Publiek toegankelijk'
LanguageFieldLabel: Taal
MainSiteTitle: 'Hoofd site'
PageTypeBlacklistFieldLabel: 'Paginatypes blacklist'
PrimaryDomainFieldLabel: 'Primaire domein'
RedirectURLFieldLabel: 'Redirect URL'
ThemeFieldLabel: Thema
TitleFieldLabel: 'Subsite naam'

13
lang/pl.yml Normal file
View File

@ -0,0 +1,13 @@
pl:
SilverStripe\Subsites\Model\Subsite:
CustomExtraMeta: 'Własne meta tagi'
CustomMetaDescription: Opis
CustomMetaTitle: Tytuł
SiteConfigTitle: 'Nazwa twojego serwisu'
belongs_many_many_Groups: Grupy
db_Language: Język
db_Theme: Szablon
db_Title: Tytuł
Subsites:
LanguageFieldLabel: Język
ThemeFieldLabel: Szablon

View File

@ -6,11 +6,51 @@ pl_PL:
ACCESSONLY: 'Tylko te podwitryny' ACCESSONLY: 'Tylko te podwitryny'
ACCESSRADIOTITLE: 'Daj tej grupie dostęp do' ACCESSRADIOTITLE: 'Daj tej grupie dostęp do'
SECURITYTABTITLE: Podwitryny SECURITYTABTITLE: Podwitryny
SilverStripe\CMS\Model\VirtualPage:
EDITCONTENT: 'Kliknij tutaj aby edytować zawartość'
SilverStripe\Subsites\Admin\SubsiteAdmin:
MENUTITLE: Podwitryny
SilverStripe\Subsites\Extensions\FileSubsites:
has_one_Subsite: Podwitryna
SilverStripe\Subsites\Extensions\FolderFormFactoryExtension:
SubsiteFieldLabel: Podwitryna
SilverStripe\Subsites\Extensions\GroupSubsites:
ACCESSALL: 'Wszystkie podwitryny'
ACCESSONLY: 'Tylko te podwitryny'
ACCESSRADIOTITLE: 'Daj tej grupie dostęp do'
SECURITYTABTITLE: Podwitryny
many_many_Subsites: Podwitryny
SilverStripe\Subsites\Extensions\SiteConfigSubsites:
has_one_Subsite: Podwitryna
SilverStripe\Subsites\Extensions\SiteTreeSubsites:
has_one_Subsite: Podwitryna
SilverStripe\Subsites\Model\Subsite:
ConfigurationTab: Konfiguracja
CustomExtraMeta: 'Własne meta tagi'
CustomMetaTitle: Tytuł
PLURALNAME: Podwitryny
SINGULARNAME: Podwitryna
db_Language: Język
db_Title: Tytuł
SilverStripe\Subsites\Model\SubsiteDomain:
DOMAIN: Domena
PLURALNAME: 'Domeny podwitryny'
SINGULARNAME: 'Domena podwitryny'
db_Domain: Domena
has_one_Subsite: Podwitryna
SilverStripe\Subsites\Pages\SubsitesVirtualPage:
SINGULARNAME: 'Wirtualna strona dla podwitryn'
SubsiteField: Podwitryna
SilverStripe\Subsites\Reports\SubsiteReportWrapper:
ReportDropdownSubsite: Podwitryna
SubsiteAdmin: SubsiteAdmin:
MENUTITLE: Podwitryny MENUTITLE: Podwitryny
SubsiteDomain: SubsiteDomain:
DOMAIN: Domena DOMAIN: Domena
PLURALNAME: 'Domeny podwitryny' PLURALNAME: 'Domeny podwitryny'
SINGULARNAME: 'Domena podwitryny' SINGULARNAME: 'Domena podwitryny'
Subsites:
DomainFieldLabel: Domena
LanguageFieldLabel: Język
SubsitesVirtualPage: SubsitesVirtualPage:
SINGULARNAME: 'Wirtualna strona dla podwitryn' SINGULARNAME: 'Wirtualna strona dla podwitryn'

View File

@ -1,6 +1,76 @@
ru: ru:
DomainNameField: DomainNameField:
INVALID_DOMAIN: 'Неверный домен' INVALID_DOMAIN: 'Неверный домен'
SilverStripe\CMS\Model\VirtualPage:
EDITCONTENT: 'Нажмите для изменения содержимого'
SilverStripe\Subsites\Admin\SubsiteAdmin:
MENUTITLE: Подсайты
SilverStripe\Subsites\Extensions\FileSubsites:
has_one_Subsite: Подсайт
SilverStripe\Subsites\Extensions\FolderFormFactoryExtension:
AllSitesDropdownOpt: 'Все сайты'
SUBSITENOTICE: 'Папки и файлы созданные на основном сайте так же доступны для под-сайтов.'
SubsiteFieldLabel: Подсайт
SilverStripe\Subsites\Extensions\GroupSubsites:
ACCESSALL: 'Все подсайты'
ACCESSONLY: 'Только эти подсайты'
ACCESSRADIOTITLE: 'Разрешить этой группе доступ к'
GlobalGroup: 'глобальная группа'
MANAGE_SUBSITES: 'Управление подсайтами для групп'
MANAGE_SUBSITES_HELP: 'Возможность ограничить права доступа для группы к одному и более подсайтам'
SECURITYTABTITLE: Подсайты
many_many_Subsites: Подсайты
SilverStripe\Subsites\Extensions\LeftAndMainSubsites:
Saved: 'Сохранено, пожалуйста обновите связанные группы'
SilverStripe\Subsites\Extensions\SiteConfigSubsites:
has_one_Subsite: Подсайт
SilverStripe\Subsites\Extensions\SiteTreeSubsites:
CopyAction: Копировать
CopyToSubsite: 'Копировать страницу на подсайт'
CopyToSubsiteWithChildren: 'Включая под-страницы?'
SubsiteOperations: 'Операции над подсайтами'
has_one_Subsite: Подсайт
SilverStripe\Subsites\Model\Subsite:
ConfigurationTab: Конфигурация
CopyMessage: 'Создана копия {title}'
CustomExtraMeta: 'Пользовательские мета-тэги'
CustomMetaDescription: Описание
CustomMetaKeywords: 'Ключевые слова'
CustomMetaTitle: Заголовок
PLURALNAME: Подсайты
PageTypeBlacklistField: 'Запретить данные типы страниц?'
SINGULARNAME: Подсайт
SiteConfigSubtitle: 'ваш слоган здесь'
SiteConfigTitle: 'Название сайта'
ValidateTitle: 'Пожалуйста, добавьте "Заголовок"'
belongs_many_many_Groups: Группы
db_DefaultSite: 'Основной сайт'
db_Language: Язык
db_RedirectURL: 'Ссылка для перенаправления'
db_Theme: Оформление
db_Title: Заголовок
has_many_Domains: Домены
SilverStripe\Subsites\Model\SubsiteDomain:
DOMAIN: Домен
DOMAIN_DESCRIPTION: 'Домен подсайта (без указания протоколов: http:// и https://). Разрешены (*) для обозначения поддоменов.'
ISPRIMARY_DESCRIPTION: 'Отметить как основной домен для данного подсайта'
IS_PRIMARY: 'Это основной домен?'
PLURALNAME: 'Домены подсайтов'
PROTOCOL_AUTOMATIC: Автоматически
PROTOCOL_HTTP: 'http://'
PROTOCOL_HTTPS: 'https://'
Protocol: Протокол
SINGULARNAME: 'Домен подсайта'
db_Domain: Домен
db_Protocol: Протокол
has_one_Subsite: Подсайт
SilverStripe\Subsites\Pages\SubsitesVirtualPage:
DESCRIPTION: 'Отображает содержимое выбранной страницы на другом подсайте'
SINGULARNAME: 'Виртуальная страница подсайта'
SubsiteField: Подсайт
SilverStripe\Subsites\Reports\SubsiteReportWrapper:
ReportDropdown: Сайты
ReportDropdownSubsite: Подсайт
Subsite: Subsite:
COPYSTRUCTURE: 'Скопировать структуры из:' COPYSTRUCTURE: 'Скопировать структуры из:'
NOTEMPLATE: 'Нет шаблона' NOTEMPLATE: 'Нет шаблона'

37
lang/sk.yml Normal file
View File

@ -0,0 +1,37 @@
sk:
SilverStripe\Subsites\Admin\SubsiteAdmin:
MENUTITLE: Podstránky
SilverStripe\Subsites\Extensions\FileSubsites:
has_one_Subsite: Podstránka
SilverStripe\Subsites\Extensions\FolderFormFactoryExtension:
SubsiteFieldLabel: Podstránka
SilverStripe\Subsites\Extensions\GroupSubsites:
SECURITYTABTITLE: Podstránky
db_AccessAllSubsites: 'Prístup na všetky podstránky'
many_many_Subsites: Podstránky
SilverStripe\Subsites\Extensions\SiteConfigSubsites:
has_one_Subsite: Podstránka
SilverStripe\Subsites\Extensions\SiteTreeSubsites:
has_one_Subsite: Podstránka
many_many_CrossSubsiteLinkTracking: 'Sledovanie odkazov naprieč podstránkami'
SilverStripe\Subsites\Model\Subsite:
ConfigurationTab: Konfigurácia
CustomExtraMeta: 'Vlastné Meta Tagy'
CustomMetaDescription: Popis
CustomMetaTitle: Názov
PLURALNAME: Podstránky
SINGULARNAME: Podstránka
SiteConfigTitle: 'Názov vášho webu'
belongs_many_many_Groups: Skupiny
db_Language: Jazyk
db_Theme: Téma
db_Title: Názov
SilverStripe\Subsites\Model\SubsiteDomain:
has_one_Subsite: Podstránka
SilverStripe\Subsites\Pages\SubsitesVirtualPage:
SubsiteField: Podstránka
SilverStripe\Subsites\Reports\SubsiteReportWrapper:
ReportDropdownSubsite: Podstránka
Subsites:
LanguageFieldLabel: Jazyk
ThemeFieldLabel: Téma

35
lang/sl.yml Normal file
View File

@ -0,0 +1,35 @@
sl:
SilverStripe\Subsites\Admin\SubsiteAdmin:
MENUTITLE: Podspletišča
SilverStripe\Subsites\Extensions\FileSubsites:
has_one_Subsite: Podspletišče
SilverStripe\Subsites\Extensions\FolderFormFactoryExtension:
SubsiteFieldLabel: Podspletišče
SilverStripe\Subsites\Extensions\GroupSubsites:
SECURITYTABTITLE: Podspletišča
db_AccessAllSubsites: 'Dostop do vseh podspletišč'
many_many_Subsites: Podspletišča
SilverStripe\Subsites\Extensions\SiteConfigSubsites:
has_one_Subsite: Podspletišče
SilverStripe\Subsites\Extensions\SiteTreeSubsites:
has_one_Subsite: Podspletišče
many_many_CrossSubsiteLinkTracking: 'Spremljanje povezav med podspletišči'
SilverStripe\Subsites\Model\Subsite:
ConfigurationTab: Nastavitve
CustomExtraMeta: 'Meta tagi po meri'
CustomMetaDescription: Opis
CustomMetaTitle: Naziv
PLURALNAME: Podspletišča
SINGULARNAME: Podspletišče
SiteConfigTitle: 'Naziv vašega spletnega mesta'
belongs_many_many_Groups: Skupine
db_Theme: Tema
db_Title: Naziv
SilverStripe\Subsites\Model\SubsiteDomain:
has_one_Subsite: Podspletišče
SilverStripe\Subsites\Pages\SubsitesVirtualPage:
SubsiteField: Podspletišče
SilverStripe\Subsites\Reports\SubsiteReportWrapper:
ReportDropdownSubsite: Podspletišče
Subsites:
ThemeFieldLabel: Tema

13
lang/sr_RS@latin.yml Normal file
View File

@ -0,0 +1,13 @@
sr_RS@latin:
SilverStripe\Subsites\Model\Subsite:
CustomExtraMeta: 'Prilagođene meta oznake'
CustomMetaDescription: Opis
CustomMetaTitle: Naslov
SiteConfigTitle: 'Naziv sajta'
belongs_many_many_Groups: Grupe
db_Language: Jezik
db_Theme: Tema
db_Title: Naslov
Subsites:
LanguageFieldLabel: Jezik
ThemeFieldLabel: Tema

14
lang/sv.yml Normal file
View File

@ -0,0 +1,14 @@
sv:
SilverStripe\Subsites\Model\Subsite:
ConfigurationTab: Konfiguration
CustomExtraMeta: 'Egna metataggar'
CustomMetaDescription: Beskrivning
CustomMetaTitle: Titel
SiteConfigTitle: 'Din Sajts Namn'
belongs_many_many_Groups: Grupper
db_Language: Språk
db_Theme: Tema
db_Title: Titel
Subsites:
LanguageFieldLabel: Språk
ThemeFieldLabel: Tema

13
lang/tr.yml Normal file
View File

@ -0,0 +1,13 @@
tr:
SilverStripe\Subsites\Model\Subsite:
CustomExtraMeta: 'Kişisel Meta Etiketleri'
CustomMetaDescription: ıklama
CustomMetaTitle: Başlık
SiteConfigTitle: 'Site Adınız'
belongs_many_many_Groups: Gruplar
db_Language: Dil
db_Theme: Tema
db_Title: Başlık
Subsites:
LanguageFieldLabel: Dil
ThemeFieldLabel: Tema

View File

@ -6,11 +6,45 @@ tr_TR:
ACCESSONLY: 'Only these subsites' ACCESSONLY: 'Only these subsites'
ACCESSRADIOTITLE: 'Give this group access to' ACCESSRADIOTITLE: 'Give this group access to'
SECURITYTABTITLE: 'Alt Siteler' SECURITYTABTITLE: 'Alt Siteler'
SilverStripe\CMS\Model\VirtualPage:
EDITCONTENT: 'İçeriği düzenlemek için tıklayınız'
SilverStripe\Subsites\Admin\SubsiteAdmin:
MENUTITLE: 'Alt Siteler'
SilverStripe\Subsites\Extensions\FileSubsites:
has_one_Subsite: 'Alt Site'
SilverStripe\Subsites\Extensions\FolderFormFactoryExtension:
SubsiteFieldLabel: 'Alt Site'
SilverStripe\Subsites\Extensions\GroupSubsites:
ACCESSALL: 'All subsites'
ACCESSONLY: 'Only these subsites'
ACCESSRADIOTITLE: 'Give this group access to'
SECURITYTABTITLE: 'Alt Siteler'
many_many_Subsites: 'Alt Siteler'
SilverStripe\Subsites\Extensions\SiteConfigSubsites:
has_one_Subsite: 'Alt Site'
SilverStripe\Subsites\Extensions\SiteTreeSubsites:
has_one_Subsite: 'Alt Site'
SilverStripe\Subsites\Model\Subsite:
PLURALNAME: 'Alt Siteler'
SINGULARNAME: 'Alt Site'
SilverStripe\Subsites\Model\SubsiteDomain:
DOMAIN: Domain
PLURALNAME: 'Subsite Domains'
SINGULARNAME: 'Subsite Domain'
db_Domain: Domain
has_one_Subsite: 'Alt Site'
SilverStripe\Subsites\Pages\SubsitesVirtualPage:
SINGULARNAME: 'Alt Site Sanal Sayfa'
SubsiteField: 'Alt Site'
SilverStripe\Subsites\Reports\SubsiteReportWrapper:
ReportDropdownSubsite: 'Alt Site'
SubsiteAdmin: SubsiteAdmin:
MENUTITLE: 'Alt Siteler' MENUTITLE: 'Alt Siteler'
SubsiteDomain: SubsiteDomain:
DOMAIN: Domain DOMAIN: Domain
PLURALNAME: 'Subsite Domains' PLURALNAME: 'Subsite Domains'
SINGULARNAME: 'Subsite Domain' SINGULARNAME: 'Subsite Domain'
Subsites:
DomainFieldLabel: Domain
SubsitesVirtualPage: SubsitesVirtualPage:
SINGULARNAME: 'Alt Site Sanal Sayfa' SINGULARNAME: 'Alt Site Sanal Sayfa'

View File

@ -1,15 +1,76 @@
zh: zh:
SilverStripe\CMS\Model\VirtualPage:
EDITCONTENT: 点击这里来编辑内容
SilverStripe\Subsites\Admin\SubsiteAdmin:
MENUTITLE: 多个子网站
SilverStripe\Subsites\Extensions\FileSubsites:
has_one_Subsite: 子网站
SilverStripe\Subsites\Extensions\FolderFormFactoryExtension:
AllSitesDropdownOpt: 所有网站
SUBSITENOTICE: 主网站上创建的文件夹和文件可以被所有子网站访问。
SubsiteFieldLabel: 子网站
SilverStripe\Subsites\Extensions\GroupSubsites:
ACCESSALL: 所有子网站
ACCESSONLY: 仅这些子网站
ACCESSRADIOTITLE: 准许该群进入
GlobalGroup: 全局小组
MANAGE_SUBSITES: 管理小组的子网站
MANAGE_SUBSITES_HELP: 能够将权限限制在一个小组、一个或多个子网站。
SECURITYTABTITLE: 多个子网站
many_many_Subsites: 多个子网站
SilverStripe\Subsites\Extensions\LeftAndMainSubsites:
Saved: 已保存,请更新相关的页面。
SilverStripe\Subsites\Extensions\SiteConfigSubsites:
has_one_Subsite: 子网站
SilverStripe\Subsites\Extensions\SiteTreeSubsites:
CopyAction: 复制
CopyToSubsite: 将页面复制到子网站
has_one_Subsite: 子网站
SilverStripe\Subsites\Model\Subsite:
ConfigurationTab: 配置
CopyMessage: '已创建一个 {title} 的副本'
CustomExtraMeta: 自定义Meta标签
CustomMetaDescription: 描述
CustomMetaKeywords: 关键词
CustomMetaTitle: 题目
PLURALNAME: 多个子网站
PageTypeBlacklistField: 禁止页面类型?
SINGULARNAME: 子网站
SiteConfigSubtitle: 您的标语在这里
SiteConfigTitle: 您的网站名称
ValidateTitle: 请添加一个“标题”
belongs_many_many_Groups: 群组
db_DefaultSite: 默认网站
db_Language: 语言
db_RedirectURL: '重定向 URL'
db_Theme: 主题
db_Title: 题目
has_many_Domains: 域名
SilverStripe\Subsites\Model\SubsiteDomain:
DOMAIN: 域名
PLURALNAME: 多个子网站域名
SINGULARNAME: 子网站域名
db_Domain: 域名
has_one_Subsite: 子网站
SilverStripe\Subsites\Pages\SubsitesVirtualPage:
DESCRIPTION: 显示另一个子网站上一个页面的内容
PLURALNAME: 基本页面
SINGULARNAME: 子网站虚拟页面
SubsiteField: 子网站
SilverStripe\Subsites\Reports\SubsiteReportWrapper:
ReportDropdown: 网站
ReportDropdownSubsite: 子网站
Subsite: Subsite:
COPYSTRUCTURE: '复制结构来自:' COPYSTRUCTURE: 复制结构来自:
NOTEMPLATE: '没有模板' NOTEMPLATE: 没有模板
Subsites: Subsites:
DefaultSiteFieldLabel: '默认网站' DefaultSiteFieldLabel: 默认网站
DomainFieldLabel: 域名 DomainFieldLabel: 域名
IsPublicFieldLabel: '启用公共访问权' IsPublicFieldLabel: 启用公共访问权
LanguageFieldLabel: 语言 LanguageFieldLabel: 语言
MainSiteTitle: '主网站' MainSiteTitle: 主网站
PageTypeBlacklistFieldLabel: '页面类型黑名单' PageTypeBlacklistFieldLabel: 页面类型黑名单
PrimaryDomainFieldLabel: '主域名' PrimaryDomainFieldLabel: 主域名
RedirectURLFieldLabel: '重定向 URL' RedirectURLFieldLabel: '重定向 URL'
ThemeFieldLabel: 主题 ThemeFieldLabel: 主题
TitleFieldLabel: '子网站名称' TitleFieldLabel: 子网站名称

View File

@ -2,6 +2,9 @@
<ruleset name="SilverStripe"> <ruleset name="SilverStripe">
<description>CodeSniffer ruleset for SilverStripe coding conventions.</description> <description>CodeSniffer ruleset for SilverStripe coding conventions.</description>
<file>src</file>
<file>tests</file>
<!-- base rules are PSR-2 --> <!-- base rules are PSR-2 -->
<rule ref="PSR2" > <rule ref="PSR2" >
<!-- Current exclusions --> <!-- Current exclusions -->

View File

@ -1,7 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit bootstrap="vendor/silverstripe/cms/tests/bootstrap.php" colors="true"> <phpunit bootstrap="vendor/silverstripe/cms/tests/bootstrap.php" colors="true">
<testsuites>
<testsuite name="Default"> <testsuite name="Default">
<directory>tests/php</directory> <directory>tests/php</directory>
</testsuite> </testsuite>
</testsuites>
<filter> <filter>
<whitelist addUncoveredFilesFromWhitelist="true"> <whitelist addUncoveredFilesFromWhitelist="true">

View File

@ -0,0 +1,33 @@
<?php
namespace SilverStripe\Subsites\Extensions;
use SilverStripe\Control\HTTP;
use SilverStripe\ORM\DataExtension;
/**
* Extension for the BaseElement object to add subsites support for CMS previews
*/
class BaseElementSubsites extends DataExtension
{
/**
* Set SubsiteID to avoid errors when a page doesn't exist on the CMS domain.
*
* @param string &$link
* @param string|null $action
* @return string
*/
public function updatePreviewLink(&$link)
{
// Get subsite ID from the element or from its page. Defaults to 0 automatically.
$subsiteID = $this->owner->SubsiteID;
if (is_null($subsiteID)) {
$page = $this->owner->getPage();
if ($page) {
$subsiteID = $page->SubsiteID;
}
}
$link = HTTP::setGetVar('SubsiteID', intval($subsiteID), $link);
}
}

View File

@ -52,6 +52,9 @@ class FileSubsites extends DataExtension
if (Subsite::$disable_subsite_filter) { if (Subsite::$disable_subsite_filter) {
return; return;
} }
if ($dataQuery && $dataQuery->getQueryParam('Subsite.filter') === false) {
return;
}
// If you're querying by ID, ignore the sub-site - this is a bit ugly... (but it was WAYYYYYYYYY worse) // If you're querying by ID, ignore the sub-site - this is a bit ugly... (but it was WAYYYYYYYYY worse)
// @TODO I don't think excluding if SiteTree_ImageTracking is a good idea however because of the SS 3.0 api and // @TODO I don't think excluding if SiteTree_ImageTracking is a good idea however because of the SS 3.0 api and
@ -74,8 +77,8 @@ class FileSubsites extends DataExtension
break; break;
} }
$sect = array_values($query->getSelect()); $sect = array_values($query->getSelect() ?? []);
$isCounting = strpos($sect[0], 'COUNT') !== false; $isCounting = strpos($sect[0] ?? '', 'COUNT') !== false;
// Ordering when deleting or counting doesn't apply // Ordering when deleting or counting doesn't apply
if (!$isCounting) { if (!$isCounting) {
@ -113,9 +116,9 @@ class FileSubsites extends DataExtension
} }
// Check the CMS_ACCESS_SecurityAdmin privileges on the subsite that owns this group // Check the CMS_ACCESS_SecurityAdmin privileges on the subsite that owns this group
$subsiteID = SubsiteState::singleton()->getSubsiteId(); $currentSubsiteID = SubsiteState::singleton()->getSubsiteId();
if ($subsiteID && $subsiteID == $this->owner->SubsiteID) { if ($currentSubsiteID && $currentSubsiteID !== $this->owner->SubsiteID) {
return true; return false;
} }
return SubsiteState::singleton()->withState(function (SubsiteState $newState) use ($member) { return SubsiteState::singleton()->withState(function (SubsiteState $newState) use ($member) {

View File

@ -35,7 +35,7 @@ class FolderFormFactoryExtension extends Extension
$fields->push($dropdown); $fields->push($dropdown);
$fields->push(LiteralField::create( $fields->push(LiteralField::create(
'Message', 'Message',
'<p class="message notice">' . '<p class="alert alert-info">' .
_t( _t(
__CLASS__ . '.SUBSITENOTICE', __CLASS__ . '.SUBSITENOTICE',
'Folders and files created in the main site are accessible by all subsites.' 'Folders and files created in the main site are accessible by all subsites.'

View File

@ -47,7 +47,7 @@ class GroupSubsites extends DataExtension implements PermissionProvider
} }
// Migration for Group.SubsiteID data from when Groups only had a single subsite // Migration for Group.SubsiteID data from when Groups only had a single subsite
$schema = DataObject::getSchema(); $schema = DataObject::getSchema();
$groupTable = $schema->tableName(Group::class); $groupTable = Convert::raw2sql($schema->tableName(Group::class));
$groupFields = DB::field_list($groupTable); $groupFields = DB::field_list($groupTable);
// Detection of SubsiteID field is the trigger for old-style-subsiteID migration // Detection of SubsiteID field is the trigger for old-style-subsiteID migration
@ -106,7 +106,7 @@ class GroupSubsites extends DataExtension implements PermissionProvider
$subsiteMap $subsiteMap
)); ));
} else { } else {
if (sizeof($subsiteMap) <= 1) { if (sizeof($subsiteMap ?? []) <= 1) {
$fields->addFieldToTab('Root.Subsites', new ReadonlyField( $fields->addFieldToTab('Root.Subsites', new ReadonlyField(
'SubsitesHuman', 'SubsitesHuman',
_t(__CLASS__ . '.ACCESSRADIOTITLE', 'Give this group access to'), _t(__CLASS__ . '.ACCESSRADIOTITLE', 'Give this group access to'),
@ -133,10 +133,10 @@ class GroupSubsites extends DataExtension implements PermissionProvider
{ {
if ($this->owner->AccessAllSubsites) { if ($this->owner->AccessAllSubsites) {
$title = _t(__CLASS__ . '.GlobalGroup', 'global group'); $title = _t(__CLASS__ . '.GlobalGroup', 'global group');
$title = htmlspecialchars($this->owner->Title, ENT_QUOTES) . ' <i>(' . $title . ')</i>'; $title = htmlspecialchars($this->owner->Title ?? '', ENT_QUOTES) . ' <i>(' . $title . ')</i>';
} else { } else {
$subsites = Convert::raw2xml(implode(', ', $this->owner->Subsites()->column('Title'))); $subsites = Convert::raw2xml(implode(', ', $this->owner->Subsites()->column('Title')));
$title = htmlspecialchars($this->owner->Title) . " <i>($subsites)</i>"; $title = htmlspecialchars($this->owner->Title ?? '') . " <i>($subsites)</i>";
} }
} }
@ -153,6 +153,9 @@ class GroupSubsites extends DataExtension implements PermissionProvider
if (Cookie::get('noSubsiteFilter') == 'true') { if (Cookie::get('noSubsiteFilter') == 'true') {
return; return;
} }
if ($dataQuery && $dataQuery->getQueryParam('Subsite.filter') === false) {
return;
}
// If you're querying by ID, ignore the sub-site - this is a bit ugly... // If you're querying by ID, ignore the sub-site - this is a bit ugly...
if (!$query->filtersOnID()) { if (!$query->filtersOnID()) {
@ -165,10 +168,10 @@ class GroupSubsites extends DataExtension implements PermissionProvider
$hasGroupSubsites = false; $hasGroupSubsites = false;
foreach ($query->getFrom() as $item) { foreach ($query->getFrom() as $item) {
if ((is_array($item) && strpos( if ((is_array($item) && strpos(
$item['table'], $item['table'] ?? '',
'Group_Subsites' 'Group_Subsites'
) !== false) || (!is_array($item) && strpos( ) !== false) || (!is_array($item) && strpos(
$item, $item ?? '',
'Group_Subsites' 'Group_Subsites'
) !== false) ) !== false)
) { ) {
@ -224,7 +227,7 @@ class GroupSubsites extends DataExtension implements PermissionProvider
// We are allowed to access this site if at we have CMS_ACCESS_SecurityAdmin permission on // We are allowed to access this site if at we have CMS_ACCESS_SecurityAdmin permission on
// at least one of the sites // at least one of the sites
return (bool)array_intersect($accessibleSites, $linkedSites); return (bool)array_intersect($accessibleSites ?? [], $linkedSites);
} }
public function providePermissions() public function providePermissions()

View File

@ -0,0 +1,22 @@
<?php
namespace SilverStripe\Subsites\Extensions;
use SilverStripe\CMS\Controllers\CMSMain;
use SilverStripe\Core\Extension;
use SilverStripe\Subsites\State\SubsiteState;
/**
* This extension adds the current Subsite ID as an additional factor to the Hints Cßache Key, which is used to cache
* the Site Tree Hints (which include allowed pagetypes).
*
* @package SilverStripe\Subsites\Extensions
* @see CMSMain::generateHintsCacheKey()
*/
class HintsCacheKeyExtension extends Extension
{
public function updateHintsCacheKey(&$baseKey)
{
$baseKey .= '_Subsite:' . SubsiteState::singleton()->getSubsiteId();
}
}

View File

@ -41,9 +41,9 @@ class LeftAndMainSubsites extends LeftAndMainExtension
public function init() public function init()
{ {
Requirements::css('silverstripe/subsites:css/LeftAndMain_Subsites.css'); Requirements::css('silverstripe/subsites:client/css/LeftAndMain_Subsites.css');
Requirements::javascript('silverstripe/subsites:javascript/LeftAndMain_Subsites.js'); Requirements::javascript('silverstripe/subsites:client/javascript/LeftAndMain_Subsites.js');
Requirements::javascript('silverstripe/subsites:javascript/VirtualPage_Subsites.js'); Requirements::javascript('silverstripe/subsites:client/javascript/VirtualPage_Subsites.js');
} }
/** /**
@ -118,7 +118,7 @@ class LeftAndMainSubsites extends LeftAndMainExtension
// Find sites that satisfy all codes conjuncitvely. // Find sites that satisfy all codes conjuncitvely.
$accessibleSites = new ArrayList(); $accessibleSites = new ArrayList();
foreach ($codesPerSite as $siteID => $siteCodes) { foreach ($codesPerSite as $siteID => $siteCodes) {
if (count($siteCodes) == count($codes)) { if (count($siteCodes ?? []) == count($codes ?? [])) {
$accessibleSites->push($sitesArray[$siteID]); $accessibleSites->push($sitesArray[$siteID]);
} }
} }
@ -150,7 +150,7 @@ class LeftAndMainSubsites extends LeftAndMainExtension
return false; return false;
} }
Requirements::javascript('silverstripe/subsites:javascript/LeftAndMain_Subsites.js'); Requirements::javascript('silverstripe/subsites:client/javascript/LeftAndMain_Subsites.js');
$output = ArrayList::create(); $output = ArrayList::create();
@ -160,7 +160,7 @@ class LeftAndMainSubsites extends LeftAndMainExtension
$output->push(ArrayData::create([ $output->push(ArrayData::create([
'CurrentState' => $currentState, 'CurrentState' => $currentState,
'ID' => $subsite->ID, 'ID' => $subsite->ID,
'Title' => Convert::raw2xml($subsite->Title) 'Title' => $subsite->Title,
])); ]));
} }
@ -169,7 +169,7 @@ class LeftAndMainSubsites extends LeftAndMainExtension
public function alternateMenuDisplayCheck($controllerName) public function alternateMenuDisplayCheck($controllerName)
{ {
if (!class_exists($controllerName)) { if (!class_exists($controllerName ?? '')) {
return false; return false;
} }
@ -215,15 +215,22 @@ class LeftAndMainSubsites extends LeftAndMainExtension
/** /**
* Check if the current controller is accessible for this user on this subsite. * Check if the current controller is accessible for this user on this subsite.
*
* @param Member $member
*/ */
public function canAccess() public function canAccess(Member $member = null)
{ {
// Admin can access everything, no point in checking. if (!$member) {
$member = Security::getCurrentUser(); $member = Security::getCurrentUser();
}
// Admin can access everything, no point in checking.
if ($member if ($member
&& (Permission::checkMember($member, 'ADMIN') // 'Full administrative rights' && (Permission::checkMember($member, [
|| Permission::checkMember($member, 'CMS_ACCESS_LeftAndMain') // 'Access to all CMS sections' 'ADMIN', // Full administrative rights
) 'CMS_ACCESS_LeftAndMain', // Access to all CMS sections
'CMS_ACCESS_CMSMain', // Access to CMS controllers
]))
) { ) {
return true; return true;
} }
@ -236,10 +243,12 @@ class LeftAndMainSubsites extends LeftAndMainExtension
/** /**
* Prevent accessing disallowed resources. This happens after onBeforeInit has executed, * Prevent accessing disallowed resources. This happens after onBeforeInit has executed,
* so all redirections should've already taken place. * so all redirections should've already taken place.
*
* @param Member $member
*/ */
public function alternateAccessCheck() public function alternateAccessCheck(Member $member = null)
{ {
return $this->owner->canAccess(); return $this->owner->canAccess($member);
} }
/** /**
@ -360,7 +369,7 @@ class LeftAndMainSubsites extends LeftAndMainExtension
} }
// We have not found any accessible section or subsite. User should be denied access. // We have not found any accessible section or subsite. User should be denied access.
return Security::permissionFailure($this->owner); // This is handled already by LeftAndMain thanks to alternateAccessCheck
} }
// Current site is accessible. Allow through. // Current site is accessible. Allow through.
@ -378,7 +387,7 @@ class LeftAndMainSubsites extends LeftAndMainExtension
if ($record->hasMethod('NormalRelated') && ($record->NormalRelated() || $record->ReverseRelated())) { if ($record->hasMethod('NormalRelated') && ($record->NormalRelated() || $record->ReverseRelated())) {
$this->owner->response->addHeader( $this->owner->response->addHeader(
'X-Status', 'X-Status',
rawurlencode(_t(__CLASS__ . '.Saved', 'Saved, please update related pages.')) rawurlencode(_t(__CLASS__ . '.Saved', 'Saved, please update related pages.') ?? '')
); );
} }
} }

View File

@ -37,7 +37,7 @@ class SiteConfigSubsites extends DataExtension
} }
$regexp = '/^(.*\.)?("|`)?SubsiteID("|`)?\s?=/'; $regexp = '/^(.*\.)?("|`)?SubsiteID("|`)?\s?=/';
foreach ($query->getWhereParameterised($parameters) as $predicate) { foreach ($query->getWhereParameterised($parameters) as $predicate) {
if (preg_match($regexp, $predicate)) { if (preg_match($regexp ?? '', $predicate ?? '')) {
return; return;
} }
} }
@ -48,7 +48,7 @@ class SiteConfigSubsites extends DataExtension
} }
$froms = $query->getFrom(); $froms = $query->getFrom();
$froms = array_keys($froms); $froms = array_keys($froms ?? []);
$tableName = array_shift($froms); $tableName = array_shift($froms);
if ($tableName !== SiteConfig::getSchema()->tableName(SiteConfig::class)) { if ($tableName !== SiteConfig::getSchema()->tableName(SiteConfig::class)) {
return; return;

View File

@ -2,7 +2,9 @@
namespace SilverStripe\Subsites\Extensions; namespace SilverStripe\Subsites\Extensions;
use SilverStripe\Dev\Deprecation;
use Page; use Page;
use SilverStripe\CMS\Forms\SiteTreeURLSegmentField;
use SilverStripe\CMS\Model\SiteTree; use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Control\Controller; use SilverStripe\Control\Controller;
use SilverStripe\Control\Director; use SilverStripe\Control\Director;
@ -25,8 +27,10 @@ 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;
use SilverStripe\VersionedAdmin\Controllers\HistoryViewerController;
/** /**
* Extension for the SiteTree object to add subsites support * Extension for the SiteTree object to add subsites support
@ -88,7 +92,7 @@ class SiteTreeSubsites extends DataExtension
foreach ($query->getFrom() as $tableName => $info) { foreach ($query->getFrom() as $tableName => $info) {
// The tableName should be SiteTree or SiteTree_Live... // The tableName should be SiteTree or SiteTree_Live...
$siteTreeTableName = SiteTree::getSchema()->tableName(SiteTree::class); $siteTreeTableName = SiteTree::getSchema()->tableName(SiteTree::class);
if (strpos($tableName, $siteTreeTableName) === false) { if (strpos($tableName ?? '', $siteTreeTableName ?? '') === false) {
break; break;
} }
$query->addWhere("\"$tableName\".\"SubsiteID\" IN ($subsiteID)"); $query->addWhere("\"$tableName\".\"SubsiteID\" IN ($subsiteID)");
@ -115,10 +119,12 @@ class SiteTreeSubsites extends DataExtension
$subsitesMap = new Map(ArrayList::create()); $subsitesMap = new Map(ArrayList::create());
} }
$viewingPageHistory = Controller::has_curr() && Controller::curr() instanceof HistoryViewerController;
// Master page edit field (only allowed from default subsite to avoid inconsistent relationships) // Master page edit field (only allowed from default subsite to avoid inconsistent relationships)
$isDefaultSubsite = $this->owner->SubsiteID == 0 || $this->owner->Subsite()->DefaultSite; $isDefaultSubsite = $this->owner->SubsiteID == 0 || $this->owner->Subsite()->DefaultSite;
if ($isDefaultSubsite && $subsitesMap->count()) { if ($isDefaultSubsite && $subsitesMap->count() && !$viewingPageHistory) {
$fields->addFieldToTab( $fields->addFieldToTab(
'Root.Main', 'Root.Main',
ToggleCompositeField::create( ToggleCompositeField::create(
@ -150,6 +156,7 @@ class SiteTreeSubsites extends DataExtension
// replace readonly link prefix // replace readonly link prefix
$subsite = $this->owner->Subsite(); $subsite = $this->owner->Subsite();
$nested_urls_enabled = Config::inst()->get(SiteTree::class, 'nested_urls'); $nested_urls_enabled = Config::inst()->get(SiteTree::class, 'nested_urls');
/** @var Subsite $subsite */
if ($subsite && $subsite->exists()) { if ($subsite && $subsite->exists()) {
// Use baseurl from domain // Use baseurl from domain
$baseLink = $subsite->absoluteBaseURL(); $baseLink = $subsite->absoluteBaseURL();
@ -163,64 +170,97 @@ class SiteTreeSubsites extends DataExtension
} }
$urlsegment = $fields->dataFieldByName('URLSegment'); $urlsegment = $fields->dataFieldByName('URLSegment');
if ($urlsegment) { if ($urlsegment && $urlsegment instanceof SiteTreeURLSegmentField) {
$urlsegment->setURLPrefix($baseLink); $urlsegment->setURLPrefix($baseLink);
} }
} }
} }
/** /**
* Does the basic duplication, but doesn't write anything * Does the basic duplication, but doesn't write anything this means we can subclass this easier and do more
* this means we can subclass this easier and do more complex * complex relation duplication.
* relation duplication. *
* Note that when duplicating including children, everything is written.
*
* @param Subsite|int $subsiteID
* @param bool $includeChildren
* @return SiteTree
*/ */
public function duplicateToSubsitePrep($subsiteID) public function duplicateToSubsitePrep($subsiteID, $includeChildren)
{ {
if (is_object($subsiteID)) { if (is_object($subsiteID)) {
$subsiteID = $subsiteID->ID; $subsiteID = $subsiteID->ID;
} }
$oldSubsite = SubsiteState::singleton()->getSubsiteId(); return SubsiteState::singleton()
if ($subsiteID) { ->withState(function (SubsiteState $newState) use ($subsiteID, $includeChildren) {
Subsite::changeSubsite($subsiteID); $newState->setSubsiteId($subsiteID);
} else {
$subsiteID = $oldSubsite; /** @var SiteTree $page */
} $page = $this->owner;
// doesn't write as we need to reset the SubsiteID, ParentID etc
$clone = $this->owner->duplicate(false); try {
$clone->CheckedPublicationDifferences = $clone->AddedToStage = true; // We have no idea what the ParentID should be, but it shouldn't be the same as it was since
$subsiteID = ($subsiteID ? $subsiteID : $oldSubsite); // we're now in a different subsite. As a workaround use the url-segment and subsite ID.
$clone->SubsiteID = $subsiteID; if ($page->Parent()) {
// We have no idea what the parentID should be, so as a workaround use the url-segment and subsite ID $parentSeg = $page->Parent()->URLSegment;
if ($this->owner->Parent()) {
$parentSeg = $this->owner->Parent()->URLSegment;
$newParentPage = Page::get()->filter('URLSegment', $parentSeg)->first(); $newParentPage = Page::get()->filter('URLSegment', $parentSeg)->first();
$originalParentID = $page->ParentID;
if ($newParentPage) { if ($newParentPage) {
$clone->ParentID = $newParentPage->ID; $page->ParentID = (int) $newParentPage->ID;
} else { } else {
// reset it to the top level, so the user can decide where to put it // reset it to the top level, so the user can decide where to put it
$clone->ParentID = 0; $page->ParentID = 0;
} }
} }
// MasterPageID is here for legacy purposes, to satisfy the subsites_relatedpages module
$clone->MasterPageID = $this->owner->ID; // Disable query filtering by subsite during actual duplication
return $clone; $originalFilter = Subsite::$disable_subsite_filter;
Subsite::disable_subsite_filter(true);
return $includeChildren ? $page->duplicateWithChildren() : $page->duplicate(false);
} finally {
Subsite::disable_subsite_filter($originalFilter);
// Re-set the original parent ID for the current page
$page->ParentID = $originalParentID;
}
});
}
/**
* When duplicating a page, assign the current subsite ID from the state
*/
public function onBeforeDuplicate()
{
$subsiteId = SubsiteState::singleton()->getSubsiteId();
if ($subsiteId !== null) {
$this->owner->SubsiteID = $subsiteId;
}
} }
/** /**
* Create a duplicate of this page and save it to another subsite * Create a duplicate of this page and save it to another subsite
* @param $subsiteID int|Subsite The Subsite to copy to, or its ID *
* @param Subsite|int $subsiteID The Subsite to copy to, or its ID
* @param boolean $includeChildren Whether to duplicate child pages too
* @return SiteTree The duplicated page
*/ */
public function duplicateToSubsite($subsiteID = null) public function duplicateToSubsite($subsiteID = null, $includeChildren = false)
{ {
$clone = $this->owner->duplicateToSubsitePrep($subsiteID); /** @var SiteTree|SiteTreeSubsites */
$clone = $this->owner->duplicateToSubsitePrep($subsiteID, $includeChildren);
$clone->invokeWithExtensions('onBeforeDuplicateToSubsite', $this->owner); $clone->invokeWithExtensions('onBeforeDuplicateToSubsite', $this->owner);
if (!$includeChildren) {
// Write the new page if "include children" is false, because it is written by default when it's true.
$clone->write(); $clone->write();
}
// Deprecated: manually duplicate any configured relationships
$clone->duplicateSubsiteRelations($this->owner); $clone->duplicateSubsiteRelations($this->owner);
// new extension hooks which happens after write,
// onAfterDuplicate isn't reliable due to
// https://github.com/silverstripe/silverstripe-cms/issues/1253
$clone->invokeWithExtensions('onAfterDuplicateToSubsite', $this->owner); $clone->invokeWithExtensions('onAfterDuplicateToSubsite', $this->owner);
return $clone; return $clone;
} }
@ -231,6 +271,10 @@ class SiteTreeSubsites extends DataExtension
* It may be that some relations are not diostinct to sub site so can stay * It may be that some relations are not diostinct to sub site so can stay
* whereas others may need to be duplicated * whereas others may need to be duplicated
* *
* This was originally deprecated - Use the "cascade_duplicates" config API instead
* Ideally this would be re-deprecated
*
* @param SiteTree $originalPage
*/ */
public function duplicateSubsiteRelations($originalPage) public function duplicateSubsiteRelations($originalPage)
{ {
@ -273,8 +317,10 @@ class SiteTreeSubsites extends DataExtension
* - Is in a group which has access to the subsite this page belongs to * - Is in a group which has access to the subsite this page belongs to
* - Is in a group with edit permissions on the "main site" * - Is in a group with edit permissions on the "main site"
* *
* @param null $member * If there are no subsites configured yet, this logic is skipped.
* @return bool *
* @param Member|null $member
* @return bool|null
*/ */
public function canEdit($member = null) public function canEdit($member = null)
{ {
@ -282,6 +328,11 @@ class SiteTreeSubsites extends DataExtension
$member = Security::getCurrentUser(); $member = Security::getCurrentUser();
} }
// Do not provide any input if there are no subsites configured
if (!Subsite::get()->exists()) {
return null;
}
// Find the sites that this user has access to // Find the sites that this user has access to
$goodSites = Subsite::accessible_sites('CMS_ACCESS_CMSMain', true, 'all', $member)->column('ID'); $goodSites = Subsite::accessible_sites('CMS_ACCESS_CMSMain', true, 'all', $member)->column('ID');
@ -297,7 +348,7 @@ class SiteTreeSubsites extends DataExtension
} }
// Return true if they have access to this object's site // Return true if they have access to this object's site
if (!(in_array(0, $goodSites) || in_array($subsiteID, $goodSites))) { if (!(in_array(0, $goodSites ?? []) || in_array($subsiteID, $goodSites ?? []))) {
return false; return false;
} }
} }
@ -329,8 +380,8 @@ class SiteTreeSubsites extends DataExtension
} }
/** /**
* @param null $member * @param Member|null $member
* @return bool * @return bool|null
*/ */
public function canPublish($member = null) public function canPublish($member = null)
{ {
@ -347,13 +398,20 @@ 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)) { $ignore_subsite_locale = Config::inst()->get(self::class, 'ignore_subsite_locale');
if (!$ignore_subsite_locale
&& $subsite
&& $subsite->Language
&& i18n::getData()->validate($subsite->Language)
) {
i18n::set_locale($subsite->Language); i18n::set_locale($subsite->Language);
} }
} }
@ -368,24 +426,41 @@ class SiteTreeSubsites extends DataExtension
// This helps deal with Link() returning an absolute URL. // This helps deal with Link() returning an absolute URL.
$url = Director::absoluteURL($this->owner->Link($action)); $url = Director::absoluteURL($this->owner->Link($action));
if ($this->owner->SubsiteID) { if ($this->owner->SubsiteID) {
$url = preg_replace('/\/\/[^\/]+\//', '//' . $this->owner->Subsite()->domain() . '/', $url); $url = preg_replace('/\/\/[^\/]+\//', '//' . $this->owner->Subsite()->domain() . '/', $url ?? '');
} }
return $url; return $url;
} }
/** /**
* Use the CMS domain for iframed CMS previews to prevent single-origin violations * Use the CMS domain for iframed CMS previews to prevent single-origin violations
* and SSL cert problems. * and SSL cert problems. Always set SubsiteID to avoid errors because a page doesn't
* @param null $action * exist on the CMS domain.
*
* @param string &$link
* @param string|null $action
* @return string
*/
public function updatePreviewLink(&$link, $action = null)
{
$url = Director::absoluteURL($this->owner->Link($action));
$link = HTTP::setGetVar('SubsiteID', $this->owner->SubsiteID, $url);
return $link;
}
/**
* This function is marked as deprecated for removal in 5.0.0 in silverstripe/cms
* so now simply passes execution to where the functionality exists for backwards compatiblity.
* CMS 4.0.0 SiteTree already throws a SilverStripe deprecation error before calling this function.
* @deprecated 2.2.0 Use updatePreviewLink() instead
*
* @param string|null $action
* @return string * @return string
*/ */
public function alternatePreviewLink($action = null) public function alternatePreviewLink($action = null)
{ {
$url = Director::absoluteURL($this->owner->Link()); Deprecation::notice('2.2.0', 'Use updatePreviewLink() instead');
if ($this->owner->SubsiteID) { $link = '';
$url = HTTP::setGetVar('SubsiteID', $this->owner->SubsiteID, $url); return $this->updatePreviewLink($link, $action);
}
return $url;
} }
/** /**
@ -410,11 +485,13 @@ class SiteTreeSubsites extends DataExtension
if ($links) { if ($links) {
foreach ($links as $link) { foreach ($links as $link) {
if (substr($link, 0, strlen('http://')) == 'http://') { if (substr($link ?? '', 0, strlen('http://')) == 'http://') {
$withoutHttp = substr($link, strlen('http://')); $withoutHttp = substr($link ?? '', strlen('http://'));
if (strpos($withoutHttp, '/') && strpos($withoutHttp, '/') < strlen($withoutHttp)) { if (strpos($withoutHttp ?? '', '/') &&
$domain = substr($withoutHttp, 0, strpos($withoutHttp, '/')); strpos($withoutHttp ?? '', '/') < strlen($withoutHttp ?? '')
$rest = substr($withoutHttp, strpos($withoutHttp, '/') + 1); ) {
$domain = substr($withoutHttp ?? '', 0, strpos($withoutHttp ?? '', '/'));
$rest = substr($withoutHttp ?? '', strpos($withoutHttp ?? '', '/') + 1);
$subsiteID = Subsite::getSubsiteIDForDomain($domain); $subsiteID = Subsite::getSubsiteIDForDomain($domain);
if ($subsiteID == 0) { if ($subsiteID == 0) {
@ -423,14 +500,10 @@ class SiteTreeSubsites extends DataExtension
$origDisableSubsiteFilter = Subsite::$disable_subsite_filter; $origDisableSubsiteFilter = Subsite::$disable_subsite_filter;
Subsite::disable_subsite_filter(true); Subsite::disable_subsite_filter(true);
$candidatePage = DataObject::get_one( $candidatePage = SiteTree::get()->filter([
SiteTree::class, 'URLSegment' => urldecode($rest),
"\"URLSegment\" = '" 'SubsiteID' => $subsiteID,
. Convert::raw2sql(urldecode($rest)) ])->first();
. "' AND \"SubsiteID\" = "
. $subsiteID,
false
);
Subsite::disable_subsite_filter($origDisableSubsiteFilter); Subsite::disable_subsite_filter($origDisableSubsiteFilter);
if ($candidatePage) { if ($candidatePage) {
@ -491,9 +564,9 @@ class SiteTreeSubsites extends DataExtension
$subsite = Subsite::currentSubsite(); $subsite = Subsite::currentSubsite();
if ($subsite && $subsite->exists() && $subsite->PageTypeBlacklist) { if ($subsite && $subsite->exists() && $subsite->PageTypeBlacklist) {
// SS 4.1: JSON encoded. SS 4.0, comma delimited // SS 4.1: JSON encoded. SS 4.0, comma delimited
$blacklist = Convert::json2array($subsite->PageTypeBlacklist); $blacklist = json_decode($subsite->PageTypeBlacklist ?? '', true);
if ($blacklist === false) { if ($blacklist === false) {
$blacklist = explode(',', $subsite->PageTypeBlacklist); $blacklist = explode(',', $subsite->PageTypeBlacklist ?? '');
} }
if (in_array(get_class($this->owner), (array) $blacklist)) { if (in_array(get_class($this->owner), (array) $blacklist)) {

View File

@ -42,7 +42,7 @@ class GridFieldSubsiteDetailFormItemRequest extends GridFieldDetailForm_ItemRequ
$templateArray $templateArray
); );
$templateDropdown->setEmptyString('(' . _t('Subsite.NOTEMPLATE', 'No template') . ')'); $templateDropdown->setEmptyString('(' . _t('Subsite.NOTEMPLATE', 'No template') . ')');
$form->Fields()->addFieldToTab('Root.Configuration', $templateDropdown); $form->Fields()->addFieldToTab('Root.Main', $templateDropdown);
} }
return $form; return $form;

View File

@ -38,7 +38,7 @@ class SubsitesTreeDropdownField extends TreeDropdownField
{ {
$html = parent::Field($properties); $html = parent::Field($properties);
Requirements::javascript('silverstripe/subsites:javascript/SubsitesTreeDropdownField.js'); Requirements::javascript('silverstripe/subsites:client/javascript/SubsitesTreeDropdownField.js');
return $html; return $html;
} }

View File

@ -36,7 +36,7 @@ class WildcardDomainField extends TextField
*/ */
public function checkHostname($hostname) public function checkHostname($hostname)
{ {
return (bool)preg_match('/^([a-z0-9\*]+[\-\.\:])*([a-z0-9\*]+)$/', $hostname); return (bool)preg_match('/^([a-z0-9\*]+[\-\.\:])*([a-z0-9\*]+)$/', $hostname ?? '');
} }
public function Type() public function Type()

View File

@ -43,8 +43,14 @@ class InitStateMiddleware implements HTTPMiddleware
return $delegate($request); return $delegate($request);
} catch (DatabaseException $ex) { } catch (DatabaseException $ex) {
// Database is not ready $message = $ex->getMessage();
if (strpos($message, 'No database selected') !== false
|| preg_match('/\s*(table|relation) .* does(n\'t| not) exist/i', $message)
) {
// Database is not ready, ignore and continue. Either it doesn't exist or it has no tables
return $delegate($request); return $delegate($request);
}
throw $ex;
} finally { } finally {
// Persist to the session if using the CMS // Persist to the session if using the CMS
if ($state->getUseSessions()) { if ($state->getUseSessions()) {
@ -62,10 +68,10 @@ class InitStateMiddleware implements HTTPMiddleware
public function getIsAdmin(HTTPRequest $request) public function getIsAdmin(HTTPRequest $request)
{ {
$adminPaths = static::config()->get('admin_url_paths'); $adminPaths = static::config()->get('admin_url_paths');
$adminPaths[] = AdminRootController::config()->get('url_base') . '/'; $adminPaths[] = AdminRootController::admin_url();
$currentPath = rtrim($request->getURL(), '/') . '/'; $currentPath = rtrim($request->getURL() ?? '', '/') . '/';
foreach ($adminPaths as $adminPath) { foreach ($adminPaths as $adminPath) {
if (substr($currentPath, 0, strlen($adminPath)) === $adminPath) { if (substr($currentPath ?? '', 0, strlen($adminPath ?? '')) === $adminPath) {
return true; return true;
} }
} }
@ -88,7 +94,7 @@ class InitStateMiddleware implements HTTPMiddleware
return (int) $request->getSession()->get('SubsiteID'); return (int) $request->getSession()->get('SubsiteID');
} }
$subsiteIdFromDomain = Subsite::getSubsiteIDForDomain(); $subsiteIdFromDomain = Subsite::getSubsiteIDForDomain($request->getHost());
if ($subsiteIdFromDomain !== null) { if ($subsiteIdFromDomain !== null) {
return (int) $subsiteIdFromDomain; return (int) $subsiteIdFromDomain;
} }

View File

@ -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;
@ -55,7 +56,7 @@ class Subsite extends DataObject
* Allows you to force a specific subsite ID, or comma separated list of IDs. * 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. * Only works for reading. An object cannot be written to more than 1 subsite.
* *
* @deprecated 2.0.0..3.0.0 Use SubsiteState::singleton()->withState() instead. * @deprecated 2.0.0 Use SubsiteState::singleton()->withState() instead.
*/ */
public static $force_subsite = null; public static $force_subsite = null;
@ -108,12 +109,13 @@ class Subsite extends DataObject
*/ */
private static $check_is_public = true; private static $check_is_public = true;
/*** @return array /**
* @var array
*/ */
private static $summary_fields = [ private static $summary_fields = [
'Title', 'Title',
'PrimaryDomain', 'PrimaryDomain',
'IsPublic' 'IsPublic.Nice'
]; ];
/** /**
@ -182,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()
@ -200,11 +201,11 @@ class Subsite extends DataObject
* *
* @return int ID of the current subsite instance * @return int ID of the current subsite instance
* *
* @deprecated 2.0..3.0 Use SubsiteState::singleton()->getSubsiteId() instead * @deprecated 2.0.0 Use SubsiteState::singleton()->getSubsiteId() instead
*/ */
public static function currentSubsiteID() public static function currentSubsiteID()
{ {
Deprecation::notice('3.0', 'Use SubsiteState::singleton()->getSubsiteId() instead'); Deprecation::notice('2.0.0', 'Use SubsiteState::singleton()->getSubsiteId() instead');
return SubsiteState::singleton()->getSubsiteId(); return SubsiteState::singleton()->getSubsiteId();
} }
@ -257,11 +258,15 @@ class Subsite extends DataObject
$host = $_SERVER['HTTP_HOST']; $host = $_SERVER['HTTP_HOST'];
} }
// Remove ports, we aren't concerned with them in terms of detecting subsites via domains
$hostParts = explode(':', $host ?? '', 2);
$host = reset($hostParts);
$matchingDomains = null; $matchingDomains = null;
$cacheKey = null; $cacheKey = null;
if ($host) { if ($host) {
if (!static::config()->get('strict_subdomain_matching')) { if (!static::config()->get('strict_subdomain_matching')) {
$host = preg_replace('/^www\./', '', $host); $host = preg_replace('/^www\./', '', $host ?? '');
} }
$currentUserId = Security::getCurrentUser() ? Security::getCurrentUser()->ID : 0; $currentUserId = Security::getCurrentUser() ? Security::getCurrentUser()->ID : 0;
@ -276,7 +281,8 @@ class Subsite extends DataObject
/** @skipUpgrade */ /** @skipUpgrade */
$domainTableName = $schema->tableName(SubsiteDomain::class); $domainTableName = $schema->tableName(SubsiteDomain::class);
if (!in_array($domainTableName, DB::table_list())) {
if (!DB::get_schema()->hasTable($domainTableName)) {
// Table hasn't been created yet. Might be a dev/build, skip. // Table hasn't been created yet. Might be a dev/build, skip.
return 0; return 0;
} }
@ -295,9 +301,9 @@ class Subsite extends DataObject
} }
if ($matchingDomains && $matchingDomains->count()) { if ($matchingDomains && $matchingDomains->count()) {
$subsiteIDs = array_unique($matchingDomains->column('SubsiteID')); $subsiteIDs = array_unique($matchingDomains->column('SubsiteID') ?? []);
$subsiteDomains = array_unique($matchingDomains->column('Domain')); $subsiteDomains = array_unique($matchingDomains->column('Domain') ?? []);
if (sizeof($subsiteIDs) > 1) { if (sizeof($subsiteIDs ?? []) > 1) {
throw new UnexpectedValueException(sprintf( throw new UnexpectedValueException(sprintf(
"Multiple subsites match on '%s': %s", "Multiple subsites match on '%s': %s",
$host, $host,
@ -307,7 +313,7 @@ class Subsite extends DataObject
$subsiteID = $subsiteIDs[0]; $subsiteID = $subsiteIDs[0];
} else { } else {
if ($default = DataObject::get_one(Subsite::class, '"DefaultSite" = 1')) { if ($default = Subsite::get()->filter('DefaultSite', 1)->setQueriedColumns(['ID'])->first()) {
// Check for a 'default' subsite // Check for a 'default' subsite
$subsiteID = $default->ID; $subsiteID = $default->ID;
} else { } else {
@ -442,7 +448,7 @@ class Subsite extends DataObject
// Rationalise member arguments // Rationalise member arguments
if (!$member) { if (!$member) {
$member = Member::currentUser(); $member = Security::getCurrentUser();
} }
if (!$member) { if (!$member) {
return new ArrayList(); return new ArrayList();
@ -571,7 +577,7 @@ class Subsite extends DataObject
foreach ($domains as $domain) { foreach ($domains as $domain) {
$domainStr = $domain->Domain; $domainStr = $domain->Domain;
if (!static::config()->get('strict_subdomain_matching')) { if (!static::config()->get('strict_subdomain_matching')) {
$domainStr = preg_replace('/^www\./', '', $domainStr); $domainStr = preg_replace('/^www\./', '', $domainStr ?? '');
} }
$hostmap[$domainStr] = $subsite->domain(); $hostmap[$domainStr] = $subsite->domain();
} }
@ -586,8 +592,8 @@ class Subsite extends DataObject
$data .= "// Generated by Subsite::writeHostMap() on " . date('d/M/y') . "\n"; $data .= "// Generated by Subsite::writeHostMap() on " . date('d/M/y') . "\n";
$data .= '$subsiteHostmap = ' . var_export($hostmap, true) . ';'; $data .= '$subsiteHostmap = ' . var_export($hostmap, true) . ';';
if (is_writable(dirname($file)) || is_writable($file)) { if (is_writable(dirname($file ?? '')) || is_writable($file ?? '')) {
file_put_contents($file, $data); file_put_contents($file ?? '', $data);
} }
} }
@ -618,7 +624,7 @@ class Subsite extends DataObject
return false; return false;
} }
if (!in_array('ADMIN', $permissionCodes)) { if (!in_array('ADMIN', $permissionCodes ?? [])) {
$permissionCodes[] = 'ADMIN'; $permissionCodes[] = 'ADMIN';
} }
@ -768,7 +774,7 @@ class Subsite extends DataObject
$labels['DefaultSite'] = _t('Subsites.DefaultSiteFieldLabel', 'Default site'); $labels['DefaultSite'] = _t('Subsites.DefaultSiteFieldLabel', 'Default site');
$labels['Theme'] = _t('Subsites.ThemeFieldLabel', 'Theme'); $labels['Theme'] = _t('Subsites.ThemeFieldLabel', 'Theme');
$labels['Language'] = _t('Subsites.LanguageFieldLabel', 'Language'); $labels['Language'] = _t('Subsites.LanguageFieldLabel', 'Language');
$labels['IsPublic'] = _t('Subsites.IsPublicFieldLabel', 'Enable public access'); $labels['IsPublic.Nice'] = _t('Subsites.IsPublicFieldLabel', 'Enable public access');
$labels['PageTypeBlacklist'] = _t('Subsites.PageTypeBlacklistFieldLabel', 'Page Type Blacklist'); $labels['PageTypeBlacklist'] = _t('Subsites.PageTypeBlacklistFieldLabel', 'Page Type Blacklist');
$labels['Domains.Domain'] = _t('Subsites.DomainFieldLabel', 'Domain'); $labels['Domains.Domain'] = _t('Subsites.DomainFieldLabel', 'Domain');
$labels['PrimaryDomain'] = _t('Subsites.PrimaryDomainFieldLabel', 'Primary Domain'); $labels['PrimaryDomain'] = _t('Subsites.PrimaryDomainFieldLabel', 'Primary Domain');
@ -783,7 +789,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);
} }
@ -793,7 +799,7 @@ class Subsite extends DataObject
if ($theme[0] == '.') { if ($theme[0] == '.') {
continue; continue;
} }
$theme = strtok($theme, '_'); $theme = strtok($theme ?? '', '_');
$themes[$theme] = $theme; $themes[$theme] = $theme;
} }
ksort($themes); ksort($themes);
@ -870,7 +876,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();
} }
/** /**
@ -983,7 +989,7 @@ JS;
* when a page, etc, is duplicated * when a page, etc, is duplicated
*/ */
$stack = [[0, 0]]; $stack = [[0, 0]];
while (count($stack) > 0) { while (count($stack ?? []) > 0) {
list($sourceParentID, $destParentID) = array_pop($stack); list($sourceParentID, $destParentID) = array_pop($stack);
$children = Versioned::get_by_stage('Page', 'Live', "\"ParentID\" = $sourceParentID", ''); $children = Versioned::get_by_stage('Page', 'Live', "\"ParentID\" = $sourceParentID", '');

View File

@ -111,13 +111,14 @@ class SubsiteDomain extends DataObject
self::PROTOCOL_HTTPS => _t(__CLASS__ . '.PROTOCOL_HTTPS', 'https://'), self::PROTOCOL_HTTPS => _t(__CLASS__ . '.PROTOCOL_HTTPS', 'https://'),
self::PROTOCOL_AUTOMATIC => _t(__CLASS__ . '.PROTOCOL_AUTOMATIC', 'Automatic') self::PROTOCOL_AUTOMATIC => _t(__CLASS__ . '.PROTOCOL_AUTOMATIC', 'Automatic')
]; ];
$fields = new FieldList( $fields = FieldList::create(
WildcardDomainField::create('Domain', $this->fieldLabel('Domain'), null, 255) WildcardDomainField::create('Domain', $this->fieldLabel('Domain'), null, 255)
->setDescription(_t( ->setDescription(_t(
__CLASS__ . '.DOMAIN_DESCRIPTION', __CLASS__ . '.DOMAIN_DESCRIPTION',
'Hostname of this subsite (exclude protocol). Allows wildcards (*).' 'Hostname of this subsite (exclude protocol). Allows wildcards (*).'
)), )),
OptionsetField::create('Protocol', $this->fieldLabel('Protocol'), $protocols) OptionsetField::create('Protocol', $this->fieldLabel('Protocol'), $protocols)
->setValue($this->Protocol ?: self::PROTOCOL_AUTOMATIC)
->setDescription(_t( ->setDescription(_t(
__CLASS__ . '.PROTOCOL_DESCRIPTION', __CLASS__ . '.PROTOCOL_DESCRIPTION',
'When generating links to this subsite, use the selected protocol. <br />' . 'When generating links to this subsite, use the selected protocol. <br />' .
@ -125,7 +126,7 @@ class SubsiteDomain extends DataObject
)), )),
CheckboxField::create('IsPrimary', $this->fieldLabel('IsPrimary')) CheckboxField::create('IsPrimary', $this->fieldLabel('IsPrimary'))
->setDescription(_t( ->setDescription(_t(
__CLASS__ . '.PROTOCOL_DESCRIPTION', __CLASS__ . '.ISPRIMARY_DESCRIPTION',
'Mark this as the default domain for this subsite' 'Mark this as the default domain for this subsite'
)) ))
); );
@ -185,18 +186,18 @@ 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:
$domain = preg_replace('/\.\*$/', ".{$currentHost}", $this->Domain); $domain = preg_replace('/\.\*$/', ".{$currentHost}", $this->Domain ?? '');
// Default to "subsite." prefix for first wildcard // Default to "subsite." prefix for first wildcard
// TODO Whats the significance of "subsite" in this context?! // TODO Whats the significance of "subsite" in this context?!
$domain = preg_replace('/^\*\./', "subsite.", $domain); $domain = preg_replace('/^\*\./', "subsite.", $domain ?? '');
// *Only* removes "intermediate" subdomains, so 'subdomain.www.domain.com' becomes 'subdomain.domain.com' // *Only* removes "intermediate" subdomains, so 'subdomain.www.domain.com' becomes 'subdomain.domain.com'
$domain = str_replace('.www.', '.', $domain); $domain = str_replace('.www.', '.', $domain ?? '');
return $domain; return $domain;
} }

View File

@ -8,7 +8,6 @@ use SilverStripe\CMS\Model\VirtualPage;
use SilverStripe\Control\Controller; use SilverStripe\Control\Controller;
use SilverStripe\Core\Config\Config; use SilverStripe\Core\Config\Config;
use SilverStripe\Forms\DropdownField; use SilverStripe\Forms\DropdownField;
use SilverStripe\Forms\LabelField;
use SilverStripe\Forms\LiteralField; use SilverStripe\Forms\LiteralField;
use SilverStripe\Forms\TextareaField; use SilverStripe\Forms\TextareaField;
use SilverStripe\Forms\TextField; use SilverStripe\Forms\TextField;
@ -113,7 +112,7 @@ class SubsitesVirtualPage extends VirtualPage
'Root.Main', 'Root.Main',
TextareaField::create( TextareaField::create(
'CustomMetaKeywords', 'CustomMetaKeywords',
$this->fieldLabel('CustomMetaTitle') $this->fieldLabel('CustomMetaKeywords')
)->setDescription(_t(__CLASS__ . '.OverrideNote', 'Overrides inherited value from the source')), )->setDescription(_t(__CLASS__ . '.OverrideNote', 'Overrides inherited value from the source')),
'MetaKeywords' 'MetaKeywords'
); );
@ -121,7 +120,7 @@ class SubsitesVirtualPage extends VirtualPage
'Root.Main', 'Root.Main',
TextareaField::create( TextareaField::create(
'CustomMetaDescription', 'CustomMetaDescription',
$this->fieldLabel('CustomMetaTitle') $this->fieldLabel('CustomMetaDescription')
)->setDescription(_t(__CLASS__ . '.OverrideNote', 'Overrides inherited value from the source')), )->setDescription(_t(__CLASS__ . '.OverrideNote', 'Overrides inherited value from the source')),
'MetaDescription' 'MetaDescription'
); );
@ -129,7 +128,7 @@ class SubsitesVirtualPage extends VirtualPage
'Root.Main', 'Root.Main',
TextField::create( TextField::create(
'CustomExtraMeta', 'CustomExtraMeta',
$this->fieldLabel('CustomMetaTitle') $this->fieldLabel('CustomExtraMeta')
)->setDescription(_t(__CLASS__ . '.OverrideNote', 'Overrides inherited value from the source')), )->setDescription(_t(__CLASS__ . '.OverrideNote', 'Overrides inherited value from the source')),
'ExtraMeta' 'ExtraMeta'
); );
@ -175,7 +174,7 @@ class SubsitesVirtualPage extends VirtualPage
} }
foreach (self::$db as $field => $type) { foreach (self::$db as $field => $type) {
if (in_array($field, $fields)) { if (in_array($field, $fields ?? [])) {
unset($fields[array_search($field, $fields)]); unset($fields[array_search($field, $fields)]);
} }
} }
@ -226,30 +225,20 @@ class SubsitesVirtualPage extends VirtualPage
// Veto the validation rules if its false. In this case, some logic // Veto the validation rules if its false. In this case, some logic
// needs to be duplicated from parent to find out the exact reason the validation failed. // needs to be duplicated from parent to find out the exact reason the validation failed.
if (!$isValid) { if (!$isValid) {
$IDFilter = $this->ID ? "AND \"SiteTree\".\"ID\" <> $this->ID" : null; $filters = [
$parentFilter = null; 'URLSegment' => $this->URLSegment,
'ID:not' => $this->ID,
];
if (Config::inst()->get(SiteTree::class, 'nested_urls')) { if (Config::inst()->get(SiteTree::class, 'nested_urls')) {
if ($this->ParentID) { $filters['ParentID'] = $this->ParentID ?: 0;
$parentFilter = " AND \"SiteTree\".\"ParentID\" = $this->ParentID";
} else {
$parentFilter = ' AND "SiteTree"."ParentID" = 0';
}
} }
$origDisableSubsiteFilter = Subsite::$disable_subsite_filter; $origDisableSubsiteFilter = Subsite::$disable_subsite_filter;
Subsite::$disable_subsite_filter = true; Subsite::disable_subsite_filter();
$existingPage = DataObject::get_one( $existingPage = SiteTree::get()->filter($filters)->first();
SiteTree::class, Subsite::disable_subsite_filter($origDisableSubsiteFilter);
"\"URLSegment\" = '$this->URLSegment' $IDFilter $parentFilter", $existingPageInSubsite = SiteTree::get()->filter($filters)->first();
false // disable cache, it doesn't include subsite status in the key
);
Subsite::$disable_subsite_filter = $origDisableSubsiteFilter;
$existingPageInSubsite = DataObject::get_one(
SiteTree::class,
"\"URLSegment\" = '$this->URLSegment' $IDFilter $parentFilter",
false // disable cache, it doesn't include subsite status in the key
);
// If URL has been vetoed because of an existing page, // If URL has been vetoed because of an existing page,
// be more specific and allow same URLSegments in different subsites // be more specific and allow same URLSegments in different subsites

View File

@ -27,10 +27,10 @@ class SubsiteReportWrapper extends ReportWrapper
_t(__CLASS__ . '.ReportDropdown', 'Sites'), _t(__CLASS__ . '.ReportDropdown', 'Sites'),
$options $options
); );
$subsiteField->setValue(array_keys($options)); $subsiteField->setValue(array_keys($options ?? []));
// We don't need to make the field editable if only one subsite is available // We don't need to make the field editable if only one subsite is available
if (sizeof($options) <= 1) { if (sizeof($options ?? []) <= 1) {
$subsiteField = $subsiteField->performReadonlyTransformation(); $subsiteField = $subsiteField->performReadonlyTransformation();
} }
@ -70,7 +70,7 @@ class SubsiteReportWrapper extends ReportWrapper
} else { } else {
$subsites = Subsite::accessible_sites('CMS_ACCESS_CMSMain'); $subsites = Subsite::accessible_sites('CMS_ACCESS_CMSMain');
$options = $subsites->toDropdownMap('ID', 'Title'); $options = $subsites->toDropdownMap('ID', 'Title');
Subsite::$force_subsite = join(',', array_keys($options)); Subsite::$force_subsite = join(',', array_keys($options ?? []));
} }
} }

View 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 ?? []);
}
}

View File

@ -5,6 +5,7 @@ namespace SilverStripe\Subsites\State;
use SilverStripe\Core\Injector\Injectable; use SilverStripe\Core\Injector\Injectable;
use SilverStripe\Core\Injector\Injector; use SilverStripe\Core\Injector\Injector;
use SilverStripe\Core\Resettable; use SilverStripe\Core\Resettable;
use SilverStripe\Dev\Deprecation;
/** /**
* SubsiteState provides static access to the current state for subsite related data during a request * SubsiteState provides static access to the current state for subsite related data during a request
@ -48,6 +49,9 @@ class SubsiteState implements Resettable
*/ */
public function setSubsiteId($id) public function setSubsiteId($id)
{ {
if (!ctype_digit((string) $id) && !is_null($id)) {
Deprecation::notice('2.8.0', 'Passing multiple IDs is deprecated, only pass a single ID instead.');
}
if (is_null($this->originalSubsiteId)) { if (is_null($this->originalSubsiteId)) {
$this->originalSubsiteId = (int) $id; $this->originalSubsiteId = (int) $id;
} }

View File

@ -56,7 +56,7 @@ class SubsiteCopyPagesTask extends BuildTask
// issues with having to check whether or not the new parents have been added to the site tree // issues with having to check whether or not the new parents have been added to the site tree
// when a page, etc, is duplicated // when a page, etc, is duplicated
$stack = [[0, 0]]; $stack = [[0, 0]];
while (count($stack) > 0) { while (count($stack ?? []) > 0) {
list($sourceParentID, $destParentID) = array_pop($stack); list($sourceParentID, $destParentID) = array_pop($stack);
$children = Versioned::get_by_stage(SiteTree::class, 'Live', "\"ParentID\" = $sourceParentID", ''); $children = Versioned::get_by_stage(SiteTree::class, 'Live', "\"ParentID\" = $sourceParentID", '');

View File

@ -0,0 +1,35 @@
<?php
namespace SilverStripe\Subsites\Tasks;
use SilverStripe\Dev\Tasks\MigrateFileTask;
use SilverStripe\Subsites\Model\Subsite;
use SilverStripe\Dev\Deprecation;
/**
* @deprecated 2.8.0 Will be removed without equivalent functionality to replace it
*/
class SubsiteMigrateFileTask extends MigrateFileTask
{
public function __construct()
{
Deprecation::withNoReplacement(function () {
Deprecation::notice(
'2.8.0',
'Will be removed without equivalent functionality to replace it',
Deprecation::SCOPE_CLASS
);
});
parent::__construct();
}
public function run($request)
{
$origDisableSubsiteFilter = Subsite::$disable_subsite_filter;
Subsite::disable_subsite_filter(true);
parent::run($request);
Subsite::disable_subsite_filter($origDisableSubsiteFilter);
}
}

View File

@ -0,0 +1,29 @@
# See https://github.com/silverstripe/silverstripe-subsites/issues/357
Feature: Insert an internal link into content
As a CMS user
I can insert internal links into my content
So that I can direct users to different parts of my website
Background:
Given a "subsite" "Subsite B"
And a "page" "My page" with "URLSegment"="my-page", "Content"="My page content"
And a "page" "Another page" with "URLSegment"="another-page", "Content"="My other page content"
And I am logged in with "CMS_ACCESS_CMSMain" permissions
Then I go to "admin/pages"
And I click on "My page" in the tree
@javascript
Scenario: I can insert an internal link
# See "insert-a-link.feature" from silverstripe/cms
When I select "My page" in the "Content" HTML field
And I press the "Insert link" HTML field button
And I click "Page on this site" in the ".mce-menu" element
Then I should see an "form#Form_editorInternalLink" element
When I click "(Search or choose Page)" in the ".Select-multi-value-wrapper" element
And I click "Another page" in the ".treedropdownfield__menu" element
And I fill in "my desc" for "Link description"
And I press the "Insert" button
Then the "Content" HTML field should contain "<a title="my desc" href="[sitetree_link"
And the "Content" HTML field should contain "My page</a>"
# Required to avoid "unsaved changes" browser dialog
Then I press the "Save" button

View File

@ -0,0 +1,81 @@
@javascript
Feature: Create and select a subsite
As a CMS user
I want to be able to select a subsite
So that I can edit content for a specific subsite
Background:
# There's a bug where you need CMS_ACCESS_CMSMain rather than CMS_ACCESS_LeftAndMain permissions to
# use subsites as expected
Given the "group" "EDITOR" has permissions "CMS_ACCESS_CMSMain" and "CMS_ACCESS_AssetAdmin" and "FILE_EDIT_ALL"
And a "page" "My page" with "URLSegment"="my-page", "Content"="My page content"
And an "image" "file1.jpg"
And an "image" "file2.jpg"
Scenario: I can operate subsites
# Create subsite as Admin
Given I am logged in with "ADMIN" permissions
Then I go to "admin/subsites"
# Add subsites button is not a regular button, so click using css selector
And I click on the ".btn-toolbar .btn__title" element
And I fill in "Subsite Name" with "My subsite"
And I press "Create"
# Add a file to the main site
When I go to "admin/assets"
And I press the "Add folder" button
And I select "Main site" from "SubsitesSelect"
# Using a short folder name so that it doesn't get truncated on the frontend
And I fill in "Folder name" with "mfol"
And I press the "Create" button
When I go to "admin/assets"
And I click on the file named "mfol" in the gallery
And I attach the file "file1.jpg" to dropzone "gallery-container"
# Change to Editor user
When I go to "/Security/login"
And I press the "Log in as someone else" button
When I am logged in as a member of "EDITOR" group
And I go to "admin/pages"
# Can see main site page on main site
When I go to "admin/pages"
Then I should see "My page" in the tree
# Cannot see main site page on subsite
When I select "My subsite" from "SubsitesSelect"
And I go to "admin/pages"
Then I should not see "My page" in the tree
# Create a page on the subsite
When I press the "Add new" button
And I select the "Page" radio button
And I press the "Create" button
When I fill in "Page name" with "My subsite page"
And I press the "Publish" button
Then I should see "My subsite page"
# Can see main site folders/files from subsite
When I go to "admin/assets"
Then I should see "mfol"
When I click on the file named "mfol" in the gallery
Then I should see "file1"
# Add a file to the subsite
When I go to "admin/assets"
And I select "My subsite" from "SubsitesSelect"
And I press the "Add folder" button
And I fill in "Folder name" with "sfol"
And I press the "Create" button
When I go to "admin/assets"
And I click on the file named "sfol" in the gallery
And I attach the file "file2.jpg" to dropzone "gallery-container"
# Change back to main subsite - cannot see subsite folders/files
When I go to "admin/assets"
And I select "Main site" from "SubsitesSelect"
Then I should see "mfol"
Then I should not see "My subsite page"

View File

@ -4,22 +4,26 @@ Feature: Preview navigation
In order to preview my content In order to preview my content
Background: Background:
Given a "subsite" "My subsite" Given a "subsite" "MySubsite"
And a "page" "My page" with "URLSegment"="my-page", "Content"="My page content <a name='aname'>aname</a><a href='other-page'>ahref</a>" and "Subsite"="=>SilverStripe\Subsites\Model\Subsite.My subsite" And a "page" "My page" with "URLSegment"="my-page", "Content"="My page content <a name='aname'>aname</a> <a href='[sitetree_link,id=5]'>ahref</a>" and "SubsiteID"="1"
And a "page" "Other page" with "URLSegment"="other-page", "Content"="Other page content <a href='my-page'>Goto my page></a>" and "Subsite"="=>SilverStripe\Subsites\Model\Subsite.My subsite" And a "page" "Other page" with "URLSegment"="other-page", "Content"="Other page content <a href='[sitetree_link,id=4]'>Goto my page</a>" and "SubsiteID"="1"
Given a "member" "Joe" belonging to "Admin Group" with "Email"="joe@test.com" and "Password"="Password1" And the "group" "EDITOR" has permissions "Access to 'Pages' section" and "Access to 'Subsites' section"
And the "group" "Admin Group" has permissions "Full administrative rights" And I am logged in as a member of "EDITOR" group
And I log in with "joe@test.com" and "Password1"
@javascript @javascript
Scenario: I can navigate the subsite preview Scenario: I can navigate the subsite preview
When I go to "admin" When I go to "/admin/pages"
And I select "My subsite" from "SubsitesSelect" And I select "MySubsite" from "SubsitesSelect"
And I go to "admin/pages" And I click on "My page" in the tree
And I press the "Publish" button
And I click on "Other page" in the tree
And I press the "Publish" button
And I click on "My page" in the tree And I click on "My page" in the tree
And I set the CMS mode to "Preview mode" And I set the CMS mode to "Preview mode"
And I follow "ahref" in preview And I follow "ahref" in preview
And I wait for 1 second
Then the preview contains "Other page content" Then the preview contains "Other page content"
# We are already on the second page, follow a link to return to first one. # We are already on the second page, follow a link to return to first one.
And I follow "Goto my page" in preview And I follow "Goto my page" in preview
And I wait for 1 second
Then the preview contains "My page content" Then the preview contains "My page content"

View File

@ -0,0 +1,26 @@
Feature: Publish a page
As a CMS user
I can author pages in a new subsite
So that I can separate my website content by site
Background:
Given a "subsite" "Subsite B"
And a "page" "My page" with "URLSegment"="my-page", "Content"="My page content"
And the "group" "EDITOR" has permissions "Access to 'Pages' section" and "Access to 'Subsites' section"
And I am logged in as a member of "EDITOR" group
Then I go to "admin/pages"
@javascript
Scenario: I can publish a new page
Given I select "Subsite B" from "SubsitesSelect"
When I press the "Add new" button
And I press the "Create" button
And I set the CMS mode to "Edit mode"
And I fill in the "Content" HTML field with "<p>Some test content</p>"
Then I should see a "Publish" button
And I should not see a "Published" button
When I press the "Publish" button
And I wait for 3 seconds
Then I should see a "Published" button
And I should see a "Saved" button

BIN
tests/behat/files/file1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

BIN
tests/behat/files/file2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -9,7 +9,7 @@ use SilverStripe\Subsites\State\SubsiteState;
class BaseSubsiteTest extends SapphireTest class BaseSubsiteTest extends SapphireTest
{ {
protected function setUp() protected function setUp(): void
{ {
parent::setUp(); parent::setUp();

View File

@ -24,8 +24,8 @@ class FolderFormFactoryExtensionTest extends SapphireTest
'Record' => $folder 'Record' => $folder
]); ]);
$source = array_values($folderForm->Fields()->fieldByName('SubsiteID')->getSource()); $source = array_values($folderForm->Fields()->fieldByName('SubsiteID')->getSource() ?? []);
$result = array_values($source); $result = array_values($source ?? []);
$this->assertContains('Main site', $result); $this->assertContains('Main site', $result);
$this->assertContains('Subsite A', $result); $this->assertContains('Subsite A', $result);

View File

@ -8,6 +8,7 @@ use SilverStripe\Core\Config\Config;
use SilverStripe\Forms\FieldList; use SilverStripe\Forms\FieldList;
use SilverStripe\Subsites\Extensions\FileSubsites; use SilverStripe\Subsites\Extensions\FileSubsites;
use SilverStripe\Subsites\Model\Subsite; use SilverStripe\Subsites\Model\Subsite;
use SilverStripe\Security\Member;
class FileSubsitesTest extends BaseSubsiteTest class FileSubsitesTest extends BaseSubsiteTest
{ {
@ -65,4 +66,64 @@ class FileSubsitesTest extends BaseSubsiteTest
$file->onAfterUpload(); $file->onAfterUpload();
$this->assertEquals($folder->SubsiteID, $file->SubsiteID); $this->assertEquals($folder->SubsiteID, $file->SubsiteID);
} }
/**
* @dataProvider provideTestCanEdit
*/
public function testCanEdit(
string $fileKey,
string $memberKey,
string $currentSubsiteKey,
bool $expected
): void {
$file = $this->objFromFixture(File::class, $fileKey);
$subsiteID = ($currentSubsiteKey === 'mainsite')
? 0 : $this->objFromFixture(Subsite::class, $currentSubsiteKey)->ID;
$member = $this->objFromFixture(Member::class, $memberKey);
Subsite::changeSubsite($subsiteID);
$this->assertSame($expected, $file->canEdit($member));
}
public function provideTestCanEdit(): array
{
$ret = [];
$data = [
// file
'subsite1file' => [
// member - has permissions to edit the file
'filetestyes' => [
// current subite => expected canEdit()
'subsite1' => true,
'subsite2' => false,
'mainsite' => true
],
// member - does not have permissions to edit the file
'filetestno' => [
'subsite1' => false,
'subsite2' => false,
'mainsite' => false
],
],
'mainsitefile' => [
'filetestyes' => [
'subsite1' => true,
'subsite2' => true,
'mainsite' => true
],
'filetestno' => [
'subsite1' => false,
'subsite2' => false,
'mainsite' => false
],
]
];
foreach (array_keys($data) as $fileKey) {
foreach (array_keys($data[$fileKey]) as $memberKey) {
foreach ($data[$fileKey][$memberKey] as $currentSubsiteKey => $expected) {
$ret[] = [$fileKey, $memberKey, $currentSubsiteKey, $expected];
}
}
}
return $ret;
}
} }

View File

@ -15,8 +15,8 @@ class GroupSubsitesTest extends BaseSubsiteTest
public function testTrivialFeatures() public function testTrivialFeatures()
{ {
$this->assertInternalType('array', singleton(GroupSubsites::class)->extraStatics()); $this->assertIsArray(singleton(GroupSubsites::class)->extraStatics());
$this->assertInternalType('array', singleton(GroupSubsites::class)->providePermissions()); $this->assertIsArray(singleton(GroupSubsites::class)->providePermissions());
$this->assertInstanceOf(FieldList::class, singleton(Group::class)->getCMSFields()); $this->assertInstanceOf(FieldList::class, singleton(Group::class)->getCMSFields());
} }

View File

@ -0,0 +1,80 @@
<?php
namespace SilverStripe\Subsites\Tests;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Subsites\Middleware\InitStateMiddleware;
use SilverStripe\Subsites\Model\Subsite;
use SilverStripe\Subsites\State\SubsiteState;
class InitStateMiddlewareTest extends BaseSubsiteTest
{
protected static $fixture_file = 'SubsiteTest.yml';
/**
* Original value of $_REQUEST
*
* @var array
*/
protected $origServer = [];
protected function setUp(): void
{
parent::setUp();
$this->origServer = $_SERVER;
}
protected function tearDown(): void
{
$_SERVER = $this->origServer;
parent::tearDown();
}
public function testDomainDetectionViaServerHeaders()
{
$_SERVER['HTTP_HOST'] = 'one.example.org';
$this->getMiddleware()->process($this->getRequest(), $this->getCallback());
$expectedSubsite = $this->objFromFixture(Subsite::class, 'domaintest1');
$this->assertEquals($expectedSubsite->ID, $this->getState()->getSubsiteId());
}
public function testDomainDetectionViaRequestOverridesServerHeaders()
{
$_SERVER['HTTP_HOST'] = 'one.example.org';
$this->getMiddleware()->process($this->getRequest('two.mysite.com'), $this->getCallback());
$expectedSubsite = $this->objFromFixture(Subsite::class, 'domaintest2');
$this->assertEquals($expectedSubsite->ID, $this->getState()->getSubsiteId());
}
protected function getMiddleware()
{
return new InitStateMiddleware();
}
protected function getRequest($domain = null)
{
$request = new HTTPRequest('GET', '/test/url');
if ($domain) {
$request->addHeader('host', $domain);
}
return $request;
}
protected function getCallback()
{
return function () {
};
}
protected function getState()
{
return Injector::inst()->get(SubsiteState::class);
}
}

View File

@ -9,6 +9,7 @@ use SilverStripe\CMS\Controllers\CMSPageEditController;
use SilverStripe\Core\Config\Config; use SilverStripe\Core\Config\Config;
use SilverStripe\Dev\FunctionalTest; use SilverStripe\Dev\FunctionalTest;
use SilverStripe\Security\Member; use SilverStripe\Security\Member;
use SilverStripe\Subsites\Extensions\LeftAndMainSubsites;
use SilverStripe\Subsites\Model\Subsite; use SilverStripe\Subsites\Model\Subsite;
use SilverStripe\Subsites\State\SubsiteState; use SilverStripe\Subsites\State\SubsiteState;
@ -37,19 +38,19 @@ class LeftAndMainSubsitesTest extends FunctionalTest
$cmsmain = singleton(CMSMain::class); $cmsmain = singleton(CMSMain::class);
$subsites = $cmsmain->sectionSites(true, 'Main site', $member); $subsites = $cmsmain->sectionSites(true, 'Main site', $member);
$this->assertDOSEquals([ $this->assertListEquals([
['Title' => 'Subsite1 Template'] ['Title' => 'Subsite1 Template']
], $subsites, 'Lists member-accessible sites for the accessible controller.'); ], $subsites, 'Lists member-accessible sites for the accessible controller.');
$assetadmin = singleton(AssetAdmin::class); $assetadmin = singleton(AssetAdmin::class);
$subsites = $assetadmin->sectionSites(true, 'Main site', $member); $subsites = $assetadmin->sectionSites(true, 'Main site', $member);
$this->assertDOSEquals([], $subsites, 'Does not list any sites for forbidden controller.'); $this->assertListEquals([], $subsites, 'Does not list any sites for forbidden controller.');
$member = $this->objFromFixture(Member::class, 'editor'); $member = $this->objFromFixture(Member::class, 'editor');
$cmsmain = singleton(CMSMain::class); $cmsmain = singleton(CMSMain::class);
$subsites = $cmsmain->sectionSites(true, 'Main site', $member); $subsites = $cmsmain->sectionSites(true, 'Main site', $member);
$this->assertDOSContains([ $this->assertListContains([
['Title' => 'Main site'] ['Title' => 'Main site']
], $subsites, 'Includes the main site for members who can access all sites.'); ], $subsites, 'Includes the main site for members who can access all sites.');
} }
@ -100,4 +101,14 @@ class LeftAndMainSubsitesTest extends FunctionalTest
$this->assertTrue($l->shouldChangeSubsite(CMSPageEditController::class, 1, 5)); $this->assertTrue($l->shouldChangeSubsite(CMSPageEditController::class, 1, 5));
$this->assertFalse($l->shouldChangeSubsite(CMSPageEditController::class, 1, 1)); $this->assertFalse($l->shouldChangeSubsite(CMSPageEditController::class, 1, 1));
} }
public function testCanAccessWithPassedMember()
{
$memberID = $this->logInWithPermission('ADMIN');
$member = Member::get()->byID($memberID);
/** @var LeftAndMain&LeftAndMainSubsites $leftAndMain */
$leftAndMain = new LeftAndMain();
$this->assertTrue($leftAndMain->canAccess($member));
}
} }

View 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(): void
{
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)
],
];
}
}

View File

@ -5,22 +5,27 @@ namespace SilverStripe\Subsites\Tests;
use Page; use Page;
use SilverStripe\CMS\Controllers\CMSMain; use SilverStripe\CMS\Controllers\CMSMain;
use SilverStripe\CMS\Controllers\ModelAsController; use SilverStripe\CMS\Controllers\ModelAsController;
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\Convert;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\ErrorPage\ErrorPage; use SilverStripe\ErrorPage\ErrorPage;
use SilverStripe\Forms\FieldList; use SilverStripe\Forms\FieldList;
use SilverStripe\i18n\i18n;
use SilverStripe\Security\Member; use SilverStripe\Security\Member;
use SilverStripe\SiteConfig\SiteConfig; 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;
use SilverStripe\Versioned\Versioned; use SilverStripe\Versioned\Versioned;
use SilverStripe\View\SSViewer; use SilverStripe\View\SSViewer;
use TractorCow\Fluent\Extension\FluentSiteTreeExtension;
class SiteTreeSubsitesTest extends BaseSubsiteTest class SiteTreeSubsitesTest extends BaseSubsiteTest
{ {
@ -33,10 +38,12 @@ class SiteTreeSubsitesTest extends BaseSubsiteTest
]; ];
protected static $illegal_extensions = [ protected static $illegal_extensions = [
SiteTree::class => ['Translatable'] // @todo implement Translatable namespace SiteTree::class => [
FluentSiteTreeExtension::class,
],
]; ];
protected function setUp() protected function setUp(): void
{ {
// We have our own home page fixtures, prevent the default one being created in this test suite. // We have our own home page fixtures, prevent the default one being created in this test suite.
Config::modify()->set(SiteTree::class, 'create_default_pages', false); Config::modify()->set(SiteTree::class, 'create_default_pages', false);
@ -185,6 +192,22 @@ class SiteTreeSubsitesTest extends BaseSubsiteTest
$this->assertEquals($p2->ID, SiteTree::get_by_link('test-page')->ID); $this->assertEquals($p2->ID, SiteTree::get_by_link('test-page')->ID);
} }
public function testIgnoreSubsiteLocale()
{
$ignore_subsite_locale = Config::inst()->set(SiteTreeSubsites::class, 'ignore_subsite_locale', true);
$subsitePage = $this->objFromFixture(Page::class, 'subsite_locale_about');
Subsite::changeSubsite($subsitePage->SubsiteID);
$controller = ModelAsController::controller_for($subsitePage);
$i18n_locale_before = i18n::get_locale();
SiteTree::singleton()->extend('contentcontrollerInit', $controller);
$i18n_locale_after = i18n::get_locale();
$this->assertEquals($i18n_locale_before, $i18n_locale_after);
}
public function testPageTypesBlacklistInClassDropdown() public function testPageTypesBlacklistInClassDropdown()
{ {
$this->logInAs('editor'); $this->logInAs('editor');
@ -268,7 +291,7 @@ class SiteTreeSubsitesTest extends BaseSubsiteTest
Subsite::changeSubsite($s1); Subsite::changeSubsite($s1);
$cmsmain = CMSMain::create(); $cmsmain = CMSMain::create();
$hints = Convert::json2array($cmsmain->SiteTreeHints()); $hints = json_decode($cmsmain->SiteTreeHints() ?? '', true);
$classes = $hints['Root']['disallowedChildren']; $classes = $hints['Root']['disallowedChildren'];
$this->assertContains(ErrorPage::class, $classes); $this->assertContains(ErrorPage::class, $classes);
$this->assertContains(TestClassA::class, $classes); $this->assertContains(TestClassA::class, $classes);
@ -279,7 +302,7 @@ class SiteTreeSubsitesTest extends BaseSubsiteTest
if ($cmsmain->hasMethod('getHintsCache')) { if ($cmsmain->hasMethod('getHintsCache')) {
$cmsmain->getHintsCache()->clear(); $cmsmain->getHintsCache()->clear();
} }
$hints = Convert::json2array($cmsmain->SiteTreeHints()); $hints = json_decode($cmsmain->SiteTreeHints() ?? '', true);
$classes = $hints['Root']['disallowedChildren']; $classes = $hints['Root']['disallowedChildren'];
$this->assertNotContains(ErrorPage::class, $classes); $this->assertNotContains(ErrorPage::class, $classes);
@ -359,65 +382,62 @@ class SiteTreeSubsitesTest extends BaseSubsiteTest
$this->assertEquals('important-page', $mainSubsiteImportantPage->URLSegment); $this->assertEquals('important-page', $mainSubsiteImportantPage->URLSegment);
} }
public function testCopySubsiteWithChildren() /**
* @param bool $withChildren
* @param int $expectedChildren
* @dataProvider duplicateToSubsiteProvider
*/
public function testDuplicateToSubsite($withChildren, $expectedChildren)
{ {
$page = $this->objFromFixture('Page', 'about'); /** @var SiteTree $page */
$page = $this->objFromFixture(Page::class, 'about');
/** @var Subsite $newSubsite */
$newSubsite = $this->objFromFixture(Subsite::class, 'subsite1'); $newSubsite = $this->objFromFixture(Subsite::class, 'subsite1');
$moved = $page->duplicateToSubsite($newSubsite->ID, true); /** @var SiteTree $duplicatedPage */
$this->assertEquals($moved->SubsiteID, $newSubsite->ID, 'Ensure returned records are on new subsite'); $duplicatedPage = $page->duplicateToSubsite($newSubsite->ID, $withChildren);
$this->assertEquals( $this->assertInstanceOf(SiteTree::class, $duplicatedPage, 'A new page is returned');
$moved->AllChildren()->count(),
$page->AllChildren()->count(), $this->assertEquals($newSubsite->ID, $duplicatedPage->SubsiteID, 'Ensure returned records are on new subsite');
'All pages are copied across'
$this->assertCount(1, $page->AllChildren());
$this->assertCount(
$expectedChildren,
$duplicatedPage->AllChildren(),
'Duplicated page also duplicates children'
); );
} }
public function testCopySubsiteWithoutChildren() /**
* @return array[]
*/
public function duplicateToSubsiteProvider()
{ {
$page = $this->objFromFixture('Page', 'about'); return [
$newSubsite = $this->objFromFixture(Subsite::class, 'subsite2'); [true, 1],
[false, 0],
$moved = $page->duplicateToSubsite($newSubsite->ID, false); ];
$this->assertEquals($moved->SubsiteID, $newSubsite->ID, 'Ensure returned records are on new subsite');
$this->assertEquals($moved->AllChildren()->count(), 0, 'All pages are copied across');
} }
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()
@ -436,7 +456,7 @@ class SiteTreeSubsitesTest extends BaseSubsiteTest
/** /**
* @dataProvider provideAlternateAbsoluteLink * @dataProvider provideAlternateAbsoluteLink
* @param name $pageFixtureName * @param string $pageFixtureName
* @param string|null $action * @param string|null $action
* @param string $expectedAbsoluteLink * @param string $expectedAbsoluteLink
*/ */
@ -452,4 +472,23 @@ class SiteTreeSubsitesTest extends BaseSubsiteTest
$this->assertEquals($expectedAbsoluteLink, $result); $this->assertEquals($expectedAbsoluteLink, $result);
} }
public function testURLSegmentBaseIsSetToSubsiteBaseURL()
{
// This subsite has a domain with 'one.example.org' as the primary domain
/** @var Subsite $subsite */
$subsite = $this->objFromFixture(Subsite::class, 'domaintest1');
Subsite::changeSubsite($subsite);
$page = new SiteTree();
$page->SubsiteID = $subsite->ID;
$page->write();
$fields = $page->getCMSFields();
/** @var SiteTreeURLSegmentField $urlSegmentField */
$urlSegmentField = $fields->dataFieldByName('URLSegment');
$this->assertInstanceOf(SiteTreeURLSegmentField::class, $urlSegmentField);
$this->assertSame('http://one.example.org/', $urlSegmentField->getURLPrefix());
}
} }

View File

@ -14,7 +14,7 @@ class SubsiteAdminFunctionalTest extends FunctionalTest
protected $autoFollowRedirection = false; protected $autoFollowRedirection = false;
protected function setUp() protected function setUp(): void
{ {
parent::setUp(); parent::setUp();
// Ensure all pages are published // Ensure all pages are published
@ -48,14 +48,18 @@ class SubsiteAdminFunctionalTest extends FunctionalTest
$this->logOut(); $this->logOut();
$response = $this->getAndFollowAll('admin/pages/?SubsiteID=0'); $response = $this->getAndFollowAll('admin/pages/?SubsiteID=0');
$this->assertContains('Security/login', $this->mainSession->lastUrl(), 'Admin is disallowed'); $this->assertStringContainsString('Security/login', $this->mainSession->lastUrl(), 'Admin is disallowed');
$subsite1 = $this->objFromFixture(Subsite::class, 'subsite1'); $subsite1 = $this->objFromFixture(Subsite::class, 'subsite1');
$response = $this->getAndFollowAll("admin/pages/?SubsiteID={$subsite1->ID}"); $response = $this->getAndFollowAll("admin/pages/?SubsiteID={$subsite1->ID}");
$this->assertContains('Security/login', $this->mainSession->lastUrl(), 'Admin is disallowed'); $this->assertStringContainsString('Security/login', $this->mainSession->lastUrl(), 'Admin is disallowed');
$response = $this->getAndFollowAll('admin/subsite_xhr'); $response = $this->getAndFollowAll('admin/subsite_xhr');
$this->assertContains('Security/login', $this->mainSession->lastUrl(), 'SubsiteXHRController is disallowed'); $this->assertStringContainsString(
'Security/login',
$this->mainSession->lastUrl(),
'SubsiteXHRController is disallowed'
);
} }
/** /**
@ -67,17 +71,21 @@ class SubsiteAdminFunctionalTest extends FunctionalTest
$this->getAndFollowAll('admin/pages/?SubsiteID=0'); $this->getAndFollowAll('admin/pages/?SubsiteID=0');
$this->assertEquals(0, $this->session()->get('SubsiteID'), 'Can access main site.'); $this->assertEquals(0, $this->session()->get('SubsiteID'), 'Can access main site.');
$this->assertContains('admin/pages', $this->mainSession->lastUrl(), 'Lands on the correct section'); $this->assertStringContainsString('admin/pages', $this->mainSession->lastUrl(), 'Lands on the correct section');
$subsite1 = $this->objFromFixture(Subsite::class, 'subsite1'); $subsite1 = $this->objFromFixture(Subsite::class, 'subsite1');
$this->getAndFollowAll("admin/pages/?SubsiteID={$subsite1->ID}"); $this->getAndFollowAll("admin/pages/?SubsiteID={$subsite1->ID}");
// Check the session manually, since the state is unique to the request, not this test // Check the session manually, since the state is unique to the request, not this test
$this->assertEquals($subsite1->ID, $this->session()->get('SubsiteID'), 'Can access other subsite.'); $this->assertEquals($subsite1->ID, $this->session()->get('SubsiteID'), 'Can access other subsite.');
$this->assertContains('admin/pages', $this->mainSession->lastUrl(), 'Lands on the correct section'); $this->assertStringContainsString('admin/pages', $this->mainSession->lastUrl(), 'Lands on the correct section');
$response = $this->getAndFollowAll('admin/subsite_xhr'); $response = $this->getAndFollowAll('admin/subsite_xhr');
$this->assertNotContains('Security/login', $this->mainSession->lastUrl(), 'SubsiteXHRController is reachable'); $this->assertStringNotContainsString(
'Security/login',
$this->mainSession->lastUrl(),
'SubsiteXHRController is reachable'
);
} }
public function testAdminIsRedirectedToObjectsSubsite() public function testAdminIsRedirectedToObjectsSubsite()
@ -92,7 +100,7 @@ class SubsiteAdminFunctionalTest extends FunctionalTest
$response = $this->get("admin/pages/edit/show/$subsite1Home->ID"); $response = $this->get("admin/pages/edit/show/$subsite1Home->ID");
$this->assertEquals(302, $response->getStatusCode()); $this->assertEquals(302, $response->getStatusCode());
$this->assertContains( $this->assertStringContainsString(
'admin/pages/edit/show/' . $subsite1Home->ID . '?SubsiteID=' . $subsite1Home->SubsiteID, 'admin/pages/edit/show/' . $subsite1Home->ID . '?SubsiteID=' . $subsite1Home->SubsiteID,
$response->getHeader('Location') $response->getHeader('Location')
); );
@ -102,7 +110,7 @@ class SubsiteAdminFunctionalTest extends FunctionalTest
$response = $this->get("admin/pages/edit/show/$subsite1Home->ID"); $response = $this->get("admin/pages/edit/show/$subsite1Home->ID");
$this->assertEquals(302, $response->getStatusCode()); $this->assertEquals(302, $response->getStatusCode());
$this->assertContains( $this->assertStringContainsString(
'admin/pages/edit/show/' . $subsite1Home->ID . '?SubsiteID=' . $subsite1Home->SubsiteID, 'admin/pages/edit/show/' . $subsite1Home->ID . '?SubsiteID=' . $subsite1Home->SubsiteID,
$response->getHeader('Location') $response->getHeader('Location')
); );
@ -122,15 +130,19 @@ class SubsiteAdminFunctionalTest extends FunctionalTest
$this->get('admin/pages/?SubsiteID=0'); $this->get('admin/pages/?SubsiteID=0');
$this->assertEquals(0, $this->session()->get('SubsiteID'), 'Can access main site.'); $this->assertEquals(0, $this->session()->get('SubsiteID'), 'Can access main site.');
$this->assertContains('admin/pages', $this->mainSession->lastUrl(), 'Lands on the correct section'); $this->assertStringContainsString('admin/pages', $this->mainSession->lastUrl(), 'Lands on the correct section');
$subsite1 = $this->objFromFixture(Subsite::class, 'subsite1'); $subsite1 = $this->objFromFixture(Subsite::class, 'subsite1');
$this->get("admin/pages/?SubsiteID={$subsite1->ID}"); $this->get("admin/pages/?SubsiteID={$subsite1->ID}");
$this->assertEquals($subsite1->ID, $this->session()->get('SubsiteID'), 'Can access other subsite.'); $this->assertEquals($subsite1->ID, $this->session()->get('SubsiteID'), 'Can access other subsite.');
$this->assertContains('admin/pages', $this->mainSession->lastUrl(), 'Lands on the correct section'); $this->assertStringContainsString('admin/pages', $this->mainSession->lastUrl(), 'Lands on the correct section');
$response = $this->get('admin/subsite_xhr'); $response = $this->get('admin/subsite_xhr');
$this->assertNotContains('Security/login', $this->mainSession->lastUrl(), 'SubsiteXHRController is reachable'); $this->assertStringNotContainsString(
'Security/login',
$this->mainSession->lastUrl(),
'SubsiteXHRController is reachable'
);
} }
/** /**
@ -146,7 +158,11 @@ class SubsiteAdminFunctionalTest extends FunctionalTest
// Check allowed URL. // Check allowed URL.
$this->getAndFollowAll("admin/pages/?SubsiteID={$subsite1->ID}"); $this->getAndFollowAll("admin/pages/?SubsiteID={$subsite1->ID}");
$this->assertEquals($subsite1->ID, $this->session()->get('SubsiteID'), 'Can access own subsite.'); $this->assertEquals($subsite1->ID, $this->session()->get('SubsiteID'), 'Can access own subsite.');
$this->assertContains('admin/pages', $this->mainSession->lastUrl(), 'Can access permitted section.'); $this->assertStringContainsString(
'admin/pages',
$this->mainSession->lastUrl(),
'Can access permitted section.'
);
// Check forbidden section in allowed subsite. // Check forbidden section in allowed subsite.
$this->getAndFollowAll("admin/assets/?SubsiteID={$subsite1->ID}"); $this->getAndFollowAll("admin/assets/?SubsiteID={$subsite1->ID}");
@ -172,10 +188,14 @@ class SubsiteAdminFunctionalTest extends FunctionalTest
$subsite1->ID, $subsite1->ID,
'Is redirected to first permitted subsite.' 'Is redirected to first permitted subsite.'
); );
$this->assertNotContains('Security/login', $this->mainSession->lastUrl(), 'Is not denied access'); $this->assertStringNotContainsString('Security/login', $this->mainSession->lastUrl(), 'Is not denied access');
// Check the standalone XHR controller. // Check the standalone XHR controller.
$response = $this->getAndFollowAll('admin/subsite_xhr'); $response = $this->getAndFollowAll('admin/subsite_xhr');
$this->assertNotContains('Security/login', $this->mainSession->lastUrl(), 'SubsiteXHRController is reachable'); $this->assertStringNotContainsString(
'Security/login',
$this->mainSession->lastUrl(),
'SubsiteXHRController is reachable'
);
} }
} }

View File

@ -3,30 +3,21 @@
namespace SilverStripe\Subsites\Tests; namespace SilverStripe\Subsites\Tests;
use SilverStripe\CMS\Controllers\CMSMain; use SilverStripe\CMS\Controllers\CMSMain;
use SilverStripe\Control\Director;
use SilverStripe\Control\Session;
use SilverStripe\Core\Config\Config; use SilverStripe\Core\Config\Config;
use SilverStripe\Security\Member; use SilverStripe\Dev\FunctionalTest;
use SilverStripe\Subsites\Model\Subsite; use SilverStripe\Subsites\Model\Subsite;
class SubsiteAdminTest extends BaseSubsiteTest class SubsiteAdminTest extends FunctionalTest
{ {
protected static $fixture_file = 'SubsiteTest.yml'; protected static $fixture_file = 'SubsiteTest.yml';
protected function setUp() protected function setUp(): void
{ {
parent::setUp(); parent::setUp();
Config::modify()->set(Subsite::class, 'write_hostmap', false); Config::modify()->set(Subsite::class, 'write_hostmap', false);
} }
protected function adminLoggedInSession()
{
return new Session([
'loggedInAs' => $this->idFromFixture(Member::class, 'admin')
]);
}
/** /**
* Test generation of the view * Test generation of the view
*/ */
@ -34,24 +25,20 @@ class SubsiteAdminTest extends BaseSubsiteTest
{ {
$subsite1ID = $this->objFromFixture(Subsite::class, 'domaintest1')->ID; $subsite1ID = $this->objFromFixture(Subsite::class, 'domaintest1')->ID;
// Open the admin area logged in as admin $this->logInAs('admin');
$response1 = Director::test('admin/subsites/', null, $this->adminLoggedInSession());
// Confirm that this URL gets you the entire page, with the edit form loaded // Confirm that this URL gets you the entire page, with the edit form loaded
$response2 = Director::test( $response = $this->get(
"admin/subsites/SilverStripe-Subsites-Model-Subsite/EditForm/field/" "admin/subsites/SilverStripe-Subsites-Model-Subsite/EditForm/field/"
."SilverStripe-Subsites-Model-Subsite/item/$subsite1ID/edit", ."SilverStripe-Subsites-Model-Subsite/item/$subsite1ID/edit"
null,
$this->adminLoggedInSession()
); );
$this->assertTrue( $this->assertTrue(
strpos($response2->getBody(), 'id="Form_ItemEditForm_ID"') !== false, strpos($response->getBody() ?? '', 'id="Form_ItemEditForm_ID"') !== false,
'Testing Form_ItemEditForm_ID exists' 'Testing Form_ItemEditForm_ID exists'
); );
$this->assertTrue(strpos($response2->getBody(), '<head') !== false, 'Testing <head> exists'); $this->assertTrue(strpos($response->getBody() ?? '', '<head') !== false, 'Testing <head> exists');
} }
/** /**
* Test that the main-site user with ADMIN permissions can access all subsites, regardless * Test that the main-site user with ADMIN permissions can access all subsites, regardless
* of whether he is in a subsite-specific group or not. * of whether he is in a subsite-specific group or not.

View File

@ -17,6 +17,8 @@ class SubsiteTest extends BaseSubsiteTest
{ {
protected static $fixture_file = 'SubsiteTest.yml'; protected static $fixture_file = 'SubsiteTest.yml';
protected $usesTransactions = false;
/** /**
* Original value of $_REQUEST * Original value of $_REQUEST
* *
@ -24,7 +26,7 @@ class SubsiteTest extends BaseSubsiteTest
*/ */
protected $origServer = []; protected $origServer = [];
protected function setUp() protected function setUp(): void
{ {
parent::setUp(); parent::setUp();
@ -36,7 +38,7 @@ class SubsiteTest extends BaseSubsiteTest
$this->origServer = $_SERVER; $this->origServer = $_SERVER;
} }
protected function tearDown() protected function tearDown(): void
{ {
$_SERVER = $this->origServer; $_SERVER = $this->origServer;
@ -191,12 +193,23 @@ class SubsiteTest extends BaseSubsiteTest
Subsite::getSubsiteIDForDomain('example.org'), Subsite::getSubsiteIDForDomain('example.org'),
'Exact matches without strict checking when not using www prefix' 'Exact matches without strict checking when not using www prefix'
); );
$this->assertEquals(
$subsite1->ID,
Subsite::getSubsiteIDForDomain('example.org:1123'),
'Exact matches without strict checking when not using www prefix and ignores port'
);
$this->assertEquals( $this->assertEquals(
$subsite1->ID, $subsite1->ID,
Subsite::getSubsiteIDForDomain('www.example.org'), Subsite::getSubsiteIDForDomain('www.example.org'),
'Matches without strict checking when using www prefix, ' 'Matches without strict checking when using www prefix, '
.'still matching first domain regardless of www prefix (falling back to subsite primary key ordering)' .'still matching first domain regardless of www prefix (falling back to subsite primary key ordering)'
); );
$this->assertEquals(
$subsite1->ID,
Subsite::getSubsiteIDForDomain('www.example.org:9923'),
'Matches without strict checking when using www prefix, '
.'still matching first domain without prefix (falling back to primary key ordering and ignoring port)'
);
$this->assertEquals( $this->assertEquals(
$subsite1->ID, $subsite1->ID,
Subsite::getSubsiteIDForDomain('www.example.com'), Subsite::getSubsiteIDForDomain('www.example.com'),
@ -215,6 +228,11 @@ class SubsiteTest extends BaseSubsiteTest
Subsite::getSubsiteIDForDomain('example.org'), Subsite::getSubsiteIDForDomain('example.org'),
'Matches with strict checking when not using www prefix' 'Matches with strict checking when not using www prefix'
); );
$this->assertEquals(
$subsite1->ID,
Subsite::getSubsiteIDForDomain('example.org:123'),
'Matches with strict checking when not using www prefix and ignores port'
);
$this->assertEquals( $this->assertEquals(
$subsite2->ID, // not 1 $subsite2->ID, // not 1
Subsite::getSubsiteIDForDomain('www.example.org'), Subsite::getSubsiteIDForDomain('www.example.org'),
@ -291,52 +309,48 @@ class SubsiteTest extends BaseSubsiteTest
/** /**
* Tests that Subsite and SubsiteDomain both respect http protocol correctly * Tests that Subsite and SubsiteDomain both respect http protocol correctly
*
* @param string $class Fixture class name
* @param string $identifier Fixture identifier
* @param bool $currentIsSecure Whether the current base URL should be secure
* @param string $expected The expected base URL for the subsite or subsite domain
* @dataProvider domainProtocolProvider
*/ */
public function testDomainProtocol() public function testDomainProtocol($class, $identifier, $currentIsSecure, $expected)
{ {
// domaintest2 has 'protocol' /** @var Subsite|SubsiteDomain $model */
$subsite2 = $this->objFromFixture(Subsite::class, 'domaintest2'); $model = $this->objFromFixture($class, $identifier);
$domain2a = $this->objFromFixture(SubsiteDomain::class, 'dt2a'); $protocol = $currentIsSecure ? 'https' : 'http';
$domain2b = $this->objFromFixture(SubsiteDomain::class, 'dt2b'); Config::modify()->set(Director::class, 'alternate_base_url', $protocol . '://www.mysite.com');
$this->assertSame($expected, $model->absoluteBaseURL());
}
// domaintest4 is 'https' (primary only) public function domainProtocolProvider()
$subsite4 = $this->objFromFixture(Subsite::class, 'domaintest4'); {
$domain4a = $this->objFromFixture(SubsiteDomain::class, 'dt4a'); return [
$domain4b = $this->objFromFixture(SubsiteDomain::class, 'dt4b'); // secondary domain is http only though [Subsite::class, 'domaintest2', false, 'http://two.mysite.com/'],
[SubsiteDomain::class, 'dt2a', false, 'http://two.mysite.com/'],
// domaintest5 is 'http' [SubsiteDomain::class, 'dt2b', false, 'http://subsite.mysite.com/'],
$subsite5 = $this->objFromFixture(Subsite::class, 'domaintest5'); [Subsite::class, 'domaintest4', false, 'https://www.primary.com/'],
$domain5a = $this->objFromFixture(SubsiteDomain::class, 'dt5'); [SubsiteDomain::class, 'dt4a', false, 'https://www.primary.com/'],
[SubsiteDomain::class, 'dt4b', false, 'http://www.secondary.com/'],
// Check protocol when current protocol is http:// [Subsite::class, 'domaintest5', false, 'http://www.tertiary.com/'],
Config::modify()->set(Director::class, 'alternate_base_url', 'http://www.mysite.com'); [SubsiteDomain::class, 'dt5', false, 'http://www.tertiary.com/'],
[Subsite::class, 'domaintest2', true, 'https://two.mysite.com/'],
$this->assertEquals('http://two.mysite.com/', $subsite2->absoluteBaseURL()); [SubsiteDomain::class, 'dt2a', true, 'https://two.mysite.com/'],
$this->assertEquals('http://two.mysite.com/', $domain2a->absoluteBaseURL()); [SubsiteDomain::class, 'dt2b', true, 'https://subsite.mysite.com/'],
$this->assertEquals('http://subsite.mysite.com/', $domain2b->absoluteBaseURL()); [Subsite::class, 'domaintest4', true, 'https://www.primary.com/'],
$this->assertEquals('https://www.primary.com/', $subsite4->absoluteBaseURL()); [SubsiteDomain::class, 'dt4a', true, 'https://www.primary.com/'],
$this->assertEquals('https://www.primary.com/', $domain4a->absoluteBaseURL()); [SubsiteDomain::class, 'dt4b', true, 'http://www.secondary.com/'],
$this->assertEquals('http://www.secondary.com/', $domain4b->absoluteBaseURL()); [Subsite::class, 'domaintest5', true, 'http://www.tertiary.com/'],
$this->assertEquals('http://www.tertiary.com/', $subsite5->absoluteBaseURL()); [SubsiteDomain::class, 'dt5', true, 'http://www.tertiary.com/'],
$this->assertEquals('http://www.tertiary.com/', $domain5a->absoluteBaseURL()); ];
// Check protocol when current protocol is https://
Config::modify()->set(Director::class, 'alternate_base_url', 'https://www.mysite.com');
$this->assertEquals('https://two.mysite.com/', $subsite2->absoluteBaseURL());
$this->assertEquals('https://two.mysite.com/', $domain2a->absoluteBaseURL());
$this->assertEquals('https://subsite.mysite.com/', $domain2b->absoluteBaseURL());
$this->assertEquals('https://www.primary.com/', $subsite4->absoluteBaseURL());
$this->assertEquals('https://www.primary.com/', $domain4a->absoluteBaseURL());
$this->assertEquals('http://www.secondary.com/', $domain4b->absoluteBaseURL());
$this->assertEquals('http://www.tertiary.com/', $subsite5->absoluteBaseURL());
$this->assertEquals('http://www.tertiary.com/', $domain5a->absoluteBaseURL());
} }
public function testAllSites() public function testAllSites()
{ {
$subsites = Subsite::all_sites(); $subsites = Subsite::all_sites();
$this->assertDOSEquals([ $this->assertListEquals([
['Title' => 'Main site'], ['Title' => 'Main site'],
['Title' => 'Template'], ['Title' => 'Template'],
['Title' => 'Subsite1 Template'], ['Title' => 'Subsite1 Template'],
@ -346,7 +360,8 @@ class SubsiteTest extends BaseSubsiteTest
['Title' => 'Test 3'], ['Title' => 'Test 3'],
['Title' => 'Test Non-SSL'], ['Title' => 'Test Non-SSL'],
['Title' => 'Test SSL'], ['Title' => 'Test SSL'],
['Title' => 'Test Vagrant VM on port 8080'] ['Title' => 'Test Vagrant VM on port 8080'],
['Title' => 'Locale subsite'],
], $subsites, 'Lists all subsites'); ], $subsites, 'Lists all subsites');
} }
@ -355,7 +370,7 @@ class SubsiteTest extends BaseSubsiteTest
$member = $this->objFromFixture(Member::class, 'subsite1member'); $member = $this->objFromFixture(Member::class, 'subsite1member');
$subsites = Subsite::all_accessible_sites(true, 'Main site', $member); $subsites = Subsite::all_accessible_sites(true, 'Main site', $member);
$this->assertDOSEquals([ $this->assertListEquals([
['Title' => 'Subsite1 Template'] ['Title' => 'Subsite1 Template']
], $subsites, 'Lists member-accessible sites.'); ], $subsites, 'Lists member-accessible sites.');
} }
@ -384,6 +399,7 @@ class SubsiteTest extends BaseSubsiteTest
$adminSiteTitles = $adminSites->column('Title'); $adminSiteTitles = $adminSites->column('Title');
sort($adminSiteTitles); sort($adminSiteTitles);
$this->assertEquals([ $this->assertEquals([
'Locale subsite',
'Subsite1 Template', 'Subsite1 Template',
'Subsite2 Template', 'Subsite2 Template',
'Template', 'Template',
@ -393,7 +409,7 @@ class SubsiteTest extends BaseSubsiteTest
'Test Non-SSL', 'Test Non-SSL',
'Test SSL', 'Test SSL',
'Test Vagrant VM on port 8080' 'Test Vagrant VM on port 8080'
], array_values($adminSiteTitles)); ], array_values($adminSiteTitles ?? []));
$member2Sites = Subsite::accessible_sites( $member2Sites = Subsite::accessible_sites(
'CMS_ACCESS_CMSMain', 'CMS_ACCESS_CMSMain',
@ -403,7 +419,7 @@ class SubsiteTest extends BaseSubsiteTest
); );
$member2SiteTitles = $member2Sites->column('Title'); $member2SiteTitles = $member2Sites->column('Title');
sort($member2SiteTitles); sort($member2SiteTitles);
$this->assertEquals('Subsite1 Template', $member2SiteTitles[0], 'Member can get to subsite via a group role'); $this->assertEquals('Subsite1 Template', $member2SiteTitles[1], 'Member can get to subsite via a group role');
} }
public function testhasMainSitePermission() public function testhasMainSitePermission()
@ -455,7 +471,7 @@ class SubsiteTest extends BaseSubsiteTest
$page1 = new Page(); $page1 = new Page();
$page1->Title = 'MyAwesomePage'; $page1->Title = 'MyAwesomePage';
$page1->write(); $page1->write();
$page1->doPublish(); $page1->publishRecursive();
$this->assertEquals($page1->SubsiteID, $subsite1->ID); $this->assertEquals($page1->SubsiteID, $subsite1->ID);
// duplicate // duplicate
@ -465,7 +481,7 @@ class SubsiteTest extends BaseSubsiteTest
$page2 = DataObject::get_one('Page', "\"Title\" = 'MyAwesomePage'"); $page2 = DataObject::get_one('Page', "\"Title\" = 'MyAwesomePage'");
$page2->Title = 'MyNewAwesomePage'; $page2->Title = 'MyNewAwesomePage';
$page2->write(); $page2->write();
$page2->doPublish(); $page2->publishRecursive();
// check change & check change has not affected subiste1 // check change & check change has not affected subiste1
$subsite1->activate(); $subsite1->activate();

View File

@ -18,6 +18,10 @@ SilverStripe\Subsites\Model\Subsite:
Title: 'Test Non-SSL' Title: 'Test Non-SSL'
domaintestVagrant: domaintestVagrant:
Title: 'Test Vagrant VM on port 8080' Title: 'Test Vagrant VM on port 8080'
subsitelocale:
Title: 'Locale subsite'
Language: 'nl_NL'
SilverStripe\Subsites\Model\SubsiteDomain: SilverStripe\Subsites\Model\SubsiteDomain:
subsite1: subsite1:
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsite1 SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsite1
@ -27,6 +31,11 @@ SilverStripe\Subsites\Model\SubsiteDomain:
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsite2 SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsite2
Domain: subsite2.* Domain: subsite2.*
Protocol: automatic Protocol: automatic
subsitelocale:
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsitelocale
Domain: subsitelocale.*
Protocol: automatic
IsPrimary: 1
dt1a: dt1a:
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.domaintest1 SubsiteID: =>SilverStripe\Subsites\Model\Subsite.domaintest1
Domain: one.example.org Domain: one.example.org
@ -118,6 +127,10 @@ Page:
Title: 'Contact Us (Subsite 2)' Title: 'Contact Us (Subsite 2)'
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsite2 SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsite2
URLSegment: contact-us URLSegment: contact-us
subsite_locale_about:
Title: 'About Locale'
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsitelocale
URLSegment: about
SilverStripe\Security\PermissionRoleCode: SilverStripe\Security\PermissionRoleCode:
roleCode1: roleCode1:
@ -159,6 +172,10 @@ SilverStripe\Security\Group:
Code: subsite1_group_via_role Code: subsite1_group_via_role
AccessAllSubsites: 1 AccessAllSubsites: 1
Roles: =>SilverStripe\Security\PermissionRole.role1 Roles: =>SilverStripe\Security\PermissionRole.role1
filetest:
Title: filetest
Code: filetest
AccessAllSubsites: 1
SilverStripe\Security\Permission: SilverStripe\Security\Permission:
admin: admin:
Code: ADMIN Code: ADMIN
@ -193,6 +210,9 @@ SilverStripe\Security\Permission:
adminsubsite1: adminsubsite1:
Code: ADMIN Code: ADMIN
GroupID: =>SilverStripe\Security\Group.subsite1admins GroupID: =>SilverStripe\Security\Group.subsite1admins
filetest:
Code: CMS_ACCESS_CMSMain
GroupID: =>SilverStripe\Security\Group.filetest
SilverStripe\Security\Member: SilverStripe\Security\Member:
admin: admin:
@ -222,7 +242,26 @@ SilverStripe\Security\Member:
subsite1member2: subsite1member2:
Email: subsite1member2@test.com Email: subsite1member2@test.com
Groups: =>SilverStripe\Security\Group.subsite1_group_via_role Groups: =>SilverStripe\Security\Group.subsite1_group_via_role
filetestyes:
Email: filetestyes@test.com
Groups: =>SilverStripe\Security\Group.filetest
filetestno:
Email: filetestno@test.com
SilverStripe\SiteConfig\SiteConfig: SilverStripe\SiteConfig\SiteConfig:
config: config:
CanCreateTopLevelType: LoggedInUsers CanCreateTopLevelType: LoggedInUsers
SilverStripe\Assets\File:
subsite1file:
Name: subsitefile.pdf
Title: subsitefile
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsite1
CanEditType: OnlyTheseUsers
EditorGroups: =>SilverStripe\Security\Group.filetest
mainsitefile:
Name: mainsitefile.pdf
Title: mainsitefile
SubsiteID: 0
CanEditType: OnlyTheseUsers
EditorGroups: =>SilverStripe\Security\Group.filetest

View File

@ -35,12 +35,13 @@ class SubsiteXHRControllerTest extends FunctionalTest
]); ]);
$this->assertEquals(200, $result->getStatusCode()); $this->assertEquals(200, $result->getStatusCode());
$this->assertEquals('text/json', $result->getHeader('Content-Type')); // SilverStripe 4.0-4.2: text/json. >=4.3: application/json
$this->assertStringContainsString('json', $result->getHeader('Content-Type'));
$body = $result->getBody(); $body = $result->getBody();
$this->assertContains('Main site', $body); $this->assertStringContainsString('Main site', $body);
$this->assertContains('Test 1', $body); $this->assertStringContainsString('Test 1', $body);
$this->assertContains('Test 2', $body); $this->assertStringContainsString('Test 2', $body);
$this->assertContains('Test 3', $body); $this->assertStringContainsString('Test 3', $body);
} }
} }

View File

@ -21,7 +21,7 @@ class SubsitesVirtualPageTest extends BaseSubsiteTest
'SubsitesVirtualPageTest.yml', 'SubsitesVirtualPageTest.yml',
]; ];
protected function setUp() protected function setUp(): void
{ {
parent::setUp(); parent::setUp();
@ -35,15 +35,15 @@ class SubsitesVirtualPageTest extends BaseSubsiteTest
$page = $this->objFromFixture(SiteTree::class, 'page1'); $page = $this->objFromFixture(SiteTree::class, 'page1');
$fromPath = __DIR__ . '/testscript-test-file.pdf'; $fromPath = __DIR__ . '/testscript-test-file.pdf';
$destPath = TestAssetStore::getLocalPath($file); $destPath = TestAssetStore::getLocalPath($file);
Filesystem::makeFolder(dirname($destPath)); Filesystem::makeFolder(dirname($destPath ?? ''));
copy($fromPath, $destPath); copy($fromPath ?? '', $destPath ?? '');
// Hack in site link tracking after the fact // Hack in site link tracking after the fact
$page->Content = '<p><img src="' . $file->getURL() . '" data-fileid="' . $file->ID . '" /></p>'; $page->Content = '<p><img src="' . $file->getURL() . '" data-fileid="' . $file->ID . '" /></p>';
$page->write(); $page->write();
} }
public function tearDown() protected function tearDown(): void
{ {
TestAssetStore::reset(); TestAssetStore::reset();
parent::tearDown(); parent::tearDown();
@ -311,4 +311,41 @@ class SubsitesVirtualPageTest extends BaseSubsiteTest
Versioned::prepopulate_versionnumber_cache(SiteTree::class, 'Live', [$p->ID]); Versioned::prepopulate_versionnumber_cache(SiteTree::class, 'Live', [$p->ID]);
} }
} }
public function testValidURLSegmentWithUniquePageAndNestedURLs()
{
SiteTree::config()->set('nested_urls', true);
$newPage = new SubsitesVirtualPage();
$newPage->Title = 'My new page';
$newPage->URLSegment = 'my-new-page';
$this->assertTrue($newPage->validURLSegment());
}
public function testValidURLSegmentWithExistingPageInSubsite()
{
$subsite1 = $this->objFromFixture(Subsite::class, 'subsite1');
Subsite::changeSubsite($subsite1->ID);
SiteTree::config()->set('nested_urls', false);
$similarContactUsPage = new SubsitesVirtualPage();
$similarContactUsPage->Title = 'Similar to Contact Us in Subsite 1';
$similarContactUsPage->URLSegment = 'contact-us';
$this->assertFalse($similarContactUsPage->validURLSegment());
}
public function testValidURLSegmentWithExistingPageInAnotherSubsite()
{
$subsite1 = $this->objFromFixture(Subsite::class, 'subsite1');
Subsite::changeSubsite($subsite1->ID);
$similarStaffPage = new SubsitesVirtualPage();
$similarStaffPage->Title = 'Similar to Staff page in main site';
$similarStaffPage->URLSegment = 'staff';
$this->assertFalse($similarStaffPage->validURLSegment());
}
} }