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  * The most common kind of controller; effectively a controller linked to a {@link DataObject}.
  4  *
  5  * ContentControllers are most useful in the content-focused areas of a site.  This is generally
  6  * the bulk of a site; however, they may be less appropriate in, for example, the user management
  7  * section of an application.
  8  *
  9  * On its own, content controller does very little.  Its constructor is passed a {@link DataObject}
 10  * which is stored in $this->dataRecord.  Any unrecognised method calls, for example, Title()
 11  * and Content(), will be passed along to the data record,
 12  *
 13  * Subclasses of ContentController are generally instantiated by ModelAsController; this will create
 14  * a controller based on the URLSegment action variable, by looking in the SiteTree table.
 15  * 
 16  * @todo Can this be used for anything other than SiteTree controllers?
 17  *
 18  * @package sapphire
 19  * @subpackage control
 20  */
 21 class ContentController extends Controller {
 22 
 23     protected $dataRecord;
 24     
 25     static $url_handlers = array(
 26         'widget/$ID/$Action' => 'handleWidget'
 27     );
 28     
 29     public static $allowed_actions = array (
 30         'PageComments',
 31         'successfullyinstalled',
 32         'deleteinstallfiles' // secured through custom code
 33     );
 34     
 35     /**
 36      * The ContentController will take the URLSegment parameter from the URL and use that to look
 37      * up a SiteTree record.
 38      */
 39     public function __construct($dataRecord = null) {
 40         if(!$dataRecord) {
 41             $dataRecord = new Page();
 42             if($this->hasMethod("Title")) $dataRecord->Title = $this->Title();
 43             $dataRecord->URLSegment = get_class($this);
 44             $dataRecord->ID = -1;
 45         }
 46         
 47         $this->dataRecord = $dataRecord;
 48         $this->failover = $this->dataRecord;
 49         parent::__construct();
 50     }
 51     
 52     /**
 53      * Return the link to this controller, but force the expanded link to be returned so that form methods and
 54      * similar will function properly.
 55      *
 56      * @return string
 57      */
 58     public function Link($action = null) {
 59         return $this->data()->Link(($action ? $action : true));
 60     }
 61     
 62     //----------------------------------------------------------------------------------//
 63     // These flexible data methods remove the need for custom code to do simple stuff
 64     
 65     /**
 66      * Return the children of a given page. The parent reference can either be a page link or an ID.
 67      *
 68      * @param string|int $parentRef
 69      * @return DataObjectSet
 70      */
 71     public function ChildrenOf($parentRef) {
 72         $parent = SiteTree::get_by_link($parentRef);
 73         
 74         if(!$parent && is_numeric($parentRef)) {
 75             $parent = DataObject::get_by_id('SiteTree', Convert::raw2sql($parentRef));
 76         }
 77         
 78         if($parent) return $parent->Children();
 79     }
 80     
 81     /**
 82      * @return DataObjectSet
 83      */
 84     public function Page($link) {
 85         return SiteTree::get_by_link($link);
 86     }
 87 
 88     public function init() {
 89         parent::init();
 90         
 91         // If we've accessed the homepage as /home/, then we should redirect to /.
 92         if($this->dataRecord && $this->dataRecord instanceof SiteTree
 93                 && RootURLController::should_be_on_root($this->dataRecord) && (!isset($this->urlParams['Action']) || !$this->urlParams['Action'] ) 
 94                 && !$_POST && !$_FILES && !Director::redirected_to() ) {
 95             $getVars = $_GET;
 96             unset($getVars['url']);
 97             if($getVars) $url = "?" . http_build_query($getVars);
 98             else $url = "";
 99             Director::redirect($url, 301);
100             return;
101         }
102         
103         // Use theme from the site config
104         if(($config = SiteConfig::current_site_config()) && $config->Theme) {
105             SSViewer::set_theme($config->Theme);
106         }
107         
108         if($this->dataRecord) $this->dataRecord->extend('contentcontrollerInit', $this);
109         else singleton('SiteTree')->extend('contentcontrollerInit', $this);
110 
111         if(Director::redirected_to()) return;
112 
113         // Check page permissions
114         if($this->dataRecord && $this->URLSegment != 'Security' && !$this->dataRecord->canView()) {
115             return Security::permissionFailure($this);
116         }
117 
118         // Draft/Archive security check - only CMS users should be able to look at stage/archived content
119         if($this->URLSegment != 'Security' && !Session::get('unsecuredDraftSite') && (Versioned::current_archived_date() || (Versioned::current_stage() && Versioned::current_stage() != 'Live'))) {
120             if(!Permission::check('CMS_ACCESS_CMSMain') && !Permission::check('VIEW_DRAFT_CONTENT')) {
121                 $link = $this->Link();
122                 $message = _t("ContentController.DRAFT_SITE_ACCESS_RESTRICTION", 'You must log in with your CMS password in order to view the draft or archived content.  <a href="%s">Click here to go back to the published site.</a>');
123                 Session::clear('currentStage');
124                 Session::clear('archiveDate');
125                 Session::clear('readingMode');
126                 
127                 return Security::permissionFailure($this, sprintf($message, "$link?stage=Live"));
128             }
129         }
130 
131         
132     }
133     
134     /**
135      * This acts the same as {@link Controller::handleRequest()}, but if an action cannot be found this will attempt to
136      * fall over to a child controller in order to provide functionality for nested URLs.
137      *
138      * @return SS_HTTPResponse
139      */
140     public function handleRequest(SS_HTTPRequest $request) {        
141         $child  = null;
142         $action = $request->param('Action');
143         
144         // If nested URLs are enabled, and there is no action handler for the current request then attempt to pass
145         // control to a child controller. This allows for the creation of chains of controllers which correspond to a
146         // nested URL.
147         if($action && SiteTree::nested_urls() && !$this->hasAction($action)) {
148             // See ModelAdController->getNestedController() for similar logic
149             Translatable::disable_locale_filter();
150             // look for a page with this URLSegment
151             $child = DataObject::get_one('SiteTree', sprintf (
152                 "\"ParentID\" = %s AND \"URLSegment\" = '%s'", $this->ID, Convert::raw2sql($action)
153             ));
154             Translatable::enable_locale_filter();
155             
156             // if we can't find a page with this URLSegment try to find one that used to have 
157             // that URLSegment but changed. See ModelAsController->getNestedController() for similiar logic.
158             if(!$child){
159                 $child = ModelAsController::find_old_page($action,$this->ID);
160                 if($child){
161                     $response = new SS_HTTPResponse();
162                     $params = $request->getVars();
163                     if(isset($params['url'])) unset($params['url']);
164                     $response->redirect(
165                         Controller::join_links(
166                             $child->Link(
167                                 Controller::join_links(
168                                     $request->param('ID'), // 'ID' is the new 'URLSegment', everything shifts up one position
169                                     $request->param('OtherID')
170                                 )
171                             ),
172                             // Needs to be in separate join links to avoid urlencoding
173                             ($params) ? '?' . http_build_query($params) : null
174                         ),
175                         301
176                     );
177                     return $response;
178                 }
179             }
180         }
181         
182         // we found a page with this URLSegment.
183         if($child) {
184             $request->shiftAllParams();
185             $request->shift();
186             
187             $response = ModelAsController::controller_for($child)->handleRequest($request);
188         } else {
189             // If a specific locale is requested, and it doesn't match the page found by URLSegment,
190             // look for a translation and redirect (see #5001). Only happens on the last child in
191             // a potentially nested URL chain.
192             if($request->getVar('locale') && $this->dataRecord && $this->dataRecord->Locale != $request->getVar('locale')) {
193                 $translation = $this->dataRecord->getTranslation($request->getVar('locale'));
194                 if($translation) {
195                     $response = new SS_HTTPResponse();
196                     $response->redirect($translation->Link(), 301);
197                     throw new SS_HTTPResponse_Exception($response);
198                 }
199             }
200             
201             Director::set_current_page($this->data());
202             $response = parent::handleRequest($request);
203             Director::set_current_page(null);
204         }
205         
206         return $response;
207     }
208     
209     /**
210      * @uses ErrorPage::response_for()
211      */
212     public function httpError($code, $message = null) {
213         if($this->request->isMedia() || !$response = ErrorPage::response_for($code)) {
214             parent::httpError($code, $message);
215         } else {
216             throw new SS_HTTPResponse_Exception($response);
217         }
218     }
219     
220     /**
221      * Handles widgets attached to a page through one or more {@link WidgetArea} elements.
222      * Iterated through each $has_one relation with a {@link WidgetArea}
223      * and looks for connected widgets by their database identifier.
224      * Assumes URLs in the following format: <URLSegment>/widget/<Widget-ID>.
225      * 
226      * @return RequestHandler
227      */
228     function handleWidget() {
229         $SQL_id = $this->request->param('ID');
230         if(!$SQL_id) return false;
231         
232         // find WidgetArea relations
233         $widgetAreaRelations = array();
234         $hasOnes = $this->dataRecord->has_one();
235         if(!$hasOnes) return false;
236         foreach($hasOnes as $hasOneName => $hasOneClass) {
237             if($hasOneClass == 'WidgetArea' || ClassInfo::is_subclass_of($hasOneClass, 'WidgetArea')) {
238                 $widgetAreaRelations[] = $hasOneName;
239             }
240         }
241 
242         // find widget
243         $widget = null;
244         foreach($widgetAreaRelations as $widgetAreaRelation) {
245             if($widget) break;
246             $widget = $this->dataRecord->$widgetAreaRelation()->Widgets(
247                 sprintf('"Widget"."ID" = %d', $SQL_id)
248             )->First();
249         }
250         if(!$widget) user_error('No widget found', E_USER_ERROR);
251         
252         // find controller
253         $controllerClass = '';
254         foreach(array_reverse(ClassInfo::ancestry($widget->class)) as $widgetClass) {
255             $controllerClass = "{$widgetClass}_Controller";
256             if(class_exists($controllerClass)) break;
257         }
258         if(!$controllerClass) user_error(
259             sprintf('No controller available for %s', $widget->class),
260             E_USER_ERROR
261         );
262 
263         return new $controllerClass($widget);
264     }
265 
266     /**
267      * Get the project name
268      *
269      * @return string
270      */
271     function project() {
272         global $project;
273         return $project;
274     }
275     
276     /**
277      * Returns the associated database record
278      */
279     public function data() {
280         return $this->dataRecord;
281     }
282 
283     /*--------------------------------------------------------------------------------*/
284 
285     /**
286      * Returns a fixed navigation menu of the given level.
287      * @return DataObjectSet
288      */
289     public function getMenu($level = 1) {
290         if($level == 1) {
291             $result = DataObject::get("SiteTree", "\"ShowInMenus\" = 1 AND \"ParentID\" = 0");
292 
293         } else {
294             $parent = $this->data();
295             $stack = array($parent);
296             
297             if($parent) {
298                 while($parent = $parent->Parent) {
299                     array_unshift($stack, $parent);
300                 }
301             }
302             
303             if(isset($stack[$level-2])) $result = $stack[$level-2]->Children();
304         }
305 
306         $visible = array();
307 
308         // Remove all entries the can not be viewed by the current user
309         // We might need to create a show in menu permission
310         if(isset($result)) {
311             foreach($result as $page) {
312                 if($page->canView()) {
313                     $visible[] = $page;
314                 }
315             }
316         }
317 
318         return new DataObjectSet($visible);
319     }
320 
321     public function Menu($level) {
322         return $this->getMenu($level);
323     }
324 
325     /**
326      * Returns the default log-in form.
327      *
328      * @todo Check if here should be returned just the default log-in form or
329      *       all available log-in forms (also OpenID...)
330      */
331     public function LoginForm() {
332         return MemberAuthenticator::get_login_form($this);
333     }
334 
335     public function SilverStripeNavigator() {
336         $member = Member::currentUser();
337         $items = '';
338         $message = '';
339 
340         if(Director::isDev() || Permission::check('CMS_ACCESS_CMSMain') || Permission::check('VIEW_DRAFT_CONTENT')) {           
341             if($this->dataRecord) {
342                 Requirements::css(SAPPHIRE_DIR . '/css/SilverStripeNavigator.css');
343                 // TODO Using jQuery for this is absolute overkill, and might cause conflicts
344                 // with other libraries.
345 //              Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/jquery/jquery.js');
346                 Requirements::javascript(SAPPHIRE_DIR . '/javascript/SilverStripeNavigator.js');
347                 
348                 $return = $nav = SilverStripeNavigator::get_for_record($this->dataRecord);
349                 $items = $return['items'];
350                 $message = $return['message'];
351             }
352 
353             if($member) {
354                 $firstname = Convert::raw2xml($member->FirstName);
355                 $surname = Convert::raw2xml($member->Surname);
356                 $logInMessage = _t('ContentController.LOGGEDINAS', 'Logged in as') ." {$firstname} {$surname} - <a href=\"Security/logout\">". _t('ContentController.LOGOUT', 'Log out'). "</a>";
357             } else {
358                 $logInMessage = _t('ContentController.NOTLOGGEDIN', 'Not logged in') ." - <a href=\"Security/login\">". _t('ContentController.LOGIN', 'Login') ."</a>";
359             }
360             $viewPageIn = _t('ContentController.VIEWPAGEIN', 'View Page in:');
361             
362             Requirements::customScript("window.name = windowName('site');");
363             
364             return <<<HTML
365                 <div id="SilverStripeNavigator">
366                     <div class="holder">
367                     <div id="logInStatus">
368                         $logInMessage
369                     </div>
370 
371                     <div id="switchView" class="bottomTabs">
372                         <div class="blank">$viewPageIn </div>
373                         $items
374                     </div>
375                     </div>
376                 </div>
377                     $message
378 HTML;
379 
380         // On live sites we should still see the archived message
381         } else {
382             if($date = Versioned::current_archived_date()) {
383                 Requirements::css(SAPPHIRE_DIR . '/css/SilverStripeNavigator.css');
384                 $dateObj = Object::create('Datetime', $date, null);
385                 // $dateObj->setVal($date);
386                 return "<div id=\"SilverStripeNavigatorMessage\">". _t('ContentController.ARCHIVEDSITEFROM') ."<br>" . $dateObj->Nice() . "</div>";
387             }
388         }
389     }
390 
391     /**
392      * Returns a page comment system
393      */
394     function PageComments() {
395         $hasComments = DB::query("SELECT COUNT(*) FROM \"PageComment\" WHERE \"PageComment\".\"ParentID\" = '". Convert::raw2sql($this->ID) . "'")->value();
396         if(($this->data() && $this->data()->ProvideComments) || ($hasComments > 0 && PageCommentInterface::$show_comments_when_disabled)) {
397             return new PageCommentInterface($this, 'PageComments', $this->data());
398         } else {
399             if(isset($_REQUEST['executeForm']) && $_REQUEST['executeForm'] == 'PageComments.PostCommentForm') {
400                 echo "Comments have been disabled for this page";
401                 die();
402             }
403         }
404     }
405     
406     function SiteConfig() {
407         return SiteConfig::current_site_config();
408     }
409 
410     /**
411      * Returns the xml:lang and lang attributes.
412      * 
413      * @deprecated 2.5 Use ContentLocale() instead and write attribute names suitable to XHTML/HTML
414      * templates directly in the template.
415      */
416     function LangAttributes() {
417         $locale = $this->ContentLocale();
418         return "xml:lang=\"$locale\" lang=\"$locale\""; 
419     }
420     
421     /**
422      * Returns an RFC1766 compliant locale string, e.g. 'fr-CA'.
423      * Inspects the associated {@link dataRecord} for a {@link SiteTree->Locale} value if present,
424      * and falls back to {@link Translatable::get_current_locale()} or {@link i18n::default_locale()},
425      * depending if Translatable is enabled.
426      * 
427      * Suitable for insertion into lang= and xml:lang=
428      * attributes in HTML or XHTML output.
429      * 
430      * @return string
431      */
432     function ContentLocale() {
433         if($this->dataRecord && $this->dataRecord->hasExtension('Translatable')) {
434             $locale = $this->dataRecord->Locale;
435         } elseif(Object::has_extension('SiteTree', 'Translatable')) {
436             $locale = Translatable::get_current_locale();
437         } else {
438             $locale = i18n::get_locale();
439         }
440         
441         return i18n::convert_rfc1766($locale);
442     }
443 
444     /**
445      * This action is called by the installation system
446      */
447     function successfullyinstalled() {
448         // The manifest should be built by now, so it's safe to publish the 404 page
449         $fourohfour = Versioned::get_one_by_stage('ErrorPage', 'Stage', '"ErrorCode" = 404');
450         if($fourohfour) {
451             $fourohfour->Status = "Published";
452             $fourohfour->write();
453             $fourohfour->publish("Stage", "Live");
454         }
455         
456         // TODO Allow this to work when allow_url_fopen=0
457         if(isset($_SESSION['StatsID']) && $_SESSION['StatsID']) {
458             $url = 'http://ss2stat.silverstripe.com/Installation/installed?ID=' . $_SESSION['StatsID'];
459             @file_get_contents($url);
460         }
461         
462         $title = new Varchar("Title");
463         $content = new HTMLText("Content");
464         $username = Session::get('username');
465         $password = Session::get('password');
466         $title->setValue("Installation Successful");
467         global $project;
468         $tutorialOnly = ($project == 'tutorial') ? "<p>This website is a simplistic version of a SilverStripe 2 site. To extend this, please take a look at <a href=\"http://doc.silverstripe.org/doku.php?id=tutorials\">our new tutorials</a>.</p>" : '';
469         $content->setValue(<<<HTML
470             <p style="margin: 1em 0"><b>Congratulations, SilverStripe has been successfully installed.</b></p>
471             
472             $tutorialOnly
473             <p>You can start editing your site's content by opening <a href="admin/">the CMS</a>. <br />
474                 &nbsp; &nbsp; Email: $username<br />
475                 &nbsp; &nbsp; Password: $password<br />
476             </p>
477             <div style="background:#ddd; border:1px solid #ccc; padding:5px; margin:5px;"><img src="cms/images/dialogs/alert.gif" style="border: none; margin-right: 10px; float: left;" /><p style="color:red;">For security reasons you should now delete the install files, unless you are planning to reinstall later (<em>requires admin login, see above</em>). The web server also now only needs write access to the "assets" folder, you can remove write access from all other folders. <a href="home/deleteinstallfiles" style="text-align: center;">Click here to delete the install files.</a></p></div>
478 HTML
479 );
480 
481         return array(
482             "Title" => $title,
483             "Content" => $content,
484         );
485     }
486 
487     function deleteinstallfiles() {
488         if(!Permission::check("ADMIN")) return Security::permissionFailure($this);
489         
490         $title = new Varchar("Title");
491         $content = new HTMLText("Content");
492         $tempcontent = '';
493         $username = Session::get('username');
494         $password = Session::get('password');
495 
496         // We can't delete index.php as it might be necessary for URL routing without mod_rewrite.
497         // There's no safe way to detect usage of mod_rewrite across webservers,
498         // so we have to assume the file is required.
499         $installfiles = array(
500             'install.php',
501             'config-form.css',
502             'config-form.html',
503             'index.html'
504         );
505 
506         foreach($installfiles as $installfile) {
507             if(file_exists(BASE_PATH . '/' . $installfile)) {
508                 @unlink(BASE_PATH . '/' . $installfile);
509             }
510 
511             if(file_exists(BASE_PATH . '/' . $installfile)) {
512                 $unsuccessful[] = $installfile;
513             }
514         }
515 
516         if(isset($unsuccessful)) {
517             $title->setValue("Unable to delete installation files");
518             $tempcontent = "<p style=\"margin: 1em 0\">Unable to delete installation files. Please delete the files below manually:</p><ul>";
519             foreach($unsuccessful as $unsuccessfulFile) {
520                 $tempcontent .= "<li>$unsuccessfulFile</li>";
521             }
522             $tempcontent .= "</ul>";
523         } else {
524             $title->setValue("Deleted installation files");
525             $tempcontent = <<<HTML
526 <p style="margin: 1em 0">Installation files have been successfully deleted.</p>
527 HTML
528             ;
529         }
530 
531         $tempcontent .= <<<HTML
532             <p style="margin: 1em 0">You can start editing your site's content by opening <a href="admin/">the CMS</a>. <br />
533                 &nbsp; &nbsp; Email: $username<br />
534                 &nbsp; &nbsp; Password: $password<br />
535             </p>
536 HTML
537         ;
538         $content->setValue($tempcontent);
539 
540         return array(
541             "Title" => $title,
542             "Content" => $content,
543         );
544     }
545 }
546 
547 ?>
548 
[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