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

  • _DiffEngine
  • _DiffOp
  • _DiffOp_Add
  • _DiffOp_Change
  • _DiffOp_Copy
  • _DiffOp_Delete
  • BookingOrderAdmin
  • BookingOrderAdmin_CollectionController
  • CatalogAdmin_CollectionController
  • CatalogAdmin_RecordController
  • CMSActionOptionsForm
  • Diff
  • GuestbookAdmin_CollectionController
  • ImportAdmin_CollectionController
  • LeftAndMain
  • LeftAndMainDecorator
  • LoggerAdmin_CollectionController
  • LoggerAdmin_RecordController
  • MappedDiff
  • ModelAdmin
  • ModelAdmin_CollectionController
  • ModelAdmin_RecordController
  • OrderAdmin_CollectionController
  • OrderAdmin_RecordController
  • PaymentAdmin
  • PaymentAdmin_CollectionController
  • RealtyImportAdmin
  • RealtyImportAdmin_CollectionController
  • RedirectEntry_Admin
  • RoomServiceAdmin
  • ShippingMethodAdmin_CollectionController
  • SubsiteAdmin_CollectionController
  • VKNotificationQueueAdmin
   1 <?php
   2 /**
   3  * LeftAndMain is the parent class of all the two-pane views in the CMS.
   4  * If you are wanting to add more areas to the CMS, you can do it by subclassing LeftAndMain.
   5  * 
   6  * This is essentially an abstract class which should be subclassed.
   7  * See {@link CMSMain} for a good example.
   8  * 
   9  * @package cms
  10  * @subpackage core
  11  */
  12 class LeftAndMain extends Controller {
  13     
  14     /**
  15      * The 'base' url for CMS administration areas.
  16      * Note that if this is changed, many javascript
  17      * behaviours need to be updated with the correct url
  18      *
  19      * @var string $url_base
  20      */
  21     static $url_base = "admin";
  22     
  23     static $url_segment;
  24     
  25     static $url_rule = '/$Action/$ID/$OtherID';
  26     
  27     static $menu_title;
  28     
  29     static $menu_priority = 0;
  30     
  31     static $url_priority = 50;
  32 
  33     static $tree_class = null;
  34     
  35     static $ForceReload;
  36 
  37     static $allowed_actions = array(
  38         'index',
  39         'ajaxupdateparent',
  40         'ajaxupdatesort',
  41         'callPageMethod',
  42         'deleteitems',
  43         'getitem',
  44         'getsubtree',
  45         'myprofile',
  46         'printable',
  47         'save',
  48         'show',
  49         'Member_ProfileForm',
  50         'EditorToolbar',
  51         'EditForm',
  52     );
  53     
  54     /**
  55      * Register additional requirements through the {@link Requirements class}.
  56      * Used mainly to work around the missing "lazy loading" functionality
  57      * for getting css/javascript required after an ajax-call (e.g. loading the editform).
  58      *
  59      * @var array $extra_requirements
  60      */
  61     protected static $extra_requirements = array(
  62         'javascript' => array(),
  63         'css' => array(),
  64         'themedcss' => array(),
  65     );
  66     
  67     /**
  68      * @param Member $member
  69      * @return boolean
  70      */
  71     function canView($member = null) {
  72         if(!$member && $member !== FALSE) {
  73             $member = Member::currentUser();
  74         }
  75         
  76         // cms menus only for logged-in members
  77         if(!$member) return false;
  78         
  79         // alternative decorated checks
  80         if($this->hasMethod('alternateAccessCheck')) {
  81             $alternateAllowed = $this->alternateAccessCheck();
  82             if($alternateAllowed === FALSE) return false;
  83         }
  84             
  85         // Default security check for LeftAndMain sub-class permissions
  86         if(!Permission::checkMember($member, "CMS_ACCESS_$this->class") && 
  87            !Permission::checkMember($member, "CMS_ACCESS_LeftAndMain")) {
  88             return false;
  89         }
  90         
  91         return true;
  92     }
  93     
  94     /**
  95      * @uses LeftAndMainDecorator->init()
  96      * @uses LeftAndMainDecorator->accessedCMS()
  97      * @uses CMSMenu
  98      */
  99     function init() {
 100         parent::init();
 101         
 102         // set language
 103         $member = Member::currentUser();
 104         if(!empty($member->Locale)) {
 105             i18n::set_locale($member->Locale);
 106         }
 107         
 108 
 109         // can't be done in cms/_config.php as locale is not set yet
 110         CMSMenu::add_link(
 111             'Help', 
 112             _t('LeftAndMain.HELP', 'Help', PR_HIGH, 'Menu title'), 
 113             'http://support.mediaweb.ru/faq31/',
 114             -100
 115         );
 116 
 117         
 118         // set reading lang
 119         if(Object::has_extension('SiteTree', 'Translatable') && !Director::is_ajax()) {
 120             Translatable::choose_site_locale(array_keys(Translatable::get_existing_content_languages('SiteTree')));
 121         }
 122 
 123         // Allow customisation of the access check by a decorator
 124         // Also all the canView() check to execute Director::redirect()
 125         if(!$this->canView() && !$this->response->isFinished()) {
 126             // When access /admin/, we should try a redirect to another part of the admin rather than be locked out
 127             $menu = $this->MainMenu();
 128             foreach($menu as $candidate) {
 129                 if(
 130                     $candidate->Link && 
 131                     $candidate->Link != $this->Link() 
 132                     && $candidate->MenuItem->controller 
 133                     && singleton($candidate->MenuItem->controller)->canView()
 134                 ) {
 135                     return Director::redirect($candidate->Link);
 136                 }
 137             }
 138             
 139             if(Member::currentUser()) {
 140                 Session::set("BackURL", null);
 141             }
 142             
 143             // if no alternate menu items have matched, return a permission error
 144             $messageSet = array(
 145                 'default' => _t('LeftAndMain.PERMDEFAULT',"Please choose an authentication method and enter your credentials to access the CMS."),
 146                 'alreadyLoggedIn' => _t('LeftAndMain.PERMALREADY',"I'm sorry, but you can't access that part of the CMS.  If you want to log in as someone else, do so below"),
 147                 'logInAgain' => _t('LeftAndMain.PERMAGAIN',"You have been logged out of the CMS.  If you would like to log in again, enter a username and password below."),
 148             );
 149 
 150             return Security::permissionFailure($this, $messageSet);
 151         }
 152         
 153         // Don't continue if there's already been a redirection request.
 154         if(Director::redirected_to()) return;
 155 
 156         // Audit logging hook
 157         if(empty($_REQUEST['executeForm']) && !Director::is_ajax()) $this->extend('accessedCMS');
 158 
 159         // Set the members html editor config
 160         HtmlEditorConfig::set_active(Member::currentUser()->getHtmlEditorConfigForCMS());
 161         
 162         
 163         // Set default values in the config if missing.  These things can't be defined in the config
 164         // file because insufficient information exists when that is being processed
 165         $htmlEditorConfig = HtmlEditorConfig::get_active();
 166         if(!$htmlEditorConfig->getOption('content_css')) {
 167             $cssFiles = 'cms/css/editor.css';
 168             
 169             // Use theme from the site config
 170             if(($config = SiteConfig::current_site_config()) && $config->Theme) {
 171                 $theme = $config->Theme;
 172             } elseif(SSViewer::current_theme()) {
 173                 $theme = SSViewer::current_theme();
 174             } else {
 175                 $theme = false;
 176             }
 177             if ($theme && file_exists(THEMES_PATH . "/{$theme}/css/editor.css")) {
 178                 $cssFiles .= ', ' . THEMES_DIR . "/{$theme}/css/editor.css?m=" . filemtime(THEMES_PATH . "/{$theme}/css/editor.css");
 179             }
 180             else if(project()) {
 181                 $cssFiles .= ', ' . project() . '/css/editor.css?m=' . filemtime(Director::baseFolder() . '/' . project() . "/css/editor.css");
 182             }
 183             $htmlEditorConfig->setOption('content_css', $cssFiles);
 184         }
 185         
 186 
 187         Requirements::css(CMS_DIR . '/css/typography.css');
 188         Requirements::css(CMS_DIR . '/css/layout.css');
 189         Requirements::css(CMS_DIR . '/css/cms_left.css');
 190         Requirements::css(CMS_DIR . '/css/cms_right.css');
 191         Requirements::css(SAPPHIRE_DIR . '/css/Form.css');
 192         
 193         if(isset($_REQUEST['debug_firebug'])) {
 194             // Firebug is a useful console for debugging javascript
 195             // Its available as a Firefox extension or a javascript library
 196             // for easy inclusion in other browsers (just append ?debug_firebug=1 to the URL)
 197             Requirements::javascript(THIRDPARTY_DIR . '/firebug-lite/firebug.js');
 198         } else {
 199             // By default, we include fake-objects for all firebug calls
 200             // to avoid javascript errors when referencing console.log() etc in javascript code
 201             Requirements::javascript(THIRDPARTY_DIR . '/firebug-lite/firebugx.js');
 202         }
 203         
 204         Requirements::javascript(SAPPHIRE_DIR . '/thirdparty/prototype/prototype.js');
 205         Requirements::javascript(THIRDPARTY_DIR . '/jquery/jquery.js');
 206         Requirements::javascript(SAPPHIRE_DIR . '/javascript/jquery_improvements.js');
 207         Requirements::javascript(THIRDPARTY_DIR . '/behaviour/behaviour.js');
 208         Requirements::javascript(THIRDPARTY_DIR . '/jquery-livequery/jquery.livequery.js');
 209         Requirements::javascript(SAPPHIRE_DIR . '/javascript/core/jquery.ondemand.js');
 210         Requirements::javascript(SAPPHIRE_DIR . '/javascript/prototype_improvements.js');
 211         Requirements::javascript(SAPPHIRE_DIR . '/javascript/loader.js');
 212         Requirements::javascript(CMS_DIR . '/javascript/hover.js');
 213         Requirements::javascript(SAPPHIRE_DIR . '/javascript/layout_helpers.js');
 214         Requirements::add_i18n_javascript(SAPPHIRE_DIR . '/javascript/lang');
 215         Requirements::add_i18n_javascript(CMS_DIR . '/javascript/lang');
 216         
 217         Requirements::javascript(THIRDPARTY_DIR . '/scriptaculous/effects.js');
 218         Requirements::javascript(THIRDPARTY_DIR . '/scriptaculous/dragdrop.js');
 219         Requirements::javascript(THIRDPARTY_DIR . '/scriptaculous/controls.js');
 220 
 221         Requirements::css(THIRDPARTY_DIR . '/greybox/greybox.css');
 222         Requirements::javascript(THIRDPARTY_DIR . '/greybox/AmiJS.js');
 223         Requirements::javascript(THIRDPARTY_DIR . '/greybox/greybox.js');
 224         
 225         Requirements::javascript(SAPPHIRE_DIR . '/javascript/tree/tree.js');
 226         Requirements::css(THIRDPARTY_DIR . '/tree/tree.css');
 227         
 228         Requirements::javascript(CMS_DIR . '/javascript/LeftAndMain.js');
 229         Requirements::javascript(CMS_DIR . '/javascript/LeftAndMain_left.js');
 230         Requirements::javascript(CMS_DIR . '/javascript/LeftAndMain_right.js');
 231     
 232         Requirements::javascript(CMS_DIR . '/javascript/SideTabs.js');
 233         Requirements::javascript(CMS_DIR . '/javascript/SideReports.js');
 234         Requirements::javascript(CMS_DIR . '/javascript/LangSelector.js');
 235         Requirements::javascript(CMS_DIR . '/javascript/TranslationTab.js');
 236         
 237         // navigator
 238         Requirements::css(SAPPHIRE_DIR . '/css/SilverStripeNavigator.css');
 239         Requirements::javascript(SAPPHIRE_DIR . '/javascript/SilverStripeNavigator.js');
 240 
 241         //Requirements::themedCSS('typography');
 242 
 243         foreach (self::$extra_requirements['javascript'] as $file) {
 244             Requirements::javascript($file[0]);
 245         }
 246         
 247         foreach (self::$extra_requirements['css'] as $file) {
 248             Requirements::css($file[0], $file[1]);
 249         }
 250         
 251         foreach (self::$extra_requirements['themedcss'] as $file) {
 252             Requirements::themedCSS($file[0], $file[1]);
 253         }
 254         
 255         Requirements::customScript('Behaviour.addLoader(hideLoading);');
 256 
 257         // Javascript combined files
 258         Requirements::combine_files(
 259             'assets/_tmp/base.js',
 260             array(
 261                 THIRDPARTY_DIR . '/prototype/prototype.js',
 262                 THIRDPARTY_DIR . '/behaviour/behaviour.js',
 263                 SAPPHIRE_DIR . '/javascript/prototype_improvements.js',
 264                 THIRDPARTY_DIR .'/jquery/jquery.js',
 265                 THIRDPARTY_DIR .'/jquery-livequery/jquery.livequery.js',
 266                 THIRDPARTY_DIR . '/jquery-effen/jquery.fn.js',
 267                 SAPPHIRE_DIR . '/javascript/core/jquery.ondemand.js',
 268                 SAPPHIRE_DIR . '/javascript/jquery_improvements.js',
 269                 THIRDPARTY_DIR . '/firebug-lite/firebugx.js',
 270                 SAPPHIRE_DIR . '/javascript/i18n.js',
 271             )
 272         );
 273 
 274         Requirements::combine_files(
 275             'assets/_tmp/leftandmain.js',
 276             array(
 277                 SAPPHIRE_DIR . '/javascript/loader.js',
 278                 CMS_DIR . '/javascript/hover.js',
 279                 SAPPHIRE_DIR . '/javascript/layout_helpers.js',
 280                 THIRDPARTY_DIR . '/scriptaculous/effects.js',
 281                 THIRDPARTY_DIR . '/scriptaculous/dragdrop.js',
 282                 THIRDPARTY_DIR . '/scriptaculous/controls.js',
 283                 THIRDPARTY_DIR . '/greybox/AmiJS.js',
 284                 THIRDPARTY_DIR . '/greybox/greybox.js',
 285                 CMS_DIR . '/javascript/LeftAndMain.js',
 286                 CMS_DIR . '/javascript/LeftAndMain_left.js',
 287                 CMS_DIR . '/javascript/LeftAndMain_right.js',
 288                 SAPPHIRE_DIR . '/javascript/tree/tree.js',
 289                 THIRDPARTY_DIR . '/tabstrip/tabstrip.js',
 290                 SAPPHIRE_DIR . '/javascript/TreeSelectorField.js',
 291                 CMS_DIR . '/javascript/ThumbnailStripField.js',
 292             )
 293         );
 294 
 295         Requirements::combine_files(
 296             'assets/_tmp/cmsmain.js',
 297             array(
 298                 CMS_DIR . '/javascript/CMSMain.js',
 299                 CMS_DIR . '/javascript/CMSMain_left.js',
 300                 CMS_DIR . '/javascript/CMSMain_right.js',
 301                 CMS_DIR . '/javascript/SideTabs.js',
 302                 CMS_DIR . '/javascript/SideReports.js',
 303                 CMS_DIR . '/javascript/LangSelector.js',
 304                 CMS_DIR . '/javascript/TranslationTab.js',
 305                 THIRDPARTY_DIR . '/calendar/calendar.js',
 306                 THIRDPARTY_DIR . '/calendar/lang/calendar-'.substr(i18n::get_locale(),0,2).'.js',
 307                 THIRDPARTY_DIR . '/calendar/calendar-setup.js',
 308             )
 309         );
 310 
 311         $dummy = null;
 312         $this->extend('init', $dummy);
 313         
 314         // выключаем html5-формы в админке
 315         if (property_exists('FormField', 'use_html5')) {
 316             FormField::allow_html5(false); // не использовуем html5 в админке
 317         }
 318         //FormField::allow_html5(true);
 319 
 320         // The user's theme shouldn't affect the CMS, if, for example, they have replaced
 321         // TableListField.ss or Form.ss.
 322         SSViewer::set_theme(null);
 323     }
 324 
 325     
 326     /**
 327      * If this is set to true, the "switchView" context in the
 328      * template is shown, with links to the staging and publish site.
 329      *
 330      * @return boolean
 331      */
 332     function ShowSwitchView() {
 333         return false;
 334     }
 335 
 336     //------------------------------------------------------------------------------------------//
 337     // Main controllers
 338 
 339     /**
 340      * You should implement a Link() function in your subclass of LeftAndMain,
 341      * to point to the URL of that particular controller.
 342      * 
 343      * @return string
 344      */
 345     public function Link($action = null) {
 346         // Handle missing url_segments
 347         if(!$this->stat('url_segment', true))
 348             self::$url_segment = $this->class;
 349         return Controller::join_links(
 350             $this->stat('url_base', true),
 351             $this->stat('url_segment', true),
 352             '/', // trailing slash needed if $action is null!
 353             "$action"
 354         );
 355     }
 356     
 357     
 358     /**
 359     * Returns the menu title for the given LeftAndMain subclass.
 360     * Implemented static so that we can get this value without instantiating an object.
 361     * Menu title is *not* internationalised.
 362     */
 363     static function menu_title_for_class($class) {
 364         $title = eval("return $class::\$menu_title;");
 365         if(!$title) $title = preg_replace('/Admin$/', '', $class);
 366         return $title;
 367     }
 368 
 369     public function show() {
 370         $params = $this->getURLParams();
 371         if($params['ID']) $this->setCurrentPageID($params['ID']);
 372         if(isset($params['OtherID'])) {
 373             Session::set('currentMember', $params['OtherID']);
 374         }
 375 
 376         if(Director::is_ajax()) {
 377             SSViewer::setOption('rewriteHashlinks', false);
 378             return $this->EditForm()->formHtmlContent();
 379 
 380         } else {
 381             return array();
 382         }
 383     }
 384 
 385 
 386     public function getitem() {
 387         $this->setCurrentPageID($_REQUEST['ID']);
 388         SSViewer::setOption('rewriteHashlinks', false);
 389 
 390         if(isset($_REQUEST['ID']) && is_numeric($_REQUEST['ID'])) {
 391             $record = DataObject::get_by_id($this->stat('tree_class'), $_REQUEST['ID']);
 392             if($record && !$record->canView()) return Security::permissionFailure($this);
 393         }
 394 
 395         $form = $this->EditForm();
 396         if ($form) {
 397             $content =  $form->formHtmlContent();
 398             if($this->ShowSwitchView()) {
 399                 $content .= '<div id="AjaxSwitchView">' . $this->SwitchView() . '</div>';
 400             }
 401             
 402             return $content;
 403         }
 404         else return "";
 405     }
 406     public function getLastFormIn($html) {
 407         $parts = split('</?form[^>]*>', $html);
 408         return $parts[sizeof($parts)-2];
 409     }
 410 
 411     //------------------------------------------------------------------------------------------//
 412     // Main UI components
 413 
 414     /**
 415      * Returns the main menu of the CMS.  This is also used by init() to work out which sections the user
 416      * has access to.
 417      * 
 418      * @return DataObjectSet
 419      */
 420     public function MainMenu() {
 421         // Don't accidentally return a menu if you're not logged in - it's used to determine access.
 422         if(!Member::currentUser()) return new DataObjectSet();
 423 
 424         // Encode into DO set
 425         $menu = new DataObjectSet();
 426         $menuItems = CMSMenu::get_viewable_menu_items();
 427         if($menuItems) foreach($menuItems as $code => $menuItem) {
 428             // alternate permission checks (in addition to LeftAndMain->canView())
 429             if(
 430                 isset($menuItem->controller) 
 431                 && $this->hasMethod('alternateMenuDisplayCheck')
 432                 && !$this->alternateMenuDisplayCheck($menuItem->controller)
 433             ) {
 434                 continue;
 435             }
 436 
 437             $linkingmode = "";
 438             
 439             if(strpos($this->Link(), $menuItem->url) !== false) {
 440                 if($this->Link() == $menuItem->url) {
 441                     $linkingmode = "current";
 442                 
 443                 // default menu is the one with a blank {@link url_segment}
 444                 } else if(singleton($menuItem->controller)->stat('url_segment') == '') {
 445                     if($this->Link() == $this->stat('url_base').'/') $linkingmode = "current";
 446 
 447                 } else {
 448                     $linkingmode = "current";
 449                 }
 450             }
 451         
 452             // already set in CMSMenu::populate_menu(), but from a static pre-controller
 453             // context, so doesn't respect the current user locale in _t() calls - as a workaround,
 454             // we simply call LeftAndMain::menu_title_for_class() again if we're dealing with a controller
 455             if($menuItem->controller) {
 456                 $defaultTitle = LeftAndMain::menu_title_for_class($menuItem->controller);
 457                 $title = _t("{$menuItem->controller}.MENUTITLE", $defaultTitle);
 458             } else {
 459                 $title = $menuItem->title;
 460             }
 461             
 462             $menu->push(new ArrayData(array(
 463                 "MenuItem" => $menuItem,
 464                 "Title" => Convert::raw2xml($title),
 465                 "Code" => $code,
 466                 "Link" => $menuItem->url,
 467                 "LinkingMode" => $linkingmode
 468             )));
 469         }
 470         
 471         // if no current item is found, assume that first item is shown
 472         //if(!isset($foundCurrent)) 
 473         return $menu;
 474     }
 475 
 476 
 477     public function CMSTopMenu() {
 478         return $this->renderWith(array('CMSTopMenu_alternative','CMSTopMenu'));
 479     }
 480 
 481   /**
 482    * Return a list of appropriate templates for this class, with the given suffix
 483    */
 484   protected function getTemplatesWithSuffix($suffix) {
 485     $classes = array_reverse(ClassInfo::ancestry($this->class));
 486     foreach($classes as $class) {
 487       $templates[] = $class . $suffix;
 488       if($class == 'LeftAndMain') break;
 489     }
 490     return $templates;
 491   }
 492 
 493     public function Left() {
 494         return $this->renderWith($this->getTemplatesWithSuffix('_left'));
 495     }
 496 
 497     public function Right() {
 498         return $this->renderWith($this->getTemplatesWithSuffix('_right'));
 499     }
 500 
 501     public function getRecord($id, $className = null) {
 502         if($id && is_numeric($id)) {
 503             if(!$className) $className = $this->stat('tree_class');
 504             return DataObject::get_by_id($className, $id);
 505         }
 506     }
 507 
 508     /**
 509      * Get a site tree displaying the nodes under the given objects
 510      * @param $className The class of the root object
 511      * @param $rootID The ID of the root object.  If this is null then a complete tree will be
 512      *                shown
 513      * @param $childrenMethod The method to call to get the children of the tree.  For example,
 514      *                        Children, AllChildrenIncludingDeleted, or AllHistoricalChildren
 515      */
 516     function getSiteTreeFor($className, $rootID = null, $childrenMethod = null, $numChildrenMethod = null, $filterFunction = null, $minNodeCount = 30) {
 517         // Default childrenMethod and numChildrenMethod
 518         if (!$childrenMethod) $childrenMethod = 'AllChildrenIncludingDeleted';
 519         if (!$numChildrenMethod) $numChildrenMethod = 'numChildren';
 520         
 521         // Get the tree root
 522         $obj = $rootID ? $this->getRecord($rootID) : singleton($className);
 523         
 524         // Mark the nodes of the tree to return
 525         if ($filterFunction) $obj->setMarkingFilterFunction($filterFunction);
 526 
 527         $obj->markPartialTree($minNodeCount, $this, $childrenMethod, $numChildrenMethod);
 528         
 529         // Ensure current page is exposed
 530         if($p = $this->currentPage()) $obj->markToExpose($p);
 531         
 532         // NOTE: SiteTree/CMSMain coupling :-(
 533         SiteTree::prepopuplate_permission_cache('CanEditType', $obj->markedNodeIDs(), 'SiteTree::can_edit_multiple');
 534 
 535         // getChildrenAsUL is a flexible and complex way of traversing the tree
 536         $titleEval = '
 537                     "<li id=\"record-$child->ID\" class=\"" . $child->CMSTreeClasses($extraArg) . "\">" .
 538                     "<a href=\"" . Controller::join_links(substr($extraArg->Link(),0,-1), "show", $child->ID) . "\" class=\"" . $child->CMSTreeClasses($extraArg) . "\" title=\"' . _t('LeftAndMain.PAGETYPE','Page type: ') . '".$child->class."\" >" . 
 539                     ($child->TreeTitle()) . 
 540                     "</a>"
 541 ';
 542         $siteTree = $obj->getChildrenAsUL(
 543             "", 
 544             $titleEval,
 545             $this, 
 546             true, 
 547             $childrenMethod,
 548             $numChildrenMethod,
 549             $minNodeCount
 550         );
 551 
 552         // Wrap the root if needs be.
 553 
 554         if(!$rootID) {
 555             $rootLink = $this->Link('show') . '/root';
 556             
 557             // This lets us override the tree title with an extension
 558             if($this->hasMethod('getCMSTreeTitle') && $customTreeTitle = $this->getCMSTreeTitle()) {
 559                 $treeTitle = $customTreeTitle;
 560             } else {
 561                 $siteConfig = SiteConfig::current_site_config();
 562                 $treeTitle =  $siteConfig->Title;
 563             }
 564             
 565             $siteTree = "<ul id=\"sitetree\" class=\"tree unformatted\"><li id=\"record-0\" class=\"Root nodelete\"><a href=\"$rootLink\"><strong>$treeTitle</strong></a>"
 566                 . $siteTree . "</li></ul>";
 567         }
 568 
 569         return $siteTree;
 570     }
 571 
 572     /**
 573      * Get a subtree underneath the request param 'ID'.
 574      * If ID = 0, then get the whole tree.
 575      */
 576     public function getsubtree($request) {
 577         // Get the tree
 578         $minNodeCount = (is_numeric($request->getVar('minNodeCount'))) ? $request->getVar('minNodeCount') : NULL;
 579         $tree = $this->getSiteTreeFor(
 580             $this->stat('tree_class'), 
 581             $request->getVar('ID'), 
 582             null, 
 583             null, 
 584             null,
 585             $minNodeCount
 586         );
 587 
 588         // Trim off the outer tag
 589         $tree = ereg_replace('^[ \t\r\n]*<ul[^>]*>','', $tree);
 590         $tree = ereg_replace('</ul[^>]*>[ \t\r\n]*$','', $tree);
 591         
 592         return $tree;
 593     }
 594 
 595     /**
 596      * Allows you to returns a new data object to the tree (subclass of sitetree)
 597      * and updates the tree via javascript.
 598      */
 599     public function returnItemToUser($p) {
 600         if(Director::is_ajax()) {
 601             // Prepare the object for insertion.
 602             $parentID = (int) $p->ParentID;
 603             $id = $p->ID ? $p->ID : "new-$p->class-$p->ParentID";
 604             $treeTitle = Convert::raw2js($p->TreeTitle());
 605             $hasChildren = (is_numeric($id) && $p->AllChildren() && $p->AllChildren()->Count()) ? ' unexpanded' : '';
 606 
 607             // Ensure there is definitly a node avaliable. if not, append to the home tree.
 608             $response = <<<JS
 609                 var tree = $('sitetree');
 610                 var newNode = tree.createTreeNode("$id", "$treeTitle", "{$p->class}{$hasChildren}");
 611                 node = tree.getTreeNodeByIdx($parentID);
 612                 if(!node) {
 613                     node = tree.getTreeNodeByIdx(0);
 614                 }
 615                 node.open();
 616                 node.appendTreeNode(newNode);
 617                 newNode.selectTreeNode();
 618 JS;
 619             FormResponse::add($response);
 620 
 621             return FormResponse::respond();
 622         } else {
 623             Director::redirect('admin/' . self::$url_segment . '/show/' . $p->ID);
 624         }
 625     }
 626 
 627     /**
 628      * Save and Publish page handler
 629      */
 630     public function save($urlParams, $form) {
 631         $className = $this->stat('tree_class');
 632 
 633         $SQL_id = Convert::raw2sql($_REQUEST['ID']);
 634         if(substr($SQL_id,0,3) != 'new') {
 635             $record = DataObject::get_one($className, "\"$className\".\"ID\" = {$SQL_id}");
 636             if($record && !$record->canEdit()) return Security::permissionFailure($this);
 637         } else {
 638             if(!singleton($this->stat('tree_class'))->canCreate()) return Security::permissionFailure($this);
 639             $record = $this->getNewItem($SQL_id, false);
 640         }
 641 
 642         // We don't want to save a new version if there are no changes
 643         $dataFields_new = $form->Fields()->dataFields();
 644         $dataFields_old = $record->getAllFields();
 645         $changed = false;
 646         $hasNonRecordFields = false;
 647         foreach($dataFields_new as $datafield) {
 648             // if the form has fields not belonging to the record
 649             if(!isset($dataFields_old[$datafield->Name()])) {
 650                 $hasNonRecordFields = true;
 651             }
 652             // if field-values have changed
 653             if(!isset($dataFields_old[$datafield->Name()]) || $dataFields_old[$datafield->Name()] != $datafield->dataValue()) {
 654                 $changed = true;
 655             }
 656         }
 657 
 658         if(!$changed && !$hasNonRecordFields) {
 659             // Tell the user we have saved even though we haven't, as not to confuse them
 660             if(is_a($record, "Page")) {
 661                 $record->Status = "Saved (update)";
 662             }
 663             FormResponse::status_message(_t('LeftAndMain.SAVEDUP',"Saved"), "good");
 664             FormResponse::update_status($record->Status);
 665             return FormResponse::respond();
 666         }
 667 
 668         $form->dataFieldByName('ID')->Value = 0;
 669 
 670         if(isset($urlParams['Sort']) && is_numeric($urlParams['Sort'])) {
 671             $record->Sort = $urlParams['Sort'];
 672         }
 673 
 674         // HACK: This should be turned into something more general
 675         $originalClass = $record->ClassName;
 676         $originalStatus = $record->Status;
 677         $originalParentID = $record->ParentID;
 678 
 679         $originalBrokenLinkValues = $record->HasBrokenLink.$record->HasBrokenFile;
 680 
 681         $record->HasBrokenLink = 0;
 682         $record->HasBrokenFile = 0;
 683 
 684         $record->writeWithoutVersion();
 685 
 686         // HACK: This should be turned into something more general
 687         $originalURLSegment = $record->URLSegment;
 688 
 689         $form->saveInto($record, true);
 690 
 691         if(is_a($record, "Page")) {
 692             $record->Status = ($record->Status == "New page" || $record->Status == "Saved (new)") ? "Saved (new)" : "Saved (update)";
 693         }
 694 
 695         if(Director::is_ajax()) {
 696             if($SQL_id != $record->ID) {
 697                 FormResponse::add("$('sitetree').setNodeIdx(\"{$SQL_id}\", \"$record->ID\");");
 698                 FormResponse::add("$('Form_EditForm').elements.ID.value = \"$record->ID\";");
 699             }
 700 
 701             if($added = DataObjectLog::getAdded('SiteTree')) {
 702                 foreach($added as $page) {
 703                     if($page->ID != $record->ID) FormResponse::add($this->addTreeNodeJS($page));
 704                 }
 705             }
 706             if($deleted = DataObjectLog::getDeleted('SiteTree')) {
 707                 foreach($deleted as $page) {
 708                     if($page->ID != $record->ID) FormResponse::add($this->deleteTreeNodeJS($page));
 709                 }
 710             }
 711             if($changed = DataObjectLog::getChanged('SiteTree')) {
 712                 foreach($changed as $page) {
 713                     if($page->ID != $record->ID) {
 714                         $title = Convert::raw2js($page->TreeTitle());
 715                         FormResponse::add("$('sitetree').setNodeTitle($page->ID, \"$title\");");
 716                     }
 717                 }
 718             }
 719 
 720             $message = _t('LeftAndMain.SAVEDUP');
 721 
 722             // Update the class instance if necessary
 723             if($originalClass != $record->ClassName) {
 724                 $newClassName = $record->ClassName;
 725                 // The records originally saved attribute was overwritten by $form->saveInto($record) before.
 726                 // This is necessary for newClassInstance() to work as expected, and trigger change detection
 727                 // on the ClassName attribute
 728                 $record->setClassName($originalClass);
 729                 // Replace $record with a new instance
 730                 $record = $record->newClassInstance($newClassName);
 731                 
 732                 // update the tree icon
 733                 FormResponse::add("if(\$('sitetree').setNodeIcon) \$('sitetree').setNodeIcon($record->ID, '$originalClass', '$record->ClassName');");
 734             }
 735 
 736             // HACK: This should be turned into somethign more general
 737             // Removed virtualpage test as we need to draft/published links when url is changed
 738             if( (/*$record->class == 'VirtualPage' &&*/ $originalURLSegment != $record->URLSegment) ||
 739                 ($originalClass != $record->ClassName) || self::$ForceReload == true) {
 740                 // avoid double loading by adding a uniqueness ID
 741                 FormResponse::add($str = "$('Form_EditForm').getPageFromServer($record->ID);", $str);
 742             }
 743 
 744             // After reloading action
 745 //          if($originalStatus != $record->Status) {
 746 //              $message .= sprintf(_t('LeftAndMain.STATUSTO',"  Status changed to '%s'"),$record->Status);
 747 //          }
 748             
 749             if($originalParentID != $record->ParentID) {
 750                 FormResponse::add("if(\$('sitetree').setNodeParentID) \$('sitetree').setNodeParentID($record->ID, $record->ParentID);");
 751             }
 752 
 753             
 754 
 755             $record->write();
 756 
 757             if( ($record->class != 'VirtualPage') && $originalURLSegment != $record->URLSegment) {
 758                 $message .= sprintf(_t('LeftAndMain.CHANGEDURL',"  Changed URL to '%s'"),$record->URLSegment);
 759                 FormResponse::add("\$('Form_EditForm').elements.URLSegment.value = \"$record->URLSegment\";");
 760                 FormResponse::add("\$('Form_EditForm_StageURLSegment').value = \"" . $record->AbsoluteLink() . "\";");
 761             }
 762             
 763             if($virtualPages = DataObject::get("VirtualPage", "\"CopyContentFromID\" = $record->ID")) {
 764                 foreach($virtualPages as $page) {
 765                     if($page->ID != $record->ID) {
 766                         $title = Convert::raw2js($page->TreeTitle());
 767                         FormResponse::add("$('sitetree').setNodeTitle($page->ID, \"$title\");");
 768                     }
 769                 }
 770             }
 771             
 772             // If there has been a change in the broken link values, reload the page
 773             if ($originalBrokenLinkValues != $record->HasBrokenLink.$record->HasBrokenFile) {               
 774                 // avoid double loading by adding a uniqueness ID
 775                 FormResponse::add($str = "$('Form_EditForm').getPageFromServer($record->ID);", $str);
 776             }
 777 
 778             // If the 'Save & Publish' button was clicked, also publish the page
 779             if (isset($urlParams['publish']) && $urlParams['publish'] == 1) {
 780                 $this->extend('onAfterSave', $record);
 781             
 782                 $record->doPublish();
 783                 
 784                 // Update classname with original and get new instance (see above for explanation)
 785                 $record->setClassName($originalClass);
 786                 $publishedRecord = $record->newClassInstance($record->ClassName);
 787 
 788                 return $this->tellBrowserAboutPublicationChange(
 789                     $publishedRecord, 
 790                     sprintf(
 791                         _t(
 792                             'LeftAndMain.STATUSPUBLISHEDSUCCESS', 
 793                             "Published '%s' successfully",
 794                             PR_MEDIUM,
 795                             'Status message after publishing a page, showing the page title'
 796                         ),
 797                         $record->Title
 798                     )
 799                 );
 800             } else {
 801                 // BUGFIX: Changed icon only shows after Save button is clicked twice http://support.silverstripe.com/gsoc/ticket/76
 802                 $title = Convert::raw2js($record->TreeTitle());
 803                 FormResponse::add("$('sitetree').setNodeTitle(\"$record->ID\", \"$title\");");
 804                 FormResponse::add($this->getActionUpdateJS($record));
 805                 FormResponse::status_message($message, "good");
 806                 FormResponse::update_status($record->Status);
 807 
 808                 $this->extend('onAfterSave', $record);
 809 
 810                 return FormResponse::respond();
 811             }
 812         }
 813     }
 814 
 815     /**
 816      * Returns a javascript snippet that will update the actions of the main form
 817      * 
 818      * @return string
 819      */
 820     public function getActionUpdateJS($record) {
 821         // Get the new action buttons
 822 
 823         $tempForm = $this->getEditForm($record->ID);
 824         $actionList = '';
 825         foreach($tempForm->Actions() as $action) {
 826             $actionList .= $action->Field() . ' ';
 827         }
 828 
 829         return "$('Form_EditForm').loadActionsFromString('" . Convert::raw2js($actionList) . "');";
 830     }
 831 
 832     /**
 833      * Returns a javascript snippet to generate a tree node for the given page, if visible
 834      *
 835      * @return string
 836      */
 837     public function addTreeNodeJS($page, $select = false) {
 838         $parentID = (int)$page->ParentID;
 839         $title = Convert::raw2js($page->TreeTitle());
 840         $response = <<<JS
 841 var newNode = $('sitetree').createTreeNode($page->ID, "$title", "$page->class");
 842 var parentNode = $('sitetree').getTreeNodeByIdx($parentID); 
 843 if(parentNode) parentNode.appendTreeNode(newNode);
 844 JS;
 845         $response .= ($select ? "newNode.selectTreeNode();\n" : "") ;
 846         return $response;
 847     }
 848     /**
 849      * Returns a javascript snippet to remove a tree node for the given page, if it exists.
 850      *
 851      * @return string
 852      */
 853     public function deleteTreeNodeJS($page) {
 854         $id = $page->ID ? $page->ID : $page->OldID;
 855         $response = <<<JS
 856 var node = $('sitetree').getTreeNodeByIdx($id);
 857 if(node && node.parentTreeNode) node.parentTreeNode.removeTreeNode(node);
 858 $('Form_EditForm').closeIfSetTo($id);
 859 JS;
 860 
 861         // If we have that page selected currently, then clear that info from the session
 862         if(Session::get("{$this->class}.currentPage") == $id) {
 863             $this->setCurrentPageID(null);
 864         }
 865         
 866         return $response;
 867     }
 868 
 869     /**
 870      * Sets a static variable on this class which means the panel will be reloaded.
 871      */
 872     static function ForceReload(){
 873         self::$ForceReload = true;
 874     }
 875 
 876     /**
 877      * Ajax handler for updating the parent of a tree node
 878      */
 879     public function ajaxupdateparent() {
 880         $id = $_REQUEST['ID'];
 881         $parentID = $_REQUEST['ParentID'];
 882         if($parentID == 'root'){
 883             $parentID = 0;
 884         }
 885         $_REQUEST['ajax'] = 1;
 886         $cleanupJS = '';
 887         
 888         if (!Permission::check('SITETREE_REORGANISE') && !Permission::check('ADMIN')) {
 889             FormResponse::status_message(_t('LeftAndMain.CANT_REORGANISE',"You do not have permission to rearange the site tree. Your change was not saved."),"bad");
 890             return FormResponse::respond();
 891         }
 892 
 893         if(is_numeric($id) && is_numeric($parentID) && $id != $parentID) {
 894             $node = DataObject::get_by_id($this->stat('tree_class'), $id);
 895             if($node){
 896                 if(!$node->canEdit() || ($node->hasMethod('checkDevPermisson') && !$node->checkDevPermisson(null,'canReorder'))){ 
 897                     return Security::permissionFailure($this);
 898                 }
 899                 
 900                 $node->ParentID = $parentID;
 901                 $node->Status = "Saved (update)";
 902                 $node->write();
 903 
 904                 if(is_numeric($_REQUEST['CurrentlyOpenPageID'])) {
 905                     $currentPage = DataObject::get_by_id($this->stat('tree_class'), $_REQUEST['CurrentlyOpenPageID']);
 906                     if($currentPage) {
 907                         $cleanupJS = $currentPage->cmsCleanup_parentChanged();
 908                     }
 909                 }
 910 
 911                 FormResponse::status_message(_t('LeftAndMain.SAVED','saved'), 'good');
 912                 if($cleanupJS) FormResponse::add($cleanupJS);
 913 
 914             }else{
 915                 FormResponse::status_message(_t('LeftAndMain.PLEASESAVE',"Please Save Page: This page could not be upated because it hasn't been saved yet."),"good");
 916             }
 917 
 918 
 919             return FormResponse::respond();
 920         } else {
 921             user_error("Error in ajaxupdateparent request; id=$id, parentID=$parentID", E_USER_ERROR);
 922         }
 923     }
 924 
 925     /**
 926      * Ajax handler for updating the order of a number of tree nodes
 927      * $_GET[ID]: An array of node ids in the correct order
 928      * $_GET[MovedNodeID]: The node that actually got moved
 929      */
 930     public function ajaxupdatesort() {
 931         $className = $this->stat('tree_class');
 932         $counter = 0;
 933         $js = '';
 934         $_REQUEST['ajax'] = 1;
 935         
 936         if (!Permission::check('SITETREE_REORGANISE') && !Permission::check('ADMIN')) {
 937             FormResponse::status_message(_t('LeftAndMain.CANT_REORGANISE',"You do not have permission to rearange the site tree. Your change was not saved."),"bad");
 938             return FormResponse::respond();
 939         }
 940 
 941         if(is_array($_REQUEST['ID'])) {
 942             if($_REQUEST['MovedNodeID']==0){ //Sorting root
 943                 $movedNode = DataObject::get($className, "\"ParentID\"=0");             
 944             }else{
 945                 $movedNode = DataObject::get_by_id($className, $_REQUEST['MovedNodeID']);
 946             }
 947             foreach($_REQUEST['ID'] as $id) {
 948                 if($id == $movedNode->ID) {
 949                     $movedNode->Sort = ++$counter;
 950                     $movedNode->Status = "Saved (update)";
 951                     $movedNode->write();
 952 
 953                     $title = Convert::raw2js($movedNode->TreeTitle());
 954                     $js .="$('sitetree').setNodeTitle($movedNode->ID, \"$title\");\n";
 955 
 956                 // Nodes that weren't "actually moved" shouldn't be registered as having been edited; do a direct SQL update instead
 957                 } else if(is_numeric($id)) {
 958                     ++$counter;
 959                     DB::query("UPDATE \"$className\" SET \"Sort\" = $counter WHERE \"ID\" = '$id'");
 960                 }
 961             }
 962             FormResponse::status_message(_t('LeftAndMain.SAVED'), 'good');
 963         } else {
 964             FormResponse::error(_t('LeftAndMain.REQUESTERROR',"Error in request"));
 965         }
 966 
 967         return FormResponse::respond();
 968     }
 969     
 970     public function CanOrganiseSitetree() {
 971         return !Permission::check('SITETREE_REORGANISE') && !Permission::check('ADMIN') ? false : true;
 972     }
 973 
 974     /**
 975      * Delete a number of items
 976      */
 977     public function deleteitems() {
 978         $ids = split(' *, *', $_REQUEST['csvIDs']);
 979 
 980         $script = "st = \$('sitetree'); \n";
 981         foreach($ids as $id) {
 982             if(is_numeric($id)) {
 983                 $record = DataObject::get_by_id($this->stat('tree_class'), $id);
 984                 if($record && !$record->canDelete()) return Security::permissionFailure($this);
 985                 
 986                 DataObject::delete_by_id($this->stat('tree_class'), $id);
 987                 $script .= "node = st.getTreeNodeByIdx($id); if(node) node.parentTreeNode.removeTreeNode(node); $('Form_EditForm').closeIfSetTo($id); \n";
 988                 
 989             }
 990         }
 991 
 992         FormResponse::add($script);
 993 
 994         return FormResponse::respond();
 995     }
 996         
 997     /**
 998      * Returns a placeholder form, used by {@link getEditForm()} if no record is selected.
 999      * Our javascript logic always requires a form to be present in the CMS interface.
1000      * 
1001      * @return Form
1002      */
1003     function EmptyForm() {
1004         $form = new Form(
1005             $this, 
1006             "EditForm", 
1007             new FieldSet(
1008                 new HeaderField(
1009                     'WelcomeHeader',
1010                     $this->getApplicationName()
1011                 ),
1012                 new LiteralField(
1013                     'WelcomeText',
1014                     sprintf('<p id="WelcomeMessage">%s %s. %s</p>',
1015                         _t('LeftAndMain_right.ss.WELCOMETO','Welcome to'),
1016                         $this->getApplicationName(),
1017                         _t('CHOOSEPAGE','Please choose an item from the left.')
1018                     )
1019                 )
1020             ), 
1021             new FieldSet()
1022         );
1023         $form->unsetValidator();
1024         
1025         return $form;
1026     }
1027 
1028     public function EditForm() {
1029         // Include JavaScript to ensure HtmlEditorField works.
1030         HtmlEditorField::include_js();
1031         
1032         if ($this->currentPageID() != 0) {
1033             $record = $this->currentPage();
1034             if(!$record) return false;
1035             if($record && !$record->canView()) return Security::permissionFailure($this);
1036         }
1037         if ($this->hasMethod('getEditForm')) {
1038             return $this->getEditForm($this->currentPageID());
1039         }
1040         
1041         return false;
1042     }
1043     
1044     public function myprofile() {
1045         $form = $this->Member_ProfileForm();
1046         return $this->customise(array(
1047             'Form' => $form
1048         ))->renderWith('BlankPage');
1049     }
1050     
1051     public function Member_ProfileForm() {
1052         return new Member_ProfileForm($this, 'Member_ProfileForm', Member::currentUser());
1053     }
1054 
1055     public function printable() {
1056         $id = $_REQUEST['ID'] ? $_REQUEST['ID'] : $this->currentPageID();
1057 
1058         if($id) $form = $this->getEditForm($id);
1059         $form->transform(new PrintableTransformation());
1060         $form->actions = null;
1061 
1062         Requirements::clear();
1063         Requirements::css(CMS_DIR . '/css/LeftAndMain_printable.css');
1064         return array(
1065             "PrintForm" => $form
1066         );
1067     }
1068 
1069     public function currentPageID() {
1070         if(isset($_REQUEST['ID']) && is_numeric($_REQUEST['ID']))   {
1071             return $_REQUEST['ID'];
1072         } elseif (isset($this->urlParams['ID']) && is_numeric($this->urlParams['ID'])) {
1073             return $this->urlParams['ID'];
1074         } elseif(Session::get("{$this->class}.currentPage")) {
1075             return Session::get("{$this->class}.currentPage");
1076         } else {
1077             return null;
1078         }
1079     }
1080 
1081     public function setCurrentPageID($id) {
1082         Session::set("{$this->class}.currentPage", $id);
1083     }
1084 
1085     public function currentPage() {
1086         return $this->getRecord($this->currentPageID());
1087     }
1088 
1089     public function isCurrentPage(DataObject $page) {
1090         return $page->ID == Session::get("{$this->class}.currentPage");
1091     }
1092     
1093     /**
1094      * Get the staus of a certain page and version.
1095      *
1096      * This function is used for concurrent editing, and providing alerts
1097      * when multiple users are editing a single page. It echoes a json
1098      * encoded string to the UA.
1099      */
1100 
1101     /**
1102      * Return the CMS's HTML-editor toolbar
1103      */
1104     public function EditorToolbar() {
1105         return Object::create('HtmlEditorField_Toolbar', $this, "EditorToolbar");
1106     }
1107 
1108     /**
1109      * Return the version number of this application.
1110      * Uses the subversion path information in <mymodule>/silverstripe_version
1111      * (automacially replaced $URL$ placeholder).
1112      * 
1113      * @return string
1114      */
1115     public function CMSVersion() {
1116         if (file_exists(BASE_PATH . '/webylon/.version')) 
1117             return file_get_contents(BASE_PATH . '/webylon/.version');
1118 
1119         $sapphireVersionFile = file_get_contents(BASE_PATH . '/sapphire/silverstripe_version');
1120         $cmsVersionFile = file_get_contents(BASE_PATH . '/cms/silverstripe_version');
1121         
1122         $sapphireVersion = $this->versionFromVersionFile($sapphireVersionFile);
1123         $cmsVersion = $this->versionFromVersionFile($cmsVersionFile);
1124 
1125         if($sapphireVersion == $cmsVersion) {
1126             return $sapphireVersion;
1127         }   else {
1128             return "cms: $cmsVersion, sapphire: $sapphireVersion";
1129         }
1130     }
1131     
1132     /**
1133      * Return the version from the content of a silverstripe_version file
1134      */
1135     public function versionFromVersionFile($fileContent) {
1136         if(preg_match('/\/trunk\/silverstripe_version/', $fileContent)) {
1137             return "trunk";
1138         } else {
1139             preg_match("/\/(?:branches|tags\/rc|tags\/beta|tags\/alpha|tags)\/([A-Za-z0-9._-]+)\/silverstripe_version/", $fileContent, $matches);
1140             return ($matches) ? $matches[1] : null;
1141         }
1142     }
1143     
1144     /**
1145      * @return array
1146      */
1147     function SwitchView() { 
1148         if($page = $this->currentPage()) { 
1149             $nav = SilverStripeNavigator::get_for_record($page); 
1150             return $nav['items']; 
1151         } 
1152     }
1153 
1154     /**
1155      * The application name. Customisable by calling
1156      * LeftAndMain::setApplicationName() - the first parameter.
1157      * 
1158      * @var String
1159      */
1160     static $application_name = 'SilverStripe CMS';
1161     
1162     /**
1163      * The application logo text. Customisable by calling
1164      * LeftAndMain::setApplicationName() - the second parameter.
1165      *
1166      * @var String
1167      */
1168     static $application_logo_text = 'SilverStripe';
1169 
1170     /**
1171      * Set the application name, and the logo text.
1172      *
1173      * @param String $name The application name
1174      * @param String $logoText The logo text
1175      */
1176     static $application_link = "http://www.silverstripe.org/";
1177     static function setApplicationName($name, $logoText = null, $link = null) {
1178         self::$application_name = $name;
1179         self::$application_logo_text = $logoText ? $logoText : $name;
1180         if($link) self::$application_link = $link;
1181     }
1182 
1183     /**
1184      * Get the application name.
1185      * @return String
1186      */
1187     function getApplicationName() {
1188         return self::$application_name;
1189     }
1190     
1191     /**
1192      * Get the application logo text.
1193      * @return String
1194      */
1195     function getApplicationLogoText() {
1196         return self::$application_logo_text;
1197     }
1198     function ApplicationLink() {
1199         return self::$application_link;
1200     }
1201 
1202     /**
1203      * Return the title of the current section, as shown on the main menu
1204      */
1205     function SectionTitle() {
1206         // Get menu - use obj() to cache it in the same place as the template engine
1207         $menu = $this->obj('MainMenu');
1208         
1209         foreach($menu as $menuItem) {
1210             if($menuItem->LinkingMode == 'current') return $menuItem->Title;
1211         }
1212     }
1213 
1214     /**
1215      * The application logo path. Customisable by calling
1216      * LeftAndMain::setLogo() - the first parameter.
1217      *
1218      * @var unknown_type
1219      */
1220     static $application_logo = 'cms/images/mainmenu/logo.gif';
1221 
1222     /**
1223      * The application logo style. Customisable by calling
1224      * LeftAndMain::setLogo() - the second parameter.
1225      *
1226      * @var String
1227      */
1228     static $application_logo_style = '';
1229     
1230     /**
1231      * Set the CMS application logo.
1232      *
1233      * @param String $logo Relative path to the logo
1234      * @param String $logoStyle Custom CSS styles for the logo
1235      *                          e.g. "border: 1px solid red; padding: 5px;"
1236      */
1237     static function setLogo($logo, $logoStyle) {
1238         self::$application_logo = $logo;
1239         self::$application_logo_style = $logoStyle;
1240         self::$application_logo_text = '';
1241     }
1242     
1243     protected static $loading_image = 'cms/images/loading.gif';
1244     
1245     /**
1246      * Set the image shown when the CMS is loading.
1247      */
1248     static function set_loading_image($loadingImage) {
1249         self::$loading_image = $loadingImage;
1250     }
1251     
1252     function LoadingImage() {
1253         return self::$loading_image;
1254     }
1255     
1256     function LogoStyle() {
1257         return "background: url(" . self::$application_logo . ") no-repeat; " . self::$application_logo_style;
1258     }
1259 
1260     /**
1261      * Return the base directory of the tiny_mce codebase
1262      */
1263     function MceRoot() {
1264         return MCE_ROOT;
1265     }
1266 
1267     /**
1268      * Use this as an action handler for custom CMS buttons.
1269      */
1270     function callPageMethod($data, $form) {
1271         $methodName = $form->buttonClicked()->extraData();
1272         $record = $this->currentPage();
1273         if(!$record) return false;
1274         
1275         return $record->$methodName($data, $form);
1276     }
1277     
1278     /**
1279      * Register the given javascript file as required in the CMS.
1280      * Filenames should be relative to the base, eg, SAPPHIRE_DIR . '/javascript/loader.js'
1281      */
1282     public static function require_javascript($file) {
1283         self::$extra_requirements['javascript'][] = array($file);
1284     }
1285     
1286     /**
1287      * Register the given stylesheet file as required.
1288      * 
1289      * @param $file String Filenames should be relative to the base, eg, THIRDPARTY_DIR . '/tree/tree.css'
1290      * @param $media String Comma-separated list of media-types (e.g. "screen,projector") 
1291      * @see http://www.w3.org/TR/REC-CSS2/media.html
1292      */
1293     public static function require_css($file, $media = null) {
1294         self::$extra_requirements['css'][] = array($file, $media);
1295     }
1296     
1297     /**
1298      * Register the given "themeable stylesheet" as required.
1299      * Themeable stylesheets have globally unique names, just like templates and PHP files.
1300      * Because of this, they can be replaced by similarly named CSS files in the theme directory.
1301      * 
1302      * @param $name String The identifier of the file.  For example, css/MyFile.css would have the identifier "MyFile"
1303      * @param $media String Comma-separated list of media-types (e.g. "screen,projector") 
1304      */
1305     static function require_themed_css($name, $media = null) {
1306         self::$extra_requirements['themedcss'][] = array($name, $media);
1307     }
1308 }
1309 
1310 
[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