mirror of
https://github.com/getgrav/grav.git
synced 2025-12-05 15:29:57 +01:00
Compare commits
49 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d4204f13f8 | ||
|
|
0554b25c78 | ||
|
|
f4e7cbaa6e | ||
|
|
83bfc1fd0f | ||
|
|
30439b864d | ||
|
|
80999121a9 | ||
|
|
ca0d0bb373 | ||
|
|
345578a05d | ||
|
|
1f0537124a | ||
|
|
2a2b9a12a7 | ||
|
|
5a19e05931 | ||
|
|
6ae4680fcb | ||
|
|
b4cf7899bb | ||
|
|
eac16911d0 | ||
|
|
21bef62b82 | ||
|
|
a1eccfdb61 | ||
|
|
90e05694d3 | ||
|
|
b97ac503a1 | ||
|
|
2cc34151df | ||
|
|
4c160533c2 | ||
|
|
59dd0d1212 | ||
|
|
3bf7e38e52 | ||
|
|
362ee4c9eb | ||
|
|
2462331884 | ||
|
|
cb4147a4bd | ||
|
|
72f3a01abf | ||
|
|
31e3c1c295 | ||
|
|
37891a1032 | ||
|
|
70284e1517 | ||
|
|
0bca1dcb48 | ||
|
|
36f9865c0b | ||
|
|
cd48551630 | ||
|
|
0ff130650f | ||
|
|
9b445ac5b6 | ||
|
|
c21248fcc2 | ||
|
|
88aa4c5ec9 | ||
|
|
0fd22ad933 | ||
|
|
00b56da220 | ||
|
|
fef6bdde5f | ||
|
|
6f7938e939 | ||
|
|
2cd469cd7c | ||
|
|
0895b15489 | ||
|
|
d9062b60d6 | ||
|
|
3f4049031d | ||
|
|
c81ce71074 | ||
|
|
f84ffd235b | ||
|
|
1ca6181856 | ||
|
|
f17954a5db | ||
|
|
e9e4106d22 |
30
CHANGELOG.md
30
CHANGELOG.md
@@ -1,3 +1,33 @@
|
||||
# v1.3.9
|
||||
## 12/05/2017
|
||||
|
||||
1. [](#new)
|
||||
* Added new core Twig templates for `partials/metadata.html.twig` and `partials/messages.html.twig`
|
||||
* Added ability to work with GPM locally [#1742](https://github.com/getgrav/grav/issues/1742)
|
||||
* Added new HTML5 audio controls [#1756](https://github.com/getgrav/grav/issues/1756)
|
||||
* Added `Medium::copy()` method to create a copy of a medium object
|
||||
* Added new `force_lowercase_urls` functionality on routes and slugs
|
||||
* Added new `item-list` filter type to remove empty items
|
||||
* Added new `setFlashCookieObject()` and `getFlashCookieObject()` methods to `Session` object
|
||||
* Added new `intl_enabled` option to disable PHP intl module collation when not needed
|
||||
1. [](#bugfix)
|
||||
* Fixed an issue with checkbox field validation [form#216](https://github.com/getgrav/grav-plugin-form/issues/216)
|
||||
* Fixed issue with multibyte Markdown link URLs [#1749](https://github.com/getgrav/grav/issues/1749)
|
||||
* Fixed issue with multibyte folder names [#1751](https://github.com/getgrav/grav/issues/1751)
|
||||
* Fixed several issues related to `system.custom_base_url` that were broken [#1736](https://github.com/getgrav/grav/issues/1736)
|
||||
* Dynamically added pages via `Pages::addPage()` were not firing `onPageProcessed()` event causing forms not to be processed
|
||||
* Fixed `Page::active()` and `Page::activeChild()` to work with UTF-8 characters in the URL [#1727](https://github.com/getgrav/grav/issues/1727)
|
||||
* Fixed typo in `modular.yaml` causing media to be ignored [#1725](https://github.com/getgrav/grav/issues/1725)
|
||||
* Reverted `case_insensitive_urls` option as it was causing issues with taxonomy [#1733](https://github.com/getgrav/grav/pull/1733)
|
||||
* Removed an extra `/` in `CompileFile.php` [#1693](https://github.com/getgrav/grav/pull/1693)
|
||||
* Uri::Encode user and password to prevent issues in browsers
|
||||
* Fixed "Invalid AJAX response" When using Built-in PHP Webserver in Windows [#1258](https://github.com/getgrav/grav-plugin-admin/issues/1258)
|
||||
* Remove support for `config.user`, it was broken and bad practise
|
||||
* Make sure that `clean cache` uses valid path [#1745](https://github.com/getgrav/grav/pull/1745)
|
||||
* Fixed token creation issue with `Uri` params like `/id:3`
|
||||
* Fixed CSS Pipeline failing with Google remote fonts if the file was minified [#1261](https://github.com/getgrav/grav-plugin-admin/issues/1261)
|
||||
* Forced `field.multiple: true` to allow use of min/max options in `checkboxes.validate`
|
||||
|
||||
# v1.3.8
|
||||
## 10/26/2017
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ $ bin/gpm update
|
||||
We appreciate any contribution to Grav, whether it is related to bugs, grammar, or simply a suggestion or improvement! Please refer to the [Contributing guide](CONTRIBUTING.md) for more guidance on this topic.
|
||||
|
||||
## Security issues
|
||||
If you discover a possible security issue related to Grav or one of its plugins, please send an email to the core team at contact@getgrav.org and we'll address it as soon as possible.
|
||||
If you discover a possible security issue related to Grav or one of its plugins, please email the core team at contact@getgrav.org and we'll address it as soon as possible.
|
||||
|
||||
# Getting Started
|
||||
|
||||
@@ -101,9 +101,11 @@ If you discover a possible security issue related to Grav or one of its plugins,
|
||||
|
||||
* Have a look at our [Basic Tutorial](https://learn.getgrav.org/basics/basic-tutorial)
|
||||
* Dive into more [advanced](https://learn.getgrav.org/advanced) functions
|
||||
* Learn about the [Grav CLI](https://learn.getgrav.org/cli-console/grav-cli)
|
||||
* Review examples in the [Grav Cookbook](https://learn.getgrav.org/cookbook)
|
||||
|
||||
# Backers
|
||||
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/grav#backer)]
|
||||
Support Grav with a monthly donation to help us continue development. [[Become a backer](https://opencollective.com/grav#backer)]
|
||||
|
||||
<a href="https://opencollective.com/grav/backer/0/website" target="_blank"><img src="https://opencollective.com/grav/backer/0/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/grav/backer/1/website" target="_blank"><img src="https://opencollective.com/grav/backer/1/avatar.svg"></a>
|
||||
|
||||
@@ -1131,6 +1131,16 @@ form:
|
||||
label: PLUGIN_ADMIN.PWD_REGEX
|
||||
help: PLUGIN_ADMIN.PWD_REGEX_HELP
|
||||
|
||||
intl_enabled:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.INTL_ENABLED
|
||||
highlight: 1
|
||||
help: PLUGIN_ADMIN.INTL_ENABLED_HELP
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
wrapped_site:
|
||||
type: toggle
|
||||
@@ -1154,17 +1164,6 @@ form:
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
case_insensitive_urls:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.CASE_INSENSITIVE_URLS
|
||||
highlight: 0
|
||||
help: PLUGIN_ADMIN.CASE_INSENSITIVE_URLS_HELP
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
param_sep:
|
||||
type: select
|
||||
size: medium
|
||||
@@ -1187,6 +1186,18 @@ form:
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
force_lowercase_urls:
|
||||
type: toggle
|
||||
label: PLUGIN_ADMIN.FORCE_LOWERCASE_URLS
|
||||
highlight: 1
|
||||
default: 1
|
||||
help: PLUGIN_ADMIN.FORCE_LOWERCASE_URLS_HELP
|
||||
options:
|
||||
1: PLUGIN_ADMIN.YES
|
||||
0: PLUGIN_ADMIN.NO
|
||||
validate:
|
||||
type: bool
|
||||
|
||||
custom_base_url:
|
||||
type: text
|
||||
size: medium
|
||||
|
||||
@@ -42,6 +42,6 @@ form:
|
||||
type: ignore
|
||||
content:
|
||||
type: ignore
|
||||
header.media_orider:
|
||||
header.media_order:
|
||||
type: ignore
|
||||
|
||||
|
||||
@@ -5,9 +5,11 @@ param_sep: ':' # Parameter separator, use ';'
|
||||
wrapped_site: false # For themes/plugins to know if Grav is wrapped by another platform
|
||||
reverse_proxy_setup: false # Running in a reverse proxy scenario with different webserver ports than proxy
|
||||
force_ssl: false # If enabled, Grav forces to be accessed via HTTPS (NOTE: Not an ideal solution)
|
||||
force_lowercase_urls: true # If you want to support mixed cased URLs set this to false
|
||||
custom_base_url: '' # Set the base_url manually, e.g. http://yoursite.com/yourpath
|
||||
username_regex: '^[a-z0-9_-]{3,16}$' # Only lowercase chars, digits, dashes, underscores. 3 - 16 chars
|
||||
pwd_regex: '(?=.*\d)(?=.*[a-z])(?=.*[A-Z]).{8,}' # At least one number, one uppercase and lowercase letter, and be at least 8+ chars
|
||||
intl_enabled: true # Special logic for PHP International Extension (mod_intl)
|
||||
|
||||
languages:
|
||||
supported: [] # List of languages supported. eg: [en, fr, de]
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
// Some standard defines
|
||||
define('GRAV', true);
|
||||
define('GRAV_VERSION', '1.3.8');
|
||||
define('GRAV_VERSION', '1.3.9');
|
||||
//define('GRAV_TESTING', true);
|
||||
define('DS', '/');
|
||||
|
||||
|
||||
@@ -35,11 +35,15 @@ class Assets
|
||||
const CSS_URL_REGEX = '{url\(([\'\"]?)(.*?)\1\)}';
|
||||
|
||||
/** @const Regex to match CSS sourcemap comments */
|
||||
const CSS_SOURCEMAP_REGEX = '{\/\*# (.*) \*\/}';
|
||||
const CSS_SOURCEMAP_REGEX = '{\/\*# (.*?) \*\/}';
|
||||
|
||||
/** @const Regex to match CSS import content */
|
||||
const CSS_IMPORT_REGEX = '{@import(.*);}';
|
||||
const CSS_IMPORT_REGEX = '{@import(.*?);}';
|
||||
|
||||
/**
|
||||
* @const Regex to match <script> or <style> tag when adding inline style/script. Note that this only supports a
|
||||
* single tag, so the check is greedy to avoid issues in JS.
|
||||
*/
|
||||
const HTML_TAG_REGEX = '#(<([A-Z][A-Z0-9]*)>)+(.*)(<\/\2>)#is';
|
||||
|
||||
|
||||
|
||||
@@ -390,6 +390,7 @@ class Cache extends Getters
|
||||
// Convert stream to a real path
|
||||
try {
|
||||
$path = $locator->findResource($stream, true, true);
|
||||
if($path === false) continue;
|
||||
|
||||
$anything = false;
|
||||
$files = glob($path . '/*');
|
||||
|
||||
@@ -28,9 +28,12 @@ class Validation
|
||||
$messages = [];
|
||||
|
||||
$validate = isset($field['validate']) ? (array) $field['validate'] : [];
|
||||
// Validate type with fallback type text.
|
||||
$type = (string) isset($validate['type']) ? $validate['type'] : $field['type'];
|
||||
$method = 'type'.strtr($type, '-', '_');
|
||||
|
||||
// If value isn't required, we will stop validation if empty value is given.
|
||||
if (empty($validate['required']) && ($value === null || $value === '')) {
|
||||
if ((empty($validate['required']) || (isset($validate['required']) && $validate['required'] !== true)) && ($value === null || $value === '' || ($field['type'] === 'checkbox' && $value == false))) {
|
||||
return $messages;
|
||||
}
|
||||
|
||||
@@ -46,10 +49,6 @@ class Validation
|
||||
// Get language class.
|
||||
$language = Grav::instance()['language'];
|
||||
|
||||
// Validate type with fallback type text.
|
||||
$type = (string) isset($field['validate']['type']) ? $field['validate']['type'] : $field['type'];
|
||||
$method = 'type'.strtr($type, '-', '_');
|
||||
|
||||
$name = ucfirst(isset($field['label']) ? $field['label'] : $field['name']);
|
||||
$message = (string) isset($field['validate']['message'])
|
||||
? $language->translate($field['validate']['message'])
|
||||
@@ -161,7 +160,7 @@ class Validation
|
||||
return is_array($value) ? $value : preg_split('/\s*,\s*/', $value, -1, PREG_SPLIT_NO_EMPTY);
|
||||
}
|
||||
|
||||
protected static function typeCommaList($value, array $params, array $field)
|
||||
public static function typeCommaList($value, array $params, array $field)
|
||||
{
|
||||
return is_array($value) ? true : self::typeText($value, $params, $field);
|
||||
}
|
||||
@@ -230,6 +229,8 @@ class Validation
|
||||
*/
|
||||
public static function typeCheckboxes($value, array $params, array $field)
|
||||
{
|
||||
// Set multiple: true so checkboxes can easily use min/max counts to control number of options required
|
||||
$field['multiple'] = true;
|
||||
return self::typeArray((array) $value, $params, $field);
|
||||
}
|
||||
|
||||
@@ -253,7 +254,7 @@ class Validation
|
||||
if (!isset($field['value'])) {
|
||||
$field['value'] = 1;
|
||||
}
|
||||
if ($value && $value != $field['value']) {
|
||||
if (isset($value) && $value != $field['value']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -758,6 +759,11 @@ class Validation
|
||||
&& $value instanceof \Countable);
|
||||
}
|
||||
|
||||
public static function filterItem_List($value, $params)
|
||||
{
|
||||
return array_values(array_filter($value, function($v) { return !empty($v); } ));
|
||||
}
|
||||
|
||||
public static function validateJson($value, $params)
|
||||
{
|
||||
return (bool) (@json_decode($value));
|
||||
|
||||
@@ -27,7 +27,7 @@ trait CompiledFile
|
||||
// If nothing has been loaded, attempt to get pre-compiled version of the file first.
|
||||
if ($var === null && $this->raw === null && $this->content === null) {
|
||||
$key = md5($this->filename);
|
||||
$file = PhpFile::instance(CACHE_DIR . DS . "compiled/files/{$key}{$this->extension}.php");
|
||||
$file = PhpFile::instance(CACHE_DIR . "compiled/files/{$key}{$this->extension}.php");
|
||||
|
||||
$modified = $this->modified();
|
||||
|
||||
|
||||
@@ -17,6 +17,9 @@ use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class GPM extends Iterator
|
||||
{
|
||||
/** @var callable */
|
||||
private $callback;
|
||||
|
||||
/**
|
||||
* Local installed Packages
|
||||
* @var Local\Packages
|
||||
@@ -29,6 +32,9 @@ class GPM extends Iterator
|
||||
*/
|
||||
private $repository;
|
||||
|
||||
/** @var bool */
|
||||
private $shouldRefresh;
|
||||
|
||||
/**
|
||||
* @var Remote\GravCore
|
||||
*/
|
||||
@@ -47,18 +53,33 @@ class GPM extends Iterator
|
||||
];
|
||||
|
||||
/**
|
||||
* Creates a new GPM instance with Local and Remote packages available
|
||||
* Loads Remote Packages available
|
||||
*/
|
||||
private function retrieveRemoteRepository()
|
||||
{
|
||||
if (!$this->repository) {
|
||||
$this->repository = new Remote\Packages($this->shouldRefresh, $this->callback);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new GPM instance with Local packages available
|
||||
* @param boolean $refresh Applies to Remote Packages only and forces a refetch of data
|
||||
* @param callable $callback Either a function or callback in array notation
|
||||
*/
|
||||
public function __construct($refresh = false, $callback = null)
|
||||
{
|
||||
$this->installed = new Local\Packages();
|
||||
try {
|
||||
$this->repository = new Remote\Packages($refresh, $callback);
|
||||
$this->grav = new Remote\GravCore($refresh, $callback);
|
||||
} catch (\Exception $e) {
|
||||
}
|
||||
$this->shouldRefresh = $refresh;
|
||||
$this->callback = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads Remote Grav package available
|
||||
*/
|
||||
public function loadRemoteGrav()
|
||||
{
|
||||
$this->grav = new Remote\GravCore($this->refresh, $this->callback);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -268,6 +289,7 @@ class GPM extends Iterator
|
||||
*/
|
||||
public function getLatestVersionOfPackage($package_name)
|
||||
{
|
||||
$this->retrieveRemoteRepository();
|
||||
$repository = $this->repository['plugins'];
|
||||
if (isset($repository[$package_name])) {
|
||||
return $repository[$package_name]->available ?: $repository[$package_name]->version;
|
||||
@@ -310,6 +332,7 @@ class GPM extends Iterator
|
||||
public function getUpdatableThemes()
|
||||
{
|
||||
$items = [];
|
||||
$this->retrieveRemoteRepository();
|
||||
$repository = $this->repository['themes'];
|
||||
|
||||
// local cache to speed things up
|
||||
@@ -357,6 +380,7 @@ class GPM extends Iterator
|
||||
*/
|
||||
public function getReleaseType($package_name)
|
||||
{
|
||||
$this->retrieveRemoteRepository();
|
||||
$repository = $this->repository['plugins'];
|
||||
if (isset($repository[$package_name])) {
|
||||
return $repository[$package_name]->release_type;
|
||||
@@ -405,6 +429,7 @@ class GPM extends Iterator
|
||||
*/
|
||||
public function getRepositoryPlugin($slug)
|
||||
{
|
||||
$this->retrieveRemoteRepository();
|
||||
return @$this->repository['plugins'][$slug];
|
||||
}
|
||||
|
||||
@@ -443,6 +468,7 @@ class GPM extends Iterator
|
||||
*/
|
||||
public function getRepository()
|
||||
{
|
||||
$this->retrieveRemoteRepository();
|
||||
return $this->repository;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ use Grav\Common\Grav;
|
||||
use Grav\Common\Page\Page;
|
||||
use Grav\Common\Uri;
|
||||
use Grav\Common\Page\Medium\Medium;
|
||||
use Grav\Common\Utils;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
|
||||
@@ -321,7 +322,7 @@ class Excerpts
|
||||
*/
|
||||
protected static function parseUrl($url)
|
||||
{
|
||||
$url_parts = parse_url($url);
|
||||
$url_parts = Utils::multibyteParseUrl($url);
|
||||
|
||||
if (isset($url_parts['scheme'])) {
|
||||
/** @var UniformResourceLocator $locator */
|
||||
|
||||
@@ -30,6 +30,114 @@ class AudioMedium extends Medium
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to set or remove the HTML5 default controls
|
||||
*
|
||||
* @param bool $display
|
||||
* @return $this
|
||||
*/
|
||||
public function controls($display = true)
|
||||
{
|
||||
if($display)
|
||||
{
|
||||
$this->attributes['controls'] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
unset($this->attributes['controls']);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to set the preload behaviour
|
||||
*
|
||||
* @param $preload
|
||||
* @return $this
|
||||
*/
|
||||
public function preload($preload)
|
||||
{
|
||||
$validPreloadAttrs = array('auto','metadata','none');
|
||||
|
||||
if (in_array($preload, $validPreloadAttrs))
|
||||
{
|
||||
$this->attributes['preload'] = $preload;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to set the controlsList behaviour
|
||||
* Separate multiple values with a hyphen
|
||||
*
|
||||
* @param $controlsList
|
||||
* @return $this
|
||||
*/
|
||||
public function controlsList($controlsList)
|
||||
{
|
||||
$controlsList = str_replace('-', ' ', $controlsList);
|
||||
$this->attributes['controlsList'] = $controlsList;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to set the muted attribute
|
||||
*
|
||||
* @param bool $status
|
||||
* @return $this
|
||||
*/
|
||||
public function muted($status = false)
|
||||
{
|
||||
if($status)
|
||||
{
|
||||
$this->attributes['muted'] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
unset($this->attributes['muted']);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to set the loop attribute
|
||||
*
|
||||
* @param bool $status
|
||||
* @return $this
|
||||
*/
|
||||
public function loop($status = false)
|
||||
{
|
||||
if($status)
|
||||
{
|
||||
$this->attributes['loop'] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
unset($this->attributes['loop']);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to set the autoplay attribute
|
||||
*
|
||||
* @param bool $status
|
||||
* @return $this
|
||||
*/
|
||||
public function autoplay($status = false)
|
||||
{
|
||||
if($status)
|
||||
{
|
||||
$this->attributes['autoplay'] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
unset($this->attributes['autoplay']);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reset medium.
|
||||
*
|
||||
|
||||
@@ -72,6 +72,16 @@ class Medium extends Data implements RenderableInterface
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a copy of this media object
|
||||
*
|
||||
* @return Medium
|
||||
*/
|
||||
public function copy()
|
||||
{
|
||||
return clone($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return just metadata from the Medium object
|
||||
*
|
||||
|
||||
@@ -125,7 +125,7 @@ class Page
|
||||
$config = Grav::instance()['config'];
|
||||
|
||||
$this->hide_home_route = $config->get('system.home.hide_in_urls', false);
|
||||
$this->home_route = $config->get('system.home.alias');
|
||||
$this->home_route = $this->adjustRouteCase($config->get('system.home.alias'));
|
||||
$this->filePath($file->getPathName());
|
||||
$this->modified($file->getMTime());
|
||||
$this->id($this->modified() . md5($this->filePath()));
|
||||
@@ -1558,7 +1558,7 @@ class Page
|
||||
}
|
||||
|
||||
if (empty($this->slug)) {
|
||||
$this->slug = strtolower(preg_replace(PAGE_ORDER_PREFIX_REGEX, '', $this->folder));
|
||||
$this->slug = $this->adjustRouteCase(preg_replace(PAGE_ORDER_PREFIX_REGEX, '', $this->folder));
|
||||
}
|
||||
|
||||
|
||||
@@ -1748,7 +1748,7 @@ class Page
|
||||
if (empty($this->raw_route)) {
|
||||
$baseRoute = $this->parent ? (string)$this->parent()->rawRoute() : null;
|
||||
|
||||
$slug = preg_replace(PAGE_ORDER_PREFIX_REGEX, '', $this->folder);
|
||||
$slug = $this->adjustRouteCase(preg_replace(PAGE_ORDER_PREFIX_REGEX, '', $this->folder));
|
||||
|
||||
$this->raw_route = isset($baseRoute) ? $baseRoute . '/' . $slug : null;
|
||||
}
|
||||
@@ -2295,7 +2295,7 @@ class Page
|
||||
*/
|
||||
public function active()
|
||||
{
|
||||
$uri_path = rtrim(Grav::instance()['uri']->path(), '/') ?: '/';
|
||||
$uri_path = rtrim(urldecode(Grav::instance()['uri']->path()), '/') ?: '/';
|
||||
$routes = Grav::instance()['pages']->routes();
|
||||
|
||||
if (isset($routes[$uri_path])) {
|
||||
@@ -2318,7 +2318,7 @@ class Page
|
||||
{
|
||||
$uri = Grav::instance()['uri'];
|
||||
$pages = Grav::instance()['pages'];
|
||||
$uri_path = rtrim($uri->path(), '/');
|
||||
$uri_path = rtrim(urldecode($uri->path()), '/');
|
||||
$routes = Grav::instance()['pages']->routes();
|
||||
|
||||
if (isset($routes[$uri_path])) {
|
||||
@@ -2896,4 +2896,15 @@ class Page
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function adjustRouteCase($route)
|
||||
{
|
||||
$case_insensitive = Grav::instance()['config']->get('system.force_lowercase_urls');
|
||||
|
||||
if ($case_insensitive) {
|
||||
return mb_strtolower($route);
|
||||
} else {
|
||||
return $route;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -265,6 +265,8 @@ class Pages
|
||||
$this->children[$page->parent()->path()][$page->path()] = ['slug' => $page->slug()];
|
||||
}
|
||||
$this->routes[$route] = $page->path();
|
||||
|
||||
$this->grav->fireEvent('onPageProcessed', new Event(['page' => $page]));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1221,6 +1223,9 @@ class Pages
|
||||
case 'basename':
|
||||
$list[$key] = basename($key);
|
||||
break;
|
||||
case 'folder':
|
||||
$list[$key] = $child->folder();
|
||||
break;
|
||||
case (is_string($header_query[0])):
|
||||
$child_header = new Header((array)$child->header());
|
||||
$header_value = $child_header->get($header_query[0]);
|
||||
@@ -1250,7 +1255,7 @@ class Pages
|
||||
$list = $this->arrayShuffle($list);
|
||||
} else {
|
||||
// else just sort the list according to specified key
|
||||
if (extension_loaded('intl')) {
|
||||
if (extension_loaded('intl') && $this->grav['config']->get('system.intl_enabled')) {
|
||||
$locale = setlocale(LC_COLLATE, 0); //`setlocale` with a 0 param returns the current locale set
|
||||
$col = Collator::create($locale);
|
||||
if ($col) {
|
||||
@@ -1258,6 +1263,13 @@ class Pages
|
||||
$list = preg_replace_callback('~([0-9]+)\.~', function($number) {
|
||||
return sprintf('%032d.', $number[0]);
|
||||
}, $list);
|
||||
|
||||
$list_vals = array_values($list);
|
||||
if (is_numeric(array_shift($list_vals))) {
|
||||
$sort_flags = Collator::SORT_REGULAR;
|
||||
} else {
|
||||
$sort_flags = Collator::SORT_STRING;
|
||||
}
|
||||
}
|
||||
|
||||
$col->asort($list, $sort_flags);
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
namespace Grav\Common\Processors;
|
||||
|
||||
use Grav\Common\Page\Page;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
|
||||
class PagesProcessor extends ProcessorBase implements ProcessorInterface
|
||||
{
|
||||
@@ -21,15 +22,15 @@ class PagesProcessor extends ProcessorBase implements ProcessorInterface
|
||||
$this->container['debugger']->addMessage($this->container['cache']->getCacheStatus());
|
||||
|
||||
$this->container['pages']->init();
|
||||
$this->container->fireEvent('onPagesInitialized');
|
||||
$this->container->fireEvent('onPageInitialized');
|
||||
$this->container->fireEvent('onPagesInitialized', new Event(['pages' => $this->container['pages']]));
|
||||
$this->container->fireEvent('onPageInitialized', new Event(['page' => $this->container['page']]));
|
||||
|
||||
/** @var Page $page */
|
||||
$page = $this->container['page'];
|
||||
|
||||
if (!$page->routable()) {
|
||||
// If no page found, fire event
|
||||
$event = $this->container->fireEvent('onPageNotFound');
|
||||
$event = $this->container->fireEvent('onPageNotFound', new Event(['page' => $page]));
|
||||
|
||||
if (isset($event->page)) {
|
||||
unset ($this->container['page']);
|
||||
|
||||
@@ -105,4 +105,20 @@ class Session extends BaseSession
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
// Store something in cookie temporarily
|
||||
public function setFlashCookieObject($name, $object, $time = 60)
|
||||
{
|
||||
setcookie($name, json_encode($object), time() + $time, '/');
|
||||
}
|
||||
|
||||
// Return object and remove it from the cookie
|
||||
public function getFlashCookieObject($name)
|
||||
{
|
||||
if (isset($_COOKIE[$name])) {
|
||||
$object = json_decode($_COOKIE[$name]);
|
||||
setcookie($name, '', time() - 3600, '/');
|
||||
return $object;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,6 +97,9 @@ class Twig
|
||||
|
||||
$this->grav->fireEvent('onTwigTemplatePaths');
|
||||
|
||||
// Add Grav core templates location
|
||||
$this->twig_paths = array_merge($this->twig_paths, $locator->findResources('system://templates'));
|
||||
|
||||
$this->loader = new \Twig_Loader_Filesystem($this->twig_paths);
|
||||
|
||||
$this->grav->fireEvent('onTwigLoader');
|
||||
|
||||
@@ -107,7 +107,9 @@ class Uri
|
||||
// Build fragment.
|
||||
$this->fragment = null;
|
||||
|
||||
// Filter path and query string.
|
||||
// Filter userinfo, path and query string.
|
||||
$this->user = $this->user !== null ? static::filterUserInfo($this->user) : null;
|
||||
$this->password = $this->password !== null ? static::filterUserInfo($this->password) : null;
|
||||
$this->path = empty($this->path) ? '/' : static::filterPath($this->path);
|
||||
$this->query = static::filterQuery($this->query);
|
||||
|
||||
@@ -148,7 +150,9 @@ class Uri
|
||||
$this->host = $this->validateHostname($this->host) ? $this->host : 'unknown';
|
||||
}
|
||||
|
||||
// Filter path, query string and fragment.
|
||||
// Filter userinfo, path, query string and fragment.
|
||||
$this->user = $this->user !== null ? static::filterUserInfo($this->user) : null;
|
||||
$this->password = $this->password !== null ? static::filterUserInfo($this->password) : null;
|
||||
$this->path = empty($this->path) ? '/' : static::filterPath($this->path);
|
||||
$this->query = static::filterQuery($this->query);
|
||||
$this->fragment = $this->fragment !== null ? static::filterQuery($this->fragment) : null;
|
||||
@@ -245,7 +249,8 @@ class Uri
|
||||
*/
|
||||
private function buildRootPath()
|
||||
{
|
||||
$scriptPath = $_SERVER['PHP_SELF'];
|
||||
// In Windows script path uses backslash, convert it:
|
||||
$scriptPath = str_replace('\\', '/', $_SERVER['PHP_SELF']);
|
||||
$rootPath = str_replace(' ', '%20', rtrim(substr($scriptPath, 0, strpos($scriptPath, 'index.php')), '/'));
|
||||
|
||||
// check if userdir in the path and workaround PHP bug with PHP_SELF
|
||||
@@ -281,22 +286,21 @@ class Uri
|
||||
$this->base .= ':' . (string)$this->port;
|
||||
}
|
||||
|
||||
// Set some defaults
|
||||
if ($grav['config']->get('system.custom_base_url')) {
|
||||
$this->root_path = parse_url($grav['config']->get('system.custom_base_url'), PHP_URL_PATH);
|
||||
$this->root = $grav['config']->get('system.custom_base_url');
|
||||
// Handle custom base
|
||||
$custom_base = rtrim($grav['config']->get('system.custom_base_url'), '/');
|
||||
|
||||
if ($custom_base) {
|
||||
$custom_parts = parse_url($custom_base);
|
||||
$orig_root_path = $this->root_path;
|
||||
$this->root_path = isset($custom_parts['path']) ? rtrim($custom_parts['path'], '/') : '';
|
||||
$this->root = isset($custom_parts['scheme']) ? $custom_base : $this->base . $this->root_path;
|
||||
$this->uri = Utils::replaceFirstOccurrence($orig_root_path, $this->root_path, $this->uri);
|
||||
} else {
|
||||
$this->root = $this->base . $this->root_path;
|
||||
}
|
||||
|
||||
$this->url = $this->base . $this->uri;
|
||||
|
||||
// if case insensitive urls is enabled, lowercase the url
|
||||
if( $grav['config']->get('system.case_insensitive_urls') ){
|
||||
$this->url = strtolower($this->url);
|
||||
}
|
||||
|
||||
// get any params and remove them
|
||||
$uri = str_replace($this->root, '', $this->url);
|
||||
|
||||
// remove the setup.php based base if set:
|
||||
@@ -306,8 +310,9 @@ class Uri
|
||||
}
|
||||
|
||||
// If configured to, redirect trailing slash URI's with a 302 redirect
|
||||
if ($uri !== '/' && $config->get('system.pages.redirect_trailing_slash', false) && Utils::endsWith($uri, '/')) {
|
||||
$grav->redirect(str_replace($this->root, '', rtrim($uri, '/')), 302);
|
||||
$redirect = str_replace($this->root, '', rtrim($uri, '/'));
|
||||
if ($redirect && $uri !== '/' && $redirect !== $this->base() && $config->get('system.pages.redirect_trailing_slash', false) && Utils::endsWith($uri, '/')) {
|
||||
$grav->redirect($redirect, 302);
|
||||
}
|
||||
|
||||
// process params
|
||||
@@ -1138,11 +1143,20 @@ class Uri
|
||||
*/
|
||||
public static function addNonce($url, $action, $nonceParamName = 'nonce')
|
||||
{
|
||||
$fake = $url && $url[0] === '/';
|
||||
|
||||
if ($fake) {
|
||||
$url = 'http://domain.com' . $url;
|
||||
}
|
||||
$uri = new static($url);
|
||||
$parts = $uri->toArray();
|
||||
$nonce = Utils::getNonce($action);
|
||||
$parts['params'] = (isset($parts['params']) ? $parts['params'] : []) + [$nonceParamName => $nonce];
|
||||
|
||||
if ($fake) {
|
||||
unset($parts['scheme'], $parts['host']);
|
||||
}
|
||||
|
||||
return static::buildUrl($parts);
|
||||
}
|
||||
|
||||
@@ -1177,6 +1191,23 @@ class Uri
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the user info string.
|
||||
*
|
||||
* @param string $info The raw user or password.
|
||||
* @return string The percent-encoded user or password string.
|
||||
*/
|
||||
public static function filterUserInfo($info)
|
||||
{
|
||||
return preg_replace_callback(
|
||||
'/(?:[^a-zA-Z0-9_\-\.~!\$&\'\(\)\*\+,;=]+|%(?![A-Fa-f0-9]{2}))/u',
|
||||
function ($match) {
|
||||
return rawurlencode($match[0]);
|
||||
},
|
||||
$info
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter Uri path.
|
||||
*
|
||||
@@ -1192,7 +1223,7 @@ class Uri
|
||||
public static function filterPath($path)
|
||||
{
|
||||
return preg_replace_callback(
|
||||
'/(?:[^a-zA-Z0-9_\-\.~:@&=\+\$,\/;%]+|%(?![A-Fa-f0-9]{2}))/',
|
||||
'/(?:[^a-zA-Z0-9_\-\.~:@&=\+\$,\/;%]+|%(?![A-Fa-f0-9]{2}))/u',
|
||||
function ($match) {
|
||||
return rawurlencode($match[0]);
|
||||
},
|
||||
@@ -1209,7 +1240,7 @@ class Uri
|
||||
public static function filterQuery($query)
|
||||
{
|
||||
return preg_replace_callback(
|
||||
'/(?:[^a-zA-Z0-9_\-\.~!\$&\'\(\)\*\+,;=%:@\/\?]+|%(?![A-Fa-f0-9]{2}))/',
|
||||
'/(?:[^a-zA-Z0-9_\-\.~!\$&\'\(\)\*\+,;=%:@\/\?]+|%(?![A-Fa-f0-9]{2}))/u',
|
||||
function ($match) {
|
||||
return rawurlencode($match[0]);
|
||||
},
|
||||
|
||||
@@ -29,7 +29,6 @@ class User extends Data
|
||||
{
|
||||
$grav = Grav::instance();
|
||||
$locator = $grav['locator'];
|
||||
$config = $grav['config'];
|
||||
|
||||
// force lowercase of username
|
||||
$username = strtolower($username);
|
||||
@@ -38,7 +37,7 @@ class User extends Data
|
||||
$blueprint = $blueprints->get('user/account');
|
||||
$file_path = $locator->findResource('account://' . $username . YAML_EXT);
|
||||
$file = CompiledYamlFile::instance($file_path);
|
||||
$content = $file->content();
|
||||
$content = (array)$file->content();
|
||||
if (!isset($content['username'])) {
|
||||
$content['username'] = $username;
|
||||
}
|
||||
@@ -48,9 +47,6 @@ class User extends Data
|
||||
$user = new User($content, $blueprint);
|
||||
$user->file($file);
|
||||
|
||||
// add user to config
|
||||
$config->set("user", $user);
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ abstract class Utils
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the substring of a string up to a specified needle. if not found, return the whole haytack
|
||||
* Returns the substring of a string up to a specified needle. if not found, return the whole haystack
|
||||
*
|
||||
* @param $haystack
|
||||
* @param $needle
|
||||
@@ -96,6 +96,46 @@ abstract class Utils
|
||||
return $haystack;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to replace only the first occurrence in a string
|
||||
*
|
||||
* @param $search
|
||||
* @param $replace
|
||||
* @param $subject
|
||||
* @return mixed
|
||||
*/
|
||||
public static function replaceFirstOccurrence($search, $replace, $subject)
|
||||
{
|
||||
if (!$search) {
|
||||
return $subject;
|
||||
}
|
||||
$pos = strpos($subject, $search);
|
||||
if ($pos !== false) {
|
||||
$subject = substr_replace($subject, $replace, $pos, strlen($search));
|
||||
}
|
||||
return $subject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility method to replace only the last occurrence in a string
|
||||
*
|
||||
* @param $search
|
||||
* @param $replace
|
||||
* @param $subject
|
||||
* @return mixed
|
||||
*/
|
||||
public static function replaceLastOccurrence($search, $replace, $subject)
|
||||
{
|
||||
$pos = strrpos($subject, $search);
|
||||
|
||||
if($pos !== false)
|
||||
{
|
||||
$subject = substr_replace($subject, $replace, $pos, strlen($search));
|
||||
}
|
||||
|
||||
return $subject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge two objects into one.
|
||||
*
|
||||
@@ -957,4 +997,36 @@ abstract class Utils
|
||||
return intval($size);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Multibyte-safe Parse URL function
|
||||
*
|
||||
* @param $url
|
||||
* @return mixed
|
||||
*/
|
||||
public static function multibyteParseUrl($url)
|
||||
{
|
||||
$enc_url = preg_replace_callback(
|
||||
'%[^:/@?&=#]+%usD',
|
||||
function ($matches)
|
||||
{
|
||||
return urlencode($matches[0]);
|
||||
},
|
||||
$url
|
||||
);
|
||||
|
||||
$parts = parse_url($enc_url);
|
||||
|
||||
if($parts === false)
|
||||
{
|
||||
throw new \InvalidArgumentException('Malformed URL: ' . $url);
|
||||
}
|
||||
|
||||
foreach($parts as $name => $value)
|
||||
{
|
||||
$parts[$name] = urldecode($value);
|
||||
}
|
||||
|
||||
return $parts;
|
||||
}
|
||||
}
|
||||
|
||||
14
system/templates/partials/messages.html.twig
Normal file
14
system/templates/partials/messages.html.twig
Normal file
@@ -0,0 +1,14 @@
|
||||
{% set status_mapping = {'info':'green', 'error': 'red', 'warning': 'yellow'} %}
|
||||
|
||||
{% if grav.messages.all %}
|
||||
<div id="messages">
|
||||
{% for message in grav.messages.fetch %}
|
||||
|
||||
{% set scope = message.scope|e %}
|
||||
{% set color = status_mapping[scope] %}
|
||||
|
||||
<div class="notices {{ scope }} {{ color }}"><p>{{ message.message|raw }}</p></div>
|
||||
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
3
system/templates/partials/metadata.html.twig
Normal file
3
system/templates/partials/metadata.html.twig
Normal file
@@ -0,0 +1,3 @@
|
||||
{% for meta in page.metadata %}
|
||||
<meta {% if meta.name %}name="{{ meta.name }}" {% endif %}{% if meta.http_equiv %}http-equiv="{{ meta.http_equiv }}" {% endif %}{% if meta.charset %}charset="{{ meta.charset }}" {% endif %}{% if meta.property %}property="{{ meta.property }}" {% endif %}{% if meta.content %}content="{{ meta.content }}" {% endif %}/>
|
||||
{% endfor %}
|
||||
@@ -320,4 +320,47 @@ class GpmTest extends \Codeception\TestCase\Test
|
||||
$this->assertSame(null, $this->gpm->calculateVersionNumberFromDependencyVersion('*'));
|
||||
$this->assertSame('2.0.2', $this->gpm->calculateVersionNumberFromDependencyVersion('2.0.2'));
|
||||
}
|
||||
|
||||
public function testRemoteRepositoryIsEmptyAfterConstruct()
|
||||
{
|
||||
$gpm = new GPM();
|
||||
$reflection = new \ReflectionClass(get_class($gpm));
|
||||
$repository = $reflection->getProperty('repository');
|
||||
$repository->setAccessible(true);
|
||||
|
||||
$this->assertSame(null, $repository->getValue($gpm));
|
||||
}
|
||||
|
||||
public function testLocalRepositoryIsNotEmptyAfterConstruct()
|
||||
{
|
||||
$gpm = new GPM();
|
||||
$reflection = new \ReflectionClass(get_class($gpm));
|
||||
$repository = $reflection->getProperty('installed');
|
||||
$repository->setAccessible(true);
|
||||
|
||||
$this->assertInstanceOf( '\Grav\Common\GPM\Local\Packages', $repository->getValue($gpm));
|
||||
}
|
||||
|
||||
public function testGetRepository()
|
||||
{
|
||||
$this->assertInstanceOf('\Grav\Common\GPM\Remote\Packages', $this->gpm->getRepository());
|
||||
}
|
||||
|
||||
public function testLatestVersionOfPackage()
|
||||
{
|
||||
$gpm = new GPM();
|
||||
$this->assertNotNull($gpm->getLatestVersionOfPackage('admin'));
|
||||
}
|
||||
|
||||
public function testReleaseType()
|
||||
{
|
||||
$gpm = new GPM();
|
||||
$this->assertNotNull($gpm->getReleaseType('admin'));
|
||||
}
|
||||
|
||||
public function testGetRepositoryPlugin()
|
||||
{
|
||||
$gpm = new GPM();
|
||||
$this->assertNotNull($gpm->getRepositoryPlugin('admin'));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user