mirror of
https://github.com/silverstripe/silverstripe-staticpublisher
synced 2024-10-22 14:05:54 +02:00
Initial import
This commit is contained in:
commit
c3c55c4b0e
24
LICENSE
Normal file
24
LICENSE
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
* Copyright (c) 2012, SilverStripe Ltd.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
* * Redistributions of source code must retain the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer.
|
||||||
|
* * Redistributions in binary form must reproduce the above copyright
|
||||||
|
* notice, this list of conditions and the following disclaimer in the
|
||||||
|
* documentation and/or other materials provided with the distribution.
|
||||||
|
* * Neither the name of the SilverStripe nor the
|
||||||
|
* names of its contributors may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY SilverStripe Ltd. ``AS IS'' AND ANY
|
||||||
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||||
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL SilverStripe Ltd. BE LIABLE FOR ANY
|
||||||
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||||
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||||
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||||
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
18
README.md
Normal file
18
README.md
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Static
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
Static provides several extensions for exporting a SilverStripe application to
|
||||||
|
both local or remote file systems
|
||||||
|
|
||||||
|
## Maintainer Contact
|
||||||
|
|
||||||
|
* Will Rossiter (Nickname: wrossiter, willr) `<will@fullscreen.io>`
|
||||||
|
* Sam Minneé (Nickname: sminnee) <sam@silverstripe.com>
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
* SilverStripe 3.1
|
||||||
|
* Tar archive
|
||||||
|
|
||||||
|
Note this is untested on Windows.
|
3
_config.php
Normal file
3
_config.php
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
define('STATIC_MODULE_DIR', dirname(__FILE__));
|
31
code/CachedPHPPage.tmpl
Normal file
31
code/CachedPHPPage.tmpl
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a system-generated PHP script that performs header management for
|
||||||
|
* the statically cached content given below.
|
||||||
|
*
|
||||||
|
* @package static
|
||||||
|
*/
|
||||||
|
|
||||||
|
define('MAX_AGE', '**MAX_AGE**');
|
||||||
|
define('LAST_MODIFIED', '**LAST_MODIFIED**');
|
||||||
|
|
||||||
|
if(MAX_AGE > 0) {
|
||||||
|
header("Cache-Control: max-age=" . MAX_AGE);
|
||||||
|
header("Pragma:");
|
||||||
|
} else {
|
||||||
|
header("Cache-Control: no-cache, max-age=0, must-revalidate");
|
||||||
|
}
|
||||||
|
|
||||||
|
header("Expires: " . gmdate('D, d M Y H:i:s', time() + MAX_AGE) . ' GMT');
|
||||||
|
header("Last-modified: " . gmdate('D, d M Y H:i:s', strtotime(LAST_MODIFIED)) . ' GMT');
|
||||||
|
|
||||||
|
if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
|
||||||
|
if(strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= strtotime(LAST_MODIFIED)) {
|
||||||
|
header("Last-modified: " . gmdate('D, d M Y H:i:s', strtotime(LAST_MODIFIED)) . ' GMT', true, 304);
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
?>
|
||||||
|
**CONTENT**
|
23
code/CachedPHPRedirection.tmpl
Normal file
23
code/CachedPHPRedirection.tmpl
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a system-generated PHP script that performs header management for
|
||||||
|
* a 301 redirection.
|
||||||
|
*
|
||||||
|
* @package static
|
||||||
|
*/
|
||||||
|
|
||||||
|
define('DESTINATION', '**DESTINATION**');
|
||||||
|
define('MAX_AGE', 3600);
|
||||||
|
|
||||||
|
if(MAX_AGE > 0) {
|
||||||
|
header("Cache-Control: max-age=" . MAX_AGE);
|
||||||
|
header("Pragma:");
|
||||||
|
} else {
|
||||||
|
header("Cache-Control: no-cache, max-age=0, must-revalidate");
|
||||||
|
}
|
||||||
|
|
||||||
|
header("Expires: " . gmdate('D, d M Y H:i:s', time() + MAX_AGE) . ' GMT');
|
||||||
|
header("Location: " . DESTINATION, true, 301);
|
||||||
|
|
||||||
|
?>
|
167
code/controllers/StaticExporter.php
Normal file
167
code/controllers/StaticExporter.php
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This class lets you export a static copy of your site either as an tar
|
||||||
|
* archive through the web browser or through the command line.
|
||||||
|
*
|
||||||
|
* The exporter will preserve the URL naming format of your pages by
|
||||||
|
* creating a number of subfolders folders each containing an index.html
|
||||||
|
* file.
|
||||||
|
*
|
||||||
|
* The bundled documentation in the 'docs' folder contains information
|
||||||
|
* and usage instructions.
|
||||||
|
*
|
||||||
|
* @see StaticPublisher
|
||||||
|
*
|
||||||
|
* @package static
|
||||||
|
*/
|
||||||
|
class StaticExporter extends Controller {
|
||||||
|
|
||||||
|
public static $allowed_actions = array(
|
||||||
|
'index',
|
||||||
|
'export',
|
||||||
|
'StaticExportForm'
|
||||||
|
);
|
||||||
|
|
||||||
|
public function init() {
|
||||||
|
parent::init();
|
||||||
|
|
||||||
|
$canAccess = (Director::isDev() || Director::is_cli() || Permission::check("ADMIN"));
|
||||||
|
if(!$canAccess) return Security::permissionFailure($this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function Link($action = null) {
|
||||||
|
return "StaticExporter/$action";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function index() {
|
||||||
|
return array(
|
||||||
|
'Title' => _t('StaticExporter.NAME','Static exporter'),
|
||||||
|
'Form' => $this->StaticExportForm()->forTemplate()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function StaticExportForm() {
|
||||||
|
$form = new Form($this, 'StaticExportForm', new FieldList(
|
||||||
|
new TextField('baseurl', _t('StaticExporter.BASEURL','Base URL'))
|
||||||
|
), new FieldList(
|
||||||
|
new FormAction('export', _t('StaticExporter.EXPORT','Export'))
|
||||||
|
));
|
||||||
|
|
||||||
|
return $form;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public function export() {
|
||||||
|
if(isset($_REQUEST['baseurl'])) {
|
||||||
|
$base = $_REQUEST['baseurl'];
|
||||||
|
if(substr($base,-1) != '/') $base .= '/';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$base = Director::baseURL();
|
||||||
|
}
|
||||||
|
|
||||||
|
$folder = TEMP_FOLDER . '/static-export';
|
||||||
|
$project = project();
|
||||||
|
|
||||||
|
$exported = $this->doExport($base, $folder .'/'. $project, false);
|
||||||
|
|
||||||
|
`cd $folder; tar -czhf $project-export.tar.gz $project`;
|
||||||
|
|
||||||
|
$archiveContent = file_get_contents("$folder/$project-export.tar.gz");
|
||||||
|
|
||||||
|
|
||||||
|
// return as download to the client
|
||||||
|
$response = SS_HTTPRequest::send_file(
|
||||||
|
$archiveContent,
|
||||||
|
"$project-export.tar.gz",
|
||||||
|
'application/x-tar-gz'
|
||||||
|
);
|
||||||
|
|
||||||
|
echo $response->output();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exports the website with the given base url. Returns the path where the
|
||||||
|
* exported version of the website is located.
|
||||||
|
*
|
||||||
|
* @param string website base url
|
||||||
|
* @param string folder to export the site into
|
||||||
|
* @param bool symlink assets
|
||||||
|
* @param bool suppress output progress
|
||||||
|
*
|
||||||
|
* @return string path to export
|
||||||
|
*/
|
||||||
|
public function doExport($base, $folder, $symlink = true, $quiet = true) {
|
||||||
|
ini_set('max_execution_time', 0);
|
||||||
|
Director::setBaseURL($base);
|
||||||
|
|
||||||
|
if(is_dir($folder)) {
|
||||||
|
Filesystem::removeFolder($folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
Filesystem::makeFolder($folder);
|
||||||
|
|
||||||
|
// symlink or copy /assets
|
||||||
|
$f1 = ASSETS_PATH;
|
||||||
|
$f2 = Director::baseFolder() . '/' . project();
|
||||||
|
|
||||||
|
if($symlink) {
|
||||||
|
`cd $folder; ln -s $f1; ln -s $f2`;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
`cp -R $f1 $folder; cp -R $f2 $folder`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterate through items we need to export
|
||||||
|
$objs = $this->getObjectsToExport();
|
||||||
|
|
||||||
|
if($objs) {
|
||||||
|
$total = $objs->count();
|
||||||
|
$i = 1;
|
||||||
|
|
||||||
|
foreach($objs as $obj) {
|
||||||
|
$link = $obj->RelativeLink(null, true);
|
||||||
|
|
||||||
|
$subfolder = "$folder/" . trim($link, '/');
|
||||||
|
$contentfile = "$folder/" . trim($link, '/') . '/index.html';
|
||||||
|
|
||||||
|
// Make the folder
|
||||||
|
if(!file_exists($subfolder)) {
|
||||||
|
Filesystem::makeFolder($subfolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run the page
|
||||||
|
Requirements::clear();
|
||||||
|
$link = Director::makeRelative($obj->Link());
|
||||||
|
$response = Director::test($link);
|
||||||
|
|
||||||
|
// Write to file
|
||||||
|
if($fh = fopen($contentfile, 'w')) {
|
||||||
|
if(!$quiet) printf("-- (%s/%s) Outputting page (%s)%s", $i, $total, $obj->RelativeLink(null, true), PHP_EOL);
|
||||||
|
|
||||||
|
fwrite($fh, $response->getBody());
|
||||||
|
fclose($fh);
|
||||||
|
}
|
||||||
|
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $folder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a list of publishable instances for the exporter to include. The
|
||||||
|
* only requirement is that for this list of objects, each one implements
|
||||||
|
* the RelativeLink() and Link() method.
|
||||||
|
*
|
||||||
|
* @return SS_List
|
||||||
|
*/
|
||||||
|
public function getObjectsToExport() {
|
||||||
|
$objs = SiteTree::get();
|
||||||
|
$this->extend('alterObjectsToExport', $objs);
|
||||||
|
|
||||||
|
return $objs;
|
||||||
|
}
|
||||||
|
}
|
360
code/extensions/FilesystemPublisher.php
Normal file
360
code/extensions/FilesystemPublisher.php
Normal file
@ -0,0 +1,360 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Usage: Object::add_extension("SiteTree", "FilesystemPublisher('static-folder', 'html')");
|
||||||
|
*
|
||||||
|
* Usage: To work with Subsite module you need to:
|
||||||
|
* - Add FilesystemPublisher::$domain_based_caching = true; in mysite/_config.php
|
||||||
|
* - Added main site host mapping in subsites/host-map.php after everytime a new subsite is created or modified
|
||||||
|
*
|
||||||
|
* You may also have a method $page->pagesAffectedByUnpublishing() to return other URLS
|
||||||
|
* that should be de-cached if $page is unpublished.
|
||||||
|
*
|
||||||
|
* @see http://doc.silverstripe.com/doku.php?id=staticpublisher
|
||||||
|
*
|
||||||
|
* @package cms
|
||||||
|
* @subpackage publishers
|
||||||
|
*/
|
||||||
|
class FilesystemPublisher extends StaticPublisher {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $destFolder = 'cache';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected $fileExtension = 'html';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var string
|
||||||
|
*/
|
||||||
|
protected static $static_base_url = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use domain based caching (put cache files into a domain subfolder).
|
||||||
|
*
|
||||||
|
* This must be true if you are using this with the "subsites" module.
|
||||||
|
*
|
||||||
|
* Please note that this form of caching requires all URLs to be provided absolute
|
||||||
|
* (not relative to the webroot) via {@link SiteTree->AbsoluteLink()}.
|
||||||
|
*
|
||||||
|
* @var boolean
|
||||||
|
*/
|
||||||
|
public static $domain_based_caching = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a different base URL for the static copy of the site.
|
||||||
|
*
|
||||||
|
* This can be useful if you are running the CMS on a different domain
|
||||||
|
* from the website.
|
||||||
|
*
|
||||||
|
* @param string
|
||||||
|
*/
|
||||||
|
public static function set_static_base_url($url) {
|
||||||
|
self::$static_base_url = $url;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param $destFolder The folder to save the cached site into.
|
||||||
|
* This needs to be set in framework/static-main.php as well through the {@link $cacheBaseDir} variable.
|
||||||
|
* @param $fileExtension The file extension to use, e.g 'html'.
|
||||||
|
* If omitted, then each page will be placed in its own directory,
|
||||||
|
* with the filename 'index.html'. If you set the extension to PHP, then a simple PHP script will
|
||||||
|
* be generated that can do appropriate cache & redirect header negotation.
|
||||||
|
*/
|
||||||
|
public function __construct($destFolder = 'cache', $fileExtension = null) {
|
||||||
|
// Remove trailing slash from folder
|
||||||
|
if(substr($destFolder, -1) == '/') $destFolder = substr($destFolder, 0, -1);
|
||||||
|
|
||||||
|
$this->destFolder = $destFolder;
|
||||||
|
$this->fileExtension = $fileExtension;
|
||||||
|
|
||||||
|
parent::__construct();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforms relative or absolute URLs to their static path equivalent.
|
||||||
|
*
|
||||||
|
* This needs to be the same logic that's used to look up these paths through
|
||||||
|
* static-main.php. Does not include the {@link $destFolder} prefix.
|
||||||
|
*
|
||||||
|
* URL filtering will have already taken place for direct SiteTree links via
|
||||||
|
* SiteTree->generateURLSegment()). For all other links (e.g. custom
|
||||||
|
* controller actions), we assume that they're pre-sanitized to suit the
|
||||||
|
* filesystem needs, as its impossible to sanitize them without risking to
|
||||||
|
* break the underlying naming assumptions in URL routing (e.g. controller
|
||||||
|
* method names).
|
||||||
|
*
|
||||||
|
* Examples (without $domain_based_caching):
|
||||||
|
* - http://mysite.com/mywebroot/ => /index.html
|
||||||
|
* - http://mysite.com/about-us => /about-us.html
|
||||||
|
* - http://mysite.com/parent/child => /parent/child.html
|
||||||
|
*
|
||||||
|
* Examples (with $domain_based_caching):
|
||||||
|
* - http://mysite.com/mywebroot/ => /mysite.com/index.html (assuming your webroot is in a subfolder)
|
||||||
|
* - http://mysite.com/about-us => /mysite.com/about-us.html
|
||||||
|
* - http://myothersite.com/about-us => /myothersite.com/about-us.html
|
||||||
|
* - http://subdomain.mysite.com/parent/child => /subdomain.mysite.com/parent/child.html
|
||||||
|
*
|
||||||
|
* @param array $urls Absolute or relative URLs
|
||||||
|
*
|
||||||
|
* @return array Map of original URLs to file system paths (relative to {@link $destFolder}).
|
||||||
|
*/
|
||||||
|
public function urlsToPaths($urls) {
|
||||||
|
$mappedUrls = array();
|
||||||
|
|
||||||
|
foreach($urls as $url) {
|
||||||
|
|
||||||
|
// parse_url() is not multibyte safe, see https://bugs.php.net/bug.php?id=52923.
|
||||||
|
// We assume that the URL hsa been correctly encoded either on storage (for SiteTree->URLSegment),
|
||||||
|
// or through URL collection (for controller method names etc.).
|
||||||
|
$urlParts = @parse_url($url);
|
||||||
|
|
||||||
|
// Remove base folders from the URL if webroot is hosted in a subfolder (same as static-main.php)
|
||||||
|
$path = isset($urlParts['path']) ? $urlParts['path'] : '';
|
||||||
|
if(mb_substr(mb_strtolower($path), 0, mb_strlen(BASE_URL)) == mb_strtolower(BASE_URL)) {
|
||||||
|
$urlSegment = mb_substr($path, mb_strlen(BASE_URL));
|
||||||
|
} else {
|
||||||
|
$urlSegment = $path;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize URLs
|
||||||
|
$urlSegment = trim($urlSegment, '/');
|
||||||
|
|
||||||
|
$filename = $urlSegment ? "$urlSegment.$this->fileExtension" : "index.$this->fileExtension";
|
||||||
|
|
||||||
|
if (self::$domain_based_caching) {
|
||||||
|
if (!$urlParts) continue; // seriously malformed url here...
|
||||||
|
$filename = $urlParts['host'] . '/' . $filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
$mappedUrls[$url] = ((dirname($filename) == '/') ? '' : (dirname($filename).'/')).basename($filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $mappedUrls;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function unpublishPages($urls) {
|
||||||
|
// Do we need to map these?
|
||||||
|
// Detect a numerically indexed arrays
|
||||||
|
if (is_numeric(join('', array_keys($urls)))) $urls = $this->urlsToPaths($urls);
|
||||||
|
|
||||||
|
// This can be quite memory hungry and time-consuming
|
||||||
|
// @todo - Make a more memory efficient publisher
|
||||||
|
increase_time_limit_to();
|
||||||
|
increase_memory_limit_to();
|
||||||
|
|
||||||
|
$cacheBaseDir = $this->getDestDir();
|
||||||
|
|
||||||
|
foreach($urls as $url => $path) {
|
||||||
|
if (file_exists($cacheBaseDir.'/'.$path)) {
|
||||||
|
@unlink($cacheBaseDir.'/'.$path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function publishPages($urls) {
|
||||||
|
// Do we need to map these?
|
||||||
|
// Detect a numerically indexed arrays
|
||||||
|
if (is_numeric(join('', array_keys($urls)))) $urls = $this->urlsToPaths($urls);
|
||||||
|
|
||||||
|
// This can be quite memory hungry and time-consuming
|
||||||
|
// @todo - Make a more memory efficient publisher
|
||||||
|
increase_time_limit_to();
|
||||||
|
increase_memory_limit_to();
|
||||||
|
|
||||||
|
// Set the appropriate theme for this publication batch.
|
||||||
|
// This may have been set explicitly via StaticPublisher::static_publisher_theme,
|
||||||
|
// or we can use the last non-null theme.
|
||||||
|
if(!StaticPublisher::static_publisher_theme())
|
||||||
|
SSViewer::set_theme(SSViewer::current_custom_theme());
|
||||||
|
else
|
||||||
|
SSViewer::set_theme(StaticPublisher::static_publisher_theme());
|
||||||
|
|
||||||
|
$currentBaseURL = Director::baseURL();
|
||||||
|
if(self::$static_base_url) Director::setBaseURL(self::$static_base_url);
|
||||||
|
if($this->fileExtension == 'php') SSViewer::setOption('rewriteHashlinks', 'php');
|
||||||
|
if(StaticPublisher::echo_progress()) echo $this->class.": Publishing to " . self::$static_base_url . "\n";
|
||||||
|
$files = array();
|
||||||
|
$i = 0;
|
||||||
|
$totalURLs = sizeof($urls);
|
||||||
|
|
||||||
|
foreach($urls as $url => $path) {
|
||||||
|
|
||||||
|
if(self::$static_base_url) Director::setBaseURL(self::$static_base_url);
|
||||||
|
$i++;
|
||||||
|
|
||||||
|
if($url && !is_string($url)) {
|
||||||
|
user_error("Bad url:" . var_export($url,true), E_USER_WARNING);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(StaticPublisher::echo_progress()) {
|
||||||
|
echo " * Publishing page $i/$totalURLs: $url\n";
|
||||||
|
flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
Requirements::clear();
|
||||||
|
|
||||||
|
if($url == "") $url = "/";
|
||||||
|
if(Director::is_relative_url($url)) $url = Director::absoluteURL($url);
|
||||||
|
$response = Director::test(str_replace('+', ' ', $url));
|
||||||
|
|
||||||
|
Requirements::clear();
|
||||||
|
|
||||||
|
singleton('DataObject')->flushCache();
|
||||||
|
|
||||||
|
//skip any responses with a 404 status code. We don't want to turn those into statically cached pages
|
||||||
|
if (!$response || $response->getStatusCode() == '404') continue;
|
||||||
|
|
||||||
|
// Generate file content
|
||||||
|
// PHP file caching will generate a simple script from a template
|
||||||
|
if($this->fileExtension == 'php') {
|
||||||
|
if(is_object($response)) {
|
||||||
|
if($response->getStatusCode() == '301' || $response->getStatusCode() == '302') {
|
||||||
|
$content = $this->generatePHPCacheRedirection($response->getHeader('Location'));
|
||||||
|
} else {
|
||||||
|
$content = $this->generatePHPCacheFile($response->getBody(), HTTP::get_cache_age(), date('Y-m-d H:i:s'));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$content = $this->generatePHPCacheFile($response . '', HTTP::get_cache_age(), date('Y-m-d H:i:s'));
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTML file caching generally just creates a simple file
|
||||||
|
} else {
|
||||||
|
if(is_object($response)) {
|
||||||
|
if($response->getStatusCode() == '301' || $response->getStatusCode() == '302') {
|
||||||
|
$absoluteURL = Director::absoluteURL($response->getHeader('Location'));
|
||||||
|
$content = "<meta http-equiv=\"refresh\" content=\"2; URL=$absoluteURL\">";
|
||||||
|
} else {
|
||||||
|
$content = $response->getBody();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$content = $response . '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$files[] = array(
|
||||||
|
'Content' => $content,
|
||||||
|
'Folder' => dirname($path).'/',
|
||||||
|
'Filename' => basename($path),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add externals
|
||||||
|
/*
|
||||||
|
$externals = $this->externalReferencesFor($content);
|
||||||
|
if($externals) foreach($externals as $external) {
|
||||||
|
// Skip absolute URLs
|
||||||
|
if(preg_match('/^[a-zA-Z]+:\/\//', $external)) continue;
|
||||||
|
// Drop querystring parameters
|
||||||
|
$external = strtok($external, '?');
|
||||||
|
|
||||||
|
if(file_exists("../" . $external)) {
|
||||||
|
// Break into folder and filename
|
||||||
|
if(preg_match('/^(.*\/)([^\/]+)$/', $external, $matches)) {
|
||||||
|
$files[$external] = array(
|
||||||
|
"Copy" => "../$external",
|
||||||
|
"Folder" => $matches[1],
|
||||||
|
"Filename" => $matches[2],
|
||||||
|
);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
user_error("Can't parse external: $external", E_USER_WARNING);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$missingFiles[$external] = true;
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
if(self::$static_base_url) Director::setBaseURL($currentBaseURL);
|
||||||
|
if($this->fileExtension == 'php') SSViewer::setOption('rewriteHashlinks', true);
|
||||||
|
|
||||||
|
$base = BASE_PATH . "/$this->destFolder";
|
||||||
|
|
||||||
|
foreach($files as $file) {
|
||||||
|
Filesystem::makeFolder("$base/$file[Folder]");
|
||||||
|
|
||||||
|
if(isset($file['Content'])) {
|
||||||
|
$fh = fopen("$base/$file[Folder]$file[Filename]", "w");
|
||||||
|
fwrite($fh, $file['Content']);
|
||||||
|
fclose($fh);
|
||||||
|
} else if(isset($file['Copy'])) {
|
||||||
|
copy($file['Copy'], "$base/$file[Folder]$file[Filename]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the templated content for a PHP script that can serve up the
|
||||||
|
* given piece of content with the given age and expiry.
|
||||||
|
*
|
||||||
|
* @param string
|
||||||
|
* @param int
|
||||||
|
* @param string
|
||||||
|
*/
|
||||||
|
protected function generatePHPCacheFile($content, $age, $lastModified) {
|
||||||
|
$template = file_get_contents(STATIC_MODULE_DIR . '/code/CachedPHPPage.tmpl');
|
||||||
|
|
||||||
|
return str_replace(
|
||||||
|
array('**MAX_AGE**', '**LAST_MODIFIED**', '**CONTENT**'),
|
||||||
|
array((int)$age, $lastModified, $content),
|
||||||
|
$template);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the templated content for a PHP script that can serve up a 301
|
||||||
|
* redirect to the given destination.
|
||||||
|
*
|
||||||
|
* @param string
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
protected function generatePHPCacheRedirection($destination) {
|
||||||
|
$template = file_get_contents(STATIC_MODULE_DIR . '/code/CachedPHPRedirection.tmpl');
|
||||||
|
|
||||||
|
return str_replace(
|
||||||
|
array('**DESTINATION**'),
|
||||||
|
array($destination),
|
||||||
|
$template
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getDestDir() {
|
||||||
|
return BASE_PATH . '/' . $this->destFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return an array of all the existing static cache files, as a map of
|
||||||
|
* URL => file. Only returns cache files that will actually map to a URL,
|
||||||
|
* based on urlsToPaths.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getExistingStaticCacheFiles() {
|
||||||
|
$cacheDir = BASE_PATH . '/' . $this->destFolder;
|
||||||
|
|
||||||
|
$urlMapper = array_flip(
|
||||||
|
$this->urlsToPaths($this->owner->allPagesToCache())
|
||||||
|
);
|
||||||
|
|
||||||
|
$output = array();
|
||||||
|
|
||||||
|
// Glob each dir, then glob each one of those
|
||||||
|
foreach(glob("$cacheDir/*", GLOB_ONLYDIR) as $cacheDir) {
|
||||||
|
foreach(glob($cacheDir.'/*') as $cacheFile) {
|
||||||
|
$mapKey = str_replace(BASE_PATH . "/cache/","",$cacheFile);
|
||||||
|
if(isset($urlMapper[$mapKey])) {
|
||||||
|
$url = $urlMapper[$mapKey];
|
||||||
|
$output[$url] = $cacheFile;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $output;
|
||||||
|
}
|
||||||
|
}
|
63
code/extensions/RsyncMultiHostPublisher.php
Normal file
63
code/extensions/RsyncMultiHostPublisher.php
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This static publisher can be used to deploy static content to multiple hosts, by generating the cache files locally and then rsyncing then to
|
||||||
|
* each destination box. This can be used to set up a load-balanced collection of static servers.
|
||||||
|
*
|
||||||
|
* @see http://doc.silverstripe.com/doku.php?id=staticpublisher
|
||||||
|
*
|
||||||
|
* @package cms
|
||||||
|
* @subpackage publishers
|
||||||
|
*/
|
||||||
|
class RsyncMultiHostPublisher extends FilesystemPublisher {
|
||||||
|
/**
|
||||||
|
* Array of rsync targets to publish to. These can either be local file names, or scp-style targets, in the form "user@server:path"
|
||||||
|
*/
|
||||||
|
protected static $targets = array();
|
||||||
|
|
||||||
|
protected static $excluded_folders = array();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the targets to publish to.
|
||||||
|
* If target is an scp-style remote path, no password is accepted - we assume key-based authentication to be set up on the application server
|
||||||
|
* initiating the publication.
|
||||||
|
*
|
||||||
|
* @param $targets An array of targets to publish to. These can either be local file names, or scp-style targets, in the form "user@server:path"
|
||||||
|
*/
|
||||||
|
static function set_targets($targets) {
|
||||||
|
self::$targets = $targets;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify folders to exclude from the rsync
|
||||||
|
* For example, you could exclude assets.
|
||||||
|
*/
|
||||||
|
static function set_excluded_folders($folders) {
|
||||||
|
self::$excluded_folders = $folders;
|
||||||
|
}
|
||||||
|
|
||||||
|
function publishPages($urls) {
|
||||||
|
parent::publishPages($urls);
|
||||||
|
$base = Director::baseFolder();
|
||||||
|
$framework = FRAMEWORK_DIR;
|
||||||
|
|
||||||
|
// Get variable that can turn off the rsync component of publication
|
||||||
|
if(isset($_GET['norsync']) && $_GET['norsync']) return;
|
||||||
|
|
||||||
|
$extraArg = "";
|
||||||
|
if(self::$excluded_folders) foreach(self::$excluded_folders as $folder) {
|
||||||
|
$extraArg .= " --exclude " . escapeshellarg($folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach(self::$targets as $target) {
|
||||||
|
// Transfer non-PHP content from everything to the target; that will ensure that we have all the JS/CSS/etc
|
||||||
|
$rsyncOutput = `cd $base; rsync -av -e ssh --exclude /.htaccess --exclude /web.config --exclude '*.php' --exclude '*.svn' --exclude '*.git' --exclude '*~' $extraArg --delete . $target`;
|
||||||
|
// Then transfer "safe" PHP from the cache/ directory
|
||||||
|
$rsyncOutput .= `cd $base; rsync -av -e ssh --exclude '*.svn' --exclude '*~' $extraArg --delete cache $target`;
|
||||||
|
// Transfer framework/static-main.php to the target
|
||||||
|
$rsyncOutput .= `cd $base; rsync -av -e ssh --delete $framework/static-main.php $target/$framework`;
|
||||||
|
if(StaticPublisher::echo_progress()) echo $rsyncOutput;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
157
code/extensions/StaticPublisher.php
Normal file
157
code/extensions/StaticPublisher.php
Normal file
@ -0,0 +1,157 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @package cms
|
||||||
|
* @subpackage publishers
|
||||||
|
*/
|
||||||
|
abstract class StaticPublisher extends DataExtension {
|
||||||
|
/**
|
||||||
|
* Defines whether to output information about publishing or not. By
|
||||||
|
* default, this is off, and should be turned on when you want debugging
|
||||||
|
* (for example, in a cron task)
|
||||||
|
*/
|
||||||
|
static $echo_progress = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Realtime static publishing... the second a page
|
||||||
|
* is saved, it is written to the cache
|
||||||
|
*/
|
||||||
|
static $disable_realtime = false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is the current static publishing theme, which can be set at any point
|
||||||
|
* If it's not set, then the last non-null theme, set via SSViewer::set_theme() is used
|
||||||
|
* The obvious place to set this is in _config.php
|
||||||
|
*/
|
||||||
|
static $static_publisher_theme=false;
|
||||||
|
|
||||||
|
abstract function publishPages($pages);
|
||||||
|
abstract function unpublishPages($pages);
|
||||||
|
|
||||||
|
static function set_static_publisher_theme($theme){
|
||||||
|
self::$static_publisher_theme=$theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function static_publisher_theme(){
|
||||||
|
return self::$static_publisher_theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function echo_progress() {
|
||||||
|
return (boolean)self::$echo_progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Either turns on (boolean true) or off (boolean false) the progress indicators.
|
||||||
|
* @see StaticPublisher::$echo_progress
|
||||||
|
*/
|
||||||
|
static function set_echo_progress($progress) {
|
||||||
|
self::$echo_progress = (boolean)$progress;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called after a page is published.
|
||||||
|
*/
|
||||||
|
function onAfterPublish($original) {
|
||||||
|
$this->republish($original);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called after link assets have been renamed, and the live site has been updated, without
|
||||||
|
* an actual publish event.
|
||||||
|
*
|
||||||
|
* Only called if the published content exists and has been modified.
|
||||||
|
*/
|
||||||
|
function onRenameLinkedAsset($original) {
|
||||||
|
$this->republish($original);
|
||||||
|
}
|
||||||
|
|
||||||
|
function republish($original) {
|
||||||
|
if (self::$disable_realtime) return;
|
||||||
|
|
||||||
|
$urls = array();
|
||||||
|
|
||||||
|
if($this->owner->hasMethod('pagesAffectedByChanges')) {
|
||||||
|
$urls = $this->owner->pagesAffectedByChanges($original);
|
||||||
|
} else {
|
||||||
|
$pages = Versioned::get_by_stage('SiteTree', 'Live', '', '', '', 10);
|
||||||
|
if($pages) {
|
||||||
|
foreach($pages as $page) {
|
||||||
|
$urls[] = $page->AbsoluteLink();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: Similiar to RebuildStaticCacheTask->rebuildCache()
|
||||||
|
foreach($urls as $i => $url) {
|
||||||
|
if(!is_string($url)) {
|
||||||
|
user_error("Bad URL: " . var_export($url, true), E_USER_WARNING);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove leading slashes from all URLs (apart from the homepage)
|
||||||
|
if(substr($url,-1) == '/' && $url != '/') $url = substr($url,0,-1);
|
||||||
|
|
||||||
|
$urls[$i] = $url;
|
||||||
|
}
|
||||||
|
|
||||||
|
$urls = array_unique($urls);
|
||||||
|
|
||||||
|
$this->publishPages($urls);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* On after unpublish, get changes and hook into underlying
|
||||||
|
* functionality
|
||||||
|
*/
|
||||||
|
function onAfterUnpublish($page) {
|
||||||
|
if (self::$disable_realtime) return;
|
||||||
|
|
||||||
|
// Get the affected URLs
|
||||||
|
if($this->owner->hasMethod('pagesAffectedByUnpublishing')) {
|
||||||
|
$urls = $this->owner->pagesAffectedByUnpublishing();
|
||||||
|
$urls = array_unique($urls);
|
||||||
|
} else {
|
||||||
|
$urls = array($this->owner->AbsoluteLink());
|
||||||
|
}
|
||||||
|
|
||||||
|
$legalPages = singleton('Page')->allPagesToCache();
|
||||||
|
|
||||||
|
$urlsToRepublish = array_intersect($urls, $legalPages);
|
||||||
|
$urlsToUnpublish = array_diff($urls, $legalPages);
|
||||||
|
|
||||||
|
$this->unpublishPages($urlsToUnpublish);
|
||||||
|
$this->publishPages($urlsToRepublish);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all external references to CSS, JS,
|
||||||
|
*/
|
||||||
|
function externalReferencesFor($content) {
|
||||||
|
$CLI_content = escapeshellarg($content);
|
||||||
|
$tidy = `echo $CLI_content | tidy -numeric -asxhtml`;
|
||||||
|
$tidy = preg_replace('/xmlns="[^"]+"/','', $tidy);
|
||||||
|
$xContent = new SimpleXMLElement($tidy);
|
||||||
|
//Debug::message($xContent->asXML());
|
||||||
|
|
||||||
|
$xlinks = array(
|
||||||
|
"//link[@rel='stylesheet']/@href" => false,
|
||||||
|
"//script/@src" => false,
|
||||||
|
"//img/@src" => false,
|
||||||
|
"//a/@href" => true,
|
||||||
|
);
|
||||||
|
|
||||||
|
$urls = array();
|
||||||
|
foreach($xlinks as $xlink => $assetsOnly) {
|
||||||
|
$matches = $xContent->xpath($xlink);
|
||||||
|
if($matches) foreach($matches as $item) {
|
||||||
|
$url = $item . '';
|
||||||
|
if($assetsOnly && substr($url,0,7) != ASSETS_DIR . '/') continue;
|
||||||
|
|
||||||
|
$urls[] = $url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $urls;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
118
code/static-main.php
Normal file
118
code/static-main.php
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is designed to be the new 'server' of sites using StaticPublisher.
|
||||||
|
* to use this, you need to modify your .htaccess to point all requests to
|
||||||
|
* static-main.php, rather than main.php. This file also allows for using
|
||||||
|
* static publisher with the subsites module.
|
||||||
|
*
|
||||||
|
* If you are using StaticPublisher+Subsites, set the following in _config.php:
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
* FilesystemPublisher::$domain_based_caching = true;
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* and added main site host mapping in subsites/host-map.php after everytime
|
||||||
|
* a new subsite is created or modified.
|
||||||
|
*
|
||||||
|
* If you are not using subsites, the host-map.php file will not exist (it is
|
||||||
|
* automatically generated by the Subsites module) and the cache will default
|
||||||
|
* to no subdirectory.
|
||||||
|
*
|
||||||
|
* @package static
|
||||||
|
*/
|
||||||
|
|
||||||
|
$cacheEnabled = true;
|
||||||
|
$cacheDebug = false;
|
||||||
|
$cacheBaseDir = '../cache/'; // Should point to the same folder as FilesystemPublisher->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(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 "<h1>File was cached</h1>";
|
||||||
|
} elseif (file_exists($cacheBaseDir . $cacheDir . $file . '.php')) {
|
||||||
|
header('X-SilverStripe-Cache: hit at '.@date('r'));
|
||||||
|
include_once $cacheBaseDir . $cacheDir . $file . '.php';
|
||||||
|
if ($cacheDebug) echo "<h1>File was cached</h1>";
|
||||||
|
} 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 "<h1>File was NOT cached</h1>";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Fall back to dynamic generation via normal routing if caching has been explicitly disabled
|
||||||
|
include 'main.php';
|
||||||
|
}
|
||||||
|
|
26
tasks/StaticExporterTask.php
Normal file
26
tasks/StaticExporterTask.php
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* @package static
|
||||||
|
*/
|
||||||
|
class StaticExporterTask extends BuildTask {
|
||||||
|
|
||||||
|
public function run($request) {
|
||||||
|
$export = new StaticExporter();
|
||||||
|
|
||||||
|
$url = $request->getVar('baseurl');
|
||||||
|
$sym = $request->getVar('symlink');
|
||||||
|
$quiet = $request->getVar('quiet');
|
||||||
|
$folder = $request->getVar('path');
|
||||||
|
|
||||||
|
if(!$folder) $folder = TEMP_FOLDER . '/static-export';
|
||||||
|
|
||||||
|
$url = ($url) ? $url : Director::baseURL();
|
||||||
|
$symlink = ($sym != "false");
|
||||||
|
$quiet = ($quiet) ? $quiet : false;
|
||||||
|
|
||||||
|
if(!$quiet) printf("Exporting website with %s base URL... %s", $url, PHP_EOL);
|
||||||
|
$path = $export->doExport($url, $folder, $symlink, $quiet);
|
||||||
|
|
||||||
|
if(!$quiet) printf("Completed. Website exported to %s. %s", $path, PHP_EOL);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user