diff --git a/code/controllers/StaticExporter.php b/code/controllers/StaticExporter.php index d8d399f..e83c3eb 100644 --- a/code/controllers/StaticExporter.php +++ b/code/controllers/StaticExporter.php @@ -14,250 +14,260 @@ * * @package staticpublisher */ -class StaticExporter extends Controller { +class StaticExporter extends Controller +{ - /** - * @config - * - * @var array $export_objects - */ - private static $export_objects = array(); + /** + * @config + * + * @var array $export_objects + */ + private static $export_objects = array(); - /** - * @config - * - * @var bool - */ - private static $disable_sitetree_export = false; + /** + * @config + * + * @var bool + */ + private static $disable_sitetree_export = false; - /** - * @var array - */ - private static $allowed_actions = array( - 'index', - 'export', - 'StaticExportForm' - ); + /** + * @var array + */ + private static $allowed_actions = array( + 'index', + 'export', + 'StaticExportForm' + ); - /** - * - */ - public function __construct() { - parent::__construct(); + /** + * + */ + public function __construct() + { + parent::__construct(); - if(class_exists('SiteTree')) { - if(!$this->config()->get('disable_sitetree_export')) { - $objs = $this->config()->export_objects; - if (!is_array($objs)) { - $objs = array($objs); - } - - if(!in_array('SiteTree', $objs)) { - $objs[] = "SiteTree"; - } - - $this->config()->export_objects = $objs; - } - } - } + if (class_exists('SiteTree')) { + if (!$this->config()->get('disable_sitetree_export')) { + $objs = $this->config()->export_objects; + if (!is_array($objs)) { + $objs = array($objs); + } + + if (!in_array('SiteTree', $objs)) { + $objs[] = "SiteTree"; + } + + $this->config()->export_objects = $objs; + } + } + } - /** - * - */ - public function init() { - parent::init(); - - $canAccess = (Director::isDev() || Director::is_cli()); + /** + * + */ + public function init() + { + parent::init(); + + $canAccess = (Director::isDev() || Director::is_cli()); - if(!Permission::check("ADMIN") && !$canAccess) { - return Security::permissionFailure($this); - } - } - - /** - * @param string $action - * - * @return string - */ - public function Link($action = null) { - return "dev/staticexporter/$action"; - } + if (!Permission::check("ADMIN") && !$canAccess) { + return Security::permissionFailure($this); + } + } + + /** + * @param string $action + * + * @return string + */ + public function Link($action = null) + { + return "dev/staticexporter/$action"; + } - /** - * @param string $action - * - * @return string - */ - public function AbsoluteLink($action = null) { - return Director::absoluteURL($this->Link($action)); - } - - /** - * @return array - */ - public function index() { - return array( - 'Title' => _t('StaticExporter.NAME','Static exporter'), - 'Form' => $this->StaticExportForm()->forTemplate() - ); - } - - /** - * @return Form - */ - 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')) - )); + /** + * @param string $action + * + * @return string + */ + public function AbsoluteLink($action = null) + { + return Director::absoluteURL($this->Link($action)); + } + + /** + * @return array + */ + public function index() + { + return array( + 'Title' => _t('StaticExporter.NAME', 'Static exporter'), + 'Form' => $this->StaticExportForm()->forTemplate() + ); + } + + /** + * @return Form + */ + 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; - } + return $form; + } - public function export() { - if(isset($_REQUEST['baseurl'])) { - $base = $_REQUEST['baseurl']; + public function export() + { + if (isset($_REQUEST['baseurl'])) { + $base = $_REQUEST['baseurl']; - if(substr($base,-1) != '/') $base .= '/'; + if (substr($base, -1) != '/') { + $base .= '/'; + } - Config::inst()->update('Director', 'alternate_base_url', $base); - } - else { - $base = Director::baseURL(); - } + Config::inst()->update('Director', 'alternate_base_url', $base); + } else { + $base = Director::baseURL(); + } - $folder = TEMP_FOLDER . '/static-export'; - $project = project(); + $folder = TEMP_FOLDER . '/static-export'; + $project = project(); - $exported = $this->doExport($base, $folder .'/'. $project, false); + $exported = $this->doExport($base, $folder .'/'. $project, false); - `cd $folder; tar -czhf $project-export.tar.gz $project`; + `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(); - } + $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); + /** + * 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); - Config::inst()->update('Director', 'alternate_base_url', $base); + Config::inst()->update('Director', 'alternate_base_url', $base); - if(is_dir($folder)) { - Filesystem::removeFolder($folder); - } + if (is_dir($folder)) { + Filesystem::removeFolder($folder); + } - Filesystem::makeFolder($folder); - - // symlink or copy /assets - $f1 = ASSETS_PATH; - $f2 = Director::baseFolder() . '/' . project(); + 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`; - } + 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 - $urls = $this->getExportUrls(); + // iterate through items we need to export + $urls = $this->getExportUrls(); - if($urls) { - $total = count($urls); - $i = 1; + if ($urls) { + $total = count($urls); + $i = 1; - foreach($urls as $url) { - $subfolder = "$folder/" . trim($url, '/'); - $contentfile = "$folder/" . trim($url, '/') . '/index.html'; - - // Make the folder - if(!file_exists($subfolder)) { - Filesystem::makeFolder($subfolder); - } - - // Run the page - Requirements::clear(); - DataObject::flush_and_destroy_cache(); + foreach ($urls as $url) { + $subfolder = "$folder/" . trim($url, '/'); + $contentfile = "$folder/" . trim($url, '/') . '/index.html'; + + // Make the folder + if (!file_exists($subfolder)) { + Filesystem::makeFolder($subfolder); + } + + // Run the page + Requirements::clear(); + DataObject::flush_and_destroy_cache(); - $response = Director::test($url); + $response = Director::test($url); - // Write to file - if($fh = fopen($contentfile, 'w')) { - if(!$quiet) { - printf("-- (%s/%s) Outputting page (%s)%s", - $i, - $total, - $url, - PHP_EOL - ); - } + // Write to file + if ($fh = fopen($contentfile, 'w')) { + if (!$quiet) { + printf("-- (%s/%s) Outputting page (%s)%s", + $i, + $total, + $url, + PHP_EOL + ); + } - fwrite($fh, $response->getBody()); - fclose($fh); - } + fwrite($fh, $response->getBody()); + fclose($fh); + } - $i++; - } - } + $i++; + } + } - return $folder; - } + return $folder; + } - /** - * Return an array of urls to publish - * - * @return array - */ - public function getExportUrls() { - $classes = $this->config()->get('export_objects'); - $urls = array(); + /** + * Return an array of urls to publish + * + * @return array + */ + public function getExportUrls() + { + $classes = $this->config()->get('export_objects'); + $urls = array(); - foreach($classes as $obj) { - if (!class_exists($obj)) { - continue; - } - foreach ($obj::get() as $objInstance) { - $link = $objInstance->Link(); - $urls[$link] = $link; - } - } + foreach ($classes as $obj) { + if (!class_exists($obj)) { + continue; + } + foreach ($obj::get() as $objInstance) { + $link = $objInstance->Link(); + $urls[$link] = $link; + } + } - $this->extend('alterExportUrls', $urls); + $this->extend('alterExportUrls', $urls); - // older api, keep around to ensure backwards compatibility - $objs = new ArrayList(); - $this->extend('alterObjectsToExport', $objs); + // older api, keep around to ensure backwards compatibility + $objs = new ArrayList(); + $this->extend('alterObjectsToExport', $objs); - if($objs) { - foreach($objs as $obj) { - $link = $obj->Link; + if ($objs) { + foreach ($objs as $obj) { + $link = $obj->Link; - $urls[$link] = $link; - } - } + $urls[$link] = $link; + } + } - return $urls; - } + return $urls; + } } diff --git a/code/extensions/FilesystemPublisher.php b/code/extensions/FilesystemPublisher.php index 0d8504b..25d293c 100644 --- a/code/extensions/FilesystemPublisher.php +++ b/code/extensions/FilesystemPublisher.php @@ -3,409 +3,432 @@ /** * @package staticpublisher */ -class FilesystemPublisher extends StaticPublisher { +class FilesystemPublisher extends StaticPublisher +{ - /** - * @var string - */ - protected $destFolder = 'cache'; - - /** - * @var string - */ - protected $fileExtension = 'html'; - - /** - * @var string - * - * @config - */ - private static $static_base_url = null; - - /** - * @config - * - * @var Boolean Use domain based cacheing (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()}. - */ - private 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. - * - * @deprecated 3.2 Use the "FilesystemPublisher.static_base_url" config setting instead - */ - static public function set_static_base_url($url) { - Deprecation::notice('3.2', 'Use the "FilesystemPublisher.static_base_url" config setting instead'); + /** + * @var string + */ + protected $destFolder = 'cache'; + + /** + * @var string + */ + protected $fileExtension = 'html'; + + /** + * @var string + * + * @config + */ + private static $static_base_url = null; + + /** + * @config + * + * @var Boolean Use domain based cacheing (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()}. + */ + private 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. + * + * @deprecated 3.2 Use the "FilesystemPublisher.static_base_url" config setting instead + */ + public static function set_static_base_url($url) + { + Deprecation::notice('3.2', 'Use the "FilesystemPublisher.static_base_url" config setting instead'); - Config::inst()->update('FilesystemPublisher', '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; - - if($fileExtension) { - $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 - * framework/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 (assuming your webroot is in a subfolder) - * - 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 filesystem 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 (Config::inst()->get('FilesystemPublisher', 'domain_based_caching')) { - if (!$urlParts) continue; // seriously malformed url here... - $filename = $urlParts['host'] . '/' . $filename; - } - - $mappedUrls[$url] = ((dirname($filename) == '/') ? '' : (dirname($filename).'/')).basename($filename); - } - - return $mappedUrls; - } - - /** - * @param array $urls - */ - 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); - } - } - } - - /** - * Uses {@link Director::test()} to perform in-memory HTTP requests - * on the passed-in URLs. - * - * @param array $urls Relative URLs - * @return array Result, keyed by URL. Keys: - * - "statuscode": The HTTP status code - * - "redirect": A redirect location (if applicable) - * - "path": The filesystem path where the cache has been written - */ - public function publishPages($urls) { - // Save current stage and temporarily force it to Live - $oldStage = Versioned::current_stage(); - Versioned::reading_stage("Live"); + Config::inst()->update('FilesystemPublisher', '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); + } - $result = array(); + $this->destFolder = $destFolder; - //nest the config so we can make changes to the config and revert easily - Config::nest(); + if ($fileExtension) { + $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 + * framework/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 (assuming your webroot is in a subfolder) + * - 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 filesystem paths (relative to {@link $destFolder}). + */ + public function urlsToPaths($urls) + { + $mappedUrls = array(); + + foreach ($urls as $url) { - // 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. - $customTheme = Config::inst()->get('StaticPublisher', 'static_publisher_theme'); + // 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; + } - if($customTheme) { - Config::inst()->update('SSViewer', 'theme', $customTheme); - } + // Normalize URLs + $urlSegment = trim($urlSegment, '/'); - // Ensure that the theme that is set gets used. - Config::inst()->update('SSViewer', 'theme_enabled', true); - - $staticBaseUrl = Config::inst()->get('FilesystemPublisher', 'static_base_url'); - - if($staticBaseUrl) { - Config::inst()->update('Director', 'alternate_base_url', $staticBaseUrl); - } - - if($this->fileExtension == 'php') { - Config::inst()->update('SSViewer', 'rewrite_hash_links', 'php'); - } - - if(Config::inst()->get('StaticPublisher', 'echo_progress')) { - echo $this->class.": Publishing to " . $staticBaseUrl . "\n"; - } - - $files = array(); - $i = 0; - $totalURLs = sizeof($urls); + $filename = $urlSegment ? "$urlSegment.$this->fileExtension" : "index.$this->fileExtension"; - foreach($urls as $url => $path) { - $origUrl = $url; - $result[$origUrl] = array( - 'statuscode' => null, - 'redirect' => null, - 'path' => null - ); + if (Config::inst()->get('FilesystemPublisher', 'domain_based_caching')) { + if (!$urlParts) { + continue; + } // seriously malformed url here... + $filename = $urlParts['host'] . '/' . $filename; + } + + $mappedUrls[$url] = ((dirname($filename) == '/') ? '' : (dirname($filename).'/')).basename($filename); + } - $i++; + return $mappedUrls; + } + + /** + * @param array $urls + */ + 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); + } + } + } + + /** + * Uses {@link Director::test()} to perform in-memory HTTP requests + * on the passed-in URLs. + * + * @param array $urls Relative URLs + * @return array Result, keyed by URL. Keys: + * - "statuscode": The HTTP status code + * - "redirect": A redirect location (if applicable) + * - "path": The filesystem path where the cache has been written + */ + public function publishPages($urls) + { + // Save current stage and temporarily force it to Live + $oldStage = Versioned::current_stage(); + Versioned::reading_stage("Live"); + + $result = array(); - if($url && !is_string($url)) { - user_error("Bad url:" . var_export($url,true), E_USER_WARNING); - continue; - } - - if(Config::inst()->get('StaticPublisher', 'echo_progress')) { - echo " * Publishing page $i/$totalURLs: $url\n"; - flush(); - } + //nest the config so we can make changes to the config and revert easily + Config::nest(); - Requirements::clear(); - - if($url == "") $url = "/"; - if(Director::is_relative_url($url)) $url = Director::absoluteURL($url); - $response = Director::test(str_replace('+', ' ', $url)); + // 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. + $customTheme = Config::inst()->get('StaticPublisher', 'static_publisher_theme'); - if (!$response) continue; + if ($customTheme) { + Config::inst()->update('SSViewer', 'theme', $customTheme); + } - if($response) { - $result[$origUrl]['statuscode'] = $response->getStatusCode(); - } - Requirements::clear(); - - singleton('DataObject')->flushCache(); + // Ensure that the theme that is set gets used. + Config::inst()->update('SSViewer', 'theme_enabled', true); + + $staticBaseUrl = Config::inst()->get('FilesystemPublisher', 'static_base_url'); + + if ($staticBaseUrl) { + Config::inst()->update('Director', 'alternate_base_url', $staticBaseUrl); + } + + if ($this->fileExtension == 'php') { + Config::inst()->update('SSViewer', 'rewrite_hash_links', 'php'); + } + + if (Config::inst()->get('StaticPublisher', 'echo_progress')) { + echo $this->class.": Publishing to " . $staticBaseUrl . "\n"; + } + + $files = array(); + $i = 0; + $totalURLs = sizeof($urls); - // Check for ErrorPages generating output - we want to handle this in a special way below. - $isErrorPage = false; - $pageObject = null; - if ($response && is_object($response) && ((int)$response->getStatusCode())>=400) { - $pageObject = SiteTree::get_by_link($url); - if ($pageObject && $pageObject instanceof ErrorPage) $isErrorPage = true; - } + foreach ($urls as $url => $path) { + $origUrl = $url; + $result[$origUrl] = array( + 'statuscode' => null, + 'redirect' => null, + 'path' => null + ); - // Skip any responses with a 404 status code unless it's the ErrorPage itself. - if (!$isErrorPage && is_object($response) && $response->getStatusCode()=='404') continue; + $i++; - // 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'), $response->getHeader('Content-Type')); - } - } else { - $content = $this->generatePHPCacheFile($response . '', HTTP::get_cache_age(), date('Y-m-d H:i:s'), $response->getHeader('Content-Type')); - } - - // 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')); - $result[$origUrl]['redirect'] = $response->getHeader('Location'); - $content = ""; - } else { - $content = $response->getBody(); - } - } else { - $content = $response . ''; - } - } - - if(Config::inst()->get('StaticPublisher', 'include_caching_metadata')) { - $content = str_replace( - '', - sprintf("\n\n", implode(" ", $this->getMetadata($url))), - $content - ); - } + if ($url && !is_string($url)) { + user_error("Bad url:" . var_export($url, true), E_USER_WARNING); + continue; + } + + if (Config::inst()->get('StaticPublisher', 'echo_progress')) { + echo " * Publishing page $i/$totalURLs: $url\n"; + flush(); + } - if (!$isErrorPage) { + Requirements::clear(); + + if ($url == "") { + $url = "/"; + } + if (Director::is_relative_url($url)) { + $url = Director::absoluteURL($url); + } + $response = Director::test(str_replace('+', ' ', $url)); - $files[$origUrl] = array( - 'Content' => $content, - 'Folder' => dirname($path).'/', - 'Filename' => basename($path), - ); + if (!$response) { + continue; + } - } else { + if ($response) { + $result[$origUrl]['statuscode'] = $response->getStatusCode(); + } + Requirements::clear(); + + singleton('DataObject')->flushCache(); - // Generate a static version of the error page with a standardised name, so they can be plugged - // into catch-all webserver statements such as Apache's ErrorDocument. - $code = (int)$response->getStatusCode(); - $files[$origUrl] = array( - 'Content' => $content, - 'Folder' => dirname($path).'/', - 'Filename' => "error-$code.html", - ); + // Check for ErrorPages generating output - we want to handle this in a special way below. + $isErrorPage = false; + $pageObject = null; + if ($response && is_object($response) && ((int)$response->getStatusCode())>=400) { + $pageObject = SiteTree::get_by_link($url); + if ($pageObject && $pageObject instanceof ErrorPage) { + $isErrorPage = true; + } + } - } - } + // Skip any responses with a 404 status code unless it's the ErrorPage itself. + if (!$isErrorPage && is_object($response) && $response->getStatusCode()=='404') { + continue; + } - //return config to its previous state - Config::unnest(); + // 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'), $response->getHeader('Content-Type')); + } + } else { + $content = $this->generatePHPCacheFile($response . '', HTTP::get_cache_age(), date('Y-m-d H:i:s'), $response->getHeader('Content-Type')); + } + + // 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')); + $result[$origUrl]['redirect'] = $response->getHeader('Location'); + $content = ""; + } else { + $content = $response->getBody(); + } + } else { + $content = $response . ''; + } + } + + if (Config::inst()->get('StaticPublisher', 'include_caching_metadata')) { + $content = str_replace( + '', + sprintf("\n\n", implode(" ", $this->getMetadata($url))), + $content + ); + } - $base = BASE_PATH . "/$this->destFolder"; - - foreach($files as $origUrl => $file) { - Filesystem::makeFolder("$base/$file[Folder]"); - - $path = "$base/$file[Folder]$file[Filename]"; - $result[$origUrl]['path'] = $path; - - if(isset($file['Content'])) { - $fh = fopen($path, "w"); - fwrite($fh, $file['Content']); - fclose($fh); - } else if(isset($file['Copy'])) { - copy($file['Copy'], $path); - } - } + if (!$isErrorPage) { + $files[$origUrl] = array( + 'Content' => $content, + 'Folder' => dirname($path).'/', + 'Filename' => basename($path), + ); + } else { - // Revert the stage back to what it was - Versioned::reading_stage($oldStage); + // Generate a static version of the error page with a standardised name, so they can be plugged + // into catch-all webserver statements such as Apache's ErrorDocument. + $code = (int)$response->getStatusCode(); + $files[$origUrl] = array( + 'Content' => $content, + 'Folder' => dirname($path).'/', + 'Filename' => "error-$code.html", + ); + } + } - return $result; - } - - /** - * 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 $content - * @param string $age - * @param string $lastModified - * @param string $contentType - * - * @return string - */ - protected function generatePHPCacheFile($content, $age, $lastModified, $contentType) { - $template = file_get_contents(STATIC_MODULE_DIR . '/code/CachedPHPPage.tmpl'); - return str_replace( - array('**MAX_AGE**', '**LAST_MODIFIED**', '**CONTENT**', '**CONTENT_TYPE**'), - array((int)$age, $lastModified, $content, $contentType), - $template - ); - } + //return config to its previous state + Config::unnest(); - /** - * Generate the templated content for a PHP script that can serve up a 301 - * redirect to the given destination. - * - * @param string $destination - * - * @return string - */ - protected function generatePHPCacheRedirection($destination) { - $template = file_get_contents(STATIC_MODULE_DIR . '/code/CachedPHPRedirection.tmpl'); + $base = BASE_PATH . "/$this->destFolder"; + + foreach ($files as $origUrl => $file) { + Filesystem::makeFolder("$base/$file[Folder]"); + + $path = "$base/$file[Folder]$file[Filename]"; + $result[$origUrl]['path'] = $path; + + if (isset($file['Content'])) { + $fh = fopen($path, "w"); + fwrite($fh, $file['Content']); + fclose($fh); + } elseif (isset($file['Copy'])) { + copy($file['Copy'], $path); + } + } - 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; + // Revert the stage back to what it was + Versioned::reading_stage($oldStage); - $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; - } + return $result; + } + + /** + * 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 $content + * @param string $age + * @param string $lastModified + * @param string $contentType + * + * @return string + */ + protected function generatePHPCacheFile($content, $age, $lastModified, $contentType) + { + $template = file_get_contents(STATIC_MODULE_DIR . '/code/CachedPHPPage.tmpl'); + return str_replace( + array('**MAX_AGE**', '**LAST_MODIFIED**', '**CONTENT**', '**CONTENT_TYPE**'), + array((int)$age, $lastModified, $content, $contentType), + $template + ); + } + + /** + * Generate the templated content for a PHP script that can serve up a 301 + * redirect to the given destination. + * + * @param string $destination + * + * @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; + } } diff --git a/code/extensions/RsyncMultiHostPublisher.php b/code/extensions/RsyncMultiHostPublisher.php index 46ec740..b0f32c3 100644 --- a/code/extensions/RsyncMultiHostPublisher.php +++ b/code/extensions/RsyncMultiHostPublisher.php @@ -8,80 +8,88 @@ * * @package staticpublisher */ -class RsyncMultiHostPublisher extends FilesystemPublisher { +class RsyncMultiHostPublisher extends FilesystemPublisher +{ - /** - * @config - * - * Array of rsync targets to publish to. These can either be local - * file names, or scp-style targets, in the form "user@server:path" - * - * @var array - */ - private static $targets = array(); - - /** - * @config - * - * @var array - */ - private 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. - * - * @deprecated 3.2 Use the "RsyncMultiHostPublisher.targets" config setting instead - * - * @param $targets An array of targets to publish to. - */ - public static function set_targets($targets) { - Deprecation::notice('3.2', 'Use the "RsyncMultiHostPublisher.targets" config setting instead'); + /** + * @config + * + * Array of rsync targets to publish to. These can either be local + * file names, or scp-style targets, in the form "user@server:path" + * + * @var array + */ + private static $targets = array(); + + /** + * @config + * + * @var array + */ + private 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. + * + * @deprecated 3.2 Use the "RsyncMultiHostPublisher.targets" config setting instead + * + * @param $targets An array of targets to publish to. + */ + public static function set_targets($targets) + { + Deprecation::notice('3.2', 'Use the "RsyncMultiHostPublisher.targets" config setting instead'); - Config::inst()->update('RsyncMultiHostPublisher', 'targets', $targets); - } - - /** - * Specify folders to exclude from the rsync - * For example, you could exclude assets. - * - * @deprecated 3.2 Use the "RsyncMultiHostPublisher.excluded_folders" config setting instead - */ - public static function set_excluded_folders($folders) { - Deprecation::notice('3.2', 'Use the "RsyncMultiHostPublisher.excluded_folders" config setting instead'); + Config::inst()->update('RsyncMultiHostPublisher', 'targets', $targets); + } + + /** + * Specify folders to exclude from the rsync + * For example, you could exclude assets. + * + * @deprecated 3.2 Use the "RsyncMultiHostPublisher.excluded_folders" config setting instead + */ + public static function set_excluded_folders($folders) + { + Deprecation::notice('3.2', 'Use the "RsyncMultiHostPublisher.excluded_folders" config setting instead'); - Config::inst()->update('RsyncMultiHostPublisher', 'excluded_folders', $folders); - } + Config::inst()->update('RsyncMultiHostPublisher', 'excluded_folders', $folders); + } - public function publishPages($urls) { - parent::publishPages($urls); - $base = Director::baseFolder(); - $framework = FRAMEWORK_DIR; + public 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 = ""; - $excludedFolders = Config::inst()->get('RsyncMultiHostPublisher', 'excluded_folders'); - if($excludedFolders) foreach($excludedFolders as $folder) { - $extraArg .= " --exclude " . escapeshellarg($folder); - } - - $targets = Config::inst()->get('RsyncMultiHostPublisher', 'targets'); - foreach((array)$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(Config::inst()->get('StaticPublisher', 'echo_progress')) { - echo $rsyncOutput; - } - } - } + // Get variable that can turn off the rsync component of publication + if (isset($_GET['norsync']) && $_GET['norsync']) { + return; + } + + $extraArg = ""; + $excludedFolders = Config::inst()->get('RsyncMultiHostPublisher', 'excluded_folders'); + if ($excludedFolders) { + foreach ($excludedFolders as $folder) { + $extraArg .= " --exclude " . escapeshellarg($folder); + } + } + + $targets = Config::inst()->get('RsyncMultiHostPublisher', 'targets'); + foreach ((array)$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 (Config::inst()->get('StaticPublisher', 'echo_progress')) { + echo $rsyncOutput; + } + } + } } diff --git a/code/extensions/StaticPublisher.php b/code/extensions/StaticPublisher.php index c235e1e..50d8209 100644 --- a/code/extensions/StaticPublisher.php +++ b/code/extensions/StaticPublisher.php @@ -2,193 +2,205 @@ /** * @package staticpublisher */ -abstract class StaticPublisher extends DataExtension { +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). - * - * @var boolean - * - * @config - */ - private static $echo_progress = false; - - /** - * Realtime static publishing... the second a page is saved, it is - * written to the cache. - * - * @var boolean - * - * @config - */ - private 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 - * - * @var string - * - * @config - */ - private static $static_publisher_theme = false; - - /** - * @var boolean includes a timestamp at the bottom of the generated HTML - * of each file, which can be useful for debugging issues with stale - * caches etc. - * - * @config - */ - private static $include_caching_metadata = false; + /** + * 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). + * + * @var boolean + * + * @config + */ + private static $echo_progress = false; + + /** + * Realtime static publishing... the second a page is saved, it is + * written to the cache. + * + * @var boolean + * + * @config + */ + private 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 + * + * @var string + * + * @config + */ + private static $static_publisher_theme = false; + + /** + * @var boolean includes a timestamp at the bottom of the generated HTML + * of each file, which can be useful for debugging issues with stale + * caches etc. + * + * @config + */ + private static $include_caching_metadata = false; - /** - * @param array - */ - abstract function publishPages($pages); + /** + * @param array + */ + abstract public function publishPages($pages); - /** - * @param array - */ - abstract function unpublishPages($pages); + /** + * @param array + */ + abstract public function unpublishPages($pages); - /** - * @deprecated - * @param string - */ - public static function set_static_publisher_theme($theme) { - Deprecation::notice('1.0', 'Use the new config system. SSViewer.static_publisher_theme'); + /** + * @deprecated + * @param string + */ + public static function set_static_publisher_theme($theme) + { + Deprecation::notice('1.0', 'Use the new config system. SSViewer.static_publisher_theme'); - Config::inst()->update('StaticPublisher', 'static_publisher_theme', $theme); - } - - /** - * @deprecated - * - * @return string - */ - public static function static_publisher_theme() { - Deprecation::notice('1.0', 'Use the new config system. SSViewer.static_publisher_theme'); + Config::inst()->update('StaticPublisher', 'static_publisher_theme', $theme); + } + + /** + * @deprecated + * + * @return string + */ + public static function static_publisher_theme() + { + Deprecation::notice('1.0', 'Use the new config system. SSViewer.static_publisher_theme'); - return Config::inst()->get('StaticPublisher', 'static_publisher_theme'); - } + return Config::inst()->get('StaticPublisher', 'static_publisher_theme'); + } - /** - * @deprecated - * - * @return boolean - */ - public static function echo_progress() { - Deprecation::notice('1.0', 'Use the new config system. SSViewer.static_publisher_theme'); + /** + * @deprecated + * + * @return boolean + */ + public static function echo_progress() + { + Deprecation::notice('1.0', 'Use the new config system. SSViewer.static_publisher_theme'); - return Config::inst()->get('StaticPublisher', 'echo_progress'); - } - - /** - * @deprecated - * - */ - public static function set_echo_progress($progress) { - Deprecation::notice('1.0', 'Use the new config system. SSViewer.static_publisher_theme'); + return Config::inst()->get('StaticPublisher', 'echo_progress'); + } + + /** + * @deprecated + * + */ + public static function set_echo_progress($progress) + { + Deprecation::notice('1.0', 'Use the new config system. SSViewer.static_publisher_theme'); - Config::inst()->get('StaticPublisher', 'echo_progress', $progress); - } + Config::inst()->get('StaticPublisher', 'echo_progress', $progress); + } - /** - * Called after a page is published. - * - * @param SiteTree - */ - public 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. - */ - public function onRenameLinkedAsset($original) { - $this->republish($original); - } - - public function republish($original) { - if (Config::inst()->get('StaticPublisher', 'disable_realtime')) { - return; - } + /** + * Called after a page is published. + * + * @param SiteTree + */ + public 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. + */ + public function onRenameLinkedAsset($original) + { + $this->republish($original); + } + + public function republish($original) + { + if (Config::inst()->get('StaticPublisher', '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; - } + $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; - } + // 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); - - $legalPages = singleton('Page')->allPagesToCache(); - $urls = array_intersect($urls, $legalPages); + $urls = array_unique($urls); + + $legalPages = singleton('Page')->allPagesToCache(); + $urls = array_intersect($urls, $legalPages); - $this->publishPages($urls); - } - - /** - * Get changes and hook into underlying functionality. - */ - public function onAfterUnpublish($page) { - if (Config::inst()->get('StaticPublisher', '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->publishPages($urls); + } + + /** + * Get changes and hook into underlying functionality. + */ + public function onAfterUnpublish($page) + { + if (Config::inst()->get('StaticPublisher', '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); - } - - /** - * - * @param string $url - * @return array - */ - public function getMetadata($url) { - return array( - 'Cache generated on ' . date('Y-m-d H:i:s T (O)') - ); - } + $this->unpublishPages($urlsToUnpublish); + $this->publishPages($urlsToRepublish); + } + + /** + * + * @param string $url + * @return array + */ + public function getMetadata($url) + { + return array( + 'Cache generated on ' . date('Y-m-d H:i:s T (O)') + ); + } } diff --git a/code/tasks/RebuildStaticCacheTask.php b/code/tasks/RebuildStaticCacheTask.php index e7f0928..29042a4 100644 --- a/code/tasks/RebuildStaticCacheTask.php +++ b/code/tasks/RebuildStaticCacheTask.php @@ -1,139 +1,142 @@ get('RebuildStaticCacheTask', 'quiet')) { - Config::inst()->update('StaticPublisher', 'echo_progress', false); - } else { - Config::inst()->update('StaticPublisher', 'echo_progress', true); - } - - $page = singleton('Page'); + public function run($request) + { + if (Config::inst()->get('RebuildStaticCacheTask', 'quiet')) { + Config::inst()->update('StaticPublisher', 'echo_progress', false); + } else { + Config::inst()->update('StaticPublisher', 'echo_progress', true); + } + + $page = singleton('Page'); - if(!$page->hasMethod('allPagesToCache')) { - user_error( - 'RebuildStaticCacheTask::index(): Please define a method "allPagesToCache()" on your Page class to return all pages affected by a cache refresh.', - E_USER_ERROR - ); - } - - - if(!empty($_GET['urls'])) { - $urls = $_GET['urls']; - } else { - $urls = $page->allPagesToCache(); - } - - $this->rebuildCache($urls, true); - } + if (!$page->hasMethod('allPagesToCache')) { + user_error( + 'RebuildStaticCacheTask::index(): Please define a method "allPagesToCache()" on your Page class to return all pages affected by a cache refresh.', + E_USER_ERROR + ); + } + + + if (!empty($_GET['urls'])) { + $urls = $_GET['urls']; + } else { + $urls = $page->allPagesToCache(); + } + + $this->rebuildCache($urls, true); + } - public function log($message = null) { - $quiet = Config::inst()->get('RebuildStaticCacheTask', 'quiet'); + public function log($message = null) + { + $quiet = Config::inst()->get('RebuildStaticCacheTask', 'quiet'); - if($quiet) { - return; - } + if ($quiet) { + return; + } - echo $message; - } - - /** - * Rebuilds the static cache for the pages passed through via $urls - * - * @param array $urls The URLs of pages to re-fetch and cache. - * @param bool $removeAll Remove all stale cache files (default TRUE). - */ - public function rebuildCache($urls, $removeAll = true) { + echo $message; + } + + /** + * Rebuilds the static cache for the pages passed through via $urls + * + * @param array $urls The URLs of pages to re-fetch and cache. + * @param bool $removeAll Remove all stale cache files (default TRUE). + */ + public function rebuildCache($urls, $removeAll = true) + { + if (!is_array($urls)) { + // $urls must be an array + user_error("Rebuild cache must be passed an array of urls. Make sure your allPagesToCache function returns an array", E_USER_ERROR); + return; + }; + + if (!Director::is_cli()) { + $this->log("
\n");
+            $this->log("Rebuilding cache.\nNOTE: Please ensure that this page ends with 'Done!' - if not, then something may have gone wrong.\n\n");
+        }
 
-		if(!is_array($urls)) {
-			// $urls must be an array	
-			user_error("Rebuild cache must be passed an array of urls. Make sure your allPagesToCache function returns an array", E_USER_ERROR);
-			return;
-		}; 
-		
-		if(!Director::is_cli()) {
-			$this->log("
\n");
-			$this->log("Rebuilding cache.\nNOTE: Please ensure that this page ends with 'Done!' - if not, then something may have gone wrong.\n\n");
-		}
+        $page = singleton('Page');
+        $cacheBaseDir = $page->getDestDir();
+        
+        if (!file_exists($cacheBaseDir)) {
+            Filesystem::makeFolder($cacheBaseDir);
+        }
+        
+        $quiet = Config::inst()->get('RebuildStaticCacheTask', 'quiet');
 
-		$page = singleton('Page');
-		$cacheBaseDir = $page->getDestDir();
-		
-		if(!file_exists($cacheBaseDir)) {
-			Filesystem::makeFolder($cacheBaseDir);
-		}
-		
-		$quiet = Config::inst()->get('RebuildStaticCacheTask', 'quiet');
+        if (!$quiet) {
+            if (file_exists($cacheBaseDir.'/lock') && !isset($_REQUEST['force'])) {
+                die("There already appears to be a publishing queue running. You can skip warning this by adding ?/&force to the URL.");
+            }
+        }
+        
+        touch($cacheBaseDir.'/lock');
+        
+        // Note: Similiar to StaticPublisher->republish()
+        foreach ($urls as $i => $url) {
+            if ($url && !is_string($url)) {
+                user_error("Bad URL: " . var_export($url, true), E_USER_WARNING);
+                continue;
+            }
+            
+            $urls[$i] = $url;
+        }
 
-		if(!$quiet) {
-			if (file_exists($cacheBaseDir.'/lock') && !isset($_REQUEST['force'])) {
-				die("There already appears to be a publishing queue running. You can skip warning this by adding ?/&force to the URL.");
-			}
-		}
-		
-		touch($cacheBaseDir.'/lock');
-		
-		// Note: Similiar to StaticPublisher->republish()
-		foreach($urls as $i => $url) {
-			if($url && !is_string($url)) {
-				user_error("Bad URL: " . var_export($url, true), E_USER_WARNING);
-				continue;
-			}
-			
-			$urls[$i] = $url;
-		}
+        $urls = array_unique($urls);
+        sort($urls);
+        
+        $start = isset($_GET['start']) ? $_GET['start'] : 0;
+        $count = isset($_GET['count']) ? $_GET['count'] : sizeof($urls);
 
-		$urls = array_unique($urls);
-		sort($urls);
-		
-		$start = isset($_GET['start']) ? $_GET['start'] : 0;
-		$count = isset($_GET['count']) ? $_GET['count'] : sizeof($urls);
+        if (($start + $count) > sizeof($urls)) {
+            $count = sizeof($urls) - $start;
+        }
 
-		if(($start + $count) > sizeof($urls)) {
-			$count = sizeof($urls) - $start;
-		}
+        $urls = array_slice($urls, $start, $count);
 
-		$urls = array_slice($urls, $start, $count);
+        $mappedUrls = $page->urlsToPaths($urls);
 
-		$mappedUrls = $page->urlsToPaths($urls);
+        if ($removeAll && !isset($_GET['urls']) && $start == 0 && file_exists("../cache")) {
+            $this->log("Removing stale cache files... \n");
 
-		if($removeAll && !isset($_GET['urls']) && $start == 0 && file_exists("../cache")) {
-			$this->log("Removing stale cache files... \n");
+            if (!$quiet) {
+                flush();
+            }
 
-			if(!$quiet) {
-				flush();
-			}
+            if (Config::inst()->get('FilesystemPublisher', 'domain_based_caching')) {
+                // Glob each dir, then glob each one of those
+                foreach (glob(BASE_PATH . '/cache/*', GLOB_ONLYDIR) as $cacheDir) {
+                    foreach (glob($cacheDir.'/*') as $cacheFile) {
+                        $searchCacheFile = trim(str_replace($cacheBaseDir, '', $cacheFile), '\/');
+                        
+                        if (!in_array($searchCacheFile, $mappedUrls)) {
+                            $this->log(" * Deleting $cacheFile\n");
+                            
+                            @unlink($cacheFile);
+                        }
+                    }
+                }
+            }
+            
+            $this->log("done.\n\n");
+        }
 
-			if (Config::inst()->get('FilesystemPublisher', 'domain_based_caching')) {
-				// Glob each dir, then glob each one of those
-				foreach(glob(BASE_PATH . '/cache/*', GLOB_ONLYDIR) as $cacheDir) {
-					foreach(glob($cacheDir.'/*') as $cacheFile) {
-						$searchCacheFile = trim(str_replace($cacheBaseDir, '', $cacheFile), '\/');
-						
-						if (!in_array($searchCacheFile, $mappedUrls)) {
-							$this->log(" * Deleting $cacheFile\n");
-							
-							@unlink($cacheFile);
-						}
-					}
-				}
-			}
-			
-			$this->log("done.\n\n");
-		}
-
-		$this->log("Rebuilding cache from " . sizeof($mappedUrls) . " urls...\n\n");
-		$page->extend('publishPages', $mappedUrls);
-		
-		if (file_exists($cacheBaseDir.'/lock')) {
-			unlink($cacheBaseDir.'/lock');
-		}
-		
-		$this->log("\n\n== Done! ==\n");
-	}
+        $this->log("Rebuilding cache from " . sizeof($mappedUrls) . " urls...\n\n");
+        $page->extend('publishPages', $mappedUrls);
+        
+        if (file_exists($cacheBaseDir.'/lock')) {
+            unlink($cacheBaseDir.'/lock');
+        }
+        
+        $this->log("\n\n== Done! ==\n");
+    }
 }
diff --git a/code/tasks/StaticExporterTask.php b/code/tasks/StaticExporterTask.php
index 606a76c..bde6185 100644
--- a/code/tasks/StaticExporterTask.php
+++ b/code/tasks/StaticExporterTask.php
@@ -3,33 +3,39 @@
 /**
  * @package staticpublisher
  */
-class StaticExporterTask extends BuildTask {
+class StaticExporterTask extends BuildTask
+{
 
-	public function run($request) {
-		$now = microtime(true);
-		$export = new StaticExporter();
+    public function run($request)
+    {
+        $now = microtime(true);
+        $export = new StaticExporter();
 
-		$url = $request->getVar('baseurl');
-		$sym = $request->getVar('symlink');
-		$quiet = $request->getVar('quiet');
-		$folder = $request->getVar('path');
+        $url = $request->getVar('baseurl');
+        $sym = $request->getVar('symlink');
+        $quiet = $request->getVar('quiet');
+        $folder = $request->getVar('path');
 
-		if(!$folder) $folder = TEMP_FOLDER . '/static-export';
+        if (!$folder) {
+            $folder = TEMP_FOLDER . '/static-export';
+        }
 
-		$url = ($url) ? $url : Director::baseURL(); 
-		$symlink = ($sym != "false");
-		$quiet = ($quiet) ? $quiet : false;
+        $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("Exporting website with %s base URL... %s", $url, PHP_EOL);
+        }
+        $path = $export->doExport($url, $folder, $symlink, $quiet);
 
-		if(!$quiet) {
-			printf("\nWebsite exported to %s\nTotal time %s\nMemory used %s. %s", 
-				$path,
-				number_format(microtime(true) - $now, 2) . 's', 
-				number_format(memory_get_peak_usage() / 1024 / 1024, 2) .'mb',
-				PHP_EOL
-			);
-		}
-	}
-}
\ No newline at end of file
+        if (!$quiet) {
+            printf("\nWebsite exported to %s\nTotal time %s\nMemory used %s. %s",
+                $path,
+                number_format(microtime(true) - $now, 2) . 's',
+                number_format(memory_get_peak_usage() / 1024 / 1024, 2) .'mb',
+                PHP_EOL
+            );
+        }
+    }
+}
diff --git a/tests/FilesystemPublisherTest.php b/tests/FilesystemPublisherTest.php
index cb37d3a..f214b67 100644
--- a/tests/FilesystemPublisherTest.php
+++ b/tests/FilesystemPublisherTest.php
@@ -5,243 +5,257 @@
  * 
  * @package staticpublisher
  */
