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 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +