mirror of
https://github.com/silverstripe/silverstripe-cms
synced 2024-09-30 13:19:14 +02:00
Compare commits
151 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
46a4d70cc9 | ||
|
602bdfc37b | ||
|
cb46fd8aac | ||
|
2ab8499ca8 | ||
|
a249efeeda | ||
|
3847b3ea19 | ||
|
a5d2b3bb32 | ||
|
4106e98b70 | ||
|
a6cbf65d8f | ||
|
3f6f554feb | ||
|
fb1cc3cde1 | ||
|
9202ce9a68 | ||
|
d5faa0158a | ||
|
ab2cfa0b1d | ||
|
fb5925b325 | ||
|
c908f1da2b | ||
|
3d94a0e3d0 | ||
|
d3959630c8 | ||
|
8ca2ade623 | ||
|
7027d654d2 | ||
|
1f1b64f890 | ||
|
04a0c928b3 | ||
|
221b698c15 | ||
|
2ccbbc2e9c | ||
|
665b208d89 | ||
|
dd2bd61387 | ||
|
cef8c0f179 | ||
|
e826152fd1 | ||
|
44049d4b28 | ||
|
eed9a79c19 | ||
|
c1eaaaabfa | ||
|
668744e728 | ||
|
7d1d93ab89 | ||
|
e252a65e81 | ||
|
2ba2ac8386 | ||
|
58a2d58173 | ||
|
7427f615de | ||
|
da66047987 | ||
|
3a8bd95452 | ||
|
1e7bba1dd3 | ||
|
34581be27d | ||
|
6259ca30ad | ||
|
061517ac45 | ||
|
4a92f5eb64 | ||
|
6cc8a26502 | ||
|
332898f3ed | ||
|
7e42058206 | ||
|
2799ef3b39 | ||
|
280eec2e17 | ||
|
d11915cffd | ||
|
57c5dcbebb | ||
|
6b3acefc47 | ||
|
b8e30925e8 | ||
|
0b3bbcd372 | ||
|
461cab09a0 | ||
|
6decb06909 | ||
|
391f836153 | ||
|
d865d508c9 | ||
|
78452fc1a8 | ||
|
c455a8288f | ||
|
c831b1be6b | ||
|
643801a01b | ||
|
e91b7c7f8e | ||
|
14eb767c9c | ||
|
06d39c2cc9 | ||
|
b44ceb95ed | ||
|
2ea9d85ee8 | ||
|
cdd6e54fce | ||
|
39fe63db8a | ||
|
b42ef6273d | ||
|
1f2bf0a8a7 | ||
|
c7db5e25a9 | ||
|
984cf404f4 | ||
|
989bb7944a | ||
|
40f7a79c09 | ||
|
81e7e1a01b | ||
|
28ecbdc845 | ||
|
ca837d6ccd | ||
|
6675f50f81 | ||
|
cfc48fad0a | ||
|
0b50824e46 | ||
|
b1efcc217d | ||
|
2afad17237 | ||
|
f2d745afec | ||
|
0c712284a9 | ||
|
f705df4b0c | ||
|
76ba5ed33b | ||
|
7fe9a04112 | ||
|
3f1dbe4d3f | ||
|
abf00ede95 | ||
|
93f57d2300 | ||
|
222972c687 | ||
|
b02beba7ba | ||
|
081eea42c5 | ||
|
30685c9227 | ||
|
37af732dbf | ||
|
18cb6d499d | ||
|
2b0374e692 | ||
|
f362d8b129 | ||
|
9bb6f700b4 | ||
|
69fabe9a34 | ||
|
b57d557bc7 | ||
|
97fdff4bb2 | ||
|
50cefa6d14 | ||
|
1bd578fc96 | ||
|
61d729285f | ||
|
a44547915d | ||
|
e7d9d1b36e | ||
|
3c7459208e | ||
|
ef4122da04 | ||
|
b9115f3f6d | ||
|
dd79cf5a23 | ||
|
cba1acb88e | ||
|
bd42008584 | ||
|
5e843d2619 | ||
|
ddbe4ea4ce | ||
|
145f063c33 | ||
|
03bac4b72d | ||
|
605daf5026 | ||
|
0e5428b78b | ||
|
8b1433b263 | ||
|
3186e0e129 | ||
|
911a71d6f6 | ||
|
003f9ba750 | ||
|
2d10a624b3 | ||
|
af1a482d20 | ||
|
8320023526 | ||
|
6e19ae737f | ||
|
9b64c7de24 | ||
|
ea9ce63438 | ||
|
6ff98c4201 | ||
|
e5cea70b54 | ||
|
d7857ebbe0 | ||
|
390f078551 | ||
|
bedd64554f | ||
|
7368df8757 | ||
|
d56682509c | ||
|
aa4ba82f38 | ||
|
f2c4423d0c | ||
|
1711c0c88e | ||
|
adcea213a2 | ||
|
f9a19e7429 | ||
|
803b4add7b | ||
|
cda7857e12 | ||
|
b46876b8a8 | ||
|
c130b55ecb | ||
|
2219899c10 | ||
|
859ff00184 | ||
|
7381de15e8 | ||
|
e580527111 | ||
|
72f8e5f71d |
1
.github/ISSUE_TEMPLATE.md
vendored
Normal file
1
.github/ISSUE_TEMPLATE.md
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
<!-- Blank templates are for use by maintainers only! If you aren't a maintainer, please go back and pick one of the issue templates. -->
|
72
.github/ISSUE_TEMPLATE/1_bug_report.yml
vendored
Normal file
72
.github/ISSUE_TEMPLATE/1_bug_report.yml
vendored
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
name: 🪳 Bug Report
|
||||||
|
description: Tell us if something isn't working the way it's supposed to
|
||||||
|
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
We strongly encourage you to [submit a pull request](https://docs.silverstripe.org/en/contributing/code/) which fixes the issue.
|
||||||
|
Bug reports which are accompanied with a pull request are a lot more likely to be resolved quickly.
|
||||||
|
- type: input
|
||||||
|
id: affected-versions
|
||||||
|
attributes:
|
||||||
|
label: Module version(s) affected
|
||||||
|
description: |
|
||||||
|
What version of _this module_ have you reproduced this bug on?
|
||||||
|
Run `composer info` to see the specific version of each module installed in your project.
|
||||||
|
If you don't have access to that, check inside the help menu in the bottom left of the CMS.
|
||||||
|
placeholder: x.y.z
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: Description
|
||||||
|
description: A clear and concise description of the problem
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: how-to-reproduce
|
||||||
|
attributes:
|
||||||
|
label: How to reproduce
|
||||||
|
description: |
|
||||||
|
⚠️ This is the most important part of the report ⚠️
|
||||||
|
Without a way to easily reproduce your issue, there is little chance we will be able to help you and work on a fix.
|
||||||
|
- Please, take the time to show us some code and/or configuration that is needed for others to reproduce the problem easily.
|
||||||
|
- If the bug is too complex to reproduce with some short code samples, please reproduce it in a public repository and provide a link to the repository along with steps for setting up and reproducing the bug using that repository.
|
||||||
|
- If part of the bug includes an error or exception, please provide a full stack trace.
|
||||||
|
- If any user interaction is required to reproduce the bug, please add an ordered list of steps that are required to reproduce it.
|
||||||
|
- Be as clear as you can, but don't miss any steps out. Simply saying "create a page" is less useful than guiding us through the steps you're taking to create a page, for example.
|
||||||
|
placeholder: |
|
||||||
|
|
||||||
|
#### Code sample
|
||||||
|
```php
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Reproduction steps
|
||||||
|
1.
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: possible-solution
|
||||||
|
attributes:
|
||||||
|
label: Possible Solution
|
||||||
|
description: |
|
||||||
|
*Optional: only if you have suggestions on a fix/reason for the bug*
|
||||||
|
Please consider [submitting a pull request](https://docs.silverstripe.org/en/contributing/code/) with your solution! It helps get faster feedback and greatly increases the chance of the bug being fixed.
|
||||||
|
- type: textarea
|
||||||
|
id: additional-context
|
||||||
|
attributes:
|
||||||
|
label: Additional Context
|
||||||
|
description: "*Optional: any other context about the problem: log messages, screenshots, etc.*"
|
||||||
|
- type: checkboxes
|
||||||
|
id: validations
|
||||||
|
attributes:
|
||||||
|
label: Validations
|
||||||
|
description: "Before submitting the issue, please make sure you do the following:"
|
||||||
|
options:
|
||||||
|
- label: Check that there isn't already an issue that reports the same bug
|
||||||
|
required: true
|
||||||
|
- label: Double check that your reproduction steps work in a fresh installation of [`silverstripe/installer`](https://github.com/silverstripe/silverstripe-installer) (with any code examples you've provided)
|
||||||
|
required: true
|
35
.github/ISSUE_TEMPLATE/2_feature_request.yml
vendored
Normal file
35
.github/ISSUE_TEMPLATE/2_feature_request.yml
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
name: 🚀 Feature Request
|
||||||
|
description: Submit a feature request (but only if you're planning on implementing it)
|
||||||
|
body:
|
||||||
|
- type: markdown
|
||||||
|
attributes:
|
||||||
|
value: |
|
||||||
|
Please only submit feature requests if you plan on implementing the feature yourself.
|
||||||
|
See the [contributing code documentation](https://docs.silverstripe.org/en/contributing/code/#make-or-find-a-github-issue) for more guidelines about submitting feature requests.
|
||||||
|
- type: textarea
|
||||||
|
id: description
|
||||||
|
attributes:
|
||||||
|
label: Description
|
||||||
|
description: A clear and concise description of the new feature, and why it belongs in core
|
||||||
|
validations:
|
||||||
|
required: true
|
||||||
|
- type: textarea
|
||||||
|
id: more-info
|
||||||
|
attributes:
|
||||||
|
label: Additional context or points of discussion
|
||||||
|
description: |
|
||||||
|
*Optional: Any additional context, points of discussion, etc that might help validate and refine your idea*
|
||||||
|
- type: checkboxes
|
||||||
|
id: validations
|
||||||
|
attributes:
|
||||||
|
label: Validations
|
||||||
|
description: "Before submitting the issue, please confirm the following:"
|
||||||
|
options:
|
||||||
|
- label: You intend to implement the feature yourself
|
||||||
|
required: true
|
||||||
|
- label: You have read the [contributing guide](https://docs.silverstripe.org/en/contributing/code/)
|
||||||
|
required: true
|
||||||
|
- label: You strongly believe this feature should be in core, rather than being its own community module
|
||||||
|
required: true
|
||||||
|
- label: You have checked for existing issues or pull requests related to this feature (and didn't find any)
|
||||||
|
required: true
|
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
blank_issues_enabled: true
|
||||||
|
contact_links:
|
||||||
|
- name: Security Vulnerability
|
||||||
|
url: https://docs.silverstripe.org/en/contributing/issues_and_bugs/#reporting-security-issues
|
||||||
|
about: ⚠️ We do not use GitHub issues to track security vulnerability reports. Click "open" on the right to see how to report security vulnerabilities.
|
||||||
|
- name: Support Question
|
||||||
|
url: https://www.silverstripe.org/community/
|
||||||
|
about: We use GitHub issues only to discuss bugs and new features. For support questions, please use one of the support options available in our community channels.
|
39
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
39
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<!--
|
||||||
|
Thanks for contributing, you're awesome! ⭐
|
||||||
|
|
||||||
|
Please read https://docs.silverstripe.org/en/contributing/code/ if you haven't contributed to this project recently.
|
||||||
|
-->
|
||||||
|
## Description
|
||||||
|
<!--
|
||||||
|
Please describe expected and observed behaviour, and what you're fixing.
|
||||||
|
For visual fixes, please include tested browsers and screenshots.
|
||||||
|
-->
|
||||||
|
|
||||||
|
## Manual testing steps
|
||||||
|
<!--
|
||||||
|
Include any manual testing steps here which a reviewer can perform to validate your pull request works correctly.
|
||||||
|
Note that this DOES NOT replace unit or end-to-end tests.
|
||||||
|
-->
|
||||||
|
|
||||||
|
## Issues
|
||||||
|
<!--
|
||||||
|
List all issues here that this pull request fixes/resolves.
|
||||||
|
If there is no issue already, create a new one! You must link your pull request to at least one issue.
|
||||||
|
-->
|
||||||
|
- #
|
||||||
|
|
||||||
|
## Pull request checklist
|
||||||
|
<!--
|
||||||
|
PLEASE check each of these to ensure you have done everything you need to do!
|
||||||
|
If there's something in this list you need help with, please ask so that we can assist you.
|
||||||
|
-->
|
||||||
|
- [ ] The target branch is correct
|
||||||
|
- See [picking the right version](https://docs.silverstripe.org/en/contributing/code/#picking-the-right-version)
|
||||||
|
- [ ] All commits are relevant to the purpose of the PR (e.g. no debug statements, unrelated refactoring, or arbitrary linting)
|
||||||
|
- Small amounts of additional linting are usually okay, but if it makes it hard to concentrate on the relevant changes, ask for the unrelated changes to be reverted, and submitted as a separate PR.
|
||||||
|
- [ ] The commit messages follow our [commit message guidelines](https://docs.silverstripe.org/en/contributing/code/#commit-messages)
|
||||||
|
- [ ] The PR follows our [contribution guidelines](https://docs.silverstripe.org/en/contributing/code/)
|
||||||
|
- [ ] Code changes follow our [coding conventions](https://docs.silverstripe.org/en/contributing/coding_conventions/)
|
||||||
|
- [ ] This change is covered with tests (or tests aren't necessary for this change)
|
||||||
|
- [ ] Any relevant User Help/Developer documentation is updated; for impactful changes, information is added to the changelog for the intended release
|
||||||
|
- [ ] CI is green
|
6
.github/workflows/keepalive.yml
vendored
6
.github/workflows/keepalive.yml
vendored
@ -1,10 +1,10 @@
|
|||||||
name: Keepalive
|
name: Keepalive
|
||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
# At 6:30 PM UTC, on day 15 of the month
|
||||||
# The 4th of every month at 10:50am UTC
|
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '50 10 4 * *'
|
- cron: '30 18 15 * *'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
keepalive:
|
keepalive:
|
||||||
|
17
.github/workflows/merge-up.yml
vendored
Normal file
17
.github/workflows/merge-up.yml
vendored
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
name: Merge-up
|
||||||
|
|
||||||
|
on:
|
||||||
|
# At 1:20 PM UTC, only on Saturday
|
||||||
|
schedule:
|
||||||
|
- cron: '20 13 * * 6'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
merge-up:
|
||||||
|
name: Merge-up
|
||||||
|
# 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: Merge-up
|
||||||
|
uses: silverstripe/gha-merge-up@v1
|
2
.github/workflows/update-js.yml
vendored
2
.github/workflows/update-js.yml
vendored
@ -4,7 +4,7 @@ on:
|
|||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
# Run on a schedule of once per quarter
|
# Run on a schedule of once per quarter
|
||||||
schedule:
|
schedule:
|
||||||
- cron: '0 0 1 */3 *'
|
- cron: '30 18 1 */3 *'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
update-js:
|
update-js:
|
||||||
|
161
.upgrade.yml
161
.upgrade.yml
@ -1,161 +0,0 @@
|
|||||||
mappings:
|
|
||||||
CMSBatchAction_Publish: SilverStripe\CMS\BatchActions\CMSBatchAction_Publish
|
|
||||||
CMSBatchAction_Unpublish: SilverStripe\CMS\BatchActions\CMSBatchAction_Unpublish
|
|
||||||
CMSBatchAction_Archive: SilverStripe\CMS\BatchActions\CMSBatchAction_Archive
|
|
||||||
CMSBatchAction_Restore: SilverStripe\CMS\BatchActions\CMSBatchAction_Restore
|
|
||||||
CMSMain: SilverStripe\CMS\Controllers\CMSMain
|
|
||||||
CMSPageAddController: SilverStripe\CMS\Controllers\CMSPageAddController
|
|
||||||
CMSPageEditController: SilverStripe\CMS\Controllers\CMSPageEditController
|
|
||||||
CMSPageHistoryController: SilverStripe\CMS\Controllers\CMSPageHistoryController
|
|
||||||
CMSPagesController: SilverStripe\CMS\Controllers\CMSPagesController
|
|
||||||
CMSPageSettingsController: SilverStripe\CMS\Controllers\CMSPageSettingsController
|
|
||||||
CMSSiteTreeFilter: SilverStripe\CMS\Controllers\CMSSiteTreeFilter
|
|
||||||
CMSSiteTreeFilter_PublishedPages: SilverStripe\CMS\Controllers\CMSSiteTreeFilter_PublishedPages
|
|
||||||
CMSSiteTreeFilter_DeletedPages: SilverStripe\CMS\Controllers\CMSSiteTreeFilter_DeletedPages
|
|
||||||
CMSSiteTreeFilter_ChangedPages: SilverStripe\CMS\Controllers\CMSSiteTreeFilter_ChangedPages
|
|
||||||
CMSSiteTreeFilter_StatusRemovedFromDraftPages: SilverStripe\CMS\Controllers\CMSSiteTreeFilter_StatusRemovedFromDraftPages
|
|
||||||
CMSSiteTreeFilter_StatusDraftPages: SilverStripe\CMS\Controllers\CMSSiteTreeFilter_StatusDraftPages
|
|
||||||
CMSSiteTreeFilter_StatusDeletedPages: SilverStripe\CMS\Controllers\CMSSiteTreeFilter_StatusDeletedPages
|
|
||||||
CMSSiteTreeFilter_Search: SilverStripe\CMS\Controllers\CMSSiteTreeFilter_Search
|
|
||||||
ContentController: SilverStripe\CMS\Controllers\ContentController
|
|
||||||
ErrorPageControllerExtension: SilverStripe\ErrorPage\ErrorPageControllerExtension
|
|
||||||
ErrorPageFileExtension: SilverStripe\ErrorPage\ErrorPageFileExtension
|
|
||||||
LeftAndMainPageIconsExtension: SilverStripe\CMS\Controllers\LeftAndMainPageIconsExtension
|
|
||||||
ModelAsController: SilverStripe\CMS\Controllers\ModelAsController
|
|
||||||
OldPageRedirector: SilverStripe\CMS\Controllers\OldPageRedirector
|
|
||||||
RootURLController: SilverStripe\CMS\Controllers\RootURLController
|
|
||||||
SilverStripeNavigator: SilverStripe\CMS\Controllers\SilverStripeNavigator
|
|
||||||
SilverStripeNavigatorItem: SilverStripe\CMS\Controllers\SilverStripeNavigatorItem
|
|
||||||
SilverStripeNavigatorItem_CMSLink: SilverStripe\CMS\Controllers\SilverStripeNavigatorItem_CMSLink
|
|
||||||
SilverStripeNavigatorItem_StageLink: SilverStripe\CMS\Controllers\SilverStripeNavigatorItem_StageLink
|
|
||||||
SilverStripeNavigatorItem_LiveLink: SilverStripe\CMS\Controllers\SilverStripeNavigatorItem_LiveLink
|
|
||||||
SilverStripeNavigatorItem_ArchiveLink: SilverStripe\CMS\Controllers\SilverStripeNavigatorItem_ArchiveLink
|
|
||||||
SiteTreeURLSegmentField: SilverStripe\CMS\Forms\SiteTreeURLSegmentField
|
|
||||||
SiteTreeURLSegmentField_Readonly: SilverStripe\CMS\Forms\SiteTreeURLSegmentField_Readonly
|
|
||||||
CurrentPageIdentifier: SilverStripe\CMS\Model\CurrentPageIdentifier
|
|
||||||
ErrorPage: SilverStripe\ErrorPage\ErrorPage
|
|
||||||
ErrorPage_Controller: SilverStripe\ErrorPage\ErrorPageController
|
|
||||||
RedirectorPage: SilverStripe\CMS\Model\RedirectorPage
|
|
||||||
RedirectorPage_Controller: SilverStripe\CMS\Model\RedirectorPageController
|
|
||||||
SiteTree: SilverStripe\CMS\Model\SiteTree
|
|
||||||
SiteTreeExtension: SilverStripe\CMS\Model\SiteTreeExtension
|
|
||||||
SiteTreeFileExtension: SilverStripe\CMS\Model\SiteTreeFileExtension
|
|
||||||
SiteTreeFolderExtension: SilverStripe\CMS\Model\SiteTreeFolderExtension
|
|
||||||
SiteTreeLinkTracking: SilverStripe\CMS\Model\SiteTreeLinkTracking
|
|
||||||
SiteTreeLinkTracking_Parser: SilverStripe\CMS\Model\SiteTreeLinkTracking_Parser
|
|
||||||
VirtualPage: SilverStripe\CMS\Model\VirtualPage
|
|
||||||
BrokenFilesReport: SilverStripe\CMS\Reports\BrokenFilesReport
|
|
||||||
SideReport_BrokenFiles: SilverStripe\CMS\Reports\BrokenFilesReport
|
|
||||||
BrokenLinksReport: SilverStripe\CMS\Reports\BrokenLinksReport
|
|
||||||
SideReport_BrokenLinks: SilverStripe\CMS\Reports\BrokenLinksReport
|
|
||||||
BrokenRedirectorPagesReport: SilverStripe\CMS\Reports\BrokenRedirectorPagesReport
|
|
||||||
SideReport_BrokenRedirectorPages: SilverStripe\CMS\Reports\BrokenRedirectorPagesReport
|
|
||||||
BrokenVirtualPagesReport: SilverStripe\CMS\Reports\BrokenVirtualPagesReport
|
|
||||||
SideReport_BrokenVirtualPages: SilverStripe\CMS\Reports\BrokenVirtualPagesReport
|
|
||||||
EmptyPagesReport: SilverStripe\CMS\Reports\EmptyPagesReport
|
|
||||||
SideReport_EmptyPages: SilverStripe\CMS\Reports\EmptyPagesReport
|
|
||||||
RecentlyEditedReport: SilverStripe\CMS\Reports\RecentlyEditedReport
|
|
||||||
SideReport_RecentlyEdited: SilverStripe\CMS\Reports\RecentlyEditedReport
|
|
||||||
ContentControllerSearchExtension: SilverStripe\CMS\Search\ContentControllerSearchExtension
|
|
||||||
SearchForm: SilverStripe\CMS\Search\SearchForm
|
|
||||||
MigrateSiteTreeLinkingTask: SilverStripe\CMS\Tasks\MigrateSiteTreeLinkingTask
|
|
||||||
RemoveOrphanedPagesTask: SilverStripe\CMS\Tasks\RemoveOrphanedPagesTask
|
|
||||||
SiteTreeMaintenanceTask: SilverStripe\CMS\Tasks\SiteTreeMaintenanceTask
|
|
||||||
AssetAdmin: SilverStripe\AssetAdmin\Controller\AssetAdmin
|
|
||||||
AssetTableField: SilverStripe\AssetAdmin\Controller\AssetAdmin
|
|
||||||
CMSAddPageController: SilverStripe\CMS\Controllers\CMSPageAddController
|
|
||||||
CMSBatchActions: SilverStripe\Admin\CMSBatchAction
|
|
||||||
CMSSearch: SilverStripe\CMS\Search\SearchForm
|
|
||||||
ContentControl: SilverStripe\CMS\Controllers\ContentController
|
|
||||||
Permissions: SilverStripe\Security\Permission
|
|
||||||
SITETREE: SilverStripe\CMS\Model\SiteTree
|
|
||||||
CMSMain_left_ss: SilverStripe\CMS\Controllers\CMSMain
|
|
||||||
CMSPageHistoryController_versions_ss: SilverStripe\CMS\Controllers\CMSPageHistoryController
|
|
||||||
CMSPagesController_ContentToolbar_ss: SilverStripe\CMS\Controllers\CMSPageHistoryController
|
|
||||||
CMSSIteTreeFilter_PublishedPages: SilverStripe\CMS\Controllers\CMSSIteTreeFilter_PublishedPages
|
|
||||||
URLSegmentField: SilverStripe\CMS\Forms\SiteTreeURLSegmentField
|
|
||||||
CMSPagesController_Tools_ss: SilverStripe\CMS\Controllers\CMSPagesController
|
|
||||||
LeftAndMain: SilverStripe\Admin\LeftAndMain
|
|
||||||
CMSBatchActionsTest: SilverStripe\CMS\Tests\CMSBatchActionsTest
|
|
||||||
CMSMainTest: SilverStripe\CMS\Tests\CMSMainTest
|
|
||||||
CMSMainTest_ClassA: SilverStripe\CMS\Tests\CMSMainTest_ClassA
|
|
||||||
CMSMainTest_ClassB: SilverStripe\CMS\Tests\CMSMainTest_ClassB
|
|
||||||
CMSMainTest_HiddenClass: SilverStripe\CMS\Tests\CMSMainTest_HiddenClass
|
|
||||||
CMSMainTest_NotRoot: SilverStripe\CMS\Tests\CMSMainTest_NotRoot
|
|
||||||
CMSPageHistoryControllerTest: SilverStripe\CMS\Tests\CMSPageHistoryControllerTest
|
|
||||||
CMSSiteTreeFilterTest: SilverStripe\CMS\Tests\CMSSiteTreeFilterTest
|
|
||||||
CMSTreeTest: SilverStripe\CMS\Tests\CMSTreeTest
|
|
||||||
ContentControllerPermissionsTest: SilverStripe\CMS\Tests\ContentControllerPermissionsTest
|
|
||||||
ContentControllerSearchExtensionTest: SilverStripe\CMS\Tests\ContentControllerSearchExtensionTest
|
|
||||||
ContentControllerTest: SilverStripe\CMS\Tests\ContentControllerTest
|
|
||||||
ContentControllerTest_Page: SilverStripe\CMS\Tests\ContentControllerTest_Page
|
|
||||||
ContentControllerTest_PageController: SilverStripe\CMS\Tests\ContentControllerTest_PageController
|
|
||||||
ContentControllerTestPage: SilverStripe\CMS\Tests\ContentControllerTestPage
|
|
||||||
ContentControllerTestPageController: SilverStripe\CMS\Tests\ContentControllerTestPageController
|
|
||||||
ContentControllerTestPageWithoutController: SilverStripe\CMS\Tests\ContentControllerTestPageWithoutController
|
|
||||||
ModelAsControllerTest: SilverStripe\CMS\Tests\ModelAsControllerTest
|
|
||||||
RootURLControllerTest: SilverStripe\CMS\Tests\RootURLControllerTest
|
|
||||||
SilverStripeNavigatorTest: SilverStripe\CMS\Tests\SilverStripeNavigatorTest
|
|
||||||
SilverStripeNavigatorTest_ProtectedTestItem: SilverStripe\CMS\Tests\SilverStripeNavigatorTest_ProtectedTestItem
|
|
||||||
SilverStripeNavigatorTest_TestItem: SilverStripe\CMS\Tests\SilverStripeNavigatorTest_TestItem
|
|
||||||
FileLinkTrackingTest: SilverStripe\CMS\Tests\FileLinkTrackingTest
|
|
||||||
RedirectorPageTest: SilverStripe\CMS\Tests\RedirectorPageTest
|
|
||||||
RedirectorPageTest_RedirectExtension: SilverStripe\CMS\Tests\RedirectorPageTest_RedirectExtension
|
|
||||||
SiteTreeActionsTest: SilverStripe\CMS\Tests\SiteTreeActionsTest
|
|
||||||
SiteTreeActionsTest_Page: SilverStripe\CMS\Tests\SiteTreeActionsTest_Page
|
|
||||||
SiteTreeBacklinksTest: SilverStripe\CMS\Tests\SiteTreeBacklinksTest
|
|
||||||
SiteTreeBacklinksTest_DOD: SilverStripe\CMS\Tests\SiteTreeBacklinksTest_DOD
|
|
||||||
SiteTreeBrokenLinksTest: SilverStripe\CMS\Tests\SiteTreeBrokenLinksTest
|
|
||||||
SiteTreeHTMLEditorFieldTest: SilverStripe\CMS\Tests\SiteTreeHTMLEditorFieldTest
|
|
||||||
SiteTreeLinkTrackingTest: SilverStripe\CMS\Tests\SiteTreeLinkTrackingTest
|
|
||||||
SiteTreePermissionsTest: SilverStripe\CMS\Tests\SiteTreePermissionsTest
|
|
||||||
SiteTreeTest: SilverStripe\CMS\Tests\SiteTreeTest
|
|
||||||
SiteTreeTest_AdminDenied: SilverStripe\CMS\Tests\SiteTreeTest_AdminDenied
|
|
||||||
SiteTreeTest_AdminDeniedExtension: SilverStripe\CMS\Tests\SiteTreeTest_AdminDeniedExtension
|
|
||||||
SiteTreeTest_ClassA: SilverStripe\CMS\Tests\SiteTreeTest_ClassA
|
|
||||||
SiteTreeTest_ClassB: SilverStripe\CMS\Tests\SiteTreeTest_ClassB
|
|
||||||
SiteTreeTest_ClassC: SilverStripe\CMS\Tests\SiteTreeTest_ClassC
|
|
||||||
SiteTreeTest_ClassCext: SilverStripe\CMS\Tests\SiteTreeTest_ClassCext
|
|
||||||
SiteTreeTest_ClassD: SilverStripe\CMS\Tests\SiteTreeTest_ClassD
|
|
||||||
SiteTreeTest_ClassE: SilverStripe\CMS\Tests\SiteTreeTest_ClassE
|
|
||||||
SiteTreeTest_Conflicted: SilverStripe\CMS\Tests\SiteTreeTest_Conflicted
|
|
||||||
SiteTreeTest_ConflictedController: SilverStripe\CMS\Tests\SiteTreeTest_ConflictedController
|
|
||||||
SiteTreeTest_DataObject: SilverStripe\CMS\Tests\SiteTreeTest_DataObject
|
|
||||||
SiteTreeTest_Extension: SilverStripe\CMS\Tests\SiteTreeTest_Extension
|
|
||||||
SiteTreeTest_ExtensionA: SilverStripe\CMS\Tests\SiteTreeTest_ExtensionA
|
|
||||||
SiteTreeTest_ExtensionB: SilverStripe\CMS\Tests\SiteTreeTest_ExtensionB
|
|
||||||
SiteTreeTest_LegacyControllerName: SilverStripe\CMS\Tests\SiteTreeTest_LegacyControllerName
|
|
||||||
SiteTreeTest_LegacyControllerName_Controller: SilverStripe\CMS\Tests\SiteTreeTest_LegacyControllerName_Controller
|
|
||||||
SiteTreeTest_NotRoot: SilverStripe\CMS\Tests\SiteTreeTest_NotRoot
|
|
||||||
SiteTreeTest_NullHtmlCleaner: SilverStripe\CMS\Tests\SiteTreeTest_NullHtmlCleaner
|
|
||||||
SiteTreeTest_PageNode: SilverStripe\CMS\Tests\SiteTreeTest_PageNode
|
|
||||||
SiteTreeTest_PageNodeController: SilverStripe\CMS\Tests\SiteTreeTest_PageNodeController
|
|
||||||
SiteTreeTest_StageStatusInherit: SilverStripe\CMS\Tests\SiteTreeTest_StageStatusInherit
|
|
||||||
VirtualPageTest: SilverStripe\CMS\Tests\VirtualPageTest
|
|
||||||
VirtualPageTest_ClassA: SilverStripe\CMS\Tests\VirtualPageTest_ClassA
|
|
||||||
VirtualPageTest_ClassAController: SilverStripe\CMS\Tests\VirtualPageTest_ClassAController
|
|
||||||
VirtualPageTest_ClassB: SilverStripe\CMS\Tests\VirtualPageTest_ClassB
|
|
||||||
VirtualPageTest_ClassC: SilverStripe\CMS\Tests\VirtualPageTest_ClassC
|
|
||||||
VirtualPageTest_NotRoot: SilverStripe\CMS\Tests\VirtualPageTest_NotRoot
|
|
||||||
VirtualPageTest_PageExtension: SilverStripe\CMS\Tests\VirtualPageTest_PageExtension
|
|
||||||
VirtualPageTest_PageWithAllowedChildren: SilverStripe\CMS\Tests\VirtualPageTest_PageWithAllowedChildren
|
|
||||||
VirtualPageTest_TestDBField: SilverStripe\CMS\Tests\VirtualPageTest_TestDBField
|
|
||||||
VirtualPageTest_VirtualPageSub: SilverStripe\CMS\Tests\VirtualPageTest_VirtualPageSub
|
|
||||||
CmsReportsTest: SilverStripe\CMS\Tests\CmsReportsTest
|
|
||||||
CMSMainSearchFormTest: SilverStripe\CMS\Tests\CMSMainSearchFormTest
|
|
||||||
ZZZSearchFormTest: SilverStripe\CMS\Tests\ZZZSearchFormTest
|
|
||||||
MigrateSiteTreeLinkingTaskTest: SilverStripe\CMS\Tests\MigrateSiteTreeLinkingTaskTest
|
|
||||||
RemoveOrphanedPagesTaskTest: SilverStripe\CMS\Tests\RemoveOrphanedPagesTaskTest
|
|
||||||
excludedPaths:
|
|
||||||
- '*/_config/legacy.yml'
|
|
||||||
warnings:
|
|
||||||
methods:
|
|
||||||
'SilverStripe\CMS\Model\SiteTree->getIsAddedToStage()':
|
|
||||||
message: 'Moved to Versioned->isOnDraftOnly()'
|
|
||||||
replacement: 'isOnDraftOnly'
|
|
||||||
'SilverStripe\CMS\Model\SiteTree->getIsModifiedOnStage()':
|
|
||||||
message: 'Moved to Versioned->isModifiedOnDraft()'
|
|
||||||
replacement: 'isModifiedOnDraft'
|
|
||||||
'SilverStripe\CMS\Model\SiteTree->getExistsOnLive()':
|
|
||||||
message: 'Removed in favour of isPublished()'
|
|
||||||
replacement: 'isPublished'
|
|
2
LICENSE
2
LICENSE
@ -1,4 +1,4 @@
|
|||||||
Copyright (c) 2016, SilverStripe Ltd.
|
Copyright (c) 2016, Silverstripe Ltd.
|
||||||
All rights reserved.
|
All rights reserved.
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
@ -7,7 +7,7 @@ PHP Content Management System (CMS), see [http://silverstripe.org](http://silver
|
|||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
See [installation instructions](https://docs.silverstripe.org/en/getting_started/installation/).
|
See the [getting started documentation](https://docs.silverstripe.org/en/getting_started/).
|
||||||
|
|
||||||
## Bugtracker
|
## Bugtracker
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@ use SilverStripe\Admin\CMSMenu;
|
|||||||
use SilverStripe\CMS\Controllers\CMSMain;
|
use SilverStripe\CMS\Controllers\CMSMain;
|
||||||
use SilverStripe\CMS\Controllers\CMSPageAddController;
|
use SilverStripe\CMS\Controllers\CMSPageAddController;
|
||||||
use SilverStripe\CMS\Controllers\CMSPageEditController;
|
use SilverStripe\CMS\Controllers\CMSPageEditController;
|
||||||
use SilverStripe\CMS\Controllers\CMSPageHistoryController;
|
|
||||||
use SilverStripe\CMS\Controllers\CMSPageSettingsController;
|
use SilverStripe\CMS\Controllers\CMSPageSettingsController;
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
use SilverStripe\Core\Manifest\ModuleLoader;
|
use SilverStripe\Core\Manifest\ModuleLoader;
|
||||||
@ -33,12 +32,7 @@ ShortcodeParser::get('default')->register(
|
|||||||
[SiteTree::class, 'link_shortcode_handler']
|
[SiteTree::class, 'link_shortcode_handler']
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO Remove once we can configure CMSMenu through static, nested configuration files
|
|
||||||
CMSMenu::remove_menu_class(CMSMain::class);
|
CMSMenu::remove_menu_class(CMSMain::class);
|
||||||
CMSMenu::remove_menu_class(CMSPageEditController::class);
|
CMSMenu::remove_menu_class(CMSPageEditController::class);
|
||||||
CMSMenu::remove_menu_class(CMSPageSettingsController::class);
|
CMSMenu::remove_menu_class(CMSPageSettingsController::class);
|
||||||
if (class_exists(CMSPageHistoryController::class)) {
|
|
||||||
// this class will be removed in CMS 5
|
|
||||||
CMSMenu::remove_menu_class(CMSPageHistoryController::class);
|
|
||||||
}
|
|
||||||
CMSMenu::remove_menu_class(CMSPageAddController::class);
|
CMSMenu::remove_menu_class(CMSPageAddController::class);
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
---
|
|
||||||
Name: cmslegacy
|
|
||||||
---
|
|
||||||
SilverStripe\ORM\DatabaseAdmin:
|
|
||||||
classname_value_remapping:
|
|
||||||
SiteTree: 'SilverStripe\CMS\Model\SiteTree'
|
|
||||||
RedirectorPage: SilverStripe\CMS\Model\RedirectorPage
|
|
||||||
VirtualPage: SilverStripe\CMS\Model\VirtualPage
|
|
@ -5,8 +5,6 @@ After: '#coreroutes'
|
|||||||
SilverStripe\Control\Director:
|
SilverStripe\Control\Director:
|
||||||
rules:
|
rules:
|
||||||
'': 'SilverStripe\CMS\Controllers\RootURLController'
|
'': 'SilverStripe\CMS\Controllers\RootURLController'
|
||||||
'RemoveOrphanedPagesTask//$Action/$ID/$OtherID': 'SilverStripe\CMS\Tasks\RemoveOrphanedPagesTask'
|
|
||||||
'SiteTreeMaintenanceTask//$Action/$ID/$OtherID': 'SilverStripe\CMS\Tasks\SiteTreeMaintenanceTask'
|
|
||||||
---
|
---
|
||||||
Name: legacycmsroutes
|
Name: legacycmsroutes
|
||||||
---
|
---
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
Name: cmsversion
|
Name: cmsversion
|
||||||
After: 'framework/*'
|
After: 'framework/coreconfig'
|
||||||
---
|
---
|
||||||
SilverStripe\Core\Manifest\VersionProvider:
|
SilverStripe\Core\Manifest\VersionProvider:
|
||||||
modules:
|
modules:
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace SilverStripe\CMS\GraphQL;
|
|
||||||
|
|
||||||
use GraphQL\Type\Definition\ResolveInfo;
|
|
||||||
use SilverStripe\Core\Injector\Injector;
|
|
||||||
use SilverStripe\GraphQL\Scaffolding\Scaffolders\CRUD\ReadOne;
|
|
||||||
use SilverStripe\GraphQL\Scaffolding\StaticSchema;
|
|
||||||
use SilverStripe\ORM\DataList;
|
|
||||||
|
|
||||||
if (!class_exists(ReadOne::class)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Shim to make readOnePage work like GraphQL 4
|
|
||||||
*
|
|
||||||
* @internal Use GraphQL v4
|
|
||||||
* @deprecated 4.8..5.0 Use silverstripe/graphql:^4 functionality.
|
|
||||||
*/
|
|
||||||
class ReadOneResolver
|
|
||||||
{
|
|
||||||
public static function resolve($obj, array $args, array $context, ResolveInfo $info)
|
|
||||||
{
|
|
||||||
$idKey = StaticSchema::inst()->formatField('ID');
|
|
||||||
$id = $args['filter'][$idKey]['eq'];
|
|
||||||
$readOne = Injector::inst()->createWithArgs(ReadOne::class, ['Page']);
|
|
||||||
unset($args['filter']);
|
|
||||||
$args[$idKey] = $id;
|
|
||||||
return $readOne->resolve($obj, $args, $context, $info);
|
|
||||||
}
|
|
||||||
}
|
|
6
babel.config.json
Normal file
6
babel.config.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"presets": [
|
||||||
|
"@babel/preset-env",
|
||||||
|
"@babel/preset-react"
|
||||||
|
]
|
||||||
|
}
|
2
client/dist/js/SilverStripeNavigator.js
vendored
2
client/dist/js/SilverStripeNavigator.js
vendored
@ -1 +1 @@
|
|||||||
!function(e){function t(n){if(r[n])return r[n].exports;var a=r[n]={i:n,l:!1,exports:{}};return e[n].call(a.exports,a,a.exports,t),a.l=!0,a.exports}var r={};t.m=e,t.c=r,t.i=function(e){return e},t.d=function(e,r,n){t.o(e,r)||Object.defineProperty(e,r,{configurable:!1,enumerable:!0,get:n})},t.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(r,"a",r),r},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s="./client/src/legacy/SilverStripeNavigator.js")}({"./client/src/legacy/SilverStripeNavigator.js":function(e,t,r){"use strict";!function(){function e(e){return document.getElementsByTagName("base")[0].href.replace("http://","").replace(/\//g,"_").replace(/\./g,"_")+e}function t(e){var t=getComputedStyle(e).display;e.style.display="none",e.dataset.__toggle_display=t}function r(e){e.style.display=e.dataset.__toggle_display?e.dataset.__toggle_display:"block"}function n(e){"none"!==getComputedStyle(e).display?t(e):r(e)}var a=document.querySelectorAll("#switchView a.newWindow");if(a.length>0){var i=!0,l=!1,o=void 0;try{for(var u,c=a.values()[Symbol.iterator]();!(i=(u=c.next()).done);i=!0){var f=u.value;!function(t){t.addEventListener("click",function(r){return r.preventDefault(),window.open(t.href,e(t.target)).focus(),!1})}(f)}}catch(e){l=!0,o=e}finally{try{!i&&c.return&&c.return()}finally{if(l)throw o}}}var v=document.getElementById("SilverStripeNavigatorLinkPopup");if(v){var s=document.getElementById("SilverStripeNavigatorLink");s&&s.addEventListener("click",function(e){return e.preventDefault(),n(v),!1});var d=v.querySelectorAll("a.close");if(d.length>0){var y=!0,p=!1,g=void 0;try{for(var S,m=d.values()[Symbol.iterator]();!(y=(S=m.next()).done);y=!0)S.value.addEventListener("click",function(e){return e.preventDefault(),t(v),!1})}catch(e){p=!0,g=e}finally{try{!y&&m.return&&m.return()}finally{if(p)throw g}}}var h=v.querySelectorAll("input");if(h.length>0){var _=!0,w=!1,b=void 0;try{for(var x,E=h.values()[Symbol.iterator]();!(_=(x=E.next()).done);_=!0){var k=x.value;!function(e){e.addEventListener("focus",function(t){e.select()})}(k)}}catch(e){w=!0,b=e}finally{try{!_&&E.return&&E.return()}finally{if(w)throw b}}}}}()}});
|
!function(){"use strict";!function(){function e(e){const t=getComputedStyle(e).display;e.style.display="none",e.dataset.__toggle_display=t}const t=document.querySelectorAll("#switchView a.newWindow");if(t.length>0)for(const e of t.values())e.addEventListener("click",(function(t){t.preventDefault();var n;return window.open(e.href,(n=e.target,document.getElementsByTagName("base")[0].href.replace("http://","").replace(/\//g,"_").replace(/\./g,"_")+n)).focus(),!1}));const n=document.getElementById("SilverStripeNavigatorLinkPopup");if(n){const t=document.getElementById("SilverStripeNavigatorLink");t&&t.addEventListener("click",(function(t){var o;return t.preventDefault(),o=n,"none"!==getComputedStyle(o).display?e(o):function(e){e.style.display=e.dataset.__toggle_display?e.dataset.__toggle_display:"block"}(o),!1}));const o=n.querySelectorAll("a.close");if(o.length>0)for(const t of o.values())t.addEventListener("click",(function(t){return t.preventDefault(),e(n),!1}));const l=n.querySelectorAll("input");if(l.length>0)for(const e of l.values())e.addEventListener("focus",(function(t){e.select()}))}}()}();
|
2
client/dist/js/TinyMCE_sslink-anchor.js
vendored
2
client/dist/js/TinyMCE_sslink-anchor.js
vendored
@ -1 +1 @@
|
|||||||
!function(e){function t(r){if(n[r])return n[r].exports;var i=n[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,t),i.l=!0,i.exports}var n={};t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s="./client/src/legacy/TinyMCE_sslink-anchor.js")}({"./client/src/legacy/TinyMCE_sslink-anchor.js":function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(1),o=r(i),a=n(9),l=r(a),c=n(2),s=r(c),u=n(5),d=r(u),p=n(4),f=n(6),A=n(0),E=r(A),_=n(8),h=r(_),C=n(7),g=n(3),R=n("./client/src/state/anchorSelector/AnchorSelectorActions.js"),O={init:function(e){l.default.addAction("sslink",{text:o.default._t("CMS.LINKLABEL_ANCHOR","Anchor on a page"),onclick:function(e){return e.execCommand("sslinkanchor")},priority:60},e.settings.editorIdentifier).addCommandWithUrlTest("sslinkanchor",/^\[sitetree_link.+]#[^#\]]+$/),e.addCommand("sslinkanchor",function(){var t=(0,E.default)("#"+e.id).entwine("ss"),n=Number((0,E.default)("#Form_EditForm_ID").val()||0),r=e.$("[id],[name]",e.getBody()).toArray().map(function(e){return e.id||e.name});ss.store.dispatch((0,R.updatedCurrentField)(n,r,e.id)),t.openLinkAnchorDialog()})}},T="insert-link__dialog-wrapper--anchor",m=(0,g.provideInjector)((0,C.createInsertLinkModal)("SilverStripe\\CMS\\Controllers\\CMSPageEditController","editorAnchorLink"));E.default.entwine("ss",function(e){e("textarea.htmleditor").entwine({openLinkAnchorDialog:function(){var t=e("#"+T);t.length||(t=e('<div id="'+T+'" />'),e("body").append(t)),t.addClass("insert-link__dialog-wrapper"),t.setElement(this),t.open()}}),e("#"+T).entwine({renderModal:function(t){var n=this,r=ss.store,i=ss.apolloClient,a=function(){return n.close()},l=function(){return n.handleInsert.apply(n,arguments)},c=this.getOriginalAttributes(),u=this.getRequireLinkText(),A=Number(e("#Form_EditForm_ID").val()||0);d.default.render(s.default.createElement(p.ApolloProvider,{client:i},s.default.createElement(f.Provider,{store:r},s.default.createElement(m,{isOpen:t,onInsert:l,onClosed:a,title:o.default._t("CMS.LINK_ANCHOR","Link to an anchor on a page"),bodyClassName:"modal__dialog",className:"insert-link__dialog-wrapper--anchor",fileAttributes:c,identifier:"Admin.InsertLinkAnchorModal",requireLinkText:u,currentPageID:A}))),this[0])},buildAttributes:function(e){return{href:h.default.serialise({name:"sitetree_link",properties:{id:e.PageID}},!0)+(e.Anchor&&e.Anchor.length?"#"+e.Anchor:""),target:e.TargetBlank?"_blank":"",title:e.Description}},getOriginalAttributes:function(){var t=this.getElement().getEditor(),n=e(t.getSelectedNode()),r=(n.attr("href")||"").split("#");if(!r[0])return{};var i=h.default.match("sitetree_link",!1,r[0]);return i?{PageID:i.properties.id?parseInt(i.properties.id,10):0,Anchor:r[1]||"",Description:n.attr("title"),TargetBlank:!!n.attr("target")}:{}}})}),tinymce.PluginManager.add("sslinkanchor",function(e){return O.init(e)}),t.default=O},"./client/src/state/anchorSelector/AnchorSelectorActionTypes.js":function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={ANCHORSELECTOR_CURRENT_FIELD:"ANCHORSELECTOR_CURRENT_FIELD",ANCHORSELECTOR_UPDATED:"ANCHORSELECTOR_UPDATED",ANCHORSELECTOR_UPDATING:"ANCHORSELECTOR_UPDATING",ANCHORSELECTOR_UPDATE_FAILED:"ANCHORSELECTOR_UPDATE_FAILED"}},"./client/src/state/anchorSelector/AnchorSelectorActions.js":function(e,t,n){"use strict";function r(e){return{type:c.default.ANCHORSELECTOR_UPDATING,payload:{pageId:e}}}function i(e,t){var n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];return{type:c.default.ANCHORSELECTOR_UPDATED,payload:{pageId:e,anchors:t,cacheResult:n}}}function o(e,t,n){return{type:c.default.ANCHORSELECTOR_CURRENT_FIELD,payload:{pageId:e,anchors:t,fieldID:n}}}function a(e){return{type:c.default.ANCHORSELECTOR_UPDATE_FAILED,payload:{pageId:e}}}Object.defineProperty(t,"__esModule",{value:!0}),t.beginUpdating=r,t.updated=i,t.updatedCurrentField=o,t.updateFailed=a;var l=n("./client/src/state/anchorSelector/AnchorSelectorActionTypes.js"),c=function(e){return e&&e.__esModule?e:{default:e}}(l)},0:function(e,t){e.exports=jQuery},1:function(e,t){e.exports=i18n},2:function(e,t){e.exports=React},3:function(e,t){e.exports=Injector},4:function(e,t){e.exports=ReactApollo},5:function(e,t){e.exports=ReactDom},6:function(e,t){e.exports=ReactRedux},7:function(e,t){e.exports=InsertLinkModal},8:function(e,t){e.exports=ShortcodeSerialiser},9:function(e,t){e.exports=TinyMCEActionRegistrar}});
|
!function(){"use strict";var e={964:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;t.default={ANCHORSELECTOR_CURRENT_FIELD:"ANCHORSELECTOR_CURRENT_FIELD",ANCHORSELECTOR_UPDATED:"ANCHORSELECTOR_UPDATED",ANCHORSELECTOR_UPDATING:"ANCHORSELECTOR_UPDATING",ANCHORSELECTOR_UPDATE_FAILED:"ANCHORSELECTOR_UPDATE_FAILED"}},447:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.beginUpdating=function(e){return{type:i.default.ANCHORSELECTOR_UPDATING,payload:{pageId:e}}},t.updateFailed=function(e){return{type:i.default.ANCHORSELECTOR_UPDATE_FAILED,payload:{pageId:e}}},t.updated=function(e,t){let n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];return{type:i.default.ANCHORSELECTOR_UPDATED,payload:{pageId:e,anchors:t,cacheResult:n}}},t.updatedCurrentField=function(e,t,n){return{type:i.default.ANCHORSELECTOR_CURRENT_FIELD,payload:{pageId:e,anchors:t,fieldID:n}}};var r,i=(r=n(964))&&r.__esModule?r:{default:r}},939:function(e){e.exports=ApolloClient},648:function(e){e.exports=Injector},595:function(e){e.exports=InsertLinkModal},363:function(e){e.exports=React},691:function(e){e.exports=ReactDomClient},624:function(e){e.exports=ReactRedux},265:function(e){e.exports=ShortcodeSerialiser},196:function(e){e.exports=TinyMCEActionRegistrar},754:function(e){e.exports=i18n},311:function(e){e.exports=jQuery}},t={};function n(r){var i=t[r];if(void 0!==i)return i.exports;var o=t[r]={exports:{}};return e[r](o,o.exports,n),o.exports}!function(){var e=p(n(754)),t=p(n(196)),r=p(n(363)),i=n(691),o=n(939),a=n(624),d=p(n(311)),l=p(n(265)),s=n(595),u=n(648),c=n(447);function p(e){return e&&e.__esModule?e:{default:e}}const f="sslinkanchor",E={init(n){t.default.addAction("sslink",{text:e.default._t("CMS.LINKLABEL_ANCHOR","Anchor on a page"),onAction:e=>e.execCommand(f),priority:60},n.getParam("editorIdentifier")).addCommandWithUrlTest(f,/^\[sitetree_link.+]#[^#\]]+$/),n.addCommand(f,(()=>{const e=(0,d.default)(`#${n.id}`).entwine("ss"),t=Number((0,d.default)("#Form_EditForm_ID").val()||0),r=(0,d.default)(n.getBody()).find("[id],[name]").toArray().map((e=>e.id||e.name));ss.store.dispatch((0,c.updatedCurrentField)(t,r,n.id)),e.openLinkAnchorDialog()}))}},A="insert-link__dialog-wrapper--anchor",C=(0,u.provideInjector)((0,s.createInsertLinkModal)("SilverStripe\\CMS\\Controllers\\CMSPageEditController","editorAnchorLink"));d.default.entwine("ss",(t=>{t("textarea.htmleditor").entwine({openLinkAnchorDialog(){let e=t(`#${A}`);e.length||(e=t(`<div id="${A}" />`),t("body").append(e)),e.addClass("insert-link__dialog-wrapper"),e.setElement(this),e.open()}}),t(`#${A}`).entwine({ReactRoot:null,renderModal(n){var d=this;const l=ss.store,s=ss.apolloClient,u=this.getOriginalAttributes(),c=this.getRequireLinkText(),p=Number(t("#Form_EditForm_ID").val()||0);let f=this.getReactRoot();f||(f=(0,i.createRoot)(this[0]),this.setReactRoot(f)),f.render(r.default.createElement(o.ApolloProvider,{client:s},r.default.createElement(a.Provider,{store:l},r.default.createElement(C,{isOpen:n,onInsert:function(){return d.handleInsert(...arguments)},onClosed:()=>this.close(),title:e.default._t("CMS.LINK_ANCHOR","Link to an anchor on a page"),bodyClassName:"modal__dialog",className:"insert-link__dialog-wrapper--anchor",fileAttributes:u,identifier:"Admin.InsertLinkAnchorModal",requireLinkText:c,currentPageID:p}))))},buildAttributes(e){return{href:`${l.default.serialise({name:"sitetree_link",properties:{id:e.PageID}},!0)}${e.Anchor&&e.Anchor.length?`#${e.Anchor}`:""}`,target:e.TargetBlank?"_blank":"",title:e.Description}},getOriginalAttributes(){const e=this.getElement().getEditor(),n=t(e.getSelectedNode()),r=(n.attr("href")||"").split("#");if(!r[0])return{};const i=l.default.match("sitetree_link",!1,r[0]);return i?{PageID:i.properties.id?parseInt(i.properties.id,10):0,Anchor:r[1]||"",Description:n.attr("title"),TargetBlank:!!n.attr("target")}:{}}})})),tinymce.PluginManager.add(f,(e=>E.init(e)))}()}();
|
2
client/dist/js/TinyMCE_sslink-internal.js
vendored
2
client/dist/js/TinyMCE_sslink-internal.js
vendored
@ -1 +1 @@
|
|||||||
!function(e){function t(r){if(n[r])return n[r].exports;var i=n[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,t),i.l=!0,i.exports}var n={};t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s="./client/src/legacy/TinyMCE_sslink-internal.js")}({"./client/src/legacy/TinyMCE_sslink-internal.js":function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(1),o=r(i),l=n(9),a=r(l),s=n(2),u=r(s),c=n(5),d=r(c),f=n(4),p=n(6),g=n(0),k=r(g),_=n(8),m=r(_),h=n(7),x=n(3),I={init:function(e){a.default.addAction("sslink",{text:o.default._t("CMS.LINKLABEL_PAGE","Page on this site"),onclick:function(e){return e.execCommand("sslinkinternal")},priority:90},e.settings.editorIdentifier).addCommandWithUrlTest("sslinkinternal",/^\[sitetree_link.+]$/),e.addCommand("sslinkinternal",function(){(0,k.default)("#"+e.id).entwine("ss").openLinkInternalDialog()})}},A="insert-link__dialog-wrapper--internal",C=(0,x.provideInjector)((0,h.createInsertLinkModal)("SilverStripe\\CMS\\Controllers\\CMSPageEditController","editorInternalLink"));k.default.entwine("ss",function(e){e("textarea.htmleditor").entwine({openLinkInternalDialog:function(){var t=e("#"+A);t.length||(t=e('<div id="'+A+'" />'),e("body").append(t)),t.addClass("insert-link__dialog-wrapper"),t.setElement(this),t.open()}}),e("#"+A).entwine({renderModal:function(e){var t=this,n=ss.store,r=ss.apolloClient,i=function(){return t.close()},l=function(){return t.handleInsert.apply(t,arguments)},a=this.getOriginalAttributes(),s=this.getRequireLinkText();d.default.render(u.default.createElement(f.ApolloProvider,{client:r},u.default.createElement(p.Provider,{store:n},u.default.createElement(C,{isOpen:e,onInsert:l,onClosed:i,title:o.default._t("CMS.LINK_PAGE","Link to a page"),bodyClassName:"modal__dialog",className:"insert-link__dialog-wrapper--internal",fileAttributes:a,identifier:"Admin.InsertLinkInternalModal",requireLinkText:s}))),this[0])},buildAttributes:function(e){return{href:m.default.serialise({name:"sitetree_link",properties:{id:e.PageID}},!0)+(e.Anchor&&e.Anchor.length?"#"+e.Anchor:""),target:e.TargetBlank?"_blank":"",title:e.Description}},getOriginalAttributes:function(){var t=this.getElement().getEditor(),n=e(t.getSelectedNode()),r=(n.attr("href")||"").split("#");if(!r[0])return{};var i=m.default.match("sitetree_link",!1,r[0]);return i?{PageID:i.properties.id?parseInt(i.properties.id,10):0,Anchor:r[1]||"",Description:n.attr("title"),TargetBlank:!!n.attr("target")}:{}}})}),tinymce.PluginManager.add("sslinkinternal",function(e){return I.init(e)}),t.default=I},0:function(e,t){e.exports=jQuery},1:function(e,t){e.exports=i18n},2:function(e,t){e.exports=React},3:function(e,t){e.exports=Injector},4:function(e,t){e.exports=ReactApollo},5:function(e,t){e.exports=ReactDom},6:function(e,t){e.exports=ReactRedux},7:function(e,t){e.exports=InsertLinkModal},8:function(e,t){e.exports=ShortcodeSerialiser},9:function(e,t){e.exports=TinyMCEActionRegistrar}});
|
!function(){"use strict";var e={939:function(e){e.exports=ApolloClient},648:function(e){e.exports=Injector},595:function(e){e.exports=InsertLinkModal},363:function(e){e.exports=React},691:function(e){e.exports=ReactDomClient},624:function(e){e.exports=ReactRedux},265:function(e){e.exports=ShortcodeSerialiser},196:function(e){e.exports=TinyMCEActionRegistrar},754:function(e){e.exports=i18n},311:function(e){e.exports=jQuery}},t={};function n(i){var r=t[i];if(void 0!==r)return r.exports;var o=t[i]={exports:{}};return e[i](o,o.exports,n),o.exports}!function(){var e=u(n(754)),t=u(n(196)),i=u(n(363)),r=n(691),o=n(939),a=n(624),l=u(n(311)),s=u(n(265)),d=n(595),c=n(648);function u(e){return e&&e.__esModule?e:{default:e}}const p="sslinkinternal",f={init(n){t.default.addAction("sslink",{text:e.default._t("CMS.LINKLABEL_PAGE","Page on this site"),onAction:e=>e.execCommand(p),priority:90},n.getParam("editorIdentifier")).addCommandWithUrlTest(p,/^\[sitetree_link.+]$/),n.addCommand(p,(()=>{(0,l.default)(`#${n.id}`).entwine("ss").openLinkInternalDialog()}))}},g="insert-link__dialog-wrapper--internal",h=(0,c.provideInjector)((0,d.createInsertLinkModal)("SilverStripe\\CMS\\Controllers\\CMSPageEditController","editorInternalLink"));l.default.entwine("ss",(t=>{t("textarea.htmleditor").entwine({openLinkInternalDialog(){let e=t(`#${g}`);e.length||(e=t(`<div id="${g}" />`),t("body").append(e)),e.addClass("insert-link__dialog-wrapper"),e.setElement(this),e.open()}}),t(`#${g}`).entwine({ReactRoot:null,renderModal(t){var n=this;const l=ss.store,s=ss.apolloClient,d=this.getOriginalAttributes(),c=this.getRequireLinkText();let u=this.getReactRoot();u||(u=(0,r.createRoot)(this[0]),this.setReactRoot(u)),u.render(i.default.createElement(o.ApolloProvider,{client:s},i.default.createElement(a.Provider,{store:l},i.default.createElement(h,{isOpen:t,onInsert:function(){return n.handleInsert(...arguments)},onClosed:()=>this.close(),title:e.default._t("CMS.LINK_PAGE","Link to a page"),bodyClassName:"modal__dialog",className:"insert-link__dialog-wrapper--internal",fileAttributes:d,identifier:"Admin.InsertLinkInternalModal",requireLinkText:c}))))},buildAttributes(e){return{href:`${s.default.serialise({name:"sitetree_link",properties:{id:e.PageID}},!0)}${e.Anchor&&e.Anchor.length?`#${e.Anchor}`:""}`,target:e.TargetBlank?"_blank":"",title:e.Description}},getOriginalAttributes(){const e=this.getElement().getEditor(),n=t(e.getSelectedNode()),i=(n.attr("href")||"").split("#");if(!i[0])return{};const r=s.default.match("sitetree_link",!1,i[0]);return r?{PageID:r.properties.id?parseInt(r.properties.id,10):0,Anchor:i[1]||"",Description:n.attr("title"),TargetBlank:!!n.attr("target")}:{}}})})),tinymce.PluginManager.add(p,(e=>f.init(e)))}()}();
|
49
client/dist/js/bundle.js
vendored
49
client/dist/js/bundle.js
vendored
@ -1 +1,48 @@
|
|||||||
!function(e){function t(r){if(n[r])return n[r].exports;var a=n[r]={i:r,l:!1,exports:{}};return e[r].call(a.exports,a,a.exports,t),a.l=!0,a.exports}var n={};t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s="./client/src/bundles/bundle.js")}({"./client/src/boot/index.js":function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}var a=n("./client/src/boot/registerReducers.js"),o=r(a),i=n("./client/src/boot/registerComponents.js"),s=r(i);window.document.addEventListener("DOMContentLoaded",function(){(0,s.default)(),(0,o.default)()})},"./client/src/boot/registerComponents.js":function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(3),o=r(a),i=n("./client/src/components/AnchorSelectorField/AnchorSelectorField.js"),s=r(i),l=n("./client/src/state/history/readOnePageQuery.js"),d=r(l),c=n("./client/src/state/history/rollbackPageMutation.js"),u=r(c);t.default=function(){o.default.component.register("AnchorSelectorField",s.default),o.default.transform("pages-history",function(e){e.component("HistoryViewer.pages-controller-cms-content",d.default,"PageHistoryViewer")}),o.default.transform("pages-history-revert",function(e){e.component("HistoryViewerToolbar.VersionedAdmin.HistoryViewer.SiteTree.HistoryViewerVersionDetail",u.default,"PageRevertMutation")})}},"./client/src/boot/registerReducers.js":function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var a=n(3),o=r(a),i=n(12),s=n("./client/src/state/anchorSelector/AnchorSelectorReducer.js"),l=r(s);t.default=function(){o.default.reducer.register("cms",(0,i.combineReducers)({anchorSelector:l.default}))}},"./client/src/bundles/bundle.js":function(e,t,n){"use strict";n("./client/src/legacy/CMSMain.AddForm.js"),n("./client/src/legacy/CMSMain.EditForm.js"),n("./client/src/legacy/CMSMain.js"),n("./client/src/legacy/CMSMain.Tree.js"),n("./client/src/legacy/CMSPageHistoryController.js"),n("./client/src/legacy/RedirectorPage.js"),n("./client/src/legacy/SiteTreeURLSegmentField.js"),n("./client/src/boot/index.js")},"./client/src/components/AnchorSelectorField/AnchorSelectorField.js":function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t<e.length;t++)n[t]=e[t];return n}return Array.from(e)}function o(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function i(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function s(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}function l(e,t){var n=(0,b.formValueSelector)(t.formid,O.default),r=t&&t.data&&t.data.targetFieldName||"PageID",a=Number(n(e,r)||0),o=[],i=a?e.cms.anchorSelector.pages.find(function(e){return e.id===a}):null;!i||i.loadingState!==E.default.SUCCESS&&i.loadingState!==E.default.DIRTY&&i.loadingState!==E.default.FIELD_ONLY||(o=i.anchors);var s=null;return s=i?i.loadingState:a?E.default.DIRTY:E.default.SUCCESS,{pageId:a,anchors:o,loadingState:s}}function d(e){return{actions:{anchorSelector:(0,_.bindActionCreators)(w,e)}}}Object.defineProperty(t,"__esModule",{value:!0}),t.ConnectedAnchorSelectorField=t.Component=void 0;var c=function(){function e(e,t){for(var n=0;n<t.length;n++){var r=t[n];r.enumerable=r.enumerable||!1,r.configurable=!0,"value"in r&&(r.writable=!0),Object.defineProperty(e,r.key,r)}}return function(t,n,r){return n&&e(t.prototype,n),r&&e(t,r),t}}(),u=n(1),f=r(u),p=n(2),h=r(p),m=n(15),g=r(m),v=n(6),_=n(12),b=n(18),C=n(19),y=r(C),S=n("./client/src/state/anchorSelector/AnchorSelectorActions.js"),w=function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&(t[n]=e[n]);return t.default=e,t}(S),P=n("./client/src/state/anchorSelector/AnchorSelectorStates.js"),E=r(P),x=n(14),F=r(x),R=n(16),A=n(21),O=r(A),T=n(20),j=r(T),L=n(11),M=r(L),D=function(){return null},I=function(e){function t(e){o(this,t);var n=i(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e));return n.handleChange=n.handleChange.bind(n),n.handleLoadingError=n.handleLoadingError.bind(n),n}return s(t,e),c(t,[{key:"componentDidMount",value:function(){this.ensurePagesLoaded()}},{key:"componentDidUpdate",value:function(e){this.props.pageId!==e.pageId&&this.ensurePagesLoaded()}},{key:"ensurePagesLoaded",value:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.props;if(t.loadingState===E.default.UPDATING||t.loadingState===E.default.SUCCESS||!t.pageId)return Promise.resolve();var n=[];t.loadingState===E.default.FIELD_ONLY&&(n=this.props.anchors),t.actions.anchorSelector.beginUpdating(t.pageId);var r=t.data.endpoint.replace(/:id/,t.pageId);return(0,g.default)(r,{credentials:"same-origin"}).then(function(e){return e.json()}).then(function(e){var r=[].concat(a(new Set([].concat(a(e),a(n)))));return t.actions.anchorSelector.updated(t.pageId,r),r}).catch(function(n){t.actions.anchorSelector.updateFailed(t.pageId),e.handleLoadingError(n,t)})}},{key:"getDropdownOptions",value:function(){var e=this,t=this.props.anchors.map(function(e){return{value:e}});return this.props.value&&!this.props.anchors.find(function(t){return t===e.props.value})&&t.unshift({value:this.props.value}),t}},{key:"handleChange",value:function(e){"function"==typeof this.props.onChange&&this.props.onChange(e?e.value:"")}},{key:"handleLoadingError",value:function(e){var t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.props;if(t.onLoadingError===D)throw e;return t.onLoadingError({errors:[{value:e.message,type:"error"}]})}},{key:"render",value:function(){var e={id:this.props.id},t=(0,j.default)("anchorselectorfield",this.props.extraClass),n=this.getDropdownOptions(),r=this.props.value||"",a=f.default._t("CMS.ANCHOR_SELECT_OR_TYPE","Select or enter anchor");return h.default.createElement(R.Creatable,{searchable:!0,options:n,className:t,name:this.props.name,inputProps:e,onChange:this.handleChange,onBlurResetsInput:!0,value:r,placeholder:a,labelKey:"value"})}}]),t}(y.default);I.propTypes={extraClass:M.default.string,id:M.default.string,name:M.default.string.isRequired,onChange:M.default.func,value:M.default.string,attributes:M.default.oneOfType([M.default.object,M.default.array]),pageId:M.default.number,anchors:M.default.array,loadingState:M.default.oneOf(Object.keys(E.default).map(function(e){return E.default[e]})),onLoadingError:M.default.func,data:M.default.shape({endpoint:M.default.string,targetFieldName:M.default.string})},I.defaultProps={value:"",extraClass:"",onLoadingError:D,attributes:{}};var k=(0,v.connect)(l,d)(I);t.Component=I,t.ConnectedAnchorSelectorField=k,t.default=(0,F.default)(k)},"./client/src/legacy/CMSMain.AddForm.js":function(e,t,n){"use strict";var r=n(0);(function(e){return e&&e.__esModule?e:{default:e}})(r).default.entwine("ss",function(e){e(".TreeDropdownField").entwine({OldValue:null}),e("#Form_AddForm_ParentID_Holder .treedropdownfield").entwine({onmatch:function(){this._super(),e(".cms-add-form").updateTypeList()}}),e(".cms-add-form .parent-mode :input").entwine({onclick:function(e){var t=this.closest("form").find("#Form_AddForm_ParentID_Holder .TreeDropdownField");"top"==this.val()?(t.setOldValue(t.getValue()),t.setValue(0)):(t.setValue(t.getOldValue()||0),t.setOldValue(null)),t.refresh(),t.trigger("change")}}),e(".cms-add-form").entwine({ParentCache:{},onadd:function(){var t=this;this.find("#Form_AddForm_ParentID_Holder .TreeDropdownField").on("change",function(){t.updateTypeList()}),this.find(".SelectionGroup.parent-mode").on("change",function(){t.updateTypeList()}),"top"==e(".cms-add-form .parent-mode :input").val()&&this.updateTypeList()},loadCachedChildren:function(e){var t=this.getParentCache();return void 0!==t[e]?t[e]:null},saveCachedChildren:function(e,t){var n=this.getParentCache();n[e]=t,this.setParentCache(n)},updateTypeList:function(){var t=this.data("hints"),n=this.find("#Form_AddForm_ParentID"),r=this.find("input[name=ParentModeField]:checked").val(),a=n.data("metadata"),o="child"===r?n.getValue():null,i=a?a.ClassName:null,s=i&&"child"===r&&o?i:"Root",l=void 0!==t[s]?t[s]:null,d=this,c=l&&void 0!==l.defaultChild?l.defaultChild:null,u=[];if(o){if(this.hasClass("loading"))return;return this.addClass("loading"),null!==(u=this.loadCachedChildren(o))?(this.updateSelectionFilter(u,c),void this.removeClass("loading")):(e.ajax({url:d.data("childfilter"),data:{ParentID:o},success:function(e){d.saveCachedChildren(o,e),d.updateSelectionFilter(e,c)},complete:function(){d.removeClass("loading")}}),!1)}u=l&&void 0!==l.disallowedChildren?l.disallowedChildren:[],this.updateSelectionFilter(u,c)},updateSelectionFilter:function(t,n){var r=this.find("#Form_AddForm_PageType div.radio.selected")[0],a=!1,o=null;if(this.find("#Form_AddForm_PageType div.radio").each(function(n,i){var s=e(this).find("input").val(),l=-1===e.inArray(s,t);i===r&&l&&(a=!0),e(this).setEnabled(l),l||e(this).setSelected(!1),o=null===o?l:o&&l}),a)var i=e(r).parents("li:first");else if(n)var i=this.find("#Form_AddForm_PageType div.radio input[value="+n+"]").parents("li:first");else var i=this.find("#Form_AddForm_PageType div.radio:not(.disabled):first");i.setSelected(!0),i.siblings().setSelected(!1),this.find("#Form_AddForm_PageType div.radio:not(.disabled)").length?this.find("button[name=action_doAdd]").removeAttr("disabled"):this.find("button[name=action_doAdd]").attr("disabled","disabled"),this.find(".message-restricted")[o?"hide":"show"]()}}),e(".cms-add-form #Form_AddForm_PageType div.radio").entwine({onclick:function(e){this.setSelected(!0)},setSelected:function(e){var t=this.find("input");e&&!t.is(":disabled")?(this.siblings().setSelected(!1),this.toggleClass("selected",!0),t.prop("checked",!0)):(this.toggleClass("selected",!1),t.prop("checked",!1))},setEnabled:function(t){e(this).toggleClass("disabled",!t),t?e(this).find("input").removeAttr("disabled"):e(this).find("input").attr("disabled","disabled").removeAttr("checked")}}),e(".cms-content-addpage-button").entwine({onclick:function(t){var n,r=e(".cms-tree"),a=e(".cms-list"),o=0;if(r.is(":visible")){var i=r.jstree("get_selected");o=i?e(i[0]).data("id"):null}else{var s=a.find('input[name="Page[GridState]"]').val();s&&(o=parseInt(JSON.parse(s).ParentID,10))}var l,d={selector:this.data("targetPanel"),pjax:this.data("pjax")};o?(n=this.data("extraParams")?this.data("extraParams"):"",l=e.path.addSearchParams(i18n.sprintf(this.data("urlAddpage"),o),n)):l=this.attr("href"),e(".cms-container").loadPanel(l,null,d),t.preventDefault(),this.blur()}})})},"./client/src/legacy/CMSMain.EditForm.js":function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){return function(){var t=e.apply(this,arguments);return new Promise(function(e,n){function r(a,o){try{var i=t[a](o),s=i.value}catch(e){return void n(e)}if(!i.done)return Promise.resolve(s).then(function(e){r("next",e)},function(e){r("throw",e)});e(s)}return r("next")})}}var o=n(0),i=r(o),s=n(1),l=r(s),d=n("./node_modules/@silverstripe/reactstrap-confirm/dist/index.js"),c=r(d);i.default.entwine("ss",function(e){e(".cms-edit-form :input#Form_EditForm_ClassName").entwine({onchange:function(){alert(l.default._t("CMS.ALERTCLASSNAME"))}}),e(".cms-edit-form input[name=Title]").entwine({onmatch:function(){var t=this;t.data("OrigVal",t.val());var n=t.closest("form"),r=e("input:text[name=URLSegment]",n),a=e("input[name=LiveLink]",n);r.length>0&&(t._addActions(),this.on("change",function(n){var o=t.data("OrigVal"),i=t.val();t.data("OrigVal",i),0===r.val().indexOf(r.data("defaultUrl"))&&""==a.val()?t.updateURLSegment(i):e(".update",t.parent()).show().parent(".form__field-holder").addClass("input-group"),t.updateRelatedFields(i,o),t.updateBreadcrumbLabel(i)})),this._super()},onunmatch:function(){this._super()},updateRelatedFields:function(t,n){this.parents("form").find("input[name=MetaTitle], input[name=MenuTitle]").each(function(){var r=e(this);r.val()==n&&(r.val(t),r.updatedRelatedFields&&r.updatedRelatedFields())})},updateURLSegment:function(t){var n=e("input:text[name=URLSegment]",this.closest("form")),r=n.closest(".field.urlsegment"),a=e(".update",this.parent());r.update(t),a.is(":visible")&&a.hide().parent(".form__field-holder").removeClass("input-group")},updateBreadcrumbLabel:function(t){var n=(e(".cms-edit-form input[name=ID]").val(),e("span.cms-panel-link.crumb"));t&&""!=t&&n.text(t)},_addActions:function(){var t,n=this;t=e("<button />",{class:"update btn btn-outline-secondary form__field-update-url",text:l.default._t("CMS.UpdateURL"),type:"button",click:function(e){e.preventDefault(),n.updateURLSegment(n.val())}}),t.insertAfter(n),t.hide()}}),e(".cms-edit-form .parentTypeSelector").entwine({onmatch:function(){var e=this;this.find(":input[name=ParentType]").on("click",function(t){e._toggleSelection(t)}),this.find(".TreeDropdownField").on("change",function(t){e._changeParentId(t)}),this._changeParentId(),this._toggleSelection(),this._super()},onunmatch:function(){this._super()},_toggleSelection:function(t){var n=this.find(":input[name=ParentType]:checked").val(),r=this.find("#Form_EditForm_ParentID_Holder");"root"==n?this.find(":input[name=ParentID]").val(0):this.find(":input[name=ParentID]").val(this.find("#Form_EditForm_ParentType_subpage").data("parentIdValue")),"root"!=n?r.slideDown(400,function(){e(this).css("overflow","visible")}):r.slideUp()},_changeParentId:function(e){var t=this.find(":input[name=ParentID]").val();this.find("#Form_EditForm_ParentType_subpage").data("parentIdValue",t)}}),e(".cms-edit-form .btn-toolbar #Form_EditForm_action_doRollback, .cms-edit-form .btn-toolbar #Form_EditForm_action_rollback").entwine({onclick:function(e){if(this.is(":disabled"))return e.preventDefault(),!1;var t=this.parents("form:first").find(":input[name=Version]").val(),n=t?l.default.sprintf(l.default._t("CMS.RollbackToVersion","Do you really want to roll back to version #%s of this page?"),t):l.default._t("CMS.ConfirmRestoreFromLive","Are you sure you want to revert draft to when the page was last published?");return confirm(n)?(this.parents("form:first").addClass("loading"),this._super(e)):(e.preventDefault(),!1)}}),e(".cms-edit-form .btn-toolbar #Form_EditForm_action_archive:not(.homepage-warning)").entwine({onclick:function(e){var t=this.parents("form:first"),n="";return n=t.find("input[name=ArchiveWarningMessage]").val().replace(/\\n/g,"\n"),!!confirm(n)&&(this.parents("form:first").addClass("loading"),this._super(e))}}),e(".cms-edit-form .btn-toolbar #Form_EditForm_action_restore").entwine({onclick:function(e){var t=this.parents("form:first"),n=t.find(":input[name=Version]").val(),r="",a=this.data("toRoot");return r=l.default.sprintf(l.default._t(a?"CMS.RestoreToRoot":"CMS.Restore"),n),!!confirm(r)&&(this.parents("form:first").addClass("loading"),this._super(e))}}),e(".cms-edit-form .btn-toolbar #Form_EditForm_action_unpublish:not(.homepage-warning)").entwine({onclick:function(e){var t=this.parents("form:first"),n=t.find(":input[name=Version]").val(),r="";return r=l.default.sprintf(l.default._t("CMS.Unpublish"),n),!!confirm(r)&&(this.parents("form:first").addClass("loading"),this._super(e))}}),e(".cms-edit-form.changed").entwine({onmatch:function(t){this.find("button[data-text-alternate]").each(function(){var t=e(this),n=t.find(".btn__title"),r=t.data("textAlternate");r&&(t.data("textStandard",n.text()),n.text(r));var a=t.data("btnAlternate");a&&(t.data("btnStandard",t.attr("class")),t.attr("class",a),t.removeClass("btn-outline-secondary").addClass("btn-primary"));var o=t.data("btnAlternateAdd");o&&t.addClass(o);var i=t.data("btnAlternateRemove");i&&t.removeClass(i)}),this._super(t)},onunmatch:function(t){this.find("button[data-text-alternate]").each(function(){var t=e(this),n=t.find(".btn__title"),r=t.data("textStandard");r&&n.text(r);var a=t.data("btnStandard");a&&(t.attr("class",a),t.addClass("btn-outline-secondary").removeClass("btn-primary"));var o=t.data("btnAlternateAdd");o&&t.removeClass(o);var i=t.data("btnAlternateRemove");i&&t.addClass(i)}),this._super(t)}}),e(".cms-edit-form .btn-toolbar button[name=action_publish]").entwine({onbuttonafterrefreshalternate:function(){this.data("showingAlternate")?(this.addClass("btn-primary"),this.removeClass("btn-secondary")):(this.removeClass("btn-primary"),this.addClass("btn-secondary"))}}),e(".cms-edit-form .btn-toolbar button[name=action_save]").entwine({onbuttonafterrefreshalternate:function(){this.data("showingAlternate")?(this.addClass("btn-primary"),this.removeClass("btn-secondary")):(this.removeClass("btn-primary"),this.addClass("btn-secondary"))}}),e('.cms-edit-form.CMSPageSettingsController input[name="ParentType"]:checked').entwine({onmatch:function(){this.redraw(),this._super()},onunmatch:function(){this._super()},redraw:function(){var t=e(".cms-edit-form.CMSPageSettingsController #Form_EditForm_ParentID_Holder");"Form_EditForm_ParentType_root"==e(this).attr("id")?t.slideUp():t.slideDown()},onclick:function(){this.redraw()}}),"Form_EditForm_ParentType_root"==e('.cms-edit-form.CMSPageSettingsController input[name="ParentType"]:checked').attr("id")&&e(".cms-edit-form.CMSPageSettingsController #Form_EditForm_ParentID_Holder").hide();var t=!1;e(".cms-edit-form .btn-toolbar #Form_EditForm_action_unpublish.homepage-warning,.cms-edit-form .btn-toolbar #Form_EditForm_action_archive.homepage-warning,#Form_EditForm_URLSegment_Holder.homepage-warning .btn.update").entwine({onclick:function(){function e(e){return n.apply(this,arguments)}var n=a(regeneratorRuntime.mark(function e(n){var r;return regeneratorRuntime.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if(!t){e.next=2;break}return e.abrupt("return",this._super(n));case 2:return n.stopPropagation(),r=l.default._t("CMS.RemoveHomePageWarningMessage","Warning: This page is the home page. By changing the URL segment visitors will not be able to view it."),e.next=6,(0,c.default)(r,{title:l.default._t("CMS.RemoveHomePageWarningTitle","Remove your home page?"),confirmLabel:l.default._t("CMS.RemoveHomePageWarningLabel","Remove"),confirmColor:"danger"});case 6:if(!e.sent){e.next=10;break}t=!0,this.trigger("click"),t=!1;case 10:return e.abrupt("return",!1);case 11:case"end":return e.stop()}},e,this)}));return e}()})})},"./client/src/legacy/CMSMain.Tree.js":function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){return function(){var t=e.apply(this,arguments);return new Promise(function(e,n){function r(a,o){try{var i=t[a](o),s=i.value}catch(e){return void n(e)}if(!i.done)return Promise.resolve(s).then(function(e){r("next",e)},function(e){r("throw",e)});e(s)}return r("next")})}}var o=n(0),i=r(o),s=n(1),l=r(s),d=n("./node_modules/@silverstripe/reactstrap-confirm/dist/index.js"),c=r(d);i.default.entwine("ss.tree",function(e){e(".cms-tree").entwine({fromDocument:{"oncontext_show.vakata":function(e){this.adjustContextClass()}},adjustContextClass:function(){var t=e("#vakata-contextmenu").find("ul ul");t.each(function(n){var r="1",a=e(t[n]).find("li").length;a>20?r="3":a>10&&(r="2"),e(t[n]).addClass("vakata-col-"+r).removeClass("right"),e(t[n]).find("li").on("mouseenter",function(t){e(this).parent("ul").removeClass("right")})})},showListViewFor:function(t){localStorage.setItem("ss.pages-view-type","listview");var n=this.closest(".cms-content-view"),r=n.data("url-listviewroot"),a=e.path.addSearchParams(r,{ParentID:t}),o=e("base").attr("href")||"";window.location.assign(o+a)},getTreeConfig:function(){var t=this,n=this._super();return this.getHints(),n.plugins.push("contextmenu"),n.contextmenu={items:function(n){var r={edit:{label:n.hasClass("edit-disabled")?l.default._t("CMS.EditPage","Edit page",100,"Used in the context menu when right-clicking on a page node in the CMS tree"):l.default._t("CMS.ViewPage","View page",100,"Used in the context menu when right-clicking on a page node in the CMS tree"),action:function(n){e(".cms-container").entwine(".ss").loadPanel(l.default.sprintf(t.data("urlEditpage"),n.data("id")))}}};n.hasClass("nochildren")||(r.showaslist={label:l.default._t("CMS.ShowAsList"),action:function(e){t.showListViewFor(e.data("id"))}});var a=(n.data("pagetype"),n.data("id")),o=n.find(">a .item").data("allowedchildren"),i={},s=!1;return e.each(o,function(n,r){s=!0,i["allowedchildren-"+r.ClassName]={label:'<span class="jstree-pageicon '+r.IconClass+'"></span>'+r.Title,_class:"class-"+r.ClassName.replace(/[^a-zA-Z0-9\-_:.]+/g,"_"),action:function(n){e(".cms-container").entwine(".ss").loadPanel(e.path.addSearchParams(l.default.sprintf(t.data("urlAddpage"),a,r.ClassName),t.data("extraParams")))}}}),s&&(r.addsubpage={label:l.default._t("CMS.AddSubPage","Add page under this page",100,"Used in the context menu when right-clicking on a page node in the CMS tree"),submenu:i}),n.hasClass("edit-disabled")||(r.duplicate={label:l.default._t("CMS.Duplicate"),submenu:[{label:l.default._t("CMS.ThisPageOnly"),action:function(n){e(".cms-container").entwine(".ss").loadPanel(e.path.addSearchParams(l.default.sprintf(t.data("urlDuplicate"),n.data("id")),t.data("extraParams")))}},{label:l.default._t("CMS.ThisPageAndSubpages"),action:function(n){e(".cms-container").entwine(".ss").loadPanel(e.path.addSearchParams(l.default.sprintf(t.data("urlDuplicatewithchildren"),n.data("id")),t.data("extraParams")))}}]}),r}},n},canMove:function(){function e(e){return t.apply(this,arguments)}var t=a(regeneratorRuntime.mark(function e(t){var n,r,a,o;return regeneratorRuntime.wrap(function(e){for(;;)switch(e.prev=e.next){case 0:if(n=t.rslt.o.find(".homepage").first().length>0){e.next=3;break}return e.abrupt("return",!0);case 3:if(r=t.rslt.op.data("id"),a=t.rslt.np.data("id"),r!==a){e.next=7;break}return e.abrupt("return",!0);case 7:return o=l.default._t("CMS.RemoveHomePageWarningMessage","Warning: This page is the home page. By changing the URL segment visitors will not be able to view it."),e.next=10,(0,c.default)(o,{title:l.default._t("CMS.RemoveHomePageWarningTitle","Remove your home page?"),confirmLabel:l.default._t("CMS.RemoveHomePageWarningLabel","Remove"),confirmColor:"danger"});case 10:return e.abrupt("return",e.sent);case 11:case"end":return e.stop()}},e,this)}));return e}()}),e(".cms-tree a.jstree-clicked").entwine({onmatch:function(){var e=this,t=e.parents(".cms-tree-view-sidebar");if(e.offset().top<0||e.offset().top>t.height()-e.height()){var n=e.parent();n.prev().length&&(n=n.prev()),n.get(0).scrollIntoView()}}}),e(".cms-tree-filtered .clear-filter").entwine({onclick:function(){window.location=location.protocol+"//"+location.host+location.pathname}}),e(".cms-tree .subtree-list-link").entwine({onclick:function(e){e.preventDefault(),this.closest(".cms-tree").showListViewFor(this.data("id"))}})})},"./client/src/legacy/CMSMain.js":function(e,t,n){"use strict";var r=n(0);(function(e){return e&&e.__esModule?e:{default:e}})(r).default.entwine("ss",function(e){e(".cms-content-header-info").entwine({"from .cms-panel":{ontoggle:function(e){var t=this.closest(".cms-content").find(e.target);0!==t.length&&this.parent()[t.hasClass("collapsed")?"addClass":"removeClass"]("collapsed")}}}),e(".cms-panel-deferred.cms-content-view").entwine({onadd:function(){if(!this.data("no-ajax")){var e=localStorage.getItem("ss.pages-view-type")||"treeview";this.closest(".cms-content-tools").length>0&&(e="treeview");var t=this.data("url-"+e),n=localStorage.getItem("ss.pages-view-filtered");"string"==typeof n&&"false"===n.toLowerCase()&&(n=!1),localStorage.setItem("ss.pages-view-filtered",!1),this.data("deferredNoCache",n||"listview"===e),this.data("url",t+location.search),this._super()}}}),e(".js-injector-boot .search-holder--cms").entwine({search:function(e){localStorage.setItem("ss.pages-view-filtered",!0),this._super(e)}}),e(".cms .page-view-link").entwine({onclick:function(t){t.preventDefault();var n=e(this).data("view"),r=this.closest(".cms-content-view"),a=r.data("url-"+n),o=0!==r.closest(".cms-content-tools").length;if(localStorage.setItem("ss.pages-view-type",n),o&&"listview"===n){var i=e("base").attr("href")||"";return void window.location.assign(i+r.data("url-listviewroot"))}r.data("url",a+location.search),r.redraw()}}),e(".cms .cms-clear-filter").entwine({onclick:function(t){t.preventDefault(),window.location=e(this).prop("href")}}),e(".cms-content-toolbar").entwine({onmatch:function(){var t=this;this._super(),e.each(this.find(".cms-actions-buttons-row .tool-button"),function(){var n=e(this),r=n.data("toolid");n.hasClass("active"),void 0!==r&&(n.data("active",!1).removeClass("active"),e("#"+r).hide(),t.bindActionButtonEvents(n))})},onunmatch:function(){var t=this;this._super(),e.each(this.find(".cms-actions-buttons-row .tool-button"),function(){var n=e(this);t.unbindActionButtonEvents(n)})},bindActionButtonEvents:function(e){var t=this;e.on("click.cmsContentToolbar",function(n){t.showHideTool(e)})},unbindActionButtonEvents:function(e){e.off(".cmsContentToolbar")},showHideTool:function(t){var n=t.data("active"),r=t.data("toolid"),a=e("#"+r);e.each(this.find(".cms-actions-buttons-row .tool-button"),function(){var t=e(this),n=e("#"+t.data("toolid"));t.data("toolid")!==r&&(n.hide(),t.data("active",!1))}),t[n?"removeClass":"addClass"]("active"),a[n?"hide":"show"](),t.data("active",!n)}})})},"./client/src/legacy/CMSPageHistoryController.js":function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}var a=n(0),o=r(a),i=n(1),s=r(i);o.default.entwine("ss",function(e){e("#Form_VersionsForm").entwine({onmatch:function(){this._super()},onunmatch:function(){this._super()},onsubmit:function(t){t.preventDefault();var n=this.find(":input[name=ID]").val();if(!n)return!1;var r=null,a=null,o=null,i=this.find(":input[name=CompareMode]").is(":checked"),l=this.find("table input[type=checkbox]").filter(":checked");if(i){if(2!==l.length)return!1;a=l.eq(0).val(),o=l.eq(1).val(),r=s.default.sprintf(this.data("linkTmplCompare"),n,o,a)}else a=l.eq(0).val(),r=s.default.sprintf(this.data("linkTmplShow"),n,a);return e(".cms-container").loadPanel(r,"",{pjax:"CurrentForm"}),!0}}),e("#Form_VersionsForm input[name=ShowUnpublished]").entwine({onmatch:function(){this.toggle(),this._super()},onunmatch:function(){this._super()},onchange:function(){this.toggle()},toggle:function(){var t=e(this),n=t.parents("form").find("tr[data-published=false]");t.attr("checked")?n.removeClass("ui-helper-hidden").show():n.addClass("ui-helper-hidden").hide()._unselect()}}),e("#Form_VersionsForm tbody tr").entwine({onclick:function(){var e=this.parents("form").find(":input[name=CompareMode]").attr("checked"),t=this.siblings(".active");return e&&this.hasClass("active")?void this._unselect():e?t.length>1?void alert(s.default._t("CMS.ONLYSELECTTWO","You can only compare two versions at this time.")):(this._select(),void(1===t.length&&this.parents("form").submit())):(this._select(),t._unselect(),void this.parents("form").submit())},_unselect:function(){this.get(0).classList.remove("active"),this.find(":input[type=checkbox][checked]").attr("checked",!1)},_select:function(){this.addClass("active"),this.find(":input[type=checkbox]").attr("checked",!0)}})})},"./client/src/legacy/RedirectorPage.js":function(e,t,n){"use strict";var r=n(0);(function(e){return e&&e.__esModule?e:{default:e}})(r).default.entwine("ss",function(e){e("#Form_EditForm_RedirectionType input").entwine({onmatch:function(){e(this).attr("checked")&&this.toggle(),this._super()},onunmatch:function(){this._super()},onclick:function(){this.toggle()},toggle:function(){"Internal"==e(this).attr("value")?(e("#Form_EditForm_ExternalURL_Holder").hide(),e("#Form_EditForm_LinkToID_Holder").show(),e("#Form_EditForm_LinkToFile_Holder").hide()):"External"==e(this).attr("value")?(e("#Form_EditForm_ExternalURL_Holder").show(),e("#Form_EditForm_LinkToID_Holder").hide(),e("#Form_EditForm_LinkToFile_Holder").hide()):(e("#Form_EditForm_LinkToFile_Holder").show(),e("#Form_EditForm_ExternalURL_Holder").hide(),e("#Form_EditForm_LinkToID_Holder").hide())}})})},"./client/src/legacy/SiteTreeURLSegmentField.js":function(e,t,n){"use strict";var r=n(0);(function(e){return e&&e.__esModule?e:{default:e}})(r).default.entwine("ss",function(e){e(".field.urlsegment:not(.readonly)").entwine({MaxPreviewLength:55,Ellipsis:"...",onmatch:function(){this.find(":text").length&&this.toggleEdit(!1),this.redraw(),this._super()},redraw:function(){var e=this.find(":text"),t=decodeURI(e.data("prefix")+e.val()),n=t;t.length>this.getMaxPreviewLength()&&(n=this.getEllipsis()+t.substr(t.length-this.getMaxPreviewLength(),t.length)),this.find(".URL-link").attr("href",encodeURI(t+e.data("suffix"))).text(n)},toggleEdit:function(e){var t=this.find(":text");this.find(".preview-holder")[e?"hide":"show"](),this.find(".edit-holder")[e?"show":"hide"](),e&&(t.data("origval",t.val()),t.focus())},update:function(){var e=this,t=this.find(":text"),n=t.data("origval"),r=arguments[0],a=r&&""!==r?r:t.val();n!=a?(this.addClass("loading"),this.suggest(a,function(n){t.val(decodeURIComponent(n.value)),e.toggleEdit(!1),e.removeClass("loading"),e.redraw()})):(this.toggleEdit(!1),this.redraw())},cancel:function(){var e=this.find(":text");e.val(e.data("origval")),this.toggleEdit(!1)},suggest:function(t,n){var r=this,a=r.find(":text"),o=e.path.parseUrl(r.closest("form").attr("action")),i=o.hrefNoSearch+"/field/"+a.attr("name")+"/suggest/?value="+encodeURIComponent(t);o.search&&(i+="&"+o.search.replace(/^\?/,"")),e.ajax({url:i,success:function(e){n.apply(this,arguments)},error:function(e,t){e.statusText=e.responseText},complete:function(){r.removeClass("loading")}})}}),e(".field.urlsegment .text").entwine({onkeydown:function(e){13===e.keyCode&&(e.preventDefault(),this.closest(".field").update())}}),e(".field.urlsegment .edit").entwine({onclick:function(e){e.preventDefault(),this.closest(".field").toggleEdit(!0)}}),e(".field.urlsegment .update").entwine({onclick:function(e){e.preventDefault(),this.closest(".field").update()}}),e(".field.urlsegment .cancel").entwine({onclick:function(e){e.preventDefault(),this.closest(".field").cancel()}})})},"./client/src/state/anchorSelector/AnchorSelectorActionTypes.js":function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={ANCHORSELECTOR_CURRENT_FIELD:"ANCHORSELECTOR_CURRENT_FIELD",ANCHORSELECTOR_UPDATED:"ANCHORSELECTOR_UPDATED",ANCHORSELECTOR_UPDATING:"ANCHORSELECTOR_UPDATING",ANCHORSELECTOR_UPDATE_FAILED:"ANCHORSELECTOR_UPDATE_FAILED"}},"./client/src/state/anchorSelector/AnchorSelectorActions.js":function(e,t,n){"use strict";function r(e){return{type:l.default.ANCHORSELECTOR_UPDATING,payload:{pageId:e}}}function a(e,t){var n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];return{type:l.default.ANCHORSELECTOR_UPDATED,payload:{pageId:e,anchors:t,cacheResult:n}}}function o(e,t,n){return{type:l.default.ANCHORSELECTOR_CURRENT_FIELD,payload:{pageId:e,anchors:t,fieldID:n}}}function i(e){return{type:l.default.ANCHORSELECTOR_UPDATE_FAILED,payload:{pageId:e}}}Object.defineProperty(t,"__esModule",{value:!0}),t.beginUpdating=r,t.updated=a,t.updatedCurrentField=o,t.updateFailed=i;var s=n("./client/src/state/anchorSelector/AnchorSelectorActionTypes.js"),l=function(e){return e&&e.__esModule?e:{default:e}}(s)},"./client/src/state/anchorSelector/AnchorSelectorReducer.js":function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}function a(e){if(Array.isArray(e)){for(var t=0,n=Array(e.length);t<e.length;t++)n[t]=e[t];return n}return Array.from(e)}function o(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:f,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null,n=function(n,r){var o=t.payload.pageId;return(0,s.default)({pages:[].concat(a(e.pages.filter(function(e){return e.id!==o})),[{id:o,loadingState:n,anchors:r}]).sort(function(e,t){return e.id-t.id})})};switch(t.type){case d.default.ANCHORSELECTOR_UPDATING:return n(u.default.UPDATING,[]);case d.default.ANCHORSELECTOR_UPDATED:var r=t.payload,o=r.anchors,i=r.cacheResult,l=u.default.SUCCESS,c=u.default.DIRTY;return n(i?l:c,o);case d.default.ANCHORSELECTOR_CURRENT_FIELD:var p=t.payload.anchors;return n(u.default.FIELD_ONLY,p);case d.default.ANCHORSELECTOR_UPDATE_FAILED:return n(u.default.FAILED,[]);default:return e}}Object.defineProperty(t,"__esModule",{value:!0}),t.default=o;var i=n(13),s=r(i),l=n("./client/src/state/anchorSelector/AnchorSelectorActionTypes.js"),d=r(l),c=n("./client/src/state/anchorSelector/AnchorSelectorStates.js"),u=r(c),f=(0,s.default)({pages:[]})},"./client/src/state/anchorSelector/AnchorSelectorStates.js":function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={SUCCESS:"SUCCESS",DIRTY:"DIRTY",FIELD_ONLY:"FIELD_ONLY",UPDATING:"UPDATING",FAILED:"FAILED"}},"./client/src/state/history/readOnePageQuery.js":function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.config=t.query=void 0;var r=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},a=function(e,t){return Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}(["\nquery ReadHistoryViewerPage ($page_id: ID!, $limit: Int!, $offset: Int!) {\n readOnePage(\n versioning: {\n mode: ALL_VERSIONS\n },\n filter: {\n id: { eq: $page_id }\n }\n ) {\n id\n versions (limit: $limit, offset: $offset, sort: {\n version: DESC\n }) {\n pageInfo {\n totalCount\n }\n nodes {\n version\n absoluteLink\n author {\n firstName\n surname\n }\n publisher {\n firstName\n surname\n }\n deleted\n draft\n published\n liveVersion\n latestDraftVersion\n lastEdited\n }\n }\n }\n}\n"],["\nquery ReadHistoryViewerPage ($page_id: ID!, $limit: Int!, $offset: Int!) {\n readOnePage(\n versioning: {\n mode: ALL_VERSIONS\n },\n filter: {\n id: { eq: $page_id }\n }\n ) {\n id\n versions (limit: $limit, offset: $offset, sort: {\n version: DESC\n }) {\n pageInfo {\n totalCount\n }\n nodes {\n version\n absoluteLink\n author {\n firstName\n surname\n }\n publisher {\n firstName\n surname\n }\n deleted\n draft\n published\n liveVersion\n latestDraftVersion\n lastEdited\n }\n }\n }\n}\n"]),o=n(4),i=n(10),s=function(e){return e&&e.__esModule?e:{default:e}}(i),l=(0,s.default)(a),d={options:function(e){var t=e.recordId,n=e.limit;return{variables:{limit:n,offset:((e.page||1)-1)*n,page_id:t},fetchPolicy:"network-only"}},props:function(e){var t=e.data,n=t.error,a=t.refetch,o=t.readOnePage,i=t.loading,s=e.ownProps,l=s.actions,d=void 0===l?{versions:{}}:l,c=s.limit,u=s.recordId,f=o||null,p=n&&n.graphQLErrors&&n.graphQLErrors.map(function(e){return e.message});return{loading:i||!f,versions:f,graphQLErrors:p,actions:r({},d,{versions:r({},f,{goToPage:function(e){a({offset:((e||1)-1)*c,limit:c,page_id:u})}})})}}};t.query=l,t.config=d,t.default=(0,o.graphql)(l,d)},"./client/src/state/history/rollbackPageMutation.js":function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.config=t.mutation=void 0;var r=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},a=function(e,t){return Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}(["\nmutation rollbackPage($id:ID!, $toVersion:Int!) {\n rollbackPage(\n id: $id\n toVersion: $toVersion\n ) {\n id\n }\n}\n"],["\nmutation rollbackPage($id:ID!, $toVersion:Int!) {\n rollbackPage(\n id: $id\n toVersion: $toVersion\n ) {\n id\n }\n}\n"]),o=n(4),i=n(10),s=function(e){return e&&e.__esModule?e:{default:e}}(i),l=(0,s.default)(a),d={props:function(e){var t=e.mutate,n=e.ownProps.actions,a=function(e,n){return t({variables:{id:e,toVersion:n}})};return{actions:r({},n,{rollbackPage:a,revertToVersion:a})}},options:{refetchQueries:["ReadHistoryViewerPage"]}};t.mutation=l,t.config=d,t.default=(0,o.graphql)(l,d)},"./node_modules/@babel/runtime/helpers/extends.js":function(e,t){function n(){return e.exports=n=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(e[r]=n[r])}return e},e.exports.default=e.exports,e.exports.__esModule=!0,n.apply(this,arguments)}e.exports=n,e.exports.default=e.exports,e.exports.__esModule=!0},"./node_modules/@babel/runtime/helpers/inheritsLoose.js":function(e,t,n){function r(e,t){e.prototype=Object.create(t.prototype),e.prototype.constructor=e,a(e,t)}var a=n("./node_modules/@babel/runtime/helpers/setPrototypeOf.js");e.exports=r,e.exports.default=e.exports,e.exports.__esModule=!0},"./node_modules/@babel/runtime/helpers/interopRequireDefault.js":function(e,t){function n(e){return e&&e.__esModule?e:{default:e}}e.exports=n,e.exports.default=e.exports,e.exports.__esModule=!0},"./node_modules/@babel/runtime/helpers/interopRequireWildcard.js":function(e,t,n){function r(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(r=function(e){return e?n:t})(e)}function a(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!==o(e)&&"function"!=typeof e)return{default:e};var n=r(t);if(n&&n.has(e))return n.get(e);var a={},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var s in e)if("default"!==s&&Object.prototype.hasOwnProperty.call(e,s)){var l=i?Object.getOwnPropertyDescriptor(e,s):null;l&&(l.get||l.set)?Object.defineProperty(a,s,l):a[s]=e[s]}return a.default=e,n&&n.set(e,a),a}var o=n("./node_modules/@babel/runtime/helpers/typeof.js").default;e.exports=a,e.exports.default=e.exports,e.exports.__esModule=!0},"./node_modules/@babel/runtime/helpers/setPrototypeOf.js":function(e,t){function n(t,r){return e.exports=n=Object.setPrototypeOf||function(e,t){return e.__proto__=t,e},e.exports.default=e.exports,e.exports.__esModule=!0,n(t,r)}e.exports=n,e.exports.default=e.exports,e.exports.__esModule=!0},"./node_modules/@babel/runtime/helpers/typeof.js":function(e,t){function n(t){"@babel/helpers - typeof";return"function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?(e.exports=n=function(e){return typeof e},e.exports.default=e.exports,e.exports.__esModule=!0):(e.exports=n=function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},e.exports.default=e.exports,e.exports.__esModule=!0),n(t)}e.exports=n,e.exports.default=e.exports,e.exports.__esModule=!0},"./node_modules/@silverstripe/reactstrap-confirm/dist/Confirmation.js":function(e,t,n){"use strict";var r=n("./node_modules/@babel/runtime/helpers/interopRequireWildcard.js"),a=n("./node_modules/@babel/runtime/helpers/interopRequireDefault.js");t.__esModule=!0,t.default=void 0;var o=a(n("./node_modules/@babel/runtime/helpers/inheritsLoose.js")),i=r(n(2)),s=a(n(11)),l=n(17),d=function(e){function t(t){var n;return n=e.call(this,t)||this,n.state={isOpen:!0},n}return(0,o.default)(t,e),t.prototype.render=function(){var e=this,t=this.props,n=t.onConfirm,r=t.onCancel,a=t.title,o=t.body,s=t.confirmLabel,d=t.confirmColor,c=t.dismissLabel,u=t.showDismissButton,f=this.state.isOpen,p=function(){"function"==typeof r&&r(),e.setState({isOpen:!1})},h=function(){n(),e.setState({isOpen:!1})};return i.default.createElement(l.Modal,{isOpen:f,toggle:p},a&&i.default.createElement(l.ModalHeader,{toggle:p},a),i.default.createElement(l.ModalBody,null,o),i.default.createElement(l.ModalFooter,null,i.default.createElement(l.Button,{color:d,onClick:h},s),(u||!a)&&i.default.createElement(l.Button,{onClick:p},c||"Cancel")))},t}(i.Component);d.propTypes={onConfirm:s.default.func.isRequired,body:s.default.string.isRequired,onCancel:s.default.func,title:s.default.string,confirmLabel:s.default.string,confirmColor:s.default.string,dismissLabel:s.default.string},d.defaultProps={confirmLabel:"Confirm",confirmColor:"primary"};var c=d;t.default=c},"./node_modules/@silverstripe/reactstrap-confirm/dist/confirm.js":function(e,t,n){"use strict";var r=n("./node_modules/@babel/runtime/helpers/interopRequireDefault.js");t.__esModule=!0,t.default=void 0;var a=r(n("./node_modules/@babel/runtime/helpers/extends.js")),o=r(n(2)),i=r(n(5)),s=r(n("./node_modules/@silverstripe/reactstrap-confirm/dist/Confirmation.js")),l=function(e,t,n,r,l){void 0===t&&(t={}),void 0===n&&(n=document.body),void 0===r&&(r=350);var d=l||s.default,c=n.appendChild(document.createElement("div"));return new Promise(function(s){var l=function(e){return function(){s(e),setTimeout(function(){i.default.unmountComponentAtNode(c),setTimeout(function(){return n.removeChild(c)})},r)}};i.default.render(o.default.createElement(d,(0,a.default)({},t,{onConfirm:l(!0),onCancel:l(!1),body:e})),c)})},d=l;t.default=d},"./node_modules/@silverstripe/reactstrap-confirm/dist/index.js":function(e,t,n){"use strict";var r=n("./node_modules/@babel/runtime/helpers/interopRequireDefault.js");t.__esModule=!0,t.default=void 0;var a=r(n("./node_modules/@silverstripe/reactstrap-confirm/dist/confirm.js")),o=r(n("./node_modules/@silverstripe/reactstrap-confirm/dist/Confirmation.js"));t.Confirmation=o.default;var i=a.default;t.default=i},0:function(e,t){e.exports=jQuery},1:function(e,t){e.exports=i18n},10:function(e,t){e.exports=GraphQLTag},11:function(e,t){e.exports=PropTypes},12:function(e,t){e.exports=Redux},13:function(e,t){e.exports=DeepFreezeStrict},14:function(e,t){e.exports=FieldHolder},15:function(e,t){e.exports=IsomorphicFetch},16:function(e,t){e.exports=ReactSelect},17:function(e,t){e.exports=Reactstrap},18:function(e,t){e.exports=ReduxForm},19:function(e,t){e.exports=SilverStripeComponent},2:function(e,t){e.exports=React},20:function(e,t){e.exports=classnames},21:function(e,t){e.exports=getFormState},3:function(e,t){e.exports=Injector},4:function(e,t){e.exports=ReactApollo},5:function(e,t){e.exports=ReactDom},6:function(e,t){e.exports=ReactRedux}});
|
!function(){"use strict";var e={274:function(e,t,n){var a=i(n(180)),o=i(n(521));function i(e){return e&&e.__esModule?e:{default:e}}window.document.addEventListener("DOMContentLoaded",(()=>{(0,o.default)(),(0,a.default)()}))},521:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var a=s(n(648)),o=s(n(93)),i=s(n(436)),r=s(n(149));function s(e){return e&&e.__esModule?e:{default:e}}t.default=()=>{a.default.component.register("AnchorSelectorField",o.default),a.default.transform("pages-history",(e=>{e.component("HistoryViewer.pages-controller-cms-content",i.default,"PageHistoryViewer")})),a.default.transform("pages-history-revert",(e=>{e.component("HistoryViewerToolbar.VersionedAdmin.HistoryViewer.SiteTree.HistoryViewerVersionDetail",r.default,"PageRevertMutation")}))}},180:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var a=r(n(648)),o=n(827),i=r(n(572));function r(e){return e&&e.__esModule?e:{default:e}}t.default=()=>{a.default.reducer.register("cms",(0,o.combineReducers)({anchorSelector:i.default}))}},93:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=t.ConnectedAnchorSelectorField=t.Component=void 0;var a=C(n(754)),o=C(n(363)),i=C(n(875)),r=n(624),s=n(827),l=n(762),d=C(n(277)),u=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!=typeof e&&"function"!=typeof e)return{default:e};var n=_(t);if(n&&n.has(e))return n.get(e);var a={__proto__:null},o=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var i in e)if("default"!==i&&Object.prototype.hasOwnProperty.call(e,i)){var r=o?Object.getOwnPropertyDescriptor(e,i):null;r&&(r.get||r.set)?Object.defineProperty(a,i,r):a[i]=e[i]}return a.default=e,n&&n.set(e,a),a}(n(447)),c=C(n(892)),f=C(n(42)),p=C(n(453)),h=C(n(78)),m=C(n(720)),g=C(n(820)),v=C(n(86));function _(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(_=function(e){return e?n:t})(e)}function C(e){return e&&e.__esModule?e:{default:e}}const b=()=>null;class w extends d.default{constructor(e){super(e),this.handleChange=this.handleChange.bind(this),this.handleLoadingError=this.handleLoadingError.bind(this)}componentDidMount(){this.ensurePagesLoaded()}componentDidUpdate(e){this.props.pageId!==e.pageId&&this.ensurePagesLoaded()}ensurePagesLoaded(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.props;if(e.loadingState===c.default.UPDATING||e.loadingState===c.default.SUCCESS||!e.pageId)return Promise.resolve();let t=[];e.loadingState===c.default.FIELD_ONLY&&(t=this.props.anchors),e.actions.anchorSelector.beginUpdating(e.pageId);const n=e.data.endpoint.replace(/:id/,e.pageId);return(0,i.default)(n,{credentials:"same-origin"}).then((e=>e.json())).then((n=>{const a=[...new Set([...n,...t])];return e.actions.anchorSelector.updated(e.pageId,a),a})).catch((t=>{e.actions.anchorSelector.updateFailed(e.pageId),this.handleLoadingError(t,e)}))}getDropdownOptions(){const e=this.props.anchors.map((e=>({value:e})));return this.props.value&&!this.props.anchors.find((e=>e===this.props.value))&&e.unshift({value:this.props.value}),e}handleChange(e){"function"==typeof this.props.onChange&&this.props.onChange(e?e.value:"")}handleLoadingError(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:this.props;if(t.onLoadingError===b)throw e;return t.onLoadingError({errors:[{value:e.message,type:"error"}]})}render(){const{extraClass:e,CreatableSelectComponent:t}=this.props,n=(0,g.default)("anchorselectorfield",e),i=this.getDropdownOptions(),r=this.props.value||"",s=a.default._t("CMS.ANCHOR_SELECT_OR_TYPE","Select or enter anchor");return o.default.createElement(h.default,null,o.default.createElement(t,{isSearchable:!0,isClearable:!0,options:i,className:n,name:this.props.name,onChange:this.handleChange,value:{value:r},noOptionsMessage:()=>a.default._t("CMS.ANCHOR_NO_OPTIONS","No options"),placeholder:s,getOptionLabel:e=>{let{value:t}=e;return t},classNamePrefix:"anchorselectorfield"}))}}t.Component=w,w.propTypes={extraClass:v.default.string,id:v.default.string,name:v.default.string.isRequired,onChange:v.default.func,value:v.default.string,attributes:v.default.oneOfType([v.default.object,v.default.array]),pageId:v.default.number,anchors:v.default.array,loadingState:v.default.oneOf(Object.keys(c.default).map((e=>c.default[e]))),onLoadingError:v.default.func,data:v.default.shape({endpoint:v.default.string,targetFieldName:v.default.string})},w.defaultProps={value:"",extraClass:"",onLoadingError:b,attributes:{},CreatableSelectComponent:p.default};const S=t.ConnectedAnchorSelectorField=(0,r.connect)((function(e,t){const n=(0,l.formValueSelector)(t.formid,m.default),a=t&&t.data&&t.data.targetFieldName||"PageID",o=Number(n(e,a)||0);let i=[];const r=o?e.cms.anchorSelector.pages.find((e=>e.id===o)):null;!r||r.loadingState!==c.default.SUCCESS&&r.loadingState!==c.default.DIRTY&&r.loadingState!==c.default.FIELD_ONLY||(i=r.anchors);let s=null;return s=r?r.loadingState:o?c.default.DIRTY:c.default.SUCCESS,{pageId:o,anchors:i,loadingState:s}}),(function(e){return{actions:{anchorSelector:(0,s.bindActionCreators)(u,e)}}}))(w);t.default=(0,f.default)(S)},554:function(e,t,n){var a;((a=n(311))&&a.__esModule?a:{default:a}).default.entwine("ss",(function(e){e(".TreeDropdownField").entwine({OldValue:null}),e("#Form_AddForm_ParentID_Holder .treedropdownfield").entwine({onmatch(){this._super(),e(".cms-add-form").updateTypeList()}}),e(".cms-add-form .parent-mode :input").entwine({onclick:function(e){var t=this.closest("form").find("#Form_AddForm_ParentID_Holder .TreeDropdownField");"top"==this.val()?(t.setOldValue(t.getValue()),t.setValue(0)):(t.setValue(t.getOldValue()||0),t.setOldValue(null)),t.refresh(),t.trigger("change")}}),e(".cms-add-form").entwine({ParentCache:{},onadd:function(){var t=this;this.find("#Form_AddForm_ParentID_Holder .TreeDropdownField").on("change",(function(){t.updateTypeList()})),this.find(".SelectionGroup.parent-mode").on("change",(function(){t.updateTypeList()})),"top"==e(".cms-add-form .parent-mode :input").val()&&this.updateTypeList()},loadCachedChildren:function(e){var t=this.getParentCache();return void 0!==t[e]?t[e]:null},saveCachedChildren:function(e,t){var n=this.getParentCache();n[e]=t,this.setParentCache(n)},updateTypeList:function(){var t=this.data("hints"),n=this.find("#Form_AddForm_ParentID"),a=this.find("input[name=ParentModeField]:checked").val(),o=n.data("metadata"),i="child"===a?n.getValue():null,r=o?o.ClassName:null,s=r&&"child"===a&&i?r:"Root",l=void 0!==t[s]?t[s]:null,d=this,u=l&&void 0!==l.defaultChild?l.defaultChild:null,c=[];if(i){if(this.hasClass("loading"))return;return this.addClass("loading"),null!==(c=this.loadCachedChildren(i))?(this.updateSelectionFilter(c,u),void this.removeClass("loading")):(e.ajax({url:d.data("childfilter"),data:{ParentID:i},success:function(e){d.saveCachedChildren(i,e),d.updateSelectionFilter(e,u)},complete:function(){d.removeClass("loading")}}),!1)}c=l&&void 0!==l.disallowedChildren?l.disallowedChildren:[],this.updateSelectionFilter(c,u)},updateSelectionFilter:function(t,n){var a=this.find("#Form_AddForm_PageType div.radio.selected")[0],o=!1,i=null;if(this.find("#Form_AddForm_PageType div.radio").each((function(n,r){var s=e(this).find("input").val(),l=-1===e.inArray(s,t);r===a&&l&&(o=!0),e(this).setEnabled(l),l||e(this).setSelected(!1),i=(null===i||i)&&l})),o)var r=e(a).parents("li:first");else if(n)r=this.find("#Form_AddForm_PageType div.radio input[value="+n+"]").parents("li:first");else r=this.find("#Form_AddForm_PageType div.radio:not(.disabled):first");r.setSelected(!0),r.siblings().setSelected(!1),this.find("#Form_AddForm_PageType div.radio:not(.disabled)").length?this.find("button[name=action_doAdd]").removeAttr("disabled"):this.find("button[name=action_doAdd]").attr("disabled","disabled"),this.find(".message-restricted")[i?"hide":"show"]()}}),e(".cms-add-form #Form_AddForm_PageType div.radio").entwine({onclick:function(e){this.setSelected(!0)},setSelected:function(e){var t=this.find("input");e&&!t.is(":disabled")?(this.siblings().setSelected(!1),this.toggleClass("selected",!0),t.prop("checked",!0)):(this.toggleClass("selected",!1),t.prop("checked",!1))},setEnabled:function(t){e(this).toggleClass("disabled",!t),t?e(this).find("input").removeAttr("disabled"):e(this).find("input").attr("disabled","disabled").removeAttr("checked")}}),e(".cms-content-addpage-button").entwine({onclick:function(t){var n,a=e(".cms-tree"),o=e(".cms-list"),i=0;if(a.is(":visible")){var r=a.jstree("get_selected");i=r?e(r[0]).data("id"):null}else{var s=o.find('input[name="Page[GridState]"]').val();s&&(i=parseInt(JSON.parse(s).ParentID,10))}var l,d={selector:this.data("targetPanel"),pjax:this.data("pjax")};i?(n=this.data("extraParams")?this.data("extraParams"):"",l=e.path.addSearchParams(i18n.sprintf(this.data("urlAddpage"),i),n)):l=this.attr("href"),e(".cms-container").loadPanel(l,null,d),t.preventDefault(),this.blur()}})}))},649:function(e,t,n){var a=r(n(311)),o=r(n(754)),i=r(n(141));function r(e){return e&&e.__esModule?e:{default:e}}a.default.entwine("ss",(function(e){e(".cms-edit-form :input#Form_EditForm_ClassName").entwine({onchange:function(){alert(o.default._t("CMS.ALERTCLASSNAME"))}}),e(".cms-edit-form input[name=Title]").entwine({onmatch:function(){var t=this;t.data("OrigVal",t.val());var n=t.closest("form"),a=e("input:text[name=URLSegment]",n),o=e("input[name=LiveLink]",n);a.length>0&&(t._addActions(),this.on("change",(function(n){var i=t.data("OrigVal"),r=t.val();t.data("OrigVal",r),0===a.val().indexOf(a.data("defaultUrl"))&&""==o.val()?t.updateURLSegment(r):e(".update",t.parent()).show().parent(".form__field-holder").addClass("input-group"),t.updateRelatedFields(r,i),t.updateBreadcrumbLabel(r)}))),this._super()},onunmatch:function(){this._super()},updateRelatedFields:function(t,n){this.parents("form").find("input[name=MetaTitle], input[name=MenuTitle]").each((function(){var a=e(this);a.val()==n&&(a.val(t),a.updatedRelatedFields&&a.updatedRelatedFields())}))},updateURLSegment:function(t){var n=e("input:text[name=URLSegment]",this.closest("form")).closest(".field.urlsegment"),a=e(".update",this.parent());n.update(t),a.is(":visible")&&a.hide().parent(".form__field-holder").removeClass("input-group")},updateBreadcrumbLabel:function(t){e(".cms-edit-form input[name=ID]").val();var n=e("span.cms-panel-link.crumb");t&&""!=t&&n.text(t)},_addActions:function(){var t,n=this;(t=e("<button />",{class:"update btn btn-outline-secondary form__field-update-url",text:o.default._t("CMS.UpdateURL"),type:"button",click:function(e){e.preventDefault(),n.updateURLSegment(n.val())}})).insertAfter(n),t.hide()}}),e(".cms-edit-form .parentTypeSelector").entwine({onmatch:function(){var e=this;this.find(":input[name=ParentType]").on("click",(function(t){e._toggleSelection(t)})),this.find(".TreeDropdownField").on("change",(function(t){e._changeParentId(t)})),this._changeParentId(),this._toggleSelection(),this._super()},onunmatch:function(){this._super()},_toggleSelection:function(t){var n=this.find(":input[name=ParentType]:checked").val(),a=this.find("#Form_EditForm_ParentID_Holder");"root"==n?this.find(":input[name=ParentID]").val(0):this.find(":input[name=ParentID]").val(this.find("#Form_EditForm_ParentType_subpage").data("parentIdValue")),"root"!=n?a.slideDown(400,(function(){e(this).css("overflow","visible")})):a.slideUp()},_changeParentId:function(e){var t=this.find(":input[name=ParentID]").val();this.find("#Form_EditForm_ParentType_subpage").data("parentIdValue",t)}}),e(".cms-edit-form .btn-toolbar #Form_EditForm_action_doRollback, .cms-edit-form .btn-toolbar #Form_EditForm_action_rollback").entwine({onclick:function(e){if(this.is(":disabled"))return e.preventDefault(),!1;const t=this.parents("form:first").find(":input[name=Version]").val(),n=t?o.default.sprintf(o.default._t("CMS.RollbackToVersion","Do you really want to roll back to version #%s of this page?"),t):o.default._t("CMS.ConfirmRestoreFromLive","Are you sure you want to revert draft to when the page was last published?");return confirm(n)?(this.parents("form:first").addClass("loading"),this._super(e)):(e.preventDefault(),!1)}}),e(".cms-edit-form .btn-toolbar #Form_EditForm_action_archive:not(.homepage-warning)").entwine({onclick:function(e){var t;return t=this.parents("form:first").find("input[name=ArchiveWarningMessage]").val().replace(/\\n/g,"\n"),!!confirm(t)&&(this.parents("form:first").addClass("loading"),this._super(e))}}),e(".cms-edit-form .btn-toolbar #Form_EditForm_action_restore").entwine({onclick:function(e){var t,n=this.parents("form:first").find(":input[name=Version]").val(),a=this.data("toRoot");return t=o.default.sprintf(o.default._t(a?"CMS.RestoreToRoot":"CMS.Restore"),n),!!confirm(t)&&(this.parents("form:first").addClass("loading"),this._super(e))}}),e(".cms-edit-form .btn-toolbar #Form_EditForm_action_unpublish:not(.homepage-warning)").entwine({onclick:function(e){var t,n=this.parents("form:first").find(":input[name=Version]").val();return t=o.default.sprintf(o.default._t("CMS.Unpublish"),n),!!confirm(t)&&(this.parents("form:first").addClass("loading"),this._super(e))}}),e(".cms-edit-form.changed").entwine({onmatch:function(t){this.find("button[data-text-alternate]").each((function(){const t=e(this),n=t.find(".btn__title"),a=t.data("textAlternate");a&&(t.data("textStandard",n.text()),n.text(a));const o=t.data("btnAlternate");o&&(t.data("btnStandard",t.attr("class")),t.attr("class",o),t.removeClass("btn-outline-secondary").addClass("btn-primary"));const i=t.data("btnAlternateAdd");i&&t.addClass(i);const r=t.data("btnAlternateRemove");r&&t.removeClass(r)})),this._super(t)},onunmatch:function(t){this.find("button[data-text-alternate]").each((function(){const t=e(this),n=t.find(".btn__title"),a=t.data("textStandard");a&&n.text(a);const o=t.data("btnStandard");o&&(t.attr("class",o),t.addClass("btn-outline-secondary").removeClass("btn-primary"));const i=t.data("btnAlternateAdd");i&&t.removeClass(i);const r=t.data("btnAlternateRemove");r&&t.addClass(r)})),this._super(t)}}),e(".cms-edit-form .btn-toolbar button[name=action_publish]").entwine({onbuttonafterrefreshalternate:function(){this.data("showingAlternate")?(this.addClass("btn-primary"),this.removeClass("btn-secondary")):(this.removeClass("btn-primary"),this.addClass("btn-secondary"))}}),e(".cms-edit-form .btn-toolbar button[name=action_save]").entwine({onbuttonafterrefreshalternate:function(){this.data("showingAlternate")?(this.addClass("btn-primary"),this.removeClass("btn-secondary")):(this.removeClass("btn-primary"),this.addClass("btn-secondary"))}}),e('.cms-edit-form.CMSPageSettingsController input[name="ParentType"]:checked').entwine({onmatch:function(){this.redraw(),this._super()},onunmatch:function(){this._super()},redraw:function(){var t=e(".cms-edit-form.CMSPageSettingsController #Form_EditForm_ParentID_Holder");"Form_EditForm_ParentType_root"==e(this).attr("id")?t.slideUp():t.slideDown()},onclick:function(){this.redraw()}}),"Form_EditForm_ParentType_root"==e('.cms-edit-form.CMSPageSettingsController input[name="ParentType"]:checked').attr("id")&&e(".cms-edit-form.CMSPageSettingsController #Form_EditForm_ParentID_Holder").hide();var t=!1;e(".cms-edit-form .btn-toolbar #Form_EditForm_action_unpublish.homepage-warning,.cms-edit-form .btn-toolbar #Form_EditForm_action_archive.homepage-warning,#Form_EditForm_URLSegment_Holder.homepage-warning .btn.update").entwine({onclick:async function(e){if(t)return this._super(e);e.stopPropagation();var n=o.default._t("CMS.RemoveHomePageWarningMessage","Warning: This page is the home page. By changing the URL segment visitors will not be able to view it.");return await(0,i.default)({title:o.default._t("CMS.RemoveHomePageWarningTitle","Remove your home page?"),message:n,confirmText:o.default._t("CMS.RemoveHomePageWarningLabel","Remove"),confirmColor:"danger"})&&(t=!0,this.trigger("click"),t=!1),!1}})}))},978:function(e,t,n){var a=s(n(311)),o=s(n(754)),i=s(n(141)),r=n(845);function s(e){return e&&e.__esModule?e:{default:e}}a.default.entwine("ss.tree",(function(e){e(".cms-tree").entwine({fromDocument:{"oncontext_show.vakata":function(e){this.adjustContextClass()}},adjustContextClass:function(){var t=e("#vakata-contextmenu").find("ul ul");t.each((function(n){var a="1",o=e(t[n]).find("li").length;o>20?a="3":o>10&&(a="2"),e(t[n]).addClass("vakata-col-"+a).removeClass("right"),e(t[n]).find("li").on("mouseenter",(function(t){e(this).parent("ul").removeClass("right")}))}))},showListViewFor:function(t){localStorage.setItem("ss.pages-view-type","listview");const n=this.closest(".cms-content-view").data("url-listviewroot"),a=e.path.addSearchParams(n,{ParentID:t}),o=e("base").attr("href")||"";window.location.assign((0,r.joinUrlPaths)(o,a))},getTreeConfig:function(){var t=this,n=this._super();this.getHints();return n.plugins.push("contextmenu"),n.contextmenu={items:function(n){var a={edit:{label:n.hasClass("edit-disabled")?o.default._t("CMS.EditPage","Edit page",100,"Used in the context menu when right-clicking on a page node in the CMS tree"):o.default._t("CMS.ViewPage","View page",100,"Used in the context menu when right-clicking on a page node in the CMS tree"),action:function(n){e(".cms-container").entwine(".ss").loadPanel(o.default.sprintf(t.data("urlEditpage"),n.data("id")))}}};n.hasClass("nochildren")||(a.showaslist={label:o.default._t("CMS.ShowAsList"),action:function(e){t.showListViewFor(e.data("id"))}});n.data("pagetype");var i=n.data("id"),r=n.find(">a .item").data("allowedchildren"),s={},l=!1;return e.each(r,(function(n,a){l=!0,s["allowedchildren-"+a.ClassName]={label:'<span class="jstree-pageicon '+a.IconClass+'"></span>'+a.Title,_class:"class-"+a.ClassName.replace(/[^a-zA-Z0-9\-_:.]+/g,"_"),action:function(n){e(".cms-container").entwine(".ss").loadPanel(e.path.addSearchParams(o.default.sprintf(t.data("urlAddpage"),i,a.ClassName),t.data("extraParams")))}}})),l&&(a.addsubpage={label:o.default._t("CMS.AddSubPage","Add page under this page",100,"Used in the context menu when right-clicking on a page node in the CMS tree"),submenu:s}),n.hasClass("edit-disabled")||(a.duplicate={label:o.default._t("CMS.Duplicate"),submenu:[{label:o.default._t("CMS.ThisPageOnly"),action:function(n){e(".cms-container").entwine(".ss").loadPanel(e.path.addSearchParams(o.default.sprintf(t.data("urlDuplicate"),n.data("id")),t.data("extraParams")))}},{label:o.default._t("CMS.ThisPageAndSubpages"),action:function(n){e(".cms-container").entwine(".ss").loadPanel(e.path.addSearchParams(o.default.sprintf(t.data("urlDuplicatewithchildren"),n.data("id")),t.data("extraParams")))}}]}),a}},n},canMove:async function(e){if(!(e.rslt.o.find(".homepage").first().length>0))return!0;if(e.rslt.op.data("id")===e.rslt.np.data("id"))return!0;var t=o.default._t("CMS.RemoveHomePageWarningMessage","Warning: This page is the home page. By changing the URL segment visitors will not be able to view it.");return await(0,i.default)({title:o.default._t("CMS.RemoveHomePageWarningTitle","Remove your home page?"),message:t,confirmText:o.default._t("CMS.RemoveHomePageWarningLabel","Remove"),confirmColor:"danger"})}}),e(".cms-tree a.jstree-clicked").entwine({onmatch:function(){var e=this,t=e.parents(".cms-tree-view-sidebar");if(e.offset().top<0||e.offset().top>t.height()-e.height()){var n=e.parent();n.prev().length&&(n=n.prev()),n.get(0).scrollIntoView()}}}),e(".cms-tree-filtered .clear-filter").entwine({onclick:function(){window.location=location.protocol+"//"+location.host+location.pathname}}),e(".cms-tree .subtree-list-link").entwine({onclick:function(e){e.preventDefault(),this.closest(".cms-tree").showListViewFor(this.data("id"))}})}))},580:function(e,t,n){var a,o=(a=n(311))&&a.__esModule?a:{default:a},i=n(845);o.default.entwine("ss",(function(e){const t="treeview",n="listview";e(".cms-content-header-info").entwine({"from .cms-panel":{ontoggle:function(e){var t=this.closest(".cms-content").find(e.target);0!==t.length&&this.parent()[t.hasClass("collapsed")?"addClass":"removeClass"]("collapsed")}}}),e(".cms-panel-deferred.cms-content-view").entwine({onadd:function(){if(this.data("no-ajax"))return;var e=localStorage.getItem("ss.pages-view-type")||t;this.closest(".cms-content-tools").length>0&&(e=t);const a=this.data(`url-${e}`);let o=localStorage.getItem("ss.pages-view-filtered");"string"==typeof o&&"false"===o.toLowerCase()&&(o=!1),localStorage.setItem("ss.pages-view-filtered",!1),this.data("deferredNoCache",o||e===n),this.data("url",a+location.search),this._super()}}),e(".js-injector-boot .search-holder--cms").entwine({search(e){localStorage.setItem("ss.pages-view-filtered",!0),this._super(e)}}),e(".cms .page-view-link").entwine({onclick:function(t){t.preventDefault();const a=e(this).data("view"),o=this.closest(".cms-content-view"),r=o.data(`url-${a}`),s=0!==o.closest(".cms-content-tools").length;if(localStorage.setItem("ss.pages-view-type",a),s&&a===n){const t=e("base").attr("href")||"";window.location.assign((0,i.joinUrlPaths)(t,o.data("url-listviewroot")))}else o.data("url",r+location.search),o.redraw()}}),e(".cms .cms-clear-filter").entwine({onclick:function(t){t.preventDefault(),window.location=e(this).prop("href")}}),e(".cms-content-toolbar").entwine({onmatch:function(){var t=this;this._super(),e.each(this.find(".cms-actions-buttons-row .tool-button"),(function(){var n=e(this),a=n.data("toolid");n.hasClass("active");void 0!==a&&(n.data("active",!1).removeClass("active"),e("#"+a).hide(),t.bindActionButtonEvents(n))}))},onunmatch:function(){var t=this;this._super(),e.each(this.find(".cms-actions-buttons-row .tool-button"),(function(){var n=e(this);t.unbindActionButtonEvents(n)}))},bindActionButtonEvents:function(e){var t=this;e.on("click.cmsContentToolbar",(function(n){t.showHideTool(e)}))},unbindActionButtonEvents:function(e){e.off(".cmsContentToolbar")},showHideTool:function(t){var n=t.data("active"),a=t.data("toolid"),o=e("#"+a);e.each(this.find(".cms-actions-buttons-row .tool-button"),(function(){var t=e(this),n=e("#"+t.data("toolid"));t.data("toolid")!==a&&(n.hide(),t.data("active",!1))})),t[n?"removeClass":"addClass"]("active"),o[n?"hide":"show"](),t.data("active",!n)}})}))},907:function(e,t,n){var a;((a=n(311))&&a.__esModule?a:{default:a}).default.entwine("ss",(function(e){e("#Form_EditForm_RedirectionType input").entwine({onmatch:function(){e(this).attr("checked")&&this.toggle(),this._super()},onunmatch:function(){this._super()},onclick:function(){this.toggle()},toggle:function(){"Internal"==e(this).attr("value")?(e("#Form_EditForm_ExternalURL_Holder").hide(),e("#Form_EditForm_LinkToID_Holder").show(),e("#Form_EditForm_LinkToFile_Holder").hide()):"External"==e(this).attr("value")?(e("#Form_EditForm_ExternalURL_Holder").show(),e("#Form_EditForm_LinkToID_Holder").hide(),e("#Form_EditForm_LinkToFile_Holder").hide()):(e("#Form_EditForm_LinkToFile_Holder").show(),e("#Form_EditForm_ExternalURL_Holder").hide(),e("#Form_EditForm_LinkToID_Holder").hide())}})}))},806:function(e,t,n){var a;((a=n(311))&&a.__esModule?a:{default:a}).default.entwine("ss",(function(e){e(".field.urlsegment:not(.readonly)").entwine({MaxPreviewLength:55,Ellipsis:"...",onmatch:function(){this.find(":text").length&&this.toggleEdit(!1),this.redraw(),this._super()},redraw:function(){var e=this.find(":text"),t=decodeURI(e.data("prefix")+e.val()),n=t;t.length>this.getMaxPreviewLength()&&(n=this.getEllipsis()+t.substr(t.length-this.getMaxPreviewLength(),t.length)),this.find(".URL-link").attr("href",encodeURI(t+e.data("suffix"))).text(n)},toggleEdit:function(e){var t=this.find(":text");this.find(".preview-holder")[e?"hide":"show"](),this.find(".edit-holder")[e?"show":"hide"](),e&&(t.data("origval",t.val()),t.focus())},update:function(){var e=this,t=this.find(":text"),n=t.data("origval"),a=arguments[0],o=a&&""!==a?a:t.val();n!=o?(this.addClass("loading"),this.suggest(o,(function(n){t.val(decodeURIComponent(n.value)),e.toggleEdit(!1),e.removeClass("loading"),e.redraw()}))):(this.toggleEdit(!1),this.redraw())},cancel:function(){var e=this.find(":text");e.val(e.data("origval")),this.toggleEdit(!1)},suggest:function(t,n){var a=this,o=a.find(":text"),i=e.path.parseUrl(a.closest("form").attr("action")),r=i.hrefNoSearch+"/field/"+o.attr("name")+"/suggest/?value="+encodeURIComponent(t);i.search&&(r+="&"+i.search.replace(/^\?/,"")),e.ajax({url:r,success:function(e){n.apply(this,arguments)},error:function(e,t){e.statusText=e.responseText},complete:function(){a.removeClass("loading")}})}}),e(".field.urlsegment .text").entwine({onkeydown:function(e){13===e.keyCode&&(e.preventDefault(),this.closest(".field").update())}}),e(".field.urlsegment .edit").entwine({onclick:function(e){e.preventDefault(),this.closest(".field").toggleEdit(!0)}}),e(".field.urlsegment .update").entwine({onclick:function(e){e.preventDefault(),this.closest(".field").update()}}),e(".field.urlsegment .cancel").entwine({onclick:function(e){e.preventDefault(),this.closest(".field").cancel()}})}))},964:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;t.default={ANCHORSELECTOR_CURRENT_FIELD:"ANCHORSELECTOR_CURRENT_FIELD",ANCHORSELECTOR_UPDATED:"ANCHORSELECTOR_UPDATED",ANCHORSELECTOR_UPDATING:"ANCHORSELECTOR_UPDATING",ANCHORSELECTOR_UPDATE_FAILED:"ANCHORSELECTOR_UPDATE_FAILED"}},447:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.beginUpdating=function(e){return{type:o.default.ANCHORSELECTOR_UPDATING,payload:{pageId:e}}},t.updateFailed=function(e){return{type:o.default.ANCHORSELECTOR_UPDATE_FAILED,payload:{pageId:e}}},t.updated=function(e,t){let n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];return{type:o.default.ANCHORSELECTOR_UPDATED,payload:{pageId:e,anchors:t,cacheResult:n}}},t.updatedCurrentField=function(e,t,n){return{type:o.default.ANCHORSELECTOR_CURRENT_FIELD,payload:{pageId:e,anchors:t,fieldID:n}}};var a,o=(a=n(964))&&a.__esModule?a:{default:a}},572:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=function(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:s,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;const n=(n,o)=>{const i=t.payload.pageId;return(0,a.default)({pages:[...e.pages.filter((e=>e.id!==i)),{id:i,loadingState:n,anchors:o}].sort(((e,t)=>e.id-t.id))})};switch(t.type){case o.default.ANCHORSELECTOR_UPDATING:return n(i.default.UPDATING,[]);case o.default.ANCHORSELECTOR_UPDATED:{const{anchors:e,cacheResult:a}=t.payload,{SUCCESS:o,DIRTY:r}=i.default;return n(a?o:r,e)}case o.default.ANCHORSELECTOR_CURRENT_FIELD:{const{anchors:e}=t.payload;return n(i.default.FIELD_ONLY,e)}case o.default.ANCHORSELECTOR_UPDATE_FAILED:return n(i.default.FAILED,[]);default:return e}};var a=r(n(752)),o=r(n(964)),i=r(n(892));function r(e){return e&&e.__esModule?e:{default:e}}const s=(0,a.default)({pages:[]})},892:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;t.default={SUCCESS:"SUCCESS",DIRTY:"DIRTY",FIELD_ONLY:"FIELD_ONLY",UPDATING:"UPDATING",FAILED:"FAILED"}},436:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.query=t.default=t.config=void 0;var a,o=n(732),i=(a=n(306))&&a.__esModule?a:{default:a};const r=t.query=i.default`
|
||||||
|
query ReadHistoryViewerPage ($page_id: ID!, $limit: Int!, $offset: Int!) {
|
||||||
|
readOnePage(
|
||||||
|
versioning: {
|
||||||
|
mode: ALL_VERSIONS
|
||||||
|
},
|
||||||
|
filter: {
|
||||||
|
id: { eq: $page_id }
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
versions (limit: $limit, offset: $offset, sort: {
|
||||||
|
version: DESC
|
||||||
|
}) {
|
||||||
|
pageInfo {
|
||||||
|
totalCount
|
||||||
|
}
|
||||||
|
nodes {
|
||||||
|
version
|
||||||
|
absoluteLink
|
||||||
|
author {
|
||||||
|
firstName
|
||||||
|
surname
|
||||||
|
}
|
||||||
|
publisher {
|
||||||
|
firstName
|
||||||
|
surname
|
||||||
|
}
|
||||||
|
deleted
|
||||||
|
draft
|
||||||
|
published
|
||||||
|
liveVersion
|
||||||
|
latestDraftVersion
|
||||||
|
lastEdited
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,s=t.config={options(e){let{recordId:t,limit:n,page:a}=e;return{variables:{limit:n,offset:((a||1)-1)*n,page_id:t},fetchPolicy:"network-only"}},props(e){let{data:{error:t,refetch:n,readOnePage:a,loading:o},ownProps:{actions:i={versions:{}},limit:r,recordId:s}}=e;const l=a||null;return{loading:o||!l,versions:l,graphQLErrors:t&&t.graphQLErrors&&t.graphQLErrors.map((e=>e.message)),actions:{...i,versions:{...l,goToPage(e){n({offset:((e||1)-1)*r,limit:r,page_id:s})}}}}}};t.default=(0,o.graphql)(r,s)},149:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.mutation=t.default=t.config=void 0;var a,o=n(732),i=(a=n(306))&&a.__esModule?a:{default:a};const r=t.mutation=i.default`
|
||||||
|
mutation rollbackPage($id:ID!, $toVersion:Int!) {
|
||||||
|
rollbackPage(
|
||||||
|
id: $id
|
||||||
|
toVersion: $toVersion
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`,s=t.config={props:e=>{let{mutate:t,ownProps:{actions:n}}=e;const a=(e,n)=>t({variables:{id:e,toVersion:n}});return{actions:{...n,rollbackPage:a,revertToVersion:a}}},options:{refetchQueries:["ReadHistoryViewerPage"]}};t.default=(0,o.graphql)(r,s)},112:function(e,t,n){function a(e){return a="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},a(e)}Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var o,i=function(e,t){if(!t&&e&&e.__esModule)return e;if(null===e||"object"!==a(e)&&"function"!=typeof e)return{default:e};var n=l(t);if(n&&n.has(e))return n.get(e);var o={},i=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var r in e)if("default"!==r&&Object.prototype.hasOwnProperty.call(e,r)){var s=i?Object.getOwnPropertyDescriptor(e,r):null;s&&(s.get||s.set)?Object.defineProperty(o,r,s):o[r]=e[r]}o.default=e,n&&n.set(e,o);return o}(n(363)),r=(o=n(86))&&o.__esModule?o:{default:o},s=n(127);function l(e){if("function"!=typeof WeakMap)return null;var t=new WeakMap,n=new WeakMap;return(l=function(e){return e?n:t})(e)}function d(){return d=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var a in n)Object.prototype.hasOwnProperty.call(n,a)&&(e[a]=n[a])}return e},d.apply(this,arguments)}var u=function(e){var t=e.onClose,n=e.message,a=e.title,o=e.confirmText,r=e.cancelText,l=e.confirmColor,u=e.cancelColor,c=e.className,f=e.buttonsComponent,p=e.size,h=e.bodyComponent,m=e.modalProps,g=i.default.createElement(i.Fragment,null,r&&i.default.createElement(s.Button,{color:u,onClick:function(){return t(!1)}},r)," ",i.default.createElement(s.Button,{color:l,onClick:function(){return t(!0)}},o));if(f){var v=f;g=i.default.createElement(v,{onClose:t})}var _=h;return i.default.createElement(s.Modal,d({size:p,isOpen:!0,toggle:function(){return t(!1)},className:"reactstrap-confirm ".concat(c)},m),a&&i.default.createElement(s.ModalHeader,{toggle:function(){return t(!1)}},a||null),i.default.createElement(s.ModalBody,null,h?i.default.createElement(_,null):n),i.default.createElement(s.ModalFooter,null,g))};u.defaultProps={message:"Are you sure?",title:"Warning!",confirmText:"Ok",cancelText:"Cancel",confirmColor:"primary",cancelColor:"",className:"",buttonsComponent:null,size:null,bodyComponent:null,modalProps:{}},u.propTypes={onClose:r.default.func.isRequired,message:r.default.node,title:r.default.node,confirmText:r.default.node,cancelText:r.default.node,confirmColor:r.default.string,cancelColor:r.default.string,className:r.default.string,size:r.default.string,buttonsComponent:r.default.func,bodyComponent:r.default.func,modalProps:r.default.object};var c=u;t.default=c},141:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;var a=r(n(363)),o=n(394),i=r(n(112));function r(e){return e&&e.__esModule?e:{default:e}}function s(){return s=Object.assign||function(e){for(var t=1;t<arguments.length;t++){var n=arguments[t];for(var a in n)Object.prototype.hasOwnProperty.call(n,a)&&(e[a]=n[a])}return e},s.apply(this,arguments)}var l=function(e){return new Promise((function(t){var n=document.createElement("div");(0,o.render)(a.default.createElement(i.default,s({},e,{onClose:function(e){(0,o.unmountComponentAtNode)(n),n=null,t(e)}})),n)}))};t.default=l},732:function(e){e.exports=ApolloClientReactHoc},752:function(e){e.exports=DeepFreezeStrict},78:function(e){e.exports=EmotionCssCacheProvider},42:function(e){e.exports=FieldHolder},306:function(e){e.exports=GraphQLTag},648:function(e){e.exports=Injector},875:function(e){e.exports=IsomorphicFetch},86:function(e){e.exports=PropTypes},363:function(e){e.exports=React},394:function(e){e.exports=ReactDom},624:function(e){e.exports=ReactRedux},453:function(e){e.exports=ReactSelectCreatable},127:function(e){e.exports=Reactstrap},827:function(e){e.exports=Redux},762:function(e){e.exports=ReduxForm},277:function(e){e.exports=SilverStripeComponent},820:function(e){e.exports=classnames},720:function(e){e.exports=getFormState},754:function(e){e.exports=i18n},311:function(e){e.exports=jQuery},845:function(e){e.exports=ssUrlLib}},t={};function n(a){var o=t[a];if(void 0!==o)return o.exports;var i=t[a]={exports:{}};return e[a](i,i.exports,n),i.exports}n(554),n(649),n(580),n(978),n(907),n(806),n(274)}();
|
2
client/dist/styles/SilverStripeNavigator.css
vendored
2
client/dist/styles/SilverStripeNavigator.css
vendored
@ -1 +1 @@
|
|||||||
#SilverStripeNavigator{position:fixed;bottom:0;left:0;width:100%;border-top:2px solid #d4d0c8;background-color:#81858d;height:22px}#SilverStripeNavigator *{font-family:Arial,Helvetica,sans-serif;font-size:10px!important}#SilverStripeNavigator .holder{text-align:center;padding-top:4px;padding-left:3px;padding-right:6px;color:#fff;border-top:1px solid #555}#SilverStripeNavigator #logInStatus{float:right}#SilverStripeNavigator #switchView{float:left}#SilverStripeNavigator a{color:#fff;text-decoration:underline}#SilverStripeNavigator a,#SilverStripeNavigator a:hover{background-color:transparent}#SilverStripeNavigator .bottomTabs a{margin-right:8px;text-decoration:underline}#SilverStripeNavigator .bottomTabs a.current{font-weight:700;text-decoration:none}#SilverStripeNavigatorMessage{font-family:Lucida Grande,Verdana,Arial,"sans-serif";position:fixed;z-index:1000;right:20px;top:40px;padding:10px;border-color:#c99;color:#fff;background-color:#c00;border:1px solid #000}#SilverStripeNavigatorLinkPopup{display:none;position:absolute;top:-60px;height:50px;width:350px;left:200px;background-color:#fff;border:1px solid #000;z-index:100;color:#000;padding:5px}#SilverStripeNavigatorLinkPopup input{width:250px}#SilverStripeNavigatorLinkPopup a.close{color:blue;text-align:right;width:80%;border:none!important;cursor:pointer}
|
#SilverStripeNavigator{position:fixed;bottom:0;left:0;width:100%;border-top:2px solid #d4d0c8;background-color:#81858d;height:22px}#SilverStripeNavigator *{font-family:Arial,Helvetica,sans-serif;font-size:10px !important}#SilverStripeNavigator .holder{text-align:center;padding-top:4px;padding-left:3px;padding-right:6px;color:#fff;border-top:1px solid #555}#SilverStripeNavigator #logInStatus{float:right}#SilverStripeNavigator #switchView{float:left}#SilverStripeNavigator a{color:#fff;background-color:rgba(0,0,0,0);text-decoration:underline}#SilverStripeNavigator a:hover{background-color:rgba(0,0,0,0)}#SilverStripeNavigator .bottomTabs a{margin-right:8px;text-decoration:underline}#SilverStripeNavigator .bottomTabs a.current{font-weight:bold;text-decoration:none}#SilverStripeNavigatorMessage{font-family:"Lucida Grande",Verdana,Arial,"sans-serif";position:fixed;z-index:1000;right:20px;top:40px;padding:10px;border-color:#c99;color:#fff;background-color:#c00;border:1px solid #000}#SilverStripeNavigatorLinkPopup{display:none;position:absolute;top:-60px;height:50px;width:350px;left:200px;background-color:#fff;border:1px solid #000;z-index:100;color:#000;padding:5px}#SilverStripeNavigatorLinkPopup input{width:250px}#SilverStripeNavigatorLinkPopup a.close{color:blue;text-align:right;width:80%;border:none !important;cursor:pointer}
|
||||||
|
2
client/dist/styles/bundle.css
vendored
2
client/dist/styles/bundle.css
vendored
@ -1 +1 @@
|
|||||||
#cms-page-history-versions tr.loading{color:#999}#cms-page-history-versions tr.loading td:hover{cursor:none}#cms-page-history-versions td:hover{cursor:pointer}.CMSPageHistoryController{overflow:hidden}.CMSPageHistoryController ins{background-color:#dfd;padding:2px;text-decoration:none}.CMSPageHistoryController del{background-color:#fdd;padding:2px;color:#f44}.CMSPageHistoryController .htmleditorfield.readonly img{max-width:100%;height:auto}.CMSPageHistoryController .cms-content-tools.collapsed{overflow:hidden}#cms-content-listview .cms-tree-expand-trigger,#cms-content-treeview .cms-tree-expand-trigger{display:none}.cms-content-tools #cms-content-treeview .cms-content-toolbar{border-bottom:none;-webkit-box-shadow:none;box-shadow:none;margin-bottom:0}.cms-content-tools #cms-content-treeview .cms-tree-expand-trigger{display:block;float:left;margin:0 0 2px}.cms-content-tools #cms-content-treeview .cms-tree-expand-trigger span.ui-button-text{padding-right:8px}.cms-content-tools #cms-content-treeview .cms-tree .badge{display:none}.cms-content-tools #cms-content-treeview .cms-tree .jstree-clicked>.text>.badge,.cms-content-tools #cms-content-treeview .cms-tree a:hover>.text>.badge{display:inline-block}#cms-content-tools-CMSMain .search-form{right:0}.cms-list .cms-list__item-breadcrumbs{margin-left:21px;margin-bottom:0;font-size:.9em;word-break:break-word}.cms-content-toolbar .view-controls{margin-top:0}.cms-content-toolbar .view-controls .page-view-link{display:inline-block;margin-right:-5px}.cms-content-toolbar .view-controls.view-controls--listview .font-icon-list,.cms-content-toolbar .view-controls.view-controls--treeview .font-icon-tree{display:none}#pages-controller-cms-content,#pages-controller-cms-content+.cms-preview{width:50%}.field.urlsegment .input-group{width:auto;-webkit-flex-wrap:nowrap;flex-wrap:nowrap}.field.urlsegment.loading .form__field-label{background-image:url(data:image/gif;base64,R0lGODlhEAAQAPQAAP///wpakvj6+z9+qYivyg9dlC5yotfk7KvG2R9om3umxGubveXt8py80sjZ5k+IsFyRtgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAAFUCAgjmRpnqUwFGwhKoRgqq2YFMaRGjWA8AbZiIBbjQQ8AmmFUJEQhQGJhaKOrCksgEla+KIkYvC6SJKQOISoNSYdeIk1ayA8ExTyeR3F749CACH5BAkKAAAALAAAAAAQABAAAAVoICCKR9KMaCoaxeCoqEAkRX3AwMHWxQIIjJSAZWgUEgzBwCBAEQpMwIDwY1FHgwJCtOW2UDWYIDyqNVVkUbYr6CK+o2eUMKgWrqKhj0FrEM8jQQALPFA3MAc8CQSAMA5ZBjgqDQmHIyEAIfkECQoAAAAsAAAAABAAEAAABWAgII4j85Ao2hRIKgrEUBQJLaSHMe8zgQo6Q8sxS7RIhILhBkgumCTZsXkACBC+0cwF2GoLLoFXREDcDlkAojBICRaFLDCOQtQKjmsQSubtDFU/NXcDBHwkaw1cKQ8MiyEAIfkECQoAAAAsAAAAABAAEAAABVIgII5kaZ6AIJQCMRTFQKiDQx4GrBfGa4uCnAEhQuRgPwCBtwK+kCNFgjh6QlFYgGO7baJ2CxIioSDpwqNggWCGDVVGphly3BkOpXDrKfNm/4AhACH5BAkKAAAALAAAAAAQABAAAAVgICCOZGmeqEAMRTEQwskYbV0Yx7kYSIzQhtgoBxCKBDQCIOcoLBimRiFhSABYU5gIgW01pLUBYkRItAYAqrlhYiwKjiWAcDMWY8QjsCf4DewiBzQ2N1AmKlgvgCiMjSQhACH5BAkKAAAALAAAAAAQABAAAAVfICCOZGmeqEgUxUAIpkA0AMKyxkEiSZEIsJqhYAg+boUFSTAkiBiNHks3sg1ILAfBiS10gyqCg0UaFBCkwy3RYKiIYMAC+RAxiQgYsJdAjw5DN2gILzEEZgVcKYuMJiEAOwAAAAAAAAAAAA==);background-repeat:no-repeat;background-position:100%;padding-right:20px}.field.urlsegment .URL-link{padding-top:8px;display:inline-block}.field.urlsegment input.text{-webkit-box-flex:0;-webkit-flex:0 1 250px;flex:0 1 250px}.field.urlsegment .input-group-append{z-index:2}.field.urlsegment .input-group-append:last-of-type{z-index:1}.field.urlsegment .input-group-append .btn{margin-top:0;padding-top:.5385rem;padding-bottom:.5385rem}.field.urlsegment .help{margin-left:0}.field.urlsegment .edit-holder{display:none}.field.urlsegment .edit-holder .form__field-description{clear:both}.form__field-update-url{border-radius:0 3px 3px 0;margin-right:0;margin-left:-1px}
|
#cms-page-history-versions tr.loading{color:#999}#cms-page-history-versions tr.loading td:hover{cursor:none}#cms-page-history-versions td:hover{cursor:pointer}#cms-content-treeview .cms-tree-expand-trigger,#cms-content-listview .cms-tree-expand-trigger{display:none}.cms-content-tools #cms-content-treeview .cms-content-toolbar{border-bottom:none;box-shadow:none;margin-bottom:0}.cms-content-tools #cms-content-treeview .cms-tree-expand-trigger{display:block;float:left;margin:0 0 2px 0}.cms-content-tools #cms-content-treeview .cms-tree-expand-trigger span.ui-button-text{padding-right:8px}.cms-content-tools #cms-content-treeview .cms-tree .badge{display:none}.cms-content-tools #cms-content-treeview .cms-tree a:hover>.text>.badge,.cms-content-tools #cms-content-treeview .cms-tree .jstree-clicked>.text>.badge{display:inline-block}#cms-content-tools-CMSMain .search-form{right:0}.cms-list .cms-list__item-breadcrumbs{margin-left:21px;margin-bottom:0;font-size:.9em;word-break:break-word}.cms-content-toolbar .view-controls{margin-top:0}.cms-content-toolbar .view-controls .page-view-link{display:inline-block;margin-right:-5px}.cms-content-toolbar .view-controls.view-controls--treeview .font-icon-tree,.cms-content-toolbar .view-controls.view-controls--listview .font-icon-list{display:none}#pages-controller-cms-content,#pages-controller-cms-content+.cms-preview{width:50%}.field.urlsegment .input-group{width:auto;flex-wrap:nowrap}.field.urlsegment.loading .form__field-label{background-image:url(data:image/gif;base64,R0lGODlhEAAQAPQAAP///wpakvj6+z9+qYivyg9dlC5yotfk7KvG2R9om3umxGubveXt8py80sjZ5k+IsFyRtgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAAFUCAgjmRpnqUwFGwhKoRgqq2YFMaRGjWA8AbZiIBbjQQ8AmmFUJEQhQGJhaKOrCksgEla+KIkYvC6SJKQOISoNSYdeIk1ayA8ExTyeR3F749CACH5BAkKAAAALAAAAAAQABAAAAVoICCKR9KMaCoaxeCoqEAkRX3AwMHWxQIIjJSAZWgUEgzBwCBAEQpMwIDwY1FHgwJCtOW2UDWYIDyqNVVkUbYr6CK+o2eUMKgWrqKhj0FrEM8jQQALPFA3MAc8CQSAMA5ZBjgqDQmHIyEAIfkECQoAAAAsAAAAABAAEAAABWAgII4j85Ao2hRIKgrEUBQJLaSHMe8zgQo6Q8sxS7RIhILhBkgumCTZsXkACBC+0cwF2GoLLoFXREDcDlkAojBICRaFLDCOQtQKjmsQSubtDFU/NXcDBHwkaw1cKQ8MiyEAIfkECQoAAAAsAAAAABAAEAAABVIgII5kaZ6AIJQCMRTFQKiDQx4GrBfGa4uCnAEhQuRgPwCBtwK+kCNFgjh6QlFYgGO7baJ2CxIioSDpwqNggWCGDVVGphly3BkOpXDrKfNm/4AhACH5BAkKAAAALAAAAAAQABAAAAVgICCOZGmeqEAMRTEQwskYbV0Yx7kYSIzQhtgoBxCKBDQCIOcoLBimRiFhSABYU5gIgW01pLUBYkRItAYAqrlhYiwKjiWAcDMWY8QjsCf4DewiBzQ2N1AmKlgvgCiMjSQhACH5BAkKAAAALAAAAAAQABAAAAVfICCOZGmeqEgUxUAIpkA0AMKyxkEiSZEIsJqhYAg+boUFSTAkiBiNHks3sg1ILAfBiS10gyqCg0UaFBCkwy3RYKiIYMAC+RAxiQgYsJdAjw5DN2gILzEEZgVcKYuMJiEAOwAAAAAAAAAAAA==);background-repeat:no-repeat;background-position:right center;padding-right:20px}.field.urlsegment .URL-link{padding-top:8px;display:inline-block}.field.urlsegment input.text{flex:0 1 250px}.field.urlsegment .input-group-append{z-index:2}.field.urlsegment .input-group-append:last-of-type{z-index:1}.field.urlsegment .input-group-append .btn{margin-top:0;padding-top:.5385rem;padding-bottom:.5385rem}.field.urlsegment .help{margin-left:0}.field.urlsegment .edit-holder{display:none}.field.urlsegment .edit-holder .form__field-description{clear:both}.form__field-update-url{border-radius:0 3px 3px 0;margin-right:0px;margin-left:-1px}.anchorselectorfield__menu{z-index:20000}
|
||||||
|
@ -2,7 +2,6 @@ require('../legacy/CMSMain.AddForm');
|
|||||||
require('../legacy/CMSMain.EditForm');
|
require('../legacy/CMSMain.EditForm');
|
||||||
require('../legacy/CMSMain');
|
require('../legacy/CMSMain');
|
||||||
require('../legacy/CMSMain.Tree');
|
require('../legacy/CMSMain.Tree');
|
||||||
require('../legacy/CMSPageHistoryController');
|
|
||||||
require('../legacy/RedirectorPage');
|
require('../legacy/RedirectorPage');
|
||||||
require('../legacy/SiteTreeURLSegmentField');
|
require('../legacy/SiteTreeURLSegmentField');
|
||||||
|
|
||||||
|
@ -8,7 +8,8 @@ import SilverStripeComponent from 'lib/SilverStripeComponent';
|
|||||||
import * as anchorSelectorActions from 'state/anchorSelector/AnchorSelectorActions';
|
import * as anchorSelectorActions from 'state/anchorSelector/AnchorSelectorActions';
|
||||||
import anchorSelectorStates from 'state/anchorSelector/AnchorSelectorStates';
|
import anchorSelectorStates from 'state/anchorSelector/AnchorSelectorStates';
|
||||||
import fieldHolder from 'components/FieldHolder/FieldHolder';
|
import fieldHolder from 'components/FieldHolder/FieldHolder';
|
||||||
import { Creatable } from 'react-select';
|
import CreatableSelect from 'react-select/creatable';
|
||||||
|
import EmotionCssCacheProvider from 'containers/EmotionCssCacheProvider/EmotionCssCacheProvider';
|
||||||
import getFormState from 'lib/getFormState';
|
import getFormState from 'lib/getFormState';
|
||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
@ -117,26 +118,27 @@ class AnchorSelectorField extends SilverStripeComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const inputProps = {
|
const { extraClass, CreatableSelectComponent } = this.props;
|
||||||
id: this.props.id,
|
const className = classnames('anchorselectorfield', extraClass);
|
||||||
};
|
|
||||||
const className = classnames('anchorselectorfield', this.props.extraClass);
|
|
||||||
const options = this.getDropdownOptions();
|
const options = this.getDropdownOptions();
|
||||||
const value = this.props.value || '';
|
const rawValue = this.props.value || '';
|
||||||
const placeholder = i18n._t('CMS.ANCHOR_SELECT_OR_TYPE', 'Select or enter anchor');
|
const placeholder = i18n._t('CMS.ANCHOR_SELECT_OR_TYPE', 'Select or enter anchor');
|
||||||
return (
|
return (
|
||||||
<Creatable
|
<EmotionCssCacheProvider>
|
||||||
searchable
|
<CreatableSelectComponent
|
||||||
|
isSearchable
|
||||||
|
isClearable
|
||||||
options={options}
|
options={options}
|
||||||
className={className}
|
className={className}
|
||||||
name={this.props.name}
|
name={this.props.name}
|
||||||
inputProps={inputProps}
|
|
||||||
onChange={this.handleChange}
|
onChange={this.handleChange}
|
||||||
onBlurResetsInput
|
value={{ value: rawValue }}
|
||||||
value={value}
|
noOptionsMessage={() => i18n._t('CMS.ANCHOR_NO_OPTIONS', 'No options')}
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
labelKey="value"
|
getOptionLabel={({ value }) => value}
|
||||||
|
classNamePrefix="anchorselectorfield"
|
||||||
/>
|
/>
|
||||||
|
</EmotionCssCacheProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -165,6 +167,7 @@ AnchorSelectorField.defaultProps = {
|
|||||||
extraClass: '',
|
extraClass: '',
|
||||||
onLoadingError: noop,
|
onLoadingError: noop,
|
||||||
attributes: {},
|
attributes: {},
|
||||||
|
CreatableSelectComponent: CreatableSelect
|
||||||
};
|
};
|
||||||
|
|
||||||
function mapStateToProps(state, ownProps) {
|
function mapStateToProps(state, ownProps) {
|
||||||
|
@ -0,0 +1,3 @@
|
|||||||
|
.anchorselectorfield__menu {
|
||||||
|
z-index: 20000;
|
||||||
|
}
|
@ -1,22 +1,18 @@
|
|||||||
/* global jest, describe, beforeEach, it, expect, setTimeout */
|
/* global jest, test, describe, beforeEach, it, expect, setTimeout */
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import anchorSelectorStates from 'state/anchorSelector/AnchorSelectorStates';
|
||||||
|
import { render, screen } from '@testing-library/react';
|
||||||
|
import { Component as AnchorSelectorField } from '../AnchorSelectorField';
|
||||||
|
|
||||||
jest.mock('isomorphic-fetch', () =>
|
jest.mock('isomorphic-fetch', () =>
|
||||||
() => Promise.resolve({
|
() => Promise.resolve({
|
||||||
json: () => ['anchor3', 'anchor4'],
|
json: () => ['anchor3', 'anchor4'],
|
||||||
}));
|
}));
|
||||||
jest.mock('i18n');
|
jest.mock('i18n');
|
||||||
|
|
||||||
import React from 'react';
|
function makeProps(obj = {}) {
|
||||||
import ReactTestUtils from 'react-dom/test-utils';
|
return {
|
||||||
import { Component as AnchorSelectorField } from '../AnchorSelectorField';
|
|
||||||
import anchorSelectorStates from 'state/anchorSelector/AnchorSelectorStates';
|
|
||||||
|
|
||||||
describe('AnchorSelectorField', () => {
|
|
||||||
let props = null;
|
|
||||||
let field = null;
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
props = {
|
|
||||||
id: 'Form_Test',
|
id: 'Form_Test',
|
||||||
name: 'Test',
|
name: 'Test',
|
||||||
data: {
|
data: {
|
||||||
@ -26,78 +22,108 @@ describe('AnchorSelectorField', () => {
|
|||||||
anchors: ['anchor1', 'anchor2'],
|
anchors: ['anchor1', 'anchor2'],
|
||||||
value: 'selectedanchor',
|
value: 'selectedanchor',
|
||||||
loadingState: anchorSelectorStates.SUCCESS,
|
loadingState: anchorSelectorStates.SUCCESS,
|
||||||
|
CreatableSelectComponent: ({ options }) => (
|
||||||
|
<div data-testid="test-creatable-select">
|
||||||
|
{options.map(option => <div key={option.value} data-option={option.value}/>)}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
...obj,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
test('AnchorSelectorField componentDidMount() Loads dirty selectors', async () => {
|
||||||
|
const beginUpdating = jest.fn();
|
||||||
|
render(<AnchorSelectorField {...makeProps({
|
||||||
|
loadingState: anchorSelectorStates.DIRTY,
|
||||||
actions: {
|
actions: {
|
||||||
anchorSelector: {
|
anchorSelector: {
|
||||||
beginUpdating: jest.fn(),
|
beginUpdating,
|
||||||
updated: jest.fn(),
|
updated: () => {},
|
||||||
updateFailed: jest.fn(),
|
updateFailed: () => {},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
})}
|
||||||
});
|
/>);
|
||||||
|
await screen.findByTestId('test-creatable-select');
|
||||||
describe('componentDidMount()', () => {
|
expect(beginUpdating).toBeCalledWith(4);
|
||||||
it('Loads dirty selectors', () => {
|
});
|
||||||
props.loadingState = anchorSelectorStates.DIRTY;
|
|
||||||
field = ReactTestUtils.renderIntoDocument(<AnchorSelectorField {...props} />);
|
test('AnchorSelectorField Merges value with page anchors', async () => {
|
||||||
expect(props.actions.anchorSelector.beginUpdating)
|
const beginUpdating = jest.fn();
|
||||||
.toHaveBeenCalledWith(4);
|
render(<AnchorSelectorField {...makeProps({
|
||||||
});
|
loadingState: anchorSelectorStates.DIRTY,
|
||||||
it('Does not load success selectors', () => {
|
actions: {
|
||||||
props.loadingState = anchorSelectorStates.SUCCESS;
|
anchorSelector: {
|
||||||
field = ReactTestUtils.renderIntoDocument(<AnchorSelectorField {...props} />);
|
beginUpdating,
|
||||||
expect(props.actions.anchorSelector.beginUpdating)
|
updated: () => {},
|
||||||
.not
|
updateFailed: () => {},
|
||||||
.toHaveBeenCalled();
|
},
|
||||||
});
|
},
|
||||||
});
|
})}
|
||||||
|
/>);
|
||||||
describe('getDropdownOptions()', () => {
|
const select = await screen.findByTestId('test-creatable-select');
|
||||||
it('Merges value with page anchors', () => {
|
const options = select.querySelectorAll('[data-option]');
|
||||||
field = ReactTestUtils.renderIntoDocument(<AnchorSelectorField {...props} />);
|
expect(options).toHaveLength(3);
|
||||||
expect(field.getDropdownOptions()).toEqual([
|
expect(options[0].getAttribute('data-option')).toBe('selectedanchor');
|
||||||
{ value: 'selectedanchor' },
|
expect(options[1].getAttribute('data-option')).toBe('anchor1');
|
||||||
{ value: 'anchor1' },
|
expect(options[2].getAttribute('data-option')).toBe('anchor2');
|
||||||
{ value: 'anchor2' },
|
});
|
||||||
]);
|
|
||||||
});
|
test('AnchorSelectorField componentDidMount() Does not load success selectors', async () => {
|
||||||
});
|
const beginUpdating = jest.fn();
|
||||||
|
render(<AnchorSelectorField {...makeProps({
|
||||||
describe('ensurePagesLoaded', () => {
|
loadingState: anchorSelectorStates.SUCCESS,
|
||||||
it('Triggers loading on dirty', () => {
|
actions: {
|
||||||
props.loadingState = anchorSelectorStates.DIRTY;
|
anchorSelector: {
|
||||||
field = ReactTestUtils.renderIntoDocument(<AnchorSelectorField {...props} />);
|
beginUpdating,
|
||||||
return field
|
updated: () => {},
|
||||||
.ensurePagesLoaded()
|
updateFailed: () => {},
|
||||||
.then((result) => {
|
},
|
||||||
expect(props.actions.anchorSelector.beginUpdating)
|
},
|
||||||
.toHaveBeenCalledWith(4);
|
})}
|
||||||
expect(props.actions.anchorSelector.updated)
|
/>);
|
||||||
.toHaveBeenCalledWith(4, ['anchor3', 'anchor4']);
|
await screen.findByTestId('test-creatable-select');
|
||||||
expect(props.actions.anchorSelector.updateFailed)
|
expect(beginUpdating).not.toBeCalled();
|
||||||
.not
|
});
|
||||||
.toHaveBeenCalled();
|
|
||||||
expect(result).toEqual(['anchor3', 'anchor4']);
|
test('AnchorSelectorField ensurePagesLoaded Triggers loading on dirty', async () => {
|
||||||
});
|
const beginUpdating = jest.fn();
|
||||||
});
|
const updated = jest.fn();
|
||||||
|
const updateFailed = jest.fn();
|
||||||
it('Does not trigger updating', () => {
|
render(<AnchorSelectorField {...makeProps({
|
||||||
props.loadingState = anchorSelectorStates.UPDATING;
|
loadingState: anchorSelectorStates.DIRTY,
|
||||||
field = ReactTestUtils.renderIntoDocument(<AnchorSelectorField {...props} />);
|
actions: {
|
||||||
return field
|
anchorSelector: {
|
||||||
.ensurePagesLoaded()
|
beginUpdating,
|
||||||
.then((result) => {
|
updated,
|
||||||
expect(props.actions.anchorSelector.beginUpdating)
|
updateFailed,
|
||||||
.not
|
},
|
||||||
.toHaveBeenCalled();
|
},
|
||||||
expect(props.actions.anchorSelector.updated)
|
})}
|
||||||
.not
|
/>);
|
||||||
.toHaveBeenCalled();
|
await screen.findByTestId('test-creatable-select');
|
||||||
expect(props.actions.anchorSelector.updateFailed)
|
expect(beginUpdating).toBeCalledWith(4);
|
||||||
.not
|
expect(updated).toBeCalledWith(4, ['anchor3', 'anchor4']);
|
||||||
.toHaveBeenCalled();
|
expect(updateFailed).not.toBeCalled();
|
||||||
expect(result).toBe(undefined);
|
});
|
||||||
});
|
|
||||||
});
|
test('AnchorSelectorField ensurePagesLoaded Does not trigger updating', async () => {
|
||||||
});
|
const beginUpdating = jest.fn();
|
||||||
|
const updated = jest.fn();
|
||||||
|
const updateFailed = jest.fn();
|
||||||
|
render(<AnchorSelectorField {...makeProps({
|
||||||
|
loadingState: anchorSelectorStates.UPDATING,
|
||||||
|
actions: {
|
||||||
|
anchorSelector: {
|
||||||
|
beginUpdating,
|
||||||
|
updated,
|
||||||
|
updateFailed,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
/>);
|
||||||
|
await screen.findByTestId('test-creatable-select');
|
||||||
|
expect(beginUpdating).not.toBeCalled();
|
||||||
|
expect(updated).not.toBeCalled();
|
||||||
|
expect(updateFailed).not.toBeCalled();
|
||||||
});
|
});
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
*/
|
*/
|
||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import i18n from 'i18n';
|
import i18n from 'i18n';
|
||||||
import reactConfirm from "@silverstripe/reactstrap-confirm";
|
import reactConfirm from 'reactstrap-confirm';
|
||||||
|
|
||||||
$.entwine('ss', function($){
|
$.entwine('ss', function($){
|
||||||
/**
|
/**
|
||||||
@ -497,12 +497,13 @@ $.entwine('ss', function($){
|
|||||||
'By changing the URL segment visitors will not be able to view it.'
|
'By changing the URL segment visitors will not be able to view it.'
|
||||||
);
|
);
|
||||||
|
|
||||||
if (await reactConfirm(message, {
|
if (await reactConfirm({
|
||||||
title: i18n._t(
|
title: i18n._t(
|
||||||
'CMS.RemoveHomePageWarningTitle',
|
'CMS.RemoveHomePageWarningTitle',
|
||||||
'Remove your home page?'
|
'Remove your home page?'
|
||||||
),
|
),
|
||||||
confirmLabel: i18n._t(
|
message,
|
||||||
|
confirmText: i18n._t(
|
||||||
'CMS.RemoveHomePageWarningLabel',
|
'CMS.RemoveHomePageWarningLabel',
|
||||||
'Remove'
|
'Remove'
|
||||||
),
|
),
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
import i18n from 'i18n';
|
import i18n from 'i18n';
|
||||||
import reactConfirm from "@silverstripe/reactstrap-confirm";
|
import reactConfirm from 'reactstrap-confirm';
|
||||||
|
import { joinUrlPaths } from 'lib/urls';
|
||||||
|
|
||||||
$.entwine('ss.tree', function($) {
|
$.entwine('ss.tree', function($) {
|
||||||
$('.cms-tree').entwine({
|
$('.cms-tree').entwine({
|
||||||
@ -45,7 +46,7 @@ $.entwine('ss.tree', function($) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const baseUrl = $('base').attr('href') || ''; // Edge17 and IE11 require absolute paths
|
const baseUrl = $('base').attr('href') || ''; // Edge17 and IE11 require absolute paths
|
||||||
window.location.assign(baseUrl + urlWithParams);
|
window.location.assign(joinUrlPaths(baseUrl, urlWithParams));
|
||||||
},
|
},
|
||||||
|
|
||||||
getTreeConfig: function() {
|
getTreeConfig: function() {
|
||||||
@ -168,12 +169,13 @@ $.entwine('ss.tree', function($) {
|
|||||||
'By changing the URL segment visitors will not be able to view it.'
|
'By changing the URL segment visitors will not be able to view it.'
|
||||||
);
|
);
|
||||||
|
|
||||||
return await reactConfirm(message, {
|
return await reactConfirm({
|
||||||
title: i18n._t(
|
title: i18n._t(
|
||||||
'CMS.RemoveHomePageWarningTitle',
|
'CMS.RemoveHomePageWarningTitle',
|
||||||
'Remove your home page?'
|
'Remove your home page?'
|
||||||
),
|
),
|
||||||
confirmLabel: i18n._t(
|
message,
|
||||||
|
confirmText: i18n._t(
|
||||||
'CMS.RemoveHomePageWarningLabel',
|
'CMS.RemoveHomePageWarningLabel',
|
||||||
'Remove'
|
'Remove'
|
||||||
),
|
),
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import $ from 'jquery';
|
import $ from 'jquery';
|
||||||
|
import { joinUrlPaths } from 'lib/urls';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Behaviour for the CMS Content Toolbar.
|
* Behaviour for the CMS Content Toolbar.
|
||||||
@ -81,7 +82,7 @@ $.entwine('ss', function ($) {
|
|||||||
localStorage.setItem('ss.pages-view-type', viewType);
|
localStorage.setItem('ss.pages-view-type', viewType);
|
||||||
if(isContentViewInSidebar && viewType === VIEW_TYPE_LIST) {
|
if(isContentViewInSidebar && viewType === VIEW_TYPE_LIST) {
|
||||||
const baseUrl = $('base').attr('href') || ''; // Edge17 and IE11 need absolute path
|
const baseUrl = $('base').attr('href') || ''; // Edge17 and IE11 need absolute path
|
||||||
window.location.assign(baseUrl + $contentView.data('url-listviewroot'));
|
window.location.assign(joinUrlPaths(baseUrl, $contentView.data('url-listviewroot')));
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1,183 +0,0 @@
|
|||||||
import $ from 'jquery';
|
|
||||||
import i18n from 'i18n';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* File: CMSPageHistoryController.js
|
|
||||||
*
|
|
||||||
* Handles related interactions between the version selection form on the
|
|
||||||
* left hand side of the panel and the version displaying on the right
|
|
||||||
* hand side.
|
|
||||||
*/
|
|
||||||
$.entwine('ss', ($) => { // eslint-disable-line no-shadow
|
|
||||||
/**
|
|
||||||
* Class: #Form_VersionsForm
|
|
||||||
*
|
|
||||||
* The left hand side version selection form is the main interface for
|
|
||||||
* users to select a version to view, or to compare two versions
|
|
||||||
*/
|
|
||||||
$('#Form_VersionsForm').entwine({
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*/
|
|
||||||
onmatch() {
|
|
||||||
this._super();
|
|
||||||
},
|
|
||||||
onunmatch() {
|
|
||||||
this._super();
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Function: submit.
|
|
||||||
*
|
|
||||||
* Submits either the compare versions form or the view single form
|
|
||||||
* display based on whether we have two or 1 option selected
|
|
||||||
*
|
|
||||||
* Todo:
|
|
||||||
* Handle coupling to admin url
|
|
||||||
*/
|
|
||||||
onsubmit(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
const id = this.find(':input[name=ID]').val();
|
|
||||||
|
|
||||||
if (!id) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
let url = null;
|
|
||||||
let to = null;
|
|
||||||
let from = null;
|
|
||||||
|
|
||||||
const compare = (this.find(':input[name=CompareMode]').is(':checked'));
|
|
||||||
const selected = this.find('table input[type=checkbox]').filter(':checked');
|
|
||||||
|
|
||||||
if (compare) {
|
|
||||||
if (selected.length !== 2) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
to = selected.eq(0).val();
|
|
||||||
from = selected.eq(1).val();
|
|
||||||
url = i18n.sprintf(this.data('linkTmplCompare'), id, from, to);
|
|
||||||
} else {
|
|
||||||
to = selected.eq(0).val();
|
|
||||||
url = i18n.sprintf(this.data('linkTmplShow'), id, to);
|
|
||||||
}
|
|
||||||
|
|
||||||
$('.cms-container').loadPanel(url, '', { pjax: 'CurrentForm' });
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class: :input[name=ShowUnpublished]
|
|
||||||
*
|
|
||||||
* Used for toggling whether to show or hide unpublished versions.
|
|
||||||
*/
|
|
||||||
$('#Form_VersionsForm input[name=ShowUnpublished]').entwine({
|
|
||||||
onmatch() {
|
|
||||||
this.toggle();
|
|
||||||
this._super();
|
|
||||||
},
|
|
||||||
onunmatch() {
|
|
||||||
this._super();
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Event: :input[name=ShowUnpublished] change
|
|
||||||
*
|
|
||||||
* Changing the show unpublished checkbox toggles whether to show
|
|
||||||
* or hide the unpublished versions. Because those rows may be being
|
|
||||||
* compared this also ensures those rows are unselected.
|
|
||||||
*/
|
|
||||||
onchange() {
|
|
||||||
this.toggle();
|
|
||||||
},
|
|
||||||
toggle() {
|
|
||||||
const self = $(this);
|
|
||||||
const unpublished = self.parents('form').find('tr[data-published=false]');
|
|
||||||
|
|
||||||
if (self.attr('checked')) {
|
|
||||||
unpublished
|
|
||||||
.removeClass('ui-helper-hidden')
|
|
||||||
.show();
|
|
||||||
} else {
|
|
||||||
unpublished
|
|
||||||
.addClass('ui-helper-hidden')
|
|
||||||
.hide()
|
|
||||||
._unselect();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class: #Form_VersionsForm tr
|
|
||||||
*
|
|
||||||
* An individual row in the versions form. Selecting the row updates
|
|
||||||
* the edit form depending on whether we're showing individual version
|
|
||||||
* information or displaying comparsion.
|
|
||||||
*/
|
|
||||||
$('#Form_VersionsForm tbody tr').entwine({
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function: onclick
|
|
||||||
*
|
|
||||||
* Selects or deselects the row (if in compare mode). Will trigger
|
|
||||||
* an update of the edit form if either selected (in single mode)
|
|
||||||
* or if this is the second row selected (in compare mode)
|
|
||||||
*/
|
|
||||||
onclick() {
|
|
||||||
// compare mode
|
|
||||||
const compare = this.parents('form').find(':input[name=CompareMode]').attr('checked');
|
|
||||||
const selected = this.siblings('.active');
|
|
||||||
|
|
||||||
if (compare && this.hasClass('active')) {
|
|
||||||
this._unselect();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (compare) {
|
|
||||||
// check if we have already selected more than two.
|
|
||||||
if (selected.length > 1) {
|
|
||||||
// eslint-disable-next-line no-alert
|
|
||||||
alert(i18n._t('CMS.ONLYSELECTTWO', 'You can only compare two versions at this time.'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._select();
|
|
||||||
|
|
||||||
// if this is the second selected then we can compare.
|
|
||||||
if (selected.length === 1) {
|
|
||||||
this.parents('form').submit();
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._select();
|
|
||||||
selected._unselect();
|
|
||||||
this.parents('form').submit();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function: _unselect()
|
|
||||||
*
|
|
||||||
* Unselects the row from the form selection.
|
|
||||||
*
|
|
||||||
* Using regular js to update the class rather than this.removeClass('active')
|
|
||||||
* because the latter causes the browser to continuously call
|
|
||||||
* element.compareDocumentPosition, causing the browser to hang for long
|
|
||||||
* periods of time, especially on pages with lots of versions (e.g. 100+)
|
|
||||||
*/
|
|
||||||
_unselect() {
|
|
||||||
this.get(0).classList.remove('active');
|
|
||||||
this.find(':input[type=checkbox][checked]').attr('checked', false);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function: _select()
|
|
||||||
*
|
|
||||||
* Selects the currently matched row in the form selection
|
|
||||||
*/
|
|
||||||
_select() {
|
|
||||||
this.addClass('active');
|
|
||||||
this.find(':input[type=checkbox]').attr('checked', true);
|
|
||||||
},
|
|
||||||
|
|
||||||
});
|
|
||||||
});
|
|
@ -2,8 +2,8 @@
|
|||||||
import i18n from 'i18n';
|
import i18n from 'i18n';
|
||||||
import TinyMCEActionRegistrar from 'lib/TinyMCEActionRegistrar';
|
import TinyMCEActionRegistrar from 'lib/TinyMCEActionRegistrar';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import { createRoot } from 'react-dom/client';
|
||||||
import { ApolloProvider } from 'react-apollo';
|
import { ApolloProvider } from '@apollo/client';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import jQuery from 'jquery';
|
import jQuery from 'jquery';
|
||||||
import ShortcodeSerialiser from 'lib/ShortcodeSerialiser';
|
import ShortcodeSerialiser from 'lib/ShortcodeSerialiser';
|
||||||
@ -21,10 +21,10 @@ const plugin = {
|
|||||||
'sslink',
|
'sslink',
|
||||||
{
|
{
|
||||||
text: i18n._t('CMS.LINKLABEL_ANCHOR', 'Anchor on a page'),
|
text: i18n._t('CMS.LINKLABEL_ANCHOR', 'Anchor on a page'),
|
||||||
onclick: (activeEditor) => activeEditor.execCommand(commandName),
|
onAction: (activeEditor) => activeEditor.execCommand(commandName),
|
||||||
priority: 60,
|
priority: 60,
|
||||||
},
|
},
|
||||||
editor.settings.editorIdentifier,
|
editor.getParam('editorIdentifier'),
|
||||||
)
|
)
|
||||||
.addCommandWithUrlTest(commandName, /^\[sitetree_link.+]#[^#\]]+$/);
|
.addCommandWithUrlTest(commandName, /^\[sitetree_link.+]#[^#\]]+$/);
|
||||||
|
|
||||||
@ -33,8 +33,8 @@ const plugin = {
|
|||||||
const field = jQuery(`#${editor.id}`).entwine('ss');
|
const field = jQuery(`#${editor.id}`).entwine('ss');
|
||||||
// Get the anchors in the current field and save them as props for AnchorSelectorField
|
// Get the anchors in the current field and save them as props for AnchorSelectorField
|
||||||
const currentPageID = Number(jQuery('#Form_EditForm_ID').val() || 0);
|
const currentPageID = Number(jQuery('#Form_EditForm_ID').val() || 0);
|
||||||
const validTargets = editor
|
const validTargets = jQuery(editor.getBody())
|
||||||
.$('[id],[name]', editor.getBody())
|
.find('[id],[name]')
|
||||||
.toArray()
|
.toArray()
|
||||||
.map((element) => element.id || element.name);
|
.map((element) => element.id || element.name);
|
||||||
ss.store.dispatch(updatedCurrentField(currentPageID, validTargets, editor.id));
|
ss.store.dispatch(updatedCurrentField(currentPageID, validTargets, editor.id));
|
||||||
@ -69,6 +69,8 @@ jQuery.entwine('ss', ($) => {
|
|||||||
* Assumes that $('.insert-link__dialog-wrapper').entwine({}); is defined for shared functions
|
* Assumes that $('.insert-link__dialog-wrapper').entwine({}); is defined for shared functions
|
||||||
*/
|
*/
|
||||||
$(`#${modalId}`).entwine({
|
$(`#${modalId}`).entwine({
|
||||||
|
ReactRoot: null,
|
||||||
|
|
||||||
renderModal(isOpen) {
|
renderModal(isOpen) {
|
||||||
const store = ss.store;
|
const store = ss.store;
|
||||||
const client = ss.apolloClient;
|
const client = ss.apolloClient;
|
||||||
@ -79,7 +81,12 @@ jQuery.entwine('ss', ($) => {
|
|||||||
const currentPageID = Number($('#Form_EditForm_ID').val() || 0);
|
const currentPageID = Number($('#Form_EditForm_ID').val() || 0);
|
||||||
|
|
||||||
// create/update the react component
|
// create/update the react component
|
||||||
ReactDOM.render(
|
let root = this.getReactRoot();
|
||||||
|
if (!root) {
|
||||||
|
root = createRoot(this[0]);
|
||||||
|
this.setReactRoot(root);
|
||||||
|
}
|
||||||
|
root.render(
|
||||||
<ApolloProvider client={client}>
|
<ApolloProvider client={client}>
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<InsertLinkInternalModal
|
<InsertLinkInternalModal
|
||||||
@ -95,8 +102,7 @@ jQuery.entwine('ss', ($) => {
|
|||||||
currentPageID={currentPageID}
|
currentPageID={currentPageID}
|
||||||
/>
|
/>
|
||||||
</Provider>
|
</Provider>
|
||||||
</ApolloProvider>,
|
</ApolloProvider>
|
||||||
this[0]
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
import i18n from 'i18n';
|
import i18n from 'i18n';
|
||||||
import TinyMCEActionRegistrar from 'lib/TinyMCEActionRegistrar';
|
import TinyMCEActionRegistrar from 'lib/TinyMCEActionRegistrar';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import { createRoot } from 'react-dom/client';
|
||||||
import { ApolloProvider } from 'react-apollo';
|
import { ApolloProvider } from '@apollo/client';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import jQuery from 'jquery';
|
import jQuery from 'jquery';
|
||||||
import ShortcodeSerialiser from 'lib/ShortcodeSerialiser';
|
import ShortcodeSerialiser from 'lib/ShortcodeSerialiser';
|
||||||
@ -20,10 +20,10 @@ const plugin = {
|
|||||||
'sslink',
|
'sslink',
|
||||||
{
|
{
|
||||||
text: i18n._t('CMS.LINKLABEL_PAGE', 'Page on this site'),
|
text: i18n._t('CMS.LINKLABEL_PAGE', 'Page on this site'),
|
||||||
onclick: (activeEditor) => activeEditor.execCommand(commandName),
|
onAction: (activeEditor) => activeEditor.execCommand(commandName),
|
||||||
priority: 90,
|
priority: 90,
|
||||||
},
|
},
|
||||||
editor.settings.editorIdentifier,
|
editor.getParam('editorIdentifier'),
|
||||||
)
|
)
|
||||||
.addCommandWithUrlTest(commandName, /^\[sitetree_link.+]$/);
|
.addCommandWithUrlTest(commandName, /^\[sitetree_link.+]$/);
|
||||||
|
|
||||||
@ -61,6 +61,8 @@ jQuery.entwine('ss', ($) => {
|
|||||||
* Assumes that $('.insert-link__dialog-wrapper').entwine({}); is defined for shared functions
|
* Assumes that $('.insert-link__dialog-wrapper').entwine({}); is defined for shared functions
|
||||||
*/
|
*/
|
||||||
$(`#${modalId}`).entwine({
|
$(`#${modalId}`).entwine({
|
||||||
|
ReactRoot: null,
|
||||||
|
|
||||||
renderModal(isOpen) {
|
renderModal(isOpen) {
|
||||||
const store = ss.store;
|
const store = ss.store;
|
||||||
const client = ss.apolloClient;
|
const client = ss.apolloClient;
|
||||||
@ -70,7 +72,12 @@ jQuery.entwine('ss', ($) => {
|
|||||||
const requireLinkText = this.getRequireLinkText();
|
const requireLinkText = this.getRequireLinkText();
|
||||||
|
|
||||||
// create/update the react component
|
// create/update the react component
|
||||||
ReactDOM.render(
|
let root = this.getReactRoot();
|
||||||
|
if (!root) {
|
||||||
|
root = createRoot(this[0]);
|
||||||
|
this.setReactRoot(root);
|
||||||
|
}
|
||||||
|
root.render(
|
||||||
<ApolloProvider client={client}>
|
<ApolloProvider client={client}>
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
<InsertLinkInternalModal
|
<InsertLinkInternalModal
|
||||||
@ -85,8 +92,7 @@ jQuery.entwine('ss', ($) => {
|
|||||||
requireLinkText={requireLinkText}
|
requireLinkText={requireLinkText}
|
||||||
/>
|
/>
|
||||||
</Provider>
|
</Provider>
|
||||||
</ApolloProvider>,
|
</ApolloProvider>
|
||||||
this[0]
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { graphql } from 'react-apollo';
|
import { graphql } from '@apollo/client/react/hoc';
|
||||||
import gql from 'graphql-tag';
|
import gql from 'graphql-tag';
|
||||||
|
|
||||||
// GraphQL query for retrieving the version history of a specific page. The
|
// GraphQL query for retrieving the version history of a specific page. The
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { graphql } from 'react-apollo';
|
import { graphql } from '@apollo/client/react/hoc';
|
||||||
import gql from 'graphql-tag';
|
import gql from 'graphql-tag';
|
||||||
|
|
||||||
const mutation = gql`
|
const mutation = gql`
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { graphql } from 'react-apollo';
|
import { graphql } from '@apollo/client/react/hoc';
|
||||||
import gql from 'graphql-tag';
|
import gql from 'graphql-tag';
|
||||||
|
|
||||||
const mutation = gql`
|
const mutation = gql`
|
||||||
|
@ -7,3 +7,5 @@
|
|||||||
|
|
||||||
@import "legacy/CMSMain";
|
@import "legacy/CMSMain";
|
||||||
@import "legacy/ReportAdmin";
|
@import "legacy/ReportAdmin";
|
||||||
|
|
||||||
|
@import "../components/AnchorSelectorField/AnchorSelectorField";
|
||||||
|
@ -30,31 +30,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.CMSPageHistoryController {
|
|
||||||
overflow: hidden; // Fixes weird bug for double scroll in history area on browser resize
|
|
||||||
|
|
||||||
ins {
|
|
||||||
background-color: #DFD;
|
|
||||||
padding: 2px;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
del {
|
|
||||||
background-color: #FDD;
|
|
||||||
padding: 2px;
|
|
||||||
color: darken(#FDD, 30%);
|
|
||||||
}
|
|
||||||
|
|
||||||
.htmleditorfield.readonly img {
|
|
||||||
max-width: 100%;
|
|
||||||
height: auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cms-content-tools.collapsed {
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** --------------------------------------------
|
/** --------------------------------------------
|
||||||
* Tree View (collapsed for sidebar)
|
* Tree View (collapsed for sidebar)
|
||||||
* -------------------------------------------- */
|
* -------------------------------------------- */
|
||||||
|
@ -4,6 +4,7 @@ namespace SilverStripe\CMS\BatchActions;
|
|||||||
|
|
||||||
use SilverStripe\ORM\SS_List;
|
use SilverStripe\ORM\SS_List;
|
||||||
use SilverStripe\Admin\CMSBatchAction;
|
use SilverStripe\Admin\CMSBatchAction;
|
||||||
|
use SilverStripe\Control\HTTPResponse;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete items batch action.
|
* Delete items batch action.
|
||||||
@ -15,7 +16,7 @@ class CMSBatchAction_Archive extends CMSBatchAction
|
|||||||
return _t(__CLASS__ . '.TITLE', 'Unpublish and archive');
|
return _t(__CLASS__ . '.TITLE', 'Unpublish and archive');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function run(SS_List $pages)
|
public function run(SS_List $pages): HTTPResponse
|
||||||
{
|
{
|
||||||
return $this->batchaction(
|
return $this->batchaction(
|
||||||
$pages,
|
$pages,
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace SilverStripe\CMS\BatchActions;
|
namespace SilverStripe\CMS\BatchActions;
|
||||||
|
|
||||||
use SilverStripe\Admin\CMSBatchAction;
|
use SilverStripe\Admin\CMSBatchAction;
|
||||||
|
use SilverStripe\Control\HTTPResponse;
|
||||||
use SilverStripe\ORM\SS_List;
|
use SilverStripe\ORM\SS_List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -15,7 +16,7 @@ class CMSBatchAction_Publish extends CMSBatchAction
|
|||||||
return _t(__CLASS__ . '.PUBLISH_PAGES', 'Publish');
|
return _t(__CLASS__ . '.PUBLISH_PAGES', 'Publish');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function run(SS_List $pages)
|
public function run(SS_List $pages): HTTPResponse
|
||||||
{
|
{
|
||||||
return $this->batchaction(
|
return $this->batchaction(
|
||||||
$pages,
|
$pages,
|
||||||
|
@ -4,6 +4,7 @@ namespace SilverStripe\CMS\BatchActions;
|
|||||||
|
|
||||||
use SilverStripe\Admin\CMSBatchAction;
|
use SilverStripe\Admin\CMSBatchAction;
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
use SilverStripe\Control\HTTPResponse;
|
||||||
use SilverStripe\ORM\ArrayList;
|
use SilverStripe\ORM\ArrayList;
|
||||||
use SilverStripe\ORM\SS_List;
|
use SilverStripe\ORM\SS_List;
|
||||||
use SilverStripe\Versioned\Versioned;
|
use SilverStripe\Versioned\Versioned;
|
||||||
@ -20,7 +21,7 @@ class CMSBatchAction_Restore extends CMSBatchAction
|
|||||||
return _t(__CLASS__ . '.RESTORE', 'Restore');
|
return _t(__CLASS__ . '.RESTORE', 'Restore');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function run(SS_List $pages)
|
public function run(SS_List $pages): HTTPResponse
|
||||||
{
|
{
|
||||||
// Sort pages by depth
|
// Sort pages by depth
|
||||||
$pageArray = $pages->toArray();
|
$pageArray = $pages->toArray();
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
namespace SilverStripe\CMS\BatchActions;
|
namespace SilverStripe\CMS\BatchActions;
|
||||||
|
|
||||||
use SilverStripe\Admin\CMSBatchAction;
|
use SilverStripe\Admin\CMSBatchAction;
|
||||||
|
use SilverStripe\Control\HTTPResponse;
|
||||||
use SilverStripe\ORM\SS_List;
|
use SilverStripe\ORM\SS_List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -15,7 +16,7 @@ class CMSBatchAction_Unpublish extends CMSBatchAction
|
|||||||
return _t(__CLASS__ . '.UNPUBLISH_PAGES', 'Unpublish');
|
return _t(__CLASS__ . '.UNPUBLISH_PAGES', 'Unpublish');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function run(SS_List $pages)
|
public function run(SS_List $pages): HTTPResponse
|
||||||
{
|
{
|
||||||
return $this->batchaction(
|
return $this->batchaction(
|
||||||
$pages,
|
$pages,
|
||||||
|
@ -2,13 +2,13 @@
|
|||||||
|
|
||||||
namespace SilverStripe\CMS\Controllers;
|
namespace SilverStripe\CMS\Controllers;
|
||||||
|
|
||||||
use SilverStripe\Dev\Deprecation;
|
|
||||||
use InvalidArgumentException;
|
use InvalidArgumentException;
|
||||||
use Psr\SimpleCache\CacheInterface;
|
use Psr\SimpleCache\CacheInterface;
|
||||||
use SilverStripe\Admin\AdminRootController;
|
use SilverStripe\Admin\AdminRootController;
|
||||||
use SilverStripe\Admin\CMSBatchActionHandler;
|
use SilverStripe\Admin\CMSBatchActionHandler;
|
||||||
use SilverStripe\Admin\LeftAndMain;
|
use SilverStripe\Admin\LeftAndMain;
|
||||||
use SilverStripe\Admin\LeftAndMainFormRequestHandler;
|
use SilverStripe\Admin\LeftAndMainFormRequestHandler;
|
||||||
|
use SilverStripe\Admin\Navigator\SilverStripeNavigator;
|
||||||
use SilverStripe\CMS\BatchActions\CMSBatchAction_Archive;
|
use SilverStripe\CMS\BatchActions\CMSBatchAction_Archive;
|
||||||
use SilverStripe\CMS\BatchActions\CMSBatchAction_Publish;
|
use SilverStripe\CMS\BatchActions\CMSBatchAction_Publish;
|
||||||
use SilverStripe\CMS\BatchActions\CMSBatchAction_Restore;
|
use SilverStripe\CMS\BatchActions\CMSBatchAction_Restore;
|
||||||
@ -23,6 +23,7 @@ use SilverStripe\Control\Director;
|
|||||||
use SilverStripe\Control\HTTPRequest;
|
use SilverStripe\Control\HTTPRequest;
|
||||||
use SilverStripe\Control\HTTPResponse;
|
use SilverStripe\Control\HTTPResponse;
|
||||||
use SilverStripe\Control\HTTPResponse_Exception;
|
use SilverStripe\Control\HTTPResponse_Exception;
|
||||||
|
use SilverStripe\Control\PjaxResponseNegotiator;
|
||||||
use SilverStripe\Core\Cache\MemberCacheFlusher;
|
use SilverStripe\Core\Cache\MemberCacheFlusher;
|
||||||
use SilverStripe\Core\Config\Config;
|
use SilverStripe\Core\Config\Config;
|
||||||
use SilverStripe\Core\Convert;
|
use SilverStripe\Core\Convert;
|
||||||
@ -69,10 +70,9 @@ use SilverStripe\SiteConfig\SiteConfig;
|
|||||||
use SilverStripe\Versioned\ChangeSet;
|
use SilverStripe\Versioned\ChangeSet;
|
||||||
use SilverStripe\Versioned\ChangeSetItem;
|
use SilverStripe\Versioned\ChangeSetItem;
|
||||||
use SilverStripe\Versioned\Versioned;
|
use SilverStripe\Versioned\Versioned;
|
||||||
|
use SilverStripe\VersionedAdmin\Controllers\CMSPageHistoryViewerController;
|
||||||
use SilverStripe\View\ArrayData;
|
use SilverStripe\View\ArrayData;
|
||||||
use SilverStripe\View\Requirements;
|
use SilverStripe\View\Requirements;
|
||||||
use Translatable;
|
|
||||||
use SilverStripe\VersionedAdmin\Controllers\CMSPageHistoryViewerController;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The main "content" area of the CMS.
|
* The main "content" area of the CMS.
|
||||||
@ -80,8 +80,6 @@ use SilverStripe\VersionedAdmin\Controllers\CMSPageHistoryViewerController;
|
|||||||
* This class creates a 2-frame layout - left-tree and right-form - to sit beneath the main
|
* This class creates a 2-frame layout - left-tree and right-form - to sit beneath the main
|
||||||
* admin menu.
|
* admin menu.
|
||||||
*
|
*
|
||||||
* @todo Create some base classes to contain the generic functionality that will be replicated.
|
|
||||||
*
|
|
||||||
* @mixin LeftAndMainPageIconsExtension
|
* @mixin LeftAndMainPageIconsExtension
|
||||||
*/
|
*/
|
||||||
class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionProvider, Flushable, MemberCacheFlusher
|
class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionProvider, Flushable, MemberCacheFlusher
|
||||||
@ -107,12 +105,6 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
|
|
||||||
private static $tree_class = SiteTree::class;
|
private static $tree_class = SiteTree::class;
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated 1.13.0 Do not use this options.
|
|
||||||
* @config
|
|
||||||
*/
|
|
||||||
private static $subitem_class = Member::class;
|
|
||||||
|
|
||||||
private static $session_namespace = self::class;
|
private static $session_namespace = self::class;
|
||||||
|
|
||||||
private static $required_permission_codes = 'CMS_ACCESS_CMSMain';
|
private static $required_permission_codes = 'CMS_ACCESS_CMSMain';
|
||||||
@ -188,19 +180,13 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
|
|
||||||
protected function init()
|
protected function init()
|
||||||
{
|
{
|
||||||
// set reading lang
|
|
||||||
if (SiteTree::has_extension('Translatable') && !$this->getRequest()->isAjax()) {
|
|
||||||
Translatable::choose_site_locale(array_keys(Translatable::get_existing_content_languages(SiteTree::class) ?? []));
|
|
||||||
}
|
|
||||||
|
|
||||||
parent::init();
|
parent::init();
|
||||||
|
|
||||||
Requirements::javascript('silverstripe/cms: client/dist/js/bundle.js');
|
Requirements::javascript('silverstripe/cms: client/dist/js/bundle.js');
|
||||||
Requirements::javascript('silverstripe/cms: client/dist/js/SilverStripeNavigator.js');
|
Requirements::javascript('silverstripe/cms: client/dist/js/SilverStripeNavigator.js');
|
||||||
Requirements::css('silverstripe/cms: client/dist/styles/bundle.css');
|
Requirements::css('silverstripe/cms: client/dist/styles/bundle.css');
|
||||||
Requirements::customCSS($this->generatePageIconsCss(), self::PAGE_ICONS_ID);
|
|
||||||
|
|
||||||
Requirements::add_i18n_javascript('silverstripe/cms: client/lang', false, true);
|
Requirements::add_i18n_javascript('silverstripe/cms: client/lang', false);
|
||||||
|
|
||||||
CMSBatchActionHandler::register('restore', CMSBatchAction_Restore::class);
|
CMSBatchActionHandler::register('restore', CMSBatchAction_Restore::class);
|
||||||
CMSBatchActionHandler::register('archive', CMSBatchAction_Archive::class);
|
CMSBatchActionHandler::register('archive', CMSBatchAction_Archive::class);
|
||||||
@ -208,7 +194,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
CMSBatchActionHandler::register('publish', CMSBatchAction_Publish::class);
|
CMSBatchActionHandler::register('publish', CMSBatchAction_Publish::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function index($request)
|
public function index(HTTPRequest $request): HTTPResponse
|
||||||
{
|
{
|
||||||
// In case we're not showing a specific record, explicitly remove any session state,
|
// In case we're not showing a specific record, explicitly remove any session state,
|
||||||
// to avoid it being highlighted in the tree, and causing an edit form to show.
|
// to avoid it being highlighted in the tree, and causing an edit form to show.
|
||||||
@ -219,7 +205,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
return parent::index($request);
|
return parent::index($request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getResponseNegotiator()
|
public function getResponseNegotiator(): PjaxResponseNegotiator
|
||||||
{
|
{
|
||||||
$negotiator = parent::getResponseNegotiator();
|
$negotiator = parent::getResponseNegotiator();
|
||||||
|
|
||||||
@ -414,10 +400,13 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
|
|
||||||
public function LinkPageHistory()
|
public function LinkPageHistory()
|
||||||
{
|
{
|
||||||
if ($id = $this->currentPageID()) {
|
$controller = Injector::inst()->get(CMSPageHistoryViewerController::class);
|
||||||
|
if (($id = $this->currentPageID()) && $controller) {
|
||||||
|
if ($controller) {
|
||||||
return $this->LinkWithSearch(
|
return $this->LinkWithSearch(
|
||||||
Controller::join_links(CMSPageHistoryViewerController::singleton()->Link('show'), $id)
|
Controller::join_links($controller->Link('show'), $id)
|
||||||
);
|
);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -664,11 +653,8 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
/**
|
/**
|
||||||
* Get a subtree underneath the request param 'ID'.
|
* Get a subtree underneath the request param 'ID'.
|
||||||
* If ID = 0, then get the whole tree.
|
* If ID = 0, then get the whole tree.
|
||||||
*
|
|
||||||
* @param HTTPRequest $request
|
|
||||||
* @return string
|
|
||||||
*/
|
*/
|
||||||
public function getsubtree($request)
|
public function getsubtree(HTTPRequest $request): HTTPResponse
|
||||||
{
|
{
|
||||||
$html = $this->getSiteTreeFor(
|
$html = $this->getSiteTreeFor(
|
||||||
$this->config()->get('tree_class'),
|
$this->config()->get('tree_class'),
|
||||||
@ -683,7 +669,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
$html = preg_replace('/^[\s\t\r\n]*<ul[^>]*>/', '', $html ?? '');
|
$html = preg_replace('/^[\s\t\r\n]*<ul[^>]*>/', '', $html ?? '');
|
||||||
$html = preg_replace('/<\/ul[^>]*>[\s\t\r\n]*$/', '', $html ?? '');
|
$html = preg_replace('/<\/ul[^>]*>[\s\t\r\n]*$/', '', $html ?? '');
|
||||||
|
|
||||||
return $html;
|
return $this->getResponse()->setBody($html);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -691,11 +677,8 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
* Similar to {@link getsubtree()}, but doesn't enforce loading
|
* Similar to {@link getsubtree()}, but doesn't enforce loading
|
||||||
* all children with the node. Useful to refresh views after
|
* all children with the node. Useful to refresh views after
|
||||||
* state modifications, e.g. saving a form.
|
* state modifications, e.g. saving a form.
|
||||||
*
|
|
||||||
* @param HTTPRequest $request
|
|
||||||
* @return HTTPResponse
|
|
||||||
*/
|
*/
|
||||||
public function updatetreenodes($request)
|
public function updatetreenodes(HTTPRequest $request): HTTPResponse
|
||||||
{
|
{
|
||||||
$data = [];
|
$data = [];
|
||||||
$ids = explode(',', $request->getVar('ids') ?? '');
|
$ids = explode(',', $request->getVar('ids') ?? '');
|
||||||
@ -717,7 +700,6 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
$markingSet->markUnexpanded($record);
|
$markingSet->markUnexpanded($record);
|
||||||
|
|
||||||
// Find the next & previous nodes, for proper positioning (Sort isn't good enough - it's not a raw offset)
|
// Find the next & previous nodes, for proper positioning (Sort isn't good enough - it's not a raw offset)
|
||||||
// TODO: These methods should really be in hierarchy - for a start it assumes Sort exists
|
|
||||||
$prev = null;
|
$prev = null;
|
||||||
|
|
||||||
$className = $this->config()->get('tree_class');
|
$className = $this->config()->get('tree_class');
|
||||||
@ -763,17 +745,15 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
* - 'SiblingIDs': Array of all sibling nodes to the moved node (incl. the node itself).
|
* - 'SiblingIDs': Array of all sibling nodes to the moved node (incl. the node itself).
|
||||||
* In case of a 'ParentID' change, relates to the new siblings under the new parent.
|
* In case of a 'ParentID' change, relates to the new siblings under the new parent.
|
||||||
*
|
*
|
||||||
* @param HTTPRequest $request
|
|
||||||
* @return HTTPResponse JSON string with a
|
|
||||||
* @throws HTTPResponse_Exception
|
* @throws HTTPResponse_Exception
|
||||||
*/
|
*/
|
||||||
public function savetreenode($request)
|
public function savetreenode(HTTPRequest $request): HTTPResponse
|
||||||
{
|
{
|
||||||
if (!SecurityToken::inst()->checkRequest($request)) {
|
if (!SecurityToken::inst()->checkRequest($request)) {
|
||||||
return $this->httpError(400);
|
$this->httpError(400);
|
||||||
}
|
}
|
||||||
if (!$this->CanOrganiseSitetree()) {
|
if (!$this->CanOrganiseSitetree()) {
|
||||||
return $this->httpError(
|
$this->httpError(
|
||||||
403,
|
403,
|
||||||
_t(
|
_t(
|
||||||
__CLASS__.'.CANT_REORGANISE',
|
__CLASS__.'.CANT_REORGANISE',
|
||||||
@ -786,14 +766,14 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
$id = $request->requestVar('ID');
|
$id = $request->requestVar('ID');
|
||||||
$parentID = $request->requestVar('ParentID');
|
$parentID = $request->requestVar('ParentID');
|
||||||
if (!is_numeric($id) || !is_numeric($parentID)) {
|
if (!is_numeric($id) || !is_numeric($parentID)) {
|
||||||
return $this->httpError(400);
|
$this->httpError(400);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check record exists in the DB
|
// Check record exists in the DB
|
||||||
/** @var SiteTree $node */
|
/** @var SiteTree $node */
|
||||||
$node = DataObject::get_by_id($className, $id);
|
$node = DataObject::get_by_id($className, $id);
|
||||||
if (!$node) {
|
if (!$node) {
|
||||||
return $this->httpError(
|
$this->httpError(
|
||||||
500,
|
500,
|
||||||
_t(
|
_t(
|
||||||
__CLASS__.'.PLEASESAVE',
|
__CLASS__.'.PLEASESAVE',
|
||||||
@ -805,7 +785,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
// Check top level permissions
|
// Check top level permissions
|
||||||
$root = $node->getParentType();
|
$root = $node->getParentType();
|
||||||
if (($parentID == '0' || $root == 'root') && !SiteConfig::current_site_config()->canCreateTopLevel()) {
|
if (($parentID == '0' || $root == 'root') && !SiteConfig::current_site_config()->canCreateTopLevel()) {
|
||||||
return $this->httpError(
|
$this->httpError(
|
||||||
403,
|
403,
|
||||||
_t(
|
_t(
|
||||||
__CLASS__.'.CANT_REORGANISE',
|
__CLASS__.'.CANT_REORGANISE',
|
||||||
@ -1000,7 +980,6 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Create the form
|
// Create the form
|
||||||
/** @skipUpgrade */
|
|
||||||
$form = Form::create(
|
$form = Form::create(
|
||||||
$this,
|
$this,
|
||||||
'SearchForm',
|
'SearchForm',
|
||||||
@ -1037,7 +1016,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
return $pageTypes;
|
return $pageTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function doSearch($data, $form)
|
public function doSearch(array $data, Form $form): HTTPResponse
|
||||||
{
|
{
|
||||||
return $this->getsubtree($this->getRequest());
|
return $this->getsubtree($this->getRequest());
|
||||||
}
|
}
|
||||||
@ -1059,10 +1038,6 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
->Link;
|
->Link;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param bool $unlinked
|
|
||||||
* @return ArrayList
|
|
||||||
*/
|
|
||||||
public function Breadcrumbs($unlinked = false)
|
public function Breadcrumbs($unlinked = false)
|
||||||
{
|
{
|
||||||
$items = new ArrayList();
|
$items = new ArrayList();
|
||||||
@ -1314,7 +1289,6 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
if (!$id) {
|
if (!$id) {
|
||||||
$id = $this->currentPageID();
|
$id = $this->currentPageID();
|
||||||
}
|
}
|
||||||
/** @var SiteTree $record */
|
|
||||||
$record = $this->getRecord($id);
|
$record = $this->getRecord($id);
|
||||||
|
|
||||||
// Check parent form can be generated
|
// Check parent form can be generated
|
||||||
@ -1352,8 +1326,9 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Added in-line to the form, but plucked into different view by LeftAndMain.Preview.js upon load
|
// Added in-line to the form, but plucked into different view by LeftAndMain.Preview.js upon load
|
||||||
/** @skipUpgrade */
|
if (($record instanceof CMSPreviewable || $record->has_extension(CMSPreviewable::class))
|
||||||
if ($record instanceof CMSPreviewable && !$fields->fieldByName('SilverStripeNavigator')) {
|
&& !$fields->fieldByName('SilverStripeNavigator')
|
||||||
|
) {
|
||||||
$navField = new LiteralField('SilverStripeNavigator', $this->getSilverStripeNavigator());
|
$navField = new LiteralField('SilverStripeNavigator', $this->getSilverStripeNavigator());
|
||||||
$navField->setAllowHTML(true);
|
$navField->setAllowHTML(true);
|
||||||
$fields->push($navField);
|
$fields->push($navField);
|
||||||
@ -1389,7 +1364,6 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Can't merge $FormAttributes in template at the moment
|
|
||||||
$form->addExtraClass('center ' . $this->BaseCSSClasses());
|
$form->addExtraClass('center ' . $this->BaseCSSClasses());
|
||||||
// Set validation exemptions for specific actions
|
// Set validation exemptions for specific actions
|
||||||
$form->setValidationExemptActions([
|
$form->setValidationExemptActions([
|
||||||
@ -1404,7 +1378,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
// Announce the capability so the frontend can decide whether to allow preview or not.
|
// Announce the capability so the frontend can decide whether to allow preview or not.
|
||||||
if ($record instanceof CMSPreviewable) {
|
if ($record instanceof CMSPreviewable || $record->has_extension(CMSPreviewable::class)) {
|
||||||
$form->addExtraClass('cms-previewable');
|
$form->addExtraClass('cms-previewable');
|
||||||
}
|
}
|
||||||
$form->addExtraClass('fill-height flexbox-area-grow');
|
$form->addExtraClass('fill-height flexbox-area-grow');
|
||||||
@ -1560,17 +1534,14 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
/**
|
/**
|
||||||
* Callback to request the list of page types allowed under a given page instance.
|
* Callback to request the list of page types allowed under a given page instance.
|
||||||
* Provides a slower but more precise response over SiteTreeHints
|
* Provides a slower but more precise response over SiteTreeHints
|
||||||
*
|
|
||||||
* @param HTTPRequest $request
|
|
||||||
* @return HTTPResponse
|
|
||||||
*/
|
*/
|
||||||
public function childfilter($request)
|
public function childfilter(HTTPRequest $request): HTTPResponse
|
||||||
{
|
{
|
||||||
// Check valid parent specified
|
// Check valid parent specified
|
||||||
$parentID = $request->requestVar('ParentID');
|
$parentID = $request->requestVar('ParentID');
|
||||||
$parent = SiteTree::get()->byID($parentID);
|
$parent = SiteTree::get()->byID($parentID);
|
||||||
if (!$parent || !$parent->exists()) {
|
if (!$parent || !$parent->exists()) {
|
||||||
return $this->httpError(404);
|
$this->httpError(404);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build hints specific to this class
|
// Build hints specific to this class
|
||||||
@ -1663,7 +1634,6 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
}
|
}
|
||||||
$gridField = GridField::create('Page', 'Pages', $list, $gridFieldConfig);
|
$gridField = GridField::create('Page', 'Pages', $list, $gridFieldConfig);
|
||||||
$gridField->setAttribute('cms-loading-ignore-url-params', true);
|
$gridField->setAttribute('cms-loading-ignore-url-params', true);
|
||||||
/** @var GridFieldDataColumns $columns */
|
|
||||||
$columns = $gridField->getConfig()->getComponentByType(GridFieldDataColumns::class);
|
$columns = $gridField->getConfig()->getComponentByType(GridFieldDataColumns::class);
|
||||||
|
|
||||||
// Don't allow navigating into children nodes on filtered lists
|
// Don't allow navigating into children nodes on filtered lists
|
||||||
@ -1672,7 +1642,6 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
'i18n_singular_name' => _t('SilverStripe\\CMS\\Model\\SiteTree.PAGETYPE', 'Page Type'),
|
'i18n_singular_name' => _t('SilverStripe\\CMS\\Model\\SiteTree.PAGETYPE', 'Page Type'),
|
||||||
'LastEdited' => _t('SilverStripe\\CMS\\Model\\SiteTree.LASTUPDATED', 'Last Updated'),
|
'LastEdited' => _t('SilverStripe\\CMS\\Model\\SiteTree.LASTUPDATED', 'Last Updated'),
|
||||||
];
|
];
|
||||||
/** @var GridFieldSortableHeader $sortableHeader */
|
|
||||||
$sortableHeader = $gridField->getConfig()->getComponentByType(GridFieldSortableHeader::class);
|
$sortableHeader = $gridField->getConfig()->getComponentByType(GridFieldSortableHeader::class);
|
||||||
$sortableHeader->setFieldSorting(['getTreeTitle' => 'Title']);
|
$sortableHeader->setFieldSorting(['getTreeTitle' => 'Title']);
|
||||||
$gridField->getState()->ParentID = $parentID;
|
$gridField->getState()->ParentID = $parentID;
|
||||||
@ -1757,12 +1726,9 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
/**
|
/**
|
||||||
* Save and Publish page handler
|
* Save and Publish page handler
|
||||||
*
|
*
|
||||||
* @param array $data
|
|
||||||
* @param Form $form
|
|
||||||
* @return HTTPResponse
|
|
||||||
* @throws HTTPResponse_Exception
|
* @throws HTTPResponse_Exception
|
||||||
*/
|
*/
|
||||||
public function save($data, $form)
|
public function save(array $data, Form $form): HTTPResponse
|
||||||
{
|
{
|
||||||
$className = $this->config()->get('tree_class');
|
$className = $this->config()->get('tree_class');
|
||||||
|
|
||||||
@ -1791,7 +1757,6 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
return Security::permissionFailure($this);
|
return Security::permissionFailure($this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Coupling to SiteTree
|
|
||||||
$record->HasBrokenLink = 0;
|
$record->HasBrokenLink = 0;
|
||||||
$record->HasBrokenFile = 0;
|
$record->HasBrokenFile = 0;
|
||||||
|
|
||||||
@ -1905,12 +1870,9 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
*
|
*
|
||||||
* @uses SiteTree->doRevertToLive()
|
* @uses SiteTree->doRevertToLive()
|
||||||
*
|
*
|
||||||
* @param array $data
|
|
||||||
* @param Form $form
|
|
||||||
* @return HTTPResponse
|
|
||||||
* @throws HTTPResponse_Exception
|
* @throws HTTPResponse_Exception
|
||||||
*/
|
*/
|
||||||
public function revert($data, $form)
|
public function revert(array $data, Form $form): HTTPResponse
|
||||||
{
|
{
|
||||||
if (!isset($data['ID'])) {
|
if (!isset($data['ID'])) {
|
||||||
throw new HTTPResponse_Exception("Please pass an ID in the form content", 400);
|
throw new HTTPResponse_Exception("Please pass an ID in the form content", 400);
|
||||||
@ -1922,7 +1884,6 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
throw new HTTPResponse_Exception("SiteTree #$id not found", 400);
|
throw new HTTPResponse_Exception("SiteTree #$id not found", 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var SiteTree $record */
|
|
||||||
$table = DataObject::singleton(SiteTree::class)->baseTable();
|
$table = DataObject::singleton(SiteTree::class)->baseTable();
|
||||||
$liveTable = DataObject::singleton(SiteTree::class)->stageTable($table, Versioned::LIVE);
|
$liveTable = DataObject::singleton(SiteTree::class)->stageTable($table, Versioned::LIVE);
|
||||||
$record = Versioned::get_one_by_stage(SiteTree::class, Versioned::LIVE, [
|
$record = Versioned::get_one_by_stage(SiteTree::class, Versioned::LIVE, [
|
||||||
@ -1958,12 +1919,9 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
*
|
*
|
||||||
* @see deletefromlive()
|
* @see deletefromlive()
|
||||||
*
|
*
|
||||||
* @param array $data
|
|
||||||
* @param Form $form
|
|
||||||
* @return HTTPResponse
|
|
||||||
* @throws HTTPResponse_Exception
|
* @throws HTTPResponse_Exception
|
||||||
*/
|
*/
|
||||||
public function delete($data, $form)
|
public function delete(array $data, Form $form): HTTPResponse
|
||||||
{
|
{
|
||||||
$id = $data['ID'];
|
$id = $data['ID'];
|
||||||
$record = SiteTree::get()->byID($id);
|
$record = SiteTree::get()->byID($id);
|
||||||
@ -1993,15 +1951,11 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
/**
|
/**
|
||||||
* Delete this page from both live and stage
|
* Delete this page from both live and stage
|
||||||
*
|
*
|
||||||
* @param array $data
|
|
||||||
* @param Form $form
|
|
||||||
* @return HTTPResponse
|
|
||||||
* @throws HTTPResponse_Exception
|
* @throws HTTPResponse_Exception
|
||||||
*/
|
*/
|
||||||
public function archive($data, $form)
|
public function archive(array $data, Form $form): HTTPResponse
|
||||||
{
|
{
|
||||||
$id = $data['ID'];
|
$id = $data['ID'];
|
||||||
/** @var SiteTree $record */
|
|
||||||
$record = SiteTree::get()->byID($id);
|
$record = SiteTree::get()->byID($id);
|
||||||
if (!$record || !$record->exists()) {
|
if (!$record || !$record->exists()) {
|
||||||
throw new HTTPResponse_Exception("Bad record ID #$id", 404);
|
throw new HTTPResponse_Exception("Bad record ID #$id", 404);
|
||||||
@ -2026,14 +1980,14 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
return $this->getResponseNegotiator()->respond($this->getRequest());
|
return $this->getResponseNegotiator()->respond($this->getRequest());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function publish($data, $form)
|
public function publish(array $data, Form $form): HTTPResponse
|
||||||
{
|
{
|
||||||
$data['publish'] = '1';
|
$data['publish'] = '1';
|
||||||
|
|
||||||
return $this->save($data, $form);
|
return $this->save($data, $form);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function unpublish($data, $form)
|
public function unpublish(array $data, Form $form): HTTPResponse
|
||||||
{
|
{
|
||||||
$className = $this->config()->get('tree_class');
|
$className = $this->config()->get('tree_class');
|
||||||
/** @var SiteTree $record */
|
/** @var SiteTree $record */
|
||||||
@ -2168,94 +2122,16 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
return $this->batchactions()->batchActionList();
|
return $this->batchactions()->batchActionList();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated 4.12.0 Use custom logic instead
|
|
||||||
* @param $request
|
|
||||||
* @return HTTPResponse|string|void
|
|
||||||
*/
|
|
||||||
public function publishall($request)
|
|
||||||
{
|
|
||||||
Deprecation::notice('4.12.0', 'Use custom logic instead');
|
|
||||||
if (!Permission::check('ADMIN')) {
|
|
||||||
return Security::permissionFailure($this);
|
|
||||||
}
|
|
||||||
|
|
||||||
Environment::increaseTimeLimitTo();
|
|
||||||
Environment::increaseMemoryLimitTo();
|
|
||||||
|
|
||||||
$response = "";
|
|
||||||
|
|
||||||
if (isset($this->requestParams['confirm'])) {
|
|
||||||
// Protect against CSRF on destructive action
|
|
||||||
if (!SecurityToken::inst()->checkRequest($request)) {
|
|
||||||
return $this->httpError(400);
|
|
||||||
}
|
|
||||||
|
|
||||||
$start = 0;
|
|
||||||
$pages = SiteTree::get()->limit("$start,30");
|
|
||||||
$count = 0;
|
|
||||||
while ($pages) {
|
|
||||||
/** @var SiteTree $page */
|
|
||||||
foreach ($pages as $page) {
|
|
||||||
if ($page && !$page->canPublish()) {
|
|
||||||
return Security::permissionFailure($this);
|
|
||||||
}
|
|
||||||
|
|
||||||
$page->publishRecursive();
|
|
||||||
$page->destroy();
|
|
||||||
unset($page);
|
|
||||||
$count++;
|
|
||||||
$response .= "<li>$count</li>";
|
|
||||||
}
|
|
||||||
if ($pages->count() > 29) {
|
|
||||||
$start += 30;
|
|
||||||
$pages = SiteTree::get()->limit("$start,30");
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$response .= _t(__CLASS__ . '.PUBPAGES', "Done: Published {count} pages", ['count' => $count]);
|
|
||||||
} else {
|
|
||||||
$token = SecurityToken::inst();
|
|
||||||
$fields = new FieldList();
|
|
||||||
$token->updateFieldSet($fields);
|
|
||||||
$tokenField = $fields->first();
|
|
||||||
$tokenHtml = ($tokenField) ? $tokenField->FieldHolder() : '';
|
|
||||||
$publishAllDescription = _t(
|
|
||||||
__CLASS__ . '.PUBALLFUN2',
|
|
||||||
'Pressing this button will do the equivalent of going to every page and pressing "publish". '
|
|
||||||
. 'It\'s intended to be used after there have been massive edits of the content, such as when '
|
|
||||||
. 'the site was first built. '
|
|
||||||
. 'For large websites, this task might not be able to run through to completion. '
|
|
||||||
. 'In this case, we recommend talking to your developers to create a custom task'
|
|
||||||
);
|
|
||||||
$response .= '<h1>' . _t(__CLASS__ . '.PUBALLFUN', '"Publish All" functionality') . '</h1>
|
|
||||||
<p>' . $publishAllDescription . '</p>
|
|
||||||
<form method="post" action="publishall">
|
|
||||||
<input type="submit" name="confirm" value="'
|
|
||||||
. _t(__CLASS__ . '.PUBALLCONFIRM', "Please publish every page in the site, copying content stage to live", 'Confirmation button') .'" />'
|
|
||||||
. $tokenHtml .
|
|
||||||
'</form>';
|
|
||||||
}
|
|
||||||
|
|
||||||
return $response;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Restore a completely deleted page from the SiteTree_versions table.
|
* Restore a completely deleted page from the SiteTree_versions table.
|
||||||
*
|
|
||||||
* @param array $data
|
|
||||||
* @param Form $form
|
|
||||||
* @return HTTPResponse
|
|
||||||
*/
|
*/
|
||||||
public function restore($data, $form)
|
public function restore(array $data, Form $form): HTTPResponse
|
||||||
{
|
{
|
||||||
if (!isset($data['ID']) || !is_numeric($data['ID'])) {
|
if (!isset($data['ID']) || !is_numeric($data['ID'])) {
|
||||||
return new HTTPResponse("Please pass an ID in the form content", 400);
|
return new HTTPResponse("Please pass an ID in the form content", 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
$id = (int)$data['ID'];
|
$id = (int)$data['ID'];
|
||||||
/** @var SiteTree $restoredPage */
|
|
||||||
$restoredPage = Versioned::get_latest_version(SiteTree::class, $id);
|
$restoredPage = Versioned::get_latest_version(SiteTree::class, $id);
|
||||||
if (!$restoredPage) {
|
if (!$restoredPage) {
|
||||||
return new HTTPResponse("SiteTree #$id not found", 400);
|
return new HTTPResponse("SiteTree #$id not found", 400);
|
||||||
@ -2275,7 +2151,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
return $this->getResponseNegotiator()->respond($this->getRequest());
|
return $this->getResponseNegotiator()->respond($this->getRequest());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function duplicate($request)
|
public function duplicate(HTTPRequest $request): HTTPResponse
|
||||||
{
|
{
|
||||||
// Protect against CSRF on destructive action
|
// Protect against CSRF on destructive action
|
||||||
if (!SecurityToken::inst()->checkRequest($request)) {
|
if (!SecurityToken::inst()->checkRequest($request)) {
|
||||||
@ -2283,7 +2159,6 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (($id = $this->urlParams['ID']) && is_numeric($id)) {
|
if (($id = $this->urlParams['ID']) && is_numeric($id)) {
|
||||||
/** @var SiteTree $page */
|
|
||||||
$page = SiteTree::get()->byID($id);
|
$page = SiteTree::get()->byID($id);
|
||||||
if ($page && !$page->canCreate(null, ['Parent' => $page->Parent()])) {
|
if ($page && !$page->canCreate(null, ['Parent' => $page->Parent()])) {
|
||||||
return Security::permissionFailure($this);
|
return Security::permissionFailure($this);
|
||||||
@ -2292,7 +2167,6 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
throw new HTTPResponse_Exception("Bad record ID #$id", 404);
|
throw new HTTPResponse_Exception("Bad record ID #$id", 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var SiteTree $newPage */
|
|
||||||
$newPage = $page->duplicate();
|
$newPage = $page->duplicate();
|
||||||
|
|
||||||
// ParentID can be hard-set in the URL. This is useful for pages with multiple parents
|
// ParentID can be hard-set in the URL. This is useful for pages with multiple parents
|
||||||
@ -2319,15 +2193,14 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
return new HTTPResponse("CMSMain::duplicate() Bad ID: '$id'", 400);
|
return new HTTPResponse("CMSMain::duplicate() Bad ID: '$id'", 400);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function duplicatewithchildren($request)
|
public function duplicatewithchildren(HTTPRequest $request): HTTPResponse
|
||||||
{
|
{
|
||||||
// Protect against CSRF on destructive action
|
// Protect against CSRF on destructive action
|
||||||
if (!SecurityToken::inst()->checkRequest($request)) {
|
if (!SecurityToken::inst()->checkRequest($request)) {
|
||||||
return $this->httpError(400);
|
$this->httpError(400);
|
||||||
}
|
}
|
||||||
Environment::increaseTimeLimitTo();
|
Environment::increaseTimeLimitTo();
|
||||||
if (($id = $this->urlParams['ID']) && is_numeric($id)) {
|
if (($id = $this->urlParams['ID']) && is_numeric($id)) {
|
||||||
/** @var SiteTree $page */
|
|
||||||
$page = SiteTree::get()->byID($id);
|
$page = SiteTree::get()->byID($id);
|
||||||
if ($page && !$page->canCreate(null, ['Parent' => $page->Parent()])) {
|
if ($page && !$page->canCreate(null, ['Parent' => $page->Parent()])) {
|
||||||
return Security::permissionFailure($this);
|
return Security::permissionFailure($this);
|
||||||
@ -2336,7 +2209,6 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
|
|||||||
throw new HTTPResponse_Exception("Bad record ID #$id", 404);
|
throw new HTTPResponse_Exception("Bad record ID #$id", 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var SiteTree $newPage */
|
|
||||||
$newPage = $page->duplicateWithChildren();
|
$newPage = $page->duplicateWithChildren();
|
||||||
|
|
||||||
$this->getResponse()->addHeader(
|
$this->getResponse()->addHeader(
|
||||||
|
@ -132,10 +132,6 @@ class CMSPageAddController extends CMSPageEditController
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
// TODO Re-enable search once it allows for HTML title display,
|
|
||||||
// see http://open.silverstripe.org/ticket/7455
|
|
||||||
// $parentField->setShowSearch(true);
|
|
||||||
|
|
||||||
$parentModeField->addExtraClass('parent-mode');
|
$parentModeField->addExtraClass('parent-mode');
|
||||||
|
|
||||||
// CMSMain->currentPageID() automatically sets the homepage,
|
// CMSMain->currentPageID() automatically sets the homepage,
|
||||||
@ -192,12 +188,7 @@ class CMSPageAddController extends CMSPageEditController
|
|||||||
return $form;
|
return $form;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function doAdd(array $data, Form $form): HTTPResponse
|
||||||
* @param array $data
|
|
||||||
* @param Form $form
|
|
||||||
* @return HTTPResponse
|
|
||||||
*/
|
|
||||||
public function doAdd($data, $form)
|
|
||||||
{
|
{
|
||||||
$className = isset($data['PageType']) ? $data['PageType'] : "Page";
|
$className = isset($data['PageType']) ? $data['PageType'] : "Page";
|
||||||
$parentID = isset($data['ParentID']) ? (int)$data['ParentID'] : 0;
|
$parentID = isset($data['ParentID']) ? (int)$data['ParentID'] : 0;
|
||||||
@ -241,7 +232,7 @@ class CMSPageAddController extends CMSPageEditController
|
|||||||
return $this->redirect(Controller::join_links($editController->Link('show'), $record->ID));
|
return $this->redirect(Controller::join_links($editController->Link('show'), $record->ID));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function doCancel($data, $form)
|
public function doCancel(array $data, Form $form): HTTPResponse
|
||||||
{
|
{
|
||||||
return $this->redirect(CMSMain::singleton()->Link());
|
return $this->redirect(CMSMain::singleton()->Link());
|
||||||
}
|
}
|
||||||
|
@ -53,30 +53,27 @@ class CMSPageEditController extends CMSMain
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Action handler for adding pages to a campaign
|
* Action handler for adding pages to a campaign
|
||||||
*
|
|
||||||
* @param array $data
|
|
||||||
* @param Form $form
|
|
||||||
* @return DBHTMLText|HTTPResponse
|
|
||||||
*/
|
*/
|
||||||
public function addtocampaign($data, $form)
|
public function addtocampaign(array $data, Form $form): HTTPResponse
|
||||||
{
|
{
|
||||||
$id = $data['ID'];
|
$id = $data['ID'];
|
||||||
$record = \Page::get()->byID($id);
|
$record = \Page::get()->byID($id);
|
||||||
|
|
||||||
$handler = AddToCampaignHandler::create($this, $record);
|
$handler = AddToCampaignHandler::create($this, $record);
|
||||||
$results = $handler->addToCampaign($record, $data);
|
$response = $handler->addToCampaign($record, $data);
|
||||||
if (is_null($results)) {
|
$message = $response->getBody();
|
||||||
return null;
|
if (empty($message)) {
|
||||||
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->getSchemaRequested()) {
|
if ($this->getSchemaRequested()) {
|
||||||
// Send extra "message" data with schema response
|
// Send extra "message" data with schema response
|
||||||
$extraData = ['message' => $results];
|
$extraData = ['message' => $message];
|
||||||
$schemaId = Controller::join_links($this->Link('schema/AddToCampaignForm'), $id);
|
$schemaId = Controller::join_links($this->Link('schema/AddToCampaignForm'), $id);
|
||||||
return $this->getSchemaResponse($schemaId, $form, null, $extraData);
|
return $this->getSchemaResponse($schemaId, $form, null, $extraData);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $results;
|
return $response;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,480 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace SilverStripe\CMS\Controllers;
|
|
||||||
|
|
||||||
use SilverStripe\Dev\Deprecation;
|
|
||||||
use SilverStripe\Admin\LeftAndMainFormRequestHandler;
|
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
|
||||||
use SilverStripe\Control\Controller;
|
|
||||||
use SilverStripe\Control\HTTPRequest;
|
|
||||||
use SilverStripe\Control\HTTPResponse;
|
|
||||||
use SilverStripe\Forms\CheckboxField;
|
|
||||||
use SilverStripe\Forms\FieldList;
|
|
||||||
use SilverStripe\Forms\Form;
|
|
||||||
use SilverStripe\Forms\FormAction;
|
|
||||||
use SilverStripe\Forms\HiddenField;
|
|
||||||
use SilverStripe\Forms\HTMLReadonlyField;
|
|
||||||
use SilverStripe\Forms\LiteralField;
|
|
||||||
use SilverStripe\Forms\Tab;
|
|
||||||
use SilverStripe\ORM\FieldType\DBField;
|
|
||||||
use SilverStripe\Versioned\Versioned;
|
|
||||||
use SilverStripe\Security\Security;
|
|
||||||
use SilverStripe\View\ArrayData;
|
|
||||||
use SilverStripe\View\ViewableData;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Legacy CMS History controller. This functionality has been moved to the `silverstripe/versioned-admin` module and
|
|
||||||
* this class will be removed completly in SilverStripe 5.0.0.
|
|
||||||
* @deprecated 4.3.0 Use silverstripe/versioned-admin instead
|
|
||||||
*/
|
|
||||||
class CMSPageHistoryController extends CMSMain
|
|
||||||
{
|
|
||||||
|
|
||||||
private static $url_segment = 'pages/history';
|
|
||||||
|
|
||||||
private static $url_rule = '/$Action/$ID/$VersionID/$OtherVersionID';
|
|
||||||
|
|
||||||
private static $url_priority = 42;
|
|
||||||
|
|
||||||
private static $menu_title = 'History';
|
|
||||||
|
|
||||||
private static $required_permission_codes = 'CMS_ACCESS_CMSMain';
|
|
||||||
|
|
||||||
private static $allowed_actions = [
|
|
||||||
'EditForm',
|
|
||||||
'VersionsForm',
|
|
||||||
'CompareVersionsForm',
|
|
||||||
'show',
|
|
||||||
'compare'
|
|
||||||
];
|
|
||||||
|
|
||||||
private static $url_handlers = [
|
|
||||||
'$Action/$ID/$VersionID/$OtherVersionID' => 'handleAction',
|
|
||||||
'EditForm/$ID/$VersionID' => 'EditForm',
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Current version ID for this request. Can be 0 for latest version
|
|
||||||
*
|
|
||||||
* @var int
|
|
||||||
*/
|
|
||||||
protected $versionID = null;
|
|
||||||
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
parent::__construct();
|
|
||||||
Deprecation::withNoReplacement(function () {
|
|
||||||
Deprecation::notice('4.3.0', 'Use silverstripe/versioned-admin instead', Deprecation::SCOPE_CLASS);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getResponseNegotiator()
|
|
||||||
{
|
|
||||||
$negotiator = parent::getResponseNegotiator();
|
|
||||||
|
|
||||||
$negotiator->setCallback('CurrentForm', function () {
|
|
||||||
$form = $this->getEditForm();
|
|
||||||
if ($form) {
|
|
||||||
return $form->forTemplate();
|
|
||||||
}
|
|
||||||
return $this->renderWith($this->getTemplatesWithSuffix('_Content'));
|
|
||||||
});
|
|
||||||
|
|
||||||
$negotiator->setCallback('default', function () {
|
|
||||||
return $this->renderWith($this->getViewer('show'));
|
|
||||||
});
|
|
||||||
|
|
||||||
return $negotiator;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param HTTPRequest $request
|
|
||||||
* @return HTTPResponse
|
|
||||||
*/
|
|
||||||
public function show($request)
|
|
||||||
{
|
|
||||||
// Record id and version for this request
|
|
||||||
$id = $request->param('ID');
|
|
||||||
$this->setCurrentPageID($id);
|
|
||||||
$versionID = $request->param('VersionID');
|
|
||||||
$this->setVersionID($versionID);
|
|
||||||
|
|
||||||
// Show id
|
|
||||||
$form = $this->getEditForm();
|
|
||||||
|
|
||||||
$negotiator = $this->getResponseNegotiator();
|
|
||||||
$negotiator->setCallback('CurrentForm', function () use ($form) {
|
|
||||||
return $form
|
|
||||||
? $form->forTemplate()
|
|
||||||
: $this->renderWith($this->getTemplatesWithSuffix('_Content'));
|
|
||||||
});
|
|
||||||
$negotiator->setCallback('default', function () use ($form) {
|
|
||||||
return $this
|
|
||||||
->customise(['EditForm' => $form])
|
|
||||||
->renderWith($this->getViewer('show'));
|
|
||||||
});
|
|
||||||
|
|
||||||
return $negotiator->respond($request);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param HTTPRequest $request
|
|
||||||
* @return HTTPResponse
|
|
||||||
*/
|
|
||||||
public function compare($request)
|
|
||||||
{
|
|
||||||
$form = $this->CompareVersionsForm(
|
|
||||||
$request->param('VersionID'),
|
|
||||||
$request->param('OtherVersionID')
|
|
||||||
);
|
|
||||||
|
|
||||||
$negotiator = $this->getResponseNegotiator();
|
|
||||||
$negotiator->setCallback('CurrentForm', function () use ($form) {
|
|
||||||
return $form ? $form->forTemplate() : $this->renderWith($this->getTemplatesWithSuffix('_Content'));
|
|
||||||
});
|
|
||||||
$negotiator->setCallback('default', function () use ($form) {
|
|
||||||
return $this->customise(['EditForm' => $form])->renderWith($this->getViewer('show'));
|
|
||||||
});
|
|
||||||
|
|
||||||
return $negotiator->respond($request);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getSilverStripeNavigator()
|
|
||||||
{
|
|
||||||
$record = $this->getRecord($this->currentPageID(), $this->getRequest()->param('VersionID'));
|
|
||||||
if ($record) {
|
|
||||||
$navigator = new SilverStripeNavigator($record);
|
|
||||||
return $navigator->renderWith($this->getTemplatesWithSuffix('_SilverStripeNavigator'));
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param HTTPRequest $request
|
|
||||||
* @return Form
|
|
||||||
*/
|
|
||||||
public function EditForm($request = null)
|
|
||||||
{
|
|
||||||
if ($request) {
|
|
||||||
// Validate VersionID is present
|
|
||||||
$versionID = $request->param('VersionID');
|
|
||||||
if (!isset($versionID)) {
|
|
||||||
$this->httpError(400);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
$this->setVersionID($versionID);
|
|
||||||
}
|
|
||||||
return parent::EditForm($request);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the read only version of the edit form. Detaches all {@link FormAction}
|
|
||||||
* instances attached since only action relates to revert.
|
|
||||||
*
|
|
||||||
* Permission checking is done at the {@link CMSMain::getEditForm()} level.
|
|
||||||
*
|
|
||||||
* @param int $id ID of the record to show
|
|
||||||
* @param array $fields optional
|
|
||||||
* @param int $versionID
|
|
||||||
* @param int $compareID Compare mode
|
|
||||||
*
|
|
||||||
* @return Form
|
|
||||||
*/
|
|
||||||
public function getEditForm($id = null, $fields = null, $versionID = null, $compareID = null)
|
|
||||||
{
|
|
||||||
if (!$id) {
|
|
||||||
$id = $this->currentPageID();
|
|
||||||
}
|
|
||||||
if (!$versionID) {
|
|
||||||
$versionID = $this->getVersionID();
|
|
||||||
}
|
|
||||||
|
|
||||||
$record = $this->getRecord($id, $versionID);
|
|
||||||
if (!$record) {
|
|
||||||
return $this->EmptyForm();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Refresh version ID
|
|
||||||
$versionID = $record->Version;
|
|
||||||
$this->setVersionID($versionID);
|
|
||||||
|
|
||||||
// Get edit form
|
|
||||||
$form = parent::getEditForm($record, $record->getCMSFields());
|
|
||||||
// Respect permission failures from parent implementation
|
|
||||||
if (!($form instanceof Form)) {
|
|
||||||
return $form;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: move to the SilverStripeNavigator structure so the new preview can pick it up.
|
|
||||||
//$nav = new SilverStripeNavigatorItem_ArchiveLink($record);
|
|
||||||
|
|
||||||
$actions = new FieldList(
|
|
||||||
$revert = FormAction::create(
|
|
||||||
'doRollback',
|
|
||||||
_t('SilverStripe\\CMS\\Controllers\\CMSPageHistoryController.REVERTTOTHISVERSION', 'Revert to this version')
|
|
||||||
)
|
|
||||||
->setUseButtonTag(true)
|
|
||||||
->addExtraClass('btn-warning font-icon-back-in-time')
|
|
||||||
);
|
|
||||||
$actions->setForm($form);
|
|
||||||
$form->setActions($actions);
|
|
||||||
|
|
||||||
$fields = $form->Fields();
|
|
||||||
$fields->removeByName("Status");
|
|
||||||
$fields->push(new HiddenField("ID"));
|
|
||||||
$fields->push(new HiddenField("Version"));
|
|
||||||
|
|
||||||
$fields = $fields->makeReadonly();
|
|
||||||
|
|
||||||
if ($compareID) {
|
|
||||||
$link = Controller::join_links(
|
|
||||||
$this->Link('show'),
|
|
||||||
$id
|
|
||||||
);
|
|
||||||
|
|
||||||
$view = _t(__CLASS__ . '.VIEW', "view");
|
|
||||||
|
|
||||||
$message = _t(
|
|
||||||
__CLASS__ . '.COMPARINGVERSION',
|
|
||||||
"Comparing versions {version1} and {version2}.",
|
|
||||||
[
|
|
||||||
'version1' => sprintf('%s (<a href="%s">%s</a>)', $versionID, Controller::join_links($link, $versionID), $view),
|
|
||||||
'version2' => sprintf('%s (<a href="%s">%s</a>)', $compareID, Controller::join_links($link, $compareID), $view)
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
$revert->setReadonly(true);
|
|
||||||
} else {
|
|
||||||
if ($record->isLatestVersion()) {
|
|
||||||
$message = _t(__CLASS__ . '.VIEWINGLATEST', 'Currently viewing the latest version.');
|
|
||||||
} else {
|
|
||||||
$message = _t(
|
|
||||||
__CLASS__ . '.VIEWINGVERSION',
|
|
||||||
"Currently viewing version {version}.",
|
|
||||||
['version' => $versionID]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @var Tab $mainTab */
|
|
||||||
$mainTab = $fields->fieldByName('Root.Main');
|
|
||||||
$mainTab->unshift(
|
|
||||||
LiteralField::create('CurrentlyViewingMessage', ArrayData::create([
|
|
||||||
'Content' => DBField::create_field('HTMLFragment', $message),
|
|
||||||
'Classes' => 'alert alert-info'
|
|
||||||
])->renderWith($this->getTemplatesWithSuffix('_notice')))
|
|
||||||
);
|
|
||||||
|
|
||||||
$form->setFields($fields->makeReadonly());
|
|
||||||
$form->loadDataFrom([
|
|
||||||
"ID" => $id,
|
|
||||||
"Version" => $versionID,
|
|
||||||
]);
|
|
||||||
|
|
||||||
if ($record->isLatestVersion()) {
|
|
||||||
$revert->setReadonly(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
$form->removeExtraClass('cms-content');
|
|
||||||
|
|
||||||
// History form has both ID and VersionID as suffixes
|
|
||||||
$form->setRequestHandler(
|
|
||||||
LeftAndMainFormRequestHandler::create($form, [$id, $versionID])
|
|
||||||
);
|
|
||||||
|
|
||||||
return $form;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Version select form. Main interface between selecting versions to view
|
|
||||||
* and comparing multiple versions.
|
|
||||||
*
|
|
||||||
* Because we can reload the page directly to a compare view (history/compare/1/2/3)
|
|
||||||
* this form has to adapt to those parameters as well.
|
|
||||||
*
|
|
||||||
* @return Form
|
|
||||||
*/
|
|
||||||
public function VersionsForm()
|
|
||||||
{
|
|
||||||
$id = $this->currentPageID();
|
|
||||||
$page = $this->getRecord($id);
|
|
||||||
$versionsHtml = '';
|
|
||||||
|
|
||||||
$action = $this->getRequest()->param('Action');
|
|
||||||
$versionID = $this->getRequest()->param('VersionID');
|
|
||||||
$otherVersionID = $this->getRequest()->param('OtherVersionID');
|
|
||||||
|
|
||||||
$showUnpublishedChecked = 0;
|
|
||||||
$compareModeChecked = ($action == "compare");
|
|
||||||
|
|
||||||
if ($page) {
|
|
||||||
$versions = $page->Versions();
|
|
||||||
$versionID = (!$versionID) ? $page->Version : $versionID;
|
|
||||||
|
|
||||||
if ($versions) {
|
|
||||||
foreach ($versions as $k => $version) {
|
|
||||||
$active = false;
|
|
||||||
|
|
||||||
if ($version->Version == $versionID || $version->Version == $otherVersionID) {
|
|
||||||
$active = true;
|
|
||||||
|
|
||||||
if (!$version->WasPublished) {
|
|
||||||
$showUnpublishedChecked = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$version->Active = ($active);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$vd = new ViewableData();
|
|
||||||
|
|
||||||
$versionsHtml = $vd->customise([
|
|
||||||
'Versions' => $versions
|
|
||||||
])->renderWith($this->getTemplatesWithSuffix('_versions'));
|
|
||||||
}
|
|
||||||
|
|
||||||
$fields = new FieldList(
|
|
||||||
new CheckboxField(
|
|
||||||
'ShowUnpublished',
|
|
||||||
_t('SilverStripe\\CMS\\Controllers\\CMSPageHistoryController.SHOWUNPUBLISHED', 'Show unpublished versions'),
|
|
||||||
$showUnpublishedChecked
|
|
||||||
),
|
|
||||||
new CheckboxField(
|
|
||||||
'CompareMode',
|
|
||||||
_t('SilverStripe\\CMS\\Controllers\\CMSPageHistoryController.COMPAREMODE', 'Compare mode (select two)'),
|
|
||||||
$compareModeChecked
|
|
||||||
),
|
|
||||||
new LiteralField('VersionsHtml', $versionsHtml),
|
|
||||||
$hiddenID = new HiddenField('ID', false, "")
|
|
||||||
);
|
|
||||||
|
|
||||||
$form = Form::create(
|
|
||||||
$this,
|
|
||||||
'VersionsForm',
|
|
||||||
$fields,
|
|
||||||
new FieldList()
|
|
||||||
)->setHTMLID('Form_VersionsForm');
|
|
||||||
$form->loadDataFrom($this->getRequest()->requestVars());
|
|
||||||
$hiddenID->setValue($id);
|
|
||||||
$form->unsetValidator();
|
|
||||||
|
|
||||||
$form
|
|
||||||
->addExtraClass('cms-versions-form') // placeholder, necessary for $.metadata() to work
|
|
||||||
->setAttribute('data-link-tmpl-compare', Controller::join_links($this->Link('compare'), '%s', '%s', '%s'))
|
|
||||||
->setAttribute('data-link-tmpl-show', Controller::join_links($this->Link('show'), '%s', '%s'));
|
|
||||||
|
|
||||||
return $form;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param int $versionID
|
|
||||||
* @param int $otherVersionID
|
|
||||||
* @return mixed
|
|
||||||
*/
|
|
||||||
public function CompareVersionsForm($versionID, $otherVersionID)
|
|
||||||
{
|
|
||||||
if ($versionID > $otherVersionID) {
|
|
||||||
$toVersion = $versionID;
|
|
||||||
$fromVersion = $otherVersionID;
|
|
||||||
} else {
|
|
||||||
$toVersion = $otherVersionID;
|
|
||||||
$fromVersion = $versionID;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$toVersion || !$fromVersion) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$id = $this->currentPageID();
|
|
||||||
/** @var SiteTree $page */
|
|
||||||
$page = SiteTree::get()->byID($id);
|
|
||||||
|
|
||||||
$record = null;
|
|
||||||
if ($page && $page->exists()) {
|
|
||||||
if (!$page->canView()) {
|
|
||||||
return Security::permissionFailure($this);
|
|
||||||
}
|
|
||||||
|
|
||||||
$record = $page->compareVersions($fromVersion, $toVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
$fromVersionRecord = Versioned::get_version(SiteTree::class, $id, $fromVersion);
|
|
||||||
$toVersionRecord = Versioned::get_version(SiteTree::class, $id, $toVersion);
|
|
||||||
|
|
||||||
if (!$fromVersionRecord) {
|
|
||||||
throw new \Exception("Can't find version $fromVersion of page $id");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$toVersionRecord) {
|
|
||||||
throw new \Exception("Can't find version $toVersion of page $id");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$record) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
$form = $this->getEditForm($id, null, $fromVersion, $toVersion);
|
|
||||||
$form->setActions(new FieldList());
|
|
||||||
$form->addExtraClass('compare');
|
|
||||||
|
|
||||||
$form->loadDataFrom($record);
|
|
||||||
$form->loadDataFrom([
|
|
||||||
"ID" => $id,
|
|
||||||
"Version" => $fromVersion,
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Comparison views shouldn't be editable.
|
|
||||||
// As the comparison output is HTML and not valid values for the various field types
|
|
||||||
$readonlyFields = $this->transformReadonly($form->Fields());
|
|
||||||
$form->setFields($readonlyFields);
|
|
||||||
|
|
||||||
return $form;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Replace all data fields with HTML readonly fields to display diff
|
|
||||||
*
|
|
||||||
* @param FieldList $fields
|
|
||||||
* @return FieldList
|
|
||||||
*/
|
|
||||||
public function transformReadonly(FieldList $fields)
|
|
||||||
{
|
|
||||||
foreach ($fields->dataFields() as $field) {
|
|
||||||
if ($field instanceof HiddenField) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$newField = $field->castedCopy(HTMLReadonlyField::class);
|
|
||||||
$fields->replaceField($field->getName(), $newField);
|
|
||||||
}
|
|
||||||
return $fields;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set current version ID
|
|
||||||
*
|
|
||||||
* @param int $versionID
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function setVersionID($versionID)
|
|
||||||
{
|
|
||||||
$this->versionID = $versionID;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get current version ID
|
|
||||||
*
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function getVersionID()
|
|
||||||
{
|
|
||||||
return $this->versionID;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getTabIdentifier()
|
|
||||||
{
|
|
||||||
return 'history';
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace SilverStripe\CMS\Controllers;
|
namespace SilverStripe\CMS\Controllers;
|
||||||
|
|
||||||
|
use SilverStripe\Admin\Navigator\SilverStripeNavigator;
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
use SilverStripe\Control\Controller;
|
use SilverStripe\Control\Controller;
|
||||||
use SilverStripe\Control\Director;
|
use SilverStripe\Control\Director;
|
||||||
@ -29,7 +30,6 @@ use SilverStripe\View\ArrayData;
|
|||||||
use SilverStripe\View\Parsers\URLSegmentFilter;
|
use SilverStripe\View\Parsers\URLSegmentFilter;
|
||||||
use SilverStripe\View\Requirements;
|
use SilverStripe\View\Requirements;
|
||||||
use SilverStripe\View\SSViewer;
|
use SilverStripe\View\SSViewer;
|
||||||
use Translatable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The most common kind of controller; effectively a controller linked to a {@link DataObject}.
|
* The most common kind of controller; effectively a controller linked to a {@link DataObject}.
|
||||||
@ -45,12 +45,12 @@ use Translatable;
|
|||||||
* Subclasses of ContentController are generally instantiated by ModelAsController; this will create
|
* Subclasses of ContentController are generally instantiated by ModelAsController; this will create
|
||||||
* a controller based on the URLSegment action variable, by looking in the SiteTree table.
|
* a controller based on the URLSegment action variable, by looking in the SiteTree table.
|
||||||
*
|
*
|
||||||
* @todo Can this be used for anything other than SiteTree controllers?
|
* @template T of SiteTree
|
||||||
*/
|
*/
|
||||||
class ContentController extends Controller
|
class ContentController extends Controller
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* @var SiteTree
|
* @var T
|
||||||
*/
|
*/
|
||||||
protected $dataRecord;
|
protected $dataRecord;
|
||||||
|
|
||||||
@ -72,7 +72,7 @@ class ContentController extends Controller
|
|||||||
* The ContentController will take the URLSegment parameter from the URL and use that to look
|
* The ContentController will take the URLSegment parameter from the URL and use that to look
|
||||||
* up a SiteTree record.
|
* up a SiteTree record.
|
||||||
*
|
*
|
||||||
* @param SiteTree $dataRecord
|
* @param T|null $dataRecord
|
||||||
*/
|
*/
|
||||||
public function __construct($dataRecord = null)
|
public function __construct($dataRecord = null)
|
||||||
{
|
{
|
||||||
@ -111,7 +111,7 @@ class ContentController extends Controller
|
|||||||
* Return the children of a given page. The parent reference can either be a page link or an ID.
|
* Return the children of a given page. The parent reference can either be a page link or an ID.
|
||||||
*
|
*
|
||||||
* @param string|int $parentRef
|
* @param string|int $parentRef
|
||||||
* @return SS_List
|
* @return SS_List<SiteTree>
|
||||||
*/
|
*/
|
||||||
public function ChildrenOf($parentRef)
|
public function ChildrenOf($parentRef)
|
||||||
{
|
{
|
||||||
@ -175,7 +175,6 @@ class ContentController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check page permissions
|
// Check page permissions
|
||||||
/** @skipUpgrade */
|
|
||||||
if ($this->dataRecord && $this->URLSegment != 'Security' && !$this->dataRecord->canView()) {
|
if ($this->dataRecord && $this->URLSegment != 'Security' && !$this->dataRecord->canView()) {
|
||||||
Security::permissionFailure($this);
|
Security::permissionFailure($this);
|
||||||
return;
|
return;
|
||||||
@ -186,13 +185,10 @@ class ContentController extends Controller
|
|||||||
* This acts the same as {@link Controller::handleRequest()}, but if an action cannot be found this will attempt to
|
* This acts the same as {@link Controller::handleRequest()}, but if an action cannot be found this will attempt to
|
||||||
* fall over to a child controller in order to provide functionality for nested URLs.
|
* fall over to a child controller in order to provide functionality for nested URLs.
|
||||||
*
|
*
|
||||||
* @param HTTPRequest $request
|
|
||||||
* @return HTTPResponse
|
|
||||||
* @throws HTTPResponse_Exception
|
* @throws HTTPResponse_Exception
|
||||||
*/
|
*/
|
||||||
public function handleRequest(HTTPRequest $request)
|
public function handleRequest(HTTPRequest $request): HTTPResponse
|
||||||
{
|
{
|
||||||
/** @var SiteTree $child */
|
|
||||||
$child = null;
|
$child = null;
|
||||||
$action = $request->param('Action');
|
$action = $request->param('Action');
|
||||||
|
|
||||||
@ -200,11 +196,6 @@ class ContentController extends Controller
|
|||||||
// control to a child controller. This allows for the creation of chains of controllers which correspond to a
|
// control to a child controller. This allows for the creation of chains of controllers which correspond to a
|
||||||
// nested URL.
|
// nested URL.
|
||||||
if ($action && SiteTree::config()->nested_urls && !$this->hasAction($action)) {
|
if ($action && SiteTree::config()->nested_urls && !$this->hasAction($action)) {
|
||||||
// See ModelAdController->getNestedController() for similar logic
|
|
||||||
if (class_exists('Translatable')) {
|
|
||||||
Translatable::disable_locale_filter();
|
|
||||||
}
|
|
||||||
|
|
||||||
$filter = URLSegmentFilter::create();
|
$filter = URLSegmentFilter::create();
|
||||||
|
|
||||||
// look for a page with this URLSegment
|
// look for a page with this URLSegment
|
||||||
@ -213,10 +204,6 @@ class ContentController extends Controller
|
|||||||
// url encode unless it's multibyte (already pre-encoded in the database)
|
// url encode unless it's multibyte (already pre-encoded in the database)
|
||||||
'URLSegment' => $filter->getAllowMultibyte() ? $action : rawurlencode($action),
|
'URLSegment' => $filter->getAllowMultibyte() ? $action : rawurlencode($action),
|
||||||
])->first();
|
])->first();
|
||||||
|
|
||||||
if (class_exists('Translatable')) {
|
|
||||||
Translatable::enable_locale_filter();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// we found a page with this URLSegment.
|
// we found a page with this URLSegment.
|
||||||
@ -226,25 +213,6 @@ class ContentController extends Controller
|
|||||||
|
|
||||||
$response = ModelAsController::controller_for($child)->handleRequest($request);
|
$response = ModelAsController::controller_for($child)->handleRequest($request);
|
||||||
} else {
|
} else {
|
||||||
// If a specific locale is requested, and it doesn't match the page found by URLSegment,
|
|
||||||
// look for a translation and redirect (see #5001). Only happens on the last child in
|
|
||||||
// a potentially nested URL chain.
|
|
||||||
if (class_exists('Translatable')) {
|
|
||||||
$locale = $request->getVar('locale');
|
|
||||||
if ($locale
|
|
||||||
&& i18n::getData()->validate($locale)
|
|
||||||
&& $this->dataRecord
|
|
||||||
&& $this->dataRecord->Locale != $locale
|
|
||||||
) {
|
|
||||||
$translation = $this->dataRecord->getTranslation($locale);
|
|
||||||
if ($translation) {
|
|
||||||
$response = new HTTPResponse();
|
|
||||||
$response->redirect($translation->Link(), 301);
|
|
||||||
throw new HTTPResponse_Exception($response);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Director::set_current_page($this->data());
|
Director::set_current_page($this->data());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -275,6 +243,7 @@ class ContentController extends Controller
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the associated database record
|
* Returns the associated database record
|
||||||
|
* @return T
|
||||||
*/
|
*/
|
||||||
public function data()
|
public function data()
|
||||||
{
|
{
|
||||||
@ -286,7 +255,7 @@ class ContentController extends Controller
|
|||||||
/**
|
/**
|
||||||
* Returns a fixed navigation menu of the given level.
|
* Returns a fixed navigation menu of the given level.
|
||||||
* @param int $level Menu level to return.
|
* @param int $level Menu level to return.
|
||||||
* @return ArrayList
|
* @return ArrayList<SiteTree>
|
||||||
*/
|
*/
|
||||||
public function getMenu($level = 1)
|
public function getMenu($level = 1)
|
||||||
{
|
{
|
||||||
@ -316,7 +285,6 @@ class ContentController extends Controller
|
|||||||
// We might need to create a show in menu permission
|
// We might need to create a show in menu permission
|
||||||
if (isset($result)) {
|
if (isset($result)) {
|
||||||
foreach ($result as $page) {
|
foreach ($result as $page) {
|
||||||
/** @var SiteTree $page */
|
|
||||||
if ($page->canView()) {
|
if ($page->canView()) {
|
||||||
$visible[] = $page;
|
$visible[] = $page;
|
||||||
}
|
}
|
||||||
@ -326,6 +294,9 @@ class ContentController extends Controller
|
|||||||
return new ArrayList($visible);
|
return new ArrayList($visible);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return ArrayList<SiteTree>
|
||||||
|
*/
|
||||||
public function Menu($level)
|
public function Menu($level)
|
||||||
{
|
{
|
||||||
return $this->getMenu($level);
|
return $this->getMenu($level);
|
||||||
@ -334,8 +305,6 @@ class ContentController extends Controller
|
|||||||
/**
|
/**
|
||||||
* Returns the default log-in form.
|
* Returns the default log-in form.
|
||||||
*
|
*
|
||||||
* @todo Check if here should be returned just the default log-in form or
|
|
||||||
* all available log-in forms (also OpenID...)
|
|
||||||
* @return \SilverStripe\Security\MemberAuthenticator\MemberLoginForm
|
* @return \SilverStripe\Security\MemberAuthenticator\MemberLoginForm
|
||||||
*/
|
*/
|
||||||
public function LoginForm()
|
public function LoginForm()
|
||||||
@ -415,9 +384,6 @@ HTML;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an RFC1766 compliant locale string, e.g. 'fr-CA'.
|
* Returns an RFC1766 compliant locale string, e.g. 'fr-CA'.
|
||||||
* Inspects the associated {@link dataRecord} for a {@link SiteTree->Locale} value if present,
|
|
||||||
* and falls back to {@link Translatable::get_current_locale()} or {@link i18n::default_locale()},
|
|
||||||
* depending if Translatable is enabled.
|
|
||||||
*
|
*
|
||||||
* Suitable for insertion into lang= and xml:lang=
|
* Suitable for insertion into lang= and xml:lang=
|
||||||
* attributes in HTML or XHTML output.
|
* attributes in HTML or XHTML output.
|
||||||
@ -426,14 +392,7 @@ HTML;
|
|||||||
*/
|
*/
|
||||||
public function ContentLocale()
|
public function ContentLocale()
|
||||||
{
|
{
|
||||||
if ($this->dataRecord && $this->dataRecord->hasExtension('Translatable')) {
|
|
||||||
$locale = $this->dataRecord->Locale;
|
|
||||||
} elseif (class_exists('Translatable') && SiteTree::has_extension('Translatable')) {
|
|
||||||
$locale = Translatable::get_current_locale();
|
|
||||||
} else {
|
|
||||||
$locale = i18n::get_locale();
|
$locale = i18n::get_locale();
|
||||||
}
|
|
||||||
|
|
||||||
return i18n::convert_rfc1766($locale);
|
return i18n::convert_rfc1766($locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -490,7 +449,6 @@ HTML;
|
|||||||
$this->httpError(410);
|
$this->httpError(410);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Allow this to work when allow_url_fopen=0
|
|
||||||
if (isset($_SESSION['StatsID']) && $_SESSION['StatsID']) {
|
if (isset($_SESSION['StatsID']) && $_SESSION['StatsID']) {
|
||||||
$url = 'http://ss2stat.silverstripe.com/Installation/installed?ID=' . $_SESSION['StatsID'];
|
$url = 'http://ss2stat.silverstripe.com/Installation/installed?ID=' . $_SESSION['StatsID'];
|
||||||
@file_get_contents($url ?? '');
|
@file_get_contents($url ?? '');
|
||||||
|
@ -2,9 +2,13 @@
|
|||||||
|
|
||||||
namespace SilverStripe\CMS\Controllers;
|
namespace SilverStripe\CMS\Controllers;
|
||||||
|
|
||||||
|
use SilverStripe\Admin\LeftAndMain;
|
||||||
use SilverStripe\CMS\Controllers\CMSMain;
|
use SilverStripe\CMS\Controllers\CMSMain;
|
||||||
use SilverStripe\Core\Extension;
|
use SilverStripe\Core\Extension;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends Extension<LeftAndMain>
|
||||||
|
*/
|
||||||
class LeftAndMainBatchActionsExtension extends Extension
|
class LeftAndMainBatchActionsExtension extends Extension
|
||||||
{
|
{
|
||||||
public function updateBatchActionsForm(&$form)
|
public function updateBatchActionsForm(&$form)
|
||||||
|
@ -5,6 +5,7 @@ namespace SilverStripe\CMS\Controllers;
|
|||||||
use Psr\SimpleCache\CacheInterface;
|
use Psr\SimpleCache\CacheInterface;
|
||||||
use Psr\SimpleCache\InvalidArgumentException;
|
use Psr\SimpleCache\InvalidArgumentException;
|
||||||
use ReflectionException;
|
use ReflectionException;
|
||||||
|
use SilverStripe\Admin\LeftAndMain;
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
use SilverStripe\Core\ClassInfo;
|
use SilverStripe\Core\ClassInfo;
|
||||||
use SilverStripe\Core\Config\Config;
|
use SilverStripe\Core\Config\Config;
|
||||||
@ -16,10 +17,11 @@ use SilverStripe\View\Requirements;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Extension to include custom page icons
|
* Extension to include custom page icons
|
||||||
|
*
|
||||||
|
* @extends Extension<LeftAndMain>
|
||||||
*/
|
*/
|
||||||
class LeftAndMainPageIconsExtension extends Extension implements Flushable
|
class LeftAndMainPageIconsExtension extends Extension implements Flushable
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @throws InvalidArgumentException
|
* @throws InvalidArgumentException
|
||||||
* @throws ReflectionException
|
* @throws ReflectionException
|
||||||
|
@ -17,7 +17,6 @@ use SilverStripe\Dev\Debug;
|
|||||||
use SilverStripe\ORM\DataObject;
|
use SilverStripe\ORM\DataObject;
|
||||||
use SilverStripe\ORM\DB;
|
use SilverStripe\ORM\DB;
|
||||||
use SilverStripe\View\Parsers\URLSegmentFilter;
|
use SilverStripe\View\Parsers\URLSegmentFilter;
|
||||||
use Translatable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ModelAsController deals with mapping the initial request to the first {@link SiteTree}/{@link ContentController}
|
* ModelAsController deals with mapping the initial request to the first {@link SiteTree}/{@link ContentController}
|
||||||
@ -33,11 +32,9 @@ class ModelAsController extends Controller implements NestedController
|
|||||||
* Get the appropriate {@link ContentController} for handling a {@link SiteTree} object, link it to the object and
|
* Get the appropriate {@link ContentController} for handling a {@link SiteTree} object, link it to the object and
|
||||||
* return it.
|
* return it.
|
||||||
*
|
*
|
||||||
* @param SiteTree $sitetree
|
|
||||||
* @param string $action
|
* @param string $action
|
||||||
* @return ContentController
|
|
||||||
*/
|
*/
|
||||||
public static function controller_for(SiteTree $sitetree, $action = null)
|
public static function controller_for(SiteTree $sitetree, $action = null): ContentController
|
||||||
{
|
{
|
||||||
$controller = $sitetree->getControllerName();
|
$controller = $sitetree->getControllerName();
|
||||||
|
|
||||||
@ -58,7 +55,6 @@ class ModelAsController extends Controller implements NestedController
|
|||||||
{
|
{
|
||||||
parent::beforeHandleRequest($request);
|
parent::beforeHandleRequest($request);
|
||||||
// If the database has not yet been created, redirect to the build page.
|
// If the database has not yet been created, redirect to the build page.
|
||||||
/** @skipUpgrade */
|
|
||||||
if (!DB::is_active() || !ClassInfo::hasTable('SiteTree')) {
|
if (!DB::is_active() || !ClassInfo::hasTable('SiteTree')) {
|
||||||
$this->getResponse()->redirect(Controller::join_links(
|
$this->getResponse()->redirect(Controller::join_links(
|
||||||
Director::absoluteBaseURL(),
|
Director::absoluteBaseURL(),
|
||||||
@ -72,10 +68,8 @@ class ModelAsController extends Controller implements NestedController
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @uses ModelAsController::getNestedController()
|
* @uses ModelAsController::getNestedController()
|
||||||
* @param HTTPRequest $request
|
|
||||||
* @return HTTPResponse
|
|
||||||
*/
|
*/
|
||||||
public function handleRequest(HTTPRequest $request)
|
public function handleRequest(HTTPRequest $request): HTTPResponse
|
||||||
{
|
{
|
||||||
$this->beforeHandleRequest($request);
|
$this->beforeHandleRequest($request);
|
||||||
|
|
||||||
@ -86,23 +80,16 @@ class ModelAsController extends Controller implements NestedController
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If the database has not yet been created, redirect to the build page.
|
// If the database has not yet been created, redirect to the build page.
|
||||||
/** @skipUpgrade */
|
|
||||||
if (!DB::is_active() || !ClassInfo::hasTable('SiteTree')) {
|
if (!DB::is_active() || !ClassInfo::hasTable('SiteTree')) {
|
||||||
$this->getResponse()->redirect(Director::absoluteBaseURL() . 'dev/build?returnURL=' . (isset($_GET['url']) ? urlencode($_GET['url']) : null));
|
$this->getResponse()->redirect(Controller::join_links(Director::absoluteBaseURL(), 'dev/build?returnURL=' . (isset($_GET['url']) ? urlencode($_GET['url']) : null)));
|
||||||
$this->popCurrent();
|
$this->popCurrent();
|
||||||
|
|
||||||
return $this->getResponse();
|
return $this->getResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$result = $this->getNestedController();
|
$result = $this->getNestedController()->handleRequest($this->getRequest());
|
||||||
|
$result = $result;
|
||||||
if ($result instanceof RequestHandler) {
|
|
||||||
$result = $result->handleRequest($this->getRequest());
|
|
||||||
} elseif (!($result instanceof HTTPResponse)) {
|
|
||||||
user_error("ModelAsController::getNestedController() returned bad object type '" .
|
|
||||||
get_class($result)."'", E_USER_WARNING);
|
|
||||||
}
|
|
||||||
} catch (HTTPResponse_Exception $responseException) {
|
} catch (HTTPResponse_Exception $responseException) {
|
||||||
$result = $responseException->getResponse();
|
$result = $responseException->getResponse();
|
||||||
}
|
}
|
||||||
@ -112,10 +99,9 @@ class ModelAsController extends Controller implements NestedController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return ContentController
|
|
||||||
* @throws Exception If URLSegment not passed in as a request parameter.
|
* @throws Exception If URLSegment not passed in as a request parameter.
|
||||||
*/
|
*/
|
||||||
public function getNestedController()
|
public function getNestedController(): ContentController
|
||||||
{
|
{
|
||||||
$request = $this->getRequest();
|
$request = $this->getRequest();
|
||||||
$urlSegment = $request->param('URLSegment');
|
$urlSegment = $request->param('URLSegment');
|
||||||
@ -124,11 +110,6 @@ class ModelAsController extends Controller implements NestedController
|
|||||||
throw new Exception('ModelAsController->getNestedController(): was not passed a URLSegment value.');
|
throw new Exception('ModelAsController->getNestedController(): was not passed a URLSegment value.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find page by link, regardless of current locale settings
|
|
||||||
if (class_exists('Translatable')) {
|
|
||||||
Translatable::disable_locale_filter();
|
|
||||||
}
|
|
||||||
|
|
||||||
// url encode unless it's multibyte (already pre-encoded in the database)
|
// url encode unless it's multibyte (already pre-encoded in the database)
|
||||||
$filter = URLSegmentFilter::create();
|
$filter = URLSegmentFilter::create();
|
||||||
|
|
||||||
@ -142,24 +123,12 @@ class ModelAsController extends Controller implements NestedController
|
|||||||
if (SiteTree::config()->get('nested_urls')) {
|
if (SiteTree::config()->get('nested_urls')) {
|
||||||
$conditions[] = [sprintf('"%s"."ParentID"', $tableName) => 0];
|
$conditions[] = [sprintf('"%s"."ParentID"', $tableName) => 0];
|
||||||
}
|
}
|
||||||
/** @var SiteTree $sitetree */
|
|
||||||
$sitetree = DataObject::get_one(SiteTree::class, $conditions);
|
$sitetree = DataObject::get_one(SiteTree::class, $conditions);
|
||||||
|
|
||||||
// Check translation module
|
|
||||||
// @todo Refactor out module specific code
|
|
||||||
if (class_exists('Translatable')) {
|
|
||||||
Translatable::enable_locale_filter();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$sitetree) {
|
if (!$sitetree) {
|
||||||
$this->httpError(404, 'The requested page could not be found.');
|
$this->httpError(404, 'The requested page could not be found.');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Enforce current locale setting to the loaded SiteTree object
|
|
||||||
if (class_exists('Translatable') && $sitetree->Locale) {
|
|
||||||
Translatable::set_current_locale($sitetree->Locale);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($_REQUEST['debug'])) {
|
if (isset($_REQUEST['debug'])) {
|
||||||
Debug::message("Using record #$sitetree->ID of type " . get_class($sitetree) . " with link {$sitetree->Link()}");
|
Debug::message("Using record #$sitetree->ID of type " . get_class($sitetree) . " with link {$sitetree->Link()}");
|
||||||
}
|
}
|
||||||
|
@ -10,17 +10,18 @@ use SilverStripe\Control\HTTPResponse;
|
|||||||
use SilverStripe\Control\HTTPResponse_Exception;
|
use SilverStripe\Control\HTTPResponse_Exception;
|
||||||
use SilverStripe\Core\Extension;
|
use SilverStripe\Core\Extension;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @extends Extension<ContentController|ModelAsController>
|
||||||
|
*/
|
||||||
class OldPageRedirector extends Extension
|
class OldPageRedirector extends Extension
|
||||||
{
|
{
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* On every URL that generates a 404, we'll capture it here and see if we can
|
* On every URL that generates a 404, we'll capture it here and see if we can
|
||||||
* find an old URL that it should be redirecting to.
|
* find an old URL that it should be redirecting to.
|
||||||
*
|
*
|
||||||
* @param HTTPRequest $request The request object
|
|
||||||
* @throws HTTPResponse_Exception
|
* @throws HTTPResponse_Exception
|
||||||
*/
|
*/
|
||||||
public function onBeforeHTTPError404($request)
|
public function onBeforeHTTPError404(HTTPRequest $request)
|
||||||
{
|
{
|
||||||
// We need to get the URL ourselves because $request->allParams() only has a max of 4 params
|
// We need to get the URL ourselves because $request->allParams() only has a max of 4 params
|
||||||
$params = preg_split('|/+|', $request->getURL() ?? '');
|
$params = preg_split('|/+|', $request->getURL() ?? '');
|
||||||
@ -76,7 +77,6 @@ class OldPageRedirector extends Extension
|
|||||||
'ParentID' => is_numeric($parent) ? $parent : $parent->ID,
|
'ParentID' => is_numeric($parent) ? $parent : $parent->ID,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
/** @var SiteTree $page */
|
|
||||||
$page = $pages->first();
|
$page = $pages->first();
|
||||||
if (!$page) {
|
if (!$page) {
|
||||||
// If we haven't found a candidate, lets resort to finding an old page with this URL segment
|
// If we haven't found a candidate, lets resort to finding an old page with this URL segment
|
||||||
@ -103,7 +103,7 @@ class OldPageRedirector extends Extension
|
|||||||
// No valid page found.
|
// No valid page found.
|
||||||
if ($redirect) {
|
if ($redirect) {
|
||||||
// If we had some redirect to be done, lets do it. imagine /foo/action -> /bar/action, we still want this redirect to happen if action isn't a page
|
// If we had some redirect to be done, lets do it. imagine /foo/action -> /bar/action, we still want this redirect to happen if action isn't a page
|
||||||
return $page->Link() . implode('/', $params);
|
return Controller::join_links($page->Link(), implode('/', $params));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -73,7 +73,6 @@ class RootURLController extends Controller implements Resettable
|
|||||||
|
|
||||||
self::$is_at_root = true;
|
self::$is_at_root = true;
|
||||||
|
|
||||||
/** @skipUpgrade */
|
|
||||||
if (!DB::is_active() || !ClassInfo::hasTable('SiteTree')) {
|
if (!DB::is_active() || !ClassInfo::hasTable('SiteTree')) {
|
||||||
$this->getResponse()->redirect(Controller::join_links(
|
$this->getResponse()->redirect(Controller::join_links(
|
||||||
Director::absoluteBaseURL(),
|
Director::absoluteBaseURL(),
|
||||||
@ -85,17 +84,12 @@ class RootURLController extends Controller implements Resettable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function handleRequest(HTTPRequest $request): HTTPResponse
|
||||||
* @param HTTPRequest $request
|
|
||||||
* @return HTTPResponse
|
|
||||||
*/
|
|
||||||
public function handleRequest(HTTPRequest $request)
|
|
||||||
{
|
{
|
||||||
self::$is_at_root = true;
|
self::$is_at_root = true;
|
||||||
$this->beforeHandleRequest($request);
|
$this->beforeHandleRequest($request);
|
||||||
|
|
||||||
if (!$this->getResponse()->isFinished()) {
|
if (!$this->getResponse()->isFinished()) {
|
||||||
/** @skipUpgrade */
|
|
||||||
if (!DB::is_active() || !ClassInfo::hasTable('SiteTree')) {
|
if (!DB::is_active() || !ClassInfo::hasTable('SiteTree')) {
|
||||||
$this->getResponse()->redirect(Director::absoluteBaseURL() . 'dev/build?returnURL=' . (isset($_GET['url']) ? urlencode($_GET['url']) : null));
|
$this->getResponse()->redirect(Director::absoluteBaseURL() . 'dev/build?returnURL=' . (isset($_GET['url']) ? urlencode($_GET['url']) : null));
|
||||||
return $this->getResponse();
|
return $this->getResponse();
|
||||||
|
@ -1,118 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace SilverStripe\CMS\Controllers;
|
|
||||||
|
|
||||||
use SilverStripe\ORM\CMSPreviewable;
|
|
||||||
use SilverStripe\Core\ClassInfo;
|
|
||||||
use SilverStripe\Dev\Deprecation;
|
|
||||||
use SilverStripe\ORM\ArrayList;
|
|
||||||
use SilverStripe\ORM\DataObject;
|
|
||||||
use SilverStripe\ORM\SS_List;
|
|
||||||
use SilverStripe\View\ViewableData;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility class representing links to different views of a record
|
|
||||||
* for CMS authors, usually for {@link SiteTree} objects with "stage" and "live" links.
|
|
||||||
* Useful both in the CMS and alongside the page template (for logged in authors).
|
|
||||||
* The class can be used for any {@link DataObject} subclass implementing the {@link CMSPreviewable} interface.
|
|
||||||
*
|
|
||||||
* New item types can be defined by extending the {@link SilverStripeNavigatorItem} class,
|
|
||||||
* for example the "cmsworkflow" module defines a new "future state" item with a date selector
|
|
||||||
* to view embargoed data at a future point in time. So the item doesn't always have to be a simple link.
|
|
||||||
*
|
|
||||||
* Class will be moved from `silverstripe/cms` to `silverstripe/admin`
|
|
||||||
* @deprecated 4.13.0 Will be renamed SilverStripe\Admin\Navigator\SilverStripeNavigator
|
|
||||||
*/
|
|
||||||
class SilverStripeNavigator extends ViewableData
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @var DataObject|\SilverStripe\ORM\CMSPreviewable
|
|
||||||
*/
|
|
||||||
protected $record;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param DataObject|\SilverStripe\ORM\CMSPreviewable $record
|
|
||||||
*/
|
|
||||||
public function __construct(CMSPreviewable $record)
|
|
||||||
{
|
|
||||||
Deprecation::withNoReplacement(function () {
|
|
||||||
Deprecation::notice(
|
|
||||||
'4.13.0',
|
|
||||||
'Will be renamed SilverStripe\Admin\Navigator\SilverStripeNavigator',
|
|
||||||
Deprecation::SCOPE_CLASS
|
|
||||||
);
|
|
||||||
});
|
|
||||||
parent::__construct();
|
|
||||||
$this->record = $record;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return SS_List of SilverStripeNavigatorItem
|
|
||||||
*/
|
|
||||||
public function getItems()
|
|
||||||
{
|
|
||||||
$items = [];
|
|
||||||
|
|
||||||
$classes = ClassInfo::subclassesFor(SilverStripeNavigatorItem::class);
|
|
||||||
array_shift($classes);
|
|
||||||
|
|
||||||
// Sort menu items according to priority
|
|
||||||
foreach ($classes as $class) {
|
|
||||||
/** @var SilverStripeNavigatorItem $item */
|
|
||||||
$item = new $class($this->record);
|
|
||||||
if (!$item->canView()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This funny litle formula ensures that the first item added with the same priority will be left-most.
|
|
||||||
$priority = $item->getPriority() * 100 - 1;
|
|
||||||
|
|
||||||
// Ensure that we can have duplicates with the same (default) priority
|
|
||||||
while (isset($items[$priority])) {
|
|
||||||
$priority++;
|
|
||||||
}
|
|
||||||
|
|
||||||
$items[$priority] = $item;
|
|
||||||
}
|
|
||||||
ksort($items);
|
|
||||||
|
|
||||||
// Drop the keys and let the ArrayList handle the numbering, so $IsFirst, $IsLast and others work properly.
|
|
||||||
return new ArrayList(array_values($items ?? []));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return DataObject|\SilverStripe\ORM\CMSPreviewable
|
|
||||||
*/
|
|
||||||
public function getRecord()
|
|
||||||
{
|
|
||||||
return $this->record;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param DataObject|CMSPreviewable $record
|
|
||||||
* @return array template data
|
|
||||||
*/
|
|
||||||
public static function get_for_record($record)
|
|
||||||
{
|
|
||||||
$html = '';
|
|
||||||
$message = '';
|
|
||||||
$navigator = new SilverStripeNavigator($record);
|
|
||||||
$items = $navigator->getItems();
|
|
||||||
foreach ($items as $item) {
|
|
||||||
$text = $item->getHTML();
|
|
||||||
if ($text) {
|
|
||||||
$html .= $text;
|
|
||||||
}
|
|
||||||
$newMessage = $item->getMessage();
|
|
||||||
if ($newMessage && $item->isActive()) {
|
|
||||||
$message = $newMessage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [
|
|
||||||
'items' => $html,
|
|
||||||
'message' => $message
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,155 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace SilverStripe\CMS\Controllers;
|
|
||||||
|
|
||||||
use SilverStripe\Dev\Deprecation;
|
|
||||||
use SilverStripe\ORM\CMSPreviewable;
|
|
||||||
use SilverStripe\ORM\DataObject;
|
|
||||||
use SilverStripe\Versioned\Versioned;
|
|
||||||
use SilverStripe\Security\Member;
|
|
||||||
use SilverStripe\View\ViewableData;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Navigator items are links that appear in the $SilverStripeNavigator bar.
|
|
||||||
* To add an item, extend this class - it will be automatically picked up.
|
|
||||||
* When instanciating items manually, please ensure to call {@link canView()}.
|
|
||||||
*
|
|
||||||
* Class have been moved from `silverstripe/cms` to `silverstripe/admin` and renamed.
|
|
||||||
* @deprecated Will be renamed SilverStripe\Admin\Navigator\SilverStripeNavigatorItem
|
|
||||||
*/
|
|
||||||
abstract class SilverStripeNavigatorItem extends ViewableData
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param DataObject|CMSPreviewable
|
|
||||||
*/
|
|
||||||
protected $record;
|
|
||||||
|
|
||||||
/** @var string */
|
|
||||||
protected $recordLink;
|
|
||||||
|
|
||||||
public function __construct(CMSPreviewable $record)
|
|
||||||
{
|
|
||||||
Deprecation::withNoReplacement(function () {
|
|
||||||
switch (static::class) {
|
|
||||||
// These classes have their own deprecation notice
|
|
||||||
case 'SilverStripe\CMS\Controllers\SilverStripeNavigatorItem_ArchiveLink':
|
|
||||||
case 'SilverStripe\CMS\Controllers\SilverStripeNavigatorItem_LiveLink':
|
|
||||||
case 'SilverStripe\CMS\Controllers\SilverStripeNavigatorItem_StageLink':
|
|
||||||
case 'SilverStripe\CMS\Controllers\SilverStripeNavigatorItem_Unversioned':
|
|
||||||
// This class is not deprecated and doesn't have a deprecation notice
|
|
||||||
case 'SilverStripe\CMS\Controllers\SilverStripeNavigatorItem_CMSLink':
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
Deprecation::notice(
|
|
||||||
'4.13.0',
|
|
||||||
'Will be renamed SilverStripe\Admin\Navigator\SilverStripeNavigatorItem',
|
|
||||||
Deprecation::SCOPE_CLASS
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
parent::__construct();
|
|
||||||
$this->record = $record;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string HTML, mostly a link - but can be more complex as well.
|
|
||||||
* For example, a "future state" item might show a date selector.
|
|
||||||
*/
|
|
||||||
abstract public function getHTML();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
* Get the Title of an item
|
|
||||||
*/
|
|
||||||
abstract public function getTitle();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Machine-friendly name.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getName()
|
|
||||||
{
|
|
||||||
return substr(static::class, strpos(static::class, '_') + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Optional link to a specific view of this record.
|
|
||||||
* Not all items are simple links, please use {@link getHTML()}
|
|
||||||
* to represent an item in markup unless you know what you're doing.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getLink()
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getMessage()
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return DataObject
|
|
||||||
*/
|
|
||||||
public function getRecord()
|
|
||||||
{
|
|
||||||
return $this->record;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return int
|
|
||||||
*/
|
|
||||||
public function getPriority()
|
|
||||||
{
|
|
||||||
return $this->config()->get('priority');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* As items might convey different record states like a "stage" or "live" table,
|
|
||||||
* an item can be active (showing the record in this state).
|
|
||||||
*
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
public function isActive()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filters items based on member permissions or other criteria,
|
|
||||||
* such as if a state is generally available for the current record.
|
|
||||||
*
|
|
||||||
* @param Member $member
|
|
||||||
* @return Boolean
|
|
||||||
*/
|
|
||||||
public function canView($member = null)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Counts as "archived" if the current record is a different version from both live and draft.
|
|
||||||
*
|
|
||||||
* @return boolean
|
|
||||||
*/
|
|
||||||
public function isArchived()
|
|
||||||
{
|
|
||||||
/** @var Versioned|DataObject $record */
|
|
||||||
$record = $this->record;
|
|
||||||
if (!$record->hasExtension(Versioned::class) || !$record->hasStages()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset($record->_cached_isArchived)) {
|
|
||||||
$record->_cached_isArchived = $record->isArchived();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $record->_cached_isArchived;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,92 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace SilverStripe\CMS\Controllers;
|
|
||||||
|
|
||||||
use SilverStripe\CMS\Model\RedirectorPage;
|
|
||||||
use SilverStripe\Control\Controller;
|
|
||||||
use SilverStripe\Core\Convert;
|
|
||||||
use SilverStripe\Dev\Deprecation;
|
|
||||||
use SilverStripe\ORM\CMSPreviewable;
|
|
||||||
use SilverStripe\ORM\DataObject;
|
|
||||||
use SilverStripe\ORM\FieldType\DBDatetime;
|
|
||||||
use SilverStripe\ORM\FieldType\DBField;
|
|
||||||
use SilverStripe\Versioned\Versioned;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class will be moved from `silverstripe/cms` to `silverstripe/admin`
|
|
||||||
* @deprecated 4.13.0 Will be renamed SilverStripe\VersionedAdmin\Navigator\SilverStripeNavigatorItem_ArchiveLink
|
|
||||||
*/
|
|
||||||
class SilverStripeNavigatorItem_ArchiveLink extends SilverStripeNavigatorItem
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* @param DataObject|CMSPreviewable $record
|
|
||||||
*/
|
|
||||||
public function __construct(CMSPreviewable $record)
|
|
||||||
{
|
|
||||||
Deprecation::withNoReplacement(function () {
|
|
||||||
Deprecation::notice(
|
|
||||||
'4.13.0',
|
|
||||||
'Will be renamed SilverStripe\VersionedAdmin\Navigator\SilverStripeNavigatorItem_ArchiveLink',
|
|
||||||
Deprecation::SCOPE_CLASS
|
|
||||||
);
|
|
||||||
});
|
|
||||||
parent::__construct($record);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @config */
|
|
||||||
private static $priority = 40;
|
|
||||||
|
|
||||||
public function getHTML()
|
|
||||||
{
|
|
||||||
$linkClass = $this->isActive() ? 'ss-ui-button current' : 'ss-ui-button';
|
|
||||||
$linkTitle = _t('SilverStripe\\CMS\\Controllers\\ContentController.ARCHIVEDSITE', 'Preview version');
|
|
||||||
$recordLink = Convert::raw2att(Controller::join_links(
|
|
||||||
$this->record->AbsoluteLink(),
|
|
||||||
'?archiveDate=' . urlencode($this->record->LastEdited ?? '')
|
|
||||||
));
|
|
||||||
return "<a class=\"{$linkClass}\" href=\"$recordLink\" target=\"_blank\">$linkTitle</a>";
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getTitle()
|
|
||||||
{
|
|
||||||
return _t('SilverStripe\\CMS\\Controllers\\SilverStripeNavigator.ARCHIVED', 'Archived');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getMessage()
|
|
||||||
{
|
|
||||||
$date = Versioned::current_archived_date();
|
|
||||||
if (empty($date)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
/** @var DBDatetime $dateObj */
|
|
||||||
$dateObj = DBField::create_field('Datetime', $date);
|
|
||||||
$title = _t('SilverStripe\\CMS\\Controllers\\ContentController.NOTEWONTBESHOWN', 'Note: this message will not be shown to your visitors');
|
|
||||||
return "<div id=\"SilverStripeNavigatorMessage\" title=\"{$title}\">"
|
|
||||||
. _t('SilverStripe\\CMS\\Controllers\\ContentController.ARCHIVEDSITEFROM', 'Archived site from')
|
|
||||||
. "<br />" . $dateObj->Nice() . "</div>";
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getLink()
|
|
||||||
{
|
|
||||||
$link = $this->record->PreviewLink();
|
|
||||||
return $link ? Controller::join_links($link, '?archiveDate=' . urlencode($this->record->LastEdited ?? '')) : '';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function canView($member = null)
|
|
||||||
{
|
|
||||||
/** @var Versioned|DataObject $record */
|
|
||||||
$record = $this->record;
|
|
||||||
return (
|
|
||||||
$record->hasExtension(Versioned::class)
|
|
||||||
&& $record->hasStages()
|
|
||||||
&& $this->isArchived()
|
|
||||||
// Don't follow redirects in preview, they break the CMS editing form
|
|
||||||
&& !($record instanceof RedirectorPage)
|
|
||||||
&& $this->getLink()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isActive()
|
|
||||||
{
|
|
||||||
return $this->isArchived();
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,6 +3,7 @@
|
|||||||
namespace SilverStripe\CMS\Controllers;
|
namespace SilverStripe\CMS\Controllers;
|
||||||
|
|
||||||
use SilverStripe\Admin\LeftAndMain;
|
use SilverStripe\Admin\LeftAndMain;
|
||||||
|
use SilverStripe\Admin\Navigator\SilverStripeNavigatorItem;
|
||||||
use SilverStripe\CMS\Model\RedirectorPage;
|
use SilverStripe\CMS\Model\RedirectorPage;
|
||||||
use SilverStripe\Control\Controller;
|
use SilverStripe\Control\Controller;
|
||||||
|
|
||||||
|
@ -1,106 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace SilverStripe\CMS\Controllers;
|
|
||||||
|
|
||||||
use SilverStripe\Control\Controller;
|
|
||||||
use SilverStripe\Core\Config\Config;
|
|
||||||
use SilverStripe\Core\Convert;
|
|
||||||
use SilverStripe\Dev\Deprecation;
|
|
||||||
use SilverStripe\ORM\CMSPreviewable;
|
|
||||||
use SilverStripe\ORM\DataObject;
|
|
||||||
use SilverStripe\Versioned\Versioned;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class will be moved from `silverstripe/cms` to `silverstripe/admin`.
|
|
||||||
* @deprecated 4.13.0 Will be renamed SilverStripe\VersionedAdmin\Navigator\SilverStripeNavigatorItem_LiveLink
|
|
||||||
*/
|
|
||||||
class SilverStripeNavigatorItem_LiveLink extends SilverStripeNavigatorItem
|
|
||||||
{
|
|
||||||
public function __construct(CMSPreviewable $record)
|
|
||||||
{
|
|
||||||
Deprecation::withNoReplacement(function () {
|
|
||||||
Deprecation::notice(
|
|
||||||
'4.13.0',
|
|
||||||
'Will be renamed SilverStripe\VersionedAdmin\Navigator\SilverStripeNavigatorItem_LiveLink',
|
|
||||||
Deprecation::SCOPE_CLASS
|
|
||||||
);
|
|
||||||
});
|
|
||||||
parent::__construct($record);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @config */
|
|
||||||
private static $priority = 30;
|
|
||||||
|
|
||||||
public function getHTML()
|
|
||||||
{
|
|
||||||
$livePage = $this->getLivePage();
|
|
||||||
if (!$livePage) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$linkClass = $this->isActive() ? 'class="current" ' : '';
|
|
||||||
$linkTitle = _t('SilverStripe\\CMS\\Controllers\\ContentController.PUBLISHEDSITE', 'Published Site');
|
|
||||||
$recordLink = Convert::raw2att(Controller::join_links($livePage->AbsoluteLink(), "?stage=Live"));
|
|
||||||
return "<a {$linkClass} href=\"$recordLink\">$linkTitle</a>";
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getTitle()
|
|
||||||
{
|
|
||||||
return _t(
|
|
||||||
'SilverStripe\\CMS\\Controllers\\ContentController.PUBLISHED',
|
|
||||||
'Published',
|
|
||||||
'Used for the Switch between draft and published view mode. Needs to be a short label'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getMessage()
|
|
||||||
{
|
|
||||||
return "<div id=\"SilverStripeNavigatorMessage\" title=\"" . _t(
|
|
||||||
'SilverStripe\\CMS\\Controllers\\ContentController.NOTEWONTBESHOWN',
|
|
||||||
'Note: this message will not be shown to your visitors'
|
|
||||||
) . "\">" . _t(
|
|
||||||
'SilverStripe\\CMS\\Controllers\\ContentController.PUBLISHEDSITE',
|
|
||||||
'Published Site'
|
|
||||||
) . "</div>";
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getLink()
|
|
||||||
{
|
|
||||||
$link = $this->getLivePage()->PreviewLink();
|
|
||||||
return $link ? Controller::join_links($link, '?stage=Live') : '';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function canView($member = null)
|
|
||||||
{
|
|
||||||
/** @var Versioned|DataObject $record */
|
|
||||||
$record = $this->record;
|
|
||||||
return (
|
|
||||||
$record->hasExtension(Versioned::class)
|
|
||||||
&& $this->showLiveLink()
|
|
||||||
&& $record->hasStages()
|
|
||||||
&& $this->getLivePage()
|
|
||||||
&& $this->getLink()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function showLiveLink()
|
|
||||||
{
|
|
||||||
return (bool)Config::inst()->get(get_class($this->record), 'show_live_link');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isActive()
|
|
||||||
{
|
|
||||||
return (
|
|
||||||
(!Versioned::get_stage() || Versioned::get_stage() == 'Live')
|
|
||||||
&& !$this->isArchived()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getLivePage()
|
|
||||||
{
|
|
||||||
$baseClass = $this->record->baseClass();
|
|
||||||
return Versioned::get_by_stage($baseClass, Versioned::LIVE)->byID($this->record->ID);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,115 +0,0 @@
|
|||||||
<?php
|
|
||||||
namespace SilverStripe\CMS\Controllers;
|
|
||||||
|
|
||||||
use SilverStripe\Control\Controller;
|
|
||||||
use SilverStripe\Core\Config\Config;
|
|
||||||
use SilverStripe\Core\ClassInfo;
|
|
||||||
use SilverStripe\Core\Convert;
|
|
||||||
use SilverStripe\Dev\Deprecation;
|
|
||||||
use SilverStripe\ORM\CMSPreviewable;
|
|
||||||
use SilverStripe\ORM\DataObject;
|
|
||||||
use SilverStripe\Versioned\Versioned;
|
|
||||||
use SiteTreeFutureState;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class will be moved from `silverstripe/cms` to `silverstripe/admin`.
|
|
||||||
* @deprecated 4.13.0 Will be renamed SilverStripe\VersionedAdmin\Navigator\SilverStripeNavigatorItem_StageLink
|
|
||||||
*/
|
|
||||||
class SilverStripeNavigatorItem_StageLink extends SilverStripeNavigatorItem
|
|
||||||
{
|
|
||||||
public function __construct(CMSPreviewable $record)
|
|
||||||
{
|
|
||||||
Deprecation::withNoReplacement(function () {
|
|
||||||
Deprecation::notice(
|
|
||||||
'4.13.0',
|
|
||||||
'Will be renamed SilverStripe\VersionedAdmin\Navigator\SilverStripeNavigatorItem_StageLink',
|
|
||||||
Deprecation::SCOPE_CLASS
|
|
||||||
);
|
|
||||||
});
|
|
||||||
parent::__construct($record);
|
|
||||||
}
|
|
||||||
/** @config */
|
|
||||||
private static $priority = 20;
|
|
||||||
|
|
||||||
public function getHTML()
|
|
||||||
{
|
|
||||||
$draftPage = $this->getDraftPage();
|
|
||||||
if (!$draftPage) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
$linkClass = $this->isActive() ? 'class="current" ' : '';
|
|
||||||
$linkTitle = _t('SilverStripe\\CMS\\Controllers\\ContentController.DRAFTSITE', 'Draft Site');
|
|
||||||
$recordLink = Convert::raw2att(Controller::join_links($draftPage->AbsoluteLink(), "?stage=Stage"));
|
|
||||||
return "<a {$linkClass} href=\"$recordLink\">$linkTitle</a>";
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getTitle()
|
|
||||||
{
|
|
||||||
return _t(
|
|
||||||
'SilverStripe\\CMS\\Controllers\\ContentController.DRAFT',
|
|
||||||
'Draft',
|
|
||||||
'Used for the Switch between draft and published view mode. Needs to be a short label'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getMessage()
|
|
||||||
{
|
|
||||||
return "<div id=\"SilverStripeNavigatorMessage\" title=\"" . _t(
|
|
||||||
'SilverStripe\\CMS\\Controllers\\ContentController.NOTEWONTBESHOWN',
|
|
||||||
'Note: this message will not be shown to your visitors'
|
|
||||||
) . "\">" . _t(
|
|
||||||
'SilverStripe\\CMS\\Controllers\\ContentController.DRAFTSITE',
|
|
||||||
'Draft Site'
|
|
||||||
) . "</div>";
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getLink()
|
|
||||||
{
|
|
||||||
$date = Versioned::current_archived_date();
|
|
||||||
$link = $this->record->PreviewLink();
|
|
||||||
if (!$link) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
return Controller::join_links(
|
|
||||||
$link,
|
|
||||||
'?stage=Stage',
|
|
||||||
$date ? '?archiveDate=' . $date : null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function canView($member = null)
|
|
||||||
{
|
|
||||||
/** @var Versioned|DataObject $record */
|
|
||||||
$record = $this->record;
|
|
||||||
return (
|
|
||||||
$record->hasExtension(Versioned::class)
|
|
||||||
&& $this->showStageLink()
|
|
||||||
&& $record->hasStages()
|
|
||||||
&& $this->getDraftPage()
|
|
||||||
&& $this->getLink()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function showStageLink()
|
|
||||||
{
|
|
||||||
return (bool)Config::inst()->get(get_class($this->record), 'show_stage_link');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function isActive()
|
|
||||||
{
|
|
||||||
return (
|
|
||||||
Versioned::get_stage() == 'Stage'
|
|
||||||
&& !(ClassInfo::exists('SiteTreeFutureState') && SiteTreeFutureState::get_future_datetime())
|
|
||||||
&& !$this->isArchived()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getDraftPage()
|
|
||||||
{
|
|
||||||
$baseClass = $this->record->baseClass();
|
|
||||||
return Versioned::get_by_stage($baseClass, Versioned::DRAFT)->byID($this->record->ID);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,98 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace SilverStripe\CMS\Controllers;
|
|
||||||
|
|
||||||
use SilverStripe\CMS\Controllers\SilverStripeNavigatorItem;
|
|
||||||
use SilverStripe\Core\Config\Config;
|
|
||||||
use SilverStripe\Core\Convert;
|
|
||||||
use SilverStripe\Dev\Deprecation;
|
|
||||||
use SilverStripe\ORM\CMSPreviewable;
|
|
||||||
use SilverStripe\Security\Member;
|
|
||||||
use SilverStripe\Versioned\Versioned;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class will be moved from `silverstripe/cms` to `silverstripe/admin`.
|
|
||||||
* @deprecated 4.13.0 Will be renamed SilverStripe\Admin\Navigator\SilverStripeNavigatorItem_Unversioned
|
|
||||||
*/
|
|
||||||
class SilverStripeNavigatorItem_Unversioned extends SilverStripeNavigatorItem
|
|
||||||
{
|
|
||||||
public function __construct(CMSPreviewable $record)
|
|
||||||
{
|
|
||||||
Deprecation::withNoReplacement(function () {
|
|
||||||
Deprecation::notice(
|
|
||||||
'4.13.0',
|
|
||||||
'Will be renamed SilverStripe\Admin\Navigator\SilverStripeNavigatorItem_Unversioned',
|
|
||||||
Deprecation::SCOPE_CLASS
|
|
||||||
);
|
|
||||||
});
|
|
||||||
parent::__construct($record);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getHTML()
|
|
||||||
{
|
|
||||||
$recordLink = Convert::raw2att($this->getLink());
|
|
||||||
$linkTitle = _t('SilverStripe\\CMS\\Controllers\\ContentController.UNVERSIONEDPREVIEW', 'Preview');
|
|
||||||
return "<a class=\"current\" href=\"$recordLink\">$linkTitle</a>";
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getLink()
|
|
||||||
{
|
|
||||||
return $this->getRecord()->PreviewLink() ?? '';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getTitle()
|
|
||||||
{
|
|
||||||
return _t(
|
|
||||||
'SilverStripe\\CMS\\Controllers\\ContentController.UNVERSIONEDPREVIEW',
|
|
||||||
'Preview',
|
|
||||||
'Used for the Switch between states (if any other other states are added). Needs to be a short label'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* True if the record doesn't have the Versioned extension and is configured to display this item.
|
|
||||||
*
|
|
||||||
* @param Member $member
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function canView($member = null)
|
|
||||||
{
|
|
||||||
return (
|
|
||||||
$this->recordIsUnversioned()
|
|
||||||
&& $this->showUnversionedLink()
|
|
||||||
&& $this->getLink()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function recordIsUnversioned(): bool
|
|
||||||
{
|
|
||||||
$record = $this->getRecord();
|
|
||||||
// If the record has the Versioned extension, it can be considered unversioned
|
|
||||||
// for the purposes of this class if it has no stages and is not archived.
|
|
||||||
if ($record->hasExtension(Versioned::class)) {
|
|
||||||
return (!$record->hasStages()) && !$this->isArchived();
|
|
||||||
}
|
|
||||||
// Completely unversioned.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* True if the record is configured to display this item.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function showUnversionedLink(): bool
|
|
||||||
{
|
|
||||||
return (bool) Config::inst()->get(get_class($this->record), 'show_unversioned_preview_link');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This item is always active, as there are unlikely to be other preview states available for the record.
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function isActive()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
@ -9,7 +9,8 @@ use SilverStripe\Forms\Form;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Decorates ModalController with insert internal link
|
* Decorates ModalController with insert internal link
|
||||||
* @see ModalController
|
*
|
||||||
|
* @extends Extension<ModalController>
|
||||||
*/
|
*/
|
||||||
class InternalLinkModalExtension extends Extension
|
class InternalLinkModalExtension extends Extension
|
||||||
{
|
{
|
||||||
@ -22,17 +23,6 @@ class InternalLinkModalExtension extends Extension
|
|||||||
'editorAnchorLink',
|
'editorAnchorLink',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
|
||||||
* @return ModalController
|
|
||||||
*/
|
|
||||||
public function getOwner()
|
|
||||||
{
|
|
||||||
/** @var ModalController $owner */
|
|
||||||
$owner = $this->owner;
|
|
||||||
return $owner;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Form for inserting internal link pages
|
* Form for inserting internal link pages
|
||||||
*
|
*
|
||||||
|
@ -134,7 +134,7 @@ class SiteTreeURLSegmentField extends TextField
|
|||||||
*/
|
*/
|
||||||
public function getURLPrefix()
|
public function getURLPrefix()
|
||||||
{
|
{
|
||||||
return $this->urlPrefix;
|
return rtrim($this->urlPrefix ?? '', '/') . '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getURLSuffix()
|
public function getURLSuffix()
|
||||||
|
@ -18,8 +18,8 @@ use SilverStripe\Versioned\Versioned;
|
|||||||
* @property string $RedirectionType Either 'Internal','External' or 'File'
|
* @property string $RedirectionType Either 'Internal','External' or 'File'
|
||||||
* @property string $ExternalURL URL to redirect to if $RedirectionType is 'External'
|
* @property string $ExternalURL URL to redirect to if $RedirectionType is 'External'
|
||||||
* @property int $LinkToID
|
* @property int $LinkToID
|
||||||
* @method SiteTree LinkTo() Page to link to if $RedirectionType is 'Internal'
|
* @method SiteTree LinkTo()
|
||||||
* @method File LinkToFile() File to link to if $RedirectionType is 'File'
|
* @method File LinkToFile()
|
||||||
*/
|
*/
|
||||||
class RedirectorPage extends Page
|
class RedirectorPage extends Page
|
||||||
{
|
{
|
||||||
@ -120,7 +120,6 @@ class RedirectorPage extends Page
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check internal redirect
|
// Check internal redirect
|
||||||
/** @var SiteTree $linkTo */
|
|
||||||
$linkTo = $this->LinkToID ? SiteTree::get()->byID($this->LinkToID) : null;
|
$linkTo = $this->LinkToID ? SiteTree::get()->byID($this->LinkToID) : null;
|
||||||
|
|
||||||
if (empty($linkTo)) {
|
if (empty($linkTo)) {
|
||||||
@ -164,7 +163,6 @@ class RedirectorPage extends Page
|
|||||||
$this->HasBrokenLink = true;
|
$this->HasBrokenLink = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// TODO implement checking of a remote site
|
|
||||||
$this->HasBrokenLink = false;
|
$this->HasBrokenLink = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,27 +3,48 @@ namespace SilverStripe\CMS\Model;
|
|||||||
|
|
||||||
use SilverStripe\Control\HTTPRequest;
|
use SilverStripe\Control\HTTPRequest;
|
||||||
use PageController;
|
use PageController;
|
||||||
|
use SilverStripe\Control\HTTPResponse_Exception;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controller for the {@link RedirectorPage}.
|
* Controller for the {@link RedirectorPage}.
|
||||||
|
*
|
||||||
|
* @extends PageController<RedirectorPage>
|
||||||
*/
|
*/
|
||||||
class RedirectorPageController extends PageController
|
class RedirectorPageController extends PageController
|
||||||
{
|
{
|
||||||
private static $allowed_actions = ['index'];
|
private static $allowed_actions = ['index'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Should respond with HTTP 404 if the page or file being redirected to is missing
|
||||||
|
*/
|
||||||
|
private static bool $missing_redirect_is_404 = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check we don't already have a redirect code set
|
* Check we don't already have a redirect code set
|
||||||
*
|
*
|
||||||
* @param HTTPRequest $request
|
* @param HTTPRequest $request
|
||||||
* @return \SilverStripe\Control\HTTPResponse
|
* @return \SilverStripe\Control\HTTPResponse
|
||||||
|
* @throws HTTPResponse_Exception
|
||||||
*/
|
*/
|
||||||
public function index(HTTPRequest $request)
|
public function index(HTTPRequest $request)
|
||||||
{
|
{
|
||||||
/** @var RedirectorPage $page */
|
|
||||||
$page = $this->data();
|
$page = $this->data();
|
||||||
|
|
||||||
|
// Redirect if we can
|
||||||
if (!$this->getResponse()->isFinished() && $link = $page->redirectionLink()) {
|
if (!$this->getResponse()->isFinished() && $link = $page->redirectionLink()) {
|
||||||
$this->redirect($link, 301);
|
return $this->redirect($link, 301);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Respond with 404 if redirecting to a missing file or page
|
||||||
|
if (($this->RedirectionType === 'Internal' && !$page->LinkTo()?->exists())
|
||||||
|
|| ($this->RedirectionType === 'File' && !$page->LinkToFile()?->exists())
|
||||||
|
) {
|
||||||
|
if (static::config()->get('missing_redirect_is_404')) {
|
||||||
|
$this->httpError(404);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to a message about the bad setup
|
||||||
return parent::handleAction($request, 'handleIndex');
|
return parent::handleAction($request, 'handleIndex');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,6 @@ use SilverStripe\Core\Manifest\ModuleResource;
|
|||||||
use SilverStripe\Core\Manifest\ModuleResourceLoader;
|
use SilverStripe\Core\Manifest\ModuleResourceLoader;
|
||||||
use SilverStripe\Core\Manifest\VersionProvider;
|
use SilverStripe\Core\Manifest\VersionProvider;
|
||||||
use SilverStripe\Core\Resettable;
|
use SilverStripe\Core\Resettable;
|
||||||
use SilverStripe\Dev\Deprecation;
|
|
||||||
use SilverStripe\Forms\CheckboxField;
|
use SilverStripe\Forms\CheckboxField;
|
||||||
use SilverStripe\Forms\CompositeField;
|
use SilverStripe\Forms\CompositeField;
|
||||||
use SilverStripe\Forms\DropdownField;
|
use SilverStripe\Forms\DropdownField;
|
||||||
@ -36,8 +35,10 @@ use SilverStripe\Forms\GridField\GridField;
|
|||||||
use SilverStripe\Forms\GridField\GridFieldDataColumns;
|
use SilverStripe\Forms\GridField\GridFieldDataColumns;
|
||||||
use SilverStripe\Forms\GridField\GridFieldLazyLoader;
|
use SilverStripe\Forms\GridField\GridFieldLazyLoader;
|
||||||
use SilverStripe\Forms\HTMLEditor\HTMLEditorField;
|
use SilverStripe\Forms\HTMLEditor\HTMLEditorField;
|
||||||
|
use SilverStripe\Forms\ListboxField;
|
||||||
use SilverStripe\Forms\LiteralField;
|
use SilverStripe\Forms\LiteralField;
|
||||||
use SilverStripe\Forms\OptionsetField;
|
use SilverStripe\Forms\OptionsetField;
|
||||||
|
use SilverStripe\Forms\SearchableMultiDropdownField;
|
||||||
use SilverStripe\Forms\Tab;
|
use SilverStripe\Forms\Tab;
|
||||||
use SilverStripe\Forms\TabSet;
|
use SilverStripe\Forms\TabSet;
|
||||||
use SilverStripe\Forms\TextareaField;
|
use SilverStripe\Forms\TextareaField;
|
||||||
@ -102,17 +103,13 @@ use SilverStripe\View\SSViewer;
|
|||||||
* @property bool $HasBrokenFile True if this page has a broken file shortcode
|
* @property bool $HasBrokenFile True if this page has a broken file shortcode
|
||||||
* @property bool $HasBrokenLink True if this page has a broken page shortcode
|
* @property bool $HasBrokenLink True if this page has a broken page shortcode
|
||||||
*
|
*
|
||||||
* @method ManyManyList ViewerGroups() List of groups that can view this object.
|
|
||||||
* @method ManyManyList EditorGroups() List of groups that can edit this object.
|
|
||||||
* @method SiteTree Parent()
|
|
||||||
* @method HasManyList|SiteTreeLink[] BackLinks() List of SiteTreeLink objects attached to this page
|
|
||||||
*
|
|
||||||
* @mixin Hierarchy
|
* @mixin Hierarchy
|
||||||
* @mixin Versioned
|
* @mixin Versioned
|
||||||
* @mixin RecursivePublishable
|
* @mixin RecursivePublishable
|
||||||
* @mixin SiteTreeLinkTracking Added via linktracking.yml to DataObject directly
|
* @mixin SiteTreeLinkTracking Added via linktracking.yml to DataObject directly
|
||||||
* @mixin FileLinkTracking Added via filetracking.yml in silverstripe/assets
|
* @mixin FileLinkTracking Added via filetracking.yml in silverstripe/assets
|
||||||
* @mixin InheritedPermissionsExtension
|
* @mixin InheritedPermissionsExtension
|
||||||
|
* @method HasManyList<SiteTreeLink> BackLinks()
|
||||||
*/
|
*/
|
||||||
class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvider, CMSPreviewable, Resettable, Flushable, MemberCacheFlusher
|
class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvider, CMSPreviewable, Resettable, Flushable, MemberCacheFlusher
|
||||||
{
|
{
|
||||||
@ -203,11 +200,20 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
|||||||
* in the cms, set this to the old class name. Eg, if you extended Product
|
* in the cms, set this to the old class name. Eg, if you extended Product
|
||||||
* to make ImprovedProduct, then you would set $hide_ancestor to Product.
|
* to make ImprovedProduct, then you would set $hide_ancestor to Product.
|
||||||
*
|
*
|
||||||
|
* @deprecated 5.2.0 Use hide_pagetypes instead
|
||||||
|
*
|
||||||
* @config
|
* @config
|
||||||
* @var string
|
* @var string
|
||||||
*/
|
*/
|
||||||
private static $hide_ancestor = null;
|
private static $hide_ancestor = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Any fully qualified class names added to this array will be hidden in the CMS
|
||||||
|
* when selecting page types, e.g. for creating a new page or changing the type
|
||||||
|
* of an existing page.
|
||||||
|
*/
|
||||||
|
private static array $hide_pagetypes = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* You can define the class of the controller that maps to your SiteTree object here if
|
* You can define the class of the controller that maps to your SiteTree object here if
|
||||||
* you don't want to rely on the magic of appending Controller to the Classname
|
* you don't want to rely on the magic of appending Controller to the Classname
|
||||||
@ -538,7 +544,7 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return a subclass map of SiteTree that shouldn't be hidden through {@link SiteTree::$hide_ancestor}
|
* Return a subclass map of SiteTree that shouldn't be hidden through {@link SiteTree::$hide_pagetypes}
|
||||||
*
|
*
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
@ -551,7 +557,7 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
|||||||
unset($classes[$baseClassIndex]);
|
unset($classes[$baseClassIndex]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$kill_ancestors = [];
|
$kill_ancestors = self::config()->get('hide_pagetypes', Config::UNINHERITED) ?? [];
|
||||||
|
|
||||||
// figure out if there are any classes we don't want to appear
|
// figure out if there are any classes we don't want to appear
|
||||||
foreach ($classes as $class) {
|
foreach ($classes as $class) {
|
||||||
@ -601,7 +607,6 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
|||||||
return null; // There were no suitable matches at all.
|
return null; // There were no suitable matches at all.
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @var SiteTree $page */
|
|
||||||
$link = Convert::raw2att($page->Link());
|
$link = Convert::raw2att($page->Link());
|
||||||
|
|
||||||
if ($content) {
|
if ($content) {
|
||||||
@ -639,7 +644,7 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
|||||||
if ($this->hasMethod('alternateAbsoluteLink')) {
|
if ($this->hasMethod('alternateAbsoluteLink')) {
|
||||||
return $this->alternateAbsoluteLink($action);
|
return $this->alternateAbsoluteLink($action);
|
||||||
} else {
|
} else {
|
||||||
return Director::absoluteURL($this->Link($action));
|
return Director::absoluteURL((string) $this->Link($action));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -652,13 +657,6 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
|||||||
*/
|
*/
|
||||||
public function PreviewLink($action = null)
|
public function PreviewLink($action = null)
|
||||||
{
|
{
|
||||||
if ($this->hasMethod('alternatePreviewLink')) {
|
|
||||||
Deprecation::withNoReplacement(function () use ($action) {
|
|
||||||
Deprecation::notice('5.0', 'Use updatePreviewLink or override PreviewLink method');
|
|
||||||
return $this->alternatePreviewLink($action);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
$link = $this->AbsoluteLink($action);
|
$link = $this->AbsoluteLink($action);
|
||||||
$this->extend('updatePreviewLink', $link, $action);
|
$this->extend('updatePreviewLink', $link, $action);
|
||||||
return $link;
|
return $link;
|
||||||
@ -699,15 +697,17 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
|||||||
$base = $this->URLSegment;
|
$base = $this->URLSegment;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->extend('updateRelativeLink', $base, $action);
|
|
||||||
|
|
||||||
// Legacy support: If $action === true, retain URLSegment for homepages,
|
// Legacy support: If $action === true, retain URLSegment for homepages,
|
||||||
// but don't append any action
|
// but don't append any action
|
||||||
if ($action === true) {
|
if ($action === true) {
|
||||||
$action = null;
|
$action = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Controller::join_links($base, '/', $action);
|
$link = Controller::join_links($base, $action);
|
||||||
|
|
||||||
|
$this->extend('updateRelativeLink', $link, $base, $action);
|
||||||
|
|
||||||
|
return $link;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -884,12 +884,10 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
|||||||
*/
|
*/
|
||||||
public function duplicateWithChildren()
|
public function duplicateWithChildren()
|
||||||
{
|
{
|
||||||
/** @var SiteTree $clone */
|
|
||||||
$clone = $this->duplicate();
|
$clone = $this->duplicate();
|
||||||
$children = $this->AllChildren();
|
$children = $this->AllChildren();
|
||||||
|
|
||||||
if ($children) {
|
if ($children) {
|
||||||
/** @var SiteTree $child */
|
|
||||||
$sort = 0;
|
$sort = 0;
|
||||||
foreach ($children as $child) {
|
foreach ($children as $child) {
|
||||||
$childClone = method_exists($child, 'duplicateWithChildren')
|
$childClone = method_exists($child, 'duplicateWithChildren')
|
||||||
@ -912,7 +910,6 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
|||||||
*/
|
*/
|
||||||
public function duplicateAsChild($id)
|
public function duplicateAsChild($id)
|
||||||
{
|
{
|
||||||
/** @var SiteTree $newSiteTree */
|
|
||||||
$newSiteTree = $this->duplicate();
|
$newSiteTree = $this->duplicate();
|
||||||
$newSiteTree->ParentID = $id;
|
$newSiteTree->ParentID = $id;
|
||||||
$newSiteTree->Sort = 0;
|
$newSiteTree->Sort = 0;
|
||||||
@ -948,7 +945,7 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
|||||||
* @param boolean|string $stopAtPageType ClassName of a page to stop the upwards traversal.
|
* @param boolean|string $stopAtPageType ClassName of a page to stop the upwards traversal.
|
||||||
* @param boolean $showHidden Include pages marked with the attribute ShowInMenus = 0
|
* @param boolean $showHidden Include pages marked with the attribute ShowInMenus = 0
|
||||||
*
|
*
|
||||||
* @return ArrayList
|
* @return ArrayList<SiteTree>
|
||||||
*/
|
*/
|
||||||
public function getBreadcrumbItems($maxDepth = 20, $stopAtPageType = false, $showHidden = false)
|
public function getBreadcrumbItems($maxDepth = 20, $stopAtPageType = false, $showHidden = false)
|
||||||
{
|
{
|
||||||
@ -1193,6 +1190,14 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check for specific users
|
||||||
|
if ($this->CanViewType === InheritedPermissions::ONLY_THESE_MEMBERS
|
||||||
|
&& $member
|
||||||
|
&& $this->ViewerMembers()->filter('ID', $member->ID)->count() > 0
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1406,7 +1411,6 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
|||||||
if (eval("return $condition;")) {
|
if (eval("return $condition;")) {
|
||||||
$collator[] = $item;
|
$collator[] = $item;
|
||||||
}
|
}
|
||||||
/** @var SiteTree $item */
|
|
||||||
$item->collateDescendants($condition, $collator);
|
$item->collateDescendants($condition, $collator);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -1758,7 +1762,6 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
|||||||
// If deleting this page, delete all its children.
|
// If deleting this page, delete all its children.
|
||||||
if ($this->isInDB() && SiteTree::config()->get('enforce_strict_hierarchy')) {
|
if ($this->isInDB() && SiteTree::config()->get('enforce_strict_hierarchy')) {
|
||||||
foreach ($this->AllChildren() as $child) {
|
foreach ($this->AllChildren() as $child) {
|
||||||
/** @var SiteTree $child */
|
|
||||||
$child->delete();
|
$child->delete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1956,11 +1959,10 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
|||||||
/**
|
/**
|
||||||
* Get the back-link tracking objects that link to this page
|
* Get the back-link tracking objects that link to this page
|
||||||
*
|
*
|
||||||
* @return ArrayList|DataObject[]
|
* @return ArrayList<DataObject>
|
||||||
*/
|
*/
|
||||||
public function BackLinkTracking()
|
public function BackLinkTracking()
|
||||||
{
|
{
|
||||||
// @todo - Implement PolymorphicManyManyList to replace this
|
|
||||||
$list = ArrayList::create();
|
$list = ArrayList::create();
|
||||||
|
|
||||||
$siteTreelinkTable = SiteTreeLink::singleton()->baseTable();
|
$siteTreelinkTable = SiteTreeLink::singleton()->baseTable();
|
||||||
@ -1998,7 +2000,7 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
|||||||
* Returns the pages that depend on this page. This includes virtual pages, pages that link to it, etc.
|
* Returns the pages that depend on this page. This includes virtual pages, pages that link to it, etc.
|
||||||
*
|
*
|
||||||
* @param bool $includeVirtuals Set to false to exlcude virtual pages.
|
* @param bool $includeVirtuals Set to false to exlcude virtual pages.
|
||||||
* @return ArrayList|SiteTree[]
|
* @return ArrayList<SiteTree>
|
||||||
*/
|
*/
|
||||||
public function DependentPages($includeVirtuals = true)
|
public function DependentPages($includeVirtuals = true)
|
||||||
{
|
{
|
||||||
@ -2048,7 +2050,7 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
|||||||
/**
|
/**
|
||||||
* Return all virtual pages that link to this page.
|
* Return all virtual pages that link to this page.
|
||||||
*
|
*
|
||||||
* @return DataList
|
* @return DataList<SiteTree>
|
||||||
*/
|
*/
|
||||||
public function VirtualPages()
|
public function VirtualPages()
|
||||||
{
|
{
|
||||||
@ -2095,7 +2097,6 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
|||||||
false,
|
false,
|
||||||
$dependentPages
|
$dependentPages
|
||||||
);
|
);
|
||||||
/** @var GridFieldDataColumns $dataColumns */
|
|
||||||
$dataColumns = $dependentTable->getConfig()->getComponentByType(GridFieldDataColumns::class);
|
$dataColumns = $dependentTable->getConfig()->getComponentByType(GridFieldDataColumns::class);
|
||||||
$dataColumns
|
$dataColumns
|
||||||
->setDisplayFields($dependentColumns)
|
->setDisplayFields($dependentColumns)
|
||||||
@ -2280,6 +2281,13 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
|||||||
_t(__CLASS__.'.VIEWERGROUPS', "Viewer Groups"),
|
_t(__CLASS__.'.VIEWERGROUPS', "Viewer Groups"),
|
||||||
Group::class
|
Group::class
|
||||||
),
|
),
|
||||||
|
$viewerMembersField = SearchableMultiDropdownField::create(
|
||||||
|
"ViewerMembers",
|
||||||
|
_t(__CLASS__.'.VIEWERMEMBERS', "Viewer Users"),
|
||||||
|
Member::get(),
|
||||||
|
)
|
||||||
|
->setIsLazyLoaded(true)
|
||||||
|
->setUseSearchContext(true),
|
||||||
$editorsOptionsField = new OptionsetField(
|
$editorsOptionsField = new OptionsetField(
|
||||||
"CanEditType",
|
"CanEditType",
|
||||||
_t(__CLASS__.'.EDITHEADER', "Who can edit this page?")
|
_t(__CLASS__.'.EDITHEADER', "Who can edit this page?")
|
||||||
@ -2288,7 +2296,14 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
|||||||
"EditorGroups",
|
"EditorGroups",
|
||||||
_t(__CLASS__.'.EDITORGROUPS', "Editor Groups"),
|
_t(__CLASS__.'.EDITORGROUPS', "Editor Groups"),
|
||||||
Group::class
|
Group::class
|
||||||
|
),
|
||||||
|
$editorMembersField = SearchableMultiDropdownField::create(
|
||||||
|
"EditorMembers",
|
||||||
|
_t(__CLASS__.'.EDITORMEMBERS', "Editor Users"),
|
||||||
|
Member::get()
|
||||||
)
|
)
|
||||||
|
->setIsLazyLoaded(true)
|
||||||
|
->setUseSearchContext(true)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@ -2328,6 +2343,10 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
|||||||
__CLASS__.'.ACCESSONLYTHESE',
|
__CLASS__.'.ACCESSONLYTHESE',
|
||||||
"Only these groups (choose from list)"
|
"Only these groups (choose from list)"
|
||||||
),
|
),
|
||||||
|
InheritedPermissions::ONLY_THESE_MEMBERS => _t(
|
||||||
|
__CLASS__.'.ACCESSONLYMEMBERS',
|
||||||
|
"Only these users (choose from list)"
|
||||||
|
),
|
||||||
];
|
];
|
||||||
$viewersOptionsField->setSource($viewersOptionsSource);
|
$viewersOptionsField->setSource($viewersOptionsSource);
|
||||||
|
|
||||||
@ -2354,17 +2373,27 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
|||||||
|
|
||||||
if (!Permission::check('SITETREE_GRANT_ACCESS')) {
|
if (!Permission::check('SITETREE_GRANT_ACCESS')) {
|
||||||
$fields->makeFieldReadonly($viewersOptionsField);
|
$fields->makeFieldReadonly($viewersOptionsField);
|
||||||
if ($this->CanEditType === InheritedPermissions::ONLY_THESE_USERS) {
|
if ($this->CanViewType === InheritedPermissions::ONLY_THESE_USERS) {
|
||||||
$fields->makeFieldReadonly($viewerGroupsField);
|
$fields->makeFieldReadonly($viewerGroupsField);
|
||||||
|
$fields->removeByName('ViewerMembers');
|
||||||
|
} elseif ($this->CanViewType === InheritedPermissions::ONLY_THESE_MEMBERS) {
|
||||||
|
$fields->makeFieldReadonly($viewerMembersField);
|
||||||
|
$fields->removeByName('ViewerGroups');
|
||||||
} else {
|
} else {
|
||||||
$fields->removeByName('ViewerGroups');
|
$fields->removeByName('ViewerGroups');
|
||||||
|
$fields->removeByName('ViewerMembers');
|
||||||
}
|
}
|
||||||
|
|
||||||
$fields->makeFieldReadonly($editorsOptionsField);
|
$fields->makeFieldReadonly($editorsOptionsField);
|
||||||
if ($this->CanEditType === InheritedPermissions::ONLY_THESE_USERS) {
|
if ($this->CanEditType === InheritedPermissions::ONLY_THESE_USERS) {
|
||||||
$fields->makeFieldReadonly($editorGroupsField);
|
$fields->makeFieldReadonly($editorGroupsField);
|
||||||
|
$fields->removeByName('EditorMembers');
|
||||||
|
} elseif ($this->CanEditType === InheritedPermissions::ONLY_THESE_MEMBERS) {
|
||||||
|
$fields->makeFieldReadonly($editorMembersField);
|
||||||
|
$fields->removeByName('EditorGroups');
|
||||||
} else {
|
} else {
|
||||||
$fields->removeByName('EditorGroups');
|
$fields->removeByName('EditorGroups');
|
||||||
|
$fields->removeByName('EditorMembers');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2473,7 +2502,6 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
|||||||
// "readonly"/viewing version that isn't the current version of the record
|
// "readonly"/viewing version that isn't the current version of the record
|
||||||
/** @var SiteTree $stageRecord */
|
/** @var SiteTree $stageRecord */
|
||||||
$stageRecord = Versioned::get_by_stage(static::class, Versioned::DRAFT)->byID($this->ID);
|
$stageRecord = Versioned::get_by_stage(static::class, Versioned::DRAFT)->byID($this->ID);
|
||||||
/** @skipUpgrade */
|
|
||||||
if ($stageRecord && $stageRecord->Version != $this->Version) {
|
if ($stageRecord && $stageRecord->Version != $this->Version) {
|
||||||
$moreOptions->push(FormAction::create('email', _t('SilverStripe\\CMS\\Controllers\\CMSMain.EMAIL', 'Email')));
|
$moreOptions->push(FormAction::create('email', _t('SilverStripe\\CMS\\Controllers\\CMSMain.EMAIL', 'Email')));
|
||||||
$moreOptions->push(FormAction::create('rollback', _t('SilverStripe\\CMS\\Controllers\\CMSMain.ROLLBACK', 'Roll back to this version')));
|
$moreOptions->push(FormAction::create('rollback', _t('SilverStripe\\CMS\\Controllers\\CMSMain.ROLLBACK', 'Roll back to this version')));
|
||||||
@ -2518,7 +2546,7 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
|||||||
// Note: It would be nice to have a canRestore() permission at some point
|
// Note: It would be nice to have a canRestore() permission at some point
|
||||||
if ($canEdit && !$isOnDraft && !$isPublished) {
|
if ($canEdit && !$isOnDraft && !$isPublished) {
|
||||||
// Determine if we should force a restore to root (where once it was a subpage)
|
// Determine if we should force a restore to root (where once it was a subpage)
|
||||||
$restoreToRoot = $this->isParentArchived();
|
$restoreToRoot = $this->isParentArchived() && $this->config()->get('can_be_root');
|
||||||
|
|
||||||
// "restore"
|
// "restore"
|
||||||
$title = $restoreToRoot
|
$title = $restoreToRoot
|
||||||
@ -2527,6 +2555,7 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
|||||||
$description = $restoreToRoot
|
$description = $restoreToRoot
|
||||||
? _t('SilverStripe\\CMS\\Controllers\\CMSMain.RESTORE_TO_ROOT_DESC', 'Restore the archived version to draft as a top level page')
|
? _t('SilverStripe\\CMS\\Controllers\\CMSMain.RESTORE_TO_ROOT_DESC', 'Restore the archived version to draft as a top level page')
|
||||||
: _t('SilverStripe\\CMS\\Controllers\\CMSMain.RESTORE_DESC', 'Restore the archived version to draft');
|
: _t('SilverStripe\\CMS\\Controllers\\CMSMain.RESTORE_DESC', 'Restore the archived version to draft');
|
||||||
|
if (!$this->isParentArchived() || $restoreToRoot) {
|
||||||
$majorActions->push(
|
$majorActions->push(
|
||||||
FormAction::create('restore', $title)
|
FormAction::create('restore', $title)
|
||||||
->setDescription($description)
|
->setDescription($description)
|
||||||
@ -2535,6 +2564,7 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
|||||||
->setUseButtonTag(true)
|
->setUseButtonTag(true)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If a page is on any stage it can be archived
|
// If a page is on any stage it can be archived
|
||||||
if (($isOnDraft || $isPublished) && $this->canArchive()) {
|
if (($isOnDraft || $isPublished) && $this->canArchive()) {
|
||||||
@ -2795,35 +2825,6 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
|||||||
return $allowedChildren;
|
return $allowedChildren;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated 4.12.0 Use creatableChildPages() instead
|
|
||||||
*
|
|
||||||
* Gets a list of the page types that can be created under this specific page
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function creatableChildren()
|
|
||||||
{
|
|
||||||
Deprecation::notice('4.12.0', 'Use creatableChildPages() instead');
|
|
||||||
// Build the list of candidate children
|
|
||||||
$cache = SiteTree::singleton()->getCreatableChildrenCache();
|
|
||||||
$cacheKey = $this->generateChildrenCacheKey(Security::getCurrentUser() ? Security::getCurrentUser()->ID : 0);
|
|
||||||
$children = $cache->get($cacheKey, []);
|
|
||||||
if (!$children || !isset($children[$this->ID])) {
|
|
||||||
$children[$this->ID] = [];
|
|
||||||
$candidates = static::page_type_classes();
|
|
||||||
foreach ($candidates as $childClass) {
|
|
||||||
$child = singleton($childClass);
|
|
||||||
if ($child->canCreate(null, ['Parent' => $this])) {
|
|
||||||
$children[$this->ID][$childClass] = $child->i18n_singular_name();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$cache->set($cacheKey, $children);
|
|
||||||
}
|
|
||||||
|
|
||||||
return $children[$this->ID];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Gets a list of the page types that can be created under this specific page, including font icons
|
* Gets a list of the page types that can be created under this specific page, including font icons
|
||||||
@ -3072,14 +3073,6 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
|||||||
// If we have a class of "{$ClassName}Controller" then we found our controller
|
// If we have a class of "{$ClassName}Controller" then we found our controller
|
||||||
if (class_exists($candidate = sprintf('%sController', $class))) {
|
if (class_exists($candidate = sprintf('%sController', $class))) {
|
||||||
return $candidate;
|
return $candidate;
|
||||||
} elseif (class_exists($candidate = sprintf('%s_Controller', $class))) {
|
|
||||||
// Support the legacy underscored filename, but raise a deprecation notice
|
|
||||||
Deprecation::notice(
|
|
||||||
'5.0',
|
|
||||||
'Underscored controller class names are deprecated. Use "MyController" instead of "My_Controller".',
|
|
||||||
Deprecation::SCOPE_GLOBAL
|
|
||||||
);
|
|
||||||
return $candidate;
|
|
||||||
} elseif (is_array($namespaceMap)) {
|
} elseif (is_array($namespaceMap)) {
|
||||||
foreach ($namespaceMap as $pageNamespace => $controllerNamespace) {
|
foreach ($namespaceMap as $pageNamespace => $controllerNamespace) {
|
||||||
if (strpos($class, $pageNamespace) !== 0) {
|
if (strpos($class, $pageNamespace) !== 0) {
|
||||||
@ -3332,7 +3325,6 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
|||||||
$this->flushCache();
|
$this->flushCache();
|
||||||
|
|
||||||
// Need to mark pages depending to this one as broken
|
// Need to mark pages depending to this one as broken
|
||||||
/** @var Page $page */
|
|
||||||
foreach ($this->DependentPages() as $page) {
|
foreach ($this->DependentPages() as $page) {
|
||||||
// Update sync link tracking
|
// Update sync link tracking
|
||||||
$page->syncLinkTracking();
|
$page->syncLinkTracking();
|
||||||
@ -3343,7 +3335,7 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cache key for creatableChildren() method
|
* Cache key for creatableChildPages() method
|
||||||
*
|
*
|
||||||
* @param int $memberID
|
* @param int $memberID
|
||||||
* @return string
|
* @return string
|
||||||
|
@ -7,6 +7,9 @@ use SilverStripe\Security\Member;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Plug-ins for additional functionality in your SiteTree classes.
|
* Plug-ins for additional functionality in your SiteTree classes.
|
||||||
|
*
|
||||||
|
* @template T of SiteTree
|
||||||
|
* @extends DataExtension<T>
|
||||||
*/
|
*/
|
||||||
abstract class SiteTreeExtension extends DataExtension
|
abstract class SiteTreeExtension extends DataExtension
|
||||||
{
|
{
|
||||||
@ -75,13 +78,15 @@ abstract class SiteTreeExtension extends DataExtension
|
|||||||
* before {@link SiteTree::RelativeLink()} calls {@link Controller::join_links()}
|
* before {@link SiteTree::RelativeLink()} calls {@link Controller::join_links()}
|
||||||
* on the $base and $action
|
* on the $base and $action
|
||||||
*
|
*
|
||||||
* @param string &$base The URL of this page relative to siteroot, not including
|
* @param string &$link The URL of this page relative to siteroot including
|
||||||
* the action
|
* the action
|
||||||
* @param string|boolean &$action The action or subpage called on this page.
|
* @param string $base The URL of this page relative to siteroot, not including
|
||||||
|
* the action
|
||||||
|
* @param string|boolean $action The action or subpage called on this page.
|
||||||
* (Legacy support) If this is true, then do not reduce the 'home' urlsegment
|
* (Legacy support) If this is true, then do not reduce the 'home' urlsegment
|
||||||
* to an empty link
|
* to an empty link
|
||||||
*/
|
*/
|
||||||
public function updateRelativeLink(&$base, &$action)
|
public function updateRelativeLink(&$link, $base, $action)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace SilverStripe\CMS\Model;
|
|
||||||
|
|
||||||
use SilverStripe\Dev\Deprecation;
|
|
||||||
use SilverStripe\Assets\File;
|
|
||||||
use SilverStripe\ORM\DataExtension;
|
|
||||||
use SilverStripe\View\SSViewer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated 4.2.0 Use FileLinkTracking instead
|
|
||||||
* @property File $owner
|
|
||||||
*/
|
|
||||||
class SiteTreeFileExtension extends DataExtension
|
|
||||||
{
|
|
||||||
private static $casting = [
|
|
||||||
'BackLinkHTMLList' => 'HTMLFragment'
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate an HTML list which provides links to where a file is used.
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
Deprecation::notice('4.2.0', 'Use FileLinkTracking instead', Deprecation::SCOPE_CLASS);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function BackLinkHTMLList()
|
|
||||||
{
|
|
||||||
$viewer = SSViewer::create(['type' => 'Includes', self::class . '_description']);
|
|
||||||
return $viewer->process($this->owner);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace SilverStripe\CMS\Model;
|
|
||||||
|
|
||||||
use SilverStripe\Assets\File;
|
|
||||||
use SilverStripe\Dev\Deprecation;
|
|
||||||
use SilverStripe\Forms\FieldList;
|
|
||||||
use SilverStripe\Forms\Tab;
|
|
||||||
use SilverStripe\Forms\TabSet;
|
|
||||||
use SilverStripe\ORM\DataExtension;
|
|
||||||
use SilverStripe\Admin\Forms\UsedOnTable;
|
|
||||||
use SilverStripe\Versioned\RecursivePublishable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated 4.12.0 Use UsedOnTable instead
|
|
||||||
*
|
|
||||||
* Extension applied to {@see FileFormFactory} to decorate with a "Used on:" information area.
|
|
||||||
* Uses tracking provided by {@see SiteTreeFileExtension} to generate this.
|
|
||||||
*
|
|
||||||
* @property File $owner
|
|
||||||
* @deprecated 4.12.0 Use UsedOnTable instead
|
|
||||||
*/
|
|
||||||
class SiteTreeFileFormFactoryExtension extends DataExtension
|
|
||||||
{
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
Deprecation::notice('4.12.0', 'Use UsedOnTable instead', Deprecation::SCOPE_CLASS);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated 4.12.0 Use UsedOnTable instead
|
|
||||||
*/
|
|
||||||
public function updateFormFields(FieldList $fields, $controller, $formName, $context)
|
|
||||||
{
|
|
||||||
Deprecation::notice('4.12.0', 'Use UsedOnTable instead');
|
|
||||||
/** @var TabSet $tabset */
|
|
||||||
$tabset = $fields->fieldByName('Editor');
|
|
||||||
if (!$tabset) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$usedOnField = UsedOnTable::create('UsedOnTableReplacement');
|
|
||||||
$usedOnField->setRecord($context['Record']);
|
|
||||||
|
|
||||||
// Add field to new tab
|
|
||||||
/** @var Tab $tab */
|
|
||||||
$tab = Tab::create('Usage', _t(__CLASS__ . '.USAGE', 'Usage'), $usedOnField);
|
|
||||||
$tabset->push($tab);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,88 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace SilverStripe\CMS\Model;
|
|
||||||
|
|
||||||
use SilverStripe\Assets\File;
|
|
||||||
use SilverStripe\Assets\Folder;
|
|
||||||
use SilverStripe\Assets\Shortcodes\FileLink;
|
|
||||||
use SilverStripe\Core\ClassInfo;
|
|
||||||
use SilverStripe\Core\Config\Config;
|
|
||||||
use SilverStripe\Core\Convert;
|
|
||||||
use SilverStripe\Dev\Deprecation;
|
|
||||||
use SilverStripe\ORM\ArrayList;
|
|
||||||
use SilverStripe\ORM\DataExtension;
|
|
||||||
use SilverStripe\ORM\DataList;
|
|
||||||
use SilverStripe\ORM\DataObject;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated 4.2.0 Will be removed without equivalent functionality to replace it
|
|
||||||
*/
|
|
||||||
class SiteTreeFolderExtension extends DataExtension
|
|
||||||
{
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
Deprecation::notice('4.2.0', 'Will be removed without equivalent functionality to replace it', Deprecation::SCOPE_CLASS);
|
|
||||||
parent::__construct();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Looks for files used in system and create where clause which contains all ID's of files.
|
|
||||||
*
|
|
||||||
* @deprecated 4.2.0 Will be removed without equivalent functionality to replace it
|
|
||||||
* @returns string where clause which will work as filter.
|
|
||||||
*/
|
|
||||||
public function getUnusedFilesListFilter()
|
|
||||||
{
|
|
||||||
Deprecation::notice('4.2.0', 'Will be removed without equivalent functionality to replace it');
|
|
||||||
|
|
||||||
// Add all records in link tracking
|
|
||||||
$usedFiles = FileLink::get()->column('LinkedID');
|
|
||||||
|
|
||||||
// Get all classes that aren't folder
|
|
||||||
$fileClasses = array_diff_key(
|
|
||||||
ClassInfo::subclassesFor(File::class) ?? [],
|
|
||||||
ClassInfo::subclassesFor(Folder::class)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Search on a class-by-class basis
|
|
||||||
$classes = ClassInfo::subclassesFor(SiteTree::class);
|
|
||||||
|
|
||||||
$schema = DataObject::getSchema();
|
|
||||||
foreach ($classes as $className) {
|
|
||||||
// Build query based on all direct has_ones on this class
|
|
||||||
$hasOnes = Config::inst()->get($className, 'has_one', Config::UNINHERITED);
|
|
||||||
if (empty($hasOnes)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$where = [];
|
|
||||||
$columns = [];
|
|
||||||
foreach ($hasOnes as $relName => $joinClass) {
|
|
||||||
if (in_array($joinClass, $fileClasses ?? [])) {
|
|
||||||
$column = $relName . 'ID';
|
|
||||||
$columns[] = $column;
|
|
||||||
$quotedColumn = $schema->sqlColumnForField($className, $column);
|
|
||||||
$where[] = "{$quotedColumn} > 0";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get all records with any file ID in the searched columns
|
|
||||||
$recordsArray = DataList::create($className)->whereAny($where)->toArray();
|
|
||||||
$records = ArrayList::create($recordsArray);
|
|
||||||
foreach ($columns as $column) {
|
|
||||||
$usedFiles = array_unique(array_merge($usedFiles, $records->column($column)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create filter based on class and id
|
|
||||||
$classFilter = sprintf(
|
|
||||||
"(\"File\".\"ClassName\" IN (%s))",
|
|
||||||
implode(", ", Convert::raw2sql($fileClasses, true))
|
|
||||||
);
|
|
||||||
if ($usedFiles) {
|
|
||||||
return "\"File\".\"ID\" NOT IN (" . implode(', ', $usedFiles) . ") AND $classFilter";
|
|
||||||
} else {
|
|
||||||
return $classFilter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,10 +7,8 @@ use SilverStripe\ORM\DataObject;
|
|||||||
/**
|
/**
|
||||||
* Represents a link between a dataobject parent and a page in a HTML content area
|
* Represents a link between a dataobject parent and a page in a HTML content area
|
||||||
*
|
*
|
||||||
* @method DataObject Parent() Parent object
|
* @method SiteTree Linked()
|
||||||
* @method SiteTree Linked() Page being linked to
|
* @method DataObject Parent()
|
||||||
*
|
|
||||||
* Run `MigrateSiteTreeLinkingTask` to migrate from old table to this.
|
|
||||||
*/
|
*/
|
||||||
class SiteTreeLink extends DataObject
|
class SiteTreeLink extends DataObject
|
||||||
{
|
{
|
||||||
|
@ -27,7 +27,9 @@ use SilverStripe\View\Parsers\HTMLValue;
|
|||||||
* field to your `db` config and this extension will ensure it's flagged appropriately.
|
* field to your `db` config and this extension will ensure it's flagged appropriately.
|
||||||
*
|
*
|
||||||
* @property DataObject|SiteTreeLinkTracking $owner
|
* @property DataObject|SiteTreeLinkTracking $owner
|
||||||
* @method ManyManyThroughList LinkTracking() List of site pages linked on this dataobject
|
* @method ManyManyThroughList<SiteTree> LinkTracking()
|
||||||
|
*
|
||||||
|
* @extends DataExtension<DataObject>
|
||||||
*/
|
*/
|
||||||
class SiteTreeLinkTracking extends DataExtension
|
class SiteTreeLinkTracking extends DataExtension
|
||||||
{
|
{
|
||||||
|
@ -61,7 +61,6 @@ class SiteTreeLinkTracking_Parser
|
|||||||
$matches = [];
|
$matches = [];
|
||||||
if (preg_match('/\[sitetree_link(?:\s*|%20|,)?id=(?<id>[0-9]+)\](#(?<anchor>.*))?/i', $href ?? '', $matches)) {
|
if (preg_match('/\[sitetree_link(?:\s*|%20|,)?id=(?<id>[0-9]+)\](#(?<anchor>.*))?/i', $href ?? '', $matches)) {
|
||||||
// Check if page link is broken
|
// Check if page link is broken
|
||||||
/** @var SiteTree $page */
|
|
||||||
$page = DataObject::get_by_id(SiteTree::class, $matches['id']);
|
$page = DataObject::get_by_id(SiteTree::class, $matches['id']);
|
||||||
if (!$page) {
|
if (!$page) {
|
||||||
// Page doesn't exist.
|
// Page doesn't exist.
|
||||||
|
@ -4,7 +4,6 @@ namespace SilverStripe\CMS\Model;
|
|||||||
|
|
||||||
use Page;
|
use Page;
|
||||||
use SilverStripe\Core\Convert;
|
use SilverStripe\Core\Convert;
|
||||||
use SilverStripe\Dev\Deprecation;
|
|
||||||
use SilverStripe\Forms\FieldList;
|
use SilverStripe\Forms\FieldList;
|
||||||
use SilverStripe\Forms\LiteralField;
|
use SilverStripe\Forms\LiteralField;
|
||||||
use SilverStripe\Forms\ReadonlyTransformation;
|
use SilverStripe\Forms\ReadonlyTransformation;
|
||||||
@ -22,8 +21,8 @@ use SilverStripe\View\HTML;
|
|||||||
*
|
*
|
||||||
* Note: This Only duplicates $db fields and not the $has_one etc..
|
* Note: This Only duplicates $db fields and not the $has_one etc..
|
||||||
*
|
*
|
||||||
* @method SiteTree CopyContentFrom()
|
|
||||||
* @property int $CopyContentFromID
|
* @property int $CopyContentFromID
|
||||||
|
* @method SiteTree CopyContentFrom()
|
||||||
*/
|
*/
|
||||||
class VirtualPage extends Page
|
class VirtualPage extends Page
|
||||||
{
|
{
|
||||||
@ -360,30 +359,6 @@ class VirtualPage extends Page
|
|||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated 4.2.0 Will be removed without equivalent functionality to replace it
|
|
||||||
*/
|
|
||||||
public function updateImageTracking()
|
|
||||||
{
|
|
||||||
Deprecation::notice('4.2.0', 'Will be removed without equivalent functionality to replace it');
|
|
||||||
|
|
||||||
// Doesn't work on unsaved records
|
|
||||||
if (!$this->isInDB()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove CopyContentFrom() from the cache
|
|
||||||
unset($this->components['CopyContentFrom']);
|
|
||||||
|
|
||||||
// Update ImageTracking
|
|
||||||
$copyContentFrom = $this->CopyContentFrom();
|
|
||||||
if (!$copyContentFrom || !$copyContentFrom->isInDB()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->FileTracking()->setByIDList($copyContentFrom->FileTracking()->column('ID'));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function CMSTreeClasses()
|
public function CMSTreeClasses()
|
||||||
{
|
{
|
||||||
$parentClass = sprintf(
|
$parentClass = sprintf(
|
||||||
|
@ -46,7 +46,7 @@ class BrokenFilesReport extends Report
|
|||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
"Title" => [
|
"Title" => [
|
||||||
"title" => "Title", // todo: use NestedTitle(2)
|
"title" => "Title",
|
||||||
"link" => true,
|
"link" => true,
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
@ -39,7 +39,7 @@ class BrokenRedirectorPagesReport extends Report
|
|||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
"Title" => [
|
"Title" => [
|
||||||
"title" => "Title", // todo: use NestedTitle(2)
|
"title" => "Title",
|
||||||
"link" => true,
|
"link" => true,
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
@ -39,7 +39,7 @@ class BrokenVirtualPagesReport extends Report
|
|||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
"Title" => [
|
"Title" => [
|
||||||
"title" => "Title", // todo: use NestedTitle(2)
|
"title" => "Title",
|
||||||
"link" => true,
|
"link" => true,
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
@ -9,7 +9,6 @@ use SilverStripe\Reports\Report;
|
|||||||
|
|
||||||
class EmptyPagesReport extends Report
|
class EmptyPagesReport extends Report
|
||||||
{
|
{
|
||||||
|
|
||||||
public function title()
|
public function title()
|
||||||
{
|
{
|
||||||
return _t(__CLASS__.'.EMPTYPAGES', "Pages without content");
|
return _t(__CLASS__.'.EMPTYPAGES', "Pages without content");
|
||||||
@ -29,7 +28,7 @@ class EmptyPagesReport extends Report
|
|||||||
* Gets the source records
|
* Gets the source records
|
||||||
*
|
*
|
||||||
* @param array $params
|
* @param array $params
|
||||||
* @return DataList
|
* @return DataList<SiteTree>
|
||||||
*/
|
*/
|
||||||
public function sourceRecords($params = null)
|
public function sourceRecords($params = null)
|
||||||
{
|
{
|
||||||
@ -43,7 +42,7 @@ class EmptyPagesReport extends Report
|
|||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
"Title" => [
|
"Title" => [
|
||||||
"title" => "Title", // todo: use NestedTitle(2)
|
"title" => "Title",
|
||||||
"link" => true,
|
"link" => true,
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
@ -31,14 +31,14 @@ class RecentlyEditedReport extends Report
|
|||||||
$threshold = strtotime('-14 days', DBDatetime::now()->getTimestamp());
|
$threshold = strtotime('-14 days', DBDatetime::now()->getTimestamp());
|
||||||
return SiteTree::get()
|
return SiteTree::get()
|
||||||
->filter('LastEdited:GreaterThan', date("Y-m-d H:i:s", $threshold))
|
->filter('LastEdited:GreaterThan', date("Y-m-d H:i:s", $threshold))
|
||||||
->sort("\"$tableName\".\"LastEdited\" DESC");
|
->orderBy("\"$tableName\".\"LastEdited\" DESC");
|
||||||
}
|
}
|
||||||
|
|
||||||
public function columns()
|
public function columns()
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
"Title" => [
|
"Title" => [
|
||||||
"title" => "Title", // todo: use NestedTitle(2)
|
"title" => "Title",
|
||||||
"link" => true,
|
"link" => true,
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace SilverStripe\CMS\Search;
|
namespace SilverStripe\CMS\Search;
|
||||||
|
|
||||||
|
use SilverStripe\CMS\Controllers\ContentController;
|
||||||
use SilverStripe\Control\HTTPRequest;
|
use SilverStripe\Control\HTTPRequest;
|
||||||
use SilverStripe\Core\Extension;
|
use SilverStripe\Core\Extension;
|
||||||
use SilverStripe\Forms\TextField;
|
use SilverStripe\Forms\TextField;
|
||||||
@ -12,6 +13,8 @@ use SilverStripe\ORM\Search\FulltextSearchable;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Extension to provide a search interface when applied to ContentController
|
* Extension to provide a search interface when applied to ContentController
|
||||||
|
*
|
||||||
|
* @extends Extension<ContentController>
|
||||||
*/
|
*/
|
||||||
class ContentControllerSearchExtension extends Extension
|
class ContentControllerSearchExtension extends Extension
|
||||||
{
|
{
|
||||||
@ -39,7 +42,6 @@ class ContentControllerSearchExtension extends Extension
|
|||||||
$actions = FieldList::create(
|
$actions = FieldList::create(
|
||||||
FormAction::create('results', _t('SilverStripe\\CMS\\Search\\SearchForm.GO', 'Go'))
|
FormAction::create('results', _t('SilverStripe\\CMS\\Search\\SearchForm.GO', 'Go'))
|
||||||
);
|
);
|
||||||
/** @skipUpgrade */
|
|
||||||
$form = SearchForm::create($this->owner, 'SearchForm', $fields, $actions);
|
$form = SearchForm::create($this->owner, 'SearchForm', $fields, $actions);
|
||||||
$form->classesToSearch(FulltextSearchable::get_searchable_classes());
|
$form->classesToSearch(FulltextSearchable::get_searchable_classes());
|
||||||
return $form;
|
return $form;
|
||||||
|
@ -13,17 +13,11 @@ use SilverStripe\Forms\HiddenField;
|
|||||||
use SilverStripe\Forms\TextField;
|
use SilverStripe\Forms\TextField;
|
||||||
use SilverStripe\ORM\DB;
|
use SilverStripe\ORM\DB;
|
||||||
use SilverStripe\ORM\SS_List;
|
use SilverStripe\ORM\SS_List;
|
||||||
use Translatable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Standard basic search form which conducts a fulltext search on all {@link SiteTree}
|
* Standard basic search form which conducts a fulltext search on all {@link SiteTree}
|
||||||
* objects.
|
* objects.
|
||||||
*
|
*
|
||||||
* If multilingual content is enabled through the {@link Translatable} extension,
|
|
||||||
* only pages the currently set language on the holder for this searchform are found.
|
|
||||||
* The language is set through a hidden field in the form, which is prepoluated
|
|
||||||
* with {@link Translatable::get_current_locale()} when then form is constructed.
|
|
||||||
*
|
|
||||||
* @see Use ModelController and SearchContext for a more generic search implementation based around DataObject
|
* @see Use ModelController and SearchContext for a more generic search implementation based around DataObject
|
||||||
*/
|
*/
|
||||||
class SearchForm extends Form
|
class SearchForm extends Form
|
||||||
@ -51,7 +45,6 @@ class SearchForm extends Form
|
|||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @skipUpgrade
|
|
||||||
* @param RequestHandler $controller
|
* @param RequestHandler $controller
|
||||||
* @param string $name The name of the form (used in URL addressing)
|
* @param string $name The name of the form (used in URL addressing)
|
||||||
* @param FieldList $fields Optional, defaults to a single field named "Search". Search logic needs to be customized
|
* @param FieldList $fields Optional, defaults to a single field named "Search". Search logic needs to be customized
|
||||||
@ -70,12 +63,6 @@ class SearchForm extends Form
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (class_exists('Translatable')
|
|
||||||
&& SiteTree::singleton()->hasExtension('Translatable')
|
|
||||||
) {
|
|
||||||
$fields->push(new HiddenField('searchlocale', 'searchlocale', Translatable::get_current_locale()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$actions) {
|
if (!$actions) {
|
||||||
$actions = new FieldList(
|
$actions = new FieldList(
|
||||||
new FormAction("results", _t(__CLASS__.'.GO', 'Go'))
|
new FormAction("results", _t(__CLASS__.'.GO', 'Go'))
|
||||||
@ -130,22 +117,6 @@ class SearchForm extends Form
|
|||||||
// Get request data from request handler
|
// Get request data from request handler
|
||||||
$request = $this->getRequestHandler()->getRequest();
|
$request = $this->getRequestHandler()->getRequest();
|
||||||
|
|
||||||
// set language (if present)
|
|
||||||
$locale = null;
|
|
||||||
$origLocale = null;
|
|
||||||
if (class_exists('Translatable')) {
|
|
||||||
$locale = $request->requestVar('searchlocale');
|
|
||||||
if (SiteTree::singleton()->hasExtension('Translatable') && $locale) {
|
|
||||||
if ($locale === "ALL") {
|
|
||||||
Translatable::disable_locale_filter();
|
|
||||||
} else {
|
|
||||||
$origLocale = Translatable::get_current_locale();
|
|
||||||
|
|
||||||
Translatable::set_current_locale($locale);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$keywords = $request->requestVar('Search');
|
$keywords = $request->requestVar('Search');
|
||||||
|
|
||||||
$andProcessor = function ($matches) {
|
$andProcessor = function ($matches) {
|
||||||
@ -181,17 +152,6 @@ class SearchForm extends Form
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// reset locale
|
|
||||||
if (class_exists('Translatable')) {
|
|
||||||
if (SiteTree::singleton()->hasExtension('Translatable') && $locale) {
|
|
||||||
if ($locale == "ALL") {
|
|
||||||
Translatable::enable_locale_filter();
|
|
||||||
} else {
|
|
||||||
Translatable::set_current_locale($origLocale);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $results;
|
return $results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,70 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace SilverStripe\CMS\Tasks;
|
|
||||||
|
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
|
||||||
use SilverStripe\Dev\BuildTask;
|
|
||||||
use SilverStripe\Dev\Debug;
|
|
||||||
use SilverStripe\Dev\Deprecation;
|
|
||||||
use SilverStripe\ORM\DataObject;
|
|
||||||
use SilverStripe\ORM\DB;
|
|
||||||
use SilverStripe\Versioned\Versioned;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates legacy SiteTree link tracking into new polymorphic many_many relation.
|
|
||||||
* This should be done for any site upgrading to 4.2.0
|
|
||||||
*
|
|
||||||
* @deprecated 4.13.0 Will be removed without equivalent functionality to replace it
|
|
||||||
*/
|
|
||||||
class MigrateSiteTreeLinkingTask extends BuildTask
|
|
||||||
{
|
|
||||||
private static $segment = 'MigrateSiteTreeLinkingTask';
|
|
||||||
|
|
||||||
protected $title = 'Migrate SiteTree Linking Task';
|
|
||||||
|
|
||||||
protected $description = 'Updates legacy SiteTree link tracking into new polymorphic many_many relation';
|
|
||||||
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
Deprecation::notice(
|
|
||||||
'4.13.0',
|
|
||||||
'Will be removed without equivalent functionality to replace it',
|
|
||||||
Deprecation::SCOPE_CLASS
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function run($request)
|
|
||||||
{
|
|
||||||
// Ensure legacy table exists
|
|
||||||
$exists = DB::get_conn()->getSchemaManager()->hasTable('SiteTree_LinkTracking');
|
|
||||||
if (!$exists) {
|
|
||||||
DB::alteration_message("Table SiteTree_LinkTracking has already been migrated, or doesn't exist");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$pages = 0;
|
|
||||||
|
|
||||||
// Ensure sync occurs on draft
|
|
||||||
Versioned::withVersionedMode(function () use (&$pages) {
|
|
||||||
Versioned::set_stage(Versioned::DRAFT);
|
|
||||||
|
|
||||||
$sitetreeTbl = DataObject::singleton(SiteTree::class)->baseTable();
|
|
||||||
|
|
||||||
/** @var SiteTree[] $linkedPages */
|
|
||||||
$linkedPages = SiteTree::get()
|
|
||||||
->innerJoin(
|
|
||||||
'SiteTree_LinkTracking',
|
|
||||||
"\"SiteTree_LinkTracking\".\"SiteTreeID\" = \"$sitetreeTbl\".\"ID\""
|
|
||||||
);
|
|
||||||
foreach ($linkedPages as $page) {
|
|
||||||
// Command page to update symlink tracking
|
|
||||||
$page->syncLinkTracking();
|
|
||||||
$pages++;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
DB::alteration_message("Migrated page links on " . SiteTree::singleton()->i18n_pluralise($pages));
|
|
||||||
|
|
||||||
// Disable table to prevent double-migration
|
|
||||||
DB::dont_require_table('SiteTree_LinkTracking');
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,388 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace SilverStripe\CMS\Tasks;
|
|
||||||
|
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
|
||||||
use SilverStripe\Control\Controller;
|
|
||||||
use SilverStripe\Forms\CheckboxSetField;
|
|
||||||
use SilverStripe\Forms\FieldList;
|
|
||||||
use SilverStripe\Forms\Form;
|
|
||||||
use SilverStripe\Forms\FormAction;
|
|
||||||
use SilverStripe\Forms\HeaderField;
|
|
||||||
use SilverStripe\Forms\LiteralField;
|
|
||||||
use SilverStripe\Forms\OptionsetField;
|
|
||||||
use SilverStripe\ORM\ArrayList;
|
|
||||||
use SilverStripe\ORM\DataObject;
|
|
||||||
use SilverStripe\ORM\SS_List;
|
|
||||||
use SilverStripe\Versioned\Versioned;
|
|
||||||
use SilverStripe\Security\Permission;
|
|
||||||
use SilverStripe\Security\Security;
|
|
||||||
use SilverStripe\View\Requirements;
|
|
||||||
use SilverStripe\Dev\Deprecation;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Identify "orphaned" pages which point to a parent
|
|
||||||
* that no longer exists in a specific stage.
|
|
||||||
* Shows the pages to an administrator, who can then
|
|
||||||
* decide which pages to remove by ticking a checkbox
|
|
||||||
* and manually executing the removal.
|
|
||||||
*
|
|
||||||
* Caution: Pages also count as orphans if they don't
|
|
||||||
* have parents in this stage, even if the parent has a representation
|
|
||||||
* in the other stage:
|
|
||||||
* - A live child is orphaned if its parent was deleted from live, but still exists on stage
|
|
||||||
* - A stage child is orphaned if its parent was deleted from stage, but still exists on live
|
|
||||||
*
|
|
||||||
* See {@link RemoveOrphanedPagesTaskTest} for an example sitetree
|
|
||||||
* before and after orphan removal.
|
|
||||||
*
|
|
||||||
* @author Ingo Schommer (<firstname>@silverstripe.com), SilverStripe Ltd.
|
|
||||||
*
|
|
||||||
* @deprecated 4.13.0 Will be removed without equivalent functionality to replace it
|
|
||||||
*/
|
|
||||||
class RemoveOrphanedPagesTask extends Controller
|
|
||||||
{
|
|
||||||
|
|
||||||
private static $allowed_actions = [
|
|
||||||
'index' => 'ADMIN',
|
|
||||||
'Form' => 'ADMIN',
|
|
||||||
'run' => 'ADMIN',
|
|
||||||
'handleAction' => 'ADMIN',
|
|
||||||
];
|
|
||||||
|
|
||||||
protected $title = 'Removed orphaned pages without existing parents from both stage and live';
|
|
||||||
|
|
||||||
protected $description = "
|
|
||||||
<p>
|
|
||||||
Identify 'orphaned' pages which point to a parent
|
|
||||||
that no longer exists in a specific stage.
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
Caution: Pages also count as orphans if they don't
|
|
||||||
have parents in this stage, even if the parent has a representation
|
|
||||||
in the other stage:<br />
|
|
||||||
- A live child is orphaned if its parent was deleted from live, but still exists on stage<br />
|
|
||||||
- A stage child is orphaned if its parent was deleted from stage, but still exists on live
|
|
||||||
</p>
|
|
||||||
";
|
|
||||||
|
|
||||||
protected $orphanedSearchClass = SiteTree::class;
|
|
||||||
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
Deprecation::notice(
|
|
||||||
'4.13.0',
|
|
||||||
'Will be removed without equivalent functionality to replace it',
|
|
||||||
Deprecation::SCOPE_CLASS
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function init()
|
|
||||||
{
|
|
||||||
parent::init();
|
|
||||||
|
|
||||||
if (!Permission::check('ADMIN')) {
|
|
||||||
Security::permissionFailure($this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function Link($action = null)
|
|
||||||
{
|
|
||||||
/** @skipUpgrade */
|
|
||||||
return Controller::join_links('RemoveOrphanedPagesTask', $action, '/');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function index()
|
|
||||||
{
|
|
||||||
Requirements::javascript('http://code.jquery.com/jquery-1.7.2.min.js');
|
|
||||||
Requirements::customCSS('#OrphanIDs .middleColumn {width: auto;}');
|
|
||||||
Requirements::customCSS('#OrphanIDs label {display: inline;}');
|
|
||||||
|
|
||||||
return $this->renderWith('BlankPage');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function Form()
|
|
||||||
{
|
|
||||||
$fields = new FieldList();
|
|
||||||
$source = [];
|
|
||||||
|
|
||||||
$fields->push(new HeaderField(
|
|
||||||
'Header',
|
|
||||||
_t(__CLASS__ . '.HEADER', 'Remove all orphaned pages task')
|
|
||||||
));
|
|
||||||
$fields->push(new LiteralField(
|
|
||||||
'Description',
|
|
||||||
$this->description
|
|
||||||
));
|
|
||||||
|
|
||||||
$orphans = $this->getOrphanedPages($this->orphanedSearchClass);
|
|
||||||
if ($orphans) {
|
|
||||||
foreach ($orphans as $orphan) {
|
|
||||||
/** @var SiteTree $latestVersion */
|
|
||||||
$latestVersion = Versioned::get_latest_version($this->orphanedSearchClass, $orphan->ID);
|
|
||||||
$latestAuthor = DataObject::get_by_id('SilverStripe\\Security\\Member', $latestVersion->AuthorID);
|
|
||||||
$orphanBaseTable = DataObject::getSchema()->baseDataTable($this->orphanedSearchClass);
|
|
||||||
$liveRecord = Versioned::get_one_by_stage(
|
|
||||||
$this->orphanedSearchClass,
|
|
||||||
'Live',
|
|
||||||
["\"$orphanBaseTable\".\"ID\"" => $orphan->ID]
|
|
||||||
);
|
|
||||||
$label = sprintf(
|
|
||||||
'<a href="admin/pages/edit/show/%d">%s</a> <small>(#%d, Last Modified Date: %s, Last Modifier: %s, %s)</small>',
|
|
||||||
$orphan->ID,
|
|
||||||
$orphan->Title,
|
|
||||||
$orphan->ID,
|
|
||||||
$orphan->dbObject('LastEdited')->Nice(),
|
|
||||||
($latestAuthor) ? $latestAuthor->Title : 'unknown',
|
|
||||||
($liveRecord) ? 'is published' : 'not published'
|
|
||||||
);
|
|
||||||
$source[$orphan->ID] = $label;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($orphans && $orphans->count()) {
|
|
||||||
$fields->push(new CheckboxSetField('OrphanIDs', false, $source));
|
|
||||||
$fields->push(new LiteralField(
|
|
||||||
'SelectAllLiteral',
|
|
||||||
sprintf(
|
|
||||||
'<p><a href="#" onclick="javascript:jQuery(\'#Form_Form_OrphanIDs :checkbox\').attr(\'checked\', \'checked\'); return false;">%s</a> ',
|
|
||||||
_t(__CLASS__ . '.SELECTALL', 'select all')
|
|
||||||
)
|
|
||||||
));
|
|
||||||
$fields->push(new LiteralField(
|
|
||||||
'UnselectAllLiteral',
|
|
||||||
sprintf(
|
|
||||||
'<a href="#" onclick="javascript:jQuery(\'#Form_Form_OrphanIDs :checkbox\').attr(\'checked\', \'\'); return false;">%s</a></p>',
|
|
||||||
_t(__CLASS__ . '.UNSELECTALL', 'unselect all')
|
|
||||||
)
|
|
||||||
));
|
|
||||||
$fields->push(new OptionsetField(
|
|
||||||
'OrphanOperation',
|
|
||||||
_t('SilverStripe\\CMS\\Tasks\\RemoveOrphanedPagesTask.CHOOSEOPERATION', 'Choose operation:'),
|
|
||||||
[
|
|
||||||
'rebase' => _t(
|
|
||||||
__CLASS__ . '.OPERATION_REBASE',
|
|
||||||
sprintf(
|
|
||||||
'Rebase selected to a new holder page "%s" and unpublish. None of these pages will show up for website visitors.',
|
|
||||||
$this->rebaseHolderTitle()
|
|
||||||
)
|
|
||||||
),
|
|
||||||
'remove' => _t(__CLASS__ . '.OPERATION_REMOVE', 'Remove selected from all stages (WARNING: Will destroy all selected pages from both stage and live)'),
|
|
||||||
],
|
|
||||||
'rebase'
|
|
||||||
));
|
|
||||||
$fields->push(new LiteralField(
|
|
||||||
'Warning',
|
|
||||||
sprintf(
|
|
||||||
'<p class="message">%s</p>',
|
|
||||||
_t(
|
|
||||||
__CLASS__ . '.DELETEWARNING',
|
|
||||||
'Warning: These operations are not reversible. Please handle with care.'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
$fields->push(new LiteralField(
|
|
||||||
'NotFoundLabel',
|
|
||||||
sprintf(
|
|
||||||
'<p class="message">%s</p>',
|
|
||||||
_t(__CLASS__ . '.NONEFOUND', 'No orphans found')
|
|
||||||
)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
$form = new Form(
|
|
||||||
$this,
|
|
||||||
'SilverStripe\\Forms\\Form',
|
|
||||||
$fields,
|
|
||||||
new FieldList(
|
|
||||||
new FormAction('doSubmit', _t(__CLASS__ . '.BUTTONRUN', 'Run'))
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!$orphans || !$orphans->count()) {
|
|
||||||
$form->makeReadonly();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $form;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function run($request)
|
|
||||||
{
|
|
||||||
// @todo Merge with BuildTask functionality
|
|
||||||
}
|
|
||||||
|
|
||||||
public function doSubmit($data, $form)
|
|
||||||
{
|
|
||||||
set_time_limit(60*10); // 10 minutes
|
|
||||||
|
|
||||||
if (!isset($data['OrphanIDs']) || !isset($data['OrphanOperation'])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$successIDs = null;
|
|
||||||
switch ($data['OrphanOperation']) {
|
|
||||||
case 'remove':
|
|
||||||
$successIDs = $this->removeOrphans($data['OrphanIDs']);
|
|
||||||
break;
|
|
||||||
case 'rebase':
|
|
||||||
$successIDs = $this->rebaseOrphans($data['OrphanIDs']);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new \InvalidArgumentException(sprintf("Unknown operation: '%s'", $data['OrphanOperation']));
|
|
||||||
}
|
|
||||||
|
|
||||||
$content = '';
|
|
||||||
if ($successIDs) {
|
|
||||||
$content .= "<ul>";
|
|
||||||
foreach ($successIDs as $id => $label) {
|
|
||||||
$content .= sprintf('<li>%s</li>', $label);
|
|
||||||
}
|
|
||||||
$content .= "</ul>";
|
|
||||||
} else {
|
|
||||||
$content = _t(__CLASS__ . '.NONEREMOVED', 'None removed');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->customise([
|
|
||||||
'Content' => $content,
|
|
||||||
'Form' => ' '
|
|
||||||
])->renderWith('BlankPage');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function removeOrphans($orphanIDs)
|
|
||||||
{
|
|
||||||
$removedOrphans = [];
|
|
||||||
$orphanBaseTable = DataObject::getSchema()->baseDataTable($this->orphanedSearchClass);
|
|
||||||
foreach ($orphanIDs as $id) {
|
|
||||||
/** @var SiteTree $stageRecord */
|
|
||||||
$stageRecord = Versioned::get_one_by_stage(
|
|
||||||
$this->orphanedSearchClass,
|
|
||||||
Versioned::DRAFT,
|
|
||||||
["\"$orphanBaseTable\".\"ID\"" => $id]
|
|
||||||
);
|
|
||||||
if ($stageRecord) {
|
|
||||||
$removedOrphans[$stageRecord->ID] = sprintf('Removed %s (#%d) from Stage', $stageRecord->Title, $stageRecord->ID);
|
|
||||||
$stageRecord->delete();
|
|
||||||
$stageRecord->destroy();
|
|
||||||
unset($stageRecord);
|
|
||||||
}
|
|
||||||
/** @var SiteTree $liveRecord */
|
|
||||||
$liveRecord = Versioned::get_one_by_stage(
|
|
||||||
$this->orphanedSearchClass,
|
|
||||||
Versioned::LIVE,
|
|
||||||
["\"$orphanBaseTable\".\"ID\"" => $id]
|
|
||||||
);
|
|
||||||
if ($liveRecord) {
|
|
||||||
$removedOrphans[$liveRecord->ID] = sprintf('Removed %s (#%d) from Live', $liveRecord->Title, $liveRecord->ID);
|
|
||||||
$liveRecord->doUnpublish();
|
|
||||||
$liveRecord->destroy();
|
|
||||||
unset($liveRecord);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $removedOrphans;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function rebaseHolderTitle()
|
|
||||||
{
|
|
||||||
return sprintf('Rebased Orphans (%s)', date('d/m/Y g:ia', time()));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function rebaseOrphans($orphanIDs)
|
|
||||||
{
|
|
||||||
$holder = new SiteTree();
|
|
||||||
$holder->ShowInMenus = 0;
|
|
||||||
$holder->ShowInSearch = 0;
|
|
||||||
$holder->ParentID = 0;
|
|
||||||
$holder->Title = $this->rebaseHolderTitle();
|
|
||||||
$holder->write();
|
|
||||||
|
|
||||||
$removedOrphans = [];
|
|
||||||
$orphanBaseTable = DataObject::getSchema()->baseDataTable($this->orphanedSearchClass);
|
|
||||||
foreach ($orphanIDs as $id) {
|
|
||||||
/** @var SiteTree $stageRecord */
|
|
||||||
$stageRecord = Versioned::get_one_by_stage(
|
|
||||||
$this->orphanedSearchClass,
|
|
||||||
'Stage',
|
|
||||||
["\"$orphanBaseTable\".\"ID\"" => $id]
|
|
||||||
);
|
|
||||||
if ($stageRecord) {
|
|
||||||
$removedOrphans[$stageRecord->ID] = sprintf('Rebased %s (#%d)', $stageRecord->Title, $stageRecord->ID);
|
|
||||||
$stageRecord->ParentID = $holder->ID;
|
|
||||||
$stageRecord->ShowInMenus = 0;
|
|
||||||
$stageRecord->ShowInSearch = 0;
|
|
||||||
$stageRecord->write();
|
|
||||||
$stageRecord->doUnpublish();
|
|
||||||
$stageRecord->destroy();
|
|
||||||
//unset($stageRecord);
|
|
||||||
}
|
|
||||||
/** @var SiteTree $liveRecord */
|
|
||||||
$liveRecord = Versioned::get_one_by_stage(
|
|
||||||
$this->orphanedSearchClass,
|
|
||||||
'Live',
|
|
||||||
["\"$orphanBaseTable\".\"ID\"" => $id]
|
|
||||||
);
|
|
||||||
if ($liveRecord) {
|
|
||||||
$removedOrphans[$liveRecord->ID] = sprintf('Rebased %s (#%d)', $liveRecord->Title, $liveRecord->ID);
|
|
||||||
$liveRecord->ParentID = $holder->ID;
|
|
||||||
$liveRecord->ShowInMenus = 0;
|
|
||||||
$liveRecord->ShowInSearch = 0;
|
|
||||||
$liveRecord->write();
|
|
||||||
if (!$stageRecord) {
|
|
||||||
$liveRecord->doRestoreToStage();
|
|
||||||
}
|
|
||||||
$liveRecord->doUnpublish();
|
|
||||||
$liveRecord->destroy();
|
|
||||||
unset($liveRecord);
|
|
||||||
}
|
|
||||||
if ($stageRecord) {
|
|
||||||
unset($stageRecord);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $removedOrphans;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets all orphans from "Stage" and "Live" stages.
|
|
||||||
*
|
|
||||||
* @param string $class
|
|
||||||
* @param array $filter
|
|
||||||
* @param string $sort
|
|
||||||
* @param string $join
|
|
||||||
* @param int|array $limit
|
|
||||||
* @return SS_List
|
|
||||||
*/
|
|
||||||
public function getOrphanedPages($class = SiteTree::class, $filter = [], $sort = null, $join = null, $limit = null)
|
|
||||||
{
|
|
||||||
// Alter condition
|
|
||||||
$table = DataObject::getSchema()->tableName($class);
|
|
||||||
if (empty($filter)) {
|
|
||||||
$where = [];
|
|
||||||
} elseif (is_array($filter)) {
|
|
||||||
$where = $filter;
|
|
||||||
} else {
|
|
||||||
$where = [$filter];
|
|
||||||
}
|
|
||||||
$where[] = ["\"{$table}\".\"ParentID\" != ?" => 0];
|
|
||||||
$where[] = '"Parents"."ID" IS NULL';
|
|
||||||
|
|
||||||
$orphans = new ArrayList();
|
|
||||||
foreach ([Versioned::DRAFT, Versioned::LIVE] as $stage) {
|
|
||||||
$table .= ($stage == Versioned::LIVE) ? '_Live' : '';
|
|
||||||
$stageOrphans = Versioned::get_by_stage(
|
|
||||||
$class,
|
|
||||||
$stage,
|
|
||||||
$where,
|
|
||||||
$sort,
|
|
||||||
null,
|
|
||||||
$limit
|
|
||||||
)->leftJoin($table, "\"$table\".\"ParentID\" = \"Parents\".\"ID\"", "Parents");
|
|
||||||
$orphans->merge($stageOrphans);
|
|
||||||
}
|
|
||||||
|
|
||||||
$orphans->removeDuplicates();
|
|
||||||
|
|
||||||
return $orphans;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace SilverStripe\CMS\Tasks;
|
|
||||||
|
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
|
||||||
use SilverStripe\Control\Controller;
|
|
||||||
use SilverStripe\ORM\DB;
|
|
||||||
use SilverStripe\ORM\DataObject;
|
|
||||||
use SilverStripe\Dev\Deprecation;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated 4.13.0 Will be removed without equivalent functionality to replace it
|
|
||||||
*/
|
|
||||||
class SiteTreeMaintenanceTask extends Controller
|
|
||||||
{
|
|
||||||
private static $allowed_actions = [
|
|
||||||
'*' => 'ADMIN'
|
|
||||||
];
|
|
||||||
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
Deprecation::notice(
|
|
||||||
'4.13.0',
|
|
||||||
'Will be removed without equivalent functionality to replace it',
|
|
||||||
Deprecation::SCOPE_CLASS
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function makelinksunique()
|
|
||||||
{
|
|
||||||
$table = DataObject::singleton(SiteTree::class)->baseTable();
|
|
||||||
$badURLs = "'" . implode("', '", DB::query("SELECT \"URLSegment\", count(*) FROM \"$table\" GROUP BY \"URLSegment\" HAVING count(*) > 1")->column()) . "'";
|
|
||||||
$pages = DataObject::get(SiteTree::class, "\"$table\".\"URLSegment\" IN ($badURLs)");
|
|
||||||
|
|
||||||
foreach ($pages as $page) {
|
|
||||||
echo "<li>$page->Title: ";
|
|
||||||
$urlSegment = $page->URLSegment;
|
|
||||||
$page->write();
|
|
||||||
if ($urlSegment != $page->URLSegment) {
|
|
||||||
echo _t(
|
|
||||||
'SilverStripe\\CMS\\Model\\SiteTree.LINKSCHANGEDTO',
|
|
||||||
" changed {url1} -> {url2}",
|
|
||||||
['url1' => $urlSegment, 'url2' => $page->URLSegment]
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
echo _t(
|
|
||||||
'SilverStripe\\CMS\\Model\\SiteTree.LINKSALREADYUNIQUE',
|
|
||||||
" {url} is already unique",
|
|
||||||
['url' => $urlSegment]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
die();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function Link($action = null)
|
|
||||||
{
|
|
||||||
/** @skipUpgrade */
|
|
||||||
return Controller::join_links('SiteTreeMaintenanceTask', $action, '/');
|
|
||||||
}
|
|
||||||
}
|
|
@ -19,19 +19,21 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"silverstripe/admin": "^1.13.20",
|
"php": "^8.1",
|
||||||
"silverstripe/campaign-admin": "^1.7@dev",
|
"silverstripe/admin": "^2.2",
|
||||||
"silverstripe/framework": "^4.11",
|
"silverstripe/campaign-admin": "^2",
|
||||||
"silverstripe/reports": "^4.7@dev",
|
"silverstripe/framework": "^5.2",
|
||||||
"silverstripe/siteconfig": "^4.7@dev",
|
"silverstripe/reports": "^5",
|
||||||
"silverstripe/versioned": "^1.7@dev",
|
"silverstripe/siteconfig": "^5",
|
||||||
"silverstripe/versioned-admin": "^1.7@dev",
|
"silverstripe/versioned": "^2",
|
||||||
"silverstripe/vendor-plugin": "^1.0",
|
"silverstripe/versioned-admin": "^2",
|
||||||
"php": "^7.4 || ^8.0"
|
"silverstripe/vendor-plugin": "^2"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit": "^9.5",
|
"phpunit/phpunit": "^9.6",
|
||||||
"squizlabs/php_codesniffer": "^3"
|
"squizlabs/php_codesniffer": "^3.7",
|
||||||
|
"silverstripe/standards": "^1",
|
||||||
|
"phpstan/extension-installer": "^1.3"
|
||||||
},
|
},
|
||||||
"extra": {
|
"extra": {
|
||||||
"expose": [
|
"expose": [
|
||||||
|
32
lang/en.yml
32
lang/en.yml
@ -12,10 +12,10 @@ en:
|
|||||||
UNPUBLISHED_PAGES: 'Unpublished %d pages'
|
UNPUBLISHED_PAGES: 'Unpublished %d pages'
|
||||||
UNPUBLISH_PAGES: Unpublish
|
UNPUBLISH_PAGES: Unpublish
|
||||||
SilverStripe\CMS\Controllers\CMSMain:
|
SilverStripe\CMS\Controllers\CMSMain:
|
||||||
ACCESS: 'Access to ''{title}'' section'
|
ACCESS: "Access to '{title}' section"
|
||||||
ACCESS_HELP: 'Allow viewing of the section containing page tree and content. View and edit permissions can be handled through page specific dropdowns, as well as the separate "Content permissions".'
|
ACCESS_HELP: 'Allow viewing of the section containing page tree and content. View and edit permissions can be handled through page specific dropdowns, as well as the separate "Content permissions".'
|
||||||
ARCHIVE: Archive
|
ARCHIVE: Archive
|
||||||
ARCHIVEDPAGE: 'Archived page ''{title}'''
|
ARCHIVEDPAGE: "Archived page '{title}'"
|
||||||
AddNew: 'Add new page'
|
AddNew: 'Add new page'
|
||||||
AddNewButton: 'Add new'
|
AddNewButton: 'Add new'
|
||||||
AddPageRestriction: 'Note: Some page types are not allowed for this selection'
|
AddPageRestriction: 'Note: Some page types are not allowed for this selection'
|
||||||
@ -28,35 +28,35 @@ en:
|
|||||||
ChoosePageParentMode: 'Choose where to create this page'
|
ChoosePageParentMode: 'Choose where to create this page'
|
||||||
ChoosePageType: 'Choose page type'
|
ChoosePageType: 'Choose page type'
|
||||||
Create: Create
|
Create: Create
|
||||||
DUPLICATED: 'Duplicated ''{title}'' successfully'
|
DUPLICATED: "Duplicated '{title}' successfully"
|
||||||
DUPLICATEDWITHCHILDREN: 'Duplicated ''{title}'' and children successfully'
|
DUPLICATEDWITHCHILDREN: "Duplicated '{title}' and children successfully"
|
||||||
EMAIL: Email
|
EMAIL: Email
|
||||||
NEWPAGE: 'New {pagetype}'
|
NEWPAGE: 'New {pagetype}'
|
||||||
PAGENOTEXISTS: 'This page doesn''t exist'
|
PAGENOTEXISTS: "This page doesn't exist"
|
||||||
PAGES: 'Page status'
|
PAGES: 'Page status'
|
||||||
PAGESALLOPT: 'All pages'
|
PAGESALLOPT: 'All pages'
|
||||||
PAGETYPEANYOPT: Any
|
PAGETYPEANYOPT: Any
|
||||||
PAGETYPEOPT: 'Page type'
|
PAGETYPEOPT: 'Page type'
|
||||||
PAGETYPE_TITLE: '(Page type: {type}) {title}'
|
PAGETYPE_TITLE: '(Page type: {type}) {title}'
|
||||||
PLEASESAVE: 'Please Save Page: This page could not be updated because it hasn''t been saved yet.'
|
PLEASESAVE: "Please Save Page: This page could not be updated because it hasn't been saved yet."
|
||||||
PUBALLCONFIRM: 'Please publish every page in the site, copying content stage to live'
|
PUBALLCONFIRM: 'Please publish every page in the site, copying content stage to live'
|
||||||
PUBALLFUN: '"Publish All" functionality'
|
PUBALLFUN: '"Publish All" functionality'
|
||||||
PUBALLFUN2: 'Pressing this button will do the equivalent of going to every page and pressing "publish". It''s intended to be used after there have been massive edits of the content, such as when the site was first built. For large websites, this task might not be able to run through to completion. In this case, we recommend talking to your developers to create a custom task'
|
PUBALLFUN2: 'Pressing this button will do the equivalent of going to every page and pressing "publish". It''s intended to be used after there have been massive edits of the content, such as when the site was first built. For large websites, this task might not be able to run through to completion. In this case, we recommend talking to your developers to create a custom task'
|
||||||
PUBLISHED: 'Published ''{title}'' successfully.'
|
PUBLISHED: "Published '{title}' successfully."
|
||||||
PUBPAGES: 'Done: Published {count} pages'
|
PUBPAGES: 'Done: Published {count} pages'
|
||||||
PageAdded: 'Successfully created page'
|
PageAdded: 'Successfully created page'
|
||||||
REMOVEDPAGE: 'Removed ''{title}'' from the published site'
|
REMOVEDPAGE: "Removed '{title}' from the published site"
|
||||||
REMOVEDPAGEFROMDRAFT: 'Removed ''{title}'' from the draft site'
|
REMOVEDPAGEFROMDRAFT: "Removed '{title}' from the draft site"
|
||||||
REORGANISATIONSUCCESSFUL: 'Reorganised the site tree successfully.'
|
REORGANISATIONSUCCESSFUL: 'Reorganised the site tree successfully.'
|
||||||
RESTORE: 'Restore draft'
|
RESTORE: 'Restore draft'
|
||||||
RESTORED: 'Restored ''{title}'' successfully'
|
RESTORED: "Restored '{title}' successfully"
|
||||||
RESTORE_DESC: 'Restore the archived version to draft'
|
RESTORE_DESC: 'Restore the archived version to draft'
|
||||||
RESTORE_TO_ROOT: 'Restore draft at top level'
|
RESTORE_TO_ROOT: 'Restore draft at top level'
|
||||||
RESTORE_TO_ROOT_DESC: 'Restore the archived version to draft as a top level page'
|
RESTORE_TO_ROOT_DESC: 'Restore the archived version to draft as a top level page'
|
||||||
ROLLBACK: 'Roll back to this version'
|
ROLLBACK: 'Roll back to this version'
|
||||||
ROLLEDBACKPUBv2: 'Rolled back to published version.'
|
ROLLEDBACKPUBv2: 'Rolled back to published version.'
|
||||||
ROLLEDBACKVERSIONv2: 'Rolled back to version #{version}.'
|
ROLLEDBACKVERSIONv2: 'Rolled back to version #{version}.'
|
||||||
SAVED: 'Saved ''{title}'' successfully.'
|
SAVED: "Saved '{title}' successfully."
|
||||||
SAVEDRAFT: Save
|
SAVEDRAFT: Save
|
||||||
SEARCHRESULTS: 'Search results'
|
SEARCHRESULTS: 'Search results'
|
||||||
SHOW_AS_LIST: 'show as list'
|
SHOW_AS_LIST: 'show as list'
|
||||||
@ -73,8 +73,8 @@ en:
|
|||||||
ParentMode_top: 'Top level'
|
ParentMode_top: 'Top level'
|
||||||
Title: 'Add page'
|
Title: 'Add page'
|
||||||
SilverStripe\CMS\Controllers\CMSPageEditController:
|
SilverStripe\CMS\Controllers\CMSPageEditController:
|
||||||
ErrorItemPermissionDenied: 'It seems you don''t have the necessary permissions to add {ObjectTitle} to a campaign'
|
ErrorItemPermissionDenied: "It seems you don't have the necessary permissions to add {ObjectTitle} to a campaign"
|
||||||
ErrorNotFound: 'That {Type} couldn''t be found'
|
ErrorNotFound: "That {Type} couldn't be found"
|
||||||
MENUTITLE: 'Edit Page'
|
MENUTITLE: 'Edit Page'
|
||||||
SilverStripe\CMS\Controllers\CMSPageHistoryController:
|
SilverStripe\CMS\Controllers\CMSPageHistoryController:
|
||||||
AUTHOR: Author
|
AUTHOR: Author
|
||||||
@ -114,7 +114,6 @@ en:
|
|||||||
SilverStripe\CMS\Controllers\CMSSiteTreeFilter_StatusRemovedFromDraftPages:
|
SilverStripe\CMS\Controllers\CMSSiteTreeFilter_StatusRemovedFromDraftPages:
|
||||||
Title: 'Live but removed from draft'
|
Title: 'Live but removed from draft'
|
||||||
SilverStripe\CMS\Controllers\ContentController:
|
SilverStripe\CMS\Controllers\ContentController:
|
||||||
ARCHIVEDSITE: 'Preview version'
|
|
||||||
ARCHIVEDSITEFROM: 'Archived site from'
|
ARCHIVEDSITEFROM: 'Archived site from'
|
||||||
CMS: CMS
|
CMS: CMS
|
||||||
DRAFT: Draft
|
DRAFT: Draft
|
||||||
@ -134,11 +133,8 @@ en:
|
|||||||
Password: Password
|
Password: Password
|
||||||
PostInstallTutorialIntro: 'This website is a simplistic version of a SilverStripe 3 site. To extend this, please take a look at {link}.'
|
PostInstallTutorialIntro: 'This website is a simplistic version of a SilverStripe 3 site. To extend this, please take a look at {link}.'
|
||||||
StartEditing: 'You can start editing your content by opening <a href="{link}">the CMS</a>.'
|
StartEditing: 'You can start editing your content by opening <a href="{link}">the CMS</a>.'
|
||||||
UNVERSIONEDPREVIEW: Preview
|
|
||||||
UnableDeleteInstall: 'Unable to delete installation files. Please delete the files below manually'
|
UnableDeleteInstall: 'Unable to delete installation files. Please delete the files below manually'
|
||||||
VIEWPAGEIN: 'View Page in:'
|
VIEWPAGEIN: 'View Page in:'
|
||||||
SilverStripe\CMS\Controllers\SilverStripeNavigator:
|
|
||||||
ARCHIVED: Archived
|
|
||||||
SilverStripe\CMS\Forms\AnchorLinkFormFactory:
|
SilverStripe\CMS\Forms\AnchorLinkFormFactory:
|
||||||
ANCHORVALUE: Anchor
|
ANCHORVALUE: Anchor
|
||||||
SilverStripe\CMS\Forms\InternalLinkFormFactory:
|
SilverStripe\CMS\Forms\InternalLinkFormFactory:
|
||||||
@ -218,7 +214,7 @@ en:
|
|||||||
LASTPUBLISHED: 'Last published'
|
LASTPUBLISHED: 'Last published'
|
||||||
LASTSAVED: 'Last saved'
|
LASTSAVED: 'Last saved'
|
||||||
LASTUPDATED: 'Last Updated'
|
LASTUPDATED: 'Last Updated'
|
||||||
LINKCHANGENOTE: 'Changing this page''s link will also affect the links of all child pages.'
|
LINKCHANGENOTE: "Changing this page's link will also affect the links of all child pages."
|
||||||
LINKSALREADYUNIQUE: ' {url} is already unique'
|
LINKSALREADYUNIQUE: ' {url} is already unique'
|
||||||
LINKSCHANGEDTO: ' changed {url1} -> {url2}'
|
LINKSCHANGEDTO: ' changed {url1} -> {url2}'
|
||||||
MENUTITLE: 'Navigation label'
|
MENUTITLE: 'Navigation label'
|
||||||
|
72
package.json
72
package.json
@ -1,15 +1,15 @@
|
|||||||
{
|
{
|
||||||
"name": "silverstripe-cms",
|
"name": "silverstripe-cms",
|
||||||
"version": "4.0.0",
|
"version": "5.0.0",
|
||||||
"description": "The SilverStripe CMS",
|
"description": "The SilverStripe CMS",
|
||||||
"directories": {
|
"directories": {
|
||||||
"test": "tests"
|
"test": "tests"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^10.x"
|
"node": "^18.x"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "yarn && yarn test && NODE_ENV=production webpack -p --bail --progress",
|
"build": "yarn && yarn lint && yarn test && rm -rf client/dist/* && NODE_ENV=production webpack --mode production --bail --progress",
|
||||||
"dev": "NODE_ENV=development webpack --progress",
|
"dev": "NODE_ENV=development webpack --progress",
|
||||||
"watch": "NODE_ENV=development webpack --watch --progress",
|
"watch": "NODE_ENV=development webpack --watch --progress",
|
||||||
"css": "WEBPACK_CHILD=css npm run build",
|
"css": "WEBPACK_CHILD=css npm run build",
|
||||||
@ -32,36 +32,42 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/silverstripe/silverstripe-cms#readme",
|
"homepage": "https://github.com/silverstripe/silverstripe-cms#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@silverstripe/reactstrap-confirm": "0.0.5",
|
"@apollo/client": "^3.7.1",
|
||||||
"apollo-client": "^2.4.2",
|
"@popperjs/core": "^2.11.6",
|
||||||
"bootstrap": "^4.3.1",
|
"bootstrap": "^4.6.2",
|
||||||
"classnames": "^2.2.5",
|
"classnames": "^2.3.2",
|
||||||
"deep-freeze-strict": "^1.1.1",
|
"deep-freeze-strict": "^1.1.1",
|
||||||
"graphql": "^14.0.0",
|
"graphql": "^16.8.1",
|
||||||
"graphql-tag": "^2.10.0",
|
"graphql-tag": "^2.12.6",
|
||||||
"isomorphic-fetch": "^2.2.1",
|
"isomorphic-fetch": "^3.0.0",
|
||||||
"jquery": "^3.5.0",
|
|
||||||
"merge": "^2.1.1",
|
"merge": "^2.1.1",
|
||||||
"popper.js": "^1.14.4",
|
"prop-types": "^15.8.1",
|
||||||
"prop-types": "^15.6.2",
|
"react": "^18.2.0",
|
||||||
"react": "^16.6.1",
|
"react-dom": "^18.2.0",
|
||||||
"react-apollo": "^2.1.11",
|
"react-redux": "^8.0.5",
|
||||||
"react-dom": "^16.6.1",
|
"react-select": "^5.6.0",
|
||||||
"react-redux": "^5.0.7",
|
"reactstrap": "^8.9.0",
|
||||||
"react-select": "^1.3",
|
"reactstrap-confirm": "^1.3.2",
|
||||||
"reactstrap": "^6.4.0",
|
"redux": "^4.2.0",
|
||||||
"redux": "^4.0.0",
|
"redux-form": "^8.3.8"
|
||||||
"redux-form": "^7.4.2"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@silverstripe/eslint-config": "^0.1.0",
|
"@silverstripe/eslint-config": "^1.1.0",
|
||||||
"@silverstripe/webpack-config": "^1.3.0",
|
"@silverstripe/webpack-config": "^2.0.0",
|
||||||
"babel-jest": "^23.6.0",
|
"@testing-library/react": "^14.0.0",
|
||||||
"babel-polyfill": "^6.26.0",
|
"babel-jest": "^29.3.0",
|
||||||
"copy-webpack-plugin": "^4",
|
"copy-webpack-plugin": "^11.0.0",
|
||||||
"jest-cli": "^23.6.0"
|
"core-js": "^3.26.0",
|
||||||
|
"jest-cli": "^29.3.0",
|
||||||
|
"jest-environment-jsdom": "^29.3.1",
|
||||||
|
"webpack": "^5.74.0",
|
||||||
|
"webpack-cli": "^5.0.0"
|
||||||
},
|
},
|
||||||
|
"browserslist": [
|
||||||
|
"defaults"
|
||||||
|
],
|
||||||
"jest": {
|
"jest": {
|
||||||
|
"testEnvironment": "jsdom",
|
||||||
"roots": [
|
"roots": [
|
||||||
"client/src"
|
"client/src"
|
||||||
],
|
],
|
||||||
@ -82,16 +88,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"colors": "1.1.2",
|
"colors": "1.4.0"
|
||||||
"eslint": "^4.6.1"
|
|
||||||
},
|
|
||||||
"babel": {
|
|
||||||
"presets": [
|
|
||||||
"env",
|
|
||||||
"react"
|
|
||||||
],
|
|
||||||
"plugins": [
|
|
||||||
"transform-object-rest-spread"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
3
phpstan.neon.dist
Normal file
3
phpstan.neon.dist
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
parameters:
|
||||||
|
paths:
|
||||||
|
- code
|
@ -1,16 +0,0 @@
|
|||||||
<div class="cms-preview fill-height flexbox-area-grow" data-layout-type="border">
|
|
||||||
<div class="panel flexbox-area-grow fill-height">
|
|
||||||
<div class="preview-note">
|
|
||||||
<div class="icon font-icon-monitor display-1"></div>
|
|
||||||
<%t SilverStripe\CMS\Controllers\CMSPageHistoryController.NO_PREVIEW 'No preview available' %>
|
|
||||||
</div>
|
|
||||||
<div class="preview__device">
|
|
||||||
<div class="preview-device-outer">
|
|
||||||
<div class="preview-device-inner">
|
|
||||||
<iframe src="about:blank" class="center" name="cms-preview-iframe"></iframe>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="toolbar toolbar--south cms-content-controls cms-preview-controls"></div>
|
|
||||||
</div>
|
|
@ -13,12 +13,12 @@
|
|||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input $AttributesHTML />
|
<input $AttributesHTML />
|
||||||
<div class="input-group-append">
|
<div class="input-group-append">
|
||||||
<button role="button" data-icon="accept" type="button" class="btn btn-primary update">
|
<button role="button" type="button" class="btn btn-primary update">
|
||||||
<%t SilverStripe\CMS\Forms\SiteTreeURLSegmentField.OK 'OK' %>
|
<%t SilverStripe\CMS\Forms\SiteTreeURLSegmentField.OK 'OK' %>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="input-group-append">
|
<div class="input-group-append">
|
||||||
<button role="button" data-icon="cancel" type="button" class="btn btn-outline-secondary btn-sm input-group-append cancel">
|
<button role="button" type="button" class="btn btn-outline-secondary btn-sm input-group-append cancel">
|
||||||
<%t SilverStripe\CMS\Forms\SiteTreeURLSegmentField.Cancel 'Cancel' %>
|
<%t SilverStripe\CMS\Forms\SiteTreeURLSegmentField.Cancel 'Cancel' %>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
<% if $BackLinkTracking %>
|
|
||||||
<table class="table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th><%t SilverStripe\CMS\Model\SiteTreeFileExtension.TITLE_INDEX '#' %></th>
|
|
||||||
<th><%t SilverStripe\CMS\Model\SiteTreeFileExtension.TITLE_USED_ON 'Used on' %></th>
|
|
||||||
<th><%t SilverStripe\CMS\Model\SiteTreeFileExtension.TITLE_TYPE 'Type' %></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<% loop $BackLinkTracking %>
|
|
||||||
<tr>
|
|
||||||
<td>$Pos</td>
|
|
||||||
<td><a href="$CMSEditLink">$MenuTitle</a></td>
|
|
||||||
<td>
|
|
||||||
$i18n_singular_name
|
|
||||||
<% if $isPublished %>
|
|
||||||
<span class="badge badge-success">Published</span>
|
|
||||||
<% else %>
|
|
||||||
<span class="badge status-addedtodraft">Draft</span>
|
|
||||||
<% end_if %>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<% end_loop %>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<% end_if %>
|
|
@ -81,19 +81,19 @@ Feature: Edit a page
|
|||||||
When I click on "About Us" in the tree
|
When I click on "About Us" in the tree
|
||||||
|
|
||||||
# Embed files from the "Files" section of the admin area
|
# Embed files from the "Files" section of the admin area
|
||||||
And I click on the "div[aria-label='Insert from Files'] button" element
|
And I press the "Insert from Files" HTML field button
|
||||||
And I click on the ".gallery__files .gallery-item__thumbnail" element
|
And I click on the ".gallery__files .gallery-item__thumbnail" element
|
||||||
And I press the "Insert file" button
|
And I press the "Insert file" button
|
||||||
|
|
||||||
# Link to a file in the "Files" section of the admin area
|
# Link to a file in the "Files" section of the admin area
|
||||||
And I click on the "div[aria-label='Insert link [Ctrl+K]'] button" element
|
And I press the "Insert link" HTML field button
|
||||||
And I select "Link to a file" from the TinyMCE menu with javascript
|
And I click "Link to a file" in the ".tox-collection__group" element
|
||||||
And I click on the ".gallery__files .gallery-item__thumbnail" element
|
And I click on the ".gallery__files .gallery-item__thumbnail" element
|
||||||
And I fill in "Form_fileInsertForm_Text" with "MyImage"
|
And I fill in "Form_fileInsertForm_Text" with "MyImage"
|
||||||
And I press the "Link to file" button
|
And I press the "Link to file" button
|
||||||
|
|
||||||
# Embed media from a URL
|
# Embed media from a URL
|
||||||
And I click on the "div[aria-label='Insert media via URL'] button" element
|
And I press the "Insert media via URL" button
|
||||||
And I fill in "Form_remoteCreateForm_Url" with "https://www.youtube.com/watch?v=ScMzIvxBSi4"
|
And I fill in "Form_remoteCreateForm_Url" with "https://www.youtube.com/watch?v=ScMzIvxBSi4"
|
||||||
And I press "Add media"
|
And I press "Add media"
|
||||||
And I wait for 15 seconds
|
And I wait for 15 seconds
|
||||||
|
@ -17,12 +17,11 @@ So that I can link to a external website or a page on my site
|
|||||||
Scenario: I can link to an internal page
|
Scenario: I can link to an internal page
|
||||||
When I select "awesome" in the "Content" HTML field
|
When I select "awesome" in the "Content" HTML field
|
||||||
And I press the "Insert link" HTML field button
|
And I press the "Insert link" HTML field button
|
||||||
And I click "Page on this site" in the ".mce-menu" element
|
And I click "Page on this site" in the ".tox-collection__group" element
|
||||||
Then I should see an "form#Form_editorInternalLink" element
|
Then I should see an "form#Form_editorInternalLink" element
|
||||||
When I click "(Search or choose Page)" in the ".Select-multi-value-wrapper" element
|
When I select "About Us" in the "#Form_editorInternalLink_PageID_Holder" tree dropdown
|
||||||
And I click "About Us" in the ".treedropdownfield__menu" element
|
|
||||||
And I fill in "my desc" for "Link description"
|
And I fill in "my desc" for "Link description"
|
||||||
And I press the "Insert" button
|
And I press the "Insert link" button
|
||||||
Then the "Content" HTML field should contain "<a title="my desc" href="[sitetree_link,id=2]">awesome</a>"
|
Then the "Content" HTML field should contain "<a title="my desc" href="[sitetree_link,id=2]">awesome</a>"
|
||||||
# Required to avoid "unsaved changes" browser dialog
|
# Required to avoid "unsaved changes" browser dialog
|
||||||
Then I press the "Save" button
|
Then I press the "Save" button
|
||||||
@ -31,12 +30,11 @@ So that I can link to a external website or a page on my site
|
|||||||
Given I fill in the "Content" HTML field with "<p><img src='file1.jpg'></p>"
|
Given I fill in the "Content" HTML field with "<p><img src='file1.jpg'></p>"
|
||||||
When I select the image "file1.jpg" in the "Content" HTML field
|
When I select the image "file1.jpg" in the "Content" HTML field
|
||||||
And I press the "Insert link" HTML field button
|
And I press the "Insert link" HTML field button
|
||||||
And I click "Page on this site" in the ".mce-menu" element
|
And I click "Page on this site" in the ".tox-collection__group" element
|
||||||
Then I should see an "form#Form_editorInternalLink" element
|
Then I should see an "form#Form_editorInternalLink" element
|
||||||
And I should not see "Link text"
|
And I should not see "Link text"
|
||||||
When I click "(Search or choose Page)" in the ".Select-multi-value-wrapper" element
|
When I select "About Us" in the "#Form_editorInternalLink_PageID_Holder" tree dropdown
|
||||||
And I click "About Us" in the ".treedropdownfield__menu" element
|
And I press the "Insert link" button
|
||||||
And I press the "Insert" button
|
|
||||||
Then the "Content" HTML field should contain "<a href="[sitetree_link,id=2]"><img src="file1.jpg"></a>"
|
Then the "Content" HTML field should contain "<a href="[sitetree_link,id=2]"><img src="file1.jpg"></a>"
|
||||||
# Required to avoid "unsaved changed" browser dialog
|
# Required to avoid "unsaved changed" browser dialog
|
||||||
And I press the "Save" button
|
And I press the "Save" button
|
||||||
@ -45,15 +43,14 @@ So that I can link to a external website or a page on my site
|
|||||||
Given I fill in the "Content" HTML field with "<a title='my desc' href='[sitetree_link,id=2]'>awesome</a>"
|
Given I fill in the "Content" HTML field with "<a title='my desc' href='[sitetree_link,id=2]'>awesome</a>"
|
||||||
And I select "awesome" in the "Content" HTML field
|
And I select "awesome" in the "Content" HTML field
|
||||||
And I press the "Insert link" HTML field button
|
And I press the "Insert link" HTML field button
|
||||||
And I click "Page on this site" in the ".mce-menu" element
|
And I click "Page on this site" in the ".tox-collection__group" element
|
||||||
And I should see an "form#Form_editorInternalLink" element
|
And I should see an "form#Form_editorInternalLink" element
|
||||||
Then I should see "About Us" in the ".Select-value" element
|
Then I should see "About Us" in the "#Form_editorInternalLink_PageID_Holder .treedropdownfield__value-container" element
|
||||||
And the "Link description" field should contain "my desc"
|
And the "Link description" field should contain "my desc"
|
||||||
# This doesn't seem to suffer from that issue
|
# This doesn't seem to suffer from that issue
|
||||||
When I click "About Us" in the ".Select-value" element
|
When I select "Home" in the "#Form_editorInternalLink_PageID_Holder" tree dropdown
|
||||||
And I click "Home" in the ".treedropdownfield__menu" element
|
|
||||||
And I fill in "my new desc" for "Link description"
|
And I fill in "my new desc" for "Link description"
|
||||||
And I press the "Insert" button
|
And I press the "Insert link" button
|
||||||
Then the "Content" HTML field should contain "<a title="my new desc" href="[sitetree_link,id=1]">awesome</a>"
|
Then the "Content" HTML field should contain "<a title="my new desc" href="[sitetree_link,id=1]">awesome</a>"
|
||||||
# Required to avoid "unsaved changes" browser dialog
|
# Required to avoid "unsaved changes" browser dialog
|
||||||
Then I press the "Save" button
|
Then I press the "Save" button
|
||||||
@ -61,11 +58,11 @@ So that I can link to a external website or a page on my site
|
|||||||
Scenario: I can link to an external URL
|
Scenario: I can link to an external URL
|
||||||
Given I select "awesome" in the "Content" HTML field
|
Given I select "awesome" in the "Content" HTML field
|
||||||
And I press the "Insert link" HTML field button
|
And I press the "Insert link" HTML field button
|
||||||
When I click "Link to external URL" in the ".mce-menu" element
|
When I click "Link to external URL" in the ".tox-collection__group" element
|
||||||
And I should see an "form#Form_ModalsEditorExternalLink" element
|
And I should see an "form#Form_ModalsEditorExternalLink" element
|
||||||
When I fill in "http://silverstripe.org" for "URL"
|
When I fill in "http://silverstripe.org" for "URL"
|
||||||
And I check "Open in new window/tab"
|
And I check "Open in new window/tab"
|
||||||
And I press the "Insert" button
|
And I press the "Insert link" button
|
||||||
Then the "Content" HTML field should contain "<a rel="noopener" href="http://silverstripe.org" target="_blank">awesome</a>"
|
Then the "Content" HTML field should contain "<a rel="noopener" href="http://silverstripe.org" target="_blank">awesome</a>"
|
||||||
# Required to avoid "unsaved changes" browser dialog
|
# Required to avoid "unsaved changes" browser dialog
|
||||||
Then I press the "Save" button
|
Then I press the "Save" button
|
||||||
@ -74,11 +71,11 @@ So that I can link to a external website or a page on my site
|
|||||||
Given I fill in the "Content" HTML field with "<p><img src='file1.jpg'></p>"
|
Given I fill in the "Content" HTML field with "<p><img src='file1.jpg'></p>"
|
||||||
When I select the image "file1.jpg" in the "Content" HTML field
|
When I select the image "file1.jpg" in the "Content" HTML field
|
||||||
And I press the "Insert link" HTML field button
|
And I press the "Insert link" HTML field button
|
||||||
When I click "Link to external URL" in the ".mce-menu" element
|
When I click "Link to external URL" in the ".tox-collection__group" element
|
||||||
And I should see an "form#Form_ModalsEditorExternalLink" element
|
And I should see an "form#Form_ModalsEditorExternalLink" element
|
||||||
And I should not see "Link text"
|
And I should not see "Link text"
|
||||||
When I fill in "http://silverstripe.org" for "URL"
|
When I fill in "http://silverstripe.org" for "URL"
|
||||||
And I press the "Insert" button
|
And I press the "Insert link" button
|
||||||
Then the "Content" HTML field should contain "<a href="http://silverstripe.org"><img src="file1.jpg"></a>"
|
Then the "Content" HTML field should contain "<a href="http://silverstripe.org"><img src="file1.jpg"></a>"
|
||||||
# Required to avoid "unsaved changed" browser dialog
|
# Required to avoid "unsaved changed" browser dialog
|
||||||
And I press the "Save" button
|
And I press the "Save" button
|
||||||
@ -87,12 +84,12 @@ So that I can link to a external website or a page on my site
|
|||||||
Given I fill in the "Content" HTML field with "<p>My <a href='http://silverstripe.org'>awesome</a> content"
|
Given I fill in the "Content" HTML field with "<p>My <a href='http://silverstripe.org'>awesome</a> content"
|
||||||
And I select "awesome" in the "Content" HTML field
|
And I select "awesome" in the "Content" HTML field
|
||||||
When I press the "Insert link" HTML field button
|
When I press the "Insert link" HTML field button
|
||||||
And I click "Link to external URL" in the ".mce-menu" element
|
And I click "Link to external URL" in the ".tox-collection__group" element
|
||||||
And I should see an "form#Form_ModalsEditorExternalLink" element
|
And I should see an "form#Form_ModalsEditorExternalLink" element
|
||||||
Then the "URL" field should contain "http://silverstripe.org"
|
Then the "URL" field should contain "http://silverstripe.org"
|
||||||
# This doesn't seem to suffer from that issue
|
# This doesn't seem to suffer from that issue
|
||||||
When I fill in "http://google.com" for "URL"
|
When I fill in "http://google.com" for "URL"
|
||||||
And I press the "Insert" button
|
And I press the "Insert link" button
|
||||||
Then the "Content" HTML field should contain "<a href="http://google.com">awesome</a>"
|
Then the "Content" HTML field should contain "<a href="http://google.com">awesome</a>"
|
||||||
# Required to avoid "unsaved changes" browser dialog
|
# Required to avoid "unsaved changes" browser dialog
|
||||||
Then I press the "Save" button
|
Then I press the "Save" button
|
||||||
|
@ -17,16 +17,14 @@ So that I can link to a external website or a page on my site
|
|||||||
Scenario: I can link to an anchor in an internal page
|
Scenario: I can link to an anchor in an internal page
|
||||||
When I select "awesome" in the "Content" HTML field
|
When I select "awesome" in the "Content" HTML field
|
||||||
And I press the "Insert link" HTML field button
|
And I press the "Insert link" HTML field button
|
||||||
And I click "Anchor on a page" in the ".mce-menu" element
|
And I click "Anchor on a page" in the ".tox-collection__group" element
|
||||||
Then I should see an "form#Form_editorAnchorLink" element
|
Then I should see an "form#Form_editorAnchorLink" element
|
||||||
And I should see "About Us" in the "#Form_editorAnchorLink_PageID_Holder .Select-multi-value-wrapper" element
|
And I should see "About Us" in the "#Form_editorAnchorLink_PageID_Holder .treedropdownfield__value-container" element
|
||||||
When I click "About Us" in the "#Form_editorAnchorLink_PageID_Holder .Select-multi-value-wrapper" element
|
When I select "Details" in the "#Form_editorAnchorLink_PageID_Holder" tree dropdown
|
||||||
And I click "Details" in the "#Form_editorAnchorLink_PageID_Holder .Select-menu-outer" element
|
And I select "youranchor" in the "#Form_editorAnchorLink_Anchor_Holder" anchor dropdown
|
||||||
And I click "Select or enter anchor" in the "#Form_editorAnchorLink_Anchor_Holder .Select-multi-value-wrapper" element
|
Then I should see "youranchor" in the "#Form_editorAnchorLink_Anchor_Holder .anchorselectorfield__value-container" element
|
||||||
And I click "youranchor" in the "#Form_editorAnchorLink_Anchor_Holder .Select-menu-outer" element
|
|
||||||
Then I should see "youranchor" in the "#Form_editorAnchorLink_Anchor_Holder .Select-value" element
|
|
||||||
When I fill in "my desc" for "Link description"
|
When I fill in "my desc" for "Link description"
|
||||||
And I press the "Insert" button
|
And I press the "Insert link" button
|
||||||
Then the "Content" HTML field should contain "<a title="my desc" href="[sitetree_link,id=3]#youranchor">awesome</a>"
|
Then the "Content" HTML field should contain "<a title="my desc" href="[sitetree_link,id=3]#youranchor">awesome</a>"
|
||||||
# Required to avoid "unsaved changes" browser dialog
|
# Required to avoid "unsaved changes" browser dialog
|
||||||
Then I press the "Save" button
|
Then I press the "Save" button
|
||||||
@ -35,16 +33,13 @@ So that I can link to a external website or a page on my site
|
|||||||
Given I fill in the "Content" HTML field with "<p><img src='file1.jpg'></p>"
|
Given I fill in the "Content" HTML field with "<p><img src='file1.jpg'></p>"
|
||||||
When I select the image "file1.jpg" in the "Content" HTML field
|
When I select the image "file1.jpg" in the "Content" HTML field
|
||||||
And I press the "Insert link" HTML field button
|
And I press the "Insert link" HTML field button
|
||||||
And I click "Anchor on a page" in the ".mce-menu" element
|
And I click "Anchor on a page" in the ".tox-collection__group" element
|
||||||
Then I should see an "form#Form_editorAnchorLink" element
|
Then I should see an "form#Form_editorAnchorLink" element
|
||||||
And I should not see "Link text"
|
And I should not see "Link text"
|
||||||
And I should see "About Us" in the "#Form_editorAnchorLink_PageID_Holder .Select-multi-value-wrapper" element
|
And I should see "About Us" in the "#Form_editorAnchorLink_PageID_Holder .treedropdownfield__value-container" element
|
||||||
When I click "About Us" in the "#Form_editorAnchorLink_PageID_Holder .Select-multi-value-wrapper" element
|
When I select "Details" in the "#Form_editorAnchorLink_PageID_Holder" tree dropdown
|
||||||
And I click "Details" in the "#Form_editorAnchorLink_PageID_Holder .Select-menu-outer" element
|
And I select "youranchor" in the "#Form_editorAnchorLink_Anchor_Holder" anchor dropdown
|
||||||
And I click "Select or enter anchor" in the "#Form_editorAnchorLink_Anchor_Holder .Select-multi-value-wrapper" element
|
And I press the "Insert link" button
|
||||||
And I click "youranchor" in the "#Form_editorAnchorLink_Anchor_Holder .Select-menu-outer" element
|
|
||||||
Then I should see "youranchor" in the "#Form_editorAnchorLink_Anchor_Holder .Select-value" element
|
|
||||||
And I press the "Insert" button
|
|
||||||
Then the "Content" HTML field should contain "<a href="[sitetree_link,id=3]#youranchor"><img src="file1.jpg"></a>"
|
Then the "Content" HTML field should contain "<a href="[sitetree_link,id=3]#youranchor"><img src="file1.jpg"></a>"
|
||||||
# Required to avoid "unsaved changed" browser dialog
|
# Required to avoid "unsaved changed" browser dialog
|
||||||
And I press the "Save" button
|
And I press the "Save" button
|
||||||
@ -52,15 +47,13 @@ So that I can link to a external website or a page on my site
|
|||||||
Scenario: I can link to an anchor from a dataobject on the current page
|
Scenario: I can link to an anchor from a dataobject on the current page
|
||||||
When I select "awesome" in the "Content" HTML field
|
When I select "awesome" in the "Content" HTML field
|
||||||
And I press the "Insert link" HTML field button
|
And I press the "Insert link" HTML field button
|
||||||
And I click "Anchor on a page" in the ".mce-menu" element
|
And I click "Anchor on a page" in the ".tox-collection__group" element
|
||||||
Then I should see an "form#Form_editorAnchorLink" element
|
Then I should see an "form#Form_editorAnchorLink" element
|
||||||
And I should see "About Us" in the "#Form_editorAnchorLink_PageID_Holder .Select-multi-value-wrapper" element
|
And I should see "About Us" in the "#Form_editorAnchorLink_PageID_Holder .treedropdownfield__value-container" element
|
||||||
When I click "Select or enter anchor" in the "#Form_editorAnchorLink_Anchor_Holder .Select-multi-value-wrapper" element
|
When I select "dataobject-anchor" in the "#Form_editorAnchorLink_Anchor_Holder" anchor dropdown
|
||||||
Then I should see "dataobject-anchor" in the "#Form_editorAnchorLink_Anchor_Holder .Select-menu-outer" element
|
Then I should see "dataobject-anchor" in the "#Form_editorAnchorLink_Anchor_Holder .anchorselectorfield__value-container" element
|
||||||
When I click "dataobject-anchor" in the "#Form_editorAnchorLink_Anchor_Holder .Select-menu-outer" element
|
|
||||||
Then I should see "dataobject-anchor" in the "#Form_editorAnchorLink_Anchor_Holder .Select-value" element
|
|
||||||
When I fill in "my desc" for "Link description"
|
When I fill in "my desc" for "Link description"
|
||||||
And I press the "Insert" button
|
And I press the "Insert link" button
|
||||||
Then the "Content" HTML field should contain "<a title="my desc" href="[sitetree_link,id=2]#dataobject-anchor">awesome</a>"
|
Then the "Content" HTML field should contain "<a title="my desc" href="[sitetree_link,id=2]#dataobject-anchor">awesome</a>"
|
||||||
# Required to avoid "unsaved changes" browser dialog
|
# Required to avoid "unsaved changes" browser dialog
|
||||||
Then I press the "Save" button
|
Then I press the "Save" button
|
||||||
@ -69,16 +62,15 @@ So that I can link to a external website or a page on my site
|
|||||||
Given I fill in the "Content" HTML field with "<p>My awesome content</p><p><a id='unsaved-anchor'></a>unsaved content</p>"
|
Given I fill in the "Content" HTML field with "<p>My awesome content</p><p><a id='unsaved-anchor'></a>unsaved content</p>"
|
||||||
When I select "awesome" in the "Content" HTML field
|
When I select "awesome" in the "Content" HTML field
|
||||||
And I press the "Insert link" HTML field button
|
And I press the "Insert link" HTML field button
|
||||||
And I click "Anchor on a page" in the ".mce-menu" element
|
And I click "Anchor on a page" in the ".tox-collection__group" element
|
||||||
Then I should see an "form#Form_editorAnchorLink" element
|
Then I should see an "form#Form_editorAnchorLink" element
|
||||||
And I should see "About Us" in the "#Form_editorAnchorLink_PageID_Holder .Select-multi-value-wrapper" element
|
And I should see "About Us" in the "#Form_editorAnchorLink_PageID_Holder .treedropdownfield__value-container" element
|
||||||
When I click "Select or enter anchor" in the "#Form_editorAnchorLink_Anchor_Holder .Select-multi-value-wrapper" element
|
When I click on the ".anchorselectorfield__dropdown-indicator" element
|
||||||
Then I should see "unsaved-anchor" in the "#Form_editorAnchorLink_Anchor_Holder .Select-menu-outer" element
|
Then I should see "dataobject-anchor" in the ".anchorselectorfield__menu-list" element
|
||||||
And I should see "dataobject-anchor" in the "#Form_editorAnchorLink_Anchor_Holder .Select-menu-outer" element
|
When I select "unsaved-anchor" in the "#Form_editorAnchorLink_Anchor_Holder" anchor dropdown
|
||||||
When I click "unsaved-anchor" in the "#Form_editorAnchorLink_Anchor_Holder .Select-menu-outer" element
|
Then I should see "unsaved-anchor" in the "#Form_editorAnchorLink_Anchor_Holder .anchorselectorfield__value-container" element
|
||||||
Then I should see "unsaved-anchor" in the "#Form_editorAnchorLink_Anchor_Holder .Select-value" element
|
|
||||||
When I fill in "my desc" for "Link description"
|
When I fill in "my desc" for "Link description"
|
||||||
And I press the "Insert" button
|
And I press the "Insert link" button
|
||||||
Then the "Content" HTML field should contain "<a title="my desc" href="[sitetree_link,id=2]#unsaved-anchor">awesome</a>"
|
Then the "Content" HTML field should contain "<a title="my desc" href="[sitetree_link,id=2]#unsaved-anchor">awesome</a>"
|
||||||
# Required to avoid "unsaved changes" browser dialog
|
# Required to avoid "unsaved changes" browser dialog
|
||||||
Then I press the "Save" button
|
Then I press the "Save" button
|
||||||
|
@ -19,10 +19,6 @@ Feature: Preview a page
|
|||||||
And the preview contains "About Us"
|
And the preview contains "About Us"
|
||||||
Then I set the CMS mode to "Edit mode"
|
Then I set the CMS mode to "Edit mode"
|
||||||
|
|
||||||
# TODO:
|
|
||||||
# - Only tests correctly on fresh database
|
|
||||||
# - We should continue testing against it after we have fixtures ready
|
|
||||||
@javascript
|
|
||||||
Scenario: I can see an updated preview when editing content
|
Scenario: I can see an updated preview when editing content
|
||||||
And I go to "/admin/pages"
|
And I go to "/admin/pages"
|
||||||
Then I should see "About Us" in the tree
|
Then I should see "About Us" in the tree
|
||||||
|
@ -8,6 +8,7 @@ use Behat\Mink\Element\NodeElement;
|
|||||||
use PHPUnit\Framework\Assert;
|
use PHPUnit\Framework\Assert;
|
||||||
use SilverStripe\BehatExtension\Context\BasicContext;
|
use SilverStripe\BehatExtension\Context\BasicContext;
|
||||||
use SilverStripe\BehatExtension\Context\FixtureContext as BehatFixtureContext;
|
use SilverStripe\BehatExtension\Context\FixtureContext as BehatFixtureContext;
|
||||||
|
use SilverStripe\BehatExtension\Utility\StepHelper;
|
||||||
use SilverStripe\CMS\Model\RedirectorPage;
|
use SilverStripe\CMS\Model\RedirectorPage;
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
use SilverStripe\Core\ClassInfo;
|
use SilverStripe\Core\ClassInfo;
|
||||||
@ -20,6 +21,8 @@ use SilverStripe\Versioned\Versioned;
|
|||||||
*/
|
*/
|
||||||
class FixtureContext extends BehatFixtureContext
|
class FixtureContext extends BehatFixtureContext
|
||||||
{
|
{
|
||||||
|
use StepHelper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var BasicContext
|
* @var BasicContext
|
||||||
*/
|
*/
|
||||||
@ -118,22 +121,6 @@ class FixtureContext extends BehatFixtureContext
|
|||||||
Assert::assertEquals($value, $radioButton->getAttribute($attribute));
|
Assert::assertEquals($value, $radioButton->getAttribute($attribute));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Assumes you've just opened the Insert link menu, e.g.
|
|
||||||
* I click on the "div[aria-label='Insert link [Ctrl+K]'] button" element
|
|
||||||
*
|
|
||||||
* @When /^I select "(.+?)" from the TinyMCE menu with javascript$/
|
|
||||||
* @param string $label
|
|
||||||
*/
|
|
||||||
public function iSelectFromTheTinyMceMenu($label)
|
|
||||||
{
|
|
||||||
// :visible and :contains are jQuery css selectors
|
|
||||||
$js = <<<JS
|
|
||||||
jQuery(".mce-menu-item:visible span:contains('{$label}')").click();
|
|
||||||
JS;
|
|
||||||
$this->getMainContext()->getSession()->executeScript($js);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* e.g. --PageOne,--PageTwo,---PageTwoChild,--PageThree
|
* e.g. --PageOne,--PageTwo,---PageTwoChild,--PageThree
|
||||||
*
|
*
|
||||||
@ -155,4 +142,33 @@ JS;
|
|||||||
$actual = $this->getMainContext()->getSession()->evaluateScript($js);
|
$actual = $this->getMainContext()->getSession()->evaluateScript($js);
|
||||||
Assert::assertEquals($expected, $actual);
|
Assert::assertEquals($expected, $actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select a value in the anchor selector field
|
||||||
|
*
|
||||||
|
* @When /^I select "([^"]*)" in the "([^"]*)" anchor dropdown$/
|
||||||
|
*/
|
||||||
|
public function iSelectValueInAnchorDropdown($text, $selector)
|
||||||
|
{
|
||||||
|
$page = $this->getMainContext()->getSession()->getPage();
|
||||||
|
/** @var NodeElement $parentElement */
|
||||||
|
$parentElement = null;
|
||||||
|
$this->retryThrowable(function () use (&$parentElement, &$page, $selector) {
|
||||||
|
$parentElement = $page->find('css', $selector);
|
||||||
|
Assert::assertNotNull($parentElement, sprintf('"%s" element not found', $selector));
|
||||||
|
$page = $this->getMainContext()->getSession()->getPage();
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->retryThrowable(function () use ($parentElement, $selector) {
|
||||||
|
$dropdown = $parentElement->find('css', '.anchorselectorfield__dropdown-indicator');
|
||||||
|
Assert::assertNotNull($dropdown, sprintf('Unable to find the dropdown in "%s"', $selector));
|
||||||
|
$dropdown->click();
|
||||||
|
});
|
||||||
|
|
||||||
|
$this->retryThrowable(function () use ($text, $parentElement, $selector) {
|
||||||
|
$element = $parentElement->find('xpath', sprintf('//*[count(*)=0 and .="%s"]', $text));
|
||||||
|
Assert::assertNotNull($element, sprintf('"%s" not found in "%s"', $text, $selector));
|
||||||
|
$element->click();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,6 @@ use SilverStripe\Versioned\Versioned;
|
|||||||
*/
|
*/
|
||||||
class CMSBatchActionsTest extends SapphireTest
|
class CMSBatchActionsTest extends SapphireTest
|
||||||
{
|
{
|
||||||
|
|
||||||
protected static $fixture_file = 'CMSBatchActionsTest.yml';
|
protected static $fixture_file = 'CMSBatchActionsTest.yml';
|
||||||
|
|
||||||
protected function setUp(): void
|
protected function setUp(): void
|
||||||
@ -142,10 +141,10 @@ class CMSBatchActionsTest extends SapphireTest
|
|||||||
$this->assertEquals($archivedID, $list->first()->ParentID);
|
$this->assertEquals($archivedID, $list->first()->ParentID);
|
||||||
|
|
||||||
// Run restore
|
// Run restore
|
||||||
$result = json_decode($action->run($list) ?? '', true);
|
$result = json_decode($action->run($list)->getBody(), true);
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
[
|
[
|
||||||
$archivedxID => $archivedxID
|
$archivedxID => $archivedxID,
|
||||||
],
|
],
|
||||||
$result['success']
|
$result['success']
|
||||||
);
|
);
|
||||||
@ -162,13 +161,13 @@ class CMSBatchActionsTest extends SapphireTest
|
|||||||
$this->assertEquals(0, $list->last()->ParentID); // archived (parent)
|
$this->assertEquals(0, $list->last()->ParentID); // archived (parent)
|
||||||
|
|
||||||
// Run restore
|
// Run restore
|
||||||
$result = json_decode($action->run($list) ?? '', true);
|
$result = json_decode($action->run($list)->getBody(), true);
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
[
|
[
|
||||||
// Order of archived is opposite to order items are passed in, as
|
// Order of archived is opposite to order items are passed in, as
|
||||||
// these are sorted by level first
|
// these are sorted by level first
|
||||||
$archivedID => $archivedID,
|
$archivedID => $archivedID,
|
||||||
$archivedyID => $archivedyID
|
$archivedyID => $archivedyID,
|
||||||
],
|
],
|
||||||
$result['success']
|
$result['success']
|
||||||
);
|
);
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace SilverStripe\CMS\Tests\Controllers;
|
namespace SilverStripe\CMS\Tests\Controllers;
|
||||||
|
|
||||||
use Page;
|
|
||||||
use Psr\SimpleCache\CacheInterface;
|
use Psr\SimpleCache\CacheInterface;
|
||||||
use SilverStripe\Admin\CMSBatchActionHandler;
|
use SilverStripe\Admin\CMSBatchActionHandler;
|
||||||
use SilverStripe\CMS\Controllers\CMSMain;
|
use SilverStripe\CMS\Controllers\CMSMain;
|
||||||
@ -25,7 +24,6 @@ use SilverStripe\Security\Member;
|
|||||||
use SilverStripe\Security\Security;
|
use SilverStripe\Security\Security;
|
||||||
use SilverStripe\SiteConfig\SiteConfig;
|
use SilverStripe\SiteConfig\SiteConfig;
|
||||||
use SilverStripe\Versioned\Versioned;
|
use SilverStripe\Versioned\Versioned;
|
||||||
use SilverStripe\Dev\Deprecation;
|
|
||||||
|
|
||||||
class CMSMainTest extends FunctionalTest
|
class CMSMainTest extends FunctionalTest
|
||||||
{
|
{
|
||||||
@ -119,40 +117,6 @@ class CMSMainTest extends FunctionalTest
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @todo Test the results of a publication better
|
|
||||||
*/
|
|
||||||
public function testPublish()
|
|
||||||
{
|
|
||||||
if (Deprecation::isEnabled()) {
|
|
||||||
$this->markTestSkipped('Test calls deprecated code');
|
|
||||||
}
|
|
||||||
$page1 = $this->objFromFixture(Page::class, "page1");
|
|
||||||
$page2 = $this->objFromFixture(Page::class, "page2");
|
|
||||||
$this->logInAs('admin');
|
|
||||||
|
|
||||||
$response = $this->get('admin/pages/publishall?confirm=1');
|
|
||||||
$this->assertStringContainsString(
|
|
||||||
'Done: Published 30 pages',
|
|
||||||
$response->getBody()
|
|
||||||
);
|
|
||||||
|
|
||||||
// Some modules (e.g., cmsworkflow) will remove this action
|
|
||||||
$actions = CMSBatchActionHandler::config()->batch_actions;
|
|
||||||
if (isset($actions['publish'])) {
|
|
||||||
$response = $this->get('admin/pages/batchactions/publish?ajax=1&csvIDs=' . implode(',', [$page1->ID, $page2->ID]));
|
|
||||||
$responseData = json_decode($response->getBody() ?? '', true);
|
|
||||||
$this->assertArrayHasKey($page1->ID, $responseData['modified']);
|
|
||||||
$this->assertArrayHasKey($page2->ID, $responseData['modified']);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the latest version of the redirector page
|
|
||||||
$pageID = $this->idFromFixture(RedirectorPage::class, 'page5');
|
|
||||||
$latestID = DB::prepared_query('select max("Version") from "RedirectorPage_Versions" where "RecordID" = ?', [$pageID])->value();
|
|
||||||
$dsCount = DB::prepared_query('select count("Version") from "RedirectorPage_Versions" where "RecordID" = ? and "Version"= ?', [$pageID, $latestID])->value();
|
|
||||||
$this->assertEquals(1, $dsCount, "Published page has no duplicate version records: it has " . $dsCount . " for version " . $latestID);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test that getCMSFields works on each page type.
|
* Test that getCMSFields works on each page type.
|
||||||
* Mostly, this is just checking that the method doesn't return an error
|
* Mostly, this is just checking that the method doesn't return an error
|
||||||
@ -185,8 +149,8 @@ class CMSMainTest extends FunctionalTest
|
|||||||
$this->logInWithPermission('ADMIN');
|
$this->logInWithPermission('ADMIN');
|
||||||
|
|
||||||
Config::modify()->set(SiteTree::class, 'enforce_strict_hierarchy', true);
|
Config::modify()->set(SiteTree::class, 'enforce_strict_hierarchy', true);
|
||||||
$parentPage = $this->objFromFixture(Page::class, 'page3');
|
$parentPage = $this->objFromFixture(SiteTree::class, 'page3');
|
||||||
$childPage = $this->objFromFixture(Page::class, 'page1');
|
$childPage = $this->objFromFixture(SiteTree::class, 'page1');
|
||||||
|
|
||||||
$parentPage->doUnpublish();
|
$parentPage->doUnpublish();
|
||||||
$childPage->doUnpublish();
|
$childPage->doUnpublish();
|
||||||
@ -208,7 +172,7 @@ class CMSMainTest extends FunctionalTest
|
|||||||
$this->logInWithPermission('ADMIN');
|
$this->logInWithPermission('ADMIN');
|
||||||
|
|
||||||
// Set up a page that is delete from live
|
// Set up a page that is delete from live
|
||||||
$page = $this->objFromFixture(Page::class, 'page1');
|
$page = $this->objFromFixture(SiteTree::class, 'page1');
|
||||||
$pageID = $page->ID;
|
$pageID = $page->ID;
|
||||||
$page->publishRecursive();
|
$page->publishRecursive();
|
||||||
$page->delete();
|
$page->delete();
|
||||||
@ -216,7 +180,7 @@ class CMSMainTest extends FunctionalTest
|
|||||||
$response = $this->get('admin/pages/edit/show/' . $pageID);
|
$response = $this->get('admin/pages/edit/show/' . $pageID);
|
||||||
|
|
||||||
$livePage = Versioned::get_one_by_stage(SiteTree::class, Versioned::LIVE, [
|
$livePage = Versioned::get_one_by_stage(SiteTree::class, Versioned::LIVE, [
|
||||||
'"SiteTree"."ID"' => $pageID
|
'"SiteTree"."ID"' => $pageID,
|
||||||
]);
|
]);
|
||||||
$this->assertInstanceOf(SiteTree::class, $livePage);
|
$this->assertInstanceOf(SiteTree::class, $livePage);
|
||||||
$this->assertTrue($livePage->canDelete());
|
$this->assertTrue($livePage->canDelete());
|
||||||
@ -233,7 +197,7 @@ class CMSMainTest extends FunctionalTest
|
|||||||
$this->logInWithPermission('ADMIN');
|
$this->logInWithPermission('ADMIN');
|
||||||
|
|
||||||
// Set up a page that is delete from live
|
// Set up a page that is delete from live
|
||||||
$page1 = $this->objFromFixture(Page::class, 'page1');
|
$page1 = $this->objFromFixture(SiteTree::class, 'page1');
|
||||||
$page1ID = $page1->ID;
|
$page1ID = $page1->ID;
|
||||||
$page1->publishRecursive();
|
$page1->publishRecursive();
|
||||||
$page1->delete();
|
$page1->delete();
|
||||||
@ -246,18 +210,18 @@ class CMSMainTest extends FunctionalTest
|
|||||||
$this->assertNull($cmsMain->getRecord('asdf'));
|
$this->assertNull($cmsMain->getRecord('asdf'));
|
||||||
|
|
||||||
// Pages that are on draft and aren't on draft should both work
|
// Pages that are on draft and aren't on draft should both work
|
||||||
$this->assertInstanceOf('Page', $cmsMain->getRecord($page1ID));
|
$this->assertInstanceOf(SiteTree::class, $cmsMain->getRecord($page1ID));
|
||||||
$this->assertInstanceOf('Page', $cmsMain->getRecord($this->idFromFixture('Page', 'page2')));
|
$this->assertInstanceOf(SiteTree::class, $cmsMain->getRecord($this->idFromFixture(SiteTree::class, 'page2')));
|
||||||
|
|
||||||
// This functionality isn't actually used any more.
|
// This functionality isn't actually used any more.
|
||||||
$newPage = $cmsMain->getRecord('new-Page-5');
|
$newPage = $cmsMain->getRecord('new-Page-5');
|
||||||
$this->assertInstanceOf('Page', $newPage);
|
$this->assertInstanceOf(SiteTree::class, $newPage);
|
||||||
$this->assertEquals('5', $newPage->ParentID);
|
$this->assertEquals('5', $newPage->ParentID);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testDeletedPagesSiteTreeFilter()
|
public function testDeletedPagesSiteTreeFilter()
|
||||||
{
|
{
|
||||||
$id = $this->idFromFixture('Page', 'page3');
|
$id = $this->idFromFixture(SiteTree::class, 'page3');
|
||||||
$this->logInWithPermission('ADMIN');
|
$this->logInWithPermission('ADMIN');
|
||||||
$result = $this->get('admin/pages/getsubtree?filter=CMSSiteTreeFilter_DeletedPages&ajax=1&ID=' . $id);
|
$result = $this->get('admin/pages/getsubtree?filter=CMSSiteTreeFilter_DeletedPages&ajax=1&ID=' . $id);
|
||||||
$this->assertEquals(200, $result->getStatusCode());
|
$this->assertEquals(200, $result->getStatusCode());
|
||||||
@ -278,7 +242,7 @@ class CMSMainTest extends FunctionalTest
|
|||||||
'admin/pages/add/AddForm',
|
'admin/pages/add/AddForm',
|
||||||
[
|
[
|
||||||
'ParentID' => '0',
|
'ParentID' => '0',
|
||||||
'PageType' => 'Page',
|
'PageType' => RedirectorPage::class,
|
||||||
'Locale' => 'en_US',
|
'Locale' => 'en_US',
|
||||||
'action_doAdd' => 1,
|
'action_doAdd' => 1,
|
||||||
'ajax' => 1,
|
'ajax' => 1,
|
||||||
@ -298,7 +262,7 @@ class CMSMainTest extends FunctionalTest
|
|||||||
'admin/pages/add/AddForm',
|
'admin/pages/add/AddForm',
|
||||||
[
|
[
|
||||||
'ParentID' => '0',
|
'ParentID' => '0',
|
||||||
'PageType' => 'Page',
|
'PageType' => RedirectorPage::class,
|
||||||
'Locale' => 'en_US',
|
'Locale' => 'en_US',
|
||||||
'action_doAdd' => 1,
|
'action_doAdd' => 1,
|
||||||
'ajax' => 1,
|
'ajax' => 1,
|
||||||
@ -332,7 +296,7 @@ class CMSMainTest extends FunctionalTest
|
|||||||
'PageType' => CMSMainTest_ClassA::class,
|
'PageType' => CMSMainTest_ClassA::class,
|
||||||
'Locale' => 'en_US',
|
'Locale' => 'en_US',
|
||||||
'action_doAdd' => 1,
|
'action_doAdd' => 1,
|
||||||
'ajax' => 1
|
'ajax' => 1,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'X-Pjax' => 'CurrentForm,Breadcrumbs',
|
'X-Pjax' => 'CurrentForm,Breadcrumbs',
|
||||||
@ -352,7 +316,7 @@ class CMSMainTest extends FunctionalTest
|
|||||||
'PageType' => CMSMainTest_ClassB::class,
|
'PageType' => CMSMainTest_ClassB::class,
|
||||||
'Locale' => 'en_US',
|
'Locale' => 'en_US',
|
||||||
'action_doAdd' => 1,
|
'action_doAdd' => 1,
|
||||||
'ajax' => 1
|
'ajax' => 1,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'X-Pjax' => 'CurrentForm,Breadcrumbs',
|
'X-Pjax' => 'CurrentForm,Breadcrumbs',
|
||||||
@ -375,10 +339,10 @@ class CMSMainTest extends FunctionalTest
|
|||||||
'admin/pages/add/AddForm',
|
'admin/pages/add/AddForm',
|
||||||
[
|
[
|
||||||
'ParentID' => $newPageId,
|
'ParentID' => $newPageId,
|
||||||
'PageType' => 'Page',
|
'PageType' => RedirectorPage::class,
|
||||||
'Locale' => 'en_US',
|
'Locale' => 'en_US',
|
||||||
'action_doAdd' => 1,
|
'action_doAdd' => 1,
|
||||||
'ajax' => 1
|
'ajax' => 1,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'X-Pjax' => 'CurrentForm,Breadcrumbs',
|
'X-Pjax' => 'CurrentForm,Breadcrumbs',
|
||||||
@ -393,8 +357,8 @@ class CMSMainTest extends FunctionalTest
|
|||||||
|
|
||||||
public function testBreadcrumbs()
|
public function testBreadcrumbs()
|
||||||
{
|
{
|
||||||
$page3 = $this->objFromFixture(Page::class, 'page3');
|
$page3 = $this->objFromFixture(SiteTree::class, 'page3');
|
||||||
$page31 = $this->objFromFixture(Page::class, 'page31');
|
$page31 = $this->objFromFixture(SiteTree::class, 'page31');
|
||||||
$this->logInAs('admin');
|
$this->logInAs('admin');
|
||||||
|
|
||||||
$response = $this->get('admin/pages/edit/show/' . $page31->ID);
|
$response = $this->get('admin/pages/edit/show/' . $page31->ID);
|
||||||
@ -420,7 +384,7 @@ class CMSMainTest extends FunctionalTest
|
|||||||
|
|
||||||
$this->assertEquals($page->Title, 'New Page');
|
$this->assertEquals($page->Title, 'New Page');
|
||||||
$this->assertNotEquals($page->Sort, 0);
|
$this->assertNotEquals($page->Sort, 0);
|
||||||
$this->assertInstanceOf('Page', $page);
|
$this->assertInstanceOf(SiteTree::class, $page);
|
||||||
|
|
||||||
// Test failure
|
// Test failure
|
||||||
try {
|
try {
|
||||||
@ -449,10 +413,10 @@ class CMSMainTest extends FunctionalTest
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Change state of tree
|
// Change state of tree
|
||||||
$page1 = $this->objFromFixture(Page::class, 'page1');
|
$page1 = $this->objFromFixture(SiteTree::class, 'page1');
|
||||||
$page3 = $this->objFromFixture(Page::class, 'page3');
|
$page3 = $this->objFromFixture(SiteTree::class, 'page3');
|
||||||
$page11 = $this->objFromFixture(Page::class, 'page11');
|
$page11 = $this->objFromFixture(SiteTree::class, 'page11');
|
||||||
$page12 = $this->objFromFixture(Page::class, 'page12');
|
$page12 = $this->objFromFixture(SiteTree::class, 'page12');
|
||||||
// Deleted
|
// Deleted
|
||||||
$page1->doUnpublish();
|
$page1->doUnpublish();
|
||||||
$page1->delete();
|
$page1->delete();
|
||||||
@ -472,7 +436,7 @@ class CMSMainTest extends FunctionalTest
|
|||||||
|
|
||||||
// Test deleted page filter
|
// Test deleted page filter
|
||||||
$params = [
|
$params = [
|
||||||
'FilterClass' => 'SilverStripe\\CMS\\Controllers\\CMSSiteTreeFilter_StatusDeletedPages'
|
'FilterClass' => 'SilverStripe\\CMS\\Controllers\\CMSSiteTreeFilter_StatusDeletedPages',
|
||||||
];
|
];
|
||||||
$pages = $controller->getList($params);
|
$pages = $controller->getList($params);
|
||||||
$this->assertEquals(1, $pages->count());
|
$this->assertEquals(1, $pages->count());
|
||||||
@ -483,7 +447,7 @@ class CMSMainTest extends FunctionalTest
|
|||||||
|
|
||||||
// Test live, but not on draft filter
|
// Test live, but not on draft filter
|
||||||
$params = [
|
$params = [
|
||||||
'FilterClass' => 'SilverStripe\\CMS\\Controllers\\CMSSiteTreeFilter_StatusRemovedFromDraftPages'
|
'FilterClass' => 'SilverStripe\\CMS\\Controllers\\CMSSiteTreeFilter_StatusRemovedFromDraftPages',
|
||||||
];
|
];
|
||||||
$pages = $controller->getList($params);
|
$pages = $controller->getList($params);
|
||||||
$this->assertEquals(1, $pages->count());
|
$this->assertEquals(1, $pages->count());
|
||||||
@ -494,7 +458,7 @@ class CMSMainTest extends FunctionalTest
|
|||||||
|
|
||||||
// Test live pages filter
|
// Test live pages filter
|
||||||
$params = [
|
$params = [
|
||||||
'FilterClass' => 'SilverStripe\\CMS\\Controllers\\CMSSiteTreeFilter_PublishedPages'
|
'FilterClass' => 'SilverStripe\\CMS\\Controllers\\CMSSiteTreeFilter_PublishedPages',
|
||||||
];
|
];
|
||||||
$pages = $controller->getList($params);
|
$pages = $controller->getList($params);
|
||||||
$this->assertEquals(2, $pages->count());
|
$this->assertEquals(2, $pages->count());
|
||||||
@ -529,7 +493,7 @@ class CMSMainTest extends FunctionalTest
|
|||||||
$this->loginWithPermission('ADMIN');
|
$this->loginWithPermission('ADMIN');
|
||||||
|
|
||||||
// Get a associated with a fixture page.
|
// Get a associated with a fixture page.
|
||||||
$page = $this->objFromFixture(Page::class, 'page1');
|
$page = $this->objFromFixture(SiteTree::class, 'page1');
|
||||||
$controller = CMSMain::create();
|
$controller = CMSMain::create();
|
||||||
$controller->setRequest(Controller::curr()->getRequest());
|
$controller->setRequest(Controller::curr()->getRequest());
|
||||||
$form = $controller->getEditForm($page->ID);
|
$form = $controller->getEditForm($page->ID);
|
||||||
@ -557,7 +521,7 @@ class CMSMainTest extends FunctionalTest
|
|||||||
$form->loadDataFrom(['ClassName' => CMSMainTest_ClassB::class]);
|
$form->loadDataFrom(['ClassName' => CMSMainTest_ClassB::class]);
|
||||||
$result = $cms->save([
|
$result = $cms->save([
|
||||||
'ID' => $page->ID,
|
'ID' => $page->ID,
|
||||||
'ClassName' => CMSMainTest_ClassB::class
|
'ClassName' => CMSMainTest_ClassB::class,
|
||||||
], $form);
|
], $form);
|
||||||
$this->assertEquals(200, $result->getStatusCode());
|
$this->assertEquals(200, $result->getStatusCode());
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
Page:
|
SilverStripe\CMS\Model\SiteTree:
|
||||||
page1:
|
page1:
|
||||||
Title: Page 1
|
Title: Page 1
|
||||||
Sort: 1
|
Sort: 1
|
||||||
@ -10,11 +10,11 @@ Page:
|
|||||||
Sort: 3
|
Sort: 3
|
||||||
page31:
|
page31:
|
||||||
Title: Page 3.1
|
Title: Page 3.1
|
||||||
Parent: =>Page.page3
|
Parent: =>SilverStripe\CMS\Model\SiteTree.page3
|
||||||
Sort: 1
|
Sort: 1
|
||||||
page32:
|
page32:
|
||||||
Title: Page 3.2
|
Title: Page 3.2
|
||||||
Parent: =>Page.page3
|
Parent: =>SilverStripe\CMS\Model\SiteTree.page3
|
||||||
Sort: 2
|
Sort: 2
|
||||||
page4:
|
page4:
|
||||||
Title: Page 4
|
Title: Page 4
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
namespace SilverStripe\CMS\Tests\Controllers;
|
namespace SilverStripe\CMS\Tests\Controllers;
|
||||||
|
|
||||||
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
use SilverStripe\Dev\TestOnly;
|
use SilverStripe\Dev\TestOnly;
|
||||||
use SilverStripe\ORM\ValidationException;
|
use SilverStripe\ORM\ValidationException;
|
||||||
use Page;
|
|
||||||
|
|
||||||
class CMSMainTest_ClassA extends Page implements TestOnly
|
class CMSMainTest_ClassA extends SiteTree implements TestOnly
|
||||||
{
|
{
|
||||||
private static $table_name = 'CMSMainTest_ClassA';
|
private static $table_name = 'CMSMainTest_ClassA';
|
||||||
|
|
||||||
|
@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
namespace SilverStripe\CMS\Tests\Controllers;
|
namespace SilverStripe\CMS\Tests\Controllers;
|
||||||
|
|
||||||
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
use SilverStripe\Dev\TestOnly;
|
use SilverStripe\Dev\TestOnly;
|
||||||
use SilverStripe\ORM\ValidationException;
|
use SilverStripe\ORM\ValidationException;
|
||||||
use Page;
|
|
||||||
|
|
||||||
class CMSMainTest_ClassB extends Page implements TestOnly
|
class CMSMainTest_ClassB extends SiteTree implements TestOnly
|
||||||
{
|
{
|
||||||
private static $table_name = 'CMSMainTest_ClassB';
|
private static $table_name = 'CMSMainTest_ClassB';
|
||||||
|
|
||||||
|
@ -2,11 +2,10 @@
|
|||||||
|
|
||||||
namespace SilverStripe\CMS\Tests\Controllers;
|
namespace SilverStripe\CMS\Tests\Controllers;
|
||||||
|
|
||||||
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
use SilverStripe\Dev\TestOnly;
|
use SilverStripe\Dev\TestOnly;
|
||||||
use SilverStripe\ORM\HiddenClass;
|
use SilverStripe\ORM\HiddenClass;
|
||||||
use Page;
|
|
||||||
|
|
||||||
class CMSMainTest_HiddenClass extends Page implements TestOnly, HiddenClass
|
class CMSMainTest_HiddenClass extends SiteTree implements TestOnly, HiddenClass
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
namespace SilverStripe\CMS\Tests\Controllers;
|
namespace SilverStripe\CMS\Tests\Controllers;
|
||||||
|
|
||||||
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
use SilverStripe\Dev\TestOnly;
|
use SilverStripe\Dev\TestOnly;
|
||||||
use Page;
|
|
||||||
|
|
||||||
class CMSMainTest_NotRoot extends Page implements TestOnly
|
class CMSMainTest_NotRoot extends SiteTree implements TestOnly
|
||||||
{
|
{
|
||||||
private static $table_name = 'CMSMainTest_NotRoot';
|
private static $table_name = 'CMSMainTest_NotRoot';
|
||||||
|
|
||||||
|
@ -1,219 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace SilverStripe\CMS\Tests\Controllers;
|
|
||||||
|
|
||||||
use Page;
|
|
||||||
use SilverStripe\CMS\Controllers\CMSPageHistoryController;
|
|
||||||
use SilverStripe\CMS\Tests\Controllers\CMSPageHistoryControllerTest\HistoryController;
|
|
||||||
use SilverStripe\Control\Controller;
|
|
||||||
use SilverStripe\Core\Injector\Injector;
|
|
||||||
use SilverStripe\Dev\FunctionalTest;
|
|
||||||
use SilverStripe\Forms\FieldGroup;
|
|
||||||
use SilverStripe\Forms\FieldList;
|
|
||||||
use SilverStripe\Forms\HiddenField;
|
|
||||||
use SilverStripe\Forms\HTMLReadonlyField;
|
|
||||||
use SilverStripe\Dev\Deprecation;
|
|
||||||
use SilverStripe\Forms\TextField;
|
|
||||||
|
|
||||||
class CMSPageHistoryControllerTest extends FunctionalTest
|
|
||||||
{
|
|
||||||
protected static $fixture_file = 'CMSPageHistoryControllerTest.yml';
|
|
||||||
|
|
||||||
protected $versionUnpublishedCheck;
|
|
||||||
protected $versionPublishCheck;
|
|
||||||
protected $versionUnpublishedCheck2;
|
|
||||||
protected $versionPublishCheck2;
|
|
||||||
protected $page;
|
|
||||||
|
|
||||||
protected static $extra_controllers = [
|
|
||||||
CMSPageHistoryControllerTest\HistoryController::class,
|
|
||||||
];
|
|
||||||
|
|
||||||
protected function setUp(): void
|
|
||||||
{
|
|
||||||
parent::setUp();
|
|
||||||
|
|
||||||
Deprecation::withNoReplacement(function () {
|
|
||||||
Injector::inst()->registerService(
|
|
||||||
new CMSPageHistoryController(),
|
|
||||||
CMSPageHistoryController::class
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
$this->loginWithPermission('ADMIN');
|
|
||||||
|
|
||||||
// creates a series of published, unpublished versions of a page
|
|
||||||
$this->page = new Page();
|
|
||||||
$this->page->URLSegment = "test";
|
|
||||||
$this->page->Content = "new content";
|
|
||||||
$this->page->write();
|
|
||||||
$this->versionUnpublishedCheck = $this->page->Version; // v1
|
|
||||||
|
|
||||||
$this->page->Content = "some further content";
|
|
||||||
$this->page->publishSingle();
|
|
||||||
$this->versionPublishCheck = $this->page->Version; // v2
|
|
||||||
|
|
||||||
$this->page->Content = "No, more changes please";
|
|
||||||
$this->page->Title = "Changing titles too";
|
|
||||||
$this->page->write();
|
|
||||||
$this->versionUnpublishedCheck2 = $this->page->Version; // v3
|
|
||||||
|
|
||||||
$this->page->Title = "Final Change";
|
|
||||||
$this->page->publishSingle();
|
|
||||||
$this->versionPublishCheck2 = $this->page->Version; // v4
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetEditForm()
|
|
||||||
{
|
|
||||||
if (Deprecation::isEnabled()) {
|
|
||||||
$this->markTestSkipped('Test calls deprecated code');
|
|
||||||
}
|
|
||||||
$controller = new CMSPageHistoryController();
|
|
||||||
$controller->setRequest(Controller::curr()->getRequest());
|
|
||||||
|
|
||||||
// should get the latest version which we cannot rollback to
|
|
||||||
$form = $controller->getEditForm($this->page->ID);
|
|
||||||
|
|
||||||
$this->assertTrue($form->Actions()->dataFieldByName('action_doRollback')->isReadonly());
|
|
||||||
|
|
||||||
$this->assertEquals($this->page->ID, $form->Fields()->dataFieldByName('ID')->Value());
|
|
||||||
$this->assertEquals($this->versionPublishCheck2, $form->Fields()->dataFieldByName('Version')->Value());
|
|
||||||
|
|
||||||
$this->assertStringContainsString(
|
|
||||||
'Currently viewing the latest version',
|
|
||||||
$form->Fields()->fieldByName('Root.Main.CurrentlyViewingMessage')->getContent()
|
|
||||||
);
|
|
||||||
|
|
||||||
// edit form with a given version
|
|
||||||
$form = $controller->getEditForm($this->page->ID, null, $this->versionPublishCheck);
|
|
||||||
$this->assertFalse($form->Actions()->dataFieldByName('action_doRollback')->isReadonly());
|
|
||||||
|
|
||||||
$this->assertEquals($this->page->ID, $form->Fields()->dataFieldByName('ID')->Value());
|
|
||||||
$this->assertEquals($this->versionPublishCheck, $form->Fields()->dataFieldByName('Version')->Value());
|
|
||||||
$this->assertStringContainsString(
|
|
||||||
sprintf("Currently viewing version %s.", $this->versionPublishCheck),
|
|
||||||
$form->Fields()->fieldByName('Root.Main.CurrentlyViewingMessage')->getContent()
|
|
||||||
);
|
|
||||||
|
|
||||||
// check that compare mode updates the message
|
|
||||||
$form = $controller->getEditForm($this->page->ID, null, $this->versionPublishCheck, $this->versionPublishCheck2);
|
|
||||||
$this->assertStringContainsString(
|
|
||||||
sprintf("Comparing versions %s", $this->versionPublishCheck),
|
|
||||||
$form->Fields()->fieldByName('Root.Main.CurrentlyViewingMessage')->getContent()
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->assertStringContainsString(
|
|
||||||
sprintf("and %s", $this->versionPublishCheck2),
|
|
||||||
$form->Fields()->fieldByName('Root.Main.CurrentlyViewingMessage')->getContent()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @todo should be less tied to cms theme.
|
|
||||||
* @todo check highlighting for comparing pages.
|
|
||||||
*/
|
|
||||||
public function testVersionsForm()
|
|
||||||
{
|
|
||||||
if (Deprecation::isEnabled()) {
|
|
||||||
$this->markTestSkipped('Test calls deprecated code');
|
|
||||||
}
|
|
||||||
$this->get('admin/pages/legacyhistory/show/' . $this->page->ID);
|
|
||||||
|
|
||||||
$form = $this->cssParser()->getBySelector('#Form_VersionsForm');
|
|
||||||
|
|
||||||
$this->assertEquals(1, count($form ?? []));
|
|
||||||
|
|
||||||
// check the page ID is present
|
|
||||||
$hidden = $form[0]->xpath("fieldset/input[@type='hidden']");
|
|
||||||
|
|
||||||
$this->assertThat($hidden, $this->logicalNot($this->isNull()), 'Hidden ID field exists');
|
|
||||||
$this->assertEquals($this->page->ID, (int) $hidden[0]->attributes()->value);
|
|
||||||
|
|
||||||
// ensure that all the versions are present in the table and displayed
|
|
||||||
$rows = $form[0]->xpath("fieldset/table/tbody/tr");
|
|
||||||
$this->assertEquals(4, count($rows ?? []));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testVersionsFormTableContainsInformation()
|
|
||||||
{
|
|
||||||
if (Deprecation::isEnabled()) {
|
|
||||||
$this->markTestSkipped('Test calls deprecated code');
|
|
||||||
}
|
|
||||||
$this->get('admin/pages/legacyhistory/show/' . $this->page->ID);
|
|
||||||
$form = $this->cssParser()->getBySelector('#Form_VersionsForm');
|
|
||||||
$rows = $form[0]->xpath("fieldset/table/tbody/tr");
|
|
||||||
|
|
||||||
$expected = [
|
|
||||||
['version' => $this->versionPublishCheck2, 'status' => 'published'],
|
|
||||||
['version' => $this->versionUnpublishedCheck2, 'status' => 'internal'],
|
|
||||||
['version' => $this->versionPublishCheck, 'status' => 'published'],
|
|
||||||
['version' => $this->versionUnpublishedCheck, 'status' => 'internal']
|
|
||||||
];
|
|
||||||
|
|
||||||
// goes the reverse order that we created in setUp()
|
|
||||||
$i = 0;
|
|
||||||
foreach ($rows as $tr) {
|
|
||||||
// data-link must be present for the javascript to load new
|
|
||||||
$this->assertStringContainsString($expected[$i]['status'], (string) $tr->attributes()->class);
|
|
||||||
$i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// test highlighting
|
|
||||||
$this->assertStringContainsString('active', (string) $rows[0]->attributes()->class);
|
|
||||||
$this->assertThat((string) $rows[1]->attributes()->class, $this->logicalNot($this->stringContains('active')));
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testVersionsFormSelectsUnpublishedCheckbox()
|
|
||||||
{
|
|
||||||
if (Deprecation::isEnabled()) {
|
|
||||||
$this->markTestSkipped('Test calls deprecated code');
|
|
||||||
}
|
|
||||||
$this->get('admin/pages/legacyhistory/show/' . $this->page->ID);
|
|
||||||
$checkbox = $this->cssParser()->getBySelector('#Form_VersionsForm_ShowUnpublished');
|
|
||||||
|
|
||||||
$this->assertThat($checkbox[0], $this->logicalNot($this->isNull()));
|
|
||||||
$checked = $checkbox[0]->attributes()->checked ?: '';
|
|
||||||
|
|
||||||
$this->assertThat($checked, $this->logicalNot($this->stringContains('checked')));
|
|
||||||
|
|
||||||
// viewing an unpublished
|
|
||||||
$this->get('admin/pages/legacyhistory/show/' . $this->page->ID . '/' . $this->versionUnpublishedCheck);
|
|
||||||
$checkbox = $this->cssParser()->getBySelector('#Form_VersionsForm_ShowUnpublished');
|
|
||||||
|
|
||||||
$this->assertThat($checkbox[0], $this->logicalNot($this->isNull()));
|
|
||||||
$this->assertEquals('checked', (string) $checkbox[0]->attributes()->checked);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testTransformReadonly()
|
|
||||||
{
|
|
||||||
if (Deprecation::isEnabled()) {
|
|
||||||
$this->markTestSkipped('Test calls deprecated code');
|
|
||||||
}
|
|
||||||
/** @var CMSPageHistoryController $history */
|
|
||||||
$history = new CMSPageHistoryController();
|
|
||||||
$history->setRequest(Controller::curr()->getRequest());
|
|
||||||
|
|
||||||
$fieldList = FieldList::create([
|
|
||||||
FieldGroup::create('group', [
|
|
||||||
TextField::create('childField', 'child field'),
|
|
||||||
]),
|
|
||||||
TextField::create('field', 'field', 'My <del>value</del><ins>change</ins>'),
|
|
||||||
HiddenField::create('hiddenField', 'hidden field'),
|
|
||||||
]);
|
|
||||||
|
|
||||||
$newList = $history->transformReadonly($fieldList);
|
|
||||||
|
|
||||||
$field = $newList->dataFieldByName('field');
|
|
||||||
$this->assertTrue($field instanceof HTMLReadonlyField);
|
|
||||||
$this->assertStringContainsString('<ins>', $field->forTemplate());
|
|
||||||
|
|
||||||
$groupField = $newList->fieldByName('group');
|
|
||||||
$this->assertTrue($groupField instanceof FieldGroup);
|
|
||||||
|
|
||||||
$childField = $newList->dataFieldByName('childField');
|
|
||||||
$this->assertTrue($childField instanceof HTMLReadonlyField);
|
|
||||||
|
|
||||||
$hiddenField = $newList->dataFieldByName('hiddenField');
|
|
||||||
$this->assertTrue($hiddenField instanceof HiddenField);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
Page:
|
|
||||||
page1:
|
|
||||||
Title: Page 1
|
|
||||||
Sort: 1
|
|
||||||
page2:
|
|
||||||
Title: Page 2
|
|
||||||
Sort: 2
|
|
||||||
page3:
|
|
||||||
Title: Page 3
|
|
||||||
Sort: 3
|
|
@ -1,15 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace SilverStripe\CMS\Tests\Controllers\CMSPageHistoryControllerTest;
|
|
||||||
|
|
||||||
use SilverStripe\CMS\Controllers\CMSPageHistoryController;
|
|
||||||
use SilverStripe\Dev\TestOnly;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to circumvent potential URL conflicts with the silverstripe/versioned-admin history viewer controller
|
|
||||||
* when running unit tests on the legacy CMSPageHistoryController.
|
|
||||||
*/
|
|
||||||
class HistoryController extends CMSPageHistoryController implements TestOnly
|
|
||||||
{
|
|
||||||
private static $url_segment = 'pages/legacyhistory';
|
|
||||||
}
|
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace SilverStripe\CMS\Tests\Controllers;
|
namespace SilverStripe\CMS\Tests\Controllers;
|
||||||
|
|
||||||
use Page;
|
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
use SilverStripe\Versioned\Versioned;
|
use SilverStripe\Versioned\Versioned;
|
||||||
use SilverStripe\CMS\Controllers\CMSSiteTreeFilter_Search;
|
use SilverStripe\CMS\Controllers\CMSSiteTreeFilter_Search;
|
||||||
@ -15,13 +14,12 @@ use SilverStripe\Dev\SapphireTest;
|
|||||||
|
|
||||||
class CMSSiteTreeFilterTest extends SapphireTest
|
class CMSSiteTreeFilterTest extends SapphireTest
|
||||||
{
|
{
|
||||||
|
|
||||||
protected static $fixture_file = 'CMSSiteTreeFilterTest.yml';
|
protected static $fixture_file = 'CMSSiteTreeFilterTest.yml';
|
||||||
|
|
||||||
public function testSearchFilterEmpty()
|
public function testSearchFilterEmpty()
|
||||||
{
|
{
|
||||||
$page1 = $this->objFromFixture('Page', 'page1');
|
$page1 = $this->objFromFixture(SiteTree::class, 'page1');
|
||||||
$page2 = $this->objFromFixture('Page', 'page2');
|
$page2 = $this->objFromFixture(SiteTree::class, 'page2');
|
||||||
|
|
||||||
$f = new CMSSiteTreeFilter_Search();
|
$f = new CMSSiteTreeFilter_Search();
|
||||||
$results = $f->pagesIncluded();
|
$results = $f->pagesIncluded();
|
||||||
@ -32,8 +30,8 @@ class CMSSiteTreeFilterTest extends SapphireTest
|
|||||||
|
|
||||||
public function testSearchFilterByTitle()
|
public function testSearchFilterByTitle()
|
||||||
{
|
{
|
||||||
$page1 = $this->objFromFixture('Page', 'page1');
|
$page1 = $this->objFromFixture(SiteTree::class, 'page1');
|
||||||
$page2 = $this->objFromFixture('Page', 'page2');
|
$page2 = $this->objFromFixture(SiteTree::class, 'page2');
|
||||||
|
|
||||||
$f = new CMSSiteTreeFilter_Search(['Title' => 'Page 1']);
|
$f = new CMSSiteTreeFilter_Search(['Title' => 'Page 1']);
|
||||||
$results = $f->pagesIncluded();
|
$results = $f->pagesIncluded();
|
||||||
@ -49,7 +47,7 @@ class CMSSiteTreeFilterTest extends SapphireTest
|
|||||||
|
|
||||||
public function testUrlSegmentFilter()
|
public function testUrlSegmentFilter()
|
||||||
{
|
{
|
||||||
$page = $this->objFromFixture(Page::class, 'page8');
|
$page = $this->objFromFixture(SiteTree::class, 'page8');
|
||||||
|
|
||||||
$filter = CMSSiteTreeFilter_Search::create(['Term' => 'lake-wanaka+adventure']);
|
$filter = CMSSiteTreeFilter_Search::create(['Term' => 'lake-wanaka+adventure']);
|
||||||
$this->assertTrue($filter->isPageIncluded($page));
|
$this->assertTrue($filter->isPageIncluded($page));
|
||||||
@ -60,8 +58,8 @@ class CMSSiteTreeFilterTest extends SapphireTest
|
|||||||
|
|
||||||
public function testIncludesParentsForNestedMatches()
|
public function testIncludesParentsForNestedMatches()
|
||||||
{
|
{
|
||||||
$parent = $this->objFromFixture('Page', 'page3');
|
$parent = $this->objFromFixture(SiteTree::class, 'page3');
|
||||||
$child = $this->objFromFixture('Page', 'page3b');
|
$child = $this->objFromFixture(SiteTree::class, 'page3b');
|
||||||
|
|
||||||
$f = new CMSSiteTreeFilter_Search(['Title' => 'Page 3b']);
|
$f = new CMSSiteTreeFilter_Search(['Title' => 'Page 3b']);
|
||||||
$results = $f->pagesIncluded();
|
$results = $f->pagesIncluded();
|
||||||
@ -78,11 +76,11 @@ class CMSSiteTreeFilterTest extends SapphireTest
|
|||||||
public function testChangedPagesFilter()
|
public function testChangedPagesFilter()
|
||||||
{
|
{
|
||||||
/** @var Page $unchangedPage */
|
/** @var Page $unchangedPage */
|
||||||
$unchangedPage = $this->objFromFixture('Page', 'page1');
|
$unchangedPage = $this->objFromFixture(SiteTree::class, 'page1');
|
||||||
$unchangedPage->publishRecursive();
|
$unchangedPage->publishRecursive();
|
||||||
|
|
||||||
/** @var Page $changedPage */
|
/** @var Page $changedPage */
|
||||||
$changedPage = $this->objFromFixture('Page', 'page2');
|
$changedPage = $this->objFromFixture(SiteTree::class, 'page2');
|
||||||
$changedPage->Title = 'Original';
|
$changedPage->Title = 'Original';
|
||||||
$changedPage->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
$changedPage->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
||||||
$changedPage->Title = 'Changed';
|
$changedPage->Title = 'Changed';
|
||||||
@ -121,7 +119,7 @@ class CMSSiteTreeFilterTest extends SapphireTest
|
|||||||
|
|
||||||
public function testDeletedPagesFilter()
|
public function testDeletedPagesFilter()
|
||||||
{
|
{
|
||||||
$deletedPage = $this->objFromFixture('Page', 'page2');
|
$deletedPage = $this->objFromFixture(SiteTree::class, 'page2');
|
||||||
$deletedPage->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
$deletedPage->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
||||||
$deletedPageID = $deletedPage->ID;
|
$deletedPageID = $deletedPage->ID;
|
||||||
$deletedPage->delete();
|
$deletedPage->delete();
|
||||||
@ -141,7 +139,7 @@ class CMSSiteTreeFilterTest extends SapphireTest
|
|||||||
|
|
||||||
public function testStatusDraftPagesFilter()
|
public function testStatusDraftPagesFilter()
|
||||||
{
|
{
|
||||||
$draftPage = $this->objFromFixture('Page', 'page4');
|
$draftPage = $this->objFromFixture(SiteTree::class, 'page4');
|
||||||
$draftPage = Versioned::get_one_by_stage(
|
$draftPage = Versioned::get_one_by_stage(
|
||||||
SiteTree::class,
|
SiteTree::class,
|
||||||
'Stage',
|
'Stage',
|
||||||
@ -164,20 +162,23 @@ class CMSSiteTreeFilterTest extends SapphireTest
|
|||||||
|
|
||||||
public function testDateFromToLastSameDate()
|
public function testDateFromToLastSameDate()
|
||||||
{
|
{
|
||||||
$draftPage = $this->objFromFixture('Page', 'page4');
|
$draftPage = $this->objFromFixture(SiteTree::class, 'page4');
|
||||||
// Grab the date
|
// Grab the date
|
||||||
$date = substr($draftPage->LastEdited ?? '', 0, 10);
|
$date = substr($draftPage->LastEdited ?? '', 0, 10);
|
||||||
// Filter with that date
|
// Filter with that date
|
||||||
$filter = new CMSSiteTreeFilter_Search([
|
$filter = new CMSSiteTreeFilter_Search([
|
||||||
'LastEditedFrom' => $date,
|
'LastEditedFrom' => $date,
|
||||||
'LastEditedTo' => $date
|
'LastEditedTo' => $date,
|
||||||
]);
|
]);
|
||||||
$this->assertTrue($filter->isPageIncluded($draftPage), 'Using the same date for from and to should show find that page');
|
$this->assertTrue(
|
||||||
|
$filter->isPageIncluded($draftPage),
|
||||||
|
'Using the same date for from and to should show find that page'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testStatusRemovedFromDraftFilter()
|
public function testStatusRemovedFromDraftFilter()
|
||||||
{
|
{
|
||||||
$removedDraftPage = $this->objFromFixture('Page', 'page6');
|
$removedDraftPage = $this->objFromFixture(SiteTree::class, 'page6');
|
||||||
$removedDraftPage->publishRecursive();
|
$removedDraftPage->publishRecursive();
|
||||||
$removedDraftPage->deleteFromStage('Stage');
|
$removedDraftPage->deleteFromStage('Stage');
|
||||||
$removedDraftPage = Versioned::get_one_by_stage(
|
$removedDraftPage = Versioned::get_one_by_stage(
|
||||||
@ -202,7 +203,7 @@ class CMSSiteTreeFilterTest extends SapphireTest
|
|||||||
|
|
||||||
public function testStatusDeletedFilter()
|
public function testStatusDeletedFilter()
|
||||||
{
|
{
|
||||||
$deletedPage = $this->objFromFixture('Page', 'page7');
|
$deletedPage = $this->objFromFixture(SiteTree::class, 'page7');
|
||||||
$deletedPage->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
$deletedPage->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
||||||
$deletedPageID = $deletedPage->ID;
|
$deletedPageID = $deletedPage->ID;
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
Page:
|
SilverStripe\CMS\Model\SiteTree:
|
||||||
page1:
|
page1:
|
||||||
Title: Page 1
|
Title: Page 1
|
||||||
page2:
|
page2:
|
||||||
@ -15,19 +15,19 @@ Page:
|
|||||||
page7:
|
page7:
|
||||||
Title: Page 7
|
Title: Page 7
|
||||||
page7a:
|
page7a:
|
||||||
Parent: =>Page.page7
|
Parent: =>SilverStripe\CMS\Model\SiteTree.page7
|
||||||
Title: Page 7a
|
Title: Page 7a
|
||||||
page2a:
|
page2a:
|
||||||
Parent: =>Page.page2
|
Parent: =>SilverStripe\CMS\Model\SiteTree.page2
|
||||||
Title: Page 2a
|
Title: Page 2a
|
||||||
page2b:
|
page2b:
|
||||||
Parent: =>Page.page2
|
Parent: =>SilverStripe\CMS\Model\SiteTree.page2
|
||||||
Title: Page 2b
|
Title: Page 2b
|
||||||
page3a:
|
page3a:
|
||||||
Parent: =>Page.page3
|
Parent: =>SilverStripe\CMS\Model\SiteTree.page3
|
||||||
Title: Page 3a
|
Title: Page 3a
|
||||||
page3b:
|
page3b:
|
||||||
Parent: =>Page.page3
|
Parent: =>SilverStripe\CMS\Model\SiteTree.page3
|
||||||
Title: Page 3b
|
Title: Page 3b
|
||||||
page8:
|
page8:
|
||||||
Title: EncodedUrlSegment
|
Title: EncodedUrlSegment
|
||||||
|
@ -32,7 +32,7 @@ class CMSTreeTest extends FunctionalTest
|
|||||||
$data = [
|
$data = [
|
||||||
'SiblingIDs' => $siblingIDs,
|
'SiblingIDs' => $siblingIDs,
|
||||||
'ID' => $page2->ID,
|
'ID' => $page2->ID,
|
||||||
'ParentID' => 0
|
'ParentID' => 0,
|
||||||
];
|
];
|
||||||
|
|
||||||
$response = $this->post('admin/pages/edit/savetreenode', $data);
|
$response = $this->post('admin/pages/edit/savetreenode', $data);
|
||||||
@ -62,12 +62,12 @@ class CMSTreeTest extends FunctionalTest
|
|||||||
$siblingIDs = [
|
$siblingIDs = [
|
||||||
$page31->ID,
|
$page31->ID,
|
||||||
$page2->ID,
|
$page2->ID,
|
||||||
$page32->ID
|
$page32->ID,
|
||||||
];
|
];
|
||||||
$data = [
|
$data = [
|
||||||
'SiblingIDs' => $siblingIDs,
|
'SiblingIDs' => $siblingIDs,
|
||||||
'ID' => $page2->ID,
|
'ID' => $page2->ID,
|
||||||
'ParentID' => $page3->ID
|
'ParentID' => $page3->ID,
|
||||||
];
|
];
|
||||||
$response = $this->post('admin/pages/edit/savetreenode', $data);
|
$response = $this->post('admin/pages/edit/savetreenode', $data);
|
||||||
$this->assertEquals(200, $response->getStatusCode());
|
$this->assertEquals(200, $response->getStatusCode());
|
||||||
@ -95,7 +95,7 @@ class CMSTreeTest extends FunctionalTest
|
|||||||
$this->logInWithPermission('ADMIN');
|
$this->logInWithPermission('ADMIN');
|
||||||
|
|
||||||
// Check page
|
// Check page
|
||||||
$result = $this->get('admin/pages/edit/updatetreenodes?ids='.$page1->ID);
|
$result = $this->get('admin/pages/edit/updatetreenodes?ids=' . $page1->ID);
|
||||||
$this->assertEquals(200, $result->getStatusCode());
|
$this->assertEquals(200, $result->getStatusCode());
|
||||||
$this->assertEquals('application/json', $result->getHeader('Content-Type'));
|
$this->assertEquals('application/json', $result->getHeader('Content-Type'));
|
||||||
$data = json_decode($result->getBody() ?? '', true);
|
$data = json_decode($result->getBody() ?? '', true);
|
||||||
@ -105,7 +105,7 @@ class CMSTreeTest extends FunctionalTest
|
|||||||
$this->assertEmpty($pageData['PrevID']);
|
$this->assertEmpty($pageData['PrevID']);
|
||||||
|
|
||||||
// check subpage
|
// check subpage
|
||||||
$result = $this->get('admin/pages/edit/updatetreenodes?ids='.$page31->ID);
|
$result = $this->get('admin/pages/edit/updatetreenodes?ids=' . $page31->ID);
|
||||||
$this->assertEquals(200, $result->getStatusCode());
|
$this->assertEquals(200, $result->getStatusCode());
|
||||||
$this->assertEquals('application/json', $result->getHeader('Content-Type'));
|
$this->assertEquals('application/json', $result->getHeader('Content-Type'));
|
||||||
$data = json_decode($result->getBody() ?? '', true);
|
$data = json_decode($result->getBody() ?? '', true);
|
||||||
@ -115,7 +115,7 @@ class CMSTreeTest extends FunctionalTest
|
|||||||
$this->assertEmpty($pageData['PrevID']);
|
$this->assertEmpty($pageData['PrevID']);
|
||||||
|
|
||||||
// Multiple pages
|
// Multiple pages
|
||||||
$result = $this->get('admin/pages/edit/updatetreenodes?ids='.$page1->ID.','.$page2->ID);
|
$result = $this->get('admin/pages/edit/updatetreenodes?ids=' . $page1->ID . ',' . $page2->ID);
|
||||||
$this->assertEquals(200, $result->getStatusCode());
|
$this->assertEquals(200, $result->getStatusCode());
|
||||||
$this->assertEquals('application/json', $result->getHeader('Content-Type'));
|
$this->assertEquals('application/json', $result->getHeader('Content-Type'));
|
||||||
$data = json_decode($result->getBody() ?? '', true);
|
$data = json_decode($result->getBody() ?? '', true);
|
||||||
|
@ -6,7 +6,7 @@ use SilverStripe\Versioned\Versioned;
|
|||||||
use SilverStripe\Control\HTTPResponse_Exception;
|
use SilverStripe\Control\HTTPResponse_Exception;
|
||||||
use SilverStripe\Core\Config\Config;
|
use SilverStripe\Core\Config\Config;
|
||||||
use SilverStripe\Dev\FunctionalTest;
|
use SilverStripe\Dev\FunctionalTest;
|
||||||
use Page;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
|
||||||
class ContentControllerPermissionsTest extends FunctionalTest
|
class ContentControllerPermissionsTest extends FunctionalTest
|
||||||
{
|
{
|
||||||
@ -17,7 +17,7 @@ class ContentControllerPermissionsTest extends FunctionalTest
|
|||||||
public function testCanViewStage()
|
public function testCanViewStage()
|
||||||
{
|
{
|
||||||
// Create a new page
|
// Create a new page
|
||||||
$page = new Page();
|
$page = new SiteTree();
|
||||||
$page->URLSegment = 'testpage';
|
$page->URLSegment = 'testpage';
|
||||||
$page->write();
|
$page->write();
|
||||||
$page->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
$page->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
||||||
@ -38,7 +38,11 @@ class ContentControllerPermissionsTest extends FunctionalTest
|
|||||||
$response = $responseException->getResponse();
|
$response = $responseException->getResponse();
|
||||||
}
|
}
|
||||||
// should redirect to login
|
// should redirect to login
|
||||||
$this->assertEquals($response->getStatusCode(), 302, 'Redirects to login page when not logged in for draft stage');
|
$this->assertEquals(
|
||||||
|
$response->getStatusCode(),
|
||||||
|
302,
|
||||||
|
'Redirects to login page when not logged in for draft stage'
|
||||||
|
);
|
||||||
$this->assertStringContainsString(
|
$this->assertStringContainsString(
|
||||||
Config::inst()->get('SilverStripe\\Security\\Security', 'login_url'),
|
Config::inst()->get('SilverStripe\\Security\\Security', 'login_url'),
|
||||||
$response->getHeader('Location')
|
$response->getHeader('Location')
|
||||||
@ -47,6 +51,10 @@ class ContentControllerPermissionsTest extends FunctionalTest
|
|||||||
$this->logInWithPermission('CMS_ACCESS_CMSMain');
|
$this->logInWithPermission('CMS_ACCESS_CMSMain');
|
||||||
|
|
||||||
$response = $this->get('/testpage/?stage=Stage');
|
$response = $this->get('/testpage/?stage=Stage');
|
||||||
$this->assertEquals($response->getStatusCode(), 200, 'Doesnt redirect to login, but shows page for authenticated user');
|
$this->assertEquals(
|
||||||
|
$response->getStatusCode(),
|
||||||
|
200,
|
||||||
|
'Doesnt redirect to login, but shows page for authenticated user'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,23 +4,23 @@ namespace SilverStripe\CMS\Tests\Controllers;
|
|||||||
|
|
||||||
use SilverStripe\Assets\File;
|
use SilverStripe\Assets\File;
|
||||||
use SilverStripe\CMS\Controllers\ContentController;
|
use SilverStripe\CMS\Controllers\ContentController;
|
||||||
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
use SilverStripe\CMS\Search\ContentControllerSearchExtension;
|
use SilverStripe\CMS\Search\ContentControllerSearchExtension;
|
||||||
use SilverStripe\Dev\SapphireTest;
|
use SilverStripe\Dev\SapphireTest;
|
||||||
use SilverStripe\ORM\Search\FulltextSearchable;
|
use SilverStripe\ORM\Search\FulltextSearchable;
|
||||||
use SilverStripe\Versioned\Versioned;
|
use SilverStripe\Versioned\Versioned;
|
||||||
use Page;
|
|
||||||
|
|
||||||
class ContentControllerSearchExtensionTest extends SapphireTest
|
class ContentControllerSearchExtensionTest extends SapphireTest
|
||||||
{
|
{
|
||||||
protected static $required_extensions = [
|
protected static $required_extensions = [
|
||||||
ContentController::class => [
|
ContentController::class => [
|
||||||
ContentControllerSearchExtension::class,
|
ContentControllerSearchExtension::class,
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
public function testCustomSearchFormClassesToTest()
|
public function testCustomSearchFormClassesToTest()
|
||||||
{
|
{
|
||||||
$page = new Page();
|
$page = new SiteTree();
|
||||||
$page->URLSegment = 'whatever';
|
$page->URLSegment = 'whatever';
|
||||||
$page->Content = 'oh really?';
|
$page->Content = 'oh really?';
|
||||||
$page->write();
|
$page->write();
|
||||||
|
10
tests/php/Controllers/ContentControllerTest.php
Executable file → Normal file
10
tests/php/Controllers/ContentControllerTest.php
Executable file → Normal file
@ -10,7 +10,6 @@ use SilverStripe\Control\HTTPResponse_Exception;
|
|||||||
use SilverStripe\Core\Config\Config;
|
use SilverStripe\Core\Config\Config;
|
||||||
use SilverStripe\Dev\FunctionalTest;
|
use SilverStripe\Dev\FunctionalTest;
|
||||||
use SilverStripe\Versioned\Versioned;
|
use SilverStripe\Versioned\Versioned;
|
||||||
use Page;
|
|
||||||
|
|
||||||
class ContentControllerTest extends FunctionalTest
|
class ContentControllerTest extends FunctionalTest
|
||||||
{
|
{
|
||||||
@ -31,8 +30,8 @@ class ContentControllerTest extends FunctionalTest
|
|||||||
Config::modify()->set(SiteTree::class, 'nested_urls', true);
|
Config::modify()->set(SiteTree::class, 'nested_urls', true);
|
||||||
|
|
||||||
// Ensure all pages are published
|
// Ensure all pages are published
|
||||||
/** @var Page $page */
|
/** @var SiteTree $page */
|
||||||
foreach (Page::get() as $page) {
|
foreach (SiteTree::get() as $page) {
|
||||||
$page->publishSingle();
|
$page->publishSingle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -95,8 +94,7 @@ class ContentControllerTest extends FunctionalTest
|
|||||||
|
|
||||||
public function testDeepNestedURLs()
|
public function testDeepNestedURLs()
|
||||||
{
|
{
|
||||||
|
$page = new SiteTree();
|
||||||
$page = new Page();
|
|
||||||
$page->URLSegment = 'base-page';
|
$page->URLSegment = 'base-page';
|
||||||
$page->write();
|
$page->write();
|
||||||
$page->publishSingle();
|
$page->publishSingle();
|
||||||
@ -174,7 +172,7 @@ class ContentControllerTest extends FunctionalTest
|
|||||||
$response = $this->get($page->RelativeLink());
|
$response = $this->get($page->RelativeLink());
|
||||||
$this->assertEquals("ContentControllerTestPageWithoutController", trim($response->getBody() ?? ''));
|
$this->assertEquals("ContentControllerTestPageWithoutController", trim($response->getBody() ?? ''));
|
||||||
|
|
||||||
// // This should fall over to user Page.ss
|
// This should fall over to user Page.ss
|
||||||
$page = new ContentControllerTestPage();
|
$page = new ContentControllerTestPage();
|
||||||
$page->URLSegment = "test";
|
$page->URLSegment = "test";
|
||||||
$page->write();
|
$page->write();
|
||||||
|
@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
namespace SilverStripe\CMS\Tests\Controllers;
|
namespace SilverStripe\CMS\Tests\Controllers;
|
||||||
|
|
||||||
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
use SilverStripe\Dev\TestOnly;
|
use SilverStripe\Dev\TestOnly;
|
||||||
use Page;
|
|
||||||
|
|
||||||
class ContentControllerTestPage extends Page implements TestOnly
|
class ContentControllerTestPage extends SiteTree implements TestOnly
|
||||||
{
|
{
|
||||||
private static $table_name = 'ContentControllerTestPage';
|
private static $table_name = 'ContentControllerTestPage';
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ class ContentControllerTestPageController extends PageController implements Test
|
|||||||
{
|
{
|
||||||
private static $allowed_actions = [
|
private static $allowed_actions = [
|
||||||
'test',
|
'test',
|
||||||
'testwithouttemplate'
|
'testwithouttemplate',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function testwithouttemplate()
|
public function testwithouttemplate()
|
||||||
|
@ -2,10 +2,9 @@
|
|||||||
|
|
||||||
namespace SilverStripe\CMS\Tests\Controllers;
|
namespace SilverStripe\CMS\Tests\Controllers;
|
||||||
|
|
||||||
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
use SilverStripe\Dev\TestOnly;
|
use SilverStripe\Dev\TestOnly;
|
||||||
use Page;
|
|
||||||
|
|
||||||
class ContentControllerTestPageWithoutController extends Page implements TestOnly
|
class ContentControllerTestPageWithoutController extends SiteTree implements TestOnly
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
namespace SilverStripe\CMS\Tests\Controllers;
|
namespace SilverStripe\CMS\Tests\Controllers;
|
||||||
|
|
||||||
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
use SilverStripe\Dev\TestOnly;
|
use SilverStripe\Dev\TestOnly;
|
||||||
use Page;
|
|
||||||
|
|
||||||
class ContentControllerTest_Page extends Page implements TestOnly
|
class ContentControllerTest_Page extends SiteTree implements TestOnly
|
||||||
{
|
{
|
||||||
private static $table_name = 'ContentControllerTest_Page';
|
private static $table_name = 'ContentControllerTest_Page';
|
||||||
}
|
}
|
||||||
|
@ -2,14 +2,13 @@
|
|||||||
|
|
||||||
namespace SilverStripe\CMS\Tests\Controllers;
|
namespace SilverStripe\CMS\Tests\Controllers;
|
||||||
|
|
||||||
|
use SilverStripe\CMS\Controllers\ContentController;
|
||||||
use SilverStripe\Dev\TestOnly;
|
use SilverStripe\Dev\TestOnly;
|
||||||
use PageController;
|
|
||||||
|
|
||||||
class ContentControllerTest_PageController extends PageController implements TestOnly
|
class ContentControllerTest_PageController extends ContentController implements TestOnly
|
||||||
{
|
{
|
||||||
|
|
||||||
private static $allowed_actions = [
|
private static $allowed_actions = [
|
||||||
'second_index'
|
'second_index',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function index()
|
public function index()
|
||||||
|
@ -10,7 +10,6 @@ use SilverStripe\Core\Config\Config;
|
|||||||
use SilverStripe\Control\Director;
|
use SilverStripe\Control\Director;
|
||||||
use SilverStripe\Control\Controller;
|
use SilverStripe\Control\Controller;
|
||||||
use SilverStripe\Dev\FunctionalTest;
|
use SilverStripe\Dev\FunctionalTest;
|
||||||
use Page;
|
|
||||||
use SilverStripe\View\Parsers\URLSegmentFilter;
|
use SilverStripe\View\Parsers\URLSegmentFilter;
|
||||||
|
|
||||||
class ModelAsControllerTest extends FunctionalTest
|
class ModelAsControllerTest extends FunctionalTest
|
||||||
@ -34,7 +33,7 @@ class ModelAsControllerTest extends FunctionalTest
|
|||||||
|
|
||||||
protected function generateNestedPagesFixture()
|
protected function generateNestedPagesFixture()
|
||||||
{
|
{
|
||||||
$level1 = new Page();
|
$level1 = new SiteTree();
|
||||||
$level1->Title = 'First Level';
|
$level1->Title = 'First Level';
|
||||||
$level1->URLSegment = 'level1';
|
$level1->URLSegment = 'level1';
|
||||||
$level1->write();
|
$level1->write();
|
||||||
@ -44,7 +43,7 @@ class ModelAsControllerTest extends FunctionalTest
|
|||||||
$level1->write();
|
$level1->write();
|
||||||
$level1->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
$level1->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
||||||
|
|
||||||
$level2 = new Page();
|
$level2 = new SiteTree();
|
||||||
$level2->Title = 'Second Level';
|
$level2->Title = 'Second Level';
|
||||||
$level2->URLSegment = 'level2';
|
$level2->URLSegment = 'level2';
|
||||||
$level2->ParentID = $level1->ID;
|
$level2->ParentID = $level1->ID;
|
||||||
@ -55,7 +54,7 @@ class ModelAsControllerTest extends FunctionalTest
|
|||||||
$level2->write();
|
$level2->write();
|
||||||
$level2->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
$level2->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
||||||
|
|
||||||
$level3 = new Page();
|
$level3 = new SiteTree();
|
||||||
$level3->Title = "Level 3";
|
$level3->Title = "Level 3";
|
||||||
$level3->URLSegment = 'level3';
|
$level3->URLSegment = 'level3';
|
||||||
$level3->ParentID = $level2->ID;
|
$level3->ParentID = $level2->ID;
|
||||||
@ -93,7 +92,7 @@ class ModelAsControllerTest extends FunctionalTest
|
|||||||
$response = $this->get('newlevel1/level2');
|
$response = $this->get('newlevel1/level2');
|
||||||
$this->assertEquals($response->getStatusCode(), 301);
|
$this->assertEquals($response->getStatusCode(), 301);
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
Controller::join_links(Director::baseURL() . 'newlevel1/newlevel2/'),
|
Controller::join_links(Director::baseURL() . 'newlevel1/newlevel2'),
|
||||||
$response->getHeader('Location')
|
$response->getHeader('Location')
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -101,7 +100,7 @@ class ModelAsControllerTest extends FunctionalTest
|
|||||||
$response = $this->get('newlevel1/newlevel2/level3');
|
$response = $this->get('newlevel1/newlevel2/level3');
|
||||||
$this->assertEquals($response->getStatusCode(), 301);
|
$this->assertEquals($response->getStatusCode(), 301);
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
Controller::join_links(Director::baseURL() . 'newlevel1/newlevel2/newlevel3/'),
|
Controller::join_links(Director::baseURL() . 'newlevel1/newlevel2/newlevel3'),
|
||||||
$response->getHeader('Location')
|
$response->getHeader('Location')
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -115,7 +114,7 @@ class ModelAsControllerTest extends FunctionalTest
|
|||||||
*/
|
*/
|
||||||
public function testHeavilyNestedRenamedRedirectedPages()
|
public function testHeavilyNestedRenamedRedirectedPages()
|
||||||
{
|
{
|
||||||
$page = new Page();
|
$page = new SiteTree();
|
||||||
$page->Title = 'First Level';
|
$page->Title = 'First Level';
|
||||||
$page->URLSegment = 'oldurl';
|
$page->URLSegment = 'oldurl';
|
||||||
$page->write();
|
$page->write();
|
||||||
@ -125,28 +124,28 @@ class ModelAsControllerTest extends FunctionalTest
|
|||||||
$page->write();
|
$page->write();
|
||||||
$page->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
$page->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
||||||
|
|
||||||
$page2 = new Page();
|
$page2 = new SiteTree();
|
||||||
$page2->Title = 'Second Level Page';
|
$page2->Title = 'Second Level Page';
|
||||||
$page2->URLSegment = 'level2';
|
$page2->URLSegment = 'level2';
|
||||||
$page2->ParentID = $page->ID;
|
$page2->ParentID = $page->ID;
|
||||||
$page2->write();
|
$page2->write();
|
||||||
$page2->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
$page2->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
||||||
|
|
||||||
$page3 = new Page();
|
$page3 = new SiteTree();
|
||||||
$page3->Title = 'Third Level Page';
|
$page3->Title = 'Third Level Page';
|
||||||
$page3->URLSegment = 'level3';
|
$page3->URLSegment = 'level3';
|
||||||
$page3->ParentID = $page2->ID;
|
$page3->ParentID = $page2->ID;
|
||||||
$page3->write();
|
$page3->write();
|
||||||
$page3->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
$page3->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
||||||
|
|
||||||
$page4 = new Page();
|
$page4 = new SiteTree();
|
||||||
$page4->Title = 'Fourth Level Page';
|
$page4->Title = 'Fourth Level Page';
|
||||||
$page4->URLSegment = 'level4';
|
$page4->URLSegment = 'level4';
|
||||||
$page4->ParentID = $page3->ID;
|
$page4->ParentID = $page3->ID;
|
||||||
$page4->write();
|
$page4->write();
|
||||||
$page4->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
$page4->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
||||||
|
|
||||||
$page5 = new Page();
|
$page5 = new SiteTree();
|
||||||
$page5->Title = 'Fifth Level Page';
|
$page5->Title = 'Fifth Level Page';
|
||||||
$page5->URLSegment = 'level5';
|
$page5->URLSegment = 'level5';
|
||||||
$page5->ParentID = $page4->ID;
|
$page5->ParentID = $page4->ID;
|
||||||
@ -154,10 +153,10 @@ class ModelAsControllerTest extends FunctionalTest
|
|||||||
$page5->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
$page5->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
||||||
|
|
||||||
// Test that the redirect still works fine when trying to access the most nested page
|
// Test that the redirect still works fine when trying to access the most nested page
|
||||||
$response = $this->get('oldurl/level2/level3/level4/level5/');
|
$response = $this->get('oldurl/level2/level3/level4/level5');
|
||||||
$this->assertEquals($response->getStatusCode(), 301);
|
$this->assertEquals($response->getStatusCode(), 301);
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
Controller::join_links(Director::baseURL() . 'newurl/level2/level3/level4/level5/'),
|
Controller::join_links(Director::baseURL() . 'newurl/level2/level3/level4/level5'),
|
||||||
$response->getHeader('Location')
|
$response->getHeader('Location')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -171,7 +170,7 @@ class ModelAsControllerTest extends FunctionalTest
|
|||||||
$response = $this->get('newlevel3');
|
$response = $this->get('newlevel3');
|
||||||
$this->assertEquals(301, $response->getStatusCode());
|
$this->assertEquals(301, $response->getStatusCode());
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
Director::baseURL() . 'newlevel1/newlevel2/newlevel3/',
|
Director::baseURL() . 'newlevel1/newlevel2/newlevel3',
|
||||||
$response->getHeader("Location")
|
$response->getHeader("Location")
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -179,7 +178,7 @@ class ModelAsControllerTest extends FunctionalTest
|
|||||||
$response = $this->get('level3');
|
$response = $this->get('level3');
|
||||||
$this->assertEquals(301, $response->getStatusCode());
|
$this->assertEquals(301, $response->getStatusCode());
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
Director::baseURL() . 'newlevel1/newlevel2/newlevel3/',
|
Director::baseURL() . 'newlevel1/newlevel2/newlevel3',
|
||||||
$response->getHeader("Location")
|
$response->getHeader("Location")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -188,8 +187,8 @@ class ModelAsControllerTest extends FunctionalTest
|
|||||||
{
|
{
|
||||||
$this->generateNestedPagesFixture();
|
$this->generateNestedPagesFixture();
|
||||||
|
|
||||||
$otherParent = new Page([
|
$otherParent = new SiteTree([
|
||||||
'URLSegment' => 'otherparent'
|
'URLSegment' => 'otherparent',
|
||||||
]);
|
]);
|
||||||
$otherParent->write();
|
$otherParent->write();
|
||||||
$otherParent->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
$otherParent->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
||||||
@ -215,10 +214,10 @@ class ModelAsControllerTest extends FunctionalTest
|
|||||||
$this->generateNestedPagesFixture();
|
$this->generateNestedPagesFixture();
|
||||||
|
|
||||||
// check third level URLSegment
|
// check third level URLSegment
|
||||||
$response = $this->get('newlevel1/newlevel2/level3/?foo=bar&test=test');
|
$response = $this->get('newlevel1/newlevel2/level3?foo=bar&test=test');
|
||||||
$this->assertEquals($response->getStatusCode(), 301);
|
$this->assertEquals($response->getStatusCode(), 301);
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
Controller::join_links(Director::baseURL() . 'newlevel1/newlevel2/newlevel3/', '?foo=bar&test=test'),
|
Controller::join_links(Director::baseURL() . 'newlevel1/newlevel2/newlevel3', '?foo=bar&test=test'),
|
||||||
$response->getHeader('Location')
|
$response->getHeader('Location')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -232,9 +231,9 @@ class ModelAsControllerTest extends FunctionalTest
|
|||||||
{
|
{
|
||||||
$this->generateNestedPagesFixture();
|
$this->generateNestedPagesFixture();
|
||||||
|
|
||||||
$otherLevel1 = new Page([
|
$otherLevel1 = new SiteTree([
|
||||||
'Title' => "Other Level 1",
|
'Title' => "Other Level 1",
|
||||||
'URLSegment' => 'level1'
|
'URLSegment' => 'level1',
|
||||||
]);
|
]);
|
||||||
$otherLevel1->write();
|
$otherLevel1->write();
|
||||||
$otherLevel1->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
$otherLevel1->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
||||||
@ -260,7 +259,7 @@ class ModelAsControllerTest extends FunctionalTest
|
|||||||
*/
|
*/
|
||||||
public function testFindOldPage()
|
public function testFindOldPage()
|
||||||
{
|
{
|
||||||
$page = new Page();
|
$page = new SiteTree();
|
||||||
$page->Title = 'First Level';
|
$page->Title = 'First Level';
|
||||||
$page->URLSegment = 'oldurl';
|
$page->URLSegment = 'oldurl';
|
||||||
$page->write();
|
$page->write();
|
||||||
@ -274,7 +273,7 @@ class ModelAsControllerTest extends FunctionalTest
|
|||||||
$matchedPage = SiteTree::get_by_link($url);
|
$matchedPage = SiteTree::get_by_link($url);
|
||||||
$this->assertEquals('First Level', $matchedPage->Title);
|
$this->assertEquals('First Level', $matchedPage->Title);
|
||||||
|
|
||||||
$page2 = new Page();
|
$page2 = new SiteTree();
|
||||||
$page2->Title = 'Second Level Page';
|
$page2->Title = 'Second Level Page';
|
||||||
$page2->URLSegment = 'oldpage2';
|
$page2->URLSegment = 'oldpage2';
|
||||||
$page2->ParentID = $page->ID;
|
$page2->ParentID = $page->ID;
|
||||||
@ -304,12 +303,12 @@ class ModelAsControllerTest extends FunctionalTest
|
|||||||
RootURLController::reset();
|
RootURLController::reset();
|
||||||
Config::modify()->set(SiteTree::class, 'nested_urls', true);
|
Config::modify()->set(SiteTree::class, 'nested_urls', true);
|
||||||
|
|
||||||
$draft = new Page();
|
$draft = new SiteTree();
|
||||||
$draft->Title = 'Root Leve Draft Page';
|
$draft->Title = 'Root Leve Draft Page';
|
||||||
$draft->URLSegment = 'root';
|
$draft->URLSegment = 'root';
|
||||||
$draft->write();
|
$draft->write();
|
||||||
|
|
||||||
$published = new Page();
|
$published = new SiteTree();
|
||||||
$published->Title = 'Published Page Under Draft Page';
|
$published->Title = 'Published Page Under Draft Page';
|
||||||
$published->URLSegment = 'sub-root';
|
$published->URLSegment = 'sub-root';
|
||||||
$published->write();
|
$published->write();
|
||||||
@ -319,7 +318,8 @@ class ModelAsControllerTest extends FunctionalTest
|
|||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
$response->getStatusCode(),
|
$response->getStatusCode(),
|
||||||
404,
|
404,
|
||||||
'The page should not be found since its parent has not been published, in this case http://<yousitename>/root/sub-root or http://<yousitename>/sub-root'
|
'The page should not be found since its parent has not been published, in this case ' .
|
||||||
|
'http://<yousitename>/root/sub-root or http://<yousitename>/sub-root'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -327,13 +327,13 @@ class ModelAsControllerTest extends FunctionalTest
|
|||||||
{
|
{
|
||||||
Config::modify()->set(URLSegmentFilter::class, 'default_allow_multibyte', true);
|
Config::modify()->set(URLSegmentFilter::class, 'default_allow_multibyte', true);
|
||||||
|
|
||||||
$parent = new Page();
|
$parent = new SiteTree();
|
||||||
$parent->Title = 'Multibyte test';
|
$parent->Title = 'Multibyte test';
|
||||||
$parent->URLSegment = 'بلاگ';
|
$parent->URLSegment = 'بلاگ';
|
||||||
$parent->write();
|
$parent->write();
|
||||||
$parent->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
$parent->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
||||||
|
|
||||||
$child = new Page();
|
$child = new SiteTree();
|
||||||
$child->Title = 'Multibyte test';
|
$child->Title = 'Multibyte test';
|
||||||
$child->URLSegment = 'فضة';
|
$child->URLSegment = 'فضة';
|
||||||
$child->ParentID = $parent->ID;
|
$child->ParentID = $parent->ID;
|
||||||
|
@ -13,7 +13,7 @@ class RootURLControllerTest extends SapphireTest
|
|||||||
|
|
||||||
public function testGetHomepageLink()
|
public function testGetHomepageLink()
|
||||||
{
|
{
|
||||||
$default = $this->objFromFixture('Page', 'home');
|
$default = $this->objFromFixture(SiteTree::class, 'home');
|
||||||
|
|
||||||
Config::modify()->set(SiteTree::class, 'nested_urls', false);
|
Config::modify()->set(SiteTree::class, 'nested_urls', false);
|
||||||
$this->assertEquals('home', RootURLController::get_homepage_link());
|
$this->assertEquals('home', RootURLController::get_homepage_link());
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
Page:
|
SilverStripe\CMS\Model\SiteTree:
|
||||||
home:
|
home:
|
||||||
Title: Home
|
Title: Home
|
||||||
nested:
|
nested:
|
||||||
Title: Nested Home
|
Title: Nested Home
|
||||||
Parent: =>Page.home
|
Parent: =>SilverStripe\CMS\Model\SiteTree.home
|
||||||
page1:
|
page1:
|
||||||
Title: First Page
|
Title: First Page
|
||||||
URLSegment: page1
|
URLSegment: page1
|
||||||
|
@ -1,239 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace SilverStripe\CMS\Tests\Controllers;
|
|
||||||
|
|
||||||
use SilverStripe\CMS\Controllers\SilverStripeNavigator;
|
|
||||||
use SilverStripe\CMS\Controllers\SilverStripeNavigatorItem_ArchiveLink;
|
|
||||||
use SilverStripe\CMS\Controllers\SilverStripeNavigatorItem_LiveLink;
|
|
||||||
use SilverStripe\CMS\Controllers\SilverStripeNavigatorItem_StageLink;
|
|
||||||
use SilverStripe\CMS\Controllers\SilverStripeNavigatorItem_Unversioned;
|
|
||||||
use SilverStripe\Dev\SapphireTest;
|
|
||||||
use SilverStripe\Security\Member;
|
|
||||||
|
|
||||||
class SilverStripeNavigatorTest extends SapphireTest
|
|
||||||
{
|
|
||||||
protected static $fixture_file = 'CMSMainTest.yml';
|
|
||||||
|
|
||||||
protected static $extra_dataobjects = [
|
|
||||||
SilverStripeNavigatorTest\UnstagedRecord::class,
|
|
||||||
SilverStripeNavigatorTest\UnversionedRecord::class,
|
|
||||||
SilverStripeNavigatorTest\VersionedRecord::class,
|
|
||||||
];
|
|
||||||
|
|
||||||
public function testGetItemsAutoDiscovery(): void
|
|
||||||
{
|
|
||||||
$record = new SilverStripeNavigatorTest\VersionedRecord();
|
|
||||||
$record->PreviewLinkTestProperty = 'some-value';
|
|
||||||
$record->write();
|
|
||||||
$navigator = new SilverStripeNavigator($record);
|
|
||||||
$classes = array_map('get_class', $navigator->getItems()->toArray());
|
|
||||||
|
|
||||||
$this->assertContains(
|
|
||||||
SilverStripeNavigatorTest_TestItem::class,
|
|
||||||
$classes,
|
|
||||||
'Autodiscovers new classes'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetItemsPublished(): void
|
|
||||||
{
|
|
||||||
$record = new SilverStripeNavigatorTest\VersionedRecord();
|
|
||||||
$record->PreviewLinkTestProperty = 'some-value';
|
|
||||||
$record->write();
|
|
||||||
$record->publishRecursive();
|
|
||||||
$navigator = new SilverStripeNavigator($record);
|
|
||||||
$classes = array_map('get_class', $navigator->getItems()->toArray());
|
|
||||||
|
|
||||||
// Has the live and staged links
|
|
||||||
$this->assertContains(SilverStripeNavigatorItem_LiveLink::class, $classes);
|
|
||||||
$this->assertContains(SilverStripeNavigatorItem_StageLink::class, $classes);
|
|
||||||
|
|
||||||
// Does not have the other links
|
|
||||||
$this->assertNotContains(SilverStripeNavigatorItem_ArchiveLink::class, $classes);
|
|
||||||
$this->assertNotContains(SilverStripeNavigatorItem_Unversioned::class, $classes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetItemsStaged(): void
|
|
||||||
{
|
|
||||||
$record = new SilverStripeNavigatorTest\VersionedRecord();
|
|
||||||
$record->PreviewLinkTestProperty = 'some-value';
|
|
||||||
$record->write();
|
|
||||||
$navigator = new SilverStripeNavigator($record);
|
|
||||||
$classes = array_map('get_class', $navigator->getItems()->toArray());
|
|
||||||
|
|
||||||
// Has the stage link
|
|
||||||
$this->assertContains(SilverStripeNavigatorItem_StageLink::class, $classes);
|
|
||||||
|
|
||||||
// Does not have the other links
|
|
||||||
$this->assertNotContains(SilverStripeNavigatorItem_ArchiveLink::class, $classes);
|
|
||||||
$this->assertNotContains(SilverStripeNavigatorItem_LiveLink::class, $classes);
|
|
||||||
$this->assertNotContains(SilverStripeNavigatorItem_Unversioned::class, $classes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetItemsArchived(): void
|
|
||||||
{
|
|
||||||
$record = new SilverStripeNavigatorTest\VersionedRecord();
|
|
||||||
$record->PreviewLinkTestProperty = 'some-value';
|
|
||||||
$record->write();
|
|
||||||
$record->doArchive();
|
|
||||||
$navigator = new SilverStripeNavigator($record);
|
|
||||||
$classes = array_map('get_class', $navigator->getItems()->toArray());
|
|
||||||
|
|
||||||
// Has the archived link
|
|
||||||
$this->assertContains(SilverStripeNavigatorItem_ArchiveLink::class, $classes);
|
|
||||||
|
|
||||||
// Does not have the other links
|
|
||||||
$this->assertNotContains(SilverStripeNavigatorItem_LiveLink::class, $classes);
|
|
||||||
$this->assertNotContains(SilverStripeNavigatorItem_StageLink::class, $classes);
|
|
||||||
$this->assertNotContains(SilverStripeNavigatorItem_UnversionedLink::class, $classes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetItemsUnstaged(): void
|
|
||||||
{
|
|
||||||
$record = new SilverStripeNavigatorTest\UnstagedRecord();
|
|
||||||
$record->previewLinkTestProperty = 'some-value';
|
|
||||||
$record->write();
|
|
||||||
$navigator = new SilverStripeNavigator($record);
|
|
||||||
$classes = array_map('get_class', $navigator->getItems()->toArray());
|
|
||||||
|
|
||||||
// Has the unversioned link
|
|
||||||
$this->assertContains(SilverStripeNavigatorItem_Unversioned::class, $classes);
|
|
||||||
|
|
||||||
// Does not have the other links
|
|
||||||
$this->assertNotContains(SilverStripeNavigatorItem_ArchiveLink::class, $classes);
|
|
||||||
$this->assertNotContains(SilverStripeNavigatorItem_LiveLink::class, $classes);
|
|
||||||
$this->assertNotContains(SilverStripeNavigatorItem_StageLink::class, $classes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetItemsUnversioned(): void
|
|
||||||
{
|
|
||||||
$record = new SilverStripeNavigatorTest\UnversionedRecord();
|
|
||||||
$record->previewLinkTestProperty = 'some-value';
|
|
||||||
$record->write();
|
|
||||||
$navigator = new SilverStripeNavigator($record);
|
|
||||||
$classes = array_map('get_class', $navigator->getItems()->toArray());
|
|
||||||
|
|
||||||
// Has the unversioned link
|
|
||||||
$this->assertContains(SilverStripeNavigatorItem_Unversioned::class, $classes);
|
|
||||||
|
|
||||||
// Does not have the other links
|
|
||||||
$this->assertNotContains(SilverStripeNavigatorItem_ArchiveLink::class, $classes);
|
|
||||||
$this->assertNotContains(SilverStripeNavigatorItem_LiveLink::class, $classes);
|
|
||||||
$this->assertNotContains(SilverStripeNavigatorItem_StageLink::class, $classes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testCanViewPublished(): void
|
|
||||||
{
|
|
||||||
$record = new SilverStripeNavigatorTest\VersionedRecord();
|
|
||||||
$record->write();
|
|
||||||
$record->publishRecursive();
|
|
||||||
$liveLinkItem = new SilverStripeNavigatorItem_LiveLink($record);
|
|
||||||
$stagedLinkItem = new SilverStripeNavigatorItem_StageLink($record);
|
|
||||||
$archivedLinkItem = new SilverStripeNavigatorItem_ArchiveLink($record);
|
|
||||||
$unversionedLinkItem = new SilverStripeNavigatorItem_Unversioned($record);
|
|
||||||
|
|
||||||
// Cannot view staged and live links when there's no preview link
|
|
||||||
$this->assertFalse($liveLinkItem->canView());
|
|
||||||
$this->assertFalse($stagedLinkItem->canView());
|
|
||||||
|
|
||||||
$record->PreviewLinkTestProperty = 'some-value';
|
|
||||||
$record->write();
|
|
||||||
$record->publishRecursive();
|
|
||||||
|
|
||||||
// Can view staged and live links
|
|
||||||
$this->assertTrue($liveLinkItem->canView());
|
|
||||||
$this->assertTrue($stagedLinkItem->canView());
|
|
||||||
// Cannot view the other links
|
|
||||||
$this->assertFalse($archivedLinkItem->canView());
|
|
||||||
$this->assertFalse($unversionedLinkItem->canView());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testCanViewStaged(): void
|
|
||||||
{
|
|
||||||
$record = new SilverStripeNavigatorTest\VersionedRecord();
|
|
||||||
$record->write();
|
|
||||||
$liveLinkItem = new SilverStripeNavigatorItem_LiveLink($record);
|
|
||||||
$stagedLinkItem = new SilverStripeNavigatorItem_StageLink($record);
|
|
||||||
$archivedLinkItem = new SilverStripeNavigatorItem_ArchiveLink($record);
|
|
||||||
$unversionedLinkItem = new SilverStripeNavigatorItem_Unversioned($record);
|
|
||||||
|
|
||||||
// Cannot view staged link when there's no preview link
|
|
||||||
$this->assertFalse($stagedLinkItem->canView());
|
|
||||||
|
|
||||||
$record->PreviewLinkTestProperty = 'some-value';
|
|
||||||
|
|
||||||
// Can view staged link
|
|
||||||
$this->assertTrue($stagedLinkItem->canView());
|
|
||||||
// Cannot view the other links
|
|
||||||
$this->assertFalse($liveLinkItem->canView());
|
|
||||||
$this->assertFalse($archivedLinkItem->canView());
|
|
||||||
$this->assertFalse($unversionedLinkItem->canView());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testCanViewArchived(): void
|
|
||||||
{
|
|
||||||
$record = new SilverStripeNavigatorTest\VersionedRecord();
|
|
||||||
$record->write();
|
|
||||||
$record->doArchive();
|
|
||||||
$liveLinkItem = new SilverStripeNavigatorItem_LiveLink($record);
|
|
||||||
$stagedLinkItem = new SilverStripeNavigatorItem_StageLink($record);
|
|
||||||
$archivedLinkItem = new SilverStripeNavigatorItem_ArchiveLink($record);
|
|
||||||
$unversionedLinkItem = new SilverStripeNavigatorItem_Unversioned($record);
|
|
||||||
|
|
||||||
// Cannot view archived link when there's no preview link
|
|
||||||
$this->assertFalse($archivedLinkItem->canView());
|
|
||||||
|
|
||||||
$record->PreviewLinkTestProperty = 'some-value';
|
|
||||||
|
|
||||||
// Can view archived link
|
|
||||||
$this->assertTrue($archivedLinkItem->canView());
|
|
||||||
// Cannot view the other links
|
|
||||||
$this->assertFalse($liveLinkItem->canView());
|
|
||||||
$this->assertFalse($stagedLinkItem->canView());
|
|
||||||
$this->assertFalse($unversionedLinkItem->canView());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testCanViewUnstaged(): void
|
|
||||||
{
|
|
||||||
$record = new SilverStripeNavigatorTest\UnstagedRecord();
|
|
||||||
$record->write();
|
|
||||||
$liveLinkItem = new SilverStripeNavigatorItem_LiveLink($record);
|
|
||||||
$stagedLinkItem = new SilverStripeNavigatorItem_StageLink($record);
|
|
||||||
$archivedLinkItem = new SilverStripeNavigatorItem_ArchiveLink($record);
|
|
||||||
$unversionedLinkItem = new SilverStripeNavigatorItem_Unversioned($record);
|
|
||||||
|
|
||||||
// Cannot view unversioned link when there's no preview link
|
|
||||||
$this->assertFalse($unversionedLinkItem->canView());
|
|
||||||
|
|
||||||
$record->previewLinkTestProperty = 'some-value';
|
|
||||||
|
|
||||||
// Can view unversioned link
|
|
||||||
$this->assertTrue($unversionedLinkItem->canView());
|
|
||||||
// Cannot view the other links
|
|
||||||
$this->assertFalse($liveLinkItem->canView());
|
|
||||||
$this->assertFalse($stagedLinkItem->canView());
|
|
||||||
$this->assertFalse($archivedLinkItem->canView());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testCanViewUnversioned(): void
|
|
||||||
{
|
|
||||||
$record = new SilverStripeNavigatorTest\UnversionedRecord();
|
|
||||||
$record->write();
|
|
||||||
$liveLinkItem = new SilverStripeNavigatorItem_LiveLink($record);
|
|
||||||
$stagedLinkItem = new SilverStripeNavigatorItem_StageLink($record);
|
|
||||||
$archivedLinkItem = new SilverStripeNavigatorItem_ArchiveLink($record);
|
|
||||||
$unversionedLinkItem = new SilverStripeNavigatorItem_Unversioned($record);
|
|
||||||
|
|
||||||
// Cannot view unversioned link when there's no preview link
|
|
||||||
$this->assertFalse($unversionedLinkItem->canView());
|
|
||||||
|
|
||||||
$record->previewLinkTestProperty = 'some-value';
|
|
||||||
|
|
||||||
// Can view unversioned link
|
|
||||||
$this->assertTrue($unversionedLinkItem->canView());
|
|
||||||
// Cannot view the other links
|
|
||||||
$this->assertFalse($liveLinkItem->canView());
|
|
||||||
$this->assertFalse($stagedLinkItem->canView());
|
|
||||||
$this->assertFalse($archivedLinkItem->canView());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace SilverStripe\CMS\Tests\Controllers\SilverStripeNavigatorTest;
|
|
||||||
|
|
||||||
use SilverStripe\Dev\TestOnly;
|
|
||||||
use SilverStripe\ORM\CMSPreviewable;
|
|
||||||
use SilverStripe\ORM\DataObject;
|
|
||||||
use SilverStripe\Versioned\Versioned;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Versioned but not staged
|
|
||||||
*/
|
|
||||||
class UnstagedRecord extends DataObject implements TestOnly, CMSPreviewable
|
|
||||||
{
|
|
||||||
private static $table_name = 'SilverStripeNavigatorTest_UnstagedRecord';
|
|
||||||
|
|
||||||
private static $show_stage_link = true;
|
|
||||||
|
|
||||||
private static $show_live_link = true;
|
|
||||||
|
|
||||||
private static $show_unversioned_preview_link = true;
|
|
||||||
|
|
||||||
private static $extensions = [
|
|
||||||
Versioned::class . '.versioned',
|
|
||||||
];
|
|
||||||
|
|
||||||
public $previewLinkTestProperty = null;
|
|
||||||
|
|
||||||
public function PreviewLink($action = null)
|
|
||||||
{
|
|
||||||
return $this->previewLinkTestProperty;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* To determine preview mechanism (e.g. embedded / iframe)
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getMimeType()
|
|
||||||
{
|
|
||||||
return 'text/html';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function CMSEditLink()
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace SilverStripe\CMS\Tests\Controllers\SilverStripeNavigatorTest;
|
|
||||||
|
|
||||||
use SilverStripe\Dev\TestOnly;
|
|
||||||
use SilverStripe\ORM\CMSPreviewable;
|
|
||||||
use SilverStripe\ORM\DataObject;
|
|
||||||
use SilverStripe\Versioned\Versioned;
|
|
||||||
|
|
||||||
class UnversionedRecord extends DataObject implements TestOnly, CMSPreviewable
|
|
||||||
{
|
|
||||||
private static $table_name = 'SilverStripeNavigatorTest_UnversionedRecord';
|
|
||||||
|
|
||||||
private static $show_stage_link = true;
|
|
||||||
|
|
||||||
private static $show_live_link = true;
|
|
||||||
|
|
||||||
private static $show_unversioned_preview_link = true;
|
|
||||||
|
|
||||||
public $previewLinkTestProperty = null;
|
|
||||||
|
|
||||||
public function PreviewLink($action = null)
|
|
||||||
{
|
|
||||||
return $this->previewLinkTestProperty;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getMimeType()
|
|
||||||
{
|
|
||||||
return 'text/html';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function CMSEditLink()
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace SilverStripe\CMS\Tests\Controllers\SilverStripeNavigatorTest;
|
|
||||||
|
|
||||||
use SilverStripe\Dev\TestOnly;
|
|
||||||
use SilverStripe\ORM\CMSPreviewable;
|
|
||||||
use SilverStripe\ORM\DataObject;
|
|
||||||
use SilverStripe\Versioned\Versioned;
|
|
||||||
|
|
||||||
class VersionedRecord extends DataObject implements TestOnly, CMSPreviewable
|
|
||||||
{
|
|
||||||
private static $table_name = 'SilverStripeNavigatorTest_VersionedRecord';
|
|
||||||
|
|
||||||
private static $show_stage_link = true;
|
|
||||||
|
|
||||||
private static $show_live_link = true;
|
|
||||||
|
|
||||||
private static $show_unversioned_preview_link = true;
|
|
||||||
|
|
||||||
private static $db = [
|
|
||||||
'PreviewLinkTestProperty' => 'Text',
|
|
||||||
];
|
|
||||||
|
|
||||||
private static $extensions = [
|
|
||||||
Versioned::class,
|
|
||||||
];
|
|
||||||
|
|
||||||
public function PreviewLink($action = null)
|
|
||||||
{
|
|
||||||
return $this->PreviewLinkTestProperty;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getMimeType()
|
|
||||||
{
|
|
||||||
return 'text/html';
|
|
||||||
}
|
|
||||||
|
|
||||||
public function CMSEditLink()
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace SilverStripe\CMS\Tests\Controllers;
|
|
||||||
|
|
||||||
use SilverStripe\CMS\Controllers\SilverStripeNavigatorItem;
|
|
||||||
use SilverStripe\Dev\TestOnly;
|
|
||||||
|
|
||||||
class SilverStripeNavigatorTest_TestItem extends SilverStripeNavigatorItem implements TestOnly
|
|
||||||
{
|
|
||||||
public function getTitle()
|
|
||||||
{
|
|
||||||
return self::class;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getHTML()
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
@ -44,7 +44,7 @@ class LinkablePluginTest extends SapphireTest
|
|||||||
|
|
||||||
public function testResolver()
|
public function testResolver()
|
||||||
{
|
{
|
||||||
$page = SiteTree::create([
|
$page = new SiteTree([
|
||||||
'Title' => 'Test page',
|
'Title' => 'Test page',
|
||||||
'URLSegment' => 'test-page',
|
'URLSegment' => 'test-page',
|
||||||
'ParentID' => 0,
|
'ParentID' => 0,
|
||||||
@ -52,7 +52,7 @@ class LinkablePluginTest extends SapphireTest
|
|||||||
$page->write();
|
$page->write();
|
||||||
$page->publishRecursive();
|
$page->publishRecursive();
|
||||||
|
|
||||||
$page = SiteTree::create([
|
$page = new SiteTree([
|
||||||
'Title' => 'Other test page',
|
'Title' => 'Other test page',
|
||||||
'URLSegment' => 'other-test-page',
|
'URLSegment' => 'other-test-page',
|
||||||
'ParentID' => 0,
|
'ParentID' => 0,
|
||||||
|
@ -2,12 +2,14 @@
|
|||||||
|
|
||||||
namespace SilverStripe\CMS\Tests\Model;
|
namespace SilverStripe\CMS\Tests\Model;
|
||||||
|
|
||||||
use Page;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
use SilverStripe\CMS\Model\RedirectorPage;
|
use SilverStripe\CMS\Model\RedirectorPage;
|
||||||
use SilverStripe\CMS\Model\RedirectorPageController;
|
use SilverStripe\CMS\Model\RedirectorPageController;
|
||||||
use SilverStripe\Control\Director;
|
use SilverStripe\Control\Director;
|
||||||
use SilverStripe\Assets\File;
|
use SilverStripe\Assets\File;
|
||||||
use SilverStripe\Assets\Dev\TestAssetStore;
|
use SilverStripe\Assets\Dev\TestAssetStore;
|
||||||
|
use SilverStripe\Control\Controller;
|
||||||
|
use SilverStripe\Core\Config\Config;
|
||||||
use SilverStripe\Dev\FunctionalTest;
|
use SilverStripe\Dev\FunctionalTest;
|
||||||
|
|
||||||
class RedirectorPageTest extends FunctionalTest
|
class RedirectorPageTest extends FunctionalTest
|
||||||
@ -36,53 +38,88 @@ class RedirectorPageTest extends FunctionalTest
|
|||||||
Director::config()->set('alternate_base_url', 'http://www.mysite.com/');
|
Director::config()->set('alternate_base_url', 'http://www.mysite.com/');
|
||||||
|
|
||||||
// Ensure all pages are published
|
// Ensure all pages are published
|
||||||
/** @var Page $page */
|
/** @var SiteTree $page */
|
||||||
foreach (Page::get() as $page) {
|
foreach (SiteTree::get() as $page) {
|
||||||
$page->publishSingle();
|
$page->publishSingle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testGoodRedirectors()
|
public function testGoodRedirectors()
|
||||||
{
|
{
|
||||||
/* For good redirectors, the final destination URL will be returned */
|
// For good redirectors, the final destination URL will be returned
|
||||||
$this->assertEquals("http://www.google.com", $this->objFromFixture(RedirectorPage::class, 'goodexternal')->Link());
|
$this->assertEquals(
|
||||||
$this->assertEquals("/redirection-dest/", $this->objFromFixture(RedirectorPage::class, 'goodinternal')->redirectionLink());
|
"http://www.google.com",
|
||||||
$this->assertEquals("/redirection-dest/", $this->objFromFixture(RedirectorPage::class, 'goodinternal')->Link());
|
$this->objFromFixture(RedirectorPage::class, 'goodexternal')->Link()
|
||||||
|
);
|
||||||
|
$this->assertEquals(
|
||||||
|
"/redirection-dest",
|
||||||
|
$this->objFromFixture(RedirectorPage::class, 'goodinternal')->redirectionLink()
|
||||||
|
);
|
||||||
|
$this->assertEquals(
|
||||||
|
"/redirection-dest",
|
||||||
|
$this->objFromFixture(RedirectorPage::class, 'goodinternal')->Link()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testEmptyRedirectors()
|
public function provideEmptyRedirectors()
|
||||||
{
|
{
|
||||||
/* If a redirector page is misconfigured, then its link method will just return the usual URLSegment-generated value */
|
return [
|
||||||
$page1 = $this->objFromFixture(RedirectorPage::class, 'badexternal');
|
'use 200' => [
|
||||||
$this->assertEquals('/bad-external/', $page1->Link());
|
'use404' => false,
|
||||||
|
],
|
||||||
|
'use 404' => [
|
||||||
|
'use404' => true,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
/* An error message will be shown if you visit it */
|
/**
|
||||||
|
* @dataProvider provideEmptyRedirectors
|
||||||
|
*/
|
||||||
|
public function testEmptyRedirectors(bool $use404)
|
||||||
|
{
|
||||||
|
Config::modify()->set(RedirectorPageController::class, 'missing_redirect_is_404', $use404);
|
||||||
|
// If a redirector page is misconfigured, then its link method will just return the usual
|
||||||
|
// URLSegment-generated value
|
||||||
|
$page1 = $this->objFromFixture(RedirectorPage::class, 'badexternal');
|
||||||
|
$this->assertEquals('/bad-external', $page1->Link());
|
||||||
|
$response = $this->get($page1->Link());
|
||||||
|
$this->assertEquals(200, $response->getStatusCode());
|
||||||
|
|
||||||
|
// An error message will be shown if you visit it
|
||||||
$content = $this->get(Director::makeRelative($page1->Link()))->getBody();
|
$content = $this->get(Director::makeRelative($page1->Link()))->getBody();
|
||||||
$this->assertStringContainsString('message-setupWithoutRedirect', $content);
|
$this->assertStringContainsString('message-setupWithoutRedirect', $content);
|
||||||
|
|
||||||
/* This also applies for internal links */
|
// This also applies for internal links
|
||||||
$page2 = $this->objFromFixture(RedirectorPage::class, 'badinternal');
|
$page2 = $this->objFromFixture(RedirectorPage::class, 'badinternal');
|
||||||
$this->assertEquals('/bad-internal/', $page2->Link());
|
$this->assertEquals('/bad-internal', $page2->Link());
|
||||||
$content = $this->get(Director::makeRelative($page2->Link()))->getBody();
|
$response = $this->get(Director::makeRelative($page2->Link()));
|
||||||
|
$content = $response->getBody();
|
||||||
|
if ($use404) {
|
||||||
|
$this->assertNull($response->getBody());
|
||||||
|
} else {
|
||||||
$this->assertStringContainsString('message-setupWithoutRedirect', $content);
|
$this->assertStringContainsString('message-setupWithoutRedirect', $content);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function testReflexiveAndTransitiveInternalRedirectors()
|
public function testReflexiveAndTransitiveInternalRedirectors()
|
||||||
{
|
{
|
||||||
/* Reflexive redirectors are those that point to themselves. They should behave the same as an empty redirector */
|
// Reflexive redirectors are those that point to themselves.
|
||||||
|
// They should behave the same as an empty redirector
|
||||||
$page = $this->objFromFixture(RedirectorPage::class, 'reflexive');
|
$page = $this->objFromFixture(RedirectorPage::class, 'reflexive');
|
||||||
$this->assertEquals('/reflexive/', $page->Link());
|
$this->assertEquals('/reflexive', $page->Link());
|
||||||
$content = $this->get(Director::makeRelative($page->Link()))->getBody();
|
$content = $this->get(Director::makeRelative($page->Link()))->getBody();
|
||||||
$this->assertStringContainsString('message-setupWithoutRedirect', $content);
|
$this->assertStringContainsString('message-setupWithoutRedirect', $content);
|
||||||
|
|
||||||
/* Transitive redirectors are those that point to another redirector page. They should send people to the URLSegment
|
// Transitive redirectors are those that point to another redirector page.
|
||||||
* of the destination page - the middle-stop, so to speak. That should redirect to the final destination */
|
// They should send people to the URLSegment of the destination page - the middle-stop, so to speak.
|
||||||
|
// That should redirect to the final destination
|
||||||
$page = $this->objFromFixture(RedirectorPage::class, 'transitive');
|
$page = $this->objFromFixture(RedirectorPage::class, 'transitive');
|
||||||
$this->assertEquals('/good-internal/', $page->Link());
|
$this->assertEquals('/good-internal', $page->Link());
|
||||||
|
|
||||||
$this->autoFollowRedirection = false;
|
$this->autoFollowRedirection = false;
|
||||||
$response = $this->get(Director::makeRelative($page->Link()));
|
$response = $this->get(Director::makeRelative($page->Link()));
|
||||||
$this->assertEquals(Director::absoluteURL('/redirection-dest/'), $response->getHeader("Location"));
|
$this->assertEquals(Director::absoluteURL('/redirection-dest'), $response->getHeader("Location"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testExternalURLGetsPrefixIfNotSet()
|
public function testExternalURLGetsPrefixIfNotSet()
|
||||||
@ -90,7 +127,11 @@ class RedirectorPageTest extends FunctionalTest
|
|||||||
$page = $this->objFromFixture(RedirectorPage::class, 'externalnoprefix');
|
$page = $this->objFromFixture(RedirectorPage::class, 'externalnoprefix');
|
||||||
$this->assertEquals($page->ExternalURL, 'http://google.com', 'onBeforeWrite has prefixed with http');
|
$this->assertEquals($page->ExternalURL, 'http://google.com', 'onBeforeWrite has prefixed with http');
|
||||||
$page->write();
|
$page->write();
|
||||||
$this->assertEquals($page->ExternalURL, 'http://google.com', 'onBeforeWrite will not double prefix if written again!');
|
$this->assertEquals(
|
||||||
|
$page->ExternalURL,
|
||||||
|
'http://google.com',
|
||||||
|
'onBeforeWrite will not double prefix if written again!'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAllowsProtocolRelative()
|
public function testAllowsProtocolRelative()
|
||||||
@ -139,4 +180,51 @@ class RedirectorPageTest extends FunctionalTest
|
|||||||
$page = $this->objFromFixture(RedirectorPage::class, 'file');
|
$page = $this->objFromFixture(RedirectorPage::class, 'file');
|
||||||
$this->assertStringContainsString("FileTest.txt", $page->Link());
|
$this->assertStringContainsString("FileTest.txt", $page->Link());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function provideUnpublishedTarget()
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'use 200 with sitetree' => [
|
||||||
|
'use404' => false,
|
||||||
|
'isFile' => false,
|
||||||
|
],
|
||||||
|
'use 404 with sitetree' => [
|
||||||
|
'use404' => true,
|
||||||
|
'isFile' => false,
|
||||||
|
],
|
||||||
|
'use 200 with file' => [
|
||||||
|
'use404' => false,
|
||||||
|
'isFile' => true,
|
||||||
|
],
|
||||||
|
'use 404 with file' => [
|
||||||
|
'use404' => true,
|
||||||
|
'isFile' => true,
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider provideUnpublishedTarget
|
||||||
|
*/
|
||||||
|
public function testUnpublishedTarget(bool $use404, bool $isFile)
|
||||||
|
{
|
||||||
|
Config::modify()->set(RedirectorPageController::class, 'missing_redirect_is_404', $use404);
|
||||||
|
$redirectorPage = $this->objFromFixture(RedirectorPage::class, $isFile ? 'file' : 'goodinternal');
|
||||||
|
$targetModel = $isFile ? $redirectorPage->LinkToFile() : $redirectorPage->LinkTo();
|
||||||
|
$targetModel->publishSingle();
|
||||||
|
$redirectorPage->publishSingle();
|
||||||
|
$this->assertEquals(Controller::normaliseTrailingSlash($isFile ? '/filedirector' : '/good-internal'), $redirectorPage->regularLink());
|
||||||
|
$redirectorPageLink = Director::makeRelative($redirectorPage->regularLink());
|
||||||
|
|
||||||
|
// redirector page should give 301 (redirection) status code
|
||||||
|
$response = $this->get($redirectorPageLink);
|
||||||
|
$this->assertEquals(301, $response->getStatusCode());
|
||||||
|
|
||||||
|
// Unpublish the target model of this redirector page.
|
||||||
|
$targetModel->doUnpublish();
|
||||||
|
|
||||||
|
// redirector page should give a 404 or a 200 based on config when there's no page to redirect to
|
||||||
|
$response = $this->get($redirectorPageLink);
|
||||||
|
$this->assertEquals($use404 ? 404 : 200, $response->getStatusCode());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
Page:
|
SilverStripe\CMS\Model\SiteTree:
|
||||||
dest:
|
dest:
|
||||||
Title: Redirection Dest
|
Title: Redirection Dest
|
||||||
URLSegment: redirection-dest
|
URLSegment: redirection-dest
|
||||||
@ -17,7 +17,7 @@ SilverStripe\CMS\Model\RedirectorPage:
|
|||||||
Title: Good Internal
|
Title: Good Internal
|
||||||
URLSegment: good-internal
|
URLSegment: good-internal
|
||||||
RedirectionType: Internal
|
RedirectionType: Internal
|
||||||
LinkTo: =>Page.dest
|
LinkTo: =>SilverStripe\CMS\Model\SiteTree.dest
|
||||||
badexternal:
|
badexternal:
|
||||||
Title: Bad External
|
Title: Bad External
|
||||||
RedirectionType: External
|
RedirectionType: External
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace SilverStripe\CMS\Tests\Model;
|
namespace SilverStripe\CMS\Tests\Model;
|
||||||
|
|
||||||
use Page;
|
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
use SilverStripe\Dev\FunctionalTest;
|
use SilverStripe\Dev\FunctionalTest;
|
||||||
use SilverStripe\ORM\DB;
|
use SilverStripe\ORM\DB;
|
||||||
@ -21,7 +20,6 @@ use SilverStripe\Versioned\Versioned;
|
|||||||
*/
|
*/
|
||||||
class SiteTreeActionsTest extends FunctionalTest
|
class SiteTreeActionsTest extends FunctionalTest
|
||||||
{
|
{
|
||||||
|
|
||||||
protected static $fixture_file = 'SiteTreeActionsTest.yml';
|
protected static $fixture_file = 'SiteTreeActionsTest.yml';
|
||||||
|
|
||||||
public function testActionsReadonly()
|
public function testActionsReadonly()
|
||||||
@ -38,7 +36,7 @@ class SiteTreeActionsTest extends FunctionalTest
|
|||||||
Security::setCurrentUser($readonlyEditor);
|
Security::setCurrentUser($readonlyEditor);
|
||||||
|
|
||||||
// Reload latest version
|
// Reload latest version
|
||||||
$page = Page::get()->byID($page->ID);
|
$page = SiteTree::get()->byID($page->ID);
|
||||||
$actions = $page->getCMSActions();
|
$actions = $page->getCMSActions();
|
||||||
|
|
||||||
$this->assertNull($actions->dataFieldByName('action_save'));
|
$this->assertNull($actions->dataFieldByName('action_save'));
|
||||||
@ -84,14 +82,14 @@ class SiteTreeActionsTest extends FunctionalTest
|
|||||||
$author = $this->objFromFixture(Member::class, 'cmseditor');
|
$author = $this->objFromFixture(Member::class, 'cmseditor');
|
||||||
Security::setCurrentUser($author);
|
Security::setCurrentUser($author);
|
||||||
|
|
||||||
/** @var Page $page */
|
/** @var SiteTree $page */
|
||||||
$page = new Page();
|
$page = new SiteTree();
|
||||||
$page->CanEditType = 'LoggedInUsers';
|
$page->CanEditType = 'LoggedInUsers';
|
||||||
$page->write();
|
$page->write();
|
||||||
$page->publishRecursive();
|
$page->publishRecursive();
|
||||||
|
|
||||||
// Reload latest version
|
// Reload latest version
|
||||||
$page = Page::get()->byID($page->ID);
|
$page = SiteTree::get()->byID($page->ID);
|
||||||
|
|
||||||
$actions = $page->getCMSActions();
|
$actions = $page->getCMSActions();
|
||||||
|
|
||||||
@ -108,7 +106,7 @@ class SiteTreeActionsTest extends FunctionalTest
|
|||||||
$author = $this->objFromFixture(Member::class, 'cmseditor');
|
$author = $this->objFromFixture(Member::class, 'cmseditor');
|
||||||
Security::setCurrentUser($author);
|
Security::setCurrentUser($author);
|
||||||
|
|
||||||
$page = new Page();
|
$page = new SiteTree();
|
||||||
$page->CanEditType = 'LoggedInUsers';
|
$page->CanEditType = 'LoggedInUsers';
|
||||||
$page->write();
|
$page->write();
|
||||||
$this->assertTrue($page->canPublish());
|
$this->assertTrue($page->canPublish());
|
||||||
@ -135,7 +133,7 @@ class SiteTreeActionsTest extends FunctionalTest
|
|||||||
$author = $this->objFromFixture(Member::class, 'cmseditor');
|
$author = $this->objFromFixture(Member::class, 'cmseditor');
|
||||||
Security::setCurrentUser($author);
|
Security::setCurrentUser($author);
|
||||||
|
|
||||||
$page = new Page();
|
$page = new SiteTree();
|
||||||
$page->CanEditType = 'LoggedInUsers';
|
$page->CanEditType = 'LoggedInUsers';
|
||||||
$page->write();
|
$page->write();
|
||||||
$this->assertTrue($page->canPublish());
|
$this->assertTrue($page->canPublish());
|
||||||
@ -145,7 +143,7 @@ class SiteTreeActionsTest extends FunctionalTest
|
|||||||
$page->flushCache();
|
$page->flushCache();
|
||||||
|
|
||||||
// Reload latest version
|
// Reload latest version
|
||||||
$page = Page::get()->byID($page->ID);
|
$page = SiteTree::get()->byID($page->ID);
|
||||||
|
|
||||||
$actions = $page->getCMSActions();
|
$actions = $page->getCMSActions();
|
||||||
$this->assertNotNull($actions->dataFieldByName('action_save'));
|
$this->assertNotNull($actions->dataFieldByName('action_save'));
|
||||||
@ -158,15 +156,17 @@ class SiteTreeActionsTest extends FunctionalTest
|
|||||||
|
|
||||||
public function testActionsViewingOldVersion()
|
public function testActionsViewingOldVersion()
|
||||||
{
|
{
|
||||||
$p = new Page();
|
$p = new SiteTree();
|
||||||
$p->Content = 'test page first version';
|
$p->Content = 'test page first version';
|
||||||
$p->write();
|
$p->write();
|
||||||
$p->Content = 'new content';
|
$p->Content = 'new content';
|
||||||
$p->write();
|
$p->write();
|
||||||
|
|
||||||
// Looking at the old version, the ability to rollback to that version is available
|
// Looking at the old version, the ability to rollback to that version is available
|
||||||
$version = DB::query('SELECT "Version" FROM "SiteTree_Versions" WHERE "Content" = \'test page first version\'')->value();
|
$version = DB::query(
|
||||||
$old = Versioned::get_version('Page', $p->ID, $version);
|
'SELECT "Version" FROM "SiteTree_Versions" WHERE "Content" = \'test page first version\''
|
||||||
|
)->value();
|
||||||
|
$old = Versioned::get_version(SiteTree::class, $p->ID, $version);
|
||||||
$actions = $old->getCMSActions();
|
$actions = $old->getCMSActions();
|
||||||
$this->assertNull($actions->dataFieldByName('action_save'));
|
$this->assertNull($actions->dataFieldByName('action_save'));
|
||||||
$this->assertNull($actions->dataFieldByName('action_publish'));
|
$this->assertNull($actions->dataFieldByName('action_publish'));
|
||||||
|
@ -4,9 +4,9 @@ namespace SilverStripe\CMS\Tests\Model;
|
|||||||
|
|
||||||
use SilverStripe\Dev\TestOnly;
|
use SilverStripe\Dev\TestOnly;
|
||||||
use SilverStripe\Security\Permission;
|
use SilverStripe\Security\Permission;
|
||||||
use Page;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
|
||||||
class SiteTreeActionsTest_Page extends Page implements TestOnly
|
class SiteTreeActionsTest_Page extends SiteTree implements TestOnly
|
||||||
{
|
{
|
||||||
public function canEdit($member = null)
|
public function canEdit($member = null)
|
||||||
{
|
{
|
||||||
|
@ -18,7 +18,7 @@ class SiteTreeBacklinksTest extends SapphireTest
|
|||||||
|
|
||||||
protected static $required_extensions = [
|
protected static $required_extensions = [
|
||||||
SiteTree::class => [
|
SiteTree::class => [
|
||||||
SiteTreeBacklinksTest_DOD::class
|
SiteTreeBacklinksTest_DOD::class,
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -34,10 +34,10 @@ class SiteTreeBacklinksTest extends SapphireTest
|
|||||||
// testing here.
|
// testing here.
|
||||||
$this->logInWithPermission('ADMIN');
|
$this->logInWithPermission('ADMIN');
|
||||||
|
|
||||||
$page3 = $this->objFromFixture('Page', 'page3');
|
$page3 = $this->objFromFixture(SiteTree::class, 'page3');
|
||||||
$page3->Content = str_replace(
|
$page3->Content = str_replace(
|
||||||
'$page1.ID',
|
'$page1.ID',
|
||||||
$this->objFromFixture('Page', 'page1')->ID ?? '',
|
$this->objFromFixture(SiteTree::class, 'page1')->ID ?? '',
|
||||||
$page3->Content ?? ''
|
$page3->Content ?? ''
|
||||||
);
|
);
|
||||||
$page3->write();
|
$page3->write();
|
||||||
@ -46,18 +46,22 @@ class SiteTreeBacklinksTest extends SapphireTest
|
|||||||
public function testSavingPageWithLinkAddsBacklink()
|
public function testSavingPageWithLinkAddsBacklink()
|
||||||
{
|
{
|
||||||
// load page 1
|
// load page 1
|
||||||
$page1 = $this->objFromFixture('Page', 'page1');
|
$page1 = $this->objFromFixture(SiteTree::class, 'page1');
|
||||||
|
|
||||||
// assert backlink to page 2 doesn't exist
|
// assert backlink to page 2 doesn't exist
|
||||||
$page2 = $this->objFromFixture('Page', 'page2');
|
$page2 = $this->objFromFixture(SiteTree::class, 'page2');
|
||||||
$this->assertNotContains($page2->ID, $page1->BackLinkTracking()->column('ID'), 'Assert backlink to page 2 doesn\'t exist');
|
$this->assertNotContains(
|
||||||
|
$page2->ID,
|
||||||
|
$page1->BackLinkTracking()->column('ID'),
|
||||||
|
'Assert backlink to page 2 doesn\'t exist'
|
||||||
|
);
|
||||||
|
|
||||||
// add hyperlink to page 1 on page 2
|
// add hyperlink to page 1 on page 2
|
||||||
$page2->Content .= '<p><a href="[sitetree_link,id='.$page1->ID.']">Testing page 1 link</a></p>';
|
$page2->Content .= '<p><a href="[sitetree_link,id=' . $page1->ID . ']">Testing page 1 link</a></p>';
|
||||||
$page2->write();
|
$page2->write();
|
||||||
|
|
||||||
// load page 1
|
// load page 1
|
||||||
$page1 = $this->objFromFixture('Page', 'page1');
|
$page1 = $this->objFromFixture(SiteTree::class, 'page1');
|
||||||
|
|
||||||
// assert backlink to page 2 exists
|
// assert backlink to page 2 exists
|
||||||
$this->assertContains($page2->ID, $page1->BackLinkTracking()->column('ID'), 'Assert backlink to page 2 exists');
|
$this->assertContains($page2->ID, $page1->BackLinkTracking()->column('ID'), 'Assert backlink to page 2 exists');
|
||||||
@ -66,10 +70,10 @@ class SiteTreeBacklinksTest extends SapphireTest
|
|||||||
public function testRemovingLinkFromPageRemovesBacklink()
|
public function testRemovingLinkFromPageRemovesBacklink()
|
||||||
{
|
{
|
||||||
// load page 1
|
// load page 1
|
||||||
$page1 = $this->objFromFixture('Page', 'page1');
|
$page1 = $this->objFromFixture(SiteTree::class, 'page1');
|
||||||
|
|
||||||
// assert backlink to page 3 exits
|
// assert backlink to page 3 exits
|
||||||
$page3 = $this->objFromFixture('Page', 'page3');
|
$page3 = $this->objFromFixture(SiteTree::class, 'page3');
|
||||||
$this->assertContains($page3->ID, $page1->BackLinkTracking()->column('ID'), 'Assert backlink to page 3 exists');
|
$this->assertContains($page3->ID, $page1->BackLinkTracking()->column('ID'), 'Assert backlink to page 3 exists');
|
||||||
|
|
||||||
// remove hyperlink to page 1
|
// remove hyperlink to page 1
|
||||||
@ -77,116 +81,156 @@ class SiteTreeBacklinksTest extends SapphireTest
|
|||||||
$page3->write();
|
$page3->write();
|
||||||
|
|
||||||
// load page 1
|
// load page 1
|
||||||
$page1 = $this->objFromFixture('Page', 'page1');
|
$page1 = $this->objFromFixture(SiteTree::class, 'page1');
|
||||||
|
|
||||||
// assert backlink to page 3 exists
|
// assert backlink to page 3 exists
|
||||||
$this->assertNotContains($page3->ID, $page1->BackLinkTracking()->column('ID'), 'Assert backlink to page 3 doesn\'t exist');
|
$this->assertNotContains(
|
||||||
|
$page3->ID,
|
||||||
|
$page1->BackLinkTracking()->column('ID'),
|
||||||
|
'Assert backlink to page 3 doesn\'t exist'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testChangingUrlOnDraftSiteRewritesLink()
|
public function testChangingUrlOnDraftSiteRewritesLink()
|
||||||
{
|
{
|
||||||
// load page 1
|
// load page 1
|
||||||
$page1 = $this->objFromFixture('Page', 'page1');
|
$page1 = $this->objFromFixture(SiteTree::class, 'page1');
|
||||||
|
|
||||||
// assert backlink to page 3 exists
|
// assert backlink to page 3 exists
|
||||||
$page3 = $this->objFromFixture('Page', 'page3');
|
$page3 = $this->objFromFixture(SiteTree::class, 'page3');
|
||||||
$this->assertContains($page3->ID, $page1->BackLinkTracking()->column('ID'), 'Assert backlink to page 3 exists');
|
$this->assertContains($page3->ID, $page1->BackLinkTracking()->column('ID'), 'Assert backlink to page 3 exists');
|
||||||
|
|
||||||
// assert hyperlink to page 1's current url exists on page 3
|
// assert hyperlink to page 1's current url exists on page 3
|
||||||
$links = HTTP::getLinksIn($page3->obj('Content')->forTemplate());
|
$links = HTTP::getLinksIn($page3->obj('Content')->forTemplate());
|
||||||
$this->assertContains(Director::baseURL().'page1/', $links, 'Assert hyperlink to page 1\'s current url exists on page 3');
|
$this->assertContains(
|
||||||
|
Director::baseURL() . 'page1',
|
||||||
|
$links,
|
||||||
|
'Assert hyperlink to page 1\'s current url exists on page 3'
|
||||||
|
);
|
||||||
|
|
||||||
// change url of page 1
|
// change url of page 1
|
||||||
$page1->URLSegment = 'new-url-segment';
|
$page1->URLSegment = 'new-url-segment';
|
||||||
$page1->write();
|
$page1->write();
|
||||||
|
|
||||||
// load page 3
|
// load page 3
|
||||||
$page3 = $this->objFromFixture('Page', 'page3');
|
$page3 = $this->objFromFixture(SiteTree::class, 'page3');
|
||||||
|
|
||||||
// assert hyperlink to page 1's new url exists
|
// assert hyperlink to page 1's new url exists
|
||||||
$links = HTTP::getLinksIn($page3->obj('Content')->forTemplate());
|
$links = HTTP::getLinksIn($page3->obj('Content')->forTemplate());
|
||||||
$this->assertContains(Director::baseURL().'new-url-segment/', $links, 'Assert hyperlink to page 1\'s new url exists on page 3');
|
$this->assertContains(
|
||||||
|
Director::baseURL() . 'new-url-segment',
|
||||||
|
$links,
|
||||||
|
'Assert hyperlink to page 1\'s new url exists on page 3'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testChangingUrlOnLiveSiteRewritesLink()
|
public function testChangingUrlOnLiveSiteRewritesLink()
|
||||||
{
|
{
|
||||||
// publish page 1 & 3
|
// publish page 1 & 3
|
||||||
$page1 = $this->objFromFixture('Page', 'page1');
|
$page1 = $this->objFromFixture(SiteTree::class, 'page1');
|
||||||
$page3 = $this->objFromFixture('Page', 'page3');
|
$page3 = $this->objFromFixture(SiteTree::class, 'page3');
|
||||||
$this->assertTrue($page1->publishRecursive());
|
$this->assertTrue($page1->publishRecursive());
|
||||||
$this->assertTrue($page3->publishRecursive());
|
$this->assertTrue($page3->publishRecursive());
|
||||||
|
|
||||||
// load pages from live
|
// load pages from live
|
||||||
$page1live = Versioned::get_one_by_stage('Page', 'Live', '"SiteTree"."ID" = ' . $page1->ID);
|
$page1live = Versioned::get_one_by_stage(SiteTree::class, 'Live', '"SiteTree"."ID" = ' . $page1->ID);
|
||||||
$page3live = Versioned::get_one_by_stage('Page', 'Live', '"SiteTree"."ID" = ' . $page3->ID);
|
$page3live = Versioned::get_one_by_stage(SiteTree::class, 'Live', '"SiteTree"."ID" = ' . $page3->ID);
|
||||||
|
|
||||||
// assert backlink to page 3 exists
|
// assert backlink to page 3 exists
|
||||||
$this->assertContains($page3live->ID, $page1live->BackLinkTracking()->column('ID'), 'Assert backlink to page 3 exists');
|
$this->assertContains(
|
||||||
|
$page3live->ID,
|
||||||
|
$page1live->BackLinkTracking()->column('ID'),
|
||||||
|
'Assert backlink to page 3 exists'
|
||||||
|
);
|
||||||
|
|
||||||
// assert hyperlink to page 1's current url exists on page 3
|
// assert hyperlink to page 1's current url exists on page 3
|
||||||
$links = HTTP::getLinksIn($page3live->obj('Content')->forTemplate());
|
$links = HTTP::getLinksIn($page3live->obj('Content')->forTemplate());
|
||||||
$this->assertContains(Director::baseURL().'page1/', $links, 'Assert hyperlink to page 1\'s current url exists on page 3');
|
$this->assertContains(
|
||||||
|
Director::baseURL() . 'page1',
|
||||||
|
$links,
|
||||||
|
'Assert hyperlink to page 1\'s current url exists on page 3'
|
||||||
|
);
|
||||||
|
|
||||||
// change url of page 1
|
// change url of page 1
|
||||||
$page1live->URLSegment = 'new-url-segment';
|
$page1live->URLSegment = 'new-url-segment';
|
||||||
$page1live->writeToStage('Live');
|
$page1live->writeToStage('Live');
|
||||||
|
|
||||||
// load page 3 from live
|
// load page 3 from live
|
||||||
$page3live = Versioned::get_one_by_stage('Page', 'Live', '"SiteTree"."ID" = ' . $page3->ID);
|
$page3live = Versioned::get_one_by_stage(SiteTree::class, 'Live', '"SiteTree"."ID" = ' . $page3->ID);
|
||||||
|
|
||||||
// assert hyperlink to page 1's new url exists
|
// assert hyperlink to page 1's new url exists
|
||||||
Versioned::set_stage(Versioned::LIVE);
|
Versioned::set_stage(Versioned::LIVE);
|
||||||
$links = HTTP::getLinksIn($page3live->obj('Content')->forTemplate());
|
$links = HTTP::getLinksIn($page3live->obj('Content')->forTemplate());
|
||||||
$this->assertContains(Director::baseURL().'new-url-segment/', $links, 'Assert hyperlink to page 1\'s new url exists on page 3');
|
$this->assertContains(
|
||||||
|
Director::baseURL() . 'new-url-segment',
|
||||||
|
$links,
|
||||||
|
'Assert hyperlink to page 1\'s new url exists on page 3'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPublishingPageWithModifiedUrlRewritesLink()
|
public function testPublishingPageWithModifiedUrlRewritesLink()
|
||||||
{
|
{
|
||||||
// publish page 1 & 3
|
// publish page 1 & 3
|
||||||
$page1 = $this->objFromFixture('Page', 'page1');
|
$page1 = $this->objFromFixture(SiteTree::class, 'page1');
|
||||||
$page3 = $this->objFromFixture('Page', 'page3');
|
$page3 = $this->objFromFixture(SiteTree::class, 'page3');
|
||||||
|
|
||||||
$this->assertTrue($page1->publishRecursive());
|
$this->assertTrue($page1->publishRecursive());
|
||||||
$this->assertTrue($page3->publishRecursive());
|
$this->assertTrue($page3->publishRecursive());
|
||||||
|
|
||||||
// load page 3 from live
|
// load page 3 from live
|
||||||
$page3live = Versioned::get_one_by_stage('Page', 'Live', '"SiteTree"."ID" = ' . $page3->ID);
|
$page3live = Versioned::get_one_by_stage(SiteTree::class, 'Live', '"SiteTree"."ID" = ' . $page3->ID);
|
||||||
|
|
||||||
// assert hyperlink to page 1's current url exists
|
// assert hyperlink to page 1's current url exists
|
||||||
$links = HTTP::getLinksIn($page3live->obj('Content')->forTemplate());
|
$links = HTTP::getLinksIn($page3live->obj('Content')->forTemplate());
|
||||||
$this->assertContains(Director::baseURL().'page1/', $links, 'Assert hyperlink to page 1\'s current url exists on page 3');
|
$this->assertContains(
|
||||||
|
Director::baseURL() . 'page1',
|
||||||
|
$links,
|
||||||
|
'Assert hyperlink to page 1\'s current url exists on page 3'
|
||||||
|
);
|
||||||
|
|
||||||
// rename url of page 1 on stage
|
// rename url of page 1 on stage
|
||||||
$page1->URLSegment = 'new-url-segment';
|
$page1->URLSegment = 'new-url-segment';
|
||||||
$page1->write();
|
$page1->write();
|
||||||
|
|
||||||
// assert hyperlink to page 1's current publish url exists
|
// assert hyperlink to page 1's current publish url exists
|
||||||
$page3live = Versioned::get_one_by_stage('Page', 'Live', '"SiteTree"."ID" = ' . $page3->ID);
|
$page3live = Versioned::get_one_by_stage(SiteTree::class, 'Live', '"SiteTree"."ID" = ' . $page3->ID);
|
||||||
Versioned::set_stage(Versioned::LIVE);
|
Versioned::set_stage(Versioned::LIVE);
|
||||||
$links = HTTP::getLinksIn($page3live->obj('Content')->forTemplate());
|
$links = HTTP::getLinksIn($page3live->obj('Content')->forTemplate());
|
||||||
$this->assertContains(Director::baseURL().'page1/', $links, 'Assert hyperlink to page 1\'s current published url exists on page 3');
|
$this->assertContains(
|
||||||
|
Director::baseURL() . 'page1',
|
||||||
|
$links,
|
||||||
|
'Assert hyperlink to page 1\'s current published url exists on page 3'
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
// publish page 1
|
// publish page 1
|
||||||
$this->assertTrue($page1->publishRecursive());
|
$this->assertTrue($page1->publishRecursive());
|
||||||
|
|
||||||
// assert hyperlink to page 1's new published url exists
|
// assert hyperlink to page 1's new published url exists
|
||||||
$page3live = Versioned::get_one_by_stage('Page', 'Live', '"SiteTree"."ID" = ' . $page3->ID);
|
$page3live = Versioned::get_one_by_stage(SiteTree::class, 'Live', '"SiteTree"."ID" = ' . $page3->ID);
|
||||||
$links = HTTP::getLinksIn($page3live->obj('Content')->forTemplate());
|
$links = HTTP::getLinksIn($page3live->obj('Content')->forTemplate());
|
||||||
$this->assertContains(Director::baseURL().'new-url-segment/', $links, 'Assert hyperlink to page 1\'s new published url exists on page 3');
|
$this->assertContains(
|
||||||
|
Director::baseURL() . 'new-url-segment',
|
||||||
|
$links,
|
||||||
|
'Assert hyperlink to page 1\'s new published url exists on page 3'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPublishingPageWithModifiedLinksRewritesLinks()
|
public function testPublishingPageWithModifiedLinksRewritesLinks()
|
||||||
{
|
{
|
||||||
// publish page 1 & 3
|
// publish page 1 & 3
|
||||||
$page1 = $this->objFromFixture('Page', 'page1');
|
$page1 = $this->objFromFixture(SiteTree::class, 'page1');
|
||||||
$page3 = $this->objFromFixture('Page', 'page3');
|
$page3 = $this->objFromFixture(SiteTree::class, 'page3');
|
||||||
$this->assertTrue($page1->publishRecursive());
|
$this->assertTrue($page1->publishRecursive());
|
||||||
$this->assertTrue($page3->publishRecursive());
|
$this->assertTrue($page3->publishRecursive());
|
||||||
|
|
||||||
// assert hyperlink to page 1's current url exists
|
// assert hyperlink to page 1's current url exists
|
||||||
$links = HTTP::getLinksIn($page3->obj('Content')->forTemplate());
|
$links = HTTP::getLinksIn($page3->obj('Content')->forTemplate());
|
||||||
$this->assertContains(Director::baseURL().'page1/', $links, 'Assert hyperlink to page 1\'s current published url exists on page 3');
|
$this->assertContains(
|
||||||
|
Director::baseURL() . 'page1',
|
||||||
|
$links,
|
||||||
|
'Assert hyperlink to page 1\'s current published url exists on page 3'
|
||||||
|
);
|
||||||
|
|
||||||
// change page 1 url on draft
|
// change page 1 url on draft
|
||||||
$page1->URLSegment = 'new-url-segment';
|
$page1->URLSegment = 'new-url-segment';
|
||||||
@ -195,42 +239,58 @@ class SiteTreeBacklinksTest extends SapphireTest
|
|||||||
$page1->write();
|
$page1->write();
|
||||||
|
|
||||||
// assert page 3 on draft contains new page 1 url
|
// assert page 3 on draft contains new page 1 url
|
||||||
$page3 = $this->objFromFixture('Page', 'page3');
|
$page3 = $this->objFromFixture(SiteTree::class, 'page3');
|
||||||
$links = HTTP::getLinksIn($page3->obj('Content')->forTemplate());
|
$links = HTTP::getLinksIn($page3->obj('Content')->forTemplate());
|
||||||
$this->assertContains(Director::baseURL().'new-url-segment/', $links, 'Assert hyperlink to page 1\'s current draft url exists on page 3');
|
$this->assertContains(
|
||||||
|
Director::baseURL() . 'new-url-segment',
|
||||||
|
$links,
|
||||||
|
'Assert hyperlink to page 1\'s current draft url exists on page 3'
|
||||||
|
);
|
||||||
|
|
||||||
// publish page 3
|
// publish page 3
|
||||||
$this->assertTrue($page3->publishRecursive());
|
$this->assertTrue($page3->publishRecursive());
|
||||||
|
|
||||||
// assert page 3 on published site contains old page 1 url
|
// assert page 3 on published site contains old page 1 url
|
||||||
$page3live = Versioned::get_one_by_stage('Page', 'Live', '"SiteTree"."ID" = ' . $page3->ID);
|
$page3live = Versioned::get_one_by_stage(SiteTree::class, 'Live', '"SiteTree"."ID" = ' . $page3->ID);
|
||||||
Versioned::set_stage(Versioned::LIVE);
|
Versioned::set_stage(Versioned::LIVE);
|
||||||
$links = HTTP::getLinksIn($page3live->obj('Content')->forTemplate());
|
$links = HTTP::getLinksIn($page3live->obj('Content')->forTemplate());
|
||||||
$this->assertContains(Director::baseURL().'page1/', $links, 'Assert hyperlink to page 1\'s current published url exists on page 3');
|
$this->assertContains(
|
||||||
|
Director::baseURL() . 'page1',
|
||||||
|
$links,
|
||||||
|
'Assert hyperlink to page 1\'s current published url exists on page 3'
|
||||||
|
);
|
||||||
|
|
||||||
// publish page 1
|
// publish page 1
|
||||||
$this->assertTrue($page1->publishRecursive());
|
$this->assertTrue($page1->publishRecursive());
|
||||||
|
|
||||||
// assert page 3 on published site contains new page 1 url
|
// assert page 3 on published site contains new page 1 url
|
||||||
$page3live = Versioned::get_one_by_stage('Page', 'Live', '"SiteTree"."ID" = ' . $page3->ID);
|
$page3live = Versioned::get_one_by_stage(SiteTree::class, 'Live', '"SiteTree"."ID" = ' . $page3->ID);
|
||||||
$links = HTTP::getLinksIn($page3live->obj('Content')->forTemplate());
|
$links = HTTP::getLinksIn($page3live->obj('Content')->forTemplate());
|
||||||
$this->assertContains(Director::baseURL().'new-url-segment/', $links, 'Assert hyperlink to page 1\'s current published url exists on page 3');
|
$this->assertContains(
|
||||||
|
Director::baseURL() . 'new-url-segment',
|
||||||
|
$links,
|
||||||
|
'Assert hyperlink to page 1\'s current published url exists on page 3'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testLinkTrackingOnExtraContentFields()
|
public function testLinkTrackingOnExtraContentFields()
|
||||||
{
|
{
|
||||||
/** @var Page $page1 */
|
/** @var SiteTree $page1 */
|
||||||
$page1 = $this->objFromFixture('Page', 'page1');
|
$page1 = $this->objFromFixture(SiteTree::class, 'page1');
|
||||||
/** @var Page $page2 */
|
/** @var SiteTree $page2 */
|
||||||
$page2 = $this->objFromFixture('Page', 'page2');
|
$page2 = $this->objFromFixture(SiteTree::class, 'page2');
|
||||||
$page1->publishRecursive();
|
$page1->publishRecursive();
|
||||||
$page2->publishRecursive();
|
$page2->publishRecursive();
|
||||||
|
|
||||||
// assert backlink to page 2 doesn't exist
|
// assert backlink to page 2 doesn't exist
|
||||||
$this->assertNotContains($page2->ID, $page1->BackLinkTracking()->column('ID'), 'Assert backlink to page 2 doesn\'t exist');
|
$this->assertNotContains(
|
||||||
|
$page2->ID,
|
||||||
|
$page1->BackLinkTracking()->column('ID'),
|
||||||
|
'Assert backlink to page 2 doesn\'t exist'
|
||||||
|
);
|
||||||
|
|
||||||
// add hyperlink to page 1 on page 2
|
// add hyperlink to page 1 on page 2
|
||||||
$page2->ExtraContent .= '<p><a href="[sitetree_link,id='.$page1->ID.']">Testing page 1 link</a></p>';
|
$page2->ExtraContent .= '<p><a href="[sitetree_link,id=' . $page1->ID . ']">Testing page 1 link</a></p>';
|
||||||
$page2->write();
|
$page2->write();
|
||||||
$page2->publishRecursive();
|
$page2->publishRecursive();
|
||||||
|
|
||||||
@ -238,23 +298,32 @@ class SiteTreeBacklinksTest extends SapphireTest
|
|||||||
$this->assertContains($page2->ID, $page1->BackLinkTracking()->column('ID'), 'Assert backlink to page 2 exists');
|
$this->assertContains($page2->ID, $page1->BackLinkTracking()->column('ID'), 'Assert backlink to page 2 exists');
|
||||||
|
|
||||||
// update page1 url
|
// update page1 url
|
||||||
$page1 = $this->objFromFixture('Page', 'page1');
|
$page1 = $this->objFromFixture(SiteTree::class, 'page1');
|
||||||
$page1->URLSegment = "page1-new-url";
|
$page1->URLSegment = "page1-new-url";
|
||||||
$page1->write();
|
$page1->write();
|
||||||
|
|
||||||
// confirm that draft link on page2 has been rewritten
|
// confirm that draft link on page2 has been rewritten
|
||||||
$page2 = $this->objFromFixture('Page', 'page2');
|
$page2 = $this->objFromFixture(SiteTree::class, 'page2');
|
||||||
$this->assertEquals('<p><a href="'.Director::baseURL().'page1-new-url/">Testing page 1 link</a></p>', $page2->obj('ExtraContent')->forTemplate());
|
$this->assertEquals(
|
||||||
|
'<p><a href="' . Director::baseURL() . 'page1-new-url">Testing page 1 link</a></p>',
|
||||||
|
$page2->obj('ExtraContent')->forTemplate()
|
||||||
|
);
|
||||||
|
|
||||||
// confirm that published link hasn't
|
// confirm that published link hasn't
|
||||||
$page2Live = Versioned::get_one_by_stage("Page", "Live", "\"SiteTree\".\"ID\" = $page2->ID");
|
$page2Live = Versioned::get_one_by_stage(SiteTree::class, "Live", "\"SiteTree\".\"ID\" = $page2->ID");
|
||||||
Versioned::set_stage(Versioned::LIVE);
|
Versioned::set_stage(Versioned::LIVE);
|
||||||
$this->assertEquals('<p><a href="'.Director::baseURL().'page1/">Testing page 1 link</a></p>', $page2Live->obj('ExtraContent')->forTemplate());
|
$this->assertEquals(
|
||||||
|
'<p><a href="' . Director::baseURL() . 'page1">Testing page 1 link</a></p>',
|
||||||
|
$page2Live->obj('ExtraContent')->forTemplate()
|
||||||
|
);
|
||||||
|
|
||||||
// publish page1 and confirm that the link on the published page2 has now been updated
|
// publish page1 and confirm that the link on the published page2 has now been updated
|
||||||
$page1->publishRecursive();
|
$page1->publishRecursive();
|
||||||
$page2Live = Versioned::get_one_by_stage("Page", "Live", "\"SiteTree\".\"ID\" = $page2->ID");
|
$page2Live = Versioned::get_one_by_stage(SiteTree::class, "Live", "\"SiteTree\".\"ID\" = $page2->ID");
|
||||||
$this->assertEquals('<p><a href="'.Director::baseURL().'page1-new-url/">Testing page 1 link</a></p>', $page2Live->obj('ExtraContent')->forTemplate());
|
$this->assertEquals(
|
||||||
|
'<p><a href="' . Director::baseURL() . 'page1-new-url">Testing page 1 link</a></p>',
|
||||||
|
$page2Live->obj('ExtraContent')->forTemplate()
|
||||||
|
);
|
||||||
|
|
||||||
// Edit draft again
|
// Edit draft again
|
||||||
Versioned::set_stage(Versioned::DRAFT);
|
Versioned::set_stage(Versioned::DRAFT);
|
||||||
@ -262,12 +331,16 @@ class SiteTreeBacklinksTest extends SapphireTest
|
|||||||
$page2->write();
|
$page2->write();
|
||||||
|
|
||||||
// assert backlink to page 2 no longer exists
|
// assert backlink to page 2 no longer exists
|
||||||
$this->assertNotContains($page2->ID, $page1->BackLinkTracking()->column('ID'), 'Assert backlink to page 2 has been removed');
|
$this->assertNotContains(
|
||||||
|
$page2->ID,
|
||||||
|
$page1->BackLinkTracking()->column('ID'),
|
||||||
|
'Assert backlink to page 2 has been removed'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testLinkTrackingWithUntitledObjectsDisplaysAReadableIdentifier()
|
public function testLinkTrackingWithUntitledObjectsDisplaysAReadableIdentifier()
|
||||||
{
|
{
|
||||||
$page = $this->objFromFixture('Page', 'page2');
|
$page = $this->objFromFixture(SiteTree::class, 'page2');
|
||||||
|
|
||||||
$referencingObject = new SiteTreeBacklinksTestContentObject();
|
$referencingObject = new SiteTreeBacklinksTestContentObject();
|
||||||
$referencingObject->Content = '<p><a href="[sitetree_link,id='
|
$referencingObject->Content = '<p><a href="[sitetree_link,id='
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
Page:
|
SilverStripe\CMS\Model\SiteTree:
|
||||||
page1:
|
page1:
|
||||||
Title: page1
|
Title: page1
|
||||||
URLSegment: page1
|
URLSegment: page1
|
||||||
@ -11,6 +11,6 @@ Page:
|
|||||||
Title: page3
|
Title: page3
|
||||||
URLSegment: page3
|
URLSegment: page3
|
||||||
Content: '<p><a href="[sitetree_link,id=$page1.ID]">Testing page 1 link</a></p>'
|
Content: '<p><a href="[sitetree_link,id=$page1.ID]">Testing page 1 link</a></p>'
|
||||||
LinkTracking: =>Page.page1
|
LinkTracking: =>SilverStripe\CMS\Model\SiteTree.page1
|
||||||
|
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace SilverStripe\CMS\Tests\Model;
|
namespace SilverStripe\CMS\Tests\Model;
|
||||||
|
|
||||||
use Page;
|
|
||||||
use Silverstripe\Assets\Dev\TestAssetStore;
|
use Silverstripe\Assets\Dev\TestAssetStore;
|
||||||
use SilverStripe\CMS\Model\RedirectorPage;
|
use SilverStripe\CMS\Model\RedirectorPage;
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
@ -42,15 +41,15 @@ class SiteTreeBrokenLinksTest extends SapphireTest
|
|||||||
|
|
||||||
public function testBrokenLinksBetweenPages()
|
public function testBrokenLinksBetweenPages()
|
||||||
{
|
{
|
||||||
/** @var Page $obj */
|
/** @var SiteTree $obj */
|
||||||
$obj = $this->objFromFixture('Page', 'content');
|
$obj = $this->objFromFixture(SiteTree::class, 'content');
|
||||||
|
|
||||||
$obj->Content = '<a href="[sitetree_link,id=3423423]">this is a broken link</a>';
|
$obj->Content = '<a href="[sitetree_link,id=3423423]">this is a broken link</a>';
|
||||||
$obj->syncLinkTracking();
|
$obj->syncLinkTracking();
|
||||||
$this->assertTrue($obj->HasBrokenLink, 'Page has a broken link');
|
$this->assertTrue($obj->HasBrokenLink, 'Page has a broken link');
|
||||||
|
|
||||||
$obj->Content = '<a href="[sitetree_link,id=' . $this->idFromFixture(
|
$obj->Content = '<a href="[sitetree_link,id=' . $this->idFromFixture(
|
||||||
'Page',
|
SiteTree::class,
|
||||||
'about'
|
'about'
|
||||||
) . ']">this is not a broken link</a>';
|
) . ']">this is not a broken link</a>';
|
||||||
$obj->syncLinkTracking();
|
$obj->syncLinkTracking();
|
||||||
@ -62,8 +61,8 @@ class SiteTreeBrokenLinksTest extends SapphireTest
|
|||||||
*/
|
*/
|
||||||
public function testBrokenLinksNonPage()
|
public function testBrokenLinksNonPage()
|
||||||
{
|
{
|
||||||
/** @var Page $aboutPage */
|
/** @var SiteTree $aboutPage */
|
||||||
$aboutPage = $this->objFromFixture('Page', 'about');
|
$aboutPage = $this->objFromFixture(SiteTree::class, 'about');
|
||||||
|
|
||||||
/** @var NotPageObject $obj */
|
/** @var NotPageObject $obj */
|
||||||
$obj = $this->objFromFixture(NotPageObject::class, 'object1');
|
$obj = $this->objFromFixture(NotPageObject::class, 'object1');
|
||||||
@ -94,7 +93,7 @@ class SiteTreeBrokenLinksTest extends SapphireTest
|
|||||||
// About-page backlinks contains this object
|
// About-page backlinks contains this object
|
||||||
$this->assertListEquals(
|
$this->assertListEquals(
|
||||||
[
|
[
|
||||||
['ID' => $obj->ID]
|
['ID' => $obj->ID],
|
||||||
],
|
],
|
||||||
$aboutPage->BackLinkTracking()
|
$aboutPage->BackLinkTracking()
|
||||||
);
|
);
|
||||||
@ -102,9 +101,9 @@ class SiteTreeBrokenLinksTest extends SapphireTest
|
|||||||
|
|
||||||
public function testBrokenAnchorBetweenPages()
|
public function testBrokenAnchorBetweenPages()
|
||||||
{
|
{
|
||||||
/** @var Page $obj */
|
/** @var SiteTree $obj */
|
||||||
$obj = $this->objFromFixture('Page', 'content');
|
$obj = $this->objFromFixture(SiteTree::class, 'content');
|
||||||
$target = $this->objFromFixture('Page', 'about');
|
$target = $this->objFromFixture(SiteTree::class, 'about');
|
||||||
|
|
||||||
$obj->Content = "<a href=\"[sitetree_link,id={$target->ID}]#no-anchor-here\">this is a broken link</a>";
|
$obj->Content = "<a href=\"[sitetree_link,id={$target->ID}]#no-anchor-here\">this is a broken link</a>";
|
||||||
$obj->syncLinkTracking();
|
$obj->syncLinkTracking();
|
||||||
@ -117,7 +116,7 @@ class SiteTreeBrokenLinksTest extends SapphireTest
|
|||||||
|
|
||||||
public function testBrokenVirtualPages()
|
public function testBrokenVirtualPages()
|
||||||
{
|
{
|
||||||
$obj = $this->objFromFixture('Page', 'content');
|
$obj = $this->objFromFixture(SiteTree::class, 'content');
|
||||||
$vp = new VirtualPage();
|
$vp = new VirtualPage();
|
||||||
|
|
||||||
$vp->CopyContentFromID = $obj->ID;
|
$vp->CopyContentFromID = $obj->ID;
|
||||||
@ -131,7 +130,7 @@ class SiteTreeBrokenLinksTest extends SapphireTest
|
|||||||
|
|
||||||
public function testBrokenInternalRedirectorPages()
|
public function testBrokenInternalRedirectorPages()
|
||||||
{
|
{
|
||||||
$obj = $this->objFromFixture('Page', 'content');
|
$obj = $this->objFromFixture(SiteTree::class, 'content');
|
||||||
$rp = new RedirectorPage();
|
$rp = new RedirectorPage();
|
||||||
|
|
||||||
$rp->RedirectionType = 'Internal';
|
$rp->RedirectionType = 'Internal';
|
||||||
@ -148,10 +147,10 @@ class SiteTreeBrokenLinksTest extends SapphireTest
|
|||||||
public function testDeletingMarksBackLinkedPagesAsBroken()
|
public function testDeletingMarksBackLinkedPagesAsBroken()
|
||||||
{
|
{
|
||||||
// Set up two published pages with a link from content -> about
|
// Set up two published pages with a link from content -> about
|
||||||
$linkDest = $this->objFromFixture('Page', 'about');
|
$linkDest = $this->objFromFixture(SiteTree::class, 'about');
|
||||||
|
|
||||||
/** @var Page $linkSrc */
|
/** @var SiteTree $linkSrc */
|
||||||
$linkSrc = $this->objFromFixture('Page', 'content');
|
$linkSrc = $this->objFromFixture(SiteTree::class, 'content');
|
||||||
$linkSrc->Content = "<p><a href=\"[sitetree_link,id=$linkDest->ID]\">about us</a></p>";
|
$linkSrc->Content = "<p><a href=\"[sitetree_link,id=$linkDest->ID]\">about us</a></p>";
|
||||||
$linkSrc->write();
|
$linkSrc->write();
|
||||||
|
|
||||||
@ -163,7 +162,7 @@ class SiteTreeBrokenLinksTest extends SapphireTest
|
|||||||
|
|
||||||
// Confirm draft has broken link
|
// Confirm draft has broken link
|
||||||
$linkSrc->flushCache();
|
$linkSrc->flushCache();
|
||||||
$linkSrc = $this->objFromFixture('Page', 'content');
|
$linkSrc = $this->objFromFixture(SiteTree::class, 'content');
|
||||||
|
|
||||||
$this->assertEquals(1, (int)$linkSrc->HasBrokenLink);
|
$this->assertEquals(1, (int)$linkSrc->HasBrokenLink);
|
||||||
}
|
}
|
||||||
@ -173,13 +172,13 @@ class SiteTreeBrokenLinksTest extends SapphireTest
|
|||||||
$this->logInWithPermission('ADMIN');
|
$this->logInWithPermission('ADMIN');
|
||||||
|
|
||||||
// Set up two draft pages with a link from content -> about
|
// Set up two draft pages with a link from content -> about
|
||||||
/** @var Page $linkDest */
|
/** @var SiteTree $linkDest */
|
||||||
$linkDest = $this->objFromFixture('Page', 'about');
|
$linkDest = $this->objFromFixture(SiteTree::class, 'about');
|
||||||
// Ensure that it's not on the published site
|
// Ensure that it's not on the published site
|
||||||
$linkDest->doUnpublish();
|
$linkDest->doUnpublish();
|
||||||
|
|
||||||
/** @var Page $linkSrc */
|
/** @var SiteTree $linkSrc */
|
||||||
$linkSrc = $this->objFromFixture('Page', 'content');
|
$linkSrc = $this->objFromFixture(SiteTree::class, 'content');
|
||||||
$linkSrc->Content = "<p><a href=\"[sitetree_link,id=$linkDest->ID]\">about us</a></p>";
|
$linkSrc->Content = "<p><a href=\"[sitetree_link,id=$linkDest->ID]\">about us</a></p>";
|
||||||
$linkSrc->write();
|
$linkSrc->write();
|
||||||
|
|
||||||
@ -197,14 +196,14 @@ class SiteTreeBrokenLinksTest extends SapphireTest
|
|||||||
public function testRestoreFixesBrokenLinks()
|
public function testRestoreFixesBrokenLinks()
|
||||||
{
|
{
|
||||||
// Create page and virtual page
|
// Create page and virtual page
|
||||||
$p = new Page();
|
$p = new SiteTree();
|
||||||
$p->Title = "source";
|
$p->Title = "source";
|
||||||
$p->write();
|
$p->write();
|
||||||
$pageID = $p->ID;
|
$pageID = $p->ID;
|
||||||
$this->assertTrue($p->publishRecursive());
|
$this->assertTrue($p->publishRecursive());
|
||||||
|
|
||||||
// Content links are one kind of link to pages
|
// Content links are one kind of link to pages
|
||||||
$p2 = new Page();
|
$p2 = new SiteTree();
|
||||||
$p2->Title = "regular link";
|
$p2->Title = "regular link";
|
||||||
$p2->Content = "<a href=\"[sitetree_link,id=$p->ID]\">test</a>";
|
$p2->Content = "<a href=\"[sitetree_link,id=$p->ID]\">test</a>";
|
||||||
$p2->write();
|
$p2->write();
|
||||||
@ -264,14 +263,14 @@ class SiteTreeBrokenLinksTest extends SapphireTest
|
|||||||
public function testRevertToLiveFixesBrokenLinks()
|
public function testRevertToLiveFixesBrokenLinks()
|
||||||
{
|
{
|
||||||
// Create page and virutal page
|
// Create page and virutal page
|
||||||
$page = new Page();
|
$page = new SiteTree();
|
||||||
$page->Title = "source";
|
$page->Title = "source";
|
||||||
$page->write();
|
$page->write();
|
||||||
$pageID = $page->ID;
|
$pageID = $page->ID;
|
||||||
$this->assertTrue($page->publishRecursive());
|
$this->assertTrue($page->publishRecursive());
|
||||||
|
|
||||||
// Content links are one kind of link to pages
|
// Content links are one kind of link to pages
|
||||||
$page2 = new Page();
|
$page2 = new SiteTree();
|
||||||
$page2->Title = "regular link";
|
$page2->Title = "regular link";
|
||||||
$page2->Content = "<a href=\"[sitetree_link,id={$pageID}]\">test</a>";
|
$page2->Content = "<a href=\"[sitetree_link,id={$pageID}]\">test</a>";
|
||||||
$page2->write();
|
$page2->write();
|
||||||
@ -302,7 +301,7 @@ class SiteTreeBrokenLinksTest extends SapphireTest
|
|||||||
$this->assertEquals(1, $redirectorPage->HasBrokenLink);
|
$this->assertEquals(1, $redirectorPage->HasBrokenLink);
|
||||||
|
|
||||||
// Call doRevertToLive and confirm that broken links are restored
|
// Call doRevertToLive and confirm that broken links are restored
|
||||||
/** @var Page $pageLive */
|
/** @var SiteTree $pageLive */
|
||||||
$pageLive = Versioned::get_one_by_stage(SiteTree::class, 'Live', '"SiteTree"."ID" = ' . $pageID);
|
$pageLive = Versioned::get_one_by_stage(SiteTree::class, 'Live', '"SiteTree"."ID" = ' . $pageID);
|
||||||
$pageLive->doRevertToLive();
|
$pageLive->doRevertToLive();
|
||||||
|
|
||||||
@ -316,15 +315,17 @@ class SiteTreeBrokenLinksTest extends SapphireTest
|
|||||||
|
|
||||||
public function testBrokenAnchorLinksInAPage()
|
public function testBrokenAnchorLinksInAPage()
|
||||||
{
|
{
|
||||||
/** @var Page $obj */
|
/** @var SiteTree $obj */
|
||||||
$obj = $this->objFromFixture('Page', 'content');
|
$obj = $this->objFromFixture(SiteTree::class, 'content');
|
||||||
$origContent = $obj->Content;
|
$origContent = $obj->Content;
|
||||||
|
|
||||||
$obj->Content = $origContent . '<a href="#no-anchor-here">this links to a non-existent in-page anchor or skiplink</a>';
|
$obj->Content = $origContent . '<a href="#no-anchor-here">this links to a non-existent in-page anchor or ' .
|
||||||
|
'skiplink</a>';
|
||||||
$obj->syncLinkTracking();
|
$obj->syncLinkTracking();
|
||||||
$this->assertTrue($obj->HasBrokenLink, 'Page has a broken anchor/skiplink');
|
$this->assertTrue($obj->HasBrokenLink, 'Page has a broken anchor/skiplink');
|
||||||
|
|
||||||
$obj->Content = $origContent . '<a href="#yes-anchor-here">this links to an existent in-page anchor/skiplink</a>';
|
$obj->Content = $origContent . '<a href="#yes-anchor-here">this links to an existent in-page ' .
|
||||||
|
'anchor/skiplink</a>';
|
||||||
$obj->syncLinkTracking();
|
$obj->syncLinkTracking();
|
||||||
$this->assertFalse($obj->HasBrokenLink, 'Page doesn\'t have a broken anchor or skiplink');
|
$this->assertFalse($obj->HasBrokenLink, 'Page doesn\'t have a broken anchor or skiplink');
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
Page:
|
SilverStripe\CMS\Model\SiteTree:
|
||||||
content:
|
content:
|
||||||
Title: ContentPage
|
Title: ContentPage
|
||||||
Content: 'This is some partially happy content. It has one missing a skiplink, but does have another <a name="yes-anchor-here">skiplink here</a>.'
|
Content: 'This is some partially happy content. It has one missing a skiplink, but does have another <a name="yes-anchor-here">skiplink here</a>.'
|
||||||
@ -13,7 +13,7 @@ Page:
|
|||||||
workingInternalRedirector:
|
workingInternalRedirector:
|
||||||
RedirectionType: Internal
|
RedirectionType: Internal
|
||||||
Title: RedirectorPageToBrokenInteralPage
|
Title: RedirectorPageToBrokenInteralPage
|
||||||
LinkTo: =>Page.content
|
LinkTo: =>SilverStripe\CMS\Model\SiteTree.content
|
||||||
SilverStripe\CMS\Tests\Model\SiteTreeBrokenLinksTest\NotPageObject:
|
SilverStripe\CMS\Tests\Model\SiteTreeBrokenLinksTest\NotPageObject:
|
||||||
object1:
|
object1:
|
||||||
Content: 'Everything will be ok'
|
Content: 'Everything will be ok'
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace SilverStripe\CMS\Tests\Model;
|
namespace SilverStripe\CMS\Tests\Model;
|
||||||
|
|
||||||
use Page;
|
|
||||||
use SilverStripe\Assets\Dev\TestAssetStore;
|
use SilverStripe\Assets\Dev\TestAssetStore;
|
||||||
use SilverStripe\Assets\File;
|
use SilverStripe\Assets\File;
|
||||||
use SilverStripe\Assets\Filesystem;
|
use SilverStripe\Assets\Filesystem;
|
||||||
@ -31,8 +30,8 @@ class SiteTreeHTMLEditorFieldTest extends FunctionalTest
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Ensure all pages are published
|
// Ensure all pages are published
|
||||||
/** @var Page $page */
|
/** @var SiteTree $page */
|
||||||
foreach (Page::get() as $page) {
|
foreach (SiteTree::get() as $page) {
|
||||||
$page->publishSingle();
|
$page->publishSingle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -55,7 +54,11 @@ class SiteTreeHTMLEditorFieldTest extends FunctionalTest
|
|||||||
$editor->setValue("<a href=\"[sitetree_link,id=$aboutID]\">Example Link</a>");
|
$editor->setValue("<a href=\"[sitetree_link,id=$aboutID]\">Example Link</a>");
|
||||||
$editor->saveInto($sitetree);
|
$editor->saveInto($sitetree);
|
||||||
$sitetree->write();
|
$sitetree->write();
|
||||||
$this->assertEquals([$aboutID => $aboutID], $sitetree->LinkTracking()->getIdList(), 'Basic link tracking works.');
|
$this->assertEquals(
|
||||||
|
[$aboutID => $aboutID],
|
||||||
|
$sitetree->LinkTracking()->getIdList(),
|
||||||
|
'Basic link tracking works.'
|
||||||
|
);
|
||||||
|
|
||||||
$editor->setValue(
|
$editor->setValue(
|
||||||
"<a href=\"[sitetree_link,id=$aboutID]\"></a><a href=\"[sitetree_link,id=$contactID]\"></a>"
|
"<a href=\"[sitetree_link,id=$aboutID]\"></a><a href=\"[sitetree_link,id=$contactID]\"></a>"
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace SilverStripe\CMS\Tests\Model;
|
namespace SilverStripe\CMS\Tests\Model;
|
||||||
|
|
||||||
use Page;
|
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
use SilverStripe\CMS\Model\SiteTreeLinkTracking_Parser;
|
use SilverStripe\CMS\Model\SiteTreeLinkTracking_Parser;
|
||||||
use SilverStripe\Control\Director;
|
use SilverStripe\Control\Director;
|
||||||
@ -32,7 +31,7 @@ class SiteTreeLinkTrackingTest extends SapphireTest
|
|||||||
|
|
||||||
public function testParser()
|
public function testParser()
|
||||||
{
|
{
|
||||||
SiteTree::add_extension(Page::class, SiteTreeLinkTracking_Extension::class);
|
SiteTree::add_extension(SiteTree::class, SiteTreeLinkTracking_Extension::class);
|
||||||
|
|
||||||
// Shortcodes
|
// Shortcodes
|
||||||
$this->assertTrue($this->isBroken('<a href="[sitetree_link,id=123]">link</a>'));
|
$this->assertTrue($this->isBroken('<a href="[sitetree_link,id=123]">link</a>'));
|
||||||
@ -55,8 +54,7 @@ class SiteTreeLinkTrackingTest extends SapphireTest
|
|||||||
$this->assertFalse($this->isBroken('<a id="anchor">anchor</a>'));
|
$this->assertFalse($this->isBroken('<a id="anchor">anchor</a>'));
|
||||||
$this->assertTrue($this->isBroken('<a href="##anchor">anchor</a>'));
|
$this->assertTrue($this->isBroken('<a href="##anchor">anchor</a>'));
|
||||||
|
|
||||||
|
$page = new SiteTree();
|
||||||
$page = new Page();
|
|
||||||
$page->Content = '<a name="yes-name-anchor">name</a><a id="yes-id-anchor">id</a>';
|
$page->Content = '<a name="yes-name-anchor">name</a><a id="yes-id-anchor">id</a>';
|
||||||
$page->write();
|
$page->write();
|
||||||
|
|
||||||
@ -72,7 +70,7 @@ class SiteTreeLinkTrackingTest extends SapphireTest
|
|||||||
|
|
||||||
protected function highlight($content)
|
protected function highlight($content)
|
||||||
{
|
{
|
||||||
$page = new Page();
|
$page = new SiteTree();
|
||||||
$page->Content = $content;
|
$page->Content = $content;
|
||||||
$page->write();
|
$page->write();
|
||||||
return $page->Content;
|
return $page->Content;
|
||||||
@ -87,7 +85,7 @@ class SiteTreeLinkTrackingTest extends SapphireTest
|
|||||||
$content = $this->highlight('<a href="[sitetree_link,id=123]">link</a>');
|
$content = $this->highlight('<a href="[sitetree_link,id=123]">link</a>');
|
||||||
$this->assertEquals(substr_count($content ?? '', 'ss-broken'), 1, 'ss-broken class is added to the broken link.');
|
$this->assertEquals(substr_count($content ?? '', 'ss-broken'), 1, 'ss-broken class is added to the broken link.');
|
||||||
|
|
||||||
$otherPage = new Page();
|
$otherPage = new SiteTree();
|
||||||
$otherPage->Content = '';
|
$otherPage->Content = '';
|
||||||
$otherPage->write();
|
$otherPage->write();
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace SilverStripe\CMS\Tests\Model;
|
namespace SilverStripe\CMS\Tests\Model;
|
||||||
|
|
||||||
use Page;
|
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
use SilverStripe\Control\HTTPResponse_Exception;
|
use SilverStripe\Control\HTTPResponse_Exception;
|
||||||
use SilverStripe\Dev\FunctionalTest;
|
use SilverStripe\Dev\FunctionalTest;
|
||||||
@ -13,10 +12,6 @@ use SilverStripe\SiteConfig\SiteConfig;
|
|||||||
use SilverStripe\Subsites\Extensions\SiteTreeSubsites;
|
use SilverStripe\Subsites\Extensions\SiteTreeSubsites;
|
||||||
use SilverStripe\Versioned\Versioned;
|
use SilverStripe\Versioned\Versioned;
|
||||||
|
|
||||||
/**
|
|
||||||
* @todo Test canAddChildren()
|
|
||||||
* @todo Test canCreate()
|
|
||||||
*/
|
|
||||||
class SiteTreePermissionsTest extends FunctionalTest
|
class SiteTreePermissionsTest extends FunctionalTest
|
||||||
{
|
{
|
||||||
protected static $fixture_file = "SiteTreePermissionsTest.yml";
|
protected static $fixture_file = "SiteTreePermissionsTest.yml";
|
||||||
@ -33,8 +28,8 @@ class SiteTreePermissionsTest extends FunctionalTest
|
|||||||
$this->autoFollowRedirection = false;
|
$this->autoFollowRedirection = false;
|
||||||
|
|
||||||
// Ensure all pages are published
|
// Ensure all pages are published
|
||||||
/** @var Page $page */
|
/** @var SiteTree $page */
|
||||||
foreach (Page::get() as $page) {
|
foreach (SiteTree::get() as $page) {
|
||||||
if ($page->URLSegment !== 'draft-only') {
|
if ($page->URLSegment !== 'draft-only') {
|
||||||
$page->publishSingle();
|
$page->publishSingle();
|
||||||
}
|
}
|
||||||
@ -46,8 +41,8 @@ class SiteTreePermissionsTest extends FunctionalTest
|
|||||||
{
|
{
|
||||||
$this->autoFollowRedirection = false;
|
$this->autoFollowRedirection = false;
|
||||||
|
|
||||||
/** @var Page $draftOnlyPage */
|
/** @var SiteTree $draftOnlyPage */
|
||||||
$draftOnlyPage = $this->objFromFixture(Page::class, 'draftOnlyPage');
|
$draftOnlyPage = $this->objFromFixture(SiteTree::class, 'draftOnlyPage');
|
||||||
$this->logOut();
|
$this->logOut();
|
||||||
|
|
||||||
$response = $this->get($draftOnlyPage->URLSegment . '?stage=Live');
|
$response = $this->get($draftOnlyPage->URLSegment . '?stage=Live');
|
||||||
@ -85,7 +80,7 @@ class SiteTreePermissionsTest extends FunctionalTest
|
|||||||
{
|
{
|
||||||
// Set up fixture - a published page deleted from draft
|
// Set up fixture - a published page deleted from draft
|
||||||
$this->logInWithPermission("ADMIN");
|
$this->logInWithPermission("ADMIN");
|
||||||
$page = $this->objFromFixture(Page::class, 'restrictedEditOnlySubadminGroup');
|
$page = $this->objFromFixture(SiteTree::class, 'restrictedEditOnlySubadminGroup');
|
||||||
$pageID = $page->ID;
|
$pageID = $page->ID;
|
||||||
$this->assertTrue($page->publishRecursive());
|
$this->assertTrue($page->publishRecursive());
|
||||||
$page->delete();
|
$page->delete();
|
||||||
@ -112,7 +107,7 @@ class SiteTreePermissionsTest extends FunctionalTest
|
|||||||
{
|
{
|
||||||
// Set up fixture - an unpublished page
|
// Set up fixture - an unpublished page
|
||||||
$this->logInWithPermission("ADMIN");
|
$this->logInWithPermission("ADMIN");
|
||||||
$page = $this->objFromFixture(Page::class, 'restrictedEditOnlySubadminGroup');
|
$page = $this->objFromFixture(SiteTree::class, 'restrictedEditOnlySubadminGroup');
|
||||||
$pageID = $page->ID;
|
$pageID = $page->ID;
|
||||||
$page->doUnpublish();
|
$page->doUnpublish();
|
||||||
|
|
||||||
@ -135,7 +130,7 @@ class SiteTreePermissionsTest extends FunctionalTest
|
|||||||
{
|
{
|
||||||
// Find a page that exists and delete it from both stage and published
|
// Find a page that exists and delete it from both stage and published
|
||||||
$this->logInWithPermission("ADMIN");
|
$this->logInWithPermission("ADMIN");
|
||||||
$page = $this->objFromFixture(Page::class, 'restrictedEditOnlySubadminGroup');
|
$page = $this->objFromFixture(SiteTree::class, 'restrictedEditOnlySubadminGroup');
|
||||||
$pageID = $page->ID;
|
$pageID = $page->ID;
|
||||||
$page->doUnpublish();
|
$page->doUnpublish();
|
||||||
$page->delete();
|
$page->delete();
|
||||||
@ -153,8 +148,8 @@ class SiteTreePermissionsTest extends FunctionalTest
|
|||||||
public function testCanViewStage()
|
public function testCanViewStage()
|
||||||
{
|
{
|
||||||
// Get page & make sure it exists on Live
|
// Get page & make sure it exists on Live
|
||||||
/** @var Page $page */
|
/** @var SiteTree $page */
|
||||||
$page = $this->objFromFixture(Page::class, 'standardpage');
|
$page = $this->objFromFixture(SiteTree::class, 'standardpage');
|
||||||
$page->publishSingle();
|
$page->publishSingle();
|
||||||
|
|
||||||
// Then make sure there's a new version on Stage
|
// Then make sure there's a new version on Stage
|
||||||
@ -173,7 +168,7 @@ class SiteTreePermissionsTest extends FunctionalTest
|
|||||||
|
|
||||||
public function testAccessTabOnlyDisplaysWithGrantAccessPermissions()
|
public function testAccessTabOnlyDisplaysWithGrantAccessPermissions()
|
||||||
{
|
{
|
||||||
$page = $this->objFromFixture(Page::class, 'standardpage');
|
$page = $this->objFromFixture(SiteTree::class, 'standardpage');
|
||||||
|
|
||||||
$subadminuser = $this->objFromFixture(Member::class, 'subadmin');
|
$subadminuser = $this->objFromFixture(Member::class, 'subadmin');
|
||||||
Security::setCurrentUser($subadminuser);
|
Security::setCurrentUser($subadminuser);
|
||||||
@ -204,7 +199,7 @@ class SiteTreePermissionsTest extends FunctionalTest
|
|||||||
|
|
||||||
public function testRestrictedViewLoggedInUsers()
|
public function testRestrictedViewLoggedInUsers()
|
||||||
{
|
{
|
||||||
$page = $this->objFromFixture(Page::class, 'restrictedViewLoggedInUsers');
|
$page = $this->objFromFixture(SiteTree::class, 'restrictedViewLoggedInUsers');
|
||||||
|
|
||||||
// unauthenticated users
|
// unauthenticated users
|
||||||
$this->assertFalse(
|
$this->assertFalse(
|
||||||
@ -223,21 +218,23 @@ class SiteTreePermissionsTest extends FunctionalTest
|
|||||||
$websiteuser = $this->objFromFixture(Member::class, 'websiteuser');
|
$websiteuser = $this->objFromFixture(Member::class, 'websiteuser');
|
||||||
$this->assertTrue(
|
$this->assertTrue(
|
||||||
$page->canView($websiteuser),
|
$page->canView($websiteuser),
|
||||||
'Authenticated members can view a page marked as "Viewable for any logged in users" even if they dont have access to the CMS'
|
'Authenticated members can view a page marked as "Viewable for any logged in users" even if they dont ' .
|
||||||
|
'have access to the CMS'
|
||||||
);
|
);
|
||||||
$this->logInAs($websiteuser);
|
$this->logInAs($websiteuser);
|
||||||
$response = $this->get($page->RelativeLink());
|
$response = $this->get($page->RelativeLink());
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
$response->getStatusCode(),
|
$response->getStatusCode(),
|
||||||
200,
|
200,
|
||||||
'Authenticated members can view a page marked as "Viewable for any logged in users" even if they dont have access to the CMS'
|
'Authenticated members can view a page marked as "Viewable for any logged in users" even if they dont ' .
|
||||||
|
'have access to the CMS'
|
||||||
);
|
);
|
||||||
$this->logOut();
|
$this->logOut();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testRestrictedViewOnlyTheseUsers()
|
public function testRestrictedViewOnlyTheseUsers()
|
||||||
{
|
{
|
||||||
$page = $this->objFromFixture(Page::class, 'restrictedViewOnlyWebsiteUsers');
|
$page = $this->objFromFixture(SiteTree::class, 'restrictedViewOnlyWebsiteUsers');
|
||||||
|
|
||||||
// unauthenticcated users
|
// unauthenticcated users
|
||||||
$this->assertFalse(
|
$this->assertFalse(
|
||||||
@ -256,14 +253,16 @@ class SiteTreePermissionsTest extends FunctionalTest
|
|||||||
$subadminuser = $this->objFromFixture(Member::class, 'subadmin');
|
$subadminuser = $this->objFromFixture(Member::class, 'subadmin');
|
||||||
$this->assertFalse(
|
$this->assertFalse(
|
||||||
$page->canView($subadminuser),
|
$page->canView($subadminuser),
|
||||||
'Authenticated members cant view a page marked as "Viewable by these groups" if theyre not in the listed groups'
|
'Authenticated members cant view a page marked as "Viewable by these groups" if theyre not in the listed ' .
|
||||||
|
'groups'
|
||||||
);
|
);
|
||||||
$this->LogInAs($subadminuser);
|
$this->LogInAs($subadminuser);
|
||||||
$response = $this->get($page->RelativeLink());
|
$response = $this->get($page->RelativeLink());
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
$response->getStatusCode(),
|
$response->getStatusCode(),
|
||||||
403,
|
403,
|
||||||
'Authenticated members cant view a page marked as "Viewable by these groups" if theyre not in the listed groups'
|
'Authenticated members cant view a page marked as "Viewable by these groups" if theyre not in the listed ' .
|
||||||
|
'groups'
|
||||||
);
|
);
|
||||||
$this->logOut();
|
$this->logOut();
|
||||||
|
|
||||||
@ -285,7 +284,7 @@ class SiteTreePermissionsTest extends FunctionalTest
|
|||||||
|
|
||||||
public function testRestrictedEditLoggedInUsers()
|
public function testRestrictedEditLoggedInUsers()
|
||||||
{
|
{
|
||||||
$page = $this->objFromFixture(Page::class, 'restrictedEditLoggedInUsers');
|
$page = $this->objFromFixture(SiteTree::class, 'restrictedEditLoggedInUsers');
|
||||||
|
|
||||||
// unauthenticcated users
|
// unauthenticcated users
|
||||||
$this->assertFalse(
|
$this->assertFalse(
|
||||||
@ -298,20 +297,22 @@ class SiteTreePermissionsTest extends FunctionalTest
|
|||||||
Security::setCurrentUser($websiteuser);
|
Security::setCurrentUser($websiteuser);
|
||||||
$this->assertFalse(
|
$this->assertFalse(
|
||||||
$page->canEdit($websiteuser),
|
$page->canEdit($websiteuser),
|
||||||
'Authenticated members cant edit a page marked as "Editable by logged in users" if they dont have cms permissions'
|
'Authenticated members cant edit a page marked as "Editable by logged in users" if they dont have cms ' .
|
||||||
|
'permissions'
|
||||||
);
|
);
|
||||||
|
|
||||||
// subadmin users
|
// subadmin users
|
||||||
$subadminuser = $this->objFromFixture(Member::class, 'subadmin');
|
$subadminuser = $this->objFromFixture(Member::class, 'subadmin');
|
||||||
$this->assertTrue(
|
$this->assertTrue(
|
||||||
$page->canEdit($subadminuser),
|
$page->canEdit($subadminuser),
|
||||||
'Authenticated members can edit a page marked as "Editable by logged in users" if they have cms permissions and belong to any of these groups'
|
'Authenticated members can edit a page marked as "Editable by logged in users" if they have cms ' .
|
||||||
|
'permissions and belong to any of these groups'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testRestrictedEditOnlySubadminGroup()
|
public function testRestrictedEditOnlySubadminGroup()
|
||||||
{
|
{
|
||||||
$page = $this->objFromFixture(Page::class, 'restrictedEditOnlySubadminGroup');
|
$page = $this->objFromFixture(SiteTree::class, 'restrictedEditOnlySubadminGroup');
|
||||||
|
|
||||||
// unauthenticated users
|
// unauthenticated users
|
||||||
$this->assertFalse(
|
$this->assertFalse(
|
||||||
@ -330,14 +331,15 @@ class SiteTreePermissionsTest extends FunctionalTest
|
|||||||
$websiteuser = $this->objFromFixture(Member::class, 'websiteuser');
|
$websiteuser = $this->objFromFixture(Member::class, 'websiteuser');
|
||||||
$this->assertFalse(
|
$this->assertFalse(
|
||||||
$page->canEdit($websiteuser),
|
$page->canEdit($websiteuser),
|
||||||
'Authenticated members cant edit a page marked as "Editable by these groups" if theyre not in the listed groups'
|
'Authenticated members cant edit a page marked as "Editable by these groups" if theyre not in the listed ' .
|
||||||
|
'groups'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testRestrictedViewInheritance()
|
public function testRestrictedViewInheritance()
|
||||||
{
|
{
|
||||||
$parentPage = $this->objFromFixture(Page::class, 'parent_restrictedViewOnlySubadminGroup');
|
$parentPage = $this->objFromFixture(SiteTree::class, 'parent_restrictedViewOnlySubadminGroup');
|
||||||
$childPage = $this->objFromFixture(Page::class, 'child_restrictedViewOnlySubadminGroup');
|
$childPage = $this->objFromFixture(SiteTree::class, 'child_restrictedViewOnlySubadminGroup');
|
||||||
|
|
||||||
// unauthenticated users
|
// unauthenticated users
|
||||||
$this->assertFalse(
|
$this->assertFalse(
|
||||||
@ -356,22 +358,24 @@ class SiteTreePermissionsTest extends FunctionalTest
|
|||||||
$subadminuser = $this->objFromFixture(Member::class, 'subadmin');
|
$subadminuser = $this->objFromFixture(Member::class, 'subadmin');
|
||||||
$this->assertTrue(
|
$this->assertTrue(
|
||||||
$childPage->canView($subadminuser),
|
$childPage->canView($subadminuser),
|
||||||
'Authenticated members can view a page marked as "Viewable by these groups" if theyre in the listed groups by inherited permission'
|
'Authenticated members can view a page marked as "Viewable by these groups" if theyre in the listed ' .
|
||||||
|
'groups by inherited permission'
|
||||||
);
|
);
|
||||||
$this->logInAs($subadminuser);
|
$this->logInAs($subadminuser);
|
||||||
$response = $this->get($childPage->RelativeLink());
|
$response = $this->get($childPage->RelativeLink());
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
$response->getStatusCode(),
|
$response->getStatusCode(),
|
||||||
200,
|
200,
|
||||||
'Authenticated members can view a page marked as "Viewable by these groups" if theyre in the listed groups by inherited permission'
|
'Authenticated members can view a page marked as "Viewable by these groups" if theyre in the listed ' .
|
||||||
|
'groups by inherited permission'
|
||||||
);
|
);
|
||||||
$this->logOut();
|
$this->logOut();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testRestrictedEditInheritance()
|
public function testRestrictedEditInheritance()
|
||||||
{
|
{
|
||||||
$parentPage = $this->objFromFixture(Page::class, 'parent_restrictedEditOnlySubadminGroup');
|
$parentPage = $this->objFromFixture(SiteTree::class, 'parent_restrictedEditOnlySubadminGroup');
|
||||||
$childPage = $this->objFromFixture(Page::class, 'child_restrictedEditOnlySubadminGroup');
|
$childPage = $this->objFromFixture(SiteTree::class, 'child_restrictedEditOnlySubadminGroup');
|
||||||
|
|
||||||
// unauthenticated users
|
// unauthenticated users
|
||||||
$this->assertFalse(
|
$this->assertFalse(
|
||||||
@ -383,14 +387,15 @@ class SiteTreePermissionsTest extends FunctionalTest
|
|||||||
$subadminuser = $this->objFromFixture(Member::class, 'subadmin');
|
$subadminuser = $this->objFromFixture(Member::class, 'subadmin');
|
||||||
$this->assertTrue(
|
$this->assertTrue(
|
||||||
$childPage->canEdit($subadminuser),
|
$childPage->canEdit($subadminuser),
|
||||||
'Authenticated members can edit a page marked as "Editable by these groups" if theyre in the listed groups by inherited permission'
|
'Authenticated members can edit a page marked as "Editable by these groups" if theyre in the listed ' .
|
||||||
|
'groups by inherited permission'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testDeleteRestrictedChild()
|
public function testDeleteRestrictedChild()
|
||||||
{
|
{
|
||||||
$parentPage = $this->objFromFixture(Page::class, 'deleteTestParentPage');
|
$parentPage = $this->objFromFixture(SiteTree::class, 'deleteTestParentPage');
|
||||||
$childPage = $this->objFromFixture(Page::class, 'deleteTestChildPage');
|
$childPage = $this->objFromFixture(SiteTree::class, 'deleteTestChildPage');
|
||||||
|
|
||||||
// unauthenticated users
|
// unauthenticated users
|
||||||
$this->assertFalse(
|
$this->assertFalse(
|
||||||
@ -405,7 +410,7 @@ class SiteTreePermissionsTest extends FunctionalTest
|
|||||||
|
|
||||||
public function testRestrictedEditLoggedInUsersDeletedFromStage()
|
public function testRestrictedEditLoggedInUsersDeletedFromStage()
|
||||||
{
|
{
|
||||||
$page = $this->objFromFixture(Page::class, 'restrictedEditLoggedInUsers');
|
$page = $this->objFromFixture(SiteTree::class, 'restrictedEditLoggedInUsers');
|
||||||
$pageID = $page->ID;
|
$pageID = $page->ID;
|
||||||
|
|
||||||
$this->logInWithPermission("ADMIN");
|
$this->logInWithPermission("ADMIN");
|
||||||
@ -421,39 +426,60 @@ class SiteTreePermissionsTest extends FunctionalTest
|
|||||||
$subadminuser = $this->objFromFixture(Member::class, 'subadmin');
|
$subadminuser = $this->objFromFixture(Member::class, 'subadmin');
|
||||||
$this->assertTrue(
|
$this->assertTrue(
|
||||||
$page->canEdit($subadminuser),
|
$page->canEdit($subadminuser),
|
||||||
'Authenticated members can edit a page that was deleted from stage and marked as "Editable by logged in users" if they have cms permissions and belong to any of these groups'
|
'Authenticated members can edit a page that was deleted from stage and marked as "Editable by logged ' .
|
||||||
|
'in users" if they have cms permissions and belong to any of these groups'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testInheritCanViewFromSiteConfig()
|
public function testInheritCanViewFromSiteConfig()
|
||||||
{
|
{
|
||||||
$page = $this->objFromFixture(Page::class, 'inheritWithNoParent');
|
$page = $this->objFromFixture(SiteTree::class, 'inheritWithNoParent');
|
||||||
$siteconfig = $this->objFromFixture(SiteConfig::class, 'default');
|
$siteconfig = $this->objFromFixture(SiteConfig::class, 'default');
|
||||||
$editor = $this->objFromFixture(Member::class, 'editor');
|
$editor = $this->objFromFixture(Member::class, 'editor');
|
||||||
$editorGroup = $this->objFromFixture(Group::class, 'editorgroup');
|
$editorGroup = $this->objFromFixture(Group::class, 'editorgroup');
|
||||||
|
|
||||||
$siteconfig->CanViewType = 'Anyone';
|
$siteconfig->CanViewType = 'Anyone';
|
||||||
$siteconfig->write();
|
$siteconfig->write();
|
||||||
$this->assertTrue($page->canView(false), 'Anyone can view a page when set to inherit from the SiteConfig, and SiteConfig has canView set to LoggedInUsers');
|
$this->assertTrue(
|
||||||
|
$page->canView(false),
|
||||||
|
'Anyone can view a page when set to inherit from the SiteConfig, and SiteConfig has canView set to ' .
|
||||||
|
'LoggedInUsers'
|
||||||
|
);
|
||||||
|
|
||||||
$siteconfig->CanViewType = 'LoggedInUsers';
|
$siteconfig->CanViewType = 'LoggedInUsers';
|
||||||
$siteconfig->write();
|
$siteconfig->write();
|
||||||
$this->assertFalse($page->canView(false), 'Anonymous can\'t view a page when set to inherit from the SiteConfig, and SiteConfig has canView set to LoggedInUsers');
|
$this->assertFalse(
|
||||||
|
$page->canView(false),
|
||||||
|
'Anonymous can\'t view a page when set to inherit from the SiteConfig, and SiteConfig has canView set to ' .
|
||||||
|
'LoggedInUsers'
|
||||||
|
);
|
||||||
|
|
||||||
$siteconfig->CanViewType = 'LoggedInUsers';
|
$siteconfig->CanViewType = 'LoggedInUsers';
|
||||||
$siteconfig->write();
|
$siteconfig->write();
|
||||||
$this->assertTrue($page->canView($editor), 'Users can view a page when set to inherit from the SiteConfig, and SiteConfig has canView set to LoggedInUsers');
|
$this->assertTrue(
|
||||||
|
$page->canView($editor),
|
||||||
|
'Users can view a page when set to inherit from the SiteConfig, and SiteConfig has canView set to ' .
|
||||||
|
'LoggedInUsers'
|
||||||
|
);
|
||||||
|
|
||||||
$siteconfig->CanViewType = 'OnlyTheseUsers';
|
$siteconfig->CanViewType = 'OnlyTheseUsers';
|
||||||
$siteconfig->ViewerGroups()->add($editorGroup);
|
$siteconfig->ViewerGroups()->add($editorGroup);
|
||||||
$siteconfig->write();
|
$siteconfig->write();
|
||||||
$this->assertTrue($page->canView($editor), 'Editors can view a page when set to inherit from the SiteConfig, and SiteConfig has canView set to OnlyTheseUsers');
|
$this->assertTrue(
|
||||||
$this->assertFalse($page->canView(false), 'Anonymous can\'t view a page when set to inherit from the SiteConfig, and SiteConfig has canView set to OnlyTheseUsers');
|
$page->canView($editor),
|
||||||
|
'Editors can view a page when set to inherit from the SiteConfig, and SiteConfig has canView set to ' .
|
||||||
|
'OnlyTheseUsers'
|
||||||
|
);
|
||||||
|
$this->assertFalse(
|
||||||
|
$page->canView(false),
|
||||||
|
'Anonymous can\'t view a page when set to inherit from the SiteConfig, and SiteConfig has canView set ' .
|
||||||
|
'to OnlyTheseUsers'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testInheritCanEditFromSiteConfig()
|
public function testInheritCanEditFromSiteConfig()
|
||||||
{
|
{
|
||||||
$page = $this->objFromFixture(Page::class, 'inheritWithNoParent');
|
$page = $this->objFromFixture(SiteTree::class, 'inheritWithNoParent');
|
||||||
$siteconfig = $this->objFromFixture(SiteConfig::class, 'default');
|
$siteconfig = $this->objFromFixture(SiteConfig::class, 'default');
|
||||||
$editor = $this->objFromFixture(Member::class, 'editor');
|
$editor = $this->objFromFixture(Member::class, 'editor');
|
||||||
$user = $this->objFromFixture(Member::class, 'websiteuser');
|
$user = $this->objFromFixture(Member::class, 'websiteuser');
|
||||||
@ -462,17 +488,99 @@ class SiteTreePermissionsTest extends FunctionalTest
|
|||||||
$siteconfig->CanEditType = 'LoggedInUsers';
|
$siteconfig->CanEditType = 'LoggedInUsers';
|
||||||
$siteconfig->write();
|
$siteconfig->write();
|
||||||
|
|
||||||
$this->assertFalse($page->canEdit(false), 'Anonymous can\'t edit a page when set to inherit from the SiteConfig, and SiteConfig has canEdit set to LoggedInUsers');
|
$this->assertFalse(
|
||||||
|
$page->canEdit(false),
|
||||||
|
'Anonymous can\'t edit a page when set to inherit from the SiteConfig, and SiteConfig has canEdit set ' .
|
||||||
|
'to LoggedInUsers'
|
||||||
|
);
|
||||||
Security::setCurrentUser($editor);
|
Security::setCurrentUser($editor);
|
||||||
$this->assertTrue($page->canEdit(), 'Users can edit a page when set to inherit from the SiteConfig, and SiteConfig has canEdit set to LoggedInUsers');
|
$this->assertTrue(
|
||||||
|
$page->canEdit(),
|
||||||
|
'Users can edit a page when set to inherit from the SiteConfig, and SiteConfig has canEdit set to ' .
|
||||||
|
'LoggedInUsers'
|
||||||
|
);
|
||||||
|
|
||||||
$siteconfig->CanEditType = 'OnlyTheseUsers';
|
$siteconfig->CanEditType = 'OnlyTheseUsers';
|
||||||
$siteconfig->EditorGroups()->add($editorGroup);
|
$siteconfig->EditorGroups()->add($editorGroup);
|
||||||
$siteconfig->write();
|
$siteconfig->write();
|
||||||
$this->assertTrue($page->canEdit($editor), 'Editors can edit a page when set to inherit from the SiteConfig, and SiteConfig has canEdit set to OnlyTheseUsers');
|
$this->assertTrue(
|
||||||
|
$page->canEdit($editor),
|
||||||
|
'Editors can edit a page when set to inherit from the SiteConfig, and SiteConfig has canEdit set to ' .
|
||||||
|
'OnlyTheseUsers'
|
||||||
|
);
|
||||||
Security::setCurrentUser(null);
|
Security::setCurrentUser(null);
|
||||||
$this->assertFalse($page->canEdit(false), 'Anonymous can\'t edit a page when set to inherit from the SiteConfig, and SiteConfig has canEdit set to OnlyTheseUsers');
|
$this->assertFalse(
|
||||||
|
$page->canEdit(false),
|
||||||
|
'Anonymous can\'t edit a page when set to inherit from the SiteConfig, and SiteConfig has canEdit set ' .
|
||||||
|
'to OnlyTheseUsers'
|
||||||
|
);
|
||||||
Security::setCurrentUser($user);
|
Security::setCurrentUser($user);
|
||||||
$this->assertFalse($page->canEdit($user), 'Website user can\'t edit a page when set to inherit from the SiteConfig, and SiteConfig has canEdit set to OnlyTheseUsers');
|
$this->assertFalse(
|
||||||
|
$page->canEdit($user),
|
||||||
|
'Website user can\'t edit a page when set to inherit from the SiteConfig, and SiteConfig has canEdit set ' .
|
||||||
|
'to OnlyTheseUsers'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test permissions on duplicate page
|
||||||
|
* @dataProvider groupWithPermissions
|
||||||
|
*/
|
||||||
|
public function testDuplicatePageWithGroupPermissions(string $userName, string $method, bool $expected)
|
||||||
|
{
|
||||||
|
$originalPage = $this->objFromFixture(SiteTree::class, 'originalpage');
|
||||||
|
$user = $this->objFromFixture(Member::class, $userName);
|
||||||
|
$dupe = $originalPage->duplicate();
|
||||||
|
|
||||||
|
$this->assertEquals($originalPage->Title, $dupe->Title);
|
||||||
|
$this->assertEquals($dupe->CanViewType, 'OnlyTheseUsers');
|
||||||
|
$this->assertEquals($dupe->CanEditType, 'OnlyTheseUsers');
|
||||||
|
$this->assertSame($dupe->{$method}($user), $expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function groupWithPermissions(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'Subadmin can view page duplicate.' => [
|
||||||
|
'subadmin',
|
||||||
|
'canView',
|
||||||
|
true,
|
||||||
|
],
|
||||||
|
'Subadmin can edit page duplicate.' => [
|
||||||
|
'subadmin',
|
||||||
|
'canEdit',
|
||||||
|
true,
|
||||||
|
],
|
||||||
|
'Editor can view page duplicate.' => [
|
||||||
|
'editor',
|
||||||
|
'canView',
|
||||||
|
true,
|
||||||
|
],
|
||||||
|
'Editor can edit page duplicate.' => [
|
||||||
|
'editor',
|
||||||
|
'canEdit',
|
||||||
|
true,
|
||||||
|
],
|
||||||
|
'User with "allsections" permission can view page duplicate.' => [
|
||||||
|
'allsections',
|
||||||
|
'canView',
|
||||||
|
true,
|
||||||
|
],
|
||||||
|
'User with "allsections" permission cannot edit page duplicate.' => [
|
||||||
|
'allsections',
|
||||||
|
'canEdit',
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
'Websiteuser permission cannot view page duplicate.' => [
|
||||||
|
'websiteuser',
|
||||||
|
'canView',
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
'Websiteuser permission cannot edit page duplicate.' => [
|
||||||
|
'websiteuser',
|
||||||
|
'canEdit',
|
||||||
|
false,
|
||||||
|
],
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,8 @@ SilverStripe\Security\Permission:
|
|||||||
Code: CMS_ACCESS_CMSMain
|
Code: CMS_ACCESS_CMSMain
|
||||||
grantaccess:
|
grantaccess:
|
||||||
Code: SITETREE_GRANT_ACCESS
|
Code: SITETREE_GRANT_ACCESS
|
||||||
|
allsections:
|
||||||
|
Code: CMS_ACCESS_LeftAndMain
|
||||||
SilverStripe\Security\Group:
|
SilverStripe\Security\Group:
|
||||||
subadmingroup:
|
subadmingroup:
|
||||||
Title: Create, edit and delete pages
|
Title: Create, edit and delete pages
|
||||||
@ -20,6 +22,9 @@ SilverStripe\Security\Group:
|
|||||||
Title: Edit existing pages
|
Title: Edit existing pages
|
||||||
Code: editorgroup
|
Code: editorgroup
|
||||||
Permissions: =>SilverStripe\Security\Permission.cmsmain2
|
Permissions: =>SilverStripe\Security\Permission.cmsmain2
|
||||||
|
allsectionsgroup:
|
||||||
|
Title: All Section Editors
|
||||||
|
Permissions: =>SilverStripe\Security\Permission.allsections
|
||||||
websiteusers:
|
websiteusers:
|
||||||
Title: View certain restricted pages
|
Title: View certain restricted pages
|
||||||
SilverStripe\Security\Member:
|
SilverStripe\Security\Member:
|
||||||
@ -31,11 +36,15 @@ SilverStripe\Security\Member:
|
|||||||
Email: editor@test.com
|
Email: editor@test.com
|
||||||
Password: test
|
Password: test
|
||||||
Groups: =>SilverStripe\Security\Group.editorgroup
|
Groups: =>SilverStripe\Security\Group.editorgroup
|
||||||
|
allsections:
|
||||||
|
Email: allsections@test.com
|
||||||
|
Password: test
|
||||||
|
Groups: =>SilverStripe\Security\Group.allsectionsgroup
|
||||||
websiteuser:
|
websiteuser:
|
||||||
Email: websiteuser@test.com
|
Email: websiteuser@test.com
|
||||||
Password: test
|
Password: test
|
||||||
Groups: =>SilverStripe\Security\Group.websiteusers
|
Groups: =>SilverStripe\Security\Group.websiteusers
|
||||||
Page:
|
SilverStripe\CMS\Model\SiteTree:
|
||||||
standardpage:
|
standardpage:
|
||||||
URLSegment: standardpage
|
URLSegment: standardpage
|
||||||
restrictedViewLoggedInUsers:
|
restrictedViewLoggedInUsers:
|
||||||
@ -66,7 +75,7 @@ Page:
|
|||||||
URLSegment: parent-restrictedViewOnlySubadminGroup
|
URLSegment: parent-restrictedViewOnlySubadminGroup
|
||||||
child_restrictedViewOnlySubadminGroup:
|
child_restrictedViewOnlySubadminGroup:
|
||||||
CanViewType: Inherit
|
CanViewType: Inherit
|
||||||
Parent: =>Page.parent_restrictedViewOnlySubadminGroup
|
Parent: =>SilverStripe\CMS\Model\SiteTree.parent_restrictedViewOnlySubadminGroup
|
||||||
URLSegment: child-restrictedViewOnlySubadminGroup
|
URLSegment: child-restrictedViewOnlySubadminGroup
|
||||||
parent_restrictedEditOnlySubadminGroup:
|
parent_restrictedEditOnlySubadminGroup:
|
||||||
CanEditType: OnlyTheseUsers
|
CanEditType: OnlyTheseUsers
|
||||||
@ -74,7 +83,7 @@ Page:
|
|||||||
URLSegment: parent-restrictedEditOnlySubadminGroup
|
URLSegment: parent-restrictedEditOnlySubadminGroup
|
||||||
child_restrictedEditOnlySubadminGroup:
|
child_restrictedEditOnlySubadminGroup:
|
||||||
CanEditType: Inherit
|
CanEditType: Inherit
|
||||||
Parent: =>Page.parent_restrictedEditOnlySubadminGroup
|
Parent: =>SilverStripe\CMS\Model\SiteTree.parent_restrictedEditOnlySubadminGroup
|
||||||
URLSegment: child-restrictedEditOnlySubadminGroup
|
URLSegment: child-restrictedEditOnlySubadminGroup
|
||||||
deleteTestParentPage:
|
deleteTestParentPage:
|
||||||
CanEditType: Inherit
|
CanEditType: Inherit
|
||||||
@ -86,3 +95,9 @@ Page:
|
|||||||
draftOnlyPage:
|
draftOnlyPage:
|
||||||
CanViewType: Anyone
|
CanViewType: Anyone
|
||||||
URLSegment: draft-only
|
URLSegment: draft-only
|
||||||
|
originalpage:
|
||||||
|
Title: Original Page for duplicate
|
||||||
|
CanEditType: OnlyTheseUsers
|
||||||
|
CanViewType: OnlyTheseUsers
|
||||||
|
EditorGroups: =>SilverStripe\Security\Group.subadmingroup,=>SilverStripe\Security\Group.editorgroup
|
||||||
|
ViewerGroups: =>SilverStripe\Security\Group.subadmingroup,=>SilverStripe\Security\Group.editorgroup,=>SilverStripe\Security\Group.allsectionsgroup
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
namespace SilverStripe\CMS\Tests\Model;
|
namespace SilverStripe\CMS\Tests\Model;
|
||||||
|
|
||||||
use LogicException;
|
use LogicException;
|
||||||
use Page;
|
|
||||||
use Psr\SimpleCache\CacheInterface;
|
use Psr\SimpleCache\CacheInterface;
|
||||||
use ReflectionMethod;
|
use ReflectionMethod;
|
||||||
use SilverStripe\CMS\Model\RedirectorPage;
|
use SilverStripe\CMS\Model\RedirectorPage;
|
||||||
@ -34,16 +33,15 @@ use SilverStripe\Security\Security;
|
|||||||
use SilverStripe\SiteConfig\SiteConfig;
|
use SilverStripe\SiteConfig\SiteConfig;
|
||||||
use SilverStripe\Subsites\Extensions\SiteTreeSubsites;
|
use SilverStripe\Subsites\Extensions\SiteTreeSubsites;
|
||||||
use SilverStripe\Versioned\Versioned;
|
use SilverStripe\Versioned\Versioned;
|
||||||
use SilverStripe\View\Parsers\Diff;
|
use SilverStripe\View\Parsers\HtmlDiff;
|
||||||
use SilverStripe\View\Parsers\ShortcodeParser;
|
use SilverStripe\View\Parsers\ShortcodeParser;
|
||||||
use SilverStripe\View\Parsers\URLSegmentFilter;
|
use SilverStripe\View\Parsers\URLSegmentFilter;
|
||||||
use SilverStripe\View\Shortcodes\EmbedShortcodeProvider;
|
use SilverStripe\View\Shortcodes\EmbedShortcodeProvider;
|
||||||
use TractorCow\Fluent\Extension\FluentSiteTreeExtension;
|
use TractorCow\Fluent\Extension\FluentSiteTreeExtension;
|
||||||
|
use Page;
|
||||||
|
use PageController;
|
||||||
|
|
||||||
use const RESOURCES_DIR;
|
use const RESOURCES_DIR;
|
||||||
use SilverStripe\Dev\Deprecation;
|
|
||||||
use SilverStripe\HTML5\HTML5Value;
|
|
||||||
use SilverStripe\View\Parsers\HTMLValue;
|
|
||||||
use SilverStripe\View\Parsers\HTML4Value;
|
|
||||||
|
|
||||||
class SiteTreeTest extends SapphireTest
|
class SiteTreeTest extends SapphireTest
|
||||||
{
|
{
|
||||||
@ -137,7 +135,7 @@ class SiteTreeTest extends SapphireTest
|
|||||||
];
|
];
|
||||||
|
|
||||||
foreach ($expectedURLs as $fixture => $urlSegment) {
|
foreach ($expectedURLs as $fixture => $urlSegment) {
|
||||||
$obj = $this->objFromFixture('Page', $fixture);
|
$obj = $this->objFromFixture(SiteTree::class, $fixture);
|
||||||
$this->assertEquals($urlSegment, $obj->URLSegment);
|
$this->assertEquals($urlSegment, $obj->URLSegment);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -148,9 +146,9 @@ class SiteTreeTest extends SapphireTest
|
|||||||
*/
|
*/
|
||||||
public function testDisallowedURLGeneration($title, $urlSegment)
|
public function testDisallowedURLGeneration($title, $urlSegment)
|
||||||
{
|
{
|
||||||
$page = Page::create(['Title' => $title]);
|
$page = new SiteTree(['Title' => $title]);
|
||||||
$id = $page->write();
|
$id = $page->write();
|
||||||
$page = Page::get()->byID($id);
|
$page = SiteTree::get()->byID($id);
|
||||||
$this->assertEquals($urlSegment, $page->URLSegment);
|
$this->assertEquals($urlSegment, $page->URLSegment);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,9 +161,9 @@ class SiteTreeTest extends SapphireTest
|
|||||||
{
|
{
|
||||||
// Using the same dataprovider, strip out the -2 from the admin and dev segment
|
// Using the same dataprovider, strip out the -2 from the admin and dev segment
|
||||||
$urlSegment = str_replace('-2', '', $urlSegment ?? '');
|
$urlSegment = str_replace('-2', '', $urlSegment ?? '');
|
||||||
$page = Page::create(['Title' => $title, 'ParentID' => 1]);
|
$page = new SiteTree(['Title' => $title, 'ParentID' => 1]);
|
||||||
$id = $page->write();
|
$id = $page->write();
|
||||||
$page = Page::get()->byID($id);
|
$page = SiteTree::get()->byID($id);
|
||||||
$this->assertEquals($urlSegment, $page->URLSegment);
|
$this->assertEquals($urlSegment, $page->URLSegment);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,7 +178,7 @@ class SiteTreeTest extends SapphireTest
|
|||||||
$this->markTestSkipped('This legacy test requires RESOURCES_DIR to be "resources"');
|
$this->markTestSkipped('This legacy test requires RESOURCES_DIR to be "resources"');
|
||||||
}
|
}
|
||||||
|
|
||||||
$page = SiteTree::create(['Title' => 'Resources']);
|
$page = new SiteTree(['Title' => 'Resources']);
|
||||||
$id = $page->write();
|
$id = $page->write();
|
||||||
$page = SiteTree::get()->byID($id);
|
$page = SiteTree::get()->byID($id);
|
||||||
$this->assertSame('resources-2', $page->URLSegment);
|
$this->assertSame('resources-2', $page->URLSegment);
|
||||||
@ -198,7 +196,7 @@ class SiteTreeTest extends SapphireTest
|
|||||||
$this->markTestSkipped('This test requires RESOURCES_DIR to be something other than "resources"');
|
$this->markTestSkipped('This test requires RESOURCES_DIR to be something other than "resources"');
|
||||||
}
|
}
|
||||||
|
|
||||||
$page = SiteTree::create(['Title' => '_Resources']);
|
$page = new SiteTree(['Title' => '_Resources']);
|
||||||
$id = $page->write();
|
$id = $page->write();
|
||||||
$page = SiteTree::get()->byID($id);
|
$page = SiteTree::get()->byID($id);
|
||||||
$this->assertSame('resources', $page->URLSegment);
|
$this->assertSame('resources', $page->URLSegment);
|
||||||
@ -209,10 +207,12 @@ class SiteTreeTest extends SapphireTest
|
|||||||
*/
|
*/
|
||||||
public function testPublishCopiesToLiveTable()
|
public function testPublishCopiesToLiveTable()
|
||||||
{
|
{
|
||||||
$obj = $this->objFromFixture('Page', 'about');
|
$obj = $this->objFromFixture(SiteTree::class, 'about');
|
||||||
$obj->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
$obj->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
||||||
|
|
||||||
$createdID = DB::query("SELECT \"ID\" FROM \"SiteTree_Live\" WHERE \"URLSegment\" = '$obj->URLSegment'")->value();
|
$createdID = DB::query(
|
||||||
|
"SELECT \"ID\" FROM \"SiteTree_Live\" WHERE \"URLSegment\" = '$obj->URLSegment'"
|
||||||
|
)->value();
|
||||||
$this->assertEquals($obj->ID, $createdID);
|
$this->assertEquals($obj->ID, $createdID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,12 +223,15 @@ class SiteTreeTest extends SapphireTest
|
|||||||
{
|
{
|
||||||
$this->logInWithPermission('ADMIN');
|
$this->logInWithPermission('ADMIN');
|
||||||
|
|
||||||
$obj = $this->objFromFixture('Page', 'about');
|
$obj = $this->objFromFixture(SiteTree::class, 'about');
|
||||||
$obj->Title = "asdfasdf";
|
$obj->Title = "asdfasdf";
|
||||||
$obj->write();
|
$obj->write();
|
||||||
$this->assertTrue($obj->publishRecursive());
|
$this->assertTrue($obj->publishRecursive());
|
||||||
|
|
||||||
$this->assertEquals('asdfasdf', DB::query("SELECT \"Title\" FROM \"SiteTree_Live\" WHERE \"ID\" = '$obj->ID'")->value());
|
$this->assertEquals(
|
||||||
|
'asdfasdf',
|
||||||
|
DB::query("SELECT \"Title\" FROM \"SiteTree_Live\" WHERE \"ID\" = '$obj->ID'")->value()
|
||||||
|
);
|
||||||
|
|
||||||
$obj->Title = null;
|
$obj->Title = null;
|
||||||
$obj->write();
|
$obj->write();
|
||||||
@ -275,7 +278,7 @@ class SiteTreeTest extends SapphireTest
|
|||||||
Versioned::set_stage(Versioned::LIVE);
|
Versioned::set_stage(Versioned::LIVE);
|
||||||
|
|
||||||
$checkSiteTree = DataObject::get_one(SiteTree::class, [
|
$checkSiteTree = DataObject::get_one(SiteTree::class, [
|
||||||
'"SiteTree"."URLSegment"' => 'get-one-test-page'
|
'"SiteTree"."URLSegment"' => 'get-one-test-page',
|
||||||
]);
|
]);
|
||||||
$this->assertEquals("V1", $checkSiteTree->Title);
|
$this->assertEquals("V1", $checkSiteTree->Title);
|
||||||
|
|
||||||
@ -314,10 +317,13 @@ class SiteTreeTest extends SapphireTest
|
|||||||
{
|
{
|
||||||
/* DataObject::write() should save to a has_one relationship if you set a field called (relname)ID */
|
/* DataObject::write() should save to a has_one relationship if you set a field called (relname)ID */
|
||||||
$page = new SiteTree();
|
$page = new SiteTree();
|
||||||
$parentID = $this->idFromFixture('Page', 'home');
|
$parentID = $this->idFromFixture(SiteTree::class, 'home');
|
||||||
$page->ParentID = $parentID;
|
$page->ParentID = $parentID;
|
||||||
$page->write();
|
$page->write();
|
||||||
$this->assertEquals($parentID, DB::query("SELECT \"ParentID\" FROM \"SiteTree\" WHERE \"ID\" = $page->ID")->value());
|
$this->assertEquals(
|
||||||
|
$parentID,
|
||||||
|
DB::query("SELECT \"ParentID\" FROM \"SiteTree\" WHERE \"ID\" = $page->ID")->value()
|
||||||
|
);
|
||||||
|
|
||||||
/* You should then be able to save a null/0/'' value to the relation */
|
/* You should then be able to save a null/0/'' value to the relation */
|
||||||
$page->ParentID = null;
|
$page->ParentID = null;
|
||||||
@ -401,23 +407,23 @@ class SiteTreeTest extends SapphireTest
|
|||||||
*/
|
*/
|
||||||
public function testRestoreToStage()
|
public function testRestoreToStage()
|
||||||
{
|
{
|
||||||
$page = $this->objFromFixture('Page', 'about');
|
$page = $this->objFromFixture(SiteTree::class, 'about');
|
||||||
$pageID = $page->ID;
|
$pageID = $page->ID;
|
||||||
$page->delete();
|
$page->delete();
|
||||||
$this->assertTrue(!DataObject::get_by_id("Page", $pageID));
|
$this->assertTrue(!DataObject::get_by_id(SiteTree::class, $pageID));
|
||||||
|
|
||||||
$deletedPage = Versioned::get_latest_version(SiteTree::class, $pageID);
|
$deletedPage = Versioned::get_latest_version(SiteTree::class, $pageID);
|
||||||
$resultPage = $deletedPage->doRestoreToStage();
|
$resultPage = $deletedPage->doRestoreToStage();
|
||||||
|
|
||||||
$requeriedPage = DataObject::get_by_id("Page", $pageID);
|
$requeriedPage = DataObject::get_by_id(SiteTree::class, $pageID);
|
||||||
|
|
||||||
$this->assertEquals($pageID, $resultPage->ID);
|
$this->assertEquals($pageID, $resultPage->ID);
|
||||||
$this->assertEquals($pageID, $requeriedPage->ID);
|
$this->assertEquals($pageID, $requeriedPage->ID);
|
||||||
$this->assertEquals('About Us', $requeriedPage->Title);
|
$this->assertEquals('About Us', $requeriedPage->Title);
|
||||||
$this->assertInstanceOf('Page', $requeriedPage);
|
$this->assertInstanceOf(SiteTree::class, $requeriedPage);
|
||||||
|
|
||||||
|
|
||||||
$page2 = $this->objFromFixture('Page', 'products');
|
$page2 = $this->objFromFixture(SiteTree::class, 'products');
|
||||||
$page2ID = $page2->ID;
|
$page2ID = $page2->ID;
|
||||||
$page2->doUnpublish();
|
$page2->doUnpublish();
|
||||||
$page2->delete();
|
$page2->delete();
|
||||||
@ -427,12 +433,14 @@ class SiteTreeTest extends SapphireTest
|
|||||||
Versioned::set_stage(Versioned::LIVE);
|
Versioned::set_stage(Versioned::LIVE);
|
||||||
$deletedPage = Versioned::get_latest_version(SiteTree::class, $page2ID);
|
$deletedPage = Versioned::get_latest_version(SiteTree::class, $page2ID);
|
||||||
$deletedPage->doRestoreToStage();
|
$deletedPage->doRestoreToStage();
|
||||||
$this->assertFalse((bool)Versioned::get_one_by_stage(SiteTree::class, Versioned::LIVE, "\"SiteTree\".\"ID\" = " . $page2ID));
|
$this->assertFalse(
|
||||||
|
(bool)Versioned::get_one_by_stage(SiteTree::class, Versioned::LIVE, "\"SiteTree\".\"ID\" = " . $page2ID)
|
||||||
|
);
|
||||||
|
|
||||||
Versioned::set_stage(Versioned::DRAFT);
|
Versioned::set_stage(Versioned::DRAFT);
|
||||||
$requeriedPage = DataObject::get_by_id("Page", $page2ID);
|
$requeriedPage = DataObject::get_by_id(SiteTree::class, $page2ID);
|
||||||
$this->assertEquals('Products', $requeriedPage->Title);
|
$this->assertEquals('Products', $requeriedPage->Title);
|
||||||
$this->assertInstanceOf('Page', $requeriedPage);
|
$this->assertInstanceOf(SiteTree::class, $requeriedPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNoCascadingDeleteWithoutID()
|
public function testNoCascadingDeleteWithoutID()
|
||||||
@ -456,11 +464,11 @@ class SiteTreeTest extends SapphireTest
|
|||||||
|
|
||||||
public function testGetByLink()
|
public function testGetByLink()
|
||||||
{
|
{
|
||||||
$home = $this->objFromFixture('Page', 'home');
|
$home = $this->objFromFixture(SiteTree::class, 'home');
|
||||||
$about = $this->objFromFixture('Page', 'about');
|
$about = $this->objFromFixture(SiteTree::class, 'about');
|
||||||
$staff = $this->objFromFixture('Page', 'staff');
|
$staff = $this->objFromFixture(SiteTree::class, 'staff');
|
||||||
$product = $this->objFromFixture('Page', 'product1');
|
$product = $this->objFromFixture(SiteTree::class, 'product1');
|
||||||
$numeric0 = $this->objFromFixture('Page', 'numeric0');
|
$numeric0 = $this->objFromFixture(SiteTree::class, 'numeric0');
|
||||||
|
|
||||||
SiteTree::config()->nested_urls = false;
|
SiteTree::config()->nested_urls = false;
|
||||||
|
|
||||||
@ -489,11 +497,11 @@ class SiteTreeTest extends SapphireTest
|
|||||||
|
|
||||||
public function testGetByLinkAbsolute()
|
public function testGetByLinkAbsolute()
|
||||||
{
|
{
|
||||||
$home = $this->objFromFixture('Page', 'home');
|
$home = $this->objFromFixture(SiteTree::class, 'home');
|
||||||
$about = $this->objFromFixture('Page', 'about');
|
$about = $this->objFromFixture(SiteTree::class, 'about');
|
||||||
$staff = $this->objFromFixture('Page', 'staff');
|
$staff = $this->objFromFixture(SiteTree::class, 'staff');
|
||||||
$product = $this->objFromFixture('Page', 'product1');
|
$product = $this->objFromFixture(SiteTree::class, 'product1');
|
||||||
$numeric0 = $this->objFromFixture('Page', 'numeric0');
|
$numeric0 = $this->objFromFixture(SiteTree::class, 'numeric0');
|
||||||
|
|
||||||
$base = 'https://example.test/';
|
$base = 'https://example.test/';
|
||||||
$this->assertEquals($home->ID, SiteTree::get_by_link(Controller::join_links($base, '/'), false)->ID);
|
$this->assertEquals($home->ID, SiteTree::get_by_link(Controller::join_links($base, '/'), false)->ID);
|
||||||
@ -506,31 +514,51 @@ class SiteTreeTest extends SapphireTest
|
|||||||
|
|
||||||
public function testRelativeLink()
|
public function testRelativeLink()
|
||||||
{
|
{
|
||||||
$about = $this->objFromFixture('Page', 'about');
|
$about = $this->objFromFixture(SiteTree::class, 'about');
|
||||||
$staff = $this->objFromFixture('Page', 'staff');
|
$staff = $this->objFromFixture(SiteTree::class, 'staff');
|
||||||
$numeric0 = $this->objFromFixture('Page', 'numeric0');
|
$numeric0 = $this->objFromFixture(SiteTree::class, 'numeric0');
|
||||||
|
|
||||||
Config::modify()->set(SiteTree::class, 'nested_urls', true);
|
Config::modify()->set(SiteTree::class, 'nested_urls', true);
|
||||||
|
|
||||||
$this->assertEquals('about-us/', $about->RelativeLink(), 'Matches URLSegment on top level without parameters');
|
$this->assertEquals(
|
||||||
$this->assertEquals('about-us/my-staff/', $staff->RelativeLink(), 'Matches URLSegment plus parent on second level without parameters');
|
'about-us',
|
||||||
$this->assertEquals('about-us/edit', $about->RelativeLink('edit'), 'Matches URLSegment plus parameter on top level');
|
$about->RelativeLink(),
|
||||||
$this->assertEquals('about-us/tom&jerry', $about->RelativeLink('tom&jerry'), 'Doesnt url encode parameter');
|
'Matches URLSegment on top level without parameters'
|
||||||
$this->assertEquals('0/', $numeric0->RelativeLink(), 'Matches URLSegment for segment = 0');
|
);
|
||||||
|
$this->assertEquals(
|
||||||
|
'about-us/my-staff',
|
||||||
|
$staff->RelativeLink(),
|
||||||
|
'Matches URLSegment plus parent on second level without parameters'
|
||||||
|
);
|
||||||
|
$this->assertEquals(
|
||||||
|
Controller::normaliseTrailingSlash('0/'),
|
||||||
|
$numeric0->RelativeLink(),
|
||||||
|
'Matches URLSegment for segment = 0'
|
||||||
|
);
|
||||||
|
$this->assertEquals(
|
||||||
|
'about-us/edit',
|
||||||
|
$about->RelativeLink('edit'),
|
||||||
|
'Matches URLSegment plus parameter on top level'
|
||||||
|
);
|
||||||
|
$this->assertEquals(
|
||||||
|
'about-us/tom&jerry',
|
||||||
|
$about->RelativeLink('tom&jerry'),
|
||||||
|
'Doesnt url encode parameter'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testPageLevel()
|
public function testPageLevel()
|
||||||
{
|
{
|
||||||
$about = $this->objFromFixture('Page', 'about');
|
$about = $this->objFromFixture(SiteTree::class, 'about');
|
||||||
$staff = $this->objFromFixture('Page', 'staff');
|
$staff = $this->objFromFixture(SiteTree::class, 'staff');
|
||||||
$this->assertEquals(1, $about->getPageLevel());
|
$this->assertEquals(1, $about->getPageLevel());
|
||||||
$this->assertEquals(2, $staff->getPageLevel());
|
$this->assertEquals(2, $staff->getPageLevel());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAbsoluteLiveLink()
|
public function testAbsoluteLiveLink()
|
||||||
{
|
{
|
||||||
$parent = $this->objFromFixture('Page', 'about');
|
$parent = $this->objFromFixture(SiteTree::class, 'about');
|
||||||
$child = $this->objFromFixture('Page', 'staff');
|
$child = $this->objFromFixture(SiteTree::class, 'staff');
|
||||||
|
|
||||||
Config::modify()->set(SiteTree::class, 'nested_urls', true);
|
Config::modify()->set(SiteTree::class, 'nested_urls', true);
|
||||||
|
|
||||||
@ -541,23 +569,23 @@ class SiteTreeTest extends SapphireTest
|
|||||||
$parent->URLSegment = 'changed-on-draft';
|
$parent->URLSegment = 'changed-on-draft';
|
||||||
$parent->write();
|
$parent->write();
|
||||||
|
|
||||||
$this->assertStringEndsWith('changed-on-live/my-staff/', $child->getAbsoluteLiveLink(false));
|
$this->assertStringEndsWith('changed-on-live/my-staff', $child->getAbsoluteLiveLink(false));
|
||||||
$this->assertStringEndsWith('changed-on-live/my-staff/?stage=Live', $child->getAbsoluteLiveLink());
|
$this->assertStringEndsWith('changed-on-live/my-staff?stage=Live', $child->getAbsoluteLiveLink());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testDuplicateChildrenRetainSort()
|
public function testDuplicateChildrenRetainSort()
|
||||||
{
|
{
|
||||||
$parent = new Page();
|
$parent = new SiteTree();
|
||||||
$parent->Title = 'Parent';
|
$parent->Title = 'Parent';
|
||||||
$parent->write();
|
$parent->write();
|
||||||
|
|
||||||
$child1 = new Page();
|
$child1 = new SiteTree();
|
||||||
$child1->ParentID = $parent->ID;
|
$child1->ParentID = $parent->ID;
|
||||||
$child1->Title = 'Child 1';
|
$child1->Title = 'Child 1';
|
||||||
$child1->Sort = 2;
|
$child1->Sort = 2;
|
||||||
$child1->write();
|
$child1->write();
|
||||||
|
|
||||||
$child2 = new Page();
|
$child2 = new SiteTree();
|
||||||
$child2->ParentID = $parent->ID;
|
$child2->ParentID = $parent->ID;
|
||||||
$child2->Title = 'Child 2';
|
$child2->Title = 'Child 2';
|
||||||
$child2->Sort = 1;
|
$child2->Sort = 1;
|
||||||
@ -581,34 +609,34 @@ class SiteTreeTest extends SapphireTest
|
|||||||
public function testDeleteFromStageOperatesRecursively()
|
public function testDeleteFromStageOperatesRecursively()
|
||||||
{
|
{
|
||||||
Config::modify()->set(SiteTree::class, 'enforce_strict_hierarchy', false);
|
Config::modify()->set(SiteTree::class, 'enforce_strict_hierarchy', false);
|
||||||
$pageAbout = $this->objFromFixture('Page', 'about');
|
$pageAbout = $this->objFromFixture(SiteTree::class, 'about');
|
||||||
$pageStaff = $this->objFromFixture('Page', 'staff');
|
$pageStaff = $this->objFromFixture(SiteTree::class, 'staff');
|
||||||
$pageStaffDuplicate = $this->objFromFixture('Page', 'staffduplicate');
|
$pageStaffDuplicate = $this->objFromFixture(SiteTree::class, 'staffduplicate');
|
||||||
|
|
||||||
$pageAbout->delete();
|
$pageAbout->delete();
|
||||||
|
|
||||||
$this->assertNull(DataObject::get_by_id('Page', $pageAbout->ID));
|
$this->assertNull(DataObject::get_by_id(SiteTree::class, $pageAbout->ID));
|
||||||
$this->assertTrue(DataObject::get_by_id('Page', $pageStaff->ID) instanceof Page);
|
$this->assertTrue(DataObject::get_by_id(SiteTree::class, $pageStaff->ID) instanceof SiteTree);
|
||||||
$this->assertTrue(DataObject::get_by_id('Page', $pageStaffDuplicate->ID) instanceof Page);
|
$this->assertTrue(DataObject::get_by_id(SiteTree::class, $pageStaffDuplicate->ID) instanceof SiteTree);
|
||||||
Config::modify()->set(SiteTree::class, 'enforce_strict_hierarchy', true);
|
Config::modify()->set(SiteTree::class, 'enforce_strict_hierarchy', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testDeleteFromStageOperatesRecursivelyStrict()
|
public function testDeleteFromStageOperatesRecursivelyStrict()
|
||||||
{
|
{
|
||||||
$pageAbout = $this->objFromFixture('Page', 'about');
|
$pageAbout = $this->objFromFixture(SiteTree::class, 'about');
|
||||||
$pageStaff = $this->objFromFixture('Page', 'staff');
|
$pageStaff = $this->objFromFixture(SiteTree::class, 'staff');
|
||||||
$pageStaffDuplicate = $this->objFromFixture('Page', 'staffduplicate');
|
$pageStaffDuplicate = $this->objFromFixture(SiteTree::class, 'staffduplicate');
|
||||||
|
|
||||||
$pageAbout->delete();
|
$pageAbout->delete();
|
||||||
|
|
||||||
$this->assertNull(DataObject::get_by_id('Page', $pageAbout->ID));
|
$this->assertNull(DataObject::get_by_id(SiteTree::class, $pageAbout->ID));
|
||||||
$this->assertNull(DataObject::get_by_id('Page', $pageStaff->ID));
|
$this->assertNull(DataObject::get_by_id(SiteTree::class, $pageStaff->ID));
|
||||||
$this->assertNull(DataObject::get_by_id('Page', $pageStaffDuplicate->ID));
|
$this->assertNull(DataObject::get_by_id(SiteTree::class, $pageStaffDuplicate->ID));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testDuplicate()
|
public function testDuplicate()
|
||||||
{
|
{
|
||||||
$pageAbout = $this->objFromFixture('Page', 'about');
|
$pageAbout = $this->objFromFixture(SiteTree::class, 'about');
|
||||||
$dupe = $pageAbout->duplicate();
|
$dupe = $pageAbout->duplicate();
|
||||||
$this->assertEquals($pageAbout->Title, $dupe->Title);
|
$this->assertEquals($pageAbout->Title, $dupe->Title);
|
||||||
$this->assertNotEquals($pageAbout->URLSegment, $dupe->URLSegment);
|
$this->assertNotEquals($pageAbout->URLSegment, $dupe->URLSegment);
|
||||||
@ -620,22 +648,22 @@ class SiteTreeTest extends SapphireTest
|
|||||||
Config::modify()->set(SiteTree::class, 'enforce_strict_hierarchy', false);
|
Config::modify()->set(SiteTree::class, 'enforce_strict_hierarchy', false);
|
||||||
$this->logInWithPermission('ADMIN');
|
$this->logInWithPermission('ADMIN');
|
||||||
|
|
||||||
$pageAbout = $this->objFromFixture('Page', 'about');
|
$pageAbout = $this->objFromFixture(SiteTree::class, 'about');
|
||||||
$pageAbout->publishRecursive();
|
$pageAbout->publishRecursive();
|
||||||
$pageStaff = $this->objFromFixture('Page', 'staff');
|
$pageStaff = $this->objFromFixture(SiteTree::class, 'staff');
|
||||||
$pageStaff->publishRecursive();
|
$pageStaff->publishRecursive();
|
||||||
$pageStaffDuplicate = $this->objFromFixture('Page', 'staffduplicate');
|
$pageStaffDuplicate = $this->objFromFixture(SiteTree::class, 'staffduplicate');
|
||||||
$pageStaffDuplicate->publishRecursive();
|
$pageStaffDuplicate->publishRecursive();
|
||||||
|
|
||||||
$parentPage = $this->objFromFixture('Page', 'about');
|
$parentPage = $this->objFromFixture(SiteTree::class, 'about');
|
||||||
|
|
||||||
$parentPage->doUnpublish();
|
$parentPage->doUnpublish();
|
||||||
|
|
||||||
Versioned::set_stage(Versioned::LIVE);
|
Versioned::set_stage(Versioned::LIVE);
|
||||||
|
|
||||||
$this->assertNull(DataObject::get_by_id('Page', $pageAbout->ID));
|
$this->assertNull(DataObject::get_by_id(SiteTree::class, $pageAbout->ID));
|
||||||
$this->assertTrue(DataObject::get_by_id('Page', $pageStaff->ID) instanceof Page);
|
$this->assertTrue(DataObject::get_by_id(SiteTree::class, $pageStaff->ID) instanceof SiteTree);
|
||||||
$this->assertTrue(DataObject::get_by_id('Page', $pageStaffDuplicate->ID) instanceof Page);
|
$this->assertTrue(DataObject::get_by_id(SiteTree::class, $pageStaffDuplicate->ID) instanceof SiteTree);
|
||||||
Versioned::set_stage(Versioned::DRAFT);
|
Versioned::set_stage(Versioned::DRAFT);
|
||||||
Config::modify()->set(SiteTree::class, 'enforce_strict_hierarchy', true);
|
Config::modify()->set(SiteTree::class, 'enforce_strict_hierarchy', true);
|
||||||
}
|
}
|
||||||
@ -645,20 +673,20 @@ class SiteTreeTest extends SapphireTest
|
|||||||
Config::modify()->set(SiteTree::class, 'enforce_strict_hierarchy', false);
|
Config::modify()->set(SiteTree::class, 'enforce_strict_hierarchy', false);
|
||||||
$this->logInWithPermission('ADMIN');
|
$this->logInWithPermission('ADMIN');
|
||||||
|
|
||||||
$pageAbout = $this->objFromFixture('Page', 'about');
|
$pageAbout = $this->objFromFixture(SiteTree::class, 'about');
|
||||||
$pageAbout->publishRecursive();
|
$pageAbout->publishRecursive();
|
||||||
$pageStaff = $this->objFromFixture('Page', 'staff');
|
$pageStaff = $this->objFromFixture(SiteTree::class, 'staff');
|
||||||
$pageStaff->publishRecursive();
|
$pageStaff->publishRecursive();
|
||||||
$pageStaffDuplicate = $this->objFromFixture('Page', 'staffduplicate');
|
$pageStaffDuplicate = $this->objFromFixture(SiteTree::class, 'staffduplicate');
|
||||||
$pageStaffDuplicate->publishRecursive();
|
$pageStaffDuplicate->publishRecursive();
|
||||||
|
|
||||||
$parentPage = $this->objFromFixture('Page', 'about');
|
$parentPage = $this->objFromFixture(SiteTree::class, 'about');
|
||||||
$parentPage->doUnpublish();
|
$parentPage->doUnpublish();
|
||||||
|
|
||||||
Versioned::set_stage(Versioned::LIVE);
|
Versioned::set_stage(Versioned::LIVE);
|
||||||
$this->assertNull(DataObject::get_by_id('Page', $pageAbout->ID));
|
$this->assertNull(DataObject::get_by_id(SiteTree::class, $pageAbout->ID));
|
||||||
$this->assertTrue(DataObject::get_by_id('Page', $pageStaff->ID) instanceof Page);
|
$this->assertTrue(DataObject::get_by_id(SiteTree::class, $pageStaff->ID) instanceof SiteTree);
|
||||||
$this->assertTrue(DataObject::get_by_id('Page', $pageStaffDuplicate->ID) instanceof Page);
|
$this->assertTrue(DataObject::get_by_id(SiteTree::class, $pageStaffDuplicate->ID) instanceof SiteTree);
|
||||||
Versioned::set_stage(Versioned::DRAFT);
|
Versioned::set_stage(Versioned::DRAFT);
|
||||||
Config::modify()->set(SiteTree::class, 'enforce_strict_hierarchy', true);
|
Config::modify()->set(SiteTree::class, 'enforce_strict_hierarchy', true);
|
||||||
}
|
}
|
||||||
@ -667,20 +695,20 @@ class SiteTreeTest extends SapphireTest
|
|||||||
{
|
{
|
||||||
$this->logInWithPermission('ADMIN');
|
$this->logInWithPermission('ADMIN');
|
||||||
|
|
||||||
$pageAbout = $this->objFromFixture('Page', 'about');
|
$pageAbout = $this->objFromFixture(SiteTree::class, 'about');
|
||||||
$pageAbout->publishRecursive();
|
$pageAbout->publishRecursive();
|
||||||
$pageStaff = $this->objFromFixture('Page', 'staff');
|
$pageStaff = $this->objFromFixture(SiteTree::class, 'staff');
|
||||||
$pageStaff->publishRecursive();
|
$pageStaff->publishRecursive();
|
||||||
$pageStaffDuplicate = $this->objFromFixture('Page', 'staffduplicate');
|
$pageStaffDuplicate = $this->objFromFixture(SiteTree::class, 'staffduplicate');
|
||||||
$pageStaffDuplicate->publishRecursive();
|
$pageStaffDuplicate->publishRecursive();
|
||||||
|
|
||||||
$parentPage = $this->objFromFixture('Page', 'about');
|
$parentPage = $this->objFromFixture(SiteTree::class, 'about');
|
||||||
$parentPage->doUnpublish();
|
$parentPage->doUnpublish();
|
||||||
|
|
||||||
Versioned::set_stage(Versioned::LIVE);
|
Versioned::set_stage(Versioned::LIVE);
|
||||||
$this->assertNull(DataObject::get_by_id('Page', $pageAbout->ID));
|
$this->assertNull(DataObject::get_by_id(SiteTree::class, $pageAbout->ID));
|
||||||
$this->assertNull(DataObject::get_by_id('Page', $pageStaff->ID));
|
$this->assertNull(DataObject::get_by_id(SiteTree::class, $pageStaff->ID));
|
||||||
$this->assertNull(DataObject::get_by_id('Page', $pageStaffDuplicate->ID));
|
$this->assertNull(DataObject::get_by_id(SiteTree::class, $pageStaffDuplicate->ID));
|
||||||
Versioned::set_stage(Versioned::DRAFT);
|
Versioned::set_stage(Versioned::DRAFT);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -691,7 +719,7 @@ class SiteTreeTest extends SapphireTest
|
|||||||
public function testReadArchiveDate()
|
public function testReadArchiveDate()
|
||||||
{
|
{
|
||||||
DBDatetime::set_mock_now('2009-07-02 14:05:07');
|
DBDatetime::set_mock_now('2009-07-02 14:05:07');
|
||||||
$oldPage = SiteTree::create();
|
$oldPage = new SiteTree();
|
||||||
$oldPage->Title = 'A really old page';
|
$oldPage->Title = 'A really old page';
|
||||||
$oldPage->write();
|
$oldPage->write();
|
||||||
DBDatetime::clear_mock_now();
|
DBDatetime::clear_mock_now();
|
||||||
@ -699,7 +727,7 @@ class SiteTreeTest extends SapphireTest
|
|||||||
$date = '2009-07-02 14:05:07';
|
$date = '2009-07-02 14:05:07';
|
||||||
Versioned::reading_archived_date($date);
|
Versioned::reading_archived_date($date);
|
||||||
$result = SiteTree::get()->where([
|
$result = SiteTree::get()->where([
|
||||||
'"SiteTree"."ParentID"' => 0
|
'"SiteTree"."ParentID"' => 0,
|
||||||
]);
|
]);
|
||||||
$this->assertCount(1, $result, '"A really old page" should be returned');
|
$this->assertCount(1, $result, '"A really old page" should be returned');
|
||||||
}
|
}
|
||||||
@ -708,11 +736,11 @@ class SiteTreeTest extends SapphireTest
|
|||||||
{
|
{
|
||||||
$editor = $this->objFromFixture(Member::class, "editor");
|
$editor = $this->objFromFixture(Member::class, "editor");
|
||||||
|
|
||||||
$home = $this->objFromFixture("Page", "home");
|
$home = $this->objFromFixture(SiteTree::class, "home");
|
||||||
$staff = $this->objFromFixture("Page", "staff");
|
$staff = $this->objFromFixture(SiteTree::class, "staff");
|
||||||
$products = $this->objFromFixture("Page", "products");
|
$products = $this->objFromFixture(SiteTree::class, "products");
|
||||||
$product1 = $this->objFromFixture("Page", "product1");
|
$product1 = $this->objFromFixture(SiteTree::class, "product1");
|
||||||
$product4 = $this->objFromFixture("Page", "product4");
|
$product4 = $this->objFromFixture(SiteTree::class, "product4");
|
||||||
|
|
||||||
// Test logged out users cannot edit
|
// Test logged out users cannot edit
|
||||||
$this->logOut();
|
$this->logOut();
|
||||||
@ -733,7 +761,7 @@ class SiteTreeTest extends SapphireTest
|
|||||||
|
|
||||||
public function testCanEditWithAccessToAllSections()
|
public function testCanEditWithAccessToAllSections()
|
||||||
{
|
{
|
||||||
$page = new Page();
|
$page = new SiteTree();
|
||||||
$page->write();
|
$page->write();
|
||||||
$allSectionMember = $this->objFromFixture(Member::class, 'allsections');
|
$allSectionMember = $this->objFromFixture(Member::class, 'allsections');
|
||||||
$securityAdminMember = $this->objFromFixture(Member::class, 'securityadmin');
|
$securityAdminMember = $this->objFromFixture(Member::class, 'securityadmin');
|
||||||
@ -758,7 +786,7 @@ class SiteTreeTest extends SapphireTest
|
|||||||
$this->assertTrue(singleton(SiteTree::class)->canCreate());
|
$this->assertTrue(singleton(SiteTree::class)->canCreate());
|
||||||
|
|
||||||
// Test creation underneath a parent which this user doesn't have access to
|
// Test creation underneath a parent which this user doesn't have access to
|
||||||
$parent = $this->objFromFixture('Page', 'about');
|
$parent = $this->objFromFixture(SiteTree::class, 'about');
|
||||||
$this->assertFalse(singleton(SiteTree::class)->canCreate(null, ['Parent' => $parent]));
|
$this->assertFalse(singleton(SiteTree::class)->canCreate(null, ['Parent' => $parent]));
|
||||||
|
|
||||||
// Test creation underneath a parent which doesn't allow a certain child
|
// Test creation underneath a parent which doesn't allow a certain child
|
||||||
@ -774,13 +802,18 @@ class SiteTreeTest extends SapphireTest
|
|||||||
$this->assertTrue(singleton(SiteTree::class)->canCreate(null, ['Parent' => singleton(SiteTree::class)]));
|
$this->assertTrue(singleton(SiteTree::class)->canCreate(null, ['Parent' => singleton(SiteTree::class)]));
|
||||||
|
|
||||||
//Test we don't check for allowedChildren on parent context if it's not SiteTree instance
|
//Test we don't check for allowedChildren on parent context if it's not SiteTree instance
|
||||||
$this->assertTrue(singleton(SiteTree::class)->canCreate(null, ['Parent' => $this->objFromFixture(SiteTreeTest_DataObject::class, 'relations')]));
|
$this->assertTrue(
|
||||||
|
singleton(SiteTree::class)->canCreate(
|
||||||
|
null,
|
||||||
|
['Parent' => $this->objFromFixture(SiteTreeTest_DataObject::class, 'relations')]
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testEditPermissionsOnDraftVsLive()
|
public function testEditPermissionsOnDraftVsLive()
|
||||||
{
|
{
|
||||||
// Create an inherit-permission page
|
// Create an inherit-permission page
|
||||||
$page = new Page();
|
$page = new SiteTree();
|
||||||
$page->write();
|
$page->write();
|
||||||
$page->CanEditType = "Inherit";
|
$page->CanEditType = "Inherit";
|
||||||
$page->publishRecursive();
|
$page->publishRecursive();
|
||||||
@ -828,10 +861,10 @@ class SiteTreeTest extends SapphireTest
|
|||||||
public function testCompareVersions()
|
public function testCompareVersions()
|
||||||
{
|
{
|
||||||
// Necessary to avoid
|
// Necessary to avoid
|
||||||
$oldCleanerClass = Diff::$html_cleaner_class;
|
$oldCleanerClass = HtmlDiff::$html_cleaner_class;
|
||||||
Diff::$html_cleaner_class = SiteTreeTest_NullHtmlCleaner::class;
|
HtmlDiff::$html_cleaner_class = SiteTreeTest_NullHtmlCleaner::class;
|
||||||
|
|
||||||
$page = new Page();
|
$page = new SiteTree();
|
||||||
$page->write();
|
$page->write();
|
||||||
$this->assertEquals(1, $page->Version);
|
$this->assertEquals(1, $page->Version);
|
||||||
|
|
||||||
@ -849,7 +882,7 @@ class SiteTreeTest extends SapphireTest
|
|||||||
$processedContent = preg_replace('/>\s*/', '>', $processedContent ?? '');
|
$processedContent = preg_replace('/>\s*/', '>', $processedContent ?? '');
|
||||||
$this->assertEquals("<ins><span>This is a test</span></ins>", $processedContent);
|
$this->assertEquals("<ins><span>This is a test</span></ins>", $processedContent);
|
||||||
|
|
||||||
Diff::$html_cleaner_class = $oldCleanerClass;
|
HtmlDiff::$html_cleaner_class = $oldCleanerClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testAuthorIDAndPublisherIDFilledOutOnPublish()
|
public function testAuthorIDAndPublisherIDFilledOutOnPublish()
|
||||||
@ -859,7 +892,7 @@ class SiteTreeTest extends SapphireTest
|
|||||||
$this->logInAs($member);
|
$this->logInAs($member);
|
||||||
|
|
||||||
// Write the page
|
// Write the page
|
||||||
$about = $this->objFromFixture('Page', 'about');
|
$about = $this->objFromFixture(SiteTree::class, 'about');
|
||||||
$about->Title = "Another title";
|
$about->Title = "Another title";
|
||||||
$about->write();
|
$about->write();
|
||||||
|
|
||||||
@ -868,7 +901,7 @@ class SiteTreeTest extends SapphireTest
|
|||||||
"SELECT \"AuthorID\", \"PublisherID\" FROM \"SiteTree_Versions\"
|
"SELECT \"AuthorID\", \"PublisherID\" FROM \"SiteTree_Versions\"
|
||||||
WHERE \"RecordID\" = ? ORDER BY \"Version\" DESC",
|
WHERE \"RecordID\" = ? ORDER BY \"Version\" DESC",
|
||||||
[$about->ID]
|
[$about->ID]
|
||||||
)->first();
|
)->record();
|
||||||
$this->assertEquals($member->ID, $savedVersion['AuthorID']);
|
$this->assertEquals($member->ID, $savedVersion['AuthorID']);
|
||||||
$this->assertEquals(0, $savedVersion['PublisherID']);
|
$this->assertEquals(0, $savedVersion['PublisherID']);
|
||||||
|
|
||||||
@ -878,7 +911,7 @@ class SiteTreeTest extends SapphireTest
|
|||||||
"SELECT \"AuthorID\", \"PublisherID\" FROM \"SiteTree_Versions\"
|
"SELECT \"AuthorID\", \"PublisherID\" FROM \"SiteTree_Versions\"
|
||||||
WHERE \"RecordID\" = ? ORDER BY \"Version\" DESC",
|
WHERE \"RecordID\" = ? ORDER BY \"Version\" DESC",
|
||||||
[$about->ID]
|
[$about->ID]
|
||||||
)->first();
|
)->record();
|
||||||
|
|
||||||
// Check the version created
|
// Check the version created
|
||||||
$this->assertEquals($member->ID, $publishedVersion['AuthorID']);
|
$this->assertEquals($member->ID, $publishedVersion['AuthorID']);
|
||||||
@ -887,7 +920,7 @@ class SiteTreeTest extends SapphireTest
|
|||||||
|
|
||||||
public function testLinkShortcodeHandler()
|
public function testLinkShortcodeHandler()
|
||||||
{
|
{
|
||||||
$aboutPage = $this->objFromFixture('Page', 'about');
|
$aboutPage = $this->objFromFixture(SiteTree::class, 'about');
|
||||||
$redirectPage = $this->objFromFixture(RedirectorPage::class, 'external');
|
$redirectPage = $this->objFromFixture(RedirectorPage::class, 'external');
|
||||||
|
|
||||||
$parser = new ShortcodeParser();
|
$parser = new ShortcodeParser();
|
||||||
@ -899,12 +932,24 @@ class SiteTreeTest extends SapphireTest
|
|||||||
$aboutShortcodeExpected = $aboutPage->Link();
|
$aboutShortcodeExpected = $aboutPage->Link();
|
||||||
$aboutEnclosedExpected = sprintf('<a href="%s">Example Content</a>', $aboutPage->Link());
|
$aboutEnclosedExpected = sprintf('<a href="%s">Example Content</a>', $aboutPage->Link());
|
||||||
|
|
||||||
$this->assertEquals($aboutShortcodeExpected, $parser->parse($aboutShortcode), 'Test that simple linking works.');
|
$this->assertEquals(
|
||||||
$this->assertEquals($aboutEnclosedExpected, $parser->parse($aboutEnclosed), 'Test enclosed content is linked.');
|
$aboutShortcodeExpected,
|
||||||
|
$parser->parse($aboutShortcode),
|
||||||
|
'Test that simple linking works.'
|
||||||
|
);
|
||||||
|
$this->assertEquals(
|
||||||
|
$aboutEnclosedExpected,
|
||||||
|
$parser->parse($aboutEnclosed),
|
||||||
|
'Test enclosed content is linked.'
|
||||||
|
);
|
||||||
|
|
||||||
$aboutPage->delete();
|
$aboutPage->delete();
|
||||||
|
|
||||||
$this->assertEquals($aboutShortcodeExpected, $parser->parse($aboutShortcode), 'Test that deleted pages still link.');
|
$this->assertEquals(
|
||||||
|
$aboutShortcodeExpected,
|
||||||
|
$parser->parse($aboutShortcode),
|
||||||
|
'Test that deleted pages still link.'
|
||||||
|
);
|
||||||
$this->assertEquals($aboutEnclosedExpected, $parser->parse($aboutEnclosed));
|
$this->assertEquals($aboutEnclosedExpected, $parser->parse($aboutEnclosed));
|
||||||
|
|
||||||
$aboutShortcode = '[sitetree_link,id="-1"]';
|
$aboutShortcode = '[sitetree_link,id="-1"]';
|
||||||
@ -918,7 +963,10 @@ class SiteTreeTest extends SapphireTest
|
|||||||
$redirectExpected = 'http://www.google.com?a&b';
|
$redirectExpected = 'http://www.google.com?a&b';
|
||||||
|
|
||||||
$this->assertEquals($redirectExpected, $parser->parse($redirectShortcode));
|
$this->assertEquals($redirectExpected, $parser->parse($redirectShortcode));
|
||||||
$this->assertEquals(sprintf('<a href="%s">Example Content</a>', $redirectExpected), $parser->parse($redirectEnclosed));
|
$this->assertEquals(
|
||||||
|
sprintf('<a href="%s">Example Content</a>', $redirectExpected),
|
||||||
|
$parser->parse($redirectEnclosed)
|
||||||
|
);
|
||||||
|
|
||||||
$this->assertEquals('', $parser->parse('[sitetree_link]'), 'Test that invalid ID attributes are not parsed.');
|
$this->assertEquals('', $parser->parse('[sitetree_link]'), 'Test that invalid ID attributes are not parsed.');
|
||||||
$this->assertEquals('', $parser->parse('[sitetree_link,id="text"]'));
|
$this->assertEquals('', $parser->parse('[sitetree_link,id="text"]'));
|
||||||
@ -927,8 +975,8 @@ class SiteTreeTest extends SapphireTest
|
|||||||
|
|
||||||
public function testIsCurrent()
|
public function testIsCurrent()
|
||||||
{
|
{
|
||||||
$aboutPage = $this->objFromFixture('Page', 'about');
|
$aboutPage = $this->objFromFixture(SiteTree::class, 'about');
|
||||||
$productPage = $this->objFromFixture('Page', 'products');
|
$productPage = $this->objFromFixture(SiteTree::class, 'products');
|
||||||
|
|
||||||
Director::set_current_page($aboutPage);
|
Director::set_current_page($aboutPage);
|
||||||
$this->assertTrue($aboutPage->isCurrent(), 'Assert that basic isCurrent checks works.');
|
$this->assertTrue($aboutPage->isCurrent(), 'Assert that basic isCurrent checks works.');
|
||||||
@ -936,7 +984,7 @@ class SiteTreeTest extends SapphireTest
|
|||||||
|
|
||||||
$this->assertTrue(
|
$this->assertTrue(
|
||||||
DataObject::get_one(SiteTree::class, [
|
DataObject::get_one(SiteTree::class, [
|
||||||
'"SiteTree"."Title"' => 'About Us'
|
'"SiteTree"."Title"' => 'About Us',
|
||||||
])->isCurrent(),
|
])->isCurrent(),
|
||||||
'Assert that isCurrent works on another instance with the same ID.'
|
'Assert that isCurrent works on another instance with the same ID.'
|
||||||
);
|
);
|
||||||
@ -947,9 +995,9 @@ class SiteTreeTest extends SapphireTest
|
|||||||
|
|
||||||
public function testIsSection()
|
public function testIsSection()
|
||||||
{
|
{
|
||||||
$about = $this->objFromFixture('Page', 'about');
|
$about = $this->objFromFixture(SiteTree::class, 'about');
|
||||||
$staff = $this->objFromFixture('Page', 'staff');
|
$staff = $this->objFromFixture(SiteTree::class, 'staff');
|
||||||
$ceo = $this->objFromFixture('Page', 'ceo');
|
$ceo = $this->objFromFixture(SiteTree::class, 'ceo');
|
||||||
|
|
||||||
Director::set_current_page($about);
|
Director::set_current_page($about);
|
||||||
$this->assertTrue($about->isSection());
|
$this->assertTrue($about->isSection());
|
||||||
@ -969,7 +1017,7 @@ class SiteTreeTest extends SapphireTest
|
|||||||
|
|
||||||
public function testURLSegmentReserved()
|
public function testURLSegmentReserved()
|
||||||
{
|
{
|
||||||
$siteTree = SiteTree::create(['URLSegment' => 'admin']);
|
$siteTree = new SiteTree(['URLSegment' => 'admin']);
|
||||||
$segment = $siteTree->validURLSegment();
|
$segment = $siteTree->validURLSegment();
|
||||||
|
|
||||||
$this->assertFalse($segment);
|
$this->assertFalse($segment);
|
||||||
@ -1057,7 +1105,7 @@ class SiteTreeTest extends SapphireTest
|
|||||||
$sitetree->URLSegment = 'home-noconflict';
|
$sitetree->URLSegment = 'home-noconflict';
|
||||||
$this->assertTrue($sitetree->validURLSegment());
|
$this->assertTrue($sitetree->validURLSegment());
|
||||||
|
|
||||||
$sitetree->ParentID = $this->idFromFixture('Page', 'about');
|
$sitetree->ParentID = $this->idFromFixture(SiteTree::class, 'about');
|
||||||
$sitetree->URLSegment = 'home';
|
$sitetree->URLSegment = 'home';
|
||||||
$this->assertFalse($sitetree->validURLSegment(), 'Conflicts are still recognised with a ParentID value');
|
$this->assertFalse($sitetree->validURLSegment(), 'Conflicts are still recognised with a ParentID value');
|
||||||
|
|
||||||
@ -1067,7 +1115,7 @@ class SiteTreeTest extends SapphireTest
|
|||||||
$sitetree->URLSegment = 'home';
|
$sitetree->URLSegment = 'home';
|
||||||
$this->assertFalse($sitetree->validURLSegment(), 'URLSegment conflicts are recognised');
|
$this->assertFalse($sitetree->validURLSegment(), 'URLSegment conflicts are recognised');
|
||||||
|
|
||||||
$sitetree->ParentID = $this->idFromFixture('Page', 'about');
|
$sitetree->ParentID = $this->idFromFixture(SiteTree::class, 'about');
|
||||||
$this->assertTrue($sitetree->validURLSegment(), 'URLSegments can be the same across levels');
|
$this->assertTrue($sitetree->validURLSegment(), 'URLSegments can be the same across levels');
|
||||||
|
|
||||||
$sitetree->URLSegment = 'my-staff';
|
$sitetree->URLSegment = 'my-staff';
|
||||||
@ -1137,13 +1185,18 @@ class SiteTreeTest extends SapphireTest
|
|||||||
$sitetree->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
$sitetree->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
||||||
$sitetree = DataObject::get_by_id(SiteTree::class, $sitetree->ID, false);
|
$sitetree = DataObject::get_by_id(SiteTree::class, $sitetree->ID, false);
|
||||||
$this->assertEquals($sitetree->URLSegment, rawurlencode('brötchen'));
|
$this->assertEquals($sitetree->URLSegment, rawurlencode('brötchen'));
|
||||||
$sitetreeLive = Versioned::get_one_by_stage(SiteTree::class, Versioned::LIVE, '"SiteTree"."ID" = ' .$sitetree->ID, false);
|
$sitetreeLive = Versioned::get_one_by_stage(
|
||||||
|
SiteTree::class,
|
||||||
|
Versioned::LIVE,
|
||||||
|
'"SiteTree"."ID" = ' . $sitetree->ID,
|
||||||
|
false
|
||||||
|
);
|
||||||
$this->assertEquals($sitetreeLive->URLSegment, rawurlencode('brötchen'));
|
$this->assertEquals($sitetreeLive->URLSegment, rawurlencode('brötchen'));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testVersionsAreCreated()
|
public function testVersionsAreCreated()
|
||||||
{
|
{
|
||||||
$p = new Page();
|
$p = new SiteTree();
|
||||||
$p->Content = "one";
|
$p->Content = "one";
|
||||||
$p->write();
|
$p->write();
|
||||||
$this->assertEquals(1, $p->Version);
|
$this->assertEquals(1, $p->Version);
|
||||||
@ -1168,6 +1221,13 @@ class SiteTreeTest extends SapphireTest
|
|||||||
$this->assertEquals(3, $p->Version);
|
$this->assertEquals(3, $p->Version);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testHidePagetypes()
|
||||||
|
{
|
||||||
|
SiteTree::config()->set('hide_pagetypes', ['Page']);
|
||||||
|
$classes = SiteTree::page_type_classes();
|
||||||
|
$this->assertNotContains('Page', $classes);
|
||||||
|
}
|
||||||
|
|
||||||
public function testPageTypeClasses()
|
public function testPageTypeClasses()
|
||||||
{
|
{
|
||||||
$classes = SiteTree::page_type_classes();
|
$classes = SiteTree::page_type_classes();
|
||||||
@ -1182,6 +1242,11 @@ class SiteTreeTest extends SapphireTest
|
|||||||
$newClasses,
|
$newClasses,
|
||||||
'Setting hide_ancestor to a boolean (incorrect) value caused a page class to be hidden'
|
'Setting hide_ancestor to a boolean (incorrect) value caused a page class to be hidden'
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Testing what happens if a valid config value is set
|
||||||
|
Config::modify()->set(SiteTreeTest_ClassA::class, 'hide_ancestor', 'Page');
|
||||||
|
$classes = SiteTree::page_type_classes();
|
||||||
|
$this->assertNotContains('Page', $classes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1231,23 +1296,23 @@ class SiteTreeTest extends SapphireTest
|
|||||||
// Expected
|
// Expected
|
||||||
[ SiteTreeTest_ClassB::class ],
|
[ SiteTreeTest_ClassB::class ],
|
||||||
// Assertion message
|
// Assertion message
|
||||||
'Direct setting of allowed children'
|
'Direct setting of allowed children',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
SiteTreeTest_ClassB::class,
|
SiteTreeTest_ClassB::class,
|
||||||
[ SiteTreeTest_ClassC::class, SiteTreeTest_ClassCext::class ],
|
[ SiteTreeTest_ClassC::class, SiteTreeTest_ClassCext::class ],
|
||||||
'Includes subclasses'
|
'Includes subclasses',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
SiteTreeTest_ClassC::class,
|
SiteTreeTest_ClassC::class,
|
||||||
[],
|
[],
|
||||||
'Null setting'
|
'Null setting',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
SiteTreeTest_ClassD::class,
|
SiteTreeTest_ClassD::class,
|
||||||
[SiteTreeTest_ClassC::class],
|
[SiteTreeTest_ClassC::class],
|
||||||
'Excludes subclasses if class is prefixed by an asterisk'
|
'Excludes subclasses if class is prefixed by an asterisk',
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1288,7 +1353,10 @@ class SiteTreeTest extends SapphireTest
|
|||||||
|
|
||||||
$classCext->ParentID = $classD->ID;
|
$classCext->ParentID = $classD->ID;
|
||||||
$valid = $classCext->validate();
|
$valid = $classCext->validate();
|
||||||
$this->assertFalse($valid->isValid(), "Doesnt allow child where only parent class is allowed on parent node, and asterisk prefixing is used");
|
$this->assertFalse(
|
||||||
|
$valid->isValid(),
|
||||||
|
"Doesnt allow child where only parent class is allowed on parent node, and asterisk prefixing is used"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testClassDropdown()
|
public function testClassDropdown()
|
||||||
@ -1307,8 +1375,8 @@ class SiteTreeTest extends SapphireTest
|
|||||||
$this->assertArrayHasKey(SiteTreeTest_ClassA::class, $method->invoke($sitetree));
|
$this->assertArrayHasKey(SiteTreeTest_ClassA::class, $method->invoke($sitetree));
|
||||||
|
|
||||||
$this->logInWithPermission('ADMIN');
|
$this->logInWithPermission('ADMIN');
|
||||||
$rootPage = $this->objFromFixture(Page::class, 'home');
|
$rootPage = $this->objFromFixture(SiteTree::class, 'home');
|
||||||
$nonRootPage = $this->objFromFixture(Page::class, 'staff');
|
$nonRootPage = $this->objFromFixture(SiteTree::class, 'staff');
|
||||||
|
|
||||||
$this->assertArrayNotHasKey(SiteTreeTest_NotRoot::class, $method->invoke($rootPage));
|
$this->assertArrayNotHasKey(SiteTreeTest_NotRoot::class, $method->invoke($rootPage));
|
||||||
$this->assertArrayHasKey(SiteTreeTest_NotRoot::class, $method->invoke($nonRootPage));
|
$this->assertArrayHasKey(SiteTreeTest_NotRoot::class, $method->invoke($nonRootPage));
|
||||||
@ -1404,11 +1472,11 @@ class SiteTreeTest extends SapphireTest
|
|||||||
|
|
||||||
public function testGetBreadcrumbItems()
|
public function testGetBreadcrumbItems()
|
||||||
{
|
{
|
||||||
$page = $this->objFromFixture("Page", "breadcrumbs");
|
$page = $this->objFromFixture(SiteTree::class, "breadcrumbs");
|
||||||
$this->assertEquals(1, $page->getBreadcrumbItems()->count(), "Only display current page.");
|
$this->assertEquals(1, $page->getBreadcrumbItems()->count(), "Only display current page.");
|
||||||
|
|
||||||
// Test breadcrumb order
|
// Test breadcrumb order
|
||||||
$page = $this->objFromFixture("Page", "breadcrumbs5");
|
$page = $this->objFromFixture(SiteTree::class, "breadcrumbs5");
|
||||||
$breadcrumbs = $page->getBreadcrumbItems();
|
$breadcrumbs = $page->getBreadcrumbItems();
|
||||||
$this->assertEquals($breadcrumbs->count(), 5, "Display all breadcrumbs");
|
$this->assertEquals($breadcrumbs->count(), 5, "Display all breadcrumbs");
|
||||||
$this->assertEquals($breadcrumbs->first()->Title, "Breadcrumbs", "Breadcrumbs should be the first item.");
|
$this->assertEquals($breadcrumbs->first()->Title, "Breadcrumbs", "Breadcrumbs should be the first item.");
|
||||||
@ -1428,16 +1496,16 @@ class SiteTreeTest extends SapphireTest
|
|||||||
public function testMetaTags()
|
public function testMetaTags()
|
||||||
{
|
{
|
||||||
$this->logInWithPermission('ADMIN');
|
$this->logInWithPermission('ADMIN');
|
||||||
$page = $this->objFromFixture('Page', 'metapage');
|
$page = $this->objFromFixture(SiteTree::class, 'metapage');
|
||||||
|
|
||||||
// Test with title
|
// Test with title
|
||||||
$meta = $page->MetaTags();
|
$meta = $page->MetaTags();
|
||||||
$charset = Config::inst()->get(ContentNegotiator::class, 'encoding');
|
$charset = Config::inst()->get(ContentNegotiator::class, 'encoding');
|
||||||
$this->assertStringContainsString('<meta http-equiv="Content-Type" content="text/html; charset='.$charset.'"', $meta);
|
$this->assertStringContainsString('<meta http-equiv="Content-Type" content="text/html; charset=' . $charset . '"', $meta);
|
||||||
$this->assertStringContainsString('<meta name="description" content="The <br /> and <br> tags"', $meta);
|
$this->assertStringContainsString('<meta name="description" content="The <br /> and <br> tags"', $meta);
|
||||||
$this->assertStringContainsString('<link rel="canonical" href="http://www.mysite.com/html-and-xml"', $meta);
|
$this->assertStringContainsString('<link rel="canonical" href="http://www.mysite.com/html-and-xml"', $meta);
|
||||||
$this->assertStringContainsString('<meta name="x-page-id" content="'.$page->ID.'"', $meta);
|
$this->assertStringContainsString('<meta name="x-page-id" content="' . $page->ID.'"', $meta);
|
||||||
$this->assertStringContainsString('<meta name="x-cms-edit-link" content="'.$page->CMSEditLink().'"', $meta);
|
$this->assertStringContainsString('<meta name="x-cms-edit-link" content="' . $page->CMSEditLink().'"', $meta);
|
||||||
$this->assertStringContainsString('<title>HTML & XML</title>', $meta);
|
$this->assertStringContainsString('<title>HTML & XML</title>', $meta);
|
||||||
|
|
||||||
// Test without title
|
// Test without title
|
||||||
@ -1452,7 +1520,7 @@ class SiteTreeTest extends SapphireTest
|
|||||||
{
|
{
|
||||||
$this->logInWithPermission('ADMIN');
|
$this->logInWithPermission('ADMIN');
|
||||||
/** @var SiteTree $page */
|
/** @var SiteTree $page */
|
||||||
$page = $this->objFromFixture('Page', 'metapage');
|
$page = $this->objFromFixture(SiteTree::class, 'metapage');
|
||||||
|
|
||||||
$charset = Config::inst()->get(ContentNegotiator::class, 'encoding');
|
$charset = Config::inst()->get(ContentNegotiator::class, 'encoding');
|
||||||
|
|
||||||
@ -1534,8 +1602,8 @@ class SiteTreeTest extends SapphireTest
|
|||||||
|
|
||||||
// both pages are viewable in stage
|
// both pages are viewable in stage
|
||||||
Versioned::set_stage(Versioned::DRAFT);
|
Versioned::set_stage(Versioned::DRAFT);
|
||||||
$about = $this->objFromFixture('Page', 'about');
|
$about = $this->objFromFixture(SiteTree::class, 'about');
|
||||||
$staff = $this->objFromFixture('Page', 'staff');
|
$staff = $this->objFromFixture(SiteTree::class, 'staff');
|
||||||
$this->assertFalse($about->isOrphaned());
|
$this->assertFalse($about->isOrphaned());
|
||||||
$this->assertFalse($staff->isOrphaned());
|
$this->assertFalse($staff->isOrphaned());
|
||||||
$this->assertTrue($about->canView($member));
|
$this->assertTrue($about->canView($member));
|
||||||
@ -1546,23 +1614,23 @@ class SiteTreeTest extends SapphireTest
|
|||||||
$this->assertFalse($staff->isOrphaned());
|
$this->assertFalse($staff->isOrphaned());
|
||||||
$this->assertTrue($staff->canView($member));
|
$this->assertTrue($staff->canView($member));
|
||||||
Versioned::set_stage(Versioned::LIVE);
|
Versioned::set_stage(Versioned::LIVE);
|
||||||
$staff = $this->objFromFixture('Page', 'staff'); // Live copy of page
|
$staff = $this->objFromFixture(SiteTree::class, 'staff'); // Live copy of page
|
||||||
$this->assertTrue($staff->isOrphaned()); // because parent isn't published
|
$this->assertTrue($staff->isOrphaned()); // because parent isn't published
|
||||||
$this->assertFalse($staff->canView($member));
|
$this->assertFalse($staff->canView($member));
|
||||||
|
|
||||||
// Publishing the parent page should restore visibility
|
// Publishing the parent page should restore visibility
|
||||||
Versioned::set_stage(Versioned::DRAFT);
|
Versioned::set_stage(Versioned::DRAFT);
|
||||||
$about = $this->objFromFixture('Page', 'about');
|
$about = $this->objFromFixture(SiteTree::class, 'about');
|
||||||
$about->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
$about->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
||||||
Versioned::set_stage(Versioned::LIVE);
|
Versioned::set_stage(Versioned::LIVE);
|
||||||
$staff = $this->objFromFixture('Page', 'staff');
|
$staff = $this->objFromFixture(SiteTree::class, 'staff');
|
||||||
$this->assertFalse($staff->isOrphaned());
|
$this->assertFalse($staff->isOrphaned());
|
||||||
$this->assertTrue($staff->canView($member));
|
$this->assertTrue($staff->canView($member));
|
||||||
|
|
||||||
// Removing staging page should not prevent live page being visible
|
// Removing staging page should not prevent live page being visible
|
||||||
$about->deleteFromStage('Stage');
|
$about->deleteFromStage('Stage');
|
||||||
$staff->deleteFromStage('Stage');
|
$staff->deleteFromStage('Stage');
|
||||||
$staff = $this->objFromFixture('Page', 'staff');
|
$staff = $this->objFromFixture(SiteTree::class, 'staff');
|
||||||
$this->assertFalse($staff->isOrphaned());
|
$this->assertFalse($staff->isOrphaned());
|
||||||
$this->assertTrue($staff->canView($member));
|
$this->assertTrue($staff->canView($member));
|
||||||
|
|
||||||
@ -1577,8 +1645,8 @@ class SiteTreeTest extends SapphireTest
|
|||||||
{
|
{
|
||||||
$this->logInWithPermission('ADMIN');
|
$this->logInWithPermission('ADMIN');
|
||||||
|
|
||||||
/** @var Page $page */
|
/** @var SiteTree $page */
|
||||||
$page = $this->objFromFixture('Page', 'home');
|
$page = $this->objFromFixture(SiteTree::class, 'home');
|
||||||
$this->assertTrue($page->canAddChildren());
|
$this->assertTrue($page->canAddChildren());
|
||||||
$this->assertTrue($page->isOnDraft());
|
$this->assertTrue($page->isOnDraft());
|
||||||
$this->assertFalse($page->isPublished());
|
$this->assertFalse($page->isPublished());
|
||||||
@ -1655,23 +1723,24 @@ class SiteTreeTest extends SapphireTest
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test that the controller name for a SiteTree instance can be gathered by appending "Controller" to the SiteTree
|
* Test that the controller name for a Page instance can be gathered by appending "Controller" to the Page
|
||||||
* class name in a PSR-2 compliant manner.
|
* class name in a PSR-2 compliant manner.
|
||||||
*/
|
*/
|
||||||
public function testGetControllerName()
|
public function testGetControllerName()
|
||||||
{
|
{
|
||||||
$class = new Page;
|
$page = new Page();
|
||||||
$this->assertSame('PageController', $class->getControllerName());
|
$this->assertSame(PageController::class, $page->getControllerName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test that the controller name for a SiteTree instance can be gathered when set directly via config var
|
* Test that the controller name for a SiteTree instance can be gathered when set directly via config var
|
||||||
*/
|
*/
|
||||||
public function testGetControllerNameFromConfig()
|
public function testGetControllerNameFromConfig()
|
||||||
{
|
{
|
||||||
Config::inst()->set(Page::class, 'controller_name', 'This\\Is\\A\\New\\Controller');
|
Config::inst()->set(SiteTree::class, 'controller_name', 'This\\Is\\A\\New\\Controller');
|
||||||
$class = new Page;
|
$page = new SiteTree();
|
||||||
$this->assertSame('This\\Is\\A\\New\\Controller', $class->getControllerName());
|
$this->assertSame('This\\Is\\A\\New\\Controller', $page->getControllerName());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1687,21 +1756,9 @@ class SiteTreeTest extends SapphireTest
|
|||||||
$this->assertSame(SiteTreeTest_NamespaceMapTestController::class, $namespacedSiteTree->getControllerName());
|
$this->assertSame(SiteTreeTest_NamespaceMapTestController::class, $namespacedSiteTree->getControllerName());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Test that underscored class names (legacy) are still supported (deprecation notice is issued though).
|
|
||||||
*/
|
|
||||||
public function testGetControllerNameWithUnderscoresIsSupported()
|
|
||||||
{
|
|
||||||
if (Deprecation::isEnabled()) {
|
|
||||||
$this->markTestSkipped('Test calls deprecated code');
|
|
||||||
}
|
|
||||||
$class = new SiteTreeTest_LegacyControllerName;
|
|
||||||
$this->assertEquals(SiteTreeTest_LegacyControllerName_Controller::class, $class->getControllerName());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testTreeTitleCache()
|
public function testTreeTitleCache()
|
||||||
{
|
{
|
||||||
$siteTree = SiteTree::create();
|
$siteTree = new SiteTree();
|
||||||
$user = $this->objFromFixture(Member::class, 'allsections');
|
$user = $this->objFromFixture(Member::class, 'allsections');
|
||||||
Security::setCurrentUser($user);
|
Security::setCurrentUser($user);
|
||||||
$pageClass = array_values(SiteTree::page_type_classes())[0];
|
$pageClass = array_values(SiteTree::page_type_classes())[0];
|
||||||
@ -1788,7 +1845,7 @@ class SiteTreeTest extends SapphireTest
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create new page on DRAFT
|
// Create new page on DRAFT
|
||||||
$page = SiteTree::create();
|
$page = new SiteTree();
|
||||||
$page->Content = $content;
|
$page->Content = $content;
|
||||||
$page->write();
|
$page->write();
|
||||||
|
|
||||||
@ -1804,7 +1861,7 @@ class SiteTreeTest extends SapphireTest
|
|||||||
public function testGetCMSActions()
|
public function testGetCMSActions()
|
||||||
{
|
{
|
||||||
// Create new page on DRAFT
|
// Create new page on DRAFT
|
||||||
$page = SiteTree::create();
|
$page = new SiteTree();
|
||||||
$page->Content = md5(rand(0, PHP_INT_MAX));
|
$page->Content = md5(rand(0, PHP_INT_MAX));
|
||||||
$page->write();
|
$page->write();
|
||||||
|
|
||||||
@ -1934,7 +1991,7 @@ class SiteTreeTest extends SapphireTest
|
|||||||
public function testGetCMSActionsWithoutForms()
|
public function testGetCMSActionsWithoutForms()
|
||||||
{
|
{
|
||||||
// Create new page on DRAFT
|
// Create new page on DRAFT
|
||||||
$page = SiteTree::create();
|
$page = new SiteTree();
|
||||||
$page->Content = md5(rand(0, PHP_INT_MAX));
|
$page->Content = md5(rand(0, PHP_INT_MAX));
|
||||||
$page->write();
|
$page->write();
|
||||||
|
|
||||||
@ -2040,21 +2097,11 @@ class SiteTreeTest extends SapphireTest
|
|||||||
*/
|
*/
|
||||||
public function testSanitiseExtraMeta(string $extraMeta, string $expected, string $message): void
|
public function testSanitiseExtraMeta(string $extraMeta, string $expected, string $message): void
|
||||||
{
|
{
|
||||||
// If using HTML5Value then the 'somethingdodgy' test won't be converted to valid html
|
|
||||||
// However if using the default HTMLValue, then it will be converted to valid html
|
|
||||||
$isDodgyAndUsingHTML5 = strpos($expected, 'somethingdodgy') !== false &&
|
|
||||||
(HTMLValue::create() instanceof HTML5Value);
|
|
||||||
if ($isDodgyAndUsingHTML5) {
|
|
||||||
$this->expectException(ValidationException::class);
|
|
||||||
$this->expectExceptionMessage('Custom Meta Tags does not contain valid HTML');
|
|
||||||
}
|
|
||||||
$siteTree = new SiteTree();
|
$siteTree = new SiteTree();
|
||||||
$siteTree->ExtraMeta = $extraMeta;
|
$siteTree->ExtraMeta = $extraMeta;
|
||||||
$siteTree->write();
|
$siteTree->write();
|
||||||
if (!$isDodgyAndUsingHTML5) {
|
|
||||||
$this->assertSame($expected, $siteTree->ExtraMeta, $message);
|
$this->assertSame($expected, $siteTree->ExtraMeta, $message);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public function provideSanitiseExtraMeta(): array
|
public function provideSanitiseExtraMeta(): array
|
||||||
{
|
{
|
||||||
@ -2083,50 +2130,12 @@ class SiteTreeTest extends SapphireTest
|
|||||||
'<link rel="canonical" accesskey="X" onclick="alert(1)" name="x" />',
|
'<link rel="canonical" accesskey="X" onclick="alert(1)" name="x" />',
|
||||||
'<link rel="canonical" name="x">',
|
'<link rel="canonical" name="x">',
|
||||||
'Multiple attributes are removed'
|
'Multiple attributes are removed'
|
||||||
]
|
],
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider provideSanatiseInvalidExtraMeta
|
|
||||||
*/
|
|
||||||
public function testSanatiseInvalidExtraMetaHTML4Value(string $extraMeta, string $expected): void
|
|
||||||
{
|
|
||||||
Injector::inst()->registerService(HTML4Value::create(), HTMLValue::class);
|
|
||||||
$siteTree = new SiteTree();
|
|
||||||
$siteTree->ExtraMeta = $extraMeta;
|
|
||||||
$siteTree->write();
|
|
||||||
$this->assertSame(
|
|
||||||
$expected,
|
|
||||||
$siteTree->ExtraMeta,
|
|
||||||
'Invalid HTML is converted to valid HTML and parsed'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @dataProvider provideSanatiseInvalidExtraMeta
|
|
||||||
*/
|
|
||||||
public function testSanatiseInvalidExtraMetaHTML5Value(string $extraMeta): void
|
|
||||||
{
|
|
||||||
// HTML5Value comes from the module silverstripe/html5
|
|
||||||
if (!class_exists(HTML5Value::class)) {
|
|
||||||
$this->markTestSkipped('HTML5Value class does not exist');
|
|
||||||
}
|
|
||||||
Injector::inst()->registerService(HTML5Value::create(), HTMLValue::class);
|
|
||||||
$this->expectException(ValidationException::class);
|
|
||||||
$this->expectExceptionMessage('Custom Meta Tags does not contain valid HTML');
|
|
||||||
$siteTree = new SiteTree();
|
|
||||||
$siteTree->ExtraMeta = $extraMeta;
|
|
||||||
$siteTree->write();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function provideSanatiseInvalidExtraMeta(): array
|
|
||||||
{
|
|
||||||
return [
|
|
||||||
[
|
[
|
||||||
'<link rel="canonical" href="valid" ;;// somethingdodgy < onmouseover=alert(1)',
|
'<link rel="canonical" href="valid" ;;// somethingdodgy <onmouseover=alert(1)>',
|
||||||
'<link rel="canonical" href="valid" somethingdodgy="">'
|
'<link rel="canonical" href="valid" somethingdodgy=""><onmouseover></onmouseover>',
|
||||||
]
|
'Invalid HTML is converted to valid HTML and parsed'
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
26
tests/php/Model/SiteTreeTest.yml
Executable file → Normal file
26
tests/php/Model/SiteTreeTest.yml
Executable file → Normal file
@ -44,7 +44,7 @@ SilverStripe\Security\Member:
|
|||||||
securityadmin:
|
securityadmin:
|
||||||
Groups: =>SilverStripe\Security\Group.securityadmins
|
Groups: =>SilverStripe\Security\Group.securityadmins
|
||||||
|
|
||||||
Page:
|
SilverStripe\CMS\Model\SiteTree:
|
||||||
home:
|
home:
|
||||||
Title: Home
|
Title: Home
|
||||||
CanEditType: OnlyTheseUsers
|
CanEditType: OnlyTheseUsers
|
||||||
@ -56,14 +56,14 @@ Page:
|
|||||||
staff:
|
staff:
|
||||||
Title: Staff
|
Title: Staff
|
||||||
URLSegment: my-staff
|
URLSegment: my-staff
|
||||||
Parent: =>Page.about
|
Parent: =>SilverStripe\CMS\Model\SiteTree.about
|
||||||
ceo:
|
ceo:
|
||||||
Title: CEO
|
Title: CEO
|
||||||
Parent: =>Page.staff
|
Parent: =>SilverStripe\CMS\Model\SiteTree.staff
|
||||||
staffduplicate:
|
staffduplicate:
|
||||||
Title: Staff
|
Title: Staff
|
||||||
URLSegment: my-staff
|
URLSegment: my-staff
|
||||||
Parent: =>Page.about
|
Parent: =>SilverStripe\CMS\Model\SiteTree.about
|
||||||
ShowInMenus: 0
|
ShowInMenus: 0
|
||||||
products:
|
products:
|
||||||
Title: Products
|
Title: Products
|
||||||
@ -71,19 +71,19 @@ Page:
|
|||||||
EditorGroups: =>SilverStripe\Security\Group.editors
|
EditorGroups: =>SilverStripe\Security\Group.editors
|
||||||
product1:
|
product1:
|
||||||
Title: 1.1 Test Product
|
Title: 1.1 Test Product
|
||||||
Parent: =>Page.products
|
Parent: =>SilverStripe\CMS\Model\SiteTree.products
|
||||||
CanEditType: Inherit
|
CanEditType: Inherit
|
||||||
product2:
|
product2:
|
||||||
Title: Another Product
|
Title: Another Product
|
||||||
Parent: =>Page.products
|
Parent: =>SilverStripe\CMS\Model\SiteTree.products
|
||||||
CanEditType: Inherit
|
CanEditType: Inherit
|
||||||
product3:
|
product3:
|
||||||
Title: Another Product
|
Title: Another Product
|
||||||
Parent: =>Page.products
|
Parent: =>SilverStripe\CMS\Model\SiteTree.products
|
||||||
CanEditType: Inherit
|
CanEditType: Inherit
|
||||||
product4:
|
product4:
|
||||||
Title: Another Product
|
Title: Another Product
|
||||||
Parent: =>Page.products
|
Parent: =>SilverStripe\CMS\Model\SiteTree.products
|
||||||
CanEditType: OnlyTheseUsers
|
CanEditType: OnlyTheseUsers
|
||||||
EditorGroups: =>SilverStripe\Security\Group.admins
|
EditorGroups: =>SilverStripe\Security\Group.admins
|
||||||
contact:
|
contact:
|
||||||
@ -102,16 +102,16 @@ Page:
|
|||||||
Title: 'Breadcrumbs'
|
Title: 'Breadcrumbs'
|
||||||
breadcrumbs2:
|
breadcrumbs2:
|
||||||
Title: 'Breadcrumbs 2'
|
Title: 'Breadcrumbs 2'
|
||||||
Parent: =>Page.breadcrumbs
|
Parent: =>SilverStripe\CMS\Model\SiteTree.breadcrumbs
|
||||||
breadcrumbs3:
|
breadcrumbs3:
|
||||||
Title: 'Breadcrumbs 3'
|
Title: 'Breadcrumbs 3'
|
||||||
Parent: =>Page.breadcrumbs2
|
Parent: =>SilverStripe\CMS\Model\SiteTree.breadcrumbs2
|
||||||
breadcrumbs4:
|
breadcrumbs4:
|
||||||
Title: 'Breadcrumbs 4'
|
Title: 'Breadcrumbs 4'
|
||||||
Parent: =>Page.breadcrumbs3
|
Parent: =>SilverStripe\CMS\Model\SiteTree.breadcrumbs3
|
||||||
breadcrumbs5:
|
breadcrumbs5:
|
||||||
Title: 'Breadcrumbs 5'
|
Title: 'Breadcrumbs 5'
|
||||||
Parent: =>Page.breadcrumbs4
|
Parent: =>SilverStripe\CMS\Model\SiteTree.breadcrumbs4
|
||||||
numeric0:
|
numeric0:
|
||||||
Title: 'urlsegment is 0'
|
Title: 'urlsegment is 0'
|
||||||
URLSegment: '0'
|
URLSegment: '0'
|
||||||
@ -130,7 +130,7 @@ SilverStripe\CMS\Model\RedirectorPage:
|
|||||||
SilverStripe\CMS\Tests\Model\SiteTreeTest_DataObject:
|
SilverStripe\CMS\Tests\Model\SiteTreeTest_DataObject:
|
||||||
relations:
|
relations:
|
||||||
Title: 'Linked DataObject'
|
Title: 'Linked DataObject'
|
||||||
Pages: =>Page.home,=>Page.about,=>Page.staff
|
Pages: =>SilverStripe\CMS\Model\SiteTree.home,=>SilverStripe\CMS\Model\SiteTree.about,=>SilverStripe\CMS\Model\SiteTree.staff
|
||||||
|
|
||||||
SilverStripe\CMS\Tests\Model\SiteTreeBrokenLinksTest\NotPageObject:
|
SilverStripe\CMS\Tests\Model\SiteTreeBrokenLinksTest\NotPageObject:
|
||||||
object1:
|
object1:
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
namespace SilverStripe\CMS\Tests\Model;
|
namespace SilverStripe\CMS\Tests\Model;
|
||||||
|
|
||||||
use SilverStripe\Dev\TestOnly;
|
use SilverStripe\Dev\TestOnly;
|
||||||
use Page;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
|
||||||
class SiteTreeTest_AdminDenied extends Page implements TestOnly
|
class SiteTreeTest_AdminDenied extends SiteTree implements TestOnly
|
||||||
{
|
{
|
||||||
private static $table_name = 'SiteTreeTest_AdminDenied';
|
private static $table_name = 'SiteTreeTest_AdminDenied';
|
||||||
|
|
||||||
|
@ -3,18 +3,18 @@
|
|||||||
namespace SilverStripe\CMS\Tests\Model;
|
namespace SilverStripe\CMS\Tests\Model;
|
||||||
|
|
||||||
use SilverStripe\Dev\TestOnly;
|
use SilverStripe\Dev\TestOnly;
|
||||||
use Page;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
|
||||||
class SiteTreeTest_ClassA extends Page implements TestOnly
|
class SiteTreeTest_ClassA extends SiteTree implements TestOnly
|
||||||
{
|
{
|
||||||
private static $table_name = 'SiteTreeTest_ClassA';
|
private static $table_name = 'SiteTreeTest_ClassA';
|
||||||
|
|
||||||
private static $need_permission = [
|
private static $need_permission = [
|
||||||
'ADMIN',
|
'ADMIN',
|
||||||
'CMS_ACCESS_CMSMain'
|
'CMS_ACCESS_CMSMain',
|
||||||
];
|
];
|
||||||
|
|
||||||
private static $allowed_children = [
|
private static $allowed_children = [
|
||||||
SiteTreeTest_ClassB::class
|
SiteTreeTest_ClassB::class,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -3,14 +3,14 @@
|
|||||||
namespace SilverStripe\CMS\Tests\Model;
|
namespace SilverStripe\CMS\Tests\Model;
|
||||||
|
|
||||||
use SilverStripe\Dev\TestOnly;
|
use SilverStripe\Dev\TestOnly;
|
||||||
use Page;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
|
||||||
class SiteTreeTest_ClassB extends Page implements TestOnly
|
class SiteTreeTest_ClassB extends SiteTree implements TestOnly
|
||||||
{
|
{
|
||||||
private static $table_name = 'SiteTreeTest_ClassB';
|
private static $table_name = 'SiteTreeTest_ClassB';
|
||||||
|
|
||||||
// Also allowed subclasses
|
// Also allowed subclasses
|
||||||
private static $allowed_children = [
|
private static $allowed_children = [
|
||||||
SiteTreeTest_ClassC::class
|
SiteTreeTest_ClassC::class,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
namespace SilverStripe\CMS\Tests\Model;
|
namespace SilverStripe\CMS\Tests\Model;
|
||||||
|
|
||||||
use SilverStripe\Dev\TestOnly;
|
use SilverStripe\Dev\TestOnly;
|
||||||
use Page;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
|
||||||
class SiteTreeTest_ClassC extends Page implements TestOnly
|
class SiteTreeTest_ClassC extends SiteTree implements TestOnly
|
||||||
{
|
{
|
||||||
private static $table_name = 'SiteTreeTest_ClassC';
|
private static $table_name = 'SiteTreeTest_ClassC';
|
||||||
|
|
||||||
|
@ -10,6 +10,6 @@ class SiteTreeTest_ClassCext extends SiteTreeTest_ClassC implements TestOnly
|
|||||||
|
|
||||||
// Override SiteTreeTest_ClassC definitions
|
// Override SiteTreeTest_ClassC definitions
|
||||||
private static $allowed_children = [
|
private static $allowed_children = [
|
||||||
SiteTreeTest_ClassB::class
|
SiteTreeTest_ClassB::class,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
namespace SilverStripe\CMS\Tests\Model;
|
namespace SilverStripe\CMS\Tests\Model;
|
||||||
|
|
||||||
use SilverStripe\Dev\TestOnly;
|
use SilverStripe\Dev\TestOnly;
|
||||||
use Page;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
|
||||||
class SiteTreeTest_ClassD extends Page implements TestOnly
|
class SiteTreeTest_ClassD extends SiteTree implements TestOnly
|
||||||
{
|
{
|
||||||
private static $table_name = 'SiteTreeTest_ClassD';
|
private static $table_name = 'SiteTreeTest_ClassD';
|
||||||
|
|
||||||
|
@ -4,9 +4,9 @@ namespace SilverStripe\CMS\Tests\Model;
|
|||||||
|
|
||||||
use SilverStripe\Dev\TestOnly;
|
use SilverStripe\Dev\TestOnly;
|
||||||
use SilverStripe\ORM\HiddenClass;
|
use SilverStripe\ORM\HiddenClass;
|
||||||
use Page;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
|
||||||
class SiteTreeTest_ClassE extends Page implements TestOnly, HiddenClass
|
class SiteTreeTest_ClassE extends SiteTree implements TestOnly, HiddenClass
|
||||||
{
|
{
|
||||||
private static $table_name = 'SiteTreeTest_ClassE';
|
private static $table_name = 'SiteTreeTest_ClassE';
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
namespace SilverStripe\CMS\Tests\Model;
|
namespace SilverStripe\CMS\Tests\Model;
|
||||||
|
|
||||||
use SilverStripe\Dev\TestOnly;
|
use SilverStripe\Dev\TestOnly;
|
||||||
use Page;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
|
||||||
class SiteTreeTest_Conflicted extends Page implements TestOnly
|
class SiteTreeTest_Conflicted extends SiteTree implements TestOnly
|
||||||
{
|
{
|
||||||
private static $table_name = 'SiteTreeTest_Conflicted';
|
private static $table_name = 'SiteTreeTest_Conflicted';
|
||||||
}
|
}
|
||||||
|
@ -3,12 +3,12 @@
|
|||||||
namespace SilverStripe\CMS\Tests\Model;
|
namespace SilverStripe\CMS\Tests\Model;
|
||||||
|
|
||||||
use SilverStripe\Dev\TestOnly;
|
use SilverStripe\Dev\TestOnly;
|
||||||
use PageController;
|
use SilverStripe\CMS\Controllers\ContentController;
|
||||||
|
|
||||||
class SiteTreeTest_ConflictedController extends PageController implements TestOnly
|
class SiteTreeTest_ConflictedController extends ContentController implements TestOnly
|
||||||
{
|
{
|
||||||
private static $allowed_actions = [
|
private static $allowed_actions = [
|
||||||
'conflicted-action'
|
'conflicted-action',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function hasActionTemplate($template)
|
public function hasActionTemplate($template)
|
||||||
|
@ -3,12 +3,12 @@
|
|||||||
namespace SilverStripe\CMS\Tests\Model;
|
namespace SilverStripe\CMS\Tests\Model;
|
||||||
|
|
||||||
use SilverStripe\Dev\TestOnly;
|
use SilverStripe\Dev\TestOnly;
|
||||||
use Page;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An empty SiteTree instance with a controller to test that legacy controller names can still be loaded
|
* An empty SiteTree instance with a controller to test that legacy controller names can still be loaded
|
||||||
*/
|
*/
|
||||||
class SiteTreeTest_LegacyControllerName extends Page implements TestOnly
|
class SiteTreeTest_LegacyControllerName extends SiteTree implements TestOnly
|
||||||
{
|
{
|
||||||
private static $table_name = 'SiteTreeTest_LegacyControllerName';
|
private static $table_name = 'SiteTreeTest_LegacyControllerName';
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,8 @@
|
|||||||
namespace SilverStripe\CMS\Tests\Model;
|
namespace SilverStripe\CMS\Tests\Model;
|
||||||
|
|
||||||
use SilverStripe\Dev\TestOnly;
|
use SilverStripe\Dev\TestOnly;
|
||||||
use PageController;
|
use SilverStripe\CMS\Controllers\ContentController;
|
||||||
|
|
||||||
class SiteTreeTest_LegacyControllerName_Controller extends PageController implements TestOnly
|
class SiteTreeTest_LegacyControllerName_Controller extends ContentController implements TestOnly
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
namespace SilverStripe\CMS\Tests\Model;
|
namespace SilverStripe\CMS\Tests\Model;
|
||||||
|
|
||||||
use SilverStripe\Dev\TestOnly;
|
use SilverStripe\Dev\TestOnly;
|
||||||
use Page;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
|
||||||
class SiteTreeTest_NotRoot extends Page implements TestOnly
|
class SiteTreeTest_NotRoot extends SiteTree implements TestOnly
|
||||||
{
|
{
|
||||||
private static $table_name = 'SiteTreeTest_NotRoot';
|
private static $table_name = 'SiteTreeTest_NotRoot';
|
||||||
|
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
namespace SilverStripe\CMS\Tests\Model;
|
namespace SilverStripe\CMS\Tests\Model;
|
||||||
|
|
||||||
use SilverStripe\Dev\TestOnly;
|
use SilverStripe\Dev\TestOnly;
|
||||||
use Page;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
|
||||||
class SiteTreeTest_PageNode extends Page implements TestOnly
|
class SiteTreeTest_PageNode extends SiteTree implements TestOnly
|
||||||
{
|
{
|
||||||
private static $table_name = 'SiteTreeTest_PageNode';
|
private static $table_name = 'SiteTreeTest_PageNode';
|
||||||
}
|
}
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
namespace SilverStripe\CMS\Tests\Model;
|
namespace SilverStripe\CMS\Tests\Model;
|
||||||
|
|
||||||
use SilverStripe\Dev\TestOnly;
|
use SilverStripe\Dev\TestOnly;
|
||||||
use PageController;
|
use SilverStripe\CMS\Controllers\ContentController;
|
||||||
|
|
||||||
class SiteTreeTest_PageNodeController extends PageController implements TestOnly
|
class SiteTreeTest_PageNodeController extends ContentController implements TestOnly
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace SilverStripe\CMS\Tests\Model;
|
namespace SilverStripe\CMS\Tests\Model;
|
||||||
|
|
||||||
use Page;
|
|
||||||
use SilverStripe\CMS\Controllers\ModelAsController;
|
use SilverStripe\CMS\Controllers\ModelAsController;
|
||||||
use SilverStripe\CMS\Model\RedirectorPage;
|
use SilverStripe\CMS\Model\RedirectorPage;
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
@ -43,8 +42,8 @@ class VirtualPageTest extends FunctionalTest
|
|||||||
|
|
||||||
protected static $required_extensions = [
|
protected static $required_extensions = [
|
||||||
SiteTree::class => [
|
SiteTree::class => [
|
||||||
VirtualPageTest_PageExtension::class
|
VirtualPageTest_PageExtension::class,
|
||||||
]
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
protected function setUp(): void
|
protected function setUp(): void
|
||||||
@ -63,8 +62,8 @@ class VirtualPageTest extends FunctionalTest
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Ensure all pages are published
|
// Ensure all pages are published
|
||||||
/** @var Page $page */
|
/** @var SiteTree $page */
|
||||||
foreach (Page::get() as $page) {
|
foreach (SiteTree::get() as $page) {
|
||||||
$page->publishSingle();
|
$page->publishSingle();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -75,8 +74,8 @@ class VirtualPageTest extends FunctionalTest
|
|||||||
*/
|
*/
|
||||||
public function testEditingSourcePageUpdatesVirtualPages()
|
public function testEditingSourcePageUpdatesVirtualPages()
|
||||||
{
|
{
|
||||||
/** @var Page $master */
|
/** @var SiteTree $master */
|
||||||
$master = $this->objFromFixture('Page', 'master');
|
$master = $this->objFromFixture(SiteTree::class, 'master');
|
||||||
$master->Title = "New title";
|
$master->Title = "New title";
|
||||||
$master->MenuTitle = "New menutitle";
|
$master->MenuTitle = "New menutitle";
|
||||||
$master->Content = "<p>New content</p>";
|
$master->Content = "<p>New content</p>";
|
||||||
@ -97,7 +96,7 @@ class VirtualPageTest extends FunctionalTest
|
|||||||
public function testMetaTags()
|
public function testMetaTags()
|
||||||
{
|
{
|
||||||
$this->logInWithPermission('ADMIN');
|
$this->logInWithPermission('ADMIN');
|
||||||
$master = $this->objFromFixture('Page', 'master');
|
$master = $this->objFromFixture(SiteTree::class, 'master');
|
||||||
$vp1 = $this->objFromFixture(VirtualPage::class, 'vp1');
|
$vp1 = $this->objFromFixture(VirtualPage::class, 'vp1');
|
||||||
|
|
||||||
// Test with title
|
// Test with title
|
||||||
@ -118,8 +117,8 @@ class VirtualPageTest extends FunctionalTest
|
|||||||
{
|
{
|
||||||
$this->logInWithPermission('ADMIN');
|
$this->logInWithPermission('ADMIN');
|
||||||
|
|
||||||
/** @var Page $master */
|
/** @var SiteTree $master */
|
||||||
$master = $this->objFromFixture('Page', 'master');
|
$master = $this->objFromFixture(SiteTree::class, 'master');
|
||||||
$master->publishRecursive();
|
$master->publishRecursive();
|
||||||
|
|
||||||
$master->Title = "New title";
|
$master->Title = "New title";
|
||||||
@ -160,13 +159,13 @@ class VirtualPageTest extends FunctionalTest
|
|||||||
$vp = new VirtualPage();
|
$vp = new VirtualPage();
|
||||||
$vp->write();
|
$vp->write();
|
||||||
|
|
||||||
$vp->CopyContentFromID = $this->idFromFixture('Page', 'master');
|
$vp->CopyContentFromID = $this->idFromFixture(SiteTree::class, 'master');
|
||||||
$vp->write();
|
$vp->write();
|
||||||
|
|
||||||
$this->assertEquals("My Page", $vp->Title);
|
$this->assertEquals("My Page", $vp->Title);
|
||||||
$this->assertEquals("My Page Nav", $vp->MenuTitle);
|
$this->assertEquals("My Page Nav", $vp->MenuTitle);
|
||||||
|
|
||||||
$vp->CopyContentFromID = $this->idFromFixture('Page', 'master2');
|
$vp->CopyContentFromID = $this->idFromFixture(SiteTree::class, 'master2');
|
||||||
$vp->write();
|
$vp->write();
|
||||||
|
|
||||||
$this->assertEquals("My Other Page", $vp->Title);
|
$this->assertEquals("My Other Page", $vp->Title);
|
||||||
@ -180,7 +179,7 @@ class VirtualPageTest extends FunctionalTest
|
|||||||
*/
|
*/
|
||||||
public function testPublishingAVirtualPageCopiedPublishedContentNotDraftContent()
|
public function testPublishingAVirtualPageCopiedPublishedContentNotDraftContent()
|
||||||
{
|
{
|
||||||
$p = new Page();
|
$p = new SiteTree();
|
||||||
$p->Content = "published content";
|
$p->Content = "published content";
|
||||||
$p->write();
|
$p->write();
|
||||||
$p->publishRecursive();
|
$p->publishRecursive();
|
||||||
@ -220,7 +219,7 @@ class VirtualPageTest extends FunctionalTest
|
|||||||
public function testCantPublishVirtualPagesBeforeTheirSource()
|
public function testCantPublishVirtualPagesBeforeTheirSource()
|
||||||
{
|
{
|
||||||
// An unpublished source page
|
// An unpublished source page
|
||||||
$p = new Page();
|
$p = new SiteTree();
|
||||||
$p->Content = "test content";
|
$p->Content = "test content";
|
||||||
$p->write();
|
$p->write();
|
||||||
|
|
||||||
@ -241,7 +240,7 @@ class VirtualPageTest extends FunctionalTest
|
|||||||
|
|
||||||
public function testCanEdit()
|
public function testCanEdit()
|
||||||
{
|
{
|
||||||
$parentPage = $this->objFromFixture('Page', 'master3');
|
$parentPage = $this->objFromFixture(SiteTree::class, 'master3');
|
||||||
$virtualPage = $this->objFromFixture(VirtualPage::class, 'vp3');
|
$virtualPage = $this->objFromFixture(VirtualPage::class, 'vp3');
|
||||||
$bob = $this->objFromFixture(Member::class, 'bob');
|
$bob = $this->objFromFixture(Member::class, 'bob');
|
||||||
$andrew = $this->objFromFixture(Member::class, 'andrew');
|
$andrew = $this->objFromFixture(Member::class, 'andrew');
|
||||||
@ -259,8 +258,8 @@ class VirtualPageTest extends FunctionalTest
|
|||||||
|
|
||||||
public function testCanView()
|
public function testCanView()
|
||||||
{
|
{
|
||||||
/** @var Page $parentPage */
|
/** @var SiteTree $parentPage */
|
||||||
$parentPage = $this->objFromFixture('Page', 'master3');
|
$parentPage = $this->objFromFixture(SiteTree::class, 'master3');
|
||||||
$parentPage->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
$parentPage->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
|
||||||
|
|
||||||
/** @var VirtualPage $virtualPage */
|
/** @var VirtualPage $virtualPage */
|
||||||
@ -283,7 +282,7 @@ class VirtualPageTest extends FunctionalTest
|
|||||||
public function testVirtualPagesArentInappropriatelyPublished()
|
public function testVirtualPagesArentInappropriatelyPublished()
|
||||||
{
|
{
|
||||||
// Fixture
|
// Fixture
|
||||||
$p = new Page();
|
$p = new SiteTree();
|
||||||
$p->Content = "test content";
|
$p->Content = "test content";
|
||||||
$p->write();
|
$p->write();
|
||||||
$vp = new VirtualPage();
|
$vp = new VirtualPage();
|
||||||
@ -332,7 +331,7 @@ class VirtualPageTest extends FunctionalTest
|
|||||||
public function testUnpublishingSourcePageOfAVirtualPageAlsoUnpublishesVirtualPage()
|
public function testUnpublishingSourcePageOfAVirtualPageAlsoUnpublishesVirtualPage()
|
||||||
{
|
{
|
||||||
// Create page and virutal page
|
// Create page and virutal page
|
||||||
$p = new Page();
|
$p = new SiteTree();
|
||||||
$p->Title = "source";
|
$p->Title = "source";
|
||||||
$p->write();
|
$p->write();
|
||||||
$this->assertTrue($p->publishRecursive());
|
$this->assertTrue($p->publishRecursive());
|
||||||
@ -364,7 +363,7 @@ class VirtualPageTest extends FunctionalTest
|
|||||||
public function testDeletingFromLiveSourcePageOfAVirtualPageAlsoUnpublishesVirtualPage()
|
public function testDeletingFromLiveSourcePageOfAVirtualPageAlsoUnpublishesVirtualPage()
|
||||||
{
|
{
|
||||||
// Create page and virutal page
|
// Create page and virutal page
|
||||||
$p = new Page();
|
$p = new SiteTree();
|
||||||
$p->Title = "source";
|
$p->Title = "source";
|
||||||
$p->write();
|
$p->write();
|
||||||
$this->assertTrue($p->publishRecursive());
|
$this->assertTrue($p->publishRecursive());
|
||||||
@ -384,7 +383,7 @@ class VirtualPageTest extends FunctionalTest
|
|||||||
->byID($pID);
|
->byID($pID);
|
||||||
$this->assertNull($vpDraft);
|
$this->assertNull($vpDraft);
|
||||||
// Delete the source page form live, confirm that the virtual page has also been unpublished
|
// Delete the source page form live, confirm that the virtual page has also been unpublished
|
||||||
/** @var Page $pLive */
|
/** @var SiteTree $pLive */
|
||||||
$pLive = Versioned::get_by_stage(SiteTree::class, Versioned::LIVE)
|
$pLive = Versioned::get_by_stage(SiteTree::class, Versioned::LIVE)
|
||||||
->byID($pID);
|
->byID($pID);
|
||||||
$this->assertTrue($pLive->doUnpublish());
|
$this->assertTrue($pLive->doUnpublish());
|
||||||
|
@ -31,7 +31,7 @@ SilverStripe\Security\Member:
|
|||||||
alice:
|
alice:
|
||||||
Email: alice@alice.com
|
Email: alice@alice.com
|
||||||
Groups: =>SilverStripe\Security\Group.alicegroup
|
Groups: =>SilverStripe\Security\Group.alicegroup
|
||||||
Page:
|
SilverStripe\CMS\Model\SiteTree:
|
||||||
master:
|
master:
|
||||||
Title: My Page
|
Title: My Page
|
||||||
MenuTitle: My Page Nav
|
MenuTitle: My Page Nav
|
||||||
@ -52,15 +52,15 @@ SilverStripe\CMS\Tests\Model\VirtualPageTest_ClassA:
|
|||||||
SilverStripe\CMS\Model\VirtualPage:
|
SilverStripe\CMS\Model\VirtualPage:
|
||||||
vp1:
|
vp1:
|
||||||
Title: vp1
|
Title: vp1
|
||||||
CopyContentFrom: =>Page.master
|
CopyContentFrom: =>SilverStripe\CMS\Model\SiteTree.master
|
||||||
Parent: =>Page.holder
|
Parent: =>SilverStripe\CMS\Model\SiteTree.holder
|
||||||
vp2:
|
vp2:
|
||||||
Title: vp2
|
Title: vp2
|
||||||
CopyContentFrom: =>Page.master
|
CopyContentFrom: =>SilverStripe\CMS\Model\SiteTree.master
|
||||||
Parent: =>Page.holder
|
Parent: =>SilverStripe\CMS\Model\SiteTree.holder
|
||||||
vp3:
|
vp3:
|
||||||
CopyContentFrom: =>Page.master3
|
CopyContentFrom: =>SilverStripe\CMS\Model\SiteTree.master3
|
||||||
Parent: =>Page.holder
|
Parent: =>SilverStripe\CMS\Model\SiteTree.holder
|
||||||
CanEditType: OnlyTheseUsers
|
CanEditType: OnlyTheseUsers
|
||||||
CanViewType: OnlyTheseUsers
|
CanViewType: OnlyTheseUsers
|
||||||
EditorGroups: =>SilverStripe\Security\Group.andrewgroup
|
EditorGroups: =>SilverStripe\Security\Group.andrewgroup
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
namespace SilverStripe\CMS\Tests\Model;
|
namespace SilverStripe\CMS\Tests\Model;
|
||||||
|
|
||||||
use SilverStripe\Dev\TestOnly;
|
use SilverStripe\Dev\TestOnly;
|
||||||
use Page;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
|
||||||
class VirtualPageTest_ClassA extends Page implements TestOnly
|
class VirtualPageTest_ClassA extends SiteTree implements TestOnly
|
||||||
{
|
{
|
||||||
private static $table_name = 'VirtualPageTest_ClassA';
|
private static $table_name = 'VirtualPageTest_ClassA';
|
||||||
|
|
||||||
|
@ -3,12 +3,12 @@
|
|||||||
namespace SilverStripe\CMS\Tests\Model;
|
namespace SilverStripe\CMS\Tests\Model;
|
||||||
|
|
||||||
use SilverStripe\Dev\TestOnly;
|
use SilverStripe\Dev\TestOnly;
|
||||||
use PageController;
|
use SilverStripe\CMS\Controllers\ContentController;
|
||||||
|
|
||||||
class VirtualPageTest_ClassAController extends PageController implements TestOnly
|
class VirtualPageTest_ClassAController extends ContentController implements TestOnly
|
||||||
{
|
{
|
||||||
private static $allowed_actions = [
|
private static $allowed_actions = [
|
||||||
'testaction'
|
'testaction',
|
||||||
];
|
];
|
||||||
|
|
||||||
public function testMethod()
|
public function testMethod()
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
namespace SilverStripe\CMS\Tests\Model;
|
namespace SilverStripe\CMS\Tests\Model;
|
||||||
|
|
||||||
use SilverStripe\Dev\TestOnly;
|
use SilverStripe\Dev\TestOnly;
|
||||||
use Page;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
|
||||||
class VirtualPageTest_ClassB extends Page implements TestOnly
|
class VirtualPageTest_ClassB extends SiteTree implements TestOnly
|
||||||
{
|
{
|
||||||
private static $table_name = 'VirtualPageTest_ClassB';
|
private static $table_name = 'VirtualPageTest_ClassB';
|
||||||
|
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
namespace SilverStripe\CMS\Tests\Model;
|
namespace SilverStripe\CMS\Tests\Model;
|
||||||
|
|
||||||
use SilverStripe\Dev\TestOnly;
|
use SilverStripe\Dev\TestOnly;
|
||||||
use Page;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
|
||||||
class VirtualPageTest_ClassC extends Page implements TestOnly
|
class VirtualPageTest_ClassC extends SiteTree implements TestOnly
|
||||||
{
|
{
|
||||||
private static $table_name = 'VirtualPageTest_ClassC';
|
private static $table_name = 'VirtualPageTest_ClassC';
|
||||||
|
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
namespace SilverStripe\CMS\Tests\Model;
|
namespace SilverStripe\CMS\Tests\Model;
|
||||||
|
|
||||||
use SilverStripe\Dev\TestOnly;
|
use SilverStripe\Dev\TestOnly;
|
||||||
use Page;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
|
||||||
class VirtualPageTest_NotRoot extends Page implements TestOnly
|
class VirtualPageTest_NotRoot extends SiteTree implements TestOnly
|
||||||
{
|
{
|
||||||
private static $table_name = 'VirtualPageTest_NotRoot';
|
private static $table_name = 'VirtualPageTest_NotRoot';
|
||||||
|
|
||||||
|
@ -9,7 +9,8 @@ class VirtualPageTest_PageExtension extends DataExtension implements TestOnly
|
|||||||
{
|
{
|
||||||
private static $db = [
|
private static $db = [
|
||||||
// These fields are just on an extension to simulate shared properties between Page and VirtualPage.
|
// These fields are just on an extension to simulate shared properties between Page and VirtualPage.
|
||||||
// Not possible through direct $db definitions due to VirtualPage inheriting from Page, and Page being defined elsewhere.
|
// Not possible through direct $db definitions due to VirtualPage inheriting from Page,
|
||||||
|
// and Page being defined elsewhere.
|
||||||
'MySharedVirtualField' => 'Text',
|
'MySharedVirtualField' => 'Text',
|
||||||
'MySharedNonVirtualField' => 'Text',
|
'MySharedNonVirtualField' => 'Text',
|
||||||
];
|
];
|
||||||
|
@ -4,9 +4,9 @@ namespace SilverStripe\CMS\Tests\Model;
|
|||||||
|
|
||||||
use SilverStripe\CMS\Model\VirtualPage;
|
use SilverStripe\CMS\Model\VirtualPage;
|
||||||
use SilverStripe\Dev\TestOnly;
|
use SilverStripe\Dev\TestOnly;
|
||||||
use Page;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
|
||||||
class VirtualPageTest_PageWithAllowedChildren extends Page implements TestOnly
|
class VirtualPageTest_PageWithAllowedChildren extends SiteTree implements TestOnly
|
||||||
{
|
{
|
||||||
private static $table_name = 'VirtualPageTest_PageWithAllowedChildren';
|
private static $table_name = 'VirtualPageTest_PageWithAllowedChildren';
|
||||||
|
|
||||||
|
@ -2,24 +2,22 @@
|
|||||||
|
|
||||||
namespace SilverStripe\CMS\Tests\Reports;
|
namespace SilverStripe\CMS\Tests\Reports;
|
||||||
|
|
||||||
|
use SilverStripe\Assets\File;
|
||||||
|
use SilverStripe\CMS\Model\RedirectorPage;
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
use SilverStripe\CMS\Model\SiteTree;
|
||||||
|
use SilverStripe\CMS\Model\VirtualPage;
|
||||||
|
use SilverStripe\CMS\Reports\BrokenFilesReport;
|
||||||
|
use SilverStripe\CMS\Reports\BrokenLinksReport;
|
||||||
|
use SilverStripe\CMS\Reports\BrokenRedirectorPagesReport;
|
||||||
|
use SilverStripe\CMS\Reports\BrokenVirtualPagesReport;
|
||||||
|
use SilverStripe\CMS\Reports\RecentlyEditedReport;
|
||||||
|
use SilverStripe\Dev\SapphireTest;
|
||||||
use SilverStripe\ORM\DB;
|
use SilverStripe\ORM\DB;
|
||||||
use SilverStripe\ORM\FieldType\DBDatetime;
|
use SilverStripe\ORM\FieldType\DBDatetime;
|
||||||
use SilverStripe\CMS\Reports\RecentlyEditedReport;
|
|
||||||
use SilverStripe\CMS\Reports\BrokenLinksReport;
|
|
||||||
use SilverStripe\CMS\Reports\BrokenFilesReport;
|
|
||||||
use SilverStripe\CMS\Model\VirtualPage;
|
|
||||||
use SilverStripe\CMS\Reports\BrokenVirtualPagesReport;
|
|
||||||
use SilverStripe\CMS\Model\RedirectorPage;
|
|
||||||
use SilverStripe\CMS\Reports\BrokenRedirectorPagesReport;
|
|
||||||
use SilverStripe\Reports\Report;
|
use SilverStripe\Reports\Report;
|
||||||
use SilverStripe\Assets\File;
|
|
||||||
use SilverStripe\Dev\SapphireTest;
|
|
||||||
use Page;
|
|
||||||
|
|
||||||
class CmsReportsTest extends SapphireTest
|
class CmsReportsTest extends SapphireTest
|
||||||
{
|
{
|
||||||
|
|
||||||
protected static $fixture_file = 'CmsReportsTest.yml';
|
protected static $fixture_file = 'CmsReportsTest.yml';
|
||||||
|
|
||||||
private static $daysAgo = 14;
|
private static $daysAgo = 14;
|
||||||
@ -29,14 +27,19 @@ class CmsReportsTest extends SapphireTest
|
|||||||
parent::setUp();
|
parent::setUp();
|
||||||
|
|
||||||
// set the dates by hand: impossible to set via yml
|
// set the dates by hand: impossible to set via yml
|
||||||
$afterThreshold = strtotime('-'.(self::$daysAgo-1).' days', strtotime('31-06-2009 00:00:00'));
|
$afterThreshold = strtotime('-' . (self::$daysAgo - 1) . ' days', strtotime('31-06-2009 00:00:00'));
|
||||||
$beforeThreshold = strtotime('-'.(self::$daysAgo+1).' days', strtotime('31-06-2009 00:00:00'));
|
$beforeThreshold = strtotime('-' . (self::$daysAgo + 1) . ' days', strtotime('31-06-2009 00:00:00'));
|
||||||
|
|
||||||
$after = $this->objFromFixture(SiteTree::class, 'after');
|
$after = $this->objFromFixture(SiteTree::class, 'after');
|
||||||
$before = $this->objFromFixture(SiteTree::class, 'before');
|
$before = $this->objFromFixture(SiteTree::class, 'before');
|
||||||
|
DB::query(
|
||||||
DB::query("UPDATE \"SiteTree\" SET \"Created\"='2009-01-01 00:00:00', \"LastEdited\"='".date('Y-m-d H:i:s', $afterThreshold)."' WHERE \"ID\"='".$after->ID."'");
|
"UPDATE \"SiteTree\" SET \"Created\"='2009-01-01 00:00:00', \"LastEdited\"='" .
|
||||||
DB::query("UPDATE \"SiteTree\" SET \"Created\"='2009-01-01 00:00:00', \"LastEdited\"='".date('Y-m-d H:i:s', $beforeThreshold)."' WHERE \"ID\"='".$before->ID."'");
|
date('Y-m-d H:i:s', $afterThreshold) . "' WHERE \"ID\"='" . $after->ID . "'"
|
||||||
|
);
|
||||||
|
DB::query(
|
||||||
|
"UPDATE \"SiteTree\" SET \"Created\"='2009-01-01 00:00:00', \"LastEdited\"='" .
|
||||||
|
date('Y-m-d H:i:s', $beforeThreshold) . "' WHERE \"ID\"='" . $before->ID . "'"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,13 +55,27 @@ class CmsReportsTest extends SapphireTest
|
|||||||
|
|
||||||
// ASSERT that the "draft" report is returning the correct results.
|
// ASSERT that the "draft" report is returning the correct results.
|
||||||
$parameters = ['CheckSite' => 'Draft'];
|
$parameters = ['CheckSite' => 'Draft'];
|
||||||
$results = count($report->sourceRecords($parameters, null, null) ?? []) > 0;
|
$results = count($report->sourceRecords($parameters, null, null)) > 0;
|
||||||
$isDraftBroken ? $this->assertTrue($results, "{$class} has NOT returned the correct DRAFT results, as NO pages were found.") : $this->assertFalse($results, "{$class} has NOT returned the correct DRAFT results, as pages were found.");
|
$isDraftBroken
|
||||||
|
? $this->assertTrue(
|
||||||
|
$results,
|
||||||
|
"{$class} has NOT returned the correct DRAFT results, as NO pages were found."
|
||||||
|
) : $this->assertFalse(
|
||||||
|
$results,
|
||||||
|
"{$class} has NOT returned the correct DRAFT results, as pages were found."
|
||||||
|
);
|
||||||
|
|
||||||
// ASSERT that the "published" report is returning the correct results.
|
// ASSERT that the "published" report is returning the correct results.
|
||||||
$parameters = ['CheckSite' => 'Published', 'OnLive' => 1];
|
$parameters = ['CheckSite' => 'Published', 'OnLive' => 1];
|
||||||
$results = count($report->sourceRecords($parameters, null, null) ?? []) > 0;
|
$results = count($report->sourceRecords($parameters, null, null) ?? []) > 0;
|
||||||
$isPublishedBroken ? $this->assertTrue($results, "{$class} has NOT returned the correct PUBLISHED results, as NO pages were found.") : $this->assertFalse($results, "{$class} has NOT returned the correct PUBLISHED results, as pages were found.");
|
$isPublishedBroken
|
||||||
|
? $this->assertTrue(
|
||||||
|
$results,
|
||||||
|
"{$class} has NOT returned the correct PUBLISHED results, as NO pages were found."
|
||||||
|
) : $this->assertFalse(
|
||||||
|
$results,
|
||||||
|
"{$class} has NOT returned the correct PUBLISHED results, as pages were found."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testRecentlyEdited()
|
public function testRecentlyEdited()
|
||||||
@ -81,18 +98,14 @@ class CmsReportsTest extends SapphireTest
|
|||||||
/**
|
/**
|
||||||
* Test the broken links side report.
|
* Test the broken links side report.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public function testBrokenLinks()
|
public function testBrokenLinks()
|
||||||
{
|
{
|
||||||
|
|
||||||
// Create a "draft" page with a broken link.
|
// Create a "draft" page with a broken link.
|
||||||
|
$page = new SiteTree();
|
||||||
$page = Page::create();
|
|
||||||
$page->Content = "<a href='[sitetree_link,id=987654321]'>This</a> is a broken link.";
|
$page->Content = "<a href='[sitetree_link,id=987654321]'>This</a> is a broken link.";
|
||||||
$page->writeToStage('Stage');
|
$page->writeToStage('Stage');
|
||||||
|
|
||||||
// Retrieve the broken links side report.
|
// Retrieve the broken links side report.
|
||||||
|
|
||||||
$reports = Report::get_reports();
|
$reports = Report::get_reports();
|
||||||
$brokenLinksReport = null;
|
$brokenLinksReport = null;
|
||||||
foreach ($reports as $report) {
|
foreach ($reports as $report) {
|
||||||
@ -109,12 +122,11 @@ class CmsReportsTest extends SapphireTest
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ASSERT that the "draft" report has detected the page having a broken link.
|
// ASSERT that the "draft" report has detected the page having a broken link.
|
||||||
// ASSERT that the "published" report has NOT detected the page having a broken link, as the page has not been "published" yet.
|
// ASSERT that the "published" report has NOT detected the page having a broken link,
|
||||||
|
// as the page has not been "published" yet.
|
||||||
$this->isReportBroken($brokenLinksReport, true, false);
|
$this->isReportBroken($brokenLinksReport, true, false);
|
||||||
|
|
||||||
// Make sure the page is now "published".
|
// Make sure the page is now "published".
|
||||||
|
|
||||||
$page->writeToStage('Live');
|
$page->writeToStage('Live');
|
||||||
|
|
||||||
// ASSERT that the "draft" report has detected the page having a broken link.
|
// ASSERT that the "draft" report has detected the page having a broken link.
|
||||||
@ -123,17 +135,15 @@ class CmsReportsTest extends SapphireTest
|
|||||||
$this->isReportBroken($brokenLinksReport, true, true);
|
$this->isReportBroken($brokenLinksReport, true, true);
|
||||||
|
|
||||||
// Correct the "draft" broken link.
|
// Correct the "draft" broken link.
|
||||||
|
|
||||||
$page->Content = str_replace('987654321', $page->ID ?? '', $page->Content ?? '');
|
$page->Content = str_replace('987654321', $page->ID ?? '', $page->Content ?? '');
|
||||||
$page->writeToStage('Stage');
|
$page->writeToStage('Stage');
|
||||||
|
|
||||||
// ASSERT that the "draft" report has NOT detected the page having a broken link.
|
// ASSERT that the "draft" report has NOT detected the page having a broken link.
|
||||||
// ASSERT that the "published" report has detected the page having a broken link, as the previous content remains "published".
|
// ASSERT that the "published" report has detected the page having a broken link,
|
||||||
|
// as the previous content remains "published".
|
||||||
$this->isReportBroken($brokenLinksReport, false, true);
|
$this->isReportBroken($brokenLinksReport, false, true);
|
||||||
|
|
||||||
// Make sure the change has now been "published".
|
// Make sure the change has now been "published".
|
||||||
|
|
||||||
$page->writeToStage('Live');
|
$page->writeToStage('Live');
|
||||||
|
|
||||||
// ASSERT that the "draft" report has NOT detected the page having a broken link.
|
// ASSERT that the "draft" report has NOT detected the page having a broken link.
|
||||||
@ -145,18 +155,14 @@ class CmsReportsTest extends SapphireTest
|
|||||||
/**
|
/**
|
||||||
* Test the broken files side report.
|
* Test the broken files side report.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public function testBrokenFiles()
|
public function testBrokenFiles()
|
||||||
{
|
{
|
||||||
|
|
||||||
// Create a "draft" page with a broken file.
|
// Create a "draft" page with a broken file.
|
||||||
|
$page = new SiteTree();
|
||||||
$page = Page::create();
|
|
||||||
$page->Content = "<a href='[file_link,id=987654321]'>This</a> is a broken file.";
|
$page->Content = "<a href='[file_link,id=987654321]'>This</a> is a broken file.";
|
||||||
$page->writeToStage('Stage');
|
$page->writeToStage('Stage');
|
||||||
|
|
||||||
// Retrieve the broken files side report.
|
// Retrieve the broken files side report.
|
||||||
|
|
||||||
$reports = Report::get_reports();
|
$reports = Report::get_reports();
|
||||||
$brokenFilesReport = null;
|
$brokenFilesReport = null;
|
||||||
foreach ($reports as $report) {
|
foreach ($reports as $report) {
|
||||||
@ -173,21 +179,18 @@ class CmsReportsTest extends SapphireTest
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ASSERT that the "draft" report has detected the page having a broken file.
|
// ASSERT that the "draft" report has detected the page having a broken file.
|
||||||
// ASSERT that the "published" report has NOT detected the page having a broken file, as the page has not been "published" yet.
|
// ASSERT that the "published" report has NOT detected the page having a broken file,
|
||||||
|
// as the page has not been "published" yet.
|
||||||
$this->isReportBroken($brokenFilesReport, true, false);
|
$this->isReportBroken($brokenFilesReport, true, false);
|
||||||
|
|
||||||
// Make sure the page is now "published".
|
// Make sure the page is now "published".
|
||||||
|
|
||||||
$page->writeToStage('Live');
|
$page->writeToStage('Live');
|
||||||
|
|
||||||
// ASSERT that the "draft" report has detected the page having a broken file.
|
// ASSERT that the "draft" report has detected the page having a broken file.
|
||||||
// ASSERT that the "published" report has detected the page having a broken file.
|
// ASSERT that the "published" report has detected the page having a broken file.
|
||||||
|
|
||||||
$this->isReportBroken($brokenFilesReport, true, true);
|
$this->isReportBroken($brokenFilesReport, true, true);
|
||||||
|
|
||||||
// Correct the "draft" broken file.
|
// Correct the "draft" broken file.
|
||||||
|
|
||||||
$file = File::create();
|
$file = File::create();
|
||||||
$file->Filename = 'name.pdf';
|
$file->Filename = 'name.pdf';
|
||||||
$file->write();
|
$file->write();
|
||||||
@ -195,17 +198,15 @@ class CmsReportsTest extends SapphireTest
|
|||||||
$page->writeToStage('Stage');
|
$page->writeToStage('Stage');
|
||||||
|
|
||||||
// ASSERT that the "draft" report has NOT detected the page having a broken file.
|
// ASSERT that the "draft" report has NOT detected the page having a broken file.
|
||||||
// ASSERT that the "published" report has detected the page having a broken file, as the previous content remains "published".
|
// ASSERT that the "published" report has detected the page having a broken file,
|
||||||
|
// as the previous content remains "published".
|
||||||
$this->isReportBroken($brokenFilesReport, false, true);
|
$this->isReportBroken($brokenFilesReport, false, true);
|
||||||
|
|
||||||
// Make sure the change has now been "published".
|
// Make sure the change has now been "published".
|
||||||
|
|
||||||
$page->writeToStage('Live');
|
$page->writeToStage('Live');
|
||||||
|
|
||||||
// ASSERT that the "draft" report has NOT detected the page having a broken file.
|
// ASSERT that the "draft" report has NOT detected the page having a broken file.
|
||||||
// ASSERT that the "published" report has NOT detected the page having a broken file.
|
// ASSERT that the "published" report has NOT detected the page having a broken file.
|
||||||
|
|
||||||
$this->isReportBroken($brokenFilesReport, false, false);
|
$this->isReportBroken($brokenFilesReport, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,15 +216,12 @@ class CmsReportsTest extends SapphireTest
|
|||||||
|
|
||||||
public function testBrokenVirtualPages()
|
public function testBrokenVirtualPages()
|
||||||
{
|
{
|
||||||
|
|
||||||
// Create a "draft" virtual page with a broken link.
|
// Create a "draft" virtual page with a broken link.
|
||||||
|
|
||||||
$page = VirtualPage::create();
|
$page = VirtualPage::create();
|
||||||
$page->CopyContentFromID = 987654321;
|
$page->CopyContentFromID = 987654321;
|
||||||
$page->writeToStage('Stage');
|
$page->writeToStage('Stage');
|
||||||
|
|
||||||
// Retrieve the broken virtual pages side report.
|
// Retrieve the broken virtual pages side report.
|
||||||
|
|
||||||
$reports = Report::get_reports();
|
$reports = Report::get_reports();
|
||||||
$brokenVirtualPagesReport = null;
|
$brokenVirtualPagesReport = null;
|
||||||
foreach ($reports as $report) {
|
foreach ($reports as $report) {
|
||||||
@ -240,22 +238,19 @@ class CmsReportsTest extends SapphireTest
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ASSERT that the "draft" report has detected the page having a broken link.
|
// ASSERT that the "draft" report has detected the page having a broken link.
|
||||||
// ASSERT that the "published" report has NOT detected the page having a broken link, as the page has not been "published" yet.
|
// ASSERT that the "published" report has NOT detected the page having a broken link,
|
||||||
|
// as the page has not been "published" yet.
|
||||||
$this->isReportBroken($brokenVirtualPagesReport, true, false);
|
$this->isReportBroken($brokenVirtualPagesReport, true, false);
|
||||||
|
|
||||||
// Make sure the page is now "published".
|
// Make sure the page is now "published".
|
||||||
|
|
||||||
$page->writeToStage('Live');
|
$page->writeToStage('Live');
|
||||||
|
|
||||||
// ASSERT that the "draft" report has detected the page having a broken link.
|
// ASSERT that the "draft" report has detected the page having a broken link.
|
||||||
// ASSERT that the "published" report has detected the page having a broken link.
|
// ASSERT that the "published" report has detected the page having a broken link.
|
||||||
|
|
||||||
$this->isReportBroken($brokenVirtualPagesReport, true, true);
|
$this->isReportBroken($brokenVirtualPagesReport, true, true);
|
||||||
|
|
||||||
// Correct the "draft" broken link.
|
// Correct the "draft" broken link.
|
||||||
|
$contentPage = new SiteTree();
|
||||||
$contentPage = Page::create();
|
|
||||||
$contentPage->Content = 'This is some content.';
|
$contentPage->Content = 'This is some content.';
|
||||||
$contentPage->writeToStage('Stage');
|
$contentPage->writeToStage('Stage');
|
||||||
$contentPage->writeToStage('Live');
|
$contentPage->writeToStage('Live');
|
||||||
@ -263,36 +258,30 @@ class CmsReportsTest extends SapphireTest
|
|||||||
$page->writeToStage('Stage');
|
$page->writeToStage('Stage');
|
||||||
|
|
||||||
// ASSERT that the "draft" report has NOT detected the page having a broken link.
|
// ASSERT that the "draft" report has NOT detected the page having a broken link.
|
||||||
// ASSERT that the "published" report has detected the page having a broken link, as the previous content remains "published".
|
// ASSERT that the "published" report has detected the page having a broken link,
|
||||||
|
// as the previous content remains "published".
|
||||||
$this->isReportBroken($brokenVirtualPagesReport, false, true);
|
$this->isReportBroken($brokenVirtualPagesReport, false, true);
|
||||||
|
|
||||||
// Make sure the change has now been "published".
|
// Make sure the change has now been "published".
|
||||||
|
|
||||||
$page->writeToStage('Live');
|
$page->writeToStage('Live');
|
||||||
|
|
||||||
// ASSERT that the "draft" report has NOT detected the page having a broken link.
|
// ASSERT that the "draft" report has NOT detected the page having a broken link.
|
||||||
// ASSERT that the "published" report has NOT detected the page having a broken link.
|
// ASSERT that the "published" report has NOT detected the page having a broken link.
|
||||||
|
|
||||||
$this->isReportBroken($brokenVirtualPagesReport, false, false);
|
$this->isReportBroken($brokenVirtualPagesReport, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test the broken redirector pages side report.
|
* Test the broken redirector pages side report.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public function testBrokenRedirectorPages()
|
public function testBrokenRedirectorPages()
|
||||||
{
|
{
|
||||||
|
|
||||||
// Create a "draft" redirector page with a broken link.
|
// Create a "draft" redirector page with a broken link.
|
||||||
|
|
||||||
$page = RedirectorPage::create();
|
$page = RedirectorPage::create();
|
||||||
$page->RedirectionType = 'Internal';
|
$page->RedirectionType = 'Internal';
|
||||||
$page->LinkToID = 987654321;
|
$page->LinkToID = 987654321;
|
||||||
$page->writeToStage('Stage');
|
$page->writeToStage('Stage');
|
||||||
|
|
||||||
// Retrieve the broken redirector pages side report.
|
// Retrieve the broken redirector pages side report.
|
||||||
|
|
||||||
$reports = Report::get_reports();
|
$reports = Report::get_reports();
|
||||||
$brokenRedirectorPagesReport = null;
|
$brokenRedirectorPagesReport = null;
|
||||||
foreach ($reports as $report) {
|
foreach ($reports as $report) {
|
||||||
@ -309,22 +298,19 @@ class CmsReportsTest extends SapphireTest
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ASSERT that the "draft" report has detected the page having a broken link.
|
// ASSERT that the "draft" report has detected the page having a broken link.
|
||||||
// ASSERT that the "published" report has NOT detected the page having a broken link, as the page has not been "published" yet.
|
// ASSERT that the "published" report has NOT detected the page having a broken link,
|
||||||
|
// as the page has not been "published" yet.
|
||||||
$this->isReportBroken($brokenRedirectorPagesReport, true, false);
|
$this->isReportBroken($brokenRedirectorPagesReport, true, false);
|
||||||
|
|
||||||
// Make sure the page is now "published".
|
// Make sure the page is now "published".
|
||||||
|
|
||||||
$page->writeToStage('Live');
|
$page->writeToStage('Live');
|
||||||
|
|
||||||
// ASSERT that the "draft" report has detected the page having a broken link.
|
// ASSERT that the "draft" report has detected the page having a broken link.
|
||||||
// ASSERT that the "published" report has detected the page having a broken link.
|
// ASSERT that the "published" report has detected the page having a broken link.
|
||||||
|
|
||||||
$this->isReportBroken($brokenRedirectorPagesReport, true, true);
|
$this->isReportBroken($brokenRedirectorPagesReport, true, true);
|
||||||
|
|
||||||
// Correct the "draft" broken link.
|
// Correct the "draft" broken link.
|
||||||
|
$contentPage = new SiteTree();
|
||||||
$contentPage = Page::create();
|
|
||||||
$contentPage->Content = 'This is some content.';
|
$contentPage->Content = 'This is some content.';
|
||||||
$contentPage->writeToStage('Stage');
|
$contentPage->writeToStage('Stage');
|
||||||
$contentPage->writeToStage('Live');
|
$contentPage->writeToStage('Live');
|
||||||
@ -332,17 +318,15 @@ class CmsReportsTest extends SapphireTest
|
|||||||
$page->writeToStage('Stage');
|
$page->writeToStage('Stage');
|
||||||
|
|
||||||
// ASSERT that the "draft" report has NOT detected the page having a broken link.
|
// ASSERT that the "draft" report has NOT detected the page having a broken link.
|
||||||
// ASSERT that the "published" report has detected the page having a broken link, as the previous content remains "published".
|
// ASSERT that the "published" report has detected the page having a broken link,
|
||||||
|
// as the previous content remains "published".
|
||||||
$this->isReportBroken($brokenRedirectorPagesReport, false, true);
|
$this->isReportBroken($brokenRedirectorPagesReport, false, true);
|
||||||
|
|
||||||
// Make sure the change has now been "published".
|
// Make sure the change has now been "published".
|
||||||
|
|
||||||
$page->writeToStage('Live');
|
$page->writeToStage('Live');
|
||||||
|
|
||||||
// ASSERT that the "draft" report has NOT detected the page having a broken link.
|
// ASSERT that the "draft" report has NOT detected the page having a broken link.
|
||||||
// ASSERT that the "published" report has NOT detected the page having a broken link.
|
// ASSERT that the "published" report has NOT detected the page having a broken link.
|
||||||
|
|
||||||
$this->isReportBroken($brokenRedirectorPagesReport, false, false);
|
$this->isReportBroken($brokenRedirectorPagesReport, false, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ class CMSMainSearchFormTest extends FunctionalTest
|
|||||||
'q' => [
|
'q' => [
|
||||||
'Term' => 'Page 10',
|
'Term' => 'Page 10',
|
||||||
'FilterClass' => CMSSiteTreeFilter_Search::class,
|
'FilterClass' => CMSSiteTreeFilter_Search::class,
|
||||||
]
|
],
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
namespace SilverStripe\CMS\Tests\Search;
|
namespace SilverStripe\CMS\Tests\Search;
|
||||||
|
|
||||||
use Page;
|
|
||||||
use SilverStripe\Assets\File;
|
use SilverStripe\Assets\File;
|
||||||
use SilverStripe\CMS\Controllers\ContentController;
|
use SilverStripe\CMS\Controllers\ContentController;
|
||||||
use SilverStripe\CMS\Controllers\ModelAsController;
|
use SilverStripe\CMS\Controllers\ModelAsController;
|
||||||
@ -22,14 +21,10 @@ use SilverStripe\Versioned\Versioned;
|
|||||||
use TractorCow\Fluent\Extension\FluentSiteTreeExtension;
|
use TractorCow\Fluent\Extension\FluentSiteTreeExtension;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @todo Fix unpublished pages check in testPublishedPagesMatchedByTitle()
|
|
||||||
* @todo All tests run on unpublished pages at the moment, due to the searchform not distinguishing between them
|
|
||||||
*
|
|
||||||
* Because this manipulates the test database in severe ways, I've renamed the test to force it to run last...
|
* Because this manipulates the test database in severe ways, I've renamed the test to force it to run last...
|
||||||
*/
|
*/
|
||||||
class ZZZSearchFormTest extends FunctionalTest
|
class ZZZSearchFormTest extends FunctionalTest
|
||||||
{
|
{
|
||||||
|
|
||||||
protected static $fixture_file = 'ZZZSearchFormTest.yml';
|
protected static $fixture_file = 'ZZZSearchFormTest.yml';
|
||||||
|
|
||||||
protected static $illegal_extensions = [
|
protected static $illegal_extensions = [
|
||||||
@ -97,9 +92,6 @@ class ZZZSearchFormTest extends FunctionalTest
|
|||||||
return $supports;
|
return $supports;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @skipUpgrade
|
|
||||||
*/
|
|
||||||
public function testSearchFormTemplateCanBeChanged()
|
public function testSearchFormTemplateCanBeChanged()
|
||||||
{
|
{
|
||||||
if (!$this->checkFulltextSupport()) {
|
if (!$this->checkFulltextSupport()) {
|
||||||
@ -116,9 +108,6 @@ class ZZZSearchFormTest extends FunctionalTest
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @skipUpgrade
|
|
||||||
*/
|
|
||||||
public function testPublishedPagesMatchedByTitle()
|
public function testPublishedPagesMatchedByTitle()
|
||||||
{
|
{
|
||||||
if (!$this->checkFulltextSupport()) {
|
if (!$this->checkFulltextSupport()) {
|
||||||
@ -149,9 +138,6 @@ class ZZZSearchFormTest extends FunctionalTest
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @skipUpgrade
|
|
||||||
*/
|
|
||||||
public function testDoubleQuotesPublishedPagesMatchedByTitle()
|
public function testDoubleQuotesPublishedPagesMatchedByTitle()
|
||||||
{
|
{
|
||||||
if (!$this->checkFulltextSupport()) {
|
if (!$this->checkFulltextSupport()) {
|
||||||
@ -184,9 +170,6 @@ class ZZZSearchFormTest extends FunctionalTest
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @skipUpgrade
|
|
||||||
*/
|
|
||||||
public function testUnpublishedPagesNotIncluded()
|
public function testUnpublishedPagesNotIncluded()
|
||||||
{
|
{
|
||||||
if (!$this->checkFulltextSupport()) {
|
if (!$this->checkFulltextSupport()) {
|
||||||
|
@ -1,91 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace SilverStripe\CMS\Tests\Tasks;
|
|
||||||
|
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
|
||||||
use SilverStripe\CMS\Tasks\MigrateSiteTreeLinkingTask;
|
|
||||||
use SilverStripe\Dev\SapphireTest;
|
|
||||||
use SilverStripe\ORM\DataObject;
|
|
||||||
use SilverStripe\ORM\DB;
|
|
||||||
use SilverStripe\Dev\Deprecation;
|
|
||||||
|
|
||||||
class MigrateSiteTreeLinkingTaskTest extends SapphireTest
|
|
||||||
{
|
|
||||||
protected static $fixture_file = 'MigrateSiteTreeLinkingTaskTest.yml';
|
|
||||||
|
|
||||||
public static function setUpBeforeClass(): void
|
|
||||||
{
|
|
||||||
parent::setUpBeforeClass();
|
|
||||||
|
|
||||||
// Cover db reset in case parent did not start
|
|
||||||
if (!static::getExtraDataObjects()) {
|
|
||||||
DataObject::reset();
|
|
||||||
static::resetDBSchema(true, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure legacy SiteTree_LinkTracking table exists
|
|
||||||
DB::get_schema()->schemaUpdate(function () {
|
|
||||||
DB::require_table('SiteTree_LinkTracking', [
|
|
||||||
'SiteTreeID' => 'Int',
|
|
||||||
'ChildID' => 'Int',
|
|
||||||
'FieldName' => 'Varchar',
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function setUp(): void
|
|
||||||
{
|
|
||||||
if (Deprecation::isEnabled()) {
|
|
||||||
$this->markTestSkipped('Test calls deprecated code');
|
|
||||||
}
|
|
||||||
parent::setUp();
|
|
||||||
|
|
||||||
// Manually bootstrap all Content blocks with soft coded IDs (raw sql to avoid save hooks)
|
|
||||||
$replacements = [
|
|
||||||
'$$ABOUTID$$' => $this->idFromFixture(SiteTree::class, 'about'),
|
|
||||||
'$$HOMEID$$' => $this->idFromFixture(SiteTree::class, 'home'),
|
|
||||||
'$$STAFFID$$' => $this->idFromFixture(SiteTree::class, 'staff'),
|
|
||||||
];
|
|
||||||
foreach (DB::query('SELECT "ID", "Content" FROM "SiteTree"') as $row) {
|
|
||||||
$id = (int)$row['ID'];
|
|
||||||
$content = str_replace(array_keys($replacements ?? []), array_values($replacements ?? []), $row['Content'] ?? '');
|
|
||||||
DB::prepared_query('UPDATE "SiteTree" SET "Content" = ? WHERE "ID" = ?', [$content, $id]);
|
|
||||||
}
|
|
||||||
DataObject::reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testLinkingMigration()
|
|
||||||
{
|
|
||||||
ob_start();
|
|
||||||
|
|
||||||
DB::quiet(false);
|
|
||||||
$task = new MigrateSiteTreeLinkingTask();
|
|
||||||
$task->run(null);
|
|
||||||
$this->assertStringContainsString(
|
|
||||||
"Migrated page links on 5 Pages",
|
|
||||||
ob_get_contents(),
|
|
||||||
'Rewritten links are correctly reported'
|
|
||||||
);
|
|
||||||
DB::quiet(true);
|
|
||||||
ob_end_clean();
|
|
||||||
|
|
||||||
// Query links for pages
|
|
||||||
/** @var SiteTree $home */
|
|
||||||
$home = $this->objFromFixture(SiteTree::class, 'home');
|
|
||||||
/** @var SiteTree $about */
|
|
||||||
$about = $this->objFromFixture(SiteTree::class, 'about');
|
|
||||||
/** @var SiteTree $staff */
|
|
||||||
$staff = $this->objFromFixture(SiteTree::class, 'staff');
|
|
||||||
/** @var SiteTree $action */
|
|
||||||
$action = $this->objFromFixture(SiteTree::class, 'action');
|
|
||||||
/** @var SiteTree $hash */
|
|
||||||
$hash = $this->objFromFixture(SiteTree::class, 'hash_link');
|
|
||||||
|
|
||||||
// Ensure all links are created
|
|
||||||
$this->assertListEquals([['ID' => $about->ID], ['ID' => $staff->ID]], $home->LinkTracking());
|
|
||||||
$this->assertListEquals([['ID' => $home->ID], ['ID' => $staff->ID]], $about->LinkTracking());
|
|
||||||
$this->assertListEquals([['ID' => $home->ID], ['ID' => $about->ID]], $staff->LinkTracking());
|
|
||||||
$this->assertListEquals([['ID' => $home->ID]], $action->LinkTracking());
|
|
||||||
$this->assertListEquals([['ID' => $home->ID], ['ID' => $about->ID]], $hash->LinkTracking());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,67 +0,0 @@
|
|||||||
SilverStripe\CMS\Model\SiteTree:
|
|
||||||
home:
|
|
||||||
Title: Home Page
|
|
||||||
URLSegment: home
|
|
||||||
Content: '<a href="[sitetree_link,id=$$ABOUTID$$]">About</a><a href="[sitetree_link,id=$$STAFFID$$]">Staff</a><a href="http://silverstripe.org/">External Link</a><a name="anchor"></a>'
|
|
||||||
about:
|
|
||||||
Title: About Us
|
|
||||||
URLSegment: about
|
|
||||||
Content: '<a href="[sitetree_link,id=$$HOMEID$$]">Home</a><a href="[sitetree_link,id=$$STAFFID$$]">Staff</a><a name="second-anchor"></a>'
|
|
||||||
staff:
|
|
||||||
Title: Staff
|
|
||||||
URLSegment: staff
|
|
||||||
Content: '<a href="[sitetree_link,id=$$HOMEID$$]">Home</a><a href="[sitetree_link,id=$$ABOUTID$$]">About</a>'
|
|
||||||
Parent: =>SilverStripe\CMS\Model\SiteTree.about
|
|
||||||
action:
|
|
||||||
Title: Action Link
|
|
||||||
URLSegment: action
|
|
||||||
Content: '<a href="[sitetree_link,id=$$HOMEID$$]SearchForm">Search Form</a>'
|
|
||||||
hash_link:
|
|
||||||
Title: Hash Link
|
|
||||||
URLSegment: hash-link
|
|
||||||
Content: '<a href="[sitetree_link,id=$$HOMEID$$]#anchor">Home</a><a href="[sitetree_link,id=$$ABOUTID$$]#second-anchor">About</a>'
|
|
||||||
admin_link:
|
|
||||||
Title: Admin Link
|
|
||||||
URLSegment: admin-link
|
|
||||||
Content: '<a href="admin">Admin</a>'
|
|
||||||
no_links:
|
|
||||||
Title: No Links
|
|
||||||
URLSegment: No Links
|
|
||||||
|
|
||||||
SiteTree_LinkTracking:
|
|
||||||
home_about:
|
|
||||||
SiteTreeID: =>SilverStripe\CMS\Model\SiteTree.home
|
|
||||||
ChildID: =>SilverStripe\CMS\Model\SiteTree.about
|
|
||||||
FieldName: Content
|
|
||||||
home_staff:
|
|
||||||
SiteTreeID: =>SilverStripe\CMS\Model\SiteTree.home
|
|
||||||
ChildID: =>SilverStripe\CMS\Model\SiteTree.staff
|
|
||||||
FieldName: Content
|
|
||||||
about_home:
|
|
||||||
SiteTreeID: =>SilverStripe\CMS\Model\SiteTree.about
|
|
||||||
ChildID: =>SilverStripe\CMS\Model\SiteTree.home
|
|
||||||
FieldName: Content
|
|
||||||
about_staff:
|
|
||||||
SiteTreeID: =>SilverStripe\CMS\Model\SiteTree.about
|
|
||||||
ChildID: =>SilverStripe\CMS\Model\SiteTree.staff
|
|
||||||
FieldName: Content
|
|
||||||
staff_home:
|
|
||||||
SiteTreeID: =>SilverStripe\CMS\Model\SiteTree.staff
|
|
||||||
ChildID: =>SilverStripe\CMS\Model\SiteTree.home
|
|
||||||
FieldName: Content
|
|
||||||
staff_about:
|
|
||||||
SiteTreeID: =>SilverStripe\CMS\Model\SiteTree.staff
|
|
||||||
ChildID: =>SilverStripe\CMS\Model\SiteTree.about
|
|
||||||
FieldName: Content
|
|
||||||
action_home:
|
|
||||||
SiteTreeID: =>SilverStripe\CMS\Model\SiteTree.action
|
|
||||||
ChildID: =>SilverStripe\CMS\Model\SiteTree.home
|
|
||||||
FieldName: Content
|
|
||||||
hash_link_home:
|
|
||||||
SiteTreeID: =>SilverStripe\CMS\Model\SiteTree.hash_link
|
|
||||||
ChildID: =>SilverStripe\CMS\Model\SiteTree.home
|
|
||||||
FieldName: Content
|
|
||||||
hash_link_about:
|
|
||||||
SiteTreeID: =>SilverStripe\CMS\Model\SiteTree.hash_link
|
|
||||||
ChildID: =>SilverStripe\CMS\Model\SiteTree.about
|
|
||||||
FieldName: Content
|
|
@ -1,113 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace SilverStripe\CMS\Tests\Tasks;
|
|
||||||
|
|
||||||
use SilverStripe\CMS\Tasks\RemoveOrphanedPagesTask;
|
|
||||||
use SilverStripe\Versioned\Versioned;
|
|
||||||
use SilverStripe\Dev\FunctionalTest;
|
|
||||||
use SilverStripe\Dev\Deprecation;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <h2>Fixture tree</h2>
|
|
||||||
* <code>
|
|
||||||
* parent1_published
|
|
||||||
* child1_1_published
|
|
||||||
* grandchild1_1_1
|
|
||||||
* grandchild1_1_2_published
|
|
||||||
* grandchild1_1_3_orphaned
|
|
||||||
* grandchild1_1_4_orphaned_published
|
|
||||||
* child1_2_published
|
|
||||||
* child1_3_orphaned
|
|
||||||
* child1_4_orphaned_published
|
|
||||||
* parent2
|
|
||||||
* child2_1_published_orphaned // is orphaned because parent is not published
|
|
||||||
* </code>
|
|
||||||
*
|
|
||||||
* <h2>Cleaned up tree</h2>
|
|
||||||
* <code>
|
|
||||||
* parent1_published
|
|
||||||
* child1_1_published
|
|
||||||
* grandchild1_1_1
|
|
||||||
* grandchild1_1_2_published
|
|
||||||
* child2_1_published_orphaned
|
|
||||||
* parent2
|
|
||||||
* </code>
|
|
||||||
*
|
|
||||||
* @author Ingo Schommer (<firstname>@silverstripe.com), SilverStripe Ltd.
|
|
||||||
*/
|
|
||||||
class RemoveOrphanedPagesTaskTest extends FunctionalTest
|
|
||||||
{
|
|
||||||
protected static $fixture_file = 'RemoveOrphanedPagesTaskTest.yml';
|
|
||||||
|
|
||||||
protected function setUp(): void
|
|
||||||
{
|
|
||||||
if (Deprecation::isEnabled()) {
|
|
||||||
$this->markTestSkipped('Test calls deprecated code');
|
|
||||||
}
|
|
||||||
parent::setUp();
|
|
||||||
|
|
||||||
$parent1_published = $this->objFromFixture('Page', 'parent1_published');
|
|
||||||
$parent1_published->publishSingle();
|
|
||||||
|
|
||||||
$child1_1_published = $this->objFromFixture('Page', 'child1_1_published');
|
|
||||||
$child1_1_published->publishSingle();
|
|
||||||
|
|
||||||
$child1_2_published = $this->objFromFixture('Page', 'child1_2_published');
|
|
||||||
$child1_2_published->publishSingle();
|
|
||||||
|
|
||||||
$child1_3_orphaned = $this->objFromFixture('Page', 'child1_3_orphaned');
|
|
||||||
$child1_3_orphaned->ParentID = 9999;
|
|
||||||
$child1_3_orphaned->write();
|
|
||||||
|
|
||||||
$child1_4_orphaned_published = $this->objFromFixture('Page', 'child1_4_orphaned_published');
|
|
||||||
$child1_4_orphaned_published->ParentID = 9999;
|
|
||||||
$child1_4_orphaned_published->write();
|
|
||||||
$child1_4_orphaned_published->publishSingle();
|
|
||||||
|
|
||||||
$grandchild1_1_2_published = $this->objFromFixture('Page', 'grandchild1_1_2_published');
|
|
||||||
$grandchild1_1_2_published->publishSingle();
|
|
||||||
|
|
||||||
$grandchild1_1_3_orphaned = $this->objFromFixture('Page', 'grandchild1_1_3_orphaned');
|
|
||||||
$grandchild1_1_3_orphaned->ParentID = 9999;
|
|
||||||
$grandchild1_1_3_orphaned->write();
|
|
||||||
|
|
||||||
$grandchild1_1_4_orphaned_published = $this->objFromFixture(
|
|
||||||
'Page',
|
|
||||||
'grandchild1_1_4_orphaned_published'
|
|
||||||
);
|
|
||||||
$grandchild1_1_4_orphaned_published->ParentID = 9999;
|
|
||||||
$grandchild1_1_4_orphaned_published->write();
|
|
||||||
$grandchild1_1_4_orphaned_published->publishSingle();
|
|
||||||
|
|
||||||
$child2_1_published_orphaned = $this->objFromFixture('Page', 'child2_1_published_orphaned');
|
|
||||||
$child2_1_published_orphaned->publishSingle();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetOrphansByStage()
|
|
||||||
{
|
|
||||||
// all orphans
|
|
||||||
$child1_3_orphaned = $this->objFromFixture('Page', 'child1_3_orphaned');
|
|
||||||
$child1_4_orphaned_published = $this->objFromFixture('Page', 'child1_4_orphaned_published');
|
|
||||||
$grandchild1_1_3_orphaned = $this->objFromFixture('Page', 'grandchild1_1_3_orphaned');
|
|
||||||
$grandchild1_1_4_orphaned_published = $this->objFromFixture(
|
|
||||||
'Page',
|
|
||||||
'grandchild1_1_4_orphaned_published'
|
|
||||||
);
|
|
||||||
$child2_1_published_orphaned = $this->objFromFixture('Page', 'child2_1_published_orphaned');
|
|
||||||
|
|
||||||
$task = singleton(RemoveOrphanedPagesTask::class);
|
|
||||||
$orphans = $task->getOrphanedPages();
|
|
||||||
$orphanIDs = $orphans->column('ID');
|
|
||||||
sort($orphanIDs);
|
|
||||||
$compareIDs = [
|
|
||||||
$child1_3_orphaned->ID,
|
|
||||||
$child1_4_orphaned_published->ID,
|
|
||||||
$grandchild1_1_3_orphaned->ID,
|
|
||||||
$grandchild1_1_4_orphaned_published->ID,
|
|
||||||
$child2_1_published_orphaned->ID
|
|
||||||
];
|
|
||||||
sort($compareIDs);
|
|
||||||
|
|
||||||
$this->assertEquals($orphanIDs, $compareIDs);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
Page:
|
|
||||||
parent1_published:
|
|
||||||
Title: Parent1
|
|
||||||
child1_1_published:
|
|
||||||
Title: Child1.1
|
|
||||||
Parent: =>Page.parent1_published
|
|
||||||
child1_2_published:
|
|
||||||
Title: Child1.2
|
|
||||||
Parent: =>Page.parent1_published
|
|
||||||
child1_3_orphaned:
|
|
||||||
Title: Child1.3
|
|
||||||
Parent: =>Page.parent1_published
|
|
||||||
child1_4_orphaned_published:
|
|
||||||
Title: Child1.4
|
|
||||||
Parent: =>Page.parent1_published
|
|
||||||
grandchild1_1_1:
|
|
||||||
Title: Grandchild1.1.1
|
|
||||||
Parent: =>Page.child1_1_published
|
|
||||||
grandchild1_1_2_published:
|
|
||||||
Title: Grandchild1.1.2
|
|
||||||
Parent: =>Page.child1_1_published
|
|
||||||
grandchild1_1_3_orphaned:
|
|
||||||
Title: Grandchild1.1.3
|
|
||||||
Parent: =>Page.child1_1_published
|
|
||||||
grandchild1_1_4_orphaned_published:
|
|
||||||
Title: Grandchild1.1.4
|
|
||||||
Parent: =>Page.child1_1_published
|
|
||||||
parent2:
|
|
||||||
Title: Parent2
|
|
||||||
child2_1_published_orphaned:
|
|
||||||
Title: Child2.1
|
|
||||||
Parent: =>Page.parent2
|
|
@ -1,19 +1,8 @@
|
|||||||
const Path = require('path');
|
const Path = require('path');
|
||||||
const webpackConfig = require('@silverstripe/webpack-config');
|
const { JavascriptWebpackConfig, CssWebpackConfig } = require('@silverstripe/webpack-config');
|
||||||
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
const CopyWebpackPlugin = require('copy-webpack-plugin');
|
||||||
const {
|
|
||||||
resolveJS,
|
|
||||||
externalJS,
|
|
||||||
moduleJS,
|
|
||||||
pluginJS,
|
|
||||||
moduleCSS,
|
|
||||||
pluginCSS,
|
|
||||||
} = webpackConfig;
|
|
||||||
|
|
||||||
const ENV = process.env.NODE_ENV;
|
|
||||||
const PATHS = {
|
const PATHS = {
|
||||||
MODULES: 'node_modules',
|
|
||||||
FILES_PATH: '../',
|
|
||||||
ROOT: Path.resolve(),
|
ROOT: Path.resolve(),
|
||||||
SRC: Path.resolve('client/src'),
|
SRC: Path.resolve('client/src'),
|
||||||
DIST: Path.resolve('client/dist'),
|
DIST: Path.resolve('client/dist'),
|
||||||
@ -21,46 +10,37 @@ const PATHS = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const config = [
|
const config = [
|
||||||
{
|
// Main JS bundles
|
||||||
name: 'js',
|
new JavascriptWebpackConfig('js', PATHS, 'silverstripe/cms')
|
||||||
entry: {
|
.setEntry({
|
||||||
bundle: `${PATHS.SRC}/bundles/bundle.js`,
|
bundle: `${PATHS.SRC}/bundles/bundle.js`,
|
||||||
// See https://github.com/webpack/webpack/issues/300#issuecomment-45313650
|
|
||||||
SilverStripeNavigator: `${PATHS.LEGACY_SRC}/SilverStripeNavigator.js`,
|
SilverStripeNavigator: `${PATHS.LEGACY_SRC}/SilverStripeNavigator.js`,
|
||||||
'TinyMCE_sslink-internal': `${PATHS.LEGACY_SRC}/TinyMCE_sslink-internal.js`,
|
'TinyMCE_sslink-internal': `${PATHS.LEGACY_SRC}/TinyMCE_sslink-internal.js`,
|
||||||
'TinyMCE_sslink-anchor': `${PATHS.LEGACY_SRC}/TinyMCE_sslink-anchor.js`,
|
'TinyMCE_sslink-anchor': `${PATHS.LEGACY_SRC}/TinyMCE_sslink-anchor.js`,
|
||||||
},
|
})
|
||||||
output: {
|
.mergeConfig({
|
||||||
path: PATHS.DIST,
|
plugins: [
|
||||||
filename: 'js/[name].js',
|
new CopyWebpackPlugin({
|
||||||
},
|
patterns: [
|
||||||
devtool: (ENV !== 'production') ? 'source-map' : '',
|
|
||||||
resolve: resolveJS(ENV, PATHS),
|
|
||||||
externals: externalJS(ENV, PATHS),
|
|
||||||
module: moduleJS(ENV, PATHS),
|
|
||||||
plugins: pluginJS(ENV, PATHS).concat([
|
|
||||||
new CopyWebpackPlugin([
|
|
||||||
{ from: 'client/src/images', to: 'images' },
|
|
||||||
])
|
|
||||||
])
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'css',
|
from: `${PATHS.SRC}/images`,
|
||||||
entry: {
|
to: `${PATHS.DIST}/images`
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
})
|
||||||
|
.getConfig(),
|
||||||
|
// sass to css
|
||||||
|
new CssWebpackConfig('css', PATHS)
|
||||||
|
.setEntry({
|
||||||
bundle: `${PATHS.SRC}/styles/bundle.scss`,
|
bundle: `${PATHS.SRC}/styles/bundle.scss`,
|
||||||
SilverStripeNavigator: `${PATHS.SRC}/styles/SilverStripeNavigator.scss`,
|
SilverStripeNavigator: `${PATHS.SRC}/styles/SilverStripeNavigator.scss`,
|
||||||
},
|
})
|
||||||
output: {
|
.getConfig(),
|
||||||
path: PATHS.DIST,
|
|
||||||
filename: 'styles/[name].css',
|
|
||||||
},
|
|
||||||
devtool: (ENV !== 'production') ? 'source-map' : '',
|
|
||||||
module: moduleCSS(ENV, PATHS),
|
|
||||||
plugins: pluginCSS(ENV, PATHS),
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
// Use WEBPACK_CHILD=js or WEBPACK_CHILD=css env var to run a single config
|
// Use WEBPACK_CHILD=js or WEBPACK_CHILD=css env var to run a single config
|
||||||
module.exports = (process.env.WEBPACK_CHILD)
|
module.exports = (process.env.WEBPACK_CHILD)
|
||||||
? config.find((entry) => entry.name === process.env.WEBPACK_CHILD)
|
? config.find((entry) => entry.name === process.env.WEBPACK_CHILD)
|
||||||
: module.exports = config;
|
: config;
|
||||||
|
Loading…
Reference in New Issue
Block a user