Bootstrap.php #1

  • //
  • guest/
  • perforce_software/
  • chronicle/
  • main/
  • application/
  • Bootstrap.php
  • View
  • Commits
  • Open Download .zip Download (43 KB)
<?php
// define constants required by the application.
defined('BASE_PATH')
    or define('BASE_PATH',          realpath(dirname(__DIR__)));
defined('DATA_PATH')
    or define('DATA_PATH',          BASE_PATH . '/data');
defined('APPLICATION_PATH')
    or define('APPLICATION_PATH',   BASE_PATH . '/application');
defined('LIBRARY_PATH')
    or define('LIBRARY_PATH',       BASE_PATH . '/library');
defined('SITES_PATH')
    or define('SITES_PATH',         BASE_PATH . '/sites');

/**
 * Initialize the application and environment.
 * Note: the init functions are called in the order that they are defined.
 *
 * @copyright   2011 Perforce Software. All rights reserved.
 * @license     Please see LICENSE.txt in top-level folder of this distribution.
 * @version     <release>/<patch>
 */
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
    /**
     * Provide default application settings to ensure proper operation.
     *
     * @param   string  $environment    the application environment to get defaults for.
     * @return  array   the default application options.
     */
    public static function getDefaultOptions($environment = null)
    {
        $options = array(
            "phpSettings"   => array(
                "display_startup_errors"        => 0,
                "display_errors"                => 0,
                "error_reporting"               => (E_ALL & ~(E_STRICT|E_NOTICE))
            ),
            "resources"     => array(
                "log"               => array(
                    "file"      => DATA_PATH . '/log',
                    "priority"  => Zend_Log::ERR,
                    "format"    => "%timestamp% PID (%pid%) %priorityName% (%priority%): %message%\n"
                ),
                "frontController"   => array(
                    "env"                       => APPLICATION_ENV,
                    "moduleDirectory"           => APPLICATION_PATH,
                    "defaultModule"             => "content",
                    "prefixDefaultModule"       => true,
                    "actionHelperPaths"         => array(
                        "P4Cms_Controller_Action_Helper" =>
                            LIBRARY_PATH . "/P4Cms/Controller/Action/Helper"
                    )
                ),
                "layout"            => array(
                    "layout"                    => "default-layout"
                ),
                "view"              => array(
                    "encoding"                  => "UTF-8",
                    "useStreamWrapper"          => true,
                    "helperPath"                => array(
                        "Zend_Dojo_View_Helper"         =>
                            LIBRARY_PATH . "/Zend/Dojo/View/Helper",
                        "Zend_View_Helper_Navigation"   =>
                            LIBRARY_PATH . "/Zend/View/Helper/Navigation",
                        "P4Cms_View_Helper"             =>
                            LIBRARY_PATH . "/P4Cms/View/Helper"
                        )
                ),
                "cachemanager"      => array(
                    "enabled"       => true,
                    "default"       => array(
                        "frontend"  => array(
                            "name"      => "Core",
                            "options"   => array("automatic_serialization" => true)
                        ),
                        "backend"   => array(
                            "name"                  => "P4Cms_Cache_Backend_File",
                            "customBackendNaming"   => true,
                            "options"               => array("cache_dir" => DATA_PATH . '/cache/default')
                        ),
                    ),
                    "page"          => array(
                        "frontend"  => array(
                            "name"                  => "P4Cms_Cache_Frontend_Action",
                            "customFrontendNaming"  => true,
                            "options"               => array(
                                'default_options'   => array(
                                    'specific_lifetime'         => 43200    // 12h
                                ),
                                'actions'           => array(
                                    'content/list-widget/rss'   => array(),
                                    'content/index/index'       => array(),
                                    'content/index/view'        => array(),
                                    'content/index/image'       => array(
                                        'compress'              => false,
                                        'cache_with_username'   => true,
                                        'make_id_with_locale'   => false,
                                        'cache_with_session'    => true,
                                        'make_id_with_session'  => false,
                                        'cache_with_get'        => true
                                    ),
                                    'content/index/download'    => array(
                                        'compress'              => false,
                                        'cache_with_username'   => true,
                                        'make_id_with_locale'   => false,
                                        'cache_with_session'    => true,
                                        'make_id_with_session'  => false,
                                        'cache_with_get'        => true
                                    ),
                                    'widget/image-widget/image' => array(
                                        'compress'              => false,
                                        'cache_with_username'   => true,
                                        'make_id_with_rolename' => false,
                                        'make_id_with_locale'   => false,
                                        'cache_with_session'    => true,
                                        'make_id_with_session'  => false
                                    ),
                                    'content/type/icon'         => array(
                                        'compress'              => false,
                                        'cache_with_username'   => true,
                                        'make_id_with_locale'   => false,
                                        'cache_with_session'    => true,
                                        'make_id_with_session'  => false,
                                        'specific_lifetime'     => null     // forever
                                    )
                                )
                            )
                        ),
                        "backend"   => array(
                            "name"                  => "P4Cms_Cache_Backend_File",
                            "customBackendNaming"   => true,
                            "options"               => array("cache_dir" => DATA_PATH . '/cache/page')
                        ),
                    ),
                    "global"        => array(
                        "frontend"  => array(
                            "name"      => "Core",
                            "options"   => array("automatic_serialization" => true)
                        ),
                        "backend"   => array(
                            "name"                  => "P4Cms_Cache_Backend_File",
                            "customBackendNaming"   => true,
                            "options"               => array(
                                "cache_dir" => DATA_PATH . '/cache/global',
                                "namespace" => false
                            )
                        )
                    )
                ),
                'assethandler'      => array(
                    'class'             => 'P4Cms_AssetHandler_File',
                    'options'           => array(
                        'outputPath'    => DATA_PATH . '/resources'
                    )
                ),
                'session'           => array(
                    'save_path'         => DATA_PATH . '/sessions',
                    'gc_maxlifetime'    => 43200    // 12h
                )
            ),
            "performance"   => array(
                "aggregateCss"  => true,
                "aggregateJs"   => true,
                "autoBuildDojo" => true,
            ),
            "reportVersion"     => false
        );

        // tweak defaults for development environments
        //  - increase error/log verbosity
        //  - disable resource aggregation
        //  - disable caching
        if ($environment == "development" || $environment == "testing") {
            $options['phpSettings']['display_startup_errors']   = 1;
            $options['phpSettings']['display_errors']           = 1;
            $options['phpSettings']['error_reporting']          = (E_ALL & ~E_STRICT);
            $options['resources']['log']['priority']            = Zend_Log::DEBUG;
            $options['resources']['cachemanager']['enabled']    = false;
            $options['performance']['aggregateCss']             = false;
            $options['performance']['aggregateJs']              = false;
            $options['performance']['autoBuildDojo']            = false;
            $options["reportVersion"]                           = true;
        }

        return $options;
    }

    /**
     * Initialize PATH to include bundled p4 binaries.
     * Used primarily by _initPath() - made public static so it can used by tests.
     */
    public static function initPath()
    {
        $system  = php_uname('s');
        $release = php_uname('r');
        $machine = php_uname('m');

        // normalize uname info according to p4-bin conventions.
        $system = strtolower($system);
        $machine = preg_replace("/i.86/", "x86", $machine);

        // treat all versions of windows the same
        if (defined('PHP_WINDOWS_VERSION_MAJOR')) {
            $system = 'nt';
        }

        switch ($system) {
            case 'darwin':
                if (floatval($release) >= 9.0) {
                    $release = '90';
                }

                // there is no easy way to detect 32 vs 64 bit on mac
                // we hard-code to 64 as only the core-duos were 32 bit.
                $machine = 'x86_64';
                break;
            case 'linux':
                $release = substr(str_replace('.', '', $release), 0, 2);
                if (intval($release) > 26) {
                    $release = '26';
                }
                break;
            case 'nt':
                $release = '';
                $machine = 'x86';
                break;
        }

        // if a matching p4-bin path exists, add to PATH.
        $path = BASE_PATH . '/p4-bin/bin.' . $system . $release . $machine;
        if (is_dir($path) && strpos(getenv('PATH'), $path . PATH_SEPARATOR) !== 0) {
            putenv('PATH=' . $path . PATH_SEPARATOR . getenv('PATH'));
        }
    }

    /**
     * Determine if setup needs to be run. Setup must be run if
     * we have no connection to Perforce or no sites are defined.
     *
     * @return  boolean     true if setup needs to be run.
     */
    public function isSetupNeeded()
    {
        return !$this->hasResource('perforce') || !$this->hasResource('site');
    }

    /**
     * Register a new resource plugin. Extended to ignore any resources
     * that are already known class resources. The plugin version won't
     * be used if there is a class resource of the same name, however,
     * we could get an exception if the plugin does not exist.
     *
     * @param   string|Zend_Application_Resource_Resource   $resource   the resource to register
     * @param   mixed                                       $options    options for the resource
     * @return  Zend_Application_Bootstrap_BootstrapAbstract            provides fluent interface
     * @throws  Zend_Application_Bootstrap_Exception                    when invalid resource is provided
     */
    public function registerPluginResource($resource, $options = null)
    {
        $classResources = $this->getClassResourceNames();
        if (is_string($resource) && in_array(strtolower($resource), $classResources)) {
            return $this;
        }

        return parent::registerPluginResource($resource, $options);
    }

    /**
     * Set default timezone to suppress PHP warnings.
     */
    protected function _initTimezone()
    {
        date_default_timezone_set(@date_default_timezone_get());
    }

    /**
     * Register known libraries with our loader.
     */
    protected function _initLibraries()
    {
        $libraries = array('P4', 'P4Cms', 'Phly', 'Zend');
        foreach ($libraries as $library) {
            P4Cms_Loader::addPackagePath($library, LIBRARY_PATH . '/' . $library);
        }
    }

    /**
     * Initialize the logger.
     *
     * @return  Zend_Log    the initialized logger.
     */
    protected function _initLog()
    {
        // don't setup logger if we can't write log file.
        $options = $this->getOption('resources');
        $options = isset($options['log']) ? (array) $options['log'] : array();
        $logFile = isset($options['file']) ? $options['file'] : null;
        if (!$logFile || !@touch($logFile)) {
            return;
        }

        // if we don't already have a logger; set one up.
        if (!P4Cms_Log::hasLogger()) {
            P4Cms_Log::setLogger(new Zend_Log);
        }

        // attach our writer to the logger
        $logger = P4Cms_Log::getLogger();
        $logger->setEventItem('pid', getmypid());
        $writer = new Zend_Log_Writer_Stream($logFile);
        $logger->addWriter($writer);

        // configure log format.
        $format = new Zend_Log_Formatter_Simple(
            isset($options['format']) ? $options['format'] : null
        );
        $writer->setFormatter($format);

        // filter by priority.
        $filter = new Zend_Log_Filter_Priority((int)$options['priority']);
        $writer->addFilter($filter);

        // log uncaught exceptions.
        set_exception_handler(
            function(Exception $e)
            {
                // abort page caching on unhandled exceptions
                // as we want users to get the real content
                // if the issue is later fixed.
                if (P4Cms_Cache::canCache('page')) {
                    P4Cms_Cache::getCache('page')->cancel();
                }

                P4Cms_Log::logException("Uncaught ", $e);

                trigger_error($e);
            }
        );

        return $logger;
    }

    /**
     * Create the request object (normally doesn't happen until dispatch).
     *
     * @return  Zend_Controller_Request_Http    the initialized request object.
     */
    protected function _initRequest()
    {
        return new P4Cms_Controller_Request_Http;
    }

    /**
     * Configure the cache manager.
     *
     * Registers the cache manager with P4Cms_Cache for easy/static
     * access to caching facilities (e.g. save(), load()).
     *
     * @return  void|P4Cms_Cache_Manager    the configured cache manager or void if none.
     */
    protected function _initCacheManager()
    {
        // don't initialize cache manager if its not configured or enabled.
        $options = $this->getOptions();
        if (!isset($options['resources']['cachemanager']['enabled'])
            || !$options['resources']['cachemanager']['enabled']
        ) {
            P4Cms_Cache::setLoggingEnabled(false);
            return;
        }

        // create the cache manager.
        $manager = new P4Cms_Cache_Manager;

        // provide convenient static access via P4Cms_Cache
        P4Cms_Cache::setManager($manager);

        return $manager;
    }

    /**
     * Register any global cache templates.
     *
     * Any template that specifies a fixed namespace option is considered
     * to be 'global' and will be initialized here. All other templates
     * are automatically assumed to be site specific and will be configured
     * after we initialize the site so that we can set the site namespace.
     */
    protected function _initGlobalCache()
    {
        $this->bootstrap('cacheManager');
        $manager = $this->getResource('cacheManager');
        $options = $this->getOptions();

        // if no cache manager, can't configure global caches.
        if (!$manager) {
            return;
        }

        // add global cache templates (explicit namespaces only)
        $templates = $options['resources']['cachemanager'];
        foreach ($templates as $name => $template) {
            if (!is_array($template) || !isset($template['backend']['options']['namespace'])) {
                continue;
            }

            // skip any backend which specifies a non-writable cache_dir
            if (isset($template['backend']['options']['cache_dir'])) {
                $cachePath = $template['backend']['options']['cache_dir'];
                @mkdir($cachePath, 0755, true);
                if (!is_writable($cachePath)) {
                    continue;
                }
            }

            $manager->setCacheTemplate($name, $template);
        }
    }

    /**
     * Figure out the current site.
     *
     * @return  P4Cms_Site  the active site model.
     */
    protected function _initSite()
    {
        $this->bootstrap('request');
        $request = $this->getResource('request');

        // tell site model where sites are stored.
        P4Cms_Site::setSitesPackagesPath(SITES_PATH);
        P4Cms_Site::setSitesDataPath(DATA_PATH . '/sites');

        // try to fetch sites without initializing perforce.
        // this will only work if we have previously cached
        // sites - if it fails, init perforce and try again.
        $perforce = null;
        try {
            $sites = P4Cms_Site::fetchAll();
        } catch (Exception $e) {
            $this->bootstrap('perforce');
            $perforce = $this->getResource('perforce');
            if (!$perforce) {
                return;
            }

            // cold cache case, need to fetch against perforce.
            $sites = P4Cms_Site::fetchAll(null, $perforce);
        }

        // can't init site if there are no sites.
        if (!$sites->count()) {
            return;
        }

        // application config may limit (whitelist) the
        // branches that can be safely exposed.
        $limit   = null;
        $options = $this->getOption('resources');
        if (isset($options['site']['whitelist'])) {
            $limit = (array) $options['site']['whitelist'];
        }

        // find the best site/branch for this request (honor limit).
        $site = P4Cms_Site::fetchByRequest($request, $limit, $perforce);

        return $site;
    }

    /**
     * Register any site specific cache templates.
     *
     * Cache templates that do not specify a namespace are automatically
     * assumed to be site specific and are configured here (post site init).
     */
    protected function _initSiteCache()
    {
        $this->bootstrap('site');
        $this->bootstrap('cacheManager');
        $site    = $this->getResource('site');
        $manager = $this->getResource('cacheManager');
        $options = $this->getOptions();

        // if no cache manager or no site, can't configure site caches.
        if (!$manager || !$site) {
            return;
        }

        // add site specific cache templates (no explicit namespace)
        $templates = $options['resources']['cachemanager'];
        foreach ($templates as $name => $template) {
            if (!is_array($template) || isset($template['backend']['options']['namespace'])) {
                continue;
            }

            // skip any backend which specify a non-writable cache_dir
            if (isset($template['backend']['options']['cache_dir'])) {
                $cachePath = $template['backend']['options']['cache_dir'];
                @mkdir($cachePath, 0755, true);
                if (!is_writable($cachePath)) {
                    continue;
                }
            }

            // make cache site specific by passing the site id as a
            // 'namespace' option to the backend (the backend will
            // simply ignore this option if it doesn't support it).
            $template['backend']['options']['namespace'] = $site->getId();

            $manager->setCacheTemplate($name, $template);
        }
    }

    /**
     * If the page cache has been configured by the cacheManager bootstrap;
     * this task will 'start' it causing a cached page to be served if possible
     * and attempting to capture the current page otherwise.
     *
     * @return void|Zend_Cache_Core     The page cache instance or void if none.
     */
    protected function _initPageCache()
    {
        $this->bootstrap('cacheManager');
        $this->bootstrap('request');
        $cacheManager = $this->getResource('cacheManager');
        $request      = $this->getResource('request');

        // early exit if no page cache is configured
        if (!$cacheManager instanceof Zend_Cache_Manager || !$cacheManager->hasCache('page')) {
            return;
        }

        $cache = $cacheManager->getCache('page');

        // setting the baseUrl allows the page cache to munge regex's to still
        // match with sub-folder installs
        $cache->setBaseUrl($request->getBaseUrl());

        // Provide default values for username/rolenames aimed at anonymous users.
        $cache->setUsername('')
              ->setRolenames(array());

        // If cookies are present attempt to pull out the user name and role names.
        // We avoid this when there are no cookies to prevent needlessly starting
        // a new session for anonymous users.
        if (count($_COOKIE)) {
            // verify the session is configured if we are taking this branch.
            $this->bootstrap('sessionHandler');

            $auth = Zend_Auth::getInstance();
            if ($auth->hasIdentity()) {
                $identity = $auth->getIdentity();
                if (is_array($identity) && isset($identity['id'])) {
                    $username  = (string)$identity['id'];

                    // the roles are cached for us by initUser, attempt to access them here.
                    $rolenames = P4Cms_Cache::load('p4cms_user_roles_' . md5($username));

                    // if we weren't able to find a list of roles for this user, then
                    // we have no way of making a suitable cache id, early exit.
                    // presumably we will put the roles into cache for the next request.
                    if ($rolenames === false) {
                        return $cacheManager->getCache('page');
                    } else {
                        $cache->setUsername($username);
                        $cache->setRolenames($rolenames);
                    }
                }
            }
        }

        // Start the page cache. If a cached copy is present it
        // will be output at this point and the script will die
        // to stop further execution.
        $cache->start();

        return $cacheManager->getCache('page');
    }

    /**
     * If session options were declared, intialize the session handler
     * at this point. By default initialization would happen much later
     * and we could end up accessing session data prior to the handler
     * being initialized.
     */
    protected function _initSessionHandler()
    {
        $options = $this->getOption('resources');

        // attempt to make writable session save path if one is set.
        if (isset($options['session']['save_path'])) {
            $path = $options['session']['save_path'];
            @mkdir($path, 0700, true);
            if (!is_writable($path)) {
                @chmod($path, 0700);
            }

            // if still not writable, clear unusable save path.
            if (!is_writable($path)) {
                P4Cms_Log::log(
                    "Session save path '$path' is not writable. Using default instead.",
                    P4Cms_Log::WARN
                );

                $this->setOptions(
                    array('resources' => array('session' => array('save_path' => null)))
                );
            }
        }

        if (isset($options['session'])) {
            $this->bootstrap('session');
        }
    }

    /**
     * Setup view with our (theme-aware) view renderer and automatic context switching.
     *
     * @return  Zend_View   the configured view instance.
     */
    protected function _initView()
    {
        // grab view options.
        $options = $this->getOptions();
        if (isset($options['resources']['view'])) {
            $options = $options['resources']['view'];
        } else {
            $options = array();
        }

        // setup view.
        $view = new Zend_View($options);

        // use our renderer.
        $renderer = new P4Cms_Controller_Action_Helper_ViewRenderer;
        $renderer->setView($view);
        Zend_Controller_Action_HelperBroker::addHelper($renderer);

        return $view;
    }

    /**
     * Define application version constants.
     */
    protected function _initVersion()
    {
        $versionFile = BASE_PATH . '/Version';
        $versionData = is_readable($versionFile)
            ? file_get_contents($versionFile)
            : '';

        // extract key/value constants from version file
        preg_match_all('/^([A-Z]+)\s*=\s*(.+[^\s])\s*;/m', $versionData, $matches);

        // preset release, patchlevel and suppdate to guard against their absence
        $constants = array(
            'NAME'          => 'P4CHRONICLE',
            'RELEASE'       => 'unknown-release',
            'PATCHLEVEL'    => 'unknown-patchlevel',
            'SUPPDATE'      => 'unknown-date'
        );

        // merge in constants from the file
        if (is_array($matches[1]) && is_array($matches[2])) {
            foreach ($matches[1] as $key => $constant) {
                if (isset($matches[2][$key])) {
                    $constants[$constant] = $matches[2][$key];
                }
            }
        }

        // properly format our constants
        $constants['RELEASE']  = str_replace(' ', '.', $constants['RELEASE']);
        $constants['SUPPDATE'] = str_replace(' ', '/', $constants['SUPPDATE']);

        // define our constants
        foreach ($constants as $name => $value) {
            $constantName = 'P4CMS_VERSION_' . $name;
            if (!defined($constantName)) {
                define($constantName, $value);
            }
        }

        // produce a version string
        if (!defined('P4CMS_VERSION')) {
            $version = P4CMS_VERSION_NAME
                     . '/' . P4CMS_VERSION_RELEASE
                     . '/' . P4CMS_VERSION_PATCHLEVEL
                     . ' (' . P4CMS_VERSION_SUPPDATE . ')';
            define('P4CMS_VERSION', $version);
        }

        $options = $this->getOptions();
        if (isset($options['reportVersion']) && $options['reportVersion'] === true) {
            $this->getResource('view')->headMeta()->appendName('chronicle-version', P4CMS_VERSION);
        }
    }

    /**
     * Setup action helpers.
     */
    protected function _initActionHelpers()
    {
        $options = $this->getOptions();

        // use our automatic context switch helper to adjust
        // view script suffix (no need to call init context).
        Zend_Controller_Action_HelperBroker::addHelper(
            new P4Cms_Controller_Action_Helper_ContextSwitch
        );

        // use the WidgetContext helper to provide contextual data to widgets
        Zend_Controller_Action_HelperBroker::addHelper(
            new P4Cms_Controller_Action_Helper_WidgetContext
        );

        // use the extended Redirector helper to provide option
        // to redirect to one of the previously visited pages
        Zend_Controller_Action_HelperBroker::addHelper(
            new P4Cms_Controller_Action_Helper_Redirector
        );

        // add acl helper to provide convenient access checks
        Zend_Controller_Action_HelperBroker::addHelper(
            new P4Cms_Controller_Action_Helper_Acl
        );

        // add audit helper to record dispatched actions
        Zend_Controller_Action_HelperBroker::addHelper(
            new P4Cms_Controller_Action_Helper_Audit
        );
    }

    /**
     * Ensure our existing request object is set on the front controller
     * so that we don't have two of them in-flight.
     */
    protected function _initFrontRequest()
    {
        $this->bootstrap('request');
        $this->bootstrap('frontController');
        $request = $this->getResource('request');
        $front   = $this->getResource('frontController');
        $front->setRequest($request);
    }

    /**
     * Setup the router to use a custom version of the default route.
     * The custom version provides support for shorter urls (e.g. the
     * controller can be omitted if it is the default ('index')
     * controller.
     *
     * @return  Zend_Controller_Router_Interface    the initialized router.
     */
    protected function _initRouter()
    {
        $this->bootstrap('frontController');
        $this->bootstrap('request');
        $front   = $this->getResource('frontController');
        $request = $this->getResource('request');

        $router  = new P4Cms_Controller_Router_Rewrite;
        $front->setRouter($router);
        $router->addRoute(
            'default',
            new P4Cms_Controller_Router_Route_Module(
                array(),
                $front->getDispatcher(),
                $request
            )
        );

        return $router;
    }

    /**
     * Initialize pub-sub component to use our provider.
     */
    protected function _initPubSub()
    {
        P4Cms_PubSub::setInstance(new P4Cms_PubSub_Provider);
    }

    /**
     * Initialize PATH to include bundled p4 binaries.
     */
    protected function _initPath()
    {
        static::initPath();
    }

    /**
     * Establish our connection with Perforce.
     */
    protected function _initPerforce()
    {
        // place the p4trust file under the data path.
        // the trust file has to be in a writable
        // location to support ssl enabled servers
        putenv('P4TRUST=' . DATA_PATH . '/p4trust');

        // set the app name to use for all connections created
        // via the connection factory (needed for license).
        P4_Connection::setAppName('chronicle');

        $options = $this->getOption('resources');
        if (!isset($options['perforce']) || !is_array($options['perforce'])) {
            return null;
        }

        // if we are using perforce we need to ensure we select
        // the proper p4/p4d by initing the PATH first.
        $this->bootstrap('path');

        // extract perforce options into local scope.
        extract($options['perforce']);

        // create a perforce connection object.
        $connection = P4_Connection::factory(
            isset($port)     ? $port     : null,
            isset($user)     ? $user     : null,
            isset($client)   ? $client   : null,
            isset($password) ? $password : null,
            isset($ticket)   ? $ticket   : null,
            isset($type)     ? $type     : null
        );

        // login and set as default connection.
        $connection->login();
        P4_Connection::setDefaultConnection($connection);

        return $connection;
    }

    /**
     * Load the current site.
     *
     * @return  P4Cms_Site  the active site model.
     */
    protected function _initLoadSite()
    {
        $this->bootstrap('site');
        $this->bootstrap('perforce');
        $site     = $this->getResource('site');
        $perforce = $this->getResource('perforce');

        // if we have a site and a connection, load the site.
        if ($site && $perforce) {
            $site->setConnection($perforce)
                 ->load();
        }

        return $site;
    }

    /**
     * Initialize the current active user.
     *
     * @return  P4Cms_User  the active user model.
     */
    protected function _initUser()
    {
        $this->bootstrap('loadSite');
        $this->bootstrap('cacheManager');

        // if auth has a valid identity and we have a default adapter,
        // fetch the authenticated user (can't fetch w.out adapter).
        $auth = Zend_Auth::getInstance();
        if ($auth->hasIdentity() && P4Cms_Record::hasDefaultAdapter()) {
            $identity = $auth->getIdentity();
            if (is_array($identity) && isset($identity['id'])) {
                try {
                    $user = P4Cms_User::fetch($identity['id']);

                    // deny access if user has no roles
                    $roles = $user->getRoles();
                    if (!count($roles)) {
                        throw new Exception(
                            'Any authenticated user must have at least one role.'
                        );
                    }

                    // make the active user's personal adapter the default
                    // so that we communicate with Perforce as the user.
                    $ticket     = isset($identity['ticket']) ? $identity['ticket'] : null;
                    $adapter    = $user->createPersonalAdapter($ticket);
                    $connection = $adapter->getConnection();

                    // test user's ticket.
                    $connection->run('login', '-s');

                    $user->setPersonalAdapter($adapter);
                    P4Cms_Record::setDefaultAdapter($adapter);
                    P4_Connection::setDefaultConnection($connection);

                    // Cache out the roles so initPageCache will have access to them
                    // on subsequent requests without having to talk to perforce.
                    if (P4Cms_Cache::canCache()) {
                        P4Cms_Cache::save(
                            $roles->invoke('getId'),
                            'p4cms_user_roles_' . md5($user->getId()),
                            array('p4cms_user_' . md5($user->getId))
                        );
                    }

                } catch (Exception $e) {
                    P4Cms_Log::logException(
                        "Failed to initialize authenticated user",
                        $e
                    );

                    unset($user);
                    $auth->clearIdentity();
                    P4Cms_Notifications::add(
                        "You have been logged out.",
                        P4Cms_Notifications::SEVERITY_WARNING
                    );
                }
            }
        }

        // if no authenticated user, create anonymous user instance.
        if (!isset($user)) {
            $user = new P4Cms_User;
        }

        // set the active user for this request.
        P4Cms_User::setActive($user);

        return $user;
    }

    /**
     * Initialize the ACL for the application.
     *
     * @return  P4Cms_Acl   the active acl.
     */
    protected function _initAcl()
    {
        $this->bootstrap('view');
        $this->bootstrap('loadSite');
        $this->bootstrap('user');

        // if there is an active site, load it's acl
        // otherwise, create a blank one.
        if (P4Cms_Site::hasActive()) {
            $acl = P4Cms_Site::fetchActive()->getAcl();
        } else {
            $acl = new P4Cms_Acl;
        }

        // if the active user has a personal adapter, use it.
        $user = P4Cms_User::fetchActive();
        if ($user->hasPersonalAdapter()) {
            $acl->getRecord()->setAdapter($user->getPersonalAdapter());
        }

        // set acl on acl action helper.
        if (Zend_Controller_Action_HelperBroker::hasHelper('acl')) {
            $helper = Zend_Controller_Action_HelperBroker::getExistingHelper('acl');
            $helper->setAcl($acl);
        }

        // set acl on navigation view helper.
        $view = $this->getResource('view');
        $role = $user->getAggregateRole($acl);
        $view->getHelper('navigation')
             ->setRole($role)
             ->setAcl($acl);

        return $acl->makeActive();
    }

    /**
     * Initialize all of the enabled modules.
     */
    protected function _initModules()
    {
        $this->bootstrap('loadSite');

        // tell package system where the application's public folder is.
        P4Cms_PackageAbstract::setDocumentRoot(BASE_PATH);

        // tell p4cms module where to find core modules.
        P4Cms_Module::setCoreModulesPath(APPLICATION_PATH);

        // if setup must be run or if setup is already running,
        // restrict module bootstrap to just an init of core modules
        if ($this->isSetupNeeded() || self::_isSetupRunning()) {
            // init all of the core modules and any optional modules that
            // are enabled by default so they can participate in setup.
            $modules = P4Cms_Module::fetchAll();
            foreach ($modules as $module) {
                if ($module->isCoreModule() || $module->getPackageInfo('enableByDefault')) {
                    $module->init();
                }
            }

            // load modules we require for proper operation during setup.
            $required = array('Error', 'Setup', 'Ui', 'Dojo');
            foreach ($required as $module) {
                P4Cms_Module::fetch($module)->load();
            }

            return;
        }

        // initialize then load the enabled modules.
        $modules = P4Cms_Module::fetchAllEnabled();
        $modules->invoke('init');
        $modules->invoke('load');

        return $modules;
    }

    /**
     * Initialize the theme.
     *
     * @return  P4Cms_Theme     the current theme.
     */
    protected function _initTheme()
    {
        // if a theme is already active, return it.
        if (P4Cms_Theme::hasActive()) {
            return P4Cms_Theme::fetchActive();
        }

        // if there is an active site, load it's theme.
        if (P4Cms_Site::hasActive()) {
            try {
                $config = P4Cms_Site::fetchActive()->getConfig();
                $theme  = P4Cms_Theme::fetch($config->getTheme());
                $theme->load();

                return $theme;
            } catch (Exception $e) {
                P4Cms_Log::logException(
                    "Failed to load theme.",
                    $e
                );
            }
        }

        // load default theme.
        P4Cms_Theme::addPackagesPath(SITES_PATH . '/all/themes');
        $theme = P4Cms_Theme::fetchDefault();
        $theme->load();

        return $theme;
    }

    /**
     * Setup the asset handler. This is primarly intended for storing generated
     * assets. In single server configurations, default, this just directs assets
     * to the local filesystem. When running multiple servers you can use a shared
     * asset store to ensure all web servers have access.
     *
     * @return P4Cms_AssetHandlerInterface|null     asset handler or null
     */
    protected function _initAssetHandler()
    {
        // return if we don't have any settings
        $options = $this->getOption('resources');
        if (!isset($options['assethandler']) || !isset($options['assethandler']['class'])) {
            return null;
        }

        // return if class is invalid
        $class = $options['assethandler']['class'];
        if (!class_exists($class)) {
            return null;
        }

        // pull out options defaulting to empty array
        $options = isset($options['assethandler']['options'])
                 ? $options['assethandler']['options']
                 : array();

        $handler = new $class($options);

        // returm null if the required interface isn't implemented
        if (!is_a($handler, 'P4Cms_AssetHandlerInterface')) {
            return null;
        }

        return $handler;
    }

    /**
     * Setup view helpers (enable dojo, css aggregation, ...)
     *
     * @todo move dojo specific parts into dojo modules init method
     */
    protected function _initViewHelpers()
    {
        $this->bootstrap('view');
        $this->bootstrap('request');
        $this->bootstrap('assetHandler');
        $view         = $this->getResource('view');
        $request      = $this->getResource('request');
        $assetHandler = $this->getResource('assetHandler');
        $options      = $this->getOption('performance');

        // enable dojo.
        Zend_Dojo::enableView($view);
        Zend_Dojo_View_Helper_Dojo::setUseDeclarative();
        $dojo = $view->getHelper('dojo');
        $dojo->setLocalPath(
            $request->getBaseUrl() . '/application/dojo/resources/dojo/dojo.js'
        );

        // setup automatic dojo builds if enabled and we have a site.
        if ($options['autoBuildDojo']) {
            $dojo->setAutoBuild(true)
                 ->setDocumentRoot(BASE_PATH)
                 ->setAssetHandler($assetHandler);
        }

        // setup head script with JS aggregation.
        $headScript = $view->getHelper('headScript');
        $headScript->appendScript(
            "if (typeof(p4cms) == 'undefined') p4cms = {};\n" .
            "p4cms.baseUrl='"       . $request->getBaseUrl() . "';\n" .
            "p4cms.branchBaseUrl='" . $request->getBranchBaseUrl() . "';\n"
        );
        if ($options['aggregateJs']) {
            $headScript->setAggregateJs(true)
                       ->setDocumentRoot(BASE_PATH)
                       ->setAssetHandler($assetHandler);
        }

        // setup css aggregation if so enabled.
        if ($options['aggregateCss']) {
            $headLink = $view->getHelper('headLink');
            $headLink->setAggregateCss(true)
                     ->setDocumentRoot(BASE_PATH)
                     ->setAssetHandler($assetHandler);
        }

        // set the page title to the app name or site name (if active).
        $title = $view->getHelper('headTitle');
        $title->setSeparator(' - ');
        if (P4Cms_Site::hasActive()) {
            $site = P4Cms_Site::fetchActive();
            $title->setPostfix($site->getConfig()->getTitle());
        } else {
            $title->setPostfix('Chronicle');
        }
    }

    /**
     * Initialize setup program if necessary.
     */
    protected function _initSetup()
    {
        $this->bootstrap('request');
        $this->bootstrap('loadSite');
        $this->bootstrap('modules');

        // if setup is needed, make it run (unless it is already running).
        if ($this->isSetupNeeded() && !self::_isSetupRunning()) {
            $this->getResource('request')->setPathInfo('/setup');
        }

        // if setup isn't going to run, nothing more to do.
        if (!self::_isSetupRunning()) {
            return;
        }

        // we want errors to show during setup regardless of app config.
        $this->getApplication()->setPhpSettings(
            array(
                'display_errors'    => 1,
                'phpSettings'       => (E_ALL & ~(E_STRICT|E_NOTICE))
            )
        );
    }

    /**
     * Determine if setup is currently set to run.
     *
     * @return  boolean     true if setup has been requested to run.
     */
    protected function _isSetupRunning()
    {
        $this->bootstrap('request');
        $request = $this->getResource('request');
        $paths   = explode('/', substr($request->getPathInfo(), 1));
        if (isset($paths[0]) && $paths[0] == 'setup') {
            return true;
        } else {
            return false;
        }
    }
}
# Change User Description Committed
#1 16170 perforce_software Move Chronicle files to follow new path scheme for branching.
//guest/perforce_software/chronicle/application/Bootstrap.php
#1 8972 Matt Attaway Initial add of the Chronicle source code