mirror of
https://github.com/silverstripe/silverstripe-cms
synced 2024-10-22 08:05:56 +02:00
API Remove legacy tasks
This commit is contained in:
parent
61d729285f
commit
1bd578fc96
@ -56,9 +56,6 @@ mappings:
|
|||||||
SideReport_RecentlyEdited: SilverStripe\CMS\Reports\RecentlyEditedReport
|
SideReport_RecentlyEdited: SilverStripe\CMS\Reports\RecentlyEditedReport
|
||||||
ContentControllerSearchExtension: SilverStripe\CMS\Search\ContentControllerSearchExtension
|
ContentControllerSearchExtension: SilverStripe\CMS\Search\ContentControllerSearchExtension
|
||||||
SearchForm: SilverStripe\CMS\Search\SearchForm
|
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
|
AssetAdmin: SilverStripe\AssetAdmin\Controller\AssetAdmin
|
||||||
AssetTableField: SilverStripe\AssetAdmin\Controller\AssetAdmin
|
AssetTableField: SilverStripe\AssetAdmin\Controller\AssetAdmin
|
||||||
CMSAddPageController: SilverStripe\CMS\Controllers\CMSPageAddController
|
CMSAddPageController: SilverStripe\CMS\Controllers\CMSPageAddController
|
||||||
@ -142,8 +139,6 @@ mappings:
|
|||||||
CmsReportsTest: SilverStripe\CMS\Tests\CmsReportsTest
|
CmsReportsTest: SilverStripe\CMS\Tests\CmsReportsTest
|
||||||
CMSMainSearchFormTest: SilverStripe\CMS\Tests\CMSMainSearchFormTest
|
CMSMainSearchFormTest: SilverStripe\CMS\Tests\CMSMainSearchFormTest
|
||||||
ZZZSearchFormTest: SilverStripe\CMS\Tests\ZZZSearchFormTest
|
ZZZSearchFormTest: SilverStripe\CMS\Tests\ZZZSearchFormTest
|
||||||
MigrateSiteTreeLinkingTaskTest: SilverStripe\CMS\Tests\MigrateSiteTreeLinkingTaskTest
|
|
||||||
RemoveOrphanedPagesTaskTest: SilverStripe\CMS\Tests\RemoveOrphanedPagesTaskTest
|
|
||||||
excludedPaths:
|
excludedPaths:
|
||||||
- '*/_config/legacy.yml'
|
- '*/_config/legacy.yml'
|
||||||
warnings:
|
warnings:
|
||||||
|
@ -5,8 +5,6 @@ After: '#coreroutes'
|
|||||||
SilverStripe\Control\Director:
|
SilverStripe\Control\Director:
|
||||||
rules:
|
rules:
|
||||||
'': 'SilverStripe\CMS\Controllers\RootURLController'
|
'': 'SilverStripe\CMS\Controllers\RootURLController'
|
||||||
'RemoveOrphanedPagesTask//$Action/$ID/$OtherID': 'SilverStripe\CMS\Tasks\RemoveOrphanedPagesTask'
|
|
||||||
'SiteTreeMaintenanceTask//$Action/$ID/$OtherID': 'SilverStripe\CMS\Tasks\SiteTreeMaintenanceTask'
|
|
||||||
---
|
---
|
||||||
Name: legacycmsroutes
|
Name: legacycmsroutes
|
||||||
---
|
---
|
||||||
|
@ -9,8 +9,6 @@ use SilverStripe\ORM\DataObject;
|
|||||||
*
|
*
|
||||||
* @method DataObject Parent() Parent object
|
* @method DataObject Parent() Parent object
|
||||||
* @method SiteTree Linked() Page being linked to
|
* @method SiteTree Linked() Page being linked to
|
||||||
*
|
|
||||||
* Run `MigrateSiteTreeLinkingTask` to migrate from old table to this.
|
|
||||||
*/
|
*/
|
||||||
class SiteTreeLink extends DataObject
|
class SiteTreeLink extends DataObject
|
||||||
{
|
{
|
||||||
|
@ -1,58 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace SilverStripe\CMS\Tasks;
|
|
||||||
|
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
|
||||||
use SilverStripe\Dev\BuildTask;
|
|
||||||
use SilverStripe\Dev\Debug;
|
|
||||||
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
|
|
||||||
*/
|
|
||||||
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 run($request)
|
|
||||||
{
|
|
||||||
// Ensure legacy table exists
|
|
||||||
$exists = DB::get_conn()->getSchemaManager()->hasTable('SiteTree_LinkTracking');
|
|
||||||
if (!$exists) {
|
|
||||||
DB::alteration_message("Table SiteTree_LinkTracking has already been migrated, or doesn't exist");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$pages = 0;
|
|
||||||
|
|
||||||
// Ensure sync occurs on draft
|
|
||||||
Versioned::withVersionedMode(function () use (&$pages) {
|
|
||||||
Versioned::set_stage(Versioned::DRAFT);
|
|
||||||
|
|
||||||
$sitetreeTbl = DataObject::singleton(SiteTree::class)->baseTable();
|
|
||||||
|
|
||||||
/** @var SiteTree[] $linkedPages */
|
|
||||||
$linkedPages = SiteTree::get()
|
|
||||||
->innerJoin(
|
|
||||||
'SiteTree_LinkTracking',
|
|
||||||
"\"SiteTree_LinkTracking\".\"SiteTreeID\" = \"$sitetreeTbl\".\"ID\""
|
|
||||||
);
|
|
||||||
foreach ($linkedPages as $page) {
|
|
||||||
// Command page to update symlink tracking
|
|
||||||
$page->syncLinkTracking();
|
|
||||||
$pages++;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
DB::alteration_message("Migrated page links on " . SiteTree::singleton()->i18n_pluralise($pages));
|
|
||||||
|
|
||||||
// Disable table to prevent double-migration
|
|
||||||
DB::dont_require_table('SiteTree_LinkTracking');
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,376 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace SilverStripe\CMS\Tasks;
|
|
||||||
|
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
|
||||||
use SilverStripe\Control\Controller;
|
|
||||||
use SilverStripe\Forms\CheckboxSetField;
|
|
||||||
use SilverStripe\Forms\FieldList;
|
|
||||||
use SilverStripe\Forms\Form;
|
|
||||||
use SilverStripe\Forms\FormAction;
|
|
||||||
use SilverStripe\Forms\HeaderField;
|
|
||||||
use SilverStripe\Forms\LiteralField;
|
|
||||||
use SilverStripe\Forms\OptionsetField;
|
|
||||||
use SilverStripe\ORM\ArrayList;
|
|
||||||
use SilverStripe\ORM\DataObject;
|
|
||||||
use SilverStripe\ORM\SS_List;
|
|
||||||
use SilverStripe\Versioned\Versioned;
|
|
||||||
use SilverStripe\Security\Permission;
|
|
||||||
use SilverStripe\Security\Security;
|
|
||||||
use SilverStripe\View\Requirements;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
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;
|
|
||||||
|
|
||||||
protected function init()
|
|
||||||
{
|
|
||||||
parent::init();
|
|
||||||
|
|
||||||
if (!Permission::check('ADMIN')) {
|
|
||||||
Security::permissionFailure($this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public function Link($action = null)
|
|
||||||
{
|
|
||||||
/** @skipUpgrade */
|
|
||||||
return Controller::join_links('RemoveOrphanedPagesTask', $action, '/');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function index()
|
|
||||||
{
|
|
||||||
Requirements::javascript('http://code.jquery.com/jquery-1.7.2.min.js');
|
|
||||||
Requirements::customCSS('#OrphanIDs .middleColumn {width: auto;}');
|
|
||||||
Requirements::customCSS('#OrphanIDs label {display: inline;}');
|
|
||||||
|
|
||||||
return $this->renderWith('BlankPage');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function Form()
|
|
||||||
{
|
|
||||||
$fields = new FieldList();
|
|
||||||
$source = [];
|
|
||||||
|
|
||||||
$fields->push(new HeaderField(
|
|
||||||
'Header',
|
|
||||||
_t(__CLASS__ . '.HEADER', 'Remove all orphaned pages task')
|
|
||||||
));
|
|
||||||
$fields->push(new LiteralField(
|
|
||||||
'Description',
|
|
||||||
$this->description
|
|
||||||
));
|
|
||||||
|
|
||||||
$orphans = $this->getOrphanedPages($this->orphanedSearchClass);
|
|
||||||
if ($orphans) {
|
|
||||||
foreach ($orphans as $orphan) {
|
|
||||||
/** @var SiteTree $latestVersion */
|
|
||||||
$latestVersion = Versioned::get_latest_version($this->orphanedSearchClass, $orphan->ID);
|
|
||||||
$latestAuthor = DataObject::get_by_id('SilverStripe\\Security\\Member', $latestVersion->AuthorID);
|
|
||||||
$orphanBaseTable = DataObject::getSchema()->baseDataTable($this->orphanedSearchClass);
|
|
||||||
$liveRecord = Versioned::get_one_by_stage(
|
|
||||||
$this->orphanedSearchClass,
|
|
||||||
'Live',
|
|
||||||
["\"$orphanBaseTable\".\"ID\"" => $orphan->ID]
|
|
||||||
);
|
|
||||||
$label = sprintf(
|
|
||||||
'<a href="admin/pages/edit/show/%d">%s</a> <small>(#%d, Last Modified Date: %s, Last Modifier: %s, %s)</small>',
|
|
||||||
$orphan->ID,
|
|
||||||
$orphan->Title,
|
|
||||||
$orphan->ID,
|
|
||||||
$orphan->dbObject('LastEdited')->Nice(),
|
|
||||||
($latestAuthor) ? $latestAuthor->Title : 'unknown',
|
|
||||||
($liveRecord) ? 'is published' : 'not published'
|
|
||||||
);
|
|
||||||
$source[$orphan->ID] = $label;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($orphans && $orphans->count()) {
|
|
||||||
$fields->push(new CheckboxSetField('OrphanIDs', false, $source));
|
|
||||||
$fields->push(new LiteralField(
|
|
||||||
'SelectAllLiteral',
|
|
||||||
sprintf(
|
|
||||||
'<p><a href="#" onclick="javascript:jQuery(\'#Form_Form_OrphanIDs :checkbox\').attr(\'checked\', \'checked\'); return false;">%s</a> ',
|
|
||||||
_t(__CLASS__ . '.SELECTALL', 'select all')
|
|
||||||
)
|
|
||||||
));
|
|
||||||
$fields->push(new LiteralField(
|
|
||||||
'UnselectAllLiteral',
|
|
||||||
sprintf(
|
|
||||||
'<a href="#" onclick="javascript:jQuery(\'#Form_Form_OrphanIDs :checkbox\').attr(\'checked\', \'\'); return false;">%s</a></p>',
|
|
||||||
_t(__CLASS__ . '.UNSELECTALL', 'unselect all')
|
|
||||||
)
|
|
||||||
));
|
|
||||||
$fields->push(new OptionsetField(
|
|
||||||
'OrphanOperation',
|
|
||||||
_t('SilverStripe\\CMS\\Tasks\\RemoveOrphanedPagesTask.CHOOSEOPERATION', 'Choose operation:'),
|
|
||||||
[
|
|
||||||
'rebase' => _t(
|
|
||||||
__CLASS__ . '.OPERATION_REBASE',
|
|
||||||
sprintf(
|
|
||||||
'Rebase selected to a new holder page "%s" and unpublish. None of these pages will show up for website visitors.',
|
|
||||||
$this->rebaseHolderTitle()
|
|
||||||
)
|
|
||||||
),
|
|
||||||
'remove' => _t(__CLASS__ . '.OPERATION_REMOVE', 'Remove selected from all stages (WARNING: Will destroy all selected pages from both stage and live)'),
|
|
||||||
],
|
|
||||||
'rebase'
|
|
||||||
));
|
|
||||||
$fields->push(new LiteralField(
|
|
||||||
'Warning',
|
|
||||||
sprintf(
|
|
||||||
'<p class="message">%s</p>',
|
|
||||||
_t(
|
|
||||||
__CLASS__ . '.DELETEWARNING',
|
|
||||||
'Warning: These operations are not reversible. Please handle with care.'
|
|
||||||
)
|
|
||||||
)
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
$fields->push(new LiteralField(
|
|
||||||
'NotFoundLabel',
|
|
||||||
sprintf(
|
|
||||||
'<p class="message">%s</p>',
|
|
||||||
_t(__CLASS__ . '.NONEFOUND', 'No orphans found')
|
|
||||||
)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
$form = new Form(
|
|
||||||
$this,
|
|
||||||
'SilverStripe\\Forms\\Form',
|
|
||||||
$fields,
|
|
||||||
new FieldList(
|
|
||||||
new FormAction('doSubmit', _t(__CLASS__ . '.BUTTONRUN', 'Run'))
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!$orphans || !$orphans->count()) {
|
|
||||||
$form->makeReadonly();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $form;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function run($request)
|
|
||||||
{
|
|
||||||
// @todo Merge with BuildTask functionality
|
|
||||||
}
|
|
||||||
|
|
||||||
public function doSubmit($data, $form)
|
|
||||||
{
|
|
||||||
set_time_limit(60*10); // 10 minutes
|
|
||||||
|
|
||||||
if (!isset($data['OrphanIDs']) || !isset($data['OrphanOperation'])) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
$successIDs = null;
|
|
||||||
switch ($data['OrphanOperation']) {
|
|
||||||
case 'remove':
|
|
||||||
$successIDs = $this->removeOrphans($data['OrphanIDs']);
|
|
||||||
break;
|
|
||||||
case 'rebase':
|
|
||||||
$successIDs = $this->rebaseOrphans($data['OrphanIDs']);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new \InvalidArgumentException(sprintf("Unknown operation: '%s'", $data['OrphanOperation']));
|
|
||||||
}
|
|
||||||
|
|
||||||
$content = '';
|
|
||||||
if ($successIDs) {
|
|
||||||
$content .= "<ul>";
|
|
||||||
foreach ($successIDs as $id => $label) {
|
|
||||||
$content .= sprintf('<li>%s</li>', $label);
|
|
||||||
}
|
|
||||||
$content .= "</ul>";
|
|
||||||
} else {
|
|
||||||
$content = _t(__CLASS__ . '.NONEREMOVED', 'None removed');
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->customise([
|
|
||||||
'Content' => $content,
|
|
||||||
'Form' => ' '
|
|
||||||
])->renderWith('BlankPage');
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function removeOrphans($orphanIDs)
|
|
||||||
{
|
|
||||||
$removedOrphans = [];
|
|
||||||
$orphanBaseTable = DataObject::getSchema()->baseDataTable($this->orphanedSearchClass);
|
|
||||||
foreach ($orphanIDs as $id) {
|
|
||||||
/** @var SiteTree $stageRecord */
|
|
||||||
$stageRecord = Versioned::get_one_by_stage(
|
|
||||||
$this->orphanedSearchClass,
|
|
||||||
Versioned::DRAFT,
|
|
||||||
["\"$orphanBaseTable\".\"ID\"" => $id]
|
|
||||||
);
|
|
||||||
if ($stageRecord) {
|
|
||||||
$removedOrphans[$stageRecord->ID] = sprintf('Removed %s (#%d) from Stage', $stageRecord->Title, $stageRecord->ID);
|
|
||||||
$stageRecord->delete();
|
|
||||||
$stageRecord->destroy();
|
|
||||||
unset($stageRecord);
|
|
||||||
}
|
|
||||||
/** @var SiteTree $liveRecord */
|
|
||||||
$liveRecord = Versioned::get_one_by_stage(
|
|
||||||
$this->orphanedSearchClass,
|
|
||||||
Versioned::LIVE,
|
|
||||||
["\"$orphanBaseTable\".\"ID\"" => $id]
|
|
||||||
);
|
|
||||||
if ($liveRecord) {
|
|
||||||
$removedOrphans[$liveRecord->ID] = sprintf('Removed %s (#%d) from Live', $liveRecord->Title, $liveRecord->ID);
|
|
||||||
$liveRecord->doUnpublish();
|
|
||||||
$liveRecord->destroy();
|
|
||||||
unset($liveRecord);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $removedOrphans;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function rebaseHolderTitle()
|
|
||||||
{
|
|
||||||
return sprintf('Rebased Orphans (%s)', date('d/m/Y g:ia', time()));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function rebaseOrphans($orphanIDs)
|
|
||||||
{
|
|
||||||
$holder = new SiteTree();
|
|
||||||
$holder->ShowInMenus = 0;
|
|
||||||
$holder->ShowInSearch = 0;
|
|
||||||
$holder->ParentID = 0;
|
|
||||||
$holder->Title = $this->rebaseHolderTitle();
|
|
||||||
$holder->write();
|
|
||||||
|
|
||||||
$removedOrphans = [];
|
|
||||||
$orphanBaseTable = DataObject::getSchema()->baseDataTable($this->orphanedSearchClass);
|
|
||||||
foreach ($orphanIDs as $id) {
|
|
||||||
/** @var SiteTree $stageRecord */
|
|
||||||
$stageRecord = Versioned::get_one_by_stage(
|
|
||||||
$this->orphanedSearchClass,
|
|
||||||
'Stage',
|
|
||||||
["\"$orphanBaseTable\".\"ID\"" => $id]
|
|
||||||
);
|
|
||||||
if ($stageRecord) {
|
|
||||||
$removedOrphans[$stageRecord->ID] = sprintf('Rebased %s (#%d)', $stageRecord->Title, $stageRecord->ID);
|
|
||||||
$stageRecord->ParentID = $holder->ID;
|
|
||||||
$stageRecord->ShowInMenus = 0;
|
|
||||||
$stageRecord->ShowInSearch = 0;
|
|
||||||
$stageRecord->write();
|
|
||||||
$stageRecord->doUnpublish();
|
|
||||||
$stageRecord->destroy();
|
|
||||||
//unset($stageRecord);
|
|
||||||
}
|
|
||||||
/** @var SiteTree $liveRecord */
|
|
||||||
$liveRecord = Versioned::get_one_by_stage(
|
|
||||||
$this->orphanedSearchClass,
|
|
||||||
'Live',
|
|
||||||
["\"$orphanBaseTable\".\"ID\"" => $id]
|
|
||||||
);
|
|
||||||
if ($liveRecord) {
|
|
||||||
$removedOrphans[$liveRecord->ID] = sprintf('Rebased %s (#%d)', $liveRecord->Title, $liveRecord->ID);
|
|
||||||
$liveRecord->ParentID = $holder->ID;
|
|
||||||
$liveRecord->ShowInMenus = 0;
|
|
||||||
$liveRecord->ShowInSearch = 0;
|
|
||||||
$liveRecord->write();
|
|
||||||
if (!$stageRecord) {
|
|
||||||
$liveRecord->doRestoreToStage();
|
|
||||||
}
|
|
||||||
$liveRecord->doUnpublish();
|
|
||||||
$liveRecord->destroy();
|
|
||||||
unset($liveRecord);
|
|
||||||
}
|
|
||||||
if ($stageRecord) {
|
|
||||||
unset($stageRecord);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return $removedOrphans;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets all orphans from "Stage" and "Live" stages.
|
|
||||||
*
|
|
||||||
* @param string $class
|
|
||||||
* @param array $filter
|
|
||||||
* @param string $sort
|
|
||||||
* @param string $join
|
|
||||||
* @param int|array $limit
|
|
||||||
* @return SS_List
|
|
||||||
*/
|
|
||||||
public function getOrphanedPages($class = SiteTree::class, $filter = [], $sort = null, $join = null, $limit = null)
|
|
||||||
{
|
|
||||||
// Alter condition
|
|
||||||
$table = DataObject::getSchema()->tableName($class);
|
|
||||||
if (empty($filter)) {
|
|
||||||
$where = [];
|
|
||||||
} elseif (is_array($filter)) {
|
|
||||||
$where = $filter;
|
|
||||||
} else {
|
|
||||||
$where = [$filter];
|
|
||||||
}
|
|
||||||
$where[] = ["\"{$table}\".\"ParentID\" != ?" => 0];
|
|
||||||
$where[] = '"Parents"."ID" IS NULL';
|
|
||||||
|
|
||||||
$orphans = new ArrayList();
|
|
||||||
foreach ([Versioned::DRAFT, Versioned::LIVE] as $stage) {
|
|
||||||
$table .= ($stage == Versioned::LIVE) ? '_Live' : '';
|
|
||||||
$stageOrphans = Versioned::get_by_stage(
|
|
||||||
$class,
|
|
||||||
$stage,
|
|
||||||
$where,
|
|
||||||
$sort,
|
|
||||||
null,
|
|
||||||
$limit
|
|
||||||
)->leftJoin($table, "\"$table\".\"ParentID\" = \"Parents\".\"ID\"", "Parents");
|
|
||||||
$orphans->merge($stageOrphans);
|
|
||||||
}
|
|
||||||
|
|
||||||
$orphans->removeDuplicates();
|
|
||||||
|
|
||||||
return $orphans;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace SilverStripe\CMS\Tasks;
|
|
||||||
|
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
|
||||||
use SilverStripe\Control\Controller;
|
|
||||||
use SilverStripe\ORM\DB;
|
|
||||||
use SilverStripe\ORM\DataObject;
|
|
||||||
|
|
||||||
class SiteTreeMaintenanceTask extends Controller
|
|
||||||
{
|
|
||||||
private static $allowed_actions = [
|
|
||||||
'*' => 'ADMIN'
|
|
||||||
];
|
|
||||||
|
|
||||||
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, '/');
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,87 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace SilverStripe\CMS\Tests\Tasks;
|
|
||||||
|
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
|
||||||
use SilverStripe\CMS\Tasks\MigrateSiteTreeLinkingTask;
|
|
||||||
use SilverStripe\Dev\SapphireTest;
|
|
||||||
use SilverStripe\ORM\DataObject;
|
|
||||||
use SilverStripe\ORM\DB;
|
|
||||||
|
|
||||||
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
|
|
||||||
{
|
|
||||||
parent::setUp();
|
|
||||||
|
|
||||||
// Manually bootstrap all Content blocks with soft coded IDs (raw sql to avoid save hooks)
|
|
||||||
$replacements = [
|
|
||||||
'$$ABOUTID$$' => $this->idFromFixture(SiteTree::class, 'about'),
|
|
||||||
'$$HOMEID$$' => $this->idFromFixture(SiteTree::class, 'home'),
|
|
||||||
'$$STAFFID$$' => $this->idFromFixture(SiteTree::class, 'staff'),
|
|
||||||
];
|
|
||||||
foreach (DB::query('SELECT "ID", "Content" FROM "SiteTree"') as $row) {
|
|
||||||
$id = (int)$row['ID'];
|
|
||||||
$content = str_replace(array_keys($replacements ?? []), array_values($replacements ?? []), $row['Content'] ?? '');
|
|
||||||
DB::prepared_query('UPDATE "SiteTree" SET "Content" = ? WHERE "ID" = ?', [$content, $id]);
|
|
||||||
}
|
|
||||||
DataObject::reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testLinkingMigration()
|
|
||||||
{
|
|
||||||
ob_start();
|
|
||||||
|
|
||||||
DB::quiet(false);
|
|
||||||
$task = new MigrateSiteTreeLinkingTask();
|
|
||||||
$task->run(null);
|
|
||||||
$this->assertStringContainsString(
|
|
||||||
"Migrated page links on 5 Pages",
|
|
||||||
ob_get_contents(),
|
|
||||||
'Rewritten links are correctly reported'
|
|
||||||
);
|
|
||||||
DB::quiet(true);
|
|
||||||
ob_end_clean();
|
|
||||||
|
|
||||||
// Query links for pages
|
|
||||||
/** @var SiteTree $home */
|
|
||||||
$home = $this->objFromFixture(SiteTree::class, 'home');
|
|
||||||
/** @var SiteTree $about */
|
|
||||||
$about = $this->objFromFixture(SiteTree::class, 'about');
|
|
||||||
/** @var SiteTree $staff */
|
|
||||||
$staff = $this->objFromFixture(SiteTree::class, 'staff');
|
|
||||||
/** @var SiteTree $action */
|
|
||||||
$action = $this->objFromFixture(SiteTree::class, 'action');
|
|
||||||
/** @var SiteTree $hash */
|
|
||||||
$hash = $this->objFromFixture(SiteTree::class, 'hash_link');
|
|
||||||
|
|
||||||
// Ensure all links are created
|
|
||||||
$this->assertListEquals([['ID' => $about->ID], ['ID' => $staff->ID]], $home->LinkTracking());
|
|
||||||
$this->assertListEquals([['ID' => $home->ID], ['ID' => $staff->ID]], $about->LinkTracking());
|
|
||||||
$this->assertListEquals([['ID' => $home->ID], ['ID' => $about->ID]], $staff->LinkTracking());
|
|
||||||
$this->assertListEquals([['ID' => $home->ID]], $action->LinkTracking());
|
|
||||||
$this->assertListEquals([['ID' => $home->ID], ['ID' => $about->ID]], $hash->LinkTracking());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,67 +0,0 @@
|
|||||||
SilverStripe\CMS\Model\SiteTree:
|
|
||||||
home:
|
|
||||||
Title: Home Page
|
|
||||||
URLSegment: home
|
|
||||||
Content: '<a href="[sitetree_link,id=$$ABOUTID$$]">About</a><a href="[sitetree_link,id=$$STAFFID$$]">Staff</a><a href="http://silverstripe.org/">External Link</a><a name="anchor"></a>'
|
|
||||||
about:
|
|
||||||
Title: About Us
|
|
||||||
URLSegment: about
|
|
||||||
Content: '<a href="[sitetree_link,id=$$HOMEID$$]">Home</a><a href="[sitetree_link,id=$$STAFFID$$]">Staff</a><a name="second-anchor"></a>'
|
|
||||||
staff:
|
|
||||||
Title: Staff
|
|
||||||
URLSegment: staff
|
|
||||||
Content: '<a href="[sitetree_link,id=$$HOMEID$$]">Home</a><a href="[sitetree_link,id=$$ABOUTID$$]">About</a>'
|
|
||||||
Parent: =>SilverStripe\CMS\Model\SiteTree.about
|
|
||||||
action:
|
|
||||||
Title: Action Link
|
|
||||||
URLSegment: action
|
|
||||||
Content: '<a href="[sitetree_link,id=$$HOMEID$$]SearchForm">Search Form</a>'
|
|
||||||
hash_link:
|
|
||||||
Title: Hash Link
|
|
||||||
URLSegment: hash-link
|
|
||||||
Content: '<a href="[sitetree_link,id=$$HOMEID$$]#anchor">Home</a><a href="[sitetree_link,id=$$ABOUTID$$]#second-anchor">About</a>'
|
|
||||||
admin_link:
|
|
||||||
Title: Admin Link
|
|
||||||
URLSegment: admin-link
|
|
||||||
Content: '<a href="admin">Admin</a>'
|
|
||||||
no_links:
|
|
||||||
Title: No Links
|
|
||||||
URLSegment: No Links
|
|
||||||
|
|
||||||
SiteTree_LinkTracking:
|
|
||||||
home_about:
|
|
||||||
SiteTreeID: =>SilverStripe\CMS\Model\SiteTree.home
|
|
||||||
ChildID: =>SilverStripe\CMS\Model\SiteTree.about
|
|
||||||
FieldName: Content
|
|
||||||
home_staff:
|
|
||||||
SiteTreeID: =>SilverStripe\CMS\Model\SiteTree.home
|
|
||||||
ChildID: =>SilverStripe\CMS\Model\SiteTree.staff
|
|
||||||
FieldName: Content
|
|
||||||
about_home:
|
|
||||||
SiteTreeID: =>SilverStripe\CMS\Model\SiteTree.about
|
|
||||||
ChildID: =>SilverStripe\CMS\Model\SiteTree.home
|
|
||||||
FieldName: Content
|
|
||||||
about_staff:
|
|
||||||
SiteTreeID: =>SilverStripe\CMS\Model\SiteTree.about
|
|
||||||
ChildID: =>SilverStripe\CMS\Model\SiteTree.staff
|
|
||||||
FieldName: Content
|
|
||||||
staff_home:
|
|
||||||
SiteTreeID: =>SilverStripe\CMS\Model\SiteTree.staff
|
|
||||||
ChildID: =>SilverStripe\CMS\Model\SiteTree.home
|
|
||||||
FieldName: Content
|
|
||||||
staff_about:
|
|
||||||
SiteTreeID: =>SilverStripe\CMS\Model\SiteTree.staff
|
|
||||||
ChildID: =>SilverStripe\CMS\Model\SiteTree.about
|
|
||||||
FieldName: Content
|
|
||||||
action_home:
|
|
||||||
SiteTreeID: =>SilverStripe\CMS\Model\SiteTree.action
|
|
||||||
ChildID: =>SilverStripe\CMS\Model\SiteTree.home
|
|
||||||
FieldName: Content
|
|
||||||
hash_link_home:
|
|
||||||
SiteTreeID: =>SilverStripe\CMS\Model\SiteTree.hash_link
|
|
||||||
ChildID: =>SilverStripe\CMS\Model\SiteTree.home
|
|
||||||
FieldName: Content
|
|
||||||
hash_link_about:
|
|
||||||
SiteTreeID: =>SilverStripe\CMS\Model\SiteTree.hash_link
|
|
||||||
ChildID: =>SilverStripe\CMS\Model\SiteTree.about
|
|
||||||
FieldName: Content
|
|
@ -1,109 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace SilverStripe\CMS\Tests\Tasks;
|
|
||||||
|
|
||||||
use SilverStripe\CMS\Tasks\RemoveOrphanedPagesTask;
|
|
||||||
use SilverStripe\Dev\FunctionalTest;
|
|
||||||
use SilverStripe\CMS\Model\SiteTree;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <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
|
|
||||||
{
|
|
||||||
parent::setUp();
|
|
||||||
|
|
||||||
$parent1_published = $this->objFromFixture(SiteTree::class, 'parent1_published');
|
|
||||||
$parent1_published->publishSingle();
|
|
||||||
|
|
||||||
$child1_1_published = $this->objFromFixture(SiteTree::class, 'child1_1_published');
|
|
||||||
$child1_1_published->publishSingle();
|
|
||||||
|
|
||||||
$child1_2_published = $this->objFromFixture(SiteTree::class, 'child1_2_published');
|
|
||||||
$child1_2_published->publishSingle();
|
|
||||||
|
|
||||||
$child1_3_orphaned = $this->objFromFixture(SiteTree::class, 'child1_3_orphaned');
|
|
||||||
$child1_3_orphaned->ParentID = 9999;
|
|
||||||
$child1_3_orphaned->write();
|
|
||||||
|
|
||||||
$child1_4_orphaned_published = $this->objFromFixture(SiteTree::class, '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(SiteTree::class, 'grandchild1_1_2_published');
|
|
||||||
$grandchild1_1_2_published->publishSingle();
|
|
||||||
|
|
||||||
$grandchild1_1_3_orphaned = $this->objFromFixture(SiteTree::class, 'grandchild1_1_3_orphaned');
|
|
||||||
$grandchild1_1_3_orphaned->ParentID = 9999;
|
|
||||||
$grandchild1_1_3_orphaned->write();
|
|
||||||
|
|
||||||
$grandchild1_1_4_orphaned_published = $this->objFromFixture(
|
|
||||||
SiteTree::class,
|
|
||||||
'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(SiteTree::class, 'child2_1_published_orphaned');
|
|
||||||
$child2_1_published_orphaned->publishSingle();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testGetOrphansByStage()
|
|
||||||
{
|
|
||||||
// all orphans
|
|
||||||
$child1_3_orphaned = $this->objFromFixture(SiteTree::class, 'child1_3_orphaned');
|
|
||||||
$child1_4_orphaned_published = $this->objFromFixture(SiteTree::class, 'child1_4_orphaned_published');
|
|
||||||
$grandchild1_1_3_orphaned = $this->objFromFixture(SiteTree::class, 'grandchild1_1_3_orphaned');
|
|
||||||
$grandchild1_1_4_orphaned_published = $this->objFromFixture(
|
|
||||||
SiteTree::class,
|
|
||||||
'grandchild1_1_4_orphaned_published'
|
|
||||||
);
|
|
||||||
$child2_1_published_orphaned = $this->objFromFixture(SiteTree::class, 'child2_1_published_orphaned');
|
|
||||||
|
|
||||||
$task = singleton(RemoveOrphanedPagesTask::class);
|
|
||||||
$orphans = $task->getOrphanedPages();
|
|
||||||
$orphanIDs = $orphans->column('ID');
|
|
||||||
sort($orphanIDs);
|
|
||||||
$compareIDs = [
|
|
||||||
$child1_3_orphaned->ID,
|
|
||||||
$child1_4_orphaned_published->ID,
|
|
||||||
$grandchild1_1_3_orphaned->ID,
|
|
||||||
$grandchild1_1_4_orphaned_published->ID,
|
|
||||||
$child2_1_published_orphaned->ID,
|
|
||||||
];
|
|
||||||
sort($compareIDs);
|
|
||||||
|
|
||||||
$this->assertEquals($orphanIDs, $compareIDs);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
SilverStripe\CMS\Model\SiteTree:
|
|
||||||
parent1_published:
|
|
||||||
Title: Parent1
|
|
||||||
child1_1_published:
|
|
||||||
Title: Child1.1
|
|
||||||
Parent: =>SilverStripe\CMS\Model\SiteTree.parent1_published
|
|
||||||
child1_2_published:
|
|
||||||
Title: Child1.2
|
|
||||||
Parent: =>SilverStripe\CMS\Model\SiteTree.parent1_published
|
|
||||||
child1_3_orphaned:
|
|
||||||
Title: Child1.3
|
|
||||||
Parent: =>SilverStripe\CMS\Model\SiteTree.parent1_published
|
|
||||||
child1_4_orphaned_published:
|
|
||||||
Title: Child1.4
|
|
||||||
Parent: =>SilverStripe\CMS\Model\SiteTree.parent1_published
|
|
||||||
grandchild1_1_1:
|
|
||||||
Title: Grandchild1.1.1
|
|
||||||
Parent: =>SilverStripe\CMS\Model\SiteTree.child1_1_published
|
|
||||||
grandchild1_1_2_published:
|
|
||||||
Title: Grandchild1.1.2
|
|
||||||
Parent: =>SilverStripe\CMS\Model\SiteTree.child1_1_published
|
|
||||||
grandchild1_1_3_orphaned:
|
|
||||||
Title: Grandchild1.1.3
|
|
||||||
Parent: =>SilverStripe\CMS\Model\SiteTree.child1_1_published
|
|
||||||
grandchild1_1_4_orphaned_published:
|
|
||||||
Title: Grandchild1.1.4
|
|
||||||
Parent: =>SilverStripe\CMS\Model\SiteTree.child1_1_published
|
|
||||||
parent2:
|
|
||||||
Title: Parent2
|
|
||||||
child2_1_published_orphaned:
|
|
||||||
Title: Child2.1
|
|
||||||
Parent: =>SilverStripe\CMS\Model\SiteTree.parent2
|
|
Loading…
Reference in New Issue
Block a user