Compare commits

..

3 Commits
5.1.5 ... 4

Author SHA1 Message Date
github-actions 6bc1171323 Merge branch '4.13' into 4 2024-02-10 13:24:58 +00:00
Guy Sartorelli ac3486938f
TLN Update translations (#2921) 2024-02-08 13:28:05 +13:00
Guy Sartorelli 9dfc5dea2e
TLN Update translations (#2920) 2024-02-07 16:02:32 +13:00
221 changed files with 13411 additions and 8216 deletions

View File

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

View File

@ -1,17 +0,0 @@
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: '30 18 1 */3 *'
- cron: '0 0 1 */3 *'
jobs:
update-js:

2
.nvmrc
View File

@ -1 +1 @@
18
10

161
.upgrade.yml Normal file
View File

@ -0,0 +1,161 @@
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 the [getting started documentation](https://docs.silverstripe.org/en/getting_started/).
See [installation instructions](https://docs.silverstripe.org/en/getting_started/installation/).
## Bugtracker

View File

@ -4,6 +4,7 @@ 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;
@ -32,7 +33,12 @@ ShortcodeParser::get('default')->register(
[SiteTree::class, 'link_shortcode_handler']
);
// TODO Remove once we can configure CMSMenu through static, nested configuration files
CMSMenu::remove_menu_class(CMSMain::class);
CMSMenu::remove_menu_class(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);

8
_config/legacy.yml Normal file
View File

@ -0,0 +1,8 @@
---
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,6 +5,8 @@ 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/coreconfig'
After: 'framework/*'
---
SilverStripe\Core\Manifest\VersionProvider:
modules:

View File

@ -0,0 +1,32 @@
<?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);
}
}

View File

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

View File

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

View File

@ -1 +1 @@
!function(){"use strict";var e={964:function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.default=void 0;t.default={ANCHORSELECTOR_CURRENT_FIELD:"ANCHORSELECTOR_CURRENT_FIELD",ANCHORSELECTOR_UPDATED:"ANCHORSELECTOR_UPDATED",ANCHORSELECTOR_UPDATING:"ANCHORSELECTOR_UPDATING",ANCHORSELECTOR_UPDATE_FAILED:"ANCHORSELECTOR_UPDATE_FAILED"}},447:function(e,t,n){Object.defineProperty(t,"__esModule",{value:!0}),t.beginUpdating=function(e){return{type:i.default.ANCHORSELECTOR_UPDATING,payload:{pageId:e}}},t.updateFailed=function(e){return{type:i.default.ANCHORSELECTOR_UPDATE_FAILED,payload:{pageId:e}}},t.updated=function(e,t){let n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];return{type:i.default.ANCHORSELECTOR_UPDATED,payload:{pageId:e,anchors:t,cacheResult:n}}},t.updatedCurrentField=function(e,t,n){return{type:i.default.ANCHORSELECTOR_CURRENT_FIELD,payload:{pageId:e,anchors:t,fieldID:n}}};var r,i=(r=n(964))&&r.__esModule?r:{default:r}},939:function(e){e.exports=ApolloClient},648:function(e){e.exports=Injector},595:function(e){e.exports=InsertLinkModal},363:function(e){e.exports=React},691:function(e){e.exports=ReactDomClient},624:function(e){e.exports=ReactRedux},265:function(e){e.exports=ShortcodeSerialiser},196:function(e){e.exports=TinyMCEActionRegistrar},754:function(e){e.exports=i18n},311:function(e){e.exports=jQuery}},t={};function n(r){var i=t[r];if(void 0!==i)return i.exports;var o=t[r]={exports:{}};return e[r](o,o.exports,n),o.exports}!function(){var e=p(n(754)),t=p(n(196)),r=p(n(363)),i=n(691),o=n(939),a=n(624),d=p(n(311)),l=p(n(265)),s=n(595),u=n(648),c=n(447);function p(e){return e&&e.__esModule?e:{default:e}}const f="sslinkanchor",E={init(n){t.default.addAction("sslink",{text:e.default._t("CMS.LINKLABEL_ANCHOR","Anchor on a page"),onAction:e=>e.execCommand(f),priority:60},n.getParam("editorIdentifier")).addCommandWithUrlTest(f,/^\[sitetree_link.+]#[^#\]]+$/),n.addCommand(f,(()=>{const e=(0,d.default)(`#${n.id}`).entwine("ss"),t=Number((0,d.default)("#Form_EditForm_ID").val()||0),r=(0,d.default)(n.getBody()).find("[id],[name]").toArray().map((e=>e.id||e.name));ss.store.dispatch((0,c.updatedCurrentField)(t,r,n.id)),e.openLinkAnchorDialog()}))}},A="insert-link__dialog-wrapper--anchor",C=(0,u.provideInjector)((0,s.createInsertLinkModal)("SilverStripe\\CMS\\Controllers\\CMSPageEditController","editorAnchorLink"));d.default.entwine("ss",(t=>{t("textarea.htmleditor").entwine({openLinkAnchorDialog(){let e=t(`#${A}`);e.length||(e=t(`<div id="${A}" />`),t("body").append(e)),e.addClass("insert-link__dialog-wrapper"),e.setElement(this),e.open()}}),t(`#${A}`).entwine({ReactRoot:null,renderModal(n){var d=this;const l=ss.store,s=ss.apolloClient,u=this.getOriginalAttributes(),c=this.getRequireLinkText(),p=Number(t("#Form_EditForm_ID").val()||0);let f=this.getReactRoot();f||(f=(0,i.createRoot)(this[0]),this.setReactRoot(f)),f.render(r.default.createElement(o.ApolloProvider,{client:s},r.default.createElement(a.Provider,{store:l},r.default.createElement(C,{isOpen:n,onInsert:function(){return d.handleInsert(...arguments)},onClosed:()=>this.close(),title:e.default._t("CMS.LINK_ANCHOR","Link to an anchor on a page"),bodyClassName:"modal__dialog",className:"insert-link__dialog-wrapper--anchor",fileAttributes:u,identifier:"Admin.InsertLinkAnchorModal",requireLinkText:c,currentPageID:p}))))},buildAttributes(e){return{href:`${l.default.serialise({name:"sitetree_link",properties:{id:e.PageID}},!0)}${e.Anchor&&e.Anchor.length?`#${e.Anchor}`:""}`,target:e.TargetBlank?"_blank":"",title:e.Description}},getOriginalAttributes(){const e=this.getElement().getEditor(),n=t(e.getSelectedNode()),r=(n.attr("href")||"").split("#");if(!r[0])return{};const i=l.default.match("sitetree_link",!1,r[0]);return i?{PageID:i.properties.id?parseInt(i.properties.id,10):0,Anchor:r[1]||"",Description:n.attr("title"),TargetBlank:!!n.attr("target")}:{}}})})),tinymce.PluginManager.add(f,(e=>E.init(e)))}()}();
!function(e){function t(r){if(n[r])return n[r].exports;var i=n[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,t),i.l=!0,i.exports}var n={};t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s="./client/src/legacy/TinyMCE_sslink-anchor.js")}({"./client/src/legacy/TinyMCE_sslink-anchor.js":function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(1),o=r(i),a=n(9),l=r(a),c=n(2),s=r(c),u=n(5),d=r(u),p=n(4),f=n(6),A=n(0),E=r(A),_=n(8),h=r(_),C=n(7),g=n(3),R=n("./client/src/state/anchorSelector/AnchorSelectorActions.js"),O={init:function(e){l.default.addAction("sslink",{text:o.default._t("CMS.LINKLABEL_ANCHOR","Anchor on a page"),onclick:function(e){return e.execCommand("sslinkanchor")},priority:60},e.settings.editorIdentifier).addCommandWithUrlTest("sslinkanchor",/^\[sitetree_link.+]#[^#\]]+$/),e.addCommand("sslinkanchor",function(){var t=(0,E.default)("#"+e.id).entwine("ss"),n=Number((0,E.default)("#Form_EditForm_ID").val()||0),r=e.$("[id],[name]",e.getBody()).toArray().map(function(e){return e.id||e.name});ss.store.dispatch((0,R.updatedCurrentField)(n,r,e.id)),t.openLinkAnchorDialog()})}},T="insert-link__dialog-wrapper--anchor",m=(0,g.provideInjector)((0,C.createInsertLinkModal)("SilverStripe\\CMS\\Controllers\\CMSPageEditController","editorAnchorLink"));E.default.entwine("ss",function(e){e("textarea.htmleditor").entwine({openLinkAnchorDialog:function(){var t=e("#"+T);t.length||(t=e('<div id="'+T+'" />'),e("body").append(t)),t.addClass("insert-link__dialog-wrapper"),t.setElement(this),t.open()}}),e("#"+T).entwine({renderModal:function(t){var n=this,r=ss.store,i=ss.apolloClient,a=function(){return n.close()},l=function(){return n.handleInsert.apply(n,arguments)},c=this.getOriginalAttributes(),u=this.getRequireLinkText(),A=Number(e("#Form_EditForm_ID").val()||0);d.default.render(s.default.createElement(p.ApolloProvider,{client:i},s.default.createElement(f.Provider,{store:r},s.default.createElement(m,{isOpen:t,onInsert:l,onClosed:a,title:o.default._t("CMS.LINK_ANCHOR","Link to an anchor on a page"),bodyClassName:"modal__dialog",className:"insert-link__dialog-wrapper--anchor",fileAttributes:c,identifier:"Admin.InsertLinkAnchorModal",requireLinkText:u,currentPageID:A}))),this[0])},buildAttributes:function(e){return{href:h.default.serialise({name:"sitetree_link",properties:{id:e.PageID}},!0)+(e.Anchor&&e.Anchor.length?"#"+e.Anchor:""),target:e.TargetBlank?"_blank":"",title:e.Description}},getOriginalAttributes:function(){var t=this.getElement().getEditor(),n=e(t.getSelectedNode()),r=(n.attr("href")||"").split("#");if(!r[0])return{};var i=h.default.match("sitetree_link",!1,r[0]);return i?{PageID:i.properties.id?parseInt(i.properties.id,10):0,Anchor:r[1]||"",Description:n.attr("title"),TargetBlank:!!n.attr("target")}:{}}})}),tinymce.PluginManager.add("sslinkanchor",function(e){return O.init(e)}),t.default=O},"./client/src/state/anchorSelector/AnchorSelectorActionTypes.js":function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={ANCHORSELECTOR_CURRENT_FIELD:"ANCHORSELECTOR_CURRENT_FIELD",ANCHORSELECTOR_UPDATED:"ANCHORSELECTOR_UPDATED",ANCHORSELECTOR_UPDATING:"ANCHORSELECTOR_UPDATING",ANCHORSELECTOR_UPDATE_FAILED:"ANCHORSELECTOR_UPDATE_FAILED"}},"./client/src/state/anchorSelector/AnchorSelectorActions.js":function(e,t,n){"use strict";function r(e){return{type:c.default.ANCHORSELECTOR_UPDATING,payload:{pageId:e}}}function i(e,t){var n=arguments.length>2&&void 0!==arguments[2]&&arguments[2];return{type:c.default.ANCHORSELECTOR_UPDATED,payload:{pageId:e,anchors:t,cacheResult:n}}}function o(e,t,n){return{type:c.default.ANCHORSELECTOR_CURRENT_FIELD,payload:{pageId:e,anchors:t,fieldID:n}}}function a(e){return{type:c.default.ANCHORSELECTOR_UPDATE_FAILED,payload:{pageId:e}}}Object.defineProperty(t,"__esModule",{value:!0}),t.beginUpdating=r,t.updated=i,t.updatedCurrentField=o,t.updateFailed=a;var l=n("./client/src/state/anchorSelector/AnchorSelectorActionTypes.js"),c=function(e){return e&&e.__esModule?e:{default:e}}(l)},0:function(e,t){e.exports=jQuery},1:function(e,t){e.exports=i18n},2:function(e,t){e.exports=React},3:function(e,t){e.exports=Injector},4:function(e,t){e.exports=ReactApollo},5:function(e,t){e.exports=ReactDom},6:function(e,t){e.exports=ReactRedux},7:function(e,t){e.exports=InsertLinkModal},8:function(e,t){e.exports=ShortcodeSerialiser},9:function(e,t){e.exports=TinyMCEActionRegistrar}});

View File

@ -1 +1 @@
!function(){"use strict";var e={939:function(e){e.exports=ApolloClient},648:function(e){e.exports=Injector},595:function(e){e.exports=InsertLinkModal},363:function(e){e.exports=React},691:function(e){e.exports=ReactDomClient},624:function(e){e.exports=ReactRedux},265:function(e){e.exports=ShortcodeSerialiser},196:function(e){e.exports=TinyMCEActionRegistrar},754:function(e){e.exports=i18n},311:function(e){e.exports=jQuery}},t={};function n(i){var r=t[i];if(void 0!==r)return r.exports;var o=t[i]={exports:{}};return e[i](o,o.exports,n),o.exports}!function(){var e=u(n(754)),t=u(n(196)),i=u(n(363)),r=n(691),o=n(939),a=n(624),l=u(n(311)),s=u(n(265)),d=n(595),c=n(648);function u(e){return e&&e.__esModule?e:{default:e}}const p="sslinkinternal",f={init(n){t.default.addAction("sslink",{text:e.default._t("CMS.LINKLABEL_PAGE","Page on this site"),onAction:e=>e.execCommand(p),priority:90},n.getParam("editorIdentifier")).addCommandWithUrlTest(p,/^\[sitetree_link.+]$/),n.addCommand(p,(()=>{(0,l.default)(`#${n.id}`).entwine("ss").openLinkInternalDialog()}))}},g="insert-link__dialog-wrapper--internal",h=(0,c.provideInjector)((0,d.createInsertLinkModal)("SilverStripe\\CMS\\Controllers\\CMSPageEditController","editorInternalLink"));l.default.entwine("ss",(t=>{t("textarea.htmleditor").entwine({openLinkInternalDialog(){let e=t(`#${g}`);e.length||(e=t(`<div id="${g}" />`),t("body").append(e)),e.addClass("insert-link__dialog-wrapper"),e.setElement(this),e.open()}}),t(`#${g}`).entwine({ReactRoot:null,renderModal(t){var n=this;const l=ss.store,s=ss.apolloClient,d=this.getOriginalAttributes(),c=this.getRequireLinkText();let u=this.getReactRoot();u||(u=(0,r.createRoot)(this[0]),this.setReactRoot(u)),u.render(i.default.createElement(o.ApolloProvider,{client:s},i.default.createElement(a.Provider,{store:l},i.default.createElement(h,{isOpen:t,onInsert:function(){return n.handleInsert(...arguments)},onClosed:()=>this.close(),title:e.default._t("CMS.LINK_PAGE","Link to a page"),bodyClassName:"modal__dialog",className:"insert-link__dialog-wrapper--internal",fileAttributes:d,identifier:"Admin.InsertLinkInternalModal",requireLinkText:c}))))},buildAttributes(e){return{href:`${s.default.serialise({name:"sitetree_link",properties:{id:e.PageID}},!0)}${e.Anchor&&e.Anchor.length?`#${e.Anchor}`:""}`,target:e.TargetBlank?"_blank":"",title:e.Description}},getOriginalAttributes(){const e=this.getElement().getEditor(),n=t(e.getSelectedNode()),i=(n.attr("href")||"").split("#");if(!i[0])return{};const r=s.default.match("sitetree_link",!1,i[0]);return r?{PageID:r.properties.id?parseInt(r.properties.id,10):0,Anchor:i[1]||"",Description:n.attr("title"),TargetBlank:!!n.attr("target")}:{}}})})),tinymce.PluginManager.add(p,(e=>f.init(e)))}()}();
!function(e){function t(r){if(n[r])return n[r].exports;var i=n[r]={i:r,l:!1,exports:{}};return e[r].call(i.exports,i,i.exports,t),i.l=!0,i.exports}var n={};t.m=e,t.c=n,t.i=function(e){return e},t.d=function(e,n,r){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:r})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s="./client/src/legacy/TinyMCE_sslink-internal.js")}({"./client/src/legacy/TinyMCE_sslink-internal.js":function(e,t,n){"use strict";function r(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var i=n(1),o=r(i),l=n(9),a=r(l),s=n(2),u=r(s),c=n(5),d=r(c),f=n(4),p=n(6),g=n(0),k=r(g),_=n(8),m=r(_),h=n(7),x=n(3),I={init:function(e){a.default.addAction("sslink",{text:o.default._t("CMS.LINKLABEL_PAGE","Page on this site"),onclick:function(e){return e.execCommand("sslinkinternal")},priority:90},e.settings.editorIdentifier).addCommandWithUrlTest("sslinkinternal",/^\[sitetree_link.+]$/),e.addCommand("sslinkinternal",function(){(0,k.default)("#"+e.id).entwine("ss").openLinkInternalDialog()})}},A="insert-link__dialog-wrapper--internal",C=(0,x.provideInjector)((0,h.createInsertLinkModal)("SilverStripe\\CMS\\Controllers\\CMSPageEditController","editorInternalLink"));k.default.entwine("ss",function(e){e("textarea.htmleditor").entwine({openLinkInternalDialog:function(){var t=e("#"+A);t.length||(t=e('<div id="'+A+'" />'),e("body").append(t)),t.addClass("insert-link__dialog-wrapper"),t.setElement(this),t.open()}}),e("#"+A).entwine({renderModal:function(e){var t=this,n=ss.store,r=ss.apolloClient,i=function(){return t.close()},l=function(){return t.handleInsert.apply(t,arguments)},a=this.getOriginalAttributes(),s=this.getRequireLinkText();d.default.render(u.default.createElement(f.ApolloProvider,{client:r},u.default.createElement(p.Provider,{store:n},u.default.createElement(C,{isOpen:e,onInsert:l,onClosed:i,title:o.default._t("CMS.LINK_PAGE","Link to a page"),bodyClassName:"modal__dialog",className:"insert-link__dialog-wrapper--internal",fileAttributes:a,identifier:"Admin.InsertLinkInternalModal",requireLinkText:s}))),this[0])},buildAttributes:function(e){return{href:m.default.serialise({name:"sitetree_link",properties:{id:e.PageID}},!0)+(e.Anchor&&e.Anchor.length?"#"+e.Anchor:""),target:e.TargetBlank?"_blank":"",title:e.Description}},getOriginalAttributes:function(){var t=this.getElement().getEditor(),n=e(t.getSelectedNode()),r=(n.attr("href")||"").split("#");if(!r[0])return{};var i=m.default.match("sitetree_link",!1,r[0]);return i?{PageID:i.properties.id?parseInt(i.properties.id,10):0,Anchor:r[1]||"",Description:n.attr("title"),TargetBlank:!!n.attr("target")}:{}}})}),tinymce.PluginManager.add("sslinkinternal",function(e){return I.init(e)}),t.default=I},0:function(e,t){e.exports=jQuery},1:function(e,t){e.exports=i18n},2:function(e,t){e.exports=React},3:function(e,t){e.exports=Injector},4:function(e,t){e.exports=ReactApollo},5:function(e,t){e.exports=ReactDom},6:function(e,t){e.exports=ReactRedux},7:function(e,t){e.exports=InsertLinkModal},8:function(e,t){e.exports=ShortcodeSerialiser},9:function(e,t){e.exports=TinyMCEActionRegistrar}});

View File

@ -1,48 +1 @@
!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)}();
!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}});

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

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}#cms-content-treeview .cms-tree-expand-trigger,#cms-content-listview .cms-tree-expand-trigger{display:none}.cms-content-tools #cms-content-treeview .cms-content-toolbar{border-bottom:none;box-shadow:none;margin-bottom:0}.cms-content-tools #cms-content-treeview .cms-tree-expand-trigger{display:block;float:left;margin:0 0 2px 0}.cms-content-tools #cms-content-treeview .cms-tree-expand-trigger span.ui-button-text{padding-right:8px}.cms-content-tools #cms-content-treeview .cms-tree .badge{display:none}.cms-content-tools #cms-content-treeview .cms-tree a:hover>.text>.badge,.cms-content-tools #cms-content-treeview .cms-tree .jstree-clicked>.text>.badge{display:inline-block}#cms-content-tools-CMSMain .search-form{right:0}.cms-list .cms-list__item-breadcrumbs{margin-left:21px;margin-bottom:0;font-size:.9em;word-break:break-word}.cms-content-toolbar .view-controls{margin-top:0}.cms-content-toolbar .view-controls .page-view-link{display:inline-block;margin-right:-5px}.cms-content-toolbar .view-controls.view-controls--treeview .font-icon-tree,.cms-content-toolbar .view-controls.view-controls--listview .font-icon-list{display:none}#pages-controller-cms-content,#pages-controller-cms-content+.cms-preview{width:50%}.field.urlsegment .input-group{width:auto;flex-wrap:nowrap}.field.urlsegment.loading .form__field-label{background-image:url(data:image/gif;base64,R0lGODlhEAAQAPQAAP///wpakvj6+z9+qYivyg9dlC5yotfk7KvG2R9om3umxGubveXt8py80sjZ5k+IsFyRtgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAAFUCAgjmRpnqUwFGwhKoRgqq2YFMaRGjWA8AbZiIBbjQQ8AmmFUJEQhQGJhaKOrCksgEla+KIkYvC6SJKQOISoNSYdeIk1ayA8ExTyeR3F749CACH5BAkKAAAALAAAAAAQABAAAAVoICCKR9KMaCoaxeCoqEAkRX3AwMHWxQIIjJSAZWgUEgzBwCBAEQpMwIDwY1FHgwJCtOW2UDWYIDyqNVVkUbYr6CK+o2eUMKgWrqKhj0FrEM8jQQALPFA3MAc8CQSAMA5ZBjgqDQmHIyEAIfkECQoAAAAsAAAAABAAEAAABWAgII4j85Ao2hRIKgrEUBQJLaSHMe8zgQo6Q8sxS7RIhILhBkgumCTZsXkACBC+0cwF2GoLLoFXREDcDlkAojBICRaFLDCOQtQKjmsQSubtDFU/NXcDBHwkaw1cKQ8MiyEAIfkECQoAAAAsAAAAABAAEAAABVIgII5kaZ6AIJQCMRTFQKiDQx4GrBfGa4uCnAEhQuRgPwCBtwK+kCNFgjh6QlFYgGO7baJ2CxIioSDpwqNggWCGDVVGphly3BkOpXDrKfNm/4AhACH5BAkKAAAALAAAAAAQABAAAAVgICCOZGmeqEAMRTEQwskYbV0Yx7kYSIzQhtgoBxCKBDQCIOcoLBimRiFhSABYU5gIgW01pLUBYkRItAYAqrlhYiwKjiWAcDMWY8QjsCf4DewiBzQ2N1AmKlgvgCiMjSQhACH5BAkKAAAALAAAAAAQABAAAAVfICCOZGmeqEgUxUAIpkA0AMKyxkEiSZEIsJqhYAg+boUFSTAkiBiNHks3sg1ILAfBiS10gyqCg0UaFBCkwy3RYKiIYMAC+RAxiQgYsJdAjw5DN2gILzEEZgVcKYuMJiEAOwAAAAAAAAAAAA==);background-repeat:no-repeat;background-position:right center;padding-right:20px}.field.urlsegment .URL-link{padding-top:8px;display:inline-block}.field.urlsegment input.text{flex:0 1 250px}.field.urlsegment .input-group-append{z-index:2}.field.urlsegment .input-group-append:last-of-type{z-index:1}.field.urlsegment .input-group-append .btn{margin-top:0;padding-top:.5385rem;padding-bottom:.5385rem}.field.urlsegment .help{margin-left:0}.field.urlsegment .edit-holder{display:none}.field.urlsegment .edit-holder .form__field-description{clear:both}.form__field-update-url{border-radius:0 3px 3px 0;margin-right:0px;margin-left:-1px}.anchorselectorfield__menu{z-index:20000}
#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}

View File

@ -8,17 +8,17 @@ if (typeof(ss) === 'undefined' || typeof(ss.i18n) === 'undefined') {
ss.i18n.addDictionary('nl', {
"CMS.ALERTCLASSNAME": "Het paginatype wordt aangepast na het opslaan van de pagina",
"CMS.AddSubPage": "Voeg nieuwe pagina toe",
"CMS.ConfirmRestoreFromLive": "Weet u zeker dat u terug wil naar de laatst gepubliceerde versie van deze pagina?",
"CMS.ConfirmRestoreFromLive": "Weet je zeker dat je terug wil naar de laatst gepubliceerde versie van deze pagina?",
"CMS.Duplicate": "Dupliceren",
"CMS.EditPage": "Aanpassen",
"CMS.ONLYSELECTTWO": "Er kunnen maar 2 pagina's tegelijk worden vergeleken.",
"CMS.Restore": "Weet u zeker dat deze pagina hersteld moet worden?",
"CMS.RestoreToRoot": "Weet u zeker dat deze pagina hersteld moet worden?\n\nDe bovenliggende pagina bestaat niet meer, waardoor deze pagina op het hoofdniveau terecht komt.",
"CMS.RollbackToVersion": "Wilt u terugdraaien naar versie #%s van deze pagina?",
"CMS.Restore": "Weet je zeker dat deze pagina hersteld moet worden?",
"CMS.RestoreToRoot": "Weet je zeker dat deze pagina hersteld moet worden?\n\nDe bovenliggende pagina bestaat niet meer, waardoor deze pagina op het hoofdniveau terecht komt.",
"CMS.RollbackToVersion": "Wil je teruggaan naar versie #%s van deze pagina?",
"CMS.ShowAsList": "Toon onderliggende pagina's als lijst",
"CMS.ThisPageAndSubpages": "Deze pagina en subpagina's",
"CMS.ThisPageOnly": "Enkel deze pagina",
"CMS.Unpublish": "Weet u zeker dat deze pagina verwijderd moet worden?\n\nDe pagina blijft wel als concept opgeslagen.",
"CMS.Unpublish": "Weet je zeker dat deze pagina verwijderd moet worden?\n\nDe pagina blijft wel als concept beschikbaar.",
"CMS.UpdateURL": "URL aanpassen",
"CMS.ViewPage": "Bekijk"
});

View File

@ -1,17 +1,17 @@
{
"CMS.ALERTCLASSNAME": "Het paginatype wordt aangepast na het opslaan van de pagina",
"CMS.AddSubPage": "Voeg nieuwe pagina toe",
"CMS.ConfirmRestoreFromLive": "Weet u zeker dat u terug wil naar de laatst gepubliceerde versie van deze pagina?",
"CMS.ConfirmRestoreFromLive": "Weet je zeker dat je terug wil naar de laatst gepubliceerde versie van deze pagina?",
"CMS.Duplicate": "Dupliceren",
"CMS.EditPage": "Aanpassen",
"CMS.ONLYSELECTTWO": "Er kunnen maar 2 pagina's tegelijk worden vergeleken.",
"CMS.Restore": "Weet u zeker dat deze pagina hersteld moet worden?",
"CMS.RestoreToRoot": "Weet u zeker dat deze pagina hersteld moet worden?\n\nDe bovenliggende pagina bestaat niet meer, waardoor deze pagina op het hoofdniveau terecht komt.",
"CMS.RollbackToVersion": "Wilt u terugdraaien naar versie #%s van deze pagina?",
"CMS.Restore": "Weet je zeker dat deze pagina hersteld moet worden?",
"CMS.RestoreToRoot": "Weet je zeker dat deze pagina hersteld moet worden?\n\nDe bovenliggende pagina bestaat niet meer, waardoor deze pagina op het hoofdniveau terecht komt.",
"CMS.RollbackToVersion": "Wil je teruggaan naar versie #%s van deze pagina?",
"CMS.ShowAsList": "Toon onderliggende pagina's als lijst",
"CMS.ThisPageAndSubpages": "Deze pagina en subpagina's",
"CMS.ThisPageOnly": "Enkel deze pagina",
"CMS.Unpublish": "Weet u zeker dat deze pagina verwijderd moet worden?\n\nDe pagina blijft wel als concept opgeslagen.",
"CMS.Unpublish": "Weet je zeker dat deze pagina verwijderd moet worden?\n\nDe pagina blijft wel als concept beschikbaar.",
"CMS.UpdateURL": "URL aanpassen",
"CMS.ViewPage": "Bekijk"
}

View File

@ -1,17 +1,17 @@
{
"CMS.ALERTCLASSNAME": "The page type will be updated after the page is saved",
"CMS.AddSubPage": "Add new page here",
"CMS.ConfirmRestoreFromLive": "Are you sure you want to revert draft to when the page was last published?",
"CMS.Duplicate": "Duplicate",
"CMS.EditPage": "Edit",
"CMS.ONLYSELECTTWO": "You can only compare two versions at this time.",
"CMS.Restore": "Are you sure you want to restore this page from archive?",
"CMS.RestoreToRoot": "Are you sure you want to restore this page from archive?\n\nBecause the parent page is not available this will be restored to the top level.",
"CMS.RollbackToVersion": "Do you really want to roll back to version #%s of this page?",
"CMS.ShowAsList": "Show children as list",
"CMS.ThisPageAndSubpages": "This page and subpages",
"CMS.ThisPageOnly": "This page only",
"CMS.Unpublish": "Are you sure you want to remove your page from the published site?\n\nThis page will still be available in the sitetree as draft.",
"CMS.UpdateURL": "Update URL",
"CMS.ViewPage": "View"
"CMS.ALERTCLASSNAME": "Тип сторінки буде оновлено після збереження сторінки",
"CMS.AddSubPage": "Додати нову сторінку тут",
"CMS.ConfirmRestoreFromLive": "Ви впевнені, що хочете повернути чернетку до часу останньої публікації сторінки?",
"CMS.Duplicate": "Дублікат",
"CMS.EditPage": "Редагувати",
"CMS.ONLYSELECTTWO": "Наразі можна порівняти лише дві версії.",
"CMS.Restore": "Ви впевнені, що бажаєте відновити цю сторінку з архіву?",
"CMS.RestoreToRoot": "Ви впевнені, що бажаєте відновити цю сторінку з архіву?\n\nОскільки батьківська сторінка недоступна, її буде відновлено до верхнього рівня.",
"CMS.RollbackToVersion": "Ви справді бажаєте повернутися до версії #%s цієї сторінки?",
"CMS.ShowAsList": "Показати дітей як список",
"CMS.ThisPageAndSubpages": "Ця сторінка та підсторінки",
"CMS.ThisPageOnly": "Тільки ця сторінка",
"CMS.Unpublish": "Ви впевнені, що хочете видалити свою сторінку з опублікованого сайту?\n\nЦя сторінка все ще буде доступна в дереві сайту як чернетка.",
"CMS.UpdateURL": "Оновити URL",
"CMS.ViewPage": "Переглянути"
}

View File

@ -6,20 +6,20 @@ if (typeof(ss) === 'undefined' || typeof(ss.i18n) === 'undefined') {
}
} else {
ss.i18n.addDictionary('uk_UA', {
"CMS.ALERTCLASSNAME": "The page type will be updated after the page is saved",
"CMS.AddSubPage": "Add new page here",
"CMS.ConfirmRestoreFromLive": "Are you sure you want to revert draft to when the page was last published?",
"CMS.Duplicate": "Duplicate",
"CMS.EditPage": "Edit",
"CMS.ONLYSELECTTWO": "You can only compare two versions at this time.",
"CMS.Restore": "Are you sure you want to restore this page from archive?",
"CMS.RestoreToRoot": "Are you sure you want to restore this page from archive?\n\nBecause the parent page is not available this will be restored to the top level.",
"CMS.RollbackToVersion": "Do you really want to roll back to version #%s of this page?",
"CMS.ShowAsList": "Show children as list",
"CMS.ThisPageAndSubpages": "This page and subpages",
"CMS.ThisPageOnly": "This page only",
"CMS.Unpublish": "Are you sure you want to remove your page from the published site?\n\nThis page will still be available in the sitetree as draft.",
"CMS.UpdateURL": "Update URL",
"CMS.ViewPage": "View"
"CMS.ALERTCLASSNAME": "Тип сторінки буде оновлено після збереження сторінки",
"CMS.AddSubPage": "Додати нову сторінку тут",
"CMS.ConfirmRestoreFromLive": "Ви впевнені, що хочете повернути чернетку до часу останньої публікації сторінки?",
"CMS.Duplicate": "Дублікат",
"CMS.EditPage": "Редагувати",
"CMS.ONLYSELECTTWO": "Наразі можна порівняти лише дві версії.",
"CMS.Restore": "Ви впевнені, що бажаєте відновити цю сторінку з архіву?",
"CMS.RestoreToRoot": "Ви впевнені, що бажаєте відновити цю сторінку з архіву?\n\nОскільки батьківська сторінка недоступна, її буде відновлено до верхнього рівня.",
"CMS.RollbackToVersion": "Ви справді бажаєте повернутися до версії #%s цієї сторінки?",
"CMS.ShowAsList": "Показати дітей як список",
"CMS.ThisPageAndSubpages": "Ця сторінка та підсторінки",
"CMS.ThisPageOnly": "Тільки ця сторінка",
"CMS.Unpublish": "Ви впевнені, що хочете видалити свою сторінку з опублікованого сайту?\n\nЦя сторінка все ще буде доступна в дереві сайту як чернетка.",
"CMS.UpdateURL": "Оновити URL",
"CMS.ViewPage": "Переглянути"
});
}

View File

@ -2,6 +2,7 @@ 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,8 +8,7 @@ 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 CreatableSelect from 'react-select/creatable';
import EmotionCssCacheProvider from 'containers/EmotionCssCacheProvider/EmotionCssCacheProvider';
import { Creatable } from 'react-select';
import getFormState from 'lib/getFormState';
import classnames from 'classnames';
import PropTypes from 'prop-types';
@ -118,27 +117,26 @@ class AnchorSelectorField extends SilverStripeComponent {
}
render() {
const { extraClass, CreatableSelectComponent } = this.props;
const className = classnames('anchorselectorfield', extraClass);
const inputProps = {
id: this.props.id,
};
const className = classnames('anchorselectorfield', this.props.extraClass);
const options = this.getDropdownOptions();
const rawValue = this.props.value || '';
const value = this.props.value || '';
const placeholder = i18n._t('CMS.ANCHOR_SELECT_OR_TYPE', 'Select or enter anchor');
return (
<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>
<Creatable
searchable
options={options}
className={className}
name={this.props.name}
inputProps={inputProps}
onChange={this.handleChange}
onBlurResetsInput
value={value}
placeholder={placeholder}
labelKey="value"
/>
);
}
}
@ -153,8 +151,8 @@ AnchorSelectorField.propTypes = {
pageId: PropTypes.number,
anchors: PropTypes.array,
loadingState: PropTypes.oneOf(Object
.keys(anchorSelectorStates)
.map((key) => anchorSelectorStates[key])),
.keys(anchorSelectorStates)
.map((key) => anchorSelectorStates[key])),
onLoadingError: PropTypes.func,
data: PropTypes.shape({
endpoint: PropTypes.string,
@ -167,7 +165,6 @@ AnchorSelectorField.defaultProps = {
extraClass: '',
onLoadingError: noop,
attributes: {},
CreatableSelectComponent: CreatableSelect
};
function mapStateToProps(state, ownProps) {

View File

@ -1,3 +0,0 @@
.anchorselectorfield__menu {
z-index: 20000;
}

View File

@ -1,129 +1,103 @@
/* global jest, test, describe, beforeEach, it, expect, setTimeout */
import React from 'react';
import anchorSelectorStates from 'state/anchorSelector/AnchorSelectorStates';
import { render, screen } from '@testing-library/react';
import { Component as AnchorSelectorField } from '../AnchorSelectorField';
/* global jest, describe, beforeEach, it, expect, setTimeout */
jest.mock('isomorphic-fetch', () =>
() => Promise.resolve({
json: () => ['anchor3', 'anchor4'],
}));
}));
jest.mock('i18n');
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,
};
}
import React from 'react';
import ReactTestUtils from 'react-dom/test-utils';
import { Component as AnchorSelectorField } from '../AnchorSelectorField';
import anchorSelectorStates from 'state/anchorSelector/AnchorSelectorStates';
test('AnchorSelectorField componentDidMount() Loads dirty selectors', async () => {
const beginUpdating = jest.fn();
render(<AnchorSelectorField {...makeProps({
loadingState: anchorSelectorStates.DIRTY,
actions: {
anchorSelector: {
beginUpdating,
updated: () => {},
updateFailed: () => {},
},
},
})}
/>);
await screen.findByTestId('test-creatable-select');
expect(beginUpdating).toBeCalledWith(4);
});
describe('AnchorSelectorField', () => {
let props = null;
let field = null;
test('AnchorSelectorField Merges value with page anchors', async () => {
const beginUpdating = jest.fn();
render(<AnchorSelectorField {...makeProps({
loadingState: anchorSelectorStates.DIRTY,
actions: {
anchorSelector: {
beginUpdating,
updated: () => {},
updateFailed: () => {},
beforeEach(() => {
props = {
id: 'Form_Test',
name: 'Test',
data: {
endpoint: 'url-callback',
},
},
})}
/>);
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');
});
pageId: 4,
anchors: ['anchor1', 'anchor2'],
value: 'selectedanchor',
loadingState: anchorSelectorStates.SUCCESS,
actions: {
anchorSelector: {
beginUpdating: jest.fn(),
updated: jest.fn(),
updateFailed: jest.fn(),
},
},
};
});
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();
});
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();
});
});
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();
});
describe('getDropdownOptions()', () => {
it('Merges value with page anchors', () => {
field = ReactTestUtils.renderIntoDocument(<AnchorSelectorField {...props} />);
expect(field.getDropdownOptions()).toEqual([
{ value: 'selectedanchor' },
{ value: 'anchor1' },
{ value: 'anchor2' },
]);
});
});
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();
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);
});
});
});
});

