diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4e86e28b88..65b15ca7b0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,16 @@
+# v0.9.7 beta
+## 11/24/2014
+
+1. [](#improved)
+ * Nginx configuration updated
+ * Added gitter.im badge to README
+ * Removed `set_time_limit()` and put checks around `ignore_user_abort`
+ * More PSR code fixes
+2. [](#bugfix)
+ * Fix issue with non-valid asset path showing up when they shouldn't
+ * Fix for JS asset pipeline and scripts that don't end in `;`
+ * Fix for schema-based markdown URLs broken routes (eg `mailto:`)
+
# v0.9.6 beta
## 11/17/2014
diff --git a/README.md b/README.md
index d9840d26db..676be93493 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,5 @@
# ![](https://avatars1.githubusercontent.com/u/8237355?v=2&s=50) Grav
+[![Gitter](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/getgrav/grav?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
Grav is a **Fast**, **Simple**, and **Flexible**, file-based Web-platform. There is **Zero** installation required. Just extract the ZIP archive, and you are already up and running. It follows similar principals to other flat-file CMS platforms, but has a different design philosophy than most. Grav comes with a powerful **Package Management System** to allow for simple installation and upgrading of plugins and themes, as well as simple updating of Grav itself.
diff --git a/nginx.conf b/nginx.conf
index e3e94f4c26..07295e4daa 100644
--- a/nginx.conf
+++ b/nginx.conf
@@ -27,26 +27,26 @@ http {
}
location /user {
- rewrite ^/user/accounts/(.*)$ /error redirect;
- rewrite ^/user/config/(.*)$ /error redirect;
- rewrite ^/user/(.*)\.(txt|md|html|php|yaml|json|twig|sh|bat)$ /error redirect;
- }
-
- location /cache {
- rewrite ^/cache/(.*) /error redirect;
- }
-
- location /bin {
- rewrite ^/bin/(.*)$ /error redirect;
- }
-
- location /system {
- rewrite ^/system/(.*)$ /error redirect;
- }
-
- location /vendor {
- rewrite ^/vendor/(.*)$ /error redirect;
- }
+ rewrite ^/user/accounts/(.*)$ /error redirect;
+ rewrite ^/user/config/(.*)$ /error redirect;
+ rewrite ^/user/(.*)\.(txt|md|html|php|yaml|json|twig|sh|bat)$ /error redirect;
+ }
+
+ location /cache {
+ rewrite ^/cache/(.*) /error redirect;
+ }
+
+ location /bin {
+ rewrite ^/bin/(.*)$ /error redirect;
+ }
+
+ location /system {
+ rewrite ^/system/(.*)\.(txt|md|html|php|yaml|json|twig|sh|bat)$ /error redirect;
+ }
+
+ location /vendor {
+ rewrite ^/vendor/(.*)\.(txt|md|html|php|yaml|json|twig|sh|bat)$ /error redirect;
+ }
# Remember to change 127.0.0.1:9000 to the Ip/port
# you configured php-cgi.exe to run from
diff --git a/system/config/system.yaml b/system/config/system.yaml
index 7cdc6703bb..88b9791991 100644
--- a/system/config/system.yaml
+++ b/system/config/system.yaml
@@ -1,4 +1,4 @@
-absolute_urls: false # Whether to use absolute URLs.
+absolute_urls: false # Absolute or relative URLs for `base_url`
home:
alias: '/home' # Default path for home, ie /
diff --git a/system/defines.php b/system/defines.php
index 38b114b95f..7760c13620 100644
--- a/system/defines.php
+++ b/system/defines.php
@@ -2,7 +2,7 @@
// Some standard defines
define('GRAV', true);
-define('GRAV_VERSION', '0.9.6');
+define('GRAV_VERSION', '0.9.7');
define('DS', '/');
// Directories and Paths
diff --git a/system/src/Grav/Common/Assets.php b/system/src/Grav/Common/Assets.php
index b23f1465e6..2270016ce5 100644
--- a/system/src/Grav/Common/Assets.php
+++ b/system/src/Grav/Common/Assets.php
@@ -17,10 +17,9 @@
*
* Based on stolz/assets (https://github.com/Stolz/Assets) package modified for use with Grav
*
- * @author RocketTheme
+ * @author RocketTheme
* @license MIT
*/
-
class Assets
{
use GravTrait;
@@ -53,6 +52,7 @@ class Assets
*
* The closure will receive as the only parameter a string with the path/URL of the asset and
* it should return the content of the asset file as a string.
+ *
* @var Closure
*/
protected $fetch_command;
@@ -86,22 +86,9 @@ class Assets
public function __construct(array $options = array())
{
// Forward config options
- if($options)
+ if ($options) {
$this->config((array)$options);
- }
-
- /**
- * Initialization called in the Grav lifecycle to initialize the Assets with appropriate configuration
- */
- public function init()
- {
- /** @var Config $config */
- $config = self::$grav['config'];
- $base_url = self::$grav['base_url'];
- $asset_config = (array) $config->get('system.assets');
-
- $this->config($asset_config);
- $this->base_url = $base_url . '/';
+ }
}
/**
@@ -111,50 +98,58 @@ public function init()
* Also, an extra option 'autoload' may be passed containing an array of
* assets and/or collections that will be automatically added on startup.
*
- * @param array $options Configurable options.
+ * @param array $config Configurable options.
+ *
* @return $this
* @throws \Exception
*/
public function config(array $config)
{
// Set pipeline modes
- if(isset($config['css_pipeline']))
+ if (isset($config['css_pipeline'])) {
$this->css_pipeline = $config['css_pipeline'];
+ }
- if(isset($config['js_pipeline']))
+ if (isset($config['js_pipeline'])) {
$this->js_pipeline = $config['js_pipeline'];
+ }
// Pipeline requires public dir
- if(($this->js_pipeline || $this->css_pipeline) && ! is_dir(ASSETS_DIR))
+ if (($this->js_pipeline || $this->css_pipeline) && !is_dir(ASSETS_DIR)) {
throw new \Exception('Assets: Public dir not found');
+ }
// Set custom pipeline fetch command
- if(isset($config['fetch_command']) and ($config['fetch_command'] instanceof Closure))
+ if (isset($config['fetch_command']) and ($config['fetch_command'] instanceof Closure)) {
$this->fetch_command = $config['fetch_command'];
+ }
// Set CSS Minify state
- if(isset($config['css_minify']))
+ if (isset($config['css_minify'])) {
$this->css_minify = $config['css_minify'];
+ }
- if(isset($config['css_minify_windows']))
+ if (isset($config['css_minify_windows'])) {
$this->css_minify_windows = $config['css_minify_windows'];
+ }
- if(isset($config['css_rewrite']))
+ if (isset($config['css_rewrite'])) {
$this->css_rewrite = $config['css_rewrite'];
+ }
// Set JS Minify state
- if(isset($config['js_minify']))
+ if (isset($config['js_minify'])) {
$this->js_minify = $config['js_minify'];
+ }
// Set collections
- if(isset($config['collections']) and is_array($config['collections']))
+ if (isset($config['collections']) and is_array($config['collections'])) {
$this->collections = $config['collections'];
+ }
// Autoload assets
- if(isset($config['autoload']) and is_array($config['autoload']))
- {
- foreach($config['autoload'] as $asset)
- {
+ if (isset($config['autoload']) and is_array($config['autoload'])) {
+ foreach ($config['autoload'] as $asset) {
$this->add($asset);
}
}
@@ -162,41 +157,51 @@ public function config(array $config)
return $this;
}
+ /**
+ * Initialization called in the Grav lifecycle to initialize the Assets with appropriate configuration
+ */
+ public function init()
+ {
+ /** @var Config $config */
+ $config = self::$grav['config'];
+ $base_url = self::$grav['base_url'];
+ $asset_config = (array)$config->get('system.assets');
+
+ $this->config($asset_config);
+ $this->base_url = $base_url . '/';
+ }
+
/**
* Add an asset or a collection of assets.
*
* It automatically detects the asset type (JavaScript, CSS or collection).
* You may add more than one asset passing an array as argument.
*
- * @param mixed $asset
- * @param int $priority the priority, bigger comes first
- * @param bool $pipeline false if this should not be pipelined
+ * @param mixed $asset
+ * @param int $priority the priority, bigger comes first
+ * @param bool $pipeline false if this should not be pipelined
+ *
* @return $this
*/
public function add($asset, $priority = 10, $pipeline = true)
{
// More than one asset
- if(is_array($asset))
- {
- foreach($asset as $a)
+ if (is_array($asset)) {
+ foreach ($asset as $a) {
$this->add($a, $priority, $pipeline);
- }
- // Collection
- elseif(isset($this->collections[$asset]))
- {
+ }
+ } elseif (isset($this->collections[$asset])) {
$this->add($this->collections[$asset], $priority, $pipeline);
- }
- else
- {
+ } else {
// JavaScript or CSS
$info = pathinfo($asset);
- if(isset($info['extension']))
- {
+ if (isset($info['extension'])) {
$ext = strtolower($info['extension']);
- if($ext === 'css')
+ if ($ext === 'css') {
$this->addCss($asset, $priority, $pipeline);
- elseif($ext === 'js')
+ } elseif ($ext === 'js') {
$this->addJs($asset, $priority, $pipeline);
+ }
}
}
@@ -204,103 +209,117 @@ public function add($asset, $priority = 10, $pipeline = true)
}
/**
- * Add an inline CSS asset.
+ * Add a CSS asset.
*
* It checks for duplicates.
- * For adding chunks of string-based inline CSS
+ * You may add more than one asset passing an array as argument.
+ *
+ * @param mixed $asset
+ * @param int $priority the priority, bigger comes first
+ * @param bool $pipeline false if this should not be pipelined
*
- * @param mixed $asset
- * @param int $priority the priority, bigger comes first
* @return $this
*/
- public function addInlineCss($asset, $priority = 10) {
+ public function addCss($asset, $priority = 10, $pipeline = true)
+ {
+ if (is_array($asset)) {
+ foreach ($asset as $a) {
+ $this->addCss($a, $priority, $pipeline);
+ }
- if (is_string($asset) && !in_array($asset, $this->inline_css)) {
- $this->inline_css[] = $asset;
+ return $this;
+ }
+
+ if (!$this->isRemoteLink($asset)) {
+ $asset = $this->buildLocalLink($asset);
+ }
+
+ if ($asset && !array_key_exists($asset, $this->css)) {
+ $this->css[$asset] = [
+ 'asset' => $asset,
+ 'priority' => $priority,
+ 'order' => count($this->css),
+ 'pipeline' => $pipeline
+ ];
}
return $this;
}
/**
- * Add a CSS asset.
+ * Add a JavaScript asset.
*
* It checks for duplicates.
* You may add more than one asset passing an array as argument.
*
- * @param mixed $asset
- * @param int $priority the priority, bigger comes first
- * @param bool $pipeline false if this should not be pipelined
+ * @param mixed $asset
+ * @param int $priority the priority, bigger comes first
+ * @param bool $pipeline false if this should not be pipelined
+ *
* @return $this
*/
- public function addCss($asset, $priority = 10, $pipeline = true)
+ public function addJs($asset, $priority = 10, $pipeline = true)
{
- if(is_array($asset))
- {
- foreach($asset as $a)
- $this->addCss($a, $priority, $pipeline);
+ if (is_array($asset)) {
+ foreach ($asset as $a) {
+ $this->addJs($a, $priority, $pipeline);
+ }
return $this;
}
- if( !$this->isRemoteLink($asset)) {
+ if (!$this->isRemoteLink($asset)) {
$asset = $this->buildLocalLink($asset);
}
- if( !array_key_exists($asset, $this->css)) {
- $this->css[$asset] = ['asset'=>$asset, 'priority'=>$priority, 'order' => count($this->css), 'pipeline'=>$pipeline];
+ if ($asset && !array_key_exists($asset, $this->js)) {
+ $this->js[$asset] = [
+ 'asset' => $asset,
+ 'priority' => $priority,
+ 'order' => count($this->js),
+ 'pipeline' => $pipeline
+ ];
}
return $this;
}
/**
- * Add an inline JS asset.
+ * Add an inline CSS asset.
*
* It checks for duplicates.
- * For adding chunks of string-based inline JS
+ * For adding chunks of string-based inline CSS
+ *
+ * @param mixed $asset
+ * @param int $priority the priority, bigger comes first
*
- * @param mixed $asset
- * @param int $priority the priority, bigger comes first
* @return $this
*/
- public function addInlineJs($asset) {
+ public function addInlineCss($asset, $priority = 10)
+ {
- if (is_string($asset) && !in_array($asset, $this->inline_js)) {
- $this->inline_js[] = $asset;
+ if (is_string($asset) && !in_array($asset, $this->inline_css)) {
+ $this->inline_css[] = $asset;
}
return $this;
}
/**
- * Add a JavaScript asset.
+ * Add an inline JS asset.
*
* It checks for duplicates.
- * You may add more than one asset passing an array as argument.
+ * For adding chunks of string-based inline JS
+ *
+ * @param mixed $asset
*
- * @param mixed $asset
- * @param int $priority the priority, bigger comes first
- * @param bool $pipeline false if this should not be pipelined
* @return $this
*/
- public function addJs($asset, $priority = 10, $pipeline = true)
+ public function addInlineJs($asset)
{
- if(is_array($asset))
- {
- foreach($asset as $a)
- $this->addJs($a, $priority, $pipeline);
- return $this;
- }
-
- if( !$this->isRemoteLink($asset)) {
- $asset = $this->buildLocalLink($asset);
- }
-
- if( !array_key_exists($asset, $this->js)) {
-
- $this->js[$asset] = ['asset' => $asset, 'priority' => $priority, 'order' => count($this->js), 'pipeline' => $pipeline];
+ if (is_string($asset) && !in_array($asset, $this->inline_js)) {
+ $this->inline_js[] = $asset;
}
return $this;
@@ -309,36 +328,38 @@ public function addJs($asset, $priority = 10, $pipeline = true)
/**
* Build the CSS link tags.
*
- * @param array $attributes
+ * @param array $attributes
+ *
* @return string
*/
public function css($attributes = [])
{
- if( ! $this->css)
+ if (!$this->css) {
return null;
-
- if (self::$grav)
+ }
// Sort array by priorities (larger priority first)
- usort($this->css, function ($a, $b) {
- if ($a['priority'] == $b['priority']) {
- return $b['order'] - $a['order'];
- }
- return $a['priority'] - $b['priority'];
- });
+ if (self::$grav) {
+ usort($this->css, function ($a, $b) {
+ if ($a['priority'] == $b['priority']) {
+ return $b['order'] - $a['order'];
+ }
+ return $a['priority'] - $b['priority'];
+ });
+ }
$this->css = array_reverse($this->css);
- $attributes = $this->attributes(array_merge([ 'type' => 'text/css', 'rel' => 'stylesheet' ], $attributes));
+ $attributes = $this->attributes(array_merge(['type' => 'text/css', 'rel' => 'stylesheet'], $attributes));
$output = '';
- if($this->css_pipeline) {
- $output .= ''."\n";
+ if ($this->css_pipeline) {
+ $output .= '' . "\n";
foreach ($this->css_no_pipeline as $file) {
- $output .= ''."\n";
+ $output .= '' . "\n";
}
} else {
- foreach($this->css as $file) {
+ foreach ($this->css as $file) {
$output .= '' . "\n";
}
}
@@ -346,7 +367,7 @@ public function css($attributes = [])
// Render Inline CSS
if (count($this->inline_css) > 0) {
$output .= "\n";
@@ -359,13 +380,15 @@ public function css($attributes = [])
/**
* Build the JavaScript script tags.
*
- * @param array $attributes
+ * @param array $attributes
+ *
* @return string
*/
public function js($attributes = [])
{
- if( ! $this->js)
+ if (!$this->js) {
return null;
+ }
// Sort array by priorities (larger priority first)
usort($this->js, function ($a, $b) {
@@ -376,16 +399,16 @@ public function js($attributes = [])
});
$this->js = array_reverse($this->js);
- $attributes = $this->attributes(array_merge([ 'type' => 'text/javascript' ], $attributes));
+ $attributes = $this->attributes(array_merge(['type' => 'text/javascript'], $attributes));
$output = '';
- if($this->js_pipeline) {
- $output .= ''."\n";
+ if ($this->js_pipeline) {
+ $output .= '' . "\n";
foreach ($this->js_no_pipeline as $file) {
- $output .= ''."\n";
+ $output .= '' . "\n";
}
} else {
- foreach($this->js as $file) {
+ foreach ($this->js as $file) {
$output .= '' . "\n";
}
}
@@ -393,7 +416,7 @@ public function js($attributes = [])
// Render Inline JS
if (count($this->inline_js) > 0) {
$output .= "\n";
@@ -402,77 +425,7 @@ public function js($attributes = [])
return $output;
}
- /**
- * Build an HTML attribute string from an array.
- *
- * @param array $attributes
- * @return string
- */
- protected function attributes(array $attributes){
- $html = '';
-
- foreach ( $attributes as $key => $value)
- {
- // For numeric keys we will assume that the key and the value are the same
- // as this will convert HTML attributes such as "required" to a correct
- // form like required="required" instead of using incorrect numerics.
- if (is_numeric($key)) $key = $value;
- if (is_array($value)) $value = implode(' ', $value);
-
- $element = $key.'="'.htmlentities($value, ENT_QUOTES, 'UTF-8', false).'"';
- $html .= ' '.$element;
- }
-
- return $html;
- }
-
- /**
- * Add/replace collection.
- *
- * @param string $collectionName
- * @param array $assets
- * @return $this
- */
- public function registerCollection($collectionName, Array $assets)
- {
- $this->collections[$collectionName] = $assets;
-
- return $this;
- }
-
- /**
- * Reset all assets.
- *
- * @return $this
- */
- public function reset()
- {
- return $this->resetCss()->resetJs();
- }
-
- /**
- * Reset CSS assets.
- *
- * @return $this
- */
- public function resetCss()
- {
- $this->css = array();
- return $this;
- }
-
- /**
- * Reset JavaScript assets.
- *
- * @return $this
- */
- public function resetJs()
- {
- $this->js = array();
-
- return $this;
- }
/**
* Minifiy and concatenate CSS / JS files.
@@ -483,7 +436,7 @@ protected function pipeline($css = true)
{
/** @var Cache $cache */
$cache = self::$grav['cache'];
- $key = '?'.$cache->getKey();
+ $key = '?' . $cache->getKey();
if ($css) {
$file = md5(json_encode($this->css) . $this->js_minify . $this->css_minify . $this->css_rewrite) . '.css';
@@ -503,12 +456,13 @@ protected function pipeline($css = true)
}
}
- $relative_path = "{$this->base_url}".basename(ASSETS_DIR)."/{$file}";
- $absolute_path = ASSETS_DIR.$file;
+ $relative_path = "{$this->base_url}" . basename(ASSETS_DIR) . "/{$file}";
+ $absolute_path = ASSETS_DIR . $file;
// If pipeline exist return it
- if(file_exists($absolute_path))
+ if (file_exists($absolute_path)) {
return $relative_path . $key;
+ }
$css_minify = $this->css_minify;
@@ -540,145 +494,52 @@ protected function pipeline($css = true)
}
/**
- * Download and concatenate the content of several links.
+ * Add/replace collection.
*
- * @param array $links
- * @return string
- */
- protected function gatherLinks(array $links, $css = true)
- {
-
-
- $buffer = '';
- $local = true;
-
- foreach($links as $asset)
- {
- $link = $asset['asset'];
- $relative_path = $link;
-
- if($this->isRemoteLink($link)) {
- $local = false;
- if('//' === substr($link, 0, 2))
- $link = 'http:' . $link;
- } else {
- // Fix to remove relative dir if grav is in one
- if (($this->base_url != '/') && (strpos($this->base_url, $link) == 0)) {
- $relative_path = str_replace($this->base_url, '/', $link);
- }
-
- $relative_dir = dirname ($relative_path);
- $link = ROOT_DIR . $relative_path;
- }
-
- $file = ($this->fetch_command instanceof Closure) ? $this->fetch_command->__invoke($link) : file_get_contents($link);
-
- // If this is CSS + the file is local + rewrite enabled
- if ($css && $local && $this->css_rewrite) {
- $file = $this->cssRewrite($file, $relative_dir);
- }
-
- $buffer .= $file;
- }
-
- // Pull out @imports and move to top
- if ($css) {
- $buffer = $this->moveImports($buffer);
- }
-
- return $buffer;
- }
-
- /**
- * Moves @import statements to the top of the file per the CSS specification
+ * @param string $collectionName
+ * @param array $assets
*
- * @param string $file the file containing the combined CSS files
- * @return string the modified file with any @imports at the top of the file
+ * @return $this
*/
- protected function moveImports($file)
+ public function registerCollection($collectionName, Array $assets)
{
- $this->imports = array();
-
- $file = preg_replace_callback(self::CSS_IMPORT_REGEX,
- function($matches) {
- $this->imports[] = $matches[0];
- return '';
- },
- $file
- );
+ $this->collections[$collectionName] = $assets;
- return implode("\n", $this->imports) . "\n\n" . $file;
+ return $this;
}
/**
- * Finds relative CSS urls() and rewrites the URL with an absolute one
- * @param string $file the css source file
- * @param string $relative_path relative path to the css file
- * @return [type] [description]
+ * Reset all assets.
+ *
+ * @return $this
*/
- protected function cssRewrite($file, $relative_path)
+ public function reset()
{
- // Strip any sourcemap comments
- $file = preg_replace(self::CSS_SOURCEMAP_REGEX, '', $file);
-
- // Find any css url() elements, grab the URLs and calculate an absolute path
- // Then replace the old url with the new one
- $file = preg_replace_callback(self::CSS_URL_REGEX,
- function($matches) use ($relative_path) {
-
- $old_url = $matches[1];
-
- // ensure this is not a data url
- if (strpos($old_url, 'data:') === 0) return $matches[0];
-
- $newpath = array();
- $paths = explode('/', $old_url);
-
- foreach ($paths as $path) {
- if ($path == '..') {
- $relative_path = dirname($relative_path);
- } else {
- $newpath[] = $path;
- }
- }
-
- $new_url = rtrim($this->base_url,'/') . $relative_path . '/' . implode('/', $newpath);
-
- return str_replace($old_url, $new_url, $matches[0]);
- },
- $file
- );
-
- return $file;
+ return $this->resetCss()->resetJs();
}
/**
- * Build local links including grav asset shortcodes
+ * Reset JavaScript assets.
*
- * @param string $asset the asset string reference
- * @return string the final link url to the asset
+ * @return $this
*/
- protected function buildLocalLink($asset)
+ public function resetJs()
{
- try {
- $asset = self::$grav['locator']->findResource($asset, false);
- } catch (\Exception $e) {}
+ $this->js = array();
- return $this->base_url . ltrim($asset, '/');
+ return $this;
}
-
/**
- * Determine whether a link is local or remote.
- *
- * Undestands both "http://" and "https://" as well as protocol agnostic links "//"
+ * Reset CSS assets.
*
- * @param string $link
- * @return bool
+ * @return $this
*/
- protected function isRemoteLink($link)
+ public function resetCss()
{
- return ('http://' === substr($link, 0, 7) or 'https://' === substr($link, 0, 8) or '//' === substr($link, 0, 2));
+ $this->css = array();
+
+ return $this;
}
/**
@@ -701,52 +562,64 @@ public function getJs()
return $this->js;
}
+ /**
+ * Add all CSS assets within $directory (relative to public dir).
+ *
+ * @param string $directory Relative to $this->public_dir
+ *
+ * @return $this
+ */
+ public function addDirCss($directory)
+ {
+ return $this->addDir($directory, self::CSS_REGEX);
+ }
+
/**
* Add all assets matching $pattern within $directory.
*
* @param string $directory Relative to $this->public_dir
- * @param string $pattern (regex)
+ * @param string $pattern (regex)
+ *
* @return $this
* @throws Exception
*/
public function addDir($directory, $pattern = self::DEFAULT_REGEX)
{
// Check if public_dir exists
- if( ! is_dir(ASSETS_DIR))
+ if (!is_dir(ASSETS_DIR)) {
throw new Exception('Assets: Public dir not found');
+ }
// Get files
$files = $this->rglob(ASSETS_DIR . DIRECTORY_SEPARATOR . $directory, $pattern, ASSETS_DIR);
// No luck? Nothing to do
- if( ! $files)
+ if (!$files) {
return $this;
+ }
// Add CSS files
- if($pattern === self::CSS_REGEX)
- {
+ if ($pattern === self::CSS_REGEX) {
$this->css = array_unique(array_merge($this->css, $files));
return $this;
}
// Add JavaScript files
- if($pattern === self::JS_REGEX)
- {
+ if ($pattern === self::JS_REGEX) {
$this->js = array_unique(array_merge($this->js, $files));
return $this;
}
// Unknown pattern. We must poll to know the extension :(
- foreach($files as $asset)
- {
+ foreach ($files as $asset) {
$info = pathinfo($asset);
- if(isset($info['extension']))
- {
+ if (isset($info['extension'])) {
$ext = strtolower($info['extension']);
- if($ext === 'css' and ! in_array($asset, $this->css))
+ if ($ext === 'css' and !in_array($asset, $this->css)) {
$this->css[] = $asset;
- elseif($ext === 'js' and ! in_array($asset, $this->js))
+ } elseif ($ext === 'js' and !in_array($asset, $this->js)) {
$this->js[] = $asset;
+ }
}
}
@@ -754,25 +627,190 @@ public function addDir($directory, $pattern = self::DEFAULT_REGEX)
}
/**
- * Add all CSS assets within $directory (relative to public dir).
+ * Determine whether a link is local or remote.
*
- * @param string $directory Relative to $this->public_dir
- * @return $this
+ * Undestands both "http://" and "https://" as well as protocol agnostic links "//"
+ *
+ * @param string $link
+ *
+ * @return bool
*/
- public function addDirCss($directory)
+ protected function isRemoteLink($link)
{
- return $this->addDir($directory, self::CSS_REGEX);
+ return ('http://' === substr($link, 0, 7) or 'https://' === substr($link, 0, 8)
+ or '//' === substr($link, 0, 2));
}
/**
- * Add all JavaScript assets within $directory.
+ * Build local links including grav asset shortcodes
*
- * @param string $directory Relative to $this->public_dir
- * @return $this
+ * @param string $asset the asset string reference
+ *
+ * @return string the final link url to the asset
*/
- public function addDirJs($directory)
+ protected function buildLocalLink($asset)
{
- return $this->addDir($directory, self::JS_REGEX);
+ try {
+ $asset = self::$grav['locator']->findResource($asset, false);
+ } catch (\Exception $e) {
+ }
+
+ return $asset ? $this->base_url . ltrim($asset, '/') : false;
+ }
+
+ /**
+ * Build an HTML attribute string from an array.
+ *
+ * @param array $attributes
+ *
+ * @return string
+ */
+ protected function attributes(array $attributes)
+ {
+ $html = '';
+
+ foreach ($attributes as $key => $value) {
+ // For numeric keys we will assume that the key and the value are the same
+ // as this will convert HTML attributes such as "required" to a correct
+ // form like required="required" instead of using incorrect numerics.
+ if (is_numeric($key)) {
+ $key = $value;
+ }
+ if (is_array($value)) {
+ $value = implode(' ', $value);
+ }
+
+ $element = $key . '="' . htmlentities($value, ENT_QUOTES, 'UTF-8', false) . '"';
+ $html .= ' ' . $element;
+ }
+
+ return $html;
+ }
+
+ /**
+ * Download and concatenate the content of several links.
+ *
+ * @param array $links
+ *
+ * @return string
+ */
+ protected function gatherLinks(array $links, $css = true)
+ {
+
+
+ $buffer = '';
+ $local = true;
+
+ foreach ($links as $asset) {
+ $link = $asset['asset'];
+ $relative_path = $link;
+
+ if ($this->isRemoteLink($link)) {
+ $local = false;
+ if ('//' === substr($link, 0, 2)) {
+ $link = 'http:' . $link;
+ }
+ } else {
+ // Fix to remove relative dir if grav is in one
+ if (($this->base_url != '/') && (strpos($this->base_url, $link) == 0)) {
+ $relative_path = str_replace($this->base_url, '/', $link);
+ }
+
+ $relative_dir = dirname($relative_path);
+ $link = ROOT_DIR . $relative_path;
+ }
+
+ $file = ($this->fetch_command instanceof Closure) ? $this->fetch_command->__invoke($link) : file_get_contents($link);
+
+ // Double check last character being
+ if (!$css) {
+ $file = rtrim($file, ' ;') . ';';
+ }
+
+ // If this is CSS + the file is local + rewrite enabled
+ if ($css && $local && $this->css_rewrite) {
+ $file = $this->cssRewrite($file, $relative_dir);
+ }
+
+ $buffer .= $file;
+ }
+
+ // Pull out @imports and move to top
+ if ($css) {
+ $buffer = $this->moveImports($buffer);
+ }
+
+ return $buffer;
+ }
+
+ /**
+ * Finds relative CSS urls() and rewrites the URL with an absolute one
+ *
+ * @param $file the css source file
+ * @param $relative_path relative path to the css file
+ *
+ * @return mixed
+ */
+ protected function cssRewrite($file, $relative_path)
+ {
+ // Strip any sourcemap comments
+ $file = preg_replace(self::CSS_SOURCEMAP_REGEX, '', $file);
+
+ // Find any css url() elements, grab the URLs and calculate an absolute path
+ // Then replace the old url with the new one
+ $file = preg_replace_callback(
+ self::CSS_URL_REGEX,
+ function ($matches) use ($relative_path) {
+
+ $old_url = $matches[1];
+
+ // ensure this is not a data url
+ if (strpos($old_url, 'data:') === 0) {
+ return $matches[0];
+ }
+
+ $newpath = array();
+ $paths = explode('/', $old_url);
+
+ foreach ($paths as $path) {
+ if ($path == '..') {
+ $relative_path = dirname($relative_path);
+ } else {
+ $newpath[] = $path;
+ }
+ }
+
+ $new_url = rtrim($this->base_url, '/') . $relative_path . '/' . implode('/', $newpath);
+
+ return str_replace($old_url, $new_url, $matches[0]);
+ },
+ $file
+ );
+
+ return $file;
+ }
+
+ /**
+ * Moves @import statements to the top of the file per the CSS specification
+ *
+ * @param string $file the file containing the combined CSS files
+ *
+ * @return string the modified file with any @imports at the top of the file
+ */
+ protected function moveImports($file)
+ {
+ $this->imports = array();
+
+ $file = preg_replace_callback(
+ self::CSS_IMPORT_REGEX,
+ function ($matches) {
+ $this->imports[] = $matches[0];
+ return '';
+ },
+ $file
+ );
+
+ return implode("\n", $this->imports) . "\n\n" . $file;
}
/**
@@ -780,24 +818,52 @@ public function addDirJs($directory)
*
* @param string $directory
* @param string $pattern (regex)
- * @param string $ltrim Will be trimed from the left of the file path
+ * @param string $ltrim Will be trimed from the left of the file path
+ *
* @return array
*/
protected function rglob($directory, $pattern, $ltrim = null)
{
- $iterator = new RegexIterator(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory, FilesystemIterator::SKIP_DOTS)), $pattern);
+ $iterator = new RegexIterator(
+ new RecursiveIteratorIterator(
+ new RecursiveDirectoryIterator(
+ $directory,
+ FilesystemIterator::SKIP_DOTS
+ )
+ ),
+ $pattern
+ );
$offset = strlen($ltrim);
$files = array();
- foreach($iterator as $file)
+ foreach ($iterator as $file) {
$files[] = substr($file->getPathname(), $offset);
+ }
return $files;
}
+ /**
+ * Add all JavaScript assets within $directory.
+ *
+ * @param string $directory Relative to $this->public_dir
+ *
+ * @return $this
+ */
+ public function addDirJs($directory)
+ {
+ return $this->addDir($directory, self::JS_REGEX);
+ }
+
+ public function __toString()
+ {
+ return '';
+ }
+
/**
* @param $a
* @param $b
+ *
* @return mixed
*/
protected function priorityCompare($a, $b)
@@ -805,8 +871,4 @@ protected function priorityCompare($a, $b)
return $a ['priority'] - $b ['priority'];
}
- public function __toString() {
- return '';
- }
-
}
diff --git a/system/src/Grav/Common/Grav.php b/system/src/Grav/Common/Grav.php
index ad02ad8dd8..b052ff3a7c 100644
--- a/system/src/Grav/Common/Grav.php
+++ b/system/src/Grav/Common/Grav.php
@@ -305,8 +305,10 @@ public function fireEvent($eventName, Event $event = null)
public function shutdown()
{
if ($this['config']->get('system.debugger.shutdown.close_connection')) {
- set_time_limit(0);
- ignore_user_abort(true);
+
+ if (function_exists('ignore_user_abort')) {
+ @ignore_user_abort(true);
+ }
if (isset($this['session'])) {
$this['session']->close();
diff --git a/system/src/Grav/Common/Markdown/MarkdownGravLinkTrait.php b/system/src/Grav/Common/Markdown/MarkdownGravLinkTrait.php
index a1ab41906d..80e051a366 100644
--- a/system/src/Grav/Common/Markdown/MarkdownGravLinkTrait.php
+++ b/system/src/Grav/Common/Markdown/MarkdownGravLinkTrait.php
@@ -29,8 +29,8 @@ protected function identifyLink($Excerpt)
$url = parse_url(htmlspecialchars_decode($Excerpt['element']['attributes']['href']));
- // if there is no host set but there is a path, the file is local
- if (!isset($url['host']) && isset($url['path'])) {
+ // if there is no scheme, the file is local
+ if (!isset($url['scheme'])) {
// convert the URl is required
$Excerpt['element']['attributes']['href'] = $this->convertUrl(Uri::build_url($url));