diff --git a/.travis.yml b/.travis.yml
index 578da9f7f..c458c24ef 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -40,8 +40,10 @@ env:
- TRAVIS_TAG=$(curl --fail --user ${GH_API_USER} -s https://api.github.com/repos/getgrav/grav/releases/latest | grep tag_name | head -n 1 | cut -d '"' -f 4)
before_install:
+ - export TZ=Pacific/Honolulu
- composer self-update
- go get github.com/aktau/github-release
+ - echo "Travis Date: `date`"
- git clone --quiet --depth=50 --branch=master https://${BB_TOKEN}bitbucket.org/rockettheme/grav-devtools.git $RT_DEVTOOLS &>/dev/null;
- if [ ! -z "$TRAVIS_TAG" ]; then
cd "${RT_DEVTOOLS}";
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d51bda0d5..a45de808d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,21 @@
+# v1.0.5
+## 12/18/2015
+
+1. [](#new)
+ * Add ability to extend markdown with plugins
+ * Added support for plugins to have individual language files
+ * Added `7z` to media formats
+ * Use Grav's fork of Parsedown until PR is merged
+ * New function to persist plugin configuration to disk
+ * GPM `selfupgrade` will now check PHP version requirements
+1. [](#improved)
+ * If the field allows multiple files, return array
+ * Handle non-array values in file validation
+1. [](#bugfix)
+ * Fix when looping `fields` param in a `list` field
+ * Properly convert commas to spaces for media attributes
+ * Forcing Travis VM to HI timezone to address future files in zip file
+
# v1.0.4
## 12/12/2015
diff --git a/composer.json b/composer.json
index 302cabfdc..ba3c6e317 100644
--- a/composer.json
+++ b/composer.json
@@ -8,6 +8,7 @@
"require": {
"php": ">=5.4.0",
"twig/twig": "~1.23",
+ "erusev/parsedown": "dev-master as 1.6.0",
"erusev/parsedown-extra": "~0.7",
"symfony/yaml": "~2.8",
"symfony/console": "~2.8",
@@ -25,6 +26,12 @@
"rockettheme/toolbox": "~1.2",
"maximebf/debugbar": "~1.10"
},
+ "repositories": [
+ {
+ "type": "vcs",
+ "url": "https://github.com/getgrav/parsedown"
+ }
+ ],
"autoload": {
"psr-4": {
"Grav\\": "system/src/Grav"
diff --git a/composer.lock b/composer.lock
index 3483fa781..d0dca3cdd 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,8 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
"This file is @generated automatically"
],
- "hash": "09fcc6b4528be7d9c8af68a66e85f0b2",
- "content-hash": "69bee250cbc5160401d50cc47c8d6aba",
+ "hash": "82d333365d7424d73da22f99a2daa42c",
+ "content-hash": "6d876da7dbe11934a98374e67d15550c",
"packages": [
{
"name": "doctrine/cache",
@@ -130,16 +130,16 @@
},
{
"name": "erusev/parsedown",
- "version": "1.6.0",
+ "version": "dev-master",
"source": {
"type": "git",
- "url": "https://github.com/erusev/parsedown.git",
- "reference": "3ebbd730b5c2cf5ce78bc1bf64071407fc6674b7"
+ "url": "https://github.com/getgrav/parsedown.git",
+ "reference": "10a7ff776c3f16b1b3aa41c176c48150fc091065"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/erusev/parsedown/zipball/3ebbd730b5c2cf5ce78bc1bf64071407fc6674b7",
- "reference": "3ebbd730b5c2cf5ce78bc1bf64071407fc6674b7",
+ "url": "https://api.github.com/repos/getgrav/parsedown/zipball/10a7ff776c3f16b1b3aa41c176c48150fc091065",
+ "reference": "10a7ff776c3f16b1b3aa41c176c48150fc091065",
"shasum": ""
},
"type": "library",
@@ -148,7 +148,6 @@
"Parsedown": ""
}
},
- "notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
@@ -165,7 +164,10 @@
"markdown",
"parser"
],
- "time": "2015-10-04 16:44:32"
+ "support": {
+ "source": "https://github.com/getgrav/parsedown/tree/master"
+ },
+ "time": "2015-12-17 17:48:21"
},
{
"name": "erusev/parsedown-extra",
@@ -1125,9 +1127,18 @@
}
],
"packages-dev": [],
- "aliases": [],
+ "aliases": [
+ {
+ "alias": "1.6.0",
+ "alias_normalized": "1.6.0.0",
+ "version": "9999999-dev",
+ "package": "erusev/parsedown"
+ }
+ ],
"minimum-stability": "stable",
- "stability-flags": [],
+ "stability-flags": {
+ "erusev/parsedown": 20
+ },
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
diff --git a/system/config/media.yaml b/system/config/media.yaml
index 8581ba4bd..a2e82d86b 100644
--- a/system/config/media.yaml
+++ b/system/config/media.yaml
@@ -168,6 +168,10 @@ zip:
type: file
thumb: media/thumb-zip.png
mime: application/zip
+7z:
+ type: file
+ thumb: media/thumb-7zip.png
+ mime: application/x-7z-compressed
gz:
type: file
thumb: media/thumb-gz.png
diff --git a/system/defines.php b/system/defines.php
index b372a5046..1153fcbe5 100644
--- a/system/defines.php
+++ b/system/defines.php
@@ -2,8 +2,9 @@
// Some standard defines
define('GRAV', true);
-define('GRAV_VERSION', '1.0.4');
+define('GRAV_VERSION', '1.0.5');
define('DS', '/');
+define('GRAV_PHP_MIN', '5.5.9');
// Directories and Paths
if (!defined('GRAV_ROOT')) {
diff --git a/system/src/Grav/Common/Assets.php b/system/src/Grav/Common/Assets.php
index be6b81f7a..7145bcfc7 100644
--- a/system/src/Grav/Common/Assets.php
+++ b/system/src/Grav/Common/Assets.php
@@ -186,7 +186,7 @@ class Assets
$this->base_url = $base_url . '/';
// Register any preconfigured collections
- foreach ($config->get('system.assets.collections') as $name => $collection) {
+ foreach ($config->get('system.assets.collections', []) as $name => $collection) {
$this->registerCollection($name, (array)$collection);
}
}
diff --git a/system/src/Grav/Common/Data/Blueprint.php b/system/src/Grav/Common/Data/Blueprint.php
index 8be6a8ea8..68de5de81 100644
--- a/system/src/Grav/Common/Data/Blueprint.php
+++ b/system/src/Grav/Common/Data/Blueprint.php
@@ -338,8 +338,10 @@ class Blueprint implements \ArrayAccess, ExportInterface
if ($field['type'] === 'list') {
// we loop through list to get the actual field
- foreach($field['fields'] as $subName => &$subField) {
- $this->parseFormField($subField);
+ if (isset($field['fields']) && $field['fields']) {
+ foreach($field['fields'] as $subName => &$subField) {
+ $this->parseFormField($subField);
+ }
}
} else {
$this->parseFormField($field);
diff --git a/system/src/Grav/Common/Data/Validation.php b/system/src/Grav/Common/Data/Validation.php
index 9e62802b1..9f0266bd4 100644
--- a/system/src/Grav/Common/Data/Validation.php
+++ b/system/src/Grav/Common/Data/Validation.php
@@ -283,7 +283,15 @@ class Validation
protected static function filterFile($value, array $params, array $field)
{
- return (array) $value;
+ if ($field['multiple'] == true) {
+ return (array) $value;
+ }
+
+ if (is_array($value)) {
+ return reset($value);
+ }
+
+ return $value;
}
/**
diff --git a/system/src/Grav/Common/GPM/Installer.php b/system/src/Grav/Common/GPM/Installer.php
index ee9db4816..158c4048c 100644
--- a/system/src/Grav/Common/GPM/Installer.php
+++ b/system/src/Grav/Common/GPM/Installer.php
@@ -281,6 +281,10 @@ class Installer
{
$msg = 'Unknown Error';
+ if (is_string(self::$error)) {
+ return self::$error;
+ }
+
switch (self::$error) {
case 0:
$msg = 'No Error';
@@ -333,7 +337,7 @@ class Installer
/**
* Allows to manually set an error
- * @param $error the Error code
+ * @param int|string $error the Error code
*/
public static function setError($error)
diff --git a/system/src/Grav/Common/GPM/Upgrader.php b/system/src/Grav/Common/GPM/Upgrader.php
index 590ad4df7..61c9ad5ce 100644
--- a/system/src/Grav/Common/GPM/Upgrader.php
+++ b/system/src/Grav/Common/GPM/Upgrader.php
@@ -1,22 +1,32 @@
remote->getChangelog($diff);
}
+ /**
+ * @return bool
+ */
+ public function meetsRequirements()
+ {
+ if (version_compare(PHP_VERSION, GRAV_PHP_MIN, '<')) {
+ return false;
+ }
+
+ return true;
+ }
+
/**
* Checks if the currently installed Grav is upgradable to a newer version
+ *
* @return boolean True if it's upgradable, False otherwise.
*/
public function isUpgradable()
@@ -83,6 +111,7 @@ class Upgrader
/**
* Checks if Grav is currently symbolically linked
+ *
* @return boolean True if Grav is symlinked, False otherwise.
*/
diff --git a/system/src/Grav/Common/Markdown/ParsedownGravTrait.php b/system/src/Grav/Common/Markdown/ParsedownGravTrait.php
index 5c075dcb5..a735a2ca9 100644
--- a/system/src/Grav/Common/Markdown/ParsedownGravTrait.php
+++ b/system/src/Grav/Common/Markdown/ParsedownGravTrait.php
@@ -3,6 +3,7 @@ namespace Grav\Common\Markdown;
use Grav\Common\GravTrait;
use Grav\Common\Uri;
+use RocketTheme\Toolbox\Event\Event;
/**
* A trait to add some custom processing to the identifyLink() method in Parsedown and ParsedownExtra
@@ -15,9 +16,11 @@ trait ParsedownGravTrait
protected $base_url;
protected $pages_dir;
protected $special_chars;
-
protected $twig_link_regex = '/\!*\[(?:.*)\]\((\{([\{%#])\s*(.*?)\s*(?:\2|\})\})\)/';
+ public $completeable_blocks = [];
+ public $continueable_blocks = [];
+
/**
* Initialization function to setup key variables needed by the MarkdownGravLinkTrait
*
@@ -26,8 +29,10 @@ trait ParsedownGravTrait
*/
protected function init($page, $defaults)
{
+ $grav = self::getGrav();
+
$this->page = $page;
- $this->pages = self::getGrav()['pages'];
+ $this->pages = $grav['pages'];
$this->BlockTypes['{'] [] = "TwigTag";
$this->base_url = rtrim(self::getGrav()['base_url'] . self::getGrav()['pages']->base(), '/');
$this->pages_dir = self::getGrav()['locator']->findResource('page://');
@@ -41,8 +46,67 @@ trait ParsedownGravTrait
$this->setUrlsLinked($defaults['auto_url_links']);
$this->setMarkupEscaped($defaults['escape_markup']);
$this->setSpecialChars($defaults['special_chars']);
+
+ $grav->fireEvent('onMarkdownInitialized', new Event(['markdown' => $this]));
+
}
+ /**
+ * Be able to define a new Block type or override an existing one
+ *
+ * @param $type
+ * @param $tag
+ */
+ public function addBlockType($type, $tag, $continueable = false, $completeable = false)
+ {
+ $this->BlockTypes[$type] []= $tag;
+
+ if ($continueable) {
+ $this->continueable_blocks[] = $tag;
+ }
+
+ if ($completeable) {
+ $this->completeable_blocks[] = $tag;
+ }
+ }
+
+ /**
+ * Be able to define a new Inline type or override an existing one
+ *
+ * @param $type
+ * @param $tag
+ */
+ public function addInlineType($type, $tag)
+ {
+ $this->InlineTypes[$type] []= $tag;
+ $this->inlineMarkerList .= $type;
+ }
+
+ /**
+ * Overrides the default behavior to allow for plugin-provided blocks to be continueable
+ *
+ * @param $Type
+ * @return bool
+ */
+ protected function isBlockContinueable($Type)
+ {
+ $continueable = in_array($Type, $this->continueable_blocks) || method_exists($this, 'block'.$Type.'Continue');
+ return $continueable;
+ }
+
+ /**
+ * Overrides the default behavior to allow for plugin-provided blocks to be completeable
+ *
+ * @param $Type
+ * @return bool
+ */
+ protected function isBlockCompleteable($Type)
+ {
+ $completeable = in_array($Type, $this->completeable_blocks) || method_exists($this, 'block'.$Type.'Complete');
+ return $completeable;
+ }
+
+
/**
* Make the element function publicly accessible, Medium uses this to render from Twig
*
@@ -73,7 +137,7 @@ trait ParsedownGravTrait
*/
protected function blockTwigTag($Line)
{
- if (preg_match('/[{%|{{|{#].*[#}|}}|%}]/', $Line['body'], $matches)) {
+ if (preg_match('/(?:{{|{%|{#)(.*)(?:}}|%}|#})/', $Line['body'], $matches)) {
$Block = array(
'markup' => $Line['body'],
);
@@ -230,7 +294,7 @@ trait ParsedownGravTrait
if ($attrib == 'classes') {
$attrib = 'class';
}
- $excerpt['element']['attributes'][$attrib] = $value;
+ $excerpt['element']['attributes'][$attrib] = str_replace(',', ' ', $value);
unset($actions[$key]);
}
}
@@ -255,4 +319,16 @@ trait ParsedownGravTrait
return $excerpt;
}
+
+ // For extending this class via plugins
+ public function __call($method, $args)
+ {
+ if (isset($this->$method) === true) {
+ $func = $this->$method;
+ return call_user_func_array($func, $args);
+ }
+ }
+
+
+
}
diff --git a/system/src/Grav/Common/Plugin.php b/system/src/Grav/Common/Plugin.php
index 3f2f90aa1..96084b0ed 100644
--- a/system/src/Grav/Common/Plugin.php
+++ b/system/src/Grav/Common/Plugin.php
@@ -6,6 +6,7 @@ use Grav\Common\Page\Page;
use Grav\Common\Config\Config;
use RocketTheme\Toolbox\Event\EventDispatcher;
use RocketTheme\Toolbox\Event\EventSubscriberInterface;
+use RocketTheme\Toolbox\File\YamlFile;
/**
* The Plugin object just holds the id and path to a plugin.
@@ -182,4 +183,26 @@ class Plugin implements EventSubscriberInterface
// Return configurations as a new data config class
return new Data($header);
}
+
+ /**
+ * Persists to disk the plugin parameters currently stored in the Grav Config object
+ *
+ * @param string $plugin_name The name of the plugin whose config it should store.
+ *
+ * @return true
+ */
+ public static function saveConfig($plugin_name) {
+ if (!$plugin_name) {
+ return false;
+ }
+
+ $locator = Grav::instance()['locator'];
+ $filename = 'config://plugins/' . $plugin_name . '.yaml';
+ $file = YamlFile::instance($locator->findResource($filename, true, true));
+ $content = Grav::instance()['config']->get('plugins.' . $plugin_name);
+ $file->save($content);
+ $file->free();
+
+ return true;
+ }
}
diff --git a/system/src/Grav/Common/Service/ConfigServiceProvider.php b/system/src/Grav/Common/Service/ConfigServiceProvider.php
index 9efbd7465..ebb156b1a 100644
--- a/system/src/Grav/Common/Service/ConfigServiceProvider.php
+++ b/system/src/Grav/Common/Service/ConfigServiceProvider.php
@@ -108,10 +108,45 @@ class ConfigServiceProvider implements ServiceProviderInterface
$files += (new ConfigFileFinder)->locateFiles($paths);
$paths = $locator->findResources('plugins://');
$files += (new ConfigFileFinder)->setBase('plugins')->locateInFolders($paths, 'languages');
+ $paths = static::pluginFolderPaths($paths, 'languages');
+ $files += (new ConfigFileFinder)->locateFiles($paths);
}
$languages = new CompiledLanguages($cache, $files, GRAV_ROOT);
return $languages->name("master-{$setup->environment}")->load();
}
+
+ /**
+ * Find specific paths in plugins
+ *
+ * @param $plugins
+ * @param $folder_path
+ * @return array
+ */
+ private static function pluginFolderPaths($plugins, $folder_path)
+ {
+ $paths = [];
+
+ foreach ($plugins as $path) {
+ $iterator = new \DirectoryIterator($path);
+
+ /** @var \DirectoryIterator $directory */
+ foreach ($iterator as $directory) {
+ if (!$directory->isDir() || $directory->isDot()) {
+ continue;
+ }
+
+ // Path to the languages folder
+ $lang_path = $directory->getPathName() . '/' . $folder_path;
+
+ // If this folder exists, add it to the list of paths
+ if (file_exists($lang_path)) {
+ $paths []= $lang_path;
+ }
+ }
+ }
+ return $paths;
+ }
+
}
diff --git a/system/src/Grav/Common/Twig/WriteCacheFileTrait.php b/system/src/Grav/Common/Twig/WriteCacheFileTrait.php
index ec0d93c2f..e20a2a43c 100644
--- a/system/src/Grav/Common/Twig/WriteCacheFileTrait.php
+++ b/system/src/Grav/Common/Twig/WriteCacheFileTrait.php
@@ -20,6 +20,10 @@ trait WriteCacheFileTrait
*/
protected function writeCacheFile($file, $content)
{
+ if (empty($file)) {
+ return;
+ }
+
if (!isset(self::$umask)) {
self::$umask = self::getGrav()['config']->get('system.twig.umask_fix', false);
}
diff --git a/system/src/Grav/Console/Gpm/SelfupgradeCommand.php b/system/src/Grav/Console/Gpm/SelfupgradeCommand.php
index d2ef2d2d6..542878380 100644
--- a/system/src/Grav/Console/Gpm/SelfupgradeCommand.php
+++ b/system/src/Grav/Console/Gpm/SelfupgradeCommand.php
@@ -82,6 +82,18 @@ class SelfupgradeCommand extends ConsoleCommand
$remote = $this->upgrader->getRemoteVersion();
$release = strftime('%c', strtotime($this->upgrader->getReleaseDate()));
+ if (!$this->upgrader->meetsRequirements()) {
+ $this->output->writeln("");
+ $this->output->writeln("ATTENTION:");
+ $this->output->writeln(" Grav has increased the minimum PHP requirement.");
+ $this->output->writeln(" You are currently running PHP " . PHP_VERSION . ", but PHP " . GRAV_PHP_MIN . " is required.");
+ $this->output->writeln(" Additional information: http://getgrav.org/blog/changing-php-requirements-to-5.5");
+ $this->output->writeln("");
+ $this->output->writeln("Selfupgrade aborted.");
+ $this->output->writeln("");
+ exit;
+ }
+
if (!$this->upgrader->isUpgradable()) {
$this->output->writeln("You are already running the latest version of Grav (v" . $local . ") released on " . $release);
exit;