mirror of
https://github.com/silverstripe/silverstripe-subsites
synced 2024-09-27 20:06:08 +02:00
Compare commits
456 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
0a3056267c | ||
|
92363c4ca8 | ||
|
5025c32fc7 | ||
|
dcc39392cb | ||
|
d66e723a51 | ||
|
23b9db798d | ||
|
83600d758a | ||
|
dd3073f41a | ||
|
4e292b8e89 | ||
|
e020766095 | ||
|
4049f7a12d | ||
|
40ee7c76db | ||
|
178b521c54 | ||
|
7d1431df5e | ||
|
39de47167d | ||
|
3ec16bbe56 | ||
|
6ff4f336f7 | ||
|
a052bfd590 | ||
|
d1d1b139d0 | ||
|
b855a555c8 | ||
|
c847f3e1d8 | ||
|
86205a6286 | ||
|
413de7014c | ||
|
b4bf58e132 | ||
|
a249d46dcc | ||
|
701c6cd053 | ||
|
4006884aa7 | ||
|
aa9797a5f1 | ||
|
5e823223d7 | ||
|
04fe468f36 | ||
|
21240a7c83 | ||
|
339b0c855b | ||
|
d11124f576 | ||
|
412b2709d2 | ||
|
7d8909ac6d | ||
|
30b1f09af4 | ||
|
6770dedc2a | ||
|
67a21914eb | ||
|
901dbc8848 | ||
|
4308ac4316 | ||
|
73f3d15bfb | ||
|
a7e9e8dcdc | ||
|
5f489b1df9 | ||
|
f45ccead3c | ||
|
416f55ad03 | ||
|
ceaa915b77 | ||
|
aba286d8a3 | ||
|
a1ee94ce61 | ||
|
8ba7070b02 | ||
|
1e311e8668 | ||
|
5eb7e8a7a3 | ||
|
837ab70a8d | ||
|
800e8dc473 | ||
|
d13d73299a | ||
|
945e2bc370 | ||
|
9ad6c8f97e | ||
|
f566ab2b2a | ||
|
0338d41626 | ||
|
193866ec62 | ||
|
74b6cec374 | ||
|
b16495cf54 | ||
|
cdc37de076 | ||
|
b9d972b7fd | ||
|
6e1b504ff3 | ||
|
2bab73ee35 | ||
|
6969fe06d3 | ||
|
7860a03180 | ||
|
ed4663be9b | ||
|
acf9715c3b | ||
|
5c3d000b9b | ||
|
dfe1ba5c6a | ||
|
07aefb1982 | ||
|
2a16e1eefa | ||
|
cae9bd51ec | ||
|
5049a5e13c | ||
|
13ab072303 | ||
|
0179176b6c | ||
|
3a2ef2b3bc | ||
|
643b8d436a | ||
|
e306264740 | ||
|
f4ec177065 | ||
|
bd690ae62b | ||
|
3fb2e9a1e0 | ||
|
08e5af4f2b | ||
|
b5bd1e69b5 | ||
|
017804dbd4 | ||
|
683e7da208 | ||
|
212790ae29 | ||
|
5a4d613d8e | ||
|
27bae53017 | ||
|
fd10b868ec | ||
|
f1fce6f739 | ||
|
2175d44755 | ||
|
831c3c3cbe | ||
|
67af02c3b2 | ||
|
b32499ef31 | ||
|
5d015c7a96 | ||
|
38b356c256 | ||
|
5cf44c9d02 | ||
|
b1c1931d5d | ||
|
917640699d | ||
|
09abe2b2f2 | ||
|
9a7cdbbe2d | ||
|
ce63a9ed08 | ||
|
58f89801b0 | ||
|
9feef185dc | ||
|
2eb04ffa78 | ||
|
e73d622bdb | ||
|
d0054a1294 | ||
|
c4adf556cf | ||
|
644b9c8b90 | ||
|
81e6d0fe59 | ||
|
9abaca6d48 | ||
|
7d27abf2b1 | ||
|
001f44d73b | ||
|
ca01e2680a | ||
|
e41dc8b018 | ||
|
9655371276 | ||
|
67d10ec0cb | ||
|
4fdf2e24e3 | ||
|
614819a1d3 | ||
|
be3bcab715 | ||
|
135ae961bf | ||
|
eddbc90524 | ||
|
a4e99a2df5 | ||
|
77fafe6450 | ||
|
4249fffc0f | ||
|
b3bd51cb6c | ||
|
0275bb1eca | ||
|
483a867289 | ||
|
46a863557b | ||
|
19edb78756 | ||
|
5c4a655106 | ||
|
f6503822e8 | ||
|
2b26876596 | ||
|
900d04d94a | ||
|
1f51fcd909 | ||
|
33244fb430 | ||
|
b8576744a1 | ||
|
1595079156 | ||
|
9ee451f706 | ||
|
2a9f3ac0f6 | ||
|
fadd42910b | ||
|
c60acb3190 | ||
|
4d7641e16a | ||
|
3b8207d70c | ||
|
68c763da3e | ||
|
1e44e1d4ba | ||
|
2644083a2d | ||
|
ee961594bc | ||
|
e313f2ed5d | ||
|
536420ec68 | ||
|
f8e4804cc1 | ||
|
46653e2b07 | ||
|
fe20bc2907 | ||
|
ec327aee7c | ||
|
d1fc84d15c | ||
|
f003fb5e74 | ||
|
9dbdd992f7 | ||
|
1294671086 | ||
|
fbd98ff402 | ||
|
d9fcaa3319 | ||
|
da2e8fcc8b | ||
|
ce9dd1a856 | ||
|
59f6685e2a | ||
|
e510213c3e | ||
|
a0ecbdf4b6 | ||
|
e4fe534f10 | ||
|
3afdd01d41 | ||
|
2a35a5c70a | ||
|
1fa549886f | ||
|
2bf4812947 | ||
|
810ee63ea6 | ||
|
5e79abdbbc | ||
|
ff9997e0f2 | ||
|
bf7dd9c37b | ||
|
9199d509d6 | ||
|
bbfb93d50d | ||
|
7cc86199e7 | ||
|
1a5666182e | ||
|
2a6f7b5dfb | ||
|
6a8f15a194 | ||
|
e52fe41a23 | ||
|
1e458ef03d | ||
|
4323db52f0 | ||
|
bb226a0652 | ||
|
fa3f1fa767 | ||
|
313d22ffca | ||
|
70dc70f494 | ||
|
7681634cb2 | ||
|
6af985420f | ||
|
039a7a8c84 | ||
|
e8a72e1c33 | ||
|
5b8a0dbf13 | ||
|
8af796fa7a | ||
|
7f28c32427 | ||
|
6747b5ffe8 | ||
|
9ba1275b49 | ||
|
687e013793 | ||
|
f24fd60f14 | ||
|
87485e39f4 | ||
|
6e35807dc7 | ||
|
c177a9f640 | ||
|
d8088edfa9 | ||
|
bf2c81dce6 | ||
|
4b6804eaab | ||
|
7e1c2eb0aa | ||
|
191c63bd9b | ||
|
f635aa9811 | ||
|
c732c0c799 | ||
|
ffbcb9a0c8 | ||
|
d78e3c4662 | ||
|
75b851326d | ||
|
19f0162265 | ||
|
5370bc8af6 | ||
|
c43abb05c2 | ||
|
56bc57a5c5 | ||
|
de0bba0df2 | ||
|
153db12d42 | ||
|
f0a5dcf63c | ||
|
f19b82f029 | ||
|
21f4d80ef2 | ||
|
dc9d6de62d | ||
|
9038a166bd | ||
|
315621892d | ||
|
5285a68404 | ||
|
90bddc7e55 | ||
|
aae3e655c4 | ||
|
875f5519ed | ||
|
e8b292ecc5 | ||
|
b6ba567ee5 | ||
|
8222f619f8 | ||
|
1e5ee559b0 | ||
|
c30c0d94eb | ||
|
af06f803c5 | ||
|
032ea73154 | ||
|
631498fd1d | ||
|
5c801a4392 | ||
|
94e88b5be9 | ||
|
adc0395c25 | ||
|
cc064c43ce | ||
|
531a38e043 | ||
|
7115d060a6 | ||
|
418fa586a3 | ||
|
fe1c0a58c0 | ||
|
c7425ef23e | ||
|
67bb7e0028 | ||
|
0fcde6c0bb | ||
|
2f7dc7a7a8 | ||
|
30454dc04f | ||
|
6feec1de34 | ||
|
856496e09d | ||
|
7f77bb0c17 | ||
|
afd23be64c | ||
|
8398a361cd | ||
|
f8f39c135f | ||
|
24ee5a186e | ||
|
742c4a7fa3 | ||
|
299f12765c | ||
|
5279748931 | ||
|
ab24aa6d9f | ||
|
510d80683a | ||
|
73943afd5b | ||
|
34c0c4946c | ||
|
5404dafac1 | ||
|
e405dff946 | ||
|
809b1d9b85 | ||
|
098660e27d | ||
|
8b5f593999 | ||
|
db69c486a0 | ||
|
bb5f2c07e1 | ||
|
aa370f8df6 | ||
|
4f15894f86 | ||
|
cde41c4409 | ||
|
34b3fbf04d | ||
|
6fb0af0f93 | ||
|
8eee411d5a | ||
|
17427fd251 | ||
|
6bbf988fda | ||
|
73f5bcaecc | ||
|
17e0db47a1 | ||
|
d02b668885 | ||
|
a88ac5d05d | ||
|
ace068b93d | ||
|
4f174ffd6e | ||
|
a0836bed79 | ||
|
1dd1f2ce6c | ||
|
a201e7e953 | ||
|
4e297d920b | ||
|
1cdae5024a | ||
|
157db89dbf | ||
|
33622c440b | ||
|
b529947362 | ||
|
bcab2af5af | ||
|
95a71566f1 | ||
|
0ebf9509bf | ||
|
8fcfe55ba8 | ||
|
7bf430ba83 | ||
|
1dcba49e5c | ||
|
0157bd0c05 | ||
|
4a1f571f1c | ||
|
a2a86c6eb1 | ||
|
7301099241 | ||
|
c2a81f76ae | ||
|
fa6b80624c | ||
|
3996810fa9 | ||
|
62df0b45a5 | ||
|
4328f8aa35 | ||
|
9822807a58 | ||
|
c343542106 | ||
|
961185a6fd | ||
|
95e437d828 | ||
|
d9fb455d3d | ||
|
e83435b2b6 | ||
|
ea00ac3520 | ||
|
ed8efc2144 | ||
|
05953f1758 | ||
|
02a0895f4a | ||
|
0f5c6008e1 | ||
|
20e1c016f1 | ||
|
188b02df6b | ||
|
32385e580d | ||
|
784c3fca5e | ||
|
1704d1d033 | ||
|
9bccfffe4e | ||
|
b8ab1eda7e | ||
|
94755775ba | ||
|
83077ff78f | ||
|
60b259ebb5 | ||
|
1452e3600b | ||
|
774dee91d6 | ||
|
40b8e102d0 | ||
|
58b8476ede | ||
|
9d186d6e7a | ||
|
f7bdc570d1 | ||
|
2da5828e90 | ||
|
de2220754c | ||
|
aab69e0baa | ||
|
8c4a4e743c | ||
|
e331c51abb | ||
|
a7fee1729e | ||
|
56accd0856 | ||
|
adfa7257d7 | ||
|
19aeb8fd64 | ||
|
65f85faff6 | ||
|
1ac6e78bb3 | ||
|
793b46ede3 | ||
|
314f218306 | ||
|
b9582167c7 | ||
|
d934fbe08c | ||
|
1a9797c185 | ||
|
46bcffaa86 | ||
|
b0087b9035 | ||
|
c155855100 | ||
|
c620ff02f4 | ||
|
38031887a9 | ||
|
e129cafa94 | ||
|
5cf2d87e59 | ||
|
ce360aa383 | ||
|
c081de1202 | ||
|
7ffaf61aeb | ||
|
8d8ee14cc2 | ||
|
9fdc1d6607 | ||
|
60725e7b5c | ||
|
8be959f747 | ||
|
ddefef11d6 | ||
|
af6f499cac | ||
|
9a289a2e17 | ||
|
a13bf10184 | ||
|
03e52101bb | ||
|
5ec1c94410 | ||
|
438836bfcc | ||
|
32c45d0856 | ||
|
2b7c3d1f85 | ||
|
877f4f5f9d | ||
|
1975861aec | ||
|
c2484365cf | ||
|
49fbfcb459 | ||
|
21a8c56217 | ||
|
a4a1ab6a78 | ||
|
55c7240425 | ||
|
9673c881c1 | ||
|
fea1684f5c | ||
|
9d3c4506af | ||
|
849c0061e8 | ||
|
25754e1158 | ||
|
17010f39a3 | ||
|
abe1ac9fe6 | ||
|
4fc13b19f7 | ||
|
e7ad086641 | ||
|
76852594a8 | ||
|
e86cc55ba6 | ||
|
e03e7d9ce9 | ||
|
212e4797b0 | ||
|
6f1e1ab953 | ||
|
709cbfa2c8 | ||
|
f348f5fa97 | ||
|
997459caf3 | ||
|
492f437589 | ||
|
e2bdd5ca41 | ||
|
5d3af16aaf | ||
|
d8e72f3ac9 | ||
|
8586749318 | ||
|
b8f98323ae | ||
|
9862cf5ea6 | ||
|
d1e829697f | ||
|
c5f507b3f9 | ||
|
2295501587 | ||
|
ef602abe47 | ||
|
a49189ef58 | ||
|
a7ef6472ee | ||
|
f814534d93 | ||
|
b71e544820 | ||
|
fe6d93eaac | ||
|
8aa6512a49 | ||
|
80885a75d7 | ||
|
ff6d28b067 | ||
|
2c3b5bf5af | ||
|
e33a5b4cae | ||
|
2c84e627db | ||
|
5bb718224c | ||
|
77fb5c8d77 | ||
|
6fb0aab811 | ||
|
84551163a6 | ||
|
e8f5f58bb0 | ||
|
6afac5f9af | ||
|
3587bc666f | ||
|
7ee8bdbf72 | ||
|
0a93a42c2c | ||
|
686bf3bdda | ||
|
d803de205f | ||
|
8458c52429 | ||
|
8afbb6842f | ||
|
2803573a9c | ||
|
218382b984 | ||
|
4c17537d1d | ||
|
e241e70fbf | ||
|
a38c3a6ff3 | ||
|
0d7dc49d6b | ||
|
d45ff44edb | ||
|
2fc67a1e5d | ||
|
7ba4a20b75 | ||
|
933cfc8cfa | ||
|
9000bc47c5 | ||
|
415206e70b | ||
|
4b0440b15b | ||
|
a9ee70c5dd | ||
|
5e18e8dca3 | ||
|
b979b38694 | ||
|
9c9e0bfa94 | ||
|
0274b9effd | ||
|
80daa54dfc | ||
|
6f06dd565b | ||
|
c97b1140bf | ||
|
5604879146 | ||
|
d99785d773 |
@ -6,18 +6,17 @@
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 4
|
||||
indent_style = tab
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.yml]
|
||||
[*.{yml,feature}]
|
||||
indent_size = 2
|
||||
indent_style = space
|
||||
|
||||
[{.travis.yml,package.json}]
|
||||
[{.travis.yml,package.json,composer.json}]
|
||||
# The indent size used in the `package.json` file cannot be changed
|
||||
# https://github.com/npm/npm/pull/3180#issuecomment-16336516
|
||||
indent_size = 2
|
||||
|
7
.gitattributes
vendored
Normal file
7
.gitattributes
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
/tests export-ignore
|
||||
/docs export-ignore
|
||||
/.gitattributes export-ignore
|
||||
/.gitignore export-ignore
|
||||
/.travis.yml export-ignore
|
||||
/.scrutinizer.yml export-ignore
|
||||
/codecov.yml export-ignore
|
11
.github/workflows/ci.yml
vendored
Normal file
11
.github/workflows/ci.yml
vendored
Normal 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
|
16
.github/workflows/deploy-userhelp-docs.yml
vendored
Normal file
16
.github/workflows/deploy-userhelp-docs.yml
vendored
Normal 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
16
.github/workflows/dispatch-ci.yml
vendored
Normal 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
17
.github/workflows/keepalive.yml
vendored
Normal 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
|
34
.travis.yml
34
.travis.yml
@ -1,34 +0,0 @@
|
||||
# See https://github.com/silverstripe/silverstripe-travis-support for setup details
|
||||
|
||||
language: php
|
||||
|
||||
sudo: false
|
||||
|
||||
dist: precise
|
||||
|
||||
php:
|
||||
- 5.4
|
||||
|
||||
env:
|
||||
- DB=MYSQL CORE_RELEASE=3.5
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- php: 5.3
|
||||
env: DB=PGSQL CORE_RELEASE=3.4
|
||||
- php: 5.6
|
||||
env: DB=MYSQL CORE_RELEASE=3.6
|
||||
- php: 5.4
|
||||
env: DB=MYSQL CORE_RELEASE=3.6
|
||||
- php: 7.1
|
||||
env: DB=MYSQL CORE_RELEASE=3.6
|
||||
|
||||
before_script:
|
||||
- composer self-update || true
|
||||
- git clone git://github.com/silverstripe/silverstripe-travis-support.git ~/travis-support
|
||||
- php ~/travis-support/travis_setup.php --source `pwd` --target ~/builds/ss
|
||||
- cd ~/builds/ss
|
||||
- composer install
|
||||
|
||||
script:
|
||||
- vendor/bin/phpunit subsites/tests
|
9
.tx/config
Normal file
9
.tx/config
Normal file
@ -0,0 +1,9 @@
|
||||
[main]
|
||||
host = https://www.transifex.com
|
||||
|
||||
[o:silverstripe:p:silverstripe-subsites:r:master]
|
||||
file_filter = lang/<lang>.yml
|
||||
source_file = lang/en.yml
|
||||
source_lang = en
|
||||
type = YML
|
||||
|
20
.upgrade.yml
Normal file
20
.upgrade.yml
Normal file
@ -0,0 +1,20 @@
|
||||
mappings:
|
||||
SubsiteAdmin: SilverStripe\Subsites\Admin\SubsiteAdmin
|
||||
SubsiteXHRController: SilverStripe\Subsites\Controller\SubsiteXHRController
|
||||
CMSPageAddControllerExtension: SilverStripe\Subsites\Extensions\CMSPageAddControllerExtension
|
||||
ControllerSubsites: SilverStripe\Subsites\Extensions\ControllerSubsites
|
||||
ErrorPageSubsite: SilverStripe\Subsites\Extensions\ErrorPageSubsite
|
||||
FileSubsites: SilverStripe\Subsites\Extensions\FileSubsites
|
||||
GroupSubsites: SilverStripe\Subsites\Extensions\GroupSubsites
|
||||
LeftAndMainSubsites: SilverStripe\Subsites\Extensions\LeftAndMainSubsites
|
||||
SiteConfigSubsites: SilverStripe\Subsites\Extensions\SiteConfigSubsites
|
||||
SiteTreeSubsites: SilverStripe\Subsites\Extensions\SiteTreeSubsites
|
||||
SubsiteMenuExtension: SilverStripe\Subsites\Extensions\SubsiteMenuExtension
|
||||
GridFieldSubsiteDetailForm: SilverStripe\Subsites\Forms\GridFieldSubsiteDetailForm
|
||||
GridFieldSubsiteDetailForm_ItemRequest: SilverStripe\Subsites\Forms\GridFieldSubsiteDetailFormItemRequest
|
||||
SubsitesTreeDropdownField: SilverStripe\Subsites\Forms\SubsitesTreeDropdownField
|
||||
Subsite: SilverStripe\Subsites\Model\Subsite
|
||||
SubsiteDomain: SilverStripe\Subsites\Model\SubsiteDomain
|
||||
SubsitesVirtualPage: SilverStripe\Subsites\Pages\SubsitesVirtualPage
|
||||
SubsiteReportWrapper: SilverStripe\Subsites\Reports\SubsiteReportWrapper
|
||||
SubsiteCopyPagesTask: SilverStripe\Subsites\Tasks\SubsiteCopyPagesTask
|
12
CHANGELOG.md
12
CHANGELOG.md
@ -4,6 +4,18 @@ All notable changes to this project will be documented in this file.
|
||||
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [2.0.0 (unreleased)]
|
||||
|
||||
* Updating to be compatible with SilverStripe 4
|
||||
* Subsite specific theme is now added to default theme, as themes are now cascadable
|
||||
* Global subsite information moved to injectable `SubsiteState` singleton service
|
||||
* `FileExtension:::default_root_folders_global` converted to a configuration property
|
||||
* `Subsite::$check_is_public` converted to a configuration property
|
||||
* `Subsite::$strict_subdomain_matching` converted to a configuration property
|
||||
* `Subsite::$force_subsite` deprecated and will be removed in future - use `SubsiteState::singleton()->withState()` instead
|
||||
* `Subsite::$write_hostmap` converted to a configuration property
|
||||
* `Subsite::$allowed_themes` made protected
|
||||
|
||||
## [1.2.3]
|
||||
|
||||
* BUG Fix issue with urlsegment being renamed in subsites
|
||||
|
24
LICENSE
24
LICENSE
@ -1,24 +0,0 @@
|
||||
* Copyright (c) 2008, Silverstripe Ltd.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of the <organization> nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY Silverstripe Ltd. ``AS IS'' AND ANY
|
||||
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL Silverstripe Ltd. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
81
README.md
81
README.md
@ -1,6 +1,7 @@
|
||||
# Subsites Module
|
||||
|
||||
[![Build Status](https://secure.travis-ci.org/silverstripe/silverstripe-subsites.png?branch=master)](http://travis-ci.org/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/)
|
||||
|
||||
## Introduction
|
||||
|
||||
@ -8,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.
|
||||
|
||||
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
|
||||
templates can also be different.
|
||||
|
||||
@ -39,6 +40,11 @@ For user documentation please see:
|
||||
|
||||
### 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.
|
||||
* 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)
|
||||
@ -53,7 +59,7 @@ If more isolation of code, security, or performance is needed, then consider run
|
||||
|
||||
## Requirements
|
||||
|
||||
* SilverStripe 3.1
|
||||
* Silverstripe 4.x
|
||||
|
||||
## Installation
|
||||
|
||||
@ -63,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`
|
||||
* 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.
|
||||
* 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
|
||||
|
||||
@ -95,7 +101,55 @@ In some Browsers the SubsiteID is visible if you hover over the "Edit" link in t
|
||||
|
||||
### 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
|
||||
|
||||
@ -123,8 +177,8 @@ Include the current SubsiteID as a hidden field on getCMSFields, or updateCMSFie
|
||||
:::php
|
||||
public function getCMSFields() {
|
||||
$fields = parent::getCMSFields();
|
||||
if(class_exists('Subsite')){
|
||||
$fields->push(new HiddenField('SubsiteID','SubsiteID', Subsite::currentSubsiteID()));
|
||||
if(class_exists(Subsite::class)){
|
||||
$fields->push(new HiddenField('SubsiteID','SubsiteID', SubsiteState::singleton()->getSubsiteId()));
|
||||
}
|
||||
return $fields;
|
||||
}
|
||||
@ -138,8 +192,8 @@ To limit your admin gridfields to the current Subsite records, you can do someth
|
||||
$form = parent::getEditForm($id, $fields);
|
||||
|
||||
$gridField = $form->Fields()->fieldByName($this->sanitiseClassName($this->modelClass));
|
||||
if(class_exists('Subsite')){
|
||||
$list = $gridField->getList()->filter(array('SubsiteID'=>Subsite::currentSubsiteID()));
|
||||
if(class_exists(Subsite::class)){
|
||||
$list = $gridField->getList()->filter(['SubsiteID'=>SubsiteState::singleton()->getSubsiteId()]);
|
||||
$gridField->setList($list);
|
||||
}
|
||||
|
||||
@ -164,6 +218,14 @@ or by defining the subsiteCMSShowInMenu function in your admin:
|
||||
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
|
||||
|
||||
@ -189,4 +251,3 @@ for all subdomains:
|
||||
## Screenshots
|
||||
|
||||
![](docs/en/_images/subsites-module-adminscreenshot.png)
|
||||
|
||||
|
27
_config.php
27
_config.php
@ -1,27 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* The subsites module modifies the behaviour of the CMS - in the SiteTree and Group databases - to store information
|
||||
* about a number of sub-sites, rather than a single site.
|
||||
*/
|
||||
|
||||
SiteTree::add_extension('SiteTreeSubsites');
|
||||
ContentController::add_extension('ControllerSubsites');
|
||||
CMSPageAddController::add_extension('CMSPageAddControllerExtension');
|
||||
LeftAndMain::add_extension('LeftAndMainSubsites');
|
||||
LeftAndMain::add_extension('ControllerSubsites');
|
||||
|
||||
Group::add_extension('GroupSubsites');
|
||||
File::add_extension('FileSubsites');
|
||||
ErrorPage::add_extension('ErrorPageSubsite');
|
||||
SiteConfig::add_extension('SiteConfigSubsites');
|
||||
|
||||
SS_Report::add_excluded_reports('SubsiteReportWrapper');
|
||||
|
||||
//Display in cms menu
|
||||
AssetAdmin::add_extension('SubsiteMenuExtension');
|
||||
SecurityAdmin::add_extension('SubsiteMenuExtension');
|
||||
CMSMain::add_extension('SubsiteMenuExtension');
|
||||
CMSPagesController::add_extension('SubsiteMenuExtension');
|
||||
SubsiteAdmin::add_extension('SubsiteMenuExtension');
|
||||
CMSSettingsController::add_extension('SubsiteMenuExtension');
|
@ -1,10 +1,11 @@
|
||||
---
|
||||
Name: mysiteconfig
|
||||
After: 'framework/*','cms/*'
|
||||
Name: subsiteconfig
|
||||
After:
|
||||
- 'framework/*'
|
||||
---
|
||||
AssetAdmin:
|
||||
SilverStripe\AssetAdmin\Controller\AssetAdmin:
|
||||
treats_subsite_0_as_global: true
|
||||
|
||||
Director:
|
||||
rules:
|
||||
'SubsiteXHRController': 'SubsiteXHRController'
|
||||
SilverStripe\Reports\Report:
|
||||
excluded_reports:
|
||||
- SilverStripe\Subsites\Reports\SubsiteReportWrapper
|
||||
|
75
_config/extensions.yml
Normal file
75
_config/extensions.yml
Normal file
@ -0,0 +1,75 @@
|
||||
---
|
||||
Name: subsiteextensions
|
||||
After:
|
||||
- 'framework/*'
|
||||
---
|
||||
SilverStripe\CMS\Model\SiteTree:
|
||||
extensions:
|
||||
- SilverStripe\Subsites\Extensions\SiteTreeSubsites
|
||||
|
||||
SilverStripe\CMS\Controllers\ContentController:
|
||||
extensions:
|
||||
- SilverStripe\Subsites\Extensions\ControllerSubsites
|
||||
|
||||
SilverStripe\CMS\Controllers\CMSPageAddController:
|
||||
extensions:
|
||||
- SilverStripe\Subsites\Extensions\CMSPageAddControllerExtension
|
||||
|
||||
SilverStripe\Admin\LeftAndMain:
|
||||
extensions:
|
||||
- SilverStripe\Subsites\Extensions\LeftAndMainSubsites
|
||||
- SilverStripe\Subsites\Extensions\ControllerSubsites
|
||||
|
||||
SilverStripe\Security\Group:
|
||||
extensions:
|
||||
- SilverStripe\Subsites\Extensions\GroupSubsites
|
||||
|
||||
SilverStripe\Assets\File:
|
||||
extensions:
|
||||
- SilverStripe\Subsites\Extensions\FileSubsites
|
||||
|
||||
SilverStripe\AssetAdmin\Forms\FolderFormFactory:
|
||||
extensions:
|
||||
- SilverStripe\Subsites\Extensions\FolderFormFactoryExtension
|
||||
|
||||
SilverStripe\ErrorPage\ErrorPage:
|
||||
extensions:
|
||||
- SilverStripe\Subsites\Extensions\ErrorPageSubsite
|
||||
|
||||
SilverStripe\SiteConfig\SiteConfig:
|
||||
extensions:
|
||||
- SilverStripe\Subsites\Extensions\SiteConfigSubsites
|
||||
|
||||
SilverStripe\AssetAdmin\Controller\AssetAdmin:
|
||||
extensions:
|
||||
- SilverStripe\Subsites\Extensions\SubsiteMenuExtension
|
||||
|
||||
SilverStripe\Admin\SecurityAdmin:
|
||||
extensions:
|
||||
- SilverStripe\Subsites\Extensions\SubsiteMenuExtension
|
||||
|
||||
SilverStripe\CMS\Controllers\CMSMain:
|
||||
extensions:
|
||||
- SilverStripe\Subsites\Extensions\HintsCacheKeyExtension
|
||||
- SilverStripe\Subsites\Extensions\SubsiteMenuExtension
|
||||
|
||||
SilverStripe\CMS\Controllers\CMSPagesController:
|
||||
extensions:
|
||||
- SilverStripe\Subsites\Extensions\SubsiteMenuExtension
|
||||
|
||||
SilverStripe\Subsites\Admin\SubsiteAdmin:
|
||||
extensions:
|
||||
- SilverStripe\Subsites\Extensions\SubsiteMenuExtension
|
||||
|
||||
SilverStripe\SiteConfig\SiteConfigLeftAndMain:
|
||||
extensions:
|
||||
- SilverStripe\Subsites\Extensions\SubsiteMenuExtension
|
||||
|
||||
---
|
||||
Name: subsite-preview-elemental
|
||||
Only:
|
||||
classexists: DNADesign\Elemental\Models\BaseElement
|
||||
---
|
||||
DNADesign\Elemental\Models\BaseElement:
|
||||
extensions:
|
||||
- SilverStripe\Subsites\Extensions\BaseElementSubsites
|
9
_config/legacy.yml
Normal file
9
_config/legacy.yml
Normal file
@ -0,0 +1,9 @@
|
||||
---
|
||||
Name: subsites-legacy
|
||||
---
|
||||
|
||||
SilverStripe\ORM\DatabaseAdmin:
|
||||
classname_value_remapping:
|
||||
Subsite: SilverStripe\Subsites\Model\Subsite
|
||||
SubsiteDomain: SilverStripe\Subsites\Model\SubsiteDomain
|
||||
SubsitesVirtualPage: SilverStripe\Subsites\Pages\SubsitesVirtualPage
|
12
_config/middleware.yml
Normal file
12
_config/middleware.yml
Normal file
@ -0,0 +1,12 @@
|
||||
---
|
||||
Name: subsitesmiddleware
|
||||
After:
|
||||
- requestprocessors
|
||||
---
|
||||
SilverStripe\Core\Injector\Injector:
|
||||
SilverStripe\Control\Director:
|
||||
properties:
|
||||
Middlewares:
|
||||
SubsitesStateMiddleware: '%$SilverStripe\Subsites\Middleware\InitStateMiddleware'
|
||||
SilverStripe\Dev\Tasks\MigrateFileTask:
|
||||
class: SilverStripe\Subsites\Tasks\SubsiteMigrateFileTask
|
30
behat.yml
Normal file
30
behat.yml
Normal file
@ -0,0 +1,30 @@
|
||||
default:
|
||||
suites:
|
||||
subsites:
|
||||
paths:
|
||||
- '%paths.modules.subsites%/tests/behat/features'
|
||||
contexts:
|
||||
- SilverStripe\Framework\Tests\Behaviour\FeatureContext
|
||||
- SilverStripe\Framework\Tests\Behaviour\CmsFormsContext
|
||||
- SilverStripe\Framework\Tests\Behaviour\CmsUiContext
|
||||
- SilverStripe\BehatExtension\Context\BasicContext
|
||||
- SilverStripe\BehatExtension\Context\EmailContext
|
||||
- SilverStripe\CMS\Tests\Behaviour\LoginContext
|
||||
- SilverStripe\CMS\Tests\Behaviour\ThemeContext
|
||||
# Using asset-admin for fixture context to get iAttachTheFileToDropzone()
|
||||
- SilverStripe\AssetAdmin\Tests\Behat\Context\FixtureContext:
|
||||
# Note: double indent for args is intentional
|
||||
- '%paths.modules.subsites%/tests/behat/files/'
|
||||
|
||||
extensions:
|
||||
SilverStripe\BehatExtension\MinkExtension:
|
||||
default_session: facebook_web_driver
|
||||
javascript_session: facebook_web_driver
|
||||
facebook_web_driver:
|
||||
browser: chrome
|
||||
wd_host: "http://127.0.0.1:9515" #chromedriver port
|
||||
browser_name: chrome
|
||||
|
||||
SilverStripe\BehatExtension\Extension:
|
||||
screenshot_path: '%paths.base%/artifacts/screenshots'
|
||||
bootstrap_file: "vendor/silverstripe/cms/tests/behat/serve-bootstrap.php"
|
82
client/css/LeftAndMain_Subsites.css
Normal file
82
client/css/LeftAndMain_Subsites.css
Normal file
@ -0,0 +1,82 @@
|
||||
/**
|
||||
* Styling for the subsite actions section in the CMS
|
||||
*/
|
||||
#SubsiteActions {
|
||||
position: absolute;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 350px;
|
||||
text-align: right;
|
||||
margin-right: 130px;
|
||||
height: 51px;
|
||||
border-bottom: 3px solid #d4d0c8;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
#SubsiteActions fieldset {
|
||||
padding: 3px;
|
||||
border-style: none;
|
||||
margin-top: 1px;
|
||||
background: none;
|
||||
}
|
||||
|
||||
#SubsiteActions fieldset span {
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
.cms-menu .cms-subsites {
|
||||
padding: 3px 0 15px;
|
||||
}
|
||||
|
||||
.cms-menu .cms-subsites .field.dropdown {
|
||||
margin: 0 10px;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.cms-menu.collapsed .cms-subsites {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.cms-menu .cms-subsites .dropdown span {
|
||||
padding-left: 5px;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.cms-subsites .chosen-container-single .chosen-single div b {
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
#AddSubsiteLink {
|
||||
display: block;
|
||||
font-size: 80%;
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
#Form_AddSubsiteForm .field {
|
||||
margin-left: 100px;
|
||||
}
|
||||
|
||||
#Form_AddSubsiteForm label.left {
|
||||
float: left;
|
||||
width: 100px;
|
||||
margin-left: -100px;
|
||||
}
|
||||
|
||||
body.SubsiteAdmin .right form #URL .fieldgroup * {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.cms-add-form #PageType li .class-SubsitesVirtualPage {
|
||||
background-position: 0 -32px !important;
|
||||
}
|
||||
|
||||
.subsites-move-dropdown {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.editor__details .subsites-move-dropdown,
|
||||
#Form_fileEditorForm.subsites-move-dropdown {
|
||||
display: block;
|
||||
}
|
@ -1,6 +1,5 @@
|
||||
/*jslint browser: true, nomen: true*/
|
||||
/*global $, window, jQuery*/
|
||||
|
||||
(function($) {
|
||||
'use strict';
|
||||
$.entwine('ss', function($) {
|
||||
@ -18,7 +17,7 @@
|
||||
*/
|
||||
$('.cms-container .cms-menu-list li a').entwine({
|
||||
onclick: function(e) {
|
||||
$('.cms-container').loadFragment('SubsiteXHRController', 'SubsiteList');
|
||||
$('.cms-container').loadFragment('admin/subsite_xhr', 'SubsiteList');
|
||||
this._super(e);
|
||||
}
|
||||
});
|
||||
@ -28,20 +27,17 @@
|
||||
*/
|
||||
$('.cms-container .SubsiteAdmin .cms-edit-form fieldset.ss-gridfield').entwine({
|
||||
onreload: function(e) {
|
||||
$('.cms-container').loadFragment('SubsiteXHRController', 'SubsiteList');
|
||||
$('.cms-container').loadFragment('admin/subsite_xhr', 'SubsiteList');
|
||||
this._super(e);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Reload subsites dropdown when subsites are added or names are modified
|
||||
*/
|
||||
$('.cms-container .cms-content-fields .subsite-model').entwine({
|
||||
$('.cms-container .tab.subsite-model').entwine({
|
||||
onadd: function(e) {
|
||||
$('.cms-container').loadFragment('SubsiteXHRController', 'SubsiteList');
|
||||
$('.cms-container').loadFragment('admin/subsite_xhr', 'SubsiteList');
|
||||
this._super(e);
|
||||
}
|
||||
});
|
||||
@ -90,26 +86,6 @@
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Binding a visibility toggle anchor to a longer list of checkboxes.
|
||||
* Hidden by default, unless either the toggle checkbox, or any of the
|
||||
* actual value checkboxes are selected.
|
||||
*/
|
||||
$('#PageTypeBlacklist').entwine({
|
||||
onmatch: function() {
|
||||
var hasLimits=Boolean($('#PageTypeBlacklist').find('input:checked').length);
|
||||
jQuery('#PageTypeBlacklist').toggle(hasLimits);
|
||||
|
||||
|
||||
//Bind listener
|
||||
$('a#PageTypeBlacklistToggle').click(function(e) {
|
||||
jQuery('#PageTypeBlacklist').toggle();
|
||||
e.stopPropagation();
|
||||
return false;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$('.cms-edit-form input[name=action_copytosubsite]').entwine({
|
||||
onclick: function(e) {
|
||||
var form = this.closest('form');
|
||||
@ -125,7 +101,7 @@
|
||||
|
||||
/**
|
||||
* 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
|
||||
* violations and SSL cert problems that come up when iframing content from a different URL.
|
34
client/javascript/SubsitesTreeDropdownField.js
Normal file
34
client/javascript/SubsitesTreeDropdownField.js
Normal file
@ -0,0 +1,34 @@
|
||||
(function($) {
|
||||
$.entwine('ss', function($) {
|
||||
/**
|
||||
* Choose a subsite from which to select pages.
|
||||
* Needs to clear tree dropdowns in case selection is changed.
|
||||
*/
|
||||
$('select.subsitestreedropdownfield-chooser').entwine({
|
||||
onchange: function() {
|
||||
// TODO Data binding between two fields
|
||||
const name = this.attr('name').replace('_SubsiteID', '');
|
||||
let field = $('#Form_EditForm_' + name).first();
|
||||
field.setValue(0);
|
||||
field.refresh();
|
||||
field.trigger('change');
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Add selected subsite from separate dropdown to the request parameters
|
||||
* before asking for the tree.
|
||||
*/
|
||||
$('.TreeDropdownField.SubsitesTreeDropdownField').entwine({
|
||||
getAttributes() {
|
||||
const fieldName = this.attr('id').replace('Form_EditForm_', '');
|
||||
const subsiteID = $('#Form_EditForm_' + fieldName + '_SubsiteID option:selected').val();
|
||||
|
||||
let attributes = this._super();
|
||||
attributes.data.urlTree += "?" + fieldName + "_SubsiteID=" + subsiteID;
|
||||
attributes.data.cacheKey = attributes.data.cacheKey.substring(0, 19) + "_" + subsiteID;
|
||||
return attributes;
|
||||
}
|
||||
});
|
||||
});
|
||||
})(jQuery);
|
1
code-of-conduct.md
Normal file
1
code-of-conduct.md
Normal file
@ -0,0 +1 @@
|
||||
When having discussions about this module in issues or pull request please adhere to the [SilverStripe Community Code of Conduct](https://docs.silverstripe.org/en/contributing/code_of_conduct).
|
@ -1,33 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Admin interface to manage and create {@link Subsite} instances.
|
||||
*
|
||||
* @package subsites
|
||||
*/
|
||||
class SubsiteAdmin extends ModelAdmin
|
||||
{
|
||||
private static $managed_models = array('Subsite');
|
||||
|
||||
private static $url_segment = 'subsites';
|
||||
|
||||
private static $menu_title = "Subsites";
|
||||
|
||||
private static $menu_icon = "subsites/images/subsites.png";
|
||||
|
||||
public $showImportForm=false;
|
||||
|
||||
private static $tree_class = 'Subsite';
|
||||
|
||||
public function getEditForm($id = null, $fields = null)
|
||||
{
|
||||
$form = parent::getEditForm($id, $fields);
|
||||
|
||||
$grid=$form->Fields()->dataFieldByName('Subsite');
|
||||
if ($grid) {
|
||||
$grid->getConfig()->removeComponentsByType('GridFieldDetailForm');
|
||||
$grid->getConfig()->addComponent(new GridFieldSubsiteDetailForm());
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
}
|
@ -1,193 +0,0 @@
|
||||
<?php
|
||||
class SubsitesVirtualPage extends VirtualPage
|
||||
{
|
||||
private static $description = 'Displays the content of a page on another subsite';
|
||||
|
||||
private static $db = array(
|
||||
'CustomMetaTitle' => 'Varchar(255)',
|
||||
'CustomMetaKeywords' => 'Varchar(255)',
|
||||
'CustomMetaDescription' => 'Text',
|
||||
'CustomExtraMeta' => 'HTMLText'
|
||||
);
|
||||
|
||||
public function getCMSFields()
|
||||
{
|
||||
$fields = parent::getCMSFields();
|
||||
|
||||
$subsites = DataObject::get('Subsite');
|
||||
if (!$subsites) {
|
||||
$subsites = new ArrayList();
|
||||
} else {
|
||||
$subsites=ArrayList::create($subsites->toArray());
|
||||
}
|
||||
|
||||
$subsites->push(new ArrayData(array('Title' => 'Main site', 'ID' => 0)));
|
||||
|
||||
$fields->addFieldToTab(
|
||||
'Root.Main',
|
||||
DropdownField::create(
|
||||
"CopyContentFromID_SubsiteID",
|
||||
_t('SubsitesVirtualPage.SubsiteField', "Subsite"),
|
||||
$subsites->map('ID', 'Title')
|
||||
)->addExtraClass('subsitestreedropdownfield-chooser no-change-track'),
|
||||
'CopyContentFromID'
|
||||
);
|
||||
|
||||
// Setup the linking to the original page.
|
||||
$pageSelectionField = new SubsitesTreeDropdownField(
|
||||
"CopyContentFromID",
|
||||
_t('VirtualPage.CHOOSE', "Choose a page to link to"),
|
||||
"SiteTree",
|
||||
"ID",
|
||||
"MenuTitle"
|
||||
);
|
||||
|
||||
if (Controller::has_curr() && Controller::curr()->getRequest()) {
|
||||
$subsiteID = Controller::curr()->getRequest()->requestVar('CopyContentFromID_SubsiteID');
|
||||
$pageSelectionField->setSubsiteID($subsiteID);
|
||||
}
|
||||
$fields->replaceField('CopyContentFromID', $pageSelectionField);
|
||||
|
||||
// Create links back to the original object in the CMS
|
||||
if ($this->CopyContentFromID) {
|
||||
$editLink = "admin/pages/edit/show/$this->CopyContentFromID/?SubsiteID=" . $this->CopyContentFrom()->SubsiteID;
|
||||
$linkToContent = "
|
||||
<a class=\"cmsEditlink\" href=\"$editLink\">" .
|
||||
_t('VirtualPage.EDITCONTENT', 'Click here to edit the content') .
|
||||
"</a>";
|
||||
$fields->removeByName("VirtualPageContentLinkLabel");
|
||||
$fields->addFieldToTab(
|
||||
"Root.Main",
|
||||
$linkToContentLabelField = new LabelField('VirtualPageContentLinkLabel', $linkToContent),
|
||||
'Title'
|
||||
);
|
||||
$linkToContentLabelField->setAllowHTML(true);
|
||||
}
|
||||
|
||||
|
||||
$fields->addFieldToTab(
|
||||
'Root.Main',
|
||||
TextField::create(
|
||||
'CustomMetaTitle',
|
||||
$this->fieldLabel('CustomMetaTitle')
|
||||
)->setDescription(_t('SubsitesVirtualPage.OverrideNote', 'Overrides inherited value from the source')),
|
||||
'MetaTitle'
|
||||
);
|
||||
$fields->addFieldToTab(
|
||||
'Root.Main',
|
||||
TextareaField::create(
|
||||
'CustomMetaKeywords',
|
||||
$this->fieldLabel('CustomMetaTitle')
|
||||
)->setDescription(_t('SubsitesVirtualPage.OverrideNote')),
|
||||
'MetaKeywords'
|
||||
);
|
||||
$fields->addFieldToTab(
|
||||
'Root.Main',
|
||||
TextareaField::create(
|
||||
'CustomMetaDescription',
|
||||
$this->fieldLabel('CustomMetaTitle')
|
||||
)->setDescription(_t('SubsitesVirtualPage.OverrideNote')),
|
||||
'MetaDescription'
|
||||
);
|
||||
$fields->addFieldToTab(
|
||||
'Root.Main',
|
||||
TextField::create(
|
||||
'CustomExtraMeta',
|
||||
$this->fieldLabel('CustomMetaTitle')
|
||||
)->setDescription(_t('SubsitesVirtualPage.OverrideNote')),
|
||||
'ExtraMeta'
|
||||
);
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
public function fieldLabels($includerelations = true)
|
||||
{
|
||||
$labels = parent::fieldLabels($includerelations);
|
||||
$labels['CustomMetaTitle'] = _t('Subsite.CustomMetaTitle', 'Title');
|
||||
$labels['CustomMetaKeywords'] = _t('Subsite.CustomMetaKeywords', 'Keywords');
|
||||
$labels['CustomMetaDescription'] = _t('Subsite.CustomMetaDescription', 'Description');
|
||||
$labels['CustomExtraMeta'] = _t('Subsite.CustomExtraMeta', 'Custom Meta Tags');
|
||||
|
||||
return $labels;
|
||||
}
|
||||
|
||||
public function getCopyContentFromID_SubsiteID()
|
||||
{
|
||||
return ($this->CopyContentFromID) ? (int)$this->CopyContentFrom()->SubsiteID : (int)Session::get('SubsiteID');
|
||||
}
|
||||
|
||||
public function getVirtualFields()
|
||||
{
|
||||
$fields = parent::getVirtualFields();
|
||||
foreach ($fields as $k => $v) {
|
||||
if ($v == 'SubsiteID') {
|
||||
unset($fields[$k]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (self::$db as $field => $type) {
|
||||
if (in_array($field, $fields)) {
|
||||
unset($fields[array_search($field, $fields)]);
|
||||
}
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
public function syncLinkTracking()
|
||||
{
|
||||
$oldState = Subsite::$disable_subsite_filter;
|
||||
Subsite::$disable_subsite_filter = true;
|
||||
if ($this->CopyContentFromID) {
|
||||
$this->HasBrokenLink = DataObject::get_by_id('SiteTree', $this->CopyContentFromID) ? false : true;
|
||||
}
|
||||
Subsite::$disable_subsite_filter = $oldState;
|
||||
}
|
||||
|
||||
public function onBeforeWrite()
|
||||
{
|
||||
parent::onBeforeWrite();
|
||||
|
||||
if ($this->CustomMetaTitle) {
|
||||
$this->MetaTitle = $this->CustomMetaTitle;
|
||||
} else {
|
||||
$this->MetaTitle = $this->ContentSource()->MetaTitle ? $this->ContentSource()->MetaTitle : $this->MetaTitle;
|
||||
}
|
||||
if ($this->CustomMetaKeywords) {
|
||||
$this->MetaKeywords = $this->CustomMetaKeywords;
|
||||
} else {
|
||||
$this->MetaKeywords = $this->ContentSource()->MetaKeywords ? $this->ContentSource()->MetaKeywords : $this->MetaKeywords;
|
||||
}
|
||||
if ($this->CustomMetaDescription) {
|
||||
$this->MetaDescription = $this->CustomMetaDescription;
|
||||
} else {
|
||||
$this->MetaDescription = $this->ContentSource()->MetaDescription ? $this->ContentSource()->MetaDescription : $this->MetaDescription;
|
||||
}
|
||||
if ($this->CustomExtraMeta) {
|
||||
$this->ExtraMeta = $this->CustomExtraMeta;
|
||||
} else {
|
||||
$this->ExtraMeta = $this->ContentSource()->ExtraMeta ? $this->ContentSource()->ExtraMeta : $this->ExtraMeta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SubsitesVirtualPage_Controller extends VirtualPage_Controller
|
||||
{
|
||||
public function reloadContent()
|
||||
{
|
||||
$this->failover->copyFrom($this->failover->CopyContentFrom());
|
||||
$this->failover->write();
|
||||
return;
|
||||
}
|
||||
|
||||
public function init()
|
||||
{
|
||||
$origDisableSubsiteFilter = Subsite::$disable_subsite_filter;
|
||||
Subsite::$disable_subsite_filter = true;
|
||||
|
||||
parent::init();
|
||||
|
||||
Subsite::$disable_subsite_filter = $origDisableSubsiteFilter;
|
||||
}
|
||||
}
|
@ -1,8 +0,0 @@
|
||||
<?php
|
||||
class CMSPageAddControllerExtension extends Extension
|
||||
{
|
||||
public function updatePageOptions(&$fields)
|
||||
{
|
||||
$fields->push(new HiddenField('SubsiteID', 'SubsiteID', Subsite::currentSubsiteID()));
|
||||
}
|
||||
}
|
@ -1,43 +0,0 @@
|
||||
<?php
|
||||
class ErrorPageSubsite extends DataExtension
|
||||
{
|
||||
/**
|
||||
* Alter file path to generated a static (static) error page file to handle error page template on different sub-sites
|
||||
*
|
||||
* @see Error::get_filepath_for_errorcode()
|
||||
*
|
||||
* FIXME since {@link Subsite::currentSubsite()} partly relies on Session, viewing other sub-site (including main site) between
|
||||
* opening ErrorPage in the CMS and publish ErrorPage causes static error page to get generated incorrectly.
|
||||
*/
|
||||
public function alternateFilepathForErrorcode($statusCode, $locale = null)
|
||||
{
|
||||
$static_filepath = Config::inst()->get($this->owner->ClassName, 'static_filepath');
|
||||
$subdomainPart = "";
|
||||
|
||||
// Try to get current subsite from session
|
||||
$subsite = Subsite::currentSubsite(false);
|
||||
|
||||
// since this function is called from Page class before the controller is created, we have to get subsite from domain instead
|
||||
if (!$subsite) {
|
||||
$subsiteID = Subsite::getSubsiteIDForDomain();
|
||||
if ($subsiteID != 0) {
|
||||
$subsite = DataObject::get_by_id("Subsite", $subsiteID);
|
||||
} else {
|
||||
$subsite = null;
|
||||
}
|
||||
}
|
||||
|
||||
if ($subsite) {
|
||||
$subdomain = $subsite->domain();
|
||||
$subdomainPart = "-{$subdomain}";
|
||||
}
|
||||
|
||||
if (singleton('SiteTree')->hasExtension('Translatable') && $locale && $locale != Translatable::default_locale()) {
|
||||
$filepath = $static_filepath . "/error-{$statusCode}-{$locale}{$subdomainPart}.html";
|
||||
} else {
|
||||
$filepath = $static_filepath . "/error-{$statusCode}{$subdomainPart}.html";
|
||||
}
|
||||
|
||||
return $filepath;
|
||||
}
|
||||
}
|
@ -1,141 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Extension for the File object to add subsites support
|
||||
*
|
||||
* @package subsites
|
||||
*/
|
||||
class FileSubsites extends DataExtension
|
||||
{
|
||||
// If this is set to true, all folders created will be default be
|
||||
// considered 'global', unless set otherwise
|
||||
public static $default_root_folders_global = false;
|
||||
|
||||
private static $has_one=array(
|
||||
'Subsite' => 'Subsite',
|
||||
);
|
||||
|
||||
/**
|
||||
* Amends the CMS tree title for folders in the Files & Images section.
|
||||
* Prefixes a '* ' to the folders that are accessible from all subsites.
|
||||
*/
|
||||
public function alternateTreeTitle()
|
||||
{
|
||||
if ($this->owner->SubsiteID == 0) {
|
||||
return " * " . $this->owner->Title;
|
||||
} else {
|
||||
return $this->owner->Title;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add subsites-specific fields to the folder editor.
|
||||
*/
|
||||
public function updateCMSFields(FieldList $fields)
|
||||
{
|
||||
if ($this->owner instanceof Folder) {
|
||||
$sites = Subsite::accessible_sites('CMS_ACCESS_AssetAdmin');
|
||||
$values = array();
|
||||
$values[0] = _t('FileSubsites.AllSitesDropdownOpt', 'All sites');
|
||||
foreach ($sites as $site) {
|
||||
$values[$site->ID] = $site->Title;
|
||||
}
|
||||
ksort($values);
|
||||
if ($sites) {
|
||||
//Dropdown needed to move folders between subsites
|
||||
$dropdown = new DropdownField(
|
||||
'SubsiteID',
|
||||
_t('FileSubsites.SubsiteFieldLabel', 'Subsite'),
|
||||
$values
|
||||
);
|
||||
$dropdown->addExtraClass('subsites-move-dropdown');
|
||||
$fields->push($dropdown);
|
||||
$fields->push(new LiteralField(
|
||||
'Message',
|
||||
'<p class="message notice">'.
|
||||
_t('ASSETADMIN.SUBSITENOTICE', 'Folders and files created in the main site are accessible by all subsites.')
|
||||
.'</p>'
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update any requests to limit the results to the current site
|
||||
*/
|
||||
public function augmentSQL(SQLQuery &$query)
|
||||
{
|
||||
if (Subsite::$disable_subsite_filter) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 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 ManyManyList::removeAll() changing the from table after this function is called there isn't much of a choice
|
||||
|
||||
$from = $query->getFrom();
|
||||
if (isset($from['SiteTree_ImageTracking']) || $query->filtersOnID()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$subsiteID = (int) Subsite::currentSubsiteID();
|
||||
|
||||
// The foreach is an ugly way of getting the first key :-)
|
||||
foreach ($query->getFrom() as $tableName => $info) {
|
||||
$where = "\"$tableName\".\"SubsiteID\" IN (0, $subsiteID)";
|
||||
$query->addWhere($where);
|
||||
break;
|
||||
}
|
||||
|
||||
$sect=array_values($query->getSelect());
|
||||
$isCounting = strpos($sect[0], 'COUNT') !== false;
|
||||
|
||||
// Ordering when deleting or counting doesn't apply
|
||||
if (!$isCounting) {
|
||||
$query->addOrderBy("\"SubsiteID\"");
|
||||
}
|
||||
}
|
||||
|
||||
public function onBeforeWrite()
|
||||
{
|
||||
if (!$this->owner->ID && !$this->owner->SubsiteID) {
|
||||
if (self::$default_root_folders_global) {
|
||||
$this->owner->SubsiteID = 0;
|
||||
} else {
|
||||
$this->owner->SubsiteID = Subsite::currentSubsiteID();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function onAfterUpload()
|
||||
{
|
||||
// If we have a parent, use it's subsite as our subsite
|
||||
if ($this->owner->Parent()) {
|
||||
$this->owner->SubsiteID = $this->owner->Parent()->SubsiteID;
|
||||
} else {
|
||||
$this->owner->SubsiteID = Subsite::currentSubsiteID();
|
||||
}
|
||||
$this->owner->write();
|
||||
}
|
||||
|
||||
public function canEdit($member = null)
|
||||
{
|
||||
// Check the CMS_ACCESS_SecurityAdmin privileges on the subsite that owns this group
|
||||
$subsiteID = Session::get('SubsiteID');
|
||||
if ($subsiteID&&$subsiteID == $this->owner->SubsiteID) {
|
||||
return true;
|
||||
} else {
|
||||
Session::set('SubsiteID', $this->owner->SubsiteID);
|
||||
$access = Permission::check(array('CMS_ACCESS_AssetAdmin', 'CMS_ACCESS_LeftAndMain'));
|
||||
Session::set('SubsiteID', $subsiteID);
|
||||
|
||||
return $access;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a piece of text to keep DataObject cache keys appropriately specific
|
||||
*/
|
||||
public function cacheKeyComponent()
|
||||
{
|
||||
return 'subsite-'.Subsite::currentSubsiteID();
|
||||
}
|
||||
}
|
@ -1,195 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Extension for the Group object to add subsites support
|
||||
*
|
||||
* @package subsites
|
||||
*/
|
||||
class GroupSubsites extends DataExtension implements PermissionProvider
|
||||
{
|
||||
private static $db = array(
|
||||
'AccessAllSubsites' => 'Boolean'
|
||||
);
|
||||
|
||||
private static $many_many = array(
|
||||
'Subsites' => 'Subsite'
|
||||
);
|
||||
|
||||
private static $defaults = array(
|
||||
'AccessAllSubsites' => true
|
||||
);
|
||||
|
||||
/**
|
||||
* Migrations for GroupSubsites data.
|
||||
*/
|
||||
public function requireDefaultRecords()
|
||||
{
|
||||
// Migration for Group.SubsiteID data from when Groups only had a single subsite
|
||||
$groupFields = DB::field_list('Group');
|
||||
|
||||
// Detection of SubsiteID field is the trigger for old-style-subsiteID migration
|
||||
if (isset($groupFields['SubsiteID'])) {
|
||||
// Migrate subsite-specific data
|
||||
DB::query('INSERT INTO "Group_Subsites" ("GroupID", "SubsiteID")
|
||||
SELECT "ID", "SubsiteID" FROM "Group" WHERE "SubsiteID" > 0');
|
||||
|
||||
// Migrate global-access data
|
||||
DB::query('UPDATE "Group" SET "AccessAllSubsites" = 1 WHERE "SubsiteID" = 0');
|
||||
|
||||
// Move the field out of the way so that this migration doesn't get executed again
|
||||
DB::get_schema()->renameField('Group', 'SubsiteID', '_obsolete_SubsiteID');
|
||||
|
||||
// No subsite access on anything means that we've just installed the subsites module.
|
||||
// Make all previous groups global-access groups
|
||||
} elseif (!DB::query('SELECT "Group"."ID" FROM "Group"
|
||||
LEFT JOIN "Group_Subsites" ON "Group_Subsites"."GroupID" = "Group"."ID" AND "Group_Subsites"."SubsiteID" > 0
|
||||
WHERE "AccessAllSubsites" = 1
|
||||
OR "Group_Subsites"."GroupID" IS NOT NULL ')->value()) {
|
||||
DB::query('UPDATE "Group" SET "AccessAllSubsites" = 1');
|
||||
}
|
||||
}
|
||||
|
||||
public function updateCMSFields(FieldList $fields)
|
||||
{
|
||||
if ($this->owner->canEdit()) {
|
||||
// i18n tab
|
||||
$fields->findOrMakeTab('Root.Subsites', _t('GroupSubsites.SECURITYTABTITLE', 'Subsites'));
|
||||
|
||||
$subsites = Subsite::accessible_sites(array('ADMIN', 'SECURITY_SUBSITE_GROUP'), true);
|
||||
$subsiteMap = $subsites->map();
|
||||
|
||||
// Prevent XSS injection
|
||||
$subsiteMap = Convert::raw2xml($subsiteMap);
|
||||
|
||||
// Interface is different if you have the rights to modify subsite group values on
|
||||
// all subsites
|
||||
if (isset($subsiteMap[0])) {
|
||||
$fields->addFieldToTab("Root.Subsites", new OptionsetField("AccessAllSubsites",
|
||||
_t('GroupSubsites.ACCESSRADIOTITLE', 'Give this group access to'),
|
||||
array(
|
||||
1 => _t('GroupSubsites.ACCESSALL', "All subsites"),
|
||||
0 => _t('GroupSubsites.ACCESSONLY', "Only these subsites"),
|
||||
)
|
||||
));
|
||||
|
||||
unset($subsiteMap[0]);
|
||||
$fields->addFieldToTab("Root.Subsites", new CheckboxSetField("Subsites", "",
|
||||
$subsiteMap));
|
||||
} else {
|
||||
if (sizeof($subsiteMap) <= 1) {
|
||||
$fields->addFieldToTab("Root.Subsites", new ReadonlyField("SubsitesHuman",
|
||||
_t('GroupSubsites.ACCESSRADIOTITLE', 'Give this group access to'),
|
||||
reset($subsiteMap)));
|
||||
} else {
|
||||
$fields->addFieldToTab("Root.Subsites", new CheckboxSetField("Subsites",
|
||||
_t('GroupSubsites.ACCESSRADIOTITLE', 'Give this group access to'),
|
||||
$subsiteMap));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If this group belongs to a subsite,
|
||||
* append the subsites title to the group title
|
||||
* to make it easy to distinguish in the tree-view
|
||||
* of the security admin interface.
|
||||
*/
|
||||
public function alternateTreeTitle()
|
||||
{
|
||||
if ($this->owner->AccessAllSubsites) {
|
||||
$title = _t('GroupSubsites.GlobalGroup', 'global group');
|
||||
return htmlspecialchars($this->owner->Title, ENT_QUOTES) . ' <i>(' . $title . ')</i>';
|
||||
} else {
|
||||
$subsites = Convert::raw2xml(implode(", ", $this->owner->Subsites()->column('Title')));
|
||||
return htmlspecialchars($this->owner->Title) . " <i>($subsites)</i>";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update any requests to limit the results to the current site
|
||||
*/
|
||||
public function augmentSQL(SQLQuery &$query)
|
||||
{
|
||||
if (Subsite::$disable_subsite_filter) {
|
||||
return;
|
||||
}
|
||||
if (Cookie::get('noSubsiteFilter') == 'true') {
|
||||
return;
|
||||
}
|
||||
|
||||
// If you're querying by ID, ignore the sub-site - this is a bit ugly...
|
||||
if (!$query->filtersOnID()) {
|
||||
|
||||
/*if($context = DataObject::context_obj()) $subsiteID = (int)$context->SubsiteID;
|
||||
else */$subsiteID = (int)Subsite::currentSubsiteID();
|
||||
|
||||
// Don't filter by Group_Subsites if we've already done that
|
||||
$hasGroupSubsites = false;
|
||||
foreach ($query->getFrom() as $item) {
|
||||
if ((is_array($item) && strpos($item['table'], 'Group_Subsites')!==false) || (!is_array($item) && strpos($item, 'Group_Subsites')!==false)) {
|
||||
$hasGroupSubsites = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$hasGroupSubsites) {
|
||||
if ($subsiteID) {
|
||||
$query->addLeftJoin("Group_Subsites", "\"Group_Subsites\".\"GroupID\"
|
||||
= \"Group\".\"ID\" AND \"Group_Subsites\".\"SubsiteID\" = $subsiteID");
|
||||
$query->addWhere("(\"Group_Subsites\".\"SubsiteID\" IS NOT NULL OR
|
||||
\"Group\".\"AccessAllSubsites\" = 1)");
|
||||
} else {
|
||||
$query->addWhere("\"Group\".\"AccessAllSubsites\" = 1");
|
||||
}
|
||||
}
|
||||
|
||||
// WORKAROUND for databases that complain about an ORDER BY when the column wasn't selected (e.g. SQL Server)
|
||||
$select=$query->getSelect();
|
||||
if (isset($select[0]) && !$select[0] == 'COUNT(*)') {
|
||||
$query->orderby = "\"AccessAllSubsites\" DESC" . ($query->orderby ? ', ' : '') . $query->orderby;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function onBeforeWrite()
|
||||
{
|
||||
// New record test approximated by checking whether the ID has changed.
|
||||
// Note also that the after write test is only used when we're *not* on a subsite
|
||||
if ($this->owner->isChanged('ID') && !Subsite::currentSubsiteID()) {
|
||||
$this->owner->AccessAllSubsites = 1;
|
||||
}
|
||||
}
|
||||
|
||||
public function onAfterWrite()
|
||||
{
|
||||
// New record test approximated by checking whether the ID has changed.
|
||||
// Note also that the after write test is only used when we're on a subsite
|
||||
if ($this->owner->isChanged('ID') && $currentSubsiteID = Subsite::currentSubsiteID()) {
|
||||
$subsites = $this->owner->Subsites();
|
||||
$subsites->add($currentSubsiteID);
|
||||
}
|
||||
}
|
||||
|
||||
public function alternateCanEdit()
|
||||
{
|
||||
// Find the sites that this group belongs to and the sites where we have appropriate perm.
|
||||
$accessibleSites = Subsite::accessible_sites('CMS_ACCESS_SecurityAdmin')->column('ID');
|
||||
$linkedSites = $this->owner->Subsites()->column('ID');
|
||||
|
||||
// We are allowed to access this site if at we have CMS_ACCESS_SecurityAdmin permission on
|
||||
// at least one of the sites
|
||||
return (bool)array_intersect($accessibleSites, $linkedSites);
|
||||
}
|
||||
|
||||
public function providePermissions()
|
||||
{
|
||||
return array(
|
||||
'SECURITY_SUBSITE_GROUP' => array(
|
||||
'name' => _t('GroupSubsites.MANAGE_SUBSITES', 'Manage subsites for groups'),
|
||||
'category' => _t('Permissions.PERMISSIONS_CATEGORY', 'Roles and access permissions'),
|
||||
'help' => _t('GroupSubsites.MANAGE_SUBSITES_HELP', 'Ability to limit the permissions for a group to one or more subsites.'),
|
||||
'sort' => 200
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
@ -1,63 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Extension for the SiteConfig object to add subsites support
|
||||
*/
|
||||
class SiteConfigSubsites extends DataExtension
|
||||
{
|
||||
private static $has_one = array(
|
||||
'Subsite' => 'Subsite', // The subsite that this page belongs to
|
||||
);
|
||||
|
||||
/**
|
||||
* Update any requests to limit the results to the current site
|
||||
*/
|
||||
public function augmentSQL(SQLQuery &$query)
|
||||
{
|
||||
if (Subsite::$disable_subsite_filter) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If you're querying by ID, ignore the sub-site - this is a bit ugly...
|
||||
if ($query->filtersOnID()) {
|
||||
return;
|
||||
}
|
||||
$regexp = '/^(.*\.)?("|`)?SubsiteID("|`)?\s?=/';
|
||||
foreach ($query->getWhereParameterised($parameters) as $predicate) {
|
||||
if (preg_match($regexp, $predicate)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*if($context = DataObject::context_obj()) $subsiteID = (int)$context->SubsiteID;
|
||||
else */$subsiteID = (int)Subsite::currentSubsiteID();
|
||||
|
||||
$froms=$query->getFrom();
|
||||
$froms=array_keys($froms);
|
||||
$tableName = array_shift($froms);
|
||||
if ($tableName != 'SiteConfig') {
|
||||
return;
|
||||
}
|
||||
$query->addWhere("\"$tableName\".\"SubsiteID\" IN ($subsiteID)");
|
||||
}
|
||||
|
||||
public function onBeforeWrite()
|
||||
{
|
||||
if ((!is_numeric($this->owner->ID) || !$this->owner->ID) && !$this->owner->SubsiteID) {
|
||||
$this->owner->SubsiteID = Subsite::currentSubsiteID();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a piece of text to keep DataObject cache keys appropriately specific
|
||||
*/
|
||||
public function cacheKeyComponent()
|
||||
{
|
||||
return 'subsite-'.Subsite::currentSubsiteID();
|
||||
}
|
||||
|
||||
public function updateCMSFields(FieldList $fields)
|
||||
{
|
||||
$fields->push(new HiddenField('SubsiteID', 'SubsiteID', Subsite::currentSubsiteID()));
|
||||
}
|
||||
}
|
@ -1,398 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Extension for the SiteTree object to add subsites support
|
||||
*/
|
||||
class SiteTreeSubsites extends DataExtension
|
||||
{
|
||||
private static $has_one = array(
|
||||
'Subsite' => 'Subsite', // The subsite that this page belongs to
|
||||
);
|
||||
|
||||
private static $many_many = array(
|
||||
'CrossSubsiteLinkTracking' => 'SiteTree' // Stored separately, as the logic for URL rewriting is different
|
||||
);
|
||||
|
||||
private static $many_many_extraFields = array(
|
||||
"CrossSubsiteLinkTracking" => array("FieldName" => "Varchar")
|
||||
);
|
||||
|
||||
public function isMainSite()
|
||||
{
|
||||
if ($this->owner->SubsiteID == 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update any requests to limit the results to the current site
|
||||
*/
|
||||
public function augmentSQL(SQLQuery &$query, DataQuery &$dataQuery = null)
|
||||
{
|
||||
if (Subsite::$disable_subsite_filter) {
|
||||
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(!$query->where || (strpos($query->where[0], ".\"ID\" = ") === false && strpos($query->where[0], ".`ID` = ") === false && strpos($query->where[0], ".ID = ") === false && strpos($query->where[0], "ID = ") !== 0)) {
|
||||
if ($query->filtersOnID()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (Subsite::$force_subsite) {
|
||||
$subsiteID = Subsite::$force_subsite;
|
||||
} else {
|
||||
/*if($context = DataObject::context_obj()) $subsiteID = (int)$context->SubsiteID;
|
||||
else */$subsiteID = (int)Subsite::currentSubsiteID();
|
||||
}
|
||||
|
||||
// The foreach is an ugly way of getting the first key :-)
|
||||
foreach ($query->getFrom() as $tableName => $info) {
|
||||
// The tableName should be SiteTree or SiteTree_Live...
|
||||
if (strpos($tableName, 'SiteTree') === false) {
|
||||
break;
|
||||
}
|
||||
$query->addWhere("\"$tableName\".\"SubsiteID\" IN ($subsiteID)");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function onBeforeWrite()
|
||||
{
|
||||
if (!$this->owner->ID && !$this->owner->SubsiteID) {
|
||||
$this->owner->SubsiteID = Subsite::currentSubsiteID();
|
||||
}
|
||||
|
||||
parent::onBeforeWrite();
|
||||
}
|
||||
|
||||
public function updateCMSFields(FieldList $fields)
|
||||
{
|
||||
$subsites = Subsite::accessible_sites("CMS_ACCESS_CMSMain");
|
||||
$subsitesMap = array();
|
||||
if ($subsites && $subsites->Count()) {
|
||||
$subsitesMap = $subsites->map('ID', 'Title');
|
||||
unset($subsitesMap[$this->owner->SubsiteID]);
|
||||
}
|
||||
|
||||
// Master page edit field (only allowed from default subsite to avoid inconsistent relationships)
|
||||
$isDefaultSubsite = $this->owner->SubsiteID == 0 || $this->owner->Subsite()->DefaultSite;
|
||||
|
||||
if ($isDefaultSubsite && $subsitesMap) {
|
||||
$fields->addFieldsToTab(
|
||||
'Root.Main',
|
||||
ToggleCompositeField::create('SubsiteOperations',
|
||||
_t('SiteTreeSubsites.SubsiteOperations', 'Subsite Operations'),
|
||||
array(
|
||||
new DropdownField("CopyToSubsiteID", _t('SiteTreeSubsites.CopyToSubsite', "Copy page to subsite"), $subsitesMap),
|
||||
new CheckboxField("CopyToSubsiteWithChildren", _t('SiteTreeSubsites.CopyToSubsiteWithChildren', 'Include children pages?')),
|
||||
$copyAction = new InlineFormAction(
|
||||
"copytosubsite",
|
||||
_t('SiteTreeSubsites.CopyAction', "Copy")
|
||||
)
|
||||
)
|
||||
)->setHeadingLevel(4)
|
||||
);
|
||||
|
||||
$copyAction->includeDefaultJS(false);
|
||||
}
|
||||
|
||||
// replace readonly link prefix
|
||||
$subsite = $this->owner->Subsite();
|
||||
$nested_urls_enabled = Config::inst()->get('SiteTree', 'nested_urls');
|
||||
if ($subsite && $subsite->exists()) {
|
||||
// Use baseurl from domain
|
||||
$baseLink = $subsite->absoluteBaseURL();
|
||||
|
||||
// Add parent page if enabled
|
||||
if($nested_urls_enabled && $this->owner->ParentID) {
|
||||
$baseLink = Controller::join_links(
|
||||
$baseLink,
|
||||
$this->owner->Parent()->RelativeLink(true)
|
||||
);
|
||||
}
|
||||
|
||||
$urlsegment = $fields->dataFieldByName('URLSegment');
|
||||
$urlsegment->setURLPrefix($baseLink);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SiteConfig
|
||||
*/
|
||||
public function alternateSiteConfig()
|
||||
{
|
||||
if (!$this->owner->SubsiteID) {
|
||||
return false;
|
||||
}
|
||||
$sc = DataObject::get_one('SiteConfig', '"SubsiteID" = ' . $this->owner->SubsiteID);
|
||||
if (!$sc) {
|
||||
$sc = new SiteConfig();
|
||||
$sc->SubsiteID = $this->owner->SubsiteID;
|
||||
$sc->Title = _t('Subsite.SiteConfigTitle', 'Your Site Name');
|
||||
$sc->Tagline = _t('Subsite.SiteConfigSubtitle', 'Your tagline here');
|
||||
$sc->write();
|
||||
}
|
||||
return $sc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only allow editing of a page if the member satisfies one of the following conditions:
|
||||
* - 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"
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public function canEdit($member = null)
|
||||
{
|
||||
if (!$member) {
|
||||
$member = Member::currentUser();
|
||||
}
|
||||
|
||||
// Find the sites that this user has access to
|
||||
$goodSites = Subsite::accessible_sites('CMS_ACCESS_CMSMain', true, 'all', $member)->column('ID');
|
||||
|
||||
if (!is_null($this->owner->SubsiteID)) {
|
||||
$subsiteID = $this->owner->SubsiteID;
|
||||
} else {
|
||||
// The relationships might not be available during the record creation when using a GridField.
|
||||
// In this case the related objects will have empty fields, and SubsiteID will not be available.
|
||||
//
|
||||
// We do the second best: fetch the likely SubsiteID from the session. The drawback is this might
|
||||
// make it possible to force relations to point to other (forbidden) subsites.
|
||||
$subsiteID = Subsite::currentSubsiteID();
|
||||
}
|
||||
|
||||
// Return true if they have access to this object's site
|
||||
if (!(in_array(0, $goodSites) || in_array($subsiteID, $goodSites))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
public function canDelete($member = null)
|
||||
{
|
||||
if (!$member && $member !== false) {
|
||||
$member = Member::currentUser();
|
||||
}
|
||||
|
||||
return $this->canEdit($member);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
public function canAddChildren($member = null)
|
||||
{
|
||||
if (!$member && $member !== false) {
|
||||
$member = Member::currentUser();
|
||||
}
|
||||
|
||||
return $this->canEdit($member);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
public function canPublish($member = null)
|
||||
{
|
||||
if (!$member && $member !== false) {
|
||||
$member = Member::currentUser();
|
||||
}
|
||||
|
||||
return $this->canEdit($member);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a duplicate of this page and save it to another subsite
|
||||
*
|
||||
* @param int|Subsite $subsiteID The Subsite to copy to, or its ID
|
||||
* @param bool $includeChildren Recursively copy child Pages.
|
||||
* @param int $parentID Where to place the Page in the SiteTree's structure.
|
||||
*
|
||||
* @return SiteTree duplicated page
|
||||
*/
|
||||
public function duplicateToSubsite($subsiteID = null, $includeChildren = false, $parentID = 0)
|
||||
{
|
||||
if ($subsiteID instanceof Subsite) {
|
||||
$subsiteID = $subsiteID->ID;
|
||||
}
|
||||
|
||||
$oldSubsite = Subsite::currentSubsiteID();
|
||||
|
||||
if ($subsiteID) {
|
||||
Subsite::changeSubsite($subsiteID);
|
||||
} else {
|
||||
$subsiteID = $oldSubsite;
|
||||
}
|
||||
|
||||
$page = $this->owner->duplicate(false);
|
||||
|
||||
$page->CheckedPublicationDifferences = $page->AddedToStage = true;
|
||||
$subsiteID = ($subsiteID ? $subsiteID : $oldSubsite);
|
||||
$page->SubsiteID = $subsiteID;
|
||||
|
||||
$page->ParentID = $parentID;
|
||||
|
||||
// MasterPageID is here for legacy purposes, to satisfy the subsites_relatedpages module
|
||||
$page->MasterPageID = $this->owner->ID;
|
||||
$page->write();
|
||||
|
||||
Subsite::changeSubsite($oldSubsite);
|
||||
|
||||
if($includeChildren) {
|
||||
foreach($this->owner->AllChildren() as $child) {
|
||||
$child->duplicateToSubsite($subsiteID, $includeChildren, $page->ID);
|
||||
}
|
||||
}
|
||||
|
||||
return $page;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by ContentController::init();
|
||||
*/
|
||||
public static function contentcontrollerInit($controller)
|
||||
{
|
||||
$subsite = Subsite::currentSubsite();
|
||||
|
||||
if ($subsite && $subsite->Theme) {
|
||||
Config::inst()->update('SSViewer', 'theme', Subsite::currentSubsite()->Theme);
|
||||
}
|
||||
|
||||
if ($subsite && i18n::validate_locale($subsite->Language)) {
|
||||
i18n::set_locale($subsite->Language);
|
||||
}
|
||||
}
|
||||
|
||||
public function alternateAbsoluteLink()
|
||||
{
|
||||
// Generate the existing absolute URL and replace the domain with the subsite domain.
|
||||
// This helps deal with Link() returning an absolute URL.
|
||||
$url = Director::absoluteURL($this->owner->Link());
|
||||
if ($this->owner->SubsiteID) {
|
||||
$url = preg_replace('/\/\/[^\/]+\//', '//' . $this->owner->Subsite()->domain() . '/', $url);
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the CMS domain for iframed CMS previews to prevent single-origin violations
|
||||
* and SSL cert problems.
|
||||
*/
|
||||
public function alternatePreviewLink($action = null)
|
||||
{
|
||||
$url = Director::absoluteURL($this->owner->Link());
|
||||
if ($this->owner->SubsiteID) {
|
||||
$url = HTTP::setGetVar('SubsiteID', $this->owner->SubsiteID, $url);
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject the subsite ID into the content so it can be used by frontend scripts.
|
||||
*/
|
||||
public function MetaTags(&$tags)
|
||||
{
|
||||
if ($this->owner->SubsiteID) {
|
||||
$tags .= "<meta name=\"x-subsite-id\" content=\"" . $this->owner->SubsiteID . "\" />\n";
|
||||
}
|
||||
|
||||
return $tags;
|
||||
}
|
||||
|
||||
public function augmentSyncLinkTracking()
|
||||
{
|
||||
// Set LinkTracking appropriately
|
||||
$links = HTTP::getLinksIn($this->owner->Content);
|
||||
$linkedPages = array();
|
||||
|
||||
if ($links) {
|
||||
foreach ($links as $link) {
|
||||
if (substr($link, 0, strlen('http://')) == 'http://') {
|
||||
$withoutHttp = substr($link, strlen('http://'));
|
||||
if (strpos($withoutHttp, '/') && strpos($withoutHttp, '/') < strlen($withoutHttp)) {
|
||||
$domain = substr($withoutHttp, 0, strpos($withoutHttp, '/'));
|
||||
$rest = substr($withoutHttp, strpos($withoutHttp, '/') + 1);
|
||||
|
||||
$subsiteID = Subsite::getSubsiteIDForDomain($domain);
|
||||
if ($subsiteID == 0) {
|
||||
continue;
|
||||
} // We have no idea what the domain for the main site is, so cant track links to it
|
||||
|
||||
$origDisableSubsiteFilter = Subsite::$disable_subsite_filter;
|
||||
Subsite::disable_subsite_filter(true);
|
||||
$candidatePage = DataObject::get_one("SiteTree", "\"URLSegment\" = '" . Convert::raw2sql(urldecode($rest)) . "' AND \"SubsiteID\" = " . $subsiteID, false);
|
||||
Subsite::disable_subsite_filter($origDisableSubsiteFilter);
|
||||
|
||||
if ($candidatePage) {
|
||||
$linkedPages[] = $candidatePage->ID;
|
||||
} else {
|
||||
$this->owner->HasBrokenLink = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->owner->CrossSubsiteLinkTracking()->setByIDList($linkedPages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that valid url segments are checked within the correct subsite of the owner object,
|
||||
* even if the current subsiteID is set to some other subsite.
|
||||
*
|
||||
* @return null|bool Either true or false, or null to not influence result
|
||||
*/
|
||||
public function augmentValidURLSegment()
|
||||
{
|
||||
// If this page is being filtered in the current subsite, then no custom validation query is required.
|
||||
$subsite = Subsite::$force_subsite ?: Subsite::currentSubsiteID();
|
||||
if (empty($this->owner->SubsiteID) || $subsite == $this->owner->SubsiteID) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Backup forced subsite
|
||||
$prevForceSubsite = Subsite::$force_subsite;
|
||||
Subsite::$force_subsite = $this->owner->SubsiteID;
|
||||
|
||||
// Repeat validation in the correct subsite
|
||||
$isValid = $this->owner->validURLSegment();
|
||||
|
||||
// Restore
|
||||
Subsite::$force_subsite = $prevForceSubsite;
|
||||
|
||||
return (bool)$isValid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a piece of text to keep DataObject cache keys appropriately specific
|
||||
*/
|
||||
public function cacheKeyComponent()
|
||||
{
|
||||
return 'subsite-'.Subsite::currentSubsiteID();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Member
|
||||
* @return boolean|null
|
||||
*/
|
||||
public function canCreate($member = null)
|
||||
{
|
||||
// Typically called on a singleton, so we're not using the Subsite() relation
|
||||
$subsite = Subsite::currentSubsite();
|
||||
if ($subsite && $subsite->exists() && $subsite->PageTypeBlacklist) {
|
||||
$blacklisted = explode(',', $subsite->PageTypeBlacklist);
|
||||
// All subclasses need to be listed explicitly
|
||||
if (in_array($this->owner->class, $blacklisted)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
<?php
|
||||
/**
|
||||
* Wraps around a TreedropdownField to add ability for temporary
|
||||
* switching of subsite sessions.
|
||||
*
|
||||
* @package subsites
|
||||
*/
|
||||
class SubsitesTreeDropdownField extends TreeDropdownField
|
||||
{
|
||||
private static $allowed_actions = array(
|
||||
'tree'
|
||||
);
|
||||
|
||||
protected $subsiteID = 0;
|
||||
|
||||
protected $extraClasses = array('SubsitesTreeDropdownField');
|
||||
|
||||
public function Field($properties = array())
|
||||
{
|
||||
$html = parent::Field($properties);
|
||||
|
||||
Requirements::javascript('subsites/javascript/SubsitesTreeDropdownField.js');
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
public function setSubsiteID($id)
|
||||
{
|
||||
$this->subsiteID = $id;
|
||||
}
|
||||
|
||||
public function getSubsiteID()
|
||||
{
|
||||
return $this->subsiteID;
|
||||
}
|
||||
|
||||
public function tree(SS_HTTPRequest $request)
|
||||
{
|
||||
$oldSubsiteID = Session::get('SubsiteID');
|
||||
Session::set('SubsiteID', $this->subsiteID);
|
||||
|
||||
$results = parent::tree($request);
|
||||
|
||||
Session::set('SubsiteID', $oldSubsiteID);
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
1
codecov.yml
Normal file
1
codecov.yml
Normal file
@ -0,0 +1 @@
|
||||
comment: false
|
@ -2,7 +2,7 @@
|
||||
"name": "silverstripe/subsites",
|
||||
"description": "Run multiple sites from a single SilverStripe install.",
|
||||
"license": "BSD-3-Clause",
|
||||
"type": "silverstripe-module",
|
||||
"type": "silverstripe-vendormodule",
|
||||
"keywords": [
|
||||
"silverstripe",
|
||||
"subsites",
|
||||
@ -15,11 +15,30 @@
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"silverstripe/framework": "~3.2",
|
||||
"silverstripe/cms": "~3.2"
|
||||
"php": "^7.4 || ^8.0",
|
||||
"silverstripe/framework": "^4.12",
|
||||
"silverstripe/cms": "^4.4@dev",
|
||||
"silverstripe/admin": "^1.4@dev",
|
||||
"silverstripe/asset-admin": "^1.4@dev",
|
||||
"silverstripe/errorpage": "^1.4@dev",
|
||||
"silverstripe/versioned": "^1.4@dev"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/PHPUnit": "~3.7@stable"
|
||||
"phpunit/phpunit": "^9.5",
|
||||
"squizlabs/php_codesniffer": "^3.0"
|
||||
},
|
||||
"extra": []
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"SilverStripe\\Subsites\\": "src/",
|
||||
"SilverStripe\\Subsites\\Tests\\": "tests/php/"
|
||||
}
|
||||
},
|
||||
"extra": {
|
||||
"expose": [
|
||||
"client/javascript",
|
||||
"client/css"
|
||||
]
|
||||
},
|
||||
"minimum-stability": "dev",
|
||||
"prefer-stable": true
|
||||
}
|
@ -1,96 +0,0 @@
|
||||
/**
|
||||
* Styling for the subsite actions section in the CMS
|
||||
*/
|
||||
#SubsiteActions {
|
||||
position: absolute;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: 350px;
|
||||
text-align: right;
|
||||
margin-right: 130px;
|
||||
height: 51px;
|
||||
border-bottom: 3px solid #d4d0c8;
|
||||
color: #fff;
|
||||
}
|
||||
#SubsiteActions fieldset {
|
||||
padding: 3px;
|
||||
border-style: none;
|
||||
margin-top: 1px;
|
||||
background: none;
|
||||
}
|
||||
#SubsiteActions fieldset span {
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
.cms-menu .cms-subsites{
|
||||
padding:3px 0px 15px;
|
||||
}
|
||||
.cms-menu .cms-subsites .field.dropdown{
|
||||
padding-bottom:0;
|
||||
margin-bottom:0;
|
||||
}
|
||||
.cms-menu.collapsed .cms-subsites {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* Custom chzn styles for dark blue background */
|
||||
.cms-subsites .chzn-container-single .chzn-single,
|
||||
.cms-subsites .chzn-container-active .chzn-single {
|
||||
filter: none; /* Fix for IE9 */
|
||||
border: 1px solid #152338;
|
||||
background:#213557;
|
||||
-webkit-box-shadow: inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
|
||||
box-shadow: inset 1px 0 0 rgba(255,255,255,.125), inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
|
||||
}
|
||||
|
||||
.cms-menu .cms-subsites .dropdown span{
|
||||
padding-left:5px;
|
||||
}
|
||||
.cms-subsites .chzn-container-single .chzn-single div b{
|
||||
background: url(../images/chosen-sprite-light.png) 3px 0 no-repeat;
|
||||
}
|
||||
.cms-subsites .chzn-container .chzn-drop{
|
||||
padding-left:5px;
|
||||
background:#213557;
|
||||
border: 1px solid #152338;
|
||||
border-top:0;
|
||||
color:#fff;
|
||||
-webkit-box-shadow: inset 1px 0 0 rgba(255,255,255,.125);
|
||||
box-shadow: inset 1px 0 0 rgba(255,255,255,.125);
|
||||
}
|
||||
|
||||
|
||||
#AddSubsiteLink {
|
||||
display: block;
|
||||
font-size: 80%;
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
#Form_AddSubsiteForm .field {
|
||||
margin-left: 100px;
|
||||
}
|
||||
|
||||
#Form_AddSubsiteForm label.left {
|
||||
float: left;
|
||||
width: 100px;
|
||||
margin-left: -100px;
|
||||
}
|
||||
|
||||
body.SubsiteAdmin .right form #URL .fieldgroup * {
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.cms-add-form #PageType li .class-SubsitesVirtualPage, .class-SubsitesVirtualPage a .jstree-pageicon {
|
||||
background-position: 0 -32px !important;
|
||||
}
|
||||
|
||||
.subsites-move-dropdown{
|
||||
display:none;
|
||||
}
|
||||
|
||||
#Root_DetailsView .subsites-move-dropdown,
|
||||
#Form_ItemEditForm .subsites-move-dropdown {
|
||||
display:block;
|
||||
}
|
@ -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.
|
||||
|
||||
### 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.
|
||||
|
@ -28,5 +28,5 @@ This method is called when a pages are being copied between the main site or ano
|
||||
### alternateAbsoluteLink
|
||||
This method modifies the absolute link to contain the valid subsite domain
|
||||
|
||||
### alternatePreviewLink
|
||||
### updatePreviewLink
|
||||
This method modifies the preview link for the CMS.
|
||||
|
@ -1,5 +1,7 @@
|
||||
---
|
||||
title: Working with multiple websites
|
||||
summary: Setting up and editing multiple websites using SilverStripe
|
||||
---
|
||||
|
||||
# Working with multiple sites
|
||||
|
||||
|
@ -1,3 +1,6 @@
|
||||
---
|
||||
title: Setting up
|
||||
---
|
||||
# Setting up
|
||||
|
||||
## Creating subsites
|
||||
|
@ -1,3 +1,6 @@
|
||||
---
|
||||
title: Working with subsites
|
||||
---
|
||||
# Working with subsites
|
||||
|
||||
## Managing content across subsites
|
||||
|
@ -1,31 +0,0 @@
|
||||
(function($) {
|
||||
$.entwine('ss', function($) {
|
||||
/**
|
||||
* Choose a subsite from which to select pages.
|
||||
* Needs to clear tree dropdowns in case selection is changed.
|
||||
*/
|
||||
$('.subsitestreedropdownfield-chooser').entwine({
|
||||
onchange: function() {
|
||||
// TODO Data binding between two fields
|
||||
// TODO create resetField method on API instead
|
||||
var fields = $('.SubsitesTreeDropdownField');
|
||||
fields.setValue(null);
|
||||
fields.setTitle(null);
|
||||
fields.find('.tree-holder').empty();
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Add selected subsite from separate dropdown to the request parameters
|
||||
* before asking for the tree.
|
||||
*/
|
||||
$('.TreeDropdownField.SubsitesTreeDropdownField').entwine({
|
||||
getRequestParams: function() {
|
||||
var name = this.find(':input[type=hidden]:first').attr('name') + '_SubsiteID',
|
||||
source = $('[name=' + name + ']'), params = {};
|
||||
params[name] = source.length ? source.val() : null;
|
||||
return params;
|
||||
}
|
||||
});
|
||||
});
|
||||
})(jQuery);
|
73
lang/ar.yml
73
lang/ar.yml
@ -1,53 +1,68 @@
|
||||
ar:
|
||||
ASSETADMIN:
|
||||
SUBSITENOTICE: 'يمكن الوصول إلى المجلدات والملفات التي تم إنشاؤها في الموقع الرئيسي من طرف كل المواقع الفرعية.'
|
||||
FileSubsites:
|
||||
SilverStripe\CMS\Model\VirtualPage:
|
||||
EDITCONTENT: 'انقر هنا لتحرير المحتوى'
|
||||
SilverStripe\Subsites\Admin\SubsiteAdmin:
|
||||
MENUTITLE: 'المواقع الفرعية'
|
||||
SilverStripe\Subsites\Extensions\FileSubsites:
|
||||
has_one_Subsite: 'الموقع الفرعي'
|
||||
SilverStripe\Subsites\Extensions\FolderFormFactoryExtension:
|
||||
AllSitesDropdownOpt: 'كافة المواقع'
|
||||
SUBSITENOTICE: 'يمكن الوصول إلى المجلدات والملفات التي تم إنشاؤها في الموقع الرئيسي من طرف كل المواقع الفرعية.'
|
||||
SubsiteFieldLabel: 'الموقع الفرعي'
|
||||
GridFieldAddFromTemplateButton:
|
||||
AddFromTemplate: 'اضف جديد من القالب'
|
||||
GroupSubsites:
|
||||
SilverStripe\Subsites\Extensions\GroupSubsites:
|
||||
ACCESSALL: 'كل المواقع الفرعية'
|
||||
ACCESSONLY: 'فقط هذه المواقع الفرعية'
|
||||
ACCESSRADIOTITLE: 'امنح هذه المجموعة تصريح الولوج إلى'
|
||||
GlobalGroup: 'المجموعة العامة'
|
||||
MANAGE_SUBSITES: 'إدارة مواقع فرعية للمجموعات'
|
||||
MANAGE_SUBSITES_HELP: 'القدرة على الحد من أذونات مجموعة ما على موقع فرعي واحدة أو أكثر.'
|
||||
SECURITYTABTITLE: 'مواقع فرعية'
|
||||
LeftAndMainSubsites:
|
||||
SECURITYTABTITLE: 'المواقع الفرعية'
|
||||
many_many_Subsites: 'المواقع الفرعية'
|
||||
SilverStripe\Subsites\Extensions\LeftAndMainSubsites:
|
||||
Saved: 'تمّ الحفظ، يرجى تحديث الصفحات ذات الصلة.'
|
||||
SiteTreeSubsites:
|
||||
SilverStripe\Subsites\Extensions\SiteConfigSubsites:
|
||||
has_one_Subsite: 'الموقع الفرعي'
|
||||
SilverStripe\Subsites\Extensions\SiteTreeSubsites:
|
||||
CopyAction: نسخ
|
||||
CopyToSubsite: 'نسخ الصفحة في موقع فرعي'
|
||||
Subsite:
|
||||
COPYSTRUCTURE: 'نسخ الهيكل من:'
|
||||
has_one_Subsite: 'الموقع الفرعي'
|
||||
SilverStripe\Subsites\Model\Subsite:
|
||||
ConfigurationTab: المواصفات
|
||||
CopyMessage: 'إنشاء نسخة من {title}'
|
||||
CustomExtraMeta: 'العلامات الوصفية المخصصة'
|
||||
CustomExtraMeta: 'تخصيص Meta Tags'
|
||||
CustomMetaDescription: الوصف
|
||||
CustomMetaKeywords: 'كلمات البحث'
|
||||
CustomMetaTitle: عنوان
|
||||
DOMAINSAVEFIRST: 'لا يمكنك إضافة النطاقات إلا بعد القيام بالحفظ لأول مرة'
|
||||
DomainsHeadline: 'نطاقات هذا الموقع الفرعي'
|
||||
DomainsListTitle: النطاقات
|
||||
IsPublicHeaderField: 'موقع فرعي نشط'
|
||||
NOTEMPLATE: 'بدون قالب'
|
||||
PLURALNAME: 'المواقع الفرعية'
|
||||
PageTypeBlacklistField: 'عدم السماح بأصناف الصفحات؟'
|
||||
SINGULARNAME: 'الموقع الفرعي'
|
||||
SiteConfigSubtitle: 'هنا سطر الوصف الخاص بك'
|
||||
SiteConfigTitle: 'اسم موقعك'
|
||||
TabTitleConfig: 'المواصفات'
|
||||
ValidateTitle: 'الرجاء إضافة "عنوان"'
|
||||
SubsiteAdmin:
|
||||
MENUTITLE: 'مواقع فرعية'
|
||||
SubsiteDomain:
|
||||
DOMAIN: نطاق
|
||||
belongs_many_many_Groups: المجموعات
|
||||
db_DefaultSite: 'الموقع الافتراضي'
|
||||
db_Language: لغة
|
||||
db_RedirectURL: 'إعادة توجيه عنوان موقع الويب'
|
||||
db_Theme: المحور
|
||||
db_Title: عنوان
|
||||
has_many_Domains: النطاقات
|
||||
SilverStripe\Subsites\Model\SubsiteDomain:
|
||||
DOMAIN: النطاق
|
||||
PLURALNAME: 'نطاقات موقع فرعي'
|
||||
SINGULARNAME: 'نطاق موقع فرعي'
|
||||
SubsiteReportWrapper:
|
||||
ReportDropdown: 'المواقع'
|
||||
SubsiteXHRController:
|
||||
MENUTITLE: 'المراقب XHR للموقع الفرعي'
|
||||
db_Domain: النطاق
|
||||
has_one_Subsite: 'الموقع الفرعي'
|
||||
SilverStripe\Subsites\Pages\SubsitesVirtualPage:
|
||||
DESCRIPTION: 'يعرض محتوى صفحة على موقع فرعي آخر'
|
||||
PLURALNAME: 'قاعدة الصفحات'
|
||||
SINGULARNAME: 'الصفحة الإفتراضية للمواقع الفرعية'
|
||||
SubsiteField: 'الموقع الفرعي'
|
||||
SilverStripe\Subsites\Reports\SubsiteReportWrapper:
|
||||
ReportDropdown: المواقع
|
||||
ReportDropdownSubsite: 'الموقع الفرعي'
|
||||
Subsite:
|
||||
COPYSTRUCTURE: 'نسخ الهيكل من:'
|
||||
NOTEMPLATE: 'بدون قالب'
|
||||
Subsites:
|
||||
DefaultSiteFieldLabel: 'الموقع الافتراضي'
|
||||
DomainFieldLabel: النطاق
|
||||
@ -57,9 +72,5 @@ ar:
|
||||
PageTypeBlacklistFieldLabel: 'نوع الصفحة قائمة سوداء'
|
||||
PrimaryDomainFieldLabel: 'النطاق الأساسي'
|
||||
RedirectURLFieldLabel: 'إعادة توجيه عنوان موقع الويب'
|
||||
ThemeFieldLabel: 'المحور'
|
||||
ThemeFieldLabel: المحور
|
||||
TitleFieldLabel: 'اسم الموقع الفرعي'
|
||||
SubsitesVirtualPage:
|
||||
DESCRIPTION: 'يعرض محتوى صفحة على موقع فرعي آخر'
|
||||
SINGULARNAME: 'الصفحة الإفتراضية للمواقع الفرعية'
|
||||
SubsiteField: 'الموقع الفرعي'
|
||||
|
30
lang/cs.yml
30
lang/cs.yml
@ -2,6 +2,36 @@ cs:
|
||||
LeftAndMain_Menu:
|
||||
Hello: Ahoj
|
||||
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:
|
||||
CopyAction: Kopírovat
|
||||
Subsite:
|
||||
|
101
lang/de.yml
101
lang/de.yml
@ -1,14 +1,17 @@
|
||||
de:
|
||||
ASSETADMIN:
|
||||
SUBSITENOTICE: 'Auf Ordner und Dateien der Hauptseite kann von allen Subsites zugegriffen werden.'
|
||||
DomainNameField:
|
||||
INVALID_DOMAIN: 'Ungültige Domain'
|
||||
FileSubsites:
|
||||
AllSitesDropdownOpt: 'Alle Subseiten'
|
||||
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
|
||||
GridFieldAddFromTemplateButton:
|
||||
AddFromTemplate: 'Neu hinzufügen von Template'
|
||||
GroupSubsites:
|
||||
SilverStripe\Subsites\Extensions\GroupSubsites:
|
||||
ACCESSALL: 'Alle Subsites'
|
||||
ACCESSONLY: 'Nur diese Subsites'
|
||||
ACCESSRADIOTITLE: 'Dieser Gruppe Zugriff geben auf'
|
||||
@ -16,49 +19,74 @@ de:
|
||||
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
|
||||
LeftAndMainSubsites:
|
||||
Saved: Gespeichert.
|
||||
LeftAndMain_Menu:
|
||||
Hello: Hallo
|
||||
LOGOUT: Abmelden
|
||||
SiteTreeSubsites:
|
||||
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'
|
||||
Subsite:
|
||||
COPYSTRUCTURE: 'Struktur kopieren von:'
|
||||
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
|
||||
DOMAINSAVEFIRST: 'Domains können erst nach dem ersten Speichern hinzugefügt werden'
|
||||
DomainsHeadline: 'Domains für diese Subsite'
|
||||
DomainsListTitle: Domains
|
||||
IsPublicHeaderField: 'Aktive Subsite'
|
||||
NOTEMPLATE: 'Kein Template'
|
||||
PLURALNAME: Subsites
|
||||
PageTypeBlacklistField: 'Seitentyp verbieten?'
|
||||
SINGULARNAME: Subsite
|
||||
PLURALS:
|
||||
one: 'Eine Subseite'
|
||||
other: '{count} Subsites'
|
||||
PageTypeBlacklistField: 'Seitentypen verbieten?'
|
||||
SINGULARNAME: Subseite
|
||||
SiteConfigSubtitle: 'Ihr Websiteslogan'
|
||||
SiteConfigTitle: 'Name Ihrer Website'
|
||||
TabTitleConfig: Einstellungen
|
||||
ThemeFieldEmptyString: '-'
|
||||
ValidateTitle: 'Bitte geben Sie einen Titel an'
|
||||
SubsiteAdmin:
|
||||
MENUTITLE: Subsites
|
||||
SubsiteDomain:
|
||||
DOMAIN: Domain
|
||||
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 Hauptdomain?'
|
||||
PLURALNAME: 'Subsite Domains'
|
||||
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: 'Subsite Domain'
|
||||
SubsiteReportWrapper:
|
||||
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
|
||||
SubsiteXHRController:
|
||||
MENUTITLE: SubsiteXHRController
|
||||
ReportDropdownSubsite: Subseite
|
||||
Subsite:
|
||||
COPYSTRUCTURE: 'Struktur kopieren von:'
|
||||
NOTEMPLATE: 'Kein Template'
|
||||
Subsites:
|
||||
DefaultSiteFieldLabel: 'Standard Seite'
|
||||
DomainFieldLabel: Domäne
|
||||
@ -70,10 +98,3 @@ de:
|
||||
RedirectURLFieldLabel: Weierleitungs-URL
|
||||
ThemeFieldLabel: Theme
|
||||
TitleFieldLabel: 'Name der Subsite'
|
||||
SubsitesVirtualPage:
|
||||
DESCRIPTION: 'Zeigt den Inhalt einer anderen Seite von einer anderen Subsite an'
|
||||
PLURALNAME: 'Basis Seiten'
|
||||
SINGULARNAME: 'Subsites Virtuelle Seite'
|
||||
SubsiteField: Subsite
|
||||
VirtualPage:
|
||||
EDITCONTENT: 'Klicken Sie hier, um den Inhalt zu bearbeiten'
|
||||
|
98
lang/en.yml
98
lang/en.yml
@ -1,14 +1,19 @@
|
||||
en:
|
||||
ASSETADMIN:
|
||||
SUBSITENOTICE: 'Folders and files created in the main site are accessible by all subsites.'
|
||||
DomainNameField:
|
||||
INVALID_DOMAIN: 'Invalid domain name'
|
||||
FileSubsites:
|
||||
SilverStripe\CMS\Model\VirtualPage:
|
||||
EDITCONTENT: 'Click here to edit the content'
|
||||
SilverStripe\Subsites\Admin\SubsiteAdmin:
|
||||
MENUTITLE: Subsites
|
||||
SilverStripe\Subsites\Controller\SubsiteXHRController:
|
||||
MENUTITLE: SilverStripe\Subsites\Controller\SubsiteXHRController
|
||||
SilverStripe\Subsites\Extensions\FileSubsites:
|
||||
has_one_Subsite: Subsite
|
||||
SilverStripe\Subsites\Extensions\FolderFormFactoryExtension:
|
||||
AllSitesDropdownOpt: 'All sites'
|
||||
SUBSITENOTICE: 'Folders and files created in the main site are accessible by all subsites.'
|
||||
SubsiteFieldLabel: Subsite
|
||||
GridFieldAddFromTemplateButton:
|
||||
AddFromTemplate: 'Add New from Template'
|
||||
GroupSubsites:
|
||||
SilverStripe\Subsites\Extensions\GroupSubsites:
|
||||
ACCESSALL: 'All subsites'
|
||||
ACCESSONLY: 'Only these subsites'
|
||||
ACCESSRADIOTITLE: 'Give this group access to'
|
||||
@ -16,52 +21,85 @@ en:
|
||||
MANAGE_SUBSITES: 'Manage subsites for groups'
|
||||
MANAGE_SUBSITES_HELP: 'Ability to limit the permissions for a group to one or more subsites.'
|
||||
SECURITYTABTITLE: Subsites
|
||||
LeftAndMainSubsites:
|
||||
db_AccessAllSubsites: 'Access all subsites'
|
||||
many_many_Subsites: Subsites
|
||||
SilverStripe\Subsites\Extensions\LeftAndMainSubsites:
|
||||
SITECONTENTLEFT: 'Site Content'
|
||||
Saved: 'Saved, please update related pages.'
|
||||
LeftAndMain_Menu:
|
||||
Hello: Hi
|
||||
LOGOUT: 'Log out'
|
||||
SiteTreeSubsites:
|
||||
SilverStripe\Subsites\Extensions\SiteConfigSubsites:
|
||||
has_one_Subsite: Subsite
|
||||
SilverStripe\Subsites\Extensions\SiteTreeSubsites:
|
||||
CopyAction: Copy
|
||||
CopyToSubsite: 'Copy page to subsite'
|
||||
CopyToSubsiteWithChildren: 'Include children pages?'
|
||||
SubsiteOperations: 'Subsite Operations'
|
||||
Subsite:
|
||||
COPYSTRUCTURE: 'Copy structure from:'
|
||||
has_one_Subsite: Subsite
|
||||
many_many_CrossSubsiteLinkTracking: 'Cross subsite link tracking'
|
||||
SilverStripe\Subsites\Model\Subsite:
|
||||
ConfigurationTab: Configuration
|
||||
CopyMessage: 'Created a copy of {title}'
|
||||
CustomExtraMeta: 'Custom Meta Tags'
|
||||
CustomMetaDescription: Description
|
||||
CustomMetaKeywords: Keywords
|
||||
CustomMetaTitle: Title
|
||||
DOMAINSAVEFIRST: 'You can only add domains after saving for the first time'
|
||||
DomainsHeadline: 'Domains for this subsite'
|
||||
DomainsListTitle: Domains
|
||||
IsPublicHeaderField: 'Active subsite'
|
||||
NOTEMPLATE: 'No template'
|
||||
PLURALNAME: Subsites
|
||||
PLURALS:
|
||||
one: 'A Subsite'
|
||||
other: '{count} Subsites'
|
||||
PageTypeBlacklistField: 'Disallow page types?'
|
||||
SINGULARNAME: Subsite
|
||||
SiteConfigSubtitle: 'Your tagline here'
|
||||
SiteConfigTitle: 'Your Site Name'
|
||||
TabTitleConfig: Configuration
|
||||
ThemeFieldEmptyString: '-'
|
||||
ValidateTitle: 'Please add a "Title"'
|
||||
SubsiteAdmin:
|
||||
MENUTITLE: Subsites
|
||||
SubsiteDomain:
|
||||
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:
|
||||
DOMAIN: Domain
|
||||
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?'
|
||||
PLURALNAME: 'Subsite Domains'
|
||||
PLURALS:
|
||||
one: 'A Subsite Domain'
|
||||
other: '{count} Subsite Domains'
|
||||
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_HTTPS: 'https://'
|
||||
Protocol: Protocol
|
||||
SINGULARNAME: 'Subsite Domain'
|
||||
SubsiteReportWrapper:
|
||||
db_Domain: Domain
|
||||
db_IsPrimary: 'Is primary'
|
||||
db_Protocol: Protocol
|
||||
has_one_Subsite: Subsite
|
||||
SilverStripe\Subsites\Pages\SubsitesVirtualPage:
|
||||
DESCRIPTION: 'Displays the content of a page on another subsite'
|
||||
OverrideNote: 'Overrides inherited value from the source'
|
||||
PLURALNAME: 'Subsites Virtual Pages'
|
||||
PLURALS:
|
||||
one: 'A Subsites Virtual Page'
|
||||
other: '{count} Subsites Virtual Pages'
|
||||
SINGULARNAME: 'Subsites Virtual Page'
|
||||
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:
|
||||
ReportDropdown: Sites
|
||||
SubsiteXHRController:
|
||||
MENUTITLE: SubsiteXHRController
|
||||
ReportDropdownAll: All
|
||||
ReportDropdownSubsite: Subsite
|
||||
Subsite:
|
||||
COPYSTRUCTURE: 'Copy structure from:'
|
||||
NOTEMPLATE: 'No template'
|
||||
Subsites:
|
||||
DefaultSiteFieldLabel: 'Default site'
|
||||
DomainFieldLabel: Domain
|
||||
@ -72,12 +110,4 @@ en:
|
||||
PrimaryDomainFieldLabel: 'Primary Domain'
|
||||
RedirectURLFieldLabel: 'Redirect URL'
|
||||
ThemeFieldLabel: Theme
|
||||
ThemeFieldEmptyString: ''
|
||||
TitleFieldLabel: 'Subsite Name'
|
||||
SubsitesVirtualPage:
|
||||
DESCRIPTION: 'Displays the content of a page on another subsite'
|
||||
PLURALNAME: 'Base Pages'
|
||||
SINGULARNAME: 'Subsites Virtual Page'
|
||||
SubsiteField: Subsite
|
||||
VirtualPage:
|
||||
EDITCONTENT: 'Click here to edit the content'
|
||||
|
107
lang/eo.yml
107
lang/eo.yml
@ -1,67 +1,105 @@
|
||||
eo:
|
||||
ASSETADMIN:
|
||||
SUBSITENOTICE: 'Dosierujoj kaj dosieroj kreitaj en la ĉefa retejo estas alireblaj de ĉiuj retejoj'
|
||||
DomainNameField:
|
||||
INVALID_DOMAIN: 'Nevalida domajna nomo'
|
||||
FileSubsites:
|
||||
SilverStripe\CMS\Model\VirtualPage:
|
||||
EDITCONTENT: 'Alklaku ĉi tie por redakti la enhavon'
|
||||
SilverStripe\Subsites\Admin\SubsiteAdmin:
|
||||
MENUTITLE: Subretejoj
|
||||
SilverStripe\Subsites\Controller\SubsiteXHRController:
|
||||
MENUTITLE: SilverStripe\Subretejoj\Reganto\SubretejaXHRReganto
|
||||
SilverStripe\Subsites\Extensions\FileSubsites:
|
||||
has_one_Subsite: Subretejo
|
||||
SilverStripe\Subsites\Extensions\FolderFormFactoryExtension:
|
||||
AllSitesDropdownOpt: 'Ĉiuj retejoj'
|
||||
SUBSITENOTICE: 'Dosierujoj kaj dosieroj kreitaj en la ĉefa retejo estas alireblaj de ĉiuj retejoj'
|
||||
SubsiteFieldLabel: Subretejo
|
||||
GridFieldAddFromTemplateButton:
|
||||
AddFromTemplate: 'Aldoni novan el ŝablono'
|
||||
GroupSubsites:
|
||||
SilverStripe\Subsites\Extensions\GroupSubsites:
|
||||
ACCESSALL: 'Ĉiuj subretejoj'
|
||||
ACCESSONLY: 'Nur ĉi tiuj subretejoj'
|
||||
ACCESSRADIOTITLE: 'Doni ĉi tiun grupon aliron al'
|
||||
ACCESSRADIOTITLE: 'Doni al ĉi tiu grupo aliron al'
|
||||
GlobalGroup: 'ĉiea grupo'
|
||||
MANAGE_SUBSITES: 'Administri subretejojn por grupoj'
|
||||
MANAGE_SUBSITES_HELP: 'Eblo limigi la permesojn por grupo al unu aŭ pluaj subretejoj.'
|
||||
SECURITYTABTITLE: Subsites
|
||||
LeftAndMainSubsites:
|
||||
SECURITYTABTITLE: Subretejoj
|
||||
db_AccessAllSubsites: 'Aliri ĉiujn subretejojn'
|
||||
many_many_Subsites: Subretejoj
|
||||
SilverStripe\Subsites\Extensions\LeftAndMainSubsites:
|
||||
SITECONTENTLEFT: 'Enhavo de retejo'
|
||||
Saved: 'Konservita, bonvole ĝisdatigi rilatajn paĝojn.'
|
||||
LeftAndMain_Menu:
|
||||
Hello: Saluton
|
||||
LOGOUT: Elsaluti
|
||||
SiteTreeSubsites:
|
||||
SilverStripe\Subsites\Extensions\SiteConfigSubsites:
|
||||
has_one_Subsite: Subretejo
|
||||
SilverStripe\Subsites\Extensions\SiteTreeSubsites:
|
||||
CopyAction: Kopio
|
||||
CopyToSubsite: 'Kopii paĝon al subretejo'
|
||||
CopyToSubsiteWithChildren: 'Ĉu inkluzivi paĝidojn?'
|
||||
SubsiteOperations: 'Subretejaj operacioj'
|
||||
Subsite:
|
||||
COPYSTRUCTURE: 'Kopii strukturon de:'
|
||||
has_one_Subsite: Subretejo
|
||||
many_many_CrossSubsiteLinkTracking: 'Trans-subreteja ligspurado'
|
||||
SilverStripe\Subsites\Model\Subsite:
|
||||
ConfigurationTab: Agordaro
|
||||
CopyMessage: 'Kreis kopion de {title}'
|
||||
CustomExtraMeta: 'Propraj meta-etikedoj'
|
||||
CustomExtraMeta: 'Propraj meta-etikedoj '
|
||||
CustomMetaDescription: Priskribo
|
||||
CustomMetaKeywords: Ŝlosilvortoj
|
||||
CustomMetaTitle: Titolo
|
||||
DOMAINSAVEFIRST: 'Vi povas aldoni domajnojn post konservante unuafoje'
|
||||
DomainsHeadline: 'Domajnoj por ĉi tiu subretejo'
|
||||
DomainsListTitle: Domajnoj
|
||||
IsPublicHeaderField: 'Aktiva subretejo'
|
||||
NOTEMPLATE: 'Mankas ŝablono'
|
||||
PLURALNAME: Subretejoj
|
||||
PageTypeBlacklistField: 'Ĉu malpermesu paĝajn tipojn?'
|
||||
PLURALS:
|
||||
one: 'Unu subretejo'
|
||||
other: '{count} subretejoj'
|
||||
PageTypeBlacklistField: 'Ĉu malpermesi paĝajn tipojn?'
|
||||
SINGULARNAME: Subretejo
|
||||
SiteConfigSubtitle: 'Jen via slogano'
|
||||
SiteConfigTitle: 'Nomo de via retejo'
|
||||
TabTitleConfig: Agordaro
|
||||
ValidateTitle: 'Bonvole aldonu "Titolon"'
|
||||
SubsiteAdmin:
|
||||
MENUTITLE: Subretejoj
|
||||
SubsiteDomain:
|
||||
ThemeFieldEmptyString: '-'
|
||||
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:
|
||||
DOMAIN: Domajno
|
||||
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?'
|
||||
PLURALNAME: 'Subretejaj domajnoj'
|
||||
PLURALS:
|
||||
one: 'Unu subreteja domajno'
|
||||
other: '{count} subretejaj domajnoj'
|
||||
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_HTTPS: 'https://'
|
||||
Protocol: Protokolo
|
||||
SINGULARNAME: 'Subreteja domajno'
|
||||
SubsiteReportWrapper:
|
||||
db_Domain: Domajno
|
||||
db_IsPrimary: 'Estas unuaranga'
|
||||
db_Protocol: Protokolo
|
||||
has_one_Subsite: Subretejo
|
||||
SilverStripe\Subsites\Pages\SubsitesVirtualPage:
|
||||
DESCRIPTION: 'Vidigas la enhavon de paĝo en alia subretejo'
|
||||
OverrideNote: 'Anstataŭigas hereditan valoron el la fonto'
|
||||
PLURALNAME: 'Virtualaj paĝoj de subretejoj'
|
||||
PLURALS:
|
||||
one: 'Unu virtuala paĝo de subretejoj'
|
||||
other: '{count} virtualaj paĝoj de subretejoj'
|
||||
SINGULARNAME: 'Virtuala paĝo de subretejoj'
|
||||
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:
|
||||
ReportDropdown: Retejoj
|
||||
SubsiteXHRController:
|
||||
MENUTITLE: SubsiteXHRController
|
||||
ReportDropdownAll: Ĉiuj
|
||||
ReportDropdownSubsite: Subretejo
|
||||
Subsite:
|
||||
COPYSTRUCTURE: 'Kopii strukturon de:'
|
||||
NOTEMPLATE: 'Mankas ŝablono'
|
||||
Subsites:
|
||||
DefaultSiteFieldLabel: 'Apriora retejo'
|
||||
DomainFieldLabel: Domajno
|
||||
@ -73,10 +111,3 @@ eo:
|
||||
RedirectURLFieldLabel: 'Redirekti je URL'
|
||||
ThemeFieldLabel: Etoso
|
||||
TitleFieldLabel: 'Nomo de subretejo'
|
||||
SubsitesVirtualPage:
|
||||
DESCRIPTION: 'Vidigas la enhavon de paĝo en alia subretejo'
|
||||
PLURALNAME: 'Bazaj paĝoj'
|
||||
SINGULARNAME: 'Virtuala paĝo de subretejoj'
|
||||
SubsiteField: Subretejo
|
||||
VirtualPage:
|
||||
EDITCONTENT: 'Alklaku ĉi tie por redakti la enhavon'
|
||||
|
13
lang/es.yml
Normal file
13
lang/es.yml
Normal 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
13
lang/et_EE.yml
Normal 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
|
@ -1,46 +1,59 @@
|
||||
fa_IR:
|
||||
DomainNameField:
|
||||
INVALID_DOMAIN: 'نام دامنه نامعتبر'
|
||||
FileSubsites:
|
||||
SilverStripe\CMS\Model\VirtualPage:
|
||||
EDITCONTENT: 'برای ویرایش محتوا اینجا را کلیک کنید'
|
||||
SilverStripe\Subsites\Admin\SubsiteAdmin:
|
||||
MENUTITLE: 'زیر سایت ها'
|
||||
SilverStripe\Subsites\Extensions\FileSubsites:
|
||||
has_one_Subsite: 'زیر سایت'
|
||||
SilverStripe\Subsites\Extensions\FolderFormFactoryExtension:
|
||||
AllSitesDropdownOpt: 'تمامی سایت ها'
|
||||
SubsiteFieldLabel: 'زیر سایت ها'
|
||||
GroupSubsites:
|
||||
SubsiteFieldLabel: 'زیر سایت'
|
||||
SilverStripe\Subsites\Extensions\GroupSubsites:
|
||||
ACCESSALL: 'تمامی زیر سایت ها'
|
||||
ACCESSONLY: 'فقط این زیر سایت ها'
|
||||
SECURITYTABTITLE: 'زیر سایت ها'
|
||||
LeftAndMain_Menu:
|
||||
Hello: 'سلام'
|
||||
LOGOUT: خروج
|
||||
Subsite:
|
||||
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: عنوان
|
||||
DomainsHeadline: 'دامنه های این زیر سایت'
|
||||
DomainsListTitle: 'دامنه ها'
|
||||
IsPublicHeaderField: 'زیر سایت فعال'
|
||||
NOTEMPLATE: 'بدون قالب'
|
||||
PLURALNAME: 'زیر سایت ها'
|
||||
SINGULARNAME: 'زیر سایت'
|
||||
SiteConfigTitle: 'نام سایت شما'
|
||||
TabTitleConfig: پیکربندی
|
||||
SubsiteAdmin:
|
||||
MENUTITLE: 'زیر سایت ها'
|
||||
SubsiteDomain:
|
||||
DOMAIN: 'دامنه'
|
||||
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: 'دمنه زیرسایت'
|
||||
SubsiteReportWrapper:
|
||||
db_Domain: دامنه
|
||||
db_Protocol: پروتکل
|
||||
has_one_Subsite: 'زیر سایت'
|
||||
SilverStripe\Subsites\Pages\SubsitesVirtualPage:
|
||||
SubsiteField: 'زیر سایت'
|
||||
SilverStripe\Subsites\Reports\SubsiteReportWrapper:
|
||||
ReportDropdown: 'سایت ها'
|
||||
ReportDropdownSubsite: 'زیر سایت'
|
||||
Subsite:
|
||||
NOTEMPLATE: 'بدون قالب'
|
||||
Subsites:
|
||||
DefaultSiteFieldLabel: 'سایت پیش فرض'
|
||||
DomainFieldLabel: 'دامنه'
|
||||
DomainFieldLabel: دامنه
|
||||
LanguageFieldLabel: زبان
|
||||
PrimaryDomainFieldLabel: 'دامنه اولیه'
|
||||
ThemeFieldLabel: پوسته
|
||||
TitleFieldLabel: 'نام زیر سایت'
|
||||
SubsitesVirtualPage:
|
||||
PLURALNAME: 'صفحه اصلی'
|
||||
SubsiteField: 'زیر سایت'
|
||||
VirtualPage:
|
||||
EDITCONTENT: 'برای ویرایش محتوا اینجا را کلیک کنید'
|
||||
|
98
lang/fi.yml
98
lang/fi.yml
@ -1,67 +1,84 @@
|
||||
fi:
|
||||
ASSETADMIN:
|
||||
SUBSITENOTICE: 'Kansiot ja tiedostot, jotka on luotu pääsivustolla, ovat käytettävissä kaikissa alisivustoissa.'
|
||||
DomainNameField:
|
||||
INVALID_DOMAIN: 'Virheellinen domain-nimi'
|
||||
FileSubsites:
|
||||
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'
|
||||
SubsiteFieldLabel: Alisivusto
|
||||
GridFieldAddFromTemplateButton:
|
||||
AddFromTemplate: 'Lisää uusi sivupohjasta'
|
||||
GroupSubsites:
|
||||
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 tälle ryhmälle pääsy kohteeseen'
|
||||
ACCESSRADIOTITLE: 'Anna ryhmän oikeudet »'
|
||||
GlobalGroup: Globaaliryhmä
|
||||
MANAGE_SUBSITES: 'Hallinnoi ryhmien alisivustoja'
|
||||
MANAGE_SUBSITES: 'Hallinnoi ryhmien alasivustoja'
|
||||
MANAGE_SUBSITES_HELP: 'Mahdollisuus rajoittaa ryhmän oikeuksia yhdelle tai useammalle alisivustolle.'
|
||||
SECURITYTABTITLE: Alisivustot
|
||||
LeftAndMainSubsites:
|
||||
SECURITYTABTITLE: Alasivustot
|
||||
many_many_Subsites: Alasivut
|
||||
SilverStripe\Subsites\Extensions\LeftAndMainSubsites:
|
||||
SITECONTENTLEFT: 'Sivuston sisältö'
|
||||
Saved: 'Tallennettu, ole hyvä ja päivitä liittyvät sivut.'
|
||||
LeftAndMain_Menu:
|
||||
Hello: Hei
|
||||
LOGOUT: 'Kirjaudu ulos'
|
||||
SiteTreeSubsites:
|
||||
SilverStripe\Subsites\Extensions\SiteConfigSubsites:
|
||||
has_one_Subsite: Alasivusto
|
||||
SilverStripe\Subsites\Extensions\SiteTreeSubsites:
|
||||
CopyAction: Kopioi
|
||||
CopyToSubsite: 'Kopioi sivu alisivustolle'
|
||||
CopyToSubsite: 'Kopioi sivu alasivustolle'
|
||||
CopyToSubsiteWithChildren: 'Sisällytä alasivut?'
|
||||
SubsiteOperations: Alisivustotoiminnot
|
||||
Subsite:
|
||||
COPYSTRUCTURE: 'Kopioi rakenne kohteesta:'
|
||||
SubsiteOperations: 'Alasivuston toiminnot'
|
||||
has_one_Subsite: Alasivusto
|
||||
SilverStripe\Subsites\Model\Subsite:
|
||||
ConfigurationTab: Asetukset
|
||||
CopyMessage: 'Luotiin kopio lähteestä {title}'
|
||||
CustomExtraMeta: 'Omat Meta-tagit'
|
||||
CustomExtraMeta: 'Omat meta-tagit'
|
||||
CustomMetaDescription: Kuvaus
|
||||
CustomMetaKeywords: Avainsanat
|
||||
CustomMetaTitle: Otsikko
|
||||
DOMAINSAVEFIRST: 'Voit lisätä domain-osoitteita vasta tallentamisen jälkeen'
|
||||
DomainsHeadline: 'Tämän alisivuston domainit'
|
||||
DomainsListTitle: Domainit
|
||||
IsPublicHeaderField: 'Aktiivinen alisivusto'
|
||||
NOTEMPLATE: 'Ei sivupohjaa'
|
||||
PLURALNAME: Alasivustot
|
||||
PLURALNAME: Alasivut
|
||||
PLURALS:
|
||||
one: Alasivu
|
||||
other: '{count} alasivua'
|
||||
PageTypeBlacklistField: 'Kiellä sivutyyppien käyttö?'
|
||||
SINGULARNAME: Alisivusto
|
||||
SiteConfigSubtitle: Iskulauseesi
|
||||
SINGULARNAME: Alasivusto
|
||||
SiteConfigSubtitle: 'Tähän sloganisi'
|
||||
SiteConfigTitle: 'Sivuston nimi'
|
||||
TabTitleConfig: Asetukset
|
||||
ValidateTitle: 'Lisää "Otsikko"'
|
||||
SubsiteAdmin:
|
||||
MENUTITLE: Alasivustot
|
||||
SubsiteDomain:
|
||||
DOMAIN: Domain
|
||||
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_DESCRIPTION: 'Merkitse tämä oletusdomainiksi tälle alisivustolle'
|
||||
PROTOCOL_HTTP: 'http://'
|
||||
PROTOCOL_HTTPS: 'https://'
|
||||
Protocol: Protokolla
|
||||
SINGULARNAME: 'Alisivuston domain-osoite'
|
||||
SubsiteReportWrapper:
|
||||
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
|
||||
SubsiteXHRController:
|
||||
MENUTITLE: SubsiteXHRController
|
||||
ReportDropdownSubsite: Alasivusto
|
||||
Subsite:
|
||||
COPYSTRUCTURE: 'Kopioi rakenne kohteesta:'
|
||||
NOTEMPLATE: 'Ei sivupohjaa'
|
||||
Subsites:
|
||||
DefaultSiteFieldLabel: Oletussivusto
|
||||
DomainFieldLabel: Domain
|
||||
@ -73,10 +90,3 @@ fi:
|
||||
RedirectURLFieldLabel: 'Edelleenohjaus URL'
|
||||
ThemeFieldLabel: Teema
|
||||
TitleFieldLabel: 'Alisivuston nimi'
|
||||
SubsitesVirtualPage:
|
||||
DESCRIPTION: 'Näyttää sisällön toisen alisivuston sivulta'
|
||||
PLURALNAME: Pohjasivut
|
||||
SINGULARNAME: 'Alisivuston Virtuaalisivu'
|
||||
SubsiteField: Alisivu
|
||||
VirtualPage:
|
||||
EDITCONTENT: 'Napsauta tästä muokataksesi sisältöä'
|
||||
|
14
lang/fr.yml
Normal file
14
lang/fr.yml
Normal 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
|
73
lang/hr.yml
73
lang/hr.yml
@ -1,14 +1,17 @@
|
||||
hr:
|
||||
ASSETADMIN:
|
||||
SUBSITENOTICE: 'Direktoriji i datoteke kreirane u glavnom sajtu su dostupne svim podsajtovima.'
|
||||
DomainNameField:
|
||||
INVALID_DOMAIN: 'Netočan naziv domene'
|
||||
FileSubsites:
|
||||
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
|
||||
GridFieldAddFromTemplateButton:
|
||||
AddFromTemplate: 'Dodaj novi iz predloška'
|
||||
GroupSubsites:
|
||||
SilverStripe\Subsites\Extensions\GroupSubsites:
|
||||
ACCESSALL: 'Svi podsajtovi'
|
||||
ACCESSONLY: 'Samo ovi podsajtovi'
|
||||
ACCESSRADIOTITLE: 'Dodijeli ovoj grupi pristup za'
|
||||
@ -16,50 +19,61 @@ hr:
|
||||
MANAGE_SUBSITES: 'Upravljaj podsajtove za grupe'
|
||||
MANAGE_SUBSITES_HELP: 'Mogućnost limitiranja prava za grupu za jedan ili više podsajtova.'
|
||||
SECURITYTABTITLE: Podsajtovi
|
||||
LeftAndMainSubsites:
|
||||
many_many_Subsites: Podsajtovi
|
||||
SilverStripe\Subsites\Extensions\LeftAndMainSubsites:
|
||||
Saved: 'Spremljeno, molimo osvježite povezane stranice.'
|
||||
LeftAndMain_Menu:
|
||||
Hello: Pozdrav
|
||||
LOGOUT: Odjava
|
||||
SiteTreeSubsites:
|
||||
SilverStripe\Subsites\Extensions\SiteConfigSubsites:
|
||||
has_one_Subsite: Podsajt
|
||||
SilverStripe\Subsites\Extensions\SiteTreeSubsites:
|
||||
CopyAction: Kopiraj
|
||||
CopyToSubsite: 'Kopiraj stranicu u podsajt'
|
||||
Subsite:
|
||||
COPYSTRUCTURE: 'Kopiraj strukturu od:'
|
||||
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
|
||||
DOMAINSAVEFIRST: 'Možete dodati domene nakon prvog spremanja'
|
||||
DomainsHeadline: 'Domene za ove podsajtove'
|
||||
DomainsListTitle: Domene
|
||||
IsPublicHeaderField: 'Aktivni podsajtovi'
|
||||
NOTEMPLATE: 'Nema predloška'
|
||||
PLURALNAME: Podsajtovi
|
||||
PageTypeBlacklistField: 'Ne dopuštaj tipove stranica?'
|
||||
SINGULARNAME: Podsajt
|
||||
SiteConfigSubtitle: 'vaš slogan ovdje'
|
||||
SiteConfigTitle: 'Naziv vašeg weba'
|
||||
TabTitleConfig: Konfiguracija
|
||||
ValidateTitle: 'Molimo dodajte "Naslov"'
|
||||
SubsiteAdmin:
|
||||
MENUTITLE: Podsajtovi
|
||||
SubsiteDomain:
|
||||
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_DESCRIPTION: 'Označi kao zadanu domenu za ovu podstranicu'
|
||||
PROTOCOL_HTTP: 'http://'
|
||||
PROTOCOL_HTTPS: 'https://'
|
||||
Protocol: Protokol
|
||||
SINGULARNAME: 'Domena podsajta'
|
||||
SubsiteReportWrapper:
|
||||
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
|
||||
SubsiteXHRController:
|
||||
MENUTITLE: SubsiteXHRController
|
||||
ReportDropdownSubsite: Podsajt
|
||||
Subsite:
|
||||
COPYSTRUCTURE: 'Kopiraj strukturu od:'
|
||||
NOTEMPLATE: 'Nema predloška'
|
||||
Subsites:
|
||||
DefaultSiteFieldLabel: 'Zadani sajt'
|
||||
DomainFieldLabel: Domena
|
||||
@ -71,10 +85,3 @@ hr:
|
||||
RedirectURLFieldLabel: 'Link preusmjeravanja'
|
||||
ThemeFieldLabel: Tema
|
||||
TitleFieldLabel: 'Naziv podsajta'
|
||||
SubsitesVirtualPage:
|
||||
DESCRIPTION: 'Prikazuje sadržaj stranice na drugom podsajtu'
|
||||
PLURALNAME: 'Bazna stranica'
|
||||
SINGULARNAME: 'Virtualna stranica podsajta'
|
||||
SubsiteField: Podsajt
|
||||
VirtualPage:
|
||||
EDITCONTENT: 'Klikni ovdje za uređivanje sadržaja'
|
||||
|
33
lang/id.yml
33
lang/id.yml
@ -3,6 +3,38 @@ id:
|
||||
SubsiteFieldLabel: Subsitus
|
||||
GroupSubsites:
|
||||
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:
|
||||
CustomMetaDescription: Deskripsi
|
||||
CustomMetaKeywords: 'Kata kunci'
|
||||
@ -15,5 +47,6 @@ id:
|
||||
ReportDropdown: Situs
|
||||
Subsites:
|
||||
LanguageFieldLabel: Bahasa
|
||||
ThemeFieldLabel: Tema
|
||||
SubsitesVirtualPage:
|
||||
SubsiteField: Subsitus
|
||||
|
87
lang/it.yml
Normal file
87
lang/it.yml
Normal file
@ -0,0 +1,87 @@
|
||||
it:
|
||||
DomainNameField:
|
||||
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:
|
||||
COPYSTRUCTURE: 'Copia struttura da:'
|
||||
NOTEMPLATE: 'Nessuno schema'
|
||||
Subsites:
|
||||
DefaultSiteFieldLabel: 'Sito di default'
|
||||
DomainFieldLabel: Dominio
|
||||
IsPublicFieldLabel: 'Permetti accesso pubblico'
|
||||
LanguageFieldLabel: Lingua
|
||||
MainSiteTitle: 'Sito principale'
|
||||
PageTypeBlacklistFieldLabel: 'Blacklist dei tipi di pagina'
|
||||
PrimaryDomainFieldLabel: 'Dominio primario'
|
||||
RedirectURLFieldLabel: 'URL di reindirizzamento'
|
||||
ThemeFieldLabel: Tema
|
||||
TitleFieldLabel: 'Nome sottosito'
|
50
lang/ja.yml
50
lang/ja.yml
@ -1,16 +1,60 @@
|
||||
ja:
|
||||
GridFieldAddFromTemplateButton:
|
||||
AddFromTemplate: 'テンプレートから新しく追加'
|
||||
AddFromTemplate: テンプレートから新しく追加
|
||||
GroupSubsites:
|
||||
ACCESSALL: '全てのサブサイト'
|
||||
ACCESSALL: 全てのサブサイト
|
||||
ACCESSONLY: これらのサブサイトのみ
|
||||
ACCESSRADIOTITLE: 'このグループに選択先へのアクセス権を与える'
|
||||
ACCESSRADIOTITLE: このグループに選択先へのアクセス権を与える
|
||||
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:
|
||||
MENUTITLE: サブサイト
|
||||
SubsiteDomain:
|
||||
DOMAIN: ドメイン
|
||||
PLURALNAME: サブサイトのドメイン
|
||||
SINGULARNAME: サブサイトのドメイン
|
||||
Subsites:
|
||||
DomainFieldLabel: ドメイン
|
||||
LanguageFieldLabel: 言語
|
||||
ThemeFieldLabel: テーマ
|
||||
SubsitesVirtualPage:
|
||||
SINGULARNAME: サブサイトの仮想ページ
|
||||
|
38
lang/lt.yml
38
lang/lt.yml
@ -1,37 +1,37 @@
|
||||
lt:
|
||||
FileSubsites:
|
||||
SilverStripe\Subsites\Extensions\FolderFormFactoryExtension:
|
||||
AllSitesDropdownOpt: 'Visos svetainės'
|
||||
GridFieldAddFromTemplateButton:
|
||||
AddFromTemplate: 'Sukurti naują pagal šabloną'
|
||||
LeftAndMainSubsites:
|
||||
SilverStripe\Subsites\Extensions\LeftAndMainSubsites:
|
||||
Saved: 'Išsaugota, prašome atnaujinti susijusius puslapius'
|
||||
LeftAndMain_Menu:
|
||||
Hello: Sveiki
|
||||
LOGOUT: Atsijungti
|
||||
SiteTreeSubsites:
|
||||
SilverStripe\Subsites\Extensions\SiteTreeSubsites:
|
||||
CopyAction: Kopijuoti
|
||||
CopyToSubsite: 'Kopijuoti puslapį į kitą svetainę'
|
||||
Subsite:
|
||||
COPYSTRUCTURE: 'Kopijuoti struktūrą iš:'
|
||||
SilverStripe\Subsites\Model\Subsite:
|
||||
ConfigurationTab: Nustatymai
|
||||
CopyMessage: 'Sukurta {title} kopija'
|
||||
CustomExtraMeta: 'Kitos meta žymės'
|
||||
CustomMetaDescription: Aprašymas
|
||||
CustomMetaKeywords: Raktažodžiai
|
||||
CustomMetaTitle: Pavadinimas
|
||||
DomainsHeadline: 'Šio puslapio domenai'
|
||||
DomainsListTitle: Domenai
|
||||
NOTEMPLATE: 'Nėra šablono'
|
||||
PageTypeBlacklistField: 'Neleidžiami puslapių tipai'
|
||||
SiteConfigSubtitle: 'Jūsų svetainės šūkis'
|
||||
SiteConfigTitle: 'Jūsų svetainės pavadinimas'
|
||||
TabTitleConfig: Nustatymai
|
||||
ValidateTitle: 'Prašome įvesti "Pavadinimą"'
|
||||
SubsiteDomain:
|
||||
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
|
||||
SubsiteReportWrapper:
|
||||
db_Domain: Domenas
|
||||
SilverStripe\Subsites\Reports\SubsiteReportWrapper:
|
||||
ReportDropdown: Svetainės
|
||||
SubsiteXHRController:
|
||||
MENUTITLE: SubsiteXHRController
|
||||
Subsite:
|
||||
COPYSTRUCTURE: 'Kopijuoti struktūrą iš:'
|
||||
NOTEMPLATE: 'Nėra šablono'
|
||||
Subsites:
|
||||
DefaultSiteFieldLabel: 'Pagrindinė svetainė'
|
||||
DomainFieldLabel: Domenas
|
||||
@ -41,5 +41,3 @@ lt:
|
||||
PrimaryDomainFieldLabel: 'Pagrindinis domenas'
|
||||
RedirectURLFieldLabel: 'Nukreipimo nuoroda'
|
||||
ThemeFieldLabel: Tema
|
||||
SubsitesVirtualPage:
|
||||
PLURALNAME: 'Baziniai puslapiai'
|
||||
|
63
lang/mi.yml
63
lang/mi.yml
@ -1,12 +1,15 @@
|
||||
mi:
|
||||
ASSETADMIN:
|
||||
SUBSITENOTICE: 'Ka taea ngā kōpaki me ngā kōnae kua hangaia i te pae matua te uru mā ngā pae iti katoa.'
|
||||
FileSubsites:
|
||||
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'
|
||||
GridFieldAddFromTemplateButton:
|
||||
AddFromTemplate: 'Tāpiri Hōu mai i te Tātauira'
|
||||
GroupSubsites:
|
||||
SilverStripe\Subsites\Extensions\GroupSubsites:
|
||||
ACCESSALL: 'Ngā pae iti katoa'
|
||||
ACCESSONLY: 'Ko ēnei pae iti anake'
|
||||
ACCESSRADIOTITLE: 'Tukuna tēnei rōpū kia uru ki'
|
||||
@ -14,40 +17,52 @@ mi:
|
||||
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'
|
||||
LeftAndMainSubsites:
|
||||
many_many_Subsites: 'Ngā pae iti'
|
||||
SilverStripe\Subsites\Extensions\LeftAndMainSubsites:
|
||||
Saved: 'Kua tiakina, whakahoutia ngā whārangi pāhono.'
|
||||
SiteTreeSubsites:
|
||||
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'
|
||||
Subsite:
|
||||
COPYSTRUCTURE: 'Tāurutia te hanganga mai i:'
|
||||
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
|
||||
DOMAINSAVEFIRST: 'Ka taea noa iho te tāpiri rohe i muri i te tiakinga tuatahitanga'
|
||||
DomainsHeadline: 'Ngā rohe mō tēnei pae iti'
|
||||
DomainsListTitle: 'Ngā Rohe'
|
||||
IsPublicHeaderField: 'Pae iti hohe'
|
||||
NOTEMPLATE: 'Kāore he tātauira'
|
||||
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'
|
||||
TabTitleConfig: Whirihoranga
|
||||
ValidateTitle: 'Tāurua he "Taitara"'
|
||||
SubsiteAdmin:
|
||||
MENUTITLE: 'Ngā pae iti'
|
||||
SubsiteDomain:
|
||||
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'
|
||||
SubsiteReportWrapper:
|
||||
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'
|
||||
SubsiteXHRController:
|
||||
MENUTITLE: ManaXHRPaeiti
|
||||
ReportDropdownSubsite: 'Pae iti'
|
||||
Subsite:
|
||||
COPYSTRUCTURE: 'Tāurutia te hanganga mai i:'
|
||||
NOTEMPLATE: 'Kāore he tātauira'
|
||||
Subsites:
|
||||
DefaultSiteFieldLabel: 'Pae taunoa'
|
||||
DomainFieldLabel: Rohe
|
||||
@ -59,7 +74,3 @@ mi:
|
||||
RedirectURLFieldLabel: 'Tukua anō te URL'
|
||||
ThemeFieldLabel: Kaupapa
|
||||
TitleFieldLabel: 'Ingoa Pae Iti'
|
||||
SubsitesVirtualPage:
|
||||
DESCRIPTION: 'Ka whakaatu i ngā ihirangi o tētahi whārangi ki tētahi atu pae iti'
|
||||
SINGULARNAME: 'Whārangi Mariko Pae Iti'
|
||||
SubsiteField: 'Pae iti'
|
||||
|
@ -6,11 +6,46 @@ nb_NO:
|
||||
ACCESSONLY: 'Only these subsites'
|
||||
ACCESSRADIOTITLE: 'Give this group access to'
|
||||
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:
|
||||
MENUTITLE: Underdomener
|
||||
SubsiteDomain:
|
||||
DOMAIN: Domain
|
||||
PLURALNAME: 'Subsite Domains'
|
||||
SINGULARNAME: 'Subsite Domain'
|
||||
Subsites:
|
||||
DomainFieldLabel: Domain
|
||||
SubsitesVirtualPage:
|
||||
SINGULARNAME: 'Subdomeners Virtuelle Side'
|
||||
|
103
lang/nl.yml
103
lang/nl.yml
@ -1,32 +1,103 @@
|
||||
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
|
||||
GridFieldAddFromTemplateButton:
|
||||
AddFromTemplate: 'Nieuwe toevegen vanaf template'
|
||||
GroupSubsites:
|
||||
SilverStripe\Subsites\Extensions\GroupSubsites:
|
||||
ACCESSALL: 'Alle subsites'
|
||||
ACCESSONLY: 'Alleen deze subsites'
|
||||
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
|
||||
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
|
||||
CustomMetaKeywords: Sleutelwoorden
|
||||
CustomMetaTitle: Titel
|
||||
PLURALNAME: Subsites
|
||||
PLURALS:
|
||||
one: 'Een Subsite'
|
||||
other: '{count} Subsites'
|
||||
PageTypeBlacklistField: 'Niet toegestane paginatypes?'
|
||||
SINGULARNAME: Subsite
|
||||
SiteConfigSubtitle: 'Jouw slagzin hier'
|
||||
SiteConfigTitle: 'Jouw Site Naam'
|
||||
TabTitleConfig: Configuratie
|
||||
SubsiteAdmin:
|
||||
MENUTITLE: Subsites
|
||||
SubsiteDomain:
|
||||
ThemeFieldEmptyString: '-'
|
||||
ValidateTitle: 'Voeg een "Titel" toe'
|
||||
belongs_many_many_Groups: Groepen
|
||||
db_DefaultSite: 'Standaard Site'
|
||||
db_Language: Taal
|
||||
db_RedirectURL: 'Redirect URL'
|
||||
db_Theme: Thema
|
||||
db_Title: Titel
|
||||
SilverStripe\Subsites\Model\SubsiteDomain:
|
||||
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'
|
||||
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'
|
||||
Subsites:
|
||||
DefaultSiteFieldLabel: 'Standaard Site'
|
||||
LanguageFieldLabel: Taal
|
||||
PrimaryDomainFieldLabel: 'Primaire domein'
|
||||
ThemeFieldLabel: Thema
|
||||
TitleFieldLabel: 'Subsite naam'
|
||||
SubsitesVirtualPage:
|
||||
db_Domain: Domein
|
||||
db_Protocol: Protocol
|
||||
has_one_Subsite: Subsite
|
||||
SilverStripe\Subsites\Pages\SubsitesVirtualPage:
|
||||
DESCRIPTION: 'Toon de inhoud van een pagina op een andere subsite'
|
||||
OverrideNote: 'Overschrijft de overgenomen tekst van de gelinkte pagina'
|
||||
PLURALNAME: 'Subsites Virtuele paginas'
|
||||
PLURALS:
|
||||
one: 'Subsites Virtuele pagina'
|
||||
other: '{count} Subsites Virtuele paginas'
|
||||
SINGULARNAME: 'Subsites Virtuele pagina'
|
||||
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
13
lang/pl.yml
Normal 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
|
@ -6,11 +6,51 @@ pl_PL:
|
||||
ACCESSONLY: 'Tylko te podwitryny'
|
||||
ACCESSRADIOTITLE: 'Daj tej grupie dostęp do'
|
||||
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:
|
||||
MENUTITLE: Podwitryny
|
||||
SubsiteDomain:
|
||||
DOMAIN: Domena
|
||||
PLURALNAME: 'Domeny podwitryny'
|
||||
SINGULARNAME: 'Domena podwitryny'
|
||||
Subsites:
|
||||
DomainFieldLabel: Domena
|
||||
LanguageFieldLabel: Język
|
||||
SubsitesVirtualPage:
|
||||
SINGULARNAME: 'Wirtualna strona dla podwitryn'
|
||||
|
71
lang/ru.yml
71
lang/ru.yml
@ -1,14 +1,17 @@
|
||||
ru:
|
||||
ASSETADMIN:
|
||||
SUBSITENOTICE: 'Папки и файлы созданные на основном сайте так же доступны для под-сайтов.'
|
||||
DomainNameField:
|
||||
INVALID_DOMAIN: 'Неверный домен'
|
||||
FileSubsites:
|
||||
SilverStripe\CMS\Model\VirtualPage:
|
||||
EDITCONTENT: 'Нажмите для изменения содержимого'
|
||||
SilverStripe\Subsites\Admin\SubsiteAdmin:
|
||||
MENUTITLE: Подсайты
|
||||
SilverStripe\Subsites\Extensions\FileSubsites:
|
||||
has_one_Subsite: Подсайт
|
||||
SilverStripe\Subsites\Extensions\FolderFormFactoryExtension:
|
||||
AllSitesDropdownOpt: 'Все сайты'
|
||||
SUBSITENOTICE: 'Папки и файлы созданные на основном сайте так же доступны для под-сайтов.'
|
||||
SubsiteFieldLabel: Подсайт
|
||||
GridFieldAddFromTemplateButton:
|
||||
AddFromTemplate: 'Добавить из шаблона'
|
||||
GroupSubsites:
|
||||
SilverStripe\Subsites\Extensions\GroupSubsites:
|
||||
ACCESSALL: 'Все подсайты'
|
||||
ACCESSONLY: 'Только эти подсайты'
|
||||
ACCESSRADIOTITLE: 'Разрешить этой группе доступ к'
|
||||
@ -16,52 +19,61 @@ ru:
|
||||
MANAGE_SUBSITES: 'Управление подсайтами для групп'
|
||||
MANAGE_SUBSITES_HELP: 'Возможность ограничить права доступа для группы к одному и более подсайтам'
|
||||
SECURITYTABTITLE: Подсайты
|
||||
LeftAndMainSubsites:
|
||||
many_many_Subsites: Подсайты
|
||||
SilverStripe\Subsites\Extensions\LeftAndMainSubsites:
|
||||
Saved: 'Сохранено, пожалуйста обновите связанные группы'
|
||||
LeftAndMain_Menu:
|
||||
Hello: Здравствуйте
|
||||
LOGOUT: 'Выход'
|
||||
SiteTreeSubsites:
|
||||
SilverStripe\Subsites\Extensions\SiteConfigSubsites:
|
||||
has_one_Subsite: Подсайт
|
||||
SilverStripe\Subsites\Extensions\SiteTreeSubsites:
|
||||
CopyAction: Копировать
|
||||
CopyToSubsite: 'Копировать страницу на подсайт'
|
||||
CopyToSubsiteWithChildren: 'Включая под-страницы?'
|
||||
SubsiteOperations: 'Операции над подсайтами'
|
||||
Subsite:
|
||||
COPYSTRUCTURE: 'Скопировать структуры из:'
|
||||
has_one_Subsite: Подсайт
|
||||
SilverStripe\Subsites\Model\Subsite:
|
||||
ConfigurationTab: Конфигурация
|
||||
CopyMessage: 'Создана копия {title}'
|
||||
CustomExtraMeta: 'Пользовательские мета-тэги'
|
||||
CustomMetaDescription: Описание
|
||||
CustomMetaKeywords: 'Ключевые слова'
|
||||
CustomMetaTitle: Заголовок
|
||||
DOMAINSAVEFIRST: 'Вы можете добавлять домены только после первого сохранения'
|
||||
DomainsHeadline: 'Домены для этого подсайта'
|
||||
DomainsListTitle: Домены
|
||||
IsPublicHeaderField: 'Активный подсайт'
|
||||
NOTEMPLATE: 'Нет шаблона'
|
||||
PLURALNAME: Подсайты
|
||||
PageTypeBlacklistField: 'Запретить данные типы страниц?'
|
||||
SINGULARNAME: Подсайт
|
||||
SiteConfigSubtitle: 'ваш слоган здесь'
|
||||
SiteConfigTitle: 'Название сайта'
|
||||
TabTitleConfig: Конфигурация
|
||||
ValidateTitle: 'Пожалуйста, добавьте "Заголовок"'
|
||||
SubsiteAdmin:
|
||||
MENUTITLE: Подсайты
|
||||
SubsiteDomain:
|
||||
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_DESCRIPTION: 'Отметить как основной домен для данного подсайта'
|
||||
PROTOCOL_HTTP: 'http://'
|
||||
PROTOCOL_HTTPS: 'https://'
|
||||
Protocol: Протокол
|
||||
SINGULARNAME: 'Домен подсайта'
|
||||
SubsiteReportWrapper:
|
||||
db_Domain: Домен
|
||||
db_Protocol: Протокол
|
||||
has_one_Subsite: Подсайт
|
||||
SilverStripe\Subsites\Pages\SubsitesVirtualPage:
|
||||
DESCRIPTION: 'Отображает содержимое выбранной страницы на другом подсайте'
|
||||
SINGULARNAME: 'Виртуальная страница подсайта'
|
||||
SubsiteField: Подсайт
|
||||
SilverStripe\Subsites\Reports\SubsiteReportWrapper:
|
||||
ReportDropdown: Сайты
|
||||
SubsiteXHRController:
|
||||
MENUTITLE: SubsiteXHRController
|
||||
ReportDropdownSubsite: Подсайт
|
||||
Subsite:
|
||||
COPYSTRUCTURE: 'Скопировать структуры из:'
|
||||
NOTEMPLATE: 'Нет шаблона'
|
||||
Subsites:
|
||||
DefaultSiteFieldLabel: 'Основной сайт'
|
||||
DomainFieldLabel: Домен
|
||||
@ -73,10 +85,3 @@ ru:
|
||||
RedirectURLFieldLabel: 'Ссылка для перенаправления'
|
||||
ThemeFieldLabel: Оформление
|
||||
TitleFieldLabel: 'Название Подсайта'
|
||||
SubsitesVirtualPage:
|
||||
DESCRIPTION: 'Отображает содержимое выбранной страницы на другом подсайте'
|
||||
PLURALNAME: 'Базовые страницы'
|
||||
SINGULARNAME: 'Виртуальная страница подсайта'
|
||||
SubsiteField: Подсайт
|
||||
VirtualPage:
|
||||
EDITCONTENT: 'Нажмите для изменения содержимого'
|
||||
|
37
lang/sk.yml
Normal file
37
lang/sk.yml
Normal 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
35
lang/sl.yml
Normal 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
13
lang/sr_RS@latin.yml
Normal 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
14
lang/sv.yml
Normal 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
13
lang/tr.yml
Normal file
@ -0,0 +1,13 @@
|
||||
tr:
|
||||
SilverStripe\Subsites\Model\Subsite:
|
||||
CustomExtraMeta: 'Kişisel Meta Etiketleri'
|
||||
CustomMetaDescription: Açı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
|
@ -6,11 +6,45 @@ tr_TR:
|
||||
ACCESSONLY: 'Only these subsites'
|
||||
ACCESSRADIOTITLE: 'Give this group access to'
|
||||
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:
|
||||
MENUTITLE: 'Alt Siteler'
|
||||
SubsiteDomain:
|
||||
DOMAIN: Domain
|
||||
PLURALNAME: 'Subsite Domains'
|
||||
SINGULARNAME: 'Subsite Domain'
|
||||
Subsites:
|
||||
DomainFieldLabel: Domain
|
||||
SubsitesVirtualPage:
|
||||
SINGULARNAME: 'Alt Site Sanal Sayfa'
|
||||
|
85
lang/zh.yml
85
lang/zh.yml
@ -1,57 +1,72 @@
|
||||
zh:
|
||||
ASSETADMIN:
|
||||
SUBSITENOTICE: 主网站上创建的文件夹和文件可以被所有子网站访问。
|
||||
FileSubsites:
|
||||
SilverStripe\CMS\Model\VirtualPage:
|
||||
EDITCONTENT: 点击这里来编辑内容
|
||||
SilverStripe\Subsites\Admin\SubsiteAdmin:
|
||||
MENUTITLE: 多个子网站
|
||||
SilverStripe\Subsites\Extensions\FileSubsites:
|
||||
has_one_Subsite: 子网站
|
||||
SilverStripe\Subsites\Extensions\FolderFormFactoryExtension:
|
||||
AllSitesDropdownOpt: 所有网站
|
||||
SUBSITENOTICE: 主网站上创建的文件夹和文件可以被所有子网站访问。
|
||||
SubsiteFieldLabel: 子网站
|
||||
GridFieldAddFromTemplateButton:
|
||||
AddFromTemplate: '从模板中取出新加入'
|
||||
GroupSubsites:
|
||||
SilverStripe\Subsites\Extensions\GroupSubsites:
|
||||
ACCESSALL: 所有子网站
|
||||
ACCESSONLY: '仅这些子网站'
|
||||
ACCESSRADIOTITLE: '准许该群进入'
|
||||
GlobalGroup: '全局小组'
|
||||
ACCESSONLY: 仅这些子网站
|
||||
ACCESSRADIOTITLE: 准许该群进入
|
||||
GlobalGroup: 全局小组
|
||||
MANAGE_SUBSITES: 管理小组的子网站
|
||||
MANAGE_SUBSITES_HELP: 能够将权限限制在一个小组、一个或多个子网站。
|
||||
SECURITYTABTITLE: 多个子网站
|
||||
LeftAndMainSubsites:
|
||||
Saved: '已保存,请更新相关的页面。'
|
||||
SiteTreeSubsites:
|
||||
many_many_Subsites: 多个子网站
|
||||
SilverStripe\Subsites\Extensions\LeftAndMainSubsites:
|
||||
Saved: 已保存,请更新相关的页面。
|
||||
SilverStripe\Subsites\Extensions\SiteConfigSubsites:
|
||||
has_one_Subsite: 子网站
|
||||
SilverStripe\Subsites\Extensions\SiteTreeSubsites:
|
||||
CopyAction: 复制
|
||||
CopyToSubsite: 将页面复制到子网站
|
||||
Subsite:
|
||||
COPYSTRUCTURE: 复制结构来自:
|
||||
has_one_Subsite: 子网站
|
||||
SilverStripe\Subsites\Model\Subsite:
|
||||
ConfigurationTab: 配置
|
||||
CopyMessage: '已创建一个 {title} 的副本'
|
||||
CustomExtraMeta: '自定义 Meta 标签'
|
||||
CustomMetaDescription: 说明
|
||||
CustomMetaKeywords: '关键词'
|
||||
CustomMetaTitle: '标题'
|
||||
DOMAINSAVEFIRST: '只有当您第一次保存后,才能添加域名'
|
||||
DomainsHeadline: 这个子网站的域名
|
||||
DomainsListTitle: 域名
|
||||
IsPublicHeaderField: 活跃的子网站
|
||||
NOTEMPLATE: 没有模板
|
||||
CustomExtraMeta: 自定义Meta标签
|
||||
CustomMetaDescription: 描述
|
||||
CustomMetaKeywords: 关键词
|
||||
CustomMetaTitle: 题目
|
||||
PLURALNAME: 多个子网站
|
||||
PageTypeBlacklistField: 禁止页面类型?
|
||||
SINGULARNAME: 子网站
|
||||
SiteConfigSubtitle: '您的标语在这里'
|
||||
SiteConfigSubtitle: 您的标语在这里
|
||||
SiteConfigTitle: 您的网站名称
|
||||
TabTitleConfig: '配置'
|
||||
ValidateTitle: '请添加一个“标题”'
|
||||
SubsiteAdmin:
|
||||
MENUTITLE: 多个子网站
|
||||
SubsiteDomain:
|
||||
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: 子网站域名
|
||||
SubsiteReportWrapper:
|
||||
db_Domain: 域名
|
||||
has_one_Subsite: 子网站
|
||||
SilverStripe\Subsites\Pages\SubsitesVirtualPage:
|
||||
DESCRIPTION: 显示另一个子网站上一个页面的内容
|
||||
PLURALNAME: 基本页面
|
||||
SINGULARNAME: 子网站虚拟页面
|
||||
SubsiteField: 子网站
|
||||
SilverStripe\Subsites\Reports\SubsiteReportWrapper:
|
||||
ReportDropdown: 网站
|
||||
SubsiteXHRController:
|
||||
MENUTITLE: '子网站 XHR 控制器'
|
||||
ReportDropdownSubsite: 子网站
|
||||
Subsite:
|
||||
COPYSTRUCTURE: 复制结构来自:
|
||||
NOTEMPLATE: 没有模板
|
||||
Subsites:
|
||||
DefaultSiteFieldLabel: 默认网站
|
||||
DomainFieldLabel: 域名
|
||||
IsPublicFieldLabel: '启用公共访问权'
|
||||
IsPublicFieldLabel: 启用公共访问权
|
||||
LanguageFieldLabel: 语言
|
||||
MainSiteTitle: 主网站
|
||||
PageTypeBlacklistFieldLabel: 页面类型黑名单
|
||||
@ -59,7 +74,3 @@ zh:
|
||||
RedirectURLFieldLabel: '重定向 URL'
|
||||
ThemeFieldLabel: 主题
|
||||
TitleFieldLabel: 子网站名称
|
||||
SubsitesVirtualPage:
|
||||
DESCRIPTION: '显示另一个子网站上一个页面的内容'
|
||||
SINGULARNAME: 子网站虚拟页面
|
||||
SubsiteField: 子网站
|
||||
|
12
license.md
Normal file
12
license.md
Normal file
@ -0,0 +1,12 @@
|
||||
Copyright (c) 2016, SilverStripe Limited
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
14
phpcs.xml.dist
Normal file
14
phpcs.xml.dist
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ruleset name="SilverStripe">
|
||||
<description>CodeSniffer ruleset for SilverStripe coding conventions.</description>
|
||||
|
||||
<file>src</file>
|
||||
<file>tests</file>
|
||||
|
||||
<!-- base rules are PSR-2 -->
|
||||
<rule ref="PSR2" >
|
||||
<!-- Current exclusions -->
|
||||
<exclude name="PSR1.Methods.CamelCapsMethodName" />
|
||||
<exclude name="PSR1.Files.SideEffects.FoundWithSymbols" />
|
||||
</rule>
|
||||
</ruleset>
|
17
phpunit.xml.dist
Normal file
17
phpunit.xml.dist
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit bootstrap="vendor/silverstripe/cms/tests/bootstrap.php" colors="true">
|
||||
<testsuites>
|
||||
<testsuite name="Default">
|
||||
<directory>tests/php</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
|
||||
<filter>
|
||||
<whitelist addUncoveredFilesFromWhitelist="true">
|
||||
<directory suffix=".php">src/</directory>
|
||||
<exclude>
|
||||
<directory suffix=".php">tests/</directory>
|
||||
</exclude>
|
||||
</whitelist>
|
||||
</filter>
|
||||
</phpunit>
|
43
src/Admin/SubsiteAdmin.php
Normal file
43
src/Admin/SubsiteAdmin.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Admin;
|
||||
|
||||
use SilverStripe\Admin\ModelAdmin;
|
||||
use SilverStripe\Forms\GridField\GridFieldDetailForm;
|
||||
use SilverStripe\Forms\GridField\GridFieldPaginator;
|
||||
use SilverStripe\Subsites\Forms\GridFieldSubsiteDetailForm;
|
||||
use SilverStripe\Subsites\Model\Subsite;
|
||||
|
||||
/**
|
||||
* Admin interface to manage and create {@link Subsite} instances.
|
||||
*
|
||||
* @package subsites
|
||||
*/
|
||||
class SubsiteAdmin extends ModelAdmin
|
||||
{
|
||||
private static $managed_models = [Subsite::class];
|
||||
|
||||
private static $url_segment = 'subsites';
|
||||
|
||||
private static $menu_title = 'Subsites';
|
||||
|
||||
private static $menu_icon_class = 'font-icon-tree';
|
||||
|
||||
public $showImportForm = false;
|
||||
|
||||
private static $tree_class = Subsite::class;
|
||||
|
||||
public function getEditForm($id = null, $fields = null)
|
||||
{
|
||||
$form = parent::getEditForm($id, $fields);
|
||||
|
||||
$grid = $form->Fields()->dataFieldByName(str_replace('\\', '-', Subsite::class));
|
||||
if ($grid) {
|
||||
$grid->getConfig()->getComponentByType(GridFieldPaginator::class)->setItemsPerPage(100);
|
||||
$grid->getConfig()->removeComponentsByType(GridFieldDetailForm::class);
|
||||
$grid->getConfig()->addComponent(new GridFieldSubsiteDetailForm());
|
||||
}
|
||||
|
||||
return $form;
|
||||
}
|
||||
}
|
@ -1,20 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Controller;
|
||||
|
||||
use SilverStripe\Admin\LeftAndMain;
|
||||
use SilverStripe\Security\Member;
|
||||
use SilverStripe\Security\Permission;
|
||||
use SilverStripe\Subsites\Model\Subsite;
|
||||
|
||||
/**
|
||||
* Section-agnostic PJAX controller.
|
||||
*/
|
||||
class SubsiteXHRController extends LeftAndMain
|
||||
{
|
||||
private static $url_segment = 'subsite_xhr';
|
||||
|
||||
private static $ignore_menuitem = true;
|
||||
|
||||
/**
|
||||
* Relax the access permissions, so anyone who has access to any CMS subsite can access this controller.
|
||||
* @param Member|null $member
|
||||
* @return bool
|
||||
*/
|
||||
public function canView($member = null)
|
||||
{
|
||||
if (parent::canView()) {
|
||||
if (parent::canView($member)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Subsite::all_accessible_sites()->count()>0) {
|
||||
if (Subsite::all_accessible_sites()->count() > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -27,20 +40,19 @@ class SubsiteXHRController extends LeftAndMain
|
||||
public function canAccess()
|
||||
{
|
||||
// Allow if any cms access is available
|
||||
return Permission::check(array(
|
||||
return Permission::check([
|
||||
'CMS_ACCESS', // Supported by 3.1.14 and up
|
||||
'CMS_ACCESS_LeftAndMain'
|
||||
));
|
||||
]);
|
||||
}
|
||||
|
||||
public function getResponseNegotiator()
|
||||
{
|
||||
$negotiator = parent::getResponseNegotiator();
|
||||
$self = $this;
|
||||
|
||||
// Register a new callback
|
||||
$negotiator->setCallback('SubsiteList', function () use (&$self) {
|
||||
return $self->SubsiteList();
|
||||
$negotiator->setCallback('SubsiteList', function () {
|
||||
return $this->SubsiteList();
|
||||
});
|
||||
|
||||
return $negotiator;
|
||||
@ -51,6 +63,6 @@ class SubsiteXHRController extends LeftAndMain
|
||||
*/
|
||||
public function SubsiteList()
|
||||
{
|
||||
return $this->renderWith('SubsiteList');
|
||||
return $this->renderWith(['type' => 'Includes', self::class . '_subsitelist']);
|
||||
}
|
||||
}
|
33
src/Extensions/BaseElementSubsites.php
Normal file
33
src/Extensions/BaseElementSubsites.php
Normal 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);
|
||||
}
|
||||
}
|
16
src/Extensions/CMSPageAddControllerExtension.php
Normal file
16
src/Extensions/CMSPageAddControllerExtension.php
Normal file
@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Extensions;
|
||||
|
||||
use SilverStripe\Core\Extension;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\HiddenField;
|
||||
use SilverStripe\Subsites\State\SubsiteState;
|
||||
|
||||
class CMSPageAddControllerExtension extends Extension
|
||||
{
|
||||
public function updatePageOptions(FieldList $fields)
|
||||
{
|
||||
$fields->push(HiddenField::create('SubsiteID', 'SubsiteID', SubsiteState::singleton()->getSubsiteId()));
|
||||
}
|
||||
}
|
@ -1,4 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Extensions;
|
||||
|
||||
use SilverStripe\Core\Extension;
|
||||
use SilverStripe\Subsites\Model\Subsite;
|
||||
use SilverStripe\View\SSViewer;
|
||||
|
||||
/**
|
||||
* @package subsites
|
||||
*/
|
||||
@ -8,7 +15,7 @@ class ControllerSubsites extends Extension
|
||||
{
|
||||
if ($subsite = Subsite::currentSubsite()) {
|
||||
if ($theme = $subsite->Theme) {
|
||||
SSViewer::set_theme($theme);
|
||||
SSViewer::set_themes([$theme, SSViewer::DEFAULT_THEME]);
|
||||
}
|
||||
}
|
||||
}
|
65
src/Extensions/ErrorPageSubsite.php
Normal file
65
src/Extensions/ErrorPageSubsite.php
Normal file
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Extensions;
|
||||
|
||||
use SilverStripe\Assets\FileNameFilter;
|
||||
use SilverStripe\CMS\Model\SiteTree;
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\ORM\DataExtension;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\Subsites\Model\Subsite;
|
||||
|
||||
class ErrorPageSubsite extends DataExtension
|
||||
{
|
||||
/**
|
||||
* Alter file path to generated a static (static) error page file to handle error page template
|
||||
* on different sub-sites
|
||||
*
|
||||
* @see ErrorPage::get_error_filename()
|
||||
*
|
||||
* FIXME since {@link Subsite::currentSubsite()} partly relies on Session, viewing other sub-site (including
|
||||
* main site) between opening ErrorPage in the CMS and publish ErrorPage causes static error page to get
|
||||
* generated incorrectly.
|
||||
*
|
||||
* @param string $name
|
||||
* @param int $statusCode
|
||||
*/
|
||||
public function updateErrorFilename(&$name, &$statusCode)
|
||||
{
|
||||
$static_filepath = Config::inst()->get($this->owner->ClassName, 'static_filepath');
|
||||
$subdomainPart = '';
|
||||
|
||||
// Try to get current subsite from session
|
||||
$subsite = Subsite::currentSubsite();
|
||||
|
||||
// since this function is called from Page class before the controller is created, we have
|
||||
// to get subsite from domain instead
|
||||
if (!$subsite) {
|
||||
$subsiteID = Subsite::getSubsiteIDForDomain();
|
||||
if ($subsiteID != 0) {
|
||||
$subsite = DataObject::get_by_id(Subsite::class, $subsiteID);
|
||||
} else {
|
||||
$subsite = null;
|
||||
}
|
||||
}
|
||||
|
||||
if ($subsite) {
|
||||
$subdomain = $subsite->domain();
|
||||
$subdomainPart = "-{$subdomain}";
|
||||
}
|
||||
|
||||
// @todo implement Translatable namespace
|
||||
if (singleton(SiteTree::class)->hasExtension('Translatable')
|
||||
&& $locale
|
||||
&& $locale != Translatable::default_locale()
|
||||
) {
|
||||
$fileName = "error-{$statusCode}-{$locale}{$subdomainPart}.html";
|
||||
} else {
|
||||
$fileName= "error-{$statusCode}{$subdomainPart}.html";
|
||||
}
|
||||
|
||||
$fileName = FileNameFilter::create()->filter($fileName);
|
||||
|
||||
$name = implode('/', [$static_filepath, $fileName]);
|
||||
}
|
||||
}
|
140
src/Extensions/FileSubsites.php
Normal file
140
src/Extensions/FileSubsites.php
Normal file
@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Extensions;
|
||||
|
||||
use SilverStripe\Assets\File;
|
||||
use SilverStripe\ORM\DataExtension;
|
||||
use SilverStripe\ORM\DataQuery;
|
||||
use SilverStripe\ORM\Queries\SQLSelect;
|
||||
use SilverStripe\Security\Permission;
|
||||
use SilverStripe\Subsites\Model\Subsite;
|
||||
use SilverStripe\Subsites\State\SubsiteState;
|
||||
|
||||
/**
|
||||
* Extension for the File object to add subsites support
|
||||
*
|
||||
* @package subsites
|
||||
*/
|
||||
class FileSubsites extends DataExtension
|
||||
{
|
||||
/**
|
||||
* If this is set to true, all folders created will be default be considered 'global', unless set otherwise
|
||||
*
|
||||
* @config
|
||||
* @var bool
|
||||
*/
|
||||
private static $default_root_folders_global = false;
|
||||
|
||||
private static $has_one = [
|
||||
'Subsite' => Subsite::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* Amends the CMS tree title for folders in the Files & Images section.
|
||||
* Prefixes a '* ' to the folders that are accessible from all subsites.
|
||||
*/
|
||||
public function alternateTreeTitle()
|
||||
{
|
||||
if ($this->owner->SubsiteID == 0) {
|
||||
return ' * ' . $this->owner->Title;
|
||||
}
|
||||
|
||||
return $this->owner->Title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update any requests to limit the results to the current site
|
||||
* @param SQLSelect $query
|
||||
* @param DataQuery|null $dataQuery
|
||||
*/
|
||||
public function augmentSQL(SQLSelect $query, DataQuery $dataQuery = null)
|
||||
{
|
||||
if (Subsite::$disable_subsite_filter) {
|
||||
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)
|
||||
// @TODO I don't think excluding if SiteTree_ImageTracking is a good idea however because of the SS 3.0 api and
|
||||
// ManyManyList::removeAll() changing the from table after this function is called there isn't much of a choice
|
||||
|
||||
$from = $query->getFrom();
|
||||
if (isset($from['SiteTree_ImageTracking']) || $query->filtersOnID()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$subsiteID = SubsiteState::singleton()->getSubsiteId();
|
||||
if ($subsiteID === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The foreach is an ugly way of getting the first key :-)
|
||||
foreach ($query->getFrom() as $tableName => $info) {
|
||||
$where = "\"$tableName\".\"SubsiteID\" IN (0, $subsiteID)";
|
||||
$query->addWhere($where);
|
||||
break;
|
||||
}
|
||||
|
||||
$sect = array_values($query->getSelect() ?? []);
|
||||
$isCounting = strpos($sect[0] ?? '', 'COUNT') !== false;
|
||||
|
||||
// Ordering when deleting or counting doesn't apply
|
||||
if (!$isCounting) {
|
||||
$query->addOrderBy('"SubsiteID"');
|
||||
}
|
||||
}
|
||||
|
||||
public function onBeforeWrite()
|
||||
{
|
||||
if (!$this->owner->ID && !$this->owner->SubsiteID) {
|
||||
if ($this->owner->config()->get('default_root_folders_global')) {
|
||||
$this->owner->SubsiteID = 0;
|
||||
} else {
|
||||
$this->owner->SubsiteID = SubsiteState::singleton()->getSubsiteId();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function onAfterUpload()
|
||||
{
|
||||
// If we have a parent, use it's subsite as our subsite
|
||||
if ($this->owner->Parent()) {
|
||||
$this->owner->SubsiteID = $this->owner->Parent()->SubsiteID;
|
||||
} else {
|
||||
$this->owner->SubsiteID = SubsiteState::singleton()->getSubsiteId();
|
||||
}
|
||||
$this->owner->write();
|
||||
}
|
||||
|
||||
public function canEdit($member = null)
|
||||
{
|
||||
// Opt out of making opinions if no subsite ID is set yet
|
||||
if (!$this->owner->SubsiteID) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check the CMS_ACCESS_SecurityAdmin privileges on the subsite that owns this group
|
||||
$currentSubsiteID = SubsiteState::singleton()->getSubsiteId();
|
||||
if ($currentSubsiteID && $currentSubsiteID !== $this->owner->SubsiteID) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return SubsiteState::singleton()->withState(function (SubsiteState $newState) use ($member) {
|
||||
$newState->setSubsiteId($this->owner->SubsiteID);
|
||||
|
||||
return $this->owner->getPermissionChecker()->canEdit($this->owner->ID, $member);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a piece of text to keep DataObject cache keys appropriately specific
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function cacheKeyComponent()
|
||||
{
|
||||
return 'subsite-' . SubsiteState::singleton()->getSubsiteId();
|
||||
}
|
||||
}
|
47
src/Extensions/FolderFormFactoryExtension.php
Normal file
47
src/Extensions/FolderFormFactoryExtension.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Extensions;
|
||||
|
||||
use SilverStripe\Core\Extension;
|
||||
use SilverStripe\Forms\DropdownField;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\LiteralField;
|
||||
use SilverStripe\Subsites\Model\Subsite;
|
||||
|
||||
class FolderFormFactoryExtension extends Extension
|
||||
{
|
||||
/**
|
||||
* Add subsites-specific fields to the folder editor.
|
||||
* @param FieldList $fields
|
||||
*/
|
||||
public function updateFormFields(FieldList $fields)
|
||||
{
|
||||
$sites = Subsite::accessible_sites('CMS_ACCESS_AssetAdmin');
|
||||
$values = [];
|
||||
$values[0] = _t(__CLASS__ . '.AllSitesDropdownOpt', 'All sites');
|
||||
foreach ($sites as $site) {
|
||||
$values[$site->ID] = $site->Title;
|
||||
}
|
||||
ksort($values);
|
||||
if ($sites) {
|
||||
// Dropdown needed to move folders between subsites
|
||||
/** @var @skipUpgrade */
|
||||
$dropdown = DropdownField::create(
|
||||
'SubsiteID',
|
||||
_t(__CLASS__ . '.SubsiteFieldLabel', 'Subsite'),
|
||||
$values
|
||||
);
|
||||
$dropdown->addExtraClass('subsites-move-dropdown');
|
||||
$fields->push($dropdown);
|
||||
$fields->push(LiteralField::create(
|
||||
'Message',
|
||||
'<p class="alert alert-info">' .
|
||||
_t(
|
||||
__CLASS__ . '.SUBSITENOTICE',
|
||||
'Folders and files created in the main site are accessible by all subsites.'
|
||||
)
|
||||
. '</p>'
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
250
src/Extensions/GroupSubsites.php
Normal file
250
src/Extensions/GroupSubsites.php
Normal file
@ -0,0 +1,250 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Extensions;
|
||||
|
||||
use SilverStripe\Control\Cookie;
|
||||
use SilverStripe\Core\Convert;
|
||||
use SilverStripe\Forms\CheckboxSetField;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\OptionsetField;
|
||||
use SilverStripe\Forms\ReadonlyField;
|
||||
use SilverStripe\ORM\DataExtension;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\ORM\DataQuery;
|
||||
use SilverStripe\ORM\DB;
|
||||
use SilverStripe\ORM\Queries\SQLSelect;
|
||||
use SilverStripe\Security\Group;
|
||||
use SilverStripe\Security\PermissionProvider;
|
||||
use SilverStripe\Subsites\Model\Subsite;
|
||||
use SilverStripe\Subsites\State\SubsiteState;
|
||||
|
||||
/**
|
||||
* Extension for the Group object to add subsites support
|
||||
*
|
||||
* @package subsites
|
||||
*/
|
||||
class GroupSubsites extends DataExtension implements PermissionProvider
|
||||
{
|
||||
private static $db = [
|
||||
'AccessAllSubsites' => 'Boolean'
|
||||
];
|
||||
|
||||
private static $many_many = [
|
||||
'Subsites' => Subsite::class
|
||||
];
|
||||
|
||||
private static $defaults = [
|
||||
'AccessAllSubsites' => true
|
||||
];
|
||||
|
||||
/**
|
||||
* Migrations for GroupSubsites data.
|
||||
*/
|
||||
public function requireDefaultRecords()
|
||||
{
|
||||
if (!$this->owner) {
|
||||
return;
|
||||
}
|
||||
// Migration for Group.SubsiteID data from when Groups only had a single subsite
|
||||
$schema = DataObject::getSchema();
|
||||
$groupTable = Convert::raw2sql($schema->tableName(Group::class));
|
||||
$groupFields = DB::field_list($groupTable);
|
||||
|
||||
// Detection of SubsiteID field is the trigger for old-style-subsiteID migration
|
||||
if (isset($groupFields['SubsiteID'])) {
|
||||
// Migrate subsite-specific data
|
||||
DB::query('INSERT INTO "Group_Subsites" ("GroupID", "SubsiteID")
|
||||
SELECT "ID", "SubsiteID" FROM "' . $groupTable . '" WHERE "SubsiteID" > 0');
|
||||
|
||||
// Migrate global-access data
|
||||
DB::query('UPDATE "' . $groupTable . '" SET "AccessAllSubsites" = 1 WHERE "SubsiteID" = 0');
|
||||
|
||||
// Move the field out of the way so that this migration doesn't get executed again
|
||||
DB::get_schema()->renameField($groupTable, 'SubsiteID', '_obsolete_SubsiteID');
|
||||
|
||||
// No subsite access on anything means that we've just installed the subsites module.
|
||||
// Make all previous groups global-access groups
|
||||
} else {
|
||||
if (!DB::query('SELECT "Group"."ID" FROM "' . $groupTable . '"
|
||||
LEFT JOIN "Group_Subsites" ON "Group_Subsites"."GroupID" = "Group"."ID" AND "Group_Subsites"."SubsiteID" > 0
|
||||
WHERE "AccessAllSubsites" = 1
|
||||
OR "Group_Subsites"."GroupID" IS NOT NULL ')->value()
|
||||
) {
|
||||
DB::query('UPDATE "' . $groupTable . '" SET "AccessAllSubsites" = 1');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function updateCMSFields(FieldList $fields)
|
||||
{
|
||||
if ($this->owner->canEdit()) {
|
||||
// i18n tab
|
||||
$fields->findOrMakeTab('Root.Subsites', _t(__CLASS__ . '.SECURITYTABTITLE', 'Subsites'));
|
||||
|
||||
$subsites = Subsite::accessible_sites(['ADMIN', 'SECURITY_SUBSITE_GROUP'], true);
|
||||
$subsiteMap = $subsites->map();
|
||||
|
||||
// Prevent XSS injection
|
||||
$subsiteMap = Convert::raw2xml($subsiteMap->toArray());
|
||||
|
||||
// Interface is different if you have the rights to modify subsite group values on
|
||||
// all subsites
|
||||
if (isset($subsiteMap[0])) {
|
||||
$fields->addFieldToTab('Root.Subsites', new OptionsetField(
|
||||
'AccessAllSubsites',
|
||||
_t(__CLASS__ . '.ACCESSRADIOTITLE', 'Give this group access to'),
|
||||
[
|
||||
1 => _t(__CLASS__ . '.ACCESSALL', 'All subsites'),
|
||||
0 => _t(__CLASS__ . '.ACCESSONLY', 'Only these subsites'),
|
||||
]
|
||||
));
|
||||
|
||||
unset($subsiteMap[0]);
|
||||
$fields->addFieldToTab('Root.Subsites', new CheckboxSetField(
|
||||
'Subsites',
|
||||
'',
|
||||
$subsiteMap
|
||||
));
|
||||
} else {
|
||||
if (sizeof($subsiteMap ?? []) <= 1) {
|
||||
$fields->addFieldToTab('Root.Subsites', new ReadonlyField(
|
||||
'SubsitesHuman',
|
||||
_t(__CLASS__ . '.ACCESSRADIOTITLE', 'Give this group access to'),
|
||||
reset($subsiteMap)
|
||||
));
|
||||
} else {
|
||||
$fields->addFieldToTab('Root.Subsites', new CheckboxSetField(
|
||||
'Subsites',
|
||||
_t(__CLASS__ . '.ACCESSRADIOTITLE', 'Give this group access to'),
|
||||
$subsiteMap
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If this group belongs to a subsite, append the subsites title to the group title to make it easy to
|
||||
* distinguish in the tree-view of the security admin interface.
|
||||
*
|
||||
* @param string $title
|
||||
*/
|
||||
public function updateTreeTitle(&$title)
|
||||
{
|
||||
if ($this->owner->AccessAllSubsites) {
|
||||
$title = _t(__CLASS__ . '.GlobalGroup', 'global group');
|
||||
$title = htmlspecialchars($this->owner->Title ?? '', ENT_QUOTES) . ' <i>(' . $title . ')</i>';
|
||||
} else {
|
||||
$subsites = Convert::raw2xml(implode(', ', $this->owner->Subsites()->column('Title')));
|
||||
$title = htmlspecialchars($this->owner->Title ?? '') . " <i>($subsites)</i>";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update any requests to limit the results to the current site
|
||||
* @param SQLSelect $query
|
||||
* @param DataQuery|null $dataQuery
|
||||
*/
|
||||
public function augmentSQL(SQLSelect $query, DataQuery $dataQuery = null)
|
||||
{
|
||||
if (Subsite::$disable_subsite_filter) {
|
||||
return;
|
||||
}
|
||||
if (Cookie::get('noSubsiteFilter') == 'true') {
|
||||
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 (!$query->filtersOnID()) {
|
||||
$subsiteID = SubsiteState::singleton()->getSubsiteId();
|
||||
if ($subsiteID === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't filter by Group_Subsites if we've already done that
|
||||
$hasGroupSubsites = false;
|
||||
foreach ($query->getFrom() as $item) {
|
||||
if ((is_array($item) && strpos(
|
||||
$item['table'] ?? '',
|
||||
'Group_Subsites'
|
||||
) !== false) || (!is_array($item) && strpos(
|
||||
$item ?? '',
|
||||
'Group_Subsites'
|
||||
) !== false)
|
||||
) {
|
||||
$hasGroupSubsites = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$hasGroupSubsites) {
|
||||
if ($subsiteID) {
|
||||
$query->addLeftJoin('Group_Subsites', "\"Group_Subsites\".\"GroupID\"
|
||||
= \"Group\".\"ID\" AND \"Group_Subsites\".\"SubsiteID\" = $subsiteID");
|
||||
$query->addWhere('("Group_Subsites"."SubsiteID" IS NOT NULL OR
|
||||
"Group"."AccessAllSubsites" = 1)');
|
||||
} else {
|
||||
$query->addWhere('"Group"."AccessAllSubsites" = 1');
|
||||
}
|
||||
}
|
||||
|
||||
// WORKAROUND for databases that complain about an ORDER BY when the column wasn't selected
|
||||
// (e.g. SQL Server)
|
||||
$select = $query->getSelect();
|
||||
if (isset($select[0]) && !$select[0] == 'COUNT(*)') {
|
||||
$query->addOrderBy('AccessAllSubsites', 'DESC');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function onBeforeWrite()
|
||||
{
|
||||
// New record test approximated by checking whether the ID has changed.
|
||||
// Note also that the after write test is only used when we're *not* on a subsite
|
||||
if ($this->owner->isChanged('ID') && !SubsiteState::singleton()->getSubsiteId()) {
|
||||
$this->owner->AccessAllSubsites = 1;
|
||||
}
|
||||
}
|
||||
|
||||
public function onAfterWrite()
|
||||
{
|
||||
// New record test approximated by checking whether the ID has changed.
|
||||
// Note also that the after write test is only used when we're on a subsite
|
||||
if ($this->owner->isChanged('ID') && $currentSubsiteID = SubsiteState::singleton()->getSubsiteId()) {
|
||||
$subsites = $this->owner->Subsites();
|
||||
$subsites->add($currentSubsiteID);
|
||||
}
|
||||
}
|
||||
|
||||
public function alternateCanEdit()
|
||||
{
|
||||
// Find the sites that this group belongs to and the sites where we have appropriate perm.
|
||||
$accessibleSites = Subsite::accessible_sites('CMS_ACCESS_SecurityAdmin')->column('ID');
|
||||
$linkedSites = $this->owner->Subsites()->column('ID');
|
||||
|
||||
// We are allowed to access this site if at we have CMS_ACCESS_SecurityAdmin permission on
|
||||
// at least one of the sites
|
||||
return (bool)array_intersect($accessibleSites ?? [], $linkedSites);
|
||||
}
|
||||
|
||||
public function providePermissions()
|
||||
{
|
||||
return [
|
||||
'SECURITY_SUBSITE_GROUP' => [
|
||||
'name' => _t(__CLASS__ . '.MANAGE_SUBSITES', 'Manage subsites for groups'),
|
||||
'category' => _t(
|
||||
'SilverStripe\\Security\\Permission.PERMISSIONS_CATEGORY',
|
||||
'Roles and access permissions'
|
||||
),
|
||||
'help' => _t(
|
||||
__CLASS__ . '.MANAGE_SUBSITES_HELP',
|
||||
'Ability to limit the permissions for a group to one or more subsites.'
|
||||
),
|
||||
'sort' => 200
|
||||
]
|
||||
];
|
||||
}
|
||||
}
|
22
src/Extensions/HintsCacheKeyExtension.php
Normal file
22
src/Extensions/HintsCacheKeyExtension.php
Normal 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();
|
||||
}
|
||||
}
|
@ -1,12 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Extensions;
|
||||
|
||||
use SilverStripe\Admin\AdminRootController;
|
||||
use SilverStripe\Admin\CMSMenu;
|
||||
use SilverStripe\Admin\LeftAndMainExtension;
|
||||
use SilverStripe\CMS\Controllers\CMSPagesController;
|
||||
use SilverStripe\CMS\Model\SiteTree;
|
||||
use SilverStripe\CMS\Controllers\CMSPageEditController;
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\Core\Convert;
|
||||
use SilverStripe\Forms\HiddenField;
|
||||
use SilverStripe\ORM\ArrayList;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\Security\Member;
|
||||
use SilverStripe\Security\Permission;
|
||||
use SilverStripe\Security\Security;
|
||||
use SilverStripe\Subsites\Controller\SubsiteXHRController;
|
||||
use SilverStripe\Subsites\Model\Subsite;
|
||||
use SilverStripe\Subsites\State\SubsiteState;
|
||||
use SilverStripe\View\ArrayData;
|
||||
use SilverStripe\View\Requirements;
|
||||
|
||||
/**
|
||||
* Decorator designed to add subsites support to LeftAndMain
|
||||
*
|
||||
* @package subsites
|
||||
*/
|
||||
class LeftAndMainSubsites extends Extension
|
||||
class LeftAndMainSubsites extends LeftAndMainExtension
|
||||
{
|
||||
private static $allowed_actions = array('CopyToSubsite');
|
||||
private static $allowed_actions = ['CopyToSubsite'];
|
||||
|
||||
/**
|
||||
* Normally SubsiteID=0 on a DataObject means it is only accessible from the special "main site".
|
||||
@ -17,9 +41,9 @@ class LeftAndMainSubsites extends Extension
|
||||
|
||||
public function init()
|
||||
{
|
||||
Requirements::css('subsites/css/LeftAndMain_Subsites.css');
|
||||
Requirements::javascript('subsites/javascript/LeftAndMain_Subsites.js');
|
||||
Requirements::javascript('subsites/javascript/VirtualPage_Subsites.js');
|
||||
Requirements::css('silverstripe/subsites:client/css/LeftAndMain_Subsites.css');
|
||||
Requirements::javascript('silverstripe/subsites:client/javascript/LeftAndMain_Subsites.js');
|
||||
Requirements::javascript('silverstripe/subsites:client/javascript/VirtualPage_Subsites.js');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -27,21 +51,25 @@ class LeftAndMainSubsites extends Extension
|
||||
*/
|
||||
public function getCMSTreeTitle()
|
||||
{
|
||||
$subsite = Subsite::currentSubSite();
|
||||
return $subsite ? Convert::raw2xml($subsite->Title) : _t('LeftAndMain.SITECONTENTLEFT');
|
||||
$subsite = Subsite::currentSubsite();
|
||||
return $subsite ? Convert::raw2xml($subsite->Title) : _t(__CLASS__.'.SITECONTENTLEFT', 'Site Content');
|
||||
}
|
||||
|
||||
public function updatePageOptions(&$fields)
|
||||
{
|
||||
$fields->push(new HiddenField('SubsiteID', 'SubsiteID', Subsite::currentSubsiteID()));
|
||||
$fields->push(HiddenField::create('SubsiteID', 'SubsiteID', SubsiteState::singleton()->getSubsiteId()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all subsites accessible for current user on this controller.
|
||||
*
|
||||
* @return ArrayList of {@link Subsite} instances.
|
||||
* @param bool $includeMainSite
|
||||
* @param string $mainSiteTitle
|
||||
* @param null $member
|
||||
* @return ArrayList of <a href='psi_element://Subsite'>Subsite</a> instances.
|
||||
* instances.
|
||||
*/
|
||||
public function sectionSites($includeMainSite = true, $mainSiteTitle = "Main site", $member = null)
|
||||
public function sectionSites($includeMainSite = true, $mainSiteTitle = 'Main site', $member = null)
|
||||
{
|
||||
if ($mainSiteTitle == 'Main site') {
|
||||
$mainSiteTitle = _t('Subsites.MainSiteTitle', 'Main site');
|
||||
@ -49,24 +77,24 @@ class LeftAndMainSubsites extends Extension
|
||||
|
||||
// Rationalise member arguments
|
||||
if (!$member) {
|
||||
$member = Member::currentUser();
|
||||
$member = Security::getCurrentUser();
|
||||
}
|
||||
if (!$member) {
|
||||
return new ArrayList();
|
||||
return ArrayList::create();
|
||||
}
|
||||
if (!is_object($member)) {
|
||||
$member = DataObject::get_by_id('Member', $member);
|
||||
$member = DataObject::get_by_id(Member::class, $member);
|
||||
}
|
||||
|
||||
// Collect permissions - honour the LeftAndMain::required_permission_codes, current model requires
|
||||
// us to check if the user satisfies ALL permissions. Code partly copied from LeftAndMain::canView.
|
||||
$codes = array();
|
||||
$extraCodes = Config::inst()->get($this->owner->class, 'required_permission_codes');
|
||||
$codes = [];
|
||||
$extraCodes = Config::inst()->get(get_class($this->owner), 'required_permission_codes');
|
||||
if ($extraCodes !== false) {
|
||||
if ($extraCodes) {
|
||||
$codes = array_merge($codes, (array)$extraCodes);
|
||||
} else {
|
||||
$codes[] = "CMS_ACCESS_{$this->owner->class}";
|
||||
$codes[] = sprintf('CMS_ACCESS_%s', get_class($this->owner));
|
||||
}
|
||||
} else {
|
||||
// Check overriden - all subsites accessible.
|
||||
@ -74,8 +102,8 @@ class LeftAndMainSubsites extends Extension
|
||||
}
|
||||
|
||||
// Find subsites satisfying all permissions for the Member.
|
||||
$codesPerSite = array();
|
||||
$sitesArray = array();
|
||||
$codesPerSite = [];
|
||||
$sitesArray = [];
|
||||
foreach ($codes as $code) {
|
||||
$sites = Subsite::accessible_sites($code, $includeMainSite, $mainSiteTitle, $member);
|
||||
foreach ($sites as $site) {
|
||||
@ -90,7 +118,7 @@ class LeftAndMainSubsites extends Extension
|
||||
// Find sites that satisfy all codes conjuncitvely.
|
||||
$accessibleSites = new ArrayList();
|
||||
foreach ($codesPerSite as $siteID => $siteCodes) {
|
||||
if (count($siteCodes)==count($codes)) {
|
||||
if (count($siteCodes ?? []) == count($codes ?? [])) {
|
||||
$accessibleSites->push($sitesArray[$siteID]);
|
||||
}
|
||||
}
|
||||
@ -116,24 +144,24 @@ class LeftAndMainSubsites extends Extension
|
||||
public function ListSubsites()
|
||||
{
|
||||
$list = $this->Subsites();
|
||||
$currentSubsiteID = Subsite::currentSubsiteID();
|
||||
$currentSubsiteID = SubsiteState::singleton()->getSubsiteId();
|
||||
|
||||
if ($list == null || $list->Count() == 1 && $list->First()->DefaultSite == true) {
|
||||
if ($list == null || $list->count() == 1 && $list->first()->DefaultSite == true) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Requirements::javascript('subsites/javascript/LeftAndMain_Subsites.js');
|
||||
Requirements::javascript('silverstripe/subsites:client/javascript/LeftAndMain_Subsites.js');
|
||||
|
||||
$output = new ArrayList();
|
||||
$output = ArrayList::create();
|
||||
|
||||
foreach ($list as $subsite) {
|
||||
$CurrentState = $subsite->ID == $currentSubsiteID ? 'selected' : '';
|
||||
$currentState = $subsite->ID == $currentSubsiteID ? 'selected' : '';
|
||||
|
||||
$output->push(new ArrayData(array(
|
||||
'CurrentState' => $CurrentState,
|
||||
$output->push(ArrayData::create([
|
||||
'CurrentState' => $currentState,
|
||||
'ID' => $subsite->ID,
|
||||
'Title' => Convert::raw2xml($subsite->Title)
|
||||
)));
|
||||
'Title' => $subsite->Title,
|
||||
]));
|
||||
}
|
||||
|
||||
return $output;
|
||||
@ -141,41 +169,45 @@ class LeftAndMainSubsites extends Extension
|
||||
|
||||
public function alternateMenuDisplayCheck($controllerName)
|
||||
{
|
||||
if (!class_exists($controllerName)) {
|
||||
if (!class_exists($controllerName ?? '')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't display SubsiteXHRController
|
||||
if (singleton($controllerName) instanceof SubsiteXHRController) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check subsite support.
|
||||
if (Subsite::currentSubsiteID() == 0) {
|
||||
if (SubsiteState::singleton()->getSubsiteId() == 0) {
|
||||
// Main site always supports everything.
|
||||
return true;
|
||||
} else {
|
||||
$controller = singleton($controllerName);
|
||||
if ($controller->hasMethod('subsiteCMSShowInMenu') && $controller->subsiteCMSShowInMenu()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// It's not necessary to check access permissions here. Framework calls canView on the controller,
|
||||
// which in turn uses the Permission API which is augmented by our GroupSubsites.
|
||||
|
||||
return false;
|
||||
$controller = singleton($controllerName);
|
||||
return $controller->hasMethod('subsiteCMSShowInMenu') && $controller->subsiteCMSShowInMenu();
|
||||
}
|
||||
|
||||
public function CanAddSubsites()
|
||||
{
|
||||
return Permission::check("ADMIN", "any", null, "all");
|
||||
return Permission::check('ADMIN', 'any', null, 'all');
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for testing if the subsite should be adjusted.
|
||||
* @param string $adminClass
|
||||
* @param int $recordSubsiteID
|
||||
* @param int $currentSubsiteID
|
||||
* @return bool
|
||||
*/
|
||||
public function shouldChangeSubsite($adminClass, $recordSubsiteID, $currentSubsiteID)
|
||||
{
|
||||
if (Config::inst()->get($adminClass, 'treats_subsite_0_as_global') && $recordSubsiteID==0) {
|
||||
if (Config::inst()->get($adminClass, 'treats_subsite_0_as_global') && $recordSubsiteID == 0) {
|
||||
return false;
|
||||
}
|
||||
if ($recordSubsiteID!=$currentSubsiteID) {
|
||||
if ($recordSubsiteID != $currentSubsiteID) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -189,26 +221,23 @@ class LeftAndMainSubsites extends Extension
|
||||
public function canAccess(Member $member = null)
|
||||
{
|
||||
if (!$member) {
|
||||
$member = Member::currentUser();
|
||||
$member = Security::getCurrentUser();
|
||||
}
|
||||
|
||||
// Admin can access everything, no point in checking.
|
||||
if ($member &&
|
||||
(
|
||||
Permission::checkMember($member, 'ADMIN') || // 'Full administrative rights' in SecurityAdmin
|
||||
Permission::checkMember($member, 'CMS_ACCESS_LeftAndMain') // 'Access to all CMS sections' in SecurityAdmin
|
||||
)) {
|
||||
if ($member
|
||||
&& (Permission::checkMember($member, [
|
||||
'ADMIN', // Full administrative rights
|
||||
'CMS_ACCESS_LeftAndMain', // Access to all CMS sections
|
||||
'CMS_ACCESS_CMSMain', // Access to CMS controllers
|
||||
]))
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check if we have access to current section on the current subsite.
|
||||
$accessibleSites = $this->owner->sectionSites(true, "Main site", $member);
|
||||
if ($accessibleSites->count() && $accessibleSites->find('ID', Subsite::currentSubsiteID())) {
|
||||
// Current section can be accessed on the current site, all good.
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
$accessibleSites = $this->owner->sectionSites(true, 'Main site', $member);
|
||||
return $accessibleSites->count() && $accessibleSites->find('ID', SubsiteState::singleton()->getSubsiteId());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -234,58 +263,94 @@ class LeftAndMainSubsites extends Extension
|
||||
*/
|
||||
public function onBeforeInit()
|
||||
{
|
||||
// We are accessing the CMS, so we need to let Subsites know we will be using the session.
|
||||
Subsite::$use_session_subsiteid = true;
|
||||
$request = Controller::curr()->getRequest();
|
||||
$session = $request->getSession();
|
||||
|
||||
$state = SubsiteState::singleton();
|
||||
|
||||
// FIRST, check if we need to change subsites due to the URL.
|
||||
|
||||
// Catch forced subsite changes that need to cause CMS reloads.
|
||||
if (isset($_GET['SubsiteID'])) {
|
||||
if ($request->getVar('SubsiteID') !== null) {
|
||||
// Clear current page when subsite changes (or is set for the first time)
|
||||
if (!Session::get('SubsiteID') || $_GET['SubsiteID'] != Session::get('SubsiteID')) {
|
||||
Session::clear("{$this->owner->class}.currentPage");
|
||||
if ($state->getSubsiteIdWasChanged()) {
|
||||
// sessionNamespace() is protected - see for info
|
||||
$override = $this->owner->config()->get('session_namespace');
|
||||
$sessionNamespace = $override ? $override : get_class($this->owner);
|
||||
$session->clear($sessionNamespace . '.currentPage');
|
||||
}
|
||||
|
||||
// Update current subsite in session
|
||||
Subsite::changeSubsite($_GET['SubsiteID']);
|
||||
// Context: Subsite ID has already been set to the state via InitStateMiddleware
|
||||
|
||||
//Redirect to clear the current page
|
||||
if ($this->owner->canView(Member::currentUser())) {
|
||||
//Redirect to clear the current page
|
||||
return $this->owner->redirect($this->owner->Link());
|
||||
// If the user cannot view the current page, redirect to the admin landing section
|
||||
if (!$this->owner->canView()) {
|
||||
return $this->owner->redirect(AdminRootController::config()->get('url_base') . '/');
|
||||
}
|
||||
//Redirect to the default CMS section
|
||||
return $this->owner->redirect('admin/');
|
||||
|
||||
$currentController = Controller::curr();
|
||||
if ($currentController instanceof CMSPageEditController) {
|
||||
/** @var SiteTree $page */
|
||||
$page = $currentController->currentPage();
|
||||
|
||||
// If the page exists but doesn't belong to the requested subsite, redirect to admin/pages which
|
||||
// will show a list of the requested subsite's pages
|
||||
$currentSubsiteId = $request->getVar('SubsiteID');
|
||||
if ($page && (int) $page->SubsiteID !== (int) $currentSubsiteId) {
|
||||
return $this->owner->redirect(CMSPagesController::singleton()->Link());
|
||||
}
|
||||
|
||||
// Page does belong to the current subsite, so remove the query string parameter and refresh the page
|
||||
// Remove the subsiteID parameter and redirect back to the current URL again
|
||||
$request->offsetSet('SubsiteID', null);
|
||||
return $this->owner->redirect($request->getURL(true));
|
||||
}
|
||||
|
||||
// Redirect back to the default admin URL
|
||||
return $this->owner->redirect($request->getURL());
|
||||
}
|
||||
|
||||
// Automatically redirect the session to appropriate subsite when requesting a record.
|
||||
// This is needed to properly initialise the session in situations where someone opens the CMS via a link.
|
||||
$record = $this->owner->currentPage();
|
||||
if ($record && isset($record->SubsiteID) && is_numeric($record->SubsiteID) && isset($this->owner->urlParams['ID'])) {
|
||||
if ($this->shouldChangeSubsite($this->owner->class, $record->SubsiteID, Subsite::currentSubsiteID())) {
|
||||
// Update current subsite in session
|
||||
Subsite::changeSubsite($record->SubsiteID);
|
||||
if ($record
|
||||
&& isset($record->SubsiteID, $this->owner->urlParams['ID'])
|
||||
&& is_numeric($record->SubsiteID)
|
||||
&& $this->shouldChangeSubsite(
|
||||
get_class($this->owner),
|
||||
$record->SubsiteID,
|
||||
SubsiteState::singleton()->getSubsiteId()
|
||||
)
|
||||
) {
|
||||
// Update current subsite
|
||||
$canViewElsewhere = SubsiteState::singleton()->withState(function ($newState) use ($record) {
|
||||
$newState->setSubsiteId($record->SubsiteID);
|
||||
|
||||
if ($this->owner->canView(Member::currentUser())) {
|
||||
//Redirect to clear the current page
|
||||
return $this->owner->redirect($this->owner->Link());
|
||||
}
|
||||
//Redirect to the default CMS section
|
||||
return $this->owner->redirect('admin/');
|
||||
return (bool) $this->owner->canView(Security::getCurrentUser());
|
||||
});
|
||||
|
||||
if ($canViewElsewhere) {
|
||||
// Redirect to clear the current page
|
||||
return $this->owner->redirect(
|
||||
Controller::join_links($this->owner->Link('show'), $record->ID, '?SubsiteID=' . $record->SubsiteID)
|
||||
);
|
||||
}
|
||||
// Redirect to the default CMS section
|
||||
return $this->owner->redirect(AdminRootController::config()->get('url_base') . '/');
|
||||
}
|
||||
|
||||
// SECOND, check if we need to change subsites due to lack of permissions.
|
||||
|
||||
if (!$this->owner->canAccess()) {
|
||||
$member = Member::currentUser();
|
||||
$member = Security::getCurrentUser();
|
||||
|
||||
// Current section is not accessible, try at least to stick to the same subsite.
|
||||
$menu = CMSMenu::get_menu_items();
|
||||
foreach ($menu as $candidate) {
|
||||
if ($candidate->controller && $candidate->controller!=$this->owner->class) {
|
||||
if ($candidate->controller && $candidate->controller != get_class($this->owner)) {
|
||||
$accessibleSites = singleton($candidate->controller)->sectionSites(true, 'Main site', $member);
|
||||
if ($accessibleSites->count() && $accessibleSites->find('ID', Subsite::currentSubsiteID())) {
|
||||
if ($accessibleSites->count()
|
||||
&& $accessibleSites->find('ID', SubsiteState::singleton()->getSubsiteId())
|
||||
) {
|
||||
// Section is accessible, redirect there.
|
||||
return $this->owner->redirect(singleton($candidate->controller)->Link());
|
||||
}
|
||||
@ -304,7 +369,7 @@ class LeftAndMainSubsites extends Extension
|
||||
}
|
||||
|
||||
// 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.
|
||||
@ -313,13 +378,17 @@ class LeftAndMainSubsites extends Extension
|
||||
|
||||
public function augmentNewSiteTreeItem(&$item)
|
||||
{
|
||||
$item->SubsiteID = isset($_POST['SubsiteID']) ? $_POST['SubsiteID'] : Subsite::currentSubsiteID();
|
||||
$request = Controller::curr()->getRequest();
|
||||
$item->SubsiteID = $request->postVar('SubsiteID') ?: SubsiteState::singleton()->getSubsiteId();
|
||||
}
|
||||
|
||||
public function onAfterSave($record)
|
||||
{
|
||||
if ($record->hasMethod('NormalRelated') && ($record->NormalRelated() || $record->ReverseRelated())) {
|
||||
$this->owner->response->addHeader('X-Status', rawurlencode(_t('LeftAndMainSubsites.Saved', 'Saved, please update related pages.')));
|
||||
$this->owner->response->addHeader(
|
||||
'X-Status',
|
||||
rawurlencode(_t(__CLASS__ . '.Saved', 'Saved, please update related pages.') ?? '')
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -329,8 +398,8 @@ class LeftAndMainSubsites extends Extension
|
||||
*/
|
||||
public function copytosubsite($data, $form)
|
||||
{
|
||||
$page = DataObject::get_by_id('SiteTree', $data['ID']);
|
||||
$subsite = DataObject::get_by_id('Subsite', $data['CopyToSubsiteID']);
|
||||
$page = DataObject::get_by_id(SiteTree::class, $data['ID']);
|
||||
$subsite = DataObject::get_by_id(Subsite::class, $data['CopyToSubsiteID']);
|
||||
$includeChildren = (isset($data['CopyToSubsiteWithChildren'])) ? $data['CopyToSubsiteWithChildren'] : false;
|
||||
|
||||
$newPage = $page->duplicateToSubsite($subsite->ID, $includeChildren);
|
78
src/Extensions/SiteConfigSubsites.php
Normal file
78
src/Extensions/SiteConfigSubsites.php
Normal file
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Extensions;
|
||||
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\HiddenField;
|
||||
use SilverStripe\ORM\DataExtension;
|
||||
use SilverStripe\ORM\DataQuery;
|
||||
use SilverStripe\ORM\Queries\SQLSelect;
|
||||
use SilverStripe\SiteConfig\SiteConfig;
|
||||
use SilverStripe\Subsites\Model\Subsite;
|
||||
use SilverStripe\Subsites\State\SubsiteState;
|
||||
|
||||
/**
|
||||
* Extension for the SiteConfig object to add subsites support
|
||||
*/
|
||||
class SiteConfigSubsites extends DataExtension
|
||||
{
|
||||
private static $has_one = [
|
||||
'Subsite' => Subsite::class, // The subsite that this page belongs to
|
||||
];
|
||||
|
||||
/**
|
||||
* Update any requests to limit the results to the current site
|
||||
* @param SQLSelect $query
|
||||
* @param DataQuery|null $dataQuery
|
||||
*/
|
||||
public function augmentSQL(SQLSelect $query, DataQuery $dataQuery = null)
|
||||
{
|
||||
if (Subsite::$disable_subsite_filter) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If you're querying by ID, ignore the sub-site - this is a bit ugly...
|
||||
if ($query->filtersOnID()) {
|
||||
return;
|
||||
}
|
||||
$regexp = '/^(.*\.)?("|`)?SubsiteID("|`)?\s?=/';
|
||||
foreach ($query->getWhereParameterised($parameters) as $predicate) {
|
||||
if (preg_match($regexp ?? '', $predicate ?? '')) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$subsiteID = SubsiteState::singleton()->getSubsiteId();
|
||||
if ($subsiteID === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
$froms = $query->getFrom();
|
||||
$froms = array_keys($froms ?? []);
|
||||
$tableName = array_shift($froms);
|
||||
if ($tableName !== SiteConfig::getSchema()->tableName(SiteConfig::class)) {
|
||||
return;
|
||||
}
|
||||
$query->addWhere("\"$tableName\".\"SubsiteID\" IN ($subsiteID)");
|
||||
}
|
||||
|
||||
public function onBeforeWrite()
|
||||
{
|
||||
if ((!is_numeric($this->owner->ID) || !$this->owner->ID) && !$this->owner->SubsiteID) {
|
||||
$this->owner->SubsiteID = SubsiteState::singleton()->getSubsiteId();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a piece of text to keep DataObject cache keys appropriately specific
|
||||
*/
|
||||
public function cacheKeyComponent()
|
||||
{
|
||||
return 'subsite-' . SubsiteState::singleton()->getSubsiteId();
|
||||
}
|
||||
|
||||
public function updateCMSFields(FieldList $fields)
|
||||
{
|
||||
$fields->push(HiddenField::create('SubsiteID', 'SubsiteID', SubsiteState::singleton()->getSubsiteId()));
|
||||
}
|
||||
}
|
577
src/Extensions/SiteTreeSubsites.php
Normal file
577
src/Extensions/SiteTreeSubsites.php
Normal file
@ -0,0 +1,577 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Extensions;
|
||||
|
||||
use SilverStripe\Dev\Deprecation;
|
||||
use Page;
|
||||
use SilverStripe\CMS\Forms\SiteTreeURLSegmentField;
|
||||
use SilverStripe\CMS\Model\SiteTree;
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Control\Director;
|
||||
use SilverStripe\Control\HTTP;
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\Core\Convert;
|
||||
use SilverStripe\Forms\CheckboxField;
|
||||
use SilverStripe\Forms\DropdownField;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\FormAction;
|
||||
use SilverStripe\Forms\ToggleCompositeField;
|
||||
use SilverStripe\i18n\i18n;
|
||||
use SilverStripe\ORM\ArrayList;
|
||||
use SilverStripe\ORM\DataExtension;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\ORM\DataQuery;
|
||||
use SilverStripe\ORM\Map;
|
||||
use SilverStripe\ORM\Queries\SQLSelect;
|
||||
use SilverStripe\Security\Member;
|
||||
use SilverStripe\Security\Security;
|
||||
use SilverStripe\SiteConfig\SiteConfig;
|
||||
use SilverStripe\Subsites\Model\Subsite;
|
||||
use SilverStripe\Subsites\Service\ThemeResolver;
|
||||
use SilverStripe\Subsites\State\SubsiteState;
|
||||
use SilverStripe\View\SSViewer;
|
||||
use SilverStripe\VersionedAdmin\Controllers\HistoryViewerController;
|
||||
|
||||
/**
|
||||
* Extension for the SiteTree object to add subsites support
|
||||
*/
|
||||
class SiteTreeSubsites extends DataExtension
|
||||
{
|
||||
private static $has_one = [
|
||||
'Subsite' => Subsite::class, // The subsite that this page belongs to
|
||||
];
|
||||
|
||||
private static $many_many = [
|
||||
'CrossSubsiteLinkTracking' => SiteTree::class // Stored separately, as the logic for URL rewriting is different
|
||||
];
|
||||
|
||||
private static $many_many_extraFields = [
|
||||
'CrossSubsiteLinkTracking' => ['FieldName' => 'Varchar']
|
||||
];
|
||||
|
||||
public function isMainSite()
|
||||
{
|
||||
return $this->owner->SubsiteID == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update any requests to limit the results to the current site
|
||||
* @param SQLSelect $query
|
||||
* @param DataQuery $dataQuery
|
||||
*/
|
||||
public function augmentSQL(SQLSelect $query, DataQuery $dataQuery = null)
|
||||
{
|
||||
if (Subsite::$disable_subsite_filter) {
|
||||
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(!$query->where
|
||||
// || (strpos($query->where[0], ".\"ID\" = ") === false
|
||||
// && strpos($query->where[0], ".`ID` = ") === false && strpos($query->where[0], ".ID = ") === false
|
||||
// && strpos($query->where[0], "ID = ") !== 0)) {
|
||||
if ($query->filtersOnID()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$subsiteID = null;
|
||||
if (Subsite::$force_subsite) {
|
||||
$subsiteID = Subsite::$force_subsite;
|
||||
} else {
|
||||
$subsiteID = SubsiteState::singleton()->getSubsiteId();
|
||||
}
|
||||
|
||||
if ($subsiteID === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The foreach is an ugly way of getting the first key :-)
|
||||
foreach ($query->getFrom() as $tableName => $info) {
|
||||
// The tableName should be SiteTree or SiteTree_Live...
|
||||
$siteTreeTableName = SiteTree::getSchema()->tableName(SiteTree::class);
|
||||
if (strpos($tableName ?? '', $siteTreeTableName ?? '') === false) {
|
||||
break;
|
||||
}
|
||||
$query->addWhere("\"$tableName\".\"SubsiteID\" IN ($subsiteID)");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function onBeforeWrite()
|
||||
{
|
||||
if (!$this->owner->ID && !$this->owner->SubsiteID) {
|
||||
$this->owner->SubsiteID = SubsiteState::singleton()->getSubsiteId();
|
||||
}
|
||||
|
||||
parent::onBeforeWrite();
|
||||
}
|
||||
|
||||
public function updateCMSFields(FieldList $fields)
|
||||
{
|
||||
$subsites = Subsite::accessible_sites('CMS_ACCESS_CMSMain');
|
||||
if ($subsites && $subsites->count()) {
|
||||
$subsitesToMap = $subsites->exclude('ID', $this->owner->SubsiteID);
|
||||
$subsitesMap = $subsitesToMap->map('ID', 'Title');
|
||||
} else {
|
||||
$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)
|
||||
$isDefaultSubsite = $this->owner->SubsiteID == 0 || $this->owner->Subsite()->DefaultSite;
|
||||
|
||||
if ($isDefaultSubsite && $subsitesMap->count() && !$viewingPageHistory) {
|
||||
$fields->addFieldToTab(
|
||||
'Root.Main',
|
||||
ToggleCompositeField::create(
|
||||
'SubsiteOperations',
|
||||
_t(__CLASS__ . '.SubsiteOperations', 'Subsite Operations'),
|
||||
[
|
||||
DropdownField::create('CopyToSubsiteID', _t(
|
||||
__CLASS__ . '.CopyToSubsite',
|
||||
'Copy page to subsite'
|
||||
), $subsitesMap),
|
||||
CheckboxField::create(
|
||||
'CopyToSubsiteWithChildren',
|
||||
_t(__CLASS__ . '.CopyToSubsiteWithChildren', 'Include children pages?')
|
||||
),
|
||||
$copyAction = FormAction::create(
|
||||
'copytosubsite',
|
||||
_t(__CLASS__ . '.CopyAction', 'Copy')
|
||||
)
|
||||
]
|
||||
)->setHeadingLevel(4)
|
||||
);
|
||||
|
||||
$copyAction->addExtraClass('btn btn-primary font-icon-save ml-3');
|
||||
|
||||
// @todo check if this needs re-implementation
|
||||
// $copyAction->includeDefaultJS(false);
|
||||
}
|
||||
|
||||
// replace readonly link prefix
|
||||
$subsite = $this->owner->Subsite();
|
||||
$nested_urls_enabled = Config::inst()->get(SiteTree::class, 'nested_urls');
|
||||
/** @var Subsite $subsite */
|
||||
if ($subsite && $subsite->exists()) {
|
||||
// Use baseurl from domain
|
||||
$baseLink = $subsite->absoluteBaseURL();
|
||||
|
||||
// Add parent page if enabled
|
||||
if ($nested_urls_enabled && $this->owner->ParentID) {
|
||||
$baseLink = Controller::join_links(
|
||||
$baseLink,
|
||||
$this->owner->Parent()->RelativeLink(true)
|
||||
);
|
||||
}
|
||||
|
||||
$urlsegment = $fields->dataFieldByName('URLSegment');
|
||||
if ($urlsegment && $urlsegment instanceof SiteTreeURLSegmentField) {
|
||||
$urlsegment->setURLPrefix($baseLink);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the basic duplication, but doesn't write anything this means we can subclass this easier and do more
|
||||
* complex relation duplication.
|
||||
*
|
||||
* Note that when duplicating including children, everything is written.
|
||||
*
|
||||
* @param Subsite|int $subsiteID
|
||||
* @param bool $includeChildren
|
||||
* @return SiteTree
|
||||
*/
|
||||
public function duplicateToSubsitePrep($subsiteID, $includeChildren)
|
||||
{
|
||||
if (is_object($subsiteID)) {
|
||||
$subsiteID = $subsiteID->ID;
|
||||
}
|
||||
|
||||
return SubsiteState::singleton()
|
||||
->withState(function (SubsiteState $newState) use ($subsiteID, $includeChildren) {
|
||||
$newState->setSubsiteId($subsiteID);
|
||||
|
||||
/** @var SiteTree $page */
|
||||
$page = $this->owner;
|
||||
|
||||
try {
|
||||
// We have no idea what the ParentID should be, but it shouldn't be the same as it was since
|
||||
// we're now in a different subsite. As a workaround use the url-segment and subsite ID.
|
||||
if ($page->Parent()) {
|
||||
$parentSeg = $page->Parent()->URLSegment;
|
||||
$newParentPage = Page::get()->filter('URLSegment', $parentSeg)->first();
|
||||
$originalParentID = $page->ParentID;
|
||||
if ($newParentPage) {
|
||||
$page->ParentID = (int) $newParentPage->ID;
|
||||
} else {
|
||||
// reset it to the top level, so the user can decide where to put it
|
||||
$page->ParentID = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Disable query filtering by subsite during actual duplication
|
||||
$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
|
||||
*
|
||||
* @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, $includeChildren = false)
|
||||
{
|
||||
/** @var SiteTree|SiteTreeSubsites */
|
||||
$clone = $this->owner->duplicateToSubsitePrep($subsiteID, $includeChildren);
|
||||
$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();
|
||||
}
|
||||
// Deprecated: manually duplicate any configured relationships
|
||||
$clone->duplicateSubsiteRelations($this->owner);
|
||||
|
||||
$clone->invokeWithExtensions('onAfterDuplicateToSubsite', $this->owner);
|
||||
|
||||
return $clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate relations using a static property to define
|
||||
* which ones we want to duplicate
|
||||
*
|
||||
* It may be that some relations are not diostinct to sub site so can stay
|
||||
* 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)
|
||||
{
|
||||
$thisClass = $originalPage->ClassName;
|
||||
$relations = Config::inst()->get($thisClass, 'duplicate_to_subsite_relations');
|
||||
|
||||
if ($relations && !empty($relations)) {
|
||||
foreach ($relations as $relation) {
|
||||
$items = $originalPage->$relation();
|
||||
foreach ($items as $item) {
|
||||
$duplicateItem = $item->duplicate(false);
|
||||
$duplicateItem->{$thisClass.'ID'} = $this->owner->ID;
|
||||
$duplicateItem->write();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return SiteConfig
|
||||
*/
|
||||
public function alternateSiteConfig()
|
||||
{
|
||||
if (!$this->owner->SubsiteID) {
|
||||
return false;
|
||||
}
|
||||
$sc = DataObject::get_one(SiteConfig::class, '"SubsiteID" = ' . $this->owner->SubsiteID);
|
||||
if (!$sc) {
|
||||
$sc = new SiteConfig();
|
||||
$sc->SubsiteID = $this->owner->SubsiteID;
|
||||
$sc->Title = _t('SilverStripe\\Subsites\\Model\\Subsite.SiteConfigTitle', 'Your Site Name');
|
||||
$sc->Tagline = _t('SilverStripe\\Subsites\\Model\\Subsite.SiteConfigSubtitle', 'Your tagline here');
|
||||
$sc->write();
|
||||
}
|
||||
return $sc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only allow editing of a page if the member satisfies one of the following conditions:
|
||||
* - 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"
|
||||
*
|
||||
* If there are no subsites configured yet, this logic is skipped.
|
||||
*
|
||||
* @param Member|null $member
|
||||
* @return bool|null
|
||||
*/
|
||||
public function canEdit($member = null)
|
||||
{
|
||||
if (!$member) {
|
||||
$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
|
||||
$goodSites = Subsite::accessible_sites('CMS_ACCESS_CMSMain', true, 'all', $member)->column('ID');
|
||||
|
||||
if (!is_null($this->owner->SubsiteID)) {
|
||||
$subsiteID = $this->owner->SubsiteID;
|
||||
} else {
|
||||
// The relationships might not be available during the record creation when using a GridField.
|
||||
// In this case the related objects will have empty fields, and SubsiteID will not be available.
|
||||
//
|
||||
// We do the second best: fetch the likely SubsiteID from the session. The drawback is this might
|
||||
// make it possible to force relations to point to other (forbidden) subsites.
|
||||
$subsiteID = SubsiteState::singleton()->getSubsiteId();
|
||||
}
|
||||
|
||||
// Return true if they have access to this object's site
|
||||
if (!(in_array(0, $goodSites ?? []) || in_array($subsiteID, $goodSites ?? []))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null $member
|
||||
* @return bool
|
||||
*/
|
||||
public function canDelete($member = null)
|
||||
{
|
||||
if (!$member && $member !== false) {
|
||||
$member = Security::getCurrentUser();
|
||||
}
|
||||
|
||||
return $this->canEdit($member);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null $member
|
||||
* @return bool
|
||||
*/
|
||||
public function canAddChildren($member = null)
|
||||
{
|
||||
if (!$member && $member !== false) {
|
||||
$member = Security::getCurrentUser();
|
||||
}
|
||||
|
||||
return $this->canEdit($member);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Member|null $member
|
||||
* @return bool|null
|
||||
*/
|
||||
public function canPublish($member = null)
|
||||
{
|
||||
if (!$member && $member !== false) {
|
||||
$member = Security::getCurrentUser();
|
||||
}
|
||||
|
||||
return $this->canEdit($member);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by ContentController::init();
|
||||
* @param $controller
|
||||
*/
|
||||
public static function contentcontrollerInit($controller)
|
||||
{
|
||||
/** @var Subsite $subsite */
|
||||
$subsite = Subsite::currentSubsite();
|
||||
|
||||
if ($subsite && $subsite->Theme) {
|
||||
SSViewer::set_themes(ThemeResolver::singleton()->getThemeList($subsite));
|
||||
}
|
||||
|
||||
$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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null $action
|
||||
* @return string
|
||||
*/
|
||||
public function alternateAbsoluteLink($action = null)
|
||||
{
|
||||
// Generate the existing absolute URL and replace the domain with the subsite domain.
|
||||
// This helps deal with Link() returning an absolute URL.
|
||||
$url = Director::absoluteURL($this->owner->Link($action));
|
||||
if ($this->owner->SubsiteID) {
|
||||
$url = preg_replace('/\/\/[^\/]+\//', '//' . $this->owner->Subsite()->domain() . '/', $url ?? '');
|
||||
}
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the CMS domain for iframed CMS previews to prevent single-origin violations
|
||||
* and SSL cert problems. Always set SubsiteID to avoid errors because a page doesn't
|
||||
* 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
|
||||
*/
|
||||
public function alternatePreviewLink($action = null)
|
||||
{
|
||||
Deprecation::notice('2.2.0', 'Use updatePreviewLink() instead');
|
||||
$link = '';
|
||||
return $this->updatePreviewLink($link, $action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject the subsite ID into the content so it can be used by frontend scripts.
|
||||
* @param $tags
|
||||
* @return string
|
||||
*/
|
||||
public function MetaTags(&$tags)
|
||||
{
|
||||
if ($this->owner->SubsiteID) {
|
||||
$tags .= '<meta name="x-subsite-id" content="' . $this->owner->SubsiteID . "\" />\n";
|
||||
}
|
||||
|
||||
return $tags;
|
||||
}
|
||||
|
||||
public function augmentSyncLinkTracking()
|
||||
{
|
||||
// Set LinkTracking appropriately
|
||||
$links = HTTP::getLinksIn($this->owner->Content);
|
||||
$linkedPages = [];
|
||||
|
||||
if ($links) {
|
||||
foreach ($links as $link) {
|
||||
if (substr($link ?? '', 0, strlen('http://')) == 'http://') {
|
||||
$withoutHttp = substr($link ?? '', strlen('http://'));
|
||||
if (strpos($withoutHttp ?? '', '/') &&
|
||||
strpos($withoutHttp ?? '', '/') < strlen($withoutHttp ?? '')
|
||||
) {
|
||||
$domain = substr($withoutHttp ?? '', 0, strpos($withoutHttp ?? '', '/'));
|
||||
$rest = substr($withoutHttp ?? '', strpos($withoutHttp ?? '', '/') + 1);
|
||||
|
||||
$subsiteID = Subsite::getSubsiteIDForDomain($domain);
|
||||
if ($subsiteID == 0) {
|
||||
continue;
|
||||
} // We have no idea what the domain for the main site is, so cant track links to it
|
||||
|
||||
$origDisableSubsiteFilter = Subsite::$disable_subsite_filter;
|
||||
Subsite::disable_subsite_filter(true);
|
||||
$candidatePage = SiteTree::get()->filter([
|
||||
'URLSegment' => urldecode($rest),
|
||||
'SubsiteID' => $subsiteID,
|
||||
])->first();
|
||||
Subsite::disable_subsite_filter($origDisableSubsiteFilter);
|
||||
|
||||
if ($candidatePage) {
|
||||
$linkedPages[] = $candidatePage->ID;
|
||||
} else {
|
||||
$this->owner->HasBrokenLink = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->owner->CrossSubsiteLinkTracking()->setByIDList($linkedPages);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that valid url segments are checked within the correct subsite of the owner object,
|
||||
* even if the current subsiteID is set to some other subsite.
|
||||
*
|
||||
* @return null|bool Either true or false, or null to not influence result
|
||||
*/
|
||||
public function augmentValidURLSegment()
|
||||
{
|
||||
// If this page is being filtered in the current subsite, then no custom validation query is required.
|
||||
$subsite = Subsite::$force_subsite ?: SubsiteState::singleton()->getSubsiteId();
|
||||
if (empty($this->owner->SubsiteID) || $subsite == $this->owner->SubsiteID) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Backup forced subsite
|
||||
$prevForceSubsite = Subsite::$force_subsite;
|
||||
Subsite::$force_subsite = $this->owner->SubsiteID;
|
||||
|
||||
// Repeat validation in the correct subsite
|
||||
$isValid = $this->owner->validURLSegment();
|
||||
|
||||
// Restore
|
||||
Subsite::$force_subsite = $prevForceSubsite;
|
||||
|
||||
return (bool)$isValid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a piece of text to keep DataObject cache keys appropriately specific
|
||||
*/
|
||||
public function cacheKeyComponent()
|
||||
{
|
||||
return 'subsite-' . SubsiteState::singleton()->getSubsiteId();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Member $member
|
||||
* @return boolean|null
|
||||
*/
|
||||
public function canCreate($member = null)
|
||||
{
|
||||
// Typically called on a singleton, so we're not using the Subsite() relation
|
||||
$subsite = Subsite::currentSubsite();
|
||||
if ($subsite && $subsite->exists() && $subsite->PageTypeBlacklist) {
|
||||
// SS 4.1: JSON encoded. SS 4.0, comma delimited
|
||||
$blacklist = json_decode($subsite->PageTypeBlacklist ?? '', true);
|
||||
if ($blacklist === false) {
|
||||
$blacklist = explode(',', $subsite->PageTypeBlacklist ?? '');
|
||||
}
|
||||
|
||||
if (in_array(get_class($this->owner), (array) $blacklist)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,9 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Extensions;
|
||||
|
||||
use SilverStripe\Core\Extension;
|
||||
|
||||
/*
|
||||
* Simple extension to show admins in the menu of subsites.
|
||||
* If an admin area should be available to a subsite, you can attach
|
10
src/Forms/GridFieldSubsiteDetailForm.php
Normal file
10
src/Forms/GridFieldSubsiteDetailForm.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Forms;
|
||||
|
||||
use SilverStripe\Forms\GridField\GridFieldDetailForm;
|
||||
|
||||
class GridFieldSubsiteDetailForm extends GridFieldDetailForm
|
||||
{
|
||||
protected $itemRequestClass = GridFieldSubsiteDetailFormItemRequest::class;
|
||||
}
|
@ -1,14 +1,18 @@
|
||||
<?php
|
||||
class GridFieldSubsiteDetailForm extends GridFieldDetailForm
|
||||
{
|
||||
protected $itemRequestClass='GridFieldSubsiteDetailForm_ItemRequest';
|
||||
}
|
||||
|
||||
class GridFieldSubsiteDetailForm_ItemRequest extends GridFieldDetailForm_ItemRequest
|
||||
namespace SilverStripe\Subsites\Forms;
|
||||
|
||||
use SilverStripe\Forms\DropdownField;
|
||||
use SilverStripe\Forms\Form;
|
||||
use SilverStripe\Forms\GridField\GridFieldDetailForm_ItemRequest;
|
||||
use SilverStripe\Subsites\Model\Subsite;
|
||||
|
||||
class GridFieldSubsiteDetailFormItemRequest extends GridFieldDetailForm_ItemRequest
|
||||
{
|
||||
private static $allowed_actions = array(
|
||||
|
||||
private static $allowed_actions = [
|
||||
'ItemEditForm',
|
||||
);
|
||||
];
|
||||
|
||||
/**
|
||||
* Builds an item edit form. The arguments to getCMSFields() are the popupController and
|
||||
@ -23,18 +27,22 @@ class GridFieldSubsiteDetailForm_ItemRequest extends GridFieldDetailForm_ItemReq
|
||||
*/
|
||||
public function ItemEditForm()
|
||||
{
|
||||
$form=parent::ItemEditForm();
|
||||
$form = parent::ItemEditForm();
|
||||
|
||||
if ($this->record->ID == 0) {
|
||||
$templates = Subsite::get()->sort('Title');
|
||||
$templateArray = array();
|
||||
$templateArray = [];
|
||||
if ($templates) {
|
||||
$templateArray = $templates->map('ID', 'Title');
|
||||
}
|
||||
|
||||
$templateDropdown = new DropdownField('TemplateID', _t('Subsite.COPYSTRUCTURE', 'Copy structure from:'), $templateArray);
|
||||
$templateDropdown = new DropdownField(
|
||||
'TemplateID',
|
||||
_t('Subsite.COPYSTRUCTURE', 'Copy structure from:'),
|
||||
$templateArray
|
||||
);
|
||||
$templateDropdown->setEmptyString('(' . _t('Subsite.NOTEMPLATE', 'No template') . ')');
|
||||
$form->Fields()->addFieldToTab('Root.Configuration', $templateDropdown);
|
||||
$form->Fields()->addFieldToTab('Root.Main', $templateDropdown);
|
||||
}
|
||||
|
||||
return $form;
|
87
src/Forms/SubsitesTreeDropdownField.php
Normal file
87
src/Forms/SubsitesTreeDropdownField.php
Normal file
@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Forms;
|
||||
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Control\HTTPRequest;
|
||||
use SilverStripe\Forms\TreeDropdownField;
|
||||
use SilverStripe\Subsites\Model\Subsite;
|
||||
use SilverStripe\View\Requirements;
|
||||
use SilverStripe\Subsites\State\SubsiteState;
|
||||
|
||||
/**
|
||||
* Wraps around a TreedropdownField to add ability for temporary
|
||||
* switching of subsite sessions.
|
||||
*
|
||||
* @package subsites
|
||||
*/
|
||||
class SubsitesTreeDropdownField extends TreeDropdownField
|
||||
{
|
||||
private static $allowed_actions = [
|
||||
'tree'
|
||||
];
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $subsiteId = 0;
|
||||
|
||||
/**
|
||||
* Extra HTML classes
|
||||
*
|
||||
* @skipUpgrade
|
||||
* @var string[]
|
||||
*/
|
||||
protected $extraClasses = ['SubsitesTreeDropdownField'];
|
||||
|
||||
public function Field($properties = [])
|
||||
{
|
||||
$html = parent::Field($properties);
|
||||
|
||||
Requirements::javascript('silverstripe/subsites:client/javascript/SubsitesTreeDropdownField.js');
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the subsite ID to use when generating the tree
|
||||
*
|
||||
* @param int $id
|
||||
* @return $this
|
||||
*/
|
||||
public function setSubsiteId($id)
|
||||
{
|
||||
$this->subsiteId = $id;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the subsite ID to use when generating the tree
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function getSubsiteId()
|
||||
{
|
||||
return $this->subsiteId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the CMS tree with the provided subsite ID applied to the state
|
||||
*
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function tree(HTTPRequest $request)
|
||||
{
|
||||
// Detect subsite ID from the request
|
||||
if ($request->getVar($this->getName() . '_SubsiteID')) {
|
||||
$this->setSubsiteId($request->getVar($this->getName() . '_SubsiteID'));
|
||||
}
|
||||
|
||||
$results = SubsiteState::singleton()->withState(function (SubsiteState $newState) use ($request) {
|
||||
$newState->setSubsiteId($this->getSubsiteId());
|
||||
return parent::tree($request);
|
||||
});
|
||||
|
||||
return $results;
|
||||
}
|
||||
}
|
@ -1,4 +1,7 @@
|
||||
<?php
|
||||
namespace SilverStripe\Subsites\Forms;
|
||||
|
||||
use SilverStripe\Forms\TextField;
|
||||
|
||||
/**
|
||||
* A text field that accepts only valid domain names, but allows the wildcard (*) character
|
||||
@ -19,8 +22,8 @@ class WildcardDomainField extends TextField
|
||||
|
||||
$validator->validationError(
|
||||
$this->getName(),
|
||||
_t("DomainNameField.INVALID_DOMAIN", "Invalid domain name"),
|
||||
"validation"
|
||||
_t('DomainNameField.INVALID_DOMAIN', 'Invalid domain name'),
|
||||
'validation'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
@ -33,7 +36,7 @@ class WildcardDomainField extends TextField
|
||||
*/
|
||||
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()
|
105
src/Middleware/InitStateMiddleware.php
Normal file
105
src/Middleware/InitStateMiddleware.php
Normal file
@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Middleware;
|
||||
|
||||
use SilverStripe\Admin\AdminRootController;
|
||||
use SilverStripe\Control\HTTPRequest;
|
||||
use SilverStripe\Control\Middleware\HTTPMiddleware;
|
||||
use SilverStripe\Core\Config\Configurable;
|
||||
use SilverStripe\Core\Injector\Injector;
|
||||
use SilverStripe\ORM\Connect\DatabaseException;
|
||||
use SilverStripe\Subsites\Model\Subsite;
|
||||
use SilverStripe\Subsites\State\SubsiteState;
|
||||
|
||||
class InitStateMiddleware implements HTTPMiddleware
|
||||
{
|
||||
use Configurable;
|
||||
|
||||
/**
|
||||
* URL paths that should be considered as admin only, i.e. not frontend
|
||||
*
|
||||
* @config
|
||||
* @var array
|
||||
*/
|
||||
private static $admin_url_paths = [
|
||||
'dev/',
|
||||
'graphql/',
|
||||
];
|
||||
|
||||
public function process(HTTPRequest $request, callable $delegate)
|
||||
{
|
||||
try {
|
||||
// Initialise and register the State
|
||||
$state = SubsiteState::create();
|
||||
Injector::inst()->registerService($state);
|
||||
|
||||
// Detect whether the request was made in the CMS area (or other admin-only areas)
|
||||
$isAdmin = $this->getIsAdmin($request);
|
||||
$state->setUseSessions($isAdmin);
|
||||
|
||||
// Detect the subsite ID
|
||||
$subsiteId = $this->detectSubsiteId($request);
|
||||
$state->setSubsiteId($subsiteId);
|
||||
|
||||
return $delegate($request);
|
||||
} catch (DatabaseException $ex) {
|
||||
$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);
|
||||
}
|
||||
throw $ex;
|
||||
} finally {
|
||||
// Persist to the session if using the CMS
|
||||
if ($state->getUseSessions()) {
|
||||
$request->getSession()->set('SubsiteID', $state->getSubsiteId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the website is being viewed from an admin protected area or not
|
||||
*
|
||||
* @param HTTPRequest $request
|
||||
* @return bool
|
||||
*/
|
||||
public function getIsAdmin(HTTPRequest $request)
|
||||
{
|
||||
$adminPaths = static::config()->get('admin_url_paths');
|
||||
$adminPaths[] = AdminRootController::admin_url();
|
||||
$currentPath = rtrim($request->getURL() ?? '', '/') . '/';
|
||||
foreach ($adminPaths as $adminPath) {
|
||||
if (substr($currentPath ?? '', 0, strlen($adminPath ?? '')) === $adminPath) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the given request to detect the current subsite ID
|
||||
*
|
||||
* @param HTTPRequest $request
|
||||
* @return int
|
||||
*/
|
||||
protected function detectSubsiteId(HTTPRequest $request)
|
||||
{
|
||||
if ($request->getVar('SubsiteID') !== null) {
|
||||
return (int) $request->getVar('SubsiteID');
|
||||
}
|
||||
|
||||
if (SubsiteState::singleton()->getUseSessions() && $request->getSession()->get('SubsiteID') !== null) {
|
||||
return (int) $request->getSession()->get('SubsiteID');
|
||||
}
|
||||
|
||||
$subsiteIdFromDomain = Subsite::getSubsiteIDForDomain($request->getHost());
|
||||
if ($subsiteIdFromDomain !== null) {
|
||||
return (int) $subsiteIdFromDomain;
|
||||
}
|
||||
|
||||
// Default fallback
|
||||
return 0;
|
||||
}
|
||||
}
|
@ -1,4 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Model;
|
||||
|
||||
use SilverStripe\Admin\CMSMenu;
|
||||
use SilverStripe\CMS\Model\SiteTree;
|
||||
use SilverStripe\Control\Director;
|
||||
use SilverStripe\Core\Convert;
|
||||
use SilverStripe\Core\Injector\Injector;
|
||||
use SilverStripe\Core\Manifest\ModuleLoader;
|
||||
use SilverStripe\Dev\Deprecation;
|
||||
use SilverStripe\Forms\CheckboxSetField;
|
||||
use SilverStripe\Forms\DropdownField;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\GridField\GridField;
|
||||
use SilverStripe\Forms\GridField\GridFieldConfig_RecordEditor;
|
||||
use SilverStripe\Forms\HiddenField;
|
||||
use SilverStripe\Forms\ToggleCompositeField;
|
||||
use SilverStripe\i18n\Data\Intl\IntlLocales;
|
||||
use SilverStripe\i18n\i18n;
|
||||
use SilverStripe\ORM\ArrayLib;
|
||||
use SilverStripe\ORM\ArrayList;
|
||||
use SilverStripe\ORM\DataList;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\ORM\DB;
|
||||
use SilverStripe\ORM\SS_List;
|
||||
use SilverStripe\Security\Group;
|
||||
use SilverStripe\Security\Member;
|
||||
use SilverStripe\Security\Permission;
|
||||
use SilverStripe\Security\Security;
|
||||
use SilverStripe\Subsites\Service\ThemeResolver;
|
||||
use SilverStripe\Subsites\State\SubsiteState;
|
||||
use SilverStripe\Versioned\Versioned;
|
||||
use UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* A dynamically created subsite. SiteTree objects can now belong to a subsite.
|
||||
* You can simulate subsite access without setting up virtual hosts by appending ?SubsiteID=<ID> to the request.
|
||||
@ -7,65 +41,135 @@
|
||||
*/
|
||||
class Subsite extends DataObject
|
||||
{
|
||||
/**
|
||||
* @var $use_session_subsiteid Boolean Set to TRUE when using the CMS and FALSE
|
||||
* when browsing the frontend of a website.
|
||||
*
|
||||
* @todo Remove flag once the Subsite CMS works without session state,
|
||||
* similarly to the Translatable module.
|
||||
*/
|
||||
public static $use_session_subsiteid = false;
|
||||
|
||||
private static $table_name = 'Subsite';
|
||||
|
||||
/**
|
||||
* @var boolean $disable_subsite_filter If enabled, bypasses the query decoration
|
||||
* to limit DataObject::get*() calls to a specific subsite. Useful for debugging.
|
||||
* to limit DataObject::get*() calls to a specific subsite. Useful for debugging. Note that
|
||||
* for now this is left as a public static property to avoid having to nest and mutate the
|
||||
* configuration manifest.
|
||||
*/
|
||||
public static $disable_subsite_filter = false;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @deprecated 2.0.0 Use SubsiteState::singleton()->withState() instead.
|
||||
*/
|
||||
public static $force_subsite = null;
|
||||
|
||||
/**
|
||||
* Whether to write a host-map.php file
|
||||
*
|
||||
* @config
|
||||
* @var boolean
|
||||
*/
|
||||
public static $write_hostmap = true;
|
||||
private static $write_hostmap = true;
|
||||
|
||||
/**
|
||||
* Memory cache of accessible sites
|
||||
*
|
||||
* @array
|
||||
*/
|
||||
private static $_cache_accessible_sites = array();
|
||||
protected static $cache_accessible_sites = [];
|
||||
|
||||
/**
|
||||
* Memory cache of subsite id for domains
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $_cache_subsite_for_domain = array();
|
||||
protected static $cache_subsite_for_domain = [];
|
||||
|
||||
/**
|
||||
* @var array $allowed_themes Numeric array of all themes which are allowed to be selected for all subsites.
|
||||
* Numeric array of all themes which are allowed to be selected for all subsites.
|
||||
* Corresponds to subfolder names within the /themes folder. By default, all themes contained in this folder
|
||||
* are listed.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $allowed_themes = array();
|
||||
protected static $allowed_themes = [];
|
||||
|
||||
/**
|
||||
* @var Boolean If set to TRUE, don't assume 'www.example.com' and 'example.com' are the same.
|
||||
* If set to TRUE, don't assume 'www.example.com' and 'example.com' are the same.
|
||||
* Doesn't affect wildcard matching, so '*.example.com' will match 'www.example.com' (but not 'example.com')
|
||||
* in both TRUE or FALSE setting.
|
||||
*
|
||||
* @config
|
||||
* @var boolean
|
||||
*/
|
||||
public static $strict_subdomain_matching = false;
|
||||
private static $strict_subdomain_matching = false;
|
||||
|
||||
/**
|
||||
* @var boolean Respects the IsPublic flag when retrieving subsites
|
||||
* Respects the IsPublic flag when retrieving subsites
|
||||
*
|
||||
* @config
|
||||
* @var boolean
|
||||
*/
|
||||
public static $check_is_public = true;
|
||||
private static $check_is_public = true;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $summary_fields = [
|
||||
'Title',
|
||||
'PrimaryDomain',
|
||||
'IsPublic.Nice'
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $db = [
|
||||
'Title' => 'Varchar(255)',
|
||||
'RedirectURL' => 'Varchar(255)',
|
||||
'DefaultSite' => 'Boolean',
|
||||
'Theme' => 'Varchar',
|
||||
'Language' => 'Varchar(6)',
|
||||
|
||||
// Used to hide unfinished/private subsites from public view.
|
||||
// If unset, will default to true
|
||||
'IsPublic' => 'Boolean',
|
||||
|
||||
// Comma-separated list of disallowed page types
|
||||
'PageTypeBlacklist' => 'Text',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $has_many = [
|
||||
'Domains' => SubsiteDomain::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $belongs_many_many = [
|
||||
'Groups' => Group::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $defaults = [
|
||||
'IsPublic' => 1
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
private static $searchable_fields = [
|
||||
'Title',
|
||||
'Domains.Domain',
|
||||
'IsPublic',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private static $default_sort = '"Title" ASC';
|
||||
|
||||
/**
|
||||
* Set allowed themes
|
||||
@ -80,13 +184,11 @@ class Subsite extends DataObject
|
||||
/**
|
||||
* Gets the subsite currently set in the session.
|
||||
*
|
||||
* @uses ControllerSubsites->controllerAugmentInit()
|
||||
* @return Subsite
|
||||
* @return DataObject The current Subsite
|
||||
*/
|
||||
public static function currentSubsite()
|
||||
{
|
||||
// get_by_id handles caching so we don't have to
|
||||
return DataObject::get_by_id('Subsite', self::currentSubsiteID());
|
||||
return Subsite::get()->byID(SubsiteState::singleton()->getSubsiteId());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -97,30 +199,19 @@ class Subsite extends DataObject
|
||||
*
|
||||
* You can simulate subsite access without creating virtual hosts by appending ?SubsiteID=<ID> to the request.
|
||||
*
|
||||
* @todo Pass $request object from controller so we don't have to rely on $_GET
|
||||
*
|
||||
* @return int ID of the current subsite instance
|
||||
*
|
||||
* @deprecated 2.0.0 Use SubsiteState::singleton()->getSubsiteId() instead
|
||||
*/
|
||||
public static function currentSubsiteID()
|
||||
{
|
||||
$id = null;
|
||||
|
||||
if (isset($_GET['SubsiteID'])) {
|
||||
$id = (int)$_GET['SubsiteID'];
|
||||
} elseif (Subsite::$use_session_subsiteid) {
|
||||
$id = Session::get('SubsiteID');
|
||||
}
|
||||
|
||||
if ($id === null) {
|
||||
$id = self::getSubsiteIDForDomain();
|
||||
}
|
||||
|
||||
return (int)$id;
|
||||
Deprecation::notice('2.0.0', 'Use SubsiteState::singleton()->getSubsiteId() instead');
|
||||
return SubsiteState::singleton()->getSubsiteId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch to another subsite through storing the subsite identifier in the current PHP session.
|
||||
* Only takes effect when {@link Subsite::$use_session_subsiteid} is set to TRUE.
|
||||
* Only takes effect when {@link SubsiteState::singleton()->getUseSessions()} is set to TRUE.
|
||||
*
|
||||
* @param int|Subsite $subsite Either the ID of the subsite, or the subsite object itself
|
||||
*/
|
||||
@ -128,7 +219,7 @@ class Subsite extends DataObject
|
||||
{
|
||||
// Session subsite change only meaningful if the session is active.
|
||||
// Otherwise we risk setting it to wrong value, e.g. if we rely on currentSubsiteID.
|
||||
if (!Subsite::$use_session_subsiteid) {
|
||||
if (!SubsiteState::singleton()->getUseSessions()) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -138,17 +229,17 @@ class Subsite extends DataObject
|
||||
$subsiteID = $subsite;
|
||||
}
|
||||
|
||||
Session::set('SubsiteID', (int)$subsiteID);
|
||||
SubsiteState::singleton()->setSubsiteId($subsiteID);
|
||||
|
||||
// Set locale
|
||||
if (is_object($subsite) && $subsite->Language != '') {
|
||||
$locale = i18n::get_locale_from_lang($subsite->Language);
|
||||
if (is_object($subsite) && $subsite->Language !== '') {
|
||||
$locale = (new IntlLocales())->localeFromLang($subsite->Language);
|
||||
if ($locale) {
|
||||
i18n::set_locale($locale);
|
||||
}
|
||||
}
|
||||
|
||||
Permission::flush_permission_cache();
|
||||
Permission::reset();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -157,7 +248,8 @@ class Subsite extends DataObject
|
||||
* for example matching all subdomains on *.example.com with one subsite,
|
||||
* and all subdomains on *.example.org on another.
|
||||
*
|
||||
* @param $host The host to find the subsite for. If not specified, $_SERVER['HTTP_HOST'] is used.
|
||||
* @param $host string The host to find the subsite for. If not specified, $_SERVER['HTTP_HOST'] is used.
|
||||
* @param bool $checkPermissions
|
||||
* @return int Subsite ID
|
||||
*/
|
||||
public static function getSubsiteIDForDomain($host = null, $checkPermissions = true)
|
||||
@ -166,30 +258,52 @@ class Subsite extends DataObject
|
||||
$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;
|
||||
$cacheKey = null;
|
||||
if ($host) {
|
||||
if (!self::$strict_subdomain_matching) {
|
||||
$host = preg_replace('/^www\./', '', $host);
|
||||
if (!static::config()->get('strict_subdomain_matching')) {
|
||||
$host = preg_replace('/^www\./', '', $host ?? '');
|
||||
}
|
||||
|
||||
$cacheKey = implode('_', array($host, Member::currentUserID(), self::$check_is_public));
|
||||
if (isset(self::$_cache_subsite_for_domain[$cacheKey])) {
|
||||
return self::$_cache_subsite_for_domain[$cacheKey];
|
||||
$currentUserId = Security::getCurrentUser() ? Security::getCurrentUser()->ID : 0;
|
||||
$cacheKey = implode('_', [$host, $currentUserId, static::config()->get('check_is_public')]);
|
||||
if (isset(self::$cache_subsite_for_domain[$cacheKey])) {
|
||||
return self::$cache_subsite_for_domain[$cacheKey];
|
||||
}
|
||||
|
||||
$SQL_host = Convert::raw2sql($host);
|
||||
$matchingDomains = DataObject::get(
|
||||
"SubsiteDomain",
|
||||
"'$SQL_host' LIKE replace(\"SubsiteDomain\".\"Domain\",'*','%')",
|
||||
"\"IsPrimary\" DESC"
|
||||
)->innerJoin('Subsite', "\"Subsite\".\"ID\" = \"SubsiteDomain\".\"SubsiteID\" AND \"Subsite\".\"IsPublic\"=1");
|
||||
|
||||
$schema = DataObject::getSchema();
|
||||
|
||||
/** @skipUpgrade */
|
||||
$domainTableName = $schema->tableName(SubsiteDomain::class);
|
||||
|
||||
if (!DB::get_schema()->hasTable($domainTableName)) {
|
||||
// Table hasn't been created yet. Might be a dev/build, skip.
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ($matchingDomains && $matchingDomains->Count()) {
|
||||
$subsiteIDs = array_unique($matchingDomains->column('SubsiteID'));
|
||||
$subsiteDomains = array_unique($matchingDomains->column('Domain'));
|
||||
if (sizeof($subsiteIDs) > 1) {
|
||||
$subsiteTableName = $schema->tableName(__CLASS__);
|
||||
/** @skipUpgrade */
|
||||
$matchingDomains = DataObject::get(
|
||||
SubsiteDomain::class,
|
||||
"'$SQL_host' LIKE replace(\"{$domainTableName}\".\"Domain\",'*','%')",
|
||||
'"IsPrimary" DESC'
|
||||
)->innerJoin(
|
||||
$subsiteTableName,
|
||||
'"' . $subsiteTableName . '"."ID" = "SubsiteDomain"."SubsiteID" AND "'
|
||||
. $subsiteTableName . '"."IsPublic"=1'
|
||||
);
|
||||
}
|
||||
|
||||
if ($matchingDomains && $matchingDomains->count()) {
|
||||
$subsiteIDs = array_unique($matchingDomains->column('SubsiteID') ?? []);
|
||||
$subsiteDomains = array_unique($matchingDomains->column('Domain') ?? []);
|
||||
if (sizeof($subsiteIDs ?? []) > 1) {
|
||||
throw new UnexpectedValueException(sprintf(
|
||||
"Multiple subsites match on '%s': %s",
|
||||
$host,
|
||||
@ -198,16 +312,18 @@ class Subsite extends DataObject
|
||||
}
|
||||
|
||||
$subsiteID = $subsiteIDs[0];
|
||||
} elseif ($default = Subsite::get()->filter('DefaultSite', 1)->setQueriedColumns(array('ID'))->first()) {
|
||||
} else {
|
||||
if ($default = Subsite::get()->filter('DefaultSite', 1)->setQueriedColumns(['ID'])->first()) {
|
||||
// Check for a 'default' subsite
|
||||
$subsiteID = $default->ID;
|
||||
} else {
|
||||
// Default subsite id = 0, the main site
|
||||
$subsiteID = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ($cacheKey) {
|
||||
self::$_cache_subsite_for_domain[$cacheKey] = $subsiteID;
|
||||
self::$cache_subsite_for_domain[$cacheKey] = $subsiteID;
|
||||
}
|
||||
|
||||
return $subsiteID;
|
||||
@ -222,7 +338,7 @@ class Subsite extends DataObject
|
||||
* @param string $limit
|
||||
* @return DataList
|
||||
*/
|
||||
public static function get_from_all_subsites($className, $filter = "", $sort = "", $join = "", $limit = "")
|
||||
public static function get_from_all_subsites($className, $filter = '', $sort = '', $join = '', $limit = '')
|
||||
{
|
||||
$result = DataObject::get($className, $filter, $sort, $join, $limit);
|
||||
$result = $result->setDataQueryParam('Subsite.filter', false);
|
||||
@ -231,6 +347,7 @@ class Subsite extends DataObject
|
||||
|
||||
/**
|
||||
* Disable the sub-site filtering; queries will select from all subsites
|
||||
* @param bool $disabled
|
||||
*/
|
||||
public static function disable_subsite_filter($disabled = true)
|
||||
{
|
||||
@ -242,16 +359,19 @@ class Subsite extends DataObject
|
||||
*/
|
||||
public static function on_db_reset()
|
||||
{
|
||||
self::$_cache_accessible_sites = array();
|
||||
self::$_cache_subsite_for_domain = array();
|
||||
self::$cache_accessible_sites = [];
|
||||
self::$cache_subsite_for_domain = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all subsites, regardless of permissions (augmented with main site).
|
||||
*
|
||||
* @return SS_List List of {@link Subsite} objects (DataList or ArrayList).
|
||||
* @param bool $includeMainSite
|
||||
* @param string $mainSiteTitle
|
||||
* @return SS_List List of <a href='psi_element://Subsite'>Subsite</a> objects (DataList or ArrayList).
|
||||
* objects (DataList or ArrayList).
|
||||
*/
|
||||
public static function all_sites($includeMainSite = true, $mainSiteTitle = "Main site")
|
||||
public static function all_sites($includeMainSite = true, $mainSiteTitle = 'Main site')
|
||||
{
|
||||
$subsites = Subsite::get();
|
||||
|
||||
@ -274,20 +394,20 @@ class Subsite extends DataObject
|
||||
*
|
||||
* @return ArrayList of {@link Subsite} instances.
|
||||
*/
|
||||
public static function all_accessible_sites($includeMainSite = true, $mainSiteTitle = "Main site", $member = null)
|
||||
public static function all_accessible_sites($includeMainSite = true, $mainSiteTitle = 'Main site', $member = null)
|
||||
{
|
||||
// Rationalise member arguments
|
||||
if (!$member) {
|
||||
$member = Member::currentUser();
|
||||
$member = Security::getCurrentUser();
|
||||
}
|
||||
if (!$member) {
|
||||
return new ArrayList();
|
||||
return ArrayList::create();
|
||||
}
|
||||
if (!is_object($member)) {
|
||||
$member = DataObject::get_by_id('Member', $member);
|
||||
$member = DataObject::get_by_id(Member::class, $member);
|
||||
}
|
||||
|
||||
$subsites = new ArrayList();
|
||||
$subsites = ArrayList::create();
|
||||
|
||||
// Collect subsites for all sections.
|
||||
$menu = CMSMenu::get_viewable_menu_items();
|
||||
@ -314,22 +434,27 @@ class Subsite extends DataObject
|
||||
* Sites will only be included if they have a Title.
|
||||
*
|
||||
* @param $permCode array|string Either a single permission code or an array of permission codes.
|
||||
* @param $includeMainSite If true, the main site will be included if appropriate.
|
||||
* @param $mainSiteTitle The label to give to the main site
|
||||
* @param $member
|
||||
* @return DataList of {@link Subsite} instances
|
||||
* @param $includeMainSite bool If true, the main site will be included if appropriate.
|
||||
* @param $mainSiteTitle string The label to give to the main site
|
||||
* @param $member int|Member The member attempting to access the sites
|
||||
* @return DataList|ArrayList of {@link Subsite} instances
|
||||
*/
|
||||
public static function accessible_sites($permCode, $includeMainSite = true, $mainSiteTitle = "Main site", $member = null)
|
||||
{
|
||||
public static function accessible_sites(
|
||||
$permCode,
|
||||
$includeMainSite = true,
|
||||
$mainSiteTitle = 'Main site',
|
||||
$member = null
|
||||
) {
|
||||
|
||||
// Rationalise member arguments
|
||||
if (!$member) {
|
||||
$member = Member::currentUser();
|
||||
$member = Security::getCurrentUser();
|
||||
}
|
||||
if (!$member) {
|
||||
return new ArrayList();
|
||||
}
|
||||
if (!is_object($member)) {
|
||||
$member = DataObject::get_by_id('Member', $member);
|
||||
$member = DataObject::get_by_id(Member::class, $member);
|
||||
}
|
||||
|
||||
// Rationalise permCode argument
|
||||
@ -341,29 +466,54 @@ class Subsite extends DataObject
|
||||
|
||||
// Cache handling
|
||||
$cacheKey = $SQL_codes . '-' . $member->ID . '-' . $includeMainSite . '-' . $mainSiteTitle;
|
||||
if (isset(self::$_cache_accessible_sites[$cacheKey])) {
|
||||
return self::$_cache_accessible_sites[$cacheKey];
|
||||
if (isset(self::$cache_accessible_sites[$cacheKey])) {
|
||||
return self::$cache_accessible_sites[$cacheKey];
|
||||
}
|
||||
|
||||
$subsites = DataList::create('Subsite')
|
||||
/** @skipUpgrade */
|
||||
$subsites = DataList::create(Subsite::class)
|
||||
->where("\"Subsite\".\"Title\" != ''")
|
||||
->leftJoin('Group_Subsites', "\"Group_Subsites\".\"SubsiteID\" = \"Subsite\".\"ID\"")
|
||||
->innerJoin('Group', "\"Group\".\"ID\" = \"Group_Subsites\".\"GroupID\" OR \"Group\".\"AccessAllSubsites\" = 1")
|
||||
->innerJoin('Group_Members', "\"Group_Members\".\"GroupID\"=\"Group\".\"ID\" AND \"Group_Members\".\"MemberID\" = $member->ID")
|
||||
->innerJoin('Permission', "\"Group\".\"ID\"=\"Permission\".\"GroupID\" AND \"Permission\".\"Code\" IN ($SQL_codes, 'CMS_ACCESS_LeftAndMain', 'ADMIN')");
|
||||
->leftJoin('Group_Subsites', '"Group_Subsites"."SubsiteID" = "Subsite"."ID"')
|
||||
->innerJoin(
|
||||
'Group',
|
||||
'"Group"."ID" = "Group_Subsites"."GroupID" OR "Group"."AccessAllSubsites" = 1'
|
||||
)
|
||||
->innerJoin(
|
||||
'Group_Members',
|
||||
"\"Group_Members\".\"GroupID\"=\"Group\".\"ID\" AND \"Group_Members\".\"MemberID\" = $member->ID"
|
||||
)
|
||||
->innerJoin(
|
||||
'Permission',
|
||||
"\"Group\".\"ID\"=\"Permission\".\"GroupID\"
|
||||
AND \"Permission\".\"Code\"
|
||||
IN ($SQL_codes, 'CMS_ACCESS_LeftAndMain', 'ADMIN')"
|
||||
);
|
||||
|
||||
if (!$subsites) {
|
||||
$subsites = new ArrayList();
|
||||
}
|
||||
|
||||
$rolesSubsites = DataList::create('Subsite')
|
||||
/** @var DataList $rolesSubsites */
|
||||
/** @skipUpgrade */
|
||||
$rolesSubsites = DataList::create(Subsite::class)
|
||||
->where("\"Subsite\".\"Title\" != ''")
|
||||
->leftJoin('Group_Subsites', "\"Group_Subsites\".\"SubsiteID\" = \"Subsite\".\"ID\"")
|
||||
->innerJoin('Group', "\"Group\".\"ID\" = \"Group_Subsites\".\"GroupID\" OR \"Group\".\"AccessAllSubsites\" = 1")
|
||||
->innerJoin('Group_Members', "\"Group_Members\".\"GroupID\"=\"Group\".\"ID\" AND \"Group_Members\".\"MemberID\" = $member->ID")
|
||||
->innerJoin('Group_Roles', "\"Group_Roles\".\"GroupID\"=\"Group\".\"ID\"")
|
||||
->innerJoin('PermissionRole', "\"Group_Roles\".\"PermissionRoleID\"=\"PermissionRole\".\"ID\"")
|
||||
->innerJoin('PermissionRoleCode', "\"PermissionRole\".\"ID\"=\"PermissionRoleCode\".\"RoleID\" AND \"PermissionRoleCode\".\"Code\" IN ($SQL_codes, 'CMS_ACCESS_LeftAndMain', 'ADMIN')");
|
||||
->leftJoin('Group_Subsites', '"Group_Subsites"."SubsiteID" = "Subsite"."ID"')
|
||||
->innerJoin(
|
||||
'Group',
|
||||
'"Group"."ID" = "Group_Subsites"."GroupID" OR "Group"."AccessAllSubsites" = 1'
|
||||
)
|
||||
->innerJoin(
|
||||
'Group_Members',
|
||||
"\"Group_Members\".\"GroupID\"=\"Group\".\"ID\" AND \"Group_Members\".\"MemberID\" = $member->ID"
|
||||
)
|
||||
->innerJoin('Group_Roles', '"Group_Roles"."GroupID"="Group"."ID"')
|
||||
->innerJoin('PermissionRole', '"Group_Roles"."PermissionRoleID"="PermissionRole"."ID"')
|
||||
->innerJoin(
|
||||
'PermissionRoleCode',
|
||||
"\"PermissionRole\".\"ID\"=\"PermissionRoleCode\".\"RoleID\"
|
||||
AND \"PermissionRoleCode\".\"Code\"
|
||||
IN ($SQL_codes, 'CMS_ACCESS_LeftAndMain', 'ADMIN')"
|
||||
);
|
||||
|
||||
if (!$subsites && $rolesSubsites) {
|
||||
return $rolesSubsites;
|
||||
@ -381,19 +531,19 @@ class Subsite extends DataObject
|
||||
|
||||
if ($includeMainSite) {
|
||||
if (!is_array($permCode)) {
|
||||
$permCode = array($permCode);
|
||||
$permCode = [$permCode];
|
||||
}
|
||||
if (self::hasMainSitePermission($member, $permCode)) {
|
||||
$subsites=$subsites->toArray();
|
||||
$subsites = $subsites->toArray();
|
||||
|
||||
$mainSite = new Subsite();
|
||||
$mainSite->Title = $mainSiteTitle;
|
||||
array_unshift($subsites, $mainSite);
|
||||
$subsites=ArrayList::create($subsites);
|
||||
$subsites = ArrayList::create($subsites);
|
||||
}
|
||||
}
|
||||
|
||||
self::$_cache_accessible_sites[$cacheKey] = $subsites;
|
||||
self::$cache_accessible_sites[$cacheKey] = $subsites;
|
||||
|
||||
return $subsites;
|
||||
}
|
||||
@ -408,16 +558,17 @@ class Subsite extends DataObject
|
||||
*/
|
||||
public static function writeHostMap($file = null)
|
||||
{
|
||||
if (!self::$write_hostmap) {
|
||||
if (!static::config()->get('write_hostmap')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$file) {
|
||||
$file = Director::baseFolder().'/subsites/host-map.php';
|
||||
$subsitesPath = ModuleLoader::getModule('silverstripe/subsites')->getRelativePath();
|
||||
$file = Director::baseFolder() . $subsitesPath . '/host-map.php';
|
||||
}
|
||||
$hostmap = array();
|
||||
$hostmap = [];
|
||||
|
||||
$subsites = DataObject::get('Subsite');
|
||||
$subsites = DataObject::get(Subsite::class);
|
||||
|
||||
if ($subsites) {
|
||||
foreach ($subsites as $subsite) {
|
||||
@ -425,8 +576,8 @@ class Subsite extends DataObject
|
||||
if ($domains) {
|
||||
foreach ($domains as $domain) {
|
||||
$domainStr = $domain->Domain;
|
||||
if (!self::$strict_subdomain_matching) {
|
||||
$domainStr = preg_replace('/^www\./', '', $domainStr);
|
||||
if (!static::config()->get('strict_subdomain_matching')) {
|
||||
$domainStr = preg_replace('/^www\./', '', $domainStr ?? '');
|
||||
}
|
||||
$hostmap[$domainStr] = $subsite->domain();
|
||||
}
|
||||
@ -441,8 +592,8 @@ class Subsite extends DataObject
|
||||
$data .= "// Generated by Subsite::writeHostMap() on " . date('d/M/y') . "\n";
|
||||
$data .= '$subsiteHostmap = ' . var_export($hostmap, true) . ';';
|
||||
|
||||
if (is_writable(dirname($file)) || is_writable($file)) {
|
||||
file_put_contents($file, $data);
|
||||
if (is_writable(dirname($file ?? '')) || is_writable($file ?? '')) {
|
||||
file_put_contents($file ?? '', $data);
|
||||
}
|
||||
}
|
||||
|
||||
@ -456,25 +607,25 @@ class Subsite extends DataObject
|
||||
* @todo Allow permission inheritance through group hierarchy.
|
||||
*
|
||||
* @param Member Member to check against. Defaults to currently logged in member
|
||||
* @param Array Permission code strings. Defaults to "ADMIN".
|
||||
* @return boolean
|
||||
* @param array $permissionCodes
|
||||
* @return bool
|
||||
*/
|
||||
public static function hasMainSitePermission($member = null, $permissionCodes = array('ADMIN'))
|
||||
public static function hasMainSitePermission($member = null, $permissionCodes = ['ADMIN'])
|
||||
{
|
||||
if (!is_array($permissionCodes)) {
|
||||
user_error('Permissions must be passed to Subsite::hasMainSitePermission as an array', E_USER_ERROR);
|
||||
}
|
||||
|
||||
if (!$member && $member !== false) {
|
||||
$member = Member::currentUser();
|
||||
$member = Security::getCurrentUser();
|
||||
}
|
||||
|
||||
if (!$member) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!in_array("ADMIN", $permissionCodes)) {
|
||||
$permissionCodes[] = "ADMIN";
|
||||
if (!in_array('ADMIN', $permissionCodes ?? [])) {
|
||||
$permissionCodes[] = 'ADMIN';
|
||||
}
|
||||
|
||||
$SQLa_perm = Convert::raw2sql($permissionCodes);
|
||||
@ -485,10 +636,12 @@ class Subsite extends DataObject
|
||||
$groupCount = DB::query("
|
||||
SELECT COUNT(\"Permission\".\"ID\")
|
||||
FROM \"Permission\"
|
||||
INNER JOIN \"Group\" ON \"Group\".\"ID\" = \"Permission\".\"GroupID\" AND \"Group\".\"AccessAllSubsites\" = 1
|
||||
INNER JOIN \"Group_Members\" ON \"Group_Members\".\"GroupID\" = \"Permission\".\"GroupID\"
|
||||
WHERE \"Permission\".\"Code\" IN ('$SQL_perms')
|
||||
AND \"Group_Members\".\"MemberID\" = {$memberID}
|
||||
INNER JOIN \"Group\"
|
||||
ON \"Group\".\"ID\" = \"Permission\".\"GroupID\" AND \"Group\".\"AccessAllSubsites\" = 1
|
||||
INNER JOIN \"Group_Members\"
|
||||
ON \"Group_Members\".\"GroupID\" = \"Permission\".\"GroupID\"
|
||||
WHERE \"Permission\".\"Code\"
|
||||
IN ('$SQL_perms') AND \"Group_Members\".\"MemberID\" = {$memberID}
|
||||
")->value();
|
||||
|
||||
// Count this user's groups which have a role that can access the main site
|
||||
@ -508,71 +661,18 @@ class Subsite extends DataObject
|
||||
return ($groupCount + $roleCount > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $db = array(
|
||||
'Title' => 'Varchar(255)',
|
||||
'RedirectURL' => 'Varchar(255)',
|
||||
'DefaultSite' => 'Boolean',
|
||||
'Theme' => 'Varchar',
|
||||
'Language' => 'Varchar(6)',
|
||||
|
||||
// Used to hide unfinished/private subsites from public view.
|
||||
// If unset, will default to true
|
||||
'IsPublic' => 'Boolean',
|
||||
|
||||
// Comma-separated list of disallowed page types
|
||||
'PageTypeBlacklist' => 'Text',
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $has_many = array(
|
||||
'Domains' => 'SubsiteDomain',
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $belongs_many_many = array(
|
||||
"Groups" => "Group",
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $defaults = array(
|
||||
'IsPublic' => 1
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $searchable_fields = array(
|
||||
'Title',
|
||||
'Domains.Domain',
|
||||
'IsPublic',
|
||||
);
|
||||
|
||||
/**
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private static $default_sort = "\"Title\" ASC";
|
||||
|
||||
/**
|
||||
* @todo Possible security issue, don't grant edit permissions to everybody.
|
||||
* @return boolean
|
||||
* @param bool $member
|
||||
* @return bool
|
||||
*/
|
||||
public function canEdit($member = false)
|
||||
{
|
||||
$extended = $this->extendedCan(__FUNCTION__, $member);
|
||||
if ($extended !== null) {
|
||||
return $extended;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -583,79 +683,82 @@ class Subsite extends DataObject
|
||||
*/
|
||||
public function getCMSFields()
|
||||
{
|
||||
if ($this->ID != 0) {
|
||||
$domainTable = GridField::create(
|
||||
"Domains",
|
||||
_t('Subsite.DomainsListTitle', "Domains"),
|
||||
$this->beforeUpdateCMSFields(function (FieldList $fields) {
|
||||
if ($this->exists()) {
|
||||
// Add a GridField for domains to a new tab if the subsite has already been created
|
||||
$fields->addFieldsToTab('Root.Domains', [
|
||||
GridField::create(
|
||||
'Domains',
|
||||
'',
|
||||
$this->Domains(),
|
||||
GridFieldConfig_RecordEditor::create(10)
|
||||
);
|
||||
} else {
|
||||
$domainTable = LiteralField::create(
|
||||
'Domains',
|
||||
'<p>'._t('Subsite.DOMAINSAVEFIRST', 'You can only add domains after saving for the first time').'</p>'
|
||||
);
|
||||
)
|
||||
]);
|
||||
}
|
||||
|
||||
$languageSelector = new DropdownField(
|
||||
// Remove the default scaffolded blacklist field, we replace it with a checkbox set field
|
||||
// in a wrapper further down. The RedirectURL field is currently not in use.
|
||||
$fields->removeByName(['PageTypeBlacklist', 'RedirectURL']);
|
||||
|
||||
$fields->addFieldToTab('Root.Main', DropdownField::create(
|
||||
'Language',
|
||||
$this->fieldLabel('Language'),
|
||||
i18n::get_common_locales()
|
||||
);
|
||||
Injector::inst()->get(IntlLocales::class)->getLocales()
|
||||
), 'DefaultSite');
|
||||
|
||||
$pageTypeMap = array();
|
||||
$pageTypes = SiteTree::page_type_classes();
|
||||
foreach ($pageTypes as $pageType) {
|
||||
$pageTypeMap[$pageType] = singleton($pageType)->i18n_singular_name();
|
||||
}
|
||||
asort($pageTypeMap);
|
||||
|
||||
$fields = FieldList::create(
|
||||
$subsiteTabs = TabSet::create('Root',
|
||||
Tab::create(
|
||||
'Configuration',
|
||||
_t('Subsite.TabTitleConfig', 'Configuration'),
|
||||
HeaderField::create($this->getClassName() . ' configuration', 2),
|
||||
TextField::create('Title', $this->fieldLabel('Title'), $this->Title),
|
||||
|
||||
HeaderField::create(
|
||||
_t('Subsite.DomainsHeadline', "Domains for this subsite")
|
||||
),
|
||||
$domainTable,
|
||||
$languageSelector,
|
||||
// new TextField('RedirectURL', 'Redirect to URL', $this->RedirectURL),
|
||||
CheckboxField::create('DefaultSite', $this->fieldLabel('DefaultSite'), $this->DefaultSite),
|
||||
CheckboxField::create('IsPublic', $this->fieldLabel('IsPublic'), $this->IsPublic),
|
||||
|
||||
LiteralField::create(
|
||||
$fields->addFieldsToTab('Root.Main', [
|
||||
ToggleCompositeField::create(
|
||||
'PageTypeBlacklistToggle',
|
||||
sprintf(
|
||||
'<div class="field"><a href="#" id="PageTypeBlacklistToggle">%s</a></div>',
|
||||
_t('Subsite.PageTypeBlacklistField', 'Disallow page types?')
|
||||
)
|
||||
),
|
||||
CheckboxSetField::create(
|
||||
'PageTypeBlacklist',
|
||||
false,
|
||||
$pageTypeMap
|
||||
)
|
||||
)
|
||||
),
|
||||
_t(__CLASS__ . '.PageTypeBlacklistField', 'Disallow page types?'),
|
||||
[
|
||||
CheckboxSetField::create('PageTypeBlacklist', '', $this->getPageTypeMap())
|
||||
]
|
||||
)->setHeadingLevel(4),
|
||||
HiddenField::create('ID', '', $this->ID),
|
||||
HiddenField::create('IsSubsite', '', 1)
|
||||
);
|
||||
]);
|
||||
|
||||
// If there are any themes available, add the dropdown
|
||||
$themes = $this->allowedThemes();
|
||||
if (!empty($themes)) {
|
||||
$fields->addFieldToTab('Root.Configuration',
|
||||
DropdownField::create('Theme', $this->fieldLabel('Theme'), $this->allowedThemes(), $this->Theme)->setEmptyString(_t('Subsite.ThemeFieldEmptyString', '')), 'PageTypeBlacklistToggle');
|
||||
$fields->addFieldToTab(
|
||||
'Root.Main',
|
||||
DropdownField::create('Theme', $this->fieldLabel('Theme'), $this->allowedThemes(), $this->Theme)
|
||||
->setEmptyString(_t(__CLASS__ . '.ThemeFieldEmptyString', '-')),
|
||||
'PageTypeBlacklistToggle'
|
||||
);
|
||||
}
|
||||
|
||||
$subsiteTabs->addExtraClass('subsite-model');
|
||||
// Targetted by the XHR PJAX JavaScript to reload the subsite list in the CMS
|
||||
$fields->fieldByName('Root.Main')->addExtraClass('subsite-model');
|
||||
|
||||
$this->extend('updateCMSFields', $fields);
|
||||
return $fields;
|
||||
// We don't need the Groups many many tab
|
||||
$fields->removeByName('Groups');
|
||||
|
||||
// Rename the main tab to configuration
|
||||
$fields->fieldByName('Root.Main')->setTitle(_t(__CLASS__ . '.ConfigurationTab', 'Configuration'));
|
||||
});
|
||||
|
||||
return parent::getCMSFields();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of the different page types available to the CMS
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getPageTypeMap()
|
||||
{
|
||||
$pageTypeMap = [];
|
||||
|
||||
$pageTypes = SiteTree::page_type_classes();
|
||||
foreach ($pageTypes as $pageType) {
|
||||
$pageTypeMap[$pageType] = singleton($pageType)->i18n_singular_name();
|
||||
}
|
||||
|
||||
asort($pageTypeMap);
|
||||
|
||||
return $pageTypeMap;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -671,7 +774,7 @@ class Subsite extends DataObject
|
||||
$labels['DefaultSite'] = _t('Subsites.DefaultSiteFieldLabel', 'Default site');
|
||||
$labels['Theme'] = _t('Subsites.ThemeFieldLabel', 'Theme');
|
||||
$labels['Language'] = _t('Subsites.LanguageFieldLabel', 'Language');
|
||||
$labels['IsPublic'] = _t('Subsites.IsPublicFieldLabel', 'Enable public access');
|
||||
$labels['IsPublic.Nice'] = _t('Subsites.IsPublicFieldLabel', 'Enable public access');
|
||||
$labels['PageTypeBlacklist'] = _t('Subsites.PageTypeBlacklistFieldLabel', 'Page Type Blacklist');
|
||||
$labels['Domains.Domain'] = _t('Subsites.DomainFieldLabel', 'Domain');
|
||||
$labels['PrimaryDomain'] = _t('Subsites.PrimaryDomainFieldLabel', 'Primary Domain');
|
||||
@ -679,19 +782,6 @@ class Subsite extends DataObject
|
||||
return $labels;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function summaryFields()
|
||||
{
|
||||
return array(
|
||||
'Title' => $this->fieldLabel('Title'),
|
||||
'PrimaryDomain' => $this->fieldLabel('PrimaryDomain'),
|
||||
'IsPublic' => _t('Subsite.IsPublicHeaderField', 'Active subsite'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the themes that can be used with this subsite, as an array of themecode => description
|
||||
*
|
||||
@ -699,23 +789,23 @@ class Subsite extends DataObject
|
||||
*/
|
||||
public function allowedThemes()
|
||||
{
|
||||
if ($themes = $this->stat('allowed_themes')) {
|
||||
if (($themes = self::$allowed_themes) || ($themes = ThemeResolver::singleton()->getCustomThemeOptions())) {
|
||||
return ArrayLib::valuekey($themes);
|
||||
} else {
|
||||
$themes = array();
|
||||
}
|
||||
|
||||
$themes = [];
|
||||
if (is_dir(THEMES_PATH)) {
|
||||
foreach (scandir(THEMES_PATH) as $theme) {
|
||||
if ($theme[0] == '.') {
|
||||
continue;
|
||||
}
|
||||
$theme = strtok($theme, '_');
|
||||
$theme = strtok($theme ?? '', '_');
|
||||
$themes[$theme] = $theme;
|
||||
}
|
||||
ksort($themes);
|
||||
}
|
||||
return $themes;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string Current locale of the subsite
|
||||
@ -724,20 +814,20 @@ class Subsite extends DataObject
|
||||
{
|
||||
if ($this->getField('Language')) {
|
||||
return $this->getField('Language');
|
||||
} else {
|
||||
return i18n::get_locale();
|
||||
}
|
||||
|
||||
return i18n::get_locale();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return ValidationResult
|
||||
* @return \SilverStripe\ORM\ValidationResult
|
||||
*/
|
||||
public function validate()
|
||||
{
|
||||
$result = parent::validate();
|
||||
if (!$this->Title) {
|
||||
$result->error(_t('Subsite.ValidateTitle', 'Please add a "Title"'));
|
||||
$result->addError(_t(__CLASS__ . '.ValidateTitle', 'Please add a "Title"'));
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
@ -761,15 +851,14 @@ class Subsite extends DataObject
|
||||
*/
|
||||
protected function createDefaultPages()
|
||||
{
|
||||
$currentSubsite = Subsite::currentSubsiteID();
|
||||
Subsite::changeSubsite($this->ID);
|
||||
SubsiteState::singleton()->withState(function (SubsiteState $newState) {
|
||||
$newState->setSubsiteId($this->ID);
|
||||
|
||||
// Silence DB schema output
|
||||
DB::quiet();
|
||||
$siteTree = new SiteTree();
|
||||
$siteTree->requireDefaultRecords();
|
||||
|
||||
Subsite::changeSubsite($currentSubsite);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -787,7 +876,7 @@ class Subsite extends DataObject
|
||||
}
|
||||
|
||||
// If there are no objects, default to the current hostname
|
||||
return $_SERVER['HTTP_HOST'];
|
||||
return Director::host();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -828,14 +917,6 @@ class Subsite extends DataObject
|
||||
return Director::absoluteBaseURL();
|
||||
}
|
||||
|
||||
/**
|
||||
* @todo getClassName is redundant, already stored as a database field?
|
||||
*/
|
||||
public function getClassName()
|
||||
{
|
||||
return $this->class;
|
||||
}
|
||||
|
||||
/**
|
||||
* Javascript admin action to duplicate this subsite
|
||||
*
|
||||
@ -845,9 +926,9 @@ class Subsite extends DataObject
|
||||
{
|
||||
$newItem = $this->duplicate();
|
||||
$message = _t(
|
||||
'Subsite.CopyMessage',
|
||||
__CLASS__ . '.CopyMessage',
|
||||
'Created a copy of {title}',
|
||||
array('title' => Convert::raw2js($this->Title))
|
||||
['title' => Convert::raw2js($this->Title)]
|
||||
);
|
||||
|
||||
return <<<JS
|
||||
@ -869,7 +950,7 @@ JS;
|
||||
* @param array $permissionCodes
|
||||
* @return DataList
|
||||
*/
|
||||
public function getMembersByPermission($permissionCodes = array('ADMIN'))
|
||||
public function getMembersByPermission($permissionCodes = ['ADMIN'])
|
||||
{
|
||||
if (!is_array($permissionCodes)) {
|
||||
user_error('Permissions must be passed to Subsite::getMembersByPermission as an array', E_USER_ERROR);
|
||||
@ -879,23 +960,26 @@ JS;
|
||||
$SQL_permissionCodes = join("','", $SQL_permissionCodes);
|
||||
|
||||
return DataObject::get(
|
||||
'Member',
|
||||
Member::class,
|
||||
"\"Group\".\"SubsiteID\" = $this->ID AND \"Permission\".\"Code\" IN ('$SQL_permissionCodes')",
|
||||
'',
|
||||
"LEFT JOIN \"Group_Members\" ON \"Member\".\"ID\" = \"Group_Members\".\"MemberID\"
|
||||
LEFT JOIN \"Group\" ON \"Group\".\"ID\" = \"Group_Members\".\"GroupID\"
|
||||
LEFT JOIN \"Permission\" ON \"Permission\".\"GroupID\" = \"Group\".\"ID\""
|
||||
'LEFT JOIN "Group_Members" ON "Member"."ID" = "Group_Members"."MemberID"
|
||||
LEFT JOIN "Group" ON "Group"."ID" = "Group_Members"."GroupID"
|
||||
LEFT JOIN "Permission" ON "Permission"."GroupID" = "Group"."ID"'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate this subsite
|
||||
* @param bool $doWrite
|
||||
* @param string $manyMany
|
||||
* @return DataObject
|
||||
*/
|
||||
public function duplicate($doWrite = true)
|
||||
public function duplicate($doWrite = true, $manyMany = 'many_many')
|
||||
{
|
||||
$duplicate = parent::duplicate($doWrite);
|
||||
|
||||
$oldSubsiteID = Session::get('SubsiteID');
|
||||
$oldSubsiteID = SubsiteState::singleton()->getSubsiteId();
|
||||
self::changeSubsite($this->ID);
|
||||
|
||||
/*
|
||||
@ -904,8 +988,8 @@ JS;
|
||||
* issues with having to check whether or not the new parents have been added to the site tree
|
||||
* when a page, etc, is duplicated
|
||||
*/
|
||||
$stack = array(array(0,0));
|
||||
while (count($stack) > 0) {
|
||||
$stack = [[0, 0]];
|
||||
while (count($stack ?? []) > 0) {
|
||||
list($sourceParentID, $destParentID) = array_pop($stack);
|
||||
$children = Versioned::get_by_stage('Page', 'Live', "\"ParentID\" = $sourceParentID", '');
|
||||
|
||||
@ -916,11 +1000,11 @@ JS;
|
||||
$childClone = $child->duplicateToSubsite($duplicate, false);
|
||||
$childClone->ParentID = $destParentID;
|
||||
$childClone->writeToStage('Stage');
|
||||
$childClone->publish('Stage', 'Live');
|
||||
$childClone->copyVersionToStage('Stage', 'Live');
|
||||
|
||||
self::changeSubsite($this->ID); //Change Back to this subsite
|
||||
|
||||
array_push($stack, array($child->ID, $childClone->ID));
|
||||
array_push($stack, [$child->ID, $childClone->ID]);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,5 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Model;
|
||||
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Control\Director;
|
||||
use SilverStripe\Forms\CheckboxField;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\OptionsetField;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\Subsites\Forms\WildcardDomainField;
|
||||
|
||||
/**
|
||||
* @property string $Domain domain name of this subsite. Can include wildcards. Do not include the URL scheme here
|
||||
* @property string $Protocol Required protocol (http or https) if only one is supported. 'automatic' implies
|
||||
@ -10,15 +20,23 @@
|
||||
*/
|
||||
class SubsiteDomain extends DataObject
|
||||
{
|
||||
|
||||
private static $table_name = 'SubsiteDomain';
|
||||
|
||||
/**
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private static $default_sort = '"IsPrimary" DESC';
|
||||
|
||||
/** *
|
||||
* @var array
|
||||
*/
|
||||
private static $db = array(
|
||||
"Domain" => "Varchar(255)",
|
||||
"Protocol" => "Enum('http,https,automatic','automatic')",
|
||||
"IsPrimary" => "Boolean",
|
||||
);
|
||||
private static $db = [
|
||||
'Domain' => 'Varchar(255)',
|
||||
'Protocol' => "Enum('http,https,automatic','automatic')",
|
||||
'IsPrimary' => 'Boolean',
|
||||
];
|
||||
|
||||
/**
|
||||
* Specifies that this subsite is http only
|
||||
@ -49,28 +67,27 @@ class SubsiteDomain extends DataObject
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private static $has_one = array(
|
||||
"Subsite" => "Subsite",
|
||||
);
|
||||
private static $has_one = [
|
||||
'Subsite' => Subsite::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* @config
|
||||
* @var array
|
||||
*/
|
||||
private static $summary_fields = array(
|
||||
private static $summary_fields = [
|
||||
'Domain',
|
||||
'IsPrimary',
|
||||
);
|
||||
];
|
||||
|
||||
/**
|
||||
* @config
|
||||
/*** @config
|
||||
* @var array
|
||||
*/
|
||||
private static $casting = array(
|
||||
private static $casting = [
|
||||
'SubstitutedDomain' => 'Varchar',
|
||||
'FullProtocol' => 'Varchar',
|
||||
'AbsoluteLink' => 'Varchar',
|
||||
);
|
||||
];
|
||||
|
||||
/**
|
||||
* Whenever a Subsite Domain is written, rewrite the hostmap
|
||||
@ -85,31 +102,31 @@ class SubsiteDomain extends DataObject
|
||||
|
||||
/**
|
||||
*
|
||||
* @return \FieldList
|
||||
* @return FieldList
|
||||
*/
|
||||
public function getCMSFields()
|
||||
{
|
||||
$protocols = array(
|
||||
self::PROTOCOL_HTTP => _t('SubsiteDomain.PROTOCOL_HTTP', 'http://'),
|
||||
self::PROTOCOL_HTTPS => _t('SubsiteDomain.PROTOCOL_HTTPS', 'https://'),
|
||||
self::PROTOCOL_AUTOMATIC => _t('SubsiteDomain.PROTOCOL_AUTOMATIC', 'Automatic')
|
||||
);
|
||||
|
||||
$fields = new FieldList(
|
||||
$protocols = [
|
||||
self::PROTOCOL_HTTP => _t(__CLASS__ . '.PROTOCOL_HTTP', 'http://'),
|
||||
self::PROTOCOL_HTTPS => _t(__CLASS__ . '.PROTOCOL_HTTPS', 'https://'),
|
||||
self::PROTOCOL_AUTOMATIC => _t(__CLASS__ . '.PROTOCOL_AUTOMATIC', 'Automatic')
|
||||
];
|
||||
$fields = FieldList::create(
|
||||
WildcardDomainField::create('Domain', $this->fieldLabel('Domain'), null, 255)
|
||||
->setDescription(_t(
|
||||
'SubsiteDomain.DOMAIN_DESCRIPTION',
|
||||
__CLASS__ . '.DOMAIN_DESCRIPTION',
|
||||
'Hostname of this subsite (exclude protocol). Allows wildcards (*).'
|
||||
)),
|
||||
OptionsetField::create('Protocol', $this->fieldLabel('Protocol'), $protocols)
|
||||
->setValue($this->Protocol ?: self::PROTOCOL_AUTOMATIC)
|
||||
->setDescription(_t(
|
||||
'SubsiteDomain.PROTOCOL_DESCRIPTION',
|
||||
__CLASS__ . '.PROTOCOL_DESCRIPTION',
|
||||
'When generating links to this subsite, use the selected protocol. <br />' .
|
||||
'Selecting \'Automatic\' means subsite links will default to the current protocol.'
|
||||
)),
|
||||
CheckboxField::create('IsPrimary', $this->fieldLabel('IsPrimary'))
|
||||
->setDescription(_t(
|
||||
'SubsiteDomain.PROTOCOL_DESCRIPTION',
|
||||
__CLASS__ . '.ISPRIMARY_DESCRIPTION',
|
||||
'Mark this as the default domain for this subsite'
|
||||
))
|
||||
);
|
||||
@ -126,9 +143,9 @@ class SubsiteDomain extends DataObject
|
||||
public function fieldLabels($includerelations = true)
|
||||
{
|
||||
$labels = parent::fieldLabels($includerelations);
|
||||
$labels['Domain'] = _t('SubsiteDomain.DOMAIN', 'Domain');
|
||||
$labels['Protocol'] = _t('SubsiteDomain.Protocol', 'Protocol');
|
||||
$labels['IsPrimary'] = _t('SubsiteDomain.IS_PRIMARY', 'Is Primary Domain?');
|
||||
$labels['Domain'] = _t(__CLASS__ . '.DOMAIN', 'Domain');
|
||||
$labels['Protocol'] = _t(__CLASS__ . '.Protocol', 'Protocol');
|
||||
$labels['IsPrimary'] = _t(__CLASS__ . '.IS_PRIMARY', 'Is Primary Domain?');
|
||||
|
||||
return $labels;
|
||||
}
|
||||
@ -152,19 +169,13 @@ class SubsiteDomain extends DataObject
|
||||
{
|
||||
switch ($this->Protocol) {
|
||||
case self::PROTOCOL_HTTPS:
|
||||
{
|
||||
return 'https://';
|
||||
}
|
||||
case self::PROTOCOL_HTTP:
|
||||
{
|
||||
return 'http://';
|
||||
}
|
||||
default:
|
||||
{
|
||||
return Director::protocol();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves domain name with wildcards substituted with actual values
|
||||
@ -175,18 +186,18 @@ class SubsiteDomain extends DataObject
|
||||
*/
|
||||
public function getSubstitutedDomain()
|
||||
{
|
||||
$currentHost = $_SERVER['HTTP_HOST'];
|
||||
$currentHost = Director::host();
|
||||
|
||||
// If there are wildcards in the primary domain (not recommended), make some
|
||||
// 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
|
||||
// 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'
|
||||
$domain = str_replace('.www.', '.', $domain);
|
||||
$domain = str_replace('.www.', '.', $domain ?? '');
|
||||
|
||||
return $domain;
|
||||
}
|
250
src/Pages/SubsitesVirtualPage.php
Normal file
250
src/Pages/SubsitesVirtualPage.php
Normal file
@ -0,0 +1,250 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Pages;
|
||||
|
||||
use SilverStripe\CMS\Controllers\CMSPageEditController;
|
||||
use SilverStripe\CMS\Model\SiteTree;
|
||||
use SilverStripe\CMS\Model\VirtualPage;
|
||||
use SilverStripe\Control\Controller;
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\Forms\DropdownField;
|
||||
use SilverStripe\Forms\LiteralField;
|
||||
use SilverStripe\Forms\TextareaField;
|
||||
use SilverStripe\Forms\TextField;
|
||||
use SilverStripe\Forms\TreeDropdownField;
|
||||
use SilverStripe\ORM\ArrayList;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\Subsites\Forms\SubsitesTreeDropdownField;
|
||||
use SilverStripe\Subsites\Model\Subsite;
|
||||
use SilverStripe\Subsites\State\SubsiteState;
|
||||
use SilverStripe\View\ArrayData;
|
||||
|
||||
class SubsitesVirtualPage extends VirtualPage
|
||||
{
|
||||
|
||||
private static $table_name = 'SubsitesVirtualPage';
|
||||
|
||||
private static $description = 'Displays the content of a page on another subsite';
|
||||
|
||||
private static $db = [
|
||||
'CustomMetaTitle' => 'Varchar(255)',
|
||||
'CustomMetaKeywords' => 'Varchar(255)',
|
||||
'CustomMetaDescription' => 'Text',
|
||||
'CustomExtraMeta' => 'HTMLText'
|
||||
];
|
||||
|
||||
private static $non_virtual_fields = [
|
||||
'SubsiteID'
|
||||
];
|
||||
|
||||
public function getCMSFields()
|
||||
{
|
||||
$fields = parent::getCMSFields();
|
||||
|
||||
$subsites = DataObject::get(Subsite::class);
|
||||
if (!$subsites) {
|
||||
$subsites = new ArrayList();
|
||||
} else {
|
||||
$subsites = ArrayList::create($subsites->toArray());
|
||||
}
|
||||
|
||||
$subsites->push(new ArrayData(['Title' => 'Main site', 'ID' => 0]));
|
||||
|
||||
$fields->addFieldToTab(
|
||||
'Root.Main',
|
||||
DropdownField::create(
|
||||
'CopyContentFromID_SubsiteID',
|
||||
_t(__CLASS__ . '.SubsiteField', 'Subsite'),
|
||||
$subsites->map('ID', 'Title')
|
||||
)->addExtraClass('subsitestreedropdownfield-chooser no-change-track'),
|
||||
'CopyContentFromID'
|
||||
);
|
||||
|
||||
// Setup the linking to the original page.
|
||||
$pageSelectionField = SubsitesTreeDropdownField::create(
|
||||
'CopyContentFromID',
|
||||
_t('SilverStripe\\CMS\\Model\\VirtualPage.CHOOSE', 'Linked Page'),
|
||||
SiteTree::class,
|
||||
'ID',
|
||||
'MenuTitle'
|
||||
);
|
||||
|
||||
$fields->addFieldToTab(
|
||||
'Root.Main',
|
||||
TreeDropdownField::create('CopyContentFromID', 'Linked Page', SiteTree::class)
|
||||
);
|
||||
|
||||
if (Controller::has_curr() && Controller::curr()->getRequest()) {
|
||||
$subsiteID = (int) Controller::curr()->getRequest()->requestVar('CopyContentFromID_SubsiteID');
|
||||
$pageSelectionField->setSubsiteID($subsiteID);
|
||||
}
|
||||
$fields->replaceField('CopyContentFromID', $pageSelectionField);
|
||||
|
||||
// Create links back to the original object in the CMS
|
||||
if ($this->CopyContentFromID) {
|
||||
$editLink = Controller::join_links(
|
||||
CMSPageEditController::singleton()->Link('show'),
|
||||
$this->CopyContentFromID
|
||||
);
|
||||
|
||||
$linkToContent = "
|
||||
<a class=\"cmsEditlink\" href=\"$editLink\">" .
|
||||
_t('SilverStripe\\CMS\\Model\\VirtualPage.EDITCONTENT', 'Click here to edit the content') .
|
||||
'</a>';
|
||||
$fields->removeByName('VirtualPageContentLinkLabel');
|
||||
$fields->addFieldToTab(
|
||||
'Root.Main',
|
||||
$linkToContentLabelField = LiteralField::create('VirtualPageContentLinkLabel', $linkToContent),
|
||||
'Title'
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
$fields->addFieldToTab(
|
||||
'Root.Main',
|
||||
TextField::create(
|
||||
'CustomMetaTitle',
|
||||
$this->fieldLabel('CustomMetaTitle')
|
||||
)->setDescription(_t(__CLASS__ . '.OverrideNote', 'Overrides inherited value from the source')),
|
||||
'MetaTitle'
|
||||
);
|
||||
$fields->addFieldToTab(
|
||||
'Root.Main',
|
||||
TextareaField::create(
|
||||
'CustomMetaKeywords',
|
||||
$this->fieldLabel('CustomMetaKeywords')
|
||||
)->setDescription(_t(__CLASS__ . '.OverrideNote', 'Overrides inherited value from the source')),
|
||||
'MetaKeywords'
|
||||
);
|
||||
$fields->addFieldToTab(
|
||||
'Root.Main',
|
||||
TextareaField::create(
|
||||
'CustomMetaDescription',
|
||||
$this->fieldLabel('CustomMetaDescription')
|
||||
)->setDescription(_t(__CLASS__ . '.OverrideNote', 'Overrides inherited value from the source')),
|
||||
'MetaDescription'
|
||||
);
|
||||
$fields->addFieldToTab(
|
||||
'Root.Main',
|
||||
TextField::create(
|
||||
'CustomExtraMeta',
|
||||
$this->fieldLabel('CustomExtraMeta')
|
||||
)->setDescription(_t(__CLASS__ . '.OverrideNote', 'Overrides inherited value from the source')),
|
||||
'ExtraMeta'
|
||||
);
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
public function fieldLabels($includerelations = true)
|
||||
{
|
||||
$labels = parent::fieldLabels($includerelations);
|
||||
$labels['CustomMetaTitle'] = _t('SilverStripe\\Subsites\\Model\\Subsite.CustomMetaTitle', 'Title');
|
||||
$labels['CustomMetaKeywords'] = _t(
|
||||
'SilverStripe\\Subsites\\Model\\Subsite.CustomMetaKeywords',
|
||||
'Keywords'
|
||||
);
|
||||
$labels['CustomMetaDescription'] = _t(
|
||||
'SilverStripe\\Subsites\\Model\\Subsite.CustomMetaDescription',
|
||||
'Description'
|
||||
);
|
||||
$labels['CustomExtraMeta'] = _t(
|
||||
'SilverStripe\\Subsites\\Model\\Subsite.CustomExtraMeta',
|
||||
'Custom Meta Tags'
|
||||
);
|
||||
|
||||
return $labels;
|
||||
}
|
||||
|
||||
public function getCopyContentFromID_SubsiteID()
|
||||
{
|
||||
if ($this->CopyContentFromID) {
|
||||
return (int) $this->CopyContentFrom()->SubsiteID;
|
||||
}
|
||||
return SubsiteState::singleton()->getSubsiteId();
|
||||
}
|
||||
|
||||
public function getVirtualFields()
|
||||
{
|
||||
$fields = parent::getVirtualFields();
|
||||
foreach ($fields as $k => $v) {
|
||||
if ($v == 'SubsiteID') {
|
||||
unset($fields[$k]);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (self::$db as $field => $type) {
|
||||
if (in_array($field, $fields ?? [])) {
|
||||
unset($fields[array_search($field, $fields)]);
|
||||
}
|
||||
}
|
||||
|
||||
return $fields;
|
||||
}
|
||||
|
||||
public function syncLinkTracking()
|
||||
{
|
||||
$oldState = Subsite::$disable_subsite_filter;
|
||||
Subsite::$disable_subsite_filter = true;
|
||||
if ($this->CopyContentFromID) {
|
||||
$this->HasBrokenLink = DataObject::get_by_id(SiteTree::class, $this->CopyContentFromID) ? false : true;
|
||||
}
|
||||
Subsite::$disable_subsite_filter = $oldState;
|
||||
}
|
||||
|
||||
public function onBeforeWrite()
|
||||
{
|
||||
parent::onBeforeWrite();
|
||||
|
||||
if ($this->CustomMetaTitle) {
|
||||
$this->MetaTitle = $this->CustomMetaTitle;
|
||||
} else {
|
||||
$this->MetaTitle = $this->ContentSource()->MetaTitle ?: $this->MetaTitle;
|
||||
}
|
||||
if ($this->CustomMetaKeywords) {
|
||||
$this->MetaKeywords = $this->CustomMetaKeywords;
|
||||
} else {
|
||||
$this->MetaKeywords = $this->ContentSource()->MetaKeywords ?: $this->MetaKeywords;
|
||||
}
|
||||
if ($this->CustomMetaDescription) {
|
||||
$this->MetaDescription = $this->CustomMetaDescription;
|
||||
} else {
|
||||
$this->MetaDescription = $this->ContentSource()->MetaDescription ?: $this->MetaDescription;
|
||||
}
|
||||
if ($this->CustomExtraMeta) {
|
||||
$this->ExtraMeta = $this->CustomExtraMeta;
|
||||
} else {
|
||||
$this->ExtraMeta = $this->ContentSource()->ExtraMeta ?: $this->ExtraMeta;
|
||||
}
|
||||
}
|
||||
|
||||
public function validURLSegment()
|
||||
{
|
||||
$isValid = parent::validURLSegment();
|
||||
|
||||
// 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.
|
||||
if (!$isValid) {
|
||||
$filters = [
|
||||
'URLSegment' => $this->URLSegment,
|
||||
'ID:not' => $this->ID,
|
||||
];
|
||||
|
||||
if (Config::inst()->get(SiteTree::class, 'nested_urls')) {
|
||||
$filters['ParentID'] = $this->ParentID ?: 0;
|
||||
}
|
||||
|
||||
$origDisableSubsiteFilter = Subsite::$disable_subsite_filter;
|
||||
Subsite::disable_subsite_filter();
|
||||
$existingPage = SiteTree::get()->filter($filters)->first();
|
||||
Subsite::disable_subsite_filter($origDisableSubsiteFilter);
|
||||
$existingPageInSubsite = SiteTree::get()->filter($filters)->first();
|
||||
|
||||
// If URL has been vetoed because of an existing page,
|
||||
// be more specific and allow same URLSegments in different subsites
|
||||
$isValid = !($existingPage && $existingPageInSubsite);
|
||||
}
|
||||
|
||||
return $isValid;
|
||||
}
|
||||
}
|
@ -1,28 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Reports;
|
||||
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Forms\TreeMultiselectField;
|
||||
use SilverStripe\Reports\ReportWrapper;
|
||||
use SilverStripe\Subsites\Model\Subsite;
|
||||
|
||||
/**
|
||||
* Creates a subsite-aware version of another report.
|
||||
* Pass another report (or its classname) into the constructor.
|
||||
*/
|
||||
class SubsiteReportWrapper extends SS_ReportWrapper
|
||||
class SubsiteReportWrapper extends ReportWrapper
|
||||
{
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Filtering
|
||||
|
||||
/**
|
||||
* @return FieldList
|
||||
*/
|
||||
public function parameterFields()
|
||||
{
|
||||
$subsites = Subsite::accessible_sites('CMS_ACCESS_CMSMain', true);
|
||||
$options = $subsites->toDropdownMap('ID', 'Title');
|
||||
|
||||
$subsiteField = new TreeMultiselectField(
|
||||
$subsiteField = TreeMultiselectField::create(
|
||||
'Subsites',
|
||||
_t('SubsiteReportWrapper.ReportDropdown', 'Sites'),
|
||||
_t(__CLASS__ . '.ReportDropdown', 'Sites'),
|
||||
$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
|
||||
if (sizeof($options) <= 1) {
|
||||
if (sizeof($options ?? []) <= 1) {
|
||||
$subsiteField = $subsiteField->performReadonlyTransformation();
|
||||
}
|
||||
|
||||
@ -30,24 +38,28 @@ class SubsiteReportWrapper extends SS_ReportWrapper
|
||||
if ($fields) {
|
||||
$fields->insertBefore($subsiteField, $fields->First()->Name());
|
||||
} else {
|
||||
$fields = new FieldList($subsiteField);
|
||||
$fields = FieldList::create($subsiteField);
|
||||
}
|
||||
return $fields;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Columns
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function columns()
|
||||
{
|
||||
$columns = parent::columns();
|
||||
$columns['Subsite.Title'] = "Subsite";
|
||||
$columns['Subsite.Title'] = Subsite::class;
|
||||
return $columns;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Querying
|
||||
|
||||
/**
|
||||
* @param arary $params
|
||||
* @return void
|
||||
*/
|
||||
public function beforeQuery($params)
|
||||
{
|
||||
// The user has select a few specific sites
|
||||
@ -58,9 +70,13 @@ class SubsiteReportWrapper extends SS_ReportWrapper
|
||||
} else {
|
||||
$subsites = Subsite::accessible_sites('CMS_ACCESS_CMSMain');
|
||||
$options = $subsites->toDropdownMap('ID', 'Title');
|
||||
Subsite::$force_subsite = join(',', array_keys($options));
|
||||
Subsite::$force_subsite = join(',', array_keys($options ?? []));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function afterQuery()
|
||||
{
|
||||
// Manually manage the subsite filtering
|
99
src/Service/ThemeResolver.php
Normal file
99
src/Service/ThemeResolver.php
Normal file
@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Service;
|
||||
|
||||
use SilverStripe\Core\Config\Configurable;
|
||||
use SilverStripe\Core\Injector\Injectable;
|
||||
use SilverStripe\Subsites\Model\Subsite;
|
||||
use SilverStripe\View\SSViewer;
|
||||
|
||||
class ThemeResolver
|
||||
{
|
||||
use Injectable;
|
||||
use Configurable;
|
||||
|
||||
/**
|
||||
* Cascading definitions for themes, keyed by the name they should appear under in the CMS. For example:
|
||||
*
|
||||
* [
|
||||
* 'theme-1' => [
|
||||
* '$public',
|
||||
* 'starter',
|
||||
* '$default',
|
||||
* ],
|
||||
* 'theme-2' => [
|
||||
* 'custom',
|
||||
* 'watea',
|
||||
* 'starter',
|
||||
* '$public',
|
||||
* '$default',
|
||||
* ]
|
||||
* ]
|
||||
*
|
||||
* @config
|
||||
* @var null|array[]
|
||||
*/
|
||||
private static $theme_options;
|
||||
|
||||
/**
|
||||
* Get the list of themes for the given sub site that can be given to SSViewer::set_themes
|
||||
*
|
||||
* @param Subsite $site
|
||||
* @return array
|
||||
*/
|
||||
public function getThemeList(Subsite $site)
|
||||
{
|
||||
$themes = array_values(SSViewer::get_themes() ?? []);
|
||||
$siteTheme = $site->Theme;
|
||||
|
||||
if (!$siteTheme) {
|
||||
return $themes;
|
||||
}
|
||||
|
||||
$customOptions = $this->config()->get('theme_options');
|
||||
if ($customOptions && isset($customOptions[$siteTheme])) {
|
||||
return $customOptions[$siteTheme];
|
||||
}
|
||||
|
||||
// Ensure themes don't cascade "up" the list
|
||||
$index = array_search($siteTheme, $themes ?? []);
|
||||
|
||||
if ($index > 0) {
|
||||
// 4.0 didn't have support for themes in the public webroot
|
||||
$constant = SSViewer::class . '::PUBLIC_THEME';
|
||||
$publicConstantDefined = defined($constant ?? '');
|
||||
|
||||
// Check if the default is public themes
|
||||
$publicDefault = $publicConstantDefined && $themes[0] === SSViewer::PUBLIC_THEME;
|
||||
|
||||
// Take only those that appear after theme chosen (non-inclusive)
|
||||
$themes = array_slice($themes ?? [], $index + 1);
|
||||
|
||||
// Add back in public
|
||||
if ($publicDefault) {
|
||||
array_unshift($themes, SSViewer::PUBLIC_THEME);
|
||||
}
|
||||
}
|
||||
|
||||
// Add our theme
|
||||
array_unshift($themes, $siteTheme);
|
||||
|
||||
return $themes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of custom cascading theme definitions if available
|
||||
*
|
||||
* @return null|array
|
||||
*/
|
||||
public function getCustomThemeOptions()
|
||||
{
|
||||
$config = $this->config()->get('theme_options');
|
||||
|
||||
if (!$config) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return array_keys($config ?? []);
|
||||
}
|
||||
}
|
132
src/State/SubsiteState.php
Normal file
132
src/State/SubsiteState.php
Normal file
@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\State;
|
||||
|
||||
use SilverStripe\Core\Injector\Injectable;
|
||||
use SilverStripe\Core\Injector\Injector;
|
||||
use SilverStripe\Core\Resettable;
|
||||
use SilverStripe\Dev\Deprecation;
|
||||
|
||||
/**
|
||||
* SubsiteState provides static access to the current state for subsite related data during a request
|
||||
*/
|
||||
class SubsiteState implements Resettable
|
||||
{
|
||||
use Injectable;
|
||||
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
protected $subsiteId;
|
||||
|
||||
|
||||
/**
|
||||
* @var int|null
|
||||
*/
|
||||
protected $originalSubsiteId;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $useSessions;
|
||||
|
||||
/**
|
||||
* Get the current subsite ID
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public function getSubsiteId()
|
||||
{
|
||||
return $this->subsiteId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the current subsite ID, and track the first subsite ID set as the "original". This is used to check
|
||||
* whether the ID has been changed through a request.
|
||||
*
|
||||
* @param int $id
|
||||
* @return $this
|
||||
*/
|
||||
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)) {
|
||||
$this->originalSubsiteId = (int) $id;
|
||||
}
|
||||
|
||||
$this->subsiteId = (int) $id;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether to use sessions for storing the subsite ID
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getUseSessions()
|
||||
{
|
||||
return $this->useSessions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to use sessions for storing the subsite ID
|
||||
*
|
||||
* @param bool $useSessions
|
||||
* @return $this
|
||||
*/
|
||||
public function setUseSessions($useSessions)
|
||||
{
|
||||
$this->useSessions = $useSessions;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get whether the subsite ID has been changed during a request, based on the original and current IDs
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getSubsiteIdWasChanged()
|
||||
{
|
||||
return $this->originalSubsiteId !== $this->getSubsiteId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a given action within the context of a new, isolated state. Modifications are temporary
|
||||
* and the existing state will be restored afterwards.
|
||||
*
|
||||
* @param callable $callback Callback to run. Will be passed the nested state as a parameter
|
||||
* @return mixed Result of callback
|
||||
*/
|
||||
public function withState(callable $callback)
|
||||
{
|
||||
$newState = clone $this;
|
||||
try {
|
||||
Injector::inst()->registerService($newState);
|
||||
return $callback($newState);
|
||||
} finally {
|
||||
Injector::inst()->registerService($this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the local cache of the singleton
|
||||
*/
|
||||
public static function reset()
|
||||
{
|
||||
SubsiteState::singleton()->resetState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the local cache of this object
|
||||
*/
|
||||
public function resetState()
|
||||
{
|
||||
$this->originalSubsiteId = null;
|
||||
$this->subsiteId = null;
|
||||
$this->useSessions = null;
|
||||
}
|
||||
}
|
@ -1,4 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Tasks;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use SilverStripe\CMS\Model\SiteTree;
|
||||
use SilverStripe\Dev\BuildTask;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\Subsites\Model\Subsite;
|
||||
use SilverStripe\Subsites\Pages\SubsitesVirtualPage;
|
||||
use SilverStripe\Versioned\Versioned;
|
||||
|
||||
/**
|
||||
* Handy alternative to copying pages when creating a subsite through the UI.
|
||||
*
|
||||
@ -12,16 +23,17 @@
|
||||
class SubsiteCopyPagesTask extends BuildTask
|
||||
{
|
||||
protected $title = 'Copy pages to different subsite';
|
||||
|
||||
protected $description = '';
|
||||
|
||||
private static $segment = 'SubsiteCopyPagesTask';
|
||||
|
||||
public function run($request)
|
||||
{
|
||||
$subsiteFromId = $request->getVar('from');
|
||||
if (!is_numeric($subsiteFromId)) {
|
||||
throw new InvalidArgumentException('Missing "from" parameter');
|
||||
}
|
||||
$subsiteFrom = DataObject::get_by_id('Subsite', $subsiteFromId);
|
||||
$subsiteFrom = DataObject::get_by_id(Subsite::class, $subsiteFromId);
|
||||
if (!$subsiteFrom) {
|
||||
throw new InvalidArgumentException('Subsite not found');
|
||||
}
|
||||
@ -30,7 +42,7 @@ class SubsiteCopyPagesTask extends BuildTask
|
||||
if (!is_numeric($subsiteToId)) {
|
||||
throw new InvalidArgumentException('Missing "to" parameter');
|
||||
}
|
||||
$subsiteTo = DataObject::get_by_id('Subsite', $subsiteToId);
|
||||
$subsiteTo = DataObject::get_by_id(Subsite::class, $subsiteToId);
|
||||
if (!$subsiteTo) {
|
||||
throw new InvalidArgumentException('Subsite not found');
|
||||
}
|
||||
@ -43,11 +55,11 @@ class SubsiteCopyPagesTask extends BuildTask
|
||||
// This will make sure that the new parents on the new subsite are correct, and there are no funny
|
||||
// issues with having to check whether or not the new parents have been added to the site tree
|
||||
// when a page, etc, is duplicated
|
||||
$stack = array(array(0,0));
|
||||
while (count($stack) > 0) {
|
||||
$stack = [[0, 0]];
|
||||
while (count($stack ?? []) > 0) {
|
||||
list($sourceParentID, $destParentID) = array_pop($stack);
|
||||
|
||||
$children = Versioned::get_by_stage('SiteTree', 'Live', "\"ParentID\" = $sourceParentID", '');
|
||||
$children = Versioned::get_by_stage(SiteTree::class, 'Live', "\"ParentID\" = $sourceParentID", '');
|
||||
|
||||
if ($children) {
|
||||
foreach ($children as $child) {
|
||||
@ -62,8 +74,8 @@ class SubsiteCopyPagesTask extends BuildTask
|
||||
|
||||
$childClone->ParentID = $destParentID;
|
||||
$childClone->writeToStage('Stage');
|
||||
$childClone->publish('Stage', 'Live');
|
||||
array_push($stack, array($child->ID, $childClone->ID));
|
||||
$childClone->copyVersionToStage('Stage', 'Live');
|
||||
array_push($stack, [$child->ID, $childClone->ID]);
|
||||
|
||||
$this->log(sprintf('Copied "%s" (#%d, %s)', $child->Title, $child->ID, $child->Link()));
|
||||
}
|
35
src/Tasks/SubsiteMigrateFileTask.php
Normal file
35
src/Tasks/SubsiteMigrateFileTask.php
Normal 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);
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
<a href="$NewFromTemplateLink" class="action ss-ui-action-constructive ss-ui-button ui-button ui-widget ui-state-default ui-corner-all new new-link" data-icon="add"><% _t('GridFieldAddFromTemplateButton.AddFromTemplate', 'Add New from Template') %></a>
|
@ -1,45 +0,0 @@
|
||||
<div class="cms-menu cms-panel cms-panel-layout west" id="cms-menu" data-layout-type="border">
|
||||
<div class="cms-logo-header north">
|
||||
<div class="cms-logo">
|
||||
<a href="$ApplicationLink" target="_blank" title="$ApplicationName (Version - $CMSVersion)">
|
||||
$ApplicationName <% if $CMSVersion %><abbr class="version">$CMSVersion</abbr><% end_if %>
|
||||
</a>
|
||||
<span><% if $SiteConfig %>$SiteConfig.Title<% else %>$ApplicationName<% end_if %></span>
|
||||
</div>
|
||||
|
||||
<div class="cms-login-status">
|
||||
<a href="$LogoutURL" class="logout-link font-icon-logout" title="<%t LeftAndMain_Menu_ss.LOGOUT 'Log out' %>"></a>
|
||||
<% with $CurrentMember %>
|
||||
<span>
|
||||
<%t LeftAndMain_Menu_ss.Hello 'Hi' %>
|
||||
<a href="{$AbsoluteBaseURL}admin/myprofile" class="profile-link">
|
||||
<% if $FirstName && $Surname %>$FirstName $Surname<% else_if $FirstName %>$FirstName<% else %>$Email<% end_if %>
|
||||
</a>
|
||||
</span>
|
||||
<% end_with %>
|
||||
</div>
|
||||
<% if $ListSubsites %>
|
||||
<% include SubsiteList %>
|
||||
<% end_if %>
|
||||
</div>
|
||||
|
||||
<div class="cms-panel-content center">
|
||||
<ul class="cms-menu-list">
|
||||
<% loop $MainMenu %>
|
||||
<li class="$LinkingMode $FirstLast <% if $LinkingMode == 'link' %><% else %>opened<% end_if %>" id="Menu-$Code" title="$Title.ATT">
|
||||
<a href="$Link" $AttributesHTML>
|
||||
<span class="icon icon-16 icon-{$Code.LowerCase}"> </span>
|
||||
<span class="text">$Title</span>
|
||||
</a>
|
||||
</li>
|
||||
<% end_loop %>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="cms-panel-toggle south">
|
||||
<button class="sticky-toggle" type="button" title="Sticky nav">Sticky nav</button>
|
||||
<span class="sticky-status-indicator">auto</span>
|
||||
<a class="toggle-expand" href="#"><span>»</span></a>
|
||||
<a class="toggle-collapse" href="#"><span>«</span></a>
|
||||
</div>
|
||||
</div>
|
22
templates/SilverStripe/Admin/LeftAndMain_Menu.ss
Normal file
22
templates/SilverStripe/Admin/LeftAndMain_Menu.ss
Normal file
@ -0,0 +1,22 @@
|
||||
<div class="cms-mobile-menu-toggle-wrapper"></div>
|
||||
|
||||
<div class="fill-height cms-menu cms-panel cms-panel-layout" id="cms-menu" data-layout-type="border" aria-expanded="false">
|
||||
<div class="cms-menu__header">
|
||||
<% include SilverStripe\\Admin\\LeftAndMain_MenuLogo %>
|
||||
<% include SilverStripe\\Admin\\LeftAndMain_MenuStatus %>
|
||||
|
||||
<% if $ListSubsites.Count > 1 %>
|
||||
<% include SilverStripe\\Subsites\\Controller\\SubsiteXHRController_subsitelist %>
|
||||
<% end_if %>
|
||||
</div>
|
||||
|
||||
<div class="flexbox-area-grow panel--scrollable panel--triple-toolbar cms-panel-content">
|
||||
<% include SilverStripe\\Admin\\LeftAndMain_MenuList %>
|
||||
</div>
|
||||
|
||||
<div class="toolbar toolbar--south cms-panel-toggle vertical-align-items">
|
||||
<% include SilverStripe\\Admin\\LeftAndMain_MenuToggle %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="fill-height fill-width cms-menu-mobile-overlay" aria-controls="cms-menu" aria-expanded="false"></button>
|
@ -1,31 +0,0 @@
|
||||
<?php
|
||||
class BaseSubsiteTest extends SapphireTest
|
||||
{
|
||||
public function setUp()
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
Subsite::$use_session_subsiteid = true;
|
||||
Subsite::$force_subsite = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Avoid subsites filtering on fixture fetching.
|
||||
*/
|
||||
public function objFromFixture($class, $id)
|
||||
{
|
||||
Subsite::disable_subsite_filter(true);
|
||||
$obj = parent::objFromFixture($class, $id);
|
||||
Subsite::disable_subsite_filter(false);
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the initial state of disable_subsite_filter
|
||||
*/
|
||||
public function testDisableSubsiteFilter()
|
||||
{
|
||||
$this->assertFalse(Subsite::$disable_subsite_filter);
|
||||
}
|
||||
}
|
@ -1,96 +0,0 @@
|
||||
<?php
|
||||
|
||||
class FileSubsitesTest extends BaseSubsiteTest
|
||||
{
|
||||
public static $fixture_file = 'subsites/tests/SubsiteTest.yml';
|
||||
|
||||
/**
|
||||
* Disable other file extensions
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $illegalExtensions = array(
|
||||
'File' => array(
|
||||
'SecureFileExtension',
|
||||
'VersionedFileExtension'
|
||||
),
|
||||
'SiteTree' => array(
|
||||
'Translatable',
|
||||
)
|
||||
);
|
||||
|
||||
public function testTrivialFeatures()
|
||||
{
|
||||
$this->assertTrue(is_array(singleton('FileSubsites')->extraStatics()));
|
||||
$file = new File();
|
||||
$file->Name = 'FileTitle';
|
||||
$file->Title = 'FileTitle';
|
||||
$this->assertEquals(' * FileTitle', $file->alternateTreeTitle());
|
||||
$file->SubsiteID = $this->objFromFixture('Subsite', 'domaintest1')->ID;
|
||||
$this->assertEquals('FileTitle', $file->getTreeTitle());
|
||||
$this->assertTrue(singleton('Folder')->getCMSFields() instanceof FieldList);
|
||||
Subsite::changeSubsite(1);
|
||||
$this->assertEquals($file->cacheKeyComponent(), 'subsite-1');
|
||||
}
|
||||
|
||||
public function testWritingSubsiteID()
|
||||
{
|
||||
$this->objFromFixture('Member', 'admin')->logIn();
|
||||
|
||||
$subsite = $this->objFromFixture('Subsite', 'domaintest1');
|
||||
FileSubsites::$default_root_folders_global = true;
|
||||
|
||||
Subsite::changeSubsite(0);
|
||||
$file = new File();
|
||||
$file->write();
|
||||
$file->onAfterUpload();
|
||||
$this->assertEquals((int)$file->SubsiteID, 0);
|
||||
|
||||
Subsite::changeSubsite($subsite->ID);
|
||||
$this->assertTrue($file->canEdit());
|
||||
|
||||
$file = new File();
|
||||
$file->write();
|
||||
$this->assertEquals((int)$file->SubsiteID, 0);
|
||||
$this->assertTrue($file->canEdit());
|
||||
|
||||
FileSubsites::$default_root_folders_global = false;
|
||||
|
||||
Subsite::changeSubsite($subsite->ID);
|
||||
$file = new File();
|
||||
$file->write();
|
||||
$this->assertEquals($file->SubsiteID, $subsite->ID);
|
||||
|
||||
// Test inheriting from parent folder
|
||||
$folder = new Folder();
|
||||
$folder->write();
|
||||
$this->assertEquals($folder->SubsiteID, $subsite->ID);
|
||||
FileSubsites::$default_root_folders_global = true;
|
||||
$file = new File();
|
||||
$file->ParentID = $folder->ID;
|
||||
$file->onAfterUpload();
|
||||
$this->assertEquals($folder->SubsiteID, $file->SubsiteID);
|
||||
}
|
||||
|
||||
public function testSubsitesFolderDropdown()
|
||||
{
|
||||
$this->objFromFixture('Member', 'admin')->logIn();
|
||||
|
||||
$file = new Folder();
|
||||
|
||||
$source = array_values($file->getCMSFields()->dataFieldByName('SubsiteID')->getSource());
|
||||
asort($source);
|
||||
|
||||
$this->assertEquals(array(
|
||||
'Main site',
|
||||
'Subsite1 Template',
|
||||
'Subsite2 Template',
|
||||
'Template',
|
||||
'Test 1',
|
||||
'Test 2',
|
||||
'Test 3',
|
||||
'Test Non-SSL',
|
||||
'Test SSL',
|
||||
), array_values($source));
|
||||
}
|
||||
}
|
@ -1,28 +0,0 @@
|
||||
<?php
|
||||
|
||||
class GroupSubsitesTest extends BaseSubsiteTest
|
||||
{
|
||||
public static $fixture_file = 'subsites/tests/SubsiteTest.yml';
|
||||
|
||||
protected $requireDefaultRecordsFrom = array('GroupSubsites');
|
||||
|
||||
public function testTrivialFeatures()
|
||||
{
|
||||
$this->assertTrue(is_array(singleton('GroupSubsites')->extraStatics()));
|
||||
$this->assertTrue(is_array(singleton('GroupSubsites')->providePermissions()));
|
||||
$this->assertTrue(singleton('Group')->getCMSFields() instanceof FieldList);
|
||||
}
|
||||
|
||||
public function testAlternateTreeTitle()
|
||||
{
|
||||
$group = new Group();
|
||||
$group->Title = 'The A Team';
|
||||
$group->AccessAllSubsites = true;
|
||||
$this->assertEquals($group->getTreeTitle(), 'The A Team <i>(global group)</i>');
|
||||
$group->AccessAllSubsites = false;
|
||||
$group->write();
|
||||
$group->Subsites()->add($this->objFromFixture('Subsite', 'domaintest1'));
|
||||
$group->Subsites()->add($this->objFromFixture('Subsite', 'domaintest2'));
|
||||
$this->assertEquals($group->getTreeTitle(), 'The A Team <i>(Test 1, Test 2)</i>');
|
||||
}
|
||||
}
|
@ -1,90 +0,0 @@
|
||||
<?php
|
||||
|
||||
class LeftAndMainSubsitesTest extends FunctionalTest
|
||||
{
|
||||
public static $fixture_file = 'subsites/tests/SubsiteTest.yml';
|
||||
|
||||
/**
|
||||
* Avoid subsites filtering on fixture fetching.
|
||||
*/
|
||||
public function objFromFixture($class, $id)
|
||||
{
|
||||
Subsite::disable_subsite_filter(true);
|
||||
$obj = parent::objFromFixture($class, $id);
|
||||
Subsite::disable_subsite_filter(false);
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public function testSectionSites()
|
||||
{
|
||||
$member = $this->objFromFixture('Member', 'subsite1member');
|
||||
|
||||
$cmsmain = singleton('CMSMain');
|
||||
$subsites = $cmsmain->sectionSites(true, "Main site", $member);
|
||||
$this->assertDOSEquals(array(
|
||||
array('Title' =>'Subsite1 Template')
|
||||
), $subsites, 'Lists member-accessible sites for the accessible controller.');
|
||||
|
||||
$assetadmin = singleton('AssetAdmin');
|
||||
$subsites = $assetadmin->sectionSites(true, "Main site", $member);
|
||||
$this->assertDOSEquals(array(), $subsites, 'Does not list any sites for forbidden controller.');
|
||||
|
||||
$member = $this->objFromFixture('Member', 'editor');
|
||||
|
||||
$cmsmain = singleton('CMSMain');
|
||||
$subsites = $cmsmain->sectionSites(true, "Main site", $member);
|
||||
$this->assertDOSContains(array(
|
||||
array('Title' =>'Main site')
|
||||
), $subsites, 'Includes the main site for members who can access all sites.');
|
||||
}
|
||||
|
||||
public function testAccessChecksDontChangeCurrentSubsite()
|
||||
{
|
||||
$admin = $this->objFromFixture("Member", "admin");
|
||||
$this->loginAs($admin);
|
||||
$ids = array();
|
||||
|
||||
$subsite1 = $this->objFromFixture('Subsite', 'domaintest1');
|
||||
$subsite2 = $this->objFromFixture('Subsite', 'domaintest2');
|
||||
$subsite3 = $this->objFromFixture('Subsite', 'domaintest3');
|
||||
|
||||
$ids[] = $subsite1->ID;
|
||||
$ids[] = $subsite2->ID;
|
||||
$ids[] = $subsite3->ID;
|
||||
$ids[] = 0;
|
||||
|
||||
// Enable session-based subsite tracking.
|
||||
Subsite::$use_session_subsiteid = true;
|
||||
|
||||
foreach ($ids as $id) {
|
||||
Subsite::changeSubsite($id);
|
||||
$this->assertEquals($id, Subsite::currentSubsiteID());
|
||||
|
||||
$left = new LeftAndMain();
|
||||
$this->assertTrue($left->canView(), "Admin user can view subsites LeftAndMain with id = '$id'");
|
||||
$this->assertEquals($id, Subsite::currentSubsiteID(),
|
||||
"The current subsite has not been changed in the process of checking permissions for admin user.");
|
||||
}
|
||||
}
|
||||
|
||||
public function testShouldChangeSubsite()
|
||||
{
|
||||
$l = new LeftAndMain();
|
||||
Config::inst()->nest();
|
||||
|
||||
Config::inst()->update('CMSPageEditController', 'treats_subsite_0_as_global', false);
|
||||
$this->assertTrue($l->shouldChangeSubsite('CMSPageEditController', 0, 5));
|
||||
$this->assertFalse($l->shouldChangeSubsite('CMSPageEditController', 0, 0));
|
||||
$this->assertTrue($l->shouldChangeSubsite('CMSPageEditController', 1, 5));
|
||||
$this->assertFalse($l->shouldChangeSubsite('CMSPageEditController', 1, 1));
|
||||
|
||||
Config::inst()->update('CMSPageEditController', 'treats_subsite_0_as_global', true);
|
||||
$this->assertFalse($l->shouldChangeSubsite('CMSPageEditController', 0, 5));
|
||||
$this->assertFalse($l->shouldChangeSubsite('CMSPageEditController', 0, 0));
|
||||
$this->assertTrue($l->shouldChangeSubsite('CMSPageEditController', 1, 5));
|
||||
$this->assertFalse($l->shouldChangeSubsite('CMSPageEditController', 1, 1));
|
||||
|
||||
Config::inst()->unnest();
|
||||
}
|
||||
}
|
@ -1,325 +0,0 @@
|
||||
<?php
|
||||
|
||||
class SiteTreeSubsitesTest extends BaseSubsiteTest
|
||||
{
|
||||
public static $fixture_file = 'subsites/tests/SubsiteTest.yml';
|
||||
|
||||
protected $extraDataObjects = array(
|
||||
'SiteTreeSubsitesTest_ClassA',
|
||||
'SiteTreeSubsitesTest_ClassB'
|
||||
);
|
||||
|
||||
protected $illegalExtensions = array(
|
||||
'SiteTree' => array('Translatable')
|
||||
);
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
// We have our own home page fixtures, prevent the default one being created in this test suite.
|
||||
Config::inst()->update('SiteTree', 'create_default_pages', false);
|
||||
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
public function testPagesInDifferentSubsitesCanShareURLSegment()
|
||||
{
|
||||
$subsiteMain = $this->objFromFixture('Subsite', 'main');
|
||||
$subsite1 = $this->objFromFixture('Subsite', 'subsite1');
|
||||
|
||||
$pageMain = new SiteTree();
|
||||
$pageMain->URLSegment = 'testpage';
|
||||
$pageMain->write();
|
||||
$pageMain->publish('Stage', 'Live');
|
||||
|
||||
$pageMainOther = new SiteTree();
|
||||
$pageMainOther->URLSegment = 'testpage';
|
||||
$pageMainOther->write();
|
||||
$pageMainOther->publish('Stage', 'Live');
|
||||
|
||||
$this->assertNotEquals($pageMain->URLSegment, $pageMainOther->URLSegment,
|
||||
'Pages in same subsite cant share the same URL'
|
||||
);
|
||||
|
||||
Subsite::changeSubsite($subsite1->ID);
|
||||
|
||||
$pageSubsite1 = new SiteTree();
|
||||
$pageSubsite1->URLSegment = 'testpage';
|
||||
$pageSubsite1->write();
|
||||
$pageSubsite1->publish('Stage', 'Live');
|
||||
|
||||
$this->assertEquals($pageMain->URLSegment, $pageSubsite1->URLSegment,
|
||||
'Pages in different subsites can share the same URL'
|
||||
);
|
||||
}
|
||||
|
||||
public function testBasicSanity()
|
||||
{
|
||||
$this->assertTrue(singleton('SiteTree')->getSiteConfig() instanceof SiteConfig);
|
||||
// The following assert is breaking in Translatable.
|
||||
$this->assertTrue(singleton('SiteTree')->getCMSFields() instanceof FieldList);
|
||||
$this->assertTrue(singleton('SubsitesVirtualPage')->getCMSFields() instanceof FieldList);
|
||||
$this->assertTrue(is_array(singleton('SiteTreeSubsites')->extraStatics()));
|
||||
}
|
||||
|
||||
public function testErrorPageLocations()
|
||||
{
|
||||
$subsite1 = $this->objFromFixture('Subsite', 'domaintest1');
|
||||
|
||||
Subsite::changeSubsite($subsite1->ID);
|
||||
$path = ErrorPage::get_filepath_for_errorcode(500);
|
||||
|
||||
$static_path = Config::inst()->get('ErrorPage', 'static_filepath');
|
||||
$expected_path = $static_path . '/error-500-'.$subsite1->domain().'.html';
|
||||
$this->assertEquals($expected_path, $path);
|
||||
}
|
||||
|
||||
public function testCanEditSiteTree()
|
||||
{
|
||||
$admin = $this->objFromFixture('Member', 'admin');
|
||||
$subsite1member = $this->objFromFixture('Member', 'subsite1member');
|
||||
$subsite2member = $this->objFromFixture('Member', 'subsite2member');
|
||||
$mainpage = $this->objFromFixture('Page', 'home');
|
||||
$subsite1page = $this->objFromFixture('Page', 'subsite1_home');
|
||||
$subsite2page = $this->objFromFixture('Page', 'subsite2_home');
|
||||
$subsite1 = $this->objFromFixture('Subsite', 'subsite1');
|
||||
$subsite2 = $this->objFromFixture('Subsite', 'subsite2');
|
||||
|
||||
// Cant pass member as arguments to canEdit() because of GroupSubsites
|
||||
Session::set("loggedInAs", $admin->ID);
|
||||
$this->assertTrue(
|
||||
(bool)$subsite1page->canEdit(),
|
||||
'Administrators can edit all subsites'
|
||||
);
|
||||
|
||||
// @todo: Workaround because GroupSubsites->augmentSQL() is relying on session state
|
||||
Subsite::changeSubsite($subsite1);
|
||||
|
||||
Session::set("loggedInAs", $subsite1member->ID);
|
||||
$this->assertTrue(
|
||||
(bool)$subsite1page->canEdit(),
|
||||
'Members can edit pages on a subsite if they are in a group belonging to this subsite'
|
||||
);
|
||||
|
||||
Session::set("loggedInAs", $subsite2member->ID);
|
||||
$this->assertFalse(
|
||||
(bool)$subsite1page->canEdit(),
|
||||
'Members cant edit pages on a subsite if they are not in a group belonging to this subsite'
|
||||
);
|
||||
|
||||
// @todo: Workaround because GroupSubsites->augmentSQL() is relying on session state
|
||||
Subsite::changeSubsite(0);
|
||||
$this->assertFalse(
|
||||
$mainpage->canEdit(),
|
||||
'Members cant edit pages on the main site if they are not in a group allowing this'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to {@link SubsitesVirtualPageTest->testSubsiteVirtualPageCanHaveSameUrlsegmentAsOtherSubsite()}.
|
||||
*/
|
||||
public function testTwoPagesWithSameURLOnDifferentSubsites()
|
||||
{
|
||||
// Set up a couple of pages with the same URL on different subsites
|
||||
$s1 = $this->objFromFixture('Subsite', 'domaintest1');
|
||||
$s2 = $this->objFromFixture('Subsite', 'domaintest2');
|
||||
|
||||
$p1 = new SiteTree();
|
||||
$p1->Title = $p1->URLSegment = "test-page";
|
||||
$p1->SubsiteID = $s1->ID;
|
||||
$p1->write();
|
||||
|
||||
$p2 = new SiteTree();
|
||||
$p2->Title = $p1->URLSegment = "test-page";
|
||||
$p2->SubsiteID = $s2->ID;
|
||||
$p2->write();
|
||||
|
||||
// Check that the URLs weren't modified in our set-up
|
||||
$this->assertEquals($p1->URLSegment, 'test-page');
|
||||
$this->assertEquals($p2->URLSegment, 'test-page');
|
||||
|
||||
// Check that if we switch between the different subsites, we receive the correct pages
|
||||
Subsite::changeSubsite($s1);
|
||||
$this->assertEquals($p1->ID, SiteTree::get_by_link('test-page')->ID);
|
||||
|
||||
Subsite::changeSubsite($s2);
|
||||
$this->assertEquals($p2->ID, SiteTree::get_by_link('test-page')->ID);
|
||||
}
|
||||
|
||||
public function testPageTypesBlacklistInClassDropdown()
|
||||
{
|
||||
$editor = $this->objFromFixture('Member', 'editor');
|
||||
Session::set("loggedInAs", $editor->ID);
|
||||
|
||||
$s1 = $this->objFromFixture('Subsite', 'domaintest1');
|
||||
$s2 = $this->objFromFixture('Subsite', 'domaintest2');
|
||||
$page = singleton('SiteTree');
|
||||
|
||||
$s1->PageTypeBlacklist = 'SiteTreeSubsitesTest_ClassA,ErrorPage';
|
||||
$s1->write();
|
||||
|
||||
Subsite::changeSubsite($s1);
|
||||
$settingsFields = $page->getSettingsFields()->dataFieldByName('ClassName')->getSource();
|
||||
|
||||
$this->assertArrayNotHasKey('ErrorPage',
|
||||
$settingsFields
|
||||
);
|
||||
$this->assertArrayNotHasKey('SiteTreeSubsitesTest_ClassA',
|
||||
$settingsFields
|
||||
);
|
||||
$this->assertArrayHasKey('SiteTreeSubsitesTest_ClassB',
|
||||
$settingsFields
|
||||
);
|
||||
|
||||
Subsite::changeSubsite($s2);
|
||||
$settingsFields = $page->getSettingsFields()->dataFieldByName('ClassName')->getSource();
|
||||
$this->assertArrayHasKey('ErrorPage',
|
||||
$settingsFields
|
||||
);
|
||||
$this->assertArrayHasKey('SiteTreeSubsitesTest_ClassA',
|
||||
$settingsFields
|
||||
);
|
||||
$this->assertArrayHasKey('SiteTreeSubsitesTest_ClassB',
|
||||
$settingsFields
|
||||
);
|
||||
}
|
||||
|
||||
public function testCopyToSubsite() {
|
||||
// Remove baseurl if testing in subdir
|
||||
Config::inst()->update('Director', 'alternate_base_url', '/');
|
||||
|
||||
/** @var Subsite $otherSubsite */
|
||||
$otherSubsite = $this->objFromFixture('Subsite', 'subsite1');
|
||||
$staffPage = $this->objFromFixture('Page', 'staff'); // nested page
|
||||
$contactPage = $this->objFromFixture('Page', 'contact'); // top level page
|
||||
|
||||
$staffPage2 = $staffPage->duplicateToSubsite($otherSubsite->ID);
|
||||
$contactPage2 = $contactPage->duplicateToSubsite($otherSubsite->ID);
|
||||
|
||||
$this->assertNotEquals($staffPage->ID, $staffPage2->ID);
|
||||
$this->assertNotEquals($staffPage->SubsiteID, $staffPage2->SubsiteID);
|
||||
$this->assertNotEquals($contactPage->ID, $contactPage2->ID);
|
||||
$this->assertNotEquals($contactPage->SubsiteID, $contactPage2->SubsiteID);
|
||||
$this->assertEmpty($staffPage2->ParentID);
|
||||
$this->assertEmpty($contactPage2->ParentID);
|
||||
$this->assertNotEmpty($staffPage->ParentID);
|
||||
$this->assertEmpty($contactPage->ParentID);
|
||||
|
||||
// Staff is shifted to top level and given a unique url segment
|
||||
$domain = $otherSubsite->domain();
|
||||
$this->assertEquals('http://'.$domain.'/staff-2/', $staffPage2->AbsoluteLink());
|
||||
$this->assertEquals('http://'.$domain.'/contact-us-2/', $contactPage2->AbsoluteLink());
|
||||
}
|
||||
|
||||
public function testPageTypesBlacklistInCMSMain()
|
||||
{
|
||||
$editor = $this->objFromFixture('Member', 'editor');
|
||||
Session::set("loggedInAs", $editor->ID);
|
||||
|
||||
$cmsmain = new CMSMain();
|
||||
|
||||
$s1 = $this->objFromFixture('Subsite', 'domaintest1');
|
||||
$s2 = $this->objFromFixture('Subsite', 'domaintest2');
|
||||
|
||||
$s1->PageTypeBlacklist = 'SiteTreeSubsitesTest_ClassA,ErrorPage';
|
||||
$s1->write();
|
||||
|
||||
Subsite::changeSubsite($s1);
|
||||
$hints = Convert::json2array($cmsmain->SiteTreeHints());
|
||||
$classes = $hints['Root']['disallowedChildren'];
|
||||
$this->assertContains('ErrorPage', $classes);
|
||||
$this->assertContains('SiteTreeSubsitesTest_ClassA', $classes);
|
||||
$this->assertNotContains('SiteTreeSubsitesTest_ClassB', $classes);
|
||||
|
||||
Subsite::changeSubsite($s2);
|
||||
$hints = Convert::json2array($cmsmain->SiteTreeHints());
|
||||
$classes = $hints['Root']['disallowedChildren'];
|
||||
$this->assertNotContains('ErrorPage', $classes);
|
||||
$this->assertNotContains('SiteTreeSubsitesTest_ClassA', $classes);
|
||||
$this->assertNotContains('SiteTreeSubsitesTest_ClassB', $classes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that url segments between subsites don't conflict, but do conflict within them
|
||||
*/
|
||||
public function testValidateURLSegment() {
|
||||
$this->logInWithPermission('ADMIN');
|
||||
// Saving existing page in the same subsite doesn't change urls
|
||||
$mainHome = $this->objFromFixture('Page', 'home');
|
||||
$mainSubsiteID = $this->idFromFixture('Subsite', 'main');
|
||||
Subsite::changeSubsite($mainSubsiteID);
|
||||
$mainHome->Content = '<p>Some new content</p>';
|
||||
$mainHome->write();
|
||||
$this->assertEquals('home', $mainHome->URLSegment);
|
||||
$mainHome->doPublish();
|
||||
$mainHomeLive = Versioned::get_one_by_stage('Page', 'Live', sprintf('"SiteTree"."ID" = \'%d\'', $mainHome->ID));
|
||||
$this->assertEquals('home', $mainHomeLive->URLSegment);
|
||||
|
||||
// Saving existing page in another subsite doesn't change urls
|
||||
Subsite::changeSubsite($mainSubsiteID);
|
||||
$subsite1Home = $this->objFromFixture('Page', 'subsite1_home');
|
||||
$subsite1Home->Content = '<p>In subsite 1</p>';
|
||||
$subsite1Home->write();
|
||||
$this->assertEquals('home', $subsite1Home->URLSegment);
|
||||
$subsite1Home->doPublish();
|
||||
$subsite1HomeLive = Versioned::get_one_by_stage('Page', 'Live', sprintf('"SiteTree"."ID" = \'%d\'', $subsite1Home->ID));
|
||||
$this->assertEquals('home', $subsite1HomeLive->URLSegment);
|
||||
|
||||
// Creating a new page in a subsite doesn't conflict with urls in other subsites
|
||||
$subsite1ID = $this->idFromFixture('Subsite', 'subsite1');
|
||||
Subsite::changeSubsite($subsite1ID);
|
||||
$subsite1NewPage = new Page();
|
||||
$subsite1NewPage->SubsiteID = $subsite1ID;
|
||||
$subsite1NewPage->Title = 'Important Page (Subsite 1)';
|
||||
$subsite1NewPage->URLSegment = 'important-page'; // Also exists in main subsite
|
||||
$subsite1NewPage->write();
|
||||
$this->assertEquals('important-page', $subsite1NewPage->URLSegment);
|
||||
$subsite1NewPage->doPublish();
|
||||
$subsite1NewPageLive = Versioned::get_one_by_stage('Page', 'Live', sprintf('"SiteTree"."ID" = \'%d\'', $subsite1NewPage->ID));
|
||||
$this->assertEquals('important-page', $subsite1NewPageLive->URLSegment);
|
||||
|
||||
// Creating a new page in a subsite DOES conflict with urls in the same subsite
|
||||
$subsite1NewPage2 = new Page();
|
||||
$subsite1NewPage2->SubsiteID = $subsite1ID;
|
||||
$subsite1NewPage2->Title = 'Important Page (Subsite 1)';
|
||||
$subsite1NewPage2->URLSegment = 'important-page'; // Also exists in main subsite
|
||||
$subsite1NewPage2->write();
|
||||
$this->assertEquals('important-page-2', $subsite1NewPage2->URLSegment);
|
||||
$subsite1NewPage2->doPublish();
|
||||
$subsite1NewPage2Live = Versioned::get_one_by_stage('Page', 'Live', sprintf('"SiteTree"."ID" = \'%d\'', $subsite1NewPage2->ID));
|
||||
$this->assertEquals('important-page-2', $subsite1NewPage2Live->URLSegment);
|
||||
|
||||
// Original page is left un-modified
|
||||
$mainSubsiteImportantPageID = $this->idFromFixture('Page', 'importantpage');
|
||||
$mainSubsiteImportantPage = Page::get()->byID($mainSubsiteImportantPageID);
|
||||
$this->assertEquals('important-page', $mainSubsiteImportantPage->URLSegment);
|
||||
$mainSubsiteImportantPage->Content = '<p>New Important Page Content</p>';
|
||||
$mainSubsiteImportantPage->write();
|
||||
$this->assertEquals('important-page', $mainSubsiteImportantPage->URLSegment);
|
||||
}
|
||||
|
||||
function testCopySubsiteWithChildren() {
|
||||
$page = $this->objFromFixture('Page', 'about');
|
||||
$newSubsite = $this->objFromFixture('Subsite', 'subsite1');
|
||||
|
||||
$moved = $page->duplicateToSubsite($newSubsite->ID, true);
|
||||
$this->assertEquals($moved->SubsiteID, $newSubsite->ID, 'Ensure returned records are on new subsite');
|
||||
$this->assertEquals($moved->AllChildren()->count(), $page->AllChildren()->count(), 'All pages are copied across');
|
||||
}
|
||||
|
||||
function testCopySubsiteWithoutChildren() {
|
||||
$page = $this->objFromFixture('Page', 'about');
|
||||
$newSubsite = $this->objFromFixture('Subsite', 'subsite2');
|
||||
|
||||
$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');
|
||||
}
|
||||
}
|
||||
|
||||
class SiteTreeSubsitesTest_ClassA extends SiteTree implements TestOnly
|
||||
{
|
||||
}
|
||||
|
||||
class SiteTreeSubsitesTest_ClassB extends SiteTree implements TestOnly
|
||||
{
|
||||
}
|
@ -1,150 +0,0 @@
|
||||
<?php
|
||||
|
||||
class SubsiteAdminFunctionalTest extends FunctionalTest
|
||||
{
|
||||
public static $fixture_file = 'subsites/tests/SubsiteTest.yml';
|
||||
public static $use_draft_site = true;
|
||||
|
||||
protected $autoFollowRedirection = false;
|
||||
|
||||
/**
|
||||
* Helper: FunctionalTest is only able to follow redirection once, we want to go all the way.
|
||||
*/
|
||||
public function getAndFollowAll($url)
|
||||
{
|
||||
$response = $this->get($url);
|
||||
while ($location = $response->getHeader('Location')) {
|
||||
$response = $this->mainSession->followRedirection();
|
||||
}
|
||||
echo $response->getHeader('Location');
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Anonymous user cannot access anything.
|
||||
*/
|
||||
public function testAnonymousIsForbiddenAdminAccess()
|
||||
{
|
||||
$response = $this->getAndFollowAll('admin/pages/?SubsiteID=0');
|
||||
$this->assertRegExp('#^Security/login.*#', $this->mainSession->lastUrl(), 'Admin is disallowed');
|
||||
|
||||
$subsite1 = $this->objFromFixture('Subsite', 'subsite1');
|
||||
$response = $this->getAndFollowAll("admin/pages/?SubsiteID={$subsite1->ID}");
|
||||
$this->assertRegExp('#^Security/login.*#', $this->mainSession->lastUrl(), 'Admin is disallowed');
|
||||
|
||||
$response = $this->getAndFollowAll('SubsiteXHRController');
|
||||
$this->assertRegExp('#^Security/login.*#', $this->mainSession->lastUrl(),
|
||||
'SubsiteXHRController is disallowed');
|
||||
}
|
||||
|
||||
/**
|
||||
* Admin should be able to access all subsites and the main site
|
||||
*/
|
||||
public function testAdminCanAccessAllSubsites()
|
||||
{
|
||||
$member = $this->objFromFixture('Member', 'admin');
|
||||
Session::set("loggedInAs", $member->ID);
|
||||
|
||||
$this->getAndFollowAll('admin/pages/?SubsiteID=0');
|
||||
$this->assertEquals(Subsite::currentSubsiteID(), '0', 'Can access main site.');
|
||||
$this->assertRegExp('#^admin/pages.*#', $this->mainSession->lastUrl(), 'Lands on the correct section');
|
||||
|
||||
$subsite1 = $this->objFromFixture('Subsite', 'subsite1');
|
||||
$this->getAndFollowAll("admin/pages/?SubsiteID={$subsite1->ID}");
|
||||
$this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Can access other subsite.');
|
||||
$this->assertRegExp('#^admin/pages.*#', $this->mainSession->lastUrl(), 'Lands on the correct section');
|
||||
|
||||
$response = $this->getAndFollowAll('SubsiteXHRController');
|
||||
$this->assertNotRegExp('#^Security/login.*#', $this->mainSession->lastUrl(),
|
||||
'SubsiteXHRController is reachable');
|
||||
}
|
||||
|
||||
public function testAdminIsRedirectedToObjectsSubsite()
|
||||
{
|
||||
$member = $this->objFromFixture('Member', 'admin');
|
||||
Session::set("loggedInAs", $member->ID);
|
||||
|
||||
$mainSubsitePage = $this->objFromFixture('Page', 'mainSubsitePage');
|
||||
$subsite1Home = $this->objFromFixture('Page', 'subsite1_home');
|
||||
|
||||
Config::inst()->nest();
|
||||
|
||||
Config::inst()->update('CMSPageEditController', 'treats_subsite_0_as_global', false);
|
||||
Subsite::changeSubsite(0);
|
||||
$this->getAndFollowAll("admin/pages/edit/show/$subsite1Home->ID");
|
||||
$this->assertEquals(Subsite::currentSubsiteID(), $subsite1Home->SubsiteID, 'Loading an object switches the subsite');
|
||||
$this->assertRegExp("#^admin/pages.*#", $this->mainSession->lastUrl(), 'Lands on the correct section');
|
||||
|
||||
Config::inst()->update('CMSPageEditController', 'treats_subsite_0_as_global', true);
|
||||
Subsite::changeSubsite(0);
|
||||
$this->getAndFollowAll("admin/pages/edit/show/$subsite1Home->ID");
|
||||
$this->assertEquals(Subsite::currentSubsiteID(), $subsite1Home->SubsiteID, 'Loading a non-main-site object still switches the subsite if configured with treats_subsite_0_as_global');
|
||||
$this->assertRegExp("#^admin/pages.*#", $this->mainSession->lastUrl(), 'Lands on the correct section');
|
||||
|
||||
$this->getAndFollowAll("admin/pages/edit/show/$mainSubsitePage->ID");
|
||||
$this->assertNotEquals(Subsite::currentSubsiteID(), $mainSubsitePage->SubsiteID, 'Loading a main-site object does not change the subsite if configured with treats_subsite_0_as_global');
|
||||
$this->assertRegExp("#^admin/pages.*#", $this->mainSession->lastUrl(), 'Lands on the correct section');
|
||||
|
||||
Config::inst()->unnest();
|
||||
}
|
||||
|
||||
/**
|
||||
* User which has AccessAllSubsites set to 1 should be able to access all subsites and main site,
|
||||
* even though he does not have the ADMIN permission.
|
||||
*/
|
||||
public function testEditorCanAccessAllSubsites()
|
||||
{
|
||||
$member = $this->objFromFixture('Member', 'editor');
|
||||
Session::set("loggedInAs", $member->ID);
|
||||
|
||||
$this->getAndFollowAll('admin/pages/?SubsiteID=0');
|
||||
$this->assertEquals(Subsite::currentSubsiteID(), '0', 'Can access main site.');
|
||||
$this->assertRegExp('#^admin/pages.*#', $this->mainSession->lastUrl(), 'Lands on the correct section');
|
||||
|
||||
$subsite1 = $this->objFromFixture('Subsite', 'subsite1');
|
||||
$this->getAndFollowAll("admin/pages/?SubsiteID={$subsite1->ID}");
|
||||
$this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Can access other subsite.');
|
||||
$this->assertRegExp('#^admin/pages.*#', $this->mainSession->lastUrl(), 'Lands on the correct section');
|
||||
|
||||
$response = $this->getAndFollowAll('SubsiteXHRController');
|
||||
$this->assertNotRegExp('#^Security/login.*#', $this->mainSession->lastUrl(),
|
||||
'SubsiteXHRController is reachable');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a member who only has access to one subsite (subsite1) and only some sections (pages and security).
|
||||
*/
|
||||
public function testSubsiteAdmin()
|
||||
{
|
||||
$member = $this->objFromFixture('Member', 'subsite1member');
|
||||
Session::set("loggedInAs", $member->ID);
|
||||
|
||||
$subsite1 = $this->objFromFixture('Subsite', 'subsite1');
|
||||
|
||||
// Check allowed URL.
|
||||
$this->getAndFollowAll("admin/pages/?SubsiteID={$subsite1->ID}");
|
||||
$this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Can access own subsite.');
|
||||
$this->assertRegExp('#^admin/pages.*#', $this->mainSession->lastUrl(), 'Can access permitted section.');
|
||||
|
||||
// Check forbidden section in allowed subsite.
|
||||
$this->getAndFollowAll("admin/assets/?SubsiteID={$subsite1->ID}");
|
||||
$this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Is redirected within subsite.');
|
||||
$this->assertNotRegExp('#^admin/assets/.*#', $this->mainSession->lastUrl(),
|
||||
'Is redirected away from forbidden section');
|
||||
|
||||
// Check forbidden site, on a section that's allowed on another subsite
|
||||
$this->getAndFollowAll("admin/pages/?SubsiteID=0");
|
||||
$this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Is redirected to permitted subsite.');
|
||||
|
||||
// Check forbidden site, on a section that's not allowed on any other subsite
|
||||
$this->getAndFollowAll("admin/assets/?SubsiteID=0");
|
||||
$this->assertEquals(Subsite::currentSubsiteID(), $subsite1->ID, 'Is redirected to first permitted subsite.');
|
||||
$this->assertNotRegExp('#^Security/login.*#', $this->mainSession->lastUrl(), 'Is not denied access');
|
||||
|
||||
// Check the standalone XHR controller.
|
||||
$response = $this->getAndFollowAll('SubsiteXHRController');
|
||||
$this->assertNotRegExp('#^Security/login.*#', $this->mainSession->lastUrl(),
|
||||
'SubsiteXHRController is reachable');
|
||||
}
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
<?php
|
||||
|
||||
class SubsiteAdminTest extends BaseSubsiteTest
|
||||
{
|
||||
public static $fixture_file = 'subsites/tests/SubsiteTest.yml';
|
||||
|
||||
public function adminLoggedInSession()
|
||||
{
|
||||
return new Session(array(
|
||||
'loggedInAs' => $this->idFromFixture('Member', 'admin')
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test generation of the view
|
||||
*/
|
||||
public function testBasicView()
|
||||
{
|
||||
Subsite::$write_hostmap = false;
|
||||
$subsite1ID = $this->objFromFixture('Subsite', 'domaintest1')->ID;
|
||||
|
||||
// Open the admin area logged in as admin
|
||||
$response1 = Director::test('admin/subsites/', null, $this->adminLoggedInSession());
|
||||
|
||||
// Confirm that this URL gets you the entire page, with the edit form loaded
|
||||
$response2 = Director::test("admin/subsites/Subsite/EditForm/field/Subsite/item/$subsite1ID/edit", null, $this->adminLoggedInSession());
|
||||
$this->assertTrue(strpos($response2->getBody(), 'id="Form_ItemEditForm_ID"') !== false, "Testing Form_ItemEditForm_ID exists");
|
||||
$this->assertTrue(strpos($response2->getBody(), '<head') !== false, "Testing <head> exists");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public function testMainsiteAdminCanAccessAllSubsites()
|
||||
{
|
||||
$member = $this->objFromFixture('Member', 'admin');
|
||||
Session::set("loggedInAs", $member->ID);
|
||||
|
||||
$cmsMain = new CMSMain();
|
||||
foreach ($cmsMain->Subsites() as $subsite) {
|
||||
$ids[$subsite->ID] = true;
|
||||
}
|
||||
|
||||
$this->assertArrayHasKey(0, $ids, "Main site accessible");
|
||||
$this->assertArrayHasKey($this->idFromFixture('Subsite', 'main'), $ids, "Site with no groups inaccesible");
|
||||
$this->assertArrayHasKey($this->idFromFixture('Subsite', 'subsite1'), $ids, "Subsite1 Template inaccessible");
|
||||
$this->assertArrayHasKey($this->idFromFixture('Subsite', 'subsite2'), $ids, "Subsite2 Template inaccessible");
|
||||
}
|
||||
}
|
@ -1,220 +0,0 @@
|
||||
Subsite:
|
||||
main:
|
||||
Title: Template
|
||||
subsite1:
|
||||
Title: Subsite1 Template
|
||||
subsite2:
|
||||
Title: Subsite2 Template
|
||||
domaintest1:
|
||||
Title: Test 1
|
||||
domaintest2:
|
||||
Title: Test 2
|
||||
domaintest3:
|
||||
Title: Test 3
|
||||
domaintest4:
|
||||
Title: 'Test SSL'
|
||||
domaintest5:
|
||||
Title: 'Test Non-SSL'
|
||||
SubsiteDomain:
|
||||
subsite1:
|
||||
SubsiteID: =>Subsite.subsite1
|
||||
Domain: subsite1.*
|
||||
Protocol: automatic
|
||||
subsite2:
|
||||
SubsiteID: =>Subsite.subsite2
|
||||
Domain: subsite2.*
|
||||
Protocol: automatic
|
||||
dt1a:
|
||||
SubsiteID: =>Subsite.domaintest1
|
||||
Domain: one.example.org
|
||||
Protocol: automatic
|
||||
IsPrimary: 1
|
||||
dt1b:
|
||||
SubsiteID: =>Subsite.domaintest1
|
||||
Domain: one.*
|
||||
Protocol: automatic
|
||||
dt2a:
|
||||
SubsiteID: =>Subsite.domaintest2
|
||||
Domain: two.mysite.com
|
||||
Protocol: automatic
|
||||
IsPrimary: 1
|
||||
dt2b:
|
||||
SubsiteID: =>Subsite.domaintest2
|
||||
Domain: *.mysite.com
|
||||
Protocol: automatic
|
||||
dt3:
|
||||
SubsiteID: =>Subsite.domaintest3
|
||||
Domain: three.*
|
||||
Protocol: automatic
|
||||
IsPrimary: 1
|
||||
dt4a:
|
||||
SubsiteID: =>Subsite.domaintest4
|
||||
Domain: www.primary.com
|
||||
Protocol: https
|
||||
dt4b:
|
||||
SubsiteID: =>Subsite.domaintest4
|
||||
Domain: www.secondary.com
|
||||
Protocol: http
|
||||
dt5:
|
||||
SubsiteID: =>Subsite.domaintest5
|
||||
Domain: www.tertiary.com
|
||||
Protocol: http
|
||||
IsPrimary: 1
|
||||
Page:
|
||||
mainSubsitePage:
|
||||
Title: MainSubsitePage
|
||||
SubsiteID: 0
|
||||
URLSegment: mainsubsitepage
|
||||
home:
|
||||
Title: Home
|
||||
SubsiteID: =>Subsite.main
|
||||
URLSegment: home
|
||||
about:
|
||||
Title: About
|
||||
SubsiteID: =>Subsite.main
|
||||
URLSegment: about
|
||||
linky:
|
||||
Title: Linky
|
||||
SubsiteID: =>Subsite.main
|
||||
URLSegment: linky
|
||||
staff:
|
||||
Title: Staff
|
||||
ParentID: =>Page.about
|
||||
SubsiteID: =>Subsite.main
|
||||
URLSegment: staff
|
||||
contact:
|
||||
Title: Contact Us
|
||||
SubsiteID: =>Subsite.main
|
||||
URLSegment: contact-us
|
||||
importantpage:
|
||||
Title: Important Page
|
||||
SubsiteID: =>Subsite.main
|
||||
URLSegment: important-page
|
||||
subsite1_home:
|
||||
Title: Home (Subsite 1)
|
||||
SubsiteID: =>Subsite.subsite1
|
||||
URLSegment: home
|
||||
subsite1_contactus:
|
||||
Title: Contact Us (Subsite 1)
|
||||
SubsiteID: =>Subsite.subsite1
|
||||
URLSegment: contact-us
|
||||
subsite1_staff:
|
||||
Title: Staff
|
||||
SubsiteID: =>Subsite.subsite1
|
||||
URLSegment: staff
|
||||
subsite2_home:
|
||||
Title: Home (Subsite 2)
|
||||
SubsiteID: =>Subsite.subsite2
|
||||
URLSegment: home
|
||||
subsite2_contactus:
|
||||
Title: Contact Us (Subsite 2)
|
||||
SubsiteID: =>Subsite.subsite2
|
||||
URLSegment: contact-us
|
||||
|
||||
PermissionRoleCode:
|
||||
roleCode1:
|
||||
Code: CMS_ACCESS_CMSMain
|
||||
PermissionRole:
|
||||
role1:
|
||||
Title: role1
|
||||
Codes: =>PermissionRoleCode.roleCode1
|
||||
Group:
|
||||
admin:
|
||||
Title: Admin
|
||||
Code: admin
|
||||
AccessAllSubsites: 1
|
||||
editor:
|
||||
Title: Editor
|
||||
Code: editor
|
||||
AccessAllSubsites: 1
|
||||
subsite1_group:
|
||||
Title: subsite1_group
|
||||
Code: subsite1_group
|
||||
AccessAllSubsites: 0
|
||||
Subsites: =>Subsite.subsite1
|
||||
subsite2_group:
|
||||
Title: subsite2_group
|
||||
Code: subsite2_group
|
||||
AccessAllSubsites: 0
|
||||
Subsites: =>Subsite.subsite2
|
||||
subsite1admins:
|
||||
Title: subsite1admins
|
||||
Code: subsite1admins
|
||||
AccessAllSubsites: 0
|
||||
Subsites: =>Subsite.subsite1
|
||||
allsubsitesauthors:
|
||||
Title: allsubsitesauthors
|
||||
Code: allsubsitesauthors
|
||||
AccessAllSubsites: 1
|
||||
subsite1_group_via_role:
|
||||
Title: subsite1_group_via_role
|
||||
Code: subsite1_group_via_role
|
||||
AccessAllSubsites: 1
|
||||
Roles: =>PermissionRole.role1
|
||||
Permission:
|
||||
admin:
|
||||
Code: ADMIN
|
||||
GroupID: =>Group.admin
|
||||
editor1:
|
||||
Code: CMS_ACCESS_CMSMain
|
||||
GroupID: =>Group.editor
|
||||
editor2:
|
||||
Code: SITETREE_VIEW_ALL
|
||||
GroupID: =>Group.editor
|
||||
editor3:
|
||||
Code: VIEW_DRAFT_CONTENT
|
||||
GroupID: =>Group.editor
|
||||
accesscmsmain1:
|
||||
Code: CMS_ACCESS_CMSMain
|
||||
GroupID: =>Group.subsite1_group
|
||||
accesscmsmain2:
|
||||
Code: CMS_ACCESS_CMSMain
|
||||
GroupID: =>Group.subsite2_group
|
||||
accesscmsmain3:
|
||||
Code: CMS_ACCESS_CMSMain
|
||||
GroupID: =>Group.subsite1admins
|
||||
accesscmsmain4:
|
||||
Code: CMS_ACCESS_CMSMain
|
||||
GroupID: =>Group.allsubsitesauthors
|
||||
securityaccess1:
|
||||
Code: CMS_ACCESS_SecurityAdmin
|
||||
GroupID: =>Group.subsite1_group
|
||||
securityaccess2:
|
||||
Code: CMS_ACCESS_SecurityAdmin
|
||||
GroupID: =>Group.subsite2_group
|
||||
adminsubsite1:
|
||||
Code: ADMIN
|
||||
GroupID: =>Group.subsite1admins
|
||||
|
||||
Member:
|
||||
admin:
|
||||
FirstName: Admin
|
||||
Surname: User
|
||||
Email: admin@test.com
|
||||
Password: rangi
|
||||
Groups: =>Group.admin
|
||||
editor:
|
||||
FirstName: Editor
|
||||
Surname: User
|
||||
Email: editor@test.com
|
||||
Password: rangi
|
||||
Groups: =>Group.editor
|
||||
subsite1member:
|
||||
Email: subsite1member@test.com
|
||||
Groups: =>Group.subsite1_group
|
||||
subsite2member:
|
||||
Email: subsite2member@test.com
|
||||
Groups: =>Group.subsite2_group
|
||||
subsite1admin:
|
||||
Email: subsite1admin@test.com
|
||||
Groups: =>Group.subsite1admins
|
||||
allsubsitesauthor:
|
||||
Email: allsubsitesauthor@test.com
|
||||
Groups: =>Group.allsubsitesauthors
|
||||
subsite1member2:
|
||||
Email: subsite1member2@test.com
|
||||
Groups: =>Group.subsite1_group_via_role
|
||||
|
||||
SiteConfig:
|
||||
config:
|
||||
CanCreateTopLevelType: LoggedInUsers
|
@ -1,44 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Created by PhpStorm.
|
||||
* User: dmooyman
|
||||
* Date: 27/05/16
|
||||
* Time: 3:10 PM
|
||||
*/
|
||||
class SubsiteXHRControllerTest extends FunctionalTest
|
||||
{
|
||||
protected static $fixture_file = 'SubsiteTest.yml';
|
||||
|
||||
public function testCanView() {
|
||||
// Test unauthenticated access
|
||||
Session::clear('MemberID');
|
||||
$result = $this->get('SubsiteXHRController', null, array(
|
||||
'X-Pjax' => 'SubsiteList',
|
||||
'X-Requested-With' => 'XMLHttpRequest'
|
||||
));
|
||||
$this->assertEquals(403, $result->getStatusCode());
|
||||
|
||||
// Login with NO permissions
|
||||
$this->logInWithPermission('NOT_CMS_PERMISSION');
|
||||
$result = $this->get('SubsiteXHRController', null, array(
|
||||
'X-Pjax' => 'SubsiteList',
|
||||
'X-Requested-With' => 'XMLHttpRequest'
|
||||
));
|
||||
$this->assertEquals(403, $result->getStatusCode());
|
||||
|
||||
// Test cms user
|
||||
$this->logInWithPermission('CMS_ACCESS_CMSMain');
|
||||
$result = $this->get('SubsiteXHRController', null, array(
|
||||
'X-Pjax' => 'SubsiteList',
|
||||
'X-Requested-With' => 'XMLHttpRequest'
|
||||
));
|
||||
$this->assertEquals(200, $result->getStatusCode());
|
||||
$this->assertEquals('text/json', $result->getHeader('Content-Type'));
|
||||
$body = $result->getBody();
|
||||
$this->assertContains('Main site', $body);
|
||||
$this->assertContains('Test 1', $body);
|
||||
$this->assertContains('Test 2', $body);
|
||||
$this->assertContains('Test 3', $body);
|
||||
}
|
||||
}
|
@ -1,10 +0,0 @@
|
||||
# These need to come first so that SiteTree has the link meta-data written.
|
||||
File:
|
||||
file1:
|
||||
Filename: assets/testscript-test-file.pdf
|
||||
|
||||
SiteTree:
|
||||
page1:
|
||||
Title: page1
|
||||
URLSegment: page1
|
||||
Content: <p><img src="assets/testscript-test-file.pdf" /></p>
|
@ -1,72 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Tests {@see WildcardDomainField}
|
||||
*/
|
||||
class WildcardDomainFieldTest extends SapphireTest {
|
||||
|
||||
/**
|
||||
* Check that valid domains are accepted
|
||||
*
|
||||
* @dataProvider validDomains
|
||||
*/
|
||||
public function testValidDomains($domain) {
|
||||
$field = new WildcardDomainField('DomainField');
|
||||
$this->assertTrue($field->checkHostname($domain), "Validate that {$domain} is a valid domain name");
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that valid domains are accepted
|
||||
*
|
||||
* @dataProvider invalidDomains
|
||||
*/
|
||||
public function testInvalidDomains($domain) {
|
||||
$field = new WildcardDomainField('DomainField');
|
||||
$this->assertFalse($field->checkHostname($domain), "Validate that {$domain} is an invalid domain name");
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that valid domains are accepted
|
||||
*
|
||||
* @dataProvider validWildcards
|
||||
*/
|
||||
public function testValidWildcards($domain) {
|
||||
$field = new WildcardDomainField('DomainField');
|
||||
$this->assertTrue($field->checkHostname($domain), "Validate that {$domain} is a valid domain wildcard");
|
||||
}
|
||||
|
||||
public function validDomains() {
|
||||
return array(
|
||||
array('www.mysite.com'),
|
||||
array('domain7'),
|
||||
array('mysite.co.n-z'),
|
||||
array('subdomain.my-site.com'),
|
||||
array('subdomain.mysite')
|
||||
);
|
||||
}
|
||||
|
||||
public function invalidDomains() {
|
||||
return array(
|
||||
array('-mysite'),
|
||||
array('.mysite'),
|
||||
array('mys..ite'),
|
||||
array('mysite-'),
|
||||
array('mysite.'),
|
||||
array('-mysite.*'),
|
||||
array('.mysite.*'),
|
||||
array('mys..ite.*'),
|
||||
array('*.mysite-'),
|
||||
array('*.mysite.')
|
||||
);
|
||||
}
|
||||
|
||||
public function validWildcards() {
|
||||
return array(
|
||||
array('*.mysite.com'),
|
||||
array('mys*ite.com'),
|
||||
array('*.my-site.*'),
|
||||
array('*')
|
||||
);
|
||||
}
|
||||
|
||||
}
|
0
tests/behat/_manifest_exclude
Normal file
0
tests/behat/_manifest_exclude
Normal file
@ -1,110 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace Subsites\Test\Behaviour;
|
||||
|
||||
if (!class_exists('SilverStripe\BehatExtension\Context\SilverStripeContext')) {
|
||||
return;
|
||||
}
|
||||
|
||||
use SilverStripe\BehatExtension\Context\SilverStripeContext;
|
||||
use SilverStripe\BehatExtension\Context\BasicContext;
|
||||
use SilverStripe\BehatExtension\Context\LoginContext;
|
||||
use SilverStripe\BehatExtension\Context\FixtureContext;
|
||||
use SilverStripe\Framework\Test\Behaviour\CmsFormsContext;
|
||||
use SilverStripe\Framework\Test\Behaviour\CmsUiContext;
|
||||
use SilverStripe\Cms\Test\Behaviour;
|
||||
|
||||
// PHPUnit
|
||||
require_once 'PHPUnit/Autoload.php';
|
||||
require_once 'PHPUnit/Framework/Assert/Functions.php';
|
||||
|
||||
/**
|
||||
* Features context
|
||||
*
|
||||
* Context automatically loaded by Behat.
|
||||
* Uses subcontexts to extend functionality.
|
||||
*/
|
||||
class FeatureContext extends SilverStripeContext
|
||||
{
|
||||
/**
|
||||
* @var FixtureFactory
|
||||
*/
|
||||
protected $fixtureFactory;
|
||||
|
||||
/**
|
||||
* Initializes context.
|
||||
* Every scenario gets it's own context object.
|
||||
*
|
||||
* @param array $parameters context parameters (set them up through behat.yml)
|
||||
*/
|
||||
public function __construct(array $parameters)
|
||||
{
|
||||
parent::__construct($parameters);
|
||||
|
||||
$this->useContext('BasicContext', new BasicContext($parameters));
|
||||
$this->useContext('LoginContext', new LoginContext($parameters));
|
||||
$this->useContext('CmsFormsContext', new CmsFormsContext($parameters));
|
||||
$this->useContext('CmsUiContext', new CmsUiContext($parameters));
|
||||
|
||||
$fixtureContext = new FixtureContext($parameters);
|
||||
$fixtureContext->setFixtureFactory($this->getFixtureFactory());
|
||||
$this->useContext('FixtureContext', $fixtureContext);
|
||||
|
||||
// Use blueprints to set user name from identifier
|
||||
$factory = $fixtureContext->getFixtureFactory();
|
||||
$blueprint = \Injector::inst()->create('FixtureBlueprint', 'Member');
|
||||
$blueprint->addCallback('beforeCreate', function ($identifier, &$data, &$fixtures) {
|
||||
if (!isset($data['FirstName'])) {
|
||||
$data['FirstName'] = $identifier;
|
||||
}
|
||||
});
|
||||
$factory->define('Member', $blueprint);
|
||||
|
||||
// Auto-publish pages
|
||||
foreach (\ClassInfo::subclassesFor('SiteTree') as $id => $class) {
|
||||
$blueprint = \Injector::inst()->create('FixtureBlueprint', $class);
|
||||
$blueprint->addCallback('afterCreate', function ($obj, $identifier, &$data, &$fixtures) {
|
||||
$obj->publish('Stage', 'Live');
|
||||
});
|
||||
$factory->define($class, $blueprint);
|
||||
}
|
||||
}
|
||||
|
||||
public function setMinkParameters(array $parameters)
|
||||
{
|
||||
parent::setMinkParameters($parameters);
|
||||
|
||||
if (isset($parameters['files_path'])) {
|
||||
$this->getSubcontext('FixtureContext')->setFilesPath($parameters['files_path']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return FixtureFactory
|
||||
*/
|
||||
public function getFixtureFactory()
|
||||
{
|
||||
if (!$this->fixtureFactory) {
|
||||
$this->fixtureFactory = \Injector::inst()->create('BehatFixtureFactory');
|
||||
}
|
||||
|
||||
return $this->fixtureFactory;
|
||||
}
|
||||
|
||||
public function setFixtureFactory(FixtureFactory $factory)
|
||||
{
|
||||
$this->fixtureFactory = $factory;
|
||||
}
|
||||
|
||||
//
|
||||
// Place your definition and hook methods here:
|
||||
//
|
||||
// /**
|
||||
// * @Given /^I have done something with "([^"]*)"$/
|
||||
// */
|
||||
// public function iHaveDoneSomethingWith($argument) {
|
||||
// $container = $this->kernel->getContainer();
|
||||
// $container->get('some_service')->doSomethingWith($argument);
|
||||
// }
|
||||
//
|
||||
}
|
29
tests/behat/features/insert-content-link.feature
Normal file
29
tests/behat/features/insert-content-link.feature
Normal 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
|
81
tests/behat/features/operate-subsites.feature
Normal file
81
tests/behat/features/operate-subsites.feature
Normal 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"
|
@ -4,23 +4,26 @@ Feature: Preview navigation
|
||||
In order to preview my content
|
||||
|
||||
Background:
|
||||
Given a "subsite" "My subsite"
|
||||
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"="=>Subsite.My subsite"
|
||||
And a "page" "Other page" with "URLSegment"="other-page", "Content"="Other page content <form action='my-page'><input type='submit' value='Submit my form'></form>" and "Subsite"="=>Subsite.My subsite"
|
||||
Given a "member" "Joe" belonging to "Admin Group" with "Email"="joe@test.com" and "Password"="Password1"
|
||||
And the "group" "Admin Group" has permissions "Full administrative rights"
|
||||
And I log in with "joe@test.com" and "Password1"
|
||||
Given a "subsite" "MySubsite"
|
||||
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='[sitetree_link,id=4]'>Goto my page</a>" and "SubsiteID"="1"
|
||||
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
|
||||
|
||||
@javascript
|
||||
Scenario: I can navigate the subsite preview
|
||||
When I go to "admin"
|
||||
And I select "My subsite" from "SubsitesSelect"
|
||||
And I go to "admin/pages"
|
||||
When I go to "/admin/pages"
|
||||
And I select "MySubsite" from "SubsitesSelect"
|
||||
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 wait for 3 seconds
|
||||
And I set the CMS mode to "Preview mode"
|
||||
And I follow "ahref" in preview
|
||||
And I wait for 1 second
|
||||
Then the preview contains "Other page content"
|
||||
# We are already on the second page, submit the form to return to first one.
|
||||
When I wait for 3 seconds
|
||||
And I press "Submit my form" in preview
|
||||
# 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 wait for 1 second
|
||||
Then the preview contains "My page content"
|
||||
|
26
tests/behat/features/publish-a-page.feature
Normal file
26
tests/behat/features/publish-a-page.feature
Normal 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
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
BIN
tests/behat/files/file2.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.8 KiB |
43
tests/php/BaseSubsiteTest.php
Normal file
43
tests/php/BaseSubsiteTest.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Tests;
|
||||
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\Subsites\Model\Subsite;
|
||||
use SilverStripe\Subsites\State\SubsiteState;
|
||||
|
||||
class BaseSubsiteTest extends SapphireTest
|
||||
{
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
SubsiteState::singleton()->setUseSessions(true);
|
||||
Config::modify()->set(Subsite::class, 'write_hostmap', false);
|
||||
Subsite::$force_subsite = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Avoid subsites filtering on fixture fetching.
|
||||
* @param string $className
|
||||
* @param string $identifier
|
||||
* @return \SilverStripe\ORM\DataObject
|
||||
*/
|
||||
protected function objFromFixture($className, $identifier)
|
||||
{
|
||||
Subsite::disable_subsite_filter(true);
|
||||
$obj = parent::objFromFixture($className, $identifier);
|
||||
Subsite::disable_subsite_filter(false);
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the initial state of disable_subsite_filter
|
||||
*/
|
||||
public function testDisableSubsiteFilter()
|
||||
{
|
||||
$this->assertFalse(Subsite::$disable_subsite_filter);
|
||||
}
|
||||
}
|
34
tests/php/Extensions/FolderFormFactoryExtensionTest.php
Normal file
34
tests/php/Extensions/FolderFormFactoryExtensionTest.php
Normal file
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Tests\Extensions;
|
||||
|
||||
use SilverStripe\AssetAdmin\Forms\FolderFormFactory;
|
||||
use SilverStripe\Assets\Folder;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\Forms\Form;
|
||||
use SilverStripe\Forms\FormFactory;
|
||||
|
||||
class FolderFormFactoryExtensionTest extends SapphireTest
|
||||
{
|
||||
protected static $fixture_file = 'FolderFormFactoryExtensionTest.yml';
|
||||
|
||||
public function testSubsitesFolderDropdown()
|
||||
{
|
||||
$this->logInWithPermission('ADMIN');
|
||||
|
||||
/** @var Folder $folder */
|
||||
$folder = $this->objFromFixture(Folder::class, 'folder_a');
|
||||
|
||||
/** @var Form $folderForm */
|
||||
$folderForm = FolderFormFactory::create()->getForm(null, FormFactory::DEFAULT_NAME, [
|
||||
'Record' => $folder
|
||||
]);
|
||||
|
||||
$source = array_values($folderForm->Fields()->fieldByName('SubsiteID')->getSource() ?? []);
|
||||
$result = array_values($source ?? []);
|
||||
|
||||
$this->assertContains('Main site', $result);
|
||||
$this->assertContains('Subsite A', $result);
|
||||
$this->assertContains('Subsite B', $result);
|
||||
}
|
||||
}
|
11
tests/php/Extensions/FolderFormFactoryExtensionTest.yml
Normal file
11
tests/php/Extensions/FolderFormFactoryExtensionTest.yml
Normal file
@ -0,0 +1,11 @@
|
||||
SilverStripe\Subsites\Model\Subsite:
|
||||
main:
|
||||
Title: Template
|
||||
subsite_a:
|
||||
Title: Subsite A
|
||||
subsite_b:
|
||||
Title: Subsite B
|
||||
|
||||
SilverStripe\Assets\Folder:
|
||||
folder_a:
|
||||
Title: Folder A
|
129
tests/php/FileSubsitesTest.php
Normal file
129
tests/php/FileSubsitesTest.php
Normal file
@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Tests;
|
||||
|
||||
use SilverStripe\Assets\File;
|
||||
use SilverStripe\Assets\Folder;
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Subsites\Extensions\FileSubsites;
|
||||
use SilverStripe\Subsites\Model\Subsite;
|
||||
use SilverStripe\Security\Member;
|
||||
|
||||
class FileSubsitesTest extends BaseSubsiteTest
|
||||
{
|
||||
protected static $fixture_file = 'SubsiteTest.yml';
|
||||
|
||||
public function testTrivialFeatures()
|
||||
{
|
||||
$this->assertTrue(is_array(singleton(FileSubsites::class)->extraStatics()));
|
||||
$file = new File();
|
||||
$file->Name = 'FileTitle';
|
||||
$file->Title = 'FileTitle';
|
||||
$this->assertEquals(' * FileTitle', $file->alternateTreeTitle());
|
||||
$file->SubsiteID = $this->objFromFixture(Subsite::class, 'domaintest1')->ID;
|
||||
$this->assertEquals('FileTitle', $file->getTreeTitle());
|
||||
$this->assertInstanceOf(FieldList::class, singleton(Folder::class)->getCMSFields());
|
||||
Subsite::changeSubsite(1);
|
||||
$this->assertEquals('subsite-1', $file->getExtensionInstance(FileSubsites::class)->cacheKeyComponent());
|
||||
}
|
||||
|
||||
public function testWritingSubsiteID()
|
||||
{
|
||||
$this->logInAs('admin');
|
||||
|
||||
$subsite = $this->objFromFixture(Subsite::class, 'domaintest1');
|
||||
Config::modify()->set(FileSubsites::class, 'default_root_folders_global', true);
|
||||
|
||||
Subsite::changeSubsite(0);
|
||||
$file = new File();
|
||||
$file->write();
|
||||
$file->onAfterUpload();
|
||||
$this->assertEquals((int)$file->SubsiteID, 0);
|
||||
|
||||
Subsite::changeSubsite($subsite->ID);
|
||||
$this->assertTrue($file->canEdit());
|
||||
|
||||
$file = new File();
|
||||
$file->write();
|
||||
$this->assertEquals((int)$file->SubsiteID, 0);
|
||||
$this->assertTrue($file->canEdit());
|
||||
|
||||
Config::modify()->set(FileSubsites::class, 'default_root_folders_global', false);
|
||||
|
||||
Subsite::changeSubsite($subsite->ID);
|
||||
$file = new File();
|
||||
$file->write();
|
||||
$this->assertEquals($file->SubsiteID, $subsite->ID);
|
||||
|
||||
// Test inheriting from parent folder
|
||||
$folder = new Folder();
|
||||
$folder->write();
|
||||
$this->assertEquals($folder->SubsiteID, $subsite->ID);
|
||||
Config::modify()->set(FileSubsites::class, 'default_root_folders_global', true);
|
||||
$file = new File();
|
||||
$file->ParentID = $folder->ID;
|
||||
$file->onAfterUpload();
|
||||
$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;
|
||||
}
|
||||
}
|
37
tests/php/GroupSubsitesTest.php
Normal file
37
tests/php/GroupSubsitesTest.php
Normal file
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Tests;
|
||||
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\Security\Group;
|
||||
use SilverStripe\Subsites\Extensions\GroupSubsites;
|
||||
use SilverStripe\Subsites\Model\Subsite;
|
||||
|
||||
class GroupSubsitesTest extends BaseSubsiteTest
|
||||
{
|
||||
protected static $fixture_file = 'SubsiteTest.yml';
|
||||
|
||||
protected $requireDefaultRecordsFrom = [GroupSubsites::class];
|
||||
|
||||
public function testTrivialFeatures()
|
||||
{
|
||||
$this->assertIsArray(singleton(GroupSubsites::class)->extraStatics());
|
||||
$this->assertIsArray(singleton(GroupSubsites::class)->providePermissions());
|
||||
$this->assertInstanceOf(FieldList::class, singleton(Group::class)->getCMSFields());
|
||||
}
|
||||
|
||||
public function testAlternateTreeTitle()
|
||||
{
|
||||
$group = new Group();
|
||||
$group->Title = 'The A Team';
|
||||
$group->AccessAllSubsites = true;
|
||||
$this->assertEquals('The A Team <i>(global group)</i>', $group->getTreeTitle());
|
||||
|
||||
$group->AccessAllSubsites = false;
|
||||
$group->write();
|
||||
|
||||
$group->Subsites()->add($this->objFromFixture(Subsite::class, 'domaintest1'));
|
||||
$group->Subsites()->add($this->objFromFixture(Subsite::class, 'domaintest2'));
|
||||
$this->assertEquals('The A Team <i>(Test 1, Test 2)</i>', $group->getTreeTitle());
|
||||
}
|
||||
}
|
80
tests/php/InitStateMiddlewareTest.php
Normal file
80
tests/php/InitStateMiddlewareTest.php
Normal 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);
|
||||
}
|
||||
}
|
114
tests/php/LeftAndMainSubsitesTest.php
Normal file
114
tests/php/LeftAndMainSubsitesTest.php
Normal file
@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Tests;
|
||||
|
||||
use SilverStripe\Admin\LeftAndMain;
|
||||
use SilverStripe\AssetAdmin\Controller\AssetAdmin;
|
||||
use SilverStripe\CMS\Controllers\CMSMain;
|
||||
use SilverStripe\CMS\Controllers\CMSPageEditController;
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\Dev\FunctionalTest;
|
||||
use SilverStripe\Security\Member;
|
||||
use SilverStripe\Subsites\Extensions\LeftAndMainSubsites;
|
||||
use SilverStripe\Subsites\Model\Subsite;
|
||||
use SilverStripe\Subsites\State\SubsiteState;
|
||||
|
||||
class LeftAndMainSubsitesTest extends FunctionalTest
|
||||
{
|
||||
protected static $fixture_file = 'SubsiteTest.yml';
|
||||
|
||||
/**
|
||||
* Avoid subsites filtering on fixture fetching.
|
||||
* @param string $className
|
||||
* @param string $identifier
|
||||
* @return \SilverStripe\ORM\DataObject
|
||||
*/
|
||||
protected function objFromFixture($className, $identifier)
|
||||
{
|
||||
Subsite::disable_subsite_filter(true);
|
||||
$obj = parent::objFromFixture($className, $identifier);
|
||||
Subsite::disable_subsite_filter(false);
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public function testSectionSites()
|
||||
{
|
||||
$member = $this->objFromFixture(Member::class, 'subsite1member');
|
||||
|
||||
$cmsmain = singleton(CMSMain::class);
|
||||
$subsites = $cmsmain->sectionSites(true, 'Main site', $member);
|
||||
$this->assertListEquals([
|
||||
['Title' => 'Subsite1 Template']
|
||||
], $subsites, 'Lists member-accessible sites for the accessible controller.');
|
||||
|
||||
$assetadmin = singleton(AssetAdmin::class);
|
||||
$subsites = $assetadmin->sectionSites(true, 'Main site', $member);
|
||||
$this->assertListEquals([], $subsites, 'Does not list any sites for forbidden controller.');
|
||||
|
||||
$member = $this->objFromFixture(Member::class, 'editor');
|
||||
|
||||
$cmsmain = singleton(CMSMain::class);
|
||||
$subsites = $cmsmain->sectionSites(true, 'Main site', $member);
|
||||
$this->assertListContains([
|
||||
['Title' => 'Main site']
|
||||
], $subsites, 'Includes the main site for members who can access all sites.');
|
||||
}
|
||||
|
||||
public function testAccessChecksDontChangeCurrentSubsite()
|
||||
{
|
||||
$this->logInAs('admin');
|
||||
$ids = [];
|
||||
|
||||
$subsite1 = $this->objFromFixture(Subsite::class, 'domaintest1');
|
||||
$subsite2 = $this->objFromFixture(Subsite::class, 'domaintest2');
|
||||
$subsite3 = $this->objFromFixture(Subsite::class, 'domaintest3');
|
||||
$ids[] = $subsite1->ID;
|
||||
$ids[] = $subsite2->ID;
|
||||
$ids[] = $subsite3->ID;
|
||||
$ids[] = 0;
|
||||
|
||||
// Enable session-based subsite tracking.
|
||||
SubsiteState::singleton()->setUseSessions(true);
|
||||
|
||||
foreach ($ids as $id) {
|
||||
Subsite::changeSubsite($id);
|
||||
$this->assertEquals($id, SubsiteState::singleton()->getSubsiteId());
|
||||
|
||||
$left = new LeftAndMain();
|
||||
$this->assertTrue($left->canView(), "Admin user can view subsites LeftAndMain with id = '$id'");
|
||||
$this->assertEquals(
|
||||
$id,
|
||||
SubsiteState::singleton()->getSubsiteId(),
|
||||
'The current subsite has not been changed in the process of checking permissions for admin user.'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public function testShouldChangeSubsite()
|
||||
{
|
||||
$l = new LeftAndMain();
|
||||
|
||||
Config::modify()->set(CMSPageEditController::class, 'treats_subsite_0_as_global', false);
|
||||
$this->assertTrue($l->shouldChangeSubsite(CMSPageEditController::class, 0, 5));
|
||||
$this->assertFalse($l->shouldChangeSubsite(CMSPageEditController::class, 0, 0));
|
||||
$this->assertTrue($l->shouldChangeSubsite(CMSPageEditController::class, 1, 5));
|
||||
$this->assertFalse($l->shouldChangeSubsite(CMSPageEditController::class, 1, 1));
|
||||
|
||||
Config::modify()->set(CMSPageEditController::class, 'treats_subsite_0_as_global', true);
|
||||
$this->assertFalse($l->shouldChangeSubsite(CMSPageEditController::class, 0, 5));
|
||||
$this->assertFalse($l->shouldChangeSubsite(CMSPageEditController::class, 0, 0));
|
||||
$this->assertTrue($l->shouldChangeSubsite(CMSPageEditController::class, 1, 5));
|
||||
$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));
|
||||
}
|
||||
}
|
143
tests/php/Service/ThemeResolverTest.php
Normal file
143
tests/php/Service/ThemeResolverTest.php
Normal file
@ -0,0 +1,143 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Tests\Service;
|
||||
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\Subsites\Model\Subsite;
|
||||
use SilverStripe\Subsites\Service\ThemeResolver;
|
||||
use SilverStripe\View\SSViewer;
|
||||
|
||||
class ThemeResolverTest extends SapphireTest
|
||||
{
|
||||
protected $themeList = [
|
||||
'$public',
|
||||
'custom',
|
||||
'main',
|
||||
'backup',
|
||||
SSViewer::DEFAULT_THEME,
|
||||
];
|
||||
|
||||
protected function setUp(): 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)
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
@ -1,15 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Tests;
|
||||
|
||||
use SilverStripe\SiteConfig\SiteConfig;
|
||||
use SilverStripe\Subsites\Extensions\SiteConfigSubsites;
|
||||
use SilverStripe\Subsites\Model\Subsite;
|
||||
|
||||
class SiteConfigSubsitesTest extends BaseSubsiteTest
|
||||
{
|
||||
public static $fixture_file = 'subsites/tests/SubsiteTest.yml';
|
||||
protected static $fixture_file = 'SubsiteTest.yml';
|
||||
|
||||
public function testEachSubsiteHasAUniqueSiteConfig()
|
||||
{
|
||||
$subsite1 = $this->objFromFixture('Subsite', 'domaintest1');
|
||||
$subsite2 = $this->objFromFixture('Subsite', 'domaintest2');
|
||||
$subsite1 = $this->objFromFixture(Subsite::class, 'domaintest1');
|
||||
$subsite2 = $this->objFromFixture(Subsite::class, 'domaintest2');
|
||||
|
||||
$this->assertTrue(is_array(singleton('SiteConfigSubsites')->extraStatics()));
|
||||
$this->assertTrue(is_array(singleton(SiteConfigSubsites::class)->extraStatics()));
|
||||
|
||||
Subsite::changeSubsite(0);
|
||||
$sc = SiteConfig::current_site_config();
|
||||
@ -27,11 +33,11 @@ class SiteConfigSubsitesTest extends BaseSubsiteTest
|
||||
$sc->write();
|
||||
|
||||
Subsite::changeSubsite(0);
|
||||
$this->assertEquals(SiteConfig::current_site_config()->Title, 'RootSite');
|
||||
$this->assertEquals('RootSite', SiteConfig::current_site_config()->Title);
|
||||
Subsite::changeSubsite($subsite1->ID);
|
||||
$this->assertEquals(SiteConfig::current_site_config()->Title, 'Subsite1');
|
||||
$this->assertEquals('Subsite1', SiteConfig::current_site_config()->Title);
|
||||
Subsite::changeSubsite($subsite2->ID);
|
||||
$this->assertEquals(SiteConfig::current_site_config()->Title, 'Subsite2');
|
||||
$this->assertEquals('Subsite2', SiteConfig::current_site_config()->Title);
|
||||
|
||||
$keys = SiteConfig::current_site_config()->extend('cacheKeyComponent');
|
||||
$this->assertContains('subsite-' . $subsite2->ID, $keys);
|
494
tests/php/SiteTreeSubsitesTest.php
Normal file
494
tests/php/SiteTreeSubsitesTest.php
Normal file
@ -0,0 +1,494 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Tests;
|
||||
|
||||
use Page;
|
||||
use SilverStripe\CMS\Controllers\CMSMain;
|
||||
use SilverStripe\CMS\Controllers\ModelAsController;
|
||||
use SilverStripe\CMS\Forms\SiteTreeURLSegmentField;
|
||||
use SilverStripe\CMS\Model\SiteTree;
|
||||
use SilverStripe\Control\Director;
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\Core\Convert;
|
||||
use SilverStripe\Core\Injector\Injector;
|
||||
use SilverStripe\ErrorPage\ErrorPage;
|
||||
use SilverStripe\Forms\FieldList;
|
||||
use SilverStripe\i18n\i18n;
|
||||
use SilverStripe\Security\Member;
|
||||
use SilverStripe\SiteConfig\SiteConfig;
|
||||
use SilverStripe\Subsites\Extensions\SiteTreeSubsites;
|
||||
use SilverStripe\Subsites\Model\Subsite;
|
||||
use SilverStripe\Subsites\Pages\SubsitesVirtualPage;
|
||||
use SilverStripe\Subsites\Service\ThemeResolver;
|
||||
use SilverStripe\Subsites\Tests\SiteTreeSubsitesTest\TestClassA;
|
||||
use SilverStripe\Subsites\Tests\SiteTreeSubsitesTest\TestClassB;
|
||||
use SilverStripe\Subsites\Tests\SiteTreeSubsitesTest\TestErrorPage;
|
||||
use SilverStripe\Versioned\Versioned;
|
||||
use SilverStripe\View\SSViewer;
|
||||
use TractorCow\Fluent\Extension\FluentSiteTreeExtension;
|
||||
|
||||
class SiteTreeSubsitesTest extends BaseSubsiteTest
|
||||
{
|
||||
protected static $fixture_file = 'SubsiteTest.yml';
|
||||
|
||||
protected static $extra_dataobjects = [
|
||||
TestClassA::class,
|
||||
TestClassB::class,
|
||||
TestErrorPage::class
|
||||
];
|
||||
|
||||
protected static $illegal_extensions = [
|
||||
SiteTree::class => [
|
||||
FluentSiteTreeExtension::class,
|
||||
],
|
||||
];
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
// 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);
|
||||
|
||||
parent::setUp();
|
||||
}
|
||||
|
||||
public function testPagesInDifferentSubsitesCanShareURLSegment()
|
||||
{
|
||||
$subsiteMain = $this->objFromFixture(Subsite::class, 'main');
|
||||
$subsite1 = $this->objFromFixture(Subsite::class, 'subsite1');
|
||||
|
||||
$pageMain = new SiteTree();
|
||||
$pageMain->URLSegment = 'testpage';
|
||||
$pageMain->write();
|
||||
$pageMain->copyVersionToStage('Stage', 'Live');
|
||||
|
||||
$pageMainOther = new SiteTree();
|
||||
$pageMainOther->URLSegment = 'testpage';
|
||||
$pageMainOther->write();
|
||||
$pageMainOther->copyVersionToStage('Stage', 'Live');
|
||||
|
||||
$this->assertNotEquals(
|
||||
$pageMain->URLSegment,
|
||||
$pageMainOther->URLSegment,
|
||||
'Pages in same subsite cant share the same URL'
|
||||
);
|
||||
|
||||
Subsite::changeSubsite($subsite1->ID);
|
||||
|
||||
$pageSubsite1 = new SiteTree();
|
||||
$pageSubsite1->URLSegment = 'testpage';
|
||||
$pageSubsite1->write();
|
||||
$pageSubsite1->copyVersionToStage('Stage', 'Live');
|
||||
|
||||
$this->assertEquals(
|
||||
$pageMain->URLSegment,
|
||||
$pageSubsite1->URLSegment,
|
||||
'Pages in different subsites can share the same URL'
|
||||
);
|
||||
}
|
||||
|
||||
public function testBasicSanity()
|
||||
{
|
||||
$this->assertInstanceOf(SiteConfig::class, singleton(SiteTree::class)->getSiteConfig());
|
||||
// The following assert is breaking in Translatable.
|
||||
$this->assertInstanceOf(FieldList::class, singleton(SiteTree::class)->getCMSFields());
|
||||
$this->assertInstanceOf(FieldList::class, singleton(SubsitesVirtualPage::class)->getCMSFields());
|
||||
$this->assertTrue(is_array(singleton(SiteTreeSubsites::class)->extraStatics()));
|
||||
}
|
||||
|
||||
public function errorPageLocationsProvider()
|
||||
{
|
||||
return [
|
||||
['domaintest1', '/error-500-one.example.org.html'],
|
||||
['domaintestVagrant', '/error-500-localhost8080.html']
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider errorPageLocationsProvider
|
||||
*/
|
||||
public function testErrorPageLocations($subsiteFixtureName, $expectedFilename)
|
||||
{
|
||||
$static_path = Config::inst()->get(ErrorPage::class, 'static_filepath');
|
||||
|
||||
$subsite = $this->objFromFixture(Subsite::class, $subsiteFixtureName);
|
||||
$expected_path = $static_path . $expectedFilename;
|
||||
|
||||
Subsite::changeSubsite($subsite->ID);
|
||||
$path = TestErrorPage::get_error_filename_spy(500);
|
||||
|
||||
$this->assertEquals($expected_path, $path);
|
||||
}
|
||||
|
||||
public function testCanEditSiteTree()
|
||||
{
|
||||
$admin = $this->objFromFixture(Member::class, 'admin');
|
||||
$subsite1member = $this->objFromFixture(Member::class, 'subsite1member');
|
||||
$subsite2member = $this->objFromFixture(Member::class, 'subsite2member');
|
||||
$mainpage = $this->objFromFixture('Page', 'home');
|
||||
$subsite1page = $this->objFromFixture('Page', 'subsite1_home');
|
||||
$subsite2page = $this->objFromFixture('Page', 'subsite2_home');
|
||||
$subsite1 = $this->objFromFixture(Subsite::class, 'subsite1');
|
||||
$subsite2 = $this->objFromFixture(Subsite::class, 'subsite2');
|
||||
|
||||
// Cant pass member as arguments to canEdit() because of GroupSubsites
|
||||
$this->logInAs($admin);
|
||||
|
||||
$this->assertTrue(
|
||||
(bool)$subsite1page->canEdit(),
|
||||
'Administrators can edit all subsites'
|
||||
);
|
||||
|
||||
// @todo: Workaround because GroupSubsites->augmentSQL() is relying on session state
|
||||
Subsite::changeSubsite($subsite1);
|
||||
|
||||
$this->logInAs($subsite1member->ID);
|
||||
$this->assertTrue(
|
||||
(bool)$subsite1page->canEdit(),
|
||||
'Members can edit pages on a subsite if they are in a group belonging to this subsite'
|
||||
);
|
||||
|
||||
$this->logInAs($subsite2member->ID);
|
||||
$this->assertFalse(
|
||||
(bool)$subsite1page->canEdit(),
|
||||
'Members cant edit pages on a subsite if they are not in a group belonging to this subsite'
|
||||
);
|
||||
|
||||
// @todo: Workaround because GroupSubsites->augmentSQL() is relying on session state
|
||||
Subsite::changeSubsite(0);
|
||||
$this->assertFalse(
|
||||
$mainpage->canEdit(),
|
||||
'Members cant edit pages on the main site if they are not in a group allowing this'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to {@link SubsitesVirtualPageTest->testSubsiteVirtualPageCanHaveSameUrlsegmentAsOtherSubsite()}.
|
||||
*/
|
||||
public function testTwoPagesWithSameURLOnDifferentSubsites()
|
||||
{
|
||||
// Set up a couple of pages with the same URL on different subsites
|
||||
$s1 = $this->objFromFixture(Subsite::class, 'domaintest1');
|
||||
$s2 = $this->objFromFixture(Subsite::class, 'domaintest2');
|
||||
|
||||
$p1 = new SiteTree();
|
||||
$p1->Title = $p1->URLSegment = 'test-page';
|
||||
$p1->SubsiteID = $s1->ID;
|
||||
$p1->write();
|
||||
|
||||
$p2 = new SiteTree();
|
||||
$p2->Title = $p1->URLSegment = 'test-page';
|
||||
$p2->SubsiteID = $s2->ID;
|
||||
$p2->write();
|
||||
|
||||
// Check that the URLs weren't modified in our set-up
|
||||
$this->assertEquals('test-page', $p1->URLSegment);
|
||||
$this->assertEquals('test-page', $p2->URLSegment);
|
||||
|
||||
// Check that if we switch between the different subsites, we receive the correct pages
|
||||
Subsite::changeSubsite($s1);
|
||||
$this->assertEquals($p1->ID, SiteTree::get_by_link('test-page')->ID);
|
||||
|
||||
Subsite::changeSubsite($s2);
|
||||
$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()
|
||||
{
|
||||
$this->logInAs('editor');
|
||||
|
||||
$s1 = $this->objFromFixture(Subsite::class, 'domaintest1');
|
||||
$s2 = $this->objFromFixture(Subsite::class, 'domaintest2');
|
||||
$page = singleton(SiteTree::class);
|
||||
|
||||
$s1->PageTypeBlacklist = json_encode([TestClassA::class, ErrorPage::class]);
|
||||
$s1->write();
|
||||
|
||||
Subsite::changeSubsite($s1);
|
||||
$settingsFields = $page->getSettingsFields()->dataFieldByName('ClassName')->getSource();
|
||||
|
||||
$this->assertArrayNotHasKey(
|
||||
ErrorPage::class,
|
||||
$settingsFields
|
||||
);
|
||||
$this->assertArrayNotHasKey(
|
||||
TestClassA::class,
|
||||
$settingsFields
|
||||
);
|
||||
$this->assertArrayHasKey(
|
||||
TestClassB::class,
|
||||
$settingsFields
|
||||
);
|
||||
|
||||
Subsite::changeSubsite($s2);
|
||||
$settingsFields = $page->getSettingsFields()->dataFieldByName('ClassName')->getSource();
|
||||
$this->assertArrayHasKey(
|
||||
ErrorPage::class,
|
||||
$settingsFields
|
||||
);
|
||||
$this->assertArrayHasKey(
|
||||
TestClassA::class,
|
||||
$settingsFields
|
||||
);
|
||||
$this->assertArrayHasKey(
|
||||
TestClassB::class,
|
||||
$settingsFields
|
||||
);
|
||||
}
|
||||
|
||||
public function testCopyToSubsite()
|
||||
{
|
||||
// Remove baseurl if testing in subdir
|
||||
Config::modify()->set(Director::class, 'alternate_base_url', '/');
|
||||
|
||||
/** @var Subsite $otherSubsite */
|
||||
$otherSubsite = $this->objFromFixture(Subsite::class, 'subsite1');
|
||||
$staffPage = $this->objFromFixture(Page::class, 'staff'); // nested page
|
||||
$contactPage = $this->objFromFixture(Page::class, 'contact'); // top level page
|
||||
|
||||
$staffPage2 = $staffPage->duplicateToSubsite($otherSubsite->ID);
|
||||
$contactPage2 = $contactPage->duplicateToSubsite($otherSubsite->ID);
|
||||
|
||||
$this->assertNotEquals($staffPage->ID, $staffPage2->ID);
|
||||
$this->assertNotEquals($staffPage->SubsiteID, $staffPage2->SubsiteID);
|
||||
$this->assertNotEquals($contactPage->ID, $contactPage2->ID);
|
||||
$this->assertNotEquals($contactPage->SubsiteID, $contactPage2->SubsiteID);
|
||||
$this->assertEmpty($staffPage2->ParentID);
|
||||
$this->assertEmpty($contactPage2->ParentID);
|
||||
$this->assertNotEmpty($staffPage->ParentID);
|
||||
$this->assertEmpty($contactPage->ParentID);
|
||||
|
||||
// Staff is shifted to top level and given a unique url segment
|
||||
$domain = $otherSubsite->domain();
|
||||
$this->assertEquals('http://' . $domain . '/staff-2/', $staffPage2->AbsoluteLink());
|
||||
$this->assertEquals('http://' . $domain . '/contact-us-2/', $contactPage2->AbsoluteLink());
|
||||
}
|
||||
|
||||
public function testPageTypesBlacklistInCMSMain()
|
||||
{
|
||||
$this->logInAs('editor');
|
||||
|
||||
$s1 = $this->objFromFixture(Subsite::class, 'domaintest1');
|
||||
$s2 = $this->objFromFixture(Subsite::class, 'domaintest2');
|
||||
|
||||
$s1->PageTypeBlacklist = json_encode([TestClassA::class, ErrorPage::class]);
|
||||
$s1->write();
|
||||
|
||||
Subsite::changeSubsite($s1);
|
||||
$cmsmain = CMSMain::create();
|
||||
$hints = json_decode($cmsmain->SiteTreeHints() ?? '', true);
|
||||
$classes = $hints['Root']['disallowedChildren'];
|
||||
$this->assertContains(ErrorPage::class, $classes);
|
||||
$this->assertContains(TestClassA::class, $classes);
|
||||
$this->assertNotContains(TestClassB::class, $classes);
|
||||
|
||||
Subsite::changeSubsite($s2);
|
||||
// SS 4.1 and above
|
||||
if ($cmsmain->hasMethod('getHintsCache')) {
|
||||
$cmsmain->getHintsCache()->clear();
|
||||
}
|
||||
$hints = json_decode($cmsmain->SiteTreeHints() ?? '', true);
|
||||
|
||||
$classes = $hints['Root']['disallowedChildren'];
|
||||
$this->assertNotContains(ErrorPage::class, $classes);
|
||||
$this->assertNotContains(TestClassA::class, $classes);
|
||||
$this->assertNotContains(TestClassB::class, $classes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that url segments between subsites don't conflict, but do conflict within them
|
||||
*/
|
||||
public function testValidateURLSegment()
|
||||
{
|
||||
$this->logInWithPermission('ADMIN');
|
||||
// Saving existing page in the same subsite doesn't change urls
|
||||
$mainHome = $this->objFromFixture(Page::class, 'home');
|
||||
$mainSubsiteID = $this->idFromFixture(Subsite::class, 'main');
|
||||
Subsite::changeSubsite($mainSubsiteID);
|
||||
$mainHome->Content = '<p>Some new content</p>';
|
||||
$mainHome->write();
|
||||
$this->assertEquals('home', $mainHome->URLSegment);
|
||||
$mainHome->publishRecursive();
|
||||
$mainHomeLive = Versioned::get_one_by_stage('Page', 'Live', sprintf('"SiteTree"."ID" = \'%d\'', $mainHome->ID));
|
||||
$this->assertEquals('home', $mainHomeLive->URLSegment);
|
||||
|
||||
// Saving existing page in another subsite doesn't change urls
|
||||
Subsite::changeSubsite($mainSubsiteID);
|
||||
$subsite1Home = $this->objFromFixture('Page', 'subsite1_home');
|
||||
$subsite1Home->Content = '<p>In subsite 1</p>';
|
||||
$subsite1Home->write();
|
||||
$this->assertEquals('home', $subsite1Home->URLSegment);
|
||||
$subsite1Home->publishRecursive();
|
||||
$subsite1HomeLive = Versioned::get_one_by_stage(
|
||||
'Page',
|
||||
'Live',
|
||||
sprintf('"SiteTree"."ID" = \'%d\'', $subsite1Home->ID)
|
||||
);
|
||||
$this->assertEquals('home', $subsite1HomeLive->URLSegment);
|
||||
|
||||
// Creating a new page in a subsite doesn't conflict with urls in other subsites
|
||||
$subsite1ID = $this->idFromFixture(Subsite::class, 'subsite1');
|
||||
Subsite::changeSubsite($subsite1ID);
|
||||
$subsite1NewPage = new Page();
|
||||
$subsite1NewPage->SubsiteID = $subsite1ID;
|
||||
$subsite1NewPage->Title = 'Important Page (Subsite 1)';
|
||||
$subsite1NewPage->URLSegment = 'important-page'; // Also exists in main subsite
|
||||
$subsite1NewPage->write();
|
||||
$this->assertEquals('important-page', $subsite1NewPage->URLSegment);
|
||||
$subsite1NewPage->publishRecursive();
|
||||
$subsite1NewPageLive = Versioned::get_one_by_stage(
|
||||
'Page',
|
||||
'Live',
|
||||
sprintf('"SiteTree"."ID" = \'%d\'', $subsite1NewPage->ID)
|
||||
);
|
||||
$this->assertEquals('important-page', $subsite1NewPageLive->URLSegment);
|
||||
|
||||
// Creating a new page in a subsite DOES conflict with urls in the same subsite
|
||||
$subsite1NewPage2 = new Page();
|
||||
$subsite1NewPage2->SubsiteID = $subsite1ID;
|
||||
$subsite1NewPage2->Title = 'Important Page (Subsite 1)';
|
||||
$subsite1NewPage2->URLSegment = 'important-page'; // Also exists in main subsite
|
||||
$subsite1NewPage2->write();
|
||||
$this->assertEquals('important-page-2', $subsite1NewPage2->URLSegment);
|
||||
$subsite1NewPage2->publishRecursive();
|
||||
$subsite1NewPage2Live = Versioned::get_one_by_stage(
|
||||
'Page',
|
||||
'Live',
|
||||
sprintf('"SiteTree"."ID" = \'%d\'', $subsite1NewPage2->ID)
|
||||
);
|
||||
$this->assertEquals('important-page-2', $subsite1NewPage2Live->URLSegment);
|
||||
|
||||
// Original page is left un-modified
|
||||
$mainSubsiteImportantPageID = $this->idFromFixture('Page', 'importantpage');
|
||||
$mainSubsiteImportantPage = Page::get()->byID($mainSubsiteImportantPageID);
|
||||
$this->assertEquals('important-page', $mainSubsiteImportantPage->URLSegment);
|
||||
$mainSubsiteImportantPage->Content = '<p>New Important Page Content</p>';
|
||||
$mainSubsiteImportantPage->write();
|
||||
$this->assertEquals('important-page', $mainSubsiteImportantPage->URLSegment);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param bool $withChildren
|
||||
* @param int $expectedChildren
|
||||
* @dataProvider duplicateToSubsiteProvider
|
||||
*/
|
||||
public function testDuplicateToSubsite($withChildren, $expectedChildren)
|
||||
{
|
||||
/** @var SiteTree $page */
|
||||
$page = $this->objFromFixture(Page::class, 'about');
|
||||
/** @var Subsite $newSubsite */
|
||||
$newSubsite = $this->objFromFixture(Subsite::class, 'subsite1');
|
||||
|
||||
/** @var SiteTree $duplicatedPage */
|
||||
$duplicatedPage = $page->duplicateToSubsite($newSubsite->ID, $withChildren);
|
||||
$this->assertInstanceOf(SiteTree::class, $duplicatedPage, 'A new page is returned');
|
||||
|
||||
$this->assertEquals($newSubsite->ID, $duplicatedPage->SubsiteID, 'Ensure returned records are on new subsite');
|
||||
|
||||
$this->assertCount(1, $page->AllChildren());
|
||||
$this->assertCount(
|
||||
$expectedChildren,
|
||||
$duplicatedPage->AllChildren(),
|
||||
'Duplicated page also duplicates children'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array[]
|
||||
*/
|
||||
public function duplicateToSubsiteProvider()
|
||||
{
|
||||
return [
|
||||
[true, 1],
|
||||
[false, 0],
|
||||
];
|
||||
}
|
||||
|
||||
public function testThemeResolverIsUsedForSettingThemeList()
|
||||
{
|
||||
$firstResolver = $this->createMock(ThemeResolver::class);
|
||||
$firstResolver->expects($this->never())->method('getThemeList');
|
||||
Injector::inst()->registerService($firstResolver, ThemeResolver::class);
|
||||
|
||||
$subsitePage = $this->objFromFixture(Page::class, 'home');
|
||||
Subsite::changeSubsite($subsitePage->SubsiteID);
|
||||
$controller = ModelAsController::controller_for($subsitePage);
|
||||
SiteTree::singleton()->extend('contentcontrollerInit', $controller);
|
||||
|
||||
$secondResolver = $this->createMock(ThemeResolver::class);
|
||||
$secondResolver->expects($this->once())->method('getThemeList');
|
||||
Injector::inst()->registerService($secondResolver, ThemeResolver::class);
|
||||
|
||||
$subsitePage = $this->objFromFixture(Page::class, 'subsite1_home');
|
||||
Subsite::changeSubsite($subsitePage->SubsiteID);
|
||||
$controller = ModelAsController::controller_for($subsitePage);
|
||||
SiteTree::singleton()->extend('contentcontrollerInit', $controller);
|
||||
}
|
||||
|
||||
public function provideAlternateAbsoluteLink()
|
||||
{
|
||||
return [
|
||||
['home', null, 'http://localhost/'],
|
||||
['home', 'myaction', 'http://localhost/home/myaction'],
|
||||
['contact', null, 'http://localhost/contact-us/'],
|
||||
['contact', 'myaction', 'http://localhost/contact-us/myaction'],
|
||||
['subsite1_home', null, 'http://subsite1.localhost/'],
|
||||
['subsite1_home', 'myaction', 'http://subsite1.localhost/home/myaction'],
|
||||
['subsite1_contactus', null, 'http://subsite1.localhost/contact-us/'],
|
||||
['subsite1_contactus', 'myaction', 'http://subsite1.localhost/contact-us/myaction']
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider provideAlternateAbsoluteLink
|
||||
* @param string $pageFixtureName
|
||||
* @param string|null $action
|
||||
* @param string $expectedAbsoluteLink
|
||||
*/
|
||||
public function testAlternateAbsoluteLink($pageFixtureName, $action, $expectedAbsoluteLink)
|
||||
{
|
||||
// Setting a control value, in case base url is set for the installation under test
|
||||
Config::modify()->set(Director::class, 'alternate_base_url', 'http://localhost/');
|
||||
|
||||
/** @var Page $page */
|
||||
$page = $this->objFromFixture(Page::class, $pageFixtureName);
|
||||
|
||||
$result = $page->AbsoluteLink($action);
|
||||
|
||||
$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());
|
||||
}
|
||||
}
|
10
tests/php/SiteTreeSubsitesTest/TestClassA.php
Normal file
10
tests/php/SiteTreeSubsitesTest/TestClassA.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Tests\SiteTreeSubsitesTest;
|
||||
|
||||
use SilverStripe\CMS\Model\SiteTree;
|
||||
use SilverStripe\Dev\TestOnly;
|
||||
|
||||
class TestClassA extends SiteTree implements TestOnly
|
||||
{
|
||||
}
|
10
tests/php/SiteTreeSubsitesTest/TestClassB.php
Normal file
10
tests/php/SiteTreeSubsitesTest/TestClassB.php
Normal file
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Tests\SiteTreeSubsitesTest;
|
||||
|
||||
use SilverStripe\CMS\Model\SiteTree;
|
||||
use SilverStripe\Dev\TestOnly;
|
||||
|
||||
class TestClassB extends SiteTree implements TestOnly
|
||||
{
|
||||
}
|
20
tests/php/SiteTreeSubsitesTest/TestErrorPage.php
Normal file
20
tests/php/SiteTreeSubsitesTest/TestErrorPage.php
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Tests\SiteTreeSubsitesTest;
|
||||
|
||||
use SilverStripe\Dev\TestOnly;
|
||||
use SilverStripe\ErrorPage\ErrorPage;
|
||||
|
||||
class TestErrorPage extends ErrorPage implements TestOnly
|
||||
{
|
||||
/**
|
||||
* Helper method to call protected members
|
||||
*
|
||||
* @param int $statusCode
|
||||
* @return string
|
||||
*/
|
||||
public static function get_error_filename_spy($statusCode)
|
||||
{
|
||||
return self::get_error_filename($statusCode);
|
||||
}
|
||||
}
|
50
tests/php/State/SubsiteStateTest.php
Normal file
50
tests/php/State/SubsiteStateTest.php
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Tests\State;
|
||||
|
||||
use SilverStripe\Core\Injector\Injector;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\Subsites\State\SubsiteState;
|
||||
|
||||
class SubsiteStateTest extends SapphireTest
|
||||
{
|
||||
public function testStateIsInjectable()
|
||||
{
|
||||
$this->assertInstanceOf(SubsiteState::class, Injector::inst()->get(SubsiteState::class));
|
||||
}
|
||||
|
||||
public function testGetSubsiteIdWasChanged()
|
||||
{
|
||||
$state = new SubsiteState;
|
||||
|
||||
// Initial set, doesn't count as being changed
|
||||
$state->setSubsiteId(123);
|
||||
$this->assertFalse($state->getSubsiteIdWasChanged());
|
||||
|
||||
// Subsequent set with the same value, doesn't count as being changed
|
||||
$state->setSubsiteId(123);
|
||||
$this->assertFalse($state->getSubsiteIdWasChanged());
|
||||
|
||||
// Subsequent set with new value, counts as changed
|
||||
$state->setSubsiteId(234);
|
||||
$this->assertTrue($state->getSubsiteIdWasChanged());
|
||||
}
|
||||
|
||||
public function testWithState()
|
||||
{
|
||||
$state = new SubsiteState;
|
||||
$state->setSubsiteId(123);
|
||||
|
||||
$state->withState(function ($newState) use ($state) {
|
||||
$this->assertInstanceOf(SubsiteState::class, $newState);
|
||||
|
||||
$this->assertNotSame($newState, $state);
|
||||
|
||||
$newState->setSubsiteId(234);
|
||||
$this->assertSame(234, $newState->getSubsiteId());
|
||||
$this->assertSame(123, $state->getSubsiteId());
|
||||
});
|
||||
|
||||
$this->assertSame(123, $state->getSubsiteId());
|
||||
}
|
||||
}
|
201
tests/php/SubsiteAdminFunctionalTest.php
Normal file
201
tests/php/SubsiteAdminFunctionalTest.php
Normal file
@ -0,0 +1,201 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Tests;
|
||||
|
||||
use Page;
|
||||
use SilverStripe\CMS\Controllers\CMSPageEditController;
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\Dev\FunctionalTest;
|
||||
use SilverStripe\Subsites\Model\Subsite;
|
||||
|
||||
class SubsiteAdminFunctionalTest extends FunctionalTest
|
||||
{
|
||||
protected static $fixture_file = 'SubsiteTest.yml';
|
||||
|
||||
protected $autoFollowRedirection = false;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
// Ensure all pages are published
|
||||
/** @var Page $page */
|
||||
foreach (Page::get() as $page) {
|
||||
$page->publishSingle();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper: FunctionalTest is only able to follow redirection once, we want to go all the way.
|
||||
* @param string $url
|
||||
* @return \SilverStripe\Control\HTTPResponse
|
||||
*/
|
||||
public function getAndFollowAll($url)
|
||||
{
|
||||
$response = $this->get($url);
|
||||
while ($location = $response->getHeader('Location')) {
|
||||
$response = $this->mainSession->followRedirection();
|
||||
}
|
||||
echo $response->getHeader('Location');
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Anonymous user cannot access anything.
|
||||
*/
|
||||
public function testAnonymousIsForbiddenAdminAccess()
|
||||
{
|
||||
$this->logOut();
|
||||
|
||||
$response = $this->getAndFollowAll('admin/pages/?SubsiteID=0');
|
||||
$this->assertStringContainsString('Security/login', $this->mainSession->lastUrl(), 'Admin is disallowed');
|
||||
|
||||
$subsite1 = $this->objFromFixture(Subsite::class, 'subsite1');
|
||||
$response = $this->getAndFollowAll("admin/pages/?SubsiteID={$subsite1->ID}");
|
||||
$this->assertStringContainsString('Security/login', $this->mainSession->lastUrl(), 'Admin is disallowed');
|
||||
|
||||
$response = $this->getAndFollowAll('admin/subsite_xhr');
|
||||
$this->assertStringContainsString(
|
||||
'Security/login',
|
||||
$this->mainSession->lastUrl(),
|
||||
'SubsiteXHRController is disallowed'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Admin should be able to access all subsites and the main site
|
||||
*/
|
||||
public function testAdminCanAccessAllSubsites()
|
||||
{
|
||||
$this->logInAs('admin');
|
||||
|
||||
$this->getAndFollowAll('admin/pages/?SubsiteID=0');
|
||||
$this->assertEquals(0, $this->session()->get('SubsiteID'), 'Can access main site.');
|
||||
$this->assertStringContainsString('admin/pages', $this->mainSession->lastUrl(), 'Lands on the correct section');
|
||||
|
||||
$subsite1 = $this->objFromFixture(Subsite::class, 'subsite1');
|
||||
$this->getAndFollowAll("admin/pages/?SubsiteID={$subsite1->ID}");
|
||||
|
||||
// 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->assertStringContainsString('admin/pages', $this->mainSession->lastUrl(), 'Lands on the correct section');
|
||||
|
||||
$response = $this->getAndFollowAll('admin/subsite_xhr');
|
||||
$this->assertStringNotContainsString(
|
||||
'Security/login',
|
||||
$this->mainSession->lastUrl(),
|
||||
'SubsiteXHRController is reachable'
|
||||
);
|
||||
}
|
||||
|
||||
public function testAdminIsRedirectedToObjectsSubsite()
|
||||
{
|
||||
$this->logInAs('admin');
|
||||
|
||||
$mainSubsitePage = $this->objFromFixture(Page::class, 'mainSubsitePage');
|
||||
$subsite1Home = $this->objFromFixture(Page::class, 'subsite1_home');
|
||||
|
||||
// Requesting a page from another subsite will redirect to that subsite
|
||||
Config::modify()->set(CMSPageEditController::class, 'treats_subsite_0_as_global', false);
|
||||
$response = $this->get("admin/pages/edit/show/$subsite1Home->ID");
|
||||
|
||||
$this->assertEquals(302, $response->getStatusCode());
|
||||
$this->assertStringContainsString(
|
||||
'admin/pages/edit/show/' . $subsite1Home->ID . '?SubsiteID=' . $subsite1Home->SubsiteID,
|
||||
$response->getHeader('Location')
|
||||
);
|
||||
|
||||
// Loading a non-main-site object still switches the subsite if configured with treats_subsite_0_as_global
|
||||
Config::modify()->set(CMSPageEditController::class, 'treats_subsite_0_as_global', true);
|
||||
|
||||
$response = $this->get("admin/pages/edit/show/$subsite1Home->ID");
|
||||
$this->assertEquals(302, $response->getStatusCode());
|
||||
$this->assertStringContainsString(
|
||||
'admin/pages/edit/show/' . $subsite1Home->ID . '?SubsiteID=' . $subsite1Home->SubsiteID,
|
||||
$response->getHeader('Location')
|
||||
);
|
||||
|
||||
// Loading a main-site object does not change the subsite if configured with treats_subsite_0_as_global
|
||||
$response = $this->get("admin/pages/edit/show/$mainSubsitePage->ID");
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
}
|
||||
|
||||
/**
|
||||
* User which has AccessAllSubsites set to 1 should be able to access all subsites and main site,
|
||||
* even though he does not have the ADMIN permission.
|
||||
*/
|
||||
public function testEditorCanAccessAllSubsites()
|
||||
{
|
||||
$this->logInAs('editor');
|
||||
|
||||
$this->get('admin/pages/?SubsiteID=0');
|
||||
$this->assertEquals(0, $this->session()->get('SubsiteID'), 'Can access main site.');
|
||||
$this->assertStringContainsString('admin/pages', $this->mainSession->lastUrl(), 'Lands on the correct section');
|
||||
|
||||
$subsite1 = $this->objFromFixture(Subsite::class, 'subsite1');
|
||||
$this->get("admin/pages/?SubsiteID={$subsite1->ID}");
|
||||
$this->assertEquals($subsite1->ID, $this->session()->get('SubsiteID'), 'Can access other subsite.');
|
||||
$this->assertStringContainsString('admin/pages', $this->mainSession->lastUrl(), 'Lands on the correct section');
|
||||
|
||||
$response = $this->get('admin/subsite_xhr');
|
||||
$this->assertStringNotContainsString(
|
||||
'Security/login',
|
||||
$this->mainSession->lastUrl(),
|
||||
'SubsiteXHRController is reachable'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test a member who only has access to one subsite (subsite1) and only some sections (pages and security).
|
||||
*/
|
||||
public function testSubsiteAdmin()
|
||||
{
|
||||
$this->markTestSkipped('wip');
|
||||
$this->logInAs('subsite1member');
|
||||
|
||||
$subsite1 = $this->objFromFixture(Subsite::class, 'subsite1');
|
||||
|
||||
// Check allowed URL.
|
||||
$this->getAndFollowAll("admin/pages/?SubsiteID={$subsite1->ID}");
|
||||
$this->assertEquals($subsite1->ID, $this->session()->get('SubsiteID'), 'Can access own subsite.');
|
||||
$this->assertStringContainsString(
|
||||
'admin/pages',
|
||||
$this->mainSession->lastUrl(),
|
||||
'Can access permitted section.'
|
||||
);
|
||||
|
||||
// Check forbidden section in allowed subsite.
|
||||
$this->getAndFollowAll("admin/assets/?SubsiteID={$subsite1->ID}");
|
||||
$this->assertEquals($subsite1->ID, $this->session()->get('SubsiteID'), 'Is redirected within subsite.');
|
||||
$this->assertNotContains(
|
||||
'admin/assets',
|
||||
$this->mainSession->lastUrl(),
|
||||
'Is redirected away from forbidden section'
|
||||
);
|
||||
|
||||
// Check forbidden site, on a section that's allowed on another subsite
|
||||
$this->getAndFollowAll('admin/pages/?SubsiteID=0');
|
||||
$this->assertEquals(
|
||||
$this->session()->get('SubsiteID'),
|
||||
$subsite1->ID,
|
||||
'Is redirected to permitted subsite.'
|
||||
);
|
||||
|
||||
// Check forbidden site, on a section that's not allowed on any other subsite
|
||||
$this->getAndFollowAll('admin/assets/?SubsiteID=0');
|
||||
$this->assertEquals(
|
||||
$this->session()->get('SubsiteID'),
|
||||
$subsite1->ID,
|
||||
'Is redirected to first permitted subsite.'
|
||||
);
|
||||
$this->assertStringNotContainsString('Security/login', $this->mainSession->lastUrl(), 'Is not denied access');
|
||||
|
||||
// Check the standalone XHR controller.
|
||||
$response = $this->getAndFollowAll('admin/subsite_xhr');
|
||||
$this->assertStringNotContainsString(
|
||||
'Security/login',
|
||||
$this->mainSession->lastUrl(),
|
||||
'SubsiteXHRController is reachable'
|
||||
);
|
||||
}
|
||||
}
|
68
tests/php/SubsiteAdminTest.php
Normal file
68
tests/php/SubsiteAdminTest.php
Normal file
@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Tests;
|
||||
|
||||
use SilverStripe\CMS\Controllers\CMSMain;
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\Dev\FunctionalTest;
|
||||
use SilverStripe\Subsites\Model\Subsite;
|
||||
|
||||
class SubsiteAdminTest extends FunctionalTest
|
||||
{
|
||||
protected static $fixture_file = 'SubsiteTest.yml';
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
Config::modify()->set(Subsite::class, 'write_hostmap', false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test generation of the view
|
||||
*/
|
||||
public function testBasicView()
|
||||
{
|
||||
$subsite1ID = $this->objFromFixture(Subsite::class, 'domaintest1')->ID;
|
||||
|
||||
$this->logInAs('admin');
|
||||
|
||||
// Confirm that this URL gets you the entire page, with the edit form loaded
|
||||
$response = $this->get(
|
||||
"admin/subsites/SilverStripe-Subsites-Model-Subsite/EditForm/field/"
|
||||
."SilverStripe-Subsites-Model-Subsite/item/$subsite1ID/edit"
|
||||
);
|
||||
$this->assertTrue(
|
||||
strpos($response->getBody() ?? '', 'id="Form_ItemEditForm_ID"') !== false,
|
||||
'Testing Form_ItemEditForm_ID 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
|
||||
* of whether he is in a subsite-specific group or not.
|
||||
*/
|
||||
public function testMainsiteAdminCanAccessAllSubsites()
|
||||
{
|
||||
$this->logInAs('admin');
|
||||
|
||||
$cmsMain = new CMSMain();
|
||||
foreach ($cmsMain->Subsites() as $subsite) {
|
||||
$ids[$subsite->ID] = true;
|
||||
}
|
||||
|
||||
$this->assertArrayHasKey(0, $ids, 'Main site accessible');
|
||||
$this->assertArrayHasKey($this->idFromFixture(Subsite::class, 'main'), $ids, 'Site with no groups inaccesible');
|
||||
$this->assertArrayHasKey(
|
||||
$this->idFromFixture(Subsite::class, 'subsite1'),
|
||||
$ids,
|
||||
'Subsite1 Template inaccessible'
|
||||
);
|
||||
$this->assertArrayHasKey(
|
||||
$this->idFromFixture(Subsite::class, 'subsite2'),
|
||||
$ids,
|
||||
'Subsite2 Template inaccessible'
|
||||
);
|
||||
}
|
||||
}
|
@ -1,37 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Tests;
|
||||
|
||||
use Page;
|
||||
use SilverStripe\CMS\Model\SiteTree;
|
||||
use SilverStripe\Control\Director;
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\ORM\DataObject;
|
||||
use SilverStripe\Security\Member;
|
||||
use SilverStripe\Subsites\Model\Subsite;
|
||||
use SilverStripe\Subsites\Model\SubsiteDomain;
|
||||
use SilverStripe\Subsites\State\SubsiteState;
|
||||
use UnexpectedValueException;
|
||||
|
||||
class SubsiteTest extends BaseSubsiteTest
|
||||
{
|
||||
public static $fixture_file = 'subsites/tests/SubsiteTest.yml';
|
||||
protected static $fixture_file = 'SubsiteTest.yml';
|
||||
|
||||
/**
|
||||
* Original value of {@see SubSite::$strict_subdomain_matching}
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $origStrictSubdomainMatching = null;
|
||||
protected $usesTransactions = false;
|
||||
|
||||
/**
|
||||
* Original value of $_REQUEST
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $origServer = array();
|
||||
protected $origServer = [];
|
||||
|
||||
public function setUp()
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
Config::inst()->update('Director', 'alternate_base_url', '/');
|
||||
$this->origStrictSubdomainMatching = Subsite::$strict_subdomain_matching;
|
||||
Config::modify()
|
||||
->set(Director::class, 'alternate_base_url', '/')
|
||||
->set(Subsite::class, 'strict_subdomain_matching', false)
|
||||
->set(Subsite::class, 'write_hostmap', false);
|
||||
|
||||
$this->origServer = $_SERVER;
|
||||
Subsite::$strict_subdomain_matching = false;
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
protected function tearDown(): void
|
||||
{
|
||||
$_SERVER = $this->origServer;
|
||||
Subsite::$strict_subdomain_matching = $this->origStrictSubdomainMatching;
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
@ -41,23 +50,22 @@ class SubsiteTest extends BaseSubsiteTest
|
||||
*/
|
||||
public function testSubsiteCreation()
|
||||
{
|
||||
Subsite::$write_hostmap = false;
|
||||
|
||||
// Create the instance
|
||||
$template = $this->objFromFixture('Subsite', 'main');
|
||||
$template = $this->objFromFixture(Subsite::class, 'main');
|
||||
|
||||
// Test that changeSubsite is working
|
||||
Subsite::changeSubsite($template->ID);
|
||||
$this->assertEquals($template->ID, Subsite::currentSubsiteID());
|
||||
$this->assertEquals($template->ID, SubsiteState::singleton()->getSubsiteId());
|
||||
$tmplStaff = $this->objFromFixture('Page', 'staff');
|
||||
$tmplHome = DataObject::get_one('Page', "\"URLSegment\" = 'home'");
|
||||
|
||||
// Publish all the pages in the template, testing that DataObject::get only returns pages from the chosen subsite
|
||||
$pages = DataObject::get("SiteTree");
|
||||
$totalPages = $pages->Count();
|
||||
// Publish all the pages in the template, testing that DataObject::get only returns pages
|
||||
// from the chosen subsite
|
||||
$pages = DataObject::get(SiteTree::class);
|
||||
$totalPages = $pages->count();
|
||||
foreach ($pages as $page) {
|
||||
$this->assertEquals($template->ID, $page->SubsiteID);
|
||||
$page->publish('Stage', 'Live');
|
||||
$page->copyVersionToStage('Stage', 'Live');
|
||||
}
|
||||
|
||||
// Create a new site
|
||||
@ -71,7 +79,9 @@ class SubsiteTest extends BaseSubsiteTest
|
||||
|
||||
$siteHome = DataObject::get_one('Page', "\"URLSegment\" = 'home'");
|
||||
$this->assertNotEquals($siteHome, false, 'Home Page for subsite not found');
|
||||
$this->assertEquals($subsite->ID, $siteHome->SubsiteID,
|
||||
$this->assertEquals(
|
||||
$subsite->ID,
|
||||
$siteHome->SubsiteID,
|
||||
'createInstance() copies existing pages retaining the same URLSegment'
|
||||
);
|
||||
|
||||
@ -84,28 +94,28 @@ class SubsiteTest extends BaseSubsiteTest
|
||||
public function testDomainLookup()
|
||||
{
|
||||
// Clear existing fixtures
|
||||
foreach (DataObject::get('Subsite') as $subsite) {
|
||||
foreach (DataObject::get(Subsite::class) as $subsite) {
|
||||
$subsite->delete();
|
||||
}
|
||||
foreach (DataObject::get('SubsiteDomain') as $domain) {
|
||||
foreach (DataObject::get(SubsiteDomain::class) as $domain) {
|
||||
$domain->delete();
|
||||
}
|
||||
|
||||
// Much more expressive than YML in this case
|
||||
$subsite1 = $this->createSubsiteWithDomains(array(
|
||||
$subsite1 = $this->createSubsiteWithDomains([
|
||||
'one.example.org' => true,
|
||||
'one.*' => false,
|
||||
));
|
||||
$subsite2 = $this->createSubsiteWithDomains(array(
|
||||
]);
|
||||
$subsite2 = $this->createSubsiteWithDomains([
|
||||
'two.mysite.com' => true,
|
||||
'*.mysite.com' => false,
|
||||
'subdomain.onmultiplesubsites.com' => false,
|
||||
));
|
||||
$subsite3 = $this->createSubsiteWithDomains(array(
|
||||
]);
|
||||
$subsite3 = $this->createSubsiteWithDomains([
|
||||
'three.*' => true, // wildcards in primary domain are not recommended
|
||||
'subdomain.unique.com' => false,
|
||||
'*.onmultiplesubsites.com' => false,
|
||||
));
|
||||
]);
|
||||
|
||||
$this->assertEquals(
|
||||
$subsite3->ID,
|
||||
@ -158,35 +168,47 @@ class SubsiteTest extends BaseSubsiteTest
|
||||
public function testStrictSubdomainMatching()
|
||||
{
|
||||
// Clear existing fixtures
|
||||
foreach (DataObject::get('Subsite') as $subsite) {
|
||||
foreach (DataObject::get(Subsite::class) as $subsite) {
|
||||
$subsite->delete();
|
||||
}
|
||||
foreach (DataObject::get('SubsiteDomain') as $domain) {
|
||||
foreach (DataObject::get(SubsiteDomain::class) as $domain) {
|
||||
$domain->delete();
|
||||
}
|
||||
|
||||
// Much more expressive than YML in this case
|
||||
$subsite1 = $this->createSubsiteWithDomains(array(
|
||||
$subsite1 = $this->createSubsiteWithDomains([
|
||||
'example.org' => true,
|
||||
'example.com' => false,
|
||||
'*.wildcard.com' => false,
|
||||
));
|
||||
$subsite2 = $this->createSubsiteWithDomains(array(
|
||||
]);
|
||||
$subsite2 = $this->createSubsiteWithDomains([
|
||||
'www.example.org' => true,
|
||||
'www.wildcard.com' => false,
|
||||
));
|
||||
]);
|
||||
|
||||
Subsite::$strict_subdomain_matching = false;
|
||||
Config::modify()->set(Subsite::class, 'strict_subdomain_matching', false);
|
||||
|
||||
$this->assertEquals(
|
||||
$subsite1->ID,
|
||||
Subsite::getSubsiteIDForDomain('example.org'),
|
||||
'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(
|
||||
$subsite1->ID,
|
||||
Subsite::getSubsiteIDForDomain('www.example.org'),
|
||||
'Matches without strict checking when using www prefix, still matching first domain regardless of www prefix (falling back to subsite primary key ordering)'
|
||||
'Matches without strict checking when using www prefix, '
|
||||
.'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(
|
||||
$subsite1->ID,
|
||||
@ -199,13 +221,18 @@ class SubsiteTest extends BaseSubsiteTest
|
||||
'Doesn\'t match www prefix without strict check, even if a wildcard subdomain is in place'
|
||||
);
|
||||
|
||||
Subsite::$strict_subdomain_matching = true;
|
||||
Config::modify()->set(Subsite::class, 'strict_subdomain_matching', true);
|
||||
|
||||
$this->assertEquals(
|
||||
$subsite1->ID,
|
||||
Subsite::getSubsiteIDForDomain('example.org'),
|
||||
'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(
|
||||
$subsite2->ID, // not 1
|
||||
Subsite::getSubsiteIDForDomain('www.example.org'),
|
||||
@ -230,16 +257,16 @@ class SubsiteTest extends BaseSubsiteTest
|
||||
|
||||
protected function createSubsiteWithDomains($domains)
|
||||
{
|
||||
$subsite = new Subsite(array(
|
||||
$subsite = new Subsite([
|
||||
'Title' => 'My Subsite'
|
||||
));
|
||||
]);
|
||||
$subsite->write();
|
||||
foreach ($domains as $domainStr => $isPrimary) {
|
||||
$domain = new SubsiteDomain(array(
|
||||
$domain = new SubsiteDomain([
|
||||
'Domain' => $domainStr,
|
||||
'IsPrimary' => $isPrimary,
|
||||
'SubsiteID' => $subsite->ID
|
||||
));
|
||||
]);
|
||||
$domain->write();
|
||||
}
|
||||
|
||||
@ -251,93 +278,101 @@ class SubsiteTest extends BaseSubsiteTest
|
||||
*/
|
||||
public function testDefaultDomain()
|
||||
{
|
||||
$this->assertEquals('one.example.org',
|
||||
$this->objFromFixture('Subsite', 'domaintest1')->domain());
|
||||
$this->assertEquals(
|
||||
'one.example.org',
|
||||
$this->objFromFixture(Subsite::class, 'domaintest1')->domain()
|
||||
);
|
||||
|
||||
$this->assertEquals('two.mysite.com',
|
||||
$this->objFromFixture('Subsite', 'domaintest2')->domain());
|
||||
$this->assertEquals(
|
||||
'two.mysite.com',
|
||||
$this->objFromFixture(Subsite::class, 'domaintest2')->domain()
|
||||
);
|
||||
|
||||
$_SERVER['HTTP_HOST'] = "www.example.org";
|
||||
$this->assertEquals('three.example.org',
|
||||
$this->objFromFixture('Subsite', 'domaintest3')->domain());
|
||||
$_SERVER['HTTP_HOST'] = 'www.example.org';
|
||||
$this->assertEquals(
|
||||
'three.example.org',
|
||||
$this->objFromFixture(Subsite::class, 'domaintest3')->domain()
|
||||
);
|
||||
|
||||
$_SERVER['HTTP_HOST'] = "mysite.example.org";
|
||||
$this->assertEquals('three.mysite.example.org',
|
||||
$this->objFromFixture('Subsite', 'domaintest3')->domain());
|
||||
$_SERVER['HTTP_HOST'] = 'mysite.example.org';
|
||||
$this->assertEquals(
|
||||
'three.mysite.example.org',
|
||||
$this->objFromFixture(Subsite::class, 'domaintest3')->domain()
|
||||
);
|
||||
|
||||
$this->assertEquals($_SERVER['HTTP_HOST'], singleton('Subsite')->PrimaryDomain);
|
||||
$this->assertEquals('http://'.$_SERVER['HTTP_HOST'].Director::baseURL(), singleton('Subsite')->absoluteBaseURL());
|
||||
$this->assertEquals($_SERVER['HTTP_HOST'], singleton(Subsite::class)->PrimaryDomain);
|
||||
$this->assertEquals(
|
||||
'http://' . $_SERVER['HTTP_HOST'] . Director::baseURL(),
|
||||
singleton(Subsite::class)->absoluteBaseURL()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
// domaintest2 has 'protocol'
|
||||
$subsite2 = $this->objFromFixture('Subsite', 'domaintest2');
|
||||
$domain2a = $this->objFromFixture('SubsiteDomain', 'dt2a');
|
||||
$domain2b = $this->objFromFixture('SubsiteDomain', 'dt2b');
|
||||
public function testDomainProtocol($class, $identifier, $currentIsSecure, $expected)
|
||||
{
|
||||
/** @var Subsite|SubsiteDomain $model */
|
||||
$model = $this->objFromFixture($class, $identifier);
|
||||
$protocol = $currentIsSecure ? 'https' : 'http';
|
||||
Config::modify()->set(Director::class, 'alternate_base_url', $protocol . '://www.mysite.com');
|
||||
$this->assertSame($expected, $model->absoluteBaseURL());
|
||||
}
|
||||
|
||||
// domaintest4 is 'https' (primary only)
|
||||
$subsite4 = $this->objFromFixture('Subsite', 'domaintest4');
|
||||
$domain4a = $this->objFromFixture('SubsiteDomain', 'dt4a');
|
||||
$domain4b = $this->objFromFixture('SubsiteDomain', 'dt4b'); // secondary domain is http only though
|
||||
|
||||
// domaintest5 is 'http'
|
||||
$subsite5 = $this->objFromFixture('Subsite', 'domaintest5');
|
||||
$domain5a = $this->objFromFixture('SubsiteDomain', 'dt5');
|
||||
|
||||
// Check protocol when current protocol is http://
|
||||
$_SERVER['HTTP_HOST'] = 'www.mysite.com';
|
||||
$_SERVER['HTTPS'] = '';
|
||||
|
||||
$this->assertEquals('http://two.mysite.com/', $subsite2->absoluteBaseURL());
|
||||
$this->assertEquals('http://two.mysite.com/', $domain2a->absoluteBaseURL());
|
||||
$this->assertEquals('http://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());
|
||||
|
||||
// Check protocol when current protocol is https://
|
||||
$_SERVER['HTTP_HOST'] = 'www.mysite.com';
|
||||
$_SERVER['HTTPS'] = 'ON';
|
||||
|
||||
$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 domainProtocolProvider()
|
||||
{
|
||||
return [
|
||||
[Subsite::class, 'domaintest2', false, 'http://two.mysite.com/'],
|
||||
[SubsiteDomain::class, 'dt2a', false, 'http://two.mysite.com/'],
|
||||
[SubsiteDomain::class, 'dt2b', false, 'http://subsite.mysite.com/'],
|
||||
[Subsite::class, 'domaintest4', false, 'https://www.primary.com/'],
|
||||
[SubsiteDomain::class, 'dt4a', false, 'https://www.primary.com/'],
|
||||
[SubsiteDomain::class, 'dt4b', false, 'http://www.secondary.com/'],
|
||||
[Subsite::class, 'domaintest5', false, 'http://www.tertiary.com/'],
|
||||
[SubsiteDomain::class, 'dt5', false, 'http://www.tertiary.com/'],
|
||||
[Subsite::class, 'domaintest2', true, 'https://two.mysite.com/'],
|
||||
[SubsiteDomain::class, 'dt2a', true, 'https://two.mysite.com/'],
|
||||
[SubsiteDomain::class, 'dt2b', true, 'https://subsite.mysite.com/'],
|
||||
[Subsite::class, 'domaintest4', true, 'https://www.primary.com/'],
|
||||
[SubsiteDomain::class, 'dt4a', true, 'https://www.primary.com/'],
|
||||
[SubsiteDomain::class, 'dt4b', true, 'http://www.secondary.com/'],
|
||||
[Subsite::class, 'domaintest5', true, 'http://www.tertiary.com/'],
|
||||
[SubsiteDomain::class, 'dt5', true, 'http://www.tertiary.com/'],
|
||||
];
|
||||
}
|
||||
|
||||
public function testAllSites()
|
||||
{
|
||||
$subsites = Subsite::all_sites();
|
||||
$this->assertDOSEquals(array(
|
||||
array('Title' =>'Main site'),
|
||||
array('Title' =>'Template'),
|
||||
array('Title' =>'Subsite1 Template'),
|
||||
array('Title' =>'Subsite2 Template'),
|
||||
array('Title' =>'Test 1'),
|
||||
array('Title' =>'Test 2'),
|
||||
array('Title' =>'Test 3'),
|
||||
array('Title' => 'Test Non-SSL'),
|
||||
array('Title' => 'Test SSL')
|
||||
), $subsites, 'Lists all subsites');
|
||||
$this->assertListEquals([
|
||||
['Title' => 'Main site'],
|
||||
['Title' => 'Template'],
|
||||
['Title' => 'Subsite1 Template'],
|
||||
['Title' => 'Subsite2 Template'],
|
||||
['Title' => 'Test 1'],
|
||||
['Title' => 'Test 2'],
|
||||
['Title' => 'Test 3'],
|
||||
['Title' => 'Test Non-SSL'],
|
||||
['Title' => 'Test SSL'],
|
||||
['Title' => 'Test Vagrant VM on port 8080'],
|
||||
['Title' => 'Locale subsite'],
|
||||
], $subsites, 'Lists all subsites');
|
||||
}
|
||||
|
||||
public function testAllAccessibleSites()
|
||||
{
|
||||
$member = $this->objFromFixture('Member', 'subsite1member');
|
||||
$member = $this->objFromFixture(Member::class, 'subsite1member');
|
||||
|
||||
$subsites = Subsite::all_accessible_sites(true, 'Main site', $member);
|
||||
$this->assertDOSEquals(array(
|
||||
array('Title' =>'Subsite1 Template')
|
||||
), $subsites, 'Lists member-accessible sites.');
|
||||
$this->assertListEquals([
|
||||
['Title' => 'Subsite1 Template']
|
||||
], $subsites, 'Lists member-accessible sites.');
|
||||
}
|
||||
|
||||
/**
|
||||
@ -345,17 +380,26 @@ class SubsiteTest extends BaseSubsiteTest
|
||||
*/
|
||||
public function testAccessibleSites()
|
||||
{
|
||||
$member1Sites = Subsite::accessible_sites("CMS_ACCESS_CMSMain", false, null,
|
||||
$this->objFromFixture('Member', 'subsite1member'));
|
||||
$member1SiteTitles = $member1Sites->column("Title");
|
||||
$member1Sites = Subsite::accessible_sites(
|
||||
'CMS_ACCESS_CMSMain',
|
||||
false,
|
||||
null,
|
||||
$this->objFromFixture(Member::class, 'subsite1member')
|
||||
);
|
||||
$member1SiteTitles = $member1Sites->column('Title');
|
||||
sort($member1SiteTitles);
|
||||
$this->assertEquals('Subsite1 Template', $member1SiteTitles[0], 'Member can get to a subsite via a group');
|
||||
|
||||
$adminSites = Subsite::accessible_sites("CMS_ACCESS_CMSMain", false, null,
|
||||
$this->objFromFixture('Member', 'admin'));
|
||||
$adminSiteTitles = $adminSites->column("Title");
|
||||
$adminSites = Subsite::accessible_sites(
|
||||
'CMS_ACCESS_CMSMain',
|
||||
false,
|
||||
null,
|
||||
$this->objFromFixture(Member::class, 'admin')
|
||||
);
|
||||
$adminSiteTitles = $adminSites->column('Title');
|
||||
sort($adminSiteTitles);
|
||||
$this->assertEquals(array(
|
||||
$this->assertEquals([
|
||||
'Locale subsite',
|
||||
'Subsite1 Template',
|
||||
'Subsite2 Template',
|
||||
'Template',
|
||||
@ -363,55 +407,58 @@ class SubsiteTest extends BaseSubsiteTest
|
||||
'Test 2',
|
||||
'Test 3',
|
||||
'Test Non-SSL',
|
||||
'Test SSL'
|
||||
), array_values($adminSiteTitles));
|
||||
'Test SSL',
|
||||
'Test Vagrant VM on port 8080'
|
||||
], array_values($adminSiteTitles ?? []));
|
||||
|
||||
$member2Sites = Subsite::accessible_sites(
|
||||
"CMS_ACCESS_CMSMain", false, null,
|
||||
$this->objFromFixture('Member', 'subsite1member2')
|
||||
'CMS_ACCESS_CMSMain',
|
||||
false,
|
||||
null,
|
||||
$this->objFromFixture(Member::class, 'subsite1member2')
|
||||
);
|
||||
$member2SiteTitles = $member2Sites->column("Title");
|
||||
$member2SiteTitles = $member2Sites->column('Title');
|
||||
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()
|
||||
{
|
||||
$admin = $this->objFromFixture('Member', 'admin');
|
||||
$subsite1member = $this->objFromFixture('Member', 'subsite1member');
|
||||
$subsite1admin = $this->objFromFixture('Member', 'subsite1admin');
|
||||
$allsubsitesauthor = $this->objFromFixture('Member', 'allsubsitesauthor');
|
||||
$admin = $this->objFromFixture(Member::class, 'admin');
|
||||
$subsite1member = $this->objFromFixture(Member::class, 'subsite1member');
|
||||
$subsite1admin = $this->objFromFixture(Member::class, 'subsite1admin');
|
||||
$allsubsitesauthor = $this->objFromFixture(Member::class, 'allsubsitesauthor');
|
||||
|
||||
$this->assertTrue(
|
||||
Subsite::hasMainSitePermission($admin),
|
||||
'Default permissions granted for super-admin'
|
||||
);
|
||||
$this->assertTrue(
|
||||
Subsite::hasMainSitePermission($admin, array("ADMIN")),
|
||||
Subsite::hasMainSitePermission($admin, ['ADMIN']),
|
||||
'ADMIN permissions granted for super-admin'
|
||||
);
|
||||
$this->assertFalse(
|
||||
Subsite::hasMainSitePermission($subsite1admin, array("ADMIN")),
|
||||
Subsite::hasMainSitePermission($subsite1admin, ['ADMIN']),
|
||||
'ADMIN permissions (on main site) denied for subsite1 admin'
|
||||
);
|
||||
$this->assertFalse(
|
||||
Subsite::hasMainSitePermission($subsite1admin, array("CMS_ACCESS_CMSMain")),
|
||||
Subsite::hasMainSitePermission($subsite1admin, ['CMS_ACCESS_CMSMain']),
|
||||
'CMS_ACCESS_CMSMain (on main site) denied for subsite1 admin'
|
||||
);
|
||||
$this->assertFalse(
|
||||
Subsite::hasMainSitePermission($allsubsitesauthor, array("ADMIN")),
|
||||
Subsite::hasMainSitePermission($allsubsitesauthor, ['ADMIN']),
|
||||
'ADMIN permissions (on main site) denied for CMS author with edit rights on all subsites'
|
||||
);
|
||||
$this->assertTrue(
|
||||
Subsite::hasMainSitePermission($allsubsitesauthor, array("CMS_ACCESS_CMSMain")),
|
||||
Subsite::hasMainSitePermission($allsubsitesauthor, ['CMS_ACCESS_CMSMain']),
|
||||
'CMS_ACCESS_CMSMain (on main site) granted for CMS author with edit rights on all subsites'
|
||||
);
|
||||
$this->assertFalse(
|
||||
Subsite::hasMainSitePermission($subsite1member, array("ADMIN")),
|
||||
Subsite::hasMainSitePermission($subsite1member, ['ADMIN']),
|
||||
'ADMIN (on main site) denied for subsite1 subsite1 cms author'
|
||||
);
|
||||
$this->assertFalse(
|
||||
Subsite::hasMainSitePermission($subsite1member, array("CMS_ACCESS_CMSMain")),
|
||||
Subsite::hasMainSitePermission($subsite1member, ['CMS_ACCESS_CMSMain']),
|
||||
'CMS_ACCESS_CMSMain (on main site) denied for subsite1 cms author'
|
||||
);
|
||||
}
|
||||
@ -419,12 +466,12 @@ class SubsiteTest extends BaseSubsiteTest
|
||||
public function testDuplicateSubsite()
|
||||
{
|
||||
// get subsite1 & create page
|
||||
$subsite1 = $this->objFromFixture('Subsite', 'domaintest1');
|
||||
$subsite1 = $this->objFromFixture(Subsite::class, 'domaintest1');
|
||||
$subsite1->activate();
|
||||
$page1 = new Page();
|
||||
$page1->Title = 'MyAwesomePage';
|
||||
$page1->write();
|
||||
$page1->doPublish();
|
||||
$page1->publishRecursive();
|
||||
$this->assertEquals($page1->SubsiteID, $subsite1->ID);
|
||||
|
||||
// duplicate
|
||||
@ -434,7 +481,7 @@ class SubsiteTest extends BaseSubsiteTest
|
||||
$page2 = DataObject::get_one('Page', "\"Title\" = 'MyAwesomePage'");
|
||||
$page2->Title = 'MyNewAwesomePage';
|
||||
$page2->write();
|
||||
$page2->doPublish();
|
||||
$page2->publishRecursive();
|
||||
|
||||
// check change & check change has not affected subiste1
|
||||
$subsite1->activate();
|
267
tests/php/SubsiteTest.yml
Normal file
267
tests/php/SubsiteTest.yml
Normal file
@ -0,0 +1,267 @@
|
||||
SilverStripe\Subsites\Model\Subsite:
|
||||
main:
|
||||
Title: Template
|
||||
subsite1:
|
||||
Title: Subsite1 Template
|
||||
Theme: subsiteTheme
|
||||
subsite2:
|
||||
Title: Subsite2 Template
|
||||
domaintest1:
|
||||
Title: Test 1
|
||||
domaintest2:
|
||||
Title: Test 2
|
||||
domaintest3:
|
||||
Title: Test 3
|
||||
domaintest4:
|
||||
Title: 'Test SSL'
|
||||
domaintest5:
|
||||
Title: 'Test Non-SSL'
|
||||
domaintestVagrant:
|
||||
Title: 'Test Vagrant VM on port 8080'
|
||||
subsitelocale:
|
||||
Title: 'Locale subsite'
|
||||
Language: 'nl_NL'
|
||||
|
||||
SilverStripe\Subsites\Model\SubsiteDomain:
|
||||
subsite1:
|
||||
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsite1
|
||||
Domain: subsite1.*
|
||||
Protocol: automatic
|
||||
subsite2:
|
||||
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsite2
|
||||
Domain: subsite2.*
|
||||
Protocol: automatic
|
||||
subsitelocale:
|
||||
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsitelocale
|
||||
Domain: subsitelocale.*
|
||||
Protocol: automatic
|
||||
IsPrimary: 1
|
||||
dt1a:
|
||||
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.domaintest1
|
||||
Domain: one.example.org
|
||||
Protocol: automatic
|
||||
IsPrimary: 1
|
||||
dt1b:
|
||||
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.domaintest1
|
||||
Domain: one.*
|
||||
Protocol: automatic
|
||||
dt2a:
|
||||
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.domaintest2
|
||||
Domain: two.mysite.com
|
||||
Protocol: automatic
|
||||
IsPrimary: 1
|
||||
dt2b:
|
||||
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.domaintest2
|
||||
Domain: '*.mysite.com'
|
||||
Protocol: automatic
|
||||
dt3:
|
||||
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.domaintest3
|
||||
Domain: three.*
|
||||
Protocol: automatic
|
||||
IsPrimary: 1
|
||||
dt4a:
|
||||
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.domaintest4
|
||||
Domain: www.primary.com
|
||||
Protocol: https
|
||||
dt4b:
|
||||
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.domaintest4
|
||||
Domain: www.secondary.com
|
||||
Protocol: http
|
||||
dt5:
|
||||
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.domaintest5
|
||||
Domain: www.tertiary.com
|
||||
Protocol: http
|
||||
IsPrimary: 1
|
||||
dtVagrant:
|
||||
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.domaintestVagrant
|
||||
Domain: localhost:8080
|
||||
Protocol: http
|
||||
IsPrimary: 1
|
||||
Page:
|
||||
mainSubsitePage:
|
||||
Title: 'MainSubsitePage'
|
||||
SubsiteID: 0
|
||||
URLSegment: mainsubsitepage
|
||||
home:
|
||||
Title: 'Home'
|
||||
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.main
|
||||
URLSegment: home
|
||||
about:
|
||||
Title: 'About'
|
||||
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.main
|
||||
URLSegment: about
|
||||
linky:
|
||||
Title: 'Linky'
|
||||
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.main
|
||||
URLSegment: linky
|
||||
staff:
|
||||
Title: 'Staff'
|
||||
ParentID: =>Page.about
|
||||
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.main
|
||||
URLSegment: staff
|
||||
contact:
|
||||
Title: 'Contact Us'
|
||||
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.main
|
||||
URLSegment: contact-us
|
||||
importantpage:
|
||||
Title: 'Important Page'
|
||||
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.main
|
||||
URLSegment: important-page
|
||||
subsite1_home:
|
||||
Title: 'Home (Subsite 1)'
|
||||
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsite1
|
||||
URLSegment: home
|
||||
subsite1_contactus:
|
||||
Title: 'Contact Us (Subsite 1)'
|
||||
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsite1
|
||||
URLSegment: contact-us
|
||||
subsite1_staff:
|
||||
Title: 'Staff'
|
||||
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsite1
|
||||
URLSegment: staff
|
||||
subsite2_home:
|
||||
Title: 'Home (Subsite 2)'
|
||||
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsite2
|
||||
URLSegment: home
|
||||
subsite2_contactus:
|
||||
Title: 'Contact Us (Subsite 2)'
|
||||
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsite2
|
||||
URLSegment: contact-us
|
||||
subsite_locale_about:
|
||||
Title: 'About Locale'
|
||||
SubsiteID: =>SilverStripe\Subsites\Model\Subsite.subsitelocale
|
||||
URLSegment: about
|
||||
|
||||
SilverStripe\Security\PermissionRoleCode:
|
||||
roleCode1:
|
||||
Code: CMS_ACCESS_CMSMain
|
||||
SilverStripe\Security\PermissionRole:
|
||||
role1:
|
||||
Title: role1
|
||||
Codes: =>SilverStripe\Security\PermissionRoleCode.roleCode1
|
||||
SilverStripe\Security\Group:
|
||||
admin:
|
||||
Title: Admin
|
||||
Code: admin
|
||||
AccessAllSubsites: 1
|
||||
editor:
|
||||
Title: Editor
|
||||
Code: editor
|
||||
AccessAllSubsites: 1
|
||||
subsite1_group:
|
||||
Title: subsite1_group
|
||||
Code: subsite1_group
|
||||
AccessAllSubsites: 0
|
||||
Subsites: =>SilverStripe\Subsites\Model\Subsite.subsite1
|
||||
subsite2_group:
|
||||
Title: subsite2_group
|
||||
Code: subsite2_group
|
||||
AccessAllSubsites: 0
|
||||
Subsites: =>SilverStripe\Subsites\Model\Subsite.subsite2
|
||||
subsite1admins:
|
||||
Title: subsite1admins
|
||||
Code: subsite1admins
|
||||
AccessAllSubsites: 0
|
||||
Subsites: =>SilverStripe\Subsites\Model\Subsite.subsite1
|
||||
allsubsitesauthors:
|
||||
Title: allsubsitesauthors
|
||||
Code: allsubsitesauthors
|
||||
AccessAllSubsites: 1
|
||||
subsite1_group_via_role:
|
||||
Title: subsite1_group_via_role
|
||||
Code: subsite1_group_via_role
|
||||
AccessAllSubsites: 1
|
||||
Roles: =>SilverStripe\Security\PermissionRole.role1
|
||||
filetest:
|
||||
Title: filetest
|
||||
Code: filetest
|
||||
AccessAllSubsites: 1
|
||||
SilverStripe\Security\Permission:
|
||||
admin:
|
||||
Code: ADMIN
|
||||
GroupID: =>SilverStripe\Security\Group.admin
|
||||
editor1:
|
||||
Code: CMS_ACCESS_CMSMain
|
||||
GroupID: =>SilverStripe\Security\Group.editor
|
||||
editor2:
|
||||
Code: SITETREE_VIEW_ALL
|
||||
GroupID: =>SilverStripe\Security\Group.editor
|
||||
editor3:
|
||||
Code: VIEW_DRAFT_CONTENT
|
||||
GroupID: =>SilverStripe\Security\Group.editor
|
||||
accesscmsmain1:
|
||||
Code: CMS_ACCESS_CMSMain
|
||||
GroupID: =>SilverStripe\Security\Group.subsite1_group
|
||||
accesscmsmain2:
|
||||
Code: CMS_ACCESS_CMSMain
|
||||
GroupID: =>SilverStripe\Security\Group.subsite2_group
|
||||
accesscmsmain3:
|
||||
Code: CMS_ACCESS_CMSMain
|
||||
GroupID: =>SilverStripe\Security\Group.subsite1admins
|
||||
accesscmsmain4:
|
||||
Code: CMS_ACCESS_CMSMain
|
||||
GroupID: =>SilverStripe\Security\Group.allsubsitesauthors
|
||||
securityaccess1:
|
||||
Code: CMS_ACCESS_SecurityAdmin
|
||||
GroupID: =>SilverStripe\Security\Group.subsite1_group
|
||||
securityaccess2:
|
||||
Code: CMS_ACCESS_SecurityAdmin
|
||||
GroupID: =>SilverStripe\Security\Group.subsite2_group
|
||||
adminsubsite1:
|
||||
Code: ADMIN
|
||||
GroupID: =>SilverStripe\Security\Group.subsite1admins
|
||||
filetest:
|
||||
Code: CMS_ACCESS_CMSMain
|
||||
GroupID: =>SilverStripe\Security\Group.filetest
|
||||
|
||||
SilverStripe\Security\Member:
|
||||
admin:
|
||||
FirstName: Admin
|
||||
Surname: User
|
||||
Email: admin@test.com
|
||||
Password: rangi
|
||||
Groups: =>SilverStripe\Security\Group.admin
|
||||
editor:
|
||||
FirstName: Editor
|
||||
Surname: User
|
||||
Email: editor@test.com
|
||||
Password: rangi
|
||||
Groups: =>SilverStripe\Security\Group.editor
|
||||
subsite1member:
|
||||
Email: subsite1member@test.com
|
||||
Groups: =>SilverStripe\Security\Group.subsite1_group
|
||||
subsite2member:
|
||||
Email: subsite2member@test.com
|
||||
Groups: =>SilverStripe\Security\Group.subsite2_group
|
||||
subsite1admin:
|
||||
Email: subsite1admin@test.com
|
||||
Groups: =>SilverStripe\Security\Group.subsite1admins
|
||||
allsubsitesauthor:
|
||||
Email: allsubsitesauthor@test.com
|
||||
Groups: =>SilverStripe\Security\Group.allsubsitesauthors
|
||||
subsite1member2:
|
||||
Email: subsite1member2@test.com
|
||||
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:
|
||||
config:
|
||||
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
|
47
tests/php/SubsiteXHRControllerTest.php
Normal file
47
tests/php/SubsiteXHRControllerTest.php
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Tests;
|
||||
|
||||
use SilverStripe\Dev\FunctionalTest;
|
||||
|
||||
class SubsiteXHRControllerTest extends FunctionalTest
|
||||
{
|
||||
protected static $fixture_file = 'SubsiteTest.yml';
|
||||
|
||||
public function testCanView()
|
||||
{
|
||||
// Test unauthenticated access
|
||||
$this->logOut();
|
||||
|
||||
$result = $this->get('admin/subsite_xhr', null, [
|
||||
'X-Pjax' => 'SubsiteList',
|
||||
'X-Requested-With' => 'XMLHttpRequest'
|
||||
]);
|
||||
$this->assertEquals(403, $result->getStatusCode());
|
||||
|
||||
// Login with NO permissions
|
||||
$this->logInWithPermission('NOT_CMS_PERMISSION');
|
||||
$result = $this->get('admin/subsite_xhr', null, [
|
||||
'X-Pjax' => 'SubsiteList',
|
||||
'X-Requested-With' => 'XMLHttpRequest'
|
||||
]);
|
||||
$this->assertEquals(403, $result->getStatusCode());
|
||||
|
||||
// Test cms user
|
||||
$this->logInWithPermission('CMS_ACCESS_CMSMain');
|
||||
$result = $this->get('admin/subsite_xhr', null, [
|
||||
'X-Pjax' => 'SubsiteList',
|
||||
'X-Requested-With' => 'XMLHttpRequest'
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $result->getStatusCode());
|
||||
// SilverStripe 4.0-4.2: text/json. >=4.3: application/json
|
||||
$this->assertStringContainsString('json', $result->getHeader('Content-Type'));
|
||||
|
||||
$body = $result->getBody();
|
||||
$this->assertStringContainsString('Main site', $body);
|
||||
$this->assertStringContainsString('Test 1', $body);
|
||||
$this->assertStringContainsString('Test 2', $body);
|
||||
$this->assertStringContainsString('Test 3', $body);
|
||||
}
|
||||
}
|
@ -1,52 +1,63 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Tests;
|
||||
|
||||
use Page;
|
||||
use SilverStripe\Assets\File;
|
||||
use SilverStripe\Assets\Filesystem;
|
||||
use SilverStripe\Assets\Tests\Storage\AssetStoreTest\TestAssetStore;
|
||||
use SilverStripe\CMS\Model\SiteTree;
|
||||
use SilverStripe\Control\Director;
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\ORM\DB;
|
||||
use SilverStripe\Subsites\Model\Subsite;
|
||||
use SilverStripe\Subsites\Pages\SubsitesVirtualPage;
|
||||
use SilverStripe\Versioned\Versioned;
|
||||
|
||||
class SubsitesVirtualPageTest extends BaseSubsiteTest
|
||||
{
|
||||
public static $fixture_file = array(
|
||||
'subsites/tests/SubsiteTest.yml',
|
||||
'subsites/tests/SubsitesVirtualPageTest.yml',
|
||||
);
|
||||
protected static $fixture_file = [
|
||||
'SubsiteTest.yml',
|
||||
'SubsitesVirtualPageTest.yml',
|
||||
];
|
||||
|
||||
protected $illegalExtensions = array(
|
||||
'SiteTree' => array('Translatable')
|
||||
);
|
||||
|
||||
public function setUp()
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->logInWithPermission('ADMIN');
|
||||
|
||||
$fh = fopen(Director::baseFolder() . '/assets/testscript-test-file.pdf', "w");
|
||||
fwrite($fh, str_repeat('x', 1000000));
|
||||
fclose($fh);
|
||||
Config::modify()->set(Subsite::class, 'write_hostmap', false);
|
||||
|
||||
// Set backend root to /DataDifferencerTest
|
||||
TestAssetStore::activate('SubsitesVirtualPageTest');
|
||||
|
||||
// Create a test files for each of the fixture references
|
||||
$file = $this->objFromFixture(File::class, 'file1');
|
||||
$page = $this->objFromFixture(SiteTree::class, 'page1');
|
||||
$fromPath = __DIR__ . '/testscript-test-file.pdf';
|
||||
$destPath = TestAssetStore::getLocalPath($file);
|
||||
Filesystem::makeFolder(dirname($destPath ?? ''));
|
||||
copy($fromPath ?? '', $destPath ?? '');
|
||||
|
||||
// Hack in site link tracking after the fact
|
||||
$page->Content = '<p><img src="' . $file->getURL() . '" data-fileid="' . $file->ID . '" /></p>';
|
||||
$page->write();
|
||||
}
|
||||
|
||||
public function tearDown()
|
||||
protected function tearDown(): void
|
||||
{
|
||||
TestAssetStore::reset();
|
||||
parent::tearDown();
|
||||
$testFiles = array(
|
||||
'/assets/testscript-test-file.pdf',
|
||||
'/assets/renamed-test-file.pdf',
|
||||
'/assets/renamed-test-file-second-time.pdf',
|
||||
);
|
||||
foreach ($testFiles as $file) {
|
||||
if (file_exists(Director::baseFolder().$file)) {
|
||||
unlink(Director::baseFolder().$file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to bring main:linky to subsite2:linky
|
||||
public function testVirtualPageFromAnotherSubsite()
|
||||
{
|
||||
Subsite::$write_hostmap = false;
|
||||
|
||||
$subsite = $this->objFromFixture('Subsite', 'subsite2');
|
||||
$subsite = $this->objFromFixture(Subsite::class, 'subsite2');
|
||||
|
||||
Subsite::changeSubsite($subsite->ID);
|
||||
Subsite::$disable_subsite_filter = false;
|
||||
|
||||
$linky = $this->objFromFixture('Page', 'linky');
|
||||
$linky = $this->objFromFixture(Page::class, 'linky');
|
||||
|
||||
$svp = new SubsitesVirtualPage();
|
||||
$svp->CopyContentFromID = $linky->ID;
|
||||
@ -61,48 +72,43 @@ class SubsitesVirtualPageTest extends BaseSubsiteTest
|
||||
|
||||
public function testFileLinkRewritingOnVirtualPages()
|
||||
{
|
||||
$this->markTestSkipped('File handling needs update');
|
||||
// File setup
|
||||
$this->logInWithPermission('ADMIN');
|
||||
touch(Director::baseFolder() . '/assets/testscript-test-file.pdf');
|
||||
|
||||
// Publish the source page
|
||||
$page = $this->objFromFixture('SiteTree', 'page1');
|
||||
$this->assertTrue($page->doPublish());
|
||||
$page = $this->objFromFixture(SiteTree::class, 'page1');
|
||||
$this->assertTrue($page->publishSingle());
|
||||
|
||||
// Create a virtual page from it, and publish that
|
||||
$svp = new SubsitesVirtualPage();
|
||||
$svp->CopyContentFromID = $page->ID;
|
||||
$svp->write();
|
||||
$svp->doPublish();
|
||||
$svp->publishSingle();
|
||||
|
||||
// Rename the file
|
||||
$file = $this->objFromFixture('File', 'file1');
|
||||
$file = $this->objFromFixture(File::class, 'file1');
|
||||
$file->Name = 'renamed-test-file.pdf';
|
||||
$file->write();
|
||||
|
||||
// Verify that the draft and publish virtual pages both have the corrected link
|
||||
$this->assertContains('<img src="assets/renamed-test-file.pdf"',
|
||||
DB::query("SELECT \"Content\" FROM \"SiteTree\" WHERE \"ID\" = $svp->ID")->value());
|
||||
$this->assertContains('<img src="assets/renamed-test-file.pdf"',
|
||||
DB::query("SELECT \"Content\" FROM \"SiteTree_Live\" WHERE \"ID\" = $svp->ID")->value());
|
||||
|
||||
// File teardown
|
||||
$testFiles = array(
|
||||
'/assets/testscript-test-file.pdf',
|
||||
'/assets/renamed-test-file.pdf',
|
||||
$this->assertContains(
|
||||
'<img src="/assets/SubsitesVirtualPageTest/464dedb70a/renamed-test-file.pdf"',
|
||||
DB::query("SELECT \"Content\" FROM \"SiteTree\" WHERE \"ID\" = $svp->ID")->value()
|
||||
);
|
||||
$this->assertContains(
|
||||
'<img src="/assets/SubsitesVirtualPageTest/464dedb70a/renamed-test-file.pdf"',
|
||||
DB::query("SELECT \"Content\" FROM \"SiteTree_Live\" WHERE \"ID\" = $svp->ID")->value()
|
||||
);
|
||||
foreach ($testFiles as $file) {
|
||||
if (file_exists(Director::baseFolder().$file)) {
|
||||
unlink(Director::baseFolder().$file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function testSubsiteVirtualPagesArentInappropriatelyPublished()
|
||||
{
|
||||
$this->markTestSkipped('Needs some update or refactoring');
|
||||
// Fixture
|
||||
$p = new Page();
|
||||
$p->Content = "test content";
|
||||
$p->Content = 'test content';
|
||||
$p->write();
|
||||
$vp = new SubsitesVirtualPage();
|
||||
$vp->CopyContentFromID = $p->ID;
|
||||
@ -112,7 +118,7 @@ class SubsitesVirtualPageTest extends BaseSubsiteTest
|
||||
$this->assertTrue($vp->IsAddedToStage);
|
||||
|
||||
// VP is still orange after we publish
|
||||
$p->doPublish();
|
||||
$p->publishSingle();
|
||||
$this->fixVersionNumberCache($vp);
|
||||
$this->assertTrue($vp->IsAddedToStage);
|
||||
|
||||
@ -123,21 +129,21 @@ class SubsitesVirtualPageTest extends BaseSubsiteTest
|
||||
$this->assertTrue($vp2->IsAddedToStage);
|
||||
|
||||
// Also remains orange after a republish
|
||||
$p->Content = "new content";
|
||||
$p->Content = 'new content';
|
||||
$p->write();
|
||||
$p->doPublish();
|
||||
$p->publishSingle();
|
||||
$this->fixVersionNumberCache($vp2);
|
||||
$this->assertTrue($vp2->IsAddedToStage);
|
||||
|
||||
// VP is now published
|
||||
$vp->doPublish();
|
||||
$vp->publishSingle();
|
||||
|
||||
$this->fixVersionNumberCache($vp);
|
||||
$this->assertTrue($vp->ExistsOnLive);
|
||||
$this->assertFalse($vp->IsModifiedOnStage);
|
||||
|
||||
// P edited, VP and P both go green
|
||||
$p->Content = "third content";
|
||||
$p->Content = 'third content';
|
||||
$p->write();
|
||||
|
||||
$this->fixVersionNumberCache($vp, $p);
|
||||
@ -145,7 +151,7 @@ class SubsitesVirtualPageTest extends BaseSubsiteTest
|
||||
$this->assertTrue($vp->IsModifiedOnStage);
|
||||
|
||||
// Publish, VP goes black
|
||||
$p->doPublish();
|
||||
$p->publishSingle();
|
||||
$this->fixVersionNumberCache($vp);
|
||||
$this->assertTrue($vp->ExistsOnLive);
|
||||
$this->assertFalse($vp->IsModifiedOnStage);
|
||||
@ -159,16 +165,18 @@ class SubsitesVirtualPageTest extends BaseSubsiteTest
|
||||
*/
|
||||
public function testPublishedSubsiteVirtualPagesUpdateIfTargetPageUpdates()
|
||||
{
|
||||
$this->markTestSkipped('Needs some update or refactoring');
|
||||
|
||||
// create page
|
||||
$p = new Page();
|
||||
$p->Content = 'Content';
|
||||
$p->Title = 'Title';
|
||||
$p->writeToStage('Stage');
|
||||
$p->publish('Stage', 'Live');
|
||||
$p->copyVersionToStage('Stage', 'Live');
|
||||
$this->assertTrue($p->ExistsOnLive);
|
||||
|
||||
// change to subsite
|
||||
$subsite = $this->objFromFixture('Subsite', 'subsite2');
|
||||
$subsite = $this->objFromFixture(Subsite::class, 'subsite2');
|
||||
Subsite::changeSubsite($subsite->ID);
|
||||
Subsite::$disable_subsite_filter = false;
|
||||
|
||||
@ -177,7 +185,7 @@ class SubsitesVirtualPageTest extends BaseSubsiteTest
|
||||
$svp->CopyContentFromID = $p->ID;
|
||||
$svp->write();
|
||||
$svp->writeToStage('Stage');
|
||||
$svp->publish('Stage', 'Live');
|
||||
$svp->copyVersionToStage('Stage', 'Live');
|
||||
$this->assertEquals($svp->SubsiteID, $subsite->ID);
|
||||
$this->assertTrue($svp->ExistsOnLive);
|
||||
|
||||
@ -188,7 +196,7 @@ class SubsitesVirtualPageTest extends BaseSubsiteTest
|
||||
$p->Title = 'New Title';
|
||||
// "save & publish"
|
||||
$p->writeToStage('Stage');
|
||||
$p->publish('Stage', 'Live');
|
||||
$p->copyVersionToStage('Stage', 'Live');
|
||||
$this->assertNotEquals($p->SubsiteID, $subsite->ID);
|
||||
|
||||
// reload SVP from database
|
||||
@ -201,41 +209,43 @@ class SubsitesVirtualPageTest extends BaseSubsiteTest
|
||||
|
||||
public function testUnpublishingParentPageUnpublishesSubsiteVirtualPages()
|
||||
{
|
||||
Config::inst()->update('StaticPublisher', 'disable_realtime', true);
|
||||
$this->markTestIncomplete('@todo fix this test');
|
||||
|
||||
Config::modify()->set('StaticPublisher', 'disable_realtime', true);
|
||||
|
||||
// Go to main site, get parent page
|
||||
$subsite = $this->objFromFixture('Subsite', 'main');
|
||||
$subsite = $this->objFromFixture(Subsite::class, 'main');
|
||||
Subsite::changeSubsite($subsite->ID);
|
||||
$page = $this->objFromFixture('Page', 'importantpage');
|
||||
|
||||
// Create two SVPs on other subsites
|
||||
$subsite = $this->objFromFixture('Subsite', 'subsite1');
|
||||
$subsite = $this->objFromFixture(Subsite::class, 'subsite1');
|
||||
Subsite::changeSubsite($subsite->ID);
|
||||
$vp1 = new SubsitesVirtualPage();
|
||||
$vp1->CopyContentFromID = $page->ID;
|
||||
$vp1->write();
|
||||
$vp1->doPublish();
|
||||
$vp1->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
||||
|
||||
$subsite = $this->objFromFixture('Subsite', 'subsite2');
|
||||
$subsite = $this->objFromFixture(Subsite::class, 'subsite2');
|
||||
Subsite::changeSubsite($subsite->ID);
|
||||
$vp2 = new SubsitesVirtualPage();
|
||||
$vp2->CopyContentFromID = $page->ID;
|
||||
$vp2->write();
|
||||
$vp2->doPublish();
|
||||
$vp2->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
||||
|
||||
// Switch back to main site, unpublish source
|
||||
$subsite = $this->objFromFixture('Subsite', 'main');
|
||||
$subsite = $this->objFromFixture(Subsite::class, 'main');
|
||||
Subsite::changeSubsite($subsite->ID);
|
||||
$page = $this->objFromFixture('Page', 'importantpage');
|
||||
$page->doUnpublish();
|
||||
|
||||
Subsite::changeSubsite($vp1->SubsiteID);
|
||||
$onLive = Versioned::get_one_by_stage('SubsitesVirtualPage', 'Live', "\"SiteTree_Live\".\"ID\" = ".$vp1->ID);
|
||||
$onLive = Versioned::get_one_by_stage(SubsitesVirtualPage::class, 'Live', '"SiteTree_Live"."ID" = ' . $vp1->ID);
|
||||
$this->assertNull($onLive, 'SVP has been removed from live');
|
||||
|
||||
$subsite = $this->objFromFixture('Subsite', 'subsite2');
|
||||
$subsite = $this->objFromFixture(Subsite::class, 'subsite2');
|
||||
Subsite::changeSubsite($vp2->SubsiteID);
|
||||
$onLive = Versioned::get_one_by_stage('SubsitesVirtualPage', 'Live', "\"SiteTree_Live\".\"ID\" = ".$vp2->ID);
|
||||
$onLive = Versioned::get_one_by_stage(SubsitesVirtualPage::class, 'Live', '"SiteTree_Live"."ID" = ' . $vp2->ID);
|
||||
$this->assertNull($onLive, 'SVP has been removed from live');
|
||||
}
|
||||
|
||||
@ -245,12 +255,13 @@ class SubsitesVirtualPageTest extends BaseSubsiteTest
|
||||
*/
|
||||
public function testSubsiteVirtualPageCanHaveSameUrlsegmentAsOtherSubsite()
|
||||
{
|
||||
Subsite::$write_hostmap = false;
|
||||
$subsite1 = $this->objFromFixture('Subsite', 'subsite1');
|
||||
$subsite2 = $this->objFromFixture('Subsite', 'subsite2');
|
||||
$this->markTestIncomplete('@todo fix this test');
|
||||
|
||||
$subsite1 = $this->objFromFixture(Subsite::class, 'subsite1');
|
||||
$subsite2 = $this->objFromFixture(Subsite::class, 'subsite2');
|
||||
Subsite::changeSubsite($subsite1->ID);
|
||||
|
||||
$subsite1Page = $this->objFromFixture('Page', 'subsite1_staff');
|
||||
$subsite1Page = $this->objFromFixture(Page::class, 'subsite1_staff');
|
||||
$subsite1Page->URLSegment = 'staff';
|
||||
$subsite1Page->write();
|
||||
|
||||
@ -259,6 +270,7 @@ class SubsitesVirtualPageTest extends BaseSubsiteTest
|
||||
$subsite1Vp->CopyContentFromID = $subsite1Page->ID;
|
||||
$subsite1Vp->SubsiteID = $subsite1->ID;
|
||||
$subsite1Vp->write();
|
||||
|
||||
$this->assertNotEquals(
|
||||
$subsite1Vp->URLSegment,
|
||||
$subsite1Page->URLSegment,
|
||||
@ -277,7 +289,7 @@ class SubsitesVirtualPageTest extends BaseSubsiteTest
|
||||
$this->assertEquals(
|
||||
$subsite2Vp->URLSegment,
|
||||
$subsite1Page->URLSegment,
|
||||
"Does allow explicit URLSegment overrides when only existing in a different subsite"
|
||||
'Does allow explicit URLSegment overrides when only existing in a different subsite'
|
||||
);
|
||||
|
||||
// When changing subsites and re-saving this page, it doesn't trigger a change
|
||||
@ -291,12 +303,49 @@ class SubsitesVirtualPageTest extends BaseSubsiteTest
|
||||
);
|
||||
}
|
||||
|
||||
public function fixVersionNumberCache($page)
|
||||
protected function fixVersionNumberCache($page)
|
||||
{
|
||||
$pages = func_get_args();
|
||||
foreach ($pages as $p) {
|
||||
Versioned::prepopulate_versionnumber_cache('SiteTree', 'Stage', array($p->ID));
|
||||
Versioned::prepopulate_versionnumber_cache('SiteTree', 'Live', array($p->ID));
|
||||
Versioned::prepopulate_versionnumber_cache(SiteTree::class, 'Stage', [$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());
|
||||
}
|
||||
}
|
12
tests/php/SubsitesVirtualPageTest.yml
Normal file
12
tests/php/SubsitesVirtualPageTest.yml
Normal file
@ -0,0 +1,12 @@
|
||||
# These need to come first so that SiteTree has the link meta-data written.
|
||||
SilverStripe\Assets\File:
|
||||
file1:
|
||||
FileFilename: testscript-test-file.pdf
|
||||
FileHash: 464dedb70af0dc7f8f3360e7f3ae43cbbf1cdf4e
|
||||
Name: testscript-test-file.pdf
|
||||
|
||||
SilverStripe\CMS\Model\SiteTree:
|
||||
page1:
|
||||
Title: page1
|
||||
URLSegment: page1
|
||||
Content: '<p><img src="/assets/SubsitesVirtualPageTest/464dedb70a/testscript-test-file.pdf" /></p>'
|
89
tests/php/WildcardDomainFieldTest.php
Normal file
89
tests/php/WildcardDomainFieldTest.php
Normal file
@ -0,0 +1,89 @@
|
||||
<?php
|
||||
|
||||
namespace SilverStripe\Subsites\Tests;
|
||||
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\Subsites\Forms\WildcardDomainField;
|
||||
|
||||
/**
|
||||
* Tests {@see WildcardDomainField}
|
||||
*/
|
||||
class WildcardDomainFieldTest extends SapphireTest
|
||||
{
|
||||
|
||||
/**
|
||||
* Check that valid domains are accepted
|
||||
*
|
||||
* @dataProvider validDomains
|
||||
* @param $domain
|
||||
*/
|
||||
public function testValidDomains($domain)
|
||||
{
|
||||
$field = new WildcardDomainField('DomainField');
|
||||
$this->assertTrue($field->checkHostname($domain), "Validate that {$domain} is a valid domain name");
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that valid domains are accepted
|
||||
*
|
||||
* @dataProvider invalidDomains
|
||||
* @param $domain
|
||||
*/
|
||||
public function testInvalidDomains($domain)
|
||||
{
|
||||
$field = new WildcardDomainField('DomainField');
|
||||
$this->assertFalse($field->checkHostname($domain), "Validate that {$domain} is an invalid domain name");
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that valid domains are accepted
|
||||
*
|
||||
* @dataProvider validWildcards
|
||||
* @param $domain
|
||||
*/
|
||||
public function testValidWildcards($domain)
|
||||
{
|
||||
$field = new WildcardDomainField('DomainField');
|
||||
$this->assertTrue($field->checkHostname($domain), "Validate that {$domain} is a valid domain wildcard");
|
||||
}
|
||||
|
||||
public function validDomains()
|
||||
{
|
||||
return [
|
||||
['www.mysite.com'],
|
||||
['domain7'],
|
||||
['mysite.co.n-z'],
|
||||
['subdomain.my-site.com'],
|
||||
['subdomain.mysite'],
|
||||
['subdomain.mysite.com:80'],
|
||||
['mysite:80']
|
||||
];
|
||||
}
|
||||
|
||||
public function invalidDomains()
|
||||
{
|
||||
return [
|
||||
['-mysite'],
|
||||
['.mysite'],
|
||||
['mys..ite'],
|
||||
['mysite-'],
|
||||
['mysite.'],
|
||||
['-mysite.*'],
|
||||
['.mysite.*'],
|
||||
['mys..ite.*'],
|
||||
['*.mysite-'],
|
||||
['*.mysite.'],
|
||||
[':1234']
|
||||
];
|
||||
}
|
||||
|
||||
public function validWildcards()
|
||||
{
|
||||
return [
|
||||
['*.mysite.com'],
|
||||
['mys*ite.com'],
|
||||
['*.my-site.*'],
|
||||
['*']
|
||||
];
|
||||
}
|
||||
}
|
BIN
tests/php/testscript-test-file.pdf
Normal file
BIN
tests/php/testscript-test-file.pdf
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user