mirror of
https://github.com/getgrav/grav.git
synced 2026-07-06 02:47:45 +02:00
Merge branch 'release/0.9.27'
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -7,6 +7,8 @@ vendor/
|
||||
.sass-cache
|
||||
|
||||
# Grav Specific
|
||||
backup/*
|
||||
!backup/.*
|
||||
cache/*
|
||||
!cache/.*
|
||||
assets/*
|
||||
|
||||
@@ -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
|
||||
|
||||
29
CHANGELOG.md
29
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
|
||||
|
||||
|
||||
0
backup/.gitkeep
Normal file
0
backup/.gitkeep
Normal file
Binary file not shown.
6
bin/gpm
6
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'];
|
||||
|
||||
3
bin/grav
3
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(),
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@ form:
|
||||
type: text
|
||||
size: large
|
||||
label: Default Email
|
||||
validate:
|
||||
type: email
|
||||
|
||||
taxonomies:
|
||||
type: text
|
||||
|
||||
@@ -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)'
|
||||
|
||||
@@ -61,7 +61,7 @@ form:
|
||||
@data-options: '\Grav\Common\Page\Pages::parents'
|
||||
@data-default: '\Grav\Plugin\admin::route'
|
||||
options:
|
||||
'/': '- Root -'
|
||||
'': '- Select -'
|
||||
validate:
|
||||
required: true
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]);
|
||||
},
|
||||
|
||||
121
system/src/Grav/Common/Backup/ZipBackup.php
Normal file
121
system/src/Grav/Common/Backup/ZipBackup.php
Normal file
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
namespace Grav\Common\Backup;
|
||||
|
||||
use Grav\Common\GravTrait;
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
|
||||
/**
|
||||
* The ZipBackup class lets you create simple zip-backups of a grav site
|
||||
*
|
||||
* @author RocketTheme
|
||||
* @license MIT
|
||||
*/
|
||||
class ZipBackup
|
||||
{
|
||||
use GravTrait;
|
||||
|
||||
protected static $ignorePaths = [
|
||||
'backup',
|
||||
'cache',
|
||||
'images',
|
||||
'logs'
|
||||
];
|
||||
|
||||
protected static $ignoreFolders = [
|
||||
'.git'
|
||||
];
|
||||
|
||||
public static function backup($destination = null, callable $messager = null)
|
||||
{
|
||||
if (!$destination) {
|
||||
$destination = self::getGrav()['locator']->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);
|
||||
}
|
||||
}
|
||||
@@ -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'];
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<?php
|
||||
namespace Grav\Common;
|
||||
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Common\Page\Medium\ImageMedium;
|
||||
use Grav\Common\Page\Pages;
|
||||
use Grav\Common\Service\ConfigServiceProvider;
|
||||
@@ -78,7 +77,7 @@ class Grav extends Container
|
||||
return new Cache($c);
|
||||
};
|
||||
$container['plugins'] = function ($c) {
|
||||
return new Plugins($c);
|
||||
return new Plugins();
|
||||
};
|
||||
$container['themes'] = function ($c) {
|
||||
return new Themes($c);
|
||||
@@ -102,7 +101,8 @@ class Grav extends Container
|
||||
/** @var Uri $uri */
|
||||
$uri = $c['uri'];
|
||||
|
||||
$path = $uri->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();
|
||||
}
|
||||
|
||||
@@ -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])) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 <cyan>%current%</cyan> files [<green>%bar%</green>] %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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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',
|
||||
|
||||
80
system/src/Grav/Console/Cli/ComposerCommand.php
Normal file
80
system/src/Grav/Console/Cli/ComposerCommand.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
namespace Grav\Console\Cli;
|
||||
|
||||
use Grav\Console\ConsoleTrait;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
/**
|
||||
* Class ComposerCommand
|
||||
* @package Grav\Console\Cli
|
||||
*/
|
||||
class ComposerCommand extends Command
|
||||
{
|
||||
use ConsoleTrait;
|
||||
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
protected $config;
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
protected $local_config;
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
protected $destination;
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
protected $user_path;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->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 <info>composer</info> 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));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
<?php
|
||||
namespace Grav\Console\Cli;
|
||||
|
||||
use Grav\Console\ConsoleTrait;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
@@ -15,7 +16,7 @@ use Symfony\Component\Yaml\Yaml;
|
||||
*/
|
||||
class InstallCommand extends Command
|
||||
{
|
||||
|
||||
use ConsoleTrait;
|
||||
/**
|
||||
* @var
|
||||
*/
|
||||
@@ -98,7 +99,7 @@ class InstallCommand extends Command
|
||||
if (!$input->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 {
|
||||
|
||||
@@ -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(' <cyan>' . $source . '</cyan> <comment>-></comment> ' . $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(' <cyan>' . $destination . '</cyan> <comment>-></comment> Created');
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -255,7 +255,7 @@ class InstallCommand extends Command
|
||||
|
||||
// Confirmation received, copy over the data
|
||||
$this->output->writeln(" |- Installing demo content... <green>ok</green> ");
|
||||
Utils::rcopy($demo_dir, $dest_dir);
|
||||
Folder::rcopy($demo_dir, $dest_dir);
|
||||
$this->output->writeln(" '- <green>Success!</green> ");
|
||||
$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 <cyan>" . $package->name . "</cyan> from " . $matches[0]);
|
||||
|
||||
$this->output->write(" |- Checking destination... ");
|
||||
|
||||
106
web.config
106
web.config
@@ -1,51 +1,55 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<system.webServer>
|
||||
<defaultDocument>
|
||||
<files>
|
||||
<remove value="index.php" />
|
||||
<add value="index.php" />
|
||||
</files>
|
||||
</defaultDocument>
|
||||
<rewrite>
|
||||
<rules>
|
||||
<rule name="request_filename" stopProcessing="true">
|
||||
<match url="." ignoreCase="false" />
|
||||
<conditions logicalGrouping="MatchAll">
|
||||
<add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" negate="true" />
|
||||
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" negate="true" />
|
||||
</conditions>
|
||||
<action type="Rewrite" url="index.php" />
|
||||
</rule>
|
||||
<rule name="user_accounts" stopProcessing="true">
|
||||
<match url="^user/accounts/(.*)$" ignoreCase="false" />
|
||||
<action type="Redirect" url="error" redirectType="Permanent" />
|
||||
</rule>
|
||||
<rule name="user_config" stopProcessing="true">
|
||||
<match url="^user/config/(.*)$" ignoreCase="false" />
|
||||
<action type="Redirect" url="error" redirectType="Permanent" />
|
||||
</rule>
|
||||
<rule name="user_error_redirect" stopProcessing="true">
|
||||
<match url="^user/(.*)\.(txt|md|html|php|yaml|json|twig|sh|bat)$" ignoreCase="false" />
|
||||
<action type="Redirect" url="error" redirectType="Permanent" />
|
||||
</rule>
|
||||
<rule name="cache" stopProcessing="true">
|
||||
<match url="^cache/(.*)" ignoreCase="false" />
|
||||
<action type="Redirect" url="error" redirectType="Permanent" />
|
||||
</rule>
|
||||
<rule name="bin" stopProcessing="true">
|
||||
<match url="^bin/(.*)$" ignoreCase="false" />
|
||||
<action type="Redirect" url="error" redirectType="Permanent" />
|
||||
</rule>
|
||||
<rule name="system" stopProcessing="true">
|
||||
<match url="^system/(.*)\.(txt|md|html|yaml|php|twig|sh|bat)$" ignoreCase="false" />
|
||||
<action type="Redirect" url="error" redirectType="Permanent" />
|
||||
</rule>
|
||||
<rule name="vendor" stopProcessing="true">
|
||||
<match url="^vendor/(.*)$" ignoreCase="false" />
|
||||
<action type="Redirect" url="error" redirectType="Permanent" />
|
||||
</rule>
|
||||
</rules>
|
||||
</rewrite>
|
||||
</system.webServer>
|
||||
</configuration>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration>
|
||||
<system.webServer>
|
||||
<defaultDocument>
|
||||
<files>
|
||||
<remove value="index.php" />
|
||||
<add value="index.php" />
|
||||
</files>
|
||||
</defaultDocument>
|
||||
<rewrite>
|
||||
<rules>
|
||||
<rule name="request_filename" stopProcessing="true">
|
||||
<match url="." ignoreCase="false" />
|
||||
<conditions logicalGrouping="MatchAll">
|
||||
<add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" negate="true" />
|
||||
<add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" negate="true" />
|
||||
</conditions>
|
||||
<action type="Rewrite" url="index.php" />
|
||||
</rule>
|
||||
<rule name="user_accounts" stopProcessing="true">
|
||||
<match url="^user/accounts/(.*)$" ignoreCase="false" />
|
||||
<action type="Redirect" url="error" redirectType="Permanent" />
|
||||
</rule>
|
||||
<rule name="user_config" stopProcessing="true">
|
||||
<match url="^user/config/(.*)$" ignoreCase="false" />
|
||||
<action type="Redirect" url="error" redirectType="Permanent" />
|
||||
</rule>
|
||||
<rule name="user_error_redirect" stopProcessing="true">
|
||||
<match url="^user/(.*)\.(txt|md|html|php|yaml|json|twig|sh|bat)$" ignoreCase="false" />
|
||||
<action type="Redirect" url="error" redirectType="Permanent" />
|
||||
</rule>
|
||||
<rule name="cache" stopProcessing="true">
|
||||
<match url="^cache/(.*)" ignoreCase="false" />
|
||||
<action type="Redirect" url="error" redirectType="Permanent" />
|
||||
</rule>
|
||||
<rule name="bin" stopProcessing="true">
|
||||
<match url="^bin/(.*)$" ignoreCase="false" />
|
||||
<action type="Redirect" url="error" redirectType="Permanent" />
|
||||
</rule>
|
||||
<rule name="backup" stopProcessing="true">
|
||||
<match url="^backup/(.*)" ignoreCase="false" />
|
||||
<action type="Redirect" url="error" redirectType="Permanent" />
|
||||
</rule>
|
||||
<rule name="system" stopProcessing="true">
|
||||
<match url="^system/(.*)\.(txt|md|html|yaml|php|twig|sh|bat)$" ignoreCase="false" />
|
||||
<action type="Redirect" url="error" redirectType="Permanent" />
|
||||
</rule>
|
||||
<rule name="vendor" stopProcessing="true">
|
||||
<match url="^vendor/(.*)$" ignoreCase="false" />
|
||||
<action type="Redirect" url="error" redirectType="Permanent" />
|
||||
</rule>
|
||||
</rules>
|
||||
</rewrite>
|
||||
</system.webServer>
|
||||
</configuration>
|
||||
|
||||
Reference in New Issue
Block a user