-class FilesystemPublisherTest extends SapphireTest {
-	
-	protected $usesDatabase = true;
-	
-	protected $orig = array();
-	
-	public function setUp() {
-		parent::setUp();
-		
-		SiteTree::add_extension("FilesystemPublisher('assets/FilesystemPublisherTest-static-folder/')");
-		
-		$this->orig['domain_based_caching'] = Config::inst()->get('FilesystemPublisher', 'domain_based_caching');
+class FilesystemPublisherTest extends SapphireTest
+{
+    
+    protected $usesDatabase = true;
+    
+    protected $orig = array();
+    
+    public function setUp()
+    {
+        parent::setUp();
+        
+        SiteTree::add_extension("FilesystemPublisher('assets/FilesystemPublisherTest-static-folder/')");
+        
+        $this->orig['domain_based_caching'] = Config::inst()->get('FilesystemPublisher', 'domain_based_caching');
 
-		Config::inst()->update('FilesystemPublisher', 'domain_based_caching', false);
-	}
-	
-	public function tearDown() {
-		parent::tearDown();
+        Config::inst()->update('FilesystemPublisher', 'domain_based_caching', false);
+    }
+    
+    public function tearDown()
+    {
+        parent::tearDown();
 
-		SiteTree::remove_extension("FilesystemPublisher('assets/FilesystemPublisherTest-static-folder/')");
+        SiteTree::remove_extension("FilesystemPublisher('assets/FilesystemPublisherTest-static-folder/')");
 
-		Config::inst()->update('FilesystemPublisher', 'domain_based_caching', $this->orig['domain_based_caching']);
+        Config::inst()->update('FilesystemPublisher', 'domain_based_caching', $this->orig['domain_based_caching']);
 
-		if(file_exists(BASE_PATH . '/assets/FilesystemPublisherTest-static-folder')) {
-			Filesystem::removeFolder(BASE_PATH . '/assets/FilesystemPublisherTest-static-folder');
-		}
-	}
-	
-	public function testUrlsToPathsWithRelativeUrls() {
-		$fsp = new FilesystemPublisher('.', 'html');
-		
-		$this->assertEquals(
-			$fsp->urlsToPaths(array('/')),
-			array('/' => './index.html'),
-			'Root URL path mapping'
-		);
-		
-		$this->assertEquals(
-			$fsp->urlsToPaths(array('about-us')),
-			array('about-us' => './about-us.html'),
-			'URLsegment path mapping'
-		);
-		
-		$this->assertEquals(
-			$fsp->urlsToPaths(array('parent/child')),
-			array('parent/child' => 'parent/child.html'),
-			'Nested URLsegment path mapping'
-		);
-	}
-	
-	public function testUrlsToPathsWithAbsoluteUrls() {
-		$fsp = new FilesystemPublisher('.', 'html');
-		
-		$url = Director::absoluteBaseUrl();
-		$this->assertEquals(
-			$fsp->urlsToPaths(array($url)),
-			array($url => './index.html'),
-			'Root URL path mapping'
-		);
-		
-		$url = Director::absoluteBaseUrl() . 'about-us';
-		$this->assertEquals(
-			$fsp->urlsToPaths(array($url)),
-			array($url => './about-us.html'),
-			'URLsegment path mapping'
-		);
-		
-		$url = Director::absoluteBaseUrl() . 'parent/child';
-		$this->assertEquals(
-			$fsp->urlsToPaths(array($url)),
-			array($url => 'parent/child.html'),
-			'Nested URLsegment path mapping'
-		);
-	}
+        if (file_exists(BASE_PATH . '/assets/FilesystemPublisherTest-static-folder')) {
+            Filesystem::removeFolder(BASE_PATH . '/assets/FilesystemPublisherTest-static-folder');
+        }
+    }
+    
+    public function testUrlsToPathsWithRelativeUrls()
+    {
+        $fsp = new FilesystemPublisher('.', 'html');
+        
+        $this->assertEquals(
+            $fsp->urlsToPaths(array('/')),
+            array('/' => './index.html'),
+            'Root URL path mapping'
+        );
+        
+        $this->assertEquals(
+            $fsp->urlsToPaths(array('about-us')),
+            array('about-us' => './about-us.html'),
+            'URLsegment path mapping'
+        );
+        
+        $this->assertEquals(
+            $fsp->urlsToPaths(array('parent/child')),
+            array('parent/child' => 'parent/child.html'),
+            'Nested URLsegment path mapping'
+        );
+    }
+    
+    public function testUrlsToPathsWithAbsoluteUrls()
+    {
+        $fsp = new FilesystemPublisher('.', 'html');
+        
+        $url = Director::absoluteBaseUrl();
+        $this->assertEquals(
+            $fsp->urlsToPaths(array($url)),
+            array($url => './index.html'),
+            'Root URL path mapping'
+        );
+        
+        $url = Director::absoluteBaseUrl() . 'about-us';
+        $this->assertEquals(
+            $fsp->urlsToPaths(array($url)),
+            array($url => './about-us.html'),
+            'URLsegment path mapping'
+        );
+        
+        $url = Director::absoluteBaseUrl() . 'parent/child';
+        $this->assertEquals(
+            $fsp->urlsToPaths(array($url)),
+            array($url => 'parent/child.html'),
+            'Nested URLsegment path mapping'
+        );
+    }
 
-	public function testUrlsToPathsWithDomainBasedCaching() {
-		$origDomainBasedCaching = Config::inst()->get('FilesystemPublisher', 'domain_based_caching');
-		Config::inst()->update('FilesystemPublisher', 'domain_based_caching', true);
-		
-		$fsp = new FilesystemPublisher('.', 'html');
-		
-		$url = 'http://domain1.com/';
-		$this->assertEquals(
-			$fsp->urlsToPaths(array($url)),
-			array($url => 'domain1.com/index.html'),
-			'Root URL path mapping'
-		);
-		
-		$url = 'http://domain1.com/about-us';
-		$this->assertEquals(
-			$fsp->urlsToPaths(array($url)),
-			array($url => 'domain1.com/about-us.html'),
-			'URLsegment path mapping'
-		);
-		
-		$url = 'http://domain2.com/parent/child';
-		$this->assertEquals(
-			$fsp->urlsToPaths(array($url)),
-			array($url => 'domain2.com/parent/child.html'),
-			'Nested URLsegment path mapping'
-		);
-		
-		Config::inst()->update('FilesystemPublisher', 'domain_based_caching', $origDomainBasedCaching);
-	}
-	
-	/**
-	 * Simple test to ensure that FileSystemPublisher::__construct()
-	 * has called parent::__construct() by checking the class property.
-	 * The class property is set on {@link Object::__construct()} and
-	 * this is therefore a good test to ensure it was called.
-	 * 
-	 * If FilesystemPublisher doesn't call parent::__construct() then
-	 * it won't be enabled propery because {@link Object::__construct()}
-	 * is where extension instances are set up and subsequently used by
-	 * {@link DataObject::defineMethods()}.
-	 */
-	public function testHasCalledParentConstructor() {
-		$fsp = new FilesystemPublisher('.', '.html');
+    public function testUrlsToPathsWithDomainBasedCaching()
+    {
+        $origDomainBasedCaching = Config::inst()->get('FilesystemPublisher', 'domain_based_caching');
+        Config::inst()->update('FilesystemPublisher', 'domain_based_caching', true);
+        
+        $fsp = new FilesystemPublisher('.', 'html');
+        
+        $url = 'http://domain1.com/';
+        $this->assertEquals(
+            $fsp->urlsToPaths(array($url)),
+            array($url => 'domain1.com/index.html'),
+            'Root URL path mapping'
+        );
+        
+        $url = 'http://domain1.com/about-us';
+        $this->assertEquals(
+            $fsp->urlsToPaths(array($url)),
+            array($url => 'domain1.com/about-us.html'),
+            'URLsegment path mapping'
+        );
+        
+        $url = 'http://domain2.com/parent/child';
+        $this->assertEquals(
+            $fsp->urlsToPaths(array($url)),
+            array($url => 'domain2.com/parent/child.html'),
+            'Nested URLsegment path mapping'
+        );
+        
+        Config::inst()->update('FilesystemPublisher', 'domain_based_caching', $origDomainBasedCaching);
+    }
+    
+    /**
+     * Simple test to ensure that FileSystemPublisher::__construct()
+     * has called parent::__construct() by checking the class property.
+     * The class property is set on {@link Object::__construct()} and
+     * this is therefore a good test to ensure it was called.
+     * 
+     * If FilesystemPublisher doesn't call parent::__construct() then
+     * it won't be enabled propery because {@link Object::__construct()}
+     * is where extension instances are set up and subsequently used by
+     * {@link DataObject::defineMethods()}.
+     */
+    public function testHasCalledParentConstructor()
+    {
+        $fsp = new FilesystemPublisher('.', '.html');
 
-		$this->assertEquals($fsp->class, 'FilesystemPublisher');
-	}
-	
-	/*
-	 * These are a few simple tests to check that we will be retrieving the 
-	 * correct theme when we need it. StaticPublishing needs to be able to 
-	 * retrieve a non-null theme at the time publishPages() is called.
-	 */
-	public function testStaticPublisherTheme(){
-		
-		// This will be the name of the default theme of this particular project
-		$default_theme = Config::inst()->get('SSViewer', 'theme');
-		
-		$p1 = new Page();
-		$p1->URLSegment = strtolower(__CLASS__).'-page-1';
-		$p1->HomepageForDomain = '';
-		$p1->write();
-		$p1->doPublish();
-		
-		$current_theme = Config::inst()->get('SSViewer', 'theme_enabled') ? Config::inst()->get('SSViewer', 'theme') : null;
-		$this->assertEquals($current_theme, $default_theme, 'After a standard publication, the theme is correct');
-		
-		//We can set the static_publishing theme to something completely different:
-		//Static publishing will use this one instead of the current_custom_theme if it is not false
-		Config::inst()->update('StaticPublisher', 'static_publisher_theme', 'otherTheme');
-		$current_theme = Config::inst()->get('StaticPublisher', 'static_publisher_theme');
+        $this->assertEquals($fsp->class, 'FilesystemPublisher');
+    }
+    
+    /*
+     * These are a few simple tests to check that we will be retrieving the 
+     * correct theme when we need it. StaticPublishing needs to be able to 
+     * retrieve a non-null theme at the time publishPages() is called.
+     */
+    public function testStaticPublisherTheme()
+    {
+        
+        // This will be the name of the default theme of this particular project
+        $default_theme = Config::inst()->get('SSViewer', 'theme');
+        
+        $p1 = new Page();
+        $p1->URLSegment = strtolower(__CLASS__).'-page-1';
+        $p1->HomepageForDomain = '';
+        $p1->write();
+        $p1->doPublish();
+        
+        $current_theme = Config::inst()->get('SSViewer', 'theme_enabled') ? Config::inst()->get('SSViewer', 'theme') : null;
+        $this->assertEquals($current_theme, $default_theme, 'After a standard publication, the theme is correct');
+        
+        //We can set the static_publishing theme to something completely different:
+        //Static publishing will use this one instead of the current_custom_theme if it is not false
+        Config::inst()->update('StaticPublisher', 'static_publisher_theme', 'otherTheme');
+        $current_theme = Config::inst()->get('StaticPublisher', 'static_publisher_theme');
 
-		$this->assertNotEquals($current_theme, $default_theme, 'The static publisher theme overrides the custom theme');
-	}
+        $this->assertNotEquals($current_theme, $default_theme, 'The static publisher theme overrides the custom theme');
+    }
 
-	public function testMenu2LinkingMode() { 
-		$this->logInWithPermission('ADMIN'); 
-		
-		Config::inst()->update('SSViewer', 'theme', null);
-		
-		$l1 = new StaticPublisherTestPage(); 
-		$l1->URLSegment = strtolower(__CLASS__).'-level-1'; 
- 		$l1->write(); 
-		$l1->doPublish(); 
-	
-		$l2_1 = new StaticPublisherTestPage(); 
-		$l2_1->URLSegment = strtolower(__CLASS__).'-level-2-1'; 
-		$l2_1->ParentID = $l1->ID; 
-		$l2_1->write(); 
-		
-		$l2_1->doPublish(); 
-		$response = Director::test($l2_1->AbsoluteLink());
+    public function testMenu2LinkingMode()
+    {
+        $this->logInWithPermission('ADMIN');
+        
+        Config::inst()->update('SSViewer', 'theme', null);
+        
+        $l1 = new StaticPublisherTestPage();
+        $l1->URLSegment = strtolower(__CLASS__).'-level-1';
+        $l1->write();
+        $l1->doPublish();
+    
+        $l2_1 = new StaticPublisherTestPage();
+        $l2_1->URLSegment = strtolower(__CLASS__).'-level-2-1';
+        $l2_1->ParentID = $l1->ID;
+        $l2_1->write();
+        
+        $l2_1->doPublish();
+        $response = Director::test($l2_1->AbsoluteLink());
 
-		$this->assertEquals(trim($response->getBody()), "current", "current page is level 2-1"); 
+        $this->assertEquals(trim($response->getBody()), "current", "current page is level 2-1");
                 
-		$l2_2 = new StaticPublisherTestPage(); 
-		$l2_2->URLSegment = strtolower(__CLASS__).'-level-2-2'; 
-		$l2_2->ParentID = $l1->ID; 
-		$l2_2->write(); 
-		$l2_2->doPublish();
+        $l2_2 = new StaticPublisherTestPage();
+        $l2_2->URLSegment = strtolower(__CLASS__).'-level-2-2';
+        $l2_2->ParentID = $l1->ID;
+        $l2_2->write();
+        $l2_2->doPublish();
 
-		$response = Director::test($l2_2->AbsoluteLink()); 
-		$this->assertEquals(trim($response->getBody()), "linkcurrent", "current page is level 2-2"); 
-	}
+        $response = Director::test($l2_2->AbsoluteLink());
+        $this->assertEquals(trim($response->getBody()), "linkcurrent", "current page is level 2-2");
+    }
 
-	public function testContentTypeHTML() {
-		SiteTree::remove_extension('FilesystemPublisher');
-		SiteTree::add_extension("FilesystemPublisher('assets/FilesystemPublisherTest-static-folder/', 'php')");
-		$l1 = new StaticPublisherTestPage();
-		$l1->URLSegment = 'mimetype';
- 		$l1->write();
-		$l1->doPublish();
-		$response = Director::test('mimetype');
-		$this->assertEquals($response->getHeader('Content-Type'), 'text/html; charset=utf-8', 'Content-Type should be text/html; charset=utf-8');
-	}
+    public function testContentTypeHTML()
+    {
+        SiteTree::remove_extension('FilesystemPublisher');
+        SiteTree::add_extension("FilesystemPublisher('assets/FilesystemPublisherTest-static-folder/', 'php')");
+        $l1 = new StaticPublisherTestPage();
+        $l1->URLSegment = 'mimetype';
+        $l1->write();
+        $l1->doPublish();
+        $response = Director::test('mimetype');
+        $this->assertEquals($response->getHeader('Content-Type'), 'text/html; charset=utf-8', 'Content-Type should be text/html; charset=utf-8');
+    }
 
-	public function testContentTypeJSON() {
-		SiteTree::remove_extension('FilesystemPublisher');
-		SiteTree::add_extension("FilesystemPublisher('assets/FilesystemPublisherTest-static-folder/', 'php')");
-		$l1 = new StaticPublisherTestPage();
-		$l1->URLSegment = 'mimetype';
- 		$l1->write();
-		$l1->doPublish();
-		$response = Director::test('mimetype/json');
-		$this->assertEquals($response->getHeader('Content-Type'), 'application/json', 'Content-Type should be application/json');
-	}
+    public function testContentTypeJSON()
+    {
+        SiteTree::remove_extension('FilesystemPublisher');
+        SiteTree::add_extension("FilesystemPublisher('assets/FilesystemPublisherTest-static-folder/', 'php')");
+        $l1 = new StaticPublisherTestPage();
+        $l1->URLSegment = 'mimetype';
+        $l1->write();
+        $l1->doPublish();
+        $response = Director::test('mimetype/json');
+        $this->assertEquals($response->getHeader('Content-Type'), 'application/json', 'Content-Type should be application/json');
+    }
 }
 
 /**
  * @package staticpublisher
  */
-class StaticPublisherTestPage extends Page implements TestOnly {
+class StaticPublisherTestPage extends Page implements TestOnly
+{
 
-	private static $allowed_children = array(
-		'StaticPublisherTestPage'
-	);
+    private static $allowed_children = array(
+        'StaticPublisherTestPage'
+    );
 
 
-	public function canPublish($member = null) { 
-		return true; 
-	}
+    public function canPublish($member = null)
+    {
+        return true;
+    }
 
-	public function getTemplate() {
-		return STATIC_MODULE_DIR . '/tests/templates/StaticPublisherTestPage.ss';
-	}
-} 
+    public function getTemplate()
+    {
+        return STATIC_MODULE_DIR . '/tests/templates/StaticPublisherTestPage.ss';
+    }
+}
 
 /**
  * @package staticpublisher
  */
-class StaticPublisherTestPage_Controller extends Page_Controller implements TestOnly {
-
-	/**
-	 *
-	 * @var array
-	 */
-	private static $allowed_actions = array('json');
-	
-	public function json(SS_HTTPRequest $request) {
-		$response = new SS_HTTPResponse('{"firstName": "John"}');
-		$response->addHeader('Content-Type', 'application/json');
-		return $response;
-
-	}
+class StaticPublisherTestPage_Controller extends Page_Controller implements TestOnly
+{
 
+    /**
+     *
+     * @var array
+     */
+    private static $allowed_actions = array('json');
+    
+    public function json(SS_HTTPRequest $request)
+    {
+        $response = new SS_HTTPResponse('{"firstName": "John"}');
+        $response->addHeader('Content-Type', 'application/json');
+        return $response;
+    }
 }