diff --git a/code/CSSAbsolutePathRewriter.php b/code/CSSAbsolutePathRewriter.php index 215b442..500e7a2 100644 --- a/code/CSSAbsolutePathRewriter.php +++ b/code/CSSAbsolutePathRewriter.php @@ -5,63 +5,73 @@ * * @package compass */ -class CSSAbsolutePathRewriter extends Requirements_Backend { - - function css($file, $media = null) { - $isurl = (bool)preg_match('{^\w+://}', $file); - return parent::css($isurl ? $file : $this->baseRewrite($file), $media); - } +class CSSAbsolutePathRewriter extends Requirements_Backend +{ + + public function css($file, $media = null) + { + $isurl = (bool)preg_match('{^\w+://}', $file); + return parent::css($isurl ? $file : $this->baseRewrite($file), $media); + } - function customCSS($script, $uniquenessID = null) { - return parent::customCSS($this->baseReplace($script), $uniquenessID); - } - - function baseReplace($css) { - return str_replace('BASE', Director::baseURL(), $css); - } - - function baseRewrite($file) { - if (preg_match('/^(jsparty|sapphire|cms)/', $file)) return $file; - - $name = basename($file, '.css'); - $dir = dirname($file); - $path = BASE_PATH.'/'.$file; - - $hash = sha1(Director::baseURL()); - $tstamp = filemtime($path); - - $out = str_replace('/', '_', $dir)."_{$name}_{$hash}_{$tstamp}.css"; - - $lnkname = "{$dir}/{$out}"; - $outname = 'assets/'.$out; - $outpath = BASE_PATH.'/'.$outname; - - if (!file_exists($outpath)) { - $css = file_get_contents($path); - if (strpos($css, 'BASE') === FALSE) return $file; - - file_put_contents($outpath, $this->baseReplace($css)); - } - - return $lnkname; - } - - /** - * Default includeInHTML strips out CSS if the file doesn't exist. We need to add it back in if it's a redirected rewrite - * @see sapphire/core/Requirements_Backend#includeInHTML($templateFile, $content) - */ - function includeInHTML($templateFile, $content) { - $content = parent::includeInHTML($templateFile, $content); - $requirements = ''; - - foreach(array_diff_key($this->css,$this->blocked) as $file => $params) { - $path = self::path_for_file($file); - if (!$path && ($path = self::path_for_file('assets/'.basename($file)))) { - $media = (isset($params['media']) && !empty($params['media'])) ? " media=\"{$params['media']}\"" : ""; - $requirements .= "\n"; - } - } - - return preg_replace("/(<\/head[^>]*>)/i", $requirements . "\\1", $content); - } -} \ No newline at end of file + public function customCSS($script, $uniquenessID = null) + { + return parent::customCSS($this->baseReplace($script), $uniquenessID); + } + + public function baseReplace($css) + { + return str_replace('BASE', Director::baseURL(), $css); + } + + public function baseRewrite($file) + { + if (preg_match('/^(jsparty|sapphire|cms)/', $file)) { + return $file; + } + + $name = basename($file, '.css'); + $dir = dirname($file); + $path = BASE_PATH.'/'.$file; + + $hash = sha1(Director::baseURL()); + $tstamp = filemtime($path); + + $out = str_replace('/', '_', $dir)."_{$name}_{$hash}_{$tstamp}.css"; + + $lnkname = "{$dir}/{$out}"; + $outname = 'assets/'.$out; + $outpath = BASE_PATH.'/'.$outname; + + if (!file_exists($outpath)) { + $css = file_get_contents($path); + if (strpos($css, 'BASE') === false) { + return $file; + } + + file_put_contents($outpath, $this->baseReplace($css)); + } + + return $lnkname; + } + + /** + * Default includeInHTML strips out CSS if the file doesn't exist. We need to add it back in if it's a redirected rewrite + * @see sapphire/core/Requirements_Backend#includeInHTML($templateFile, $content) + */ + public function includeInHTML($templateFile, $content) + { + $content = parent::includeInHTML($templateFile, $content); + $requirements = ''; + + foreach (array_diff_key($this->css, $this->blocked) as $file => $params) { + $path = self::path_for_file($file); + if (!$path && ($path = self::path_for_file('assets/'.basename($file)))) { + $media = (isset($params['media']) && !empty($params['media'])) ? " media=\"{$params['media']}\"" : ""; + $requirements .= "\n"; + } + } + + return preg_replace("/(<\/head[^>]*>)/i", $requirements . "\\1", $content); + } +} diff --git a/code/Compass.php b/code/Compass.php index 6ca47ab..b868473 100644 --- a/code/Compass.php +++ b/code/Compass.php @@ -3,376 +3,431 @@ /** * @package compass */ -class Compass extends Controller { - - /** - * @var array - */ - public static $url_handlers = array( - '$Action' => '$Action' - ); +class Compass extends Controller +{ + + /** + * @var array + */ + public static $url_handlers = array( + '$Action' => '$Action' + ); - /** - * @var bool Are compass errors actually errors, or should we just ignore - * them? True means complain, false means don't, null means don't complain - * on live servers, do otherwise. - */ - public static $errors_are_errors = null; - - /** - * @var bool Set to true to force no automatic rebuilding, even if isDev() - * is true or flush is passed and the gems are all available. - */ - public static $force_no_rebuild = false; + /** + * @var bool Are compass errors actually errors, or should we just ignore + * them? True means complain, false means don't, null means don't complain + * on live servers, do otherwise. + */ + public static $errors_are_errors = null; + + /** + * @var bool Set to true to force no automatic rebuilding, even if isDev() + * is true or flush is passed and the gems are all available. + */ + public static $force_no_rebuild = false; - /** - * @var float Which version of sass should we use. - */ - public static $sass_version = '3'; - - /** - * @var string - preferred syntax to use. - */ - public static $syntax = "scss"; + /** + * @var float Which version of sass should we use. + */ + public static $sass_version = '3'; + + /** + * @var string - preferred syntax to use. + */ + public static $syntax = "scss"; - /** - * @var array map of required gems for each version. - */ - public static $required_gems = array( - '2' => array( - 'yard' => '', 'maruku' => '', 'haml' => '~> 2.2', 'compass' => '~> 0.8.0', 'compass-colors' => '' - ), - '3' => array( - 'yard' => '', 'maruku' => '', 'sass' => '~>3.2', 'compass' => '~> 0.12.2', 'compass-colors' => '' - ), - 'latest' => array( - 'yard' => '', 'maruku' => '', 'sass' => '', 'compass' => '', 'compass-colors' => '' - ) - ); - - /** - * @var bool Internal cache variable - is the version of rubygems currently - * available good enough? - */ - private static $check_gems_result = null; - - /** - * Directory to store sass cache. Defaults to temp_folder/.sass. - * - * @see set_tmp_dir() - * @var string - */ - private static $temp_dir; - - - protected function checkGems() { - if (self::$check_gems_result === null) { - - self::$check_gems_result = true; - - foreach (self::$required_gems[self::$sass_version] as $gem => $version) { - if(!$version) $version = ">= 0"; - - if($error = Rubygems::require_gem($gem, $version)) { - self::$check_gems_result = $error; - } - } - } - - return self::$check_gems_result; - } + /** + * @var array map of required gems for each version. + */ + public static $required_gems = array( + '2' => array( + 'yard' => '', 'maruku' => '', 'haml' => '~> 2.2', 'compass' => '~> 0.8.0', 'compass-colors' => '' + ), + '3' => array( + 'yard' => '', 'maruku' => '', 'sass' => '~>3.2', 'compass' => '~> 0.12.2', 'compass-colors' => '' + ), + 'latest' => array( + 'yard' => '', 'maruku' => '', 'sass' => '', 'compass' => '', 'compass-colors' => '' + ) + ); + + /** + * @var bool Internal cache variable - is the version of rubygems currently + * available good enough? + */ + private static $check_gems_result = null; + + /** + * Directory to store sass cache. Defaults to temp_folder/.sass. + * + * @see set_tmp_dir() + * @var string + */ + private static $temp_dir; + + + protected function checkGems() + { + if (self::$check_gems_result === null) { + self::$check_gems_result = true; + + foreach (self::$required_gems[self::$sass_version] as $gem => $version) { + if (!$version) { + $version = ">= 0"; + } + + if ($error = Rubygems::require_gem($gem, $version)) { + self::$check_gems_result = $error; + } + } + } + + return self::$check_gems_result; + } - static function error($message) { - // If errors_are_errors is null, work out if it should be true or false based on server mode - if (self::$errors_are_errors === null) { - $runningTest = class_exists('SapphireTest',false) && SapphireTest::is_running_test(); - self::$errors_are_errors = Director::isDev() && !$runningTest; - } + public static function error($message) + { + // If errors_are_errors is null, work out if it should be true or false based on server mode + if (self::$errors_are_errors === null) { + $runningTest = class_exists('SapphireTest', false) && SapphireTest::is_running_test(); + self::$errors_are_errors = Director::isDev() && !$runningTest; + } - // Then raise the actual error (if errors are errors) - if (self::$errors_are_errors) user_error('Compass Error:
' . preg_replace('/[\r\n]+/', '
', $message), E_USER_ERROR); - return false; - } - - public function init() { - parent::init(); - - // We allow access to this controller regardless of live-status or - // ADMIN permission only if on CLI. Access to this controller is - // always allowed in "dev-mode", or of the user is ADMIN. - - $canAccess = (Director::isDev() || Director::is_cli() || Permission::check("ADMIN")); - - if (!$canAccess) { - return Security::permissionFailure($this); - } - } - - /** - * Convert a css based theme to a sass based one - * - * Designed to be called as a sake command - * - * - * sapphire/sake dev/compass/convert --theme=blackcandy - * - * - * @param bool $verbose to anything positive to output - * status (calling as a controller passed HTTPRequest, which is good enough) - */ - function convert($verbose = false) { - $dir = null; - - if (@$_GET['theme']) $dir = THEMES_PATH . DIRECTORY_SEPARATOR . $_GET['theme']; - if (@$_GET['module']) $dir = BASE_PATH . DIRECTORY_SEPARATOR . $_GET['module']; + // Then raise the actual error (if errors are errors) + if (self::$errors_are_errors) { + user_error('Compass Error:
' . preg_replace('/[\r\n]+/', '
', $message), E_USER_ERROR); + } + return false; + } + + public function init() + { + parent::init(); + + // We allow access to this controller regardless of live-status or + // ADMIN permission only if on CLI. Access to this controller is + // always allowed in "dev-mode", or of the user is ADMIN. - // Check for no arguments - if (!$dir) { - echo "\n"; - echo "Usage: convert --module=module | convert --theme=theme\n"; - echo "Alternatively, use convertTheme or convertModule\n\n"; - exit(); - } - - // Check the directory exists - if (!is_dir($dir)) { - echo "\nERROR:\n\nPath $dir doesn't exist.\n\n"; - exit(); - } - - // And has css in it - if (!is_dir($dir . DIRECTORY_SEPARATOR . 'css')) { - echo "\nERROR:\n\nPath $dir doesn't contain any css\n\n"; - exit(); - } - - // And doesn't have sass commands in it - if(is_dir($dir . DIRECTORY_SEPARATOR . self::$syntax)) { - if (!@$_GET['force'] && array_search('--force', (array)@$_GET['args']) === false) { - echo "\nERROR:\n\nPath $dir is already a compass or sass based theme or module.\nUse --force to force overwriting\n\n"; - exit(); - } - } - - self::generate_config($dir); - - // Make sure the gems we need are available - if (($error = $this->checkGems()) !== true) return self::error($error); + $canAccess = (Director::isDev() || Director::is_cli() || Permission::check("ADMIN")); + + if (!$canAccess) { + return Security::permissionFailure($this); + } + } + + /** + * Convert a css based theme to a sass based one + * + * Designed to be called as a sake command + * + * + * sapphire/sake dev/compass/convert --theme=blackcandy + * + * + * @param bool $verbose to anything positive to output + * status (calling as a controller passed HTTPRequest, which is good enough) + */ + public function convert($verbose = false) + { + $dir = null; + + if (@$_GET['theme']) { + $dir = THEMES_PATH . DIRECTORY_SEPARATOR . $_GET['theme']; + } + if (@$_GET['module']) { + $dir = BASE_PATH . DIRECTORY_SEPARATOR . $_GET['module']; + } - $this->recursivelyConvert($dir.DIRECTORY_SEPARATOR.'css', $dir.DIRECTORY_SEPARATOR.'sass'); - - if ($verbose) echo "\nConversion succesfull\n"; - } - - protected function recursivelyConvert($from, $to) { - - $dir = dir($from); - if (!is_dir($to)) mkdir($to); - - while ($entry = $dir->read()) { - - $from_ = $from.DIRECTORY_SEPARATOR.$entry; - - if (fnmatch('.*', $entry)) continue; - - if (is_dir($from_)) { - $this->recursivelyConvert($from_, $to.DIRECTORY_SEPARATOR.$entry); - } - - else if (fnmatch('*.css', $entry) && is_file($from_)) { - - $to_ = $to.DIRECTORY_SEPARATOR.preg_replace('/.css$/', '.sass', $entry); - - $res = Rubygems::run(self::$required_gems[self::$sass_version], 'css2sass', "'$from_' '$to_'", $out, $err); - - if ($res != 0) { - echo "\nError converting ".Director::makeRelative($from_)."\n\nError from css2sass was:\n$err"; - } - } - } - } + // Check for no arguments + if (!$dir) { + echo "\n"; + echo "Usage: convert --module=module | convert --theme=theme\n"; + echo "Alternatively, use convertTheme or convertModule\n\n"; + exit(); + } + + // Check the directory exists + if (!is_dir($dir)) { + echo "\nERROR:\n\nPath $dir doesn't exist.\n\n"; + exit(); + } + + // And has css in it + if (!is_dir($dir . DIRECTORY_SEPARATOR . 'css')) { + echo "\nERROR:\n\nPath $dir doesn't contain any css\n\n"; + exit(); + } + + // And doesn't have sass commands in it + if (is_dir($dir . DIRECTORY_SEPARATOR . self::$syntax)) { + if (!@$_GET['force'] && array_search('--force', (array)@$_GET['args']) === false) { + echo "\nERROR:\n\nPath $dir is already a compass or sass based theme or module.\nUse --force to force overwriting\n\n"; + exit(); + } + } + + self::generate_config($dir); + + // Make sure the gems we need are available + if (($error = $this->checkGems()) !== true) { + return self::error($error); + } - /** - * Utility function that returns an array of all themes. - * - * @return array - */ - protected function getAllThemes() { - $baseDir = BASE_PATH . DIRECTORY_SEPARATOR . THEMES_DIR; - - $themes = array(); - - if(is_dir($baseDir)) { - $dir = dir($baseDir); - - while ($file = $dir->read()) { - $fullPath = $baseDir . DIRECTORY_SEPARATOR . $file; - if (strpos($file, '.') === false && is_dir($fullPath)) $themes[$file] = $file; - } - } - - return $themes; - } + $this->recursivelyConvert($dir.DIRECTORY_SEPARATOR.'css', $dir.DIRECTORY_SEPARATOR.'sass'); + + if ($verbose) { + echo "\nConversion succesfull\n"; + } + } + + protected function recursivelyConvert($from, $to) + { + $dir = dir($from); + if (!is_dir($to)) { + mkdir($to); + } + + while ($entry = $dir->read()) { + $from_ = $from.DIRECTORY_SEPARATOR.$entry; + + if (fnmatch('.*', $entry)) { + continue; + } + + if (is_dir($from_)) { + $this->recursivelyConvert($from_, $to.DIRECTORY_SEPARATOR.$entry); + } elseif (fnmatch('*.css', $entry) && is_file($from_)) { + $to_ = $to.DIRECTORY_SEPARATOR.preg_replace('/.css$/', '.sass', $entry); + + $res = Rubygems::run(self::$required_gems[self::$sass_version], 'css2sass', "'$from_' '$to_'", $out, $err); + + if ($res != 0) { + echo "\nError converting ".Director::makeRelative($from_)."\n\nError from css2sass was:\n$err"; + } + } + } + } - /** - * Utility function that returns an array of all modules. - * - * @return array Map of module names to their path - */ - protected function getAllModules() { - $modules = array(); - - if(class_exists('SS_ClassLoader')) { - // SilverStripe 3.x - $modules = SS_ClassLoader::instance()->getManifest()->getModules(); - } else { - // SilverStripe 2.x - global $_CLASS_MANIFEST; - $paths = $_CLASS_MANIFEST; - foreach ($paths as $path) { - if (preg_match('#'.preg_quote(BASE_PATH, '#').'/([^/]+)/#', $path, $matches)) { - $modules[$matches[1]] = BASE_PATH . DIRECTORY_SEPARATOR . $matches[1]; - } - } - } + /** + * Utility function that returns an array of all themes. + * + * @return array + */ + protected function getAllThemes() + { + $baseDir = BASE_PATH . DIRECTORY_SEPARATOR . THEMES_DIR; + + $themes = array(); + + if (is_dir($baseDir)) { + $dir = dir($baseDir); + + while ($file = $dir->read()) { + $fullPath = $baseDir . DIRECTORY_SEPARATOR . $file; + if (strpos($file, '.') === false && is_dir($fullPath)) { + $themes[$file] = $file; + } + } + } + + return $themes; + } - return $modules; - } - - /** - * Convert the sass files to css files - * - * Called automatically on dev machines, and when flush=all. Can also be - * called as a sake command (sapphire/sake dev/compass/rebuild). - * - * Set $verbose to anything positive to output status (calling as a - * controller passed HTTPRequest, which is good enough). - * - * @param bool $verbose - use Compass::$errors_are_errors = false to suppress. - */ - function rebuild($verbose = false) { - // Make sure the gems we need are available - if (($error = $this->checkGems()) !== true) return self::error($error); + /** + * Utility function that returns an array of all modules. + * + * @return array Map of module names to their path + */ + protected function getAllModules() + { + $modules = array(); + + if (class_exists('SS_ClassLoader')) { + // SilverStripe 3.x + $modules = SS_ClassLoader::instance()->getManifest()->getModules(); + } else { + // SilverStripe 2.x + global $_CLASS_MANIFEST; + $paths = $_CLASS_MANIFEST; + foreach ($paths as $path) { + if (preg_match('#'.preg_quote(BASE_PATH, '#').'/([^/]+)/#', $path, $matches)) { + $modules[$matches[1]] = BASE_PATH . DIRECTORY_SEPARATOR . $matches[1]; + } + } + } - $dir = null; - - if (@$_GET['theme']) $dir = THEMES_PATH . DIRECTORY_SEPARATOR . $_GET['theme']; - if (@$_GET['module']) $dir = BASE_PATH . DIRECTORY_SEPARATOR . $_GET['module']; + return $modules; + } + + /** + * Convert the sass files to css files + * + * Called automatically on dev machines, and when flush=all. Can also be + * called as a sake command (sapphire/sake dev/compass/rebuild). + * + * Set $verbose to anything positive to output status (calling as a + * controller passed HTTPRequest, which is good enough). + * + * @param bool $verbose - use Compass::$errors_are_errors = false to suppress. + */ + public function rebuild($verbose = false) + { + // Make sure the gems we need are available + if (($error = $this->checkGems()) !== true) { + return self::error($error); + } - if ($dir) { - $this->rebuildDirectory($dir); - } - else { - if ($verbose) echo "\nRebuilding all\n"; - - foreach ($this->getAllThemes() as $theme) { - $dir = THEMES_PATH . DIRECTORY_SEPARATOR . $theme; - - if (file_exists($dir . DIRECTORY_SEPARATOR . 'config.rb')) { - if ($verbose) echo "\nRebuilding theme: $theme\n"; - $this->rebuildDirectory($dir); - } - } + $dir = null; + + if (@$_GET['theme']) { + $dir = THEMES_PATH . DIRECTORY_SEPARATOR . $_GET['theme']; + } + if (@$_GET['module']) { + $dir = BASE_PATH . DIRECTORY_SEPARATOR . $_GET['module']; + } - foreach ($this->getAllModules() as $name => $path) { - // If this is in the compass module, skip - if ($name == 'compass') continue; - - if (file_exists($path . DIRECTORY_SEPARATOR . 'config.rb')) { - if ($verbose) echo "\nRebuilding module: $name\n"; - $this->rebuildDirectory($path); - } - } - } - - if ($verbose) echo "\nRebuild succesfull\n"; - } - - /** - * Rebuild the scss files from a given directory - * - * @param string $dir - */ - protected function rebuildDirectory($dir) { - if (!is_dir($dir)) return self::error("Could not rebuild $dir, as it doesn't exist"); - - self::generate_config($dir); - - $orig = getcwd(); - chdir($dir); - - $args = (self::$sass_version > 2) ? "compile -e production ": ""; - if(isset($_REQUEST['flush'])) $args .= "--force"; - - $code = Rubygems::run(self::$required_gems[self::$sass_version], "compass", $args, $out, $err); - chdir($orig); - - if ($code !== 0) return self::error($err); - } + if ($dir) { + $this->rebuildDirectory($dir); + } else { + if ($verbose) { + echo "\nRebuilding all\n"; + } + + foreach ($this->getAllThemes() as $theme) { + $dir = THEMES_PATH . DIRECTORY_SEPARATOR . $theme; + + if (file_exists($dir . DIRECTORY_SEPARATOR . 'config.rb')) { + if ($verbose) { + echo "\nRebuilding theme: $theme\n"; + } + $this->rebuildDirectory($dir); + } + } - /** - * Make sure the compass and sass gems are up to date. - * - * If the gems are not present, the system will install them automatically, - * but won't update them after that for speeds sake. Call this from sake to - * ensure you've got the most up-to-date version. - * - * Designed to be called as a sake command: - * - * sapphire/sake dev/compass/updategems - * - * - * @param bool $verbose - use Compass::$errors_are_errors = false to suppress. - * - */ - public function updategems($verbose = false) { - foreach (self::$required_gems[self::$sass_version] as $gem => $version) { - if (is_numeric($gem)) { $gem = $version; $version = null; } - if ($error = Rubygems::require_gem($gem, $version, true)) echo $error; - } - - if ($verbose) echo "\nGem update succesfull\n"; - } - - /** - * Generate a configuration file for a given directory - * - * @param string - folder name - */ - protected function generate_config($dir) { - if(!is_file($dir . DIRECTORY_SEPARATOR . 'config.rb')) { - file_put_contents( - $dir . DIRECTORY_SEPARATOR . 'config.rb', - $this->customise(new ArrayData(array( - 'TmpDir' => self::get_temp_dir(), - 'Mode' => self::$syntax - )))->renderWith('CompassConfig') - ); - } - } - - /** - * Set the temp directory for storing the compass cache. Path should be a folder - * to filesystem which is writable by the web server. - * - * @param string - */ - public static function set_temp_dir($dir) { - self::$temp_dir = $dir; - } - - /** - * Return the temp directory for storing the sass cache. Path should be writable - * by the web server. - * - * @return string - */ - public static function get_temp_dir() { - if(!self::$temp_dir) - return Controller::join_links(TEMP_FOLDER, '.sass-cache'); - - return self::$temp_dir; - } + foreach ($this->getAllModules() as $name => $path) { + // If this is in the compass module, skip + if ($name == 'compass') { + continue; + } + + if (file_exists($path . DIRECTORY_SEPARATOR . 'config.rb')) { + if ($verbose) { + echo "\nRebuilding module: $name\n"; + } + $this->rebuildDirectory($path); + } + } + } + + if ($verbose) { + echo "\nRebuild succesfull\n"; + } + } + + /** + * Rebuild the scss files from a given directory + * + * @param string $dir + */ + protected function rebuildDirectory($dir) + { + if (!is_dir($dir)) { + return self::error("Could not rebuild $dir, as it doesn't exist"); + } + + self::generate_config($dir); + + $orig = getcwd(); + chdir($dir); + + $args = (self::$sass_version > 2) ? "compile -e production ": ""; + if (isset($_REQUEST['flush'])) { + $args .= "--force"; + } + + $code = Rubygems::run(self::$required_gems[self::$sass_version], "compass", $args, $out, $err); + chdir($orig); + + if ($code !== 0) { + return self::error($err); + } + } + + /** + * Make sure the compass and sass gems are up to date. + * + * If the gems are not present, the system will install them automatically, + * but won't update them after that for speeds sake. Call this from sake to + * ensure you've got the most up-to-date version. + * + * Designed to be called as a sake command: + * + * sapphire/sake dev/compass/updategems + * + * + * @param bool $verbose - use Compass::$errors_are_errors = false to suppress. + * + */ + public function updategems($verbose = false) + { + foreach (self::$required_gems[self::$sass_version] as $gem => $version) { + if (is_numeric($gem)) { + $gem = $version; + $version = null; + } + if ($error = Rubygems::require_gem($gem, $version, true)) { + echo $error; + } + } + + if ($verbose) { + echo "\nGem update succesfull\n"; + } + } + + /** + * Generate a configuration file for a given directory + * + * @param string - folder name + */ + protected function generate_config($dir) + { + if (!is_file($dir . DIRECTORY_SEPARATOR . 'config.rb')) { + file_put_contents( + $dir . DIRECTORY_SEPARATOR . 'config.rb', + $this->customise(new ArrayData(array( + 'TmpDir' => self::get_temp_dir(), + 'Mode' => self::$syntax + )))->renderWith('CompassConfig') + ); + } + } + + /** + * Set the temp directory for storing the compass cache. Path should be a folder + * to filesystem which is writable by the web server. + * + * @param string + */ + public static function set_temp_dir($dir) + { + self::$temp_dir = $dir; + } + + /** + * Return the temp directory for storing the sass cache. Path should be writable + * by the web server. + * + * @return string + */ + public static function get_temp_dir() + { + if (!self::$temp_dir) { + return Controller::join_links(TEMP_FOLDER, '.sass-cache'); + } + + return self::$temp_dir; + } } /** @@ -384,21 +439,30 @@ public static function get_temp_dir() { * * @package compass */ -class Compass_RebuildDecorator extends DataExtension { - - public function init() { - // Don't auto-rebuild if explicitly disabled - if (Compass::$force_no_rebuild) return; - - // Don't auto-rebuild in test mode - $runningTest = class_exists('SapphireTest',false) && SapphireTest::is_running_test(); - if ($runningTest) return; +class Compass_RebuildDecorator extends DataExtension +{ + + public function init() + { + // Don't auto-rebuild if explicitly disabled + if (Compass::$force_no_rebuild) { + return; + } + + // Don't auto-rebuild in test mode + $runningTest = class_exists('SapphireTest', false) && SapphireTest::is_running_test(); + if ($runningTest) { + return; + } - // If we are in dev mode, or flush called, auto-rebuild - if (Director::isDev() || @$_GET['flush']) singleton('Compass')->rebuild(); - } - - public function contentcontrollerInit() { - return $this->init(); - } + // If we are in dev mode, or flush called, auto-rebuild + if (Director::isDev() || @$_GET['flush']) { + singleton('Compass')->rebuild(); + } + } + + public function contentcontrollerInit() + { + return $this->init(); + } } diff --git a/code/Rubygems.php b/code/Rubygems.php index 2b99ef7..1e6225d 100644 --- a/code/Rubygems.php +++ b/code/Rubygems.php @@ -3,177 +3,200 @@ /** * @package compass */ -class Rubygems extends Object { - - /** - * @var bool - is ruby available? - */ - private static $ruby_ok = null; +class Rubygems extends Object +{ + + /** + * @var bool - is ruby available? + */ + private static $ruby_ok = null; - /** - * @var bool - is the version of rubygems currently available good enough? - */ - private static $gem_version_ok = null; - - /** - * Get the path that gems live in, creating it if it doesn't exist . - * - * @return string - */ - private static function gem_path() { - $path = TEMP_FOLDER . '/gems'; - if (defined('SS_GEM_PATH')) $path = SS_GEM_PATH; - - if (!file_exists($path)) mkdir($path, 0770); - return $path; - } + /** + * @var bool - is the version of rubygems currently available good enough? + */ + private static $gem_version_ok = null; + + /** + * Get the path that gems live in, creating it if it doesn't exist . + * + * @return string + */ + private static function gem_path() + { + $path = TEMP_FOLDER . '/gems'; + if (defined('SS_GEM_PATH')) { + $path = SS_GEM_PATH; + } + + if (!file_exists($path)) { + mkdir($path, 0770); + } + return $path; + } - /** - * Internal helper function that calls an external executable - can't just - * use backticks, as we want stderr and stdout as separate variables - * - * Also sets this modules gem path into the environment of the external - * executable - * - * @param $cmd string - the command to run - * @param $stdout reference to string - the resultant stdout - * @param $stderr reference to string - the resultant stderr - * - * @return int - process exit code, or -1 if the process couldn't be executed - */ - protected static function _run($cmd, &$stdout, &$stderr) { - $descriptorspec = array( - 0 => array("pipe", "r"), // stdin is a pipe that the child will read from - 1 => array("pipe", "w"), // stdout is a pipe that the child will write to - 2 => array("pipe", "w") // stderr is a file to write to - ); - - $gempath = self::gem_path(); - $process = proc_open("HOME='$gempath' GEM_HOME='$gempath' " . (@$_GET['flush'] ? "FLUSH={$_GET['flush']} " : '') . $cmd, $descriptorspec, $pipes); - - $stdout = ""; - $stderr = ""; - - if (!is_resource($process)) return -1; + /** + * Internal helper function that calls an external executable - can't just + * use backticks, as we want stderr and stdout as separate variables + * + * Also sets this modules gem path into the environment of the external + * executable + * + * @param $cmd string - the command to run + * @param $stdout reference to string - the resultant stdout + * @param $stderr reference to string - the resultant stderr + * + * @return int - process exit code, or -1 if the process couldn't be executed + */ + protected static function _run($cmd, &$stdout, &$stderr) + { + $descriptorspec = array( + 0 => array("pipe", "r"), // stdin is a pipe that the child will read from + 1 => array("pipe", "w"), // stdout is a pipe that the child will write to + 2 => array("pipe", "w") // stderr is a file to write to + ); + + $gempath = self::gem_path(); + $process = proc_open("HOME='$gempath' GEM_HOME='$gempath' " . (@$_GET['flush'] ? "FLUSH={$_GET['flush']} " : '') . $cmd, $descriptorspec, $pipes); + + $stdout = ""; + $stderr = ""; + + if (!is_resource($process)) { + return -1; + } - fclose($pipes[0]); // close child's input immediately - stream_set_blocking($pipes[1],false); - stream_set_blocking($pipes[2],false); - - while (true) { - $read = array(); - $w = null; - $e = null; - - if (!feof($pipes[1])) $read[]= $pipes[1]; - if (!feof($pipes[2])) $read[]= $pipes[2]; - - if (!$read) break; - if (!stream_select($read, $w, $e, 120)) break; - - foreach ($read as $r) { - $s = fread($r,1024); - if ($r == $pipes[1]) $stdout .= $s; else $stderr .= $s; - } - } - - fclose($pipes[1]); - fclose($pipes[2]); - - return proc_close($process); - } - - /** - * Make sure a gem is available - * - * @param $gem string - the name of the gem to install - * @param $version string - the specific version to install - * @param $tryupdating bool - if the gem is present, check for update? (hits the internet, so slow) - * - * @return null | string - an error string on error, nothing on success - */ - public static function require_gem($gem, $version = null, $tryupdating = false) { - // Check that ruby exists - if (self::$ruby_ok === null) { - self::$ruby_ok = (bool)`which ruby`; - } - - if (!self::$ruby_ok) { - return 'Ruby isn\'t present. The "ruby" command needs to be in the webserver\'s path'; - } - - // Check that rubygems exists and is a good enough version - if (self::$gem_version_ok === null) { - $code = self::_run('gem environment version', $ver, $err); - - if ($code !== 0) { - return 'Ruby is present, but there was a problem accessing the \ + fclose($pipes[0]); // close child's input immediately + stream_set_blocking($pipes[1], false); + stream_set_blocking($pipes[2], false); + + while (true) { + $read = array(); + $w = null; + $e = null; + + if (!feof($pipes[1])) { + $read[]= $pipes[1]; + } + if (!feof($pipes[2])) { + $read[]= $pipes[2]; + } + + if (!$read) { + break; + } + if (!stream_select($read, $w, $e, 120)) { + break; + } + + foreach ($read as $r) { + $s = fread($r, 1024); + if ($r == $pipes[1]) { + $stdout .= $s; + } else { + $stderr .= $s; + } + } + } + + fclose($pipes[1]); + fclose($pipes[2]); + + return proc_close($process); + } + + /** + * Make sure a gem is available + * + * @param $gem string - the name of the gem to install + * @param $version string - the specific version to install + * @param $tryupdating bool - if the gem is present, check for update? (hits the internet, so slow) + * + * @return null | string - an error string on error, nothing on success + */ + public static function require_gem($gem, $version = null, $tryupdating = false) + { + // Check that ruby exists + if (self::$ruby_ok === null) { + self::$ruby_ok = (bool)`which ruby`; + } + + if (!self::$ruby_ok) { + return 'Ruby isn\'t present. The "ruby" command needs to be in the webserver\'s path'; + } + + // Check that rubygems exists and is a good enough version + if (self::$gem_version_ok === null) { + $code = self::_run('gem environment version', $ver, $err); + + if ($code !== 0) { + return 'Ruby is present, but there was a problem accessing the \ current rubygems version - is rubygems available? The "gem" \ command needs to be in the webserver\'s path.'; - } - - $vers = explode('.', $ver); - - self::$gem_version_ok = (($vers[0] >= 1 && $vers[1] >= 2) OR ($vers[0] >= 2)); - } + } + + $vers = explode('.', $ver); + + self::$gem_version_ok = (($vers[0] >= 1 && $vers[1] >= 2) or ($vers[0] >= 2)); + } - if (!self::$gem_version_ok) { - return "Rubygems is too old. You have version $ver, but we need at \ + if (!self::$gem_version_ok) { + return "Rubygems is too old. You have version $ver, but we need at \ least version 1.2. Please upgrade."; - } - - $veropt = $version ? "-v '$version'" : ''; + } + + $veropt = $version ? "-v '$version'" : ''; - // See if the gem exists. If not, try adding it - self::_run("gem list -i $gem $veropt", $out, $err); + // See if the gem exists. If not, try adding it + self::_run("gem list -i $gem $veropt", $out, $err); - if (trim($out) != 'true' || $tryupdating) { - $code = self::_run("gem install $gem $veropt --no-rdoc --no-ri", $out, $err); - - if ($code !== 0) { - return "Could not install required gem $gem. Either manually \ + if (trim($out) != 'true' || $tryupdating) { + $code = self::_run("gem install $gem $veropt --no-rdoc --no-ri", $out, $err); + + if ($code !== 0) { + return "Could not install required gem $gem. Either manually \ install, or repair error. Error message was: $err"; - } - } - } - - /** - * Execute a command provided by a gem - * - * @param string | array $gem - the name of the gem, or an array of names - * of gems, possibly associated with versions, to require. - * - * @param string $command - the name of the command - * @param string $args - arguments to pass to the command - * @param string $out - stdout result of the command - * @param string $err - stderr result of the command - * - * @return int - process exit code, or -1 if the process couldn't be executed - */ - public static function run($gems, $command, $args="", &$out, &$err) { - $reqs = array(); + } + } + } + + /** + * Execute a command provided by a gem + * + * @param string | array $gem - the name of the gem, or an array of names + * of gems, possibly associated with versions, to require. + * + * @param string $command - the name of the command + * @param string $args - arguments to pass to the command + * @param string $out - stdout result of the command + * @param string $err - stderr result of the command + * + * @return int - process exit code, or -1 if the process couldn't be executed + */ + public static function run($gems, $command, $args="", &$out, &$err) + { + $reqs = array(); - if (is_string($gems)) { - $reqs[] = "-e 'gem \"$gem\", \">= 0\"'"; - } else { - foreach ($gems as $gem => $version) { - if (!$version) { - $version = '>= 0'; - } - - $reqs[] = "-e 'gem \"$gem\", \"$version\"'"; - } - } - - $version = (isset($gems[$command])) ? $gems[$command] : ">= 0"; - $reqs = implode(' ', $reqs); + if (is_string($gems)) { + $reqs[] = "-e 'gem \"$gem\", \">= 0\"'"; + } else { + foreach ($gems as $gem => $version) { + if (!$version) { + $version = '>= 0'; + } + + $reqs[] = "-e 'gem \"$gem\", \"$version\"'"; + } + } + + $version = (isset($gems[$command])) ? $gems[$command] : ">= 0"; + $reqs = implode(' ', $reqs); - return self::_run( - sprintf("ruby -rubygems $reqs -e 'load Gem.bin_path(\"%s\", \"%s\", \"%s\")' -- $args", - $command, $command, $version - ), - $out, - $err - ); - } + return self::_run( + sprintf("ruby -rubygems $reqs -e 'load Gem.bin_path(\"%s\", \"%s\", \"%s\")' -- $args", + $command, $command, $version + ), + $out, + $err + ); + } }