Merge branch 'release/0.9.28'

This commit is contained in:
Andy Miller
2015-06-16 16:15:42 -06:00
18 changed files with 287 additions and 114 deletions

View File

@@ -1,3 +1,27 @@
# v0.9.28
## 06/16/2015
1. [](#new)
* Added method to set raw markdown on a page
* Added ability to enabled system and page level `etag` and `last_modified` headers
2. [](#improved)
* Improved image path processing
* 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
* Updated Doctrine Cache library
* Updated Symfony libraries
* Moved `convertUrl()` method to Uri object
3. [](#bugfix)
* Fix incorrect slug causing problems with CLI `uninstall`
* Fix Twig runtime error with assets pipeline in sufolder installations
* Fix for `+` in image filenames
* Fix for dot files causing issues with page processing
* Fix for Uri path detection on Windows platform
* Fix for atlernative media resolutions
* Fix for modularTypes key properties
# v0.9.27
## 05/09/2015

View File

@@ -6,10 +6,17 @@ if (version_compare($ver = PHP_VERSION, $req = '5.4.0', '<')) {
exit(sprintf("You are running PHP %s, but Grav needs at least PHP %s to run.\n", $ver, $req));
}
if (!file_exists(__DIR__ . '/../vendor')){
require_once __DIR__ . '/../system/src/Grav/Common/Composer.php';
}
use Grav\Common\Composer;
if (!file_exists(__DIR__ . '/../vendor')){
// Before we can even start, we need to run composer first
$composer = Composer::getComposerExecutor();
echo "Preparing to install vendor dependencies...\n\n";
echo system('php bin/composer.phar --working-dir="'.__DIR__.'/../" --no-interaction --no-dev --prefer-dist -o install');
echo system($composer.' --working-dir="'.__DIR__.'/../" --no-interaction --no-dev --prefer-dist -o install');
echo "\n\n";
}

View File

@@ -6,10 +6,17 @@ if (version_compare($ver = PHP_VERSION, $req = '5.4.0', '<')) {
exit(sprintf("You are running PHP %s, but Grav needs at least PHP %s to run.\n", $ver, $req));
}
if (!file_exists(__DIR__ . '/../vendor')){
require_once __DIR__ . '/../system/src/Grav/Common/Composer.php';
}
use Grav\Common\Composer;
if (!file_exists(__DIR__ . '/../vendor')){
// Before we can even start, we need to run composer first
$composer = Composer::getComposerExecutor();
echo "Preparing to install vendor dependencies...\n\n";
echo system('php bin/composer.phar --working-dir="'.__DIR__.'/../" --no-interaction --no-dev --prefer-dist -o install');
echo system($composer.' --working-dir="'.__DIR__.'/../" --no-interaction --no-dev --prefer-dist -o install');
echo "\n\n";
}

View File

@@ -9,10 +9,10 @@
"php": ">=5.4.0",
"twig/twig": "~1.16",
"erusev/parsedown-extra": "~0.7",
"symfony/yaml": "~2.6",
"symfony/console": "~2.6",
"symfony/event-dispatcher": "~2.6",
"doctrine/cache": "~1.3",
"symfony/yaml": "2.7.*",
"symfony/console": "2.7.*",
"symfony/event-dispatcher": "2.7.*",
"doctrine/cache": "~1.4",
"maximebf/debugbar": "dev-master",
"filp/whoops": "1.2.*@dev",
"monolog/monolog": "~1.0",

View File

@@ -32,6 +32,8 @@ pages:
'<': 'lt'
types: 'txt|xml|html|json|rss|atom' # Pipe separated list of valid page types
expires: 0 # Page expires time in seconds (604800 seconds = 7 days)
last_modified: true # Set the last modified header
etag: true # Set the expires header tag
cache:
enabled: true # Set to true to enable caching

View File

@@ -2,7 +2,7 @@
// Some standard defines
define('GRAV', true);
define('GRAV_VERSION', '0.9.27');
define('GRAV_VERSION', '0.9.28');
define('DS', '/');
// Directories and Paths

View File

@@ -825,7 +825,8 @@ class Assets
} else {
// Fix to remove relative dir if grav is in one
if (($this->base_url != '/') && (strpos($this->base_url, $link) == 0)) {
$relative_path = ltrim(preg_replace($this->base_url, '/', $link, 1), '/');
$base_url = '#' . preg_quote($this->base_url, '#') . '#';
$relative_path = ltrim(preg_replace($base_url, '/', $link, 1), '/');
}
$relative_dir = dirname($relative_path);

View File

@@ -0,0 +1,55 @@
<?php
namespace Grav\Common;
/**
* Offers composer helper methods.
*
* @author eschmar
* @license MIT
*/
class Composer
{
/** @const Default composer location */
const DEFAULT_PATH = "bin/composer.phar";
/**
* Returns the location of composer.
*
* @return string
*/
public static function getComposerLocation()
{
if (!function_exists('shell_exec') || strtolower(substr(PHP_OS, 0, 3)) === 'win') {
return self::DEFAULT_PATH;
}
// check for global composer install
$path = trim(shell_exec("command -v composer"));
// fall back to grav bundled composer
if (!$path || !preg_match('/(composer|composer\.phar)$/', $path)) {
$path = self::DEFAULT_PATH;
}
return $path;
}
public static function getComposerExecutor()
{
$executor = PHP_BINARY . ' ';
$composer = static::getComposerLocation();
if ($composer !== static::DEFAULT_PATH && is_executable($composer)) {
$file = fopen($composer, 'r');
$firstLine = fgets($file);
fclose($file);
if (!preg_match('/^#!.+php/i', $firstLine)) {
$executor = '';
}
}
return $executor . $composer;
}
}

View File

@@ -160,7 +160,7 @@ class Installer
/**
* Unnstalls one or more given package
*
* @param string $package The slug of the package(s)
* @param string $path The slug of the package(s)
* @param array $options Options to use for uninstalling
*
* @return boolean True if everything went fine, False otherwise.

View File

@@ -312,11 +312,15 @@ class Grav extends Container
}
// Set the last modified time
$last_modified_date = gmdate('D, d M Y H:i:s', $page->modified()) . ' GMT';
header('Last-Modified: ' . $last_modified_date);
if ($page->lastModified()) {
$last_modified_date = gmdate('D, d M Y H:i:s', $page->modified()) . ' GMT';
header('Last-Modified: ' . $last_modified_date);
}
// Calculate a Hash based on the raw file
header('ETag: ' . md5($page->raw().$page->modified()));
if ($page->eTag()) {
header('ETag: ' . md5($page->raw() . $page->modified()));
}
// Set debugger data in headers
if (!($extension === null || $extension == 'html')) {

View File

@@ -133,10 +133,9 @@ trait ParsedownGravTrait
// get the local path to page media if possible
if ($path_parts['dirname'] == $this->page->url()) {
$url['path'] = ltrim(str_replace($this->page->url(), '', $url['path']), '/');
$url['path'] = $path_parts['basename'];
// get the media objects for this page
$media = $this->page->media();
} else {
// see if this is an external page to this one
$page_route = str_replace($this->base_url, '', $path_parts['dirname']);
@@ -205,81 +204,10 @@ trait ParsedownGravTrait
// if there is no scheme, the file is local
if (!isset($url['scheme']) && (count($url) > 0)) {
// convert the URl is required
$excerpt['element']['attributes']['href'] = $this->convertUrl(Uri::buildUrl($url));
$excerpt['element']['attributes']['href'] = Uri::convertUrl($this->page, Uri::buildUrl($url));
}
}
return $excerpt;
}
/**
* Converts links from absolute '/' or relative (../..) to a grav friendly format
* @param string $markdown_url the URL as it was written in the markdown
* @return string the more friendly formatted url
*/
protected function convertUrl($markdown_url)
{
// if absolute and starts with a base_url move on
if ($this->base_url != '' && Utils::startsWith($markdown_url, $this->base_url)) {
return $markdown_url;
// if contains only a fragment
} elseif (Utils::startsWith($markdown_url, '#')) {
return $markdown_url;
} else {
$target = null;
// see if page is relative to this or absolute
if (Utils::startsWith($markdown_url, '/')) {
$normalized_path = Utils::normalizePath($this->pages_dir . $markdown_url);
$normalized_url = Utils::normalizePath($this->base_url . $markdown_url);
} else {
$normalized_url = $this->base_url . Utils::normalizePath($this->page->route() . '/' . $markdown_url);
$normalized_path = Utils::normalizePath($this->page->path() . '/' . $markdown_url);
}
// special check to see if path checking is required.
$just_path = str_replace($normalized_url, '', $normalized_path);
if ($just_path == $this->page->path()) {
return $normalized_url;
}
// if this file exits, get the page and work with that
if ($normalized_path) {
$url_bits = parse_url($normalized_path);
$full_path = $url_bits['path'];
if ($full_path && file_exists($full_path)) {
$path_info = pathinfo($full_path);
$page_path = $path_info['dirname'];
$filename = '';
if ($markdown_url == '..') {
$page_path = $full_path;
} else {
// save the filename if a file is part of the path
$filename_regex = "/([\w\d-_]+\.([a-zA-Z]{2,4}))$/";
if (preg_match($filename_regex, $full_path, $matches)) {
if ($matches[2] != 'md') {
$filename = '/' . $matches[1];
}
} else {
$page_path = $full_path;
}
}
// get page instances and try to find one that fits
$instances = $this->pages->instances();
if (isset($instances[$page_path])) {
$target = $instances[$page_path];
$url_bits['path'] = $this->base_url . $target->route() . $filename;
return Uri::buildUrl($url_bits);
}
}
}
return $normalized_url;
}
}
}

View File

@@ -120,7 +120,7 @@ class Media extends Getters
}
foreach ($types['alternative'] as $ratio => $altMedium) {
$medium->addAlternative($ratio, $altMedium);
$medium->addAlternative($ratio, $altMedium['file']);
}
}

View File

@@ -78,6 +78,8 @@ class Page
protected $process;
protected $summary_size;
protected $markdown_extra;
protected $etag;
protected $last_modified;
/**
* @var Page Unmodified (original) version of the page. Used for copying and moving the page.
@@ -277,6 +279,12 @@ class Page
if (isset($this->header->expires)) {
$this->expires = intval($this->header->expires);
}
if (isset($this->header->etag)) {
$this->etag = (bool)$this->header->etag;
}
if (isset($this->header->last_modified)) {
$this->last_modified = (bool)$this->header->last_modified;
}
}
@@ -361,7 +369,6 @@ class Page
$this->id(time().md5($this->filePath()));
$this->content = null;
}
// If no content, process it
if ($this->content === null) {
// Get media
@@ -568,6 +575,15 @@ class Page
return $default;
}
public function rawMarkdown($var = null)
{
if ($var !== null) {
$this->raw_content = $var;
}
return $this->raw_content;
}
/**
* Get file object to the page.
*
@@ -1134,6 +1150,40 @@ class Page
return $this->modified;
}
/**
* Gets and sets the option to show the etag header for the page.
*
* @param boolean $var show etag header
* @return boolean show etag header
*/
public function eTag($var = null)
{
if ($var !== null) {
$this->etag = $var;
}
if (!isset($this->etag)) {
$this->etag = (bool) self::getGrav()['config']->get('system.pages.etag');
}
return $this->etag;
}
/**
* Gets and sets the option to show the last_modified header for the page.
*
* @param boolean $var show last_modified header
* @return boolean show last_modified header
*/
public function lastModified($var = null)
{
if ($var !== null) {
$this->last_modified = $var;
}
if (!isset($this->last_modified)) {
$this->last_modified = (bool) self::getGrav()['config']->get('system.pages.last_modified');
}
return $this->last_modified;
}
/**
* Gets and sets the path to the .md file for this Page object.
*

View File

@@ -557,7 +557,7 @@ class Pages
$last_modified = $modified;
}
if (Utils::endsWith($name, CONTENT_EXT)) {
if (preg_match('/^[^.].*'.CONTENT_EXT.'$/', $name)) {
$page->init($file);
$content_exists = true;

View File

@@ -78,7 +78,7 @@ class Types implements \ArrayAccess, \Iterator, \Countable
if (strpos($name, 'modular/') !== 0) {
continue;
}
$list[$name] = trim(ucfirst(strtr(basename($name), '_', ' ')));
$list[basename($name)] = trim(ucfirst(strtr(basename($name), '_', ' ')));
}
ksort($list);
return $list;

View File

@@ -74,15 +74,27 @@ class Uri
{
$config = Grav::instance()['config'];
// resets
$this->paths = [];
$this->params = [];
$this->query = [];
// get any params and remove them
$uri = str_replace($this->root, '', $this->url);
// reset params
$this->params = [];
// process params
$uri = $this->processParams($uri, $config->get('system.param_sep'));
// split the URL and params
$bits = parse_url($uri);
// process query string
if (isset($bits['query'])) {
parse_str($bits['query'], $this->query);
$uri = $bits['path'];
}
// remove the extension if there is one set
$parts = pathinfo($uri);
@@ -90,25 +102,13 @@ class Uri
$this->basename = $parts['basename'];
if (preg_match("/\.(".$config->get('system.pages.types').")$/", $parts['basename'])) {
$uri = rtrim($parts['dirname'], '/').'/'.$parts['filename'];
$uri = rtrim(str_replace(DIRECTORY_SEPARATOR, DS, $parts['dirname']), DS). '/' .$parts['filename'];
$this->extension = $parts['extension'];
}
// set the new url
$this->url = $this->root . $uri;
// split into bits
$this->bits = parse_url($uri);
$this->query = array();
if (isset($this->bits['query'])) {
parse_str($this->bits['query'], $this->query);
}
$path = $this->bits['path'];
$this->paths = array();
$this->path = $path;
$this->path = $uri;
$this->content_path = trim(str_replace($this->base, '', $this->path), '/');
if ($this->content_path != '') {
$this->paths = explode('/', $this->content_path);
@@ -441,4 +441,90 @@ class Uri
$fragment = isset($parsed_url['fragment']) ? '#' . $parsed_url['fragment'] : '';
return "$scheme$user$pass$host$port$path$query$fragment";
}
/**
* Converts links from absolute '/' or relative (../..) to a grav friendly format
*
* @param $page the current page to use as reference
* @param string $markdown_url the URL as it was written in the markdown
*
* @return string the more friendly formatted url
*/
public static function convertUrl($page, $markdown_url)
{
$grav = Grav::instance();
$pages_dir = $grav['locator']->findResource('page://');
$base_url = rtrim($grav['base_url'] . $grav['pages']->base(), '/');
// if absolute and starts with a base_url move on
if (pathinfo($markdown_url, PATHINFO_DIRNAME) == '.') {
if ($page->url() == '/') {
return '/' . $markdown_url;
} else {
return $page->url() . '/' . $markdown_url;
}
// no path to convert
} elseif ($base_url != '' && Utils::startsWith($markdown_url, $base_url)) {
return $markdown_url;
// if contains only a fragment
} elseif (Utils::startsWith($markdown_url, '#')) {
return $markdown_url;
} else {
$target = null;
// see if page is relative to this or absolute
if (Utils::startsWith($markdown_url, '/')) {
$normalized_url = Utils::normalizePath($base_url . $markdown_url);
$normalized_path = Utils::normalizePath($pages_dir . $markdown_url);
} else {
$normalized_url = $base_url . Utils::normalizePath($page->route() . '/' . $markdown_url);
$normalized_path = Utils::normalizePath($page->path() . '/' . $markdown_url);
}
// special check to see if path checking is required.
$just_path = str_replace($normalized_url, '', $normalized_path);
if ($just_path == $page->path()) {
return $normalized_url;
}
$url_bits = parse_url($normalized_path);
$full_path = ($url_bits['path']);
if (file_exists($full_path)) {
// do nothing
} elseif (file_exists(urldecode($full_path))) {
$full_path = urldecode($full_path);
} else {
return $normalized_url;
}
$path_info = pathinfo($full_path);
$page_path = $path_info['dirname'];
$filename = '';
if ($markdown_url == '..') {
$page_path = $full_path;
} else {
// save the filename if a file is part of the path
if (is_file($full_path)) {
if ($path_info['extension'] != 'md') {
$filename = '/' . $path_info['basename'];
}
} else {
$page_path = $full_path;
}
}
// get page instances and try to find one that fits
$instances = $grav['pages']->instances();
if (isset($instances[$page_path])) {
$target = $instances[$page_path];
$url_bits['path'] = $base_url . $target->route() . $filename;
return Uri::buildUrl($url_bits);
}
return $normalized_url;
}
}
}

