Compare commits

...

85 Commits
4 ... 5.0.7

Author SHA1 Message Date
github-actions
7427f615de Merge branch '4' into 5.0 2023-09-23 13:23:31 +00:00
Guy Sartorelli
6259ca30ad
Merge branch '4' into 5.0
# Conflicts:
#	client/dist/js/TinyMCE_sslink-anchor.js
#	client/dist/js/TinyMCE_sslink-internal.js
#	composer.json
#	tests/behat/features/insert-a-link.feature
#	tests/behat/features/insert-anchor-link.feature
2023-09-18 11:10:29 +12:00
Guy Sartorelli
4a92f5eb64
FIX Allow wrapping an image in a link (#2884) 2023-09-11 12:33:00 +12:00
Steve Boyd
7e42058206 Merge branch '4' into 5.0 2023-09-05 15:19:35 +12:00
Guy Sartorelli
d11915cffd
Merge pull request #2876 from creative-commoners/pulls/5.0/module-standardiser-1693278538
MNT Run module-standardiser
2023-08-29 17:08:18 +12:00
Steve Boyd
57c5dcbebb MNT Run module-standardiser 2023-08-29 15:08:58 +12:00
github-actions
b8e30925e8 Merge branch '4' into 5.0 2023-08-26 13:23:33 +00:00
github-actions
461cab09a0 Merge branch '4' into 5.0 2023-08-19 13:23:55 +00:00
Steve Boyd
b44ceb95ed Merge branch '4' into 5.0 2023-06-16 11:40:00 +12:00
Steve Boyd
1f2bf0a8a7 Merge branch '4' into 5.0 2023-05-31 14:40:54 +12:00
Steve Boyd
984cf404f4 Merge branch '4' into 5.0 2023-05-31 11:26:36 +12:00
Steve Boyd
989bb7944a Merge branch '4.13' into 4 2023-05-31 11:24:51 +12:00
Guy Sartorelli
81e7e1a01b
Merge pull request #2849 from creative-commoners/pulls/5.0/react-testing-library
MNT Use React Testing Library
2023-05-01 17:48:45 +12:00
Steve Boyd
28ecbdc845 MNT Use React Testing Library 2023-04-28 10:48:47 +12:00
Guy Sartorelli
ca837d6ccd
Merge branch '4' into 5.0 2023-04-27 14:44:59 +12:00
Sabina Talipova
6675f50f81
Merge pull request #2850 from creative-commoners/pulls/5.0/cms5-readme
DOC Update README.md for CMS 5
2023-04-21 15:29:11 +12:00
Guy Sartorelli
cfc48fad0a
DOC Update README.md for CMS 5 2023-04-19 17:38:49 +12:00
Maxime Rainville
0b50824e46
Merge pull request #2847 from creative-commoners/pulls/5.0/devjs
MNT Update dev JS
2023-04-03 15:32:17 +12:00
Steve Boyd
b1efcc217d MNT Update dev JS 2023-04-03 14:30:30 +12:00
Steve Boyd
f2d745afec Merge branch '4' into 5.0 2023-03-30 13:28:53 +13:00
Steve Boyd
f705df4b0c Merge branch '4' into 5.0 2023-03-08 12:22:34 +13:00
Steve Boyd
7fe9a04112 Merge branch '4' into 5.0 2023-03-02 16:22:11 +13:00
Guy Sartorelli
3f1dbe4d3f
Merge pull request #2836 from creative-commoners/pulls/5.0/fix-broken-builds
FIX Replace Diff class with HtmlDiff
2023-02-14 18:55:21 +13:00
Guy Sartorelli
abf00ede95
MNT Get JS tests passing again (#2834) 2023-02-13 15:20:56 +13:00
Sabina Talipova
93f57d2300 FIX Replace Diff class with HtmlDiff 2023-02-09 10:32:25 +13:00
Steve Boyd
222972c687 Merge branch '4' into 5 2023-02-02 16:34:19 +13:00
Guy Sartorelli
b02beba7ba
Resolve a couple of behat issues (#2830)
* FIX Correctly add message to reactstrap-confirm

* MNT Fix behat tests
2023-01-30 10:32:26 +13:00
Maxime Rainville
081eea42c5
Merge pull request #2824 from creative-commoners/pulls/5/remove-legacy-upgrader
MNT Remove legacy upgrader config
2023-01-23 10:36:04 +13:00
Steve Boyd
30685c9227 MNT Remove legacy upgrader config 2023-01-20 17:13:11 +13:00
Guy Sartorelli
37af732dbf
Merge pull request #2771 from xini/fix-5-remove-trailing-slash-from-sitetree-links
SS5: Remove trailing slash from SiteTree links if no action present
2023-01-20 15:46:39 +13:00
Florian Thoma
18cb6d499d
API Normalise trailing slashes in links 2023-01-20 15:08:37 +13:00
Maxime Rainville
2b0374e692
Merge pull request #2823 from creative-commoners/pulls/5/broken-builds
MNT Broken builds
2023-01-20 14:53:28 +13:00
Steve Boyd
f362d8b129 MNT Broken builds 2023-01-19 16:54:57 +13:00
Guy Sartorelli
9bb6f700b4
Merge pull request #2820 from creative-commoners/pulls/5/remove-translatable
ENH Remove unused Translatable code
2023-01-19 09:05:03 +13:00
Maxime Rainville
69fabe9a34
Merge pull request #2818 from creative-commoners/pulls/5/remove-legacy-tasks
API Remove legacy tasks
2023-01-18 14:22:01 +13:00
Steve Boyd
b57d557bc7 ENH Remove unused Translatable code 2023-01-18 14:07:39 +13:00
Maxime Rainville
97fdff4bb2
API Remove deprecated CMSMain::$subitem_class config (#2821) 2023-01-18 11:06:45 +13:00
Maxime Rainville
50cefa6d14 Merge branch '4' into 5 2023-01-17 22:35:51 +13:00
Steve Boyd
1bd578fc96 API Remove legacy tasks 2023-01-17 12:31:51 +13:00
Guy Sartorelli
61d729285f
Merge pull request #2814 from creative-commoners/pulls/5/lint
MNT Add yarn lint back in
2023-01-13 15:40:31 +13:00
Steve Boyd
a44547915d MNT Add yarn lint back in 2023-01-13 15:17:54 +13:00
Maxime Rainville
e7d9d1b36e
Merge pull request #2813 from creative-commoners/pulls/5/fix-behat2
Fix anchor select field component
2023-01-11 17:53:01 +13:00
Maxime Rainville
3c7459208e
Merge pull request #2811 from creative-commoners/pulls/5/fix-behat
MNT Fix behat test for inserting WYSIWYG links
2023-01-11 17:17:31 +13:00
Guy Sartorelli
ef4122da04
ENH Use the EmotionCssCacheProvider component
This ensures we can override the react-select CSS if we need to.
2023-01-11 16:06:36 +13:00
Guy Sartorelli
b9115f3f6d
Fix anchor select field component
This should have been done when we upgraded react-select. My bad.
2023-01-10 12:34:13 +13:00
Guy Sartorelli
dd79cf5a23
MNT Fix behat test for inserting WYSIWYG links
The selectors for TreeDropdownField's markup changed when we updated
react-select.
2023-01-09 13:01:49 +13:00
Guy Sartorelli
cba1acb88e
Merge pull request #2809 from creative-commoners/pulls/5/remove-silverstripe-version-file
MNT Remove silverstripe_version file
2022-12-20 12:11:47 +13:00
Guy Sartorelli
bd42008584
MNT Remove silverstripe_version file
This file hasn't been updated since 2012 and was only referenced in the now-removed `SilverStripe\Dev\SapphireInfo` class.
2022-12-20 11:35:45 +13:00
Guy Sartorelli
5e843d2619
DEP Upgrade build stack (#2795)
* DEP Upgrade webpack config and various deps

* DEP Update code to work with upgraded libraries

* MNT run yarn build
2022-12-19 10:21:10 +13:00
Guy Sartorelli
ddbe4ea4ce
DEP Upgrade build stack (#2795)
* DEP Upgrade webpack config and various deps

* DEP Update code to work with upgraded libraries

* MNT run yarn build
2022-12-19 10:20:52 +13:00
Guy Sartorelli
145f063c33
Merge branch '4' into 5 2022-12-14 15:22:01 +13:00
Guy Sartorelli
03bac4b72d
Merge pull request #2806 from creative-commoners/pulls/5/absolute-link
FIX Cast absoluteUrl() argument to string
2022-12-13 16:27:07 +13:00
Steve Boyd
605daf5026 FIX Cast absoluteUrl() argument to string 2022-12-09 10:02:43 +13:00
Sabina Talipova
0e5428b78b
API Remove deprecated code (#2800) 2022-12-08 10:44:36 +13:00
Guy Sartorelli
8b1433b263
Merge pull request #2804 from creative-commoners/pulls/5/sortraw
FIX Use orderBy() instead of sort()
2022-12-07 14:53:18 +13:00
Steve Boyd
3186e0e129 FIX Use orderBy() instead of sort() 2022-12-05 17:46:44 +13:00
Guy Sartorelli
911a71d6f6
Merge branch '4' into '5' 2022-11-21 18:07:57 +13:00
Guy Sartorelli
003f9ba750
Revert "Merge branch '4' into 5"
This reverts commit 2d10a624b3, reversing
changes made to af1a482d20.
2022-11-21 17:11:42 +13:00
Sabina Talipova
2d10a624b3 Merge branch '4' into 5 2022-11-21 16:59:42 +13:00
Guy Sartorelli
af1a482d20
FIX Loosen config order to allow "after: '*'" in framework (#2793) 2022-10-19 15:15:48 +13:00
Guy Sartorelli
8320023526
Merge pull request #2788 from creative-commoners/pulls/5/action-signature
API Strongly-type action method signatures
2022-10-19 10:08:22 +13:00
Steve Boyd
6e19ae737f API Strongly-type action method signatures 2022-10-18 18:21:09 +13:00
Guy Sartorelli
9b64c7de24
Merge branch '4' into 5 2022-10-13 11:18:14 +13:00
Sabina Talipova
ea9ce63438
Merge pull request #2779 from creative-commoners/pulls/5/previewable-via-extension
ENH Records can be made previewable via an extension
2022-10-07 13:34:19 +13:00
Guy Sartorelli
6ff98c4201
ENH Records can be made previewable via an extension 2022-10-06 15:51:30 +13:00
Sabina Talipova
e5cea70b54
Merge pull request #2761 from creative-commoners/pulls/5/migrate-preview-bits
API Migrate SilverStripeNagivator classes
2022-10-06 13:46:38 +13:00
Guy Sartorelli
d7857ebbe0
API Migrate SilverStripeNagivator classes
These classes are useful with `silverstripe/admin` and
`silverstripe/versioned` without needing `silverstripe/cms`.
2022-10-05 14:24:11 +13:00
Sabina Talipova
390f078551
Merge pull request #2781 from creative-commoners/pulls/5/tinymce6
FIX Fix link plugins to support tinymce6
2022-10-03 12:10:14 +13:00
Guy Sartorelli
bedd64554f
FIX Use correct tinymce selection logic 2022-09-30 18:26:13 +13:00
Guy Sartorelli
7368df8757
FIX Fix link plugins to support tinymce6 2022-09-30 15:27:38 +13:00
Guy Sartorelli
d56682509c
Merge branch '4' into 5 2022-09-02 11:01:22 +12:00
Steve Boyd
aa4ba82f38
Merge pull request #2769 from creative-commoners/pulls/5/rescue-master-remove-history-viewer
API Rescue Master Branch PR: Remove deprecated CMSPageHistoryViewer
2022-09-01 14:50:58 +12:00
Guy Sartorelli
f2c4423d0c
Merge branch '4' into 5 2022-08-31 13:56:26 +12:00
Florian Thoma
1711c0c88e
API Move updateRelativeLink hook after concatination (#2770)
* move updateRelativeLink hook after concatination to make it actually updatable

* keep existing parameters the same

* revert to link parameter be first

* update updateRelativeLink method signature in SiteTreeExtension

* don't pass old parameters by reference
2022-08-31 12:33:05 +12:00
Guy Sartorelli
adcea213a2
ENH Remove css for CMSPageHistoryController. 2022-08-30 11:14:51 +12:00
Steve Boyd
f9a19e7429
Merge pull request #2768 from creative-commoners/pulls/5/rescue-master-canberoot-check
Rescue Master Branch PR: FIX Don't offer to restore to root if can_be_root is false
2022-08-29 18:07:56 +12:00
Guy Sartorelli
803b4add7b
FIX Resolve CI failure 2022-08-25 16:18:07 +12:00
Aaron Carlino
cda7857e12
API: Remove deprecated CMSPageHistoryViewer 2022-08-25 16:18:01 +12:00
Mike Cochrane
b46876b8a8
FIX Don't offer to restore to root if can_be_root is false 2022-08-25 14:18:50 +12:00
Steve Boyd
c130b55ecb
Merge pull request #2766 from creative-commoners/pulls/5/rescue-master-sitetree-not-page
Rescue Master Branch PR: MNT Use SiteTree instead of Page in tests
2022-08-25 11:47:14 +12:00
Mike Cochrane
2219899c10
MNT Use SiteTree instead of Page in tests 2022-08-25 10:17:41 +12:00
Guy Sartorelli
859ff00184
Merge pull request #2763 from creative-commoners/pulls/5/other-deps
DEP Update dependencies for CMS 5
2022-08-10 11:14:52 +12:00
Steve Boyd
7381de15e8 DEP Update dependencies for CMS 5 2022-08-09 17:24:39 +12:00
Guy Sartorelli
e580527111
Merge pull request #2759 from creative-commoners/pulls/5/major-deps
DEP Update core dependencies for CMS 5
2022-08-09 09:41:32 +12:00
Steve Boyd
72f8e5f71d DEP Update core dependencies for CMS 5 2022-08-04 17:36:54 +12:00
151 changed files with 7391 additions and 13093 deletions

View File

@ -1,10 +1,10 @@
name: Keepalive
on:
workflow_dispatch:
# The 4th of every month at 10:50am UTC
# At 6:30 PM UTC, on day 15 of the month
schedule:
- cron: '50 10 4 * *'
- cron: '30 18 15 * *'
workflow_dispatch:
jobs:
keepalive:

17
.github/workflows/merge-up.yml vendored Normal file
View 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

View File

@ -4,7 +4,7 @@ on:
workflow_dispatch:
# Run on a schedule of once per quarter
schedule:
- cron: '0 0 1 */3 *'
- cron: '30 18 1 */3 *'
jobs:
update-js:

2
.nvmrc
View File

@ -1 +1 @@
10
18

View File

@ -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'

View File

@ -1,4 +1,4 @@
Copyright (c) 2016, SilverStripe Ltd.
Copyright (c) 2016, Silverstripe Ltd.
All rights reserved.
Redistribution and use in source and binary forms, with or without

View File

@ -7,7 +7,7 @@ PHP Content Management System (CMS), see [http://silverstripe.org](http://silver
## 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

View File

@ -4,7 +4,6 @@ use SilverStripe\Admin\CMSMenu;
use SilverStripe\CMS\Controllers\CMSMain;
use SilverStripe\CMS\Controllers\CMSPageAddController;
use SilverStripe\CMS\Controllers\CMSPageEditController;
use SilverStripe\CMS\Controllers\CMSPageHistoryController;
use SilverStripe\CMS\Controllers\CMSPageSettingsController;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Core\Manifest\ModuleLoader;
@ -37,8 +36,4 @@ ShortcodeParser::get('default')->register(
CMSMenu::remove_menu_class(CMSMain::class);
CMSMenu::remove_menu_class(CMSPageEditController::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);

View File

@ -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

View File

@ -5,8 +5,6 @@ After: '#coreroutes'
SilverStripe\Control\Director:
rules:
'': 'SilverStripe\CMS\Controllers\RootURLController'
'RemoveOrphanedPagesTask//$Action/$ID/$OtherID': 'SilverStripe\CMS\Tasks\RemoveOrphanedPagesTask'
'SiteTreeMaintenanceTask//$Action/$ID/$OtherID': 'SilverStripe\CMS\Tasks\SiteTreeMaintenanceTask'
---
Name: legacycmsroutes
---

View File

@ -1,6 +1,6 @@
---
Name: cmsversion
After: 'framework/*'
After: 'framework/coreconfig'
---
SilverStripe\Core\Manifest\VersionProvider:
modules:

View File

@ -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
View File

@ -0,0 +1,6 @@
{
"presets": [
"@babel/preset-env",
"@babel/preset-react"
]
}

View File

@ -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()}))}}()}();

View File

@ -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");l.default.addAction("sslink",{text:o.default._t("CMS.LINKLABEL_ANCHOR","Anchor on a page"),onclick:function(e){return e.execCommand("sslinkanchor")},priority:60},editorIdentifier).addCommandWithUrlTest("sslinkanchor",/^\[sitetree_link.+]#[^#\]]+$/);var O={init:function(e){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";t.default.addAction("sslink",{text:e.default._t("CMS.LINKLABEL_ANCHOR","Anchor on a page"),onAction:e=>e.execCommand(f),priority:60},editorIdentifier).addCommandWithUrlTest(f,/^\[sitetree_link.+]#[^#\]]+$/);const E={init(e){e.addCommand(f,(()=>{const t=(0,d.default)(`#${e.id}`).entwine("ss"),n=Number((0,d.default)("#Form_EditForm_ID").val()||0),r=(0,d.default)(e.getBody()).find("[id],[name]").toArray().map((e=>e.id||e.name));ss.store.dispatch((0,c.updatedCurrentField)(n,r,e.id)),t.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)))}()}();

View File

@ -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),a=n(9),l=r(a),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);l.default.addAction("sslink",{text:o.default._t("CMS.LINKLABEL_PAGE","Page on this site"),onclick:function(e){return e.execCommand("sslinkinternal")},priority:90},editorIdentifier).addCommandWithUrlTest("sslinkinternal",/^\[sitetree_link.+]$/);var I={init:function(e){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()},a=function(){return t.handleInsert.apply(t,arguments)},l=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:a,onClosed:i,title:o.default._t("CMS.LINK_PAGE","Link to a page"),bodyClassName:"modal__dialog",className:"insert-link__dialog-wrapper--internal",fileAttributes:l,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";t.default.addAction("sslink",{text:e.default._t("CMS.LINKLABEL_PAGE","Page on this site"),onAction:e=>e.execCommand(p),priority:90},editorIdentifier).addCommandWithUrlTest(p,/^\[sitetree_link.+]$/);const f={init(e){e.addCommand(p,(()=>{(0,l.default)(`#${e.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)))}()}();

View File

@ -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={},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]}a.default=e,n&&n.set(e,a);return 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=(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.ConnectedAnchorSelectorField=S;var E=(0,f.default)(S);t.default=E},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);const i=((a=n(306))&&a.__esModule?a:{default:a}).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
}
}
}
}
`;t.query=i;const r={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.config=r;var s=(0,o.graphql)(i,r);t.default=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);const i=((a=n(306))&&a.__esModule?a:{default:a}).default`
mutation rollbackPage($id:ID!, $toVersion:Int!) {
rollbackPage(
id: $id
toVersion: $toVersion
) {
id
}
}
`;t.mutation=i;const r={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.config=r;var s=(0,o.graphql)(i,r);t.default=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)}();

View File

@ -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}

View File

@ -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}

View File

@ -2,7 +2,6 @@ require('../legacy/CMSMain.AddForm');
require('../legacy/CMSMain.EditForm');
require('../legacy/CMSMain');
require('../legacy/CMSMain.Tree');
require('../legacy/CMSPageHistoryController');
require('../legacy/RedirectorPage');
require('../legacy/SiteTreeURLSegmentField');

View File

@ -8,7 +8,8 @@ import SilverStripeComponent from 'lib/SilverStripeComponent';
import * as anchorSelectorActions from 'state/anchorSelector/AnchorSelectorActions';
import anchorSelectorStates from 'state/anchorSelector/AnchorSelectorStates';
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 classnames from 'classnames';
import PropTypes from 'prop-types';
@ -117,26 +118,27 @@ class AnchorSelectorField extends SilverStripeComponent {
}
render() {
const inputProps = {
id: this.props.id,
};
const className = classnames('anchorselectorfield', this.props.extraClass);
const { extraClass, CreatableSelectComponent } = this.props;
const className = classnames('anchorselectorfield', extraClass);
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');
return (
<Creatable
searchable
options={options}
className={className}
name={this.props.name}
inputProps={inputProps}
onChange={this.handleChange}
onBlurResetsInput
value={value}
placeholder={placeholder}
labelKey="value"
/>
<EmotionCssCacheProvider>
<CreatableSelectComponent
isSearchable
isClearable
options={options}
className={className}
name={this.props.name}
onChange={this.handleChange}
value={{ value: rawValue }}
noOptionsMessage={() => i18n._t('CMS.ANCHOR_NO_OPTIONS', 'No options')}
placeholder={placeholder}
getOptionLabel={({ value }) => value}
classNamePrefix="anchorselectorfield"
/>
</EmotionCssCacheProvider>
);
}
}
@ -165,6 +167,7 @@ AnchorSelectorField.defaultProps = {
extraClass: '',
onLoadingError: noop,
attributes: {},
CreatableSelectComponent: CreatableSelect
};
function mapStateToProps(state, ownProps) {

View File

@ -1,4 +1,9 @@
/* global jest, describe, beforeEach, it, expect, setTimeout */
/* global jest, test, describe, beforeEach, it, expect, setTimeout */
import React from 'react';
import { Component as AnchorSelectorField } from '../AnchorSelectorField';
import anchorSelectorStates from 'state/anchorSelector/AnchorSelectorStates';
import { render, screen } from '@testing-library/react';
jest.mock('isomorphic-fetch', () =>
() => Promise.resolve({
@ -6,98 +11,119 @@ jest.mock('isomorphic-fetch', () =>
}));
jest.mock('i18n');
import React from 'react';
import ReactTestUtils from 'react-dom/test-utils';
import { Component as AnchorSelectorField } from '../AnchorSelectorField';
import anchorSelectorStates from 'state/anchorSelector/AnchorSelectorStates';
function makeProps(obj = {}) {
return {
id: 'Form_Test',
name: 'Test',
data: {
endpoint: 'url-callback',
},
pageId: 4,
anchors: ['anchor1', 'anchor2'],
value: 'selectedanchor',
loadingState: anchorSelectorStates.SUCCESS,
CreatableSelectComponent: ({ options }) => (
<div data-testid="test-creatable-select">
{options.map(option => <div key={option.value} data-option={option.value}/>)}
</div>
),
...obj,
};
}
describe('AnchorSelectorField', () => {
let props = null;
let field = null;
beforeEach(() => {
props = {
id: 'Form_Test',
name: 'Test',
data: {
endpoint: 'url-callback',
test('AnchorSelectorField componentDidMount() Loads dirty selectors', async () => {
const beginUpdating = jest.fn();
render(<AnchorSelectorField {...makeProps({
loadingState: anchorSelectorStates.DIRTY,
actions: {
anchorSelector: {
beginUpdating,
updated: () => {},
updateFailed: () => {},
},
pageId: 4,
anchors: ['anchor1', 'anchor2'],
value: 'selectedanchor',
loadingState: anchorSelectorStates.SUCCESS,
actions: {
anchorSelector: {
beginUpdating: jest.fn(),
updated: jest.fn(),
updateFailed: jest.fn(),
},
},
};
});
describe('componentDidMount()', () => {
it('Loads dirty selectors', () => {
props.loadingState = anchorSelectorStates.DIRTY;
field = ReactTestUtils.renderIntoDocument(<AnchorSelectorField {...props} />);
expect(props.actions.anchorSelector.beginUpdating)
.toHaveBeenCalledWith(4);
});
it('Does not load success selectors', () => {
props.loadingState = anchorSelectorStates.SUCCESS;
field = ReactTestUtils.renderIntoDocument(<AnchorSelectorField {...props} />);
expect(props.actions.anchorSelector.beginUpdating)
.not
.toHaveBeenCalled();
});
});
describe('getDropdownOptions()', () => {
it('Merges value with page anchors', () => {
field = ReactTestUtils.renderIntoDocument(<AnchorSelectorField {...props} />);
expect(field.getDropdownOptions()).toEqual([
{ value: 'selectedanchor' },
{ value: 'anchor1' },
{ value: 'anchor2' },
]);
});
});
describe('ensurePagesLoaded', () => {
it('Triggers loading on dirty', () => {
props.loadingState = anchorSelectorStates.DIRTY;
field = ReactTestUtils.renderIntoDocument(<AnchorSelectorField {...props} />);
return field
.ensurePagesLoaded()
.then((result) => {
expect(props.actions.anchorSelector.beginUpdating)
.toHaveBeenCalledWith(4);
expect(props.actions.anchorSelector.updated)
.toHaveBeenCalledWith(4, ['anchor3', 'anchor4']);
expect(props.actions.anchorSelector.updateFailed)
.not
.toHaveBeenCalled();
expect(result).toEqual(['anchor3', 'anchor4']);
});
});
it('Does not trigger updating', () => {
props.loadingState = anchorSelectorStates.UPDATING;
field = ReactTestUtils.renderIntoDocument(<AnchorSelectorField {...props} />);
return field
.ensurePagesLoaded()
.then((result) => {
expect(props.actions.anchorSelector.beginUpdating)
.not
.toHaveBeenCalled();
expect(props.actions.anchorSelector.updated)
.not
.toHaveBeenCalled();
expect(props.actions.anchorSelector.updateFailed)
.not
.toHaveBeenCalled();
expect(result).toBe(undefined);
});
});
});
},
})}
/>);
await screen.findByTestId('test-creatable-select');
expect(beginUpdating).toBeCalledWith(4);
});
test('AnchorSelectorField Merges value with page anchors', async () => {
const beginUpdating = jest.fn();
const { container } = render(<AnchorSelectorField {...makeProps({
loadingState: anchorSelectorStates.DIRTY,
actions: {
anchorSelector: {
beginUpdating,
updated: () => {},
updateFailed: () => {},
},
},
})}
/>);
const select = await screen.findByTestId('test-creatable-select');
const options = select.querySelectorAll('[data-option]');
expect(options).toHaveLength(3);
expect(options[0].getAttribute('data-option')).toBe('selectedanchor');
expect(options[1].getAttribute('data-option')).toBe('anchor1');
expect(options[2].getAttribute('data-option')).toBe('anchor2');
});
test('AnchorSelectorField componentDidMount() Does not load success selectors', async () => {
const beginUpdating = jest.fn();
render(<AnchorSelectorField {...makeProps({
loadingState: anchorSelectorStates.SUCCESS,
actions: {
anchorSelector: {
beginUpdating,
updated: () => {},
updateFailed: () => {},
},
},
})}
/>);
await screen.findByTestId('test-creatable-select');
expect(beginUpdating).not.toBeCalled();
});
test('AnchorSelectorField ensurePagesLoaded Triggers loading on dirty', async () => {
const beginUpdating = jest.fn();
const updated = jest.fn();
const updateFailed = jest.fn();
render(<AnchorSelectorField {...makeProps({
loadingState: anchorSelectorStates.DIRTY,
actions: {
anchorSelector: {
beginUpdating,
updated,
updateFailed,
},
},
})}
/>);
await screen.findByTestId('test-creatable-select');
expect(beginUpdating).toBeCalledWith(4);
expect(updated).toBeCalledWith(4, ['anchor3', 'anchor4']);
expect(updateFailed).not.toBeCalled();
});
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();
});

View File

@ -3,7 +3,7 @@
*/
import $ from 'jquery';
import i18n from 'i18n';
import reactConfirm from "@silverstripe/reactstrap-confirm";
import reactConfirm from 'reactstrap-confirm';
$.entwine('ss', function($){
/**
@ -497,12 +497,13 @@ $.entwine('ss', function($){
'By changing the URL segment visitors will not be able to view it.'
);
if (await reactConfirm(message, {
if (await reactConfirm({
title: i18n._t(
'CMS.RemoveHomePageWarningTitle',
'Remove your home page?'
),
confirmLabel: i18n._t(
message,
confirmText: i18n._t(
'CMS.RemoveHomePageWarningLabel',
'Remove'
),

View File

@ -1,6 +1,7 @@
import $ from 'jquery';
import i18n from 'i18n';
import reactConfirm from "@silverstripe/reactstrap-confirm";
import reactConfirm from 'reactstrap-confirm';
import { joinUrlPaths } from 'lib/urls';
$.entwine('ss.tree', function($) {
$('.cms-tree').entwine({
@ -45,7 +46,7 @@ $.entwine('ss.tree', function($) {
});
const baseUrl = $('base').attr('href') || ''; // Edge17 and IE11 require absolute paths
window.location.assign(baseUrl + urlWithParams);
window.location.assign(joinUrlPaths(baseUrl, urlWithParams));
},
getTreeConfig: function() {
@ -168,12 +169,13 @@ $.entwine('ss.tree', function($) {
'By changing the URL segment visitors will not be able to view it.'
);
return await reactConfirm(message, {
return await reactConfirm({
title: i18n._t(
'CMS.RemoveHomePageWarningTitle',
'Remove your home page?'
),
confirmLabel: i18n._t(
message,
confirmText: i18n._t(
'CMS.RemoveHomePageWarningLabel',
'Remove'
),

View File

@ -1,4 +1,5 @@
import $ from 'jquery';
import { joinUrlPaths } from 'lib/urls';
/**
* Behaviour for the CMS Content Toolbar.
@ -81,7 +82,7 @@ $.entwine('ss', function ($) {
localStorage.setItem('ss.pages-view-type', viewType);
if(isContentViewInSidebar && viewType === VIEW_TYPE_LIST) {
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;
}

View File

@ -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);
},
});
});

View File

@ -2,8 +2,8 @@
import i18n from 'i18n';
import TinyMCEActionRegistrar from 'lib/TinyMCEActionRegistrar';
import React from 'react';
import ReactDOM from 'react-dom';
import { ApolloProvider } from 'react-apollo';
import { createRoot } from 'react-dom/client';
import { ApolloProvider } from '@apollo/client';
import { Provider } from 'react-redux';
import jQuery from 'jquery';
import ShortcodeSerialiser from 'lib/ShortcodeSerialiser';
@ -19,7 +19,7 @@ TinyMCEActionRegistrar
'sslink',
{
text: i18n._t('CMS.LINKLABEL_ANCHOR', 'Anchor on a page'),
onclick: (activeEditor) => activeEditor.execCommand(commandName),
onAction: (activeEditor) => activeEditor.execCommand(commandName),
priority: 60,
},
editorIdentifier,
@ -32,8 +32,8 @@ const plugin = {
const field = jQuery(`#${editor.id}`).entwine('ss');
// Get the anchors in the current field and save them as props for AnchorSelectorField
const currentPageID = Number(jQuery('#Form_EditForm_ID').val() || 0);
const validTargets = editor
.$('[id],[name]', editor.getBody())
const validTargets = jQuery(editor.getBody())
.find('[id],[name]')
.toArray()
.map((element) => element.id || element.name);
ss.store.dispatch(updatedCurrentField(currentPageID, validTargets, editor.id));
@ -68,6 +68,8 @@ jQuery.entwine('ss', ($) => {
* Assumes that $('.insert-link__dialog-wrapper').entwine({}); is defined for shared functions
*/
$(`#${modalId}`).entwine({
ReactRoot: null,
renderModal(isOpen) {
const store = ss.store;
const client = ss.apolloClient;
@ -78,7 +80,12 @@ jQuery.entwine('ss', ($) => {
const currentPageID = Number($('#Form_EditForm_ID').val() || 0);
// 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}>
<Provider store={store}>
<InsertLinkInternalModal
@ -94,8 +101,7 @@ jQuery.entwine('ss', ($) => {
currentPageID={currentPageID}
/>
</Provider>
</ApolloProvider>,
this[0]
</ApolloProvider>
);
},

View File

@ -2,8 +2,8 @@
import i18n from 'i18n';
import TinyMCEActionRegistrar from 'lib/TinyMCEActionRegistrar';
import React from 'react';
import ReactDOM from 'react-dom';
import { ApolloProvider } from 'react-apollo';
import { createRoot } from 'react-dom/client';
import { ApolloProvider } from '@apollo/client';
import { Provider } from 'react-redux';
import jQuery from 'jquery';
import ShortcodeSerialiser from 'lib/ShortcodeSerialiser';
@ -18,7 +18,7 @@ TinyMCEActionRegistrar
'sslink',
{
text: i18n._t('CMS.LINKLABEL_PAGE', 'Page on this site'),
onclick: (activeEditor) => activeEditor.execCommand(commandName),
onAction: (activeEditor) => activeEditor.execCommand(commandName),
priority: 90,
},
editorIdentifier,
@ -60,6 +60,8 @@ jQuery.entwine('ss', ($) => {
* Assumes that $('.insert-link__dialog-wrapper').entwine({}); is defined for shared functions
*/
$(`#${modalId}`).entwine({
ReactRoot: null,
renderModal(isOpen) {
const store = ss.store;
const client = ss.apolloClient;
@ -69,7 +71,12 @@ jQuery.entwine('ss', ($) => {
const requireLinkText = this.getRequireLinkText();
// 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}>
<Provider store={store}>
<InsertLinkInternalModal
@ -84,8 +91,7 @@ jQuery.entwine('ss', ($) => {
requireLinkText={requireLinkText}
/>
</Provider>
</ApolloProvider>,
this[0]
</ApolloProvider>
);
},

View File

@ -1,4 +1,4 @@
import { graphql } from 'react-apollo';
import { graphql } from '@apollo/client/react/hoc';
import gql from 'graphql-tag';
// GraphQL query for retrieving the version history of a specific page. The

View File

@ -1,4 +1,4 @@
import { graphql } from 'react-apollo';
import { graphql } from '@apollo/client/react/hoc';
import gql from 'graphql-tag';
const mutation = gql`

View File

@ -1,4 +1,4 @@
import { graphql } from 'react-apollo';
import { graphql } from '@apollo/client/react/hoc';
import gql from 'graphql-tag';
const mutation = gql`

View File

@ -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)
* -------------------------------------------- */

View File

@ -4,6 +4,7 @@ namespace SilverStripe\CMS\BatchActions;
use SilverStripe\ORM\SS_List;
use SilverStripe\Admin\CMSBatchAction;
use SilverStripe\Control\HTTPResponse;
/**
* Delete items batch action.
@ -15,7 +16,7 @@ class CMSBatchAction_Archive extends CMSBatchAction
return _t(__CLASS__ . '.TITLE', 'Unpublish and archive');
}
public function run(SS_List $pages)
public function run(SS_List $pages): HTTPResponse
{
return $this->batchaction(
$pages,

View File

@ -3,6 +3,7 @@
namespace SilverStripe\CMS\BatchActions;
use SilverStripe\Admin\CMSBatchAction;
use SilverStripe\Control\HTTPResponse;
use SilverStripe\ORM\SS_List;
/**
@ -15,7 +16,7 @@ class CMSBatchAction_Publish extends CMSBatchAction
return _t(__CLASS__ . '.PUBLISH_PAGES', 'Publish');
}
public function run(SS_List $pages)
public function run(SS_List $pages): HTTPResponse
{
return $this->batchaction(
$pages,

View File

@ -4,6 +4,7 @@ namespace SilverStripe\CMS\BatchActions;
use SilverStripe\Admin\CMSBatchAction;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Control\HTTPResponse;
use SilverStripe\ORM\ArrayList;
use SilverStripe\ORM\SS_List;
use SilverStripe\Versioned\Versioned;
@ -20,7 +21,7 @@ class CMSBatchAction_Restore extends CMSBatchAction
return _t(__CLASS__ . '.RESTORE', 'Restore');
}
public function run(SS_List $pages)
public function run(SS_List $pages): HTTPResponse
{
// Sort pages by depth
$pageArray = $pages->toArray();

View File

@ -3,6 +3,7 @@
namespace SilverStripe\CMS\BatchActions;
use SilverStripe\Admin\CMSBatchAction;
use SilverStripe\Control\HTTPResponse;
use SilverStripe\ORM\SS_List;
/**
@ -15,7 +16,7 @@ class CMSBatchAction_Unpublish extends CMSBatchAction
return _t(__CLASS__ . '.UNPUBLISH_PAGES', 'Unpublish');
}
public function run(SS_List $pages)
public function run(SS_List $pages): HTTPResponse
{
return $this->batchaction(
$pages,

View File

@ -2,13 +2,13 @@
namespace SilverStripe\CMS\Controllers;
use SilverStripe\Dev\Deprecation;
use InvalidArgumentException;
use Psr\SimpleCache\CacheInterface;
use SilverStripe\Admin\AdminRootController;
use SilverStripe\Admin\CMSBatchActionHandler;
use SilverStripe\Admin\LeftAndMain;
use SilverStripe\Admin\LeftAndMainFormRequestHandler;
use SilverStripe\Admin\Navigator\SilverStripeNavigator;
use SilverStripe\CMS\BatchActions\CMSBatchAction_Archive;
use SilverStripe\CMS\BatchActions\CMSBatchAction_Publish;
use SilverStripe\CMS\BatchActions\CMSBatchAction_Restore;
@ -23,6 +23,7 @@ use SilverStripe\Control\Director;
use SilverStripe\Control\HTTPRequest;
use SilverStripe\Control\HTTPResponse;
use SilverStripe\Control\HTTPResponse_Exception;
use SilverStripe\Control\PjaxResponseNegotiator;
use SilverStripe\Core\Cache\MemberCacheFlusher;
use SilverStripe\Core\Config\Config;
use SilverStripe\Core\Convert;
@ -69,10 +70,9 @@ use SilverStripe\SiteConfig\SiteConfig;
use SilverStripe\Versioned\ChangeSet;
use SilverStripe\Versioned\ChangeSetItem;
use SilverStripe\Versioned\Versioned;
use SilverStripe\VersionedAdmin\Controllers\CMSPageHistoryViewerController;
use SilverStripe\View\ArrayData;
use SilverStripe\View\Requirements;
use Translatable;
use SilverStripe\VersionedAdmin\Controllers\CMSPageHistoryViewerController;
/**
* The main "content" area of the CMS.
@ -107,12 +107,6 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
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 $required_permission_codes = 'CMS_ACCESS_CMSMain';
@ -188,11 +182,6 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
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();
Requirements::javascript('silverstripe/cms: client/dist/js/bundle.js');
@ -200,7 +189,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
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('archive', CMSBatchAction_Archive::class);
@ -208,7 +197,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
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,
// to avoid it being highlighted in the tree, and causing an edit form to show.
@ -219,7 +208,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
return parent::index($request);
}
public function getResponseNegotiator()
public function getResponseNegotiator(): PjaxResponseNegotiator
{
$negotiator = parent::getResponseNegotiator();
@ -414,10 +403,13 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
public function LinkPageHistory()
{
if ($id = $this->currentPageID()) {
return $this->LinkWithSearch(
Controller::join_links(CMSPageHistoryViewerController::singleton()->Link('show'), $id)
);
$controller = Injector::inst()->get(CMSPageHistoryViewerController::class);
if (($id = $this->currentPageID()) && $controller) {
if ($controller) {
return $this->LinkWithSearch(
Controller::join_links($controller->Link('show'), $id)
);
}
} else {
return null;
}
@ -664,11 +656,8 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
/**
* Get a subtree underneath the request param 'ID'.
* 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(
$this->config()->get('tree_class'),
@ -683,7 +672,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
$html = preg_replace('/^[\s\t\r\n]*<ul[^>]*>/', '', $html ?? '');
$html = preg_replace('/<\/ul[^>]*>[\s\t\r\n]*$/', '', $html ?? '');
return $html;
return $this->getResponse()->setBody($html);
}
/**
@ -691,11 +680,8 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
* Similar to {@link getsubtree()}, but doesn't enforce loading
* all children with the node. Useful to refresh views after
* state modifications, e.g. saving a form.
*
* @param HTTPRequest $request
* @return HTTPResponse
*/
public function updatetreenodes($request)
public function updatetreenodes(HTTPRequest $request): HTTPResponse
{
$data = [];
$ids = explode(',', $request->getVar('ids') ?? '');
@ -763,17 +749,15 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
* - '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.
*
* @param HTTPRequest $request
* @return HTTPResponse JSON string with a
* @throws HTTPResponse_Exception
*/
public function savetreenode($request)
public function savetreenode(HTTPRequest $request): HTTPResponse
{
if (!SecurityToken::inst()->checkRequest($request)) {
return $this->httpError(400);
$this->httpError(400);
}
if (!$this->CanOrganiseSitetree()) {
return $this->httpError(
$this->httpError(
403,
_t(
__CLASS__.'.CANT_REORGANISE',
@ -786,14 +770,14 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
$id = $request->requestVar('ID');
$parentID = $request->requestVar('ParentID');
if (!is_numeric($id) || !is_numeric($parentID)) {
return $this->httpError(400);
$this->httpError(400);
}
// Check record exists in the DB
/** @var SiteTree $node */
$node = DataObject::get_by_id($className, $id);
if (!$node) {
return $this->httpError(
$this->httpError(
500,
_t(
__CLASS__.'.PLEASESAVE',
@ -805,7 +789,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
// Check top level permissions
$root = $node->getParentType();
if (($parentID == '0' || $root == 'root') && !SiteConfig::current_site_config()->canCreateTopLevel()) {
return $this->httpError(
$this->httpError(
403,
_t(
__CLASS__.'.CANT_REORGANISE',
@ -1000,7 +984,6 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
);
// Create the form
/** @skipUpgrade */
$form = Form::create(
$this,
'SearchForm',
@ -1037,7 +1020,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
return $pageTypes;
}
public function doSearch($data, $form)
public function doSearch(array $data, Form $form): HTTPResponse
{
return $this->getsubtree($this->getRequest());
}
@ -1352,8 +1335,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
/** @skipUpgrade */
if ($record instanceof CMSPreviewable && !$fields->fieldByName('SilverStripeNavigator')) {
if (($record instanceof CMSPreviewable || $record->has_extension(CMSPreviewable::class))
&& !$fields->fieldByName('SilverStripeNavigator')
) {
$navField = new LiteralField('SilverStripeNavigator', $this->getSilverStripeNavigator());
$navField->setAllowHTML(true);
$fields->push($navField);
@ -1404,7 +1388,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
]);
// 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('fill-height flexbox-area-grow');
@ -1560,17 +1544,14 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
/**
* Callback to request the list of page types allowed under a given page instance.
* 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
$parentID = $request->requestVar('ParentID');
$parent = SiteTree::get()->byID($parentID);
if (!$parent || !$parent->exists()) {
return $this->httpError(404);
$this->httpError(404);
}
// Build hints specific to this class
@ -1757,12 +1738,9 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
/**
* Save and Publish page handler
*
* @param array $data
* @param Form $form
* @return HTTPResponse
* @throws HTTPResponse_Exception
*/
public function save($data, $form)
public function save(array $data, Form $form): HTTPResponse
{
$className = $this->config()->get('tree_class');
@ -1905,12 +1883,9 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
*
* @uses SiteTree->doRevertToLive()
*
* @param array $data
* @param Form $form
* @return HTTPResponse
* @throws HTTPResponse_Exception
*/
public function revert($data, $form)
public function revert(array $data, Form $form): HTTPResponse
{
if (!isset($data['ID'])) {
throw new HTTPResponse_Exception("Please pass an ID in the form content", 400);
@ -1958,12 +1933,9 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
*
* @see deletefromlive()
*
* @param array $data
* @param Form $form
* @return HTTPResponse
* @throws HTTPResponse_Exception
*/
public function delete($data, $form)
public function delete(array $data, Form $form): HTTPResponse
{
$id = $data['ID'];
$record = SiteTree::get()->byID($id);
@ -1993,12 +1965,9 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
/**
* Delete this page from both live and stage
*
* @param array $data
* @param Form $form
* @return HTTPResponse
* @throws HTTPResponse_Exception
*/
public function archive($data, $form)
public function archive(array $data, Form $form): HTTPResponse
{
$id = $data['ID'];
/** @var SiteTree $record */
@ -2026,14 +1995,14 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
return $this->getResponseNegotiator()->respond($this->getRequest());
}
public function publish($data, $form)
public function publish(array $data, Form $form): HTTPResponse
{
$data['publish'] = '1';
return $this->save($data, $form);
}
public function unpublish($data, $form)
public function unpublish(array $data, Form $form): HTTPResponse
{
$className = $this->config()->get('tree_class');
/** @var SiteTree $record */
@ -2168,87 +2137,10 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
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.
*
* @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'])) {
return new HTTPResponse("Please pass an ID in the form content", 400);
@ -2275,7 +2167,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
return $this->getResponseNegotiator()->respond($this->getRequest());
}
public function duplicate($request)
public function duplicate(HTTPRequest $request): HTTPResponse
{
// Protect against CSRF on destructive action
if (!SecurityToken::inst()->checkRequest($request)) {
@ -2319,11 +2211,11 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
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
if (!SecurityToken::inst()->checkRequest($request)) {
return $this->httpError(400);
$this->httpError(400);
}
Environment::increaseTimeLimitTo();
if (($id = $this->urlParams['ID']) && is_numeric($id)) {

View File

@ -192,12 +192,7 @@ class CMSPageAddController extends CMSPageEditController
return $form;
}
/**
* @param array $data
* @param Form $form
* @return HTTPResponse
*/
public function doAdd($data, $form)
public function doAdd(array $data, Form $form): HTTPResponse
{
$className = isset($data['PageType']) ? $data['PageType'] : "Page";
$parentID = isset($data['ParentID']) ? (int)$data['ParentID'] : 0;
@ -241,7 +236,7 @@ class CMSPageAddController extends CMSPageEditController
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());
}

View File

@ -53,30 +53,27 @@ class CMSPageEditController extends CMSMain
/**
* 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'];
$record = \Page::get()->byID($id);
$handler = AddToCampaignHandler::create($this, $record);
$results = $handler->addToCampaign($record, $data);
if (is_null($results)) {
return null;
$response = $handler->addToCampaign($record, $data);
$message = $response->getBody();
if (empty($message)) {
return $response;
}
if ($this->getSchemaRequested()) {
// Send extra "message" data with schema response
$extraData = ['message' => $results];
$extraData = ['message' => $message];
$schemaId = Controller::join_links($this->Link('schema/AddToCampaignForm'), $id);
return $this->getSchemaResponse($schemaId, $form, null, $extraData);
}
return $results;
return $response;
}
/**

View File

@ -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';
}
}

View File

@ -2,6 +2,7 @@
namespace SilverStripe\CMS\Controllers;
use SilverStripe\Admin\Navigator\SilverStripeNavigator;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Control\Controller;
use SilverStripe\Control\Director;
@ -29,7 +30,6 @@ use SilverStripe\View\ArrayData;
use SilverStripe\View\Parsers\URLSegmentFilter;
use SilverStripe\View\Requirements;
use SilverStripe\View\SSViewer;
use Translatable;
/**
* The most common kind of controller; effectively a controller linked to a {@link DataObject}.
@ -175,7 +175,6 @@ class ContentController extends Controller
}
// Check page permissions
/** @skipUpgrade */
if ($this->dataRecord && $this->URLSegment != 'Security' && !$this->dataRecord->canView()) {
Security::permissionFailure($this);
return;
@ -186,11 +185,9 @@ class ContentController extends Controller
* 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.
*
* @param HTTPRequest $request
* @return HTTPResponse
* @throws HTTPResponse_Exception
*/
public function handleRequest(HTTPRequest $request)
public function handleRequest(HTTPRequest $request): HTTPResponse
{
/** @var SiteTree $child */
$child = null;
@ -200,11 +197,6 @@ class ContentController extends Controller
// control to a child controller. This allows for the creation of chains of controllers which correspond to a
// nested URL.
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();
// look for a page with this URLSegment
@ -213,10 +205,6 @@ class ContentController extends Controller
// url encode unless it's multibyte (already pre-encoded in the database)
'URLSegment' => $filter->getAllowMultibyte() ? $action : rawurlencode($action),
])->first();
if (class_exists('Translatable')) {
Translatable::enable_locale_filter();
}
}
// we found a page with this URLSegment.
@ -226,25 +214,6 @@ class ContentController extends Controller
$response = ModelAsController::controller_for($child)->handleRequest($request);
} 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());
try {
@ -415,9 +384,6 @@ HTML;
/**
* 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=
* attributes in HTML or XHTML output.
@ -426,14 +392,7 @@ HTML;
*/
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);
}

View File

@ -17,7 +17,6 @@ use SilverStripe\Dev\Debug;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\DB;
use SilverStripe\View\Parsers\URLSegmentFilter;
use Translatable;
/**
* 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
* return it.
*
* @param SiteTree $sitetree
* @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();
@ -58,7 +55,6 @@ class ModelAsController extends Controller implements NestedController
{
parent::beforeHandleRequest($request);
// If the database has not yet been created, redirect to the build page.
/** @skipUpgrade */
if (!DB::is_active() || !ClassInfo::hasTable('SiteTree')) {
$this->getResponse()->redirect(Controller::join_links(
Director::absoluteBaseURL(),
@ -72,10 +68,8 @@ class ModelAsController extends Controller implements NestedController
/**
* @uses ModelAsController::getNestedController()
* @param HTTPRequest $request
* @return HTTPResponse
*/
public function handleRequest(HTTPRequest $request)
public function handleRequest(HTTPRequest $request): HTTPResponse
{
$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.
/** @skipUpgrade */
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();
return $this->getResponse();
}
try {
$result = $this->getNestedController();
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);
}
$result = $this->getNestedController()->handleRequest($this->getRequest());
$result = $result;
} catch (HTTPResponse_Exception $responseException) {
$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.
*/
public function getNestedController()
public function getNestedController(): ContentController
{
$request = $this->getRequest();
@ -123,11 +109,6 @@ class ModelAsController extends Controller implements NestedController
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)
$filter = URLSegmentFilter::create();
if (!$filter->getAllowMultibyte()) {
@ -143,21 +124,10 @@ class ModelAsController extends Controller implements NestedController
/** @var SiteTree $sitetree */
$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) {
$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'])) {
Debug::message("Using record #$sitetree->ID of type " . get_class($sitetree) . " with link {$sitetree->Link()}");
}

View File

@ -17,10 +17,9 @@ class OldPageRedirector extends Extension
* 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.
*
* @param HTTPRequest $request The request object
* @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
$params = preg_split('|/+|', $request->getURL() ?? '');
@ -103,7 +102,7 @@ class OldPageRedirector extends Extension
// No valid page found.
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
return $page->Link() . implode('/', $params);
return Controller::join_links($page->Link(), implode('/', $params));
}
}
} else {

View File

@ -73,7 +73,6 @@ class RootURLController extends Controller implements Resettable
self::$is_at_root = true;
/** @skipUpgrade */
if (!DB::is_active() || !ClassInfo::hasTable('SiteTree')) {
$this->getResponse()->redirect(Controller::join_links(
Director::absoluteBaseURL(),
@ -85,17 +84,12 @@ class RootURLController extends Controller implements Resettable
}
}
/**
* @param HTTPRequest $request
* @return HTTPResponse
*/
public function handleRequest(HTTPRequest $request)
public function handleRequest(HTTPRequest $request): HTTPResponse
{
self::$is_at_root = true;
$this->beforeHandleRequest($request);
if (!$this->getResponse()->isFinished()) {
/** @skipUpgrade */
if (!DB::is_active() || !ClassInfo::hasTable('SiteTree')) {
$this->getResponse()->redirect(Director::absoluteBaseURL() . 'dev/build?returnURL=' . (isset($_GET['url']) ? urlencode($_GET['url']) : null));
return $this->getResponse();

View File

@ -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
];
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -3,6 +3,7 @@
namespace SilverStripe\CMS\Controllers;
use SilverStripe\Admin\LeftAndMain;
use SilverStripe\Admin\Navigator\SilverStripeNavigatorItem;
use SilverStripe\CMS\Model\RedirectorPage;
use SilverStripe\Control\Controller;

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -134,7 +134,7 @@ class SiteTreeURLSegmentField extends TextField
*/
public function getURLPrefix()
{
return $this->urlPrefix;
return rtrim($this->urlPrefix ?? '', '/') . '/';
}
public function getURLSuffix()

View File

@ -25,7 +25,6 @@ use SilverStripe\Core\Manifest\ModuleResource;
use SilverStripe\Core\Manifest\ModuleResourceLoader;
use SilverStripe\Core\Manifest\VersionProvider;
use SilverStripe\Core\Resettable;
use SilverStripe\Dev\Deprecation;
use SilverStripe\Forms\CheckboxField;
use SilverStripe\Forms\CompositeField;
use SilverStripe\Forms\DropdownField;
@ -639,7 +638,7 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
if ($this->hasMethod('alternateAbsoluteLink')) {
return $this->alternateAbsoluteLink($action);
} else {
return Director::absoluteURL($this->Link($action));
return Director::absoluteURL((string) $this->Link($action));
}
}
@ -652,13 +651,6 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
*/
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);
$this->extend('updatePreviewLink', $link, $action);
return $link;
@ -699,15 +691,17 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
$base = $this->URLSegment;
}
$this->extend('updateRelativeLink', $base, $action);
// Legacy support: If $action === true, retain URLSegment for homepages,
// but don't append any action
if ($action === true) {
$action = null;
}
return Controller::join_links($base, '/', $action);
$link = Controller::join_links($base, $action);
$this->extend('updateRelativeLink', $link, $base, $action);
return $link;
}
/**
@ -2468,7 +2462,6 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
// "readonly"/viewing version that isn't the current version of the record
/** @var SiteTree $stageRecord */
$stageRecord = Versioned::get_by_stage(static::class, Versioned::DRAFT)->byID($this->ID);
/** @skipUpgrade */
if ($stageRecord && $stageRecord->Version != $this->Version) {
$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')));
@ -2513,7 +2506,7 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
// Note: It would be nice to have a canRestore() permission at some point
if ($canEdit && !$isOnDraft && !$isPublished) {
// 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"
$title = $restoreToRoot
@ -2522,13 +2515,15 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
$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_DESC', 'Restore the archived version to draft');
$majorActions->push(
FormAction::create('restore', $title)
->setDescription($description)
->setAttribute('data-to-root', $restoreToRoot)
->addExtraClass('btn-warning font-icon-back-in-time')
->setUseButtonTag(true)
);
if (!$this->isParentArchived() || $restoreToRoot) {
$majorActions->push(
FormAction::create('restore', $title)
->setDescription($description)
->setAttribute('data-to-root', $restoreToRoot)
->addExtraClass('btn-warning font-icon-back-in-time')
->setUseButtonTag(true)
);
}
}
// If a page is on any stage it can be archived
@ -2790,35 +2785,6 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
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
@ -3067,14 +3033,6 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
// If we have a class of "{$ClassName}Controller" then we found our controller
if (class_exists($candidate = sprintf('%sController', $class))) {
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)) {
foreach ($namespaceMap as $pageNamespace => $controllerNamespace) {
if (strpos($class, $pageNamespace) !== 0) {
@ -3338,7 +3296,7 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
}
/**
* Cache key for creatableChildren() method
* Cache key for creatableChildPages() method
*
* @param int $memberID
* @return string

View File

@ -75,13 +75,15 @@ abstract class SiteTreeExtension extends DataExtension
* before {@link SiteTree::RelativeLink()} calls {@link Controller::join_links()}
* 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
* @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
* to an empty link
*/
public function updateRelativeLink(&$base, &$action)
public function updateRelativeLink(&$link, $base, $action)
{
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}
}

View File

@ -9,8 +9,6 @@ use SilverStripe\ORM\DataObject;
*
* @method DataObject Parent() Parent object
* @method SiteTree Linked() Page being linked to
*
* Run `MigrateSiteTreeLinkingTask` to migrate from old table to this.
*/
class SiteTreeLink extends DataObject
{

View File

@ -4,7 +4,6 @@ namespace SilverStripe\CMS\Model;
use Page;
use SilverStripe\Core\Convert;
use SilverStripe\Dev\Deprecation;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\LiteralField;
use SilverStripe\Forms\ReadonlyTransformation;
@ -360,30 +359,6 @@ class VirtualPage extends Page
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()
{
$parentClass = sprintf(

View File

@ -31,7 +31,7 @@ class RecentlyEditedReport extends Report
$threshold = strtotime('-14 days', DBDatetime::now()->getTimestamp());
return SiteTree::get()
->filter('LastEdited:GreaterThan', date("Y-m-d H:i:s", $threshold))
->sort("\"$tableName\".\"LastEdited\" DESC");
->orderBy("\"$tableName\".\"LastEdited\" DESC");
}
public function columns()

View File

@ -39,7 +39,6 @@ class ContentControllerSearchExtension extends Extension
$actions = FieldList::create(
FormAction::create('results', _t('SilverStripe\\CMS\\Search\\SearchForm.GO', 'Go'))
);
/** @skipUpgrade */
$form = SearchForm::create($this->owner, 'SearchForm', $fields, $actions);
$form->classesToSearch(FulltextSearchable::get_searchable_classes());
return $form;

View File

@ -13,17 +13,11 @@ use SilverStripe\Forms\HiddenField;
use SilverStripe\Forms\TextField;
use SilverStripe\ORM\DB;
use SilverStripe\ORM\SS_List;
use Translatable;
/**
* Standard basic search form which conducts a fulltext search on all {@link SiteTree}
* 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
*/
class SearchForm extends Form
@ -51,7 +45,6 @@ class SearchForm extends Form
];
/**
* @skipUpgrade
* @param RequestHandler $controller
* @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
@ -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) {
$actions = new FieldList(
new FormAction("results", _t(__CLASS__.'.GO', 'Go'))
@ -130,22 +117,6 @@ class SearchForm extends Form
// Get request data from request handler
$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');
$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;
}

View File

@ -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');
}
}

View File

@ -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>&nbsp;',
_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;
}
}

View File

@ -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, '/');
}
}

View File

@ -19,19 +19,19 @@
}
],
"require": {
"silverstripe/admin": "^1.13@dev",
"silverstripe/campaign-admin": "^1.7@dev",
"silverstripe/framework": "^4.11",
"silverstripe/reports": "^4.7@dev",
"silverstripe/siteconfig": "^4.7@dev",
"silverstripe/versioned": "^1.7@dev",
"silverstripe/versioned-admin": "^1.7@dev",
"silverstripe/vendor-plugin": "^1.0",
"php": "^7.4 || ^8.0"
"php": "^8.1",
"silverstripe/admin": "^2",
"silverstripe/campaign-admin": "^2",
"silverstripe/framework": "^5",
"silverstripe/reports": "^5",
"silverstripe/siteconfig": "^5",
"silverstripe/versioned": "^2",
"silverstripe/versioned-admin": "^2",
"silverstripe/vendor-plugin": "^2"
},
"require-dev": {
"phpunit/phpunit": "^9.5",
"squizlabs/php_codesniffer": "^3"
"phpunit/phpunit": "^9.6",
"squizlabs/php_codesniffer": "^3.7"
},
"extra": {
"expose": [

View File

@ -114,8 +114,6 @@ en:
SilverStripe\CMS\Controllers\CMSSiteTreeFilter_StatusRemovedFromDraftPages:
Title: 'Live but removed from draft'
SilverStripe\CMS\Controllers\ContentController:
ARCHIVEDSITE: 'Preview version'
ARCHIVEDSITEFROM: 'Archived site from'
CMS: CMS
DRAFT: Draft
DRAFTSITE: 'Draft Site'
@ -134,11 +132,8 @@ en:
Password: Password
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>.'
UNVERSIONEDPREVIEW: Preview
UnableDeleteInstall: 'Unable to delete installation files. Please delete the files below manually'
VIEWPAGEIN: 'View Page in:'
SilverStripe\CMS\Controllers\SilverStripeNavigator:
ARCHIVED: Archived
SilverStripe\CMS\Forms\AnchorLinkFormFactory:
ANCHORVALUE: Anchor
SilverStripe\CMS\Forms\InternalLinkFormFactory:

View File

@ -1,15 +1,15 @@
{
"name": "silverstripe-cms",
"version": "4.0.0",
"version": "5.0.0",
"description": "The SilverStripe CMS",
"directories": {
"test": "tests"
},
"engines": {
"node": "^10.x"
"node": "^18.x"
},
"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",
"watch": "NODE_ENV=development webpack --watch --progress",
"css": "WEBPACK_CHILD=css npm run build",
@ -32,36 +32,42 @@
},
"homepage": "https://github.com/silverstripe/silverstripe-cms#readme",
"dependencies": {
"@silverstripe/reactstrap-confirm": "0.0.5",
"apollo-client": "^2.4.2",
"bootstrap": "^4.3.1",
"classnames": "^2.2.5",
"@apollo/client": "^3.7.1",
"@popperjs/core": "^2.11.6",
"bootstrap": "^4.6.2",
"classnames": "^2.3.2",
"deep-freeze-strict": "^1.1.1",
"graphql": "^14.0.0",
"graphql-tag": "^2.10.0",
"isomorphic-fetch": "^2.2.1",
"jquery": "^3.5.0",
"graphql": "^16.6.0",
"graphql-tag": "^2.12.6",
"isomorphic-fetch": "^3.0.0",
"merge": "^2.1.1",
"popper.js": "^1.14.4",
"prop-types": "^15.6.2",
"react": "^16.6.1",
"react-apollo": "^2.1.11",
"react-dom": "^16.6.1",
"react-redux": "^5.0.7",
"react-select": "^1.3",
"reactstrap": "^6.4.0",
"redux": "^4.0.0",
"redux-form": "^7.4.2"
"prop-types": "^15.8.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-redux": "^8.0.5",
"react-select": "^5.6.0",
"reactstrap": "^8.9.0",
"reactstrap-confirm": "^1.3.2",
"redux": "^4.2.0",
"redux-form": "^8.3.8"
},
"devDependencies": {
"@silverstripe/eslint-config": "^0.1.0",
"@silverstripe/webpack-config": "^1.3.0",
"babel-jest": "^23.6.0",
"babel-polyfill": "^6.26.0",
"copy-webpack-plugin": "^4",
"jest-cli": "^23.6.0"
"@silverstripe/eslint-config": "^1.0.0",
"@silverstripe/webpack-config": "^2.0.0",
"@testing-library/react": "^14.0.0",
"babel-jest": "^29.3.0",
"copy-webpack-plugin": "^11.0.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": {
"testEnvironment": "jsdom",
"roots": [
"client/src"
],
@ -82,16 +88,6 @@
}
},
"resolutions": {
"colors": "1.1.2",
"eslint": "^4.6.1"
},
"babel": {
"presets": [
"env",
"react"
],
"plugins": [
"transform-object-rest-spread"
]
"colors": "1.4.0"
}
}

View File

View File

@ -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>

View File

@ -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 %>

View File

@ -64,7 +64,7 @@ Feature: Edit a page
Then I should see "About Us"
And I go to "/about-modified-us"
Then I should not see "About Us"
# Assert URL segment + metadata on frontend
When I go to "/admin/pages"
And I click on "About Us" in the tree
@ -81,19 +81,19 @@ Feature: Edit a page
When I click on "About Us" in the tree
# 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 press the "Insert file" button
# 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 select "Link to a file" from the TinyMCE menu with javascript
And I press the "Insert link" HTML field button
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 fill in "Form_fileInsertForm_Text" with "MyImage"
And I press the "Link to file" button
# 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 press "Add media"
And I wait for 15 seconds

View File

@ -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
When I select "awesome" in the "Content" HTML field
And I press the "Insert link" HTML field button
And I click "Page on this site" in the ".mce-menu" element
And I click "Page on this site" in the ".tox-collection__group" element
Then I should see an "form#Form_editorInternalLink" element
When I click "(Search or choose Page)" in the ".Select-multi-value-wrapper" element
And I click "About Us" in the ".treedropdownfield__menu" element
When I select "About Us" in the "#Form_editorInternalLink_PageID_Holder" tree dropdown
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>"
# Required to avoid "unsaved changes" browser dialog
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>"
When I select the image "file1.jpg" in the "Content" HTML field
And I press the "Insert link" HTML field button
And I click "Page on this site" in the ".mce-menu" element
And I click "Page on this site" in the ".tox-collection__group" element
Then I should see an "form#Form_editorInternalLink" element
And I should not see "Link text"
When I click "(Search or choose Page)" in the ".Select-multi-value-wrapper" element
And I click "About Us" in the ".treedropdownfield__menu" element
And I press the "Insert" button
When I select "About Us" in the "#Form_editorInternalLink_PageID_Holder" tree dropdown
And I press the "Insert link" button
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
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>"
And I select "awesome" in the "Content" HTML field
And I press the "Insert link" HTML field button
And I click "Page on this site" in the ".mce-menu" element
And I click "Page on this site" in the ".tox-collection__group" 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"
# This doesn't seem to suffer from that issue
When I click "About Us" in the ".Select-value" element
And I click "Home" in the ".treedropdownfield__menu" element
When I select "Home" in the "#Form_editorInternalLink_PageID_Holder" tree dropdown
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>"
# Required to avoid "unsaved changes" browser dialog
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
Given I select "awesome" in the "Content" HTML field
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
When I fill in "http://silverstripe.org" for "URL"
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>"
# Required to avoid "unsaved changes" browser dialog
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>"
When I select the image "file1.jpg" in the "Content" HTML field
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 not see "Link text"
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>"
# Required to avoid "unsaved changed" browser dialog
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"
And I select "awesome" in the "Content" HTML field
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
Then the "URL" field should contain "http://silverstripe.org"
# This doesn't seem to suffer from that issue
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>"
# Required to avoid "unsaved changes" browser dialog
Then I press the "Save" button

View File

@ -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
When I select "awesome" in the "Content" HTML field
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
And I should see "About Us" in the "#Form_editorAnchorLink_PageID_Holder .Select-multi-value-wrapper" element
When I click "About Us" in the "#Form_editorAnchorLink_PageID_Holder .Select-multi-value-wrapper" element
And I click "Details" in the "#Form_editorAnchorLink_PageID_Holder .Select-menu-outer" element
And I click "Select or enter anchor" in the "#Form_editorAnchorLink_Anchor_Holder .Select-multi-value-wrapper" 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
And I should see "About Us" in the "#Form_editorAnchorLink_PageID_Holder .treedropdownfield__value-container" element
When I select "Details" in the "#Form_editorAnchorLink_PageID_Holder" tree dropdown
And I select "youranchor" in the "#Form_editorAnchorLink_Anchor_Holder" anchor dropdown
Then I should see "youranchor" in the "#Form_editorAnchorLink_Anchor_Holder .anchorselectorfield__value-container" element
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>"
# Required to avoid "unsaved changes" browser dialog
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>"
When I select the image "file1.jpg" in the "Content" HTML field
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
And I should not see "Link text"
And I should see "About Us" in the "#Form_editorAnchorLink_PageID_Holder .Select-multi-value-wrapper" element
When I click "About Us" in the "#Form_editorAnchorLink_PageID_Holder .Select-multi-value-wrapper" element
And I click "Details" in the "#Form_editorAnchorLink_PageID_Holder .Select-menu-outer" element
And I click "Select or enter anchor" in the "#Form_editorAnchorLink_Anchor_Holder .Select-multi-value-wrapper" 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
And I press the "Insert" button
And I should see "About Us" in the "#Form_editorAnchorLink_PageID_Holder .treedropdownfield__value-container" element
When I select "Details" in the "#Form_editorAnchorLink_PageID_Holder" tree dropdown
And I select "youranchor" in the "#Form_editorAnchorLink_Anchor_Holder" anchor dropdown
And I press the "Insert link" button
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
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
When I select "awesome" in the "Content" HTML field
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
And I should see "About Us" in the "#Form_editorAnchorLink_PageID_Holder .Select-multi-value-wrapper" element
When I click "Select or enter anchor" in the "#Form_editorAnchorLink_Anchor_Holder .Select-multi-value-wrapper" element
Then I should see "dataobject-anchor" in the "#Form_editorAnchorLink_Anchor_Holder .Select-menu-outer" 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
And I should see "About Us" in the "#Form_editorAnchorLink_PageID_Holder .treedropdownfield__value-container" 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 .anchorselectorfield__value-container" element
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>"
# Required to avoid "unsaved changes" browser dialog
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>"
When I select "awesome" in the "Content" HTML field
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
And I should see "About Us" in the "#Form_editorAnchorLink_PageID_Holder .Select-multi-value-wrapper" element
When I click "Select or enter anchor" in the "#Form_editorAnchorLink_Anchor_Holder .Select-multi-value-wrapper" element
Then I should see "unsaved-anchor" in the "#Form_editorAnchorLink_Anchor_Holder .Select-menu-outer" element
And I should see "dataobject-anchor" in the "#Form_editorAnchorLink_Anchor_Holder .Select-menu-outer" element
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 .Select-value" element
And I should see "About Us" in the "#Form_editorAnchorLink_PageID_Holder .treedropdownfield__value-container" element
When I click on the ".anchorselectorfield__dropdown-indicator" element
Then I should see "dataobject-anchor" in the ".anchorselectorfield__menu-list" element
When I select "unsaved-anchor" in the "#Form_editorAnchorLink_Anchor_Holder" anchor dropdown
Then I should see "unsaved-anchor" in the "#Form_editorAnchorLink_Anchor_Holder .anchorselectorfield__value-container" element
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>"
# Required to avoid "unsaved changes" browser dialog
Then I press the "Save" button

View File

@ -8,6 +8,7 @@ use Behat\Mink\Element\NodeElement;
use PHPUnit\Framework\Assert;
use SilverStripe\BehatExtension\Context\BasicContext;
use SilverStripe\BehatExtension\Context\FixtureContext as BehatFixtureContext;
use SilverStripe\BehatExtension\Utility\StepHelper;
use SilverStripe\CMS\Model\RedirectorPage;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Core\ClassInfo;
@ -20,6 +21,8 @@ use SilverStripe\Versioned\Versioned;
*/
class FixtureContext extends BehatFixtureContext
{
use StepHelper;
/**
* @var BasicContext
*/
@ -118,22 +121,6 @@ class FixtureContext extends BehatFixtureContext
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
*
@ -155,4 +142,33 @@ JS;
$actual = $this->getMainContext()->getSession()->evaluateScript($js);
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();
});
}
}

View File

@ -16,7 +16,6 @@ use SilverStripe\Versioned\Versioned;
*/
class CMSBatchActionsTest extends SapphireTest
{
protected static $fixture_file = 'CMSBatchActionsTest.yml';
protected function setUp(): void
@ -142,10 +141,10 @@ class CMSBatchActionsTest extends SapphireTest
$this->assertEquals($archivedID, $list->first()->ParentID);
// Run restore
$result = json_decode($action->run($list) ?? '', true);
$result = json_decode($action->run($list)->getBody(), true);
$this->assertEquals(
[
$archivedxID => $archivedxID
$archivedxID => $archivedxID,
],
$result['success']
);
@ -162,13 +161,13 @@ class CMSBatchActionsTest extends SapphireTest
$this->assertEquals(0, $list->last()->ParentID); // archived (parent)
// Run restore
$result = json_decode($action->run($list) ?? '', true);
$result = json_decode($action->run($list)->getBody(), true);
$this->assertEquals(
[
// Order of archived is opposite to order items are passed in, as
// these are sorted by level first
$archivedID => $archivedID,
$archivedyID => $archivedyID
$archivedyID => $archivedyID,
],
$result['success']
);

View File

@ -2,7 +2,6 @@
namespace SilverStripe\CMS\Tests\Controllers;
use Page;
use Psr\SimpleCache\CacheInterface;
use SilverStripe\Admin\CMSBatchActionHandler;
use SilverStripe\CMS\Controllers\CMSMain;
@ -25,7 +24,6 @@ use SilverStripe\Security\Member;
use SilverStripe\Security\Security;
use SilverStripe\SiteConfig\SiteConfig;
use SilverStripe\Versioned\Versioned;
use SilverStripe\Dev\Deprecation;
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.
* Mostly, this is just checking that the method doesn't return an error
@ -185,8 +149,8 @@ class CMSMainTest extends FunctionalTest
$this->logInWithPermission('ADMIN');
Config::modify()->set(SiteTree::class, 'enforce_strict_hierarchy', true);
$parentPage = $this->objFromFixture(Page::class, 'page3');
$childPage = $this->objFromFixture(Page::class, 'page1');
$parentPage = $this->objFromFixture(SiteTree::class, 'page3');
$childPage = $this->objFromFixture(SiteTree::class, 'page1');
$parentPage->doUnpublish();
$childPage->doUnpublish();
@ -208,7 +172,7 @@ class CMSMainTest extends FunctionalTest
$this->logInWithPermission('ADMIN');
// Set up a page that is delete from live
$page = $this->objFromFixture(Page::class, 'page1');
$page = $this->objFromFixture(SiteTree::class, 'page1');
$pageID = $page->ID;
$page->publishRecursive();
$page->delete();
@ -216,7 +180,7 @@ class CMSMainTest extends FunctionalTest
$response = $this->get('admin/pages/edit/show/' . $pageID);
$livePage = Versioned::get_one_by_stage(SiteTree::class, Versioned::LIVE, [
'"SiteTree"."ID"' => $pageID
'"SiteTree"."ID"' => $pageID,
]);
$this->assertInstanceOf(SiteTree::class, $livePage);
$this->assertTrue($livePage->canDelete());
@ -233,7 +197,7 @@ class CMSMainTest extends FunctionalTest
$this->logInWithPermission('ADMIN');
// Set up a page that is delete from live
$page1 = $this->objFromFixture(Page::class, 'page1');
$page1 = $this->objFromFixture(SiteTree::class, 'page1');
$page1ID = $page1->ID;
$page1->publishRecursive();
$page1->delete();
@ -246,18 +210,18 @@ class CMSMainTest extends FunctionalTest
$this->assertNull($cmsMain->getRecord('asdf'));
// Pages that are on draft and aren't on draft should both work
$this->assertInstanceOf('Page', $cmsMain->getRecord($page1ID));
$this->assertInstanceOf('Page', $cmsMain->getRecord($this->idFromFixture('Page', 'page2')));
$this->assertInstanceOf(SiteTree::class, $cmsMain->getRecord($page1ID));
$this->assertInstanceOf(SiteTree::class, $cmsMain->getRecord($this->idFromFixture(SiteTree::class, 'page2')));
// This functionality isn't actually used any more.
$newPage = $cmsMain->getRecord('new-Page-5');
$this->assertInstanceOf('Page', $newPage);
$this->assertInstanceOf(SiteTree::class, $newPage);
$this->assertEquals('5', $newPage->ParentID);
}
public function testDeletedPagesSiteTreeFilter()
{
$id = $this->idFromFixture('Page', 'page3');
$id = $this->idFromFixture(SiteTree::class, 'page3');
$this->logInWithPermission('ADMIN');
$result = $this->get('admin/pages/getsubtree?filter=CMSSiteTreeFilter_DeletedPages&ajax=1&ID=' . $id);
$this->assertEquals(200, $result->getStatusCode());
@ -278,7 +242,7 @@ class CMSMainTest extends FunctionalTest
'admin/pages/add/AddForm',
[
'ParentID' => '0',
'PageType' => 'Page',
'PageType' => RedirectorPage::class,
'Locale' => 'en_US',
'action_doAdd' => 1,
'ajax' => 1,
@ -298,7 +262,7 @@ class CMSMainTest extends FunctionalTest
'admin/pages/add/AddForm',
[
'ParentID' => '0',
'PageType' => 'Page',
'PageType' => RedirectorPage::class,
'Locale' => 'en_US',
'action_doAdd' => 1,
'ajax' => 1,
@ -332,7 +296,7 @@ class CMSMainTest extends FunctionalTest
'PageType' => CMSMainTest_ClassA::class,
'Locale' => 'en_US',
'action_doAdd' => 1,
'ajax' => 1
'ajax' => 1,
],
[
'X-Pjax' => 'CurrentForm,Breadcrumbs',
@ -352,7 +316,7 @@ class CMSMainTest extends FunctionalTest
'PageType' => CMSMainTest_ClassB::class,
'Locale' => 'en_US',
'action_doAdd' => 1,
'ajax' => 1
'ajax' => 1,
],
[
'X-Pjax' => 'CurrentForm,Breadcrumbs',
@ -375,10 +339,10 @@ class CMSMainTest extends FunctionalTest
'admin/pages/add/AddForm',
[
'ParentID' => $newPageId,
'PageType' => 'Page',
'PageType' => RedirectorPage::class,
'Locale' => 'en_US',
'action_doAdd' => 1,
'ajax' => 1
'ajax' => 1,
],
[
'X-Pjax' => 'CurrentForm,Breadcrumbs',
@ -393,8 +357,8 @@ class CMSMainTest extends FunctionalTest
public function testBreadcrumbs()
{
$page3 = $this->objFromFixture(Page::class, 'page3');
$page31 = $this->objFromFixture(Page::class, 'page31');
$page3 = $this->objFromFixture(SiteTree::class, 'page3');
$page31 = $this->objFromFixture(SiteTree::class, 'page31');
$this->logInAs('admin');
$response = $this->get('admin/pages/edit/show/' . $page31->ID);
@ -420,7 +384,7 @@ class CMSMainTest extends FunctionalTest
$this->assertEquals($page->Title, 'New Page');
$this->assertNotEquals($page->Sort, 0);
$this->assertInstanceOf('Page', $page);
$this->assertInstanceOf(SiteTree::class, $page);
// Test failure
try {
@ -449,10 +413,10 @@ class CMSMainTest extends FunctionalTest
);
// Change state of tree
$page1 = $this->objFromFixture(Page::class, 'page1');
$page3 = $this->objFromFixture(Page::class, 'page3');
$page11 = $this->objFromFixture(Page::class, 'page11');
$page12 = $this->objFromFixture(Page::class, 'page12');
$page1 = $this->objFromFixture(SiteTree::class, 'page1');
$page3 = $this->objFromFixture(SiteTree::class, 'page3');
$page11 = $this->objFromFixture(SiteTree::class, 'page11');
$page12 = $this->objFromFixture(SiteTree::class, 'page12');
// Deleted
$page1->doUnpublish();
$page1->delete();
@ -472,7 +436,7 @@ class CMSMainTest extends FunctionalTest
// Test deleted page filter
$params = [
'FilterClass' => 'SilverStripe\\CMS\\Controllers\\CMSSiteTreeFilter_StatusDeletedPages'
'FilterClass' => 'SilverStripe\\CMS\\Controllers\\CMSSiteTreeFilter_StatusDeletedPages',
];
$pages = $controller->getList($params);
$this->assertEquals(1, $pages->count());
@ -483,7 +447,7 @@ class CMSMainTest extends FunctionalTest
// Test live, but not on draft filter
$params = [
'FilterClass' => 'SilverStripe\\CMS\\Controllers\\CMSSiteTreeFilter_StatusRemovedFromDraftPages'
'FilterClass' => 'SilverStripe\\CMS\\Controllers\\CMSSiteTreeFilter_StatusRemovedFromDraftPages',
];
$pages = $controller->getList($params);
$this->assertEquals(1, $pages->count());
@ -494,7 +458,7 @@ class CMSMainTest extends FunctionalTest
// Test live pages filter
$params = [
'FilterClass' => 'SilverStripe\\CMS\\Controllers\\CMSSiteTreeFilter_PublishedPages'
'FilterClass' => 'SilverStripe\\CMS\\Controllers\\CMSSiteTreeFilter_PublishedPages',
];
$pages = $controller->getList($params);
$this->assertEquals(2, $pages->count());
@ -529,7 +493,7 @@ class CMSMainTest extends FunctionalTest
$this->loginWithPermission('ADMIN');
// Get a associated with a fixture page.
$page = $this->objFromFixture(Page::class, 'page1');
$page = $this->objFromFixture(SiteTree::class, 'page1');
$controller = CMSMain::create();
$controller->setRequest(Controller::curr()->getRequest());
$form = $controller->getEditForm($page->ID);
@ -557,7 +521,7 @@ class CMSMainTest extends FunctionalTest
$form->loadDataFrom(['ClassName' => CMSMainTest_ClassB::class]);
$result = $cms->save([
'ID' => $page->ID,
'ClassName' => CMSMainTest_ClassB::class
'ClassName' => CMSMainTest_ClassB::class,
], $form);
$this->assertEquals(200, $result->getStatusCode());

View File

@ -1,4 +1,4 @@
Page:
SilverStripe\CMS\Model\SiteTree:
page1:
Title: Page 1
Sort: 1
@ -10,11 +10,11 @@ Page:
Sort: 3
page31:
Title: Page 3.1
Parent: =>Page.page3
Parent: =>SilverStripe\CMS\Model\SiteTree.page3
Sort: 1
page32:
Title: Page 3.2
Parent: =>Page.page3
Parent: =>SilverStripe\CMS\Model\SiteTree.page3
Sort: 2
page4:
Title: Page 4

View File

@ -2,11 +2,11 @@
namespace SilverStripe\CMS\Tests\Controllers;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Dev\TestOnly;
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';

View File

@ -2,11 +2,11 @@
namespace SilverStripe\CMS\Tests\Controllers;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Dev\TestOnly;
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';

View File

@ -2,11 +2,10 @@
namespace SilverStripe\CMS\Tests\Controllers;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Dev\TestOnly;
use SilverStripe\ORM\HiddenClass;
use Page;
class CMSMainTest_HiddenClass extends Page implements TestOnly, HiddenClass
class CMSMainTest_HiddenClass extends SiteTree implements TestOnly, HiddenClass
{
}

View File

@ -2,10 +2,10 @@
namespace SilverStripe\CMS\Tests\Controllers;
use SilverStripe\CMS\Model\SiteTree;
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';

View File

@ -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);
}
}

View File

@ -1,10 +0,0 @@
Page:
page1:
Title: Page 1
Sort: 1
page2:
Title: Page 2
Sort: 2
page3:
Title: Page 3
Sort: 3

View File

@ -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';
}

View File

@ -2,7 +2,6 @@
namespace SilverStripe\CMS\Tests\Controllers;
use Page;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Versioned\Versioned;
use SilverStripe\CMS\Controllers\CMSSiteTreeFilter_Search;
@ -15,13 +14,12 @@ use SilverStripe\Dev\SapphireTest;
class CMSSiteTreeFilterTest extends SapphireTest
{
protected static $fixture_file = 'CMSSiteTreeFilterTest.yml';
public function testSearchFilterEmpty()
{
$page1 = $this->objFromFixture('Page', 'page1');
$page2 = $this->objFromFixture('Page', 'page2');
$page1 = $this->objFromFixture(SiteTree::class, 'page1');
$page2 = $this->objFromFixture(SiteTree::class, 'page2');
$f = new CMSSiteTreeFilter_Search();
$results = $f->pagesIncluded();
@ -32,8 +30,8 @@ class CMSSiteTreeFilterTest extends SapphireTest
public function testSearchFilterByTitle()
{
$page1 = $this->objFromFixture('Page', 'page1');
$page2 = $this->objFromFixture('Page', 'page2');
$page1 = $this->objFromFixture(SiteTree::class, 'page1');
$page2 = $this->objFromFixture(SiteTree::class, 'page2');
$f = new CMSSiteTreeFilter_Search(['Title' => 'Page 1']);
$results = $f->pagesIncluded();
@ -49,7 +47,7 @@ class CMSSiteTreeFilterTest extends SapphireTest
public function testUrlSegmentFilter()
{
$page = $this->objFromFixture(Page::class, 'page8');
$page = $this->objFromFixture(SiteTree::class, 'page8');
$filter = CMSSiteTreeFilter_Search::create(['Term' => 'lake-wanaka+adventure']);
$this->assertTrue($filter->isPageIncluded($page));
@ -60,8 +58,8 @@ class CMSSiteTreeFilterTest extends SapphireTest
public function testIncludesParentsForNestedMatches()
{
$parent = $this->objFromFixture('Page', 'page3');
$child = $this->objFromFixture('Page', 'page3b');
$parent = $this->objFromFixture(SiteTree::class, 'page3');
$child = $this->objFromFixture(SiteTree::class, 'page3b');
$f = new CMSSiteTreeFilter_Search(['Title' => 'Page 3b']);
$results = $f->pagesIncluded();
@ -78,11 +76,11 @@ class CMSSiteTreeFilterTest extends SapphireTest
public function testChangedPagesFilter()
{
/** @var Page $unchangedPage */
$unchangedPage = $this->objFromFixture('Page', 'page1');
$unchangedPage = $this->objFromFixture(SiteTree::class, 'page1');
$unchangedPage->publishRecursive();
/** @var Page $changedPage */
$changedPage = $this->objFromFixture('Page', 'page2');
$changedPage = $this->objFromFixture(SiteTree::class, 'page2');
$changedPage->Title = 'Original';
$changedPage->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
$changedPage->Title = 'Changed';
@ -121,7 +119,7 @@ class CMSSiteTreeFilterTest extends SapphireTest
public function testDeletedPagesFilter()
{
$deletedPage = $this->objFromFixture('Page', 'page2');
$deletedPage = $this->objFromFixture(SiteTree::class, 'page2');
$deletedPage->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
$deletedPageID = $deletedPage->ID;
$deletedPage->delete();
@ -141,7 +139,7 @@ class CMSSiteTreeFilterTest extends SapphireTest
public function testStatusDraftPagesFilter()
{
$draftPage = $this->objFromFixture('Page', 'page4');
$draftPage = $this->objFromFixture(SiteTree::class, 'page4');
$draftPage = Versioned::get_one_by_stage(
SiteTree::class,
'Stage',
@ -164,20 +162,23 @@ class CMSSiteTreeFilterTest extends SapphireTest
public function testDateFromToLastSameDate()
{
$draftPage = $this->objFromFixture('Page', 'page4');
$draftPage = $this->objFromFixture(SiteTree::class, 'page4');
// Grab the date
$date = substr($draftPage->LastEdited ?? '', 0, 10);
// Filter with that date
$filter = new CMSSiteTreeFilter_Search([
'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()
{
$removedDraftPage = $this->objFromFixture('Page', 'page6');
$removedDraftPage = $this->objFromFixture(SiteTree::class, 'page6');
$removedDraftPage->publishRecursive();
$removedDraftPage->deleteFromStage('Stage');
$removedDraftPage = Versioned::get_one_by_stage(
@ -202,7 +203,7 @@ class CMSSiteTreeFilterTest extends SapphireTest
public function testStatusDeletedFilter()
{
$deletedPage = $this->objFromFixture('Page', 'page7');
$deletedPage = $this->objFromFixture(SiteTree::class, 'page7');
$deletedPage->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
$deletedPageID = $deletedPage->ID;

View File

@ -1,4 +1,4 @@
Page:
SilverStripe\CMS\Model\SiteTree:
page1:
Title: Page 1
page2:
@ -15,19 +15,19 @@ Page:
page7:
Title: Page 7
page7a:
Parent: =>Page.page7
Parent: =>SilverStripe\CMS\Model\SiteTree.page7
Title: Page 7a
page2a:
Parent: =>Page.page2
Parent: =>SilverStripe\CMS\Model\SiteTree.page2
Title: Page 2a
page2b:
Parent: =>Page.page2
Parent: =>SilverStripe\CMS\Model\SiteTree.page2
Title: Page 2b
page3a:
Parent: =>Page.page3
Parent: =>SilverStripe\CMS\Model\SiteTree.page3
Title: Page 3a
page3b:
Parent: =>Page.page3
Parent: =>SilverStripe\CMS\Model\SiteTree.page3
Title: Page 3b
page8:
Title: EncodedUrlSegment

View File

@ -32,7 +32,7 @@ class CMSTreeTest extends FunctionalTest
$data = [
'SiblingIDs' => $siblingIDs,
'ID' => $page2->ID,
'ParentID' => 0
'ParentID' => 0,
];
$response = $this->post('admin/pages/edit/savetreenode', $data);
@ -62,12 +62,12 @@ class CMSTreeTest extends FunctionalTest
$siblingIDs = [
$page31->ID,
$page2->ID,
$page32->ID
$page32->ID,
];
$data = [
'SiblingIDs' => $siblingIDs,
'ID' => $page2->ID,
'ParentID' => $page3->ID
'ParentID' => $page3->ID,
];
$response = $this->post('admin/pages/edit/savetreenode', $data);
$this->assertEquals(200, $response->getStatusCode());
@ -95,7 +95,7 @@ class CMSTreeTest extends FunctionalTest
$this->logInWithPermission('ADMIN');
// 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('application/json', $result->getHeader('Content-Type'));
$data = json_decode($result->getBody() ?? '', true);
@ -105,7 +105,7 @@ class CMSTreeTest extends FunctionalTest
$this->assertEmpty($pageData['PrevID']);
// 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('application/json', $result->getHeader('Content-Type'));
$data = json_decode($result->getBody() ?? '', true);
@ -115,7 +115,7 @@ class CMSTreeTest extends FunctionalTest
$this->assertEmpty($pageData['PrevID']);
// 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('application/json', $result->getHeader('Content-Type'));
$data = json_decode($result->getBody() ?? '', true);

View File

@ -6,7 +6,7 @@ use SilverStripe\Versioned\Versioned;
use SilverStripe\Control\HTTPResponse_Exception;
use SilverStripe\Core\Config\Config;
use SilverStripe\Dev\FunctionalTest;
use Page;
use SilverStripe\CMS\Model\SiteTree;
class ContentControllerPermissionsTest extends FunctionalTest
{
@ -17,7 +17,7 @@ class ContentControllerPermissionsTest extends FunctionalTest
public function testCanViewStage()
{
// Create a new page
$page = new Page();
$page = new SiteTree();
$page->URLSegment = 'testpage';
$page->write();
$page->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
@ -38,7 +38,11 @@ class ContentControllerPermissionsTest extends FunctionalTest
$response = $responseException->getResponse();
}
// 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(
Config::inst()->get('SilverStripe\\Security\\Security', 'login_url'),
$response->getHeader('Location')
@ -47,6 +51,10 @@ class ContentControllerPermissionsTest extends FunctionalTest
$this->logInWithPermission('CMS_ACCESS_CMSMain');
$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'
);
}
}

View File

@ -4,23 +4,23 @@ namespace SilverStripe\CMS\Tests\Controllers;
use SilverStripe\Assets\File;
use SilverStripe\CMS\Controllers\ContentController;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\CMS\Search\ContentControllerSearchExtension;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\ORM\Search\FulltextSearchable;
use SilverStripe\Versioned\Versioned;
use Page;
class ContentControllerSearchExtensionTest extends SapphireTest
{
protected static $required_extensions = [
ContentController::class => [
ContentControllerSearchExtension::class,
]
],
];
public function testCustomSearchFormClassesToTest()
{
$page = new Page();
$page = new SiteTree();
$page->URLSegment = 'whatever';
$page->Content = 'oh really?';
$page->write();

10
tests/php/Controllers/ContentControllerTest.php Executable file → Normal file
View File

@ -10,7 +10,6 @@ use SilverStripe\Control\HTTPResponse_Exception;
use SilverStripe\Core\Config\Config;
use SilverStripe\Dev\FunctionalTest;
use SilverStripe\Versioned\Versioned;
use Page;
class ContentControllerTest extends FunctionalTest
{
@ -31,8 +30,8 @@ class ContentControllerTest extends FunctionalTest
Config::modify()->set(SiteTree::class, 'nested_urls', true);
// Ensure all pages are published
/** @var Page $page */
foreach (Page::get() as $page) {
/** @var SiteTree $page */
foreach (SiteTree::get() as $page) {
$page->publishSingle();
}
}
@ -95,8 +94,7 @@ class ContentControllerTest extends FunctionalTest
public function testDeepNestedURLs()
{
$page = new Page();
$page = new SiteTree();
$page->URLSegment = 'base-page';
$page->write();
$page->publishSingle();
@ -174,7 +172,7 @@ class ContentControllerTest extends FunctionalTest
$response = $this->get($page->RelativeLink());
$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->URLSegment = "test";
$page->write();

View File

@ -2,10 +2,10 @@
namespace SilverStripe\CMS\Tests\Controllers;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Dev\TestOnly;
use Page;
class ContentControllerTestPage extends Page implements TestOnly
class ContentControllerTestPage extends SiteTree implements TestOnly
{
private static $table_name = 'ContentControllerTestPage';
}

View File

@ -9,7 +9,7 @@ class ContentControllerTestPageController extends PageController implements Test
{
private static $allowed_actions = [
'test',
'testwithouttemplate'
'testwithouttemplate',
];
public function testwithouttemplate()

View File

@ -2,10 +2,9 @@
namespace SilverStripe\CMS\Tests\Controllers;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Dev\TestOnly;
use Page;
class ContentControllerTestPageWithoutController extends Page implements TestOnly
class ContentControllerTestPageWithoutController extends SiteTree implements TestOnly
{
}

View File

@ -2,10 +2,10 @@
namespace SilverStripe\CMS\Tests\Controllers;
use SilverStripe\CMS\Model\SiteTree;
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';
}

View File

@ -2,14 +2,13 @@
namespace SilverStripe\CMS\Tests\Controllers;
use SilverStripe\CMS\Controllers\ContentController;
use SilverStripe\Dev\TestOnly;
use PageController;
class ContentControllerTest_PageController extends PageController implements TestOnly
class ContentControllerTest_PageController extends ContentController implements TestOnly
{
private static $allowed_actions = [
'second_index'
'second_index',
];
public function index()

View File

@ -10,7 +10,6 @@ use SilverStripe\Core\Config\Config;
use SilverStripe\Control\Director;
use SilverStripe\Control\Controller;
use SilverStripe\Dev\FunctionalTest;
use Page;
use SilverStripe\View\Parsers\URLSegmentFilter;
class ModelAsControllerTest extends FunctionalTest
@ -34,7 +33,7 @@ class ModelAsControllerTest extends FunctionalTest
protected function generateNestedPagesFixture()
{
$level1 = new Page();
$level1 = new SiteTree();
$level1->Title = 'First Level';
$level1->URLSegment = 'level1';
$level1->write();
@ -44,7 +43,7 @@ class ModelAsControllerTest extends FunctionalTest
$level1->write();
$level1->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
$level2 = new Page();
$level2 = new SiteTree();
$level2->Title = 'Second Level';
$level2->URLSegment = 'level2';
$level2->ParentID = $level1->ID;
@ -55,7 +54,7 @@ class ModelAsControllerTest extends FunctionalTest
$level2->write();
$level2->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
$level3 = new Page();
$level3 = new SiteTree();
$level3->Title = "Level 3";
$level3->URLSegment = 'level3';
$level3->ParentID = $level2->ID;
@ -93,7 +92,7 @@ class ModelAsControllerTest extends FunctionalTest
$response = $this->get('newlevel1/level2');
$this->assertEquals($response->getStatusCode(), 301);
$this->assertEquals(
Controller::join_links(Director::baseURL() . 'newlevel1/newlevel2/'),
Controller::join_links(Director::baseURL() . 'newlevel1/newlevel2'),
$response->getHeader('Location')
);
@ -101,7 +100,7 @@ class ModelAsControllerTest extends FunctionalTest
$response = $this->get('newlevel1/newlevel2/level3');
$this->assertEquals($response->getStatusCode(), 301);
$this->assertEquals(
Controller::join_links(Director::baseURL() . 'newlevel1/newlevel2/newlevel3/'),
Controller::join_links(Director::baseURL() . 'newlevel1/newlevel2/newlevel3'),
$response->getHeader('Location')
);
@ -115,7 +114,7 @@ class ModelAsControllerTest extends FunctionalTest
*/
public function testHeavilyNestedRenamedRedirectedPages()
{
$page = new Page();
$page = new SiteTree();
$page->Title = 'First Level';
$page->URLSegment = 'oldurl';
$page->write();
@ -125,28 +124,28 @@ class ModelAsControllerTest extends FunctionalTest
$page->write();
$page->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
$page2 = new Page();
$page2 = new SiteTree();
$page2->Title = 'Second Level Page';
$page2->URLSegment = 'level2';
$page2->ParentID = $page->ID;
$page2->write();
$page2->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
$page3 = new Page();
$page3 = new SiteTree();
$page3->Title = 'Third Level Page';
$page3->URLSegment = 'level3';
$page3->ParentID = $page2->ID;
$page3->write();
$page3->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
$page4 = new Page();
$page4 = new SiteTree();
$page4->Title = 'Fourth Level Page';
$page4->URLSegment = 'level4';
$page4->ParentID = $page3->ID;
$page4->write();
$page4->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
$page5 = new Page();
$page5 = new SiteTree();
$page5->Title = 'Fifth Level Page';
$page5->URLSegment = 'level5';
$page5->ParentID = $page4->ID;
@ -154,10 +153,10 @@ class ModelAsControllerTest extends FunctionalTest
$page5->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
// 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(
Controller::join_links(Director::baseURL() . 'newurl/level2/level3/level4/level5/'),
Controller::join_links(Director::baseURL() . 'newurl/level2/level3/level4/level5'),
$response->getHeader('Location')
);
}
@ -171,7 +170,7 @@ class ModelAsControllerTest extends FunctionalTest
$response = $this->get('newlevel3');
$this->assertEquals(301, $response->getStatusCode());
$this->assertEquals(
Director::baseURL() . 'newlevel1/newlevel2/newlevel3/',
Director::baseURL() . 'newlevel1/newlevel2/newlevel3',
$response->getHeader("Location")
);
@ -179,7 +178,7 @@ class ModelAsControllerTest extends FunctionalTest
$response = $this->get('level3');
$this->assertEquals(301, $response->getStatusCode());
$this->assertEquals(
Director::baseURL() . 'newlevel1/newlevel2/newlevel3/',
Director::baseURL() . 'newlevel1/newlevel2/newlevel3',
$response->getHeader("Location")
);
}
@ -188,8 +187,8 @@ class ModelAsControllerTest extends FunctionalTest
{
$this->generateNestedPagesFixture();
$otherParent = new Page([
'URLSegment' => 'otherparent'
$otherParent = new SiteTree([
'URLSegment' => 'otherparent',
]);
$otherParent->write();
$otherParent->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
@ -215,10 +214,10 @@ class ModelAsControllerTest extends FunctionalTest
$this->generateNestedPagesFixture();
// 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(
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')
);
}
@ -232,9 +231,9 @@ class ModelAsControllerTest extends FunctionalTest
{
$this->generateNestedPagesFixture();
$otherLevel1 = new Page([
$otherLevel1 = new SiteTree([
'Title' => "Other Level 1",
'URLSegment' => 'level1'
'URLSegment' => 'level1',
]);
$otherLevel1->write();
$otherLevel1->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
@ -260,7 +259,7 @@ class ModelAsControllerTest extends FunctionalTest
*/
public function testFindOldPage()
{
$page = new Page();
$page = new SiteTree();
$page->Title = 'First Level';
$page->URLSegment = 'oldurl';
$page->write();
@ -274,7 +273,7 @@ class ModelAsControllerTest extends FunctionalTest
$matchedPage = SiteTree::get_by_link($url);
$this->assertEquals('First Level', $matchedPage->Title);
$page2 = new Page();
$page2 = new SiteTree();
$page2->Title = 'Second Level Page';
$page2->URLSegment = 'oldpage2';
$page2->ParentID = $page->ID;
@ -304,12 +303,12 @@ class ModelAsControllerTest extends FunctionalTest
RootURLController::reset();
Config::modify()->set(SiteTree::class, 'nested_urls', true);
$draft = new Page();
$draft = new SiteTree();
$draft->Title = 'Root Leve Draft Page';
$draft->URLSegment = 'root';
$draft->write();
$published = new Page();
$published = new SiteTree();
$published->Title = 'Published Page Under Draft Page';
$published->URLSegment = 'sub-root';
$published->write();
@ -319,7 +318,8 @@ class ModelAsControllerTest extends FunctionalTest
$this->assertEquals(
$response->getStatusCode(),
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);
$parent = new Page();
$parent = new SiteTree();
$parent->Title = 'Multibyte test';
$parent->URLSegment = 'بلاگ';
$parent->write();
$parent->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
$child = new Page();
$child = new SiteTree();
$child->Title = 'Multibyte test';
$child->URLSegment = 'فضة';
$child->ParentID = $parent->ID;

View File

@ -13,7 +13,7 @@ class RootURLControllerTest extends SapphireTest
public function testGetHomepageLink()
{
$default = $this->objFromFixture('Page', 'home');
$default = $this->objFromFixture(SiteTree::class, 'home');
Config::modify()->set(SiteTree::class, 'nested_urls', false);
$this->assertEquals('home', RootURLController::get_homepage_link());

View File

@ -1,9 +1,9 @@
Page:
SilverStripe\CMS\Model\SiteTree:
home:
Title: Home
nested:
Title: Nested Home
Parent: =>Page.home
Parent: =>SilverStripe\CMS\Model\SiteTree.home
page1:
Title: First Page
URLSegment: page1

View File

@ -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());
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -44,7 +44,7 @@ class LinkablePluginTest extends SapphireTest
public function testResolver()
{
$page = SiteTree::create([
$page = new SiteTree([
'Title' => 'Test page',
'URLSegment' => 'test-page',
'ParentID' => 0,
@ -52,7 +52,7 @@ class LinkablePluginTest extends SapphireTest
$page->write();
$page->publishRecursive();
$page = SiteTree::create([
$page = new SiteTree([
'Title' => 'Other test page',
'URLSegment' => 'other-test-page',
'ParentID' => 0,

View File

@ -2,7 +2,7 @@
namespace SilverStripe\CMS\Tests\Model;
use Page;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\CMS\Model\RedirectorPage;
use SilverStripe\CMS\Model\RedirectorPageController;
use SilverStripe\Control\Director;
@ -36,53 +36,65 @@ class RedirectorPageTest extends FunctionalTest
Director::config()->set('alternate_base_url', 'http://www.mysite.com/');
// Ensure all pages are published
/** @var Page $page */
foreach (Page::get() as $page) {
/** @var SiteTree $page */
foreach (SiteTree::get() as $page) {
$page->publishSingle();
}
}
public function testGoodRedirectors()
{
/* For good redirectors, the final destination URL will be returned */
$this->assertEquals("http://www.google.com", $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());
// For good redirectors, the final destination URL will be returned
$this->assertEquals(
"http://www.google.com",
$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()
{
/* If a redirector page is misconfigured, then its link method will just return the usual URLSegment-generated value */
// 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());
$this->assertEquals('/bad-external', $page1->Link());
/* An error message will be shown if you visit it */
// An error message will be shown if you visit it
$content = $this->get(Director::makeRelative($page1->Link()))->getBody();
$this->assertStringContainsString('message-setupWithoutRedirect', $content);
/* This also applies for internal links */
// This also applies for internal links
$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();
$this->assertStringContainsString('message-setupWithoutRedirect', $content);
}
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');
$this->assertEquals('/reflexive/', $page->Link());
$this->assertEquals('/reflexive', $page->Link());
$content = $this->get(Director::makeRelative($page->Link()))->getBody();
$this->assertStringContainsString('message-setupWithoutRedirect', $content);
/* Transitive redirectors are those that point to another redirector page. They should send people to the URLSegment
* of the destination page - the middle-stop, so to speak. That should redirect to the final destination */
// Transitive redirectors are those that point to another redirector page.
// 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');
$this->assertEquals('/good-internal/', $page->Link());
$this->assertEquals('/good-internal', $page->Link());
$this->autoFollowRedirection = false;
$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()
@ -90,7 +102,11 @@ class RedirectorPageTest extends FunctionalTest
$page = $this->objFromFixture(RedirectorPage::class, 'externalnoprefix');
$this->assertEquals($page->ExternalURL, 'http://google.com', 'onBeforeWrite has prefixed with http');
$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()

View File

@ -1,4 +1,4 @@
Page:
SilverStripe\CMS\Model\SiteTree:
dest:
Title: Redirection Dest
URLSegment: redirection-dest
@ -17,7 +17,7 @@ SilverStripe\CMS\Model\RedirectorPage:
Title: Good Internal
URLSegment: good-internal
RedirectionType: Internal
LinkTo: =>Page.dest
LinkTo: =>SilverStripe\CMS\Model\SiteTree.dest
badexternal:
Title: Bad External
RedirectionType: External

View File

@ -2,7 +2,6 @@
namespace SilverStripe\CMS\Tests\Model;
use Page;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Dev\FunctionalTest;
use SilverStripe\ORM\DB;
@ -21,7 +20,6 @@ use SilverStripe\Versioned\Versioned;
*/
class SiteTreeActionsTest extends FunctionalTest
{
protected static $fixture_file = 'SiteTreeActionsTest.yml';
public function testActionsReadonly()
@ -38,7 +36,7 @@ class SiteTreeActionsTest extends FunctionalTest
Security::setCurrentUser($readonlyEditor);
// Reload latest version
$page = Page::get()->byID($page->ID);
$page = SiteTree::get()->byID($page->ID);
$actions = $page->getCMSActions();
$this->assertNull($actions->dataFieldByName('action_save'));
@ -84,14 +82,14 @@ class SiteTreeActionsTest extends FunctionalTest
$author = $this->objFromFixture(Member::class, 'cmseditor');
Security::setCurrentUser($author);
/** @var Page $page */
$page = new Page();
/** @var SiteTree $page */
$page = new SiteTree();
$page->CanEditType = 'LoggedInUsers';
$page->write();
$page->publishRecursive();
// Reload latest version
$page = Page::get()->byID($page->ID);
$page = SiteTree::get()->byID($page->ID);
$actions = $page->getCMSActions();
@ -108,7 +106,7 @@ class SiteTreeActionsTest extends FunctionalTest
$author = $this->objFromFixture(Member::class, 'cmseditor');
Security::setCurrentUser($author);
$page = new Page();
$page = new SiteTree();
$page->CanEditType = 'LoggedInUsers';
$page->write();
$this->assertTrue($page->canPublish());
@ -135,7 +133,7 @@ class SiteTreeActionsTest extends FunctionalTest
$author = $this->objFromFixture(Member::class, 'cmseditor');
Security::setCurrentUser($author);
$page = new Page();
$page = new SiteTree();
$page->CanEditType = 'LoggedInUsers';
$page->write();
$this->assertTrue($page->canPublish());
@ -145,7 +143,7 @@ class SiteTreeActionsTest extends FunctionalTest
$page->flushCache();
// Reload latest version
$page = Page::get()->byID($page->ID);
$page = SiteTree::get()->byID($page->ID);
$actions = $page->getCMSActions();
$this->assertNotNull($actions->dataFieldByName('action_save'));
@ -158,15 +156,17 @@ class SiteTreeActionsTest extends FunctionalTest
public function testActionsViewingOldVersion()
{
$p = new Page();
$p = new SiteTree();
$p->Content = 'test page first version';
$p->write();
$p->Content = 'new content';
$p->write();
// 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();
$old = Versioned::get_version('Page', $p->ID, $version);
$version = DB::query(
'SELECT "Version" FROM "SiteTree_Versions" WHERE "Content" = \'test page first version\''
)->value();
$old = Versioned::get_version(SiteTree::class, $p->ID, $version);
$actions = $old->getCMSActions();
$this->assertNull($actions->dataFieldByName('action_save'));
$this->assertNull($actions->dataFieldByName('action_publish'));

View File

@ -4,9 +4,9 @@ namespace SilverStripe\CMS\Tests\Model;
use SilverStripe\Dev\TestOnly;
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)
{

View File

@ -18,7 +18,7 @@ class SiteTreeBacklinksTest extends SapphireTest
protected static $required_extensions = [
SiteTree::class => [
SiteTreeBacklinksTest_DOD::class
SiteTreeBacklinksTest_DOD::class,
],
];
@ -34,10 +34,10 @@ class SiteTreeBacklinksTest extends SapphireTest
// testing here.
$this->logInWithPermission('ADMIN');
$page3 = $this->objFromFixture('Page', 'page3');
$page3 = $this->objFromFixture(SiteTree::class, 'page3');
$page3->Content = str_replace(
'$page1.ID',
$this->objFromFixture('Page', 'page1')->ID ?? '',
$this->objFromFixture(SiteTree::class, 'page1')->ID ?? '',
$page3->Content ?? ''
);
$page3->write();
@ -46,18 +46,22 @@ class SiteTreeBacklinksTest extends SapphireTest
public function testSavingPageWithLinkAddsBacklink()
{
// load page 1
$page1 = $this->objFromFixture('Page', 'page1');
$page1 = $this->objFromFixture(SiteTree::class, 'page1');
// assert backlink to page 2 doesn't exist
$page2 = $this->objFromFixture('Page', 'page2');
$this->assertNotContains($page2->ID, $page1->BackLinkTracking()->column('ID'), 'Assert backlink to page 2 doesn\'t exist');
$page2 = $this->objFromFixture(SiteTree::class, 'page2');
$this->assertNotContains(
$page2->ID,
$page1->BackLinkTracking()->column('ID'),
'Assert backlink to page 2 doesn\'t exist'
);
// 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();
// load page 1
$page1 = $this->objFromFixture('Page', 'page1');
$page1 = $this->objFromFixture(SiteTree::class, 'page1');
// 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()
{
// load page 1
$page1 = $this->objFromFixture('Page', 'page1');
$page1 = $this->objFromFixture(SiteTree::class, 'page1');
// 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');
// remove hyperlink to page 1
@ -77,116 +81,156 @@ class SiteTreeBacklinksTest extends SapphireTest
$page3->write();
// load page 1
$page1 = $this->objFromFixture('Page', 'page1');
$page1 = $this->objFromFixture(SiteTree::class, 'page1');
// 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()
{
// load page 1
$page1 = $this->objFromFixture('Page', 'page1');
$page1 = $this->objFromFixture(SiteTree::class, 'page1');
// 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');
// assert hyperlink to page 1's current url exists on page 3
$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
$page1->URLSegment = 'new-url-segment';
$page1->write();
// load page 3
$page3 = $this->objFromFixture('Page', 'page3');
$page3 = $this->objFromFixture(SiteTree::class, 'page3');
// assert hyperlink to page 1's new url exists
$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()
{
// publish page 1 & 3
$page1 = $this->objFromFixture('Page', 'page1');
$page3 = $this->objFromFixture('Page', 'page3');
$page1 = $this->objFromFixture(SiteTree::class, 'page1');
$page3 = $this->objFromFixture(SiteTree::class, 'page3');
$this->assertTrue($page1->publishRecursive());
$this->assertTrue($page3->publishRecursive());
// load pages from live
$page1live = Versioned::get_one_by_stage('Page', 'Live', '"SiteTree"."ID" = ' . $page1->ID);
$page3live = Versioned::get_one_by_stage('Page', 'Live', '"SiteTree"."ID" = ' . $page3->ID);
$page1live = Versioned::get_one_by_stage(SiteTree::class, 'Live', '"SiteTree"."ID" = ' . $page1->ID);
$page3live = Versioned::get_one_by_stage(SiteTree::class, 'Live', '"SiteTree"."ID" = ' . $page3->ID);
// 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
$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
$page1live->URLSegment = 'new-url-segment';
$page1live->writeToStage('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
Versioned::set_stage(Versioned::LIVE);
$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()
{
// publish page 1 & 3
$page1 = $this->objFromFixture('Page', 'page1');
$page3 = $this->objFromFixture('Page', 'page3');
$page1 = $this->objFromFixture(SiteTree::class, 'page1');
$page3 = $this->objFromFixture(SiteTree::class, 'page3');
$this->assertTrue($page1->publishRecursive());
$this->assertTrue($page3->publishRecursive());
// 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
$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
$page1->URLSegment = 'new-url-segment';
$page1->write();
// 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);
$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
$this->assertTrue($page1->publishRecursive());
// 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());
$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()
{
// publish page 1 & 3
$page1 = $this->objFromFixture('Page', 'page1');
$page3 = $this->objFromFixture('Page', 'page3');
$page1 = $this->objFromFixture(SiteTree::class, 'page1');
$page3 = $this->objFromFixture(SiteTree::class, 'page3');
$this->assertTrue($page1->publishRecursive());
$this->assertTrue($page3->publishRecursive());
// assert hyperlink to page 1's current url exists
$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
$page1->URLSegment = 'new-url-segment';
@ -195,42 +239,58 @@ class SiteTreeBacklinksTest extends SapphireTest
$page1->write();
// 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());
$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
$this->assertTrue($page3->publishRecursive());
// 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);
$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
$this->assertTrue($page1->publishRecursive());
// 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());
$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()
{
/** @var Page $page1 */
$page1 = $this->objFromFixture('Page', 'page1');
/** @var Page $page2 */
$page2 = $this->objFromFixture('Page', 'page2');
/** @var SiteTree $page1 */
$page1 = $this->objFromFixture(SiteTree::class, 'page1');
/** @var SiteTree $page2 */
$page2 = $this->objFromFixture(SiteTree::class, 'page2');
$page1->publishRecursive();
$page2->publishRecursive();
// 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
$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->publishRecursive();
@ -238,23 +298,32 @@ class SiteTreeBacklinksTest extends SapphireTest
$this->assertContains($page2->ID, $page1->BackLinkTracking()->column('ID'), 'Assert backlink to page 2 exists');
// update page1 url
$page1 = $this->objFromFixture('Page', 'page1');
$page1 = $this->objFromFixture(SiteTree::class, 'page1');
$page1->URLSegment = "page1-new-url";
$page1->write();
// confirm that draft link on page2 has been rewritten
$page2 = $this->objFromFixture('Page', 'page2');
$this->assertEquals('<p><a href="'.Director::baseURL().'page1-new-url/">Testing page 1 link</a></p>', $page2->obj('ExtraContent')->forTemplate());
$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()
);
// 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);
$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
$page1->publishRecursive();
$page2Live = Versioned::get_one_by_stage("Page", "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());
$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()
);
// Edit draft again
Versioned::set_stage(Versioned::DRAFT);
@ -262,12 +331,16 @@ class SiteTreeBacklinksTest extends SapphireTest
$page2->write();
// 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()
{
$page = $this->objFromFixture('Page', 'page2');
$page = $this->objFromFixture(SiteTree::class, 'page2');
$referencingObject = new SiteTreeBacklinksTestContentObject();
$referencingObject->Content = '<p><a href="[sitetree_link,id='

View File

@ -1,4 +1,4 @@
Page:
SilverStripe\CMS\Model\SiteTree:
page1:
Title: page1
URLSegment: page1
@ -11,6 +11,6 @@ Page:
Title: page3
URLSegment: page3
Content: '<p><a href="[sitetree_link,id=$page1.ID]">Testing page 1 link</a></p>'
LinkTracking: =>Page.page1
LinkTracking: =>SilverStripe\CMS\Model\SiteTree.page1

View File

@ -2,7 +2,6 @@
namespace SilverStripe\CMS\Tests\Model;
use Page;
use Silverstripe\Assets\Dev\TestAssetStore;
use SilverStripe\CMS\Model\RedirectorPage;
use SilverStripe\CMS\Model\SiteTree;
@ -42,15 +41,15 @@ class SiteTreeBrokenLinksTest extends SapphireTest
public function testBrokenLinksBetweenPages()
{
/** @var Page $obj */
$obj = $this->objFromFixture('Page', 'content');
/** @var SiteTree $obj */
$obj = $this->objFromFixture(SiteTree::class, 'content');
$obj->Content = '<a href="[sitetree_link,id=3423423]">this is a broken link</a>';
$obj->syncLinkTracking();
$this->assertTrue($obj->HasBrokenLink, 'Page has a broken link');
$obj->Content = '<a href="[sitetree_link,id=' . $this->idFromFixture(
'Page',
SiteTree::class,
'about'
) . ']">this is not a broken link</a>';
$obj->syncLinkTracking();
@ -62,8 +61,8 @@ class SiteTreeBrokenLinksTest extends SapphireTest
*/
public function testBrokenLinksNonPage()
{
/** @var Page $aboutPage */
$aboutPage = $this->objFromFixture('Page', 'about');
/** @var SiteTree $aboutPage */
$aboutPage = $this->objFromFixture(SiteTree::class, 'about');
/** @var NotPageObject $obj */
$obj = $this->objFromFixture(NotPageObject::class, 'object1');
@ -94,7 +93,7 @@ class SiteTreeBrokenLinksTest extends SapphireTest
// About-page backlinks contains this object
$this->assertListEquals(
[
['ID' => $obj->ID]
['ID' => $obj->ID],
],
$aboutPage->BackLinkTracking()
);
@ -102,9 +101,9 @@ class SiteTreeBrokenLinksTest extends SapphireTest
public function testBrokenAnchorBetweenPages()
{
/** @var Page $obj */
$obj = $this->objFromFixture('Page', 'content');
$target = $this->objFromFixture('Page', 'about');
/** @var SiteTree $obj */
$obj = $this->objFromFixture(SiteTree::class, 'content');
$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->syncLinkTracking();
@ -117,7 +116,7 @@ class SiteTreeBrokenLinksTest extends SapphireTest
public function testBrokenVirtualPages()
{
$obj = $this->objFromFixture('Page', 'content');
$obj = $this->objFromFixture(SiteTree::class, 'content');
$vp = new VirtualPage();
$vp->CopyContentFromID = $obj->ID;
@ -131,7 +130,7 @@ class SiteTreeBrokenLinksTest extends SapphireTest
public function testBrokenInternalRedirectorPages()
{
$obj = $this->objFromFixture('Page', 'content');
$obj = $this->objFromFixture(SiteTree::class, 'content');
$rp = new RedirectorPage();
$rp->RedirectionType = 'Internal';
@ -148,10 +147,10 @@ class SiteTreeBrokenLinksTest extends SapphireTest
public function testDeletingMarksBackLinkedPagesAsBroken()
{
// 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 */
$linkSrc = $this->objFromFixture('Page', 'content');
/** @var SiteTree $linkSrc */
$linkSrc = $this->objFromFixture(SiteTree::class, 'content');
$linkSrc->Content = "<p><a href=\"[sitetree_link,id=$linkDest->ID]\">about us</a></p>";
$linkSrc->write();
@ -163,7 +162,7 @@ class SiteTreeBrokenLinksTest extends SapphireTest
// Confirm draft has broken link
$linkSrc->flushCache();
$linkSrc = $this->objFromFixture('Page', 'content');
$linkSrc = $this->objFromFixture(SiteTree::class, 'content');
$this->assertEquals(1, (int)$linkSrc->HasBrokenLink);
}
@ -173,13 +172,13 @@ class SiteTreeBrokenLinksTest extends SapphireTest
$this->logInWithPermission('ADMIN');
// Set up two draft pages with a link from content -> about
/** @var Page $linkDest */
$linkDest = $this->objFromFixture('Page', 'about');
/** @var SiteTree $linkDest */
$linkDest = $this->objFromFixture(SiteTree::class, 'about');
// Ensure that it's not on the published site
$linkDest->doUnpublish();
/** @var Page $linkSrc */
$linkSrc = $this->objFromFixture('Page', 'content');
/** @var SiteTree $linkSrc */
$linkSrc = $this->objFromFixture(SiteTree::class, 'content');
$linkSrc->Content = "<p><a href=\"[sitetree_link,id=$linkDest->ID]\">about us</a></p>";
$linkSrc->write();
@ -191,20 +190,20 @@ class SiteTreeBrokenLinksTest extends SapphireTest
// Live doesn't have separate broken link tracking
$this->assertEquals(0, DB::query("SELECT \"HasBrokenLink\" FROM \"SiteTree_Live\"
WHERE \"ID\" = $linkSrc->ID")->value());
WHERE \"ID\" = $linkSrc->ID")->value());
}
public function testRestoreFixesBrokenLinks()
{
// Create page and virtual page
$p = new Page();
$p = new SiteTree();
$p->Title = "source";
$p->write();
$pageID = $p->ID;
$this->assertTrue($p->publishRecursive());
// Content links are one kind of link to pages
$p2 = new Page();
$p2 = new SiteTree();
$p2->Title = "regular link";
$p2->Content = "<a href=\"[sitetree_link,id=$p->ID]\">test</a>";
$p2->write();
@ -264,14 +263,14 @@ class SiteTreeBrokenLinksTest extends SapphireTest
public function testRevertToLiveFixesBrokenLinks()
{
// Create page and virutal page
$page = new Page();
$page = new SiteTree();
$page->Title = "source";
$page->write();
$pageID = $page->ID;
$this->assertTrue($page->publishRecursive());
// Content links are one kind of link to pages
$page2 = new Page();
$page2 = new SiteTree();
$page2->Title = "regular link";
$page2->Content = "<a href=\"[sitetree_link,id={$pageID}]\">test</a>";
$page2->write();
@ -302,7 +301,7 @@ class SiteTreeBrokenLinksTest extends SapphireTest
$this->assertEquals(1, $redirectorPage->HasBrokenLink);
// 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->doRevertToLive();
@ -316,15 +315,17 @@ class SiteTreeBrokenLinksTest extends SapphireTest
public function testBrokenAnchorLinksInAPage()
{
/** @var Page $obj */
$obj = $this->objFromFixture('Page', 'content');
/** @var SiteTree $obj */
$obj = $this->objFromFixture(SiteTree::class, '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();
$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();
$this->assertFalse($obj->HasBrokenLink, 'Page doesn\'t have a broken anchor or skiplink');
}

View File

@ -1,4 +1,4 @@
Page:
SilverStripe\CMS\Model\SiteTree:
content:
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>.'
@ -13,7 +13,7 @@ Page:
workingInternalRedirector:
RedirectionType: Internal
Title: RedirectorPageToBrokenInteralPage
LinkTo: =>Page.content
LinkTo: =>SilverStripe\CMS\Model\SiteTree.content
SilverStripe\CMS\Tests\Model\SiteTreeBrokenLinksTest\NotPageObject:
object1:
Content: 'Everything will be ok'

View File

@ -2,7 +2,6 @@
namespace SilverStripe\CMS\Tests\Model;
use Page;
use SilverStripe\Assets\Dev\TestAssetStore;
use SilverStripe\Assets\File;
use SilverStripe\Assets\Filesystem;
@ -31,8 +30,8 @@ class SiteTreeHTMLEditorFieldTest extends FunctionalTest
}
// Ensure all pages are published
/** @var Page $page */
foreach (Page::get() as $page) {
/** @var SiteTree $page */
foreach (SiteTree::get() as $page) {
$page->publishSingle();
}
}
@ -55,7 +54,11 @@ class SiteTreeHTMLEditorFieldTest extends FunctionalTest
$editor->setValue("<a href=\"[sitetree_link,id=$aboutID]\">Example Link</a>");
$editor->saveInto($sitetree);
$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(
"<a href=\"[sitetree_link,id=$aboutID]\"></a><a href=\"[sitetree_link,id=$contactID]\"></a>"

View File

@ -2,7 +2,6 @@
namespace SilverStripe\CMS\Tests\Model;
use Page;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\CMS\Model\SiteTreeLinkTracking_Parser;
use SilverStripe\Control\Director;
@ -32,7 +31,7 @@ class SiteTreeLinkTrackingTest extends SapphireTest
public function testParser()
{
SiteTree::add_extension(Page::class, SiteTreeLinkTracking_Extension::class);
SiteTree::add_extension(SiteTree::class, SiteTreeLinkTracking_Extension::class);
// Shortcodes
$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->assertTrue($this->isBroken('<a href="##anchor">anchor</a>'));
$page = new Page();
$page = new SiteTree();
$page->Content = '<a name="yes-name-anchor">name</a><a id="yes-id-anchor">id</a>';
$page->write();
@ -72,7 +70,7 @@ class SiteTreeLinkTrackingTest extends SapphireTest
protected function highlight($content)
{
$page = new Page();
$page = new SiteTree();
$page->Content = $content;
$page->write();
return $page->Content;
@ -87,7 +85,7 @@ class SiteTreeLinkTrackingTest extends SapphireTest
$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.');
$otherPage = new Page();
$otherPage = new SiteTree();
$otherPage->Content = '';
$otherPage->write();

View File

@ -2,7 +2,6 @@
namespace SilverStripe\CMS\Tests\Model;
use Page;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Control\HTTPResponse_Exception;
use SilverStripe\Dev\FunctionalTest;
@ -33,8 +32,8 @@ class SiteTreePermissionsTest extends FunctionalTest
$this->autoFollowRedirection = false;
// Ensure all pages are published
/** @var Page $page */
foreach (Page::get() as $page) {
/** @var SiteTree $page */
foreach (SiteTree::get() as $page) {
if ($page->URLSegment !== 'draft-only') {
$page->publishSingle();
}
@ -46,8 +45,8 @@ class SiteTreePermissionsTest extends FunctionalTest
{
$this->autoFollowRedirection = false;
/** @var Page $draftOnlyPage */
$draftOnlyPage = $this->objFromFixture(Page::class, 'draftOnlyPage');
/** @var SiteTree $draftOnlyPage */
$draftOnlyPage = $this->objFromFixture(SiteTree::class, 'draftOnlyPage');
$this->logOut();
$response = $this->get($draftOnlyPage->URLSegment . '?stage=Live');
@ -85,7 +84,7 @@ class SiteTreePermissionsTest extends FunctionalTest
{
// Set up fixture - a published page deleted from draft
$this->logInWithPermission("ADMIN");
$page = $this->objFromFixture(Page::class, 'restrictedEditOnlySubadminGroup');
$page = $this->objFromFixture(SiteTree::class, 'restrictedEditOnlySubadminGroup');
$pageID = $page->ID;
$this->assertTrue($page->publishRecursive());
$page->delete();
@ -112,7 +111,7 @@ class SiteTreePermissionsTest extends FunctionalTest
{
// Set up fixture - an unpublished page
$this->logInWithPermission("ADMIN");
$page = $this->objFromFixture(Page::class, 'restrictedEditOnlySubadminGroup');
$page = $this->objFromFixture(SiteTree::class, 'restrictedEditOnlySubadminGroup');
$pageID = $page->ID;
$page->doUnpublish();
@ -135,7 +134,7 @@ class SiteTreePermissionsTest extends FunctionalTest
{
// Find a page that exists and delete it from both stage and published
$this->logInWithPermission("ADMIN");
$page = $this->objFromFixture(Page::class, 'restrictedEditOnlySubadminGroup');
$page = $this->objFromFixture(SiteTree::class, 'restrictedEditOnlySubadminGroup');
$pageID = $page->ID;
$page->doUnpublish();
$page->delete();
@ -153,8 +152,8 @@ class SiteTreePermissionsTest extends FunctionalTest
public function testCanViewStage()
{
// Get page & make sure it exists on Live
/** @var Page $page */
$page = $this->objFromFixture(Page::class, 'standardpage');
/** @var SiteTree $page */
$page = $this->objFromFixture(SiteTree::class, 'standardpage');
$page->publishSingle();
// Then make sure there's a new version on Stage
@ -173,7 +172,7 @@ class SiteTreePermissionsTest extends FunctionalTest
public function testAccessTabOnlyDisplaysWithGrantAccessPermissions()
{
$page = $this->objFromFixture(Page::class, 'standardpage');
$page = $this->objFromFixture(SiteTree::class, 'standardpage');
$subadminuser = $this->objFromFixture(Member::class, 'subadmin');
Security::setCurrentUser($subadminuser);
@ -204,7 +203,7 @@ class SiteTreePermissionsTest extends FunctionalTest
public function testRestrictedViewLoggedInUsers()
{
$page = $this->objFromFixture(Page::class, 'restrictedViewLoggedInUsers');
$page = $this->objFromFixture(SiteTree::class, 'restrictedViewLoggedInUsers');
// unauthenticated users
$this->assertFalse(
@ -223,21 +222,23 @@ class SiteTreePermissionsTest extends FunctionalTest
$websiteuser = $this->objFromFixture(Member::class, 'websiteuser');
$this->assertTrue(
$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);
$response = $this->get($page->RelativeLink());
$this->assertEquals(
$response->getStatusCode(),
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();
}
public function testRestrictedViewOnlyTheseUsers()
{
$page = $this->objFromFixture(Page::class, 'restrictedViewOnlyWebsiteUsers');
$page = $this->objFromFixture(SiteTree::class, 'restrictedViewOnlyWebsiteUsers');
// unauthenticcated users
$this->assertFalse(
@ -256,14 +257,16 @@ class SiteTreePermissionsTest extends FunctionalTest
$subadminuser = $this->objFromFixture(Member::class, 'subadmin');
$this->assertFalse(
$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);
$response = $this->get($page->RelativeLink());
$this->assertEquals(
$response->getStatusCode(),
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();
@ -285,7 +288,7 @@ class SiteTreePermissionsTest extends FunctionalTest
public function testRestrictedEditLoggedInUsers()
{
$page = $this->objFromFixture(Page::class, 'restrictedEditLoggedInUsers');
$page = $this->objFromFixture(SiteTree::class, 'restrictedEditLoggedInUsers');
// unauthenticcated users
$this->assertFalse(
@ -298,20 +301,22 @@ class SiteTreePermissionsTest extends FunctionalTest
Security::setCurrentUser($websiteuser);
$this->assertFalse(
$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
$subadminuser = $this->objFromFixture(Member::class, 'subadmin');
$this->assertTrue(
$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()
{
$page = $this->objFromFixture(Page::class, 'restrictedEditOnlySubadminGroup');
$page = $this->objFromFixture(SiteTree::class, 'restrictedEditOnlySubadminGroup');
// unauthenticated users
$this->assertFalse(
@ -330,14 +335,15 @@ class SiteTreePermissionsTest extends FunctionalTest
$websiteuser = $this->objFromFixture(Member::class, 'websiteuser');
$this->assertFalse(
$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()
{
$parentPage = $this->objFromFixture(Page::class, 'parent_restrictedViewOnlySubadminGroup');
$childPage = $this->objFromFixture(Page::class, 'child_restrictedViewOnlySubadminGroup');
$parentPage = $this->objFromFixture(SiteTree::class, 'parent_restrictedViewOnlySubadminGroup');
$childPage = $this->objFromFixture(SiteTree::class, 'child_restrictedViewOnlySubadminGroup');
// unauthenticated users
$this->assertFalse(
@ -356,22 +362,24 @@ class SiteTreePermissionsTest extends FunctionalTest
$subadminuser = $this->objFromFixture(Member::class, 'subadmin');
$this->assertTrue(
$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);
$response = $this->get($childPage->RelativeLink());
$this->assertEquals(
$response->getStatusCode(),
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();
}
public function testRestrictedEditInheritance()
{
$parentPage = $this->objFromFixture(Page::class, 'parent_restrictedEditOnlySubadminGroup');
$childPage = $this->objFromFixture(Page::class, 'child_restrictedEditOnlySubadminGroup');
$parentPage = $this->objFromFixture(SiteTree::class, 'parent_restrictedEditOnlySubadminGroup');
$childPage = $this->objFromFixture(SiteTree::class, 'child_restrictedEditOnlySubadminGroup');
// unauthenticated users
$this->assertFalse(
@ -383,14 +391,15 @@ class SiteTreePermissionsTest extends FunctionalTest
$subadminuser = $this->objFromFixture(Member::class, 'subadmin');
$this->assertTrue(
$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()
{
$parentPage = $this->objFromFixture(Page::class, 'deleteTestParentPage');
$childPage = $this->objFromFixture(Page::class, 'deleteTestChildPage');
$parentPage = $this->objFromFixture(SiteTree::class, 'deleteTestParentPage');
$childPage = $this->objFromFixture(SiteTree::class, 'deleteTestChildPage');
// unauthenticated users
$this->assertFalse(
@ -405,7 +414,7 @@ class SiteTreePermissionsTest extends FunctionalTest
public function testRestrictedEditLoggedInUsersDeletedFromStage()
{
$page = $this->objFromFixture(Page::class, 'restrictedEditLoggedInUsers');
$page = $this->objFromFixture(SiteTree::class, 'restrictedEditLoggedInUsers');
$pageID = $page->ID;
$this->logInWithPermission("ADMIN");
@ -421,39 +430,60 @@ class SiteTreePermissionsTest extends FunctionalTest
$subadminuser = $this->objFromFixture(Member::class, 'subadmin');
$this->assertTrue(
$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()
{
$page = $this->objFromFixture(Page::class, 'inheritWithNoParent');
$page = $this->objFromFixture(SiteTree::class, 'inheritWithNoParent');
$siteconfig = $this->objFromFixture(SiteConfig::class, 'default');
$editor = $this->objFromFixture(Member::class, 'editor');
$editorGroup = $this->objFromFixture(Group::class, 'editorgroup');
$siteconfig->CanViewType = 'Anyone';
$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->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->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->ViewerGroups()->add($editorGroup);
$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->assertFalse($page->canView(false), 'Anonymous can\'t view a page when set to inherit from the SiteConfig, and SiteConfig has canView set to OnlyTheseUsers');
$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->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()
{
$page = $this->objFromFixture(Page::class, 'inheritWithNoParent');
$page = $this->objFromFixture(SiteTree::class, 'inheritWithNoParent');
$siteconfig = $this->objFromFixture(SiteConfig::class, 'default');
$editor = $this->objFromFixture(Member::class, 'editor');
$user = $this->objFromFixture(Member::class, 'websiteuser');
@ -462,17 +492,37 @@ class SiteTreePermissionsTest extends FunctionalTest
$siteconfig->CanEditType = 'LoggedInUsers';
$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);
$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->EditorGroups()->add($editorGroup);
$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);
$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);
$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'
);
}
}

View File

@ -35,7 +35,7 @@ SilverStripe\Security\Member:
Email: websiteuser@test.com
Password: test
Groups: =>SilverStripe\Security\Group.websiteusers
Page:
SilverStripe\CMS\Model\SiteTree:
standardpage:
URLSegment: standardpage
restrictedViewLoggedInUsers:
@ -66,7 +66,7 @@ Page:
URLSegment: parent-restrictedViewOnlySubadminGroup
child_restrictedViewOnlySubadminGroup:
CanViewType: Inherit
Parent: =>Page.parent_restrictedViewOnlySubadminGroup
Parent: =>SilverStripe\CMS\Model\SiteTree.parent_restrictedViewOnlySubadminGroup
URLSegment: child-restrictedViewOnlySubadminGroup
parent_restrictedEditOnlySubadminGroup:
CanEditType: OnlyTheseUsers
@ -74,7 +74,7 @@ Page:
URLSegment: parent-restrictedEditOnlySubadminGroup
child_restrictedEditOnlySubadminGroup:
CanEditType: Inherit
Parent: =>Page.parent_restrictedEditOnlySubadminGroup
Parent: =>SilverStripe\CMS\Model\SiteTree.parent_restrictedEditOnlySubadminGroup
URLSegment: child-restrictedEditOnlySubadminGroup
deleteTestParentPage:
CanEditType: Inherit

View File

@ -3,7 +3,6 @@
namespace SilverStripe\CMS\Tests\Model;
use LogicException;
use Page;
use Psr\SimpleCache\CacheInterface;
use ReflectionMethod;
use SilverStripe\CMS\Model\RedirectorPage;
@ -34,16 +33,15 @@ use SilverStripe\Security\Security;
use SilverStripe\SiteConfig\SiteConfig;
use SilverStripe\Subsites\Extensions\SiteTreeSubsites;
use SilverStripe\Versioned\Versioned;
use SilverStripe\View\Parsers\Diff;
use SilverStripe\View\Parsers\HtmlDiff;
use SilverStripe\View\Parsers\ShortcodeParser;
use SilverStripe\View\Parsers\URLSegmentFilter;
use SilverStripe\View\Shortcodes\EmbedShortcodeProvider;
use TractorCow\Fluent\Extension\FluentSiteTreeExtension;
use Page;
use PageController;
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
{
@ -88,28 +86,28 @@ class SiteTreeTest extends SapphireTest
public function testCreateDefaultpages()
{
$remove = SiteTree::get();
$remove = SiteTree::get();
if ($remove) {
foreach ($remove as $page) {
$page->delete();
}
}
// Make sure the table is empty
$this->assertEquals(DB::query('SELECT COUNT("ID") FROM "SiteTree"')->value(), 0);
// Make sure the table is empty
$this->assertEquals(DB::query('SELECT COUNT("ID") FROM "SiteTree"')->value(), 0);
// Disable the creation
SiteTree::config()->create_default_pages = false;
singleton(SiteTree::class)->requireDefaultRecords();
// Disable the creation
SiteTree::config()->create_default_pages = false;
singleton(SiteTree::class)->requireDefaultRecords();
// The table should still be empty
$this->assertEquals(DB::query('SELECT COUNT("ID") FROM "SiteTree"')->value(), 0);
// The table should still be empty
$this->assertEquals(DB::query('SELECT COUNT("ID") FROM "SiteTree"')->value(), 0);
// Enable the creation
SiteTree::config()->create_default_pages = true;
singleton(SiteTree::class)->requireDefaultRecords();
// Enable the creation
SiteTree::config()->create_default_pages = true;
singleton(SiteTree::class)->requireDefaultRecords();
// The table should now have three rows (home, about-us, contact-us)
$this->assertEquals(DB::query('SELECT COUNT("ID") FROM "SiteTree"')->value(), 3);
// The table should now have three rows (home, about-us, contact-us)
$this->assertEquals(DB::query('SELECT COUNT("ID") FROM "SiteTree"')->value(), 3);
}
/**
@ -136,7 +134,7 @@ class SiteTreeTest extends SapphireTest
];
foreach ($expectedURLs as $fixture => $urlSegment) {
$obj = $this->objFromFixture('Page', $fixture);
$obj = $this->objFromFixture(SiteTree::class, $fixture);
$this->assertEquals($urlSegment, $obj->URLSegment);
}
}
@ -147,9 +145,9 @@ class SiteTreeTest extends SapphireTest
*/
public function testDisallowedURLGeneration($title, $urlSegment)
{
$page = Page::create(['Title' => $title]);
$page = new SiteTree(['Title' => $title]);
$id = $page->write();
$page = Page::get()->byID($id);
$page = SiteTree::get()->byID($id);
$this->assertEquals($urlSegment, $page->URLSegment);
}
@ -162,9 +160,9 @@ class SiteTreeTest extends SapphireTest
{
// Using the same dataprovider, strip out the -2 from the admin and dev segment
$urlSegment = str_replace('-2', '', $urlSegment ?? '');
$page = Page::create(['Title' => $title, 'ParentID' => 1]);
$page = new SiteTree(['Title' => $title, 'ParentID' => 1]);
$id = $page->write();
$page = Page::get()->byID($id);
$page = SiteTree::get()->byID($id);
$this->assertEquals($urlSegment, $page->URLSegment);
}
@ -179,7 +177,7 @@ class SiteTreeTest extends SapphireTest
$this->markTestSkipped('This legacy test requires RESOURCES_DIR to be "resources"');
}
$page = SiteTree::create(['Title' => 'Resources']);
$page = new SiteTree(['Title' => 'Resources']);
$id = $page->write();
$page = SiteTree::get()->byID($id);
$this->assertSame('resources-2', $page->URLSegment);
@ -197,7 +195,7 @@ class SiteTreeTest extends SapphireTest
$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();
$page = SiteTree::get()->byID($id);
$this->assertSame('resources', $page->URLSegment);
@ -208,10 +206,12 @@ class SiteTreeTest extends SapphireTest
*/
public function testPublishCopiesToLiveTable()
{
$obj = $this->objFromFixture('Page', 'about');
$obj = $this->objFromFixture(SiteTree::class, 'about');
$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);
}
@ -222,12 +222,15 @@ class SiteTreeTest extends SapphireTest
{
$this->logInWithPermission('ADMIN');
$obj = $this->objFromFixture('Page', 'about');
$obj = $this->objFromFixture(SiteTree::class, 'about');
$obj->Title = "asdfasdf";
$obj->write();
$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->write();
@ -274,7 +277,7 @@ class SiteTreeTest extends SapphireTest
Versioned::set_stage(Versioned::LIVE);
$checkSiteTree = DataObject::get_one(SiteTree::class, [
'"SiteTree"."URLSegment"' => 'get-one-test-page'
'"SiteTree"."URLSegment"' => 'get-one-test-page',
]);
$this->assertEquals("V1", $checkSiteTree->Title);
@ -313,10 +316,13 @@ class SiteTreeTest extends SapphireTest
{
/* DataObject::write() should save to a has_one relationship if you set a field called (relname)ID */
$page = new SiteTree();
$parentID = $this->idFromFixture('Page', 'home');
$parentID = $this->idFromFixture(SiteTree::class, 'home');
$page->ParentID = $parentID;
$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 */
$page->ParentID = null;
@ -400,23 +406,23 @@ class SiteTreeTest extends SapphireTest
*/
public function testRestoreToStage()
{
$page = $this->objFromFixture('Page', 'about');
$page = $this->objFromFixture(SiteTree::class, 'about');
$pageID = $page->ID;
$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);
$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, $requeriedPage->ID);
$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;
$page2->doUnpublish();
$page2->delete();
@ -426,12 +432,14 @@ class SiteTreeTest extends SapphireTest
Versioned::set_stage(Versioned::LIVE);
$deletedPage = Versioned::get_latest_version(SiteTree::class, $page2ID);
$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);
$requeriedPage = DataObject::get_by_id("Page", $page2ID);
$requeriedPage = DataObject::get_by_id(SiteTree::class, $page2ID);
$this->assertEquals('Products', $requeriedPage->Title);
$this->assertInstanceOf('Page', $requeriedPage);
$this->assertInstanceOf(SiteTree::class, $requeriedPage);
}
public function testNoCascadingDeleteWithoutID()
@ -455,10 +463,10 @@ class SiteTreeTest extends SapphireTest
public function testGetByLink()
{
$home = $this->objFromFixture('Page', 'home');
$about = $this->objFromFixture('Page', 'about');
$staff = $this->objFromFixture('Page', 'staff');
$product = $this->objFromFixture('Page', 'product1');
$home = $this->objFromFixture(SiteTree::class, 'home');
$about = $this->objFromFixture(SiteTree::class, 'about');
$staff = $this->objFromFixture(SiteTree::class, 'staff');
$product = $this->objFromFixture(SiteTree::class, 'product1');
SiteTree::config()->nested_urls = false;
@ -485,10 +493,10 @@ class SiteTreeTest extends SapphireTest
public function testGetByLinkAbsolute()
{
$home = $this->objFromFixture('Page', 'home');
$about = $this->objFromFixture('Page', 'about');
$staff = $this->objFromFixture('Page', 'staff');
$product = $this->objFromFixture('Page', 'product1');
$home = $this->objFromFixture(SiteTree::class, 'home');
$about = $this->objFromFixture(SiteTree::class, 'about');
$staff = $this->objFromFixture(SiteTree::class, 'staff');
$product = $this->objFromFixture(SiteTree::class, 'product1');
$base = 'https://example.test/';
$this->assertEquals($home->ID, SiteTree::get_by_link(Controller::join_links($base, '/'), false)->ID);
@ -500,29 +508,45 @@ class SiteTreeTest extends SapphireTest
public function testRelativeLink()
{
$about = $this->objFromFixture('Page', 'about');
$staff = $this->objFromFixture('Page', 'staff');
$about = $this->objFromFixture(SiteTree::class, 'about');
$staff = $this->objFromFixture(SiteTree::class, 'staff');
Config::modify()->set(SiteTree::class, 'nested_urls', true);
$this->assertEquals('about-us/', $about->RelativeLink(), 'Matches URLSegment on top level without parameters');
$this->assertEquals('about-us/my-staff/', $staff->RelativeLink(), 'Matches URLSegment plus parent on second level without parameters');
$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');
$this->assertEquals(
'about-us',
$about->RelativeLink(),
'Matches URLSegment on top level without parameters'
);
$this->assertEquals(
'about-us/my-staff',
$staff->RelativeLink(),
'Matches URLSegment plus parent on second level without parameters'
);
$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()
{
$about = $this->objFromFixture('Page', 'about');
$staff = $this->objFromFixture('Page', 'staff');
$about = $this->objFromFixture(SiteTree::class, 'about');
$staff = $this->objFromFixture(SiteTree::class, 'staff');
$this->assertEquals(1, $about->getPageLevel());
$this->assertEquals(2, $staff->getPageLevel());
}
public function testAbsoluteLiveLink()
{
$parent = $this->objFromFixture('Page', 'about');
$child = $this->objFromFixture('Page', 'staff');
$parent = $this->objFromFixture(SiteTree::class, 'about');
$child = $this->objFromFixture(SiteTree::class, 'staff');
Config::modify()->set(SiteTree::class, 'nested_urls', true);
@ -533,23 +557,23 @@ class SiteTreeTest extends SapphireTest
$parent->URLSegment = 'changed-on-draft';
$parent->write();
$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', $child->getAbsoluteLiveLink(false));
$this->assertStringEndsWith('changed-on-live/my-staff?stage=Live', $child->getAbsoluteLiveLink());
}
public function testDuplicateChildrenRetainSort()
{
$parent = new Page();
$parent = new SiteTree();
$parent->Title = 'Parent';
$parent->write();
$child1 = new Page();
$child1 = new SiteTree();
$child1->ParentID = $parent->ID;
$child1->Title = 'Child 1';
$child1->Sort = 2;
$child1->write();
$child2 = new Page();
$child2 = new SiteTree();
$child2->ParentID = $parent->ID;
$child2->Title = 'Child 2';
$child2->Sort = 1;
@ -573,34 +597,34 @@ class SiteTreeTest extends SapphireTest
public function testDeleteFromStageOperatesRecursively()
{
Config::modify()->set(SiteTree::class, 'enforce_strict_hierarchy', false);
$pageAbout = $this->objFromFixture('Page', 'about');
$pageStaff = $this->objFromFixture('Page', 'staff');
$pageStaffDuplicate = $this->objFromFixture('Page', 'staffduplicate');
$pageAbout = $this->objFromFixture(SiteTree::class, 'about');
$pageStaff = $this->objFromFixture(SiteTree::class, 'staff');
$pageStaffDuplicate = $this->objFromFixture(SiteTree::class, 'staffduplicate');
$pageAbout->delete();
$this->assertNull(DataObject::get_by_id('Page', $pageAbout->ID));
$this->assertTrue(DataObject::get_by_id('Page', $pageStaff->ID) instanceof Page);
$this->assertTrue(DataObject::get_by_id('Page', $pageStaffDuplicate->ID) instanceof Page);
$this->assertNull(DataObject::get_by_id(SiteTree::class, $pageAbout->ID));
$this->assertTrue(DataObject::get_by_id(SiteTree::class, $pageStaff->ID) instanceof SiteTree);
$this->assertTrue(DataObject::get_by_id(SiteTree::class, $pageStaffDuplicate->ID) instanceof SiteTree);
Config::modify()->set(SiteTree::class, 'enforce_strict_hierarchy', true);
}
public function testDeleteFromStageOperatesRecursivelyStrict()
{
$pageAbout = $this->objFromFixture('Page', 'about');
$pageStaff = $this->objFromFixture('Page', 'staff');
$pageStaffDuplicate = $this->objFromFixture('Page', 'staffduplicate');
$pageAbout = $this->objFromFixture(SiteTree::class, 'about');
$pageStaff = $this->objFromFixture(SiteTree::class, 'staff');
$pageStaffDuplicate = $this->objFromFixture(SiteTree::class, 'staffduplicate');
$pageAbout->delete();
$this->assertNull(DataObject::get_by_id('Page', $pageAbout->ID));
$this->assertNull(DataObject::get_by_id('Page', $pageStaff->ID));
$this->assertNull(DataObject::get_by_id('Page', $pageStaffDuplicate->ID));
$this->assertNull(DataObject::get_by_id(SiteTree::class, $pageAbout->ID));
$this->assertNull(DataObject::get_by_id(SiteTree::class, $pageStaff->ID));
$this->assertNull(DataObject::get_by_id(SiteTree::class, $pageStaffDuplicate->ID));
}
public function testDuplicate()
{
$pageAbout = $this->objFromFixture('Page', 'about');
$pageAbout = $this->objFromFixture(SiteTree::class, 'about');
$dupe = $pageAbout->duplicate();
$this->assertEquals($pageAbout->Title, $dupe->Title);
$this->assertNotEquals($pageAbout->URLSegment, $dupe->URLSegment);
@ -612,22 +636,22 @@ class SiteTreeTest extends SapphireTest
Config::modify()->set(SiteTree::class, 'enforce_strict_hierarchy', false);
$this->logInWithPermission('ADMIN');
$pageAbout = $this->objFromFixture('Page', 'about');
$pageAbout = $this->objFromFixture(SiteTree::class, 'about');
$pageAbout->publishRecursive();
$pageStaff = $this->objFromFixture('Page', 'staff');
$pageStaff = $this->objFromFixture(SiteTree::class, 'staff');
$pageStaff->publishRecursive();
$pageStaffDuplicate = $this->objFromFixture('Page', 'staffduplicate');
$pageStaffDuplicate = $this->objFromFixture(SiteTree::class, 'staffduplicate');
$pageStaffDuplicate->publishRecursive();
$parentPage = $this->objFromFixture('Page', 'about');
$parentPage = $this->objFromFixture(SiteTree::class, 'about');
$parentPage->doUnpublish();
Versioned::set_stage(Versioned::LIVE);
$this->assertNull(DataObject::get_by_id('Page', $pageAbout->ID));
$this->assertTrue(DataObject::get_by_id('Page', $pageStaff->ID) instanceof Page);
$this->assertTrue(DataObject::get_by_id('Page', $pageStaffDuplicate->ID) instanceof Page);
$this->assertNull(DataObject::get_by_id(SiteTree::class, $pageAbout->ID));
$this->assertTrue(DataObject::get_by_id(SiteTree::class, $pageStaff->ID) instanceof SiteTree);
$this->assertTrue(DataObject::get_by_id(SiteTree::class, $pageStaffDuplicate->ID) instanceof SiteTree);
Versioned::set_stage(Versioned::DRAFT);
Config::modify()->set(SiteTree::class, 'enforce_strict_hierarchy', true);
}
@ -637,20 +661,20 @@ class SiteTreeTest extends SapphireTest
Config::modify()->set(SiteTree::class, 'enforce_strict_hierarchy', false);
$this->logInWithPermission('ADMIN');
$pageAbout = $this->objFromFixture('Page', 'about');
$pageAbout = $this->objFromFixture(SiteTree::class, 'about');
$pageAbout->publishRecursive();
$pageStaff = $this->objFromFixture('Page', 'staff');
$pageStaff = $this->objFromFixture(SiteTree::class, 'staff');
$pageStaff->publishRecursive();
$pageStaffDuplicate = $this->objFromFixture('Page', 'staffduplicate');
$pageStaffDuplicate = $this->objFromFixture(SiteTree::class, 'staffduplicate');
$pageStaffDuplicate->publishRecursive();
$parentPage = $this->objFromFixture('Page', 'about');
$parentPage = $this->objFromFixture(SiteTree::class, 'about');
$parentPage->doUnpublish();
Versioned::set_stage(Versioned::LIVE);
$this->assertNull(DataObject::get_by_id('Page', $pageAbout->ID));
$this->assertTrue(DataObject::get_by_id('Page', $pageStaff->ID) instanceof Page);
$this->assertTrue(DataObject::get_by_id('Page', $pageStaffDuplicate->ID) instanceof Page);
$this->assertNull(DataObject::get_by_id(SiteTree::class, $pageAbout->ID));
$this->assertTrue(DataObject::get_by_id(SiteTree::class, $pageStaff->ID) instanceof SiteTree);
$this->assertTrue(DataObject::get_by_id(SiteTree::class, $pageStaffDuplicate->ID) instanceof SiteTree);
Versioned::set_stage(Versioned::DRAFT);
Config::modify()->set(SiteTree::class, 'enforce_strict_hierarchy', true);
}
@ -659,20 +683,20 @@ class SiteTreeTest extends SapphireTest
{
$this->logInWithPermission('ADMIN');
$pageAbout = $this->objFromFixture('Page', 'about');
$pageAbout = $this->objFromFixture(SiteTree::class, 'about');
$pageAbout->publishRecursive();
$pageStaff = $this->objFromFixture('Page', 'staff');
$pageStaff = $this->objFromFixture(SiteTree::class, 'staff');
$pageStaff->publishRecursive();
$pageStaffDuplicate = $this->objFromFixture('Page', 'staffduplicate');
$pageStaffDuplicate = $this->objFromFixture(SiteTree::class, 'staffduplicate');
$pageStaffDuplicate->publishRecursive();
$parentPage = $this->objFromFixture('Page', 'about');
$parentPage = $this->objFromFixture(SiteTree::class, 'about');
$parentPage->doUnpublish();
Versioned::set_stage(Versioned::LIVE);
$this->assertNull(DataObject::get_by_id('Page', $pageAbout->ID));
$this->assertNull(DataObject::get_by_id('Page', $pageStaff->ID));
$this->assertNull(DataObject::get_by_id('Page', $pageStaffDuplicate->ID));
$this->assertNull(DataObject::get_by_id(SiteTree::class, $pageAbout->ID));
$this->assertNull(DataObject::get_by_id(SiteTree::class, $pageStaff->ID));
$this->assertNull(DataObject::get_by_id(SiteTree::class, $pageStaffDuplicate->ID));
Versioned::set_stage(Versioned::DRAFT);
}
@ -683,7 +707,7 @@ class SiteTreeTest extends SapphireTest
public function testReadArchiveDate()
{
DBDatetime::set_mock_now('2009-07-02 14:05:07');
$oldPage = SiteTree::create();
$oldPage = new SiteTree();
$oldPage->Title = 'A really old page';
$oldPage->write();
DBDatetime::clear_mock_now();
@ -691,7 +715,7 @@ class SiteTreeTest extends SapphireTest
$date = '2009-07-02 14:05:07';
Versioned::reading_archived_date($date);
$result = SiteTree::get()->where([
'"SiteTree"."ParentID"' => 0
'"SiteTree"."ParentID"' => 0,
]);
$this->assertCount(1, $result, '"A really old page" should be returned');
}
@ -700,11 +724,11 @@ class SiteTreeTest extends SapphireTest
{
$editor = $this->objFromFixture(Member::class, "editor");
$home = $this->objFromFixture("Page", "home");
$staff = $this->objFromFixture("Page", "staff");
$products = $this->objFromFixture("Page", "products");
$product1 = $this->objFromFixture("Page", "product1");
$product4 = $this->objFromFixture("Page", "product4");
$home = $this->objFromFixture(SiteTree::class, "home");
$staff = $this->objFromFixture(SiteTree::class, "staff");
$products = $this->objFromFixture(SiteTree::class, "products");
$product1 = $this->objFromFixture(SiteTree::class, "product1");
$product4 = $this->objFromFixture(SiteTree::class, "product4");
// Test logged out users cannot edit
$this->logOut();
@ -725,7 +749,7 @@ class SiteTreeTest extends SapphireTest
public function testCanEditWithAccessToAllSections()
{
$page = new Page();
$page = new SiteTree();
$page->write();
$allSectionMember = $this->objFromFixture(Member::class, 'allsections');
$securityAdminMember = $this->objFromFixture(Member::class, 'securityadmin');
@ -750,7 +774,7 @@ class SiteTreeTest extends SapphireTest
$this->assertTrue(singleton(SiteTree::class)->canCreate());
// 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]));
// Test creation underneath a parent which doesn't allow a certain child
@ -766,13 +790,18 @@ class SiteTreeTest extends SapphireTest
$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
$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()
{
// Create an inherit-permission page
$page = new Page();
$page = new SiteTree();
$page->write();
$page->CanEditType = "Inherit";
$page->publishRecursive();
@ -820,10 +849,10 @@ class SiteTreeTest extends SapphireTest
public function testCompareVersions()
{
// Necessary to avoid
$oldCleanerClass = Diff::$html_cleaner_class;
Diff::$html_cleaner_class = SiteTreeTest_NullHtmlCleaner::class;
$oldCleanerClass = HtmlDiff::$html_cleaner_class;
HtmlDiff::$html_cleaner_class = SiteTreeTest_NullHtmlCleaner::class;
$page = new Page();
$page = new SiteTree();
$page->write();
$this->assertEquals(1, $page->Version);
@ -841,7 +870,7 @@ class SiteTreeTest extends SapphireTest
$processedContent = preg_replace('/>\s*/', '>', $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()
@ -851,16 +880,16 @@ class SiteTreeTest extends SapphireTest
$this->logInAs($member);
// Write the page
$about = $this->objFromFixture('Page', 'about');
$about = $this->objFromFixture(SiteTree::class, 'about');
$about->Title = "Another title";
$about->write();
// Check the version created
$savedVersion = DB::prepared_query(
"SELECT \"AuthorID\", \"PublisherID\" FROM \"SiteTree_Versions\"
WHERE \"RecordID\" = ? ORDER BY \"Version\" DESC",
WHERE \"RecordID\" = ? ORDER BY \"Version\" DESC",
[$about->ID]
)->first();
)->record();
$this->assertEquals($member->ID, $savedVersion['AuthorID']);
$this->assertEquals(0, $savedVersion['PublisherID']);
@ -868,9 +897,9 @@ class SiteTreeTest extends SapphireTest
$about->publishRecursive();
$publishedVersion = DB::prepared_query(
"SELECT \"AuthorID\", \"PublisherID\" FROM \"SiteTree_Versions\"
WHERE \"RecordID\" = ? ORDER BY \"Version\" DESC",
WHERE \"RecordID\" = ? ORDER BY \"Version\" DESC",
[$about->ID]
)->first();
)->record();
// Check the version created
$this->assertEquals($member->ID, $publishedVersion['AuthorID']);
@ -879,7 +908,7 @@ class SiteTreeTest extends SapphireTest
public function testLinkShortcodeHandler()
{
$aboutPage = $this->objFromFixture('Page', 'about');
$aboutPage = $this->objFromFixture(SiteTree::class, 'about');
$redirectPage = $this->objFromFixture(RedirectorPage::class, 'external');
$parser = new ShortcodeParser();
@ -891,12 +920,24 @@ class SiteTreeTest extends SapphireTest
$aboutShortcodeExpected = $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($aboutEnclosedExpected, $parser->parse($aboutEnclosed), 'Test enclosed content is linked.');
$this->assertEquals(
$aboutShortcodeExpected,
$parser->parse($aboutShortcode),
'Test that simple linking works.'
);
$this->assertEquals(
$aboutEnclosedExpected,
$parser->parse($aboutEnclosed),
'Test enclosed content is linked.'
);
$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));
$aboutShortcode = '[sitetree_link,id="-1"]';
@ -910,7 +951,10 @@ class SiteTreeTest extends SapphireTest
$redirectExpected = 'http://www.google.com?a&amp;b';
$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,id="text"]'));
@ -919,8 +963,8 @@ class SiteTreeTest extends SapphireTest
public function testIsCurrent()
{
$aboutPage = $this->objFromFixture('Page', 'about');
$productPage = $this->objFromFixture('Page', 'products');
$aboutPage = $this->objFromFixture(SiteTree::class, 'about');
$productPage = $this->objFromFixture(SiteTree::class, 'products');
Director::set_current_page($aboutPage);
$this->assertTrue($aboutPage->isCurrent(), 'Assert that basic isCurrent checks works.');
@ -928,7 +972,7 @@ class SiteTreeTest extends SapphireTest
$this->assertTrue(
DataObject::get_one(SiteTree::class, [
'"SiteTree"."Title"' => 'About Us'
'"SiteTree"."Title"' => 'About Us',
])->isCurrent(),
'Assert that isCurrent works on another instance with the same ID.'
);
@ -939,9 +983,9 @@ class SiteTreeTest extends SapphireTest
public function testIsSection()
{
$about = $this->objFromFixture('Page', 'about');
$staff = $this->objFromFixture('Page', 'staff');
$ceo = $this->objFromFixture('Page', 'ceo');
$about = $this->objFromFixture(SiteTree::class, 'about');
$staff = $this->objFromFixture(SiteTree::class, 'staff');
$ceo = $this->objFromFixture(SiteTree::class, 'ceo');
Director::set_current_page($about);
$this->assertTrue($about->isSection());
@ -961,7 +1005,7 @@ class SiteTreeTest extends SapphireTest
public function testURLSegmentReserved()
{
$siteTree = SiteTree::create(['URLSegment' => 'admin']);
$siteTree = new SiteTree(['URLSegment' => 'admin']);
$segment = $siteTree->validURLSegment();
$this->assertFalse($segment);
@ -1049,7 +1093,7 @@ class SiteTreeTest extends SapphireTest
$sitetree->URLSegment = 'home-noconflict';
$this->assertTrue($sitetree->validURLSegment());
$sitetree->ParentID = $this->idFromFixture('Page', 'about');
$sitetree->ParentID = $this->idFromFixture(SiteTree::class, 'about');
$sitetree->URLSegment = 'home';
$this->assertFalse($sitetree->validURLSegment(), 'Conflicts are still recognised with a ParentID value');
@ -1059,7 +1103,7 @@ class SiteTreeTest extends SapphireTest
$sitetree->URLSegment = 'home';
$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');
$sitetree->URLSegment = 'my-staff';
@ -1129,13 +1173,18 @@ class SiteTreeTest extends SapphireTest
$sitetree->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
$sitetree = DataObject::get_by_id(SiteTree::class, $sitetree->ID, false);
$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'));
}
public function testVersionsAreCreated()
{
$p = new Page();
$p = new SiteTree();
$p->Content = "one";
$p->write();
$this->assertEquals(1, $p->Version);
@ -1223,23 +1272,23 @@ class SiteTreeTest extends SapphireTest
// Expected
[ SiteTreeTest_ClassB::class ],
// Assertion message
'Direct setting of allowed children'
'Direct setting of allowed children',
],
[
SiteTreeTest_ClassB::class,
[ SiteTreeTest_ClassC::class, SiteTreeTest_ClassCext::class ],
'Includes subclasses'
'Includes subclasses',
],
[
SiteTreeTest_ClassC::class,
[],
'Null setting'
'Null setting',
],
[
SiteTreeTest_ClassD::class,
[SiteTreeTest_ClassC::class],
'Excludes subclasses if class is prefixed by an asterisk'
]
'Excludes subclasses if class is prefixed by an asterisk',
],
];
}
@ -1280,7 +1329,10 @@ class SiteTreeTest extends SapphireTest
$classCext->ParentID = $classD->ID;
$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()
@ -1299,8 +1351,8 @@ class SiteTreeTest extends SapphireTest
$this->assertArrayHasKey(SiteTreeTest_ClassA::class, $method->invoke($sitetree));
$this->logInWithPermission('ADMIN');
$rootPage = $this->objFromFixture(Page::class, 'home');
$nonRootPage = $this->objFromFixture(Page::class, 'staff');
$rootPage = $this->objFromFixture(SiteTree::class, 'home');
$nonRootPage = $this->objFromFixture(SiteTree::class, 'staff');
$this->assertArrayNotHasKey(SiteTreeTest_NotRoot::class, $method->invoke($rootPage));
$this->assertArrayHasKey(SiteTreeTest_NotRoot::class, $method->invoke($nonRootPage));
@ -1396,11 +1448,11 @@ class SiteTreeTest extends SapphireTest
public function testGetBreadcrumbItems()
{
$page = $this->objFromFixture("Page", "breadcrumbs");
$page = $this->objFromFixture(SiteTree::class, "breadcrumbs");
$this->assertEquals(1, $page->getBreadcrumbItems()->count(), "Only display current page.");
// Test breadcrumb order
$page = $this->objFromFixture("Page", "breadcrumbs5");
$page = $this->objFromFixture(SiteTree::class, "breadcrumbs5");
$breadcrumbs = $page->getBreadcrumbItems();
$this->assertEquals($breadcrumbs->count(), 5, "Display all breadcrumbs");
$this->assertEquals($breadcrumbs->first()->Title, "Breadcrumbs", "Breadcrumbs should be the first item.");
@ -1420,16 +1472,16 @@ class SiteTreeTest extends SapphireTest
public function testMetaTags()
{
$this->logInWithPermission('ADMIN');
$page = $this->objFromFixture('Page', 'metapage');
$page = $this->objFromFixture(SiteTree::class, 'metapage');
// Test with title
$meta = $page->MetaTags();
$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 &lt;br /&gt; and &lt;br&gt; tags"', $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-cms-edit-link" content="'.$page->CMSEditLink().'"', $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('<title>HTML &amp; XML</title>', $meta);
// Test without title
@ -1444,7 +1496,7 @@ class SiteTreeTest extends SapphireTest
{
$this->logInWithPermission('ADMIN');
/** @var SiteTree $page */
$page = $this->objFromFixture('Page', 'metapage');
$page = $this->objFromFixture(SiteTree::class, 'metapage');
$charset = Config::inst()->get(ContentNegotiator::class, 'encoding');
@ -1526,8 +1578,8 @@ class SiteTreeTest extends SapphireTest
// both pages are viewable in stage
Versioned::set_stage(Versioned::DRAFT);
$about = $this->objFromFixture('Page', 'about');
$staff = $this->objFromFixture('Page', 'staff');
$about = $this->objFromFixture(SiteTree::class, 'about');
$staff = $this->objFromFixture(SiteTree::class, 'staff');
$this->assertFalse($about->isOrphaned());
$this->assertFalse($staff->isOrphaned());
$this->assertTrue($about->canView($member));
@ -1538,23 +1590,23 @@ class SiteTreeTest extends SapphireTest
$this->assertFalse($staff->isOrphaned());
$this->assertTrue($staff->canView($member));
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->assertFalse($staff->canView($member));
// Publishing the parent page should restore visibility
Versioned::set_stage(Versioned::DRAFT);
$about = $this->objFromFixture('Page', 'about');
$about = $this->objFromFixture(SiteTree::class, 'about');
$about->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
Versioned::set_stage(Versioned::LIVE);
$staff = $this->objFromFixture('Page', 'staff');
$staff = $this->objFromFixture(SiteTree::class, 'staff');
$this->assertFalse($staff->isOrphaned());
$this->assertTrue($staff->canView($member));
// Removing staging page should not prevent live page being visible
$about->deleteFromStage('Stage');
$staff->deleteFromStage('Stage');
$staff = $this->objFromFixture('Page', 'staff');
$staff = $this->objFromFixture(SiteTree::class, 'staff');
$this->assertFalse($staff->isOrphaned());
$this->assertTrue($staff->canView($member));
@ -1569,8 +1621,8 @@ class SiteTreeTest extends SapphireTest
{
$this->logInWithPermission('ADMIN');
/** @var Page $page */
$page = $this->objFromFixture('Page', 'home');
/** @var SiteTree $page */
$page = $this->objFromFixture(SiteTree::class, 'home');
$this->assertTrue($page->canAddChildren());
$this->assertTrue($page->isOnDraft());
$this->assertFalse($page->isPublished());
@ -1647,23 +1699,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.
*/
public function testGetControllerName()
{
$class = new Page;
$this->assertSame('PageController', $class->getControllerName());
$page = new Page();
$this->assertSame(PageController::class, $page->getControllerName());
}
/**
* Test that the controller name for a SiteTree instance can be gathered when set directly via config var
*/
public function testGetControllerNameFromConfig()
{
Config::inst()->set(Page::class, 'controller_name', 'This\\Is\\A\\New\\Controller');
$class = new Page;
$this->assertSame('This\\Is\\A\\New\\Controller', $class->getControllerName());
Config::inst()->set(SiteTree::class, 'controller_name', 'This\\Is\\A\\New\\Controller');
$page = new SiteTree();
$this->assertSame('This\\Is\\A\\New\\Controller', $page->getControllerName());
}
/**
@ -1679,21 +1732,9 @@ class SiteTreeTest extends SapphireTest
$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()
{
$siteTree = SiteTree::create();
$siteTree = new SiteTree();
$user = $this->objFromFixture(Member::class, 'allsections');
Security::setCurrentUser($user);
$pageClass = array_values(SiteTree::page_type_classes())[0];
@ -1780,7 +1821,7 @@ class SiteTreeTest extends SapphireTest
}
// Create new page on DRAFT
$page = SiteTree::create();
$page = new SiteTree();
$page->Content = $content;
$page->write();
@ -1796,7 +1837,7 @@ class SiteTreeTest extends SapphireTest
public function testGetCMSActions()
{
// Create new page on DRAFT
$page = SiteTree::create();
$page = new SiteTree();
$page->Content = md5(rand(0, PHP_INT_MAX));
$page->write();
@ -1926,7 +1967,7 @@ class SiteTreeTest extends SapphireTest
public function testGetCMSActionsWithoutForms()
{
// Create new page on DRAFT
$page = SiteTree::create();
$page = new SiteTree();
$page->Content = md5(rand(0, PHP_INT_MAX));
$page->write();
@ -2032,20 +2073,10 @@ class SiteTreeTest extends SapphireTest
*/
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->ExtraMeta = $extraMeta;
$siteTree->write();
if (!$isDodgyAndUsingHTML5) {
$this->assertSame($expected, $siteTree->ExtraMeta, $message);
}
$this->assertSame($expected, $siteTree->ExtraMeta, $message);
}
public function provideSanitiseExtraMeta(): array
@ -2075,50 +2106,12 @@ class SiteTreeTest extends SapphireTest
'<link rel="canonical" accesskey="X" onclick="alert(1)" name="x" />',
'<link rel="canonical" name="x">',
'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="">'
]
'<link rel="canonical" href="valid" ;;// somethingdodgy <onmouseover=alert(1)>',
'<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
View File

@ -44,7 +44,7 @@ SilverStripe\Security\Member:
securityadmin:
Groups: =>SilverStripe\Security\Group.securityadmins
Page:
SilverStripe\CMS\Model\SiteTree:
home:
Title: Home
CanEditType: OnlyTheseUsers
@ -56,14 +56,14 @@ Page:
staff:
Title: Staff
URLSegment: my-staff
Parent: =>Page.about
Parent: =>SilverStripe\CMS\Model\SiteTree.about
ceo:
Title: CEO
Parent: =>Page.staff
Parent: =>SilverStripe\CMS\Model\SiteTree.staff
staffduplicate:
Title: Staff
URLSegment: my-staff
Parent: =>Page.about
Parent: =>SilverStripe\CMS\Model\SiteTree.about
ShowInMenus: 0
products:
Title: Products
@ -71,19 +71,19 @@ Page:
EditorGroups: =>SilverStripe\Security\Group.editors
product1:
Title: 1.1 Test Product
Parent: =>Page.products
Parent: =>SilverStripe\CMS\Model\SiteTree.products
CanEditType: Inherit
product2:
Title: Another Product
Parent: =>Page.products
Parent: =>SilverStripe\CMS\Model\SiteTree.products
CanEditType: Inherit
product3:
Title: Another Product
Parent: =>Page.products
Parent: =>SilverStripe\CMS\Model\SiteTree.products
CanEditType: Inherit
product4:
Title: Another Product
Parent: =>Page.products
Parent: =>SilverStripe\CMS\Model\SiteTree.products
CanEditType: OnlyTheseUsers
EditorGroups: =>SilverStripe\Security\Group.admins
contact:
@ -102,16 +102,16 @@ Page:
Title: 'Breadcrumbs'
breadcrumbs2:
Title: 'Breadcrumbs 2'
Parent: =>Page.breadcrumbs
Parent: =>SilverStripe\CMS\Model\SiteTree.breadcrumbs
breadcrumbs3:
Title: 'Breadcrumbs 3'
Parent: =>Page.breadcrumbs2
Parent: =>SilverStripe\CMS\Model\SiteTree.breadcrumbs2
breadcrumbs4:
Title: 'Breadcrumbs 4'
Parent: =>Page.breadcrumbs3
Parent: =>SilverStripe\CMS\Model\SiteTree.breadcrumbs3
breadcrumbs5:
Title: 'Breadcrumbs 5'
Parent: =>Page.breadcrumbs4
Parent: =>SilverStripe\CMS\Model\SiteTree.breadcrumbs4
SilverStripe\CMS\Tests\Model\SiteTreeTest_Conflicted:
parent:
@ -127,7 +127,7 @@ SilverStripe\CMS\Model\RedirectorPage:
SilverStripe\CMS\Tests\Model\SiteTreeTest_DataObject:
relations:
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:
object1:

View File

@ -3,9 +3,9 @@
namespace SilverStripe\CMS\Tests\Model;
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';

View File

@ -3,18 +3,18 @@
namespace SilverStripe\CMS\Tests\Model;
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 $need_permission = [
'ADMIN',
'CMS_ACCESS_CMSMain'
'CMS_ACCESS_CMSMain',
];
private static $allowed_children = [
SiteTreeTest_ClassB::class
SiteTreeTest_ClassB::class,
];
}

View File

@ -3,14 +3,14 @@
namespace SilverStripe\CMS\Tests\Model;
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';
// Also allowed subclasses
private static $allowed_children = [
SiteTreeTest_ClassC::class
SiteTreeTest_ClassC::class,
];
}

View File

@ -3,9 +3,9 @@
namespace SilverStripe\CMS\Tests\Model;
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';

View File

@ -10,6 +10,6 @@ class SiteTreeTest_ClassCext extends SiteTreeTest_ClassC implements TestOnly
// Override SiteTreeTest_ClassC definitions
private static $allowed_children = [
SiteTreeTest_ClassB::class
SiteTreeTest_ClassB::class,
];
}

View File

@ -3,9 +3,9 @@
namespace SilverStripe\CMS\Tests\Model;
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';

View File

@ -4,9 +4,9 @@ namespace SilverStripe\CMS\Tests\Model;
use SilverStripe\Dev\TestOnly;
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';
}

View File

@ -3,9 +3,9 @@
namespace SilverStripe\CMS\Tests\Model;
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';
}

View File

@ -3,12 +3,12 @@
namespace SilverStripe\CMS\Tests\Model;
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 = [
'conflicted-action'
'conflicted-action',
];
public function hasActionTemplate($template)

View File

@ -3,12 +3,12 @@
namespace SilverStripe\CMS\Tests\Model;
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
*/
class SiteTreeTest_LegacyControllerName extends Page implements TestOnly
class SiteTreeTest_LegacyControllerName extends SiteTree implements TestOnly
{
private static $table_name = 'SiteTreeTest_LegacyControllerName';
}

View File

@ -3,9 +3,8 @@
namespace SilverStripe\CMS\Tests\Model;
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
{
}

View File

@ -3,9 +3,9 @@
namespace SilverStripe\CMS\Tests\Model;
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';

View File

@ -3,9 +3,9 @@
namespace SilverStripe\CMS\Tests\Model;
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';
}

View File

@ -3,8 +3,8 @@
namespace SilverStripe\CMS\Tests\Model;
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
{
}

View File

@ -2,7 +2,6 @@
namespace SilverStripe\CMS\Tests\Model;
use Page;
use SilverStripe\CMS\Controllers\ModelAsController;
use SilverStripe\CMS\Model\RedirectorPage;
use SilverStripe\CMS\Model\SiteTree;
@ -43,8 +42,8 @@ class VirtualPageTest extends FunctionalTest
protected static $required_extensions = [
SiteTree::class => [
VirtualPageTest_PageExtension::class
]
VirtualPageTest_PageExtension::class,
],
];
protected function setUp(): void
@ -63,8 +62,8 @@ class VirtualPageTest extends FunctionalTest
);
// Ensure all pages are published
/** @var Page $page */
foreach (Page::get() as $page) {
/** @var SiteTree $page */
foreach (SiteTree::get() as $page) {
$page->publishSingle();
}
}
@ -75,8 +74,8 @@ class VirtualPageTest extends FunctionalTest
*/
public function testEditingSourcePageUpdatesVirtualPages()
{
/** @var Page $master */
$master = $this->objFromFixture('Page', 'master');
/** @var SiteTree $master */
$master = $this->objFromFixture(SiteTree::class, 'master');
$master->Title = "New title";
$master->MenuTitle = "New menutitle";
$master->Content = "<p>New content</p>";
@ -97,7 +96,7 @@ class VirtualPageTest extends FunctionalTest
public function testMetaTags()
{
$this->logInWithPermission('ADMIN');
$master = $this->objFromFixture('Page', 'master');
$master = $this->objFromFixture(SiteTree::class, 'master');
$vp1 = $this->objFromFixture(VirtualPage::class, 'vp1');
// Test with title
@ -118,8 +117,8 @@ class VirtualPageTest extends FunctionalTest
{
$this->logInWithPermission('ADMIN');
/** @var Page $master */
$master = $this->objFromFixture('Page', 'master');
/** @var SiteTree $master */
$master = $this->objFromFixture(SiteTree::class, 'master');
$master->publishRecursive();
$master->Title = "New title";
@ -160,13 +159,13 @@ class VirtualPageTest extends FunctionalTest
$vp = new VirtualPage();
$vp->write();
$vp->CopyContentFromID = $this->idFromFixture('Page', 'master');
$vp->CopyContentFromID = $this->idFromFixture(SiteTree::class, 'master');
$vp->write();
$this->assertEquals("My Page", $vp->Title);
$this->assertEquals("My Page Nav", $vp->MenuTitle);
$vp->CopyContentFromID = $this->idFromFixture('Page', 'master2');
$vp->CopyContentFromID = $this->idFromFixture(SiteTree::class, 'master2');
$vp->write();
$this->assertEquals("My Other Page", $vp->Title);
@ -180,7 +179,7 @@ class VirtualPageTest extends FunctionalTest
*/
public function testPublishingAVirtualPageCopiedPublishedContentNotDraftContent()
{
$p = new Page();
$p = new SiteTree();
$p->Content = "published content";
$p->write();
$p->publishRecursive();
@ -220,7 +219,7 @@ class VirtualPageTest extends FunctionalTest
public function testCantPublishVirtualPagesBeforeTheirSource()
{
// An unpublished source page
$p = new Page();
$p = new SiteTree();
$p->Content = "test content";
$p->write();
@ -241,7 +240,7 @@ class VirtualPageTest extends FunctionalTest
public function testCanEdit()
{
$parentPage = $this->objFromFixture('Page', 'master3');
$parentPage = $this->objFromFixture(SiteTree::class, 'master3');
$virtualPage = $this->objFromFixture(VirtualPage::class, 'vp3');
$bob = $this->objFromFixture(Member::class, 'bob');
$andrew = $this->objFromFixture(Member::class, 'andrew');
@ -259,8 +258,8 @@ class VirtualPageTest extends FunctionalTest
public function testCanView()
{
/** @var Page $parentPage */
$parentPage = $this->objFromFixture('Page', 'master3');
/** @var SiteTree $parentPage */
$parentPage = $this->objFromFixture(SiteTree::class, 'master3');
$parentPage->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
/** @var VirtualPage $virtualPage */
@ -283,7 +282,7 @@ class VirtualPageTest extends FunctionalTest
public function testVirtualPagesArentInappropriatelyPublished()
{
// Fixture
$p = new Page();
$p = new SiteTree();
$p->Content = "test content";
$p->write();
$vp = new VirtualPage();
@ -332,7 +331,7 @@ class VirtualPageTest extends FunctionalTest
public function testUnpublishingSourcePageOfAVirtualPageAlsoUnpublishesVirtualPage()
{
// Create page and virutal page
$p = new Page();
$p = new SiteTree();
$p->Title = "source";
$p->write();
$this->assertTrue($p->publishRecursive());
@ -364,7 +363,7 @@ class VirtualPageTest extends FunctionalTest
public function testDeletingFromLiveSourcePageOfAVirtualPageAlsoUnpublishesVirtualPage()
{
// Create page and virutal page
$p = new Page();
$p = new SiteTree();
$p->Title = "source";
$p->write();
$this->assertTrue($p->publishRecursive());
@ -384,7 +383,7 @@ class VirtualPageTest extends FunctionalTest
->byID($pID);
$this->assertNull($vpDraft);
// 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)
->byID($pID);
$this->assertTrue($pLive->doUnpublish());

View File

@ -31,7 +31,7 @@ SilverStripe\Security\Member:
alice:
Email: alice@alice.com
Groups: =>SilverStripe\Security\Group.alicegroup
Page:
SilverStripe\CMS\Model\SiteTree:
master:
Title: My Page
MenuTitle: My Page Nav
@ -52,15 +52,15 @@ SilverStripe\CMS\Tests\Model\VirtualPageTest_ClassA:
SilverStripe\CMS\Model\VirtualPage:
vp1:
Title: vp1
CopyContentFrom: =>Page.master
Parent: =>Page.holder
CopyContentFrom: =>SilverStripe\CMS\Model\SiteTree.master
Parent: =>SilverStripe\CMS\Model\SiteTree.holder
vp2:
Title: vp2
CopyContentFrom: =>Page.master
Parent: =>Page.holder
CopyContentFrom: =>SilverStripe\CMS\Model\SiteTree.master
Parent: =>SilverStripe\CMS\Model\SiteTree.holder
vp3:
CopyContentFrom: =>Page.master3
Parent: =>Page.holder
CopyContentFrom: =>SilverStripe\CMS\Model\SiteTree.master3
Parent: =>SilverStripe\CMS\Model\SiteTree.holder
CanEditType: OnlyTheseUsers
CanViewType: OnlyTheseUsers
EditorGroups: =>SilverStripe\Security\Group.andrewgroup

View File

@ -3,9 +3,9 @@
namespace SilverStripe\CMS\Tests\Model;
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';

View File

@ -3,12 +3,12 @@
namespace SilverStripe\CMS\Tests\Model;
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 = [
'testaction'
'testaction',
];
public function testMethod()

View File

@ -3,9 +3,9 @@
namespace SilverStripe\CMS\Tests\Model;
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';

View File

@ -3,9 +3,9 @@
namespace SilverStripe\CMS\Tests\Model;
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';

View File

@ -3,9 +3,9 @@
namespace SilverStripe\CMS\Tests\Model;
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';

View File

@ -9,7 +9,8 @@ class VirtualPageTest_PageExtension extends DataExtension implements TestOnly
{
private static $db = [
// 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',
'MySharedNonVirtualField' => 'Text',
];

View File

@ -4,9 +4,9 @@ namespace SilverStripe\CMS\Tests\Model;
use SilverStripe\CMS\Model\VirtualPage;
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';

View File

@ -2,24 +2,22 @@
namespace SilverStripe\CMS\Tests\Reports;
use SilverStripe\Assets\File;
use SilverStripe\CMS\Model\RedirectorPage;
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\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\Assets\File;
use SilverStripe\Dev\SapphireTest;
use Page;
class CmsReportsTest extends SapphireTest
{
protected static $fixture_file = 'CmsReportsTest.yml';
private static $daysAgo = 14;
@ -29,14 +27,19 @@ class CmsReportsTest extends SapphireTest
parent::setUp();
// set the dates by hand: impossible to set via yml
$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'));
$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'));
$after = $this->objFromFixture(SiteTree::class, 'after');
$before = $this->objFromFixture(SiteTree::class, 'before');
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."'");
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."'");
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 . "'"
);
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.
$parameters = ['CheckSite' => 'Draft'];
$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.");
$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."
);
// ASSERT that the "published" report is returning the correct results.
$parameters = ['CheckSite' => 'Published', 'OnLive' => 1];
$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()
@ -81,18 +98,14 @@ class CmsReportsTest extends SapphireTest
/**
* Test the broken links side report.
*/
public function testBrokenLinks()
{
// Create a "draft" page with a broken link.
$page = Page::create();
$page = new SiteTree();
$page->Content = "<a href='[sitetree_link,id=987654321]'>This</a> is a broken link.";
$page->writeToStage('Stage');
// Retrieve the broken links side report.
$reports = Report::get_reports();
$brokenLinksReport = null;
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 "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);
// Make sure the page is now "published".
$page->writeToStage('Live');
// 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);
// Correct the "draft" broken link.
$page->Content = str_replace('987654321', $page->ID ?? '', $page->Content ?? '');
$page->writeToStage('Stage');
// 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);
// Make sure the change has now been "published".
$page->writeToStage('Live');
// 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.
*/
public function testBrokenFiles()
{
// Create a "draft" page with a broken file.
$page = Page::create();
$page = new SiteTree();
$page->Content = "<a href='[file_link,id=987654321]'>This</a> is a broken file.";
$page->writeToStage('Stage');
// Retrieve the broken files side report.
$reports = Report::get_reports();
$brokenFilesReport = null;
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 "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);
// Make sure the page is now "published".
$page->writeToStage('Live');
// 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.
$this->isReportBroken($brokenFilesReport, true, true);
// Correct the "draft" broken file.
$file = File::create();
$file->Filename = 'name.pdf';
$file->write();
@ -195,17 +198,15 @@ class CmsReportsTest extends SapphireTest
$page->writeToStage('Stage');
// 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);
// Make sure the change has now been "published".
$page->writeToStage('Live');
// 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.
$this->isReportBroken($brokenFilesReport, false, false);
}
@ -215,15 +216,12 @@ class CmsReportsTest extends SapphireTest
public function testBrokenVirtualPages()
{
// Create a "draft" virtual page with a broken link.
$page = VirtualPage::create();
$page->CopyContentFromID = 987654321;
$page->writeToStage('Stage');
// Retrieve the broken virtual pages side report.
$reports = Report::get_reports();
$brokenVirtualPagesReport = null;
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 "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);
// Make sure the page is now "published".
$page->writeToStage('Live');
// 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.
$this->isReportBroken($brokenVirtualPagesReport, true, true);
// Correct the "draft" broken link.
$contentPage = Page::create();
$contentPage = new SiteTree();
$contentPage->Content = 'This is some content.';
$contentPage->writeToStage('Stage');
$contentPage->writeToStage('Live');
@ -263,36 +258,30 @@ class CmsReportsTest extends SapphireTest
$page->writeToStage('Stage');
// 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);
// Make sure the change has now been "published".
$page->writeToStage('Live');
// 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.
$this->isReportBroken($brokenVirtualPagesReport, false, false);
}
/**
* Test the broken redirector pages side report.
*/
public function testBrokenRedirectorPages()
{
// Create a "draft" redirector page with a broken link.
$page = RedirectorPage::create();
$page->RedirectionType = 'Internal';
$page->LinkToID = 987654321;
$page->writeToStage('Stage');
// Retrieve the broken redirector pages side report.
$reports = Report::get_reports();
$brokenRedirectorPagesReport = null;
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 "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);
// Make sure the page is now "published".
$page->writeToStage('Live');
// 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.
$this->isReportBroken($brokenRedirectorPagesReport, true, true);
// Correct the "draft" broken link.
$contentPage = Page::create();
$contentPage = new SiteTree();
$contentPage->Content = 'This is some content.';
$contentPage->writeToStage('Stage');
$contentPage->writeToStage('Live');
@ -332,17 +318,15 @@ class CmsReportsTest extends SapphireTest
$page->writeToStage('Stage');
// 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);
// Make sure the change has now been "published".
$page->writeToStage('Live');
// 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.
$this->isReportBroken($brokenRedirectorPagesReport, false, false);
}
}

View File

@ -20,7 +20,7 @@ class CMSMainSearchFormTest extends FunctionalTest
'q' => [
'Term' => 'Page 10',
'FilterClass' => CMSSiteTreeFilter_Search::class,
]
],
])
);

View File

@ -2,7 +2,6 @@
namespace SilverStripe\CMS\Tests\Search;
use Page;
use SilverStripe\Assets\File;
use SilverStripe\CMS\Controllers\ContentController;
use SilverStripe\CMS\Controllers\ModelAsController;
@ -29,7 +28,6 @@ use TractorCow\Fluent\Extension\FluentSiteTreeExtension;
*/
class ZZZSearchFormTest extends FunctionalTest
{
protected static $fixture_file = 'ZZZSearchFormTest.yml';
protected static $illegal_extensions = [
@ -97,9 +95,6 @@ class ZZZSearchFormTest extends FunctionalTest
return $supports;
}
/**
* @skipUpgrade
*/
public function testSearchFormTemplateCanBeChanged()
{
if (!$this->checkFulltextSupport()) {
@ -116,9 +111,6 @@ class ZZZSearchFormTest extends FunctionalTest
);
}
/**
* @skipUpgrade
*/
public function testPublishedPagesMatchedByTitle()
{
if (!$this->checkFulltextSupport()) {
@ -149,9 +141,6 @@ class ZZZSearchFormTest extends FunctionalTest
);
}
/**
* @skipUpgrade
*/
public function testDoubleQuotesPublishedPagesMatchedByTitle()
{
if (!$this->checkFulltextSupport()) {
@ -184,9 +173,6 @@ class ZZZSearchFormTest extends FunctionalTest
);
}
/**
* @skipUpgrade
*/
public function testUnpublishedPagesNotIncluded()
{
if (!$this->checkFulltextSupport()) {

View File

@ -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());
}
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -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

View File

@ -1,19 +1,8 @@
const Path = require('path');
const webpackConfig = require('@silverstripe/webpack-config');
const { JavascriptWebpackConfig, CssWebpackConfig } = require('@silverstripe/webpack-config');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const {
resolveJS,
externalJS,
moduleJS,
pluginJS,
moduleCSS,
pluginCSS,
} = webpackConfig;
const ENV = process.env.NODE_ENV;
const PATHS = {
MODULES: 'node_modules',
FILES_PATH: '../',
ROOT: Path.resolve(),
SRC: Path.resolve('client/src'),
DIST: Path.resolve('client/dist'),
@ -21,46 +10,37 @@ const PATHS = {
};
const config = [
{
name: 'js',
entry: {
// Main JS bundles
new JavascriptWebpackConfig('js', PATHS, 'silverstripe/cms')
.setEntry({
bundle: `${PATHS.SRC}/bundles/bundle.js`,
// See https://github.com/webpack/webpack/issues/300#issuecomment-45313650
SilverStripeNavigator: `${PATHS.LEGACY_SRC}/SilverStripeNavigator.js`,
'TinyMCE_sslink-internal': `${PATHS.LEGACY_SRC}/TinyMCE_sslink-internal.js`,
'TinyMCE_sslink-anchor': `${PATHS.LEGACY_SRC}/TinyMCE_sslink-anchor.js`,
},
output: {
path: PATHS.DIST,
filename: 'js/[name].js',
},
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',
entry: {
})
.mergeConfig({
plugins: [
new CopyWebpackPlugin({
patterns: [
{
from: `${PATHS.SRC}/images`,
to: `${PATHS.DIST}/images`
},
]
}),
],
})
.getConfig(),
// sass to css
new CssWebpackConfig('css', PATHS)
.setEntry({
bundle: `${PATHS.SRC}/styles/bundle.scss`,
SilverStripeNavigator: `${PATHS.SRC}/styles/SilverStripeNavigator.scss`,
},
output: {
path: PATHS.DIST,
filename: 'styles/[name].css',
},
devtool: (ENV !== 'production') ? 'source-map' : '',
module: moduleCSS(ENV, PATHS),
plugins: pluginCSS(ENV, PATHS),
},
})
.getConfig(),
];
// Use WEBPACK_CHILD=js or WEBPACK_CHILD=css env var to run a single config
module.exports = (process.env.WEBPACK_CHILD)
? config.find((entry) => entry.name === process.env.WEBPACK_CHILD)
: module.exports = config;
: config;

14437
yarn.lock

File diff suppressed because it is too large Load Diff