diff --git a/.gitignore b/.gitignore
index d436576ff..da248e548 100644
--- a/.gitignore
+++ b/.gitignore
@@ -7,6 +7,8 @@ vendor/
.sass-cache
# Grav Specific
+backup/*
+!backup/.*
cache/*
!cache/.*
assets/*
diff --git a/.htaccess b/.htaccess
index ce48b2674..ecf1097c0 100644
--- a/.htaccess
+++ b/.htaccess
@@ -44,7 +44,7 @@ RewriteRule .* index.php [L]
## Begin - Security
# Block all direct access for these folders
-RewriteRule ^(cache|bin|logs)/(.*) error [L]
+RewriteRule ^(cache|bin|logs|backup)/(.*) error [L]
# Block access to specific file types for these folders
RewriteRule ^(system|user|vendor)/(.*)\.(txt|md|html|yaml|php|twig|sh|bat)$ error [L]
## End - Security
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6ce9860c7..7e914ad1b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,32 @@
+# v0.9.27
+## 05/09/2015
+
+1. [](#new)
+ * Added new composer CLI command
+ * Added page-level summary header overrides
+ * Added `size` back for Media objects
+ * Refactored Backup command in preparation for admin plugin
+ * Added a new `parseLinks` method to Plugins class
+ * Added `starts_with` and `ends_with` Twig filters
+2. [](#improved)
+ * Opitmized install of vendor libraries for speed improvement
+ * Improved configuration handling in preparation for admin plugin
+ * Cache optimization: Don't cache Twig templates when you pass dynamic params
+ * Moved `Utils::rcopy` to `Folder::rcopy`
+ * Improved `Folder::doDelete`
+ * Added check for required Curl in GPM
+ * Updated included composer.phar to latest version
+ * Various blueprint fixes for admin plugin
+ * Various PSR and code cleanup tasks
+3. [](#bugfix)
+ * Fix issue with Gzip not working with `onShutDown()` event
+ * Fix for URLs with trailing slashes
+ * Handle condition where certain errors resulted in blank page
+ * Fix for issue with theme name equal to base_url and asset pipeline
+ * Fix to properly nomralize font rewrite path
+ * Fix for absolute URLs below the current page
+ * Fix for `..` page references
+
# v0.9.26
## 04/24/2015
diff --git a/backup/.gitkeep b/backup/.gitkeep
new file mode 100644
index 000000000..e69de29bb
diff --git a/bin/composer.phar b/bin/composer.phar
index 31b9d3915..a08ea4eba 100755
Binary files a/bin/composer.phar and b/bin/composer.phar differ
diff --git a/bin/gpm b/bin/gpm
index 803722981..a19fea5d1 100755
--- a/bin/gpm
+++ b/bin/gpm
@@ -9,7 +9,7 @@ if (version_compare($ver = PHP_VERSION, $req = '5.4.0', '<')) {
if (!file_exists(__DIR__ . '/../vendor')){
// Before we can even start, we need to run composer first
echo "Preparing to install vendor dependencies...\n\n";
- echo system('php bin/composer.phar --working-dir="'.__DIR__.'/../" --no-interaction install');
+ echo system('php bin/composer.phar --working-dir="'.__DIR__.'/../" --no-interaction --no-dev --prefer-dist -o install');
echo "\n\n";
}
@@ -26,6 +26,10 @@ if (!file_exists(ROOT_DIR . 'index.php')) {
exit('FATAL: Must be run from ROOT directory of Grav!');
}
+if (!function_exists('curl_version')) {
+ exit('FATAL: GPM requires PHP Curl module to be installed');
+}
+
$grav = Grav::instance(array('loader' => $autoload));
$grav['config']->init();
$grav['streams'];
diff --git a/bin/grav b/bin/grav
index 2b754f153..1d31dd687 100755
--- a/bin/grav
+++ b/bin/grav
@@ -9,7 +9,7 @@ if (version_compare($ver = PHP_VERSION, $req = '5.4.0', '<')) {
if (!file_exists(__DIR__ . '/../vendor')){
// Before we can even start, we need to run composer first
echo "Preparing to install vendor dependencies...\n\n";
- echo system('php bin/composer.phar --working-dir="'.__DIR__.'/../" --no-interaction install');
+ echo system('php bin/composer.phar --working-dir="'.__DIR__.'/../" --no-interaction --no-dev --prefer-dist -o install');
echo "\n\n";
}
@@ -28,6 +28,7 @@ if (!file_exists(ROOT_DIR . 'index.php')) {
$app = new Application('Grav CLI Application', '0.1.0');
$app->addCommands(array(
new Grav\Console\Cli\InstallCommand(),
+ new Grav\Console\Cli\ComposerCommand(),
new Grav\Console\Cli\SandboxCommand(),
new Grav\Console\Cli\CleanCommand(),
new Grav\Console\Cli\ClearCacheCommand(),
diff --git a/nginx.conf b/nginx.conf
index 12a48054a..afa450c23 100644
--- a/nginx.conf
+++ b/nginx.conf
@@ -25,7 +25,7 @@ http {
index index.php;
if (!-e $request_filename){ rewrite ^(.*)$ /index.php last; }
}
-
+
location /images/ {
# Serve images as static
}
@@ -44,6 +44,10 @@ http {
rewrite ^/bin/(.*)$ /error redirect;
}
+ location /backup {
+ rewrite ^/backup/(.*) /error redirect;
+ }
+
location /system {
rewrite ^/system/(.*)\.(txt|md|html|php|yaml|json|twig|sh|bat)$ /error redirect;
}
diff --git a/system/blueprints/config/site.yaml b/system/blueprints/config/site.yaml
index 9ec49f43d..604be106d 100644
--- a/system/blueprints/config/site.yaml
+++ b/system/blueprints/config/site.yaml
@@ -24,6 +24,8 @@ form:
type: text
size: large
label: Default Email
+ validate:
+ type: email
taxonomies:
type: text
diff --git a/system/blueprints/config/system.yaml b/system/blueprints/config/system.yaml
index 2a3f0119f..071a5c1b2 100644
--- a/system/blueprints/config/system.yaml
+++ b/system/blueprints/config/system.yaml
@@ -27,7 +27,7 @@ form:
label: Default Theme
help: "Set the theme (defaults to 'default')"
- pages.markdown_extra:
+ pages.markdown.extra:
type: toggle
label: Markdown Extra
highlight: 1
@@ -101,6 +101,16 @@ form:
validate:
type: number
min: 1
+ pages.publish_dates:
+ type: toggle
+ label: Date-based publishing
+ help: Automatically (un)publish posts based on their date
+ highlight: 1
+ options:
+ 1: Yes
+ 0: No
+ validate:
+ type: bool
@@ -175,6 +185,36 @@ form:
label: Cache Prefix
placeholder: "Derived from base URL (override by entering random string)"
+ cache.gzip:
+ type: toggle
+ label: GZIP compression
+ highlight: 1
+ options:
+ 1: Yes
+ 0: No
+ validate:
+ type: bool
+
+ assets.enable_asset_timestamp:
+ type: toggle
+ label: Enable timestamps on assets
+ highlight: 1
+ options:
+ 1: Yes
+ 0: No
+ validate:
+ type: bool
+
+ media.enable_media_timestamp:
+ type: toggle
+ label: Enable timestamps on media
+ highlight: 1
+ options:
+ 1: Yes
+ 0: No
+ validate:
+ type: bool
+
twig:
type: section
title: Twig Templating
@@ -287,6 +327,32 @@ form:
validate:
type: bool
+ errors:
+ type: section
+ title: Error handler
+ underline: true
+
+ fields:
+ errors.display:
+ type: toggle
+ label: Display errors
+ highlight: 1
+ options:
+ 1: Yes
+ 0: No
+ validate:
+ type: bool
+
+ errors.log:
+ type: toggle
+ label: Log errors
+ highlight: 1
+ options:
+ 1: Yes
+ 0: No
+ validate:
+ type: bool
+
debugger:
type: section
title: Debugger
@@ -362,3 +428,59 @@ form:
0: No
validate:
type: bool
+
+ media:
+ type: section
+ title: Media
+ underline: true
+
+ fields:
+ images.default_image_quality:
+ type: text
+ label: Default image quality
+ classes: x-small
+ validate:
+ type: number
+ min: 1
+ max: 100
+
+ images.debug:
+ type: toggle
+ label: Image debug watermark
+ highlight: 1
+ options:
+ 1: Yes
+ 0: No
+ validate:
+ type: bool
+
+ media.upload_limit:
+ type: text
+ label: File upload limit
+ classes: small
+ validate:
+ type: number
+
+ system:
+ type: section
+ title: System
+ underline: true
+
+ fields:
+ timezone:
+ type: select
+ label: Timezone
+ classes: fancy
+ @data-options: '\Grav\Common\Utils::timezones'
+ default: ''
+ options:
+ '': '- None -'
+
+ param_sep:
+ type: select
+ label: Parameter separator
+ classes: fancy
+ default: ''
+ options:
+ ':': ': (default)'
+ ';': '; (use this for apache on Windows)'
diff --git a/system/blueprints/pages/modular_raw.yaml b/system/blueprints/pages/modular_raw.yaml
index 8e008ac60..f7877772f 100644
--- a/system/blueprints/pages/modular_raw.yaml
+++ b/system/blueprints/pages/modular_raw.yaml
@@ -61,7 +61,7 @@ form:
@data-options: '\Grav\Common\Page\Pages::parents'
@data-default: '\Grav\Plugin\admin::route'
options:
- '/': '- Root -'
+ '': '- Select -'
validate:
required: true
diff --git a/system/blueprints/user/account.yaml b/system/blueprints/user/account.yaml
index 821d5d7bc..7ed36145a 100644
--- a/system/blueprints/user/account.yaml
+++ b/system/blueprints/user/account.yaml
@@ -28,8 +28,6 @@ form:
validate:
required: true
-
-
fullname:
type: text
size: large
@@ -41,46 +39,3 @@ form:
type: text
size: large
label: Title
-
- admin:
- type: section
- title: Admin Access
-
- fields:
- access.admin.super:
- type: toggle
- label: Super user
- default: 0
- highlight: 1
- options:
- 1: Yes
- 0: No
- validate:
- type: bool
-
- access.admin.login:
- type: toggle
- label: Admin login
- default: 0
- highlight: 1
- options:
- 1: Yes
- 0: No
- validate:
- type: bool
-
- site:
- type: section
- title: Site Access
-
- fields:
- access.site.login:
- type: toggle
- label: Site login
- default: 1
- highlight: 1
- options:
- 1: Yes
- 0: No
- validate:
- type: bool
diff --git a/system/config/site.yaml b/system/config/site.yaml
index 23c0f1f1b..72bc8027a 100644
--- a/system/config/site.yaml
+++ b/system/config/site.yaml
@@ -1,18 +1,31 @@
title: Grav # Name of the site
+
author:
name: John Appleseed # Default author name
email: 'john@email.com' # Default author email
+
taxonomies: [category,tag] # Arbitrary list of taxonomy types
+
blog:
- route: '/blog' # Route to blog
+ route: '/blog' # Route to blog (deprecated)
+
metadata:
description: 'My Grav Site' # Site description
+
summary:
enabled: true # enable or disable summary of page
format: short # long = summary delimiter will be ignored; short = use the first occurence of delimter or size
size: 300 # Maximum length of summary (characters)
delimiter: === # The summary delimiter
+
routes:
/something/else: '/blog/sample-3' # Alias for /blog/sample-3
/another/one/here: '/blog/sample-3' # Another alias for /blog/sample-3
/new/*: '/blog/*' # Wildcard any /new/my-page URL to /blog/my-page Route
+
+#menu: # Sample Menu Example
+# - text: Source
+# icon: github
+# url: https://github.com/getgrav/grav
+# - icon: twitter
+# url: http://twitter.com/getgrav
diff --git a/system/config/system.yaml b/system/config/system.yaml
index 38550d690..98e183b1a 100644
--- a/system/config/system.yaml
+++ b/system/config/system.yaml
@@ -8,7 +8,7 @@ home:
pages:
theme: antimatter # Default theme (defaults to "antimatter" theme)
order:
- by: defaults # Order pages by "default", "alpha" or "date"
+ by: default # Order pages by "default", "alpha" or "date"
dir: asc # Default ordering direction, "asc" or "desc"
list:
count: 20 # Default item count per page
@@ -78,6 +78,7 @@ images:
media:
enable_media_timestamp: false # Enable media timetsamps
+ upload_limit: 0 # Set maximum upload size in bytes (0 is unlimited)
security:
default_hash: $2y$10$kwsyMVwM8/7j0K/6LHT.g.Fs49xOCTp2b8hh/S5.dPJuJcJB6T.UK
diff --git a/system/defines.php b/system/defines.php
index 2055be734..71aa234fd 100644
--- a/system/defines.php
+++ b/system/defines.php
@@ -2,7 +2,7 @@
// Some standard defines
define('GRAV', true);
-define('GRAV_VERSION', '0.9.26');
+define('GRAV_VERSION', '0.9.27');
define('DS', '/');
// Directories and Paths
diff --git a/system/src/Grav/Common/Assets.php b/system/src/Grav/Common/Assets.php
index 3b232f457..2d44912a8 100644
--- a/system/src/Grav/Common/Assets.php
+++ b/system/src/Grav/Common/Assets.php
@@ -825,7 +825,7 @@ class Assets
} 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_path = ltrim(preg_replace($this->base_url, '/', $link, 1), '/');
}
$relative_dir = dirname($relative_path);
@@ -881,18 +881,7 @@ class Assets
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);
+ $new_url = $this->base_url . ltrim(Utils::normalizePath($relative_path . '/' . $old_url), '/');
return str_replace($old_url, $new_url, $matches[0]);
},
diff --git a/system/src/Grav/Common/Backup/ZipBackup.php b/system/src/Grav/Common/Backup/ZipBackup.php
new file mode 100644
index 000000000..d275d21eb
--- /dev/null
+++ b/system/src/Grav/Common/Backup/ZipBackup.php
@@ -0,0 +1,121 @@
+findResource('backup://', true);
+
+ if (!$destination)
+ throw new \RuntimeException('The backup folder is missing.');
+
+ Folder::mkdir($destination);
+ }
+
+ $name = self::getGrav()['config']->get('site.title', basename(GRAV_ROOT));
+
+ if (is_dir($destination)) {
+ $date = date('YmdHis', time());
+ $filename = $name . '-' . $date . '.zip';
+ $destination = rtrim($destination, DS) . DS . $filename;
+ }
+
+ $messager && $messager([
+ 'type' => 'message',
+ 'level' => 'info',
+ 'message' => 'Creating new Backup "' . $destination . '"'
+ ]);
+ $messager && $messager([
+ 'type' => 'message',
+ 'level' => 'info',
+ 'message' => ''
+ ]);
+
+ $zip = new \ZipArchive();
+ $zip->open($destination, \ZipArchive::CREATE);
+
+ static::folderToZip(GRAV_ROOT, $zip, strlen(rtrim(GRAV_ROOT, DS) . DS), $messager);
+
+ $messager && $messager([
+ 'type' => 'progress',
+ 'percentage' => false,
+ 'complete' => true
+ ]);
+
+ $messager && $messager([
+ 'type' => 'message',
+ 'level' => 'info',
+ 'message' => ''
+ ]);
+ $messager && $messager([
+ 'type' => 'message',
+ 'level' => 'info',
+ 'message' => 'Saving and compressing archive...'
+ ]);
+
+ $zip->close();
+
+ return $destination;
+ }
+
+ /**
+ * @param $folder
+ * @param $zipFile
+ * @param $exclusiveLength
+ * @param $messager
+ */
+ private static function folderToZip($folder, \ZipArchive &$zipFile, $exclusiveLength, callable $messager = null)
+ {
+ $handle = opendir($folder);
+ while (false !== $f = readdir($handle)) {
+ if ($f != '.' && $f != '..') {
+ $filePath = "$folder/$f";
+ // Remove prefix from file path before add to zip.
+ $localPath = substr($filePath, $exclusiveLength);
+
+ if (in_array($f, static::$ignoreFolders) || in_array($localPath, static::$ignorePaths)) {
+ continue;
+ }
+
+ if (is_file($filePath)) {
+ $zipFile->addFile($filePath, $localPath);
+
+ $messager && $messager([
+ 'type' => 'progress',
+ 'percentage' => false,
+ 'complete' => false
+ ]);
+ } elseif (is_dir($filePath)) {
+ // Add sub-directory.
+ $zipFile->addEmptyDir($localPath);
+ static::folderToZip($filePath, $zipFile, $exclusiveLength, $messager);
+ }
+ }
+ }
+ closedir($handle);
+ }
+}
diff --git a/system/src/Grav/Common/Config/Config.php b/system/src/Grav/Common/Config/Config.php
index 1fe373a40..f2c6abcbc 100644
--- a/system/src/Grav/Common/Config/Config.php
+++ b/system/src/Grav/Common/Config/Config.php
@@ -72,9 +72,17 @@ class Config extends Data
'prefixes' => [
'' => ['logs']
]
+ ],
+ 'backup' => [
+ 'type' => 'Stream',
+ 'prefixes' => [
+ '' => ['backup']
+ ]
]
];
+ protected $setup = [];
+
protected $blueprintFiles = [];
protected $configFiles = [];
protected $checksum;
@@ -88,33 +96,25 @@ class Config extends Data
protected $environment;
protected $messages = [];
- public function __construct(array $items = array(), Grav $grav = null, $environment = null)
+ public function __construct(array $setup = array(), Grav $grav = null, $environment = null)
{
$this->grav = $grav ?: Grav::instance();
$this->finder = new ConfigFinder;
$this->environment = $environment ?: 'localhost';
$this->messages[] = 'Environment Name: ' . $this->environment;
- if (isset($items['@class'])) {
- if ($items['@class'] != get_class($this)) {
- throw new \InvalidArgumentException('Unrecognized config cache file!');
- }
- // Loading pre-compiled configuration.
- $this->timestamp = (int) $items['timestamp'];
- $this->checksum = $items['checksum'];
- $this->items = (array) $items['data'];
- } else {
- // Make sure that
- if (!isset($items['streams']['schemes'])) {
- $items['streams']['schemes'] = [];
- }
- $items['streams']['schemes'] += $this->streams;
-
- $items = $this->autoDetectEnvironmentConfig($items);
- $this->messages[] = $items['streams']['schemes']['config']['prefixes'][''];
-
- parent::__construct($items);
+ // Make sure that
+ if (!isset($setup['streams']['schemes'])) {
+ $setup['streams']['schemes'] = [];
}
+ $setup['streams']['schemes'] += $this->streams;
+
+ $setup = $this->autoDetectEnvironmentConfig($setup);
+ $this->messages[] = $setup['streams']['schemes']['config']['prefixes'][''];
+
+ $this->setup = $setup;
+ parent::__construct($setup);
+
$this->check();
}
@@ -125,8 +125,10 @@ class Config extends Data
public function reload()
{
+ $this->items = $this->setup;
$this->check();
$this->init();
+ $this->debug();
return $this;
}
@@ -150,6 +152,7 @@ class Config extends Data
foreach ($this->messages as $message) {
$this->grav['debugger']->addMessage($message);
}
+ $this->messages = [];
}
public function init()
@@ -161,15 +164,6 @@ class Config extends Data
$this->blueprintLookup = $locator->findResources('blueprints://config');
$this->pluginLookup = $locator->findResources('plugins://');
- if (!isset($this->checksum)) {
- $this->messages[] = 'No cached configuration, compiling new configuration..';
- } elseif ($this->checksum() != $this->checksum) {
- $this->messages[] = 'Configuration checksum mismatch, reloading configuration..';
- } else {
- $this->messages[] = 'Configuration checksum matches, using cached version.';
- return;
- }
-
$this->loadCompiledBlueprints($this->blueprintLookup, $this->pluginLookup, 'master');
$this->loadCompiledConfig($this->configLookup, $this->pluginLookup, 'master');
@@ -255,7 +249,6 @@ class Config extends Data
'files' => $blueprintFiles,
'data' => $this->blueprints->toArray()
];
-
// If compiled file wasn't already locked by another process, save it.
if ($file->locked() !== false) {
$this->messages[] = 'Saving compiled blueprints.';
@@ -269,44 +262,51 @@ class Config extends Data
protected function loadCompiledConfig($configs, $plugins, $filename = null)
{
- $checksum = md5(json_encode($configs));
$filename = $filename
? CACHE_DIR . 'compiled/config/' . $filename . '-' . $this->environment . '.php'
: CACHE_DIR . 'compiled/config/' . $checksum . '-' . $this->environment . '.php';
$file = PhpFile::instance($filename);
$cache = $file->exists() ? $file->content() : null;
- $configFiles = $this->finder->locateConfigFiles($configs, $plugins);
- $checksum .= ':'.md5(json_encode($configFiles));
$class = get_class($this);
+ $checksum = $this->checksum();
- // Load real file if cache isn't up to date (or is invalid).
if (
!is_array($cache)
|| !isset($cache['checksum'])
|| !isset($cache['@class'])
- || $cache['checksum'] != $checksum
|| $cache['@class'] != $class
) {
- // Attempt to lock the file for writing.
- $file->lock(false);
+ $this->messages[] = 'No cached configuration, compiling new configuration..';
+ } else if ($cache['checksum'] !== $checksum) {
+ $this->messages[] = 'Configuration checksum mismatch, reloading configuration..';
+ } else {
+ $this->messages[] = 'Configuration checksum matches, using cached version.';
- // Load configuration.
- foreach ($configFiles as $files) {
- $this->loadConfigFiles($files);
- }
- $cache = [
- '@class' => $class,
- 'timestamp' => time(),
- 'checksum' => $this->checksum(),
- 'data' => $this->toArray()
- ];
+ $this->items = $cache['data'];
+ return;
+ }
- // If compiled file wasn't already locked by another process, save it.
- if ($file->locked() !== false) {
- $this->messages[] = 'Saving compiled configuration.';
- $file->save($cache);
- $file->unlock();
- }
+ $configFiles = $this->finder->locateConfigFiles($configs, $plugins);
+
+ // Attempt to lock the file for writing.
+ $file->lock(false);
+
+ // Load configuration.
+ foreach ($configFiles as $files) {
+ $this->loadConfigFiles($files);
+ }
+ $cache = [
+ '@class' => $class,
+ 'timestamp' => time(),
+ 'checksum' => $checksum,
+ 'data' => $this->toArray()
+ ];
+
+ // If compiled file wasn't already locked by another process, save it.
+ if ($file->locked() !== false) {
+ $this->messages[] = 'Saving compiled configuration.';
+ $file->save($cache);
+ $file->unlock();
}
$this->items = $cache['data'];
diff --git a/system/src/Grav/Common/Filesystem/Folder.php b/system/src/Grav/Common/Filesystem/Folder.php
index 9355038b8..8309bebff 100644
--- a/system/src/Grav/Common/Filesystem/Folder.php
+++ b/system/src/Grav/Common/Filesystem/Folder.php
@@ -278,6 +278,46 @@ abstract class Folder
}
}
+ /**
+ * Recursive copy of one directory to another
+ *
+ * @param $src
+ * @param $dest
+ *
+ * @return bool
+ */
+ public static function rcopy($src, $dest)
+ {
+
+ // If the src is not a directory do a simple file copy
+ if (!is_dir($src)) {
+ copy($src, $dest);
+ return true;
+ }
+
+ // If the destination directory does not exist create it
+ if (!is_dir($dest)) {
+ if (!mkdir($dest)) {
+ // If the destination directory could not be created stop processing
+ return false;
+ }
+ }
+
+ // Open the source directory to read in files
+ $i = new \DirectoryIterator($src);
+ /** @var \DirectoryIterator $f */
+ foreach ($i as $f) {
+ if ($f->isFile()) {
+ copy($f->getRealPath(), "$dest/" . $f->getFilename());
+ } else {
+ if (!$f->isDot() && $f->isDir()) {
+ static::rcopy($f->getRealPath(), "$dest/$f");
+ }
+ }
+ }
+ return true;
+ }
+
/**
* @param string $folder
* @return bool
@@ -290,13 +330,24 @@ abstract class Folder
return @unlink($folder);
}
- // Go through all items in filesystem and recursively remove everything.
- $files = array_diff(scandir($folder), array('.', '..'));
- foreach ($files as $file) {
- $path = "{$folder}/{$file}";
- (is_dir($path)) ? self::doDelete($path) : @unlink($path);
+ $files = new \RecursiveIteratorIterator(
+ new \RecursiveDirectoryIterator($folder, \RecursiveDirectoryIterator::SKIP_DOTS),
+ \RecursiveIteratorIterator::CHILD_FIRST
+ );
+
+ /** @var \DirectoryIterator $fileinfo */
+ foreach ($files as $fileinfo) {
+ if ($fileinfo->isDir()) {
+ if (false === rmdir($fileinfo->getRealPath())) {
+ return false;
+ }
+ } else {
+ if (false === unlink($fileinfo->getRealPath())) {
+ return false;
+ }
+ }
}
- return @rmdir($folder);
+ return rmdir($folder);
}
}
diff --git a/system/src/Grav/Common/Grav.php b/system/src/Grav/Common/Grav.php
index 42085cd29..710984aca 100644
--- a/system/src/Grav/Common/Grav.php
+++ b/system/src/Grav/Common/Grav.php
@@ -1,7 +1,6 @@
path();
+ $path = rtrim($uri->path(), '/');
+ $path = $path ?: '/';
$page = $pages->dispatch($path);
@@ -168,12 +168,6 @@ class Grav extends Container
public function process()
{
- // Use output buffering to prevent headers from being sent too early.
- ob_start();
- if ($this['config']->get('system.cache.gzip')) {
- ob_start('ob_gzhandler');
- }
-
/** @var Debugger $debugger */
$debugger = $this['debugger'];
@@ -186,6 +180,12 @@ class Grav extends Container
$this['config']->debug();
$debugger->stopTimer('_config');
+ // Use output buffering to prevent headers from being sent too early.
+ ob_start();
+ if ($this['config']->get('system.cache.gzip')) {
+ ob_start('ob_gzhandler');
+ }
+
// Initialize the timezone
if ($this['config']->get('system.timezone')) {
date_default_timezone_set($this['config']->get('system.timezone'));
@@ -350,26 +350,31 @@ class Grav extends Container
public function shutdown()
{
if ($this['config']->get('system.debugger.shutdown.close_connection')) {
-
+ //stop user abort
if (function_exists('ignore_user_abort')) {
@ignore_user_abort(true);
}
+ // close the session
if (isset($this['session'])) {
$this['session']->close();
}
+ // flush buffer if gzip buffer was started
if ($this['config']->get('system.cache.gzip')) {
ob_end_flush(); // gzhandler buffer
}
+ // get lengh and close the connection
header('Content-Length: ' . ob_get_length());
- header("Connection: close\r\n");
+ header("Connection: close");
- ob_end_flush(); // regular buffer
+ // flush the regular buffer
+ ob_end_flush();
@ob_flush();
flush();
+ // fix for fastcgi close connection issue
if (function_exists('fastcgi_finish_request')) {
@fastcgi_finish_request();
}
diff --git a/system/src/Grav/Common/Markdown/ParsedownGravTrait.php b/system/src/Grav/Common/Markdown/ParsedownGravTrait.php
index b3d204232..c265bf599 100644
--- a/system/src/Grav/Common/Markdown/ParsedownGravTrait.php
+++ b/system/src/Grav/Common/Markdown/ParsedownGravTrait.php
@@ -37,7 +37,7 @@ trait ParsedownGravTrait
$this->pages_dir = self::getGrav()['locator']->findResource('page://');
$this->special_chars = array('>' => 'gt', '<' => 'lt', '"' => 'quot');
- if ($defaults == null) {
+ if ($defaults === null) {
$defaults = self::getGrav()['config']->get('system.pages.markdown');
}
@@ -232,13 +232,14 @@ trait ParsedownGravTrait
$normalized_path = Utils::normalizePath($this->pages_dir . $markdown_url);
$normalized_url = Utils::normalizePath($this->base_url . $markdown_url);
} else {
- // contains path, so need to normalize it
- if (Utils::contains($markdown_url, '/')) {
- $normalized_path = Utils::normalizePath($this->page->path() . '/' . $markdown_url);
- } else {
- $normalized_path = false;
- }
$normalized_url = $this->base_url . Utils::normalizePath($this->page->route() . '/' . $markdown_url);
+ $normalized_path = Utils::normalizePath($this->page->path() . '/' . $markdown_url);
+ }
+
+ // special check to see if path checking is required.
+ $just_path = str_replace($normalized_url, '', $normalized_path);
+ if ($just_path == $this->page->path()) {
+ return $normalized_url;
}
// if this file exits, get the page and work with that
@@ -251,16 +252,23 @@ trait ParsedownGravTrait
$page_path = $path_info['dirname'];
$filename = '';
- // save the filename if a file is part of the path
- $filename_regex = "/([\w\d-_]+\.([a-zA-Z]{2,4}))$/";
- if (preg_match($filename_regex, $full_path, $matches)) {
- if ($matches[2] != 'md') {
- $filename = '/' . $matches[1];
- }
- } else {
+
+ if ($markdown_url == '..') {
$page_path = $full_path;
+ } else {
+ // save the filename if a file is part of the path
+ $filename_regex = "/([\w\d-_]+\.([a-zA-Z]{2,4}))$/";
+ if (preg_match($filename_regex, $full_path, $matches)) {
+ if ($matches[2] != 'md') {
+ $filename = '/' . $matches[1];
+ }
+ } else {
+ $page_path = $full_path;
+ }
}
+
+
// get page instances and try to find one that fits
$instances = $this->pages->instances();
if (isset($instances[$page_path])) {
diff --git a/system/src/Grav/Common/Page/Media.php b/system/src/Grav/Common/Page/Media.php
index 93c8ddb04..d73064618 100644
--- a/system/src/Grav/Common/Page/Media.php
+++ b/system/src/Grav/Common/Page/Media.php
@@ -47,7 +47,7 @@ class Media extends Getters
/** @var \DirectoryIterator $info */
foreach ($iterator as $path => $info) {
// Ignore folders and Markdown files.
- if (!$info->isFile() || $info->getExtension() == 'md') {
+ if (!$info->isFile() || $info->getExtension() == 'md' || $info->getBasename() === '.DS_Store') {
continue;
}
@@ -58,27 +58,36 @@ class Media extends Getters
if ($type === 'alternative') {
$media["{$basename}.{$ext}"][$type] = isset($media["{$basename}.{$ext}"][$type]) ? $media["{$basename}.{$ext}"][$type] : [];
- $media["{$basename}.{$ext}"][$type][$extra] = $path;
+ $media["{$basename}.{$ext}"][$type][$extra] = [ 'file' => $path, 'size' => $info->getSize() ];
} else {
- $media["{$basename}.{$ext}"][$type] = $path;
+ $media["{$basename}.{$ext}"][$type] = [ 'file' => $path, 'size' => $info->getSize() ];
}
}
foreach ($media as $name => $types) {
// First prepare the alternatives in case there is no base medium
if (!empty($types['alternative'])) {
- foreach ($types['alternative'] as $ratio => &$file) {
- $file = MediumFactory::fromFile($file);
+ foreach ($types['alternative'] as $ratio => &$alt) {
+ $alt['file'] = MediumFactory::fromFile($alt['file']);
+
+ if (!$alt['file']) {
+ unset($types['alternative'][$ratio]);
+ } else {
+ $alt['file']->set('size', $alt['size']);
+ }
}
}
// Create the base medium
if (!empty($types['base'])) {
- $medium = MediumFactory::fromFile($types['base']);
+ $medium = MediumFactory::fromFile($types['base']['file']);
+ $medium && $medium->set('size', $types['base']['size']);
} else if (!empty($types['alternative'])) {
$altMedium = reset($types['alternative']);
$ratio = key($types['alternative']);
+ $altMedium = $altMedium['file'];
+
$medium = MediumFactory::scaledFromMedium($altMedium, $ratio, 1);
}
@@ -87,13 +96,13 @@ class Media extends Getters
}
if (!empty($types['meta'])) {
- $medium->addMetaFile($types['meta']);
+ $medium->addMetaFile($types['meta']['file']);
}
if (!empty($types['thumb'])) {
// We will not turn it into medium yet because user might never request the thumbnail
// not wasting any resources on that, maybe we should do this for medium in general?
- $medium->set('thumbnails.page', $types['thumb']);
+ $medium->set('thumbnails.page', $types['thumb']['file']);
}
// Build missing alternatives
diff --git a/system/src/Grav/Common/Page/Medium/ImageMedium.php b/system/src/Grav/Common/Page/Medium/ImageMedium.php
index 17a9663de..bea2b10d6 100644
--- a/system/src/Grav/Common/Page/Medium/ImageMedium.php
+++ b/system/src/Grav/Common/Page/Medium/ImageMedium.php
@@ -308,7 +308,7 @@ class ImageMedium extends Medium
}
try {
- $result = call_user_func_array([$this->image, $method], $args);
+ call_user_func_array([$this->image, $method], $args);
foreach ($this->alternatives as $ratio => $medium) {
$args_copy = $args;
diff --git a/system/src/Grav/Common/Page/Medium/Link.php b/system/src/Grav/Common/Page/Medium/Link.php
index 1262bddf9..a92ec228e 100644
--- a/system/src/Grav/Common/Page/Medium/Link.php
+++ b/system/src/Grav/Common/Page/Medium/Link.php
@@ -60,6 +60,6 @@ class Link implements RenderableInterface
// Don't start nesting links, if user has multiple link calls in his
// actions, we will drop the previous links.
- return $this->source instanceof LinkMedium ? $this->source : $this;
+ return $this->source instanceof Link ? $this->source : $this;
}
}
diff --git a/system/src/Grav/Common/Page/Medium/Medium.php b/system/src/Grav/Common/Page/Medium/Medium.php
index 5c210e563..0eb0666ce 100644
--- a/system/src/Grav/Common/Page/Medium/Medium.php
+++ b/system/src/Grav/Common/Page/Medium/Medium.php
@@ -399,7 +399,7 @@ class Medium extends Data implements RenderableInterface
$thumb = $this->get('thumbnails.' . $type, false);
if ($thumb) {
- $thumb = $thumb instanceof ThumbnailMedium ? $thumb : MediumFactory::fromFile($thumb, ['type' => 'thumbnail']);
+ $thumb = $thumb instanceof ThumbnailImageMedium ? $thumb : MediumFactory::fromFile($thumb, ['type' => 'thumbnail']);
$thumb->parent = $this;
}
diff --git a/system/src/Grav/Common/Page/Page.php b/system/src/Grav/Common/Page/Page.php
index e0c7ca963..1d8a80241 100644
--- a/system/src/Grav/Common/Page/Page.php
+++ b/system/src/Grav/Common/Page/Page.php
@@ -303,21 +303,25 @@ class Page
public function summary($size = null)
{
/** @var Config $config */
- $config = self::getGrav()['config'];
+ $config = self::getGrav()['config']->get('site.summary');
$content = $this->content();
+ if (isset($this->header->summary)) {
+ $config = array_merge($config, $this->header->summary);
+ }
+
// Return summary based on settings in site config file
- if (!$config->get('site.summary.enabled', true)) {
+ if (!$config['enabled']) {
return $content;
}
// Get summary size from site config's file
if (is_null($size)) {
- $size = $config->get('site.summary.size', null);
+ $size = $config['size'];
}
// Return calculated summary based on summary divider's position
- $format = $config->get('site.summary.format', 'short');
+ $format = $config['format'];
// Return entire page content on wrong/ unknown format
if (!in_array($format, array('short', 'long'))) {
return $content;
diff --git a/system/src/Grav/Common/Plugin.php b/system/src/Grav/Common/Plugin.php
index 4197dd192..8643a89c0 100644
--- a/system/src/Grav/Common/Plugin.php
+++ b/system/src/Grav/Common/Plugin.php
@@ -114,6 +114,25 @@ class Plugin implements EventSubscriberInterface
}
}
+ /**
+ * This function will search a string for markdown links in a specific format. The link value can be
+ * optionally compared against via the $internal_regex and operated on by the callback $function
+ * provided.
+ *
+ * format: [plugin:myplugin_name](function_data)
+ *
+ * @param $content The string to perform operations upon
+ * @param $function The anonymous callback function
+ * @param string $internal_regex Optional internal regex to extra data from
+ *
+ * @return string
+ */
+ protected function parseLinks($content, $function, $internal_regex = '(.*)')
+ {
+ $regex = '/\[plugin:(?:'.$this->name.')\]\('.$internal_regex.'\)/i';
+ return preg_replace_callback($regex, $function, $content);
+ }
+
/**
* Merge global and page configurations.
*
diff --git a/system/src/Grav/Common/Plugins.php b/system/src/Grav/Common/Plugins.php
index d0c30c3e3..77a2afa2e 100644
--- a/system/src/Grav/Common/Plugins.php
+++ b/system/src/Grav/Common/Plugins.php
@@ -4,6 +4,7 @@ namespace Grav\Common;
use Grav\Common\Config\Config;
use Grav\Common\Data\Blueprints;
use Grav\Common\Data\Data;
+use Grav\Common\GravTrait;
use Grav\Common\File\CompiledYamlFile;
use RocketTheme\Toolbox\Event\EventDispatcher;
use RocketTheme\Toolbox\Event\EventSubscriberInterface;
@@ -17,12 +18,7 @@ use RocketTheme\Toolbox\Event\EventSubscriberInterface;
*/
class Plugins extends Iterator
{
- protected $grav;
-
- public function __construct(Grav $grav)
- {
- $this->grav = $grav;
- }
+ use GravTrait;
/**
* Recurses through the plugins directory creating Plugin objects for each plugin it finds.
@@ -33,11 +29,11 @@ class Plugins extends Iterator
public function init()
{
/** @var Config $config */
- $config = $this->grav['config'];
+ $config = self::getGrav()['config'];
$plugins = (array) $config->get('plugins');
/** @var EventDispatcher $events */
- $events = $this->grav['events'];
+ $events = self::getGrav()['events'];
foreach ($plugins as $plugin => $data) {
if (empty($data['enabled'])) {
@@ -45,9 +41,10 @@ class Plugins extends Iterator
continue;
}
- $filePath = $this->grav['locator']('plugins://' . $plugin . DS . $plugin . PLUGIN_EXT);
+ $locator = self::getGrav()['locator'];
+ $filePath = $locator->findResource('plugins://' . $plugin . DS . $plugin . PLUGIN_EXT);
if (!is_file($filePath)) {
- $this->grav['log']->addWarning(sprintf("Plugin '%s' enabled but not found! Try clearing cache with `bin/grav clear-cache`", $plugin));
+ self::getGrav()['log']->addWarning(sprintf("Plugin '%s' enabled but not found! Try clearing cache with `bin/grav clear-cache`", $plugin));
continue;
}
@@ -70,7 +67,7 @@ class Plugins extends Iterator
throw new \RuntimeException(sprintf("Plugin '%s' class not found! Try reinstalling this plugin.", $plugin));
}
- $instance = new $pluginClassName($plugin, $this->grav, $config);
+ $instance = new $pluginClassName($plugin, self::getGrav(), $config);
if ($instance instanceof EventSubscriberInterface) {
$events->addSubscriber($instance);
}
@@ -123,10 +120,10 @@ class Plugins extends Iterator
$obj = new Data($file->content(), $blueprint);
// Override with user configuration.
- $file = CompiledYamlFile::instance("user://config/plugins/{$name}.yaml");
- $obj->merge($file->content());
+ $obj->merge(self::getGrav()['config']->get('plugins.' . $name) ?: []);
// Save configuration always to user/config.
+ $file = CompiledYamlFile::instance("config://plugins/{$name}.yaml");
$obj->file($file);
return $obj;
diff --git a/system/src/Grav/Common/Service/ConfigServiceProvider.php b/system/src/Grav/Common/Service/ConfigServiceProvider.php
index c66732b08..28a6ac4d9 100644
--- a/system/src/Grav/Common/Service/ConfigServiceProvider.php
+++ b/system/src/Grav/Common/Service/ConfigServiceProvider.php
@@ -38,18 +38,8 @@ class ConfigServiceProvider implements ServiceProviderInterface
public function loadMasterConfig(Container $container)
{
$environment = $this->getEnvironment($container);
- $file = CACHE_DIR . 'compiled/config/master-'.$environment.'.php';
- $data = is_file($file) ? (array) include $file : [];
- if ($data) {
- try {
- $config = new Config($data, $container, $environment);
- } catch (\Exception $e) {
- }
- }
- if (!isset($config)) {
- $config = new Config($this->setup, $container, $environment);
- }
+ $config = new Config($this->setup, $container, $environment);
return $config;
}
diff --git a/system/src/Grav/Common/Service/ErrorServiceProvider.php b/system/src/Grav/Common/Service/ErrorServiceProvider.php
index 66453e34d..ac676ad98 100644
--- a/system/src/Grav/Common/Service/ErrorServiceProvider.php
+++ b/system/src/Grav/Common/Service/ErrorServiceProvider.php
@@ -30,7 +30,11 @@ class ErrorServiceProvider implements ServiceProviderInterface
$logger = $container['log'];
$errors->pushHandler(function (\Exception $exception, $inspector, $run) use ($logger) {
- $logger->addCritical($exception->getMessage(). ' - Trace: '. $exception->getTraceAsString());
+ try {
+ $logger->addCritical($exception->getMessage() . ' - Trace: ' . $exception->getTraceAsString());
+ } catch (\Exception $e) {
+ echo $e;
+ }
}, 'log');
$errors->register();
diff --git a/system/src/Grav/Common/Themes.php b/system/src/Grav/Common/Themes.php
index 4ec394ee9..ab4696a88 100644
--- a/system/src/Grav/Common/Themes.php
+++ b/system/src/Grav/Common/Themes.php
@@ -106,10 +106,10 @@ class Themes extends Iterator
$obj = new Data($file->content(), $blueprint);
// Override with user configuration.
- $file = CompiledYamlFile::instance("user://config/themes/{$name}" . YAML_EXT);
- $obj->merge($file->content());
+ $obj->merge($this->grav['config']->get('themes.' . $name) ?: []);
// Save configuration always to user/config.
+ $file = CompiledYamlFile::instance("config://themes/{$name}" . YAML_EXT);
$obj->file($file);
return $obj;
diff --git a/system/src/Grav/Common/Twig.php b/system/src/Grav/Common/Twig.php
index e5a7fafbd..6a92f86a0 100644
--- a/system/src/Grav/Common/Twig.php
+++ b/system/src/Grav/Common/Twig.php
@@ -292,6 +292,12 @@ class Twig
$twig_vars['content'] = $page->content();
$ext = '.' . ($format ? $format : 'html') . TWIG_EXT;
+ // determine if params are set, if so disable twig cache
+ $params = $this->grav['uri']->params(null, true);
+ if (!empty($params)) {
+ $this->twig->setCache(false);
+ }
+
// Get Twig template layout
$template = $this->template($page->template() . $ext);
diff --git a/system/src/Grav/Common/TwigExtension.php b/system/src/Grav/Common/TwigExtension.php
index cfec33ba7..c9d5f2289 100644
--- a/system/src/Grav/Common/TwigExtension.php
+++ b/system/src/Grav/Common/TwigExtension.php
@@ -52,7 +52,9 @@ class TwigExtension extends \Twig_Extension
new \Twig_SimpleFilter('contains', [$this, 'containsFilter']),
new \Twig_SimpleFilter('nicetime', [$this, 'nicetimeFilter']),
new \Twig_SimpleFilter('absolute_url', [$this, 'absoluteUrlFilter']),
- new \Twig_SimpleFilter('markdown', [$this, 'markdownFilter'])
+ new \Twig_SimpleFilter('markdown', [$this, 'markdownFilter']),
+ new \Twig_SimpleFilter('starts_with', [$this, 'startsWithFilter']),
+ new \Twig_SimpleFilter('ends_with', [$this, 'endsWithFilter'])
];
}
@@ -347,6 +349,16 @@ class TwigExtension extends \Twig_Extension
return $string;
}
+ public function startsWithFilter($needle, $haystack)
+ {
+ return Utils::startsWith($needle, $haystack);
+ }
+
+ public function endsWithFilter($needle, $haystack)
+ {
+ return Utils::endsWith($needle, $haystack);
+ }
+
/**
* Repeat given string x times.
*
diff --git a/system/src/Grav/Common/Uri.php b/system/src/Grav/Common/Uri.php
index bacd14b38..c4297bc20 100644
--- a/system/src/Grav/Common/Uri.php
+++ b/system/src/Grav/Common/Uri.php
@@ -193,20 +193,27 @@ class Uri
* Return all or a single query parameter as a URI compatible string.
*
* @param string $id Optional parameter name.
+ * @param boolean $array return the array format or not
* @return null|string
*/
- public function params($id = null)
+ public function params($id = null, $array = false)
{
$config = Grav::instance()['config'];
$params = null;
if ($id === null) {
+ if ($array) {
+ return $this->params;
+ }
$output = array();
foreach ($this->params as $key => $value) {
$output[] = $key . $config->get('system.param_sep') . $value;
$params = '/'.implode('/', $output);
}
} elseif (isset($this->params[$id])) {
+ if ($array) {
+ return $this->params[$id];
+ }
$params = "/{$id}". $config->get('system.param_sep') . $this->params[$id];
}
diff --git a/system/src/Grav/Common/Utils.php b/system/src/Grav/Common/Utils.php
index eee0ee4f9..ac1a2dcec 100644
--- a/system/src/Grav/Common/Utils.php
+++ b/system/src/Grav/Common/Utils.php
@@ -54,75 +54,6 @@ abstract class Utils
return (object) array_merge((array) $obj1, (array) $obj2);
}
- /**
- * Recursive remove a directory - DANGEROUS! USE WITH CARE!!!!
- *
- * @param $dir
- * @return bool
- */
- public static function rrmdir($dir)
- {
- $files = new \RecursiveIteratorIterator(
- new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS),
- \RecursiveIteratorIterator::CHILD_FIRST
- );
-
- /** @var \DirectoryIterator $fileinfo */
- foreach ($files as $fileinfo) {
- if ($fileinfo->isDir()) {
- if (false === rmdir($fileinfo->getRealPath())) {
- return false;
- }
- } else {
- if (false === unlink($fileinfo->getRealPath())) {
- return false;
- }
- }
- }
-
- return rmdir($dir);
- }
-
- /**
- * Recursive copy of one directory to another
- *
- * @param $src
- * @param $dest
- *
- * @return bool
- */
- public static function rcopy($src, $dest)
- {
-
- // If the src is not a directory do a simple file copy
- if (!is_dir($src)) {
- copy($src, $dest);
- return true;
- }
-
- // If the destination directory does not exist create it
- if (!is_dir($dest)) {
- if (!mkdir($dest)) {
- // If the destination directory could not be created stop processing
- return false;
- }
- }
-
- // Open the source directory to read in files
- $i = new \DirectoryIterator($src);
- /** @var \DirectoryIterator $f */
- foreach ($i as $f) {
- if ($f->isFile()) {
- copy($f->getRealPath(), "$dest/" . $f->getFilename());
- } else {
- if (!$f->isDot() && $f->isDir()) {
- static::rcopy($f->getRealPath(), "$dest/$f");
- }
- }
- }
- return true;
- }
-
/**
* Truncate HTML by text length.
*
@@ -250,12 +181,9 @@ abstract class Utils
$file_parts = pathinfo($file);
$filesize = filesize($file);
- $range = false;
set_time_limit(0);
ignore_user_abort(false);
- ini_set('output_buffering', 0);
- ini_set('zlib.output_compression', 0);
if ($force_download) {
header('Content-Description: File Transfer');
@@ -420,4 +348,32 @@ abstract class Utils
}
return $root . implode('/', $ret);
}
+
+ public static function timezones()
+ {
+ $timezones = \DateTimeZone::listIdentifiers(\DateTimeZone::ALL);
+ $offsets = [];
+ $testDate = new \DateTime;
+
+ foreach ($timezones as $zone) {
+ $tz = new \DateTimeZone($zone);
+ $offsets[$zone] = $tz->getOffset($testDate);
+ }
+
+ asort($offsets);
+
+ $timezone_list = array();
+ foreach( $offsets as $timezone => $offset )
+ {
+ $offset_prefix = $offset < 0 ? '-' : '+';
+ $offset_formatted = gmdate( 'H:i', abs($offset) );
+
+ $pretty_offset = "UTC${offset_prefix}${offset_formatted}";
+
+ $timezone_list[$timezone] = "(${pretty_offset}) $timezone";
+ }
+
+ return $timezone_list;
+
+ }
}
diff --git a/system/src/Grav/Console/Cli/BackupCommand.php b/system/src/Grav/Console/Cli/BackupCommand.php
index 050301762..46590bf39 100644
--- a/system/src/Grav/Console/Cli/BackupCommand.php
+++ b/system/src/Grav/Console/Cli/BackupCommand.php
@@ -7,6 +7,7 @@ use Symfony\Component\Console\Helper\ProgressBar;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
+use Grav\Common\Backup\ZipBackup;
/**
* Class BackupCommand
@@ -23,6 +24,10 @@ class BackupCommand extends Command
* @var
*/
protected $progress;
+ /**
+ * @var
+ */
+ protected $output;
/**
*
@@ -52,6 +57,8 @@ class BackupCommand extends Command
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
+ $this->output = $output;
+
$output->getFormatter()->setStyle('red', new OutputFormatterStyle('red'));
$output->getFormatter()->setStyle('cyan', new OutputFormatterStyle('cyan'));
$output->getFormatter()->setStyle('green', new OutputFormatterStyle('green'));
@@ -60,25 +67,10 @@ class BackupCommand extends Command
$this->progress = new ProgressBar($output);
$this->progress->setFormat('Archiving %current% files [%bar%] %elapsed:6s% %memory:6s%');
- $name = basename($this->source);
- $dir = dirname($this->source);
- $date = date('YmdHis', time());
- $filename = $name . '-' . $date . '.zip';
-
$destination = ($input->getArgument('destination')) ? $input->getArgument('destination') : ROOT_DIR;
- $destination = rtrim($destination, DS) . DS . $filename;
- $output->writeln('');
- $output->writeln('Creating new Backup "' . $destination . '"');
- $this->progress->start();
+ ZipBackup::backup($destination, [$this, 'output']);
- $zip = new \ZipArchive();
- $zip->open($destination, \ZipArchive::CREATE);
- $zip->addEmptyDir($name);
-
- $this->folderToZip($this->source, $zip, strlen($dir . DS), $this->progress);
- $zip->close();
- $this->progress->finish();
$output->writeln('');
$output->writeln('');
@@ -90,25 +82,20 @@ class BackupCommand extends Command
* @param $exclusiveLength
* @param $progress
*/
- private static function folderToZip($folder, \ZipArchive &$zipFile, $exclusiveLength, ProgressBar $progress)
+ public function output($args)
{
- $handle = opendir($folder);
- while (false !== $f = readdir($handle)) {
- if ($f != '.' && $f != '..') {
- $filePath = "$folder/$f";
- // Remove prefix from file path before add to zip.
- $localPath = substr($filePath, $exclusiveLength);
- if (is_file($filePath)) {
- $zipFile->addFile($filePath, $localPath);
- $progress->advance();
- } elseif (is_dir($filePath)) {
- // Add sub-directory.
- $zipFile->addEmptyDir($localPath);
- self::folderToZip($filePath, $zipFile, $exclusiveLength, $progress);
+ switch ($args['type']) {
+ case 'message':
+ $this->output->writeln($args['message']);
+ break;
+ case 'progress':
+ if ($args['complete']) {
+ $this->progress->finish();
+ } else {
+ $this->progress->advance();
}
- }
+ break;
}
- closedir($handle);
}
}
diff --git a/system/src/Grav/Console/Cli/CleanCommand.php b/system/src/Grav/Console/Cli/CleanCommand.php
index 749b7c5e8..5ceaed611 100644
--- a/system/src/Grav/Console/Cli/CleanCommand.php
+++ b/system/src/Grav/Console/Cli/CleanCommand.php
@@ -66,7 +66,6 @@ class CleanCommand extends Command
'vendor/filp/whoops/.scrutinizer.yml',
'vendor/filp/whoops/.travis.yml',
'vendor/filp/whoops/phpunit.xml.dist',
- 'vendor/filp/whoops/src/deprecated',
'vendor/gregwar/image/Gregwar/Image/composer.json',
'vendor/gregwar/image/Gregwar/Image/phpunit.xml',
'vendor/gregwar/image/Gregwar/Image/.gitignore',
diff --git a/system/src/Grav/Console/Cli/ComposerCommand.php b/system/src/Grav/Console/Cli/ComposerCommand.php
new file mode 100644
index 000000000..3a28718ac
--- /dev/null
+++ b/system/src/Grav/Console/Cli/ComposerCommand.php
@@ -0,0 +1,80 @@
+setName("composer")
+ ->addOption(
+ 'install',
+ 'i',
+ InputOption::VALUE_NONE,
+ 'install the dependencies'
+ )
+ ->addOption(
+ 'update',
+ 'u',
+ InputOption::VALUE_NONE,
+ 'update the dependencies'
+ )
+ ->setDescription("Updates the composer vendordependencies needed by Grav.")
+ ->setHelp('The composer command updates the composer vendordependencies needed by Grav');
+ }
+
+ /**
+ * @param InputInterface $input
+ * @param OutputInterface $output
+ *
+ * @return int|null|void
+ */
+ protected function execute(InputInterface $input, OutputInterface $output)
+ {
+ $action = 'update';
+
+ if ($input->getOption('install')) {
+ $action = 'install';
+ }
+
+ // Updates composer first
+ $output->writeln("\nInstalling vendor dependencies");
+ $output->writeln($this->composerUpdate(GRAV_ROOT, $action));
+ }
+
+}
diff --git a/system/src/Grav/Console/Cli/InstallCommand.php b/system/src/Grav/Console/Cli/InstallCommand.php
index f0510a51f..8aead008f 100644
--- a/system/src/Grav/Console/Cli/InstallCommand.php
+++ b/system/src/Grav/Console/Cli/InstallCommand.php
@@ -1,6 +1,7 @@
getOption('symlink')) {
// Updates composer first
$output->writeln("\nInstalling vendor dependencies");
- $output->writeln(system('php bin/composer.phar --working-dir="'.$this->destination.'" --no-interaction update'));
+ $output->writeln($this->composerUpdate(GRAV_ROOT, 'install'));
$this->gitclone($output);
} else {
diff --git a/system/src/Grav/Console/Cli/SandboxCommand.php b/system/src/Grav/Console/Cli/SandboxCommand.php
index 4d984d219..43cfc230c 100644
--- a/system/src/Grav/Console/Cli/SandboxCommand.php
+++ b/system/src/Grav/Console/Cli/SandboxCommand.php
@@ -2,7 +2,6 @@
namespace Grav\Console\Cli;
use Grav\Common\Filesystem\Folder;
-use Grav\Common\Utils;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use Symfony\Component\Console\Input\InputArgument;
@@ -20,6 +19,7 @@ class SandboxCommand extends Command
* @var array
*/
protected $directories = array(
+ '/backup',
'/cache',
'/logs',
'/images',
@@ -189,7 +189,7 @@ class SandboxCommand extends Command
$to = $this->destination . $target;
$this->output->writeln(' ' . $source . ' -> ' . $to);
- Utils::rcopy($from, $to);
+ Folder::rcopy($from, $to);
}
}
@@ -269,7 +269,7 @@ class SandboxCommand extends Command
if (count($pages_files) == 0) {
$destination = $this->source . '/user/pages';
- Utils::rcopy($destination, $pages_dir);
+ Folder::rcopy($destination, $pages_dir);
$this->output->writeln(' ' . $destination . ' -> Created');
}
diff --git a/system/src/Grav/Console/ConsoleTrait.php b/system/src/Grav/Console/ConsoleTrait.php
index 42c65d495..a2e80b6e8 100644
--- a/system/src/Grav/Console/ConsoleTrait.php
+++ b/system/src/Grav/Console/ConsoleTrait.php
@@ -83,6 +83,11 @@ trait ConsoleTrait
}
}
+ public function composerUpdate($path, $action = 'install')
+ {
+ return system('php bin/composer.phar --working-dir="'.$path.'" --no-interaction --no-dev --prefer-dist -o '. $action);
+ }
+
/**
* @param array $all
*
diff --git a/system/src/Grav/Console/Gpm/InstallCommand.php b/system/src/Grav/Console/Gpm/InstallCommand.php
index c39087db4..6e8e40bbe 100644
--- a/system/src/Grav/Console/Gpm/InstallCommand.php
+++ b/system/src/Grav/Console/Gpm/InstallCommand.php
@@ -255,7 +255,7 @@ class InstallCommand extends Command
// Confirmation received, copy over the data
$this->output->writeln(" |- Installing demo content... ok ");
- Utils::rcopy($demo_dir, $dest_dir);
+ Folder::rcopy($demo_dir, $dest_dir);
$this->output->writeln(" '- Success! ");
$this->output->writeln('');
}
@@ -359,8 +359,6 @@ class InstallCommand extends Command
{
$matches = $this->getGitRegexMatches($package);
- $to = $this->destination . DS . $package->install_path;
-
$this->output->writeln("Preparing to Git clone " . $package->name . " from " . $matches[0]);
$this->output->write(" |- Checking destination... ");
diff --git a/web.config b/web.config
index 85d2c1b1b..c3dd31424 100644
--- a/web.config
+++ b/web.config
@@ -1,51 +1,55 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+