mirror of
https://github.com/getgrav/grav.git
synced 2025-10-26 07:56:07 +01:00
Merge branch 'develop' of https://github.com/getgrav/grav into feature/multi-config
Conflicts: composer.json system/config/streams.yaml system/src/Grav/Common/Page/Page.php system/src/Grav/Common/Theme.php system/src/Grav/Common/Themes.php system/src/Grav/Component/Filesystem/ResourceLocator.php vendor/autoload.php vendor/composer/autoload_classmap.php vendor/composer/autoload_files.php vendor/composer/autoload_real.php vendor/composer/installed.json
This commit is contained in:
24
.htaccess
24
.htaccess
@@ -1,26 +1,36 @@
|
||||
<IfModule mod_rewrite.c>
|
||||
|
||||
RewriteEngine On
|
||||
|
||||
# access site
|
||||
##
|
||||
# If you are getting 404 errors on subpages, you may have to uncomment the RewriteBase entry
|
||||
# You should change the '/' to your appropriate subfolder. For example if you have
|
||||
# your Grav install at the root of your site '/' should work, else it might be something
|
||||
# along the lines of: RewriteBase /<your_sub_folder>
|
||||
##
|
||||
|
||||
# RewriteBase /
|
||||
|
||||
# Access site
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule . index.php [L]
|
||||
RewriteRule .* index.php [L]
|
||||
|
||||
# block various user files from being accessed directly
|
||||
# Block various user files from being accessed directly
|
||||
RewriteRule ^user/accounts/(.*)$ error [R=301,L]
|
||||
RewriteRule ^user/config/(.*)$ error [R=301,L]
|
||||
RewriteRule ^user/(.*)\.(txt|md|html|php|yaml|json|twig|sh|bat)$ error [R=301,L]
|
||||
|
||||
# block cache
|
||||
# Block cache/
|
||||
RewriteRule ^cache/(.*) error [R=301,L]
|
||||
|
||||
# block bin
|
||||
# Block bin/
|
||||
RewriteRule ^bin/(.*)$ error [R=301,L]
|
||||
|
||||
# block system
|
||||
# Block system/
|
||||
RewriteRule ^system/(.*)$ error [R=301,L]
|
||||
|
||||
# block vendor
|
||||
# Block vendor/
|
||||
RewriteRule ^vendor/(.*)$ error [R=301,L]
|
||||
|
||||
</IfModule>
|
||||
|
||||
@@ -7,7 +7,12 @@ The underlying architecture of Grav has been designed to use well-established an
|
||||
* [Twig Templating](http://twig.sensiolabs.org/): for powerful control of the user interface
|
||||
* [Markdown](http://en.wikipedia.org/wiki/Markdown): for easy content creation
|
||||
* [YAML](http://yaml.org): for simple configuration
|
||||
* [Doctrine Cache](http://docs.doctrine-project.org/en/2.0.x/reference/caching.html): layer for incredible performance
|
||||
* [Parsedown](http://parsedown.org/): for fast Markdown and Mardown Extra support
|
||||
* [Doctrine Cache](http://docs.doctrine-project.org/en/2.0.x/reference/caching.html): layer for performance
|
||||
* [Pimple Dependency Injection Container](http://pimple.sensiolabs.org/): for extensibility and maintainability
|
||||
* [Symfony Event Dispacher](http://symfony.com/doc/current/components/event_dispatcher/introduction.html): for plugin event handling
|
||||
* [Symfony Console](http://symfony.com/doc/current/components/console/introduction.html): for CLI interface
|
||||
* [Gregwar Image Library](https://github.com/Gregwar/Image): for dynamic image manipulation
|
||||
|
||||
# QuickStart
|
||||
|
||||
|
||||
4
bin/grav
4
bin/grav
@@ -1,6 +1,10 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
use Symfony\Component\Console\Application;
|
||||
|
||||
$autoload = __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"require": {
|
||||
"php": ">=5.4.0",
|
||||
"twig/twig": "~1.16",
|
||||
"erusev/parsedown-extra": "~0.2",
|
||||
"erusev/parsedown-extra": "dev-master",
|
||||
"symfony/yaml": "~2.5",
|
||||
"symfony/console": "~2.5",
|
||||
"symfony/event-dispatcher": "~2.5",
|
||||
@@ -16,8 +16,8 @@
|
||||
"tracy/tracy": "~2.2",
|
||||
"gregwar/image": "~2.0",
|
||||
"ircmaxell/password-compat": "1.0.*",
|
||||
"mrclay/minify": "~2.2",
|
||||
"ornicar/php-user-agent": "1.0.*",
|
||||
"mrclay/minify": "dev-master",
|
||||
"donatj/phpuseragentparser": "dev-master",
|
||||
"pimple/pimple": "~3.0",
|
||||
"rockettheme/toolbox": "dev-develop"
|
||||
},
|
||||
@@ -25,11 +25,15 @@
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/rockettheme/toolbox"
|
||||
},
|
||||
{
|
||||
"type": "vcs",
|
||||
"url": "https://github.com/rhukster/minify"
|
||||
}
|
||||
],
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Grav\\": "system/src/"
|
||||
"Grav\\": "system/src/Grav"
|
||||
},
|
||||
"files": ["system/defines.php"]
|
||||
},
|
||||
|
||||
@@ -1,13 +1,39 @@
|
||||
schemes:
|
||||
plugin:
|
||||
type: ReadOnlyStream
|
||||
paths:
|
||||
- user/plugins
|
||||
- system/plugins
|
||||
|
||||
asset:
|
||||
type: ReadOnlyStream
|
||||
paths:
|
||||
- assets
|
||||
|
||||
cache:
|
||||
type: ReadOnlyStream
|
||||
paths:
|
||||
- cache
|
||||
|
||||
log:
|
||||
type: ReadOnlyStream
|
||||
paths:
|
||||
- logs
|
||||
|
||||
image:
|
||||
type: ReadOnlyStream
|
||||
paths:
|
||||
- user/images
|
||||
|
||||
user:
|
||||
type: ReadOnlyStream
|
||||
paths:
|
||||
- user
|
||||
|
||||
# asset:
|
||||
# type: ReadOnlyStream
|
||||
# paths:
|
||||
# - assets
|
||||
page:
|
||||
type: ReadOnlyStream
|
||||
paths:
|
||||
- user/pages
|
||||
|
||||
account:
|
||||
type: ReadOnlyStream
|
||||
@@ -19,20 +45,8 @@ schemes:
|
||||
paths:
|
||||
- user/data
|
||||
|
||||
page:
|
||||
theme:
|
||||
type: ReadOnlyStream
|
||||
prefixes:
|
||||
'/':
|
||||
- user/pages
|
||||
|
||||
image:
|
||||
prefixes:
|
||||
'/':
|
||||
- user/images
|
||||
- system/images
|
||||
|
||||
theme:
|
||||
prefixes:
|
||||
'/':
|
||||
- user/themes
|
||||
- system/themes
|
||||
|
||||
@@ -16,13 +16,13 @@ pages:
|
||||
markdown: true # Process Markdown
|
||||
twig: false # Process Twig
|
||||
events:
|
||||
page: false # Enable page level events
|
||||
page: true # Enable page level events
|
||||
twig: true # Enable twig level events
|
||||
|
||||
cache:
|
||||
enabled: true # Set to true to enable caching
|
||||
check:
|
||||
pages: true # Check to see if page has been modifying to flush the cache
|
||||
method: file # Method to check for updates in pages: file|folder|none
|
||||
driver: auto # One of: auto|file|apc|xcache|memcache|memcached|wincache
|
||||
prefix: 'g' # Cache prefix string (prevents cache conflicts)
|
||||
|
||||
@@ -32,15 +32,16 @@ twig:
|
||||
auto_reload: true # Refresh cache on changes
|
||||
autoescape: false # Autoescape Twig vars
|
||||
|
||||
assets: # Configuration for Assets Manager (JS, CSS)
|
||||
css_pipeline: false # The CSS pipeline is the unification of multiple CSS resources into one file
|
||||
css_minify: true # Minify the CSS during pipelining
|
||||
css_rewrite: true # Rewrite any CSS relative URLs during pipelining
|
||||
js_pipeline: false # The JS pipeline is the unification of multiple JS resources into one file
|
||||
js_minify: true # Minify the JS during pipelining
|
||||
assets: # Configuration for Assets Manager (JS, CSS)
|
||||
css_pipeline: false # The CSS pipeline is the unification of multiple CSS resources into one file
|
||||
css_minify: true # Minify the CSS during pipelining
|
||||
css_rewrite: true # Rewrite any CSS relative URLs during pipelining
|
||||
js_pipeline: false # The JS pipeline is the unification of multiple JS resources into one file
|
||||
js_minify: true # Minify the JS during pipelining
|
||||
|
||||
debugger:
|
||||
enabled: false # Enable Grav debugger and following settings
|
||||
mode: detect # Mode tracy Debugger should be set to when enabled: detect|development|production
|
||||
strict: false # Throw fatal error also on PHP warnings and notices
|
||||
max_depth: 10 # How many nested levels to display for objects or arrays
|
||||
log:
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
// Some standard defines
|
||||
define('GRAV', true);
|
||||
define('GRAV_VERSION', '0.8.1');
|
||||
define('GRAV_VERSION', '0.9.1');
|
||||
define('DS', '/');
|
||||
|
||||
// Directories and Paths
|
||||
|
||||
36
system/src/Grav/Common/Browser.php
Normal file
36
system/src/Grav/Common/Browser.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
namespace Grav\Common;
|
||||
|
||||
/**
|
||||
* Simple wrapper for the very simple parse_user_agent() function
|
||||
*/
|
||||
class Browser {
|
||||
|
||||
protected $useragent;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->useragent = parse_user_agent();
|
||||
}
|
||||
|
||||
public function getBrowser()
|
||||
{
|
||||
return strtolower($this->useragent['browser']);
|
||||
}
|
||||
|
||||
public function getPlatform()
|
||||
{
|
||||
return strtolower($this->useragent['platform']);
|
||||
}
|
||||
|
||||
public function getLongVersion()
|
||||
{
|
||||
return $this->useragent['version'];
|
||||
}
|
||||
|
||||
public function getVersion()
|
||||
{
|
||||
$version = explode('.', $this->getLongVersion());
|
||||
return intval($version[0]);
|
||||
}
|
||||
}
|
||||
@@ -66,6 +66,9 @@ class Cache extends Getters
|
||||
$this->key = substr(md5(($prefix ? $prefix : 'g') . $uri->rootUrl(true) . $this->config->key . GRAV_VERSION), 2, 8);
|
||||
|
||||
$this->driver = $this->getCacheDriver();
|
||||
|
||||
// Set the cache namespace to our unique key
|
||||
$this->driver->setNamespace($this->key);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -132,7 +135,6 @@ class Cache extends Getters
|
||||
public function fetch($id)
|
||||
{
|
||||
if ($this->enabled) {
|
||||
$id = $this->key . $id;
|
||||
return $this->driver->fetch($id);
|
||||
} else {
|
||||
return false;
|
||||
@@ -149,7 +151,6 @@ class Cache extends Getters
|
||||
public function save($id, $data, $lifetime = null)
|
||||
{
|
||||
if ($this->enabled) {
|
||||
$id = $this->key . $id;
|
||||
$this->driver->save($id, $data, $lifetime);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ class Debugger
|
||||
{
|
||||
const PRODUCTION = TracyDebugger::PRODUCTION;
|
||||
const DEVELOPMENT = TracyDebugger::DEVELOPMENT;
|
||||
const DETECT = TracyDebugger::DETECT;
|
||||
|
||||
public function __construct($mode = self::PRODUCTION)
|
||||
{
|
||||
@@ -27,6 +28,7 @@ class Debugger
|
||||
/** @var Config $config */
|
||||
$config = $grav['config'];
|
||||
|
||||
$mode = $config->get('system.debugger.mode');
|
||||
TracyDebugger::$logDirectory = $config->get('system.debugger.log.enabled') ? LOG_DIR : null;
|
||||
TracyDebugger::$maxDepth = $config->get('system.debugger.max_depth');
|
||||
|
||||
@@ -39,7 +41,16 @@ class Debugger
|
||||
if (function_exists('ini_set')) {
|
||||
ini_set('display_errors', true);
|
||||
}
|
||||
TracyDebugger::$productionMode = TracyDebugger::DEVELOPMENT;
|
||||
|
||||
if ($mode == strtolower('detect')) {
|
||||
TracyDebugger::$productionMode = self::DETECT;
|
||||
} elseif ($mode == strtolower('production')) {
|
||||
TracyDebugger::$productionMode = self::PRODUCTION;
|
||||
} else {
|
||||
TracyDebugger::$productionMode = self::DEVELOPMENT;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -99,8 +99,8 @@ class Grav extends Container
|
||||
$container['output'] = function ($c) {
|
||||
return $c['twig']->processSite($c['uri']->extension());
|
||||
};
|
||||
$container['user_agent'] = function ($c) {
|
||||
return new \phpUserAgent();
|
||||
$container['browser'] = function ($c) {
|
||||
return new Browser();
|
||||
};
|
||||
|
||||
$container->register(new StreamsServiceProvider);
|
||||
|
||||
13
system/src/Grav/Common/Markdown/Markdown.php
Normal file
13
system/src/Grav/Common/Markdown/Markdown.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
namespace Grav\Common\Markdown;
|
||||
|
||||
class Markdown extends \Parsedown
|
||||
{
|
||||
use MarkdownGravLinkTrait;
|
||||
|
||||
function __construct($page)
|
||||
{
|
||||
$this->page = $page;
|
||||
}
|
||||
|
||||
}
|
||||
13
system/src/Grav/Common/Markdown/MarkdownExtra.php
Normal file
13
system/src/Grav/Common/Markdown/MarkdownExtra.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
namespace Grav\Common\Markdown;
|
||||
|
||||
class MarkdownExtra extends \ParsedownExtra
|
||||
{
|
||||
use MarkdownGravLinkTrait;
|
||||
|
||||
function __construct($page)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->page = $page;
|
||||
}
|
||||
}
|
||||
147
system/src/Grav/Common/Markdown/MarkdownGravLinkTrait.php
Normal file
147
system/src/Grav/Common/Markdown/MarkdownGravLinkTrait.php
Normal file
@@ -0,0 +1,147 @@
|
||||
<?php
|
||||
namespace Grav\Common\Markdown;
|
||||
|
||||
use Grav\Common\Debugger;
|
||||
use Grav\Common\GravTrait;
|
||||
|
||||
/**
|
||||
* A trait to add some custom processing to the identifyLink() method in Parsedown and ParsedownExtra
|
||||
*/
|
||||
trait MarkdownGravLinkTrait
|
||||
{
|
||||
use GravTrait;
|
||||
|
||||
protected function identifyLink($Excerpt)
|
||||
{
|
||||
// Run the parent method to get the actual results
|
||||
$Excerpt = parent::identifyLink($Excerpt);
|
||||
$actions = array();
|
||||
$this->base_url = trim(self::$grav['config']->get('system.base_url_relative'));
|
||||
|
||||
// if this is a link
|
||||
if (isset($Excerpt['element']['attributes']['href'])) {
|
||||
|
||||
$url = parse_url(htmlspecialchars_decode($Excerpt['element']['attributes']['href']));
|
||||
|
||||
// if there is no host set but there is a path, the file is local
|
||||
if (!isset($url['host']) && isset($url['path'])) {
|
||||
|
||||
// convert the URl is required
|
||||
$Excerpt['element']['attributes']['href'] = $this->convertUrl($url['path']);
|
||||
}
|
||||
}
|
||||
|
||||
// if this is an image
|
||||
if (isset($Excerpt['element']['attributes']['src'])) {
|
||||
|
||||
$alt = isset($Excerpt['element']['attributes']['alt']) ? $Excerpt['element']['attributes']['alt'] : '';
|
||||
$title = isset($Excerpt['element']['attributes']['title']) ? $Excerpt['element']['attributes']['title'] : '';
|
||||
|
||||
//get the url and parse it
|
||||
$url = parse_url(htmlspecialchars_decode($Excerpt['element']['attributes']['src']));
|
||||
|
||||
// if there is no host set but there is a path, the file is local
|
||||
if (!isset($url['host']) && isset($url['path'])) {
|
||||
// get the media objects for this page
|
||||
$media = $this->page->media();
|
||||
|
||||
// if there is a media file that matches the path referenced..
|
||||
if (isset($media->images()[$url['path']])) {
|
||||
// get the medium object
|
||||
$medium = $media->images()[$url['path']];
|
||||
|
||||
// if there is a query, then parse it and build action calls
|
||||
if (isset($url['query'])) {
|
||||
parse_str($url['query'], $actions);
|
||||
}
|
||||
|
||||
// loop through actions for the image and call them
|
||||
foreach ($actions as $action => $params) {
|
||||
// as long as it's not an html, url or ligtbox action
|
||||
if (!in_array($action, ['html','url','lightbox'])) {
|
||||
call_user_func_array(array(&$medium, $action), explode(',', $params));
|
||||
}
|
||||
}
|
||||
|
||||
// Get the URL for regular images, or an array of bits needed to put together
|
||||
// the lightbox HTML
|
||||
if (!isset($actions['lightbox'])) {
|
||||
$src = $medium->url();
|
||||
} else {
|
||||
$src = $medium->lightboxRaw();
|
||||
}
|
||||
|
||||
// set the src element with the new generated url
|
||||
if (!isset($actions['lightbox']) && !is_array($src)) {
|
||||
$Excerpt['element']['attributes']['src'] = $src;
|
||||
} else {
|
||||
|
||||
// Create the custom lightbox element
|
||||
$Element = array(
|
||||
'name' => 'a',
|
||||
'attributes' => array('rel' => $src['a_rel'], 'href' => $src['a_url']),
|
||||
'handler' => 'element',
|
||||
'text' => array(
|
||||
'name' => 'img',
|
||||
'attributes' => array('src' => $src['img_url'], 'alt' => $alt, 'title' => $title)
|
||||
),
|
||||
);
|
||||
|
||||
// Set the lightbox element on the Excerpt
|
||||
$Excerpt['element'] = $Element;
|
||||
}
|
||||
} else {
|
||||
// not a current page media file, see if it needs converting to relative
|
||||
$Excerpt['element']['attributes']['src'] = $this->convertUrl($url['path']);
|
||||
}
|
||||
}
|
||||
}
|
||||
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 absolue and starts with a base_url move on
|
||||
if ($this->base_url == '' || strpos($markdown_url, $this->base_url) === 0) {
|
||||
$new_url = $markdown_url;
|
||||
// if its absolute with /
|
||||
} elseif (strpos($markdown_url, '/') === 0) {
|
||||
$new_url = rtrim($this->base_url, '/') . $markdown_url;
|
||||
} else {
|
||||
$relative_path = rtrim($this->base_url, '/') . $this->page->route();
|
||||
|
||||
// If this is a 'real' filepath clean it up
|
||||
if (file_exists($this->page->path().'/'.$markdown_url)) {
|
||||
$relative_path = rtrim($this->base_url, '/') .
|
||||
preg_replace('/\/([\d]+.)/', '/',
|
||||
str_replace(PAGES_DIR, '/', $this->page->path()));
|
||||
$markdown_url = preg_replace('/^([\d]+.)/', '',
|
||||
preg_replace('/\/([\d]+.)/', '/',
|
||||
trim(preg_replace('/[^\/]+(\.md$)/', '', $markdown_url), '/')));
|
||||
}
|
||||
|
||||
// else its a relative path already
|
||||
$newpath = array();
|
||||
$paths = explode('/', $markdown_url);
|
||||
|
||||
// remove the updirectory references (..)
|
||||
foreach ($paths as $path) {
|
||||
if ($path == '..') {
|
||||
$relative_path = dirname($relative_path);
|
||||
} else {
|
||||
$newpath[] = $path;
|
||||
}
|
||||
}
|
||||
|
||||
// build the new url
|
||||
$new_url = $relative_path . '/' . implode('/', $newpath);
|
||||
}
|
||||
|
||||
return $new_url;
|
||||
}
|
||||
}
|
||||
@@ -202,6 +202,15 @@ class Medium extends Data
|
||||
return $this->link($width, $height);
|
||||
}
|
||||
|
||||
public function lightboxRaw($width = null, $height = null)
|
||||
{
|
||||
$url = $this->url();
|
||||
$this->link($width, $height);
|
||||
$lightbox_url = self::$grav['config']->get('system.base_url_relative') . '/'. $this->linkTarget;
|
||||
|
||||
return array('a_url' => $lightbox_url, 'a_rel' => 'lightbox', 'img_url' => $url);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return link HTML for the medium.
|
||||
*
|
||||
|
||||
@@ -9,6 +9,8 @@ use Grav\Common\Twig;
|
||||
use Grav\Common\Uri;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Taxonomy;
|
||||
use Grav\Common\Markdown\Markdown;
|
||||
use Grav\Common\Markdown\MarkdownExtra;
|
||||
use Grav\Component\Data\Blueprint;
|
||||
use Grav\Component\Filesystem\File;
|
||||
use Grav\Component\Filesystem\Folder;
|
||||
@@ -68,6 +70,7 @@ class Page
|
||||
protected $modular_twig;
|
||||
protected $process;
|
||||
protected $summary_size;
|
||||
protected $markdown_extra;
|
||||
|
||||
/**
|
||||
* @var Page Unmodified (original) version of the page. Used for copying and moving the page.
|
||||
@@ -103,7 +106,7 @@ class Page
|
||||
public function init($file)
|
||||
{
|
||||
$this->filePath($file->getPathName());
|
||||
$this->modified(filemtime($file->getPath()));
|
||||
$this->modified($file->getMTime());
|
||||
$this->id($this->modified().md5($this->filePath()));
|
||||
$this->header();
|
||||
$this->slug();
|
||||
@@ -199,6 +202,9 @@ class Page
|
||||
if (isset($this->header->date)) {
|
||||
$this->date = strtotime($this->header->date);
|
||||
}
|
||||
if (isset($this->header->markdown_extra)) {
|
||||
$this->markdown_extra = (bool)$this->header->markdown_extra;
|
||||
}
|
||||
if (isset($this->header->taxonomy)) {
|
||||
foreach ($this->header->taxonomy as $taxonomy => $taxitems) {
|
||||
$this->taxonomy[$taxonomy] = (array)$taxitems;
|
||||
@@ -212,7 +218,9 @@ class Page
|
||||
$this->process[$process] = $status;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $this->header;
|
||||
}
|
||||
|
||||
@@ -272,6 +280,9 @@ class Page
|
||||
// If no content, process it
|
||||
if ($this->content === null) {
|
||||
|
||||
// Get media
|
||||
$this->media();
|
||||
|
||||
// Load cached content
|
||||
/** @var Cache $cache */
|
||||
$cache = self::$grav['cache'];
|
||||
@@ -319,7 +330,6 @@ class Page
|
||||
|
||||
$this->content = $content;
|
||||
|
||||
$this->media();
|
||||
}
|
||||
|
||||
return $this->content;
|
||||
@@ -1315,7 +1325,7 @@ class Page
|
||||
}
|
||||
}
|
||||
|
||||
$config->set('system.cache.enabled', false);
|
||||
$config->set('system.cache.enabled', false); // TODO: Do we still need this?
|
||||
}
|
||||
}
|
||||
// TODO: END OF MOVE
|
||||
@@ -1513,10 +1523,12 @@ class Page
|
||||
{
|
||||
/** @var Config $config */
|
||||
$config = self::$grav['config'];
|
||||
if ($config->get('system.pages.markdown_extra')) {
|
||||
$parsedown = new \ParsedownExtra();
|
||||
|
||||
// get the appropriate setting for markdown extra
|
||||
if (isset($this->markdown_extra) ? $this->markdown_extra : $config->get('system.pages.markdown_extra')) {
|
||||
$parsedown = new MarkdownExtra($this);
|
||||
} else {
|
||||
$parsedown = new \Parsedown();
|
||||
$parsedown = new Markdown($this);
|
||||
}
|
||||
$content = $parsedown->parse($content);
|
||||
return $content;
|
||||
|
||||
@@ -349,8 +349,22 @@ class Pages
|
||||
$cache = $this->grav['cache'];
|
||||
/** @var Taxonomy $taxonomy */
|
||||
$taxonomy = $this->grav['taxonomy'];
|
||||
$last_modified = $config->get('system.cache.check.pages', true)
|
||||
? Folder::lastModified(PAGES_DIR) : 0;
|
||||
|
||||
$last_modified = 0;
|
||||
|
||||
// how should we check for last modified? Default is by file
|
||||
switch (strtolower($config->get('system.cache.check.method', 'file'))) {
|
||||
case 'none':
|
||||
case 'off':
|
||||
$last_modified = 0;
|
||||
break;
|
||||
case 'folder':
|
||||
$last_modified = Folder::lastModifiedFolder(PAGES_DIR);
|
||||
break;
|
||||
default:
|
||||
$last_modified = Folder::lastModifiedFile(PAGES_DIR);
|
||||
}
|
||||
|
||||
$page_cache_id = md5(USER_DIR.$last_modified);
|
||||
|
||||
list($this->instances, $this->routes, $this->children, $taxonomy_map, $this->sort) = $cache->fetch($page_cache_id);
|
||||
@@ -405,10 +419,17 @@ class Pages
|
||||
throw new \RuntimeException('Fatal error when creating page instances.');
|
||||
}
|
||||
|
||||
$last_modified = 0;
|
||||
|
||||
/** @var \DirectoryIterator $file */
|
||||
foreach ($iterator as $file) {
|
||||
$name = $file->getFilename();
|
||||
|
||||
$date = $file->getMTime();
|
||||
if ($date > $last_modified) {
|
||||
$last_modified = $date;
|
||||
}
|
||||
|
||||
if ($file->isFile() && Utils::endsWith($name, CONTENT_EXT)) {
|
||||
|
||||
$page->init($file);
|
||||
@@ -444,8 +465,14 @@ class Pages
|
||||
$this->grav->fireEvent('onFolderProcessed', new Event(['page' => $page]));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Override the modified and ID so that it takes the latest change
|
||||
// into account
|
||||
$page->modified($last_modified);
|
||||
$page->id($last_modified.md5($page->filePath()));
|
||||
|
||||
// Sort based on Defaults or Page Overridden sort order
|
||||
$this->children[$page->path()] = $this->sort($page);
|
||||
|
||||
@@ -532,8 +559,14 @@ class Pages
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by the new list.
|
||||
asort($list);
|
||||
// handle special case when order_by is random
|
||||
if ($order_by == 'random') {
|
||||
$list = $this->array_shuffle($list);
|
||||
} else {
|
||||
// else just sort the list according to specified key
|
||||
asort($list);
|
||||
}
|
||||
|
||||
|
||||
// Move manually ordered items into the beginning of the list. Order of the unlisted items does not change.
|
||||
if (is_array($manual) && !empty($manual)) {
|
||||
@@ -561,4 +594,17 @@ class Pages
|
||||
$this->sort[$path][$order_by][$key] = $info;
|
||||
}
|
||||
}
|
||||
|
||||
// Shuffles and associative array
|
||||
protected function array_shuffle($list) {
|
||||
$keys = array_keys($list);
|
||||
shuffle($keys);
|
||||
|
||||
$new = array();
|
||||
foreach($keys as $key) {
|
||||
$new[$key] = $list[$key];
|
||||
}
|
||||
|
||||
return $new;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,8 +62,10 @@ class Plugins extends Iterator
|
||||
}
|
||||
}
|
||||
|
||||
$instance = $this->grav['themes']->load();
|
||||
$instance->configure();
|
||||
/** @var Themes $themes */
|
||||
$themes = $this->grav['themes'];
|
||||
$themes->configure();
|
||||
$instance = $themes->load();
|
||||
if ($instance instanceof EventSubscriberInterface) {
|
||||
$events->addSubscriber($instance);
|
||||
}
|
||||
@@ -86,7 +88,7 @@ class Plugins extends Iterator
|
||||
static public function all()
|
||||
{
|
||||
$list = array();
|
||||
$iterator = new \DirectoryIterator('plugin://');
|
||||
$iterator = new \DirectoryIterator('plugin:///');
|
||||
|
||||
/** @var \DirectoryIterator $directory */
|
||||
foreach ($iterator as $directory) {
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
<?php
|
||||
namespace Grav\Common;
|
||||
|
||||
use Grav\Common\Config\Config;
|
||||
use Grav\Component\Filesystem\File\Yaml;
|
||||
use Grav\Component\Filesystem\ResourceLocator;
|
||||
|
||||
class Theme extends Plugin
|
||||
{
|
||||
public $name;
|
||||
@@ -22,49 +18,4 @@ class Theme extends Plugin
|
||||
|
||||
parent::__construct($grav, $config);
|
||||
}
|
||||
|
||||
public function configure() {
|
||||
$this->loadConfiguration();
|
||||
|
||||
/** @var ResourceLocator $locator */
|
||||
$locator = $this->grav['locator'];
|
||||
|
||||
// TODO: move
|
||||
$registered = stream_get_wrappers();
|
||||
$schemes = $this->config->get(
|
||||
"themes.{$this->name}.streams.scheme",
|
||||
['theme' => ['paths' => ["user/themes/{$this->name}"]]]
|
||||
);
|
||||
|
||||
foreach ($schemes as $scheme => $config) {
|
||||
if (isset($config['paths'])) {
|
||||
$locator->addPath($scheme, '', $config['paths']);
|
||||
}
|
||||
if (isset($config['prefixes'])) {
|
||||
foreach ($config['prefixes'] as $prefix => $paths) {
|
||||
$locator->addPath($scheme, $prefix, $paths);
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array($scheme, $registered)) {
|
||||
stream_wrapper_unregister($scheme);
|
||||
}
|
||||
$type = !empty($config['type']) ? $config['type'] : 'ReadOnlyStream';
|
||||
if ($type[0] != '\\') {
|
||||
$type = '\\Grav\\Component\\Filesystem\\StreamWrapper\\' . $type;
|
||||
}
|
||||
|
||||
if (!stream_wrapper_register($scheme, $type)) {
|
||||
throw new \InvalidArgumentException("Stream '{$type}' could not be initialized.");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected function loadConfiguration()
|
||||
{
|
||||
$themeConfig = Yaml::instance(THEMES_DIR . "{$this->name}/{$this->name}.yaml")->content();
|
||||
|
||||
$this->config->merge(['themes' => [$this->name => $themeConfig]]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ namespace Grav\Common;
|
||||
use Grav\Component\Data\Blueprints;
|
||||
use Grav\Component\Data\Data;
|
||||
use Grav\Component\Filesystem\File;
|
||||
use Grav\Common\Filesystem\File\Yaml;
|
||||
use Grav\Component\Filesystem\ResourceLocator;
|
||||
|
||||
/**
|
||||
* The Themes object holds an array of all the theme objects that Grav knows about.
|
||||
@@ -61,23 +63,25 @@ class Themes
|
||||
throw new \RuntimeException('Theme name not provided.');
|
||||
}
|
||||
|
||||
$blueprints = new Blueprints("theme://{$name}");
|
||||
$blueprints = new Blueprints("theme:///{$name}");
|
||||
$blueprint = $blueprints->get('blueprints');
|
||||
$blueprint->name = $name;
|
||||
|
||||
/** @var Config $config */
|
||||
$config = $this->grav['config'];
|
||||
|
||||
// Find thumbnail.
|
||||
$thumb = THEMES_DIR . "{$name}/thumbnail.jpg";
|
||||
$thumb = "theme:///{$name}/thumbnail.jpg";
|
||||
if (file_exists($thumb)) {
|
||||
// TODO: use real URL with base path.
|
||||
$blueprint->set('thumbnail', "/user/themes/{$name}/thumbnail.jpg");
|
||||
$blueprint->set('thumbnail', $config->get('system.base_url_relative') . "/user/themes/{$name}/thumbnail.jpg");
|
||||
}
|
||||
|
||||
// Load default configuration.
|
||||
$file = File\Yaml::instance("theme://{$name}.yaml");
|
||||
$file = Yaml::instance("theme:///{$name}/{$name}.yaml");
|
||||
$obj = new Data($file->content(), $blueprint);
|
||||
|
||||
// Override with user configuration.
|
||||
$file = File\Yaml::instance("user://config/themes/{$name}.yaml");
|
||||
$file = Yaml::instance("user://config/themes/{$name}.yaml");
|
||||
$obj->merge($file->content());
|
||||
|
||||
// Save configuration always to user/config.
|
||||
@@ -86,20 +90,31 @@ class Themes
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public function load($name = null)
|
||||
public function current($name = null)
|
||||
{
|
||||
$grav = $this->grav;
|
||||
/** @var Config $config */
|
||||
$config = $grav['config'];
|
||||
$config = $this->grav['config'];
|
||||
|
||||
if (!$name) {
|
||||
$name = $config->get('system.pages.theme');
|
||||
}
|
||||
|
||||
$path = THEMES_DIR . $name;
|
||||
$file = "{$path}/{$name}.php";
|
||||
return $name;
|
||||
}
|
||||
|
||||
if (file_exists($file)) {
|
||||
public function load($name = null)
|
||||
{
|
||||
$name = $this->current($name);
|
||||
$grav = $this->grav;
|
||||
|
||||
/** @var Config $config */
|
||||
$config = $grav['config'];
|
||||
|
||||
/** @var ResourceLocator $locator */
|
||||
$locator = $grav['locator'];
|
||||
|
||||
$file = $locator("theme://theme.php") ?: $locator("theme://{$name}.php");
|
||||
if ($file) {
|
||||
// Local variables available in the file: $grav, $config, $name, $path, $file
|
||||
$class = include $file;
|
||||
|
||||
@@ -118,4 +133,49 @@ class Themes
|
||||
|
||||
return $class;
|
||||
}
|
||||
|
||||
public function configure($name = null) {
|
||||
$name = $this->current($name);
|
||||
|
||||
/** @var Config $config */
|
||||
$config = $this->grav['config'];
|
||||
|
||||
$themeConfig = Yaml::instance(THEMES_DIR . "{$name}/{$name}.yaml")->content();
|
||||
|
||||
$config->merge(['themes' => [$name => $themeConfig]]);
|
||||
|
||||
/** @var ResourceLocator $locator */
|
||||
$locator = $this->grav['locator'];
|
||||
|
||||
// TODO: move
|
||||
$registered = stream_get_wrappers();
|
||||
$schemes = $config->get(
|
||||
"themes.{$name}.streams.schemes",
|
||||
['theme' => ['paths' => ["user/themes/{$name}"]]]
|
||||
);
|
||||
|
||||
foreach ($schemes as $scheme => $config) {
|
||||
if (isset($config['paths'])) {
|
||||
$locator->addPath($scheme, '', $config['paths']);
|
||||
}
|
||||
if (isset($config['prefixes'])) {
|
||||
foreach ($config['prefixes'] as $prefix => $paths) {
|
||||
$locator->addPath($scheme, $prefix, $paths);
|
||||
}
|
||||
}
|
||||
|
||||
if (in_array($scheme, $registered)) {
|
||||
stream_wrapper_unregister($scheme);
|
||||
}
|
||||
$type = !empty($config['type']) ? $config['type'] : 'ReadOnlyStream';
|
||||
if ($type[0] != '\\') {
|
||||
$type = '\\Grav\\Component\\Filesystem\\StreamWrapper\\' . $type;
|
||||
}
|
||||
|
||||
if (!stream_wrapper_register($scheme, $type)) {
|
||||
throw new \InvalidArgumentException("Stream '{$type}' could not be initialized.");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
namespace Grav\Common;
|
||||
|
||||
use \Grav\Common\Page\Page;
|
||||
use Grav\Component\Filesystem\ResourceLocator;
|
||||
|
||||
/**
|
||||
* The Twig object handles all the Twig template rendering for Grav. It's a singleton object
|
||||
@@ -66,8 +67,10 @@ class Twig
|
||||
if (!isset($this->twig)) {
|
||||
/** @var Config $config */
|
||||
$config = $this->grav['config'];
|
||||
/** @var ResourceLocator $locator */
|
||||
$locator = $this->grav['locator'];
|
||||
|
||||
$this->twig_paths = array(THEMES_DIR . $config->get('system.pages.theme') . '/templates');
|
||||
$this->twig_paths = $locator->findResources('theme://templates');
|
||||
$this->grav->fireEvent('onTwigTemplatePaths');
|
||||
|
||||
$this->loader = new \Twig_Loader_Filesystem($this->twig_paths);
|
||||
@@ -76,7 +79,7 @@ class Twig
|
||||
|
||||
$params = $config->get('system.twig');
|
||||
if (!empty($params['cache'])) {
|
||||
$params['cache'] = CACHE_DIR;
|
||||
$params['cache'] = $locator->findResource('cache://');
|
||||
}
|
||||
|
||||
$this->twig = new \Twig_Environment($loader_chain, $params);
|
||||
@@ -106,12 +109,12 @@ class Twig
|
||||
'base_dir' => rtrim(ROOT_DIR, '/'),
|
||||
'base_url_absolute' => $baseUrlAbsolute,
|
||||
'base_url_relative' => $baseUrlRelative,
|
||||
'theme_dir' => THEMES_DIR . $theme,
|
||||
'theme_dir' => $locator->findResource('theme://'),
|
||||
'theme_url' => $themeUrl,
|
||||
'site' => $config->get('site'),
|
||||
'assets' => $this->grav['assets'],
|
||||
'taxonomy' => $this->grav['taxonomy'],
|
||||
'user_agent' => $this->grav['user_agent'],
|
||||
'browser' => $this->grav['browser'],
|
||||
);
|
||||
|
||||
}
|
||||
@@ -181,24 +184,6 @@ class Twig
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $string string to render.
|
||||
* @param array $vars Optional variables
|
||||
* @return string
|
||||
*/
|
||||
public function processString($string, array $vars = array())
|
||||
{
|
||||
// override the twig header vars for local resolution
|
||||
$this->grav->fireEvent('onTwigStringVariables');
|
||||
$vars += $this->twig_vars;
|
||||
|
||||
$name = '@Var:' . $string;
|
||||
$this->setTemplate($name, $string);
|
||||
$output = $this->twig->render($name, $vars);
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Twig process that renders the site layout. This is the main twig process that renders the overall
|
||||
* page and handles all the layout for the site display.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
namespace Grav\Common;
|
||||
use Grav\Component\Filesystem\ResourceLocator;
|
||||
|
||||
/**
|
||||
* The Twig extension adds some filters and functions that are useful for Grav
|
||||
@@ -44,7 +45,8 @@ class TwigExtension extends \Twig_Extension
|
||||
public function getFunctions()
|
||||
{
|
||||
return array(
|
||||
new \Twig_SimpleFunction('repeat', array($this, 'repeatFunc'))
|
||||
new \Twig_SimpleFunction('repeat', array($this, 'repeatFunc')),
|
||||
new \Twig_SimpleFunction('url', array($this, 'urlFunc'))
|
||||
);
|
||||
}
|
||||
|
||||
@@ -195,4 +197,22 @@ class TwigExtension extends \Twig_Extension
|
||||
{
|
||||
return str_repeat($input, $multiplier);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return URL to the resource.
|
||||
*
|
||||
* @param string $input
|
||||
* @param bool $domain
|
||||
* @return string
|
||||
*/
|
||||
public function urlFunc($input, $domain = false)
|
||||
{
|
||||
$grav = Grav::instance();
|
||||
/** @var ResourceLocator $locator */
|
||||
$locator = $grav['locator'];
|
||||
/** @var Uri $uri */
|
||||
$uri = $grav['uri'];
|
||||
|
||||
return $uri->rootUrl($domain) . $locator->findResource($input, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ class Uri
|
||||
|
||||
// remove the extension if there is one set
|
||||
$parts = pathinfo($uri);
|
||||
if (strpos($parts['basename'], '.')) {
|
||||
if (preg_match("/\.(txt|xml|html|json|rss|atom)$/", $parts['basename'])) {
|
||||
$uri = rtrim($parts['dirname'], '/').'/'.$parts['filename'];
|
||||
$this->extension = $parts['extension'];
|
||||
}
|
||||
|
||||
@@ -11,6 +11,8 @@ use Grav\Component\Data\Data;
|
||||
*/
|
||||
class User extends Data
|
||||
{
|
||||
protected $password;
|
||||
|
||||
/**
|
||||
* Authenticate user.
|
||||
*
|
||||
|
||||
@@ -109,7 +109,7 @@ class Markdown extends General
|
||||
$var = preg_replace("/(\r\n|\r)/", "\n", $var);
|
||||
|
||||
// Parse header.
|
||||
preg_match("/---\n(.+?)\n---(\n\n|$)/uism", $this->raw(), $m);
|
||||
preg_match("/---\n(.+?)\n---(\n\n|$)/uism", $var, $m);
|
||||
$content['header'] = isset($m[1]) ? YamlParser::parse(preg_replace("/\n\t/", "\n ", $m[1])) : array();
|
||||
|
||||
// Strip header to get content.
|
||||
|
||||
@@ -15,13 +15,13 @@ abstract class Folder
|
||||
* @param string $path
|
||||
* @return int
|
||||
*/
|
||||
public static function lastModified($path)
|
||||
public static function lastModifiedFolder($path)
|
||||
{
|
||||
$last_modified = 0;
|
||||
|
||||
$directory = new \RecursiveDirectoryIterator($path, \RecursiveDirectoryIterator::SKIP_DOTS);
|
||||
$iterator = new \RecursiveIteratorIterator($directory, \RecursiveIteratorIterator::SELF_FIRST);
|
||||
|
||||
$last_modified = 0;
|
||||
|
||||
/** @var \RecursiveDirectoryIterator $file */
|
||||
foreach ($iterator as $file) {
|
||||
$dir_modified = $file->getMTime();
|
||||
@@ -43,6 +43,34 @@ abstract class Folder
|
||||
|
||||
return $to;
|
||||
}
|
||||
/**
|
||||
* Recursively find the last modified time under given path by file.
|
||||
*
|
||||
* @param string $path
|
||||
* @return int
|
||||
*/
|
||||
public static function lastModifiedFile($path)
|
||||
{
|
||||
$last_modified = 0;
|
||||
|
||||
$dirItr = new \RecursiveDirectoryIterator($path);
|
||||
$filterItr = new GravRecursiveFilterIterator($dirItr);
|
||||
$itr = new \RecursiveIteratorIterator($filterItr, \RecursiveIteratorIterator::SELF_FIRST);
|
||||
|
||||
/** @var \RecursiveDirectoryIterator $file */
|
||||
foreach ($itr as $file) {
|
||||
if (!$file->isDir()) {
|
||||
$file_modified = $file->getMTime();
|
||||
if ($file_modified > $last_modified) {
|
||||
$last_modified = $file_modified;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return $last_modified;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return recursive list of all files and directories under given path.
|
||||
*
|
||||
@@ -237,3 +265,19 @@ abstract class Folder
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class GravRecursiveFilterIterator extends \RecursiveFilterIterator {
|
||||
|
||||
public static $FILTERS = array(
|
||||
'.', '..', '.DS_Store'
|
||||
);
|
||||
|
||||
public function accept() {
|
||||
return !in_array(
|
||||
$this->current()->getFilename(),
|
||||
self::$FILTERS,
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -87,13 +87,14 @@ class ResourceLocator
|
||||
$scheme = 'file';
|
||||
}
|
||||
|
||||
if (!$file || $uri[0] == ':') {
|
||||
throw new \InvalidArgumentException('Invalid resource URI');
|
||||
}
|
||||
if (!isset($this->schemes[$scheme])) {
|
||||
throw new \InvalidArgumentException("Invalid resource {$scheme}://");
|
||||
}
|
||||
|
||||
if (!$file && $scheme == 'file') {
|
||||
$file = getcwd();
|
||||
}
|
||||
|
||||
return [$file, $scheme];
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,13 @@ class CleanCommand extends Command {
|
||||
'user/plugins/email/vendor/swiftmailer/swiftmailer/notes',
|
||||
'user/plugins/email/vendor/swiftmailer/swiftmailer/doc',
|
||||
'user/themes/antimatter/.sass-cache',
|
||||
'vendor/donatj/phpuseragentparser/.git',
|
||||
'vendor/donatj/phpuseragentparser/.gitignore',
|
||||
'vendor/donatj/phpuseragentparser/.travis.yml',
|
||||
'vendor/donatj/phpuseragentparser/composer.json',
|
||||
'vendor/donatj/phpuseragentparser/phpunit.xml.dist',
|
||||
'vendor/donatj/phpuseragentparser/Tests',
|
||||
'vendor/donatj/phpuseragentparser/Tools',
|
||||
'vendor/doctrine/cache/.travis.yml',
|
||||
'vendor/doctrine/cache/build.properties',
|
||||
'vendor/doctrine/cache/build.xml',
|
||||
@@ -74,11 +81,6 @@ class CleanCommand extends Command {
|
||||
'vendor/mrclay/minify/min/quick-test.css',
|
||||
'vendor/mrclay/minify/min/quick-test.js',
|
||||
'vendor/mrclay/minify/min/utils.php',
|
||||
'vendor/ornicar/php-user-agent/.git',
|
||||
'vendor/ornicar/php-user-agent/.gitignore',
|
||||
'vendor/ornicar/php-user-agent/composer.json',
|
||||
'vendor/ornicar/php-user-agent/prove.php',
|
||||
'vendor/ornicar/php-user-agent/test',
|
||||
'vendor/pimple/pimple/.gitignore',
|
||||
'vendor/pimple/pimple/.travis.yml',
|
||||
'vendor/pimple/pimple/composer.json',
|
||||
|
||||
@@ -3,18 +3,15 @@ home:
|
||||
|
||||
pages:
|
||||
theme: antimatter
|
||||
markdown_extra: true
|
||||
markdown_extra: false
|
||||
process:
|
||||
markdown: true
|
||||
twig: false
|
||||
events:
|
||||
page: false
|
||||
twig: true
|
||||
|
||||
cache:
|
||||
enabled: true
|
||||
check:
|
||||
pages: true
|
||||
method: file
|
||||
driver: auto
|
||||
prefix: 'g'
|
||||
|
||||
@@ -25,7 +22,7 @@ twig:
|
||||
autoescape: false
|
||||
|
||||
assets:
|
||||
css_pipeline: false
|
||||
css_pipeline: true
|
||||
css_minify: true
|
||||
css_rewrite: true
|
||||
js_pipeline: false
|
||||
|
||||
5
user/pages/02.test/default.md
Normal file
5
user/pages/02.test/default.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
title: Test
|
||||
---
|
||||
|
||||
# Testing
|
||||
@@ -1,6 +1,7 @@
|
||||
The MIT License
|
||||
===============
|
||||
|
||||
Copyright (c) 2010 Thibault Duplessis
|
||||
Copyright (c) 2013 Jesse G. Donat
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -18,4 +19,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
THE SOFTWARE.
|
||||
110
vendor/donatj/phpuseragentparser/README.md
vendored
Normal file
110
vendor/donatj/phpuseragentparser/README.md
vendored
Normal file
@@ -0,0 +1,110 @@
|
||||
# PHP User Agent Parser
|
||||
|
||||
[](https://packagist.org/packages/donatj/phpuseragentparser) [](https://packagist.org/packages/donatj/phpuseragentparser) [](https://packagist.org/packages/donatj/phpuseragentparser) [](https://packagist.org/packages/donatj/phpuseragentparser)
|
||||
[](https://travis-ci.org/donatj/PhpUserAgent)
|
||||
[](http://hhvm.h4cc.de/package/donatj/phpuseragentparser) [](https://scrutinizer-ci.com/g/donatj/PhpUserAgent/?branch=master)
|
||||
|
||||
## What It Is
|
||||
|
||||
A simple, streamlined PHP user-agent parser!
|
||||
|
||||
Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php
|
||||
|
||||
|
||||
## Why Use This
|
||||
|
||||
You have your choice in user-agent parsers. This one detects **all modern browsers** in a very light, quick, understandable fashion.
|
||||
It is less than 150 lines of code, and consists of just two regular expressions!
|
||||
It can also correctly identify exotic versions of IE others fail on.
|
||||
|
||||
It offers 100% unit test coverage, is installable via Composer, and is very easy to use.
|
||||
|
||||
## What It Doesn't Do
|
||||
|
||||
### OS Versions
|
||||
|
||||
User-agent strings **are not** a reliable source of OS Version!
|
||||
|
||||
- Many agents simply don't send the information.
|
||||
- Others provide varying levels of accuracy.
|
||||
- Parsing Windows versions alone almost nearly doubles the size of the code.
|
||||
|
||||
I'm much more interested in keeping this thing *tiny* and accurate than adding niché features and would rather focus on things that can be **done well**.
|
||||
|
||||
All that said, there is the start of a [branch to do it](https://github.com/donatj/PhpUserAgent/tree/os_version_detection) I created for a client if you want to poke it, I update it from time to time, but frankly if you need to *reliably detect OS Version*, using user-agent isn't the way to do it. I'd go with JavaScript.
|
||||
|
||||
## Requirements
|
||||
|
||||
- PHP 5.3.0+
|
||||
|
||||
## Installing
|
||||
|
||||
PHP User Agent is available through Packagist via Composer.
|
||||
|
||||
```json
|
||||
{
|
||||
"require": {
|
||||
"donatj/phpuseragentparser": "*"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Sample Usage
|
||||
|
||||
```php
|
||||
$ua_info = parse_user_agent();
|
||||
/*
|
||||
array(
|
||||
'platform' => '[Detected Platform]',
|
||||
'browser' => '[Detected Browser]',
|
||||
'version' => '[Detected Browser Version]',
|
||||
);
|
||||
*/
|
||||
```
|
||||
|
||||
## Currently Detected Platforms
|
||||
|
||||
- Desktop
|
||||
- Windows
|
||||
- Linux
|
||||
- Macintosh
|
||||
- Chrome OS
|
||||
- Mobile
|
||||
- Android
|
||||
- iPhone
|
||||
- iPad
|
||||
- Windows Phone OS
|
||||
- Kindle
|
||||
- Kindle Fire
|
||||
- BlackBerry
|
||||
- Playbook
|
||||
- Console
|
||||
- Nintendo 3DS
|
||||
- Nintendo Wii
|
||||
- Nintendo WiiU
|
||||
- PlayStation 3
|
||||
- PlayStation 4
|
||||
- PlayStation Vita
|
||||
- Xbox 360
|
||||
- Xbox One
|
||||
|
||||
## Currently Detected Browsers
|
||||
|
||||
- Android Browser
|
||||
- BlackBerry Browser
|
||||
- Camino
|
||||
- Kindle / Silk
|
||||
- Firefox / Iceweasel
|
||||
- Safari
|
||||
- Internet Explorer
|
||||
- IEMobile
|
||||
- Chrome
|
||||
- Opera
|
||||
- Midori
|
||||
- Lynx
|
||||
- Wget
|
||||
- Curl
|
||||
|
||||
|
||||
|
||||
More information is available at [Donat Studios](http://donatstudios.com/PHP-Parser-HTTP_USER_AGENT).
|
||||
144
vendor/donatj/phpuseragentparser/Source/UserAgentParser.php
vendored
Normal file
144
vendor/donatj/phpuseragentparser/Source/UserAgentParser.php
vendored
Normal file
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Parses a user agent string into its important parts
|
||||
*
|
||||
* @author Jesse G. Donat <donatj@gmail.com>
|
||||
* @link https://github.com/donatj/PhpUserAgent
|
||||
* @link http://donatstudios.com/PHP-Parser-HTTP_USER_AGENT
|
||||
* @param string|null $u_agent User agent string to parse or null. Uses $_SERVER['HTTP_USER_AGENT'] on NULL
|
||||
* @throws InvalidArgumentException on not having a proper user agent to parse.
|
||||
* @return array an array with browser, version and platform keys
|
||||
*/
|
||||
function parse_user_agent( $u_agent = null ) {
|
||||
if( is_null($u_agent) ) {
|
||||
if( isset($_SERVER['HTTP_USER_AGENT']) ) {
|
||||
$u_agent = $_SERVER['HTTP_USER_AGENT'];
|
||||
} else {
|
||||
throw new \InvalidArgumentException('parse_user_agent requires a user agent');
|
||||
}
|
||||
}
|
||||
|
||||
$platform = null;
|
||||
$browser = null;
|
||||
$version = null;
|
||||
|
||||
$empty = array( 'platform' => $platform, 'browser' => $browser, 'version' => $version );
|
||||
|
||||
if( !$u_agent ) return $empty;
|
||||
|
||||
if( preg_match('/\((.*?)\)/im', $u_agent, $parent_matches) ) {
|
||||
|
||||
preg_match_all('/(?P<platform>BB\d+;|Android|CrOS|iPhone|iPad|Linux|Macintosh|Windows(\ Phone)?|Silk|linux-gnu|BlackBerry|PlayBook|Nintendo\ (WiiU?|3DS)|Xbox(\ One)?)
|
||||
(?:\ [^;]*)?
|
||||
(?:;|$)/imx', $parent_matches[1], $result, PREG_PATTERN_ORDER);
|
||||
|
||||
$priority = array( 'Android', 'Xbox One', 'Xbox' );
|
||||
$result['platform'] = array_unique($result['platform']);
|
||||
if( count($result['platform']) > 1 ) {
|
||||
if( $keys = array_intersect($priority, $result['platform']) ) {
|
||||
$platform = reset($keys);
|
||||
} else {
|
||||
$platform = $result['platform'][0];
|
||||
}
|
||||
} elseif( isset($result['platform'][0]) ) {
|
||||
$platform = $result['platform'][0];
|
||||
}
|
||||
}
|
||||
|
||||
if( $platform == 'linux-gnu' ) {
|
||||
$platform = 'Linux';
|
||||
} elseif( $platform == 'CrOS' ) {
|
||||
$platform = 'Chrome OS';
|
||||
}
|
||||
|
||||
preg_match_all('%(?P<browser>Camino|Kindle(\ Fire\ Build)?|Firefox|Iceweasel|Safari|MSIE|Trident/.*rv|AppleWebKit|Chrome|IEMobile|Opera|OPR|Silk|Lynx|Midori|Version|Wget|curl|NintendoBrowser|PLAYSTATION\ (\d|Vita)+)
|
||||
(?:\)?;?)
|
||||
(?:(?:[:/ ])(?P<version>[0-9A-Z.]+)|/(?:[A-Z]*))%ix',
|
||||
$u_agent, $result, PREG_PATTERN_ORDER);
|
||||
|
||||
|
||||
// If nothing matched, return null (to avoid undefined index errors)
|
||||
if( !isset($result['browser'][0]) || !isset($result['version'][0]) ) {
|
||||
return $empty;
|
||||
}
|
||||
|
||||
$browser = $result['browser'][0];
|
||||
$version = $result['version'][0];
|
||||
|
||||
$find = function ( $search, &$key ) use ( $result ) {
|
||||
$xkey = array_search(strtolower($search), array_map('strtolower', $result['browser']));
|
||||
if( $xkey !== false ) {
|
||||
$key = $xkey;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
$key = 0;
|
||||
if( $browser == 'Iceweasel' ) {
|
||||
$browser = 'Firefox';
|
||||
} elseif( $find('Playstation Vita', $key) ) {
|
||||
$platform = 'PlayStation Vita';
|
||||
$browser = 'Browser';
|
||||
} elseif( $find('Kindle Fire Build', $key) || $find('Silk', $key) ) {
|
||||
$browser = $result['browser'][$key] == 'Silk' ? 'Silk' : 'Kindle';
|
||||
$platform = 'Kindle Fire';
|
||||
if( !($version = $result['version'][$key]) || !is_numeric($version[0]) ) {
|
||||
$version = $result['version'][array_search('Version', $result['browser'])];
|
||||
}
|
||||
} elseif( $find('NintendoBrowser', $key) || $platform == 'Nintendo 3DS' ) {
|
||||
$browser = 'NintendoBrowser';
|
||||
$version = $result['version'][$key];
|
||||
} elseif( $find('Kindle', $key) ) {
|
||||
$browser = $result['browser'][$key];
|
||||
$platform = 'Kindle';
|
||||
$version = $result['version'][$key];
|
||||
} elseif( $find('OPR', $key) ) {
|
||||
$browser = 'Opera Next';
|
||||
$version = $result['version'][$key];
|
||||
} elseif( $find('Opera', $key) ) {
|
||||
$browser = 'Opera';
|
||||
$find('Version', $key);
|
||||
$version = $result['version'][$key];
|
||||
} elseif( $find('Midori', $key) ) {
|
||||
$browser = 'Midori';
|
||||
$version = $result['version'][$key];
|
||||
} elseif( $browser == 'MSIE' || strpos($browser, 'Trident') !== false ) {
|
||||
if( $find('IEMobile', $key) ) {
|
||||
$browser = 'IEMobile';
|
||||
} else {
|
||||
$browser = 'MSIE';
|
||||
$key = 0;
|
||||
}
|
||||
$version = $result['version'][$key];
|
||||
} elseif( $find('Chrome', $key) ) {
|
||||
$browser = 'Chrome';
|
||||
$version = $result['version'][$key];
|
||||
} elseif( $browser == 'AppleWebKit' ) {
|
||||
if( ($platform == 'Android' && !($key = 0)) ) {
|
||||
$browser = 'Android Browser';
|
||||
} elseif( strpos($platform, 'BB') === 0 ) {
|
||||
$browser = 'BlackBerry Browser';
|
||||
$platform = 'BlackBerry';
|
||||
} elseif( $platform == 'BlackBerry' || $platform == 'PlayBook' ) {
|
||||
$browser = 'BlackBerry Browser';
|
||||
} elseif( $find('Safari', $key) ) {
|
||||
$browser = 'Safari';
|
||||
}
|
||||
|
||||
$find('Version', $key);
|
||||
|
||||
$version = $result['version'][$key];
|
||||
} elseif( $key = preg_grep('/playstation \d/i', array_map('strtolower', $result['browser'])) ) {
|
||||
$key = reset($key);
|
||||
|
||||
$platform = 'PlayStation ' . preg_replace('/[^\d]/i', '', $key);
|
||||
$browser = 'NetFront';
|
||||
}
|
||||
|
||||
return array( 'platform' => $platform, 'browser' => $browser, 'version' => $version );
|
||||
|
||||
}
|
||||
3
vendor/mrclay/minify/HISTORY.txt
vendored
3
vendor/mrclay/minify/HISTORY.txt
vendored
@@ -1,5 +1,8 @@
|
||||
Minify Release History
|
||||
|
||||
(master)
|
||||
* Builder styled with Bootstrap (thanks to help from acidvertigo)
|
||||
|
||||
Version 2.2.0
|
||||
* Fix handling of RegEx in certain situations in JSMin
|
||||
* Thanks to Vovan-VE for reporting this
|
||||
|
||||
6
vendor/mrclay/minify/min/lib/CSSmin.php
vendored
6
vendor/mrclay/minify/min/lib/CSSmin.php
vendored
@@ -325,6 +325,10 @@ class CSSmin
|
||||
// @media screen and (-webkit-min-device-pixel-ratio:0){
|
||||
$css = preg_replace('/\band\(/i', 'and (', $css);
|
||||
|
||||
// Put the space back in for @support tag
|
||||
// @supports (display: flex) and @supports not (display: flex)
|
||||
$css = preg_replace('/\b(supports|not)\(/i', '$1 (', $css);
|
||||
|
||||
// Remove the spaces after the things that should not have spaces after them.
|
||||
$css = preg_replace('/([\!\{\}\:;\>\+\(\[\~\=,])\s+/S', '$1', $css);
|
||||
|
||||
@@ -772,4 +776,4 @@ class CSSmin
|
||||
|
||||
return (int) $size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ class Minify_CSS_UriRewriter {
|
||||
// rewrite
|
||||
$css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/'
|
||||
,array(self::$className, '_processUriCB'), $css);
|
||||
$css = preg_replace_callback('/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
|
||||
$css = preg_replace_callback('/url\\(\\s*([\'"](.*?)[\'"]|[^\\)\\s]+)\\s*\\)/'
|
||||
,array(self::$className, '_processUriCB'), $css);
|
||||
|
||||
return $css;
|
||||
@@ -94,7 +94,7 @@ class Minify_CSS_UriRewriter {
|
||||
// append
|
||||
$css = preg_replace_callback('/@import\\s+([\'"])(.*?)[\'"]/'
|
||||
,array(self::$className, '_processUriCB'), $css);
|
||||
$css = preg_replace_callback('/url\\(\\s*([^\\)\\s]+)\\s*\\)/'
|
||||
$css = preg_replace_callback('/url\\(\\s*([\'"](.*?)[\'"]|[^\\)\\s]+)\\s*\\)/'
|
||||
,array(self::$className, '_processUriCB'), $css);
|
||||
|
||||
self::$_prependPath = null;
|
||||
|
||||
@@ -98,6 +98,9 @@ class Minify_Cache_File {
|
||||
{
|
||||
if ($this->_locking) {
|
||||
$fp = fopen($this->_path . '/' . $id, 'rb');
|
||||
if (!$fp) {
|
||||
return false;
|
||||
}
|
||||
flock($fp, LOCK_SH);
|
||||
$ret = stream_get_contents($fp);
|
||||
flock($fp, LOCK_UN);
|
||||
|
||||
@@ -33,6 +33,11 @@
|
||||
*/
|
||||
class Minify_ClosureCompiler {
|
||||
|
||||
const OPTION_CHARSET = 'charset';
|
||||
const OPTION_COMPILATION_LEVEL = 'compilation_level';
|
||||
|
||||
public static $isDebug = false;
|
||||
|
||||
/**
|
||||
* Filepath of the Closure Compiler jar file. This must be set before
|
||||
* calling minifyJs().
|
||||
@@ -65,18 +70,28 @@ class Minify_ClosureCompiler {
|
||||
* @see https://code.google.com/p/closure-compiler/source/browse/trunk/README
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @throws Minify_ClosureCompiler_Exception
|
||||
*/
|
||||
public static function minify($js, $options = array())
|
||||
{
|
||||
self::_prepare();
|
||||
if (! ($tmpFile = tempnam(self::$tempDir, 'cc_'))) {
|
||||
throw new Exception('Minify_ClosureCompiler : could not create temp file in "'.self::$tempDir.'".');
|
||||
throw new Minify_ClosureCompiler_Exception('Minify_ClosureCompiler : could not create temp file in "'.self::$tempDir.'".');
|
||||
}
|
||||
file_put_contents($tmpFile, $js);
|
||||
exec(self::_getCmd($options, $tmpFile), $output, $result_code);
|
||||
$cmd = self::_getCmd($options, $tmpFile);
|
||||
exec($cmd, $output, $result_code);
|
||||
unlink($tmpFile);
|
||||
if ($result_code != 0) {
|
||||
throw new Exception('Minify_ClosureCompiler : Closure Compiler execution failed.');
|
||||
$message = 'Minify_ClosureCompiler : Closure Compiler execution failed.';
|
||||
if (self::$isDebug) {
|
||||
exec($cmd . ' 2>&1', $error);
|
||||
if ($error) {
|
||||
$message .= "\nReason:\n" . join("\n", $error);
|
||||
}
|
||||
}
|
||||
throw new Minify_ClosureCompiler_Exception($message);
|
||||
}
|
||||
return implode("\n", $output);
|
||||
}
|
||||
@@ -85,17 +100,18 @@ class Minify_ClosureCompiler {
|
||||
{
|
||||
$o = array_merge(
|
||||
array(
|
||||
'charset' => 'utf-8',
|
||||
'compilation_level' => 'SIMPLE_OPTIMIZATIONS',
|
||||
self::OPTION_CHARSET => 'utf-8',
|
||||
self::OPTION_COMPILATION_LEVEL => 'SIMPLE_OPTIMIZATIONS',
|
||||
),
|
||||
$userOptions
|
||||
);
|
||||
$charsetOption = $o[self::OPTION_CHARSET];
|
||||
$cmd = self::$javaExecutable . ' -jar ' . escapeshellarg(self::$jarFile)
|
||||
. (preg_match('/^[\\da-zA-Z0-9\\-]+$/', $o['charset'])
|
||||
? " --charset {$o['charset']}"
|
||||
. (preg_match('/^[\\da-zA-Z0-9\\-]+$/', $charsetOption)
|
||||
? " --charset {$charsetOption}"
|
||||
: '');
|
||||
|
||||
foreach (array('compilation_level') as $opt) {
|
||||
foreach (array(self::OPTION_COMPILATION_LEVEL) as $opt) {
|
||||
if ($o[$opt]) {
|
||||
$cmd .= " --{$opt} ". escapeshellarg($o[$opt]);
|
||||
}
|
||||
@@ -106,18 +122,18 @@ class Minify_ClosureCompiler {
|
||||
private static function _prepare()
|
||||
{
|
||||
if (! is_file(self::$jarFile)) {
|
||||
throw new Exception('Minify_ClosureCompiler : $jarFile('.self::$jarFile.') is not a valid file.');
|
||||
throw new Minify_ClosureCompiler_Exception('Minify_ClosureCompiler : $jarFile('.self::$jarFile.') is not a valid file.');
|
||||
}
|
||||
if (! is_readable(self::$jarFile)) {
|
||||
throw new Exception('Minify_ClosureCompiler : $jarFile('.self::$jarFile.') is not readable.');
|
||||
throw new Minify_ClosureCompiler_Exception('Minify_ClosureCompiler : $jarFile('.self::$jarFile.') is not readable.');
|
||||
}
|
||||
if (! is_dir(self::$tempDir)) {
|
||||
throw new Exception('Minify_ClosureCompiler : $tempDir('.self::$tempDir.') is not a valid direcotry.');
|
||||
throw new Minify_ClosureCompiler_Exception('Minify_ClosureCompiler : $tempDir('.self::$tempDir.') is not a valid direcotry.');
|
||||
}
|
||||
if (! is_writable(self::$tempDir)) {
|
||||
throw new Exception('Minify_ClosureCompiler : $tempDir('.self::$tempDir.') is not writable.');
|
||||
throw new Minify_ClosureCompiler_Exception('Minify_ClosureCompiler : $tempDir('.self::$tempDir.') is not writable.');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* vim:ts=4:sw=4:et */
|
||||
class Minify_ClosureCompiler_Exception extends Exception {}
|
||||
|
||||
9
vendor/ornicar/php-user-agent/CHANGELOG.md
vendored
9
vendor/ornicar/php-user-agent/CHANGELOG.md
vendored
@@ -1,9 +0,0 @@
|
||||
# CHANGELOG
|
||||
|
||||
### 1.0.0 (2013-08-09)
|
||||
|
||||
* Add a version following semver spec.
|
||||
|
||||
### v1.0 - 2010-04-13
|
||||
|
||||
* create initial 1.0 version
|
||||
92
vendor/ornicar/php-user-agent/README.md
vendored
92
vendor/ornicar/php-user-agent/README.md
vendored
@@ -1,92 +0,0 @@
|
||||
# PHP User Agent
|
||||
|
||||
Browser detection in PHP5.
|
||||
Uses a simple and fast algorithm to recognize major browsers.
|
||||
|
||||
## Overview
|
||||
|
||||
``` php
|
||||
$userAgent = new phpUserAgent();
|
||||
|
||||
$userAgent->getBrowserName() // firefox
|
||||
$userAgent->getBrowserVersion() // 3.6
|
||||
$userAgent->getOperatingSystem() // linux
|
||||
$userAgent->getEngine() // gecko
|
||||
```
|
||||
|
||||
### Why you should use it
|
||||
|
||||
PHP provides a native function to detect user browser: [get_browser()](http://us2.php.net/manual/en/function.get-browser.php).
|
||||
get_browser() requires the "browscap.ini" file which is 300KB+.
|
||||
Loading and processing this file impact script performance.
|
||||
And sometimes, the production server just doesn't provide browscap.ini.
|
||||
|
||||
Although get_browser() surely provides excellent detection results, in most
|
||||
cases a much simpler method can be just as effective.
|
||||
php-user-agent has the advantage of being compact and easy to extend.
|
||||
It is performant as well, since it doesn't do any iteration or recursion.
|
||||
|
||||
## Usage
|
||||
|
||||
``` php
|
||||
// include classes or rely on Composer autoloader
|
||||
require_once '/path/to/php-user-agent/phpUserAgent.php';
|
||||
require_once '/path/to/php-user-agent/phpUserAgentStringParser.php';
|
||||
|
||||
// Create a user agent
|
||||
$userAgent = new phpUserAgent();
|
||||
|
||||
// Interrogate the user agent
|
||||
$userAgent->getBrowserName() // firefox
|
||||
$userAgent->getBrowserVersion() // 3.6
|
||||
$userAgent->getOperatingSystem() // linux
|
||||
$userAgent->getEngine() // gecko
|
||||
```
|
||||
|
||||
## Advanced
|
||||
|
||||
### Custom user agent string
|
||||
|
||||
When you create a phpUserAgent object, the current user agent string is used.
|
||||
You can specify another user agent string:
|
||||
|
||||
``` php
|
||||
// use another user agent string
|
||||
$userAgent = new phpUserAgent('msnbot/2.0b (+http://search.msn.com/msnbot.htm)');
|
||||
$userAgent->getBrowserName() // msnbot
|
||||
|
||||
// use current user agent string
|
||||
$userAgent = new phpUserAgent($_SERVER['HTTP_USER_AGENT');
|
||||
// this is equivalent to:
|
||||
$userAgent = new phpUserAgent();
|
||||
```
|
||||
|
||||
### Custom parser class
|
||||
|
||||
By default, phpUserAgentStringParser is used to analyse the user agent string.
|
||||
You can replace the parser instance and customize it to match your needs:
|
||||
|
||||
``` php
|
||||
// create a custom user agent string parser
|
||||
class myUserAgentStringParser extends phpUserAgentStringParser
|
||||
{
|
||||
// override methods
|
||||
}
|
||||
|
||||
// inject the custom parser when creating a user agent:
|
||||
$userAgent = new phpUserAgent(null, new myUserAgentStringParser());
|
||||
```
|
||||
|
||||
## Run tests
|
||||
|
||||
You can run the unit tests on your server:
|
||||
|
||||
``` bash
|
||||
$ php prove.php
|
||||
```
|
||||
|
||||
## Contribute
|
||||
|
||||
If you found a browser of operating system this library fails to recognize,
|
||||
feel free to submit an issue. Please provide the user agent string.
|
||||
And well, if you also want to provide the patch, it's even better.
|
||||
194
vendor/ornicar/php-user-agent/lib/phpUserAgent.php
vendored
194
vendor/ornicar/php-user-agent/lib/phpUserAgent.php
vendored
@@ -1,194 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Simple PHP User agent
|
||||
*
|
||||
* @link http://github.com/ornicar/php-user-agent
|
||||
* @version 1.0
|
||||
* @author Thibault Duplessis <thibault.duplessis at gmail dot com>
|
||||
* @license MIT License
|
||||
*
|
||||
* Documentation: http://github.com/ornicar/php-user-agent/blob/master/README.markdown
|
||||
* Tickets: http://github.com/ornicar/php-user-agent/issues
|
||||
*/
|
||||
|
||||
class phpUserAgent
|
||||
{
|
||||
protected $userAgentString;
|
||||
protected $browserName;
|
||||
protected $browserVersion;
|
||||
protected $operatingSystem;
|
||||
protected $engine;
|
||||
|
||||
public function __construct($userAgentString = null, phpUserAgentStringParser $userAgentStringParser = null)
|
||||
{
|
||||
$this->configureFromUserAgentString($userAgentString, $userAgentStringParser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the browser name
|
||||
*
|
||||
* @return string the browser name
|
||||
*/
|
||||
public function getBrowserName()
|
||||
{
|
||||
return $this->browserName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the browser name
|
||||
*
|
||||
* @param string $name the browser name
|
||||
*/
|
||||
public function setBrowserName($name)
|
||||
{
|
||||
$this->browserName = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the browser version
|
||||
*
|
||||
* @return string the browser version
|
||||
*/
|
||||
public function getBrowserVersion()
|
||||
{
|
||||
return $this->browserVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the browser version
|
||||
*
|
||||
* @param string $version the browser version
|
||||
*/
|
||||
public function setBrowserVersion($version)
|
||||
{
|
||||
$this->browserVersion = $version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the operating system name
|
||||
*
|
||||
* @return string the operating system name
|
||||
*/
|
||||
public function getOperatingSystem()
|
||||
{
|
||||
return $this->operatingSystem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the operating system name
|
||||
*
|
||||
* @param string $operatingSystem the operating system name
|
||||
*/
|
||||
public function setOperatingSystem($operatingSystem)
|
||||
{
|
||||
$this->operatingSystem = $operatingSystem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the engine name
|
||||
*
|
||||
* @return string the engine name
|
||||
*/
|
||||
public function getEngine()
|
||||
{
|
||||
return $this->engine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the engine name
|
||||
*
|
||||
* @param string $operatingSystem the engine name
|
||||
*/
|
||||
public function setEngine($engine)
|
||||
{
|
||||
$this->engine = $engine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user agent string
|
||||
*
|
||||
* @return string the user agent string
|
||||
*/
|
||||
public function getUserAgentString()
|
||||
{
|
||||
return $this->userAgentString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the user agent string
|
||||
*
|
||||
* @param string $userAgentString the user agent string
|
||||
*/
|
||||
public function setUserAgentString($userAgentString)
|
||||
{
|
||||
$this->userAgentString = $userAgentString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell whether this user agent is unknown or not
|
||||
*
|
||||
* @return boolean true if this user agent is unknown, false otherwise
|
||||
*/
|
||||
public function isUnknown()
|
||||
{
|
||||
return empty($this->browserName);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string combined browser name and version
|
||||
*/
|
||||
public function getFullName()
|
||||
{
|
||||
return $this->getBrowserName().' '.$this->getBrowserVersion();
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->getFullName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the user agent from a user agent string
|
||||
* @param string $userAgentString the user agent string
|
||||
* @param phpUserAgentStringParser $userAgentStringParser the parser used to parse the string
|
||||
*/
|
||||
public function configureFromUserAgentString($userAgentString, phpUserAgentStringParser $userAgentStringParser = null)
|
||||
{
|
||||
if(null === $userAgentStringParser)
|
||||
{
|
||||
$userAgentStringParser = new phpUserAgentStringParser();
|
||||
}
|
||||
|
||||
$this->setUserAgentString($userAgentString);
|
||||
|
||||
$this->fromArray($userAgentStringParser->parse($userAgentString));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the user agent to a data array
|
||||
*
|
||||
* @return array data
|
||||
*/
|
||||
public function toArray()
|
||||
{
|
||||
return array(
|
||||
'browser_name' => $this->getBrowserName(),
|
||||
'browser_version' => $this->getBrowserVersion(),
|
||||
'operating_system' => $this->getOperatingSystem()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the user agent from a data array
|
||||
*
|
||||
* @param array $data
|
||||
*/
|
||||
public function fromArray(array $data)
|
||||
{
|
||||
$this->setBrowserName($data['browser_name']);
|
||||
$this->setBrowserVersion($data['browser_version']);
|
||||
$this->setOperatingSystem($data['operating_system']);
|
||||
$this->setEngine($data['engine']);
|
||||
}
|
||||
}
|
||||
@@ -1,321 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Simple PHP User Agent string parser
|
||||
*/
|
||||
|
||||
class phpUserAgentStringParser
|
||||
{
|
||||
/**
|
||||
* Parse a user agent string.
|
||||
*
|
||||
* @param string $userAgentString defaults to $_SERVER['HTTP_USER_AGENT'] if empty
|
||||
* @return array ( the user agent informations
|
||||
* 'browser_name' => 'firefox',
|
||||
* 'browser_version' => '3.6',
|
||||
* 'operating_system' => 'linux'
|
||||
* )
|
||||
*/
|
||||
public function parse($userAgentString = null)
|
||||
{
|
||||
// use current user agent string as default
|
||||
if(!$userAgentString)
|
||||
{
|
||||
$userAgentString = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : null;
|
||||
}
|
||||
|
||||
// parse quickly (with medium accuracy)
|
||||
$informations = $this->doParse($userAgentString);
|
||||
|
||||
|
||||
// run some filters to increase accuracy
|
||||
foreach($this->getFilters() as $filter)
|
||||
{
|
||||
$this->$filter($informations);
|
||||
}
|
||||
|
||||
return $informations;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect quickly informations from the user agent string
|
||||
*
|
||||
* @param string $userAgentString user agent string
|
||||
* @return array user agent informations array
|
||||
*/
|
||||
protected function doParse($userAgentString)
|
||||
{
|
||||
$userAgent = array(
|
||||
'string' => $this->cleanUserAgentString($userAgentString),
|
||||
'browser_name' => null,
|
||||
'browser_version' => null,
|
||||
'operating_system' => null,
|
||||
'engine' => null
|
||||
);
|
||||
|
||||
if(empty($userAgent['string']))
|
||||
{
|
||||
return $userAgent;
|
||||
}
|
||||
|
||||
// build regex that matches phrases for known browsers
|
||||
// (e.g. "Firefox/2.0" or "MSIE 6.0" (This only matches the major and minor
|
||||
// version numbers. E.g. "2.0.0.6" is parsed as simply "2.0"
|
||||
$pattern = '#('.join('|', $this->getKnownBrowsers()).')[/ ]+([0-9]+(?:\.[0-9]+)?)#';
|
||||
|
||||
// Find all phrases (or return empty array if none found)
|
||||
if (preg_match_all($pattern, $userAgent['string'], $matches))
|
||||
{
|
||||
// Since some UAs have more than one phrase (e.g Firefox has a Gecko phrase,
|
||||
// Opera 7,8 have a MSIE phrase), use the last one found (the right-most one
|
||||
// in the UA). That's usually the most correct.
|
||||
$i = count($matches[1])-1;
|
||||
|
||||
if (isset($matches[1][$i]))
|
||||
{
|
||||
$userAgent['browser_name'] = $matches[1][$i];
|
||||
}
|
||||
if (isset($matches[2][$i]))
|
||||
{
|
||||
$userAgent['browser_version'] = $matches[2][$i];
|
||||
}
|
||||
}
|
||||
|
||||
// Find operating system
|
||||
$pattern = '#'.join('|', $this->getKnownOperatingSystems()).'#';
|
||||
|
||||
if (preg_match($pattern, $userAgent['string'], $match))
|
||||
{
|
||||
if (isset($match[0]))
|
||||
{
|
||||
$userAgent['operating_system'] = $match[0];
|
||||
}
|
||||
}
|
||||
|
||||
// Find engine
|
||||
$pattern = '#'.join('|', $this->getKnownEngines()).'#';
|
||||
|
||||
if (preg_match($pattern, $userAgent['string'], $match))
|
||||
{
|
||||
if (isset($match[0]))
|
||||
{
|
||||
$userAgent['engine'] = $match[0];
|
||||
}
|
||||
}
|
||||
|
||||
return $userAgent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Make user agent string lowercase, and replace browser aliases
|
||||
*
|
||||
* @param string $userAgentString the dirty user agent string
|
||||
* @return string the clean user agent string
|
||||
*/
|
||||
public function cleanUserAgentString($userAgentString)
|
||||
{
|
||||
// clean up the string
|
||||
$userAgentString = trim(strtolower($userAgentString));
|
||||
|
||||
// replace browser names with their aliases
|
||||
$userAgentString = strtr($userAgentString, $this->getKnownBrowserAliases());
|
||||
|
||||
// replace operating system names with their aliases
|
||||
$userAgentString = strtr($userAgentString, $this->getKnownOperatingSystemAliases());
|
||||
|
||||
// replace engine names with their aliases
|
||||
$userAgentString = strtr($userAgentString, $this->getKnownEngineAliases());
|
||||
|
||||
return $userAgentString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of filters that get called when parsing a user agent
|
||||
*
|
||||
* @return array list of valid callables
|
||||
*/
|
||||
public function getFilters()
|
||||
{
|
||||
return array(
|
||||
'filterAndroid',
|
||||
'filterGoogleChrome',
|
||||
'filterSafariVersion',
|
||||
'filterOperaVersion',
|
||||
'filterYahoo',
|
||||
'filterMsie',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a filter to be called when parsing a user agent
|
||||
*
|
||||
* @param string $filter name of the filter method
|
||||
*/
|
||||
public function addFilter($filter)
|
||||
{
|
||||
$this->filters += $filter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get known browsers
|
||||
*
|
||||
* @return array the browsers
|
||||
*/
|
||||
protected function getKnownBrowsers()
|
||||
{
|
||||
return array(
|
||||
'msie',
|
||||
'firefox',
|
||||
'safari',
|
||||
'webkit',
|
||||
'opera',
|
||||
'netscape',
|
||||
'konqueror',
|
||||
'gecko',
|
||||
'chrome',
|
||||
'googlebot',
|
||||
'iphone',
|
||||
'msnbot',
|
||||
'applewebkit'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get known browser aliases
|
||||
*
|
||||
* @return array the browser aliases
|
||||
*/
|
||||
protected function getKnownBrowserAliases()
|
||||
{
|
||||
return array(
|
||||
'shiretoko' => 'firefox',
|
||||
'namoroka' => 'firefox',
|
||||
'shredder' => 'firefox',
|
||||
'minefield' => 'firefox',
|
||||
'granparadiso' => 'firefox'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get known operating system
|
||||
*
|
||||
* @return array the operating systems
|
||||
*/
|
||||
protected function getKnownOperatingSystems()
|
||||
{
|
||||
return array(
|
||||
'windows',
|
||||
'macintosh',
|
||||
'linux',
|
||||
'freebsd',
|
||||
'unix',
|
||||
'iphone'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get known operating system aliases
|
||||
*
|
||||
* @return array the operating system aliases
|
||||
*/
|
||||
protected function getKnownOperatingSystemAliases()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get known engines
|
||||
*
|
||||
* @return array the engines
|
||||
*/
|
||||
protected function getKnownEngines()
|
||||
{
|
||||
return array(
|
||||
'gecko',
|
||||
'webkit',
|
||||
'trident',
|
||||
'presto'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get known engines aliases
|
||||
*
|
||||
* @return array the engines aliases
|
||||
*/
|
||||
protected function getKnownEngineAliases()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters
|
||||
*/
|
||||
|
||||
/**
|
||||
* Google chrome has a safari like signature
|
||||
*/
|
||||
protected function filterGoogleChrome(array &$userAgent)
|
||||
{
|
||||
if ('safari' === $userAgent['browser_name'] && strpos($userAgent['string'], 'chrome/'))
|
||||
{
|
||||
$userAgent['browser_name'] = 'chrome';
|
||||
$userAgent['browser_version'] = preg_replace('|.+chrome/([0-9]+(?:\.[0-9]+)?).+|', '$1', $userAgent['string']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Safari version is not encoded "normally"
|
||||
*/
|
||||
protected function filterSafariVersion(array &$userAgent)
|
||||
{
|
||||
if ('safari' === $userAgent['browser_name'] && strpos($userAgent['string'], ' version/'))
|
||||
{
|
||||
$userAgent['browser_version'] = preg_replace('|.+\sversion/([0-9]+(?:\.[0-9]+)?).+|', '$1', $userAgent['string']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Opera 10.00 (and higher) version number is located at the end
|
||||
*/
|
||||
protected function filterOperaVersion(array &$userAgent)
|
||||
{
|
||||
if('opera' === $userAgent['browser_name'] && strpos($userAgent['string'], ' version/'))
|
||||
{
|
||||
$userAgent['browser_version'] = preg_replace('|.+\sversion/([0-9]+\.[0-9]+)\s*.*|', '$1', $userAgent['string']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Yahoo bot has a special user agent string
|
||||
*/
|
||||
protected function filterYahoo(array &$userAgent)
|
||||
{
|
||||
if (null === $userAgent['browser_name'] && strpos($userAgent['string'], 'yahoo! slurp'))
|
||||
{
|
||||
$userAgent['browser_name'] = 'yahoobot';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* MSIE does not always declare its engine
|
||||
*/
|
||||
protected function filterMsie(array &$userAgent)
|
||||
{
|
||||
if ('msie' === $userAgent['browser_name'] && empty($userAgent['engine']))
|
||||
{
|
||||
$userAgent['engine'] = 'trident';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Android has a safari like signature
|
||||
*/
|
||||
protected function filterAndroid(array &$userAgent) {
|
||||
if ('safari' === $userAgent['browser_name'] && strpos($userAgent['string'], 'android ')) {
|
||||
$userAgent['browser_name'] = 'android';
|
||||
$userAgent['operating_system'] = 'android';
|
||||
$userAgent['browser_version'] = preg_replace('|.+android ([0-9]+(?:\.[0-9]+)+).+|', '$1', $userAgent['string']);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user