Webylon 3.1 API Docs
  • Package
  • Class
  • Tree
  • Deprecated
  • Download
Version: current
  • 3.2
  • 3.1

Packages

  • auth
  • Booking
  • cart
    • shipping
    • steppedcheckout
  • Catalog
  • cms
    • assets
    • batchaction
    • batchactions
    • bulkloading
    • comments
    • content
    • core
    • export
    • newsletter
    • publishers
    • reports
    • security
    • tasks
  • Dashboard
  • DataObjectManager
  • event
  • faq
  • forms
    • actions
    • core
    • fields-basic
    • fields-dataless
    • fields-datetime
    • fields-files
    • fields-formatted
    • fields-formattedinput
    • fields-relational
    • fields-structural
    • transformations
    • validators
  • googlesitemaps
  • guestbook
  • installer
  • newsletter
  • None
  • photo
    • gallery
  • PHP
  • polls
  • recaptcha
  • sapphire
    • api
    • bulkloading
    • control
    • core
    • cron
    • dev
    • email
    • fields-formattedinput
    • filesystem
    • formatters
    • forms
    • i18n
    • integration
    • misc
    • model
    • parsers
    • search
    • security
    • tasks
    • testing
    • tools
    • validation
    • view
    • widgets
  • seo
    • open
      • graph
  • sfDateTimePlugin
  • spamprotection
  • stealth
    • captha
  • subsites
  • userform
    • pagetypes
  • userforms
  • webylon
  • widgets

Classes

  • Announcement_Controller
  • AnnouncementHolder_Controller
  • BookingAdminPage_Controller
  • BookingPage_Controller
  • Cart_Controller
  • CartPage_Controller
  • Catalog_Controller
  • CheckoutPage_Controller
  • ChequePayment_Handler
  • ContactsPage_Controller
  • ContentController
  • ContentNegotiator
  • Controller
  • DataObjectManager_Controller
  • DatePickerField_Controller
  • Director
  • DocPage_Controller
  • DocumentsPage_Controller
  • Event_Controller
  • EventHolder_Controller
  • FileDataObjectManager_Controller
  • FindCyrillic_Controller
  • HomePage_Controller
  • LastDoc_Controller
  • LiveCalendarWidget_Controller
  • MapObject_Controller
  • MapObjectGroup_Controller
  • MapPage_Controller
  • MediawebPage_Controller
  • ModelAsController
  • MultiUploadControls
  • NewsArchive
  • Orders1CExchange_Controller
  • Page_Controller
  • Payment_Handler
  • PhotoAlbumManager_Controller
  • Product_Controller
  • ProductSearchPage_Controller
  • ProfilePage_Controller
  • PublHolder_Controller
  • Publication_Controller
  • RatingExtension_Controller
  • RegistrationPage_Controller
  • RemoveOrphanedPagesTask
  • RequestHandler
  • Room_Controller
  • RoomCatalog_Controller
  • RootURLController
  • SapphireInfo
  • Search_Controller
  • Session
  • SimpleOrderPage_Controller
  • SiteMap_Controller
  • SpecialCatalog_Controller
  • SS_HTTPRequest
  • SS_HTTPResponse
  • StartCatalog_Controller
  • SubsitesSelectorPage_Controller
  • VideoBankPage_Controller

Interfaces

  • NestedController

Exceptions

  • SS_HTTPResponse_Exception
  1 <?php
  2 /**
  3  * Identify "orphaned" pages which point to a parent
  4  * that no longer exists in a specific stage.
  5  * Shows the pages to an administrator, who can then
  6  * decide which pages to remove by ticking a checkbox
  7  * and manually executing the removal.
  8  * 
  9  * Caution: Pages also count as orphans if they don't
 10  * have parents in this stage, even if the parent has a representation
 11  * in the other stage:
 12  * - A live child is orphaned if its parent was deleted from live, but still exists on stage
 13  * - A stage child is orphaned if its parent was deleted from stage, but still exists on live
 14  *
 15  * See {@link RemoveOrphanedPagesTaskTest} for an example sitetree
 16  * before and after orphan removal.
 17  *
 18  * @author Ingo Schommer (<firstname>@silverstripe.com), SilverStripe Ltd.
 19  * 
 20  * @package sapphire
 21  * @subpackage tasks
 22  */
 23 //class RemoveOrphanedPagesTask extends BuildTask {
 24 class RemoveOrphanedPagesTask extends Controller {
 25     
 26     static $allowed_actions = array(
 27         'index' => 'ADMIN',
 28         'Form' => 'ADMIN',
 29         'run' => 'ADMIN',
 30         'handleAction' => 'ADMIN',
 31     );
 32     
 33     protected $title = 'Removed orphaned pages without existing parents from both stage and live';
 34     
 35     protected $description = "
 36 <p>
 37 Identify 'orphaned' pages which point to a parent
 38 that no longer exists in a specific stage.
 39 </p>
 40 <p>
 41 Caution: Pages also count as orphans if they don't
 42 have parents in this stage, even if the parent has a representation
 43 in the other stage:<br />
 44 - A live child is orphaned if its parent was deleted from live, but still exists on stage<br />
 45 - A stage child is orphaned if its parent was deleted from stage, but still exists on live
 46 </p>
 47     ";
 48     
 49     protected $orphanedSearchClass = 'SiteTree';
 50     
 51     function Link() {
 52         return $this->class;
 53     }
 54     
 55     function init() {
 56         parent::init();
 57         
 58         if(!Permission::check('ADMIN')) {
 59             return Security::permissionFailure($this);
 60         }
 61     }
 62     
 63     function index() {
 64         Requirements::javascript(THIRDPARTY_DIR . '/jquery/jquery.js');
 65         Requirements::customCSS('#OrphanIDs .middleColumn {width: auto;}');
 66         Requirements::customCSS('#OrphanIDs label {display: inline;}');
 67         
 68         return $this->renderWith('BlankPage');
 69     }
 70     
 71     function Form() {
 72         $fields = new FieldSet();
 73         $source = array();
 74         
 75         $fields->push(new HeaderField(
 76             'Header',
 77             _t('RemoveOrphanedPagesTask.HEADER', 'Remove all orphaned pages task')
 78         ));
 79         $fields->push(new LiteralField(
 80             'Description',
 81             $this->description
 82         ));
 83         
 84         $orphans = $this->getOrphanedPages($this->orphanedSearchClass);
 85         if($orphans) foreach($orphans as $orphan) {
 86             $latestVersion = Versioned::get_latest_version($this->orphanedSearchClass, $orphan->ID);
 87             $latestAuthor = DataObject::get_by_id('Member', $latestVersion->AuthorID);
 88             $stageRecord = Versioned::get_one_by_stage(
 89                 $this->orphanedSearchClass, 
 90                 'Stage', 
 91                 sprintf("\"%s\".\"ID\" = %d", 
 92                     ClassInfo::baseDataClass($this->orphanedSearchClass), 
 93                     $orphan->ID
 94                 )
 95             );
 96             $liveRecord = Versioned::get_one_by_stage(
 97                 $this->orphanedSearchClass, 
 98                 'Live', 
 99                 sprintf("\"%s\".\"ID\" = %d", 
100                     ClassInfo::baseDataClass($this->orphanedSearchClass), 
101                     $orphan->ID
102                 )
103             );
104             $label = sprintf(
105                 '<a href="admin/show/%d">%s</a> <small>(#%d, Last Modified Date: %s, Last Modifier: %s, %s)</small>',
106                 $orphan->ID,
107                 $orphan->Title,
108                 $orphan->ID,
109                 DBField::create('Date', $orphan->LastEdited)->Nice(),
110                 ($latestAuthor) ? $latestAuthor->Title : 'unknown',
111                 ($liveRecord) ? 'is published' : 'not published'
112             );
113             $source[$orphan->ID] = $label;
114         }
115         
116         if($orphans && $orphans->Count()) {
117             $fields->push(new CheckboxSetField('OrphanIDs', false, $source));
118             $fields->push(new LiteralField(
119                 'SelectAllLiteral',
120                 sprintf(
121                     '<p><a href="#" onclick="javascript:jQuery(\'#Form_Form_OrphanIDs :checkbox\').attr(\'checked\', \'checked\'); return false;">%s</a>&nbsp;',
122                     _t('RemoveOrphanedPagesTask.SELECTALL', 'select all')
123                 )
124             ));
125             $fields->push(new LiteralField(
126                 'UnselectAllLiteral',
127                 sprintf(
128                     '<a href="#" onclick="javascript:jQuery(\'#Form_Form_OrphanIDs :checkbox\').attr(\'checked\', \'\'); return false;">%s</a></p>',
129                     _t('RemoveOrphanedPagesTask.UNSELECTALL', 'unselect all')
130                 )
131             ));
132             $fields->push(new OptionSetField(
133                 'OrphanOperation', 
134                 _t('RemoveOrphanedPagesTask.CHOOSEOPERATION', 'Choose operation:'),
135                 array(
136                     'rebase' => _t(
137                         'RemoveOrphanedPagesTask.OPERATION_REBASE', 
138                         sprintf(
139                             'Rebase selected to a new holder page "%s" and unpublish. None of these pages will show up for website visitors.',
140                             $this->rebaseHolderTitle()
141                         )
142                     ),
143                     'remove' => _t('RemoveOrphanedPagesTask.OPERATION_REMOVE', 'Remove selected from all stages (WARNING: Will destroy all selected pages from both stage and live)'),
144                 ),
145                 'rebase'
146             ));
147             $fields->push(new LiteralField(
148                 'Warning',
149                 sprintf('<p class="message">%s</p>',
150                     _t(
151                         'RemoveOrphanedPagesTask.DELETEWARNING', 
152                         'Warning: These operations are not reversible. Please handle with care.'
153                     )
154                 )
155             ));
156         } else {
157             $fields->push(new LiteralField(
158                 'NotFoundLabel',
159                 sprintf(
160                     '<p class="message">%s</p>',
161                     _t('RemoveOrphanedPagesTask.NONEFOUND', 'No orphans found')
162                 )
163             ));
164         }
165         
166         $form = new Form(
167             $this,
168             'Form',
169             $fields,
170             new FieldSet(
171                 new FormAction('doSubmit', _t('RemoveOrphanedPagesTask.BUTTONRUN', 'Run'))
172             )
173         );
174         
175         if(!$orphans || !$orphans->Count()) {
176             $form->makeReadonly();
177         }
178         
179         return $form;
180     }
181     
182     function run($request) {
183         // @todo Merge with BuildTask functionality
184     }
185     
186     function doSubmit($data, $form) {
187         set_time_limit(60*10); // 10 minutes
188         
189         if(!isset($data['OrphanIDs']) || !isset($data['OrphanOperation'])) return false;
190         
191         switch($data['OrphanOperation']) {
192             case 'remove':
193                 $successIDs = $this->removeOrphans($data['OrphanIDs']);
194                 break;
195             case 'rebase':
196                 $successIDs = $this->rebaseOrphans($data['OrphanIDs']);
197                 break;
198             default:
199                 user_error(sprintf("Unknown operation: '%s'", $data['OrphanOperation']), E_USER_ERROR);
200         }
201         
202         $content = '';
203         if($successIDs) {
204             $content .= "<ul>";
205             foreach($successIDs as $id => $label) {
206                 $content .= sprintf('<li>%s</li>', $label);
207             }
208             $content .= "</ul>";
209         } else {
210             $content = _t('RemoveOrphanedPagesTask.NONEREMOVED', 'None removed');
211         }
212         
213         return $this->customise(array(
214             'Content' => $content,
215             'Form' => ' '
216         ))->renderWith('BlankPage');
217     }
218     
219     protected function removeOrphans($orphanIDs) {
220         $removedOrphans = array();
221         foreach($orphanIDs as $id) {
222             $stageRecord = Versioned::get_one_by_stage(
223                 $this->orphanedSearchClass, 
224                 'Stage', 
225                 sprintf("\"%s\".\"ID\" = %d", 
226                     ClassInfo::baseDataClass($this->orphanedSearchClass), 
227                     $id
228                 )
229             );
230             if($stageRecord) {
231                 $removedOrphans[$stageRecord->ID] = sprintf('Removed %s (#%d) from Stage', $stageRecord->Title, $stageRecord->ID);
232                 $stageRecord->delete();
233                 $stageRecord->destroy();
234                 unset($stageRecord);
235             }
236             $liveRecord = Versioned::get_one_by_stage(
237                 $this->orphanedSearchClass, 
238                 'Live', 
239                 sprintf("\"%s\".\"ID\" = %d", 
240                     ClassInfo::baseDataClass($this->orphanedSearchClass), 
241                     $id
242                 )
243             );
244             if($liveRecord) {
245                 $removedOrphans[$liveRecord->ID] = sprintf('Removed %s (#%d) from Live', $liveRecord->Title, $liveRecord->ID);
246                 $liveRecord->doDeleteFromLive();
247                 $liveRecord->destroy();
248                 unset($liveRecord);
249             }
250         }
251         
252         return $removedOrphans;
253     }
254     
255     protected function rebaseHolderTitle() {
256         return sprintf('Rebased Orphans (%s)', date('d/m/Y g:ia', time()));
257     }
258     
259     protected function rebaseOrphans($orphanIDs) {
260         $holder = new SiteTree();
261         $holder->ShowInMenus = 0;
262         $holder->ShowInSearch = 0;
263         $holder->ParentID = 0;
264         $holder->Title = $this->rebaseHolderTitle();
265         $holder->write();
266         
267         $removedOrphans = array();
268         foreach($orphanIDs as $id) {
269             $stageRecord = Versioned::get_one_by_stage(
270                 $this->orphanedSearchClass, 
271                 'Stage', 
272                 sprintf("\"%s\".\"ID\" = %d", 
273                     ClassInfo::baseDataClass($this->orphanedSearchClass), 
274                     $id
275                 )
276             );
277             if($stageRecord) {
278                 $removedOrphans[$stageRecord->ID] = sprintf('Rebased %s (#%d)', $stageRecord->Title, $stageRecord->ID);
279                 $stageRecord->ParentID = $holder->ID;
280                 $stageRecord->ShowInMenus = 0;
281                 $stageRecord->ShowInSearch = 0;
282                 $stageRecord->write();
283                 $stageRecord->doUnpublish();
284                 $stageRecord->destroy();
285                 //unset($stageRecord);
286             }
287             $liveRecord = Versioned::get_one_by_stage(
288                 $this->orphanedSearchClass, 
289                 'Live', 
290                 sprintf("\"%s\".\"ID\" = %d", 
291                     ClassInfo::baseDataClass($this->orphanedSearchClass), 
292                     $id
293                 )
294             );
295             if($liveRecord) {
296                 $removedOrphans[$liveRecord->ID] = sprintf('Rebased %s (#%d)', $liveRecord->Title, $liveRecord->ID);
297                 $liveRecord->ParentID = $holder->ID;
298                 $liveRecord->ShowInMenus = 0;
299                 $liveRecord->ShowInSearch = 0;
300                 $liveRecord->write();
301                 if(!$stageRecord) $liveRecord->doRestoreToStage();
302                 $liveRecord->doUnpublish();
303                 $liveRecord->destroy();
304                 unset($liveRecord);
305             }
306             if($stageRecord) {
307                 unset($stageRecord);
308             }
309         }
310         
311         return $removedOrphans;
312     }
313     
314     /**
315      * Gets all orphans from "Stage" and "Live" stages.
316      * 
317      * @param string $class
318      * @param string $filter
319      * @param string $sort
320      * @param string $join
321      * @param int|array $limit
322      * @return DataObjectSet
323      */
324     function getOrphanedPages($class = 'SiteTree', $filter = '', $sort = null, $join = null, $limit = null) {
325         $filter .= ($filter) ? ' AND ' : '';
326         $filter .= sprintf("\"%s\".\"ParentID\" != 0 AND \"Parents\".\"ID\" IS NULL", $class);
327         
328         $orphans = new DataObjectSet();
329         foreach(array('Stage', 'Live') as $stage) {
330             $joinByStage = $join;
331             $table = $class;
332             $table .= ($stage == 'Live') ? '_Live' : '';
333             $joinByStage .= sprintf(
334                 "LEFT JOIN \"%s\" AS \"Parents\" ON \"%s\".\"ParentID\" = \"Parents\".\"ID\"",
335                 $table,
336                 $table
337             );
338             $stageOrphans = Versioned::get_by_stage(
339                 $class,
340                 $stage,
341                 $filter,
342                 $sort,
343                 $joinByStage,
344                 $limit
345             );
346             $orphans->merge($stageOrphans);
347         }
348         
349         $orphans->removeDuplicates();
350     
351         return $orphans;
352     }
353 }
354 ?>
[Raise a SilverStripe Framework issue/bug](https://github.com/silverstripe/silverstripe-framework/issues/new)
- [Raise a SilverStripe CMS issue/bug](https://github.com/silverstripe/silverstripe-cms/issues/new)
- Please use the Silverstripe Forums to ask development related questions. -
Webylon 3.1 API Docs API documentation generated by ApiGen 2.8.0