diff --git a/.htaccess b/.htaccess index 58a7a7706..010a7b61d 100644 --- a/.htaccess +++ b/.htaccess @@ -1,7 +1,7 @@ Deny from all - + Allow from all diff --git a/dev/DevelopmentAdmin.php b/dev/DevelopmentAdmin.php index d9beb471c..cfc2492c2 100644 --- a/dev/DevelopmentAdmin.php +++ b/dev/DevelopmentAdmin.php @@ -76,7 +76,6 @@ class DevelopmentAdmin extends Controller { function index() { $actions = array( "build" => "Build/rebuild this environment. Call this whenever you have updated your project sources", - "buildcache" => "Rebuild the static cache, if you're using StaticPublisher", "tests" => "See a list of unit tests to run", "tests/all" => "Run all tests", "tests/startsession" => "Start a test session in your browser (gives you a temporary database with default content)", diff --git a/docs/en/reference/staticpublisher.md b/docs/en/reference/staticpublisher.md deleted file mode 100644 index 00701eeda..000000000 --- a/docs/en/reference/staticpublisher.md +++ /dev/null @@ -1,283 +0,0 @@ -# Static Publisher - -## Introduction - -Many sites get too much traffic to justify dynamically sending every request. Caching is needed. Static Publishing -will generate static versions of your content (HTML) that can be served without ever hitting PHP or the Database. - -See `[api:StaticExporter]` for a less flexible, but easier way of building a local static cache from all of -your pages. - -See [Partial-Caching](partial-caching) for a much more flexible way of building in caching without statically delivering -content. Partial Caching is recommended as a basic enhancement to any SilverStripe site however if your site is planning -a vast amount of traffic (eg an article is being dug) then Static Publisher will be appropriate. - -## Usage - -SilverStripe doesn't have enough information about your template and data-structures to automatically determine which -URLs need to be cached, and at which time they are considered outdated. By adding a custom method allPagesToCache() to -your Page class, you can determine which URLs need caching, and hook in custom logic. This array of URLs is used by the -publisher to generate folders and HTML-files. - - :::php - class Page extends SiteTree { - // ... - - /** - - * Return a list of all the pages to cache - */ - public function allPagesToCache() { - // Get each page type to define its sub-urls - $urls = array(); - - // memory intensive depending on number of pages - $pages = SiteTree::get(); - - foreach($pages as $page) { - $urls = array_merge($urls, (array)$page->subPagesToCache()); - } - - // add any custom URLs which are not SiteTree instances - $urls[] = "sitemap.xml"; - - return $urls; - } - - /** - - * Get a list of URLs to cache related to this page - */ - public function subPagesToCache() { - $urls = array(); - - // add current page - $urls[] = $this->Link(); - - // cache the RSS feed if comments are enabled - if ($this->ProvideComments) { - $urls[] = Director::absoluteBaseURL() . "pagecomment/rss/" . $this->ID; - } - - return $urls; - } - - public function pagesAffectedByChanges() { - $urls = $this->subPagesToCache(); - if($p = $this->Parent) $urls = array_merge((array)$urls, (array)$p->subPagesToCache()); - return $urls; - } - } - -## Excluding Pages - -The allPagesToCache function returns all the URLs needed to cache. So if you want to exclude specific pages from the -cache then you unset these URLs from the returned array. If you do not want to cache a specific class (eg UserDefinedForms) -you can also add an exclusion - - :::php - public function allPagesToCache() { - $urls = array(); - $pages = SiteTree::get(); - - // ignored page types - $ignored = array('UserDefinedForm'); - - foreach($pages as $page) { - // check to make sure this page is not in the classname - if(!in_array($page->ClassName, $ignored)) { - $urls = array_merge($urls, (array)$page->subPagesToCache()); - } - } - - return $urls; - } - -You can also pass the filtering to the original `SiteTree::get()`; - - :::php - public function allPagesToCache() { - $urls = array(); - $pages = SiteTree::get()->where("ClassName != 'UserDefinedForm'"); - ... - -## Single server Caching - -This setup will store the cached content on the same server as the CMS. This is good for a basic performance enhancement. - -### Setup - -Put this in mysite/_config.php. This will create static content in a "cache/" subdirectory, with an HTML suffix. - - :::php - Object::add_extension("SiteTree", "FilesystemPublisher('cache/', 'html')"); - - -* Put this into your .htaccess. It will serve requests from the cache, statically, if the cache file exists. Replace -**sitedir** with the a subdirectory that you would like to serve the site from (for example, in your dev environment). - -[View .htaccess -example](http://open.silverstripe.com/browser/modules/cms/trunk/code/staticpublisher/htaccess_example_rsyncsingleserver) - -* We use a simple PHP script, static-main.php, to control cache lookup. This makes the .htaccess update simpler. - -Just look for this line: - - RewriteRule .* framework/main.php?url=%1&%{QUERY_STRING} [L] - - -And change the PHP script from main.php to static-main.php: - - RewriteRule .* framework/static-main.php?url=%1&%{QUERY_STRING} [L] - -## Using Static Publisher With Subsites Module - -Append the following code to mysite/config.php - - :::php - FilesystemPublisher::$domain_based_caching = true; - - -Instead of the above code snippet for Page.php, use the following code: - - :::php - class Page extends SiteTree { - - // ... - - public function allPagesToCache() { - // Get each page type to define its sub-urls - $urls = array(); - - // memory intensive depending on number of pages - $pages = Subsite::get_from_all_subsites("SiteTree"); - - foreach($pages as $page) { - $urls = array_merge($urls, (array)$page->subPagesToCache()); - } - - return $urls; - } - - public function subPagesToCache() { - $urls = array(); - $urls[] = $this->AbsoluteLink(); - return $urls; - } - - public function pagesAffectedByChanges() { - $urls = $this->subPagesToCache(); - if($p = $this->Parent) $urls = array_merge((array)$urls, (array)$p->subPagesToCache()); - return $urls; - } - - // ... some other code ... - - } - - -And the last thing you need to do is adding your main site's host mapping to subsites/host-map.php. For example, your -main site's host is mysite.com the content of the file would be: - - :::php - @:', - '@:', - )); - - -Where `` is a unix account with write permissions to `` (e.g. `/var/www`), and -`` and `` are the names of your static content servers. The number of servers is -flexible and depends on your infrastructure and scalability needs. - -* Ensure that the `rsync` unix tool is installed on the CMS server, and ssh access is enabled on the static content -servers. - -* No password can be specified for the SSH connection . The class assumes a key-based authentication without requiring -a password for the username specified in `` (see [http://www.csua.berkeley.edu/~ranga/notes/ssh_nopass.html -tutorial](http://www.csua.berkeley.edu/~ranga/notes/ssh_nopass.html tutorial)). - -* Put the .htaccess file linked below into the webroot of each static content server (and rename it to `.htaccess`). -It will serve requests from the cache, statically, if the cache file exists. Replace **sitedir** with the a -subdirectory that you would like to serve the site from (for example, in your dev environment). - -[View .htaccess -example](http://open.silverstripe.com/browser/modules/cms/trunk/code/staticpublisher/htaccess_example_rsyncmultiservers) - -## Cache Control - -There is also the option to wrap some PHP logic around the static HTML content served by the content servers, which can -greatly reduce the bandwidth required on your content servers. This code takes care of cache control through HTTP -headers (''Cache-control'', `If-modified-since`), meaning the files will only be delivered if they changed since the -browser client last requested them. The last modification date for each static file is controlled by the publication -script, meaning the cache gets invalidated on each publication. - -To enable cache control, specify "php" instead of "html" in the RsyncMultiHostPublisher definition. - - :::php - Object::add_extension("SiteTree", "RsyncMultiHostPublisher('cache/', 'php')"); - - -And use this slightly different .htaccess file. Make sure that index.php can be used as a directory index! - -[View .htaccess -example](http://open.silverstripe.com/browser/modules/cms/trunk/code/staticpublisher/htaccess_example_rsyncwithphp) - -## Deployment - -Once you've set up your rewrite rules and defined which pages need caching, you can build the static HTML files. This is -done by the `[api:RebuildStaticCacheTask]` - -Execution via URL - - http://www.example.com/dev/buildcache?flush=1 - -Execution on CLI (via [sake](/topics/commandline)) - - sake dev/buildcache flush=1 - -Depending on which extension you've set up for your SiteTree (FilesystemPublisher or RsyncMultiHostPublisher), the -method publishPages() either stores the generated HTML-files on the server's filesystem, or deploys them to other -servers via rsync. - -It is adviseable to set dev/buildcache up as an automated task (e.g. unix cron) which continually rebuilds and redeploys -the cache. - -## Related - -* `[api:StaticExporter]` -* [Partial-Caching](partial-caching) - -## API Documentation -* `[api:StaticPublisher]` diff --git a/main.php b/main.php index edace3bf7..d0110eef1 100644 --- a/main.php +++ b/main.php @@ -103,6 +103,8 @@ if (substr(strtolower($url), 0, strlen(BASE_URL)) == strtolower(BASE_URL)) $url // Connect to database require_once('model/DB.php'); +global $databaseConfig; + // Redirect to the installer if no database is selected if(!isset($databaseConfig) || !isset($databaseConfig['database']) || !$databaseConfig['database']) { if(!file_exists(BASE_PATH . '/install.php')) { diff --git a/static-main.php b/static-main.php deleted file mode 100644 index 339603573..000000000 --- a/static-main.php +++ /dev/null @@ -1,109 +0,0 @@ -destFolder - -// Optional settings for FilesystemPublisher::$domain_based_mapping=TRUE -$hostmapLocation = '../subsites/host-map.php'; - -// Specific to 'homepagefordomain' module -$homepageMapLocation = '../assets/_homepage-map.php'; - -if ( - $cacheEnabled - && empty($_COOKIE['bypassStaticCache']) - // No GET params other than cache relevant config is passed (e.g. "?stage=Stage"), - // which would mean that we have to bypass the cache - && count(array_diff(array_keys($_GET), array('url', 'cacheSubdir'))) == 0 - // Request is not POST (which would have to be handled dynamically) - && count($_POST) == 0 -) { - // Define system paths (copied from Core.php) - if(!defined('BASE_PATH')) { - // Assuming that this file is framework/static-main.php we can then determine the base path - define('BASE_PATH', rtrim(dirname(dirname(__FILE__))), DIRECTORY_SEPARATOR); - } - if(!defined('BASE_URL')) { - // Determine the base URL by comparing SCRIPT_NAME to SCRIPT_FILENAME and getting common elements - $path = realpath($_SERVER['SCRIPT_FILENAME']); - if(substr($path, 0, strlen(BASE_PATH)) == BASE_PATH) { - $urlSegmentToRemove = substr($path, strlen(BASE_PATH)); - if(substr($_SERVER['SCRIPT_NAME'], -strlen($urlSegmentToRemove)) == $urlSegmentToRemove) { - $baseURL = substr($_SERVER['SCRIPT_NAME'], 0, -strlen($urlSegmentToRemove)); - define('BASE_URL', rtrim($baseURL, DIRECTORY_SEPARATOR)); - } - } - } - - $url = $_GET['url']; - // Remove base folders from the URL if webroot is hosted in a subfolder - if (substr(strtolower($url), 0, strlen(BASE_URL)) == strtolower(BASE_URL)) { - $url = substr($url, strlen(BASE_URL)); - } - - $host = str_replace('www.', '', $_SERVER['HTTP_HOST']); - - // Custom cache dir for debugging purposes - if (isset($_GET['cacheSubdir']) && !preg_match('/[^a-zA-Z0-9\-_]/', $_GET['cacheSubdir'])) { - $cacheDir = $_GET['cacheSubdir'].'/'; - } - // Custom mapping through PHP file (assumed FilesystemPublisher::$domain_based_mapping=TRUE) - else if (file_exists($hostmapLocation)) { - include_once $hostmapLocation; - $subsiteHostmap['default'] = isset($subsiteHostmap['default']) ? $subsiteHostmap['default'] : ''; - $cacheDir = (isset($subsiteHostmap[$host]) ? $subsiteHostmap[$host] : $subsiteHostmap['default']) . '/'; - } - // No subfolder (for FilesystemPublisher::$domain_based_mapping=FALSE) - else { - $cacheDir = ''; - } - - // Look for the file in the cachedir - $file = trim($url, '/'); - $file = $file ? $file : 'index'; - - // Route to the 'correct' index file (if applicable) - if ($file == 'index' && file_exists($homepageMapLocation)) { - include_once $homepageMapLocation; - $file = isset($homepageMap[$_SERVER['HTTP_HOST']]) ? $homepageMap[$_SERVER['HTTP_HOST']] : $file; - } - - // Encode each part of the path individually, in order to support multibyte paths. - // SiteTree.URLSegment and hence the static folder and filenames are stored in encoded form, - // to avoid filesystem incompatibilities. - $file = implode('/', array_map('rawurlencode', explode('/', $file))); - // Find file by extension (either *.html or *.php) - if (file_exists($cacheBaseDir . $cacheDir . $file . '.html')) { - header('X-SilverStripe-Cache: hit at '.@date('r')); - echo file_get_contents($cacheBaseDir . $cacheDir . $file . '.html'); - if ($cacheDebug) echo "

File was cached

"; - } elseif (file_exists($cacheBaseDir . $cacheDir . $file . '.php')) { - header('X-SilverStripe-Cache: hit at '.@date('r')); - include_once $cacheBaseDir . $cacheDir . $file . '.php'; - if ($cacheDebug) echo "

File was cached

"; - } else { - header('X-SilverStripe-Cache: miss at '.@date('r') . ' on ' . $cacheDir . $file); - // No cache hit... fallback to dynamic routing - include 'main.php'; - if ($cacheDebug) echo "

File was NOT cached

"; - } -} else { - // Fall back to dynamic generation via normal routing if caching has been explicitly disabled - include 'main.php'; -} -