View File

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

View File

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

View File

@ -1,5 +1,4 @@
import $ from 'jquery';
import { joinUrlPaths } from 'lib/urls';
/**
* Behaviour for the CMS Content Toolbar.
@ -82,7 +81,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(joinUrlPaths(baseUrl, $contentView.data('url-listviewroot')));
window.location.assign(baseUrl + $contentView.data('url-listviewroot'));
return;
}

View File

@ -0,0 +1,183 @@
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 { createRoot } from 'react-dom/client';
import { ApolloProvider } from '@apollo/client';
import ReactDOM from 'react-dom';
import { ApolloProvider } from 'react-apollo';
import { Provider } from 'react-redux';
import jQuery from 'jquery';
import ShortcodeSerialiser from 'lib/ShortcodeSerialiser';
@ -17,24 +17,24 @@ const plugin = {
init(editor) {
// Add "Anchor on a page" to link menu for this editor
TinyMCEActionRegistrar
.addAction(
'sslink',
{
text: i18n._t('CMS.LINKLABEL_ANCHOR', 'Anchor on a page'),
onAction: (activeEditor) => activeEditor.execCommand(commandName),
priority: 60,
},
editor.getParam('editorIdentifier'),
)
.addCommandWithUrlTest(commandName, /^\[sitetree_link.+]#[^#\]]+$/);
.addAction(
'sslink',
{
text: i18n._t('CMS.LINKLABEL_ANCHOR', 'Anchor on a page'),
onclick: (activeEditor) => activeEditor.execCommand(commandName),
priority: 60,
},
editor.settings.editorIdentifier,
)
.addCommandWithUrlTest(commandName, /^\[sitetree_link.+]#[^#\]]+$/);
// Add a command that corresponds with the above menu item
editor.addCommand(commandName, () => {
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 = jQuery(editor.getBody())
.find('[id],[name]')
const validTargets = editor
.$('[id],[name]', editor.getBody())
.toArray()
.map((element) => element.id || element.name);
ss.store.dispatch(updatedCurrentField(currentPageID, validTargets, editor.id));
@ -69,8 +69,6 @@ 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;
@ -81,12 +79,7 @@ jQuery.entwine('ss', ($) => {
const currentPageID = Number($('#Form_EditForm_ID').val() || 0);
// create/update the react component
let root = this.getReactRoot();
if (!root) {
root = createRoot(this[0]);
this.setReactRoot(root);
}
root.render(
ReactDOM.render(
<ApolloProvider client={client}>
<Provider store={store}>
<InsertLinkInternalModal
@ -102,7 +95,8 @@ jQuery.entwine('ss', ($) => {
currentPageID={currentPageID}
/>
</Provider>
</ApolloProvider>
</ApolloProvider>,
this[0]
);
},

View File

@ -2,8 +2,8 @@
import i18n from 'i18n';
import TinyMCEActionRegistrar from 'lib/TinyMCEActionRegistrar';
import React from 'react';
import { createRoot } from 'react-dom/client';
import { ApolloProvider } from '@apollo/client';
import ReactDOM from 'react-dom';
import { ApolloProvider } from 'react-apollo';
import { Provider } from 'react-redux';
import jQuery from 'jquery';
import ShortcodeSerialiser from 'lib/ShortcodeSerialiser';
@ -16,16 +16,16 @@ const plugin = {
init(editor) {
// Add "Page on this site" to link menu for this editor
TinyMCEActionRegistrar
.addAction(
'sslink',
{
text: i18n._t('CMS.LINKLABEL_PAGE', 'Page on this site'),
onAction: (activeEditor) => activeEditor.execCommand(commandName),
priority: 90,
},
editor.getParam('editorIdentifier'),
)
.addCommandWithUrlTest(commandName, /^\[sitetree_link.+]$/);
.addAction(
'sslink',
{
text: i18n._t('CMS.LINKLABEL_PAGE', 'Page on this site'),
onclick: (activeEditor) => activeEditor.execCommand(commandName),
priority: 90,
},
editor.settings.editorIdentifier,
)
.addCommandWithUrlTest(commandName, /^\[sitetree_link.+]$/);
// Add a command that corresponds with the above menu item
editor.addCommand(commandName, () => {
@ -61,8 +61,6 @@ 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;
@ -72,12 +70,7 @@ jQuery.entwine('ss', ($) => {
const requireLinkText = this.getRequireLinkText();
// create/update the react component
let root = this.getReactRoot();
if (!root) {
root = createRoot(this[0]);
this.setReactRoot(root);
}
root.render(
ReactDOM.render(
<ApolloProvider client={client}>
<Provider store={store}>
<InsertLinkInternalModal
@ -92,7 +85,8 @@ jQuery.entwine('ss', ($) => {
requireLinkText={requireLinkText}
/>
</Provider>
</ApolloProvider>
</ApolloProvider>,
this[0]
);
},

View File

@ -1,4 +1,4 @@
import { graphql } from '@apollo/client/react/hoc';
import { graphql } from 'react-apollo';
import gql from 'graphql-tag';
// GraphQL query for retrieving the version history of a specific page. The
@ -88,11 +88,11 @@ const config = {
versions: {
...versions,
goToPage(page) {
refetch({
refetch({
offset: ((page || 1) - 1) * limit,
limit,
page_id: recordId,
});
});
}
},
},

View File

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

View File

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

View File

@ -7,5 +7,3 @@
@import "legacy/CMSMain";
@import "legacy/ReportAdmin";
@import "../components/AnchorSelectorField/AnchorSelectorField";

View File

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

View File

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

View File

@ -4,7 +4,6 @@ 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;
@ -21,7 +20,7 @@ class CMSBatchAction_Restore extends CMSBatchAction
return _t(__CLASS__ . '.RESTORE', 'Restore');
}
public function run(SS_List $pages): HTTPResponse
public function run(SS_List $pages)
{
// Sort pages by depth
$pageArray = $pages->toArray();

View File

@ -3,7 +3,6 @@
namespace SilverStripe\CMS\BatchActions;
use SilverStripe\Admin\CMSBatchAction;
use SilverStripe\Control\HTTPResponse;
use SilverStripe\ORM\SS_List;
/**
@ -16,7 +15,7 @@ class CMSBatchAction_Unpublish extends CMSBatchAction
return _t(__CLASS__ . '.UNPUBLISH_PAGES', 'Unpublish');
}
public function run(SS_List $pages): HTTPResponse
public function run(SS_List $pages)
{
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,7 +23,6 @@ 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;
@ -70,9 +69,10 @@ 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.
@ -80,6 +80,8 @@ use SilverStripe\View\Requirements;
* This class creates a 2-frame layout - left-tree and right-form - to sit beneath the main
* admin menu.
*
* @todo Create some base classes to contain the generic functionality that will be replicated.
*
* @mixin LeftAndMainPageIconsExtension
*/
class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionProvider, Flushable, MemberCacheFlusher
@ -105,6 +107,12 @@ 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';
@ -180,13 +188,19 @@ 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');
Requirements::javascript('silverstripe/cms: client/dist/js/SilverStripeNavigator.js');
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);
Requirements::add_i18n_javascript('silverstripe/cms: client/lang', false, true);
CMSBatchActionHandler::register('restore', CMSBatchAction_Restore::class);
CMSBatchActionHandler::register('archive', CMSBatchAction_Archive::class);
@ -194,7 +208,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
CMSBatchActionHandler::register('publish', CMSBatchAction_Publish::class);
}
public function index(HTTPRequest $request): HTTPResponse
public function index($request)
{
// 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.
@ -205,7 +219,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
return parent::index($request);
}
public function getResponseNegotiator(): PjaxResponseNegotiator
public function getResponseNegotiator()
{
$negotiator = parent::getResponseNegotiator();
@ -400,13 +414,10 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
public function LinkPageHistory()
{
$controller = Injector::inst()->get(CMSPageHistoryViewerController::class);
if (($id = $this->currentPageID()) && $controller) {
if ($controller) {
return $this->LinkWithSearch(
Controller::join_links($controller->Link('show'), $id)
);
}
if ($id = $this->currentPageID()) {
return $this->LinkWithSearch(
Controller::join_links(CMSPageHistoryViewerController::singleton()->Link('show'), $id)
);
} else {
return null;
}
@ -653,8 +664,11 @@ 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(HTTPRequest $request): HTTPResponse
public function getsubtree($request)
{
$html = $this->getSiteTreeFor(
$this->config()->get('tree_class'),
@ -669,7 +683,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 $this->getResponse()->setBody($html);
return $html;
}
/**
@ -677,8 +691,11 @@ 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(HTTPRequest $request): HTTPResponse
public function updatetreenodes($request)
{
$data = [];
$ids = explode(',', $request->getVar('ids') ?? '');
@ -700,6 +717,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
$markingSet->markUnexpanded($record);
// Find the next & previous nodes, for proper positioning (Sort isn't good enough - it's not a raw offset)
// TODO: These methods should really be in hierarchy - for a start it assumes Sort exists
$prev = null;
$className = $this->config()->get('tree_class');
@ -745,15 +763,17 @@ 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(HTTPRequest $request): HTTPResponse
public function savetreenode($request)
{
if (!SecurityToken::inst()->checkRequest($request)) {
$this->httpError(400);
return $this->httpError(400);
}
if (!$this->CanOrganiseSitetree()) {
$this->httpError(
return $this->httpError(
403,
_t(
__CLASS__.'.CANT_REORGANISE',
@ -766,14 +786,14 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
$id = $request->requestVar('ID');
$parentID = $request->requestVar('ParentID');
if (!is_numeric($id) || !is_numeric($parentID)) {
$this->httpError(400);
return $this->httpError(400);
}
// Check record exists in the DB
/** @var SiteTree $node */
$node = DataObject::get_by_id($className, $id);
if (!$node) {
$this->httpError(
return $this->httpError(
500,
_t(
__CLASS__.'.PLEASESAVE',
@ -785,7 +805,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()) {
$this->httpError(
return $this->httpError(
403,
_t(
__CLASS__.'.CANT_REORGANISE',
@ -980,6 +1000,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
);
// Create the form
/** @skipUpgrade */
$form = Form::create(
$this,
'SearchForm',
@ -1016,7 +1037,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
return $pageTypes;
}
public function doSearch(array $data, Form $form): HTTPResponse
public function doSearch($data, $form)
{
return $this->getsubtree($this->getRequest());
}
@ -1331,9 +1352,8 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
}
// Added in-line to the form, but plucked into different view by LeftAndMain.Preview.js upon load
if (($record instanceof CMSPreviewable || $record->has_extension(CMSPreviewable::class))
&& !$fields->fieldByName('SilverStripeNavigator')
) {
/** @skipUpgrade */
if ($record instanceof CMSPreviewable && !$fields->fieldByName('SilverStripeNavigator')) {
$navField = new LiteralField('SilverStripeNavigator', $this->getSilverStripeNavigator());
$navField->setAllowHTML(true);
$fields->push($navField);
@ -1369,6 +1389,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
}
}
// TODO Can't merge $FormAttributes in template at the moment
$form->addExtraClass('center ' . $this->BaseCSSClasses());
// Set validation exemptions for specific actions
$form->setValidationExemptActions([
@ -1383,7 +1404,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 || $record->has_extension(CMSPreviewable::class)) {
if ($record instanceof CMSPreviewable) {
$form->addExtraClass('cms-previewable');
}
$form->addExtraClass('fill-height flexbox-area-grow');
@ -1539,14 +1560,17 @@ 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(HTTPRequest $request): HTTPResponse
public function childfilter($request)
{
// Check valid parent specified
$parentID = $request->requestVar('ParentID');
$parent = SiteTree::get()->byID($parentID);
if (!$parent || !$parent->exists()) {
$this->httpError(404);
return $this->httpError(404);
}
// Build hints specific to this class
@ -1733,9 +1757,12 @@ 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(array $data, Form $form): HTTPResponse
public function save($data, $form)
{
$className = $this->config()->get('tree_class');
@ -1764,6 +1791,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
return Security::permissionFailure($this);
}
// TODO Coupling to SiteTree
$record->HasBrokenLink = 0;
$record->HasBrokenFile = 0;
@ -1877,9 +1905,12 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
*
* @uses SiteTree->doRevertToLive()
*
* @param array $data
* @param Form $form
* @return HTTPResponse
* @throws HTTPResponse_Exception
*/
public function revert(array $data, Form $form): HTTPResponse
public function revert($data, $form)
{
if (!isset($data['ID'])) {
throw new HTTPResponse_Exception("Please pass an ID in the form content", 400);
@ -1927,9 +1958,12 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
*
* @see deletefromlive()
*
* @param array $data
* @param Form $form
* @return HTTPResponse
* @throws HTTPResponse_Exception
*/
public function delete(array $data, Form $form): HTTPResponse
public function delete($data, $form)
{
$id = $data['ID'];
$record = SiteTree::get()->byID($id);
@ -1959,9 +1993,12 @@ 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(array $data, Form $form): HTTPResponse
public function archive($data, $form)
{
$id = $data['ID'];
/** @var SiteTree $record */
@ -1989,14 +2026,14 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
return $this->getResponseNegotiator()->respond($this->getRequest());
}
public function publish(array $data, Form $form): HTTPResponse
public function publish($data, $form)
{
$data['publish'] = '1';
return $this->save($data, $form);
}
public function unpublish(array $data, Form $form): HTTPResponse
public function unpublish($data, $form)
{
$className = $this->config()->get('tree_class');
/** @var SiteTree $record */
@ -2132,9 +2169,86 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
}
/**
* Restore a completely deleted page from the SiteTree_versions table.
* @deprecated 4.12.0 Use custom logic instead
* @param $request
* @return HTTPResponse|string|void
*/
public function restore(array $data, Form $form): HTTPResponse
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)
{
if (!isset($data['ID']) || !is_numeric($data['ID'])) {
return new HTTPResponse("Please pass an ID in the form content", 400);
@ -2161,7 +2275,7 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
return $this->getResponseNegotiator()->respond($this->getRequest());
}
public function duplicate(HTTPRequest $request): HTTPResponse
public function duplicate($request)
{
// Protect against CSRF on destructive action
if (!SecurityToken::inst()->checkRequest($request)) {
@ -2205,11 +2319,11 @@ class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionPr
return new HTTPResponse("CMSMain::duplicate() Bad ID: '$id'", 400);
}
public function duplicatewithchildren(HTTPRequest $request): HTTPResponse
public function duplicatewithchildren($request)
{
// Protect against CSRF on destructive action
if (!SecurityToken::inst()->checkRequest($request)) {
$this->httpError(400);
return $this->httpError(400);
}
Environment::increaseTimeLimitTo();
if (($id = $this->urlParams['ID']) && is_numeric($id)) {

View File

@ -132,6 +132,10 @@ class CMSPageAddController extends CMSPageEditController
]);
});
// TODO Re-enable search once it allows for HTML title display,
// see http://open.silverstripe.org/ticket/7455
// $parentField->setShowSearch(true);
$parentModeField->addExtraClass('parent-mode');
// CMSMain->currentPageID() automatically sets the homepage,
@ -188,7 +192,12 @@ class CMSPageAddController extends CMSPageEditController
return $form;
}
public function doAdd(array $data, Form $form): HTTPResponse
/**
* @param array $data
* @param Form $form
* @return HTTPResponse
*/
public function doAdd($data, $form)
{
$className = isset($data['PageType']) ? $data['PageType'] : "Page";
$parentID = isset($data['ParentID']) ? (int)$data['ParentID'] : 0;
@ -232,7 +241,7 @@ class CMSPageAddController extends CMSPageEditController
return $this->redirect(Controller::join_links($editController->Link('show'), $record->ID));
}
public function doCancel(array $data, Form $form): HTTPResponse
public function doCancel($data, $form)
{
return $this->redirect(CMSMain::singleton()->Link());
}

View File

@ -53,27 +53,30 @@ class CMSPageEditController extends CMSMain
/**
* Action handler for adding pages to a campaign
*
* @param array $data
* @param Form $form
* @return DBHTMLText|HTTPResponse
*/
public function addtocampaign(array $data, Form $form): HTTPResponse
public function addtocampaign($data, $form)
{
$id = $data['ID'];
$record = \Page::get()->byID($id);
$handler = AddToCampaignHandler::create($this, $record);
$response = $handler->addToCampaign($record, $data);
$message = $response->getBody();
if (empty($message)) {
return $response;
$results = $handler->addToCampaign($record, $data);
if (is_null($results)) {
return null;
}
if ($this->getSchemaRequested()) {
// Send extra "message" data with schema response
$extraData = ['message' => $message];
$extraData = ['message' => $results];
$schemaId = Controller::join_links($this->Link('schema/AddToCampaignForm'), $id);
return $this->getSchemaResponse($schemaId, $form, null, $extraData);
}
return $response;
return $results;
}
/**

View File

@ -0,0 +1,480 @@
<?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,7 +2,6 @@
namespace SilverStripe\CMS\Controllers;
use SilverStripe\Admin\Navigator\SilverStripeNavigator;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Control\Controller;
use SilverStripe\Control\Director;
@ -30,6 +29,7 @@ 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}.
@ -45,6 +45,7 @@ use SilverStripe\View\SSViewer;
* Subclasses of ContentController are generally instantiated by ModelAsController; this will create
* a controller based on the URLSegment action variable, by looking in the SiteTree table.
*
* @todo Can this be used for anything other than SiteTree controllers?
*/
class ContentController extends Controller
{
@ -174,6 +175,7 @@ class ContentController extends Controller
}
// Check page permissions
/** @skipUpgrade */
if ($this->dataRecord && $this->URLSegment != 'Security' && !$this->dataRecord->canView()) {
Security::permissionFailure($this);
return;
@ -184,9 +186,11 @@ 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): HTTPResponse
public function handleRequest(HTTPRequest $request)
{
/** @var SiteTree $child */
$child = null;
@ -196,6 +200,11 @@ 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
@ -204,6 +213,10 @@ 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.
@ -213,6 +226,25 @@ 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 {
@ -302,6 +334,8 @@ class ContentController extends Controller
/**
* Returns the default log-in form.
*
* @todo Check if here should be returned just the default log-in form or
* all available log-in forms (also OpenID...)
* @return \SilverStripe\Security\MemberAuthenticator\MemberLoginForm
*/
public function LoginForm()
@ -381,6 +415,9 @@ 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.
@ -389,7 +426,14 @@ HTML;
*/
public function ContentLocale()
{
$locale = i18n::get_locale();
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();
}
return i18n::convert_rfc1766($locale);
}
@ -446,6 +490,7 @@ HTML;
$this->httpError(410);
}
// TODO Allow this to work when allow_url_fopen=0
if (isset($_SESSION['StatsID']) && $_SESSION['StatsID']) {
$url = 'http://ss2stat.silverstripe.com/Installation/installed?ID=' . $_SESSION['StatsID'];
@file_get_contents($url ?? '');

View File

@ -17,6 +17,7 @@ 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}
@ -32,9 +33,11 @@ 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): ContentController
public static function controller_for(SiteTree $sitetree, $action = null)
{
$controller = $sitetree->getControllerName();
@ -55,6 +58,7 @@ 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(),
@ -68,8 +72,10 @@ class ModelAsController extends Controller implements NestedController
/**
* @uses ModelAsController::getNestedController()
* @param HTTPRequest $request
* @return HTTPResponse
*/
public function handleRequest(HTTPRequest $request): HTTPResponse
public function handleRequest(HTTPRequest $request)
{
$this->beforeHandleRequest($request);
@ -80,16 +86,23 @@ 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(Controller::join_links(Director::absoluteBaseURL(), 'dev/build?returnURL=' . (isset($_GET['url']) ? urlencode($_GET['url']) : null)));
$this->getResponse()->redirect(Director::absoluteBaseURL() . 'dev/build?returnURL=' . (isset($_GET['url']) ? urlencode($_GET['url']) : null));
$this->popCurrent();
return $this->getResponse();
}
try {
$result = $this->getNestedController()->handleRequest($this->getRequest());
$result = $result;
$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);
}
} catch (HTTPResponse_Exception $responseException) {
$result = $responseException->getResponse();
}
@ -99,9 +112,10 @@ class ModelAsController extends Controller implements NestedController
}
/**
* @return ContentController
* @throws Exception If URLSegment not passed in as a request parameter.
*/
public function getNestedController(): ContentController
public function getNestedController()
{
$request = $this->getRequest();
$urlSegment = $request->param('URLSegment');
@ -110,6 +124,11 @@ 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();
@ -126,10 +145,21 @@ 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,9 +17,10 @@ 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(HTTPRequest $request)
public function onBeforeHTTPError404($request)
{
// We need to get the URL ourselves because $request->allParams() only has a max of 4 params
$params = preg_split('|/+|', $request->getURL() ?? '');
@ -102,7 +103,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 Controller::join_links($page->Link(), implode('/', $params));
return $page->Link() . implode('/', $params);
}
}
} else {

View File

@ -73,6 +73,7 @@ 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(),
@ -84,12 +85,17 @@ class RootURLController extends Controller implements Resettable
}
}
public function handleRequest(HTTPRequest $request): HTTPResponse
/**
* @param HTTPRequest $request
* @return HTTPResponse
*/
public function handleRequest(HTTPRequest $request)
{
self::$is_at_root = true;
$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

@ -0,0 +1,118 @@
<?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

@ -0,0 +1,155 @@
<?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

@ -0,0 +1,92 @@
<?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,7 +3,6 @@
namespace SilverStripe\CMS\Controllers;
use SilverStripe\Admin\LeftAndMain;
use SilverStripe\Admin\Navigator\SilverStripeNavigatorItem;
use SilverStripe\CMS\Model\RedirectorPage;
use SilverStripe\Control\Controller;

View File

@ -0,0 +1,106 @@
<?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

@ -0,0 +1,115 @@
<?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

@ -0,0 +1,98 @@
<?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 rtrim($this->urlPrefix ?? '', '/') . '/';
return $this->urlPrefix;
}
public function getURLSuffix()

View File

@ -18,8 +18,8 @@ use SilverStripe\Versioned\Versioned;
* @property string $RedirectionType Either 'Internal','External' or 'File'
* @property string $ExternalURL URL to redirect to if $RedirectionType is 'External'
* @property int $LinkToID
* @method SiteTree LinkTo()
* @method File LinkToFile()
* @method SiteTree LinkTo() Page to link to if $RedirectionType is 'Internal'
* @method File LinkToFile() File to link to if $RedirectionType is 'File'
*/
class RedirectorPage extends Page
{
@ -164,6 +164,7 @@ class RedirectorPage extends Page
$this->HasBrokenLink = true;
}
} else {
// TODO implement checking of a remote site
$this->HasBrokenLink = false;
}
}

View File

@ -25,6 +25,7 @@ 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;
@ -35,7 +36,6 @@ use SilverStripe\Forms\GridField\GridField;
use SilverStripe\Forms\GridField\GridFieldDataColumns;
use SilverStripe\Forms\GridField\GridFieldLazyLoader;
use SilverStripe\Forms\HTMLEditor\HTMLEditorField;
use SilverStripe\Forms\ListboxField;
use SilverStripe\Forms\LiteralField;
use SilverStripe\Forms\OptionsetField;
use SilverStripe\Forms\Tab;
@ -102,14 +102,17 @@ use SilverStripe\View\SSViewer;
* @property bool $HasBrokenFile True if this page has a broken file shortcode
* @property bool $HasBrokenLink True if this page has a broken page shortcode
*
* @method ManyManyList ViewerGroups() List of groups that can view this object.
* @method ManyManyList EditorGroups() List of groups that can edit this object.
* @method SiteTree Parent()
* @method HasManyList|SiteTreeLink[] BackLinks() List of SiteTreeLink objects attached to this page
*
* @mixin Hierarchy
* @mixin Versioned
* @mixin RecursivePublishable
* @mixin SiteTreeLinkTracking Added via linktracking.yml to DataObject directly
* @mixin FileLinkTracking Added via filetracking.yml in silverstripe/assets
* @mixin InheritedPermissionsExtension
* @method HasManyList<SiteTreeLink> BackLinks()
* @method HasManyList<VirtualPage> VirtualPages()
*/
class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvider, CMSPreviewable, Resettable, Flushable, MemberCacheFlusher
{
@ -636,7 +639,7 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
if ($this->hasMethod('alternateAbsoluteLink')) {
return $this->alternateAbsoluteLink($action);
} else {
return Director::absoluteURL((string) $this->Link($action));
return Director::absoluteURL($this->Link($action));
}
}
@ -649,6 +652,13 @@ 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;
@ -689,17 +699,15 @@ 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;
}
$link = Controller::join_links($base, $action);
$this->extend('updateRelativeLink', $link, $base, $action);
return $link;
return Controller::join_links($base, '/', $action);
}
/**
@ -1185,14 +1193,6 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
return true;
}
// check for specific users
if ($this->CanViewType === InheritedPermissions::ONLY_THESE_MEMBERS
&& $member
&& $this->ViewerMembers()->filter('ID', $member->ID)->count() > 0
) {
return true;
}
return false;
}
@ -1960,6 +1960,7 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
*/
public function BackLinkTracking()
{
// @todo - Implement PolymorphicManyManyList to replace this
$list = ArrayList::create();
$siteTreelinkTable = SiteTreeLink::singleton()->baseTable();
@ -2249,16 +2250,6 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
$viewAllGroupsMap = $mapFn(Permission::get_groups_by_permission(['SITETREE_VIEW_ALL', 'ADMIN']));
$editAllGroupsMap = $mapFn(Permission::get_groups_by_permission(['SITETREE_EDIT_ALL', 'ADMIN']));
// $membersMap is limited to 100 records specifically so that it does not crash the front-end
// if the website has a large number of Members, which is likely to happen if the website also
// uses the Member table for non-cms public users
// This limit should be removed if the ListboxField front-end component is switched out or
// modified so that it does not load all users at once and instead uses XHR to fetch a subset
// of users based on what the user types in
$membersMap = Member::get()
->limit(100)
->map('ID', 'Name');
$fields = new FieldList(
$rootTab = new TabSet(
"Root",
@ -2289,11 +2280,6 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
_t(__CLASS__.'.VIEWERGROUPS', "Viewer Groups"),
Group::class
),
$viewerMembersField = ListboxField::create(
"ViewerMembers",
_t(__CLASS__.'.VIEWERMEMBERS', "Viewer Users"),
$membersMap,
),
$editorsOptionsField = new OptionsetField(
"CanEditType",
_t(__CLASS__.'.EDITHEADER', "Who can edit this page?")
@ -2302,11 +2288,6 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
"EditorGroups",
_t(__CLASS__.'.EDITORGROUPS', "Editor Groups"),
Group::class
),
$editorMembersField = ListboxField::create(
"EditorMembers",
_t(__CLASS__.'.EDITORMEMBERS', "Editor Users"),
$membersMap
)
)
)
@ -2347,10 +2328,6 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
__CLASS__.'.ACCESSONLYTHESE',
"Only these groups (choose from list)"
),
InheritedPermissions::ONLY_THESE_MEMBERS => _t(
__CLASS__.'.ACCESSONLYMEMBERS',
"Only these users (choose from list)"
),
];
$viewersOptionsField->setSource($viewersOptionsSource);
@ -2377,27 +2354,17 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
if (!Permission::check('SITETREE_GRANT_ACCESS')) {
$fields->makeFieldReadonly($viewersOptionsField);
if ($this->CanViewType === InheritedPermissions::ONLY_THESE_USERS) {
if ($this->CanEditType === InheritedPermissions::ONLY_THESE_USERS) {
$fields->makeFieldReadonly($viewerGroupsField);
$fields->removeByName('ViewerMembers');
} elseif ($this->CanViewType === InheritedPermissions::ONLY_THESE_MEMBERS) {
$fields->makeFieldReadonly($viewerMembersField);
$fields->removeByName('ViewerGroups');
} else {
$fields->removeByName('ViewerGroups');
$fields->removeByName('ViewerMembers');
}
$fields->makeFieldReadonly($editorsOptionsField);
if ($this->CanEditType === InheritedPermissions::ONLY_THESE_USERS) {
$fields->makeFieldReadonly($editorGroupsField);
$fields->removeByName('EditorMembers');
} elseif ($this->CanEditType === InheritedPermissions::ONLY_THESE_MEMBERS) {
$fields->makeFieldReadonly($editorMembersField);
$fields->removeByName('EditorGroups');
} else {
$fields->removeByName('EditorGroups');
$fields->removeByName('EditorMembers');
}
}
@ -2506,6 +2473,7 @@ 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')));
@ -2550,7 +2518,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() && $this->config()->get('can_be_root');
$restoreToRoot = $this->isParentArchived();
// "restore"
$title = $restoreToRoot
@ -2559,15 +2527,13 @@ 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');
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)
);
}
$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
@ -2829,6 +2795,35 @@ 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
@ -3077,6 +3072,14 @@ 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) {
@ -3340,7 +3343,7 @@ class SiteTree extends DataObject implements PermissionProvider, i18nEntityProvi
}
/**
* Cache key for creatableChildPages() method
* Cache key for creatableChildren() method
*
* @param int $memberID
* @return string

View File

@ -75,15 +75,13 @@ abstract class SiteTreeExtension extends DataExtension
* before {@link SiteTree::RelativeLink()} calls {@link Controller::join_links()}
* on the $base and $action
*
* @param string &$link The URL of this page relative to siteroot including
* @param string &$base The URL of this page relative to siteroot, not including
* the action
* @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.
* @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(&$link, $base, $action)
public function updateRelativeLink(&$base, &$action)
{
}
}

View File

@ -0,0 +1,35 @@
<?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

@ -0,0 +1,50 @@
<?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

@ -0,0 +1,88 @@
<?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

@ -7,8 +7,10 @@ use SilverStripe\ORM\DataObject;
/**
* Represents a link between a dataobject parent and a page in a HTML content area
*
* @method SiteTree Linked()
* @method DataObject Parent()
* @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

@ -27,7 +27,7 @@ use SilverStripe\View\Parsers\HTMLValue;
* field to your `db` config and this extension will ensure it's flagged appropriately.
*
* @property DataObject|SiteTreeLinkTracking $owner
* @method ManyManyThroughList<SiteTree> LinkTracking()
* @method ManyManyThroughList LinkTracking() List of site pages linked on this dataobject
*/
class SiteTreeLinkTracking extends DataExtension
{

View File

@ -4,6 +4,7 @@ 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;
@ -21,8 +22,8 @@ use SilverStripe\View\HTML;
*
* Note: This Only duplicates $db fields and not the $has_one etc..
*
* @property int $CopyContentFromID
* @method SiteTree CopyContentFrom()
* @property int $CopyContentFromID
*/
class VirtualPage extends Page
{
@ -359,6 +360,30 @@ 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

@ -46,7 +46,7 @@ class BrokenFilesReport extends Report
{
return [
"Title" => [
"title" => "Title",
"title" => "Title", // todo: use NestedTitle(2)
"link" => true,
],
];

View File

@ -39,7 +39,7 @@ class BrokenRedirectorPagesReport extends Report
{
return [
"Title" => [
"title" => "Title",
"title" => "Title", // todo: use NestedTitle(2)
"link" => true,
],
];

View File

@ -39,7 +39,7 @@ class BrokenVirtualPagesReport extends Report
{
return [
"Title" => [
"title" => "Title",
"title" => "Title", // todo: use NestedTitle(2)
"link" => true,
],
];

View File

@ -43,7 +43,7 @@ class EmptyPagesReport extends Report
{
return [
"Title" => [
"title" => "Title",
"title" => "Title", // todo: use NestedTitle(2)
"link" => true,
],
];

View File

@ -31,14 +31,14 @@ 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))
->orderBy("\"$tableName\".\"LastEdited\" DESC");
->sort("\"$tableName\".\"LastEdited\" DESC");
}
public function columns()
{
return [
"Title" => [
"title" => "Title",
"title" => "Title", // todo: use NestedTitle(2)
"link" => true,
],
];

View File

@ -39,6 +39,7 @@ 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,11 +13,17 @@ 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
@ -45,6 +51,7 @@ 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
@ -63,6 +70,12 @@ 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'))
@ -117,6 +130,22 @@ 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) {
@ -152,6 +181,17 @@ 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

@ -0,0 +1,70 @@
<?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

@ -0,0 +1,388 @@
<?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

@ -0,0 +1,61 @@
<?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": {
"php": "^8.1",
"silverstripe/admin": "^2.1.11",
"silverstripe/campaign-admin": "^2",
"silverstripe/framework": "^5.1",
"silverstripe/reports": "^5",
"silverstripe/siteconfig": "^5",
"silverstripe/versioned": "^2",
"silverstripe/versioned-admin": "^2",
"silverstripe/vendor-plugin": "^2"
"silverstripe/admin": "^1.13.20",
"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"
},
"require-dev": {
"phpunit/phpunit": "^9.6",
"squizlabs/php_codesniffer": "^3.7"
"phpunit/phpunit": "^9.5",
"squizlabs/php_codesniffer": "^3"
},
"extra": {
"expose": [

View File

@ -25,8 +25,6 @@ az:
ANCHORVALUE: Ankor
SilverStripe\CMS\Forms\InternalLinkFormFactory:
LINKDESCR: 'Link təsviri'
SilverStripe\CMS\Forms\SiteTreeURLSegmentField:
Edit: Edit
SilverStripe\CMS\Model\RedirectorPage:
FILE: Fayl
HEADER: 'Bu səhifə istifadəçiləri başqa səhifəyə yönləndirəcək'

View File

@ -145,7 +145,6 @@ bg:
Cancel: Отказ
Edit: Редактирай
HelpChars: 'Специалните символи са автоматично конвертирани или изтрити.'
OK: OK
SilverStripe\CMS\Model\RedirectorPage:
DESCRIPTION: 'Пренасочва към страница от сайта или външен URL адрес'
FILE: Файл
@ -260,7 +259,6 @@ bg:
many_many_ImageTracking: 'Следене на снимките'
many_many_LinkTracking: 'Следене на връзките'
SilverStripe\CMS\Model\SiteTreeFileExtension:
TITLE_INDEX: '#'
TITLE_TYPE: Тип
TITLE_USED_ON: 'Използвано в'
SilverStripe\CMS\Model\SiteTreeFileFormFactoryExtension:

View File

@ -15,8 +15,6 @@ bs:
ROLLBACK: 'Vrati na ovo izdanje'
SAVEDRAFT: Snimi
TabContent: Sadržaj
SilverStripe\CMS\Controllers\CMSPagesController:
FILTER: Filter
SilverStripe\CMS\Controllers\ContentController:
Email: E-mail
Password: Šifra
@ -24,8 +22,6 @@ bs:
ANCHORVALUE: Sidro
SilverStripe\CMS\Forms\InternalLinkFormFactory:
LINKDESCR: 'Opis linka'
SilverStripe\CMS\Forms\SiteTreeURLSegmentField:
Edit: Edit
SilverStripe\CMS\Model\RedirectorPage:
FILE: Datoteka
HEADER: 'Ova stranica će preusmjeriti korisnike na drugu stranicu'

View File

@ -29,8 +29,6 @@ ca:
ANCHORVALUE: Ancla
SilverStripe\CMS\Forms\InternalLinkFormFactory:
LINKDESCR: 'Descripció de l''enllaç'
SilverStripe\CMS\Forms\SiteTreeURLSegmentField:
Edit: Edit
SilverStripe\CMS\Model\RedirectorPage:
FILE: Fitxer
HEADER: 'Aquesta pàgina redirigirà usuaris a una altra pàgina'

View File

@ -114,7 +114,6 @@ cs:
SilverStripe\CMS\Controllers\ContentController:
ARCHIVEDSITE: 'Náhled verze'
ARCHIVEDSITEFROM: 'Archivován web z'
CMS: CMS
DRAFT: Koncept
DRAFTSITE: 'Koncept webu'
Email: E-mail
@ -145,7 +144,6 @@ cs:
Cancel: Storno
Edit: Editovat
HelpChars: 'Zvláštní znaky jsou automaticky zkonvertovány nebo odstraněny'
OK: OK
SilverStripe\CMS\Model\RedirectorPage:
DESCRIPTION: 'Přesměruje na interní stránku nebo na externí URL'
FILE: Soubor
@ -209,7 +207,6 @@ cs:
METAEXTRA: 'Vlastní meta tagy'
MODIFIEDONDRAFTHELP: 'Stránka má nezveřejněné změny'
MODIFIEDONDRAFTSHORT: Upraveno
MetadataToggle: Metadata
MoreOptions: 'Více voleb'
NOTPUBLISHED: Nezveřejněno
OBSOLETECLASS: 'Tato stránka je zastaralého typu {type}. Uložení zpúsobí zrušení svého typu a múžete tak ztratit data'
@ -253,7 +250,6 @@ cs:
many_many_ImageTracking: 'Stopování obrázku'
many_many_LinkTracking: 'Stopování odkazu'
SilverStripe\CMS\Model\SiteTreeFileExtension:
TITLE_INDEX: '#'
TITLE_TYPE: Typ
TITLE_USED_ON: 'Použito na'
SilverStripe\CMS\Model\VirtualPage:
@ -278,7 +274,6 @@ cs:
ColumnDateLastModified: 'Datum poslední změny'
ColumnDateLastPublished: 'Datum posledního zveřejnění'
ColumnProblemType: 'Problém typu'
ColumnURL: URL
HasBrokenFile: 'porušen soubor'
HasBrokenLink: 'porušen odkaz'
HasBrokenLinkAndFile: 'porušen odkaz a soubor'

View File

@ -30,7 +30,6 @@ da:
Create: Opret
DUPLICATED: 'Dublerede ''{title}'' med succes'
DUPLICATEDWITHCHILDREN: 'Dublerede ''{title}'' og undersider med succes'
EMAIL: Email
NEWPAGE: 'Ny {pagetype}'
PAGENOTEXISTS: 'Denne side eksisterer ikke'
PAGES: 'Side status'
@ -95,7 +94,6 @@ da:
SilverStripe\CMS\Controllers\CMSPageSettingsController:
MENUTITLE: 'Rediger side'
SilverStripe\CMS\Controllers\CMSPagesController:
FILTER: Filter
ListView: Listevisning
MENUTITLE: Sider
TreeView: Trævisning
@ -114,12 +112,9 @@ da:
SilverStripe\CMS\Controllers\CMSSiteTreeFilter_StatusRemovedFromDraftPages:
Title: 'Udgivet men slettet fra kladde'
SilverStripe\CMS\Controllers\ContentController:
ARCHIVEDSITE: 'Preview version'
ARCHIVEDSITEFROM: 'Arkiveret side fra'
CMS: CMS
DRAFT: Kladde
DRAFTSITE: Kladdeside
Email: Email
INSTALL_SUCCESS: 'Installation gennemført succesfuldt'
InstallFilesDeleted: 'Installationsfiler slettet succesfuldt.'
InstallSecurityWarning: 'Af sikkerhedmæssige årsager bør du slette installationsfilerne, medmindre du planlægger at installere igen på et senere tidspunkt (<em>kræver admin login, se det ovenstående</em>). Webserveren behøver også kun skriverettigheder til mappen "assets", du kan fjerne skriverettigheder til alle de andre mapper. <a href="{link}" style="text-align: center;">Klik her for at slette installationsfilerne.</a>'
@ -151,7 +146,6 @@ da:
EMPTY: 'Indtast en URL eller klik annuller'
Edit: Rediger
HelpChars: 'Specialkarakterer bliver automatisk konverteret eller fjernet.'
OK: OK
SilverStripe\CMS\Model\RedirectorPage:
DESCRIPTION: 'Omdirigerer til en side eller en ekstern URL'
HEADER: 'Denne side omdirigerer besøgende til en anden side'
@ -218,7 +212,6 @@ da:
METAEXTRA: 'Brugerdefinerede Meta Tags'
MODIFIEDONDRAFTHELP: 'Siden har ikke-udgivne ændringer'
MODIFIEDONDRAFTSHORT: Ændret
MetadataToggle: Metadata
MoreOptions: 'Flere muligheder'
NOTPUBLISHED: 'Ikke udgivet'
OBSOLETECLASS: 'Denne side er af den udgåede sidetype {type}. Gemmer du bliver sidetypen nulstillet og du kan tabe data.'
@ -271,8 +264,6 @@ da:
many_many_ImageTracking: 'Sporing af billeder'
many_many_LinkTracking: 'Sporing af links'
SilverStripe\CMS\Model\SiteTreeFileExtension:
TITLE_INDEX: '#'
TITLE_TYPE: Type
TITLE_USED_ON: 'Brugt på'
SilverStripe\CMS\Model\SiteTreeFileFormFactoryExtension:
USAGE: Brug
@ -307,7 +298,6 @@ da:
ColumnDateLastModified: 'Dato for seneste ændring'
ColumnDateLastPublished: 'Dato for seneste udgivelse'
ColumnProblemType: Problemtype
ColumnURL: URL
HasBrokenFile: 'har en ødelagt fil'
HasBrokenLink: 'har et dårligt link'
HasBrokenLinkAndFile: 'har et dårligt link og en ødelagt fil'

View File

@ -96,7 +96,6 @@ de:
SilverStripe\CMS\Controllers\CMSPageSettingsController:
MENUTITLE: 'Seite bearbeiten'
SilverStripe\CMS\Controllers\CMSPagesController:
FILTER: Filter
ListView: Listenansicht
MENUTITLE: Seiten
TreeView: Baumansicht
@ -117,7 +116,6 @@ de:
SilverStripe\CMS\Controllers\ContentController:
ARCHIVEDSITE: Vorschauversion
ARCHIVEDSITEFROM: 'Site archiviert von'
CMS: CMS
DRAFT: Entwurf
DRAFTSITE: Vorschau-Site
Email: E-Mail
@ -152,7 +150,6 @@ de:
EMPTY: 'Bitte geben Sie ein URL-Segment ein oder drücken Sie auf Abbrechen'
Edit: Bearbeiten
HelpChars: 'Sonderzeichen werden automatisch umgewandelt oder entfernt.'
OK: OK
SilverStripe\CMS\Model\RedirectorPage:
DESCRIPTION: 'Leitet zu einer anderen internen oder externen Seite weiter'
FILE: Datei
@ -276,7 +273,6 @@ de:
many_many_ImageTracking: Bild-Verfolgung
many_many_LinkTracking: Link-Verfolgung
SilverStripe\CMS\Model\SiteTreeFileExtension:
TITLE_INDEX: '#'
TITLE_TYPE: Typ
TITLE_USED_ON: 'Verwendet auf'
SilverStripe\CMS\Model\SiteTreeFileFormFactoryExtension:
@ -313,7 +309,6 @@ de:
ColumnDateLastModified: 'Zuletzt geändert'
ColumnDateLastPublished: 'Zuletzt veröffentlicht'
ColumnProblemType: Problemtyp
ColumnURL: URL
HasBrokenFile: 'Defekter Dateilink'
HasBrokenLink: 'Defekter Link'
HasBrokenLinkAndFile: 'Defekter Link und defekter Dateilink'

View File

@ -23,7 +23,6 @@ de_DE:
UNKNOWN: Unbekannt
WHEN: Wann
SilverStripe\CMS\Controllers\CMSPagesController:
FILTER: Filter
MENUTITLE: Seiten
SilverStripe\CMS\Controllers\CMSSiteTreeFilter_ChangedPages:
Title: 'Bearbeitete Seiten'

View File

@ -10,7 +10,6 @@ el:
Cancel: Άκυρο
ChoosePageType: 'Επιλέξτε τύπο σελίδας'
Create: Δημιουργία
EMAIL: Email
NEWPAGE: 'Νέο {pagetype}'
PAGENOTEXISTS: 'Αυτή η σελίδα δεν υπάρχει'
PAGESALLOPT: 'Όλες οι σελίδες'
@ -51,7 +50,6 @@ el:
Title: 'Όλες οι σελίδες'
SilverStripe\CMS\Controllers\ContentController:
ARCHIVEDSITE: Προεπισκόπιση
Email: Email
INSTALL_SUCCESS: 'Η Εγκατάσταση ήταν επιτυχής!'
LOGGEDINAS: 'Συνδεδεμένος ως'
LOGIN: Σύνδεση
@ -66,7 +64,6 @@ el:
ARCHIVED: Αρχειοθετημένο
SilverStripe\CMS\Forms\SiteTreeURLSegmentField:
Cancel: Άκυρο
Edit: Edit
OK: ΟΚ
SilverStripe\CMS\Model\RedirectorPage:
HEADER: 'Αυτή η σελίδα θα ανακατευθύνει τους χρήστες σε μια άλλη σελίδα'
@ -136,7 +133,6 @@ el:
ColumnDateLastModified: 'Ημερομηνία τελευταίας τροποποίησης'
ColumnDateLastPublished: 'Ημερομηνία τελευταίας δημοσιεύσης'
ColumnProblemType: 'Τύπος προβλήματος'
ColumnURL: URL
HasBrokenFile: 'έχει ανενεργό αρχείο'
HasBrokenLink: 'έχει ανενεργό σύνδεσμο '
HasBrokenLinkAndFile: 'έχει ανενεργό σύνδεσμο και αρχείο'

View File

@ -12,10 +12,10 @@ en:
UNPUBLISHED_PAGES: 'Unpublished %d pages'
UNPUBLISH_PAGES: Unpublish
SilverStripe\CMS\Controllers\CMSMain:
ACCESS: "Access to '{title}' section"
ACCESS: 'Access to ''{title}'' section'
ACCESS_HELP: 'Allow viewing of the section containing page tree and content. View and edit permissions can be handled through page specific dropdowns, as well as the separate "Content permissions".'
ARCHIVE: Archive
ARCHIVEDPAGE: "Archived page '{title}'"
ARCHIVEDPAGE: 'Archived page ''{title}'''
AddNew: 'Add new page'
AddNewButton: 'Add new'
AddPageRestriction: 'Note: Some page types are not allowed for this selection'
@ -28,35 +28,35 @@ en:
ChoosePageParentMode: 'Choose where to create this page'
ChoosePageType: 'Choose page type'
Create: Create
DUPLICATED: "Duplicated '{title}' successfully"
DUPLICATEDWITHCHILDREN: "Duplicated '{title}' and children successfully"
DUPLICATED: 'Duplicated ''{title}'' successfully'
DUPLICATEDWITHCHILDREN: 'Duplicated ''{title}'' and children successfully'
EMAIL: Email
NEWPAGE: 'New {pagetype}'
PAGENOTEXISTS: "This page doesn't exist"
PAGENOTEXISTS: 'This page doesn''t exist'
PAGES: 'Page status'
PAGESALLOPT: 'All pages'
PAGETYPEANYOPT: Any
PAGETYPEOPT: 'Page type'
PAGETYPE_TITLE: '(Page type: {type}) {title}'
PLEASESAVE: "Please Save Page: This page could not be updated because it hasn't been saved yet."
PLEASESAVE: 'Please Save Page: This page could not be updated because it hasn''t been saved yet.'
PUBALLCONFIRM: 'Please publish every page in the site, copying content stage to live'
PUBALLFUN: '"Publish All" functionality'
PUBALLFUN2: 'Pressing this button will do the equivalent of going to every page and pressing "publish". It''s intended to be used after there have been massive edits of the content, such as when the site was first built. For large websites, this task might not be able to run through to completion. In this case, we recommend talking to your developers to create a custom task'
PUBLISHED: "Published '{title}' successfully."
PUBLISHED: 'Published ''{title}'' successfully.'
PUBPAGES: 'Done: Published {count} pages'
PageAdded: 'Successfully created page'
REMOVEDPAGE: "Removed '{title}' from the published site"
REMOVEDPAGEFROMDRAFT: "Removed '{title}' from the draft site"
REMOVEDPAGE: 'Removed ''{title}'' from the published site'
REMOVEDPAGEFROMDRAFT: 'Removed ''{title}'' from the draft site'
REORGANISATIONSUCCESSFUL: 'Reorganised the site tree successfully.'
RESTORE: 'Restore draft'
RESTORED: "Restored '{title}' successfully"
RESTORED: 'Restored ''{title}'' successfully'
RESTORE_DESC: 'Restore the archived version to draft'
RESTORE_TO_ROOT: 'Restore draft at top level'
RESTORE_TO_ROOT_DESC: 'Restore the archived version to draft as a top level page'
ROLLBACK: 'Roll back to this version'
ROLLEDBACKPUBv2: 'Rolled back to published version.'
ROLLEDBACKVERSIONv2: 'Rolled back to version #{version}.'
SAVED: "Saved '{title}' successfully."
SAVED: 'Saved ''{title}'' successfully.'
SAVEDRAFT: Save
SEARCHRESULTS: 'Search results'
SHOW_AS_LIST: 'show as list'
@ -73,8 +73,8 @@ en:
ParentMode_top: 'Top level'
Title: 'Add page'
SilverStripe\CMS\Controllers\CMSPageEditController:
ErrorItemPermissionDenied: "It seems you don't have the necessary permissions to add {ObjectTitle} to a campaign"
ErrorNotFound: "That {Type} couldn't be found"
ErrorItemPermissionDenied: 'It seems you don''t have the necessary permissions to add {ObjectTitle} to a campaign'
ErrorNotFound: 'That {Type} couldn''t be found'
MENUTITLE: 'Edit Page'
SilverStripe\CMS\Controllers\CMSPageHistoryController:
AUTHOR: Author
@ -114,6 +114,7 @@ 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
@ -133,8 +134,11 @@ 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:
@ -214,7 +218,7 @@ en:
LASTPUBLISHED: 'Last published'
LASTSAVED: 'Last saved'
LASTUPDATED: 'Last Updated'
LINKCHANGENOTE: "Changing this page's link will also affect the links of all child pages."
LINKCHANGENOTE: 'Changing this page''s link will also affect the links of all child pages.'
LINKSALREADYUNIQUE: ' {url} is already unique'
LINKSCHANGEDTO: ' changed {url1} -> {url2}'
MENUTITLE: 'Navigation label'

View File

@ -117,7 +117,6 @@ eo:
SilverStripe\CMS\Controllers\ContentController:
ARCHIVEDSITE: 'Antaŭvida versio'
ARCHIVEDSITEFROM: 'Enarkivigita retejo el'
CMS: CMS
DRAFT: Malneta
DRAFTSITE: 'Malneta retejo'
Email: Retpoŝto
@ -178,6 +177,7 @@ eo:
ACCESSANYONE: Iu
ACCESSHEADER: 'Kiuj povas vidi ĉi tiun paĝon?'
ACCESSLOGGEDIN: 'Ensalutintaj uzantoj'
ACCESSONLYMEMBERS: 'Nur ĉi tiuj homoj (elektu el listo)'
ACCESSONLYTHESE: 'Nur ĉi tiuj homoj (elektu el listo)'
ADDEDTODRAFTHELP: 'Paĝo ankoraŭ estas ne publikigita'
ADDEDTODRAFTSHORT: Malneto
@ -205,6 +205,7 @@ eo:
DependtPageColumnLinkType: 'Ligila tipo'
EDITHEADER: 'Kiuj povas redakti ĉi tiun paĝon?'
EDITORGROUPS: 'Grupoj de redaktantoj'
EDITORMEMBERS: 'Redaktantaj membroj'
EDITOR_GROUPS_FIELD_DESC: 'Grupoj kun ĉieaj permesoj: {groupList}'
EDIT_ALL_DESCRIPTION: 'Redakti ajnan paĝon'
EDIT_ALL_HELP: 'Eblo redakti ajnan paĝon en la retejo, senkonsidere de la agordoj en la langeto Aliro. Necesas la permeso "Aliro al sekcio ''Paĝoj'' "'
@ -262,6 +263,7 @@ eo:
URLSegment: URL-segmento
UntitledDependentObject: 'Sentitola {instanceType}'
VIEWERGROUPS: 'Grupoj de vidantoj'
VIEWERMEMBERS: 'Vidantaj membroj'
VIEWER_GROUPS_FIELD_DESC: 'Grupoj kun ĉieaj vidigaj permesoj: {groupList}'
VIEW_ALL_DESCRIPTION: 'Vidigi ajnan paĝon'
VIEW_ALL_HELP: 'Povo vidigi ajnan paĝon en la retejo, senkonsidere la agordaĵojn en la langeto Aliro. Bezonatas la permeso "Aliro al sekcio ''Paĝoj''"'
@ -310,11 +312,12 @@ eo:
many_many_BackLinkTracking: 'Spuri retroligilojn'
many_many_CrossSubsiteLinkTracking: 'Trans-subreteja ligspurado'
many_many_EditorGroups: 'Grupoj de redaktantoj'
many_many_EditorMembers: 'Redaktantaj membroj'
many_many_ImageTracking: 'Spuri Bildojn'
many_many_LinkTracking: 'Spuri Ligilojn'
many_many_ViewerGroups: 'Grupoj de vidantoj'
many_many_ViewerMembers: 'Vidantaj membroj'
SilverStripe\CMS\Model\SiteTreeFileExtension:
TITLE_INDEX: '#'
TITLE_TYPE: Tipo
TITLE_USED_ON: 'Uzita je'
SilverStripe\CMS\Model\SiteTreeFileFormFactoryExtension:
@ -356,7 +359,6 @@ eo:
ColumnDateLastModified: 'Dato de lasta modifo'
ColumnDateLastPublished: 'Dato de lasta publikigo'
ColumnProblemType: Problemtipo
ColumnURL: URL
HasBrokenFile: 'havas rompitan dosieron'
HasBrokenLink: 'havas rompitan ligilon'
HasBrokenLinkAndFile: 'havas rompitan ligilon kaj dosieron'

View File

@ -110,7 +110,6 @@ es:
SilverStripe\CMS\Controllers\ContentController:
ARCHIVEDSITE: 'Vista previa de la versión'
ARCHIVEDSITEFROM: 'Sitio archivado desde'
CMS: CMS
DRAFT: Borrador
DRAFTSITE: 'Sitio en Borrador'
Email: 'Correo electrónico'
@ -211,7 +210,6 @@ es:
METAEXTRA: 'Meta Tags Personalizadas'
MODIFIEDONDRAFTHELP: 'La página tiene cambios sin publicar'
MODIFIEDONDRAFTSHORT: Modificado
MetadataToggle: Metadata
MoreOptions: 'Más opciones'
NOTPUBLISHED: 'No publicado'
OBSOLETECLASS: 'Clase obsoleta'
@ -260,7 +258,6 @@ es:
many_many_ImageTracking: 'Rastreo de Imágenes'
many_many_LinkTracking: 'Rastreo de Enlaces'
SilverStripe\CMS\Model\SiteTreeFileExtension:
TITLE_INDEX: '#'
TITLE_TYPE: Tipo
TITLE_USED_ON: 'Usado en'
SilverStripe\CMS\Model\SiteTreeFileFormFactoryExtension:
@ -294,7 +291,6 @@ es:
ColumnDateLastModified: 'Fecha de última modificación'
ColumnDateLastPublished: 'Fecha de última publicación'
ColumnProblemType: 'Tipo de problema'
ColumnURL: URL
HasBrokenFile: 'tiene un archivo roto'
HasBrokenLink: 'tiene un enlace roto'
HasBrokenLinkAndFile: 'tiene un enlace y archivo roto'

View File

@ -41,8 +41,6 @@ es_AR:
ANCHORVALUE: Anclar
SilverStripe\CMS\Forms\InternalLinkFormFactory:
LINKDESCR: 'Descripción del enlace'
SilverStripe\CMS\Forms\SiteTreeURLSegmentField:
Edit: Edit
SilverStripe\CMS\Model\RedirectorPage:
FILE: Archivo
HEADER: 'Esta página dirigirá al usuarios a otra página'

View File

@ -30,7 +30,6 @@ es_MX:
Create: Crear
DUPLICATED: '''{title}'' se ha duplicado con éxito'
DUPLICATEDWITHCHILDREN: '''{title}'' y sub páginas se han duplicado con éxito'
EMAIL: Email
NEWPAGE: 'Nueva {pagetype}'
PAGENOTEXISTS: 'Esta página no existe'
PAGES: 'Estado de página'
@ -112,10 +111,8 @@ es_MX:
SilverStripe\CMS\Controllers\ContentController:
ARCHIVEDSITE: 'Versión de vista previa'
ARCHIVEDSITEFROM: 'Sito archivado de'
CMS: CMS
DRAFT: Borrador
DRAFTSITE: 'Proyecto del Sitio'
Email: Email
INSTALL_SUCCESS: 'Instalación exitosa!'
InstallFilesDeleted: 'Los archivos de instalación han sido eliminados con éxito.'
InstallSecurityWarning: 'Por razones de seguridad debes eliminar los archivos de instalación, a menos que tengas planeado reinstalar luego (<em>requiere ingresar como administrador</em>). Además, el servidor web ahora solo requiere permisos de escritura sobre el directorio "assets", puedes remover el acceso de escritura a los demás directorios. <a href="{link}" style="text-align: center;">Haz click aquí para eliminar los archivos de instalación.</a>'
@ -146,7 +143,6 @@ es_MX:
Cancel: Cancelar
Edit: Editar
HelpChars: 'Carácteres especiales son automáticamente convertidos o eliminados.'
OK: OK
SilverStripe\CMS\Model\RedirectorPage:
DESCRIPTION: 'Redirecciona a una página interna o a una URL externa'
FILE: Archivo
@ -237,7 +233,6 @@ es_MX:
many_many_ImageTracking: 'Seguir la Imagen'
many_many_LinkTracking: 'Seguir el Enlace'
SilverStripe\CMS\Model\SiteTreeFileExtension:
TITLE_INDEX: '#'
TITLE_TYPE: Tipo
TITLE_USED_ON: 'Usado en'
SilverStripe\CMS\Model\VirtualPage:
@ -263,7 +258,6 @@ es_MX:
ColumnDateLastModified: 'Fecha de última modificación'
ColumnDateLastPublished: 'Fecha de última publicación'
ColumnProblemType: 'Tipo de problema'
ColumnURL: URL
HasBrokenFile: 'tiene un archivo defectuoso'
HasBrokenLink: 'tiene un enlace roto'
HasBrokenLinkAndFile: 'tiene un enlace roto y un archivo defectuoso'

View File

@ -17,7 +17,6 @@ et_EE:
ChoosePageParentMode: 'Valige, kuhu see leht luua'
ChoosePageType: 'Valige lehe tüüp'
Create: Loo
EMAIL: Email
NEWPAGE: 'Uus {pagetype}'
PAGENOTEXISTS: 'Antud lehekülge ei eksisteeri'
PAGESALLOPT: 'Kõik lehed'
@ -59,7 +58,6 @@ et_EE:
UNKNOWN: Tundmatu
VIEW: vaade
VIEWINGVERSION: 'Hetkel kuvatud versioon {version}.'
WHEN: When
SilverStripe\CMS\Controllers\CMSPageSettingsController:
MENUTITLE: 'Muuda lehte'
SilverStripe\CMS\Controllers\CMSPagesController:
@ -74,7 +72,6 @@ et_EE:
ARCHIVEDSITEFROM: 'Arhiveeritud sait asukohast'
DRAFT: Mustand
DRAFTSITE: 'Mustandi sait'
Email: Email
INSTALL_SUCCESS: 'Paigaldus oli edukas!'
InstallFilesDeleted: 'Installifailid on edukalt kustutatud.'
InstallSecurityWarning: 'Turvalisuse põhjustel soovitame nüüd installifailid kustutada, juhul kui te ei plaani hiljem uuesti installida (<em>nõuab administraatorina sisselogimist, vt ülevalt</em>). Edaspidi vajab veebiserver juurdepääsu kirjutamiseks ainult kaustale "assets". Võite eemaldada kõigi teiste kaustade kirjutusõigused. <a href="{link}" style="text-align: center;">Klõpsake siin installifailide kustutamiseks.</a>'
@ -212,7 +209,6 @@ et_EE:
ColumnDateLastModified: 'Viimane muutmiskuupäev'
ColumnDateLastPublished: 'Viimane avaldamiskuupäev'
ColumnProblemType: 'Probleemi tüüp'
ColumnURL: URL
HasBrokenFile: 'sisaldab katkist faili'
HasBrokenLink: 'sisaldab katkist linki'
HasBrokenLinkAndFile: 'sisaldab katkist linki ja faili'

View File

@ -106,7 +106,6 @@ fa_IR:
SilverStripe\CMS\Controllers\ContentController:
ARCHIVEDSITE: 'نسخه پیش‌نمایش'
ARCHIVEDSITEFROM: 'سایت بایگانی شده از'
CMS: CMS
DRAFT: پیش‌نویس
DRAFTSITE: 'سایت پیش‌نویس'
Email: ایمیل
@ -234,7 +233,6 @@ fa_IR:
many_many_ImageTracking: 'ردیابی تصویر'
many_many_LinkTracking: 'ردیابی پیوند'
SilverStripe\CMS\Model\SiteTreeFileExtension:
TITLE_INDEX: '#'
TITLE_TYPE: نوع
TITLE_USED_ON: 'استفاده شده در'
SilverStripe\CMS\Model\SiteTreeLink:

View File

@ -115,7 +115,6 @@ fi:
SilverStripe\CMS\Controllers\ContentController:
ARCHIVEDSITE: 'Esikatselu versio'
ARCHIVEDSITEFROM: 'Arkistoitu sivusto lähteestä:'
CMS: CMS
DRAFT: Luonnos
DRAFTSITE: Luonnossivusto
Email: Sähköposti
@ -150,7 +149,6 @@ fi:
EMPTY: 'Ole hyvä ja anna URL-osoitteen osio tai napsauta Peruuta'
Edit: Muokkaa
HelpChars: 'Erikoismerkit muunnetaan tai poistetaan automaattisesti.'
OK: OK
SilverStripe\CMS\Model\RedirectorPage:
DESCRIPTION: 'Edelleenohjaa toiselle sisäiselle sivulle tai ulkoiseen URL-osoitteeseen'
FILE: Tiedosto
@ -274,7 +272,6 @@ fi:
many_many_ImageTracking: 'Kuvan seuranta'
many_many_LinkTracking: 'Linkin seuranta'
SilverStripe\CMS\Model\SiteTreeFileExtension:
TITLE_INDEX: '#'
TITLE_TYPE: Tyyppi
TITLE_USED_ON: 'Käytössä:'
SilverStripe\CMS\Model\SiteTreeFileFormFactoryExtension:

View File

@ -114,7 +114,6 @@ fi_FI:
SilverStripe\CMS\Controllers\ContentController:
ARCHIVEDSITE: 'Esikatselu versio'
ARCHIVEDSITEFROM: 'Arkistoitu sivusto lähteestä:'
CMS: CMS
DRAFT: Luonnos
DRAFTSITE: Luonnossivusto
Email: Sähköposti
@ -147,7 +146,6 @@ fi_FI:
Cancel: Peruuta
Edit: Muokkaa
HelpChars: 'Erikoismerkit muunnetaan tai poistetaan automaattisesti.'
OK: OK
SilverStripe\CMS\Model\RedirectorPage:
DESCRIPTION: 'Edelleenohjaa toiselle sisäiselle sivulle tai ulkoiseen URL-osoitteeseen'
HEADER: 'Tämä sivu ohjaa käyttäjän toiselle sivulle'
@ -262,7 +260,6 @@ fi_FI:
many_many_ImageTracking: 'Kuvan seuranta'
many_many_LinkTracking: 'Linkin seuranta'
SilverStripe\CMS\Model\SiteTreeFileExtension:
TITLE_INDEX: '#'
TITLE_TYPE: Tyyppi
TITLE_USED_ON: 'Käytössä:'
SilverStripe\CMS\Model\SiteTreeFileFormFactoryExtension:

View File

@ -19,8 +19,6 @@ fo:
Password: Loyniorð
SilverStripe\CMS\Forms\InternalLinkFormFactory:
LINKDESCR: 'Leinku frágreiðing'
SilverStripe\CMS\Forms\SiteTreeURLSegmentField:
Edit: Edit
SilverStripe\CMS\Model\RedirectorPage:
FILE: Fíla
REDIRECTTO: 'Víðarisend til'

View File

@ -14,7 +14,6 @@ fr:
SilverStripe\CMS\Controllers\CMSMain:
ACCESS: "Accès à la section «\_{title}\_»"
ACCESS_HELP: 'Autoriser la visualisation de la section contenant l''arborescence et le contenu. Les autorisations de voir ou modifier le contenu peuvent être gérées dans les menus déroulants spécifiques à chaque page, ainsi que dans les "permissions de contenu".'
ARCHIVE: Archive
ARCHIVEDPAGE: 'Page ''{title}'' archivée'
AddNew: 'Ajouter une nouvelle page'
AddNewButton: Ajouter
@ -30,7 +29,6 @@ fr:
Create: Créer
DUPLICATED: '''{title}'' dupliqué avec succès'
DUPLICATEDWITHCHILDREN: '''{title}'' et ses enfants dupliqués avec succès'
EMAIL: Email
NEWPAGE: 'Nouveau {pagetype}'
PAGENOTEXISTS: 'Cette page n''existe pas'
PAGES: 'Statut de la page'
@ -96,7 +94,6 @@ fr:
SilverStripe\CMS\Controllers\CMSPagesController:
FILTER: Filtrer
ListView: 'Affichage en liste'
MENUTITLE: Pages
TreeView: Arborescence
SilverStripe\CMS\Controllers\CMSSiteTreeFilter_ChangedPages:
Title: 'Page modifiées'
@ -115,10 +112,8 @@ fr:
SilverStripe\CMS\Controllers\ContentController:
ARCHIVEDSITE: 'Aperçu de cette version'
ARCHIVEDSITEFROM: 'Site archivé depuis'
CMS: CMS
DRAFT: Brouillon
DRAFTSITE: 'Site Brouillon'
Email: Email
INSTALL_SUCCESS: 'Installation terminée!'
InstallFilesDeleted: 'Les fichiers d''installation ont été supprimés avec succès'
InstallSecurityWarning: "Pour des raisons de sécurité vous devriez maintenant supprimer les fichiers dinstallation, à moins que vous nayez prévu de réinstaller le système ultérieurement (<em>ce qui nécessite de se connecter comme administrateur, voir plus haut</em>). Désormais, votre serveur naura besoin de droits d'accès en écriture que pour le dossier «\_assets\_», vous pouvez donc les révoquer pour tous les autres dossiers. <a href=\"{link}\" style=\"text-align: center;\">Cliquez ici pour supprimer les fichiers dinstallation.</a>"
@ -150,13 +145,11 @@ fr:
EMPTY: 'Entrez un segment d''URL ou cliquez sur annuler'
Edit: Editer
HelpChars: 'Les caractères spéciaux sont automatiquement convertis ou supprimés.'
OK: OK
SilverStripe\CMS\Model\RedirectorPage:
DESCRIPTION: 'Redirige vers une page interne ou une URL externe'
FILE: Fichier
HEADER: 'Cette page va rediriger les utilisateurs vers une autre page'
OTHERURL: 'URL d''un autre site web'
PLURALNAME: 'Base Pages'
PLURALS:
one: 'Une page de redirection'
other: '{count} pages de redirection'
@ -213,7 +206,6 @@ fr:
LINKSALREADYUNIQUE: ' {url} est déjà unique'
LINKSCHANGEDTO: '{url1} changée pour {url2}'
MENUTITLE: 'Titre dans les menus'
METADESC: 'Meta Description'
METADESCHELP: 'Les moteurs de recherche reprennent cette description dans leurs résultats (bien quelle nait pas dinfluence sur le classement).'
METAEXTRA: 'Balises Méta personnalisées'
MODIFIEDONDRAFTHELP: 'Des changements effectués sur cette page n''''ont pas été publiés'
@ -233,7 +225,6 @@ fr:
PARENTTYPE_SUBPAGE: 'Sous-page d''une page parente (choisir en-dessous) '
PERMISSION_GRANTACCESS_DESCRIPTION: 'Gérer les droits d''accès au contenu'
PERMISSION_GRANTACCESS_HELP: 'Autoriser le réglage de restrictions spécifiques dans la section "Pages".'
PLURALNAME: Pages
PLURALS:
many: '{count} pages'
one: 'Une page'
@ -245,7 +236,6 @@ fr:
REORGANISE_HELP: 'Réorganiser les pages du site par glisser & déposer.'
SHOWINMENUS: 'Afficher dans les menus ?'
SHOWINSEARCH: 'Afficher dans les recherches ?'
SINGULARNAME: Page
TABBEHAVIOUR: Comportement
TABCONTENT: 'Contenu principal'
TABDEPENDENT: 'Pages dépendantes'
@ -267,28 +257,21 @@ fr:
db_Sort: Tri
db_Title: Titre
db_URLSegment: 'Segment d''URL'
db_Version: Version
has_one_Parent: 'Page parente'
many_many_BackLinkTracking: 'Suivi des liens retour'
many_many_ImageTracking: 'Suivi des images'
many_many_LinkTracking: 'Suivi des Liens'
SilverStripe\CMS\Model\SiteTreeFileExtension:
TITLE_INDEX: '#'
TITLE_TYPE: Type
TITLE_USED_ON: 'Utilisé dans'
SilverStripe\CMS\Model\SiteTreeFileFormFactoryExtension:
USAGE: Usage
SilverStripe\CMS\Model\SiteTreeLink:
PLURALNAME: 'Liens Site Tree'
SINGULARNAME: 'Lien Site Tree'
has_one_Parent: Parent
SilverStripe\CMS\Model\VirtualPage:
CHOOSE: 'Page liée'
DESCRIPTION: 'Affiche le contenu d''une autre page'
EditLink: éditer
HEADER: 'Ceci est une page virtuelle'
HEADERWITHLINK: 'Ceci est une page virtuelle dupliquant le contenu de "{title}" ({link})'
PLURALNAME: 'Base Pages'
PLURALS:
one: 'Une page virtuelle'
other: '{count} pages virtuelles'
@ -307,7 +290,6 @@ fr:
ColumnDateLastModified: 'Date de la dernière modification'
ColumnDateLastPublished: 'Date de la dernière publication'
ColumnProblemType: 'Type de problème'
ColumnURL: URL
HasBrokenFile: 'comporte un fichier brisé'
HasBrokenLink: 'comporte un lien brisé'
HasBrokenLinkAndFile: 'comporte un lien et un fichier brisés'

View File

@ -70,8 +70,6 @@ gl_ES:
ANCHORVALUE: Áncora
SilverStripe\CMS\Forms\InternalLinkFormFactory:
LINKDESCR: 'Descrición da ligazón'
SilverStripe\CMS\Forms\SiteTreeURLSegmentField:
Edit: Edit
SilverStripe\CMS\Model\RedirectorPage:
FILE: Ficheiro
HEADER: 'Esta páxina redirixirá os usuarios a outra páxina'

View File

@ -87,7 +87,6 @@ he_IL:
SilverStripe\CMS\Forms\InternalLinkFormFactory:
LINKDESCR: 'תאור הקישור'
SilverStripe\CMS\Forms\SiteTreeURLSegmentField:
Edit: Edit
HelpChars: ' תווים מיוחדים מומרים אוטומטית או שהם מוסרים.'
SilverStripe\CMS\Model\RedirectorPage:
FILE: קובץ

View File

@ -25,7 +25,6 @@ hr:
Create: Kreiraj
DUPLICATED: 'Dupliciran ''{title}'' uspješno'
DUPLICATEDWITHCHILDREN: 'Dupliciran ''{title}'' i podstranice uspješno'
EMAIL: Email
NEWPAGE: 'Novi {pagetype}'
PAGENOTEXISTS: 'Stranica ne postoji'
PAGES: 'Status stranice'
@ -87,7 +86,6 @@ hr:
SilverStripe\CMS\Controllers\CMSPageSettingsController:
MENUTITLE: 'Uredi stranicu'
SilverStripe\CMS\Controllers\CMSPagesController:
FILTER: Filter
ListView: Lista
MENUTITLE: Stranice
TreeView: Stablo
@ -108,10 +106,8 @@ hr:
SilverStripe\CMS\Controllers\ContentController:
ARCHIVEDSITE: 'Pregledna verzija'
ARCHIVEDSITEFROM: 'Arhivirana stranica iz'
CMS: CMS
DRAFT: Predložak
DRAFTSITE: Predložak
Email: Email
INSTALL_SUCCESS: 'Instalacija uspješna!'
InstallFilesDeleted: 'Instalacijske datoteke su uspješno obrisane.'
InstallSecurityWarning: 'Zbog sigurnosnih razloga trebali bi ste obrisati instalacijske datoteke, osima ako planirate reinstalaciju kasnije (<em>zahtjeva administracijsku prijavu, pogledaj iznad</em>). Web server sada treba samo dozvole pisanja na "assets" direktorij, pa možete maknuti pravo pisanja sa svih drugih direktorija. <a href="{link}" style="text-align: center;">kliknite ovjde za brisanje instalacijskih datoteka.</a>'
@ -142,7 +138,6 @@ hr:
Cancel: Odustani
Edit: Uredi
HelpChars: 'Posebni znakovi se automatski pretvaraju ili uklanjaju.'
OK: OK
SilverStripe\CMS\Model\RedirectorPage:
DESCRIPTION: 'Preusmjerava na internu stranicu ili vanjski URL'
FILE: Datoteka
@ -245,7 +240,6 @@ hr:
many_many_ImageTracking: 'Praćenje slika'
many_many_LinkTracking: 'Praćenje linkova'
SilverStripe\CMS\Model\SiteTreeFileExtension:
TITLE_INDEX: '#'
TITLE_TYPE: Tip
TITLE_USED_ON: 'Korišteno na'
SilverStripe\CMS\Model\SiteTreeLink:

View File

@ -96,7 +96,6 @@ hu:
Cancel: Mégsem
Edit: Szerkeszt
HelpChars: 'A speciális karakterek automatikusan konvertálódnak vagy törlődnek.'
OK: OK
SilverStripe\CMS\Model\RedirectorPage:
FILE: Fájl
HEADER: 'Ez az oldal egy másik oldalra fogja írányítani a felhasználókat'

View File

@ -19,7 +19,6 @@ id:
Create: Buat
DUPLICATED: '''{title}'' berhasil diduplikasi'
DUPLICATEDWITHCHILDREN: '''{title}'' dan turunannya berhasil diduplikasi'
EMAIL: Email
NEWPAGE: '{pagetype} baru'
PAGENOTEXISTS: 'Laman ini tidak ada'
PAGESALLOPT: 'Semua laman'
@ -80,10 +79,8 @@ id:
SilverStripe\CMS\Controllers\ContentController:
ARCHIVEDSITE: 'Versi pratinjau'
ARCHIVEDSITEFROM: 'Arsip dari'
CMS: CMS
DRAFT: Draf
DRAFTSITE: 'Draf situs'
Email: Email
INSTALL_SUCCESS: 'Penginstalan berhasil!'
InstallFilesDeleted: 'Berkas penginstalan telah berhasil dihapus.'
InstallSecurityWarning: 'Untuk alasan keamanan, sekarang Anda perlu menghapus berkas-berkas penginstalan, kecuali Anda berencana untuk melakukan penginstalan ulang lagi (<em>memerlukan login admin, lihat di atas</em>). Selanjutnya server juga memerlukan akses tulis ke folder "assets", Anda dapat mencabut akses tulis dari folder lainnya. <a href="{link}" style="text-align: center;">Klik di sini untuk menghapus berkas-berkas penginstalan.</a>'
@ -109,9 +106,7 @@ id:
LINKDESCR: 'Deskripsi tautan'
SilverStripe\CMS\Forms\SiteTreeURLSegmentField:
Cancel: Batal
Edit: Edit
HelpChars: 'Karakter khusus akan dikonversi secara otomatis atau dihapus.'
OK: OK
SilverStripe\CMS\Model\RedirectorPage:
FILE: Berkas
HEADER: 'Laman ini akan mengarahkan pengguna ke laman lain'
@ -166,7 +161,6 @@ id:
METAEXTRA: 'Penanda Meta'
MODIFIEDONDRAFTHELP: 'Laman memiliki perubahan yang tidak terbit'
MODIFIEDONDRAFTSHORT: Diubah
MetadataToggle: Metadata
MoreOptions: 'Pilihan lain'
NOTPUBLISHED: 'Tidak diterbitkan'
OBSOLETECLASS: 'Jenis laman {type} ini sudah usang. Menyimpannya akan memperbarui jenisnya dan Anda kemungkinan akan kehilangan data'
@ -216,7 +210,6 @@ id:
SilverStripe\CMS\Model\VirtualPage:
CHOOSE: 'Laman Tertaut'
DESCRIPTION: 'Tampilkan konten laman lain'
EditLink: edit
HEADER: 'Ini adalah laman virtual'
HEADERWITHLINK: 'Ini adalah laman virtual dengan konten salinan dari "{title}" ({link})'
PLURALNAME: 'Laman Dasar'
@ -235,7 +228,6 @@ id:
ColumnDateLastModified: 'Tanggal terakhir modifikasi'
ColumnDateLastPublished: 'Tanggal terbit terakhir'
ColumnProblemType: 'Jenis masalah'
ColumnURL: URL
HasBrokenFile: 'terdapat berkas yang rusak'
HasBrokenLink: 'terdapat tautan yang rusak'
HasBrokenLinkAndFile: 'terdapat berkas dan tautan yang rusak'

View File

@ -5,7 +5,6 @@ id_ID:
CANT_REORGANISE: 'Anda tidak diijinkan mengubah laman Tingkat Atas. Pengubahan Anda tidak tersimpan.'
Cancel: Batal
Create: Buat
EMAIL: Email
PAGETYPEANYOPT: Semua
PLEASESAVE: 'Mohon Simpan Laman: Laman ini tidak dapat diperbarui karena belum disimpan.'
REORGANISATIONSUCCESSFUL: 'Pengaturan ulang struktur situs berhasil.'
@ -20,7 +19,6 @@ id_ID:
SilverStripe\CMS\Controllers\CMSPagesController:
FILTER: Saring
SilverStripe\CMS\Controllers\ContentController:
Email: Email
LOGOUT: Keluar
NOTLOGGEDIN: 'Belum masuk'
Password: 'Kata kunci'
@ -31,7 +29,6 @@ id_ID:
LINKDESCR: 'Deskripsi tautan'
SilverStripe\CMS\Forms\SiteTreeURLSegmentField:
Cancel: Batal
Edit: Edit
SilverStripe\CMS\Model\RedirectorPage:
FILE: Berkas
REDIRECTTOEXTERNAL: 'Situs lain'
@ -46,7 +43,6 @@ id_ID:
db_Title: Judul
SilverStripe\CMS\Reports\BrokenLinksReport:
Any: Semua
ColumnURL: URL
SilverStripe\CMS\Search\SearchForm:
FILTERLABELTEXT: Cari
GO: Lanjut

View File

@ -30,7 +30,6 @@ it:
Create: Crea
DUPLICATED: '''{title}'' duplicato correttamente'
DUPLICATEDWITHCHILDREN: '''{title}'' e figlie duplicate con successo'
EMAIL: Email
NEWPAGE: 'Nuova {pagetype}'
PAGENOTEXISTS: 'Questa pagina non esiste'
PAGES: 'Stato pagina'
@ -117,10 +116,8 @@ it:
SilverStripe\CMS\Controllers\ContentController:
ARCHIVEDSITE: 'Anteprima versione'
ARCHIVEDSITEFROM: 'Sito archiviato da'
CMS: CMS
DRAFT: Bozza
DRAFTSITE: 'Sito Bozza'
Email: Email
INSTALL_SUCCESS: 'Installazione conclusa con successo!'
InstallFilesDeleted: 'I file di installazione sono stati eliminati con successo.'
InstallSecurityWarning: 'Ora per motivi di sicurezza devi eliminare i file di installazione, a meno di non aver pianificato una nuova installazione (<em>richiede accesso da amministratore, vedi sopra</em>). Inoltre il server web richiede permessi di scrittura per la sola cartella "assets", puoi quindi rimuovere i permessi di scrittura da tutte le altre cartelle. <a href="{link}" style="text-align: center;">Clicca qui per cancellare i file di installazione.</a>'
@ -132,7 +129,6 @@ it:
NOTLOGGEDIN: 'Non connesso'
PUBLISHED: Pubblicato
PUBLISHEDSITE: 'Sito pubblicato'
Password: Password
PostInstallTutorialIntro: 'Questo sito web è una versione semplificata di un sito SilverStripe 3. Per estenderlo, dai un''occhiata a {link}.'
StartEditing: 'Puoi iniziare a modificare i contenuti del sito aprendo <a href="{link}">il CMS</a>.'
UNVERSIONEDPREVIEW: Anteprima
@ -152,10 +148,8 @@ it:
EMPTY: 'Inserire un segmento URL o cliccare annulla'
Edit: Modifica
HelpChars: 'I caratteri speciali sono automaticamente convertiti o eliminati.'
OK: OK
SilverStripe\CMS\Model\RedirectorPage:
DESCRIPTION: 'Reinoltra ad una pagina interna o una URL esterna'
FILE: File
HEADER: 'Questa pagina redirigerà gli utenti su un''altra'
OTHERURL: 'Altro indirizzo del sito web'
PLURALNAME: 'Pagine Base'
@ -197,7 +191,6 @@ it:
DEFAULTCONTACTCONTENT: '<p>Puoi riempire questa pagina con i tuoi contenuti, oppure cancellarla e creare le tue pagine.</p>'
DEFAULTCONTACTTITLE: Contattaci
DEFAULTHOMECONTENT: '<p>Benvenuto in SilverStripe! Questa è la home page di default. Puoi modificare questa pagina direttamente <a href="admin/">dal CMS</a>.</p><p>Puoi accedere alla <a href="http://docs.silverstripe.org">documentazione per gli sviluppatori</a> o partire dalle <a href="http://www.silverstripe.org/learn/lessons">lezioni di SilverStripe</a>.</p>'
DEFAULTHOMETITLE: Home
DEPENDENT_NOTE: 'Le seguenti pagine dipendono da questa pagina. Queste includono pagine virtuali, pagine di redirezione e pagine con link nel contenuto.'
DESCRIPTION: 'Pagina di contenuto generico'
DependtPageColumnLinkType: 'Tipo di link'
@ -280,7 +273,6 @@ it:
many_many_ImageTracking: 'Monitoraggio immagine'
many_many_LinkTracking: 'Monitoraggio link'
SilverStripe\CMS\Model\SiteTreeFileExtension:
TITLE_INDEX: '#'
TITLE_TYPE: Tipo
TITLE_USED_ON: 'Usato in'
SilverStripe\CMS\Model\SiteTreeFileFormFactoryExtension:
@ -319,7 +311,6 @@ it:
ColumnDateLastModified: 'Data ultima modifica'
ColumnDateLastPublished: 'Data ultima pubblicazione'
ColumnProblemType: 'Tipo problema'
ColumnURL: URL
HasBrokenFile: 'ha un file non funzionante'
HasBrokenLink: 'ha un link non funzionante'
HasBrokenLinkAndFile: 'ha link e file non funzionanti'

View File

@ -88,7 +88,6 @@ ja:
SilverStripe\CMS\Controllers\ContentController:
ARCHIVEDSITE: プレビュー版
ARCHIVEDSITEFROM: からアーカイブされたサイト
CMS: CMS
DRAFT: 下書き
DRAFTSITE: 下書きサイト
Email: メール
@ -237,7 +236,6 @@ ja:
ColumnDateLastModified: 最終更新日
ColumnDateLastPublished: 最終公開日
ColumnProblemType: 問題の種類
ColumnURL: URL
HasBrokenFile: 壊れたファイルがあります
HasBrokenLink: リンクが壊れています
HasBrokenLinkAndFile: リンク切れのファイルがあります

View File

@ -76,7 +76,6 @@ ja_JP:
SilverStripe\CMS\Forms\InternalLinkFormFactory:
LINKDESCR: リンクの説明
SilverStripe\CMS\Forms\SiteTreeURLSegmentField:
Edit: Edit
HelpChars: 特殊文字は自動的に変換されたか取り除かれました
SilverStripe\CMS\Model\RedirectorPage:
FILE: ファイル

View File

@ -74,7 +74,6 @@ ko:
SilverStripe\CMS\Controllers\ContentController:
ARCHIVEDSITE: '미리보기 버젼'
ARCHIVEDSITEFROM: '로부터 보관된 사이트'
CMS: CMS
DRAFT: 초안
DRAFTSITE: '초안 사이트'
Email: 이메일
@ -101,7 +100,6 @@ ko:
Cancel: 취소
Edit: 편집
HelpChars: '특수 문자는 자동으로 변환되었는지 제거되었습니다'
OK: OK
SilverStripe\CMS\Model\RedirectorPage:
HEADER: '이 페이지는 다른 페이지로 사용자들을 이동시킵니다.'
OTHERURL: '다른 웹사이트 URL'
@ -217,7 +215,6 @@ ko:
ColumnDateLastModified: '최종 수정일'
ColumnDateLastPublished: '최종 게시일'
ColumnProblemType: '문제 유형'
ColumnURL: URL
HasBrokenFile: '깨진 파일이 있습니다'
HasBrokenLink: '링크가 손상되었습니다'
HasBrokenLinkAndFile: '손상된 링크와 파일이 있습니다'

View File

@ -25,8 +25,6 @@ lv:
ANCHORVALUE: Enkurs
SilverStripe\CMS\Forms\InternalLinkFormFactory:
LINKDESCR: 'Saites apraksts'
SilverStripe\CMS\Forms\SiteTreeURLSegmentField:
Edit: Edit
SilverStripe\CMS\Model\RedirectorPage:
FILE: Fails
HEADER: 'Šī lapa novirzīs lietotājus uz citu lapu '

View File

@ -77,7 +77,6 @@ mi:
SilverStripe\CMS\Controllers\ContentController:
ARCHIVEDSITE: 'Putanga Arokite'
ARCHIVEDSITEFROM: 'Pae putumōhio mai i'
CMS: CMS
DRAFT: Hukihuki
DRAFTSITE: 'Pae Hukihuki'
Email: Īmēra

View File

@ -10,8 +10,6 @@ ms:
Password: 'Kata Laluan'
SilverStripe\CMS\Forms\InternalLinkFormFactory:
LINKDESCR: 'Deskripsi pautan'
SilverStripe\CMS\Forms\SiteTreeURLSegmentField:
Edit: Edit
SilverStripe\CMS\Model\RedirectorPage:
FILE: Fail
HEADER: 'Mukasurat ini akan mengalih pengguna ke mukasurat lain'

View File

@ -128,7 +128,6 @@ nb:
Cancel: Avbryt
Edit: Rediger
HelpChars: 'Spesialtegn blir automatisk konvertert eller fjernet.'
OK: OK
SilverStripe\CMS\Model\RedirectorPage:
FILE: Fil
HEADER: 'Denne siden vil omdirigere brukere til en annen side'
@ -185,7 +184,6 @@ nb:
METAEXTRA: 'Egendefinerte meta-elementer'
MODIFIEDONDRAFTHELP: 'Siden har upubliserte forandringer'
MODIFIEDONDRAFTSHORT: Endret
MetadataToggle: Metadata
MoreOptions: 'Flere alternativer'
NOTPUBLISHED: 'Ikke publisert'
OBSOLETECLASS: 'Denne siden er av den utdaterte typen {type}. Lagring vil tilbakestille typen og kan medføre tap av informasjon.'
@ -248,7 +246,6 @@ nb:
ColumnDateLastModified: 'Dato sist endret'
ColumnDateLastPublished: 'Dato sist publisert'
ColumnProblemType: Problemtype
ColumnURL: URL
HasBrokenFile: 'har ødelagt fil'
HasBrokenLink: 'har ødelagt lenke'
HasBrokenLinkAndFile: 'har ødelagt lenke og fil'

View File

@ -10,8 +10,6 @@ ne:
Password: पासओड
SilverStripe\CMS\Forms\InternalLinkFormFactory:
LINKDESCR: 'लिन्क विवरण'
SilverStripe\CMS\Forms\SiteTreeURLSegmentField:
Edit: Edit
SilverStripe\CMS\Model\RedirectorPage:
FILE: फाइल
HASBEENSETUP: 'कुनै पनि पुनर्निर्देशनबिना नै एउटा पुनर्निदेशन पृष्ठ सेटअप गरियो । '

View File

@ -19,13 +19,13 @@ nl:
AddNew: 'Voeg nieuwe pagina toe'
AddNewButton: Nieuw...
AddPageRestriction: 'Opmerking: Sommige paginatypes zijn niet toegestaan voor deze selectie'
ArchiveWarning: 'Let op: de publicatie van deze pagina wordt ingetrokken voordat hij wordt gearchiveerd.\n\nWeet u zeker dat u wilt doorgaan?'
ArchiveWarningWithCampaigns: 'Let op: de publicatie van deze pagina wordt ingetrokken en de pagina wordt automatisch uit alle {NumCampaigns} campagnes verwijderd voordat deze wordt gearchiveerd.\n\nWeet u zeker dat u wilt doorgaan?'
ArchiveWarningWithChildren: 'Let op: deze pagina en alle bijbehorende subpagina''s worden van de gepubliceerde site verwijderd voordat ze worden gearchiveerd.\n\nWeet u zeker dat u wilt doorgaan?'
ArchiveWarningWithChildrenAndCampaigns: 'Let op: deze pagina en alle bijbehorende subpagina''s worden automatisch van de gepubliceerde site én uit alle {NumCampaigns} campagnes verwijderd, voordat ze worden gearchiveerd.\n\nWeet u zeker dat u wilt doorgaan?'
CANT_REORGANISE: 'U hebt onvoldoende rechten om pagina''s op het topniveau te wijzigen. Uw wijziging is niet opgeslagen.'
ArchiveWarning: 'Let op: de publicatie van deze pagina wordt ingetrokken voordat hij wordt gearchiveerd.\n\nWeet je zeker dat je wil doorgaan?'
ArchiveWarningWithCampaigns: 'Let op: de publicatie van deze pagina wordt ingetrokken en de pagina wordt automatisch uit alle {NumCampaigns} campagnes verwijderd voordat deze wordt gearchiveerd.\n\nWeet je zeker dat je wil doorgaan?'
ArchiveWarningWithChildren: 'Let op: deze pagina en alle bijbehorende subpagina''s worden van de gepubliceerde site verwijderd voordat ze worden gearchiveerd.\n\nWeet je zeker dat je wil doorgaan?'
ArchiveWarningWithChildrenAndCampaigns: 'Let op: deze pagina en alle bijbehorende subpagina''s worden automatisch van de gepubliceerde site én uit alle {NumCampaigns} campagnes verwijderd, voordat ze worden gearchiveerd.\n\nWeet je zeker dat je wil doorgaan?'
CANT_REORGANISE: 'Je hebt onvoldoende rechten om pagina''s op het topniveau te wijzigen. Je wijziging is niet opgeslagen.'
Cancel: Annuleren
ChoosePageParentMode: 'Kies waar u deze pagina wilt aanmaken'
ChoosePageParentMode: 'Kies waar je deze pagina wilt aanmaken'
ChoosePageType: 'Kies een pagina type'
Create: Aanmaken
DUPLICATED: '''{title}'' met succes gedupliceerd'
@ -79,7 +79,7 @@ nl:
SilverStripe\CMS\Controllers\CMSPageHistoryController:
AUTHOR: Auteur
COMPAREMODE: 'Vergelijken (selecteer 2)'
COMPARINGVERSION: 'U vergelijkt versie {version1} en {version2}.'
COMPARINGVERSION: 'Vergelijking versie {version1} en {version2}.'
MENUTITLE: Geschiedenis
MULTISELECT: 'Bulk acties'
NOTPUBLISHED: 'Niet gepubliceerd'
@ -89,13 +89,12 @@ nl:
SHOWUNPUBLISHED: 'Toon nog niet gepubliceerde versies'
UNKNOWN: Onbekend
VIEW: toon
VIEWINGLATEST: 'U bekijkt nu de laatste versie.'
VIEWINGVERSION: 'U bekijkt nu versie {version}.'
VIEWINGLATEST: 'Je bekijkt nu de laatste versie.'
VIEWINGVERSION: 'Je bekijkt nu versie {version}.'
WHEN: Wanneer
SilverStripe\CMS\Controllers\CMSPageSettingsController:
MENUTITLE: 'Bewerk pagina'
SilverStripe\CMS\Controllers\CMSPagesController:
FILTER: Filter
ListView: Lijstweergave
MENUTITLE: 'Pagina''s'
TreeView: Boomstructuur
@ -116,7 +115,6 @@ nl:
SilverStripe\CMS\Controllers\ContentController:
ARCHIVEDSITE: 'Voorbeeld van versie bekijken'
ARCHIVEDSITEFROM: 'Gearchiveerde site van'
CMS: CMS
DRAFT: Concept
DRAFTSITE: 'Concept site'
Email: E-mail
@ -133,7 +131,7 @@ nl:
PUBLISHEDSITE: 'Gepubliceerde site'
Password: Wachtwoord
PostInstallTutorialIntro: 'Deze website is een eenvoudige versie van een SilverStripe 3 website. Voor uitbreidingen kunt u hier een kijkje nemen: {link}.'
StartEditing: 'U kunt de inhoud aanpassen via <a href="{link}">het CMS</a>.'
StartEditing: 'Je kan de inhoud aanpassen via <a href="{link}">het CMS</a>.'
UNVERSIONEDPREVIEW: Voorbeeld
UnableDeleteInstall: 'De installatiebestanden konden niet verwijderd worden. Verwijder onderstaande bestanden handmatig:'
VIEWPAGEIN: 'Bekijk pagina in:'
@ -151,7 +149,6 @@ nl:
EMPTY: 'Vul een URL segment in of klik op annuleren'
Edit: Bewerken
HelpChars: 'Speciale tekens worden automatische omgezet of verwijderd.'
OK: OK
SilverStripe\CMS\Model\RedirectorPage:
DESCRIPTION: 'Stuurt door naar een interne pagina of een externe URL'
FILE: Bestand
@ -190,12 +187,11 @@ nl:
BUTTONUNPUBLISHDESC: 'Deze pagina verwijderen uit de gepubliceerde site'
Comments: Commentaar
Content: Inhoud
DEFAULTABOUTCONTENT: '<p>U kunt deze pagina vullen met eigen tekst, of deze verwijderen en nieuwe pagina''s aanmaken.</p>'
DEFAULTABOUTCONTENT: '<p>Je kan deze pagina vullen met eigen tekst, of deze verwijderen en nieuwe pagina''s aanmaken.</p>'
DEFAULTABOUTTITLE: 'Over Ons'
DEFAULTCONTACTCONTENT: '<p>U kunt deze pagina vullen met nieuwe tekst, of verwijderen en nieuwe pagina''s aanmaken.</p>'
DEFAULTCONTACTCONTENT: '<p>Je kan deze pagina vullen met nieuwe tekst, of verwijderen en nieuwe pagina''s aanmaken.</p>'
DEFAULTCONTACTTITLE: Contact
DEFAULTHOMECONTENT: '<p>Welkom bij Silverstripe! Dit is de standaard homepagina. Je kan deze pagina wijzigen door <a href="admin/">het CMS</a> te openen.</p><p>Je kan verder de <a href="http://docs.silverstripe.org">documentatie voor ontwikkelaars</a> raadplegen of aan <a href="http://www.silverstripe.org/learn/lessons">de tutorials</a> beginnen.</p>'
DEFAULTHOMETITLE: Home
DEPENDENT_NOTE: 'De volgende pagina''s zijn afhankelijk van deze pagina. Hieronder vallen virtuele pagina''s, verwijzingspagina''s en pagina''s met links in hun inhoud.'
DESCRIPTION: 'Algemene pagina'
DependtPageColumnLinkType: Linktype
@ -223,7 +219,6 @@ nl:
METAEXTRAHELP: 'HTML-tags voor extra meta-informatie. Bijvoorbeeld <meta name="customName" content="your custom content here">'
MODIFIEDONDRAFTHELP: 'Pagina heeft wijzigingen die nog niet gepubliceerd zijn'
MODIFIEDONDRAFTSHORT: Aangepast
MetadataToggle: Metadata
MoreOptions: 'Meer opties'
NOTPUBLISHED: 'Niet gepubliceerd'
OBSOLETECLASS: 'Deze pagina is van het type {type}. Dit type is niet meer in gebruik. Het opslaan van deze pagina zal het type wijzigen. Hierbij kan data verloren gaan.'
@ -255,7 +250,6 @@ nl:
TABDEPENDENT: 'Afhankelijke pagina''s'
TOPLEVEL: 'Site inhoud (hoogste niveau)'
UNTITLED: 'Naamloos {pagetype}'
URLSegment: 'URL segment'
UntitledDependentObject: 'Naamloos {instanceType}'
VIEWERGROUPS: Bekijkersgroepen
VIEWER_GROUPS_FIELD_DESC: 'Groepen die alles mogen bekijken: {groupList}'
@ -263,23 +257,20 @@ nl:
VIEW_ALL_HELP: 'Bevoegdheid om alle pagina''s op de site te wijzigen, onafhankelijk van de instellingen in het ''Toegang'' tabblad. Vereist "Toegang tot site inhoud" rechten'
VIEW_DRAFT_CONTENT: 'Bekijk concept inhoud'
VIEW_DRAFT_CONTENT_HELP: 'Bevoegdheid om pagina''s buiten het CMS in concept mode te bekijken. Nuttig voor externe medewerkers zonder CMS toegang.'
VIRTUALPAGEDRAFTWARNING: 'U dient eerst de gekoppelde pagina te publiceren voordat u de virtuele pagina publiceert.'
VIRTUALPAGEWARNING: 'U dient eerst een gekoppelde pagina te selecteren en op te slaan voordat u deze pagina kunt publiceren.'
VIRTUALPAGEWARNINGSETTINGS: 'U dient een gekoppelde pagina te selecteren op het hoofdscherm voordat u kunt publiceren.'
VIRTUALPAGEDRAFTWARNING: 'Publiceer eerst de gekoppelde pagina, om de virtuele pagina te kunnen publiceren'
VIRTUALPAGEWARNING: 'Kies eerst een gekoppelde pagina en sla deze eerst op, om deze pagina te kunnen publiceren'
VIRTUALPAGEWARNINGSETTINGS: 'Kies eerst een gekoppelde pagina op het hoofdscherm, voordat je deze pagina kan publiceren'
Viewers: Bekijkersgroepen
Visibility: Zichtbaarheid
db_Content: Inhoud
db_Sort: Volgorde
db_Title: Titel
db_URLSegment: 'URL segment'
db_Version: Versie
has_one_Parent: 'Bovenliggende pagina'
has_one_Subsite: Subsite
many_many_BackLinkTracking: 'Backlinks traceren'
many_many_ImageTracking: 'Afbeeldingen traceren'
many_many_LinkTracking: 'Links traceren'
SilverStripe\CMS\Model\SiteTreeFileExtension:
TITLE_INDEX: '#'
TITLE_TYPE: Type
TITLE_USED_ON: 'Gebruikt op:'
SilverStripe\CMS\Model\SiteTreeFileFormFactoryExtension:
USAGE: Gebruik
@ -317,7 +308,6 @@ nl:
ColumnDateLastModified: 'Laatste wijzigingsdatum'
ColumnDateLastPublished: 'Laatste publicatiedatum'
ColumnProblemType: Probleemtype
ColumnURL: URL
HasBrokenFile: 'heeft verbroken bestand'
HasBrokenLink: 'heeft verbroken link'
HasBrokenLinkAndFile: 'heeft verbroken link en bestand'

View File

@ -30,7 +30,6 @@ pl:
Create: Utwórz
DUPLICATED: 'Duplikowanie ''{title}'' zakończone powodzeniem'
DUPLICATEDWITHCHILDREN: 'Duplikowanie ''{title}'' oraz podstron zakończone powodzeniem'
EMAIL: Email
NEWPAGE: 'Nowa {pagetype}'
PAGENOTEXISTS: 'Ta strona nie istnieje'
PAGES: 'Status strony'
@ -120,7 +119,6 @@ pl:
CMS: 'System Zarządzania Treścią'
DRAFT: Szkic
DRAFTSITE: 'Szkic witryny'
Email: Email
INSTALL_SUCCESS: 'Instalacja przebiegła pomyślnie'
InstallFilesDeleted: 'Pliki instalacyjne zostały pomyślnie usunięte'
InstallSecurityWarning: 'Dla bezpieczeństwa powinieneś skasować pliki instalacyjne, chyba że planujesz reinstalację w późniejszym czasie (<em>wymaga zalogowania się jako admin, patrz wyżej</em>). Serwer potrzebuje teraz tylko dostępu do folderu "assets", możesz usunąć dostęp do innych folderów. <a href="{link}" style="text-align: center;">Kliknij tutaj aby usunąć pliki instalacyjne.</a>'
@ -152,7 +150,6 @@ pl:
EMPTY: 'Proszę podać adres URL lub kliknąć anuluj'
Edit: Edytuj
HelpChars: 'Znaki specjalne są automatycznie konwertowane lub usuwane.'
OK: OK
SilverStripe\CMS\Model\RedirectorPage:
DESCRIPTION: 'Przekierowuje do wskazanej wewnętrznej strony'
FILE: Plik
@ -280,7 +277,6 @@ pl:
many_many_ImageTracking: 'Śledzenie obrazków'
many_many_LinkTracking: 'Śledzenie linków'
SilverStripe\CMS\Model\SiteTreeFileExtension:
TITLE_INDEX: '#'
TITLE_TYPE: 'Rodzaj strony'
TITLE_USED_ON: 'Używany na:'
SilverStripe\CMS\Model\SiteTreeFileFormFactoryExtension:

View File

@ -151,7 +151,6 @@ pl_PL:
EMPTY: 'Proszę podać adres URL lub kliknąć anuluj'
Edit: Edytuj
HelpChars: 'Znaki specjalne są automatycznie konwertowane lub usuwane.'
OK: OK
SilverStripe\CMS\Model\RedirectorPage:
DESCRIPTION: 'Przekierowuje do wskazanej wewnętrznej strony'
FILE: Plik
@ -279,7 +278,6 @@ pl_PL:
many_many_ImageTracking: 'Śledzenie obrazków'
many_many_LinkTracking: 'Śledzenie linków'
SilverStripe\CMS\Model\SiteTreeFileExtension:
TITLE_INDEX: '#'
TITLE_TYPE: Typ
TITLE_USED_ON: 'Użyto na'
SilverStripe\CMS\Model\SiteTreeFileFormFactoryExtension:
@ -320,7 +318,6 @@ pl_PL:
ColumnDateLastModified: 'Data ostatniej modyfikacji'
ColumnDateLastPublished: 'Data ostatniej publikacji'
ColumnProblemType: 'Problem do sprawdzenia'
ColumnURL: URL
HasBrokenFile: 'ma uszkodzony plik'
HasBrokenLink: 'ma uszkodzony link'
HasBrokenLinkAndFile: 'ma uszkodzony link oraz plik'

View File

@ -96,7 +96,6 @@ pt:
LINKDESCR: 'Descrição do link'
SilverStripe\CMS\Forms\SiteTreeURLSegmentField:
Cancel: Cancelar
Edit: Edit
HelpChars: 'Os caracteres especiais são automaticamente convertido ou removidos.'
SilverStripe\CMS\Model\RedirectorPage:
FILE: Ficheiro

View File

@ -25,14 +25,11 @@ pt_BR:
MENUTITLE: Páginas
SilverStripe\CMS\Controllers\ContentController:
Email: E-mail
LOGIN: Login
Password: Senha
SilverStripe\CMS\Forms\AnchorLinkFormFactory:
ANCHORVALUE: Âncora
SilverStripe\CMS\Forms\InternalLinkFormFactory:
LINKDESCR: 'Descrição do link'
SilverStripe\CMS\Forms\SiteTreeURLSegmentField:
Edit: Edit
SilverStripe\CMS\Model\RedirectorPage:
FILE: Arquivo
HEADER: 'Esta página redireccionará os utilizadores para outra página'

View File

@ -72,7 +72,6 @@ ro:
SilverStripe\CMS\Controllers\ContentController:
ARCHIVEDSITE: 'Previzualizare versiune'
ARCHIVEDSITEFROM: 'Site arhivat din'
CMS: CMS
DRAFT: Cironă
DRAFTSITE: 'Site ciornă'
Email: E-mail
@ -81,7 +80,6 @@ ro:
InstallSecurityWarning: 'Din motive de secutitate ar trebuii să ștergeți acum fișierele de instalare, în cazul în care nu planificați o reinstalare ulterioară (<em>necesită drepturi de admin, referințe mai sue</em>). De acum serverul necesită doar drepturi de scriere in directorul "assets", puteți șterge dreturile de scriere pentru celelalte directoare.. <a href="{link}" style="text-align: center;">Click aici pentru ștergerea fișierelor de instalare.</a>'
InstallSuccessCongratulations: 'Silverstripe a fost instalat cu succes!'
LOGGEDINAS: 'Logat ca'
LOGIN: Login
LOGOUT: Logout
NOTEWONTBESHOWN: 'Notă: acest mesaj nu v-a fi afișat vizitatorilor'
NOTLOGGEDIN: Ne-loginat
@ -101,7 +99,6 @@ ro:
Cancel: Anulare
Edit: Editare
HelpChars: 'Caracterele speciale sunt convertite automat sau șterse'
OK: OK
SilverStripe\CMS\Model\RedirectorPage:
FILE: Fisier
HEADER: 'Pagina v-a redirecționa utilizatorii către altă pagină'
@ -156,7 +153,6 @@ ro:
METAEXTRA: 'Etichetă Meta preferențială'
MODIFIEDONDRAFTHELP: 'Pagina are modificări nepublicate'
MODIFIEDONDRAFTSHORT: Modificat
MetadataToggle: Metadata
MoreOptions: 'Mai multe opțiuni'
NOTPUBLISHED: Nepublicat
OBSOLETECLASS: 'Această pagină este de un tip {type} scos din uz. Salvând se va reseta tipul și se pot pierde datele'
@ -221,7 +217,6 @@ ro:
ColumnDateLastModified: 'Data ultimei modificări'
ColumnDateLastPublished: 'Data ultimei publicări'
ColumnProblemType: 'Tip problemă'
ColumnURL: URL
HasBrokenFile: 'are fişiere corupte'
HasBrokenLink: 'are link-uri nefuncţionale'
HasBrokenLinkAndFile: 'are link-uri nefuncţionale şi fişiere corupte'

View File

@ -30,7 +30,6 @@ ru:
Create: Создать
DUPLICATED: 'Копия ''{title}'' создана'
DUPLICATEDWITHCHILDREN: 'Копия ''{title}'' и дочерних элементов создана'
EMAIL: Email
NEWPAGE: 'Новая {pagetype}'
PAGENOTEXISTS: 'Страница не существует'
PAGES: 'Статус страницы'
@ -114,10 +113,8 @@ ru:
SilverStripe\CMS\Controllers\ContentController:
ARCHIVEDSITE: 'Версия для просмотра'
ARCHIVEDSITEFROM: 'Архив сайта от'
CMS: CMS
DRAFT: Черновик
DRAFTSITE: 'Черновой сайт'
Email: Email
INSTALL_SUCCESS: 'Инсталляция прошла успешно!'
InstallFilesDeleted: 'Установочные файлы были успешно удалены.'
InstallSecurityWarning: 'Если вы не планируете в ближайшее время переустанавливать CMS, по соображениям безопасности рекомендуется удалить инсталляционные файлы (<em>для этого требуются права доступа администратора см. выше</em>). Теперь веб-серверу необходим доступ для записи только к папке "assets"; все остальные папки можно защитить от записи. <a href="{link}" style="text-align: center;">Щелкните здесь, чтобы удалить инсталляционные файлы.</a>'
@ -302,7 +299,6 @@ ru:
ColumnDateLastModified: 'Дата последнего изменения'
ColumnDateLastPublished: 'Дата последней публикации'
ColumnProblemType: 'Вид проблемы'
ColumnURL: URL
HasBrokenFile: 'содержит поврежденный файл'
HasBrokenLink: 'содержит нерабочую ссылку'
HasBrokenLinkAndFile: 'содержит нерабочую ссылку и поврежденный файл'

View File

@ -17,8 +17,6 @@ si:
Password: 'මුර පදය'
SilverStripe\CMS\Forms\InternalLinkFormFactory:
LINKDESCR: 'බැදීම් විස්තරය'
SilverStripe\CMS\Forms\SiteTreeURLSegmentField:
Edit: Edit
SilverStripe\CMS\Model\RedirectorPage:
FILE: ලිපිය
HEADER: 'මෙම පිටුව පරිබෝඡකයන් වෙනත් යොමු කරයි'

View File

@ -37,9 +37,11 @@ sk:
PAGESALLOPT: 'Všetky stránky'
PAGETYPEANYOPT: Akákoľvek
PAGETYPEOPT: 'Typ stránky'
PAGETYPE_TITLE: '(Typ stránky: {type}) {title}'
PLEASESAVE: 'Prosím uložte stránku: Táto stránka nemôže byť aktualizovaná, lebo ešte nebola uložená.'
PUBALLCONFIRM: 'Prosím, zverejnite všetky stránky webu, zkopírovaním obsahu na verejné'
PUBALLFUN: 'Funkcia "Publikovať všetko"'
PUBALLFUN2: 'Stlačením tohto tlačidla sa vykoná ekvivalent prechodu na každú stránku a stlačenie tlačidla "publikovať". Je to určené na použitie po rozsiahlych úpravách obsahu, napríklad pri prvom spustení stránky. V prípade veľkých webových stránok sa táto úloha nemusí podariť dokončiť. V tomto prípade vám odporúčame porozprávať sa s vašimi vývojármi a vytvoriť vlastnú úlohu'
PUBLISHED: 'Uverejnené "{title}" úspešne.'
PUBPAGES: 'Hotovo: Publikované {count} stránky'
PageAdded: 'Stránka vytvorená úspešne'
@ -56,6 +58,7 @@ sk:
ROLLEDBACKVERSIONv2: 'Vrátené späť na verziu #{version}.'
SAVED: 'Uložené ''{title}'' úspešne.'
SAVEDRAFT: Uložiť
SEARCHRESULTS: 'Výsledky vyhľadávania'
SHOW_AS_LIST: 'ukázať ako zoznam'
TOO_MANY_PAGES: 'Príliš veľa stránok'
TabContent: Obsah
@ -80,6 +83,7 @@ sk:
MENUTITLE: História
MULTISELECT: 'Dávkové akcie'
NOTPUBLISHED: Nepublikované
NO_PREVIEW: 'Žiaden náhľad k dispozícii'
PREVIEW: 'Náhľad webu'
PUBLISHER: Vydavateľ
REVERTTOTHISVERSION: 'Vrátiť sa k tejto verzii'
@ -92,7 +96,6 @@ sk:
SilverStripe\CMS\Controllers\CMSPageSettingsController:
MENUTITLE: 'Upraviť stránku'
SilverStripe\CMS\Controllers\CMSPagesController:
FILTER: Filter
ListView: 'Zobraziť zoznam'
MENUTITLE: Stránky
TreeView: 'Zobraziť strom'
@ -113,7 +116,6 @@ sk:
SilverStripe\CMS\Controllers\ContentController:
ARCHIVEDSITE: 'Zobrazenie verzie'
ARCHIVEDSITEFROM: 'Archivovaný web z'
CMS: CMS
DRAFT: Koncept
DRAFTSITE: 'Koncept webu'
Email: E-mail
@ -140,29 +142,42 @@ sk:
ANCHORVALUE: Kotva
SilverStripe\CMS\Forms\InternalLinkFormFactory:
LINKDESCR: 'Popis odkazu'
LINKOPENNEWWIN: 'Otvoriť v novom okne/záložke'
LINKTEXT: 'Text odkazu'
SELECT_PAGE: 'Vyberte stránku'
SilverStripe\CMS\Forms\SiteTreeURLSegmentField:
Cancel: Zrušiť
EMPTY: 'Zadajte prosím URL segment alebo kliknite zrušiť'
Edit: Editovať
HelpChars: 'Špeciálne znaky sú automaticky zkonvertované alebo ostránené.'
OK: OK
SilverStripe\CMS\Model\RedirectorPage:
DESCRIPTION: 'Presmeruje na internú stránku alebo na externé URL'
FILE: Súbor
HEADER: 'Táto stránka presmeruje používateľov na inú stránku'
OTHERURL: 'Iné web URL.'
PLURALNAME: 'Základné stránky'
PLURALS:
few: '{count} Základné stránky'
many: '{count} Základných stránok'
one: 'Presmerovacia stránka'
other: '{count} Základných stránok'
REDIRECTTO: 'Presmerovať na'
REDIRECTTOEXTERNAL: 'Iná web stránka'
REDIRECTTOFILE: 'Súbor na vašom webe'
REDIRECTTOPAGE: 'Stránka na vašom webe'
SINGULARNAME: 'Presmerovacia stránka'
YOURPAGE: 'Stránka na vašom webe'
db_ExternalURL: 'Externá URL'
db_RedirectionType: 'Typ presmerovania'
has_one_LinkTo: 'Odkaz na'
has_one_LinkToFile: 'Odkaz na súbor'
SilverStripe\CMS\Model\RedirectorPageController:
HASBEENSETUP: 'Stránka na presmerovanie bola nastavená bez cieľa.'
SilverStripe\CMS\Model\SiteTree:
ACCESSANYONE: Ktokoľvek
ACCESSHEADER: 'Kto môže prezerať túto stránku?'
ACCESSLOGGEDIN: 'Prihlásení používatelia'
ACCESSONLYMEMBERS: 'Iba títo používatelia (vyberte zo zoznamu)'
ACCESSONLYTHESE: 'Iba títo ľudia (vyberte zo zoznamu)'
ADDEDTODRAFTHELP: 'Stránka ešte nebola zverejnená'
ADDEDTODRAFTSHORT: Koncept
@ -190,12 +205,16 @@ sk:
DependtPageColumnLinkType: 'Typ odkazu'
EDITHEADER: 'Kto môže editovať túto stránku?'
EDITORGROUPS: 'Skupiny upravovateľov'
EDITORMEMBERS: Upravovatelia
EDITOR_GROUPS_FIELD_DESC: 'Skupiny s globálnymi právami na úpravy: {groupList}'
EDIT_ALL_DESCRIPTION: 'Upraviť akúkoľvek stránku'
EDIT_ALL_HELP: 'Schopnosť upravovať ktorúkoľvek stránku na webe, nedbajúc na nastavenia v záložke prístupov. Vyžaduje "Prístup k ''Stránkam'' sekcia" povolenie'
Editors: 'Skupiny upravovateľov'
HASBROKENLINKS: 'Táto stránka obsahuje nefunkčné odkazy.'
HTMLEDITORTITLE: Obsah
INHERIT: 'Dediť od nadradenej stránky'
INHERITSITECONFIG: 'Zdediť z nastavení prístupu na stránku'
InvalidExtraMeta: 'Vlastné metaznačky neobsahujú platný kód HTML'
LASTPUBLISHED: 'Posledne zverejnené'
LASTSAVED: 'Posledne uložené'
LASTUPDATED: 'Posledne aktualizované'
@ -206,6 +225,7 @@ sk:
METADESC: 'Meta Popis'
METADESCHELP: 'Vyhľadávače používajú tento obsah pre zobrazovanie výsledkov vyhľadávania (aj keď to nebude mať vplyv na ich hodnotenie).'
METAEXTRA: 'Vlastné Meta Tagy'
METAEXTRAHELP: 'HTML tagy pre ďalšie meta informácie. Napríklad <meta name="customName" content="your custom content here">'
MODIFIEDONDRAFTHELP: 'Stránka má nezverejnené zmeny'
MODIFIEDONDRAFTSHORT: Zmenené
MetadataToggle: Metaúdaje
@ -224,6 +244,11 @@ sk:
PERMISSION_GRANTACCESS_DESCRIPTION: 'Spravovať prístupové práva pre obsah'
PERMISSION_GRANTACCESS_HELP: 'Povoliť nastavenie obmedzenia pre stránku v sekcií "Stránky".'
PLURALNAME: Stránky
PLURALS:
few: '{count} Stránky'
many: '{count} Stránok'
one: Stránka
other: '{count} Stránok'
PageTypNotAllowedOnRoot: 'Stránka typu "{type}" nie je povolená na najvyššej úrovni'
PageTypeNotAllowed: 'Stránka typu "{type}" nie je povolená ako potomok nadradenej stránky'
REMOVE_INSTALL_WARNING: 'Upozornenie: Z bezpečnostných dôvodov odstránte súbor install.php z SilverStripe inštalácie.'
@ -233,9 +258,15 @@ sk:
SHOWINSEARCH: 'Zobraziť v hľadaní?'
SINGULARNAME: Stránka
TABBEHAVIOUR: Správanie
TABCONTENT: 'Hlavný obsah'
TABDEPENDENT: 'Závislé stránky'
TOPLEVEL: 'Obsah Webu (Najvyššia úroveň)'
UNTITLED: 'Bez názvu {pagetype}'
URLSegment: 'Časť URL'
UntitledDependentObject: 'Bez názvu {instanceType}'
VIEWERGROUPS: 'Skupiny prezeračov'
VIEWERMEMBERS: Prezerači
VIEWER_GROUPS_FIELD_DESC: 'Skupiny s globálnymi právami na zobrazenie: {groupList}'
VIEW_ALL_DESCRIPTION: 'Prezerať akúkoľvek stránku'
VIEW_ALL_HELP: 'Schopnosť zobrazovať ktorúkoľvek stránku na webe, nedbajúc na nastavenia v záložke prístupov. Vyžaduje povolenie "Prístup k sekcii ''Stránky''"'
VIEW_DRAFT_CONTENT: 'Zobraziť obsah konceptu'
@ -245,19 +276,65 @@ sk:
VIRTUALPAGEWARNINGSETTINGS: 'Zvolte prosím odkazovanú stránku v poliach hlavného obsahu pre zverejnenie'
Viewers: 'Skupiny prezeračov'
Visibility: Viditeľnosť
belongs_many_many_ContentReviewGroups: 'Skupiny na kontrolu obsahu'
belongs_many_many_ContentReviewUsers: 'Používatelia na kontrolu obsahu'
belongs_many_many_RelationFieldsTestPages: 'Testovacie stránky pre polia vzťahov'
db_CanEditType: 'Môže upraviť typ'
db_CanViewType: 'Môže zobraziť typ'
db_Content: Obsah
db_ContentReviewType: 'Typ kontroly obsahu'
db_HasBrokenFile: 'Obsahuje porušený súbor'
db_HasBrokenLink: 'Obsahuje porušený odkaz'
db_LastEditedByName: 'Naposledy upravené podľa mena'
db_MenuTitle: 'Titulok menu'
db_MetaDescription: 'Meta popis'
db_NextReviewDate: 'Dátum ďalšej kontroly'
db_OwnerNames: 'Mená vlastníkov'
db_PublicHistory: 'Verejná história'
db_ReportClass: 'Trieda správy'
db_ReviewPeriodDays: 'Počet dní na kontrolu'
db_ShareTokenSalt: 'Zdielať soľ znaku'
db_ShowInMenus: 'Zobraziť v ponuke'
db_ShowInSearch: 'Zobraziť vo vyhľadávaní'
db_ShowPageUtilities: 'Zobraziť nástroje stránky'
db_Sort: Usporiadanie
db_Title: Názov
db_URLSegment: 'Časť URL'
db_Version: Verzia
has_many_BackLinks: 'Spätné odkazy'
has_many_ReviewLogs: 'Prezerať záznamy'
has_many_ShareTokens: 'Zdielať znaky'
has_many_VirtualPages: 'Virtuálne stránky'
has_one_Parent: 'Nadradená stránka'
has_one_RelationFieldsTestPage: 'Testovacia stránka pre polia vzťahov'
has_one_Subsite: Podstránka
has_one_WorkflowDefinition: 'Definícia pracovného toku'
many_many_AdditionalWorkflowDefinitions: 'Ďalšie definície pracovného toku'
many_many_BackLinkTracking: 'Záznamy o Backlinkoch'
many_many_CrossSubsiteLinkTracking: 'Sledovanie odkazov naprieč podstránkami'
many_many_EditorGroups: 'Skupiny upravovateľov'
many_many_EditorMembers: 'Členovia upravovateľov'
many_many_ImageTracking: 'Záznamy o obrázkoch'
many_many_LinkTracking: 'Záznamy o odkazoch'
many_many_ViewerGroups: 'Skupiny prezeračov'
many_many_ViewerMembers: 'Členovia prezeračov'
SilverStripe\CMS\Model\SiteTreeFileExtension:
TITLE_INDEX: '#'
TITLE_TYPE: Typ
TITLE_USED_ON: 'Použité na'
SilverStripe\CMS\Model\SiteTreeFileFormFactoryExtension:
USAGE: Použitie
SilverStripe\CMS\Model\SiteTreeLink:
PLURALNAME: 'Odkazy na strom stránky'
PLURALS:
few: '{count} Odkazy na strom stránky'
many: '{count} Odkazov na strom stránky'
one: 'Odkaz na strom stránky'
other: '{count} Odkazov na strom stránky'
SINGULARNAME: 'Odkaz na strom stránky'
has_one_Linked: Prepojené
has_one_Parent: 'Komentovaný príspevok'
SilverStripe\CMS\Model\SiteTreeLinkTracking:
many_many_LinkTracking: 'Záznamy o odkazoch'
SilverStripe\CMS\Model\VirtualPage:
CHOOSE: 'Odkazovaná stránka'
DESCRIPTION: 'Zobrazí obsah inej stránky'
@ -265,8 +342,15 @@ sk:
HEADER: 'Toto je virtuálna stránka'
HEADERWITHLINK: 'Toto je virtuálna stránka, kopíruje sa obsah z "{title}" ({link})'
PLURALNAME: 'Základné stránky'
PLURALS:
few: '{count} Základné stránky'
many: '{count} Základných stránok'
one: 'Virtuálna stránka'
other: '{count} Základných stránok'
PageTypNotAllowedOnRoot: 'Pôvodná stránka typu "{type}" nie je povolená na najvyššej úrovni pre túto virtuálnu stránku'
SINGULARNAME: 'Virtuálna stránka'
db_VersionID: 'ID verzie'
has_one_CopyContentFrom: 'Kopírovať obsah z'
SilverStripe\CMS\Reports\BrokenFilesReport:
BROKENFILES: 'Stránky s poškodenými súbormi'
BrokenLinksGroupTitle: 'Výkazy porušených odkazov'
@ -280,7 +364,6 @@ sk:
ColumnDateLastModified: 'Dátum poslednej úpravy'
ColumnDateLastPublished: 'Dátum poslednej publikácie'
ColumnProblemType: 'Problémový typ'
ColumnURL: URL
HasBrokenFile: 'obsahuje porušený súbor'
HasBrokenLink: 'obsahuje porušený odkaz'
HasBrokenLinkAndFile: 'obsahuje porušený odkaz a súbor'
@ -303,6 +386,7 @@ sk:
ParameterLiveCheckbox: 'Skontrolovať publikovaný web'
SilverStripe\CMS\Reports\EmptyPagesReport:
ContentGroupTitle: 'Výkazy obsahu'
EMPTYPAGES: 'Stránky bez obsahu'
SilverStripe\CMS\Reports\RecentlyEditedReport:
ContentGroupTitle: 'Výkazy obsahu'
LAST2WEEKS: 'Stránky upravené počas posledných 2 týždňov'
@ -315,7 +399,7 @@ sk:
SEARCH: Hľadať
SearchResults: 'Výsledky vyhľadávania'
SilverStripe\CMS\Tasks\RemoveOrphanedPagesTask:
BUTTONRUN: Bež
BUTTONRUN: Spustiť
CHOOSEOPERATION: 'Vyberte operáciu:'
DELETEWARNING: 'Upozornenie: Tieto operácie sú nezvratné. Prosím buďte opatrní.'
HEADER: 'Odstrániť všetky osamotené stránky'

View File

@ -70,7 +70,7 @@ sl:
SilverStripe\CMS\Controllers\CMSPageAddController:
MENUTITLE: 'Dodaj stran'
ParentMode_child: 'Nivo nižje'
ParentMode_top: 'Stran na prvem nivoju'
ParentMode_top: 'Stran na vrhnjem nivoju'
Title: 'Dodaj stran'
SilverStripe\CMS\Controllers\CMSPageEditController:
ErrorItemPermissionDenied: 'Nimate potrebnih pravic, da bi lahko dodali {ObjectTitle} v kampanjo.'
@ -96,7 +96,6 @@ sl:
SilverStripe\CMS\Controllers\CMSPageSettingsController:
MENUTITLE: 'Uredi stran'
SilverStripe\CMS\Controllers\CMSPagesController:
FILTER: Filter
ListView: Seznam
MENUTITLE: 'Seznam strani'
TreeView: Drevo
@ -117,7 +116,6 @@ sl:
SilverStripe\CMS\Controllers\ContentController:
ARCHIVEDSITE: 'Predogled različice'
ARCHIVEDSITEFROM: 'Arhivirana različica iz'
CMS: CMS
DRAFT: Osnutek
DRAFTSITE: 'Osnutek spletnega mesta'
Email: E-pošta
@ -166,17 +164,22 @@ sl:
two: '{count} izvorni strani'
REDIRECTTO: 'Preusmeri na'
REDIRECTTOEXTERNAL: 'Druga spletna stran'
REDIRECTTOFILE: 'Datoteka z vašega spletišča'
REDIRECTTOPAGE: 'Stran na tvojem spletnem mestu'
SINGULARNAME: 'Preusmeritvena stran'
YOURPAGE: 'Stran na tvojem spletnem mestu'
db_ExternalURL: 'Zunanja povezava'
db_RedirectionType: 'Tip preusmeritve'
has_one_LinkTo: 'Poveži na'
has_one_LinkToFile: 'Povezava do datoteke'
SilverStripe\CMS\Model\RedirectorPageController:
HASBEENSETUP: 'Nastavljena preusmeritev še nima določenega cilja.'
SilverStripe\CMS\Model\SiteTree:
ACCESSANYONE: Vsakdo
ACCESSHEADER: 'Kdo lahko vidi to stran?'
ACCESSLOGGEDIN: 'Prijavljeni uporabniki'
ACCESSONLYTHESE: 'Samo ti uporabniki (izberite jih s seznama)'
ACCESSONLYMEMBERS: 'Izbrani uporabniki (izberite s seznama)'
ACCESSONLYTHESE: 'Izbrane skupine (izberite s seznama)'
ADDEDTODRAFTHELP: 'Stran še ni bila objavljena'
ADDEDTODRAFTSHORT: Osnutek
APPEARSVIRTUALPAGES: 'Vsebina je uporabljena na navideznih straneh iz razdelka {title}.'
@ -203,6 +206,7 @@ sl:
DependtPageColumnLinkType: 'Tip povezave'
EDITHEADER: 'Kdo lahko ureja to stran?'
EDITORGROUPS: 'Skupine urednikov'
EDITORMEMBERS: Uredniki
EDITOR_GROUPS_FIELD_DESC: 'Skupine s splošno pravico za urejanje vsebine: {groupList}'
EDIT_ALL_DESCRIPTION: 'Urejanje poljubne strani'
EDIT_ALL_HELP: 'Možnost urejanja katerekoli strani na spletnem mestu, ne glede na nastavitve v sklopu "Dostop". Zahteva dovoljenje za dostop do vsebine spletnega mesta.'
@ -211,6 +215,7 @@ sl:
HTMLEDITORTITLE: Vsebina
INHERIT: 'Privzemi od nadrejene strani'
INHERITSITECONFIG: 'Privzemi iz nastavitev spletišča'
InvalidExtraMeta: 'Prilagojene meta oznake ne uporabljajo pravilne kode HTML'
LASTPUBLISHED: 'Nazadnje objavljeno'
LASTSAVED: 'Nazadnje shranjeno'
LASTUPDATED: 'Nazadnje urejeno'
@ -234,9 +239,9 @@ sl:
PAGETITLE: 'Naslov strani'
PAGETYPE: 'Tip strani'
PARENTID: 'Nadrejena stran'
PARENTTYPE: 'Lokacija strani'
PARENTTYPE_ROOT: 'Stran na prvem nivoju'
PARENTTYPE_SUBPAGE: 'Podstran (izberite nadrejeno stran spodaj)'
PARENTTYPE: Umestitev
PARENTTYPE_ROOT: 'Stran na vrhnjem nivoju'
PARENTTYPE_SUBPAGE: 'Podstran (izberite nadrejeno stran)'
PERMISSION_GRANTACCESS_DESCRIPTION: 'Upravljaj s pravicami za dostop do vsebine'
PERMISSION_GRANTACCESS_HELP: 'Dovoli nastavitve pravic za dostop do izbrane strani v sklopu "Strani".'
PLURALNAME: Strani
@ -250,17 +255,18 @@ sl:
REMOVE_INSTALL_WARNING: 'Pozor: Zaradi varnosti priporočamo, da odstranite install.php it te SilverStripe namestitve.'
REORGANISE_DESCRIPTION: 'Spremeni strukturo spletnega mesta'
REORGANISE_HELP: 'Razvrsti strani v drevesni strukturi s funkcijo "povleci in izpusti".'
SHOWINMENUS: 'Prikaži v navigaciji'
SHOWINSEARCH: 'Prikaži v iskalnikih'
SHOWINMENUS: 'Izpiši v navigaciji'
SHOWINSEARCH: 'Izpiši med rezultati iskanja'
SINGULARNAME: Stran
TABBEHAVIOUR: Obnašanje
TABCONTENT: 'Osrednja vsebina'
TABDEPENDENT: 'Odvisne strani'
TOPLEVEL: 'Vsebina Strani (Zgornja Stopnja)'
TOPLEVEL: 'Vsebina spletišča (vrhnji nivo)'
UNTITLED: '{pagetype} brez naslova'
URLSegment: 'Naslov URL'
UntitledDependentObject: '{instanceType} brez naslova'
VIEWERGROUPS: 'Skupine obiskovalcev'
VIEWERGROUPS: 'Skupine ogledovalcev'
VIEWERMEMBERS: Ogledovalci
VIEWER_GROUPS_FIELD_DESC: 'Skupine s splošno pravico za ogled vsebine: {groupList}'
VIEW_ALL_DESCRIPTION: 'Ogled vsake strani'
VIEW_ALL_HELP: 'Možnost ogleda katere koli strani ne glede na to, kako so nastavljene pravice za ogled strani v sklopu ''Dostop''. Predpogoj je pravica za dostop do razdelka "Seznam strani".'
@ -271,15 +277,50 @@ sl:
VIRTUALPAGEWARNINGSETTINGS: 'Najprej v glavnih vsebinskih poljih izberite povezano stran, potem boste lahko objavili stran.'
Viewers: 'Skupine obiskovalcev (viewers)'
Visibility: Vidnost
belongs_many_many_ContentReviewGroups: 'Skupine redaktorjev'
belongs_many_many_ContentReviewUsers: Redaktorji
belongs_many_many_RelationFieldsTestPages: 'Stran za preizkus relacij'
db_CanEditType: 'Lahko ureja'
db_CanViewType: 'Lahko ogleduje'
db_Content: Vsebina
db_Title: Naziv
db_ContentReviewType: 'Lahko redagira'
db_ExtraMeta: 'Dodatne meta oznake'
db_HasBrokenFile: 'Vsebuje nedelujočo datoteko'
db_HasBrokenLink: 'Vsebuje nedelujočo povezavo'
db_LastEditedByName: 'Zadnje urejanje (poimensko)'
db_MenuTitle: 'Naslov v navigaciji'
db_MetaDescription: 'Meta opis'
db_NextReviewDate: 'Rok za naslednji pregled'
db_OwnerNames: 'Imena lastnikov'
db_PublicHistory: 'Javna zgodovina'
db_ReportClass: Poročilo
db_ReviewPeriodDays: 'Interval za pregled v dnevih'
db_ShareTokenSalt: 'Sol za ključe za dostop'
db_ShowInMenus: 'Izpiši v navigaciji'
db_ShowInSearch: 'Izpiši med rezultati iskanja'
db_ShowPageUtilities: 'Prikaži uporabo strani'
db_Sort: Razvrščanje
db_Title: Naslov
db_URLSegment: 'Naslov URL'
db_Version: Verzija
has_many_BackLinks: 'Povratne povezave'
has_many_ReviewLogs: 'Dnevnik pregledov'
has_many_ShareTokens: 'Ključi za dostop'
has_many_VirtualPages: 'Navidezne strani'
has_one_Parent: 'Nadrejena stran'
many_many_BackLinkTracking: 'Spremljanje povezav "nazaj"'
has_one_RelationFieldsTestPage: 'Stran za preizkus relacij'
has_one_Subsite: Podspletišče
has_one_WorkflowDefinition: 'Definicija uredniškega procesa'
many_many_AdditionalWorkflowDefinitions: 'Dodatne definicije uredniških procesov'
many_many_BackLinkTracking: 'Spremljanje povratnih povezav'
many_many_CrossSubsiteLinkTracking: 'Spremljanje povezav med podspletišči'
many_many_EditorGroups: 'Skupine urednikov'
many_many_EditorMembers: Uredniki
many_many_ImageTracking: 'Spremljanje slik'
many_many_LinkTracking: 'Spremljanje povezav'
many_many_ViewerGroups: 'Skupine ogledovalcev'
many_many_ViewerMembers: Ogledovalci
SilverStripe\CMS\Model\SiteTreeFileExtension:
TITLE_INDEX: '#'
TITLE_TYPE: Tip
TITLE_USED_ON: 'Uporabljena na'
SilverStripe\CMS\Model\SiteTreeFileFormFactoryExtension:
@ -292,6 +333,10 @@ sl:
other: '{count} povezav v strukturi'
two: '{count} povezavi v strukturi'
SINGULARNAME: 'Povezava v strukturi'
has_one_Linked: Povezano
has_one_Parent: 'Nadrejeno pravilo'
SilverStripe\CMS\Model\SiteTreeLinkTracking:
many_many_LinkTracking: 'Spremljanje povezav'
SilverStripe\CMS\Model\VirtualPage:
CHOOSE: 'Povezana stran'
DESCRIPTION: 'Prikaže vsebino, ki je vnesena na drugi strani'
@ -305,7 +350,9 @@ sl:
other: '{count} izvornih strani'
two: '{count} izvorni strani'
PageTypNotAllowedOnRoot: 'Tipa izvorne strani ({type}), ki je osnova za to virtualno stran, ni dovoljeno umestiti na korenskem nivoju'
SINGULARNAME: 'Virtualna stran'
SINGULARNAME: 'Navidezna stran'
db_VersionID: Različica
has_one_CopyContentFrom: 'Kopiraj vsebino iz'
SilverStripe\CMS\Reports\BrokenFilesReport:
BROKENFILES: 'Strani z nedelujočimi datotekami'
BrokenLinksGroupTitle: 'Poročila o nedelujočih povezavah'
@ -319,7 +366,6 @@ sl:
ColumnDateLastModified: 'Datum zadnje spremembe'
ColumnDateLastPublished: 'Datum zadnje objave'
ColumnProblemType: 'Vrsta težave'
ColumnURL: URL
HasBrokenFile: 'vsebuje datoteko z napako'
HasBrokenLink: 'ima nedelujočo povezavo'
HasBrokenLinkAndFile: 'ima nedelujočo povezavo in vsebuje datoteko z napako'

View File

@ -77,7 +77,6 @@ sr:
SilverStripe\CMS\Controllers\ContentController:
ARCHIVEDSITE: 'Верзија за претходни преглед'
ARCHIVEDSITEFROM: 'Архивирани сајт из'
CMS: CMS
DRAFT: Нацрт
DRAFTSITE: 'Нарцт сајта'
Email: Е-пошта
@ -226,7 +225,6 @@ sr:
ColumnDateLastModified: 'Датум последње измене'
ColumnDateLastPublished: 'Датум последњег публиковања'
ColumnProblemType: 'Тип проблема'
ColumnURL: URL
HasBrokenFile: 'садржи оштећену датотеку'
HasBrokenLink: 'садржи оштећен линк'
HasBrokenLinkAndFile: 'садржи оштећене линк и датотеку'

View File

@ -14,8 +14,6 @@ sr@latin:
TabContent: Sadržaj
SilverStripe\CMS\Controllers\CMSPageHistoryController:
UNKNOWN: Nepoznato
SilverStripe\CMS\Controllers\CMSPagesController:
FILTER: Filter
SilverStripe\CMS\Controllers\ContentController:
Email: 'Elektronska pošta'
LOGOUT: 'Odjavi se'
@ -46,7 +44,6 @@ sr@latin:
has_one_Parent: Roditelj
SilverStripe\CMS\Reports\BrokenLinksReport:
Any: 'Bilo koji'
ColumnURL: URL
SilverStripe\CMS\Search\SearchForm:
FILTERLABELTEXT: Pretraga
GO: Idi

View File

@ -4,7 +4,6 @@ sr_RS:
CANT_REORGANISE: 'Немате право да мењате странице вршног нивоа. Ваше измене нису сачуване.'
Cancel: Одустани
Create: Креирај
EMAIL: Email
PAGETYPEANYOPT: 'Било који'
PLEASESAVE: 'Сачувајте страну: ова страна не може да буде ажурирана јер још увек није сачувана.'
REORGANISATIONSUCCESSFUL: 'Стабло сајта је успешно реорганизовано.'
@ -16,13 +15,11 @@ sr_RS:
MENUTITLE: 'Измени страницу'
SilverStripe\CMS\Controllers\CMSPageHistoryController:
UNKNOWN: Непознато
WHEN: When
SilverStripe\CMS\Controllers\CMSPageSettingsController:
MENUTITLE: 'Измени страницу'
SilverStripe\CMS\Controllers\CMSPagesController:
FILTER: Филтер
SilverStripe\CMS\Controllers\ContentController:
Email: Email
LOGOUT: 'Одјави се'
NOTLOGGEDIN: 'Нисте пријављени'
Password: Лозинка
@ -51,9 +48,7 @@ sr_RS:
PLURALNAME: 'User Defined Forms'
SilverStripe\CMS\Reports\BrokenLinksReport:
Any: 'Било који'
ColumnURL: URL
SilverStripe\CMS\Search\SearchForm:
FILTERDATEFROM: From
FILTERLABELTEXT: Претрага
GO: Иди
SEARCH: Претрага

View File

@ -68,7 +68,6 @@ sr_RS@latin:
SilverStripe\CMS\Controllers\CMSPageSettingsController:
MENUTITLE: 'Izmeni stranicu'
SilverStripe\CMS\Controllers\CMSPagesController:
FILTER: Filter
ListView: 'Prikaz u vidu liste'
MENUTITLE: Stranice
TreeView: 'Prikaz u vidu stabla'
@ -77,7 +76,6 @@ sr_RS@latin:
SilverStripe\CMS\Controllers\ContentController:
ARCHIVEDSITE: 'Verzija za prethodni pregled'
ARCHIVEDSITEFROM: 'Arhivirani sajt iz'
CMS: CMS
DRAFT: Nacrt
DRAFTSITE: 'Narct sajta'
Email: E-pošta
@ -226,7 +224,6 @@ sr_RS@latin:
ColumnDateLastModified: 'Datum poslednje izmene'
ColumnDateLastPublished: 'Datum poslednjeg publikovanja'
ColumnProblemType: 'Tip problema'
ColumnURL: URL
HasBrokenFile: 'sadrži oštećenu datoteku'
HasBrokenLink: 'sadrži oštećen link'
HasBrokenLinkAndFile: 'sadrži oštećene link i datoteku'

View File

@ -94,7 +94,6 @@ sv:
SilverStripe\CMS\Controllers\CMSPageSettingsController:
MENUTITLE: 'Redigera sida'
SilverStripe\CMS\Controllers\CMSPagesController:
FILTER: Filter
ListView: Listvy
MENUTITLE: Sidor
TreeView: Trädvy
@ -115,7 +114,6 @@ sv:
SilverStripe\CMS\Controllers\ContentController:
ARCHIVEDSITE: 'Utkast version'
ARCHIVEDSITEFROM: 'Arkiverad sajt från'
CMS: CMS
DRAFT: Utkast
DRAFTSITE: Utkast
Email: E-post
@ -149,7 +147,6 @@ sv:
Cancel: Avbryt
Edit: Redigera
HelpChars: 'Specialtecken konverteras eller tas bort'
OK: OK
SilverStripe\CMS\Model\RedirectorPage:
DESCRIPTION: 'Omdirigerar till en annan intern eller extern sida'
FILE: Fil
@ -217,7 +214,6 @@ sv:
METAEXTRA: 'Egna metataggar'
MODIFIEDONDRAFTHELP: 'Sidan har ej publicerade ändringar'
MODIFIEDONDRAFTSHORT: Ändrad
MetadataToggle: Metadata
MoreOptions: 'Fler alternativ'
NOTPUBLISHED: 'Ej publicerad'
OBSOLETECLASS: 'Denna sida är den föråldrade typen {type}. Att spara kommer att återställa dess typ och du kan förlora data'
@ -266,7 +262,6 @@ sv:
many_many_ImageTracking: 'Spåra bild'
many_many_LinkTracking: 'Spåra länk'
SilverStripe\CMS\Model\SiteTreeFileExtension:
TITLE_INDEX: '#'
TITLE_TYPE: Typ
TITLE_USED_ON: 'Använd på'
SilverStripe\CMS\Model\SiteTreeFileFormFactoryExtension:
@ -300,7 +295,6 @@ sv:
ColumnDateLastModified: 'Datum vid senaste modifiering'
ColumnDateLastPublished: 'Datum vid senaste publicering'
ColumnProblemType: Problemtyp
ColumnURL: URL
HasBrokenFile: 'har trasig fil'
HasBrokenLink: 'har trasig länk'
HasBrokenLinkAndFile: 'har trasig länk och fil'

View File

@ -62,8 +62,6 @@ tr:
ANCHORVALUE: Çapa
SilverStripe\CMS\Forms\InternalLinkFormFactory:
LINKDESCR: 'Bağlantııklaması'
SilverStripe\CMS\Forms\SiteTreeURLSegmentField:
Edit: Edit
SilverStripe\CMS\Model\RedirectorPage:
FILE: Dosya
HEADER: 'Bu sayfa, kullanıcıları başka bir sayfaya yönlendirir'

View File

@ -82,7 +82,6 @@ uk:
LINKDESCR: 'Опис посилання'
SilverStripe\CMS\Forms\SiteTreeURLSegmentField:
Cancel: Відмінити
Edit: Edit
HelpChars: 'Спеціальні символи автоматично перетворені або видалені.'
SilverStripe\CMS\Model\RedirectorPage:
FILE: Файл

50
lang/uk_UA.yml Normal file
View File

@ -0,0 +1,50 @@
uk_UA:
SilverStripe\CMS\Controllers\CMSMain:
ACCESS_HELP: 'Дозволити перегляд розділу, що містить дерево сторінок і вміст. Дозволи на перегляд і редагування можна налаштувати за допомогою спадних меню для окремих сторінок, а також за допомогою окремих "вміст дозволів".'
PAGETYPEOPT: 'Тип сторінки'
PUBALLCONFIRM: 'Будь ласка, опублікуйте кожну сторінку сайту, скопіювавши етап вмісту'
PUBALLFUN2: 'Натискання цієї кнопки еквівалентно переходу на кожну сторінку та натисканню "опублікувати". Він призначений для використання після масових редагувань вмісту, наприклад під час створення сайту. Для великих веб-сайтів це завдання може бути не виконано до кінця. У цьому випадку ми рекомендуємо поговорити з вашими розробниками, щоб створити спеціальне завдання'
TabContent: Зміст
SilverStripe\CMS\Controllers\ContentController:
StartEditing: 'Ви можете почати редагувати свій зміст, відкривши <a href="{link}">CMS</a>.'
SilverStripe\CMS\Forms\SiteTreeURLSegmentField:
Edit: Редагувати
SilverStripe\CMS\Model\SiteTree:
APPEARSVIRTUALPAGES: 'Цей зміст також відображається на віртуальних сторінках у розділах {title}.'
Content: Зміст
DEFAULTABOUTCONTENT: '<p>Ви можете заповнити цю сторінку власним змістом або видалити її та створити власні сторінки.</p>'
DEFAULTCONTACTCONTENT: '<p>Ви можете заповнити цю сторінку власним змістом або видалити її та створити власні сторінки.</p>'
DEPENDENT_NOTE: 'Наступні сторінки залежать від цієї сторінки. Це включає віртуальні сторінки, сторінки перенаправлення та сторінки з посиланнями на зміст.'
DESCRIPTION: 'Сторінка загального змісту'
HTMLEDITORTITLE: Зміст
MENUTITLE: 'Навігаційна мітка'
METADESCHELP: 'Пошукові системи використовують цей зміст для відображення результатів пошуку (хоча це не вплине на їх рейтинг).'
METAEXTRAHELP: 'Теги HTML для додаткової метаінформації. Наприклад <meta name="customName" content="ваш спеціальний зміст тут">'
PAGELOCATION: 'Розташування сторінки'
PAGETITLE: 'Назва сторінки'
PAGETYPE: 'Тип сторінки'
PARENTID: 'Батьківська сторінка'
PARENTTYPE: 'Розташування сторінки'
PARENTTYPE_ROOT: 'Сторінка верхнього рівня'
PERMISSION_GRANTACCESS_DESCRIPTION: 'Керувати правами доступу до змісту'
TABCONTENT: 'Основний зміст'
TOPLEVEL: 'Зміст сайту (верхній рівень)'
URLSegment: 'сегмент URL'
VIEW_DRAFT_CONTENT: 'Переглянути чернетку змісту'
VIRTUALPAGEWARNINGSETTINGS: 'Виберіть пов''язану сторінку в полях основного змісту, щоб опублікувати'
belongs_many_many_ContentReviewGroups: 'Групи перегляду змісту'
belongs_many_many_ContentReviewUsers: 'Користувачі перегляду змісту'
db_Content: Зміст
db_ContentReviewType: 'Тип перегляду змісту'
db_URLSegment: 'сегмент URL'
SilverStripe\CMS\Model\VirtualPage:
DESCRIPTION: 'Відображає зміст іншої сторінки'
HEADERWITHLINK: 'Це віртуальна сторінка, яка копіює вміст із "{title}" ({link})'
has_one_CopyContentFrom: 'Копіювати зміст із'
SilverStripe\CMS\Reports\BrokenLinksReport:
PageName: 'Назва сторінки'
SilverStripe\CMS\Reports\EmptyPagesReport:
ContentGroupTitle: 'Зміст звітів'
EMPTYPAGES: 'Сторінки без змісту'
SilverStripe\CMS\Reports\RecentlyEditedReport:
ContentGroupTitle: 'Зміст звітів'

View File

@ -7,8 +7,6 @@ vi_VN:
PAGETYPEOPT: Loại
SAVEDRAFT: Lưu
TabContent: 'Nội dung'
SilverStripe\CMS\Forms\SiteTreeURLSegmentField:
Edit: Edit
SilverStripe\CMS\Model\RedirectorPage:
HEADER: 'Trang này sẽ di chuyển người dùng đến một trang khác'
OTHERURL: 'Đường dẩn website khác'

View File

@ -27,7 +27,6 @@ zh:
Create: 创建
DUPLICATED: '成功复制‘{title}'
DUPLICATEDWITHCHILDREN: '成功复制‘{title}’及其子页面'
EMAIL: Email
NEWPAGE: '新建{pagetype}'
PAGENOTEXISTS: 该页面不存在
PAGES: 页面状态

View File

@ -27,8 +27,6 @@ zh_CN:
Password: 密码
SilverStripe\CMS\Forms\InternalLinkFormFactory:
LINKDESCR: 链接描述
SilverStripe\CMS\Forms\SiteTreeURLSegmentField:
Edit: Edit
SilverStripe\CMS\Model\RedirectorPage:
FILE: 文件
HEADER: 该页会将用户重新导向另一网页

View File

@ -44,8 +44,6 @@ zh_TW:
Password: 密碼
SilverStripe\CMS\Forms\InternalLinkFormFactory:
LINKDESCR: 連結敘述
SilverStripe\CMS\Forms\SiteTreeURLSegmentField:
Edit: Edit
SilverStripe\CMS\Model\RedirectorPage:
FILE: 檔案
HEADER: 本網頁將重新導向用戶至別的網頁

View File

@ -1,15 +1,15 @@
{
"name": "silverstripe-cms",
"version": "5.0.0",
"version": "4.0.0",
"description": "The SilverStripe CMS",
"directories": {
"test": "tests"
},
"engines": {
"node": "^18.x"
"node": "^10.x"
},
"scripts": {
"build": "yarn && yarn lint && yarn test && rm -rf client/dist/* && NODE_ENV=production webpack --mode production --bail --progress",
"build": "yarn && yarn test && NODE_ENV=production webpack -p --bail --progress",
"dev": "NODE_ENV=development webpack --progress",
"watch": "NODE_ENV=development webpack --watch --progress",
"css": "WEBPACK_CHILD=css npm run build",
@ -32,42 +32,36 @@
},
"homepage": "https://github.com/silverstripe/silverstripe-cms#readme",
"dependencies": {
"@apollo/client": "^3.7.1",
"@popperjs/core": "^2.11.6",
"bootstrap": "^4.6.2",
"classnames": "^2.3.2",
"@silverstripe/reactstrap-confirm": "0.0.5",
"apollo-client": "^2.4.2",
"bootstrap": "^4.3.1",
"classnames": "^2.2.5",
"deep-freeze-strict": "^1.1.1",
"graphql": "^16.6.0",
"graphql-tag": "^2.12.6",
"isomorphic-fetch": "^3.0.0",
"graphql": "^14.0.0",
"graphql-tag": "^2.10.0",
"isomorphic-fetch": "^2.2.1",
"jquery": "^3.5.0",
"merge": "^2.1.1",
"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"
"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"
},
"devDependencies": {
"@silverstripe/eslint-config": "^1.1.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"
"@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"
},
"browserslist": [
"defaults"
],
"jest": {
"testEnvironment": "jsdom",
"roots": [
"client/src"
],
@ -88,6 +82,16 @@
}
},
"resolutions": {
"colors": "1.4.0"
"colors": "1.1.2",
"eslint": "^4.6.1"
},
"babel": {
"presets": [
"env",
"react"
],
"plugins": [
"transform-object-rest-spread"
]
}
}

0
silverstripe_version Normal file
View File

View File

@ -0,0 +1,16 @@
<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

@ -13,12 +13,12 @@
<div class="input-group">
<input $AttributesHTML />
<div class="input-group-append">
<button role="button" type="button" class="btn btn-primary update">
<button role="button" data-icon="accept" type="button" class="btn btn-primary update">
<%t SilverStripe\CMS\Forms\SiteTreeURLSegmentField.OK 'OK' %>
</button>
</div>
<div class="input-group-append">
<button role="button" type="button" class="btn btn-outline-secondary btn-sm input-group-append cancel">
<button role="button" data-icon="cancel" type="button" class="btn btn-outline-secondary btn-sm input-group-append cancel">
<%t SilverStripe\CMS\Forms\SiteTreeURLSegmentField.Cancel 'Cancel' %>
</button>
</div>

View File

@ -0,0 +1,27 @@
<% 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 press the "Insert from Files" HTML field button
And I click on the "div[aria-label='Insert from Files'] button" element
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 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 "div[aria-label='Insert link [Ctrl+K]'] button" element
And I select "Link to a file" from the TinyMCE menu with javascript
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 press the "Insert media via URL" button
And I click on the "div[aria-label='Insert media via URL'] button" element
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,11 +17,12 @@ 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 ".tox-collection__group" element
And I click "Page on this site" in the ".mce-menu" element
Then I should see an "form#Form_editorInternalLink" element
When I select "About Us" in the "#Form_editorInternalLink_PageID_Holder" tree dropdown
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 fill in "my desc" for "Link description"
And I press the "Insert link" button
And I press the "Insert" 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
@ -30,11 +31,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><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 ".tox-collection__group" element
And I click "Page on this site" in the ".mce-menu" element
Then I should see an "form#Form_editorInternalLink" element
And I should not see "Link text"
When I select "About Us" in the "#Form_editorInternalLink_PageID_Holder" tree dropdown
And I press the "Insert link" button
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
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
@ -43,14 +45,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 "<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 ".tox-collection__group" element
And I click "Page on this site" in the ".mce-menu" element
And I should see an "form#Form_editorInternalLink" element
Then I should see "About Us" in the "#Form_editorInternalLink_PageID_Holder .treedropdownfield__value-container" element
Then I should see "About Us" in the ".Select-value" element
And the "Link description" field should contain "my desc"
# This doesn't seem to suffer from that issue
When I select "Home" in the "#Form_editorInternalLink_PageID_Holder" tree dropdown
When I click "About Us" in the ".Select-value" element
And I click "Home" in the ".treedropdownfield__menu" element
And I fill in "my new desc" for "Link description"
And I press the "Insert link" button
And I press the "Insert" 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
@ -58,11 +61,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 ".tox-collection__group" element
When I click "Link to external URL" in the ".mce-menu" 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 link" button
And I press the "Insert" 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
@ -71,11 +74,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 ".tox-collection__group" element
When I click "Link to external URL" in the ".mce-menu" 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 link" button
And I press the "Insert" 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
@ -84,12 +87,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 ".tox-collection__group" element
And I click "Link to external URL" in the ".mce-menu" 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 link" button
And I press the "Insert" 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,14 +17,16 @@ 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 ".tox-collection__group" element
And I click "Anchor on a page" in the ".mce-menu" element
Then I should see an "form#Form_editorAnchorLink" 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
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
When I fill in "my desc" for "Link description"
And I press the "Insert link" button
And I press the "Insert" 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
@ -33,13 +35,16 @@ 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 ".tox-collection__group" element
And I click "Anchor on a page" in the ".mce-menu" 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 .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
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
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
@ -47,13 +52,15 @@ 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 ".tox-collection__group" element
And I click "Anchor on a page" in the ".mce-menu" element
Then I should see an "form#Form_editorAnchorLink" 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
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
When I fill in "my desc" for "Link description"
And I press the "Insert link" button
And I press the "Insert" 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
@ -62,15 +69,16 @@ 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 ".tox-collection__group" element
And I click "Anchor on a page" in the ".mce-menu" element
Then I should see an "form#Form_editorAnchorLink" 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
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
When I fill in "my desc" for "Link description"
And I press the "Insert link" button
And I press the "Insert" 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

@ -19,6 +19,10 @@ Feature: Preview a page
And the preview contains "About Us"
Then I set the CMS mode to "Edit mode"
# TODO:
# - Only tests correctly on fresh database
# - We should continue testing against it after we have fixtures ready
@javascript
Scenario: I can see an updated preview when editing content
And I go to "/admin/pages"
Then I should see "About Us" in the tree

View File

@ -8,7 +8,6 @@ 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;
@ -21,8 +20,6 @@ use SilverStripe\Versioned\Versioned;
*/
class FixtureContext extends BehatFixtureContext
{
use StepHelper;
/**
* @var BasicContext
*/
@ -121,6 +118,22 @@ 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
*
@ -142,33 +155,4 @@ 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,6 +16,7 @@ use SilverStripe\Versioned\Versioned;
*/
class CMSBatchActionsTest extends SapphireTest
{
protected static $fixture_file = 'CMSBatchActionsTest.yml';
protected function setUp(): void
@ -141,10 +142,10 @@ class CMSBatchActionsTest extends SapphireTest
$this->assertEquals($archivedID, $list->first()->ParentID);
// Run restore
$result = json_decode($action->run($list)->getBody(), true);
$result = json_decode($action->run($list) ?? '', true);
$this->assertEquals(
[
$archivedxID => $archivedxID,
$archivedxID => $archivedxID
],
$result['success']
);
@ -161,13 +162,13 @@ class CMSBatchActionsTest extends SapphireTest
$this->assertEquals(0, $list->last()->ParentID); // archived (parent)
// Run restore
$result = json_decode($action->run($list)->getBody(), true);
$result = json_decode($action->run($list) ?? '', 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,6 +2,7 @@
namespace SilverStripe\CMS\Tests\Controllers;
use Page;
use Psr\SimpleCache\CacheInterface;
use SilverStripe\Admin\CMSBatchActionHandler;
use SilverStripe\CMS\Controllers\CMSMain;
@ -24,6 +25,7 @@ use SilverStripe\Security\Member;
use SilverStripe\Security\Security;
use SilverStripe\SiteConfig\SiteConfig;
use SilverStripe\Versioned\Versioned;
use SilverStripe\Dev\Deprecation;
class CMSMainTest extends FunctionalTest
{
@ -117,6 +119,40 @@ 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
@ -149,8 +185,8 @@ class CMSMainTest extends FunctionalTest
$this->logInWithPermission('ADMIN');
Config::modify()->set(SiteTree::class, 'enforce_strict_hierarchy', true);
$parentPage = $this->objFromFixture(SiteTree::class, 'page3');
$childPage = $this->objFromFixture(SiteTree::class, 'page1');
$parentPage = $this->objFromFixture(Page::class, 'page3');
$childPage = $this->objFromFixture(Page::class, 'page1');
$parentPage->doUnpublish();
$childPage->doUnpublish();
@ -172,7 +208,7 @@ class CMSMainTest extends FunctionalTest
$this->logInWithPermission('ADMIN');
// Set up a page that is delete from live
$page = $this->objFromFixture(SiteTree::class, 'page1');
$page = $this->objFromFixture(Page::class, 'page1');
$pageID = $page->ID;
$page->publishRecursive();
$page->delete();
@ -180,7 +216,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());
@ -197,7 +233,7 @@ class CMSMainTest extends FunctionalTest
$this->logInWithPermission('ADMIN');
// Set up a page that is delete from live
$page1 = $this->objFromFixture(SiteTree::class, 'page1');
$page1 = $this->objFromFixture(Page::class, 'page1');
$page1ID = $page1->ID;
$page1->publishRecursive();
$page1->delete();
@ -210,18 +246,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(SiteTree::class, $cmsMain->getRecord($page1ID));
$this->assertInstanceOf(SiteTree::class, $cmsMain->getRecord($this->idFromFixture(SiteTree::class, 'page2')));
$this->assertInstanceOf('Page', $cmsMain->getRecord($page1ID));
$this->assertInstanceOf('Page', $cmsMain->getRecord($this->idFromFixture('Page', 'page2')));
// This functionality isn't actually used any more.
$newPage = $cmsMain->getRecord('new-Page-5');
$this->assertInstanceOf(SiteTree::class, $newPage);
$this->assertInstanceOf('Page', $newPage);
$this->assertEquals('5', $newPage->ParentID);
}
public function testDeletedPagesSiteTreeFilter()
{
$id = $this->idFromFixture(SiteTree::class, 'page3');
$id = $this->idFromFixture('Page', 'page3');
$this->logInWithPermission('ADMIN');
$result = $this->get('admin/pages/getsubtree?filter=CMSSiteTreeFilter_DeletedPages&ajax=1&ID=' . $id);
$this->assertEquals(200, $result->getStatusCode());
@ -242,7 +278,7 @@ class CMSMainTest extends FunctionalTest
'admin/pages/add/AddForm',
[
'ParentID' => '0',
'PageType' => RedirectorPage::class,
'PageType' => 'Page',
'Locale' => 'en_US',
'action_doAdd' => 1,
'ajax' => 1,
@ -262,7 +298,7 @@ class CMSMainTest extends FunctionalTest
'admin/pages/add/AddForm',
[
'ParentID' => '0',
'PageType' => RedirectorPage::class,
'PageType' => 'Page',
'Locale' => 'en_US',
'action_doAdd' => 1,
'ajax' => 1,
@ -296,7 +332,7 @@ class CMSMainTest extends FunctionalTest
'PageType' => CMSMainTest_ClassA::class,
'Locale' => 'en_US',
'action_doAdd' => 1,
'ajax' => 1,
'ajax' => 1
],
[
'X-Pjax' => 'CurrentForm,Breadcrumbs',
@ -316,7 +352,7 @@ class CMSMainTest extends FunctionalTest
'PageType' => CMSMainTest_ClassB::class,
'Locale' => 'en_US',
'action_doAdd' => 1,
'ajax' => 1,
'ajax' => 1
],
[
'X-Pjax' => 'CurrentForm,Breadcrumbs',
@ -339,10 +375,10 @@ class CMSMainTest extends FunctionalTest
'admin/pages/add/AddForm',
[
'ParentID' => $newPageId,
'PageType' => RedirectorPage::class,
'PageType' => 'Page',
'Locale' => 'en_US',
'action_doAdd' => 1,
'ajax' => 1,
'ajax' => 1
],
[
'X-Pjax' => 'CurrentForm,Breadcrumbs',
@ -357,8 +393,8 @@ class CMSMainTest extends FunctionalTest
public function testBreadcrumbs()
{
$page3 = $this->objFromFixture(SiteTree::class, 'page3');
$page31 = $this->objFromFixture(SiteTree::class, 'page31');
$page3 = $this->objFromFixture(Page::class, 'page3');
$page31 = $this->objFromFixture(Page::class, 'page31');
$this->logInAs('admin');
$response = $this->get('admin/pages/edit/show/' . $page31->ID);
@ -384,7 +420,7 @@ class CMSMainTest extends FunctionalTest
$this->assertEquals($page->Title, 'New Page');
$this->assertNotEquals($page->Sort, 0);
$this->assertInstanceOf(SiteTree::class, $page);
$this->assertInstanceOf('Page', $page);
// Test failure
try {
@ -413,10 +449,10 @@ class CMSMainTest extends FunctionalTest
);
// Change state of tree
$page1 = $this->objFromFixture(SiteTree::class, 'page1');
$page3 = $this->objFromFixture(SiteTree::class, 'page3');
$page11 = $this->objFromFixture(SiteTree::class, 'page11');
$page12 = $this->objFromFixture(SiteTree::class, 'page12');
$page1 = $this->objFromFixture(Page::class, 'page1');
$page3 = $this->objFromFixture(Page::class, 'page3');
$page11 = $this->objFromFixture(Page::class, 'page11');
$page12 = $this->objFromFixture(Page::class, 'page12');
// Deleted
$page1->doUnpublish();
$page1->delete();
@ -436,7 +472,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());
@ -447,7 +483,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());
@ -458,7 +494,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());
@ -493,7 +529,7 @@ class CMSMainTest extends FunctionalTest
$this->loginWithPermission('ADMIN');
// Get a associated with a fixture page.
$page = $this->objFromFixture(SiteTree::class, 'page1');
$page = $this->objFromFixture(Page::class, 'page1');
$controller = CMSMain::create();
$controller->setRequest(Controller::curr()->getRequest());
$form = $controller->getEditForm($page->ID);
@ -521,7 +557,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 @@
SilverStripe\CMS\Model\SiteTree:
Page:
page1:
Title: Page 1
Sort: 1
@ -10,11 +10,11 @@ SilverStripe\CMS\Model\SiteTree:
Sort: 3
page31:
Title: Page 3.1
Parent: =>SilverStripe\CMS\Model\SiteTree.page3
Parent: =>Page.page3
Sort: 1
page32:
Title: Page 3.2
Parent: =>SilverStripe\CMS\Model\SiteTree.page3
Parent: =>Page.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 SiteTree implements TestOnly
class CMSMainTest_ClassA extends Page 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 SiteTree implements TestOnly
class CMSMainTest_ClassB extends Page implements TestOnly
{
private static $table_name = 'CMSMainTest_ClassB';

View File

@ -2,10 +2,11 @@
namespace SilverStripe\CMS\Tests\Controllers;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Dev\TestOnly;
use SilverStripe\ORM\HiddenClass;
use Page;
class CMSMainTest_HiddenClass extends SiteTree implements TestOnly, HiddenClass
class CMSMainTest_HiddenClass extends Page 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 SiteTree implements TestOnly
class CMSMainTest_NotRoot extends Page implements TestOnly
{
private static $table_name = 'CMSMainTest_NotRoot';

View File

@ -0,0 +1,219 @@
<?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

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

View File

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

View File

@ -1,4 +1,4 @@
SilverStripe\CMS\Model\SiteTree:
Page:
page1:
Title: Page 1
page2:
@ -15,19 +15,19 @@ SilverStripe\CMS\Model\SiteTree:
page7:
Title: Page 7
page7a:
Parent: =>SilverStripe\CMS\Model\SiteTree.page7
Parent: =>Page.page7
Title: Page 7a
page2a:
Parent: =>SilverStripe\CMS\Model\SiteTree.page2
Parent: =>Page.page2
Title: Page 2a
page2b:
Parent: =>SilverStripe\CMS\Model\SiteTree.page2
Parent: =>Page.page2
Title: Page 2b
page3a:
Parent: =>SilverStripe\CMS\Model\SiteTree.page3
Parent: =>Page.page3
Title: Page 3a
page3b:
Parent: =>SilverStripe\CMS\Model\SiteTree.page3
Parent: =>Page.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 SilverStripe\CMS\Model\SiteTree;
use Page;
class ContentControllerPermissionsTest extends FunctionalTest
{
@ -17,7 +17,7 @@ class ContentControllerPermissionsTest extends FunctionalTest
public function testCanViewStage()
{
// Create a new page
$page = new SiteTree();
$page = new Page();
$page->URLSegment = 'testpage';
$page->write();
$page->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
@ -38,11 +38,7 @@ 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')
@ -51,10 +47,6 @@ 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 SiteTree();
$page = new Page();
$page->URLSegment = 'whatever';
$page->Content = 'oh really?';
$page->write();

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

@ -10,6 +10,7 @@ use SilverStripe\Control\HTTPResponse_Exception;
use SilverStripe\Core\Config\Config;
use SilverStripe\Dev\FunctionalTest;
use SilverStripe\Versioned\Versioned;
use Page;
class ContentControllerTest extends FunctionalTest
{
@ -30,8 +31,8 @@ class ContentControllerTest extends FunctionalTest
Config::modify()->set(SiteTree::class, 'nested_urls', true);
// Ensure all pages are published
/** @var SiteTree $page */
foreach (SiteTree::get() as $page) {
/** @var Page $page */
foreach (Page::get() as $page) {
$page->publishSingle();
}
}
@ -94,7 +95,8 @@ class ContentControllerTest extends FunctionalTest
public function testDeepNestedURLs()
{
$page = new SiteTree();
$page = new Page();
$page->URLSegment = 'base-page';
$page->write();
$page->publishSingle();
@ -172,7 +174,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 SiteTree implements TestOnly
class ContentControllerTestPage extends Page 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,9 +2,10 @@
namespace SilverStripe\CMS\Tests\Controllers;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Dev\TestOnly;
use Page;
class ContentControllerTestPageWithoutController extends SiteTree implements TestOnly
class ContentControllerTestPageWithoutController extends Page 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 SiteTree implements TestOnly
class ContentControllerTest_Page extends Page implements TestOnly
{
private static $table_name = 'ContentControllerTest_Page';
}

View File

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

View File

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

View File

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

View File

@ -0,0 +1,239 @@
<?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

@ -0,0 +1,48 @@
<?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

@ -0,0 +1,36 @@
<?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

@ -0,0 +1,42 @@
<?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

@ -0,0 +1,19 @@
<?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 = new SiteTree([
$page = SiteTree::create([
'Title' => 'Test page',
'URLSegment' => 'test-page',
'ParentID' => 0,
@ -52,7 +52,7 @@ class LinkablePluginTest extends SapphireTest
$page->write();
$page->publishRecursive();
$page = new SiteTree([
$page = SiteTree::create([
'Title' => 'Other test page',
'URLSegment' => 'other-test-page',
'ParentID' => 0,

View File

@ -2,7 +2,7 @@
namespace SilverStripe\CMS\Tests\Model;
use SilverStripe\CMS\Model\SiteTree;
use Page;
use SilverStripe\CMS\Model\RedirectorPage;
use SilverStripe\CMS\Model\RedirectorPageController;
use SilverStripe\Control\Director;
@ -36,65 +36,53 @@ class RedirectorPageTest extends FunctionalTest
Director::config()->set('alternate_base_url', 'http://www.mysite.com/');
// Ensure all pages are published
/** @var SiteTree $page */
foreach (SiteTree::get() as $page) {
/** @var Page $page */
foreach (Page::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()
@ -102,11 +90,7 @@ 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 @@
SilverStripe\CMS\Model\SiteTree:
Page:
dest:
Title: Redirection Dest
URLSegment: redirection-dest
@ -17,7 +17,7 @@ SilverStripe\CMS\Model\RedirectorPage:
Title: Good Internal
URLSegment: good-internal
RedirectionType: Internal
LinkTo: =>SilverStripe\CMS\Model\SiteTree.dest
LinkTo: =>Page.dest
badexternal:
Title: Bad External
RedirectionType: External

View File

@ -2,6 +2,7 @@
namespace SilverStripe\CMS\Tests\Model;
use Page;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Dev\FunctionalTest;
use SilverStripe\ORM\DB;
@ -20,6 +21,7 @@ use SilverStripe\Versioned\Versioned;
*/
class SiteTreeActionsTest extends FunctionalTest
{
protected static $fixture_file = 'SiteTreeActionsTest.yml';
public function testActionsReadonly()
@ -36,7 +38,7 @@ class SiteTreeActionsTest extends FunctionalTest
Security::setCurrentUser($readonlyEditor);
// Reload latest version
$page = SiteTree::get()->byID($page->ID);
$page = Page::get()->byID($page->ID);
$actions = $page->getCMSActions();
$this->assertNull($actions->dataFieldByName('action_save'));
@ -82,14 +84,14 @@ class SiteTreeActionsTest extends FunctionalTest
$author = $this->objFromFixture(Member::class, 'cmseditor');
Security::setCurrentUser($author);
/** @var SiteTree $page */
$page = new SiteTree();
/** @var Page $page */
$page = new Page();
$page->CanEditType = 'LoggedInUsers';
$page->write();
$page->publishRecursive();
// Reload latest version
$page = SiteTree::get()->byID($page->ID);
$page = Page::get()->byID($page->ID);
$actions = $page->getCMSActions();
@ -106,7 +108,7 @@ class SiteTreeActionsTest extends FunctionalTest
$author = $this->objFromFixture(Member::class, 'cmseditor');
Security::setCurrentUser($author);
$page = new SiteTree();
$page = new Page();
$page->CanEditType = 'LoggedInUsers';
$page->write();
$this->assertTrue($page->canPublish());
@ -133,7 +135,7 @@ class SiteTreeActionsTest extends FunctionalTest
$author = $this->objFromFixture(Member::class, 'cmseditor');
Security::setCurrentUser($author);
$page = new SiteTree();
$page = new Page();
$page->CanEditType = 'LoggedInUsers';
$page->write();
$this->assertTrue($page->canPublish());
@ -143,7 +145,7 @@ class SiteTreeActionsTest extends FunctionalTest
$page->flushCache();
// Reload latest version
$page = SiteTree::get()->byID($page->ID);
$page = Page::get()->byID($page->ID);
$actions = $page->getCMSActions();
$this->assertNotNull($actions->dataFieldByName('action_save'));
@ -156,17 +158,15 @@ class SiteTreeActionsTest extends FunctionalTest
public function testActionsViewingOldVersion()
{
$p = new SiteTree();
$p = new Page();
$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(SiteTree::class, $p->ID, $version);
$version = DB::query('SELECT "Version" FROM "SiteTree_Versions" WHERE "Content" = \'test page first version\'')->value();
$old = Versioned::get_version('Page', $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 SilverStripe\CMS\Model\SiteTree;
use Page;
class SiteTreeActionsTest_Page extends SiteTree implements TestOnly
class SiteTreeActionsTest_Page extends Page 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(SiteTree::class, 'page3');
$page3 = $this->objFromFixture('Page', 'page3');
$page3->Content = str_replace(
'$page1.ID',
$this->objFromFixture(SiteTree::class, 'page1')->ID ?? '',
$this->objFromFixture('Page', 'page1')->ID ?? '',
$page3->Content ?? ''
);
$page3->write();
@ -46,22 +46,18 @@ class SiteTreeBacklinksTest extends SapphireTest
public function testSavingPageWithLinkAddsBacklink()
{
// load page 1
$page1 = $this->objFromFixture(SiteTree::class, 'page1');
$page1 = $this->objFromFixture('Page', 'page1');
// 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'
);
$page2 = $this->objFromFixture('Page', '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(SiteTree::class, 'page1');
$page1 = $this->objFromFixture('Page', 'page1');
// assert backlink to page 2 exists
$this->assertContains($page2->ID, $page1->BackLinkTracking()->column('ID'), 'Assert backlink to page 2 exists');
@ -70,10 +66,10 @@ class SiteTreeBacklinksTest extends SapphireTest
public function testRemovingLinkFromPageRemovesBacklink()
{
// load page 1
$page1 = $this->objFromFixture(SiteTree::class, 'page1');
$page1 = $this->objFromFixture('Page', 'page1');
// assert backlink to page 3 exits
$page3 = $this->objFromFixture(SiteTree::class, 'page3');
$page3 = $this->objFromFixture('Page', 'page3');
$this->assertContains($page3->ID, $page1->BackLinkTracking()->column('ID'), 'Assert backlink to page 3 exists');
// remove hyperlink to page 1
@ -81,156 +77,116 @@ class SiteTreeBacklinksTest extends SapphireTest
$page3->write();
// load page 1
$page1 = $this->objFromFixture(SiteTree::class, 'page1');
$page1 = $this->objFromFixture('Page', '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(SiteTree::class, 'page1');
$page1 = $this->objFromFixture('Page', 'page1');
// assert backlink to page 3 exists
$page3 = $this->objFromFixture(SiteTree::class, 'page3');
$page3 = $this->objFromFixture('Page', '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(SiteTree::class, 'page3');
$page3 = $this->objFromFixture('Page', '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(SiteTree::class, 'page1');
$page3 = $this->objFromFixture(SiteTree::class, 'page3');
$page1 = $this->objFromFixture('Page', 'page1');
$page3 = $this->objFromFixture('Page', 'page3');
$this->assertTrue($page1->publishRecursive());
$this->assertTrue($page3->publishRecursive());
// load pages from live
$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);
$page1live = Versioned::get_one_by_stage('Page', 'Live', '"SiteTree"."ID" = ' . $page1->ID);
$page3live = Versioned::get_one_by_stage('Page', '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(SiteTree::class, 'Live', '"SiteTree"."ID" = ' . $page3->ID);
$page3live = Versioned::get_one_by_stage('Page', '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(SiteTree::class, 'page1');
$page3 = $this->objFromFixture(SiteTree::class, 'page3');
$page1 = $this->objFromFixture('Page', 'page1');
$page3 = $this->objFromFixture('Page', 'page3');
$this->assertTrue($page1->publishRecursive());
$this->assertTrue($page3->publishRecursive());
// load page 3 from live
$page3live = Versioned::get_one_by_stage(SiteTree::class, 'Live', '"SiteTree"."ID" = ' . $page3->ID);
$page3live = Versioned::get_one_by_stage('Page', '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(SiteTree::class, 'Live', '"SiteTree"."ID" = ' . $page3->ID);
$page3live = Versioned::get_one_by_stage('Page', '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(SiteTree::class, 'Live', '"SiteTree"."ID" = ' . $page3->ID);
$page3live = Versioned::get_one_by_stage('Page', '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(SiteTree::class, 'page1');
$page3 = $this->objFromFixture(SiteTree::class, 'page3');
$page1 = $this->objFromFixture('Page', 'page1');
$page3 = $this->objFromFixture('Page', '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';
@ -239,58 +195,42 @@ class SiteTreeBacklinksTest extends SapphireTest
$page1->write();
// assert page 3 on draft contains new page 1 url
$page3 = $this->objFromFixture(SiteTree::class, 'page3');
$page3 = $this->objFromFixture('Page', '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(SiteTree::class, 'Live', '"SiteTree"."ID" = ' . $page3->ID);
$page3live = Versioned::get_one_by_stage('Page', '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(SiteTree::class, 'Live', '"SiteTree"."ID" = ' . $page3->ID);
$page3live = Versioned::get_one_by_stage('Page', '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 SiteTree $page1 */
$page1 = $this->objFromFixture(SiteTree::class, 'page1');
/** @var SiteTree $page2 */
$page2 = $this->objFromFixture(SiteTree::class, 'page2');
/** @var Page $page1 */
$page1 = $this->objFromFixture('Page', 'page1');
/** @var Page $page2 */
$page2 = $this->objFromFixture('Page', '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();
@ -298,32 +238,23 @@ class SiteTreeBacklinksTest extends SapphireTest
$this->assertContains($page2->ID, $page1->BackLinkTracking()->column('ID'), 'Assert backlink to page 2 exists');
// update page1 url
$page1 = $this->objFromFixture(SiteTree::class, 'page1');
$page1 = $this->objFromFixture('Page', 'page1');
$page1->URLSegment = "page1-new-url";
$page1->write();
// confirm that draft link on page2 has been rewritten
$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()
);
$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());
// confirm that published link hasn't
$page2Live = Versioned::get_one_by_stage(SiteTree::class, "Live", "\"SiteTree\".\"ID\" = $page2->ID");
$page2Live = Versioned::get_one_by_stage("Page", "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(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()
);
$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());
// Edit draft again
Versioned::set_stage(Versioned::DRAFT);
@ -331,16 +262,12 @@ 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(SiteTree::class, 'page2');
$page = $this->objFromFixture('Page', 'page2');
$referencingObject = new SiteTreeBacklinksTestContentObject();
$referencingObject->Content = '<p><a href="[sitetree_link,id='

View File

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

View File

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

View File

@ -2,6 +2,7 @@
namespace SilverStripe\CMS\Tests\Model;
use Page;
use SilverStripe\Assets\Dev\TestAssetStore;
use SilverStripe\Assets\File;
use SilverStripe\Assets\Filesystem;
@ -30,8 +31,8 @@ class SiteTreeHTMLEditorFieldTest extends FunctionalTest
}
// Ensure all pages are published
/** @var SiteTree $page */
foreach (SiteTree::get() as $page) {
/** @var Page $page */
foreach (Page::get() as $page) {
$page->publishSingle();
}
}
@ -54,11 +55,7 @@ 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,6 +2,7 @@
namespace SilverStripe\CMS\Tests\Model;
use Page;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\CMS\Model\SiteTreeLinkTracking_Parser;
use SilverStripe\Control\Director;
@ -31,7 +32,7 @@ class SiteTreeLinkTrackingTest extends SapphireTest
public function testParser()
{
SiteTree::add_extension(SiteTree::class, SiteTreeLinkTracking_Extension::class);
SiteTree::add_extension(Page::class, SiteTreeLinkTracking_Extension::class);
// Shortcodes
$this->assertTrue($this->isBroken('<a href="[sitetree_link,id=123]">link</a>'));
@ -54,7 +55,8 @@ class SiteTreeLinkTrackingTest extends SapphireTest
$this->assertFalse($this->isBroken('<a id="anchor">anchor</a>'));
$this->assertTrue($this->isBroken('<a href="##anchor">anchor</a>'));
$page = new SiteTree();
$page = new Page();
$page->Content = '<a name="yes-name-anchor">name</a><a id="yes-id-anchor">id</a>';
$page->write();
@ -70,7 +72,7 @@ class SiteTreeLinkTrackingTest extends SapphireTest
protected function highlight($content)
{
$page = new SiteTree();
$page = new Page();
$page->Content = $content;
$page->write();
return $page->Content;
@ -85,7 +87,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 SiteTree();
$otherPage = new Page();
$otherPage->Content = '';
$otherPage->write();

View File

@ -2,6 +2,7 @@
namespace SilverStripe\CMS\Tests\Model;
use Page;
use SilverStripe\CMS\Model\SiteTree;
use SilverStripe\Control\HTTPResponse_Exception;
use SilverStripe\Dev\FunctionalTest;
@ -12,6 +13,10 @@ use SilverStripe\SiteConfig\SiteConfig;
use SilverStripe\Subsites\Extensions\SiteTreeSubsites;
use SilverStripe\Versioned\Versioned;
/**
* @todo Test canAddChildren()
* @todo Test canCreate()
*/
class SiteTreePermissionsTest extends FunctionalTest
{
protected static $fixture_file = "SiteTreePermissionsTest.yml";
@ -28,8 +33,8 @@ class SiteTreePermissionsTest extends FunctionalTest
$this->autoFollowRedirection = false;
// Ensure all pages are published
/** @var SiteTree $page */
foreach (SiteTree::get() as $page) {
/** @var Page $page */
foreach (Page::get() as $page) {
if ($page->URLSegment !== 'draft-only') {
$page->publishSingle();
}
@ -41,8 +46,8 @@ class SiteTreePermissionsTest extends FunctionalTest
{
$this->autoFollowRedirection = false;
/** @var SiteTree $draftOnlyPage */
$draftOnlyPage = $this->objFromFixture(SiteTree::class, 'draftOnlyPage');
/** @var Page $draftOnlyPage */
$draftOnlyPage = $this->objFromFixture(Page::class, 'draftOnlyPage');
$this->logOut();
$response = $this->get($draftOnlyPage->URLSegment . '?stage=Live');
@ -80,7 +85,7 @@ class SiteTreePermissionsTest extends FunctionalTest
{
// Set up fixture - a published page deleted from draft
$this->logInWithPermission("ADMIN");
$page = $this->objFromFixture(SiteTree::class, 'restrictedEditOnlySubadminGroup');
$page = $this->objFromFixture(Page::class, 'restrictedEditOnlySubadminGroup');
$pageID = $page->ID;
$this->assertTrue($page->publishRecursive());
$page->delete();
@ -107,7 +112,7 @@ class SiteTreePermissionsTest extends FunctionalTest
{
// Set up fixture - an unpublished page
$this->logInWithPermission("ADMIN");
$page = $this->objFromFixture(SiteTree::class, 'restrictedEditOnlySubadminGroup');
$page = $this->objFromFixture(Page::class, 'restrictedEditOnlySubadminGroup');
$pageID = $page->ID;
$page->doUnpublish();
@ -130,7 +135,7 @@ class SiteTreePermissionsTest extends FunctionalTest
{
// Find a page that exists and delete it from both stage and published
$this->logInWithPermission("ADMIN");
$page = $this->objFromFixture(SiteTree::class, 'restrictedEditOnlySubadminGroup');
$page = $this->objFromFixture(Page::class, 'restrictedEditOnlySubadminGroup');
$pageID = $page->ID;
$page->doUnpublish();
$page->delete();
@ -148,8 +153,8 @@ class SiteTreePermissionsTest extends FunctionalTest
public function testCanViewStage()
{
// Get page & make sure it exists on Live
/** @var SiteTree $page */
$page = $this->objFromFixture(SiteTree::class, 'standardpage');
/** @var Page $page */
$page = $this->objFromFixture(Page::class, 'standardpage');
$page->publishSingle();
// Then make sure there's a new version on Stage
@ -168,7 +173,7 @@ class SiteTreePermissionsTest extends FunctionalTest
public function testAccessTabOnlyDisplaysWithGrantAccessPermissions()
{
$page = $this->objFromFixture(SiteTree::class, 'standardpage');
$page = $this->objFromFixture(Page::class, 'standardpage');
$subadminuser = $this->objFromFixture(Member::class, 'subadmin');
Security::setCurrentUser($subadminuser);
@ -199,7 +204,7 @@ class SiteTreePermissionsTest extends FunctionalTest
public function testRestrictedViewLoggedInUsers()
{
$page = $this->objFromFixture(SiteTree::class, 'restrictedViewLoggedInUsers');
$page = $this->objFromFixture(Page::class, 'restrictedViewLoggedInUsers');
// unauthenticated users
$this->assertFalse(
@ -218,23 +223,21 @@ 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(SiteTree::class, 'restrictedViewOnlyWebsiteUsers');
$page = $this->objFromFixture(Page::class, 'restrictedViewOnlyWebsiteUsers');
// unauthenticcated users
$this->assertFalse(
@ -253,16 +256,14 @@ 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();
@ -284,7 +285,7 @@ class SiteTreePermissionsTest extends FunctionalTest
public function testRestrictedEditLoggedInUsers()
{
$page = $this->objFromFixture(SiteTree::class, 'restrictedEditLoggedInUsers');
$page = $this->objFromFixture(Page::class, 'restrictedEditLoggedInUsers');
// unauthenticcated users
$this->assertFalse(
@ -297,22 +298,20 @@ 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(SiteTree::class, 'restrictedEditOnlySubadminGroup');
$page = $this->objFromFixture(Page::class, 'restrictedEditOnlySubadminGroup');
// unauthenticated users
$this->assertFalse(
@ -331,15 +330,14 @@ 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(SiteTree::class, 'parent_restrictedViewOnlySubadminGroup');
$childPage = $this->objFromFixture(SiteTree::class, 'child_restrictedViewOnlySubadminGroup');
$parentPage = $this->objFromFixture(Page::class, 'parent_restrictedViewOnlySubadminGroup');
$childPage = $this->objFromFixture(Page::class, 'child_restrictedViewOnlySubadminGroup');
// unauthenticated users
$this->assertFalse(
@ -358,24 +356,22 @@ 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(SiteTree::class, 'parent_restrictedEditOnlySubadminGroup');
$childPage = $this->objFromFixture(SiteTree::class, 'child_restrictedEditOnlySubadminGroup');
$parentPage = $this->objFromFixture(Page::class, 'parent_restrictedEditOnlySubadminGroup');
$childPage = $this->objFromFixture(Page::class, 'child_restrictedEditOnlySubadminGroup');
// unauthenticated users
$this->assertFalse(
@ -387,15 +383,14 @@ 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(SiteTree::class, 'deleteTestParentPage');
$childPage = $this->objFromFixture(SiteTree::class, 'deleteTestChildPage');
$parentPage = $this->objFromFixture(Page::class, 'deleteTestParentPage');
$childPage = $this->objFromFixture(Page::class, 'deleteTestChildPage');
// unauthenticated users
$this->assertFalse(
@ -410,7 +405,7 @@ class SiteTreePermissionsTest extends FunctionalTest
public function testRestrictedEditLoggedInUsersDeletedFromStage()
{
$page = $this->objFromFixture(SiteTree::class, 'restrictedEditLoggedInUsers');
$page = $this->objFromFixture(Page::class, 'restrictedEditLoggedInUsers');
$pageID = $page->ID;
$this->logInWithPermission("ADMIN");
@ -426,60 +421,39 @@ 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(SiteTree::class, 'inheritWithNoParent');
$page = $this->objFromFixture(Page::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(SiteTree::class, 'inheritWithNoParent');
$page = $this->objFromFixture(Page::class, 'inheritWithNoParent');
$siteconfig = $this->objFromFixture(SiteConfig::class, 'default');
$editor = $this->objFromFixture(Member::class, 'editor');
$user = $this->objFromFixture(Member::class, 'websiteuser');
@ -488,99 +462,17 @@ 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'
);
}
/**
* Test permissions on duplicate page
* @dataProvider groupWithPermissions
*/
public function testDuplicatePageWithGroupPermissions(string $userName, string $method, bool $expected)
{
$originalPage = $this->objFromFixture(SiteTree::class, 'originalpage');
$user = $this->objFromFixture(Member::class, $userName);
$dupe = $originalPage->duplicate();
$this->assertEquals($originalPage->Title, $dupe->Title);
$this->assertEquals($dupe->CanViewType, 'OnlyTheseUsers');
$this->assertEquals($dupe->CanEditType, 'OnlyTheseUsers');
$this->assertSame($dupe->{$method}($user), $expected);
}
public function groupWithPermissions(): array
{
return [
'Subadmin can view page duplicate.' => [
'subadmin',
'canView',
true,
],
'Subadmin can edit page duplicate.' => [
'subadmin',
'canEdit',
true,
],
'Editor can view page duplicate.' => [
'editor',
'canView',
true,
],
'Editor can edit page duplicate.' => [
'editor',
'canEdit',
true,
],
'User with "allsections" permission can view page duplicate.' => [
'allsections',
'canView',
true,
],
'User with "allsections" permission cannot edit page duplicate.' => [
'allsections',
'canEdit',
false,
],
'Websiteuser permission cannot view page duplicate.' => [
'websiteuser',
'canView',
false,
],
'Websiteuser permission cannot edit page duplicate.' => [
'websiteuser',
'canEdit',
false,
],
];
$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

@ -11,8 +11,6 @@ SilverStripe\Security\Permission:
Code: CMS_ACCESS_CMSMain
grantaccess:
Code: SITETREE_GRANT_ACCESS
allsections:
Code: CMS_ACCESS_LeftAndMain
SilverStripe\Security\Group:
subadmingroup:
Title: Create, edit and delete pages
@ -22,9 +20,6 @@ SilverStripe\Security\Group:
Title: Edit existing pages
Code: editorgroup
Permissions: =>SilverStripe\Security\Permission.cmsmain2
allsectionsgroup:
Title: All Section Editors
Permissions: =>SilverStripe\Security\Permission.allsections
websiteusers:
Title: View certain restricted pages
SilverStripe\Security\Member:
@ -36,15 +31,11 @@ SilverStripe\Security\Member:
Email: editor@test.com
Password: test
Groups: =>SilverStripe\Security\Group.editorgroup
allsections:
Email: allsections@test.com
Password: test
Groups: =>SilverStripe\Security\Group.allsectionsgroup
websiteuser:
Email: websiteuser@test.com
Password: test
Groups: =>SilverStripe\Security\Group.websiteusers
SilverStripe\CMS\Model\SiteTree:
Page:
standardpage:
URLSegment: standardpage
restrictedViewLoggedInUsers:
@ -75,7 +66,7 @@ SilverStripe\CMS\Model\SiteTree:
URLSegment: parent-restrictedViewOnlySubadminGroup
child_restrictedViewOnlySubadminGroup:
CanViewType: Inherit
Parent: =>SilverStripe\CMS\Model\SiteTree.parent_restrictedViewOnlySubadminGroup
Parent: =>Page.parent_restrictedViewOnlySubadminGroup
URLSegment: child-restrictedViewOnlySubadminGroup
parent_restrictedEditOnlySubadminGroup:
CanEditType: OnlyTheseUsers
@ -83,7 +74,7 @@ SilverStripe\CMS\Model\SiteTree:
URLSegment: parent-restrictedEditOnlySubadminGroup
child_restrictedEditOnlySubadminGroup:
CanEditType: Inherit
Parent: =>SilverStripe\CMS\Model\SiteTree.parent_restrictedEditOnlySubadminGroup
Parent: =>Page.parent_restrictedEditOnlySubadminGroup
URLSegment: child-restrictedEditOnlySubadminGroup
deleteTestParentPage:
CanEditType: Inherit
@ -95,9 +86,3 @@ SilverStripe\CMS\Model\SiteTree:
draftOnlyPage:
CanViewType: Anyone
URLSegment: draft-only
originalpage:
Title: Original Page for duplicate
CanEditType: OnlyTheseUsers
CanViewType: OnlyTheseUsers
EditorGroups: =>SilverStripe\Security\Group.subadmingroup,=>SilverStripe\Security\Group.editorgroup
ViewerGroups: =>SilverStripe\Security\Group.subadmingroup,=>SilverStripe\Security\Group.editorgroup,=>SilverStripe\Security\Group.allsectionsgroup

View File

@ -3,6 +3,7 @@
namespace SilverStripe\CMS\Tests\Model;
use LogicException;
use Page;
use Psr\SimpleCache\CacheInterface;
use ReflectionMethod;
use SilverStripe\CMS\Model\RedirectorPage;
@ -33,15 +34,16 @@ use SilverStripe\Security\Security;
use SilverStripe\SiteConfig\SiteConfig;
use SilverStripe\Subsites\Extensions\SiteTreeSubsites;
use SilverStripe\Versioned\Versioned;
use SilverStripe\View\Parsers\HtmlDiff;
use SilverStripe\View\Parsers\Diff;
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
{
@ -86,28 +88,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);
}
/**
@ -135,7 +137,7 @@ class SiteTreeTest extends SapphireTest
];
foreach ($expectedURLs as $fixture => $urlSegment) {
$obj = $this->objFromFixture(SiteTree::class, $fixture);
$obj = $this->objFromFixture('Page', $fixture);
$this->assertEquals($urlSegment, $obj->URLSegment);
}
}
@ -146,9 +148,9 @@ class SiteTreeTest extends SapphireTest
*/
public function testDisallowedURLGeneration($title, $urlSegment)
{
$page = new SiteTree(['Title' => $title]);
$page = Page::create(['Title' => $title]);
$id = $page->write();
$page = SiteTree::get()->byID($id);
$page = Page::get()->byID($id);
$this->assertEquals($urlSegment, $page->URLSegment);
}
@ -161,9 +163,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 = new SiteTree(['Title' => $title, 'ParentID' => 1]);
$page = Page::create(['Title' => $title, 'ParentID' => 1]);
$id = $page->write();
$page = SiteTree::get()->byID($id);
$page = Page::get()->byID($id);
$this->assertEquals($urlSegment, $page->URLSegment);
}
@ -178,7 +180,7 @@ class SiteTreeTest extends SapphireTest
$this->markTestSkipped('This legacy test requires RESOURCES_DIR to be "resources"');
}
$page = new SiteTree(['Title' => 'Resources']);
$page = SiteTree::create(['Title' => 'Resources']);
$id = $page->write();
$page = SiteTree::get()->byID($id);
$this->assertSame('resources-2', $page->URLSegment);
@ -196,7 +198,7 @@ class SiteTreeTest extends SapphireTest
$this->markTestSkipped('This test requires RESOURCES_DIR to be something other than "resources"');
}
$page = new SiteTree(['Title' => '_Resources']);
$page = SiteTree::create(['Title' => '_Resources']);
$id = $page->write();
$page = SiteTree::get()->byID($id);
$this->assertSame('resources', $page->URLSegment);
@ -207,12 +209,10 @@ class SiteTreeTest extends SapphireTest
*/
public function testPublishCopiesToLiveTable()
{
$obj = $this->objFromFixture(SiteTree::class, 'about');
$obj = $this->objFromFixture('Page', '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);
}
@ -223,15 +223,12 @@ class SiteTreeTest extends SapphireTest
{
$this->logInWithPermission('ADMIN');
$obj = $this->objFromFixture(SiteTree::class, 'about');
$obj = $this->objFromFixture('Page', '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();
@ -278,7 +275,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);
@ -317,13 +314,10 @@ 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(SiteTree::class, 'home');
$parentID = $this->idFromFixture('Page', '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;
@ -407,23 +401,23 @@ class SiteTreeTest extends SapphireTest
*/
public function testRestoreToStage()
{
$page = $this->objFromFixture(SiteTree::class, 'about');
$page = $this->objFromFixture('Page', 'about');
$pageID = $page->ID;
$page->delete();
$this->assertTrue(!DataObject::get_by_id(SiteTree::class, $pageID));
$this->assertTrue(!DataObject::get_by_id("Page", $pageID));
$deletedPage = Versioned::get_latest_version(SiteTree::class, $pageID);
$resultPage = $deletedPage->doRestoreToStage();
$requeriedPage = DataObject::get_by_id(SiteTree::class, $pageID);
$requeriedPage = DataObject::get_by_id("Page", $pageID);
$this->assertEquals($pageID, $resultPage->ID);
$this->assertEquals($pageID, $requeriedPage->ID);
$this->assertEquals('About Us', $requeriedPage->Title);
$this->assertInstanceOf(SiteTree::class, $requeriedPage);
$this->assertInstanceOf('Page', $requeriedPage);
$page2 = $this->objFromFixture(SiteTree::class, 'products');
$page2 = $this->objFromFixture('Page', 'products');
$page2ID = $page2->ID;
$page2->doUnpublish();
$page2->delete();
@ -433,14 +427,12 @@ 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(SiteTree::class, $page2ID);
$requeriedPage = DataObject::get_by_id("Page", $page2ID);
$this->assertEquals('Products', $requeriedPage->Title);
$this->assertInstanceOf(SiteTree::class, $requeriedPage);
$this->assertInstanceOf('Page', $requeriedPage);
}
public function testNoCascadingDeleteWithoutID()
@ -464,11 +456,11 @@ class SiteTreeTest extends SapphireTest
public function testGetByLink()
{
$home = $this->objFromFixture(SiteTree::class, 'home');
$about = $this->objFromFixture(SiteTree::class, 'about');
$staff = $this->objFromFixture(SiteTree::class, 'staff');
$product = $this->objFromFixture(SiteTree::class, 'product1');
$numeric0 = $this->objFromFixture(SiteTree::class, 'numeric0');
$home = $this->objFromFixture('Page', 'home');
$about = $this->objFromFixture('Page', 'about');
$staff = $this->objFromFixture('Page', 'staff');
$product = $this->objFromFixture('Page', 'product1');
$numeric0 = $this->objFromFixture('Page', 'numeric0');
SiteTree::config()->nested_urls = false;
@ -497,11 +489,11 @@ class SiteTreeTest extends SapphireTest
public function testGetByLinkAbsolute()
{
$home = $this->objFromFixture(SiteTree::class, 'home');
$about = $this->objFromFixture(SiteTree::class, 'about');
$staff = $this->objFromFixture(SiteTree::class, 'staff');
$product = $this->objFromFixture(SiteTree::class, 'product1');
$numeric0 = $this->objFromFixture(SiteTree::class, 'numeric0');
$home = $this->objFromFixture('Page', 'home');
$about = $this->objFromFixture('Page', 'about');
$staff = $this->objFromFixture('Page', 'staff');
$product = $this->objFromFixture('Page', 'product1');
$numeric0 = $this->objFromFixture('Page', 'numeric0');
$base = 'https://example.test/';
$this->assertEquals($home->ID, SiteTree::get_by_link(Controller::join_links($base, '/'), false)->ID);
@ -514,51 +506,31 @@ class SiteTreeTest extends SapphireTest
public function testRelativeLink()
{
$about = $this->objFromFixture(SiteTree::class, 'about');
$staff = $this->objFromFixture(SiteTree::class, 'staff');
$numeric0 = $this->objFromFixture(SiteTree::class, 'numeric0');
$about = $this->objFromFixture('Page', 'about');
$staff = $this->objFromFixture('Page', 'staff');
$numeric0 = $this->objFromFixture('Page', 'numeric0');
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(
Controller::normaliseTrailingSlash('0/'),
$numeric0->RelativeLink(),
'Matches URLSegment for segment = 0'
);
$this->assertEquals(
'about-us/edit',
$about->RelativeLink('edit'),
'Matches URLSegment plus parameter on top level'
);
$this->assertEquals(
'about-us/tom&jerry',
$about->RelativeLink('tom&jerry'),
'Doesnt url encode parameter'
);
$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('0/', $numeric0->RelativeLink(), 'Matches URLSegment for segment = 0');
}
public function testPageLevel()
{
$about = $this->objFromFixture(SiteTree::class, 'about');
$staff = $this->objFromFixture(SiteTree::class, 'staff');
$about = $this->objFromFixture('Page', 'about');
$staff = $this->objFromFixture('Page', 'staff');
$this->assertEquals(1, $about->getPageLevel());
$this->assertEquals(2, $staff->getPageLevel());
}
public function testAbsoluteLiveLink()
{
$parent = $this->objFromFixture(SiteTree::class, 'about');
$child = $this->objFromFixture(SiteTree::class, 'staff');
$parent = $this->objFromFixture('Page', 'about');
$child = $this->objFromFixture('Page', 'staff');
Config::modify()->set(SiteTree::class, 'nested_urls', true);
@ -569,23 +541,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 SiteTree();
$parent = new Page();
$parent->Title = 'Parent';
$parent->write();
$child1 = new SiteTree();
$child1 = new Page();
$child1->ParentID = $parent->ID;
$child1->Title = 'Child 1';
$child1->Sort = 2;
$child1->write();
$child2 = new SiteTree();
$child2 = new Page();
$child2->ParentID = $parent->ID;
$child2->Title = 'Child 2';
$child2->Sort = 1;
@ -609,34 +581,34 @@ class SiteTreeTest extends SapphireTest
public function testDeleteFromStageOperatesRecursively()
{
Config::modify()->set(SiteTree::class, 'enforce_strict_hierarchy', false);
$pageAbout = $this->objFromFixture(SiteTree::class, 'about');
$pageStaff = $this->objFromFixture(SiteTree::class, 'staff');
$pageStaffDuplicate = $this->objFromFixture(SiteTree::class, 'staffduplicate');
$pageAbout = $this->objFromFixture('Page', 'about');
$pageStaff = $this->objFromFixture('Page', 'staff');
$pageStaffDuplicate = $this->objFromFixture('Page', 'staffduplicate');
$pageAbout->delete();
$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);
$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);
Config::modify()->set(SiteTree::class, 'enforce_strict_hierarchy', true);
}
public function testDeleteFromStageOperatesRecursivelyStrict()
{
$pageAbout = $this->objFromFixture(SiteTree::class, 'about');
$pageStaff = $this->objFromFixture(SiteTree::class, 'staff');
$pageStaffDuplicate = $this->objFromFixture(SiteTree::class, 'staffduplicate');
$pageAbout = $this->objFromFixture('Page', 'about');
$pageStaff = $this->objFromFixture('Page', 'staff');
$pageStaffDuplicate = $this->objFromFixture('Page', 'staffduplicate');
$pageAbout->delete();
$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));
$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));
}
public function testDuplicate()
{
$pageAbout = $this->objFromFixture(SiteTree::class, 'about');
$pageAbout = $this->objFromFixture('Page', 'about');
$dupe = $pageAbout->duplicate();
$this->assertEquals($pageAbout->Title, $dupe->Title);
$this->assertNotEquals($pageAbout->URLSegment, $dupe->URLSegment);
@ -648,22 +620,22 @@ class SiteTreeTest extends SapphireTest
Config::modify()->set(SiteTree::class, 'enforce_strict_hierarchy', false);
$this->logInWithPermission('ADMIN');
$pageAbout = $this->objFromFixture(SiteTree::class, 'about');
$pageAbout = $this->objFromFixture('Page', 'about');
$pageAbout->publishRecursive();
$pageStaff = $this->objFromFixture(SiteTree::class, 'staff');
$pageStaff = $this->objFromFixture('Page', 'staff');
$pageStaff->publishRecursive();
$pageStaffDuplicate = $this->objFromFixture(SiteTree::class, 'staffduplicate');
$pageStaffDuplicate = $this->objFromFixture('Page', 'staffduplicate');
$pageStaffDuplicate->publishRecursive();
$parentPage = $this->objFromFixture(SiteTree::class, 'about');
$parentPage = $this->objFromFixture('Page', 'about');
$parentPage->doUnpublish();
Versioned::set_stage(Versioned::LIVE);
$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);
$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);
Versioned::set_stage(Versioned::DRAFT);
Config::modify()->set(SiteTree::class, 'enforce_strict_hierarchy', true);
}
@ -673,20 +645,20 @@ class SiteTreeTest extends SapphireTest
Config::modify()->set(SiteTree::class, 'enforce_strict_hierarchy', false);
$this->logInWithPermission('ADMIN');
$pageAbout = $this->objFromFixture(SiteTree::class, 'about');
$pageAbout = $this->objFromFixture('Page', 'about');
$pageAbout->publishRecursive();
$pageStaff = $this->objFromFixture(SiteTree::class, 'staff');
$pageStaff = $this->objFromFixture('Page', 'staff');
$pageStaff->publishRecursive();
$pageStaffDuplicate = $this->objFromFixture(SiteTree::class, 'staffduplicate');
$pageStaffDuplicate = $this->objFromFixture('Page', 'staffduplicate');
$pageStaffDuplicate->publishRecursive();
$parentPage = $this->objFromFixture(SiteTree::class, 'about');
$parentPage = $this->objFromFixture('Page', 'about');
$parentPage->doUnpublish();
Versioned::set_stage(Versioned::LIVE);
$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);
$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);
Versioned::set_stage(Versioned::DRAFT);
Config::modify()->set(SiteTree::class, 'enforce_strict_hierarchy', true);
}
@ -695,20 +667,20 @@ class SiteTreeTest extends SapphireTest
{
$this->logInWithPermission('ADMIN');
$pageAbout = $this->objFromFixture(SiteTree::class, 'about');
$pageAbout = $this->objFromFixture('Page', 'about');
$pageAbout->publishRecursive();
$pageStaff = $this->objFromFixture(SiteTree::class, 'staff');
$pageStaff = $this->objFromFixture('Page', 'staff');
$pageStaff->publishRecursive();
$pageStaffDuplicate = $this->objFromFixture(SiteTree::class, 'staffduplicate');
$pageStaffDuplicate = $this->objFromFixture('Page', 'staffduplicate');
$pageStaffDuplicate->publishRecursive();
$parentPage = $this->objFromFixture(SiteTree::class, 'about');
$parentPage = $this->objFromFixture('Page', 'about');
$parentPage->doUnpublish();
Versioned::set_stage(Versioned::LIVE);
$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));
$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));
Versioned::set_stage(Versioned::DRAFT);
}
@ -719,7 +691,7 @@ class SiteTreeTest extends SapphireTest
public function testReadArchiveDate()
{
DBDatetime::set_mock_now('2009-07-02 14:05:07');
$oldPage = new SiteTree();
$oldPage = SiteTree::create();
$oldPage->Title = 'A really old page';
$oldPage->write();
DBDatetime::clear_mock_now();
@ -727,7 +699,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');
}
@ -736,11 +708,11 @@ class SiteTreeTest extends SapphireTest
{
$editor = $this->objFromFixture(Member::class, "editor");
$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");
$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");
// Test logged out users cannot edit
$this->logOut();
@ -761,7 +733,7 @@ class SiteTreeTest extends SapphireTest
public function testCanEditWithAccessToAllSections()
{
$page = new SiteTree();
$page = new Page();
$page->write();
$allSectionMember = $this->objFromFixture(Member::class, 'allsections');
$securityAdminMember = $this->objFromFixture(Member::class, 'securityadmin');
@ -786,7 +758,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(SiteTree::class, 'about');
$parent = $this->objFromFixture('Page', 'about');
$this->assertFalse(singleton(SiteTree::class)->canCreate(null, ['Parent' => $parent]));
// Test creation underneath a parent which doesn't allow a certain child
@ -802,18 +774,13 @@ 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 SiteTree();
$page = new Page();
$page->write();
$page->CanEditType = "Inherit";
$page->publishRecursive();
@ -861,10 +828,10 @@ class SiteTreeTest extends SapphireTest
public function testCompareVersions()
{
// Necessary to avoid
$oldCleanerClass = HtmlDiff::$html_cleaner_class;
HtmlDiff::$html_cleaner_class = SiteTreeTest_NullHtmlCleaner::class;
$oldCleanerClass = Diff::$html_cleaner_class;
Diff::$html_cleaner_class = SiteTreeTest_NullHtmlCleaner::class;
$page = new SiteTree();
$page = new Page();
$page->write();
$this->assertEquals(1, $page->Version);
@ -882,7 +849,7 @@ class SiteTreeTest extends SapphireTest
$processedContent = preg_replace('/>\s*/', '>', $processedContent ?? '');
$this->assertEquals("<ins><span>This is a test</span></ins>", $processedContent);
HtmlDiff::$html_cleaner_class = $oldCleanerClass;
Diff::$html_cleaner_class = $oldCleanerClass;
}
public function testAuthorIDAndPublisherIDFilledOutOnPublish()
@ -892,16 +859,16 @@ class SiteTreeTest extends SapphireTest
$this->logInAs($member);
// Write the page
$about = $this->objFromFixture(SiteTree::class, 'about');
$about = $this->objFromFixture('Page', '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]
)->record();
)->first();
$this->assertEquals($member->ID, $savedVersion['AuthorID']);
$this->assertEquals(0, $savedVersion['PublisherID']);
@ -909,9 +876,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]
)->record();
)->first();
// Check the version created
$this->assertEquals($member->ID, $publishedVersion['AuthorID']);
@ -920,7 +887,7 @@ class SiteTreeTest extends SapphireTest
public function testLinkShortcodeHandler()
{
$aboutPage = $this->objFromFixture(SiteTree::class, 'about');
$aboutPage = $this->objFromFixture('Page', 'about');
$redirectPage = $this->objFromFixture(RedirectorPage::class, 'external');
$parser = new ShortcodeParser();
@ -932,24 +899,12 @@ 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"]';
@ -963,10 +918,7 @@ 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"]'));
@ -975,8 +927,8 @@ class SiteTreeTest extends SapphireTest
public function testIsCurrent()
{
$aboutPage = $this->objFromFixture(SiteTree::class, 'about');
$productPage = $this->objFromFixture(SiteTree::class, 'products');
$aboutPage = $this->objFromFixture('Page', 'about');
$productPage = $this->objFromFixture('Page', 'products');
Director::set_current_page($aboutPage);
$this->assertTrue($aboutPage->isCurrent(), 'Assert that basic isCurrent checks works.');
@ -984,7 +936,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.'
);
@ -995,9 +947,9 @@ class SiteTreeTest extends SapphireTest
public function testIsSection()
{
$about = $this->objFromFixture(SiteTree::class, 'about');
$staff = $this->objFromFixture(SiteTree::class, 'staff');
$ceo = $this->objFromFixture(SiteTree::class, 'ceo');
$about = $this->objFromFixture('Page', 'about');
$staff = $this->objFromFixture('Page', 'staff');
$ceo = $this->objFromFixture('Page', 'ceo');
Director::set_current_page($about);
$this->assertTrue($about->isSection());
@ -1017,7 +969,7 @@ class SiteTreeTest extends SapphireTest
public function testURLSegmentReserved()
{
$siteTree = new SiteTree(['URLSegment' => 'admin']);
$siteTree = SiteTree::create(['URLSegment' => 'admin']);
$segment = $siteTree->validURLSegment();
$this->assertFalse($segment);
@ -1105,7 +1057,7 @@ class SiteTreeTest extends SapphireTest
$sitetree->URLSegment = 'home-noconflict';
$this->assertTrue($sitetree->validURLSegment());
$sitetree->ParentID = $this->idFromFixture(SiteTree::class, 'about');
$sitetree->ParentID = $this->idFromFixture('Page', 'about');
$sitetree->URLSegment = 'home';
$this->assertFalse($sitetree->validURLSegment(), 'Conflicts are still recognised with a ParentID value');
@ -1115,7 +1067,7 @@ class SiteTreeTest extends SapphireTest
$sitetree->URLSegment = 'home';
$this->assertFalse($sitetree->validURLSegment(), 'URLSegment conflicts are recognised');
$sitetree->ParentID = $this->idFromFixture(SiteTree::class, 'about');
$sitetree->ParentID = $this->idFromFixture('Page', 'about');
$this->assertTrue($sitetree->validURLSegment(), 'URLSegments can be the same across levels');
$sitetree->URLSegment = 'my-staff';
@ -1185,18 +1137,13 @@ 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 SiteTree();
$p = new Page();
$p->Content = "one";
$p->write();
$this->assertEquals(1, $p->Version);
@ -1284,23 +1231,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'
]
];
}
@ -1341,10 +1288,7 @@ 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()
@ -1363,8 +1307,8 @@ class SiteTreeTest extends SapphireTest
$this->assertArrayHasKey(SiteTreeTest_ClassA::class, $method->invoke($sitetree));
$this->logInWithPermission('ADMIN');
$rootPage = $this->objFromFixture(SiteTree::class, 'home');
$nonRootPage = $this->objFromFixture(SiteTree::class, 'staff');
$rootPage = $this->objFromFixture(Page::class, 'home');
$nonRootPage = $this->objFromFixture(Page::class, 'staff');
$this->assertArrayNotHasKey(SiteTreeTest_NotRoot::class, $method->invoke($rootPage));
$this->assertArrayHasKey(SiteTreeTest_NotRoot::class, $method->invoke($nonRootPage));
@ -1460,11 +1404,11 @@ class SiteTreeTest extends SapphireTest
public function testGetBreadcrumbItems()
{
$page = $this->objFromFixture(SiteTree::class, "breadcrumbs");
$page = $this->objFromFixture("Page", "breadcrumbs");
$this->assertEquals(1, $page->getBreadcrumbItems()->count(), "Only display current page.");
// Test breadcrumb order
$page = $this->objFromFixture(SiteTree::class, "breadcrumbs5");
$page = $this->objFromFixture("Page", "breadcrumbs5");
$breadcrumbs = $page->getBreadcrumbItems();
$this->assertEquals($breadcrumbs->count(), 5, "Display all breadcrumbs");
$this->assertEquals($breadcrumbs->first()->Title, "Breadcrumbs", "Breadcrumbs should be the first item.");
@ -1484,16 +1428,16 @@ class SiteTreeTest extends SapphireTest
public function testMetaTags()
{
$this->logInWithPermission('ADMIN');
$page = $this->objFromFixture(SiteTree::class, 'metapage');
$page = $this->objFromFixture('Page', '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
@ -1508,7 +1452,7 @@ class SiteTreeTest extends SapphireTest
{
$this->logInWithPermission('ADMIN');
/** @var SiteTree $page */
$page = $this->objFromFixture(SiteTree::class, 'metapage');
$page = $this->objFromFixture('Page', 'metapage');
$charset = Config::inst()->get(ContentNegotiator::class, 'encoding');
@ -1590,8 +1534,8 @@ class SiteTreeTest extends SapphireTest
// both pages are viewable in stage
Versioned::set_stage(Versioned::DRAFT);
$about = $this->objFromFixture(SiteTree::class, 'about');
$staff = $this->objFromFixture(SiteTree::class, 'staff');
$about = $this->objFromFixture('Page', 'about');
$staff = $this->objFromFixture('Page', 'staff');
$this->assertFalse($about->isOrphaned());
$this->assertFalse($staff->isOrphaned());
$this->assertTrue($about->canView($member));
@ -1602,23 +1546,23 @@ class SiteTreeTest extends SapphireTest
$this->assertFalse($staff->isOrphaned());
$this->assertTrue($staff->canView($member));
Versioned::set_stage(Versioned::LIVE);
$staff = $this->objFromFixture(SiteTree::class, 'staff'); // Live copy of page
$staff = $this->objFromFixture('Page', '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(SiteTree::class, 'about');
$about = $this->objFromFixture('Page', 'about');
$about->copyVersionToStage(Versioned::DRAFT, Versioned::LIVE);
Versioned::set_stage(Versioned::LIVE);
$staff = $this->objFromFixture(SiteTree::class, 'staff');
$staff = $this->objFromFixture('Page', '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(SiteTree::class, 'staff');
$staff = $this->objFromFixture('Page', 'staff');
$this->assertFalse($staff->isOrphaned());
$this->assertTrue($staff->canView($member));
@ -1633,8 +1577,8 @@ class SiteTreeTest extends SapphireTest
{
$this->logInWithPermission('ADMIN');
/** @var SiteTree $page */
$page = $this->objFromFixture(SiteTree::class, 'home');
/** @var Page $page */
$page = $this->objFromFixture('Page', 'home');
$this->assertTrue($page->canAddChildren());
$this->assertTrue($page->isOnDraft());
$this->assertFalse($page->isPublished());
@ -1711,24 +1655,23 @@ class SiteTreeTest extends SapphireTest
}
/**
* Test that the controller name for a Page instance can be gathered by appending "Controller" to the Page
* Test that the controller name for a SiteTree instance can be gathered by appending "Controller" to the SiteTree
* class name in a PSR-2 compliant manner.
*/
public function testGetControllerName()
{
$page = new Page();
$this->assertSame(PageController::class, $page->getControllerName());
$class = new Page;
$this->assertSame('PageController', $class->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(SiteTree::class, 'controller_name', 'This\\Is\\A\\New\\Controller');
$page = new SiteTree();
$this->assertSame('This\\Is\\A\\New\\Controller', $page->getControllerName());
Config::inst()->set(Page::class, 'controller_name', 'This\\Is\\A\\New\\Controller');
$class = new Page;
$this->assertSame('This\\Is\\A\\New\\Controller', $class->getControllerName());
}
/**
@ -1744,9 +1687,21 @@ 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 = new SiteTree();
$siteTree = SiteTree::create();
$user = $this->objFromFixture(Member::class, 'allsections');
Security::setCurrentUser($user);
$pageClass = array_values(SiteTree::page_type_classes())[0];
@ -1833,7 +1788,7 @@ class SiteTreeTest extends SapphireTest
}
// Create new page on DRAFT
$page = new SiteTree();
$page = SiteTree::create();
$page->Content = $content;
$page->write();
@ -1849,7 +1804,7 @@ class SiteTreeTest extends SapphireTest
public function testGetCMSActions()
{
// Create new page on DRAFT
$page = new SiteTree();
$page = SiteTree::create();
$page->Content = md5(rand(0, PHP_INT_MAX));
$page->write();
@ -1979,7 +1934,7 @@ class SiteTreeTest extends SapphireTest
public function testGetCMSActionsWithoutForms()
{
// Create new page on DRAFT
$page = new SiteTree();
$page = SiteTree::create();
$page->Content = md5(rand(0, PHP_INT_MAX));
$page->write();
@ -2085,10 +2040,20 @@ 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();
$this->assertSame($expected, $siteTree->ExtraMeta, $message);
if (!$isDodgyAndUsingHTML5) {
$this->assertSame($expected, $siteTree->ExtraMeta, $message);
}
}
public function provideSanitiseExtraMeta(): array
@ -2118,12 +2083,50 @@ 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=""><onmouseover></onmouseover>',
'Invalid HTML is converted to valid HTML and parsed'
],
'<link rel="canonical" href="valid" ;;// somethingdodgy < onmouseover=alert(1)',
'<link rel="canonical" href="valid" somethingdodgy="">'
]
];
}

26
tests/php/Model/SiteTreeTest.yml Normal file → Executable file
View File

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

View File

@ -3,9 +3,9 @@
namespace SilverStripe\CMS\Tests\Model;
use SilverStripe\Dev\TestOnly;
use SilverStripe\CMS\Model\SiteTree;
use Page;
class SiteTreeTest_AdminDenied extends SiteTree implements TestOnly
class SiteTreeTest_AdminDenied extends Page implements TestOnly
{
private static $table_name = 'SiteTreeTest_AdminDenied';

View File

@ -3,18 +3,18 @@
namespace SilverStripe\CMS\Tests\Model;
use SilverStripe\Dev\TestOnly;
use SilverStripe\CMS\Model\SiteTree;
use Page;
class SiteTreeTest_ClassA extends SiteTree implements TestOnly
class SiteTreeTest_ClassA extends Page 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 SilverStripe\CMS\Model\SiteTree;
use Page;
class SiteTreeTest_ClassB extends SiteTree implements TestOnly
class SiteTreeTest_ClassB extends Page 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 SilverStripe\CMS\Model\SiteTree;
use Page;
class SiteTreeTest_ClassC extends SiteTree implements TestOnly
class SiteTreeTest_ClassC extends Page 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 SilverStripe\CMS\Model\SiteTree;
use Page;
class SiteTreeTest_ClassD extends SiteTree implements TestOnly
class SiteTreeTest_ClassD extends Page 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 SilverStripe\CMS\Model\SiteTree;
use Page;
class SiteTreeTest_ClassE extends SiteTree implements TestOnly, HiddenClass
class SiteTreeTest_ClassE extends Page 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 SilverStripe\CMS\Model\SiteTree;
use Page;
class SiteTreeTest_Conflicted extends SiteTree implements TestOnly
class SiteTreeTest_Conflicted extends Page implements TestOnly
{
private static $table_name = 'SiteTreeTest_Conflicted';
}

View File

@ -3,12 +3,12 @@
namespace SilverStripe\CMS\Tests\Model;
use SilverStripe\Dev\TestOnly;
use SilverStripe\CMS\Controllers\ContentController;
use PageController;
class SiteTreeTest_ConflictedController extends ContentController implements TestOnly
class SiteTreeTest_ConflictedController extends PageController 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 SilverStripe\CMS\Model\SiteTree;
use Page;
/**
* An empty SiteTree instance with a controller to test that legacy controller names can still be loaded
*/
class SiteTreeTest_LegacyControllerName extends SiteTree implements TestOnly
class SiteTreeTest_LegacyControllerName extends Page implements TestOnly
{
private static $table_name = 'SiteTreeTest_LegacyControllerName';
}

View File

@ -3,8 +3,9 @@
namespace SilverStripe\CMS\Tests\Model;
use SilverStripe\Dev\TestOnly;
use SilverStripe\CMS\Controllers\ContentController;
use PageController;
class SiteTreeTest_LegacyControllerName_Controller extends ContentController implements TestOnly
class SiteTreeTest_LegacyControllerName_Controller extends PageController implements TestOnly
{
}

View File

@ -3,9 +3,9 @@
namespace SilverStripe\CMS\Tests\Model;
use SilverStripe\Dev\TestOnly;
use SilverStripe\CMS\Model\SiteTree;
use Page;
class SiteTreeTest_NotRoot extends SiteTree implements TestOnly
class SiteTreeTest_NotRoot extends Page implements TestOnly
{
private static $table_name = 'SiteTreeTest_NotRoot';

View File

@ -3,9 +3,9 @@
namespace SilverStripe\CMS\Tests\Model;
use SilverStripe\Dev\TestOnly;
use SilverStripe\CMS\Model\SiteTree;
use Page;
class SiteTreeTest_PageNode extends SiteTree implements TestOnly
class SiteTreeTest_PageNode extends Page implements TestOnly
{
private static $table_name = 'SiteTreeTest_PageNode';
}

View File

@ -3,8 +3,8 @@
namespace SilverStripe\CMS\Tests\Model;
use SilverStripe\Dev\TestOnly;
use SilverStripe\CMS\Controllers\ContentController;
use PageController;
class SiteTreeTest_PageNodeController extends ContentController implements TestOnly
class SiteTreeTest_PageNodeController extends PageController implements TestOnly
{
}

View File

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

View File

@ -3,12 +3,12 @@
namespace SilverStripe\CMS\Tests\Model;
use SilverStripe\Dev\TestOnly;
use SilverStripe\CMS\Controllers\ContentController;
use PageController;
class VirtualPageTest_ClassAController extends ContentController implements TestOnly
class VirtualPageTest_ClassAController extends PageController 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 SilverStripe\CMS\Model\SiteTree;
use Page;
class VirtualPageTest_ClassB extends SiteTree implements TestOnly
class VirtualPageTest_ClassB extends Page implements TestOnly
{
private static $table_name = 'VirtualPageTest_ClassB';

View File

@ -3,9 +3,9 @@
namespace SilverStripe\CMS\Tests\Model;
use SilverStripe\Dev\TestOnly;
use SilverStripe\CMS\Model\SiteTree;
use Page;
class VirtualPageTest_ClassC extends SiteTree implements TestOnly
class VirtualPageTest_ClassC extends Page implements TestOnly
{
private static $table_name = 'VirtualPageTest_ClassC';

View File

@ -3,9 +3,9 @@
namespace SilverStripe\CMS\Tests\Model;
use SilverStripe\Dev\TestOnly;
use SilverStripe\CMS\Model\SiteTree;
use Page;
class VirtualPageTest_NotRoot extends SiteTree implements TestOnly
class VirtualPageTest_NotRoot extends Page implements TestOnly
{
private static $table_name = 'VirtualPageTest_NotRoot';

View File

@ -9,8 +9,7 @@ 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 SilverStripe\CMS\Model\SiteTree;
use Page;
class VirtualPageTest_PageWithAllowedChildren extends SiteTree implements TestOnly
class VirtualPageTest_PageWithAllowedChildren extends Page implements TestOnly
{
private static $table_name = 'VirtualPageTest_PageWithAllowedChildren';

View File

@ -2,22 +2,24 @@
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;
@ -27,19 +29,14 @@ 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."'");
}
/**
@ -55,27 +52,13 @@ 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()
@ -98,14 +81,18 @@ class CmsReportsTest extends SapphireTest
/**
* Test the broken links side report.
*/
public function testBrokenLinks()
{
// Create a "draft" page with a broken link.
$page = new SiteTree();
$page = Page::create();
$page->Content = "<a href='[sitetree_link,id=987654321]'>This</a> is a broken link.";
$page->writeToStage('Stage');
// Retrieve the broken links side report.
$reports = Report::get_reports();
$brokenLinksReport = null;
foreach ($reports as $report) {
@ -122,11 +109,12 @@ 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.
@ -135,15 +123,17 @@ 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.
@ -155,14 +145,18 @@ class CmsReportsTest extends SapphireTest
/**
* Test the broken files side report.
*/
public function testBrokenFiles()
{
// Create a "draft" page with a broken file.
$page = new SiteTree();
$page = Page::create();
$page->Content = "<a href='[file_link,id=987654321]'>This</a> is a broken file.";
$page->writeToStage('Stage');
// Retrieve the broken files side report.
$reports = Report::get_reports();
$brokenFilesReport = null;
foreach ($reports as $report) {
@ -179,18 +173,21 @@ 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();
@ -198,15 +195,17 @@ 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);
}
@ -216,12 +215,15 @@ 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) {
@ -238,19 +240,22 @@ 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 = new SiteTree();
$contentPage = Page::create();
$contentPage->Content = 'This is some content.';
$contentPage->writeToStage('Stage');
$contentPage->writeToStage('Live');
@ -258,30 +263,36 @@ 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) {
@ -298,19 +309,22 @@ 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 = new SiteTree();
$contentPage = Page::create();
$contentPage->Content = 'This is some content.';
$contentPage->writeToStage('Stage');
$contentPage->writeToStage('Live');
@ -318,15 +332,17 @@ 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,6 +2,7 @@
namespace SilverStripe\CMS\Tests\Search;
use Page;
use SilverStripe\Assets\File;
use SilverStripe\CMS\Controllers\ContentController;
use SilverStripe\CMS\Controllers\ModelAsController;
@ -21,10 +22,14 @@ use SilverStripe\Versioned\Versioned;
use TractorCow\Fluent\Extension\FluentSiteTreeExtension;
/**
* @todo Fix unpublished pages check in testPublishedPagesMatchedByTitle()
* @todo All tests run on unpublished pages at the moment, due to the searchform not distinguishing between them
*
* Because this manipulates the test database in severe ways, I've renamed the test to force it to run last...
*/
class ZZZSearchFormTest extends FunctionalTest
{
protected static $fixture_file = 'ZZZSearchFormTest.yml';
protected static $illegal_extensions = [
@ -92,6 +97,9 @@ class ZZZSearchFormTest extends FunctionalTest
return $supports;
}
/**
* @skipUpgrade
*/
public function testSearchFormTemplateCanBeChanged()
{
if (!$this->checkFulltextSupport()) {
@ -108,6 +116,9 @@ class ZZZSearchFormTest extends FunctionalTest
);
}
/**
* @skipUpgrade
*/
public function testPublishedPagesMatchedByTitle()
{
if (!$this->checkFulltextSupport()) {
@ -138,6 +149,9 @@ class ZZZSearchFormTest extends FunctionalTest
);
}
/**
* @skipUpgrade
*/
public function testDoubleQuotesPublishedPagesMatchedByTitle()
{
if (!$this->checkFulltextSupport()) {
@ -170,6 +184,9 @@ class ZZZSearchFormTest extends FunctionalTest
);
}
/**
* @skipUpgrade
*/
public function testUnpublishedPagesNotIncluded()
{
if (!$this->checkFulltextSupport()) {

View File

@ -0,0 +1,91 @@
<?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

@ -0,0 +1,67 @@
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

@ -0,0 +1,113 @@
<?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

@ -0,0 +1,32 @@
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,8 +1,19 @@
const Path = require('path');
const { JavascriptWebpackConfig, CssWebpackConfig } = require('@silverstripe/webpack-config');
const webpackConfig = 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'),
@ -10,37 +21,46 @@ const PATHS = {
};
const config = [
// Main JS bundles
new JavascriptWebpackConfig('js', PATHS, 'silverstripe/cms')
.setEntry({
{
name: 'js',
entry: {
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`,
})
.mergeConfig({
plugins: [
new CopyWebpackPlugin({
patterns: [
{
from: `${PATHS.SRC}/images`,
to: `${PATHS.DIST}/images`
},
]
}),
],
})
.getConfig(),
// sass to css
new CssWebpackConfig('css', PATHS)
.setEntry({
},
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: {
bundle: `${PATHS.SRC}/styles/bundle.scss`,
SilverStripeNavigator: `${PATHS.SRC}/styles/SilverStripeNavigator.scss`,
})
.getConfig(),
},
output: {
path: PATHS.DIST,
filename: 'styles/[name].css',
},
devtool: (ENV !== 'production') ? 'source-map' : '',
module: moduleCSS(ENV, PATHS),
plugins: pluginCSS(ENV, PATHS),
},
];
// Use WEBPACK_CHILD=js or WEBPACK_CHILD=css env var to run a single config
module.exports = (process.env.WEBPACK_CHILD)
? config.find((entry) => entry.name === process.env.WEBPACK_CHILD)
: config;
: module.exports = config;

14782
yarn.lock

File diff suppressed because it is too large Load Diff