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;