1 <?php
2 3 4 5 6 7 8 9 10
11 class CMSMain extends LeftAndMain implements CurrentPageIdentifier, PermissionProvider {
12
13 static $url_segment = '';
14
15 static $url_rule = '/$Action/$ID/$OtherID';
16
17
18
19 static $url_priority = 40;
20
21 static = 'Pages';
22
23 static = 10;
24
25 static $tree_class = "SiteTree";
26
27 static $subitem_class = "Member";
28
29 static $allowed_actions = array(
30 'addmember',
31 'addpage',
32 'buildbrokenlinks',
33 'canceldraftchangesdialog',
34 'compareversions',
35 'createtranslation',
36 'delete',
37 'deletefromlive',
38 'deleteitems',
39 'DeleteItemsForm',
40 'dialog',
41 'duplicate',
42 'duplicatewithchildren',
43 'getpagecount',
44 'getversion',
45 'publishall',
46 'publishitems',
47 'PublishItemsForm',
48 'restore',
49 'revert',
50 'rollback',
51 'sidereport',
52 'submit',
53 'unpublish',
54 'versions',
55 'EditForm',
56 'AddPageOptionsForm',
57 'SiteTreeAsUL',
58 'getshowdeletedsubtree',
59 'getfilteredsubtree',
60 'batchactions'
61 );
62
63 64 65
66 static function T_SiteTreeFilterOptions(){
67 return array(
68 'Title' => _t('CMSMain.TITLEOPT', 'Title', 0, 'The dropdown title in CMSMain left SiteTreeFilterOptions'),
69 'MenuTitle' => _t('CMSMain.MENUTITLEOPT', 'Navigation Label', 0, 'The dropdown title in CMSMain left SiteTreeFilterOptions'),
70 'ClassName' => _t('CMSMain.PAGETYPEOPT', 'Page Type'),
71 'Status' => _t('CMSMain.STATUSOPT', 'Status', 0, "The dropdown title in CMSMain left SiteTreeFilterOptions"),
72 'MetaDescription' => _t('CMSMain.METADESCOPT', 'Description', 0, "The dropdown title in CMSMain left SiteTreeFilterOptions"),
73 'MetaKeywords' => _t('CMSMain.METAKEYWORDSOPT', 'Keywords', 0, "The dropdown title in CMSMain left SiteTreeFilterOptions")
74 );
75 }
76
77 public function init() {
78 parent::init();
79
80
81
82
83
84 if($this->getRequest()->requestVar("Locale")) {
85 $this->Locale = $this->getRequest()->requestVar("Locale");
86 } elseif($this->getRequest()->requestVar("locale")) {
87 $this->Locale = $this->getRequest()->requestVar("locale");
88 } else {
89 $this->Locale = Translatable::default_locale();
90 }
91 Translatable::set_current_locale($this->Locale);
92
93
94
95 $langName = i18n::get_locale_name($this->Locale);
96 HtmlEditorConfig::get('cms')->setOption('spellchecker_languages', "+{$langName}={$this->Locale}");
97
98 Requirements::javascript(CMS_DIR . '/javascript/CMSMain.js');
99 Requirements::javascript(CMS_DIR . '/javascript/CMSMain_left.js');
100 Requirements::javascript(CMS_DIR . '/javascript/CMSMain_right.js');
101 }
102
103 104 105 106 107 108
109 function ShowSwitchView() {
110 return true;
111 }
112
113 114 115 116
117 function SwitchView($page = null) {
118 if(!$page) {
119 $page = $this->currentPage();
120 }
121
122 if($page) {
123 $nav = SilverStripeNavigator::get_for_record($page);
124 Requirements::customScript("window.name = windowName('cms');");
125 return $nav['items'];
126 }
127 }
128
129
130
131
132
133
134
135 136 137 138 139
140 public function Link($action = null) {
141 return Controller::join_links(
142 $this->stat('url_base', true),
143 $this->stat('url_segment', true),
144 '/',
145 "$action"
146 );
147 }
148
149 150 151
152 public function SiteTreeAsUL() {
153 $this->generateDataTreeHints();
154 $this->generateTreeStylingJS();
155
156
157 Versioned::prepopulate_versionnumber_cache("SiteTree", "Stage");
158 Versioned::prepopulate_versionnumber_cache("SiteTree", "Live");
159
160 return $this->getSiteTreeFor("SiteTree");
161 }
162
163 public function getCMSTreeTitle() {
164 return SiteConfig::current_site_config()->i18n_singular_name();
165 }
166
167 168 169 170 171
172 public function getfilteredsubtree() {
173
174 if (!isset($_REQUEST['filter'])) die('No filter passed');
175 if (!ClassInfo::exists($_REQUEST['filter'])) die ('That filter class does not exist');
176 if (!is_subclass_of($_REQUEST['filter'], 'CMSSiteTreeFilter')) die ('That is not a valid filter');
177
178
179 $filter = new $_REQUEST['filter']();
180 return $filter->getTree();
181 }
182
183 184 185
186 function SiteTreeFilters() {
187 $filters = ClassInfo::subclassesFor('CMSSiteTreeFilter');
188 array_shift($filters);
189 $doSet = new DataObjectSet();
190 $doSet->push(new ArrayData(array(
191 'ClassName' => 'all',
192 'Title' => _t('CMSSiteTreeFilter.ALL', 'All items')
193 )));
194 foreach($filters as $filter) {
195 if (call_user_func(array($filter, 'showInList'))) {
196 $doSet->push(new ArrayData(array(
197 'ClassName' => $filter,
198 'Title' => call_user_func(array($filter, 'title'))
199 )));
200 }
201 }
202 return $doSet;
203 }
204
205 206 207
208 public function SiteTreeFilterOptions() {
209 $filter_options = new DataObjectSet();
210 foreach(self::T_SiteTreeFilterOptions() as $key => $value) {
211 $record = array(
212 'Column' => $key,
213 'Title' => $value,
214 );
215 $filter_options->push(new ArrayData($record));
216 }
217 return $filter_options;
218 }
219 public function SiteTreeFilterDateField() {
220 $dateField = new DateField('SiteTreeFilterDate');
221 $dateField->setConfig('showcalendar', true);
222 return $dateField->Field();
223 }
224 public function SiteTreeFilterPageTypeField() {
225 $types = SiteTree::page_type_classes();
226 $source = array_combine($types, $types);
227 $source['All']= _t('CMSMain.AllClasses', 'All');
228 ksort($source);
229 $optionsetField = new DropdownField('ClassName', 'ClassName', $source, 'Any');
230 return $optionsetField->Field();
231 }
232
233 public function generateDataTreeHints() {
234 $classes = ClassInfo::subclassesFor( $this->stat('tree_class') );
235
236 $def['Root'] = array();
237
238 foreach($classes as $class) {
239 $obj = singleton($class);
240 if($obj instanceof HiddenClass) continue;
241
242 $allowedChildren = $obj->allowedChildren();
243 if($allowedChildren != "none") $def[$class]['allowedChildren'] = $allowedChildren;
244 $def[$class]['defaultChild'] = $obj->defaultChild();
245 $def[$class]['defaultParent'] = ($obj->defaultParent() && isset(SiteTree::get_by_link($obj->defaultParent())->ID)) ? SiteTree::get_by_link($obj->defaultParent())->ID : null;
246
247 if(is_array($allowedChildren)) foreach($allowedChildren as $allowedChild) {
248 $def[$allowedChild]['allowedParents'][] = $class;
249 }
250
251 if($obj->stat('can_be_root')) {
252 $def['Root']['allowedChildren'][] = $class;
253 }
254 }
255
256
257 Requirements::customScript("siteTreeHints = " . $this->jsDeclaration($def) . ";");
258 }
259
260 public function generateTreeStylingJS() {
261 $classes = ClassInfo::subclassesFor('SiteTree');
262 foreach($classes as $class) {
263 $obj = singleton($class);
264 if($obj instanceof HiddenClass) continue;
265 if($icon = $obj->stat('icon')) $iconInfo[$class] = $icon;
266 }
267 $iconInfo['BrokenLink'] = 'cms/images/treeicons/brokenlink';
268
269
270 $js = "var _TREE_ICONS = [];\n";
271
272
273 foreach($iconInfo as $class => $icon) {
274
275
276 $option = null;
277 if(is_array($icon)) list($icon, $option) = $icon;
278
279 $fileImage = ($option == "folder") ? $icon . '-closedfolder.gif' : $icon . '-file.gif';
280 $openFolderImage = $icon . '-openfolder.gif';
281 if(!Director::fileExists($openFolderImage) || $option == "file") $openFolderImage = $fileImage;
282 $closedFolderImage = $icon . '-closedfolder.gif';
283 if(!Director::fileExists($closedFolderImage) || $option == "file") $closedFolderImage = $fileImage;
284
285 $js .= <<<JS
286 _TREE_ICONS['$class'] = {
287 fileIcon: '$fileImage',
288 openFolderIcon: '$openFolderImage',
289 closedFolderIcon: '$closedFolderImage'
290 };
291
292 JS;
293 }
294
295 Requirements::customScript($js);
296 }
297
298 299 300
301 protected function jsDeclaration($array) {
302 $parts = array();
303 if(is_array($array)) {
304 $object = false;
305 foreach(array_keys($array) as $key) {
306 if(!is_numeric($key)) {
307 $object = true;
308 break;
309 }
310 }
311
312 if($object) {
313 foreach($array as $k => $v) {
314 $parts[] = "$k : " . $this->jsDeclaration($v);
315 }
316 return " {\n " . implode(", \n", $parts) . " }\n";
317 } else {
318 foreach($array as $part) $parts[] = $this->jsDeclaration($part);
319 return " [ " . implode(", ", $parts) . " ]\n";
320 }
321 } else {
322 return "'" . addslashes($array) . "'";
323 }
324 }
325
326 327 328 329 330 331
332 public function PageTypes() {
333 $classes = SiteTree::page_type_classes();
334
335 $result = new DataObjectSet();
336
337 foreach($classes as $class) {
338 $instance = singleton($class);
339
340 if($instance instanceof HiddenClass) continue;
341
342 if(!$instance->canCreate()) continue;
343
344
345 if($instance->stat('need_permission') && !$this->can(singleton($class)->stat('need_permission'))) continue;
346
347 $addAction = $instance->i18n_singular_name();
348
349
350
351
352
353 if (Director::isDev()) {
354 $addAction .= " ({$class})";
355 }
356
357 $result->push(new ArrayData(array(
358 'ClassName' => $class,
359 'AddAction' => $addAction,
360 'Sort' => ($class == 'DocPage') ? '0' : $addAction,
361 )));
362 }
363
364 $result->sort('Sort');
365
366 return $result;
367 }
368
369 370 371 372 373 374 375
376 function save_siteconfig($data, $form) {
377 $siteConfig = SiteConfig::current_site_config();
378 $form->saveInto($siteConfig);
379 $siteConfig->write();
380 FormResponse::status_message('Saved site configuration', "good");
381 FormResponse::add("$('Form_EditForm').resetElements();");
382
383 return FormResponse::respond();
384 }
385 386 387
388 public function getRecord($id) {
389
390 $treeClass = $this->stat('tree_class');
391
392 if($id && is_numeric($id)) {
393 $record = DataObject::get_one( $treeClass, "\"$treeClass\".\"ID\" = $id");
394
395
396 if(!$record) {
397
398 Versioned::reading_stage('Live');
399 singleton($treeClass)->flushCache();
400
401 $record = DataObject::get_one( $treeClass, "\"$treeClass\".\"ID\" = $id");
402 if($record) Versioned::set_reading_mode('');
403 }
404
405
406 if(!$record) {
407 $record = Versioned::get_latest_version($treeClass, $id);
408 }
409
410
411 412 413 414 415 416
417 418 419
420
421 return $record;
422
423 } else if(substr($id,0,3) == 'new') {
424 return $this->getNewItem($id);
425 }
426 }
427
428 public function getEditForm($id) {
429 $record = $this->getRecord($id);
430
431 if($record) {
432 if($record->IsDeletedFromStage) $record->Status = _t('CMSMain.REMOVEDFD',"Removed from the draft site");
433
434 $fields = $record->getCMSFields($this);
435 if ($fields == null) {
436 user_error("getCMSFields returned null on a '".get_class($record)."' object - it should return a FieldSet object. Perhaps you forgot to put a return statement at the end of your method?", E_USER_ERROR);
437 }
438 $fields->push($idField = new HiddenField("ID"));
439 $fields->push($liveURLField = new HiddenField("LiveURLSegment"));
440 $fields->push($stageURLField = new HiddenField("StageURLSegment"));
441
442
443 $fields->push(new HiddenField('Sort','', $record->Sort ));
444
445 $idField->setValue($id);
446
447 if($record->ID && is_numeric( $record->ID ) ) {
448 $liveRecord = Versioned::get_one_by_stage('SiteTree', 'Live', "\"SiteTree\".\"ID\" = $record->ID");
449 if($liveRecord) $liveURLField->setValue($liveRecord->AbsoluteLink());
450 }
451
452 if(!$record->IsDeletedFromStage) {
453 $stageURLField->setValue($record->AbsoluteLink());
454 }
455
456
457 if($record->hasMethod('getAllCMSActions')) {
458 $actions = $record->getAllCMSActions();
459 } else {
460 $actions = $record->getCMSActions();
461 }
462
463
464
465
466
467
468
469
470
471 if($record->hasMethod('getCMSValidator')) {
472 $validator = $record->getCMSValidator();
473 } else {
474 $validator = new RequiredFields();
475 }
476
477
478
479
480 $validator->setJavascriptValidationHandler('prototype');
481
482 $form = new Form($this, "EditForm", $fields, $actions, $validator);
483 $form->loadDataFrom($record);
484 $form->disableDefaultAction();
485
486 if(!$record->canEdit() || $record->IsDeletedFromStage) {
487 $readonlyFields = $form->Fields()->makeReadonly();
488 $form->setFields($readonlyFields);
489 }
490
491 $this->extend('updateEditForm', $form);
492
493 return $form;
494 } if ($id == 0 || $id == 'root') {
495 return $this->RootForm();
496 } else if($id) {
497 return new Form($this, "EditForm", new FieldSet(
498 new LabelField('PageDoesntExistLabel',_t('CMSMain.PAGENOTEXISTS',"This page doesn't exist"))), new FieldSet());
499
500 }
501 }
502
503 504 505
506 function RootForm() {
507 $siteConfig = SiteConfig::current_site_config();
508 $fields = $siteConfig->getCMSFields();
509 if(Object::has_extension('SiteConfig',"Translatable")){
510 $fields->push(new HiddenField('Locale','', $siteConfig->Locale ));
511 }
512 $form = new Form($this, "EditForm", $fields, $siteConfig->getCMSActions());
513 $form->loadDataFrom($siteConfig);
514
515 $this->extend('updateEditForm', $form);
516
517 return $form;
518 }
519
520
521
522
523
524 public function addpage() {
525
526 $className = 'DocPage';
527 if(isset($_REQUEST['PageType']) && $_REQUEST['PageType']!='' && class_exists($_REQUEST['PageType'])){
528 $className = $_REQUEST['PageType'];
529 }
530
531
532 $parent = isset($_REQUEST['ParentID']) ? $_REQUEST['ParentID'] : 0;
533 $suffix = isset($_REQUEST['Suffix']) ? "-" . $_REQUEST['Suffix'] : null;
534
535 if(!$parent && isset($_REQUEST['Parent'])) {
536 $page = SiteTree::get_by_link($_REQUEST['Parent']);
537 if($page) $parent = $page->ID;
538 }
539
540 if(is_numeric($parent)) $parentObj = DataObject::get_by_id("SiteTree", $parent);
541 if(!$parentObj || !$parentObj->ID) $parent = 0;
542
543 if($parentObj){
544 if(!$parentObj->canAddChildren()) return Security::permissionFailure($this);
545 if(!singleton($className)->canCreate()) return Security::permissionFailure($this);
546 }else{
547 if(!SiteConfig::current_site_config()->canCreateTopLevel())
548 return Security::permissionFailure($this);
549 }
550
551 $p = $this->getNewItem("new-$className-$parent".$suffix, false);
552 $p->Locale = $_REQUEST['Locale'];
553 $p->write();
554
555 return $this->returnItemToUser($p);
556 }
557
558 559 560
561 public function getNewItem($id, $setID = true) {
562 list($dummy, $className, $parentID, $suffix) = array_pad(explode('-',$id),4,null);
563
564 $newItem = new $className();
565
566 if( !$suffix ) {
567 $sessionTag = "NewItems." . $parentID . "." . $className;
568 if(Session::get($sessionTag)) {
569 $suffix = '-' . Session::get($sessionTag);
570 Session::set($sessionTag, Session::get($sessionTag) + 1);
571 }
572 else
573 Session::set($sessionTag, 1);
574
575 $id = $id . $suffix;
576 }
577
578
579 if (!$newItem->Title) $newItem->Title = singleton($className)->i18n_singular_name();
580 if (!$newItem->URLSegment) $newItem->URLSegment = "new-" . strtolower($className);
581 $newItem->ClassName = $className;
582 $newItem->ParentID = $parentID;
583
584
585
586 if($newItem->castingHelper('Sort')) {
587 $newItem->Sort = DB::query("SELECT MAX(\"Sort\") FROM \"SiteTree\" WHERE \"ParentID\" = '" . Convert::raw2sql($parentID) . "'")->value() + 1;
588 }
589
590 if($setID) $newItem->ID = $id;
591
592
593 $this->extend('augmentNewSiteTreeItem', $newItem);
594
595 return $newItem;
596 }
597
598 599 600 601 602
603 public function deletefromlive($urlParams, $form) {
604 $id = $_REQUEST['ID'];
605 Versioned::reading_stage('Live');
606 $record = DataObject::get_by_id("SiteTree", $id);
607 if($record && !($record->canDelete() && $record->canDeleteFromLive())) return Security::permissionFailure($this);
608
609 $descRemoved = '';
610 $descendantsRemoved = 0;
611
612
613 if($record) {
614 $descendantIDs = $record->getDescendantIDList();
615
616
617 $descendantsRemoved = 0;
618 foreach( $descendantIDs as $descID )
619 if( $descendant = DataObject::get_by_id('SiteTree', $descID) ) {
620 $descendant->doDeleteFromLive();
621 $descendantsRemoved++;
622 }
623
624
625 $record->doDeleteFromLive();
626 }
627
628 Versioned::reading_stage('Stage');
629
630 if(isset($descendantsRemoved)) {
631 $descRemoved = " and $descendantsRemoved descendants";
632 $descRemoved = sprintf(' '._t('CMSMain.DESCREMOVED', 'and %s descendants'), $descendantsRemoved);
633 } else {
634 $descRemoved = '';
635 }
636
637 FormResponse::add($this->deleteTreeNodeJS($record));
638 FormResponse::status_message(sprintf(_t('CMSMain.REMOVED', 'Deleted \'%s\'%s from live site'), $record->Title, $descRemoved), 'good');
639
640 return FormResponse::respond();
641 }
642
643 644 645
646 public function performPublish($record) {
647 if($record && !$record->canPublish()) return Security::permissionFailure($this);
648
649 $record->doPublish();
650 }
651
652 653 654 655 656 657 658
659 public function revert($urlParams, $form) {
660 $id = (int)$_REQUEST['ID'];
661 $record = Versioned::get_one_by_stage('SiteTree', 'Live', "\"SiteTree_Live\".\"ID\" = '{$id}'");
662
663
664
665 if(isset($record) && $record && !$record->canEdit()) return Security::permissionFailure($this);
666
667 $record->doRevertToLive();
668
669 $title = Convert::raw2js($record->Title);
670 FormResponse::get_page($id);
671 FormResponse::add("$('sitetree').setNodeTitle($id, '$title');");
672 FormResponse::status_message(sprintf(_t('CMSMain.RESTORED',"Restored '%s' successfully",PR_MEDIUM,'Param %s is a title'),$title),'good');
673
674 return FormResponse::respond();
675 }
676
677 678 679 680
681 public function delete($data, $form) {
682 $record = DataObject::get_one(
683 "SiteTree",
684 sprintf("\"SiteTree\".\"ID\" = %d", Convert::raw2sql($data['ID']))
685 );
686 if($record && !$record->canDelete()) return Security::permissionFailure();
687
688
689 $recordID = $record->ID;
690 $record->delete();
691
692 if(Director::is_ajax()) {
693
694
695 $liveRecord = Versioned::get_one_by_stage('SiteTree', 'Live', "\"SiteTree_Live\".\"ID\" = $recordID");
696
697 if($liveRecord) {
698
699 FormResponse::get_page($recordID);
700 return $this->tellBrowserAboutPublicationChange($liveRecord, sprintf(_t('CMSMain.REMOVEDPAGEFROMDRAFT',"Removed '%s' from the draft site"),$record->Title));
701 } else {
702 FormResponse::add($this->deleteTreeNodeJS($record));
703 FormResponse::status_message(sprintf(_t('CMSMain.REMOVEDPAGEFROMDRAFT',"Removed '%s' from the draft site"),$record->Title), 'good');
704 return FormResponse::respond();
705 }
706 } else {
707 Director::redirectBack();
708 }
709 }
710
711 function SideReports() {
712 return SS_Report::get_reports('SideReport');
713 }
714
715 716 717
718 function ReportSelector() {
719
720 foreach($this->SideReports() as $report) {
721 if($report->canView()) {
722 $options[$report->group()][$report->sort()][$report->ID()] = $report->title();
723 }
724 }
725
726 $finalOptions = array();
727 foreach($options as $group => $weights) {
728 ksort($weights);
729 foreach($weights as $weight => $reports) {
730 foreach($reports as $class => $report) {
731 $finalOptions[$group][$class] = $report;
732 }
733 }
734 }
735
736 return new GroupedDropdownField("ReportSelector", _t('CMSMain.REPORT', 'Report'),$finalOptions);
737 }
738
739 740 741 742 743
744 function ReportFormParameters() {
745 $forms = array();
746 foreach($this->SideReports() as $report) {
747 if ($report->canView()) {
748 if ($fieldset = $report->parameterFields()) {
749 $formHtml = '';
750 foreach($fieldset as $field) {
751 $formHtml .= $field->FieldHolder();
752 }
753 $forms[$report->ID()] = $formHtml;
754 }
755 }
756 }
757 $pageHtml = '';
758 foreach($forms as $class => $html) {
759 $pageHtml .= "<div id=\"SideReportForm_$class\" style=\"display:none\">$html</div>\n\n";
760 }
761 return new LiteralField("ReportFormParameters", '<div id="SideReportForms" style="display:none">'.$pageHtml.'</div>');
762 }
763
764 765 766
767 function sidereport() {
768 $reportClass = $this->urlParams['ID'];
769 $reports = $this->SideReports();
770 if(isset($reports[$reportClass])) {
771 $report = $reports[$reportClass];
772 if($report) {
773 $view = new SideReportView($this, $report);
774 $view->setParameters($this->request->requestVars());
775 return $view->forTemplate();
776 } else {
777 return false;
778 }
779 }
780 }
781
782
783 784 785
786 function versions() {
787 $pageID = $this->urlParams['ID'];
788 $page = $this->getRecord($pageID);
789 if($page) {
790 $versions = $page->allVersions($_REQUEST['unpublished'] ? "" : "\"SiteTree\".\"WasPublished\" = 1");
791 return array(
792 'Versions' => $versions,
793 );
794 } else {
795 return sprintf(_t('CMSMain.VERSIONSNOPAGE',"Can't find page #%d",PR_LOW),$pageID);
796 }
797 }
798
799 800 801
802 function rollback() {
803 if(isset($_REQUEST['Version']) && (bool)$_REQUEST['Version']) {
804 $this->extend('onBeforeRollback', $_REQUEST['ID']);
805 $record = $this->performRollback($_REQUEST['ID'], $_REQUEST['Version']);
806 echo sprintf(_t('CMSMain.ROLLEDBACKVERSION',"Rolled back to version #%d. New version number is #%d"),$_REQUEST['Version'],$record->Version);
807 } else {
808 $record = $this->performRollback($_REQUEST['ID'], "Live");
809 echo sprintf(_t('CMSMain.ROLLEDBACKPUB',"Rolled back to published version. New version number is #%d"),$record->Version);
810 }
811 }
812
813 function unpublish() {
814 $SQL_id = Convert::raw2sql($_REQUEST['ID']);
815
816 $page = DataObject::get_by_id("SiteTree", $SQL_id);
817
818 if($page && !$page->canDeleteFromLive()) return Security::permissionFailure($this);
819
820 $page->doUnpublish();
821
822 return $this->tellBrowserAboutPublicationChange($page, sprintf(_t('CMSMain.REMOVEDPAGE',"Removed '%s' from the published site"),$page->Title));
823 }
824
825 826 827 828 829 830 831
832 function tellBrowserAboutPublicationChange($page, $statusMessage) {
833 $JS_title = Convert::raw2js($page->TreeTitle());
834
835 $JS_stageURL = $page->IsDeletedFromStage ? '' : Convert::raw2js($page->AbsoluteLink());
836 $liveRecord = Versioned::get_one_by_stage('SiteTree', 'Live', "\"SiteTree\".\"ID\" = $page->ID");
837
838 $JS_liveURL = $liveRecord ? Convert::raw2js($liveRecord->AbsoluteLink()) : '';
839
840 FormResponse::add($this->getActionUpdateJS($page));
841 FormResponse::update_status($page->Status);
842
843 if($JS_stageURL || $JS_liveURL) {
844 FormResponse::add("\$('sitetree').setNodeTitle($page->ID, '$JS_title');");
845 } else {
846 FormResponse::add("var node = $('sitetree').getTreeNodeByIdx('$page->ID');");
847 FormResponse::add("if(node && node.parentTreeNode) node.parentTreeNode.removeTreeNode(node);");
848 FormResponse::add("$('Form_EditForm').reloadIfSetTo($page->ID);");
849 }
850
851 if($statusMessage) FormResponse::status_message($statusMessage, 'good');
852 FormResponse::add("$('Form_EditForm').elements.StageURLSegment.value = '$JS_stageURL';");
853 FormResponse::add("$('Form_EditForm').elements.LiveURLSegment.value = '$JS_liveURL';");
854 FormResponse::add("$('Form_EditForm').notify('PagePublished', $('Form_EditForm').elements.ID.value);");
855
856 return FormResponse::respond();
857 }
858
859 function performRollback($id, $version) {
860 $record = DataObject::get_by_id($this->stat('tree_class'), $id);
861 if($record && !$record->canEdit()) return Security::permissionFailure($this);
862
863 $record->doRollbackTo($version);
864 return $record;
865 }
866
867 function getversion() {
868 $id = $this->urlParams['ID'];
869 $version = str_replace('&ajax=1','',$this->urlParams['OtherID']);
870 $record = Versioned::get_version("SiteTree", $id, $version);
871
872 if($record) {
873 if($record && !$record->canView()) return Security::permissionFailure($this);
874 $fields = $record->getCMSFields($this);
875 $fields->removeByName("Status");
876
877 $fields->push(new HiddenField("ID"));
878 $fields->push(new HiddenField("Version"));
879
880 $versionAuthor = DataObject::get_by_id('Member', $record->AuthorID);
881 if(!$versionAuthor) $versionAuthor = new ArrayData(array('Title' => 'Unknown author'));
882 $fields->insertBefore(
883 new LiteralField(
884 'YouAreViewingHeader',
885 '<p class="message notice">' .
886 sprintf(
887 _t(
888 'CMSMain.VIEWING',
889 "You are viewing version #%s, created %s by %s",
890 PR_MEDIUM,
891 'Version number is a linked string, created is a relative time (e.g. 2 days ago), by a specific author'
892 ),
893 "<a href=\"admin/getversion/$record->ID/$version\" title=\"" . ($versionAuthor ? $versionAuthor->Title : '') . "\">$version</a>",
894 $record->obj('LastEdited')->Ago(),
895 ($versionAuthor ? $versionAuthor->Title : '')
896 ) .
897 '</p>'
898 ),
899 'Root'
900 );
901
902 $actions = new FieldSet(
903 new FormAction("email", _t('CMSMain.EMAIL',"Email")),
904 new FormAction("rollback", _t('CMSMain.ROLLBACK',"Roll back to this version"))
905 );
906
907
908 $archiveURL = Director::absoluteBaseURL() . $record->URLSegment . '?archiveDate=' . $record->obj('LastEdited')->URLDatetime();
909
910
911 SSViewer::set_source_file_comments(false);
912
913 $archiveEmailMessage = urlencode( $this->customise( array( 'ArchiveDate' => $record->obj('LastEdited'), 'ArchiveURL' => $archiveURL ) )->renderWith( 'ViewArchivedEmail' ) );
914 $archiveEmailMessage = preg_replace( '/\+/', '%20', $archiveEmailMessage );
915
916 $fields->push( new HiddenField( 'ArchiveEmailMessage', '', $archiveEmailMessage ) );
917 $fields->push( new HiddenField( 'ArchiveEmailSubject', '', preg_replace( '/\+/', '%20', urlencode( 'Archived version of ' . $record->Title ) ) ) );
918 $fields->push( new HiddenField( 'ArchiveURL', '', $archiveURL ) );
919
920 $form = new Form($this, "EditForm", $fields, $actions);
921 $form->loadDataFrom($record);
922 $form->loadDataFrom(array(
923 "ID" => $id,
924 "Version" => $version,
925 ));
926
927
928 $readonlyFields = $form->Fields()->makeReadonly();
929 $form->setFields($readonlyFields);
930
931 $templateData = $this->customise(array(
932 "EditForm" => $form
933 ));
934
935 SSViewer::setOption('rewriteHashlinks', false);
936
937 if(Director::is_ajax()) {
938 $result = $templateData->renderWith($this->class . '_right');
939 $parts = split('</?form[^>]*>', $result);
940 $content = $parts[sizeof($parts)-2];
941 if($this->ShowSwitchView()) {
942 $content .= '<div id="AjaxSwitchView">' . $this->SwitchView($record) . '</div>';
943 }
944 return $content;
945 } else {
946 return $templateData->renderWith('LeftAndMain');
947 }
948 }
949 }
950
951 function compareversions() {
952 $id = (int)$this->urlParams['ID'];
953 $version1 = (int)$_REQUEST['From'];
954 $version2 = (int)$_REQUEST['To'];
955
956 if( $version1 > $version2 ) {
957 $toVersion = $version1;
958 $fromVersion = $version2;
959 } else {
960 $toVersion = $version2;
961 $fromVersion = $version1;
962 }
963
964 $page = DataObject::get_by_id("SiteTree", $id);
965 if($page && !$page->canView()) return Security::permissionFailure($this);
966
967 $record = $page->compareVersions($fromVersion, $toVersion);
968
969 $fromVersionRecord = Versioned::get_version('SiteTree', $id, $fromVersion);
970 $toVersionRecord = Versioned::get_version('SiteTree', $id, $toVersion);
971 if(!$fromVersionRecord) user_error("Can't find version $fromVersion of page $id", E_USER_ERROR);
972 if(!$toVersionRecord) user_error("Can't find version $toVersion of page $id", E_USER_ERROR);
973
974 if($record) {
975 $fromDateNice = $fromVersionRecord->obj('LastEdited')->Ago();
976 $toDateNice = $toVersionRecord->obj('LastEdited')->Ago();
977 $fromAuthor = DataObject::get_by_id('Member', $fromVersionRecord->AuthorID);
978 if(!$fromAuthor) $fromAuthor = new ArrayData(array('Title' => 'Unknown author'));
979 $toAuthor = DataObject::get_by_id('Member', $toVersionRecord->AuthorID);
980 if(!$toAuthor) $toAuthor = new ArrayData(array('Title' => 'Unknown author'));
981
982 $fields = $record->getCMSFields($this);
983 $fields->push(new HiddenField("ID"));
984 $fields->push(new HiddenField("Version"));
985 $fields->insertBefore(
986 new LiteralField(
987 'YouAreComparingHeader',
988 '<p class="message notice">' .
989 sprintf(
990 _t('CMSMain.COMPARINGV',"Comparing versions %s and %s"),
991 "<a href=\"admin/getversion/$id/$fromVersionRecord->Version\" title=\"$fromAuthor->Title\">$fromVersionRecord->Version</a> <small>($fromDateNice)</small>",
992 "<a href=\"admin/getversion/$id/$toVersionRecord->Version\" title=\"$toAuthor->Title\">$toVersionRecord->Version</a> <small>($toDateNice)</small>"
993 ) .
994 '</p>'
995 ),
996 "Root"
997 );
998
999 $actions = new FieldSet();
1000
1001 $form = new Form($this, "EditForm", $fields, $actions);
1002 $form->loadDataFrom($record);
1003 $form->loadDataFrom(array(
1004 "ID" => $id,
1005 "Version" => $fromVersion,
1006 ));
1007
1008
1009 $readonlyFields = $form->Fields()->makeReadonly();
1010 $form->setFields($readonlyFields);
1011
1012 foreach($form->Fields()->dataFields() as $field) {
1013 $field->dontEscape = true;
1014 }
1015
1016 return $this->sendFormToBrowser(array(
1017 "EditForm" => $form
1018 ));
1019 }
1020 }
1021
1022 function sendFormToBrowser($templateData) {
1023 if(Director::is_ajax()) {
1024 SSViewer::setOption('rewriteHashlinks', false);
1025 $result = $this->customise($templateData)->renderWith($this->class . '_right');
1026 $parts = split('</?form[^>]*>', $result);
1027 return $parts[sizeof($parts)-2];
1028 } else {
1029 return array(
1030 "Right" => $this->customise($templateData)->renderWith($this->class . '_right'),
1031 );
1032 }
1033 }
1034
1035 function dialog() {
1036 Requirements::clear();
1037
1038 $buttons = new DataObjectSet;
1039 if($_REQUEST['Buttons']) foreach($_REQUEST['Buttons'] as $button) {
1040 list($name, $title) = explode(',',$button,2);
1041 $buttons->push(new ArrayData(array(
1042 "Name" => $name,
1043 "Title" => $title,
1044 )));
1045 }
1046
1047 return array(
1048 "Message" => htmlentities($_REQUEST['Message']),
1049 "Buttons" => $buttons,
1050 "Modal" => $_REQUEST['Modal'] ? true : false,
1051 );
1052 }
1053
1054 function canceldraftchangesdialog() {
1055 Requirements::clear();
1056 Requirements::css(CMS_DIR . 'css/dialog.css');
1057 Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/prototype/prototype.js');
1058 Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/behaviour/behaviour.js');
1059 Requirements::javascript(SAPPHIRE_DIR . '/javascript/prototype_improvements.js');
1060 Requirements::javascript(CMS_DIR . '/javascript/dialog.js');
1061
1062 $message = _t('CMSMain.COPYPUBTOSTAGE',"Do you really want to copy the published content to the stage site?");
1063 $buttons = "<button name=\"OK\">" . _t('CMSMain.OK','OK') ."</button><button name=\"Cancel\">" . _t('CMSMain.CANCEL',"Cancel") . "</button>";
1064
1065 return $this->customise( array(
1066 'Message' => $message,
1067 'Buttons' => $buttons,
1068 'DialogType' => 'alert'
1069 ))->renderWith('Dialog');
1070 }
1071
1072 1073 1074
1075 function batchactions() {
1076 return new CMSBatchActionHandler($this, 'batchactions');
1077 }
1078
1079 1080 1081
1082 public function PublishItemsForm() {
1083 $form = new Form(
1084 $this,
1085 'PublishItemsForm',
1086 new FieldSet(
1087 new HiddenField('csvIDs'),
1088 new CheckboxField('ShowDrafts', _t('CMSMain_left.ss.SHOWONLYCHANGED','Show only changed pages'))
1089 ),
1090 new FieldSet(
1091 new FormAction('publishitems', _t('CMSMain_left.ss.PUBLISHCONFIRM','Publish the selected pages'))
1092 )
1093 );
1094 $form->addExtraClass('actionparams');
1095 return $form;
1096 }
1097
1098 function BatchActionParameters() {
1099 $batchActions = CMSBatchActionHandler::$batch_actions;
1100
1101 $forms = array();
1102 foreach($batchActions as $urlSegment => $batchAction) {
1103 $SNG_action = singleton($batchAction);
1104 if ($SNG_action->canView() && $fieldset = $SNG_action->getParameterFields()) {
1105 $formHtml = '';
1106 foreach($fieldset as $field) {
1107 $formHtml .= $field->Field();
1108 }
1109 $forms[$urlSegment] = $formHtml;
1110 }
1111 }
1112 $pageHtml = '';
1113 foreach($forms as $urlSegment => $html) {
1114 $pageHtml .= "<div class=\"params\" id=\"BatchActionParameters_$urlSegment\">$html</div>\n\n";
1115 }
1116 return new LiteralField("BatchActionParameters", '<div id="BatchActionParameters" style="display:none">'.$pageHtml.'</div>');
1117 }
1118 1119 1120
1121 function BatchActionList() {
1122 return $this->batchactions()->batchActionList();
1123 }
1124
1125 1126 1127
1128 function DeleteItemsForm() {
1129 $form = new Form(
1130 $this,
1131 'DeleteItemsForm',
1132 new FieldSet(
1133 new LiteralField('SelectedPagesNote',
1134 sprintf('<p>%s</p>', _t('CMSMain_left.ss.SELECTPAGESACTIONS','Select the pages that you want to change & then click an action:'))
1135 ),
1136 new HiddenField('csvIDs')
1137 ),
1138 new FieldSet(
1139 new FormAction('deleteitems', _t('CMSMain_left.ss.DELETECONFIRM','Delete the selected pages'))
1140 )
1141 );
1142 $form->addExtraClass('actionparams');
1143 return $form;
1144 }
1145
1146 function buildbrokenlinks() {
1147 if($this->urlParams['ID']) {
1148 $newPageSet[] = DataObject::get_by_id("Page", $this->urlParams['ID']);
1149 } else {
1150 $pages = DataObject::get("Page");
1151 foreach($pages as $page) $newPageSet[] = $page;
1152 $pages = null;
1153 }
1154
1155 $content = new HtmlEditorField('Content');
1156 $download = new HtmlEditorField('Download');
1157
1158 foreach($newPageSet as $i => $page) {
1159 $page->HasBrokenLink = 0;
1160 $page->HasBrokenFile = 0;
1161
1162 $lastUsage = (memory_get_usage() - $lastPoint);
1163 $lastPoint = memory_get_usage();
1164 $content->setValue($page->Content);
1165 $content->saveInto($page);
1166
1167 $download->setValue($page->Download);
1168 $download->saveInto($page);
1169
1170 echo "<li>$page->Title (link:$page->HasBrokenLink, file:$page->HasBrokenFile)";
1171
1172 $page->writeWithoutVersion();
1173 $page->destroy();
1174 $newPageSet[$i] = null;
1175 }
1176 }
1177
1178 function AddPageOptionsForm() {
1179 $pageTypes = array();
1180
1181 foreach( $this->PageTypes() as $arrayData ) {
1182 $pageTypes[$arrayData->getField('ClassName')] = $arrayData->getField('AddAction');
1183 }
1184
1185 $fields = new FieldSet(
1186 new HiddenField("ParentID"),
1187 new HiddenField("Locale", 'Locale', Translatable::get_current_locale()),
1188 new DropdownField("PageType", "", $pageTypes, 'Page')
1189 );
1190
1191 $this->extend('updatePageOptions', $fields);
1192
1193 $actions = new FieldSet(
1194 new FormAction("addpage", _t('CMSMain.GO',"Go"))
1195 );
1196
1197 return new Form($this, "AddPageOptionsForm", $fields, $actions);
1198 }
1199
1200 1201 1202
1203 function getpagecount() {
1204 ini_set('max_execution_time', 0);
1205 $excludePages = split(" *, *", $_GET['exclude']);
1206
1207 $pages = DataObject::get("SiteTree", "\"ParentID\" = 0");
1208 foreach($pages as $page) $pageArr[] = $page;
1209
1210 while(list($i,$page) = each($pageArr)) {
1211 if(!in_array($page->URLSegment, $excludePages)) {
1212 if($children = $page->AllChildren()) {
1213 foreach($children as $child) $pageArr[] = $child;
1214 }
1215
1216
1217 if(!$_GET['onlywithcontent'] || strlen(Convert::xml2raw($page->Content)) > 100) {
1218 echo "<li>" . $page->Breadcrumbs(null, true) . "</li>";
1219 $count++;
1220 } else {
1221 echo "<li style=\"color: #777\">" . $page->Breadcrumbs(null, true) . " - " . _t('CMSMain.NOCONTENT',"no content") . "</li>";
1222 }
1223
1224 }
1225 }
1226
1227 echo '<p>' . _t('CMSMain.TOTALPAGES',"Total pages: ") . "$count</p>";
1228 }
1229
1230 function publishall() {
1231 ini_set("memory_limit", -1);
1232 ini_set('max_execution_time', 0);
1233
1234 $response = "";
1235
1236 if(isset($this->requestParams['confirm'])) {
1237 $start = 0;
1238 $pages = DataObject::get("SiteTree", "", "", "", "$start,30");
1239 $count = 0;
1240 if($pages){
1241 while(true) {
1242 foreach($pages as $page) {
1243 if($page && !$page->canPublish()) return Security::permissionFailure($this);
1244
1245 $page->doPublish();
1246 $page->destroy();
1247 unset($page);
1248 $count++;
1249 $response .= "<li>$count</li>";
1250 }
1251 if($pages->Count() > 29) {
1252 $start += 30;
1253 $pages = DataObject::get("SiteTree", "", "", "", "$start,30");
1254 } else {
1255 break;
1256 }
1257 }
1258 }
1259 $response .= sprintf(_t('CMSMain.PUBPAGES',"Done: Published %d pages"), $count);
1260
1261 } else {
1262 $response .= '<h1>' . _t('CMSMain.PUBALLFUN','"Publish All" functionality') . '</h1>
1263 <p>' . _t('CMSMain.PUBALLFUN2', 'Pressing this button will do the equivalent of going to every page and pressing "publish". It\'s
1264 intended to be used after there have been massive edits of the content, such as when the site was
1265 first built.') . '</p>
1266 <form method="post" action="publishall">
1267 <input type="submit" name="confirm" value="'
1268 . _t('CMSMain.PUBALLCONFIRM',"Please publish every page in the site, copying content stage to live",PR_LOW,'Confirmation button') .'" />
1269 </form>';
1270 }
1271
1272 return $response;
1273 }
1274
1275 1276 1277
1278 function restore() {
1279 if(($id = $_REQUEST['ID']) && is_numeric($id)) {
1280 $restoredPage = Versioned::get_latest_version("SiteTree", $id);
1281 if($restoredPage) {
1282 $restoredPage = $restoredPage->doRestoreToStage();
1283
1284 FormResponse::get_page($id);
1285 $title = Convert::raw2js($restoredPage->TreeTitle());
1286 FormResponse::add("$('sitetree').setNodeTitle($id, '$title');");
1287 FormResponse::status_message(sprintf(_t('CMSMain.RESTORED',"Restored '%s' successfully",PR_MEDIUM,'Param %s is a title'),$title),'good');
1288 return FormResponse::respond();
1289
1290 } else {
1291 return new SS_HTTPResponse("SiteTree #$id not found", 400);
1292 }
1293 } else {
1294 return new SS_HTTPResponse("Please pass an ID in the form content", 400);
1295 }
1296 }
1297
1298 function duplicate() {
1299 if(($id = $this->urlParams['ID']) && is_numeric($id)) {
1300 $page = DataObject::get_by_id("SiteTree", $id);
1301 if($page && (!$page->canEdit() || !$page->canCreate())) {
1302 return Security::permissionFailure($this);
1303 }
1304
1305 $newPage = $page->duplicate();
1306
1307
1308 if($_GET['parentID'] && is_numeric($_GET['parentID'])) {
1309 $newPage->ParentID = $_GET['parentID'];
1310 $newPage->write();
1311 }
1312
1313 return $this->returnItemToUser($newPage);
1314 } else {
1315 user_error("CMSMain::duplicate() Bad ID: '$id'", E_USER_WARNING);
1316 }
1317 }
1318
1319 function duplicatewithchildren() {
1320 if(($id = $this->urlParams['ID']) && is_numeric($id)) {
1321 $page = DataObject::get_by_id("SiteTree", $id);
1322 if($page && (!$page->canEdit() || !$page->canCreate())) {
1323 return Security::permissionFailure($this);
1324 }
1325
1326 $newPage = $page->duplicateWithChildren();
1327
1328 return $this->returnItemToUser($newPage);
1329 } else {
1330 user_error("CMSMain::duplicate() Bad ID: '$id'", E_USER_WARNING);
1331 }
1332 }
1333
1334
1335
1336 1337 1338
1339 function createtranslation () {
1340 $langCode = Convert::raw2sql($_REQUEST['newlang']);
1341 $originalLangID = (int)$_REQUEST['ID'];
1342
1343 $record = $this->getRecord($originalLangID);
1344
1345 $this->Locale = $langCode;
1346 Translatable::set_current_locale($langCode);
1347
1348
1349
1350
1351
1352
1353 $translatedRecord = $record->createTranslation($langCode);
1354
1355 $url = sprintf(
1356 "%s/%d/?locale=%s",
1357 $this->Link('show'),
1358 $translatedRecord->ID,
1359 $langCode
1360 );
1361 FormResponse::add(sprintf('window.location.href = "%s";', $url));
1362 return FormResponse::respond();
1363 }
1364
1365 1366 1367 1368
1369 function providePermissions() {
1370 $classes = ClassInfo::subclassesFor('LeftAndMain');
1371
1372 foreach($classes as $i => $class) {
1373 $title = _t("{$class}.MENUTITLE", LeftAndMain::menu_title_for_class($class));
1374 $perms["CMS_ACCESS_" . $class] = array(
1375 'name' => sprintf(_t(
1376 'CMSMain.ACCESS',
1377 "Access to '%s' section",
1378 PR_MEDIUM,
1379 "Item in permission selection identifying the admin section. Example: Access to 'Files & Images'"
1380 ), $title, null),
1381 'category' => _t('Permission.CMS_ACCESS_CATEGORY', 'CMS Access')
1382 );
1383 }
1384 $perms["CMS_ACCESS_LeftAndMain"] = array(
1385 'name' => _t('CMSMain.ACCESSALLINTERFACES', 'Access to all CMS sections'),
1386 'category' => _t('Permission.CMS_ACCESS_CATEGORY', 'CMS Access'),
1387 'help' => _t('CMSMain.ACCESSALLINTERFACESHELP', 'Overrules more specific access settings.'),
1388 'sort' => -100
1389 );
1390 $perms['CMS_ACCESS_CMSMain']['help'] = _t(
1391 'CMSMain.ACCESS_HELP',
1392 '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".'
1393 );
1394 $perms['CMS_ACCESS_SecurityAdmin']['help'] = _t(
1395 'SecurityAdmin.ACCESS_HELP',
1396 'Allow viewing, adding and editing users, as well as assigning permissions and roles to them.'
1397 );
1398
1399 if (isset($perms['CMS_ACCESS_ModelAdmin'])) unset($perms['CMS_ACCESS_ModelAdmin']);
1400
1401 return $perms;
1402 }
1403
1404 1405 1406 1407
1408 function LangSelector() {
1409 $member = Member::currentUser();
1410 $dropdown = new LanguageDropdownField(
1411 'LangSelector',
1412 'Language',
1413 array(),
1414 'SiteTree',
1415 'Locale-English',
1416 singleton('SiteTree')
1417 );
1418 $dropdown->setValue(Translatable::get_current_locale());
1419 return $dropdown;
1420 }
1421
1422 1423 1424 1425 1426
1427 function MultipleLanguages() {
1428 $langs = Translatable::get_existing_content_languages('SiteTree');
1429
1430 return (count($langs) > 1);
1431 }
1432
1433 1434 1435
1436 function IsTranslatableEnabled() {
1437 return Object::has_extension('SiteTree', 'Translatable');
1438 }
1439 }
1440
1441 1442 1443 1444
1445 class CMSMainMarkingFilter {
1446
1447 function __construct() {
1448 $this->ids = array();
1449 $this->expanded = array();
1450
1451 $where = array();
1452
1453
1454 if (isset($_REQUEST['SiteTreeSearchTerm'])) {
1455 $term = Convert::raw2sql($_REQUEST['SiteTreeSearchTerm']);
1456 $where[] = "\"URLSegment\" LIKE '%$term%' OR \"Title\" LIKE '%$term%' OR \"MenuTitle\" LIKE '%$term%' OR \"Content\" LIKE '%$term%'";
1457 }
1458
1459
1460 if (isset($_REQUEST['SiteTreeFilterDate'])) {
1461 $date = $_REQUEST['SiteTreeFilterDate'];
1462 $date = ((int)substr($date,6,4)) . '-' . ((int)substr($date,3,2)) . '-' . ((int)substr($date,0,2));
1463 $where[] = "\"LastEdited\" > '$date'";
1464 }
1465
1466
1467 if (isset($_REQUEST['ClassName']) && $_REQUEST['ClassName'] != 'All') {
1468 $klass = Convert::raw2sql($_REQUEST['ClassName']);
1469 $where[] = "\"ClassName\" = '$klass'";
1470 }
1471
1472
1473 foreach (CMSMain::T_SiteTreeFilterOptions() as $key => $value) {
1474 if (!empty($_REQUEST[$key])) {
1475 $match = Convert::raw2sql($_REQUEST[$key]);
1476 $where[] = "\"$key\" LIKE '%$match%'";
1477 }
1478 }
1479
1480 $where = empty($where) ? '' : 'WHERE (' . implode(') AND (',$where) . ')';
1481
1482 $parents = array();
1483
1484
1485 $res = DB::query('SELECT "ParentID", "ID" FROM "SiteTree" '.$where);
1486 if (!$res) return;
1487
1488
1489 foreach($res as $row) {
1490 if ($row['ParentID']) $parents[$row['ParentID']] = true;
1491 $this->ids[$row['ID']] = true;
1492 }
1493
1494
1495 while (!empty($parents)) {
1496 $res = DB::query('SELECT "ParentID", "ID" FROM "SiteTree" WHERE "ID" in ('.implode(',',array_keys($parents)).')');
1497 $parents = array();
1498
1499 foreach($res as $row) {
1500 if ($row['ParentID']) $parents[$row['ParentID']] = true;
1501 $this->ids[$row['ID']] = true;
1502 $this->expanded[$row['ID']] = true;
1503 }
1504 }
1505 }
1506
1507 function mark($node) {
1508 $id = $node->ID;
1509 if(array_key_exists((int) $id, $this->expanded)) $node->markOpened();
1510 return array_key_exists((int) $id, $this->ids) ? $this->ids[$id] : false;
1511 }
1512 }
1513
1514 ?>
1515
[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.
-