View File

@@ -2,6 +2,7 @@
namespace Grav\Console;
use Grav\Common\GravTrait;
use Grav\Common\Composer;
use Grav\Console\Cli\ClearCacheCommand;
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
use Symfony\Component\Console\Input\ArrayInput;
@@ -85,7 +86,9 @@ trait ConsoleTrait
public function composerUpdate($path, $action = 'install')
{
return system('php bin/composer.phar --working-dir="'.$path.'" --no-interaction --no-dev --prefer-dist -o '. $action);
$composer = Composer::getComposerExecutor();
return system($composer . ' --working-dir="'.$path.'" --no-interaction --no-dev --prefer-dist -o '. $action);
}
/**

View File

@@ -110,14 +110,14 @@ class UninstallCommand extends Command
$this->output->write(" |- Checking destination... ");
$checks = $this->checkDestination($package);
$checks = $this->checkDestination($slug, $package);
if (!$checks) {
$this->output->writeln(" '- <red>Installation failed or aborted.</red>");
$this->output->writeln('');
} else {
$this->output->write(" |- Uninstalling package... ");
$uninstall = $this->uninstallPackage($package);
$uninstall = $this->uninstallPackage($slug, $package);
if (!$uninstall) {
$this->output->writeln(" '- <red>Uninstallation failed or aborted.</red>");
@@ -135,12 +135,16 @@ class UninstallCommand extends Command
/**
* @param $slug
* @param $package
*
* @return bool
*/
private function uninstallPackage($package)
private function uninstallPackage($slug, $package)
{
$path = self::getGrav()['locator']->findResource($package->package_type . '://' . $package->slug);
$locator = self::getGrav()['locator'];
$path = self::getGrav()['locator']->findResource($package->package_type . '://' .$slug);
Installer::uninstall($path);
$errorCode = Installer::lastErrorCode();
@@ -159,15 +163,17 @@ class UninstallCommand extends Command
return true;
}
/**
* @param $slug
* @param $package
*
* @return bool
*/
private function checkDestination($package)
private function checkDestination($slug, $package)
{
$path = self::getGrav()['locator']->findResource($package->package_type . '://' . $package->slug);
$path = self::getGrav()['locator']->findResource($package->package_type . '://' . $slug);
$questionHelper = $this->getHelper('question');
$skipPrompt = $this->input->getOption('all-yes');