diff --git a/CHANGELOG.md b/CHANGELOG.md index 7dbfe81c5..77f69d397 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,20 @@ +# v1.1.2 +## 08/10/2016 + +1. [](#new) + * Allow forcing SSL by setting `system.force_ssl` (Force SSL in the Admin System Config) [#899](https://github.com/getgrav/grav/pull/899) +1. [](#improved) + * Improved `authorize` Twig extension to accept a nested array of authorizations [#948](https://github.com/getgrav/grav/issues/948) + * Don't add timestamps on remote assets as it can cause conflicts + * Grav now looks at types from `media.yaml` when retrieving page mime types [#966](https://github.com/getgrav/grav/issues/966) + * Added support for dumping exceptions in the Debugger +1. [](#bugfix) + * Fixed `Folder::delete` method to recursively remove files and folders and causing Upgrade to fail. + * Fix [#952](https://github.com/getgrav/grav/issues/952) hyphenize the session name. + * If no parent is set and siblings collection is called, return a new and empty collection [grav-plugin-sitemap/issues/22](https://github.com/getgrav/grav-plugin-sitemap/issues/22) + * Prevent exception being thrown when calling the Collator constructor failed in a Windows environment with the Intl PHP Extension enabled [#961](https://github.com/getgrav/grav/issues/961) + * Fix for markdown images not properly rendering `id` attribute [#956](https://github.com/getgrav/grav/issues/956) + # v1.1.1 ## 07/16/2016 @@ -817,7 +834,7 @@ * Improved query string handling * Optimization to image handling supporting URL encoded filenames * Use global `composer` when available rather than Grv provided one - * Use `PHP_BINARY` contant rather than `php` executable + * Use `PHP_BINARY` constant rather than `php` executable * Updated Doctrine Cache library * Updated Symfony libraries * Moved `convertUrl()` method to Uri object diff --git a/composer.lock b/composer.lock index 2ec248965..48a5d5ea0 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": "9c19c674a6ce22978364e1327bab1bbd", - "content-hash": "4b371a1eaef8d32d9ce9536c7c317625", + "hash": "65cd6cc2a20addb345acc1f84d5ae3ab", + "content-hash": "0ddb599b8e9fb7f0fb76619343180ef9", "packages": [ { "name": "doctrine/cache", @@ -533,16 +533,16 @@ }, { "name": "monolog/monolog", - "version": "1.19.0", + "version": "1.20.0", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "5f56ed5212dc509c8dc8caeba2715732abb32dbf" + "reference": "55841909e2bcde01b5318c35f2b74f8ecc86e037" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/5f56ed5212dc509c8dc8caeba2715732abb32dbf", - "reference": "5f56ed5212dc509c8dc8caeba2715732abb32dbf", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/55841909e2bcde01b5318c35f2b74f8ecc86e037", + "reference": "55841909e2bcde01b5318c35f2b74f8ecc86e037", "shasum": "" }, "require": { @@ -561,8 +561,8 @@ "php-console/php-console": "^3.1.3", "phpunit/phpunit": "~4.5", "phpunit/phpunit-mock-objects": "2.3.0", - "raven/raven": "^0.13", "ruflin/elastica": ">=0.90 <3.0", + "sentry/sentry": "^0.13", "swiftmailer/swiftmailer": "~5.3" }, "suggest": { @@ -574,9 +574,9 @@ "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", "php-console/php-console": "Allow sending log messages to Google Chrome", - "raven/raven": "Allow sending log messages to a Sentry server", "rollbar/rollbar": "Allow sending log messages to Rollbar", - "ruflin/elastica": "Allow sending log messages to an Elastic Search server" + "ruflin/elastica": "Allow sending log messages to an Elastic Search server", + "sentry/sentry": "Allow sending log messages to a Sentry server" }, "type": "library", "extra": { @@ -607,7 +607,7 @@ "logging", "psr-3" ], - "time": "2016-04-12 18:29:35" + "time": "2016-07-02 14:02:10" }, { "name": "pimple/pimple", @@ -743,16 +743,16 @@ }, { "name": "symfony/console", - "version": "v2.8.7", + "version": "v2.8.8", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "5ac8bc9aa77bb2edf06af3a1bb6bc1020d23acd3" + "reference": "c392a6ec72f2122748032c2ad6870420561ffcfa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/5ac8bc9aa77bb2edf06af3a1bb6bc1020d23acd3", - "reference": "5ac8bc9aa77bb2edf06af3a1bb6bc1020d23acd3", + "url": "https://api.github.com/repos/symfony/console/zipball/c392a6ec72f2122748032c2ad6870420561ffcfa", + "reference": "c392a6ec72f2122748032c2ad6870420561ffcfa", "shasum": "" }, "require": { @@ -799,20 +799,20 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2016-06-06 15:06:25" + "time": "2016-06-29 07:02:14" }, { "name": "symfony/event-dispatcher", - "version": "v2.8.7", + "version": "v2.8.8", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "2a6b8713f8bdb582058cfda463527f195b066110" + "reference": "b180b70439dca70049b6b9b7e21d75e6e5d7aca9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/2a6b8713f8bdb582058cfda463527f195b066110", - "reference": "2a6b8713f8bdb582058cfda463527f195b066110", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b180b70439dca70049b6b9b7e21d75e6e5d7aca9", + "reference": "b180b70439dca70049b6b9b7e21d75e6e5d7aca9", "shasum": "" }, "require": { @@ -859,7 +859,7 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2016-06-06 11:11:27" + "time": "2016-06-29 05:29:29" }, { "name": "symfony/polyfill-iconv", @@ -981,16 +981,16 @@ }, { "name": "symfony/var-dumper", - "version": "v2.8.7", + "version": "v2.8.8", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "0c5970fc6cb9222b78559f20858f76db1e5ac326" + "reference": "eb21798f1cd2d3a1c1e1e4d0632b6d04a798f210" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/0c5970fc6cb9222b78559f20858f76db1e5ac326", - "reference": "0c5970fc6cb9222b78559f20858f76db1e5ac326", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/eb21798f1cd2d3a1c1e1e4d0632b6d04a798f210", + "reference": "eb21798f1cd2d3a1c1e1e4d0632b6d04a798f210", "shasum": "" }, "require": { @@ -1040,20 +1040,20 @@ "debug", "dump" ], - "time": "2016-05-24 10:00:02" + "time": "2016-06-29 05:29:29" }, { "name": "symfony/yaml", - "version": "v2.8.7", + "version": "v2.8.8", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "815fabf3f48c7d1df345a69d1ad1a88f59757b34" + "reference": "dba4bb5846798cd12f32e2d8f3f35d77045773c8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/815fabf3f48c7d1df345a69d1ad1a88f59757b34", - "reference": "815fabf3f48c7d1df345a69d1ad1a88f59757b34", + "url": "https://api.github.com/repos/symfony/yaml/zipball/dba4bb5846798cd12f32e2d8f3f35d77045773c8", + "reference": "dba4bb5846798cd12f32e2d8f3f35d77045773c8", "shasum": "" }, "require": { @@ -1089,7 +1089,7 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2016-06-06 11:11:27" + "time": "2016-06-29 05:29:29" }, { "name": "twig/twig", @@ -1214,16 +1214,16 @@ }, { "name": "codeception/codeception", - "version": "2.2.1", + "version": "2.2.2", "source": { "type": "git", "url": "https://github.com/Codeception/Codeception.git", - "reference": "f137636f0c185e7d4a495b9cea70c1afae27dfc7" + "reference": "8d80bb4ec7470e8df5de0e4c401785bc3fa1f4f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Codeception/Codeception/zipball/f137636f0c185e7d4a495b9cea70c1afae27dfc7", - "reference": "f137636f0c185e7d4a495b9cea70c1afae27dfc7", + "url": "https://api.github.com/repos/Codeception/Codeception/zipball/8d80bb4ec7470e8df5de0e4c401785bc3fa1f4f6", + "reference": "8d80bb4ec7470e8df5de0e4c401785bc3fa1f4f6", "shasum": "" }, "require": { @@ -1236,6 +1236,8 @@ "php": ">=5.4.0 <8.0", "phpunit/php-code-coverage": ">=2.1.3", "phpunit/phpunit": ">4.8.20 <5.5", + "sebastian/comparator": "~1.1", + "sebastian/diff": "^1.4", "symfony/browser-kit": ">=2.7 <4.0", "symfony/console": ">=2.7 <4.0", "symfony/css-selector": ">=2.7 <4.0", @@ -1299,7 +1301,7 @@ "functional testing", "unit testing" ], - "time": "2016-06-03 12:44:06" + "time": "2016-06-29 00:59:28" }, { "name": "doctrine/instantiator", @@ -1357,19 +1359,20 @@ }, { "name": "facebook/webdriver", - "version": "1.1.1", + "version": "1.1.2", "source": { "type": "git", "url": "https://github.com/facebook/php-webdriver.git", - "reference": "1c98108ba3eb435b681655764de11502a0653705" + "reference": "0b889d7de7461439f8a3bbcca46e0f696cb27986" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/facebook/php-webdriver/zipball/1c98108ba3eb435b681655764de11502a0653705", - "reference": "1c98108ba3eb435b681655764de11502a0653705", + "url": "https://api.github.com/repos/facebook/php-webdriver/zipball/0b889d7de7461439f8a3bbcca46e0f696cb27986", + "reference": "0b889d7de7461439f8a3bbcca46e0f696cb27986", "shasum": "" }, "require": { + "ext-curl": "*", "php": ">=5.3.19" }, "require-dev": { @@ -1396,7 +1399,7 @@ "selenium", "webdriver" ], - "time": "2015-12-31 15:58:49" + "time": "2016-06-04 00:02:34" }, { "name": "fzaninotto/faker", @@ -1448,27 +1451,27 @@ }, { "name": "guzzlehttp/guzzle", - "version": "6.2.0", + "version": "6.2.1", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "d094e337976dff9d8e2424e8485872194e768662" + "reference": "3f808fba627f2c5b69e2501217bf31af349c1427" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/d094e337976dff9d8e2424e8485872194e768662", - "reference": "d094e337976dff9d8e2424e8485872194e768662", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/3f808fba627f2c5b69e2501217bf31af349c1427", + "reference": "3f808fba627f2c5b69e2501217bf31af349c1427", "shasum": "" }, "require": { - "guzzlehttp/promises": "~1.0", - "guzzlehttp/psr7": "~1.1", - "php": ">=5.5.0" + "guzzlehttp/promises": "^1.0", + "guzzlehttp/psr7": "^1.3.1", + "php": ">=5.5" }, "require-dev": { "ext-curl": "*", - "phpunit/phpunit": "~4.0", - "psr/log": "~1.0" + "phpunit/phpunit": "^4.0", + "psr/log": "^1.0" }, "type": "library", "extra": { @@ -1506,7 +1509,7 @@ "rest", "web service" ], - "time": "2016-03-21 20:02:09" + "time": "2016-07-15 17:22:37" }, { "name": "guzzlehttp/promises", @@ -1561,16 +1564,16 @@ }, { "name": "guzzlehttp/psr7", - "version": "1.3.0", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "31382fef2889136415751badebbd1cb022a4ed72" + "reference": "5c6447c9df362e8f8093bda8f5d8873fe5c7f65b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/31382fef2889136415751badebbd1cb022a4ed72", - "reference": "31382fef2889136415751badebbd1cb022a4ed72", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/5c6447c9df362e8f8093bda8f5d8873fe5c7f65b", + "reference": "5c6447c9df362e8f8093bda8f5d8873fe5c7f65b", "shasum": "" }, "require": { @@ -1586,7 +1589,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "1.4-dev" } }, "autoload": { @@ -1615,7 +1618,7 @@ "stream", "uri" ], - "time": "2016-04-13 19:56:01" + "time": "2016-06-24 23:00:38" }, { "name": "phpdocumentor/reflection-common", @@ -2413,16 +2416,16 @@ }, { "name": "sebastian/exporter", - "version": "1.2.1", + "version": "1.2.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "7ae5513327cb536431847bcc0c10edba2701064e" + "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/7ae5513327cb536431847bcc0c10edba2701064e", - "reference": "7ae5513327cb536431847bcc0c10edba2701064e", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/42c4c2eec485ee3e159ec9884f95b431287edde4", + "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4", "shasum": "" }, "require": { @@ -2430,12 +2433,13 @@ "sebastian/recursion-context": "~1.0" }, "require-dev": { + "ext-mbstring": "*", "phpunit/phpunit": "~4.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2.x-dev" + "dev-master": "1.3.x-dev" } }, "autoload": { @@ -2475,7 +2479,7 @@ "export", "exporter" ], - "time": "2015-06-21 07:55:53" + "time": "2016-06-17 09:04:28" }, { "name": "sebastian/global-state", @@ -2618,16 +2622,16 @@ }, { "name": "symfony/browser-kit", - "version": "v3.1.0", + "version": "v3.1.2", "source": { "type": "git", "url": "https://github.com/symfony/browser-kit.git", - "reference": "b645a9b23d6c0eeba5ac823fa87bf010db9aff22" + "reference": "dcf41ed026b0499254385b5c88f03247b2ba010b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/browser-kit/zipball/b645a9b23d6c0eeba5ac823fa87bf010db9aff22", - "reference": "b645a9b23d6c0eeba5ac823fa87bf010db9aff22", + "url": "https://api.github.com/repos/symfony/browser-kit/zipball/dcf41ed026b0499254385b5c88f03247b2ba010b", + "reference": "dcf41ed026b0499254385b5c88f03247b2ba010b", "shasum": "" }, "require": { @@ -2671,20 +2675,20 @@ ], "description": "Symfony BrowserKit Component", "homepage": "https://symfony.com", - "time": "2016-03-04 07:56:56" + "time": "2016-06-29 05:41:56" }, { "name": "symfony/css-selector", - "version": "v3.1.0", + "version": "v3.1.2", "source": { "type": "git", "url": "https://github.com/symfony/css-selector.git", - "reference": "e17f386efef7258ac671c24e727673abd086b0cf" + "reference": "2851e1932d77ce727776154d659b232d061e816a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/css-selector/zipball/e17f386efef7258ac671c24e727673abd086b0cf", - "reference": "e17f386efef7258ac671c24e727673abd086b0cf", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/2851e1932d77ce727776154d659b232d061e816a", + "reference": "2851e1932d77ce727776154d659b232d061e816a", "shasum": "" }, "require": { @@ -2724,20 +2728,20 @@ ], "description": "Symfony CssSelector Component", "homepage": "https://symfony.com", - "time": "2016-03-04 07:56:56" + "time": "2016-06-29 05:41:56" }, { "name": "symfony/dom-crawler", - "version": "v3.1.0", + "version": "v3.1.2", "source": { "type": "git", "url": "https://github.com/symfony/dom-crawler.git", - "reference": "12aa63fd41b060d2bee9a34623d29eda70bc8fe3" + "reference": "99ec4a23330fcd0c8667095f3ef7aa204ffd9dc0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/12aa63fd41b060d2bee9a34623d29eda70bc8fe3", - "reference": "12aa63fd41b060d2bee9a34623d29eda70bc8fe3", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/99ec4a23330fcd0c8667095f3ef7aa204ffd9dc0", + "reference": "99ec4a23330fcd0c8667095f3ef7aa204ffd9dc0", "shasum": "" }, "require": { @@ -2780,20 +2784,20 @@ ], "description": "Symfony DomCrawler Component", "homepage": "https://symfony.com", - "time": "2016-05-13 15:49:09" + "time": "2016-06-29 05:41:56" }, { "name": "symfony/finder", - "version": "v3.1.0", + "version": "v3.1.2", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "40d17ed287bf51a2f884c4619ce8ff2a1c5cd219" + "reference": "8201978de88a9fa0923e18601bb17f1df9c721e7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/40d17ed287bf51a2f884c4619ce8ff2a1c5cd219", - "reference": "40d17ed287bf51a2f884c4619ce8ff2a1c5cd219", + "url": "https://api.github.com/repos/symfony/finder/zipball/8201978de88a9fa0923e18601bb17f1df9c721e7", + "reference": "8201978de88a9fa0923e18601bb17f1df9c721e7", "shasum": "" }, "require": { @@ -2829,7 +2833,7 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2016-05-13 18:06:41" + "time": "2016-06-29 05:41:56" }, { "name": "webmozart/assert", @@ -2900,7 +2904,8 @@ "php": ">=5.5.9", "ext-mbstring": "*", "ext-openssl": "*", - "ext-curl": "*" + "ext-curl": "*", + "ext-zip": "*" }, "platform-dev": [] } diff --git a/system/blueprints/config/system.yaml b/system/blueprints/config/system.yaml index bfb854405..780aaeb88 100644 --- a/system/blueprints/config/system.yaml +++ b/system/blueprints/config/system.yaml @@ -513,7 +513,7 @@ form: label: PLUGIN_ADMIN.MEMCACHED_PORT help: PLUGIN_ADMIN.MEMCACHED_PORT_HELP placeholder: "11211" - + cache.redis.server: type: text size: medium @@ -1018,3 +1018,14 @@ form: options: ':': ': (default)' ';': '; (for Apache running on Windows)' + + force_ssl: + type: toggle + label: PLUGIN_ADMIN.FORCE_SSL + highlight: 0 + help: PLUGIN_ADMIN.FORCE_SSL_HELP + options: + 1: PLUGIN_ADMIN.YES + 0: PLUGIN_ADMIN.NO + validate: + type: bool diff --git a/system/config/site.yaml b/system/config/site.yaml index 840d7227e..9bf28526a 100644 --- a/system/config/site.yaml +++ b/system/config/site.yaml @@ -16,12 +16,12 @@ summary: delimiter: === # The summary delimiter redirects: -# /redirect-test: / # Redirect test goes to home page -# /old/(.*): /new/$1 # Would redirect /old/my-page to /new/my-page +# '/redirect-test': '/' # Redirect test goes to home page +# '/old/(.*)': '/new/$1' # Would redirect /old/my-page to /new/my-page routes: -# /something/else: '/blog/sample-3' # Alias for /blog/sample-3 -# /new/(.*): '/blog/$1' # Regex any /new/my-page URL to /blog/my-page Route +# '/something/else': '/blog/sample-3' # Alias for /blog/sample-3 +# '/new/(.*)': '/blog/$1' # Regex any /new/my-page URL to /blog/my-page Route blog: route: '/blog' # Custom value added (accessible via system.blog.route) diff --git a/system/config/system.yaml b/system/config/system.yaml index c2c88bdbe..3d35c0740 100644 --- a/system/config/system.yaml +++ b/system/config/system.yaml @@ -4,7 +4,7 @@ default_locale: # Default locale (defaults to system param_sep: ':' # Parameter separator, use ';' for Apache on windows 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 languages: supported: [] # List of languages supported. eg: [en, fr, de] diff --git a/system/defines.php b/system/defines.php index 2cebe6db9..72f934b16 100644 --- a/system/defines.php +++ b/system/defines.php @@ -8,7 +8,7 @@ // Some standard defines define('GRAV', true); -define('GRAV_VERSION', '1.1.1'); +define('GRAV_VERSION', '1.1.2'); define('GRAV_TESTING', false); define('DS', '/'); define('GRAV_PHP_MIN', '5.5.9'); diff --git a/system/src/Grav/Common/Assets.php b/system/src/Grav/Common/Assets.php index b87cd1cc3..c324c4df7 100644 --- a/system/src/Grav/Common/Assets.php +++ b/system/src/Grav/Common/Assets.php @@ -281,8 +281,9 @@ class Assets return $this; } - $modified = 0; - if (!$this->isRemoteLink($asset)) { + $modified = false; + $remote = $this->isRemoteLink($asset); + if (!$remote) { $modified = $this->getLastModificationTime($asset); $asset = $this->buildLocalLink($asset); } @@ -294,6 +295,7 @@ class Assets $data = [ 'asset' => $asset, + 'remote' => $remote, 'priority' => intval($priority ?: 10), 'order' => count($this->css), 'pipeline' => (bool) $pipeline, @@ -345,8 +347,9 @@ class Assets return $this; } - $modified = 0; - if (!$this->isRemoteLink($asset)) { + $modified = false; + $remote = $this->isRemoteLink($asset); + if (!$remote) { $modified = $this->getLastModificationTime($asset); $asset = $this->buildLocalLink($asset); } @@ -358,6 +361,7 @@ class Assets $data = [ 'asset' => $asset, + 'remote' => $remote , 'priority' => intval($priority ?: 10), 'order' => count($this->js), 'pipeline' => (bool) $pipeline, @@ -558,7 +562,7 @@ class Assets foreach ($this->css_no_pipeline as $file) { if ($group && $file['group'] == $group) { $media = isset($file['media']) ? sprintf(' media="%s"', $file['media']) : ''; - $output .= '' . "\n"; + $output .= '' . "\n"; } } if (!$this->css_pipeline_before_excludes && $pipeline_result) { @@ -568,7 +572,7 @@ class Assets foreach ($this->css as $file) { if ($group && $file['group'] == $group) { $media = isset($file['media']) ? sprintf(' media="%s"', $file['media']) : ''; - $output .= '' . "\n"; + $output .= '' . "\n"; } } } @@ -636,7 +640,7 @@ class Assets } foreach ($this->js_no_pipeline as $file) { if ($group && $file['group'] == $group) { - $output .= '' . "\n"; + $output .= '' . "\n"; } } if (!$this->js_pipeline_before_excludes && $pipeline_result) { @@ -645,7 +649,7 @@ class Assets } else { foreach ($this->js as $file) { if ($group && $file['group'] == $group) { - $output .= '' . "\n"; + $output .= '' . "\n"; } } } @@ -693,14 +697,14 @@ class Assets // If pipeline exist return it if (file_exists($this->assets_dir . $file)) { - return $relative_path . $this->timestamp; + return $relative_path . $this->getTimestamp(); } // Remove any non-pipeline files foreach ($this->css as $id => $asset) { if ($asset['group'] == $group) { if (!$asset['pipeline'] || - ($this->isRemoteLink($asset['asset']) && $this->css_pipeline_include_externals === false)) { + ($asset['remote'] && $this->css_pipeline_include_externals === false)) { $this->css_no_pipeline[$id] = $asset; } else { $temp_css[$id] = $asset; @@ -740,7 +744,7 @@ class Assets if (strlen(trim($buffer)) > 0) { file_put_contents($this->assets_dir . $file, $buffer); - return $relative_path . $this->timestamp; + return $relative_path . $this->getTimestamp(); } else { return false; } @@ -775,14 +779,14 @@ class Assets // If pipeline exist return it if (file_exists($this->assets_dir . $file)) { - return $relative_path . $this->timestamp; + return $relative_path . $this->getTimestamp(); } // Remove any non-pipeline files foreach ($this->js as $id => $asset) { if ($asset['group'] == $group) { if (!$asset['pipeline'] || - ($this->isRemoteLink($asset['asset']) && $this->js_pipeline_include_externals === false)) { + ($asset['remote'] && $this->js_pipeline_include_externals === false)) { $this->js_no_pipeline[] = $asset; } else { $temp_js[$id] = $asset; @@ -812,7 +816,7 @@ class Assets if (strlen(trim($buffer)) > 0) { file_put_contents($this->assets_dir . $file, $buffer); - return $relative_path . $this->timestamp; + return $relative_path . $this->getTimestamp(); } else { return false; } @@ -1342,6 +1346,21 @@ class Assets $this->timestamp = '?' . $value; } + public function getTimestamp($asset = null) + { + if (is_array($asset)) { + if ($asset['remote'] == false) { + if (Utils::contains($asset['asset'], '?')) { + return str_replace('?', '&', $this->timestamp); + } else { + return $this->timestamp; + } + } + } elseif (empty($asset)) { + return $this->timestamp; + } + } + /** * @return string */ diff --git a/system/src/Grav/Common/Debugger.php b/system/src/Grav/Common/Debugger.php index f1dce1480..3784b8724 100644 --- a/system/src/Grav/Common/Debugger.php +++ b/system/src/Grav/Common/Debugger.php @@ -217,4 +217,19 @@ class Debugger return $this; } + + /** + * Dump exception into the Messages tab of the Debug Bar + * + * @param \Exception $e + * @return Debugger + */ + public function addException(\Exception $e) + { + if ($this->enabled()) { + $this->debugbar['exceptions']->addException($e); + } + + return $this; + } } diff --git a/system/src/Grav/Common/Filesystem/Folder.php b/system/src/Grav/Common/Filesystem/Folder.php index 14da7b576..b43fada7c 100644 --- a/system/src/Grav/Common/Filesystem/Folder.php +++ b/system/src/Grav/Common/Filesystem/Folder.php @@ -436,22 +436,11 @@ abstract class Folder return @unlink($folder); } - $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; - } - } + // 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); } return $include_target ? @rmdir($folder) : true; diff --git a/system/src/Grav/Common/Grav.php b/system/src/Grav/Common/Grav.php index 0ec82f8d0..05041f9bd 100644 --- a/system/src/Grav/Common/Grav.php +++ b/system/src/Grav/Common/Grav.php @@ -217,7 +217,10 @@ class Grav extends Container */ public function mime($format) { + // look for some standard types switch ($format) { + case null: + return 'text/html'; case 'json': return 'application/json'; case 'html': @@ -230,6 +233,16 @@ class Grav extends Container return 'application/xml'; } + // Try finding mime type from media + $media_types = $this['config']->get('media.types'); + if (key_exists($format, $media_types)) { + $type = $media_types[$format]; + if (isset($type['mime'])) { + return $type['mime']; + } + } + + // Can't find the mime type, send as HTML return 'text/html'; } diff --git a/system/src/Grav/Common/Markdown/ParsedownGravTrait.php b/system/src/Grav/Common/Markdown/ParsedownGravTrait.php index d0eae55e2..9b491c22c 100644 --- a/system/src/Grav/Common/Markdown/ParsedownGravTrait.php +++ b/system/src/Grav/Common/Markdown/ParsedownGravTrait.php @@ -212,6 +212,7 @@ trait ParsedownGravTrait $alt = $excerpt['element']['attributes']['alt'] ?: ''; $title = $excerpt['element']['attributes']['title'] ?: ''; $class = isset($excerpt['element']['attributes']['class']) ? $excerpt['element']['attributes']['class'] : ''; + $id = isset($excerpt['element']['attributes']['id']) ? $excerpt['element']['attributes']['id'] : ''; //get the url and parse it $url = parse_url(htmlspecialchars_decode($excerpt['element']['attributes']['src'])); @@ -264,7 +265,7 @@ trait ParsedownGravTrait $medium->urlHash($url['fragment']); } - $excerpt['element'] = $medium->parseDownElement($title, $alt, $class, true); + $excerpt['element'] = $medium->parseDownElement($title, $alt, $class, $id, true); } else { // not a current page media file, see if it needs converting to relative diff --git a/system/src/Grav/Common/Page/Medium/Link.php b/system/src/Grav/Common/Page/Medium/Link.php index 1b7663567..dc01f1401 100644 --- a/system/src/Grav/Common/Page/Medium/Link.php +++ b/system/src/Grav/Common/Page/Medium/Link.php @@ -36,12 +36,13 @@ class Link implements RenderableInterface * @param string $title * @param string $alt * @param string $class + * @param string $id * @param boolean $reset * @return array */ - public function parsedownElement($title = null, $alt = null, $class = null, $reset = true) + public function parsedownElement($title = null, $alt = null, $class = null, $id = null, $reset = true) { - $innerElement = $this->source->parsedownElement($title, $alt, $class, $reset); + $innerElement = $this->source->parsedownElement($title, $alt, $class, $id, $reset); return [ 'name' => 'a', diff --git a/system/src/Grav/Common/Page/Medium/Medium.php b/system/src/Grav/Common/Page/Medium/Medium.php index 029583832..20f0d297c 100644 --- a/system/src/Grav/Common/Page/Medium/Medium.php +++ b/system/src/Grav/Common/Page/Medium/Medium.php @@ -199,10 +199,11 @@ class Medium extends Data implements RenderableInterface * @param string $title * @param string $alt * @param string $class + * @param string $id * @param boolean $reset * @return array */ - public function parsedownElement($title = null, $alt = null, $class = null, $reset = true) + public function parsedownElement($title = null, $alt = null, $class = null, $id = null, $reset = true) { $attributes = $this->attributes; @@ -241,6 +242,14 @@ class Medium extends Data implements RenderableInterface } } + if (empty($attributes['id'])) { + if (!empty($id)) { + $attributes['id'] = $id; + } elseif (!empty($this->items['id'])) { + $attributes['id'] = $this->items['id']; + } + } + switch ($this->mode) { case 'text': $element = $this->textParsedownElement($attributes, false); @@ -417,6 +426,7 @@ class Medium extends Data implements RenderableInterface */ public function id($id) { + xdebug_break(); if (is_string($id)) { $this->attributes['id'] = trim($id); } diff --git a/system/src/Grav/Common/Page/Medium/ParsedownHtmlTrait.php b/system/src/Grav/Common/Page/Medium/ParsedownHtmlTrait.php index 1337daa26..b51b427f2 100644 --- a/system/src/Grav/Common/Page/Medium/ParsedownHtmlTrait.php +++ b/system/src/Grav/Common/Page/Medium/ParsedownHtmlTrait.php @@ -21,13 +21,15 @@ trait ParsedownHtmlTrait * Return HTML markup from the medium. * * @param string $title + * @param string $alt * @param string $class + * @param string $id * @param bool $reset * @return string */ - public function html($title = null, $alt = null, $class = null, $reset = true) + public function html($title = null, $alt = null, $class = null, $id = null, $reset = true) { - $element = $this->parsedownElement($title, $alt, $class, $reset); + $element = $this->parsedownElement($title, $alt, $class, $id, $reset); if (!$this->parsedown) { $this->parsedown = new Parsedown(null, null); diff --git a/system/src/Grav/Common/Page/Medium/RenderableInterface.php b/system/src/Grav/Common/Page/Medium/RenderableInterface.php index 522abe13a..b9ea37e77 100644 --- a/system/src/Grav/Common/Page/Medium/RenderableInterface.php +++ b/system/src/Grav/Common/Page/Medium/RenderableInterface.php @@ -27,8 +27,9 @@ interface RenderableInterface * @param string $title * @param string $alt * @param string $class + * @param string $id * @param bool $reset * @return string */ - public function parsedownElement($title = null, $alt = null, $class = null, $reset = true); + public function parsedownElement($title = null, $alt = null, $class = null, $id = null, $reset = true); } diff --git a/system/src/Grav/Common/Page/Medium/ThumbnailImageMedium.php b/system/src/Grav/Common/Page/Medium/ThumbnailImageMedium.php index 3296fc757..8115cbe7f 100644 --- a/system/src/Grav/Common/Page/Medium/ThumbnailImageMedium.php +++ b/system/src/Grav/Common/Page/Medium/ThumbnailImageMedium.php @@ -34,15 +34,16 @@ class ThumbnailImageMedium extends ImageMedium /** * Get an element (is array) that can be rendered by the Parsedown engine * - * @param string $title - * @param string $alt - * @param string $class + * @param string $title + * @param string $alt + * @param string $class + * @param string $id * @param boolean $reset * @return array */ - public function parsedownElement($title = null, $alt = null, $class = null, $reset = true) + public function parsedownElement($title = null, $alt = null, $class = null, $id = null, $reset = true) { - return $this->bubble('parsedownElement', [$title, $alt, $class, $reset]); + return $this->bubble('parsedownElement', [$title, $alt, $class, $id, $reset]); } /** diff --git a/system/src/Grav/Common/Page/Page.php b/system/src/Grav/Common/Page/Page.php index b3679bbdb..abb421078 100644 --- a/system/src/Grav/Common/Page/Page.php +++ b/system/src/Grav/Common/Page/Page.php @@ -2337,6 +2337,9 @@ class Page $results = $collection->addPage($this->parent()); break; case 'siblings': + if (!$this->parent()) { + return new Collection(); + } $results = $this->parent()->children()->remove($this->path()); break; case 'descendants': diff --git a/system/src/Grav/Common/Page/Pages.php b/system/src/Grav/Common/Page/Pages.php index 87a81d60c..26a338e6a 100644 --- a/system/src/Grav/Common/Page/Pages.php +++ b/system/src/Grav/Common/Page/Pages.php @@ -1065,8 +1065,13 @@ class Pages } else { // else just sort the list according to specified key if (extension_loaded('intl')) { - $col = new \Collator(setlocale(LC_COLLATE, 0)); //`setlocale` with a 0 param returns the current locale set - $col->asort($list, $sort_flags); + $locale = setlocale(LC_COLLATE, 0); //`setlocale` with a 0 param returns the current locale set + $col = \Collator::create($locale); + if ($col) { + $col->asort($list, $sort_flags); + } else { + asort($list, $sort_flags); + } } else { asort($list, $sort_flags); } diff --git a/system/src/Grav/Common/Service/PageServiceProvider.php b/system/src/Grav/Common/Service/PageServiceProvider.php index 5df40c7e6..5042afe9b 100644 --- a/system/src/Grav/Common/Service/PageServiceProvider.php +++ b/system/src/Grav/Common/Service/PageServiceProvider.php @@ -33,6 +33,12 @@ class PageServiceProvider implements ServiceProviderInterface // Redirection tests if ($page) { + if ($c['config']->get('system.force_ssl')) { + if (!isset($_SERVER['HTTPS']) || $_SERVER["HTTPS"] != "on") { + $url = "https://" . $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"]; + $c->redirect($url); + } + } $url = $page->route(); diff --git a/system/src/Grav/Common/Session.php b/system/src/Grav/Common/Session.php index 5a3987481..b7594842d 100644 --- a/system/src/Grav/Common/Session.php +++ b/system/src/Grav/Common/Session.php @@ -67,7 +67,8 @@ class Session extends BaseSession $httponly = $config->get('system.session.httponly', true); $unique_identifier = GRAV_ROOT; - $this->setName($config->get('system.session.name', 'grav_site') . '-' . substr(md5($unique_identifier), 0, 7) . ($is_admin ? '-admin' : '')); + $inflector = new Inflector(); + $this->setName($inflector->hyphenize($config->get('system.session.name', 'grav_site')) . '-' . substr(md5($unique_identifier), 0, 7) . ($is_admin ? '-admin' : '')); $this->start(); setcookie(session_name(), session_id(), time() + $session_timeout, $session_path, $domain, $secure, $httponly); } diff --git a/system/src/Grav/Common/Twig/TwigExtension.php b/system/src/Grav/Common/Twig/TwigExtension.php index 6dc257b80..311162dd0 100644 --- a/system/src/Grav/Common/Twig/TwigExtension.php +++ b/system/src/Grav/Common/Twig/TwigExtension.php @@ -729,11 +729,15 @@ class TwigExtension extends \Twig_Extension } /** - * Authorize an action. Returns true if the user is logged in and has the right to execute $action. + * Authorize an action. Returns true if the user is logged in and + * has the right to execute $action. * - * @param string $action - * - * @return bool + * @param string|array $action An action or a list of actions. Each + * entry can be a string like 'group.action' + * or without dot notation an associative + * array. + * @return bool Returns TRUE if the user is authorized to + * perform the action, FALSE otherwise. */ public function authorize($action) { @@ -741,11 +745,14 @@ class TwigExtension extends \Twig_Extension return false; } - $action = (array)$action; - - foreach ($action as $a) { - if ($this->grav['user']->authorize($a)) { - return true; + $action = (array) $action; + foreach ($action as $key => $perms) { + $prefix = is_int($key) ? '' : $key . '.'; + $perms = $prefix ? (array) $perms : [$perms => true]; + foreach ($perms as $action => $authenticated) { + if ($this->grav['user']->authorize($prefix . $action)) { + return $authenticated; + } } } diff --git a/tests/unit/Grav/Common/AssetsTest.php b/tests/unit/Grav/Common/AssetsTest.php index b4ba6dd0d..e02593cf9 100644 --- a/tests/unit/Grav/Common/AssetsTest.php +++ b/tests/unit/Grav/Common/AssetsTest.php @@ -37,6 +37,7 @@ class AssetsTest extends \Codeception\TestCase\Test $array = $this->assets->getCss(); $this->assertSame([ 'asset' => '/test.css', + 'remote' => false, 'priority' => 10, 'order' => 0, 'pipeline' => true, @@ -51,6 +52,7 @@ class AssetsTest extends \Codeception\TestCase\Test $array = $this->assets->getCss(); $this->assertSame([ 'asset' => '/test.css', + 'remote' => false, 'priority' => 10, 'order' => 0, 'pipeline' => true, @@ -67,6 +69,24 @@ class AssetsTest extends \Codeception\TestCase\Test $array = $this->assets->getCss(); $this->assertSame([ 'asset' => '/test.css', + 'remote' => false, + 'priority' => 10, + 'order' => 0, + 'pipeline' => true, + 'group' => 'head', + 'modified' => false + ], reset($array)); + + //test addCss(). Testing with remote URL + $this->assets->reset(); + $this->assets->addCSS('http://www.somesite.com/test.css'); + $css = $this->assets->css(); + $this->assertSame('' . PHP_EOL, $css); + + $array = $this->assets->getCss(); + $this->assertSame([ + 'asset' => 'http://www.somesite.com/test.css', + 'remote' => true, 'priority' => 10, 'order' => 0, 'pipeline' => true, @@ -89,6 +109,7 @@ class AssetsTest extends \Codeception\TestCase\Test $array = $this->assets->getJs(); $this->assertSame([ 'asset' => '/test.js', + 'remote' => false, 'priority' => 10, 'order' => 0, 'pipeline' => true, @@ -108,6 +129,7 @@ class AssetsTest extends \Codeception\TestCase\Test $array = $this->assets->getCss(); $this->assertSame([ 'asset' => '/test.css', + 'remote' => false, 'priority' => 10, 'order' => 0, 'pipeline' => true, @@ -126,6 +148,7 @@ class AssetsTest extends \Codeception\TestCase\Test $array = $this->assets->getJs(); $this->assertSame([ 'asset' => '/test.js', + 'remote' => false, 'priority' => 10, 'order' => 0, 'pipeline' => true, @@ -143,6 +166,7 @@ class AssetsTest extends \Codeception\TestCase\Test $array = $this->assets->getJs(); $this->assertSame([ 'asset' => '/test.js', + 'remote' => false, 'priority' => 10, 'order' => 0, 'pipeline' => true, @@ -159,6 +183,7 @@ class AssetsTest extends \Codeception\TestCase\Test $array = $this->assets->getJs(); $this->assertSame([ 'asset' => '/test.js', + 'remote' => false, 'priority' => 10, 'order' => 0, 'pipeline' => true, @@ -298,6 +323,19 @@ class AssetsTest extends \Codeception\TestCase\Test $this->assertContains('type="text/css" rel="stylesheet" />', $css); } + public function testPipelineWithTimestamp() + { + $this->assets->reset(); + $this->assets->setTimestamp('foo'); + + //Add a core Grav CSS file, which is found. Pipeline will now return a file + $this->assets->add('/system/assets/debugger.css', null, true); + $css = $this->assets->css(); + $this->assertContains('', $css); + $this->assertContains($this->assets->getTimestamp(), $css); + } + public function testAddAsyncJs() { $this->assets->reset(); @@ -314,6 +352,65 @@ class AssetsTest extends \Codeception\TestCase\Test $this->assertSame('' . PHP_EOL, $js); } + public function testTimestamps() + { + // local CSS nothing extra + $this->assets->reset(); + $this->assets->setTimestamp('foo'); + $this->assets->addCSS('test.css'); + $css = $this->assets->css(); + $this->assertSame('' . PHP_EOL, $css); + + // local CSS already with param + $this->assets->reset(); + $this->assets->setTimestamp('foo'); + $this->assets->addCSS('test.css?bar'); + $css = $this->assets->css(); + $this->assertSame('' . PHP_EOL, $css); + + // external CSS already + $this->assets->reset(); + $this->assets->setTimestamp('foo'); + $this->assets->addCSS('http://somesite.com/test.css'); + $css = $this->assets->css(); + $this->assertSame('' . PHP_EOL, $css); + + // external CSS already with param + $this->assets->reset(); + $this->assets->setTimestamp('foo'); + $this->assets->addCSS('http://somesite.com/test.css?bar'); + $css = $this->assets->css(); + $this->assertSame('' . PHP_EOL, $css); + + // local JS nothing extra + $this->assets->reset(); + $this->assets->setTimestamp('foo'); + $this->assets->addJs('test.js'); + $css = $this->assets->js(); + $this->assertSame('' . PHP_EOL, $css); + + // local JS already with param + $this->assets->reset(); + $this->assets->setTimestamp('foo'); + $this->assets->addJs('test.js?bar'); + $css = $this->assets->js(); + $this->assertSame('' . PHP_EOL, $css); + + // external JS already + $this->assets->reset(); + $this->assets->setTimestamp('foo'); + $this->assets->addJs('http://somesite.com/test.js'); + $css = $this->assets->js(); + $this->assertSame('' . PHP_EOL, $css); + + // external JS already with param + $this->assets->reset(); + $this->assets->setTimestamp('foo'); + $this->assets->addJs('http://somesite.com/test.js?bar'); + $css = $this->assets->js(); + $this->assertSame('' . PHP_EOL, $css); + } + public function testAddInlineCss() { $this->assets->reset(); diff --git a/tests/unit/Grav/Common/Markdown/ParsedownTest.php b/tests/unit/Grav/Common/Markdown/ParsedownTest.php index 28069b14d..ec4521b4d 100644 --- a/tests/unit/Grav/Common/Markdown/ParsedownTest.php +++ b/tests/unit/Grav/Common/Markdown/ParsedownTest.php @@ -148,6 +148,27 @@ class ParsedownTest extends \Codeception\TestCase\Test $this->parsedown->text('![](/home-missing-image.jpg)')); } + public function testImagesAttributes() + { + $this->uri->initializeWithURL('http://testing.dev/item2/item2-2')->init(); + + $this->assertSame('

', + $this->parsedown->text('![](sample-image.jpg "My Title")')); + $this->assertSame('

', + $this->parsedown->text('![](sample-image.jpg?classes=foo)')); + $this->assertSame('

', + $this->parsedown->text('![](sample-image.jpg?classes=foo,bar)')); + $this->assertSame('

', + $this->parsedown->text('![](sample-image.jpg?id=foo)')); + $this->assertSame('

Alt Text

', + $this->parsedown->text('![Alt Text](sample-image.jpg?id=foo)')); + $this->assertSame('

Alt Text

', + $this->parsedown->text('![Alt Text](sample-image.jpg?class=bar&id=foo)')); + $this->assertSame('

Alt Text

', + $this->parsedown->text('![Alt Text](sample-image.jpg?class=bar&id=foo "My Title")')); + } + + public function testRootImages() { $this->uri->initializeWithURL('http://testing.dev/')->init();