first commit
This commit is contained in:
632
system/src/Grav/Common/Page/Collection.php
Normal file
632
system/src/Grav/Common/Page/Collection.php
Normal file
@@ -0,0 +1,632 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.Page
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Page;
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Iterator;
|
||||
use Grav\Common\Utils;
|
||||
|
||||
class Collection extends Iterator
|
||||
{
|
||||
/**
|
||||
* @var Pages
|
||||
*/
|
||||
protected $pages;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $params;
|
||||
|
||||
/**
|
||||
* Collection constructor.
|
||||
*
|
||||
* @param array $items
|
||||
* @param array $params
|
||||
* @param Pages|null $pages
|
||||
*/
|
||||
public function __construct($items = [], array $params = [], Pages $pages = null)
|
||||
{
|
||||
parent::__construct($items);
|
||||
|
||||
$this->params = $params;
|
||||
$this->pages = $pages ? $pages : Grav::instance()->offsetGet('pages');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the collection params
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function params()
|
||||
{
|
||||
return $this->params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a single page to a collection
|
||||
*
|
||||
* @param Page $page
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addPage(Page $page)
|
||||
{
|
||||
$this->items[$page->path()] = ['slug' => $page->slug()];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a page with path and slug
|
||||
*
|
||||
* @param $path
|
||||
* @param $slug
|
||||
* @return $this
|
||||
*/
|
||||
public function add($path, $slug)
|
||||
{
|
||||
$this->items[$path] = ['slug' => $slug];
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Create a copy of this collection
|
||||
*
|
||||
* @return static
|
||||
*/
|
||||
public function copy()
|
||||
{
|
||||
return new static($this->items, $this->params, $this->pages);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Merge another collection with the current collection
|
||||
*
|
||||
* @param Collection $collection
|
||||
* @return $this
|
||||
*/
|
||||
public function merge(Collection $collection)
|
||||
{
|
||||
foreach($collection as $page) {
|
||||
$this->addPage($page);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Intersect another collection with the current collection
|
||||
*
|
||||
* @param Collection $collection
|
||||
* @return $this
|
||||
*/
|
||||
public function intersect(Collection $collection)
|
||||
{
|
||||
$array1 = $this->items;
|
||||
$array2 = $collection->toArray();
|
||||
|
||||
$this->items = array_uintersect($array1, $array2, function($val1, $val2) {
|
||||
return strcmp($val1['slug'], $val2['slug']);
|
||||
});
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set parameters to the Collection
|
||||
*
|
||||
* @param array $params
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setParams(array $params)
|
||||
{
|
||||
$this->params = array_merge($this->params, $params);
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current page.
|
||||
*
|
||||
* @return Page
|
||||
*/
|
||||
public function current()
|
||||
{
|
||||
$current = parent::key();
|
||||
|
||||
return $this->pages->get($current);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns current slug.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function key()
|
||||
{
|
||||
$current = parent::current();
|
||||
|
||||
return $current['slug'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value at specified offset.
|
||||
*
|
||||
* @param mixed $offset The offset to retrieve.
|
||||
*
|
||||
* @return mixed Can return all value types.
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return !empty($this->items[$offset]) ? $this->pages->get($offset) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Split collection into array of smaller collections.
|
||||
*
|
||||
* @param $size
|
||||
* @return array|Collection[]
|
||||
*/
|
||||
public function batch($size)
|
||||
{
|
||||
$chunks = array_chunk($this->items, $size, true);
|
||||
|
||||
$list = [];
|
||||
foreach ($chunks as $chunk) {
|
||||
$list[] = new static($chunk, $this->params, $this->pages);
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove item from the list.
|
||||
*
|
||||
* @param Page|string|null $key
|
||||
*
|
||||
* @return $this
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function remove($key = null)
|
||||
{
|
||||
if ($key instanceof Page) {
|
||||
$key = $key->path();
|
||||
} elseif (is_null($key)) {
|
||||
$key = key($this->items);
|
||||
}
|
||||
if (!is_string($key)) {
|
||||
throw new \InvalidArgumentException('Invalid argument $key.');
|
||||
}
|
||||
|
||||
parent::remove($key);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reorder collection.
|
||||
*
|
||||
* @param string $by
|
||||
* @param string $dir
|
||||
* @param array $manual
|
||||
* @param string $sort_flags
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function order($by, $dir = 'asc', $manual = null, $sort_flags = null)
|
||||
{
|
||||
$this->items = $this->pages->sortCollection($this, $by, $dir, $manual, $sort_flags);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if this item is the first in the collection.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return boolean True if item is first.
|
||||
*/
|
||||
public function isFirst($path)
|
||||
{
|
||||
if ($this->items && $path == array_keys($this->items)[0]) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check to see if this item is the last in the collection.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return boolean True if item is last.
|
||||
*/
|
||||
public function isLast($path)
|
||||
{
|
||||
if ($this->items && $path == array_keys($this->items)[count($this->items) - 1]) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the previous sibling based on current position.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return Page The previous item.
|
||||
*/
|
||||
public function prevSibling($path)
|
||||
{
|
||||
return $this->adjacentSibling($path, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next sibling based on current position.
|
||||
*
|
||||
* @param string $path
|
||||
*
|
||||
* @return Page The next item.
|
||||
*/
|
||||
public function nextSibling($path)
|
||||
{
|
||||
return $this->adjacentSibling($path, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the adjacent sibling based on a direction.
|
||||
*
|
||||
* @param string $path
|
||||
* @param integer $direction either -1 or +1
|
||||
*
|
||||
* @return Page The sibling item.
|
||||
*/
|
||||
public function adjacentSibling($path, $direction = 1)
|
||||
{
|
||||
$values = array_keys($this->items);
|
||||
$keys = array_flip($values);
|
||||
|
||||
if (array_key_exists($path, $keys)) {
|
||||
$index = $keys[$path] - $direction;
|
||||
|
||||
return isset($values[$index]) ? $this->offsetGet($values[$index]) : $this;
|
||||
}
|
||||
|
||||
return $this;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the item in the current position.
|
||||
*
|
||||
* @param string $path the path the item
|
||||
*
|
||||
* @return Integer the index of the current page.
|
||||
*/
|
||||
public function currentPosition($path)
|
||||
{
|
||||
return array_search($path, array_keys($this->items));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the items between a set of date ranges of either the page date field (default) or
|
||||
* an arbitrary datetime page field where end date is optional
|
||||
* Dates can be passed in as text that strtotime() can process
|
||||
* http://php.net/manual/en/function.strtotime.php
|
||||
*
|
||||
* @param $startDate
|
||||
* @param bool $endDate
|
||||
* @param $field
|
||||
*
|
||||
* @return $this
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function dateRange($startDate, $endDate = false, $field = false)
|
||||
{
|
||||
$start = Utils::date2timestamp($startDate);
|
||||
$end = $endDate ? Utils::date2timestamp($endDate) : false;
|
||||
|
||||
$date_range = [];
|
||||
foreach ($this->items as $path => $slug) {
|
||||
$page = $this->pages->get($path);
|
||||
if ($page !== null) {
|
||||
$date = $field ? strtotime($page->value($field)) : $page->date();
|
||||
|
||||
if ($date >= $start && (!$end || $date <= $end)) {
|
||||
$date_range[$path] = $slug;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->items = $date_range;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with only visible pages
|
||||
*
|
||||
* @return Collection The collection with only visible pages
|
||||
*/
|
||||
public function visible()
|
||||
{
|
||||
$visible = [];
|
||||
|
||||
foreach ($this->items as $path => $slug) {
|
||||
$page = $this->pages->get($path);
|
||||
if ($page !== null && $page->visible()) {
|
||||
$visible[$path] = $slug;
|
||||
}
|
||||
}
|
||||
$this->items = $visible;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with only non-visible pages
|
||||
*
|
||||
* @return Collection The collection with only non-visible pages
|
||||
*/
|
||||
public function nonVisible()
|
||||
{
|
||||
$visible = [];
|
||||
|
||||
foreach ($this->items as $path => $slug) {
|
||||
$page = $this->pages->get($path);
|
||||
if ($page !== null && !$page->visible()) {
|
||||
$visible[$path] = $slug;
|
||||
}
|
||||
}
|
||||
$this->items = $visible;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with only modular pages
|
||||
*
|
||||
* @return Collection The collection with only modular pages
|
||||
*/
|
||||
public function modular()
|
||||
{
|
||||
$modular = [];
|
||||
|
||||
foreach ($this->items as $path => $slug) {
|
||||
$page = $this->pages->get($path);
|
||||
if ($page !== null && $page->modular()) {
|
||||
$modular[$path] = $slug;
|
||||
}
|
||||
}
|
||||
$this->items = $modular;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with only non-modular pages
|
||||
*
|
||||
* @return Collection The collection with only non-modular pages
|
||||
*/
|
||||
public function nonModular()
|
||||
{
|
||||
$modular = [];
|
||||
|
||||
foreach ($this->items as $path => $slug) {
|
||||
$page = $this->pages->get($path);
|
||||
if ($page !== null && !$page->modular()) {
|
||||
$modular[$path] = $slug;
|
||||
}
|
||||
}
|
||||
$this->items = $modular;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with only published pages
|
||||
*
|
||||
* @return Collection The collection with only published pages
|
||||
*/
|
||||
public function published()
|
||||
{
|
||||
$published = [];
|
||||
|
||||
foreach ($this->items as $path => $slug) {
|
||||
$page = $this->pages->get($path);
|
||||
if ($page !== null && $page->published()) {
|
||||
$published[$path] = $slug;
|
||||
}
|
||||
}
|
||||
$this->items = $published;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with only non-published pages
|
||||
*
|
||||
* @return Collection The collection with only non-published pages
|
||||
*/
|
||||
public function nonPublished()
|
||||
{
|
||||
$published = [];
|
||||
|
||||
foreach ($this->items as $path => $slug) {
|
||||
$page = $this->pages->get($path);
|
||||
if ($page !== null && !$page->published()) {
|
||||
$published[$path] = $slug;
|
||||
}
|
||||
}
|
||||
$this->items = $published;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with only routable pages
|
||||
*
|
||||
* @return Collection The collection with only routable pages
|
||||
*/
|
||||
public function routable()
|
||||
{
|
||||
$routable = [];
|
||||
|
||||
foreach ($this->items as $path => $slug) {
|
||||
$page = $this->pages->get($path);
|
||||
|
||||
if ($page !== null && $page->routable()) {
|
||||
$routable[$path] = $slug;
|
||||
}
|
||||
}
|
||||
|
||||
$this->items = $routable;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with only non-routable pages
|
||||
*
|
||||
* @return Collection The collection with only non-routable pages
|
||||
*/
|
||||
public function nonRoutable()
|
||||
{
|
||||
$routable = [];
|
||||
|
||||
foreach ($this->items as $path => $slug) {
|
||||
$page = $this->pages->get($path);
|
||||
if ($page !== null && !$page->routable()) {
|
||||
$routable[$path] = $slug;
|
||||
}
|
||||
}
|
||||
$this->items = $routable;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with only pages of the specified type
|
||||
*
|
||||
* @param $type
|
||||
*
|
||||
* @return Collection The collection
|
||||
*/
|
||||
public function ofType($type)
|
||||
{
|
||||
$items = [];
|
||||
|
||||
foreach ($this->items as $path => $slug) {
|
||||
$page = $this->pages->get($path);
|
||||
if ($page !== null && $page->template() == $type) {
|
||||
$items[$path] = $slug;
|
||||
}
|
||||
}
|
||||
|
||||
$this->items = $items;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with only pages of one of the specified types
|
||||
*
|
||||
* @param $types
|
||||
*
|
||||
* @return Collection The collection
|
||||
*/
|
||||
public function ofOneOfTheseTypes($types)
|
||||
{
|
||||
$items = [];
|
||||
|
||||
foreach ($this->items as $path => $slug) {
|
||||
$page = $this->pages->get($path);
|
||||
if ($page !== null && in_array($page->template(), $types)) {
|
||||
$items[$path] = $slug;
|
||||
}
|
||||
}
|
||||
|
||||
$this->items = $items;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new collection with only pages of one of the specified access levels
|
||||
*
|
||||
* @param $accessLevels
|
||||
*
|
||||
* @return Collection The collection
|
||||
*/
|
||||
public function ofOneOfTheseAccessLevels($accessLevels)
|
||||
{
|
||||
$items = [];
|
||||
|
||||
foreach ($this->items as $path => $slug) {
|
||||
$page = $this->pages->get($path);
|
||||
|
||||
if ($page !== null && isset($page->header()->access)) {
|
||||
if (is_array($page->header()->access)) {
|
||||
//Multiple values for access
|
||||
$valid = false;
|
||||
|
||||
foreach ($page->header()->access as $index => $accessLevel) {
|
||||
if (is_array($accessLevel)) {
|
||||
foreach ($accessLevel as $innerIndex => $innerAccessLevel) {
|
||||
if (in_array($innerAccessLevel, $accessLevels)) {
|
||||
$valid = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (in_array($index, $accessLevels)) {
|
||||
$valid = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($valid) {
|
||||
$items[$path] = $slug;
|
||||
}
|
||||
} else {
|
||||
//Single value for access
|
||||
if (in_array($page->header()->access, $accessLevels)) {
|
||||
$items[$path] = $slug;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
$this->items = $items;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the extended version of this Collection with each page keyed by route
|
||||
*
|
||||
* @return array
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function toExtendedArray()
|
||||
{
|
||||
$items = [];
|
||||
foreach ($this->items as $path => $slug) {
|
||||
$page = $this->pages->get($path);
|
||||
|
||||
if ($page !== null) {
|
||||
$items[$page->route()] = $page->toArray();
|
||||
}
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
}
|
||||
17
system/src/Grav/Common/Page/Header.php
Normal file
17
system/src/Grav/Common/Page/Header.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.Page
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Page;
|
||||
|
||||
use RocketTheme\Toolbox\ArrayTraits\Constructor;
|
||||
use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccess;
|
||||
|
||||
class Header implements \ArrayAccess
|
||||
{
|
||||
use NestedArrayAccess, Constructor;
|
||||
}
|
||||
9
system/src/Grav/Common/Page/Interfaces/PageInterface.php
Normal file
9
system/src/Grav/Common/Page/Interfaces/PageInterface.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
namespace Grav\Common\Page\Interfaces;
|
||||
|
||||
/**
|
||||
* Class implements page interface.
|
||||
*/
|
||||
interface PageInterface
|
||||
{
|
||||
}
|
||||
218
system/src/Grav/Common/Page/Media.php
Normal file
218
system/src/Grav/Common/Page/Media.php
Normal file
@@ -0,0 +1,218 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.Page
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Page;
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Yaml;
|
||||
use Grav\Common\Page\Medium\AbstractMedia;
|
||||
use Grav\Common\Page\Medium\GlobalMedia;
|
||||
use Grav\Common\Page\Medium\MediumFactory;
|
||||
use RocketTheme\Toolbox\File\File;
|
||||
|
||||
class Media extends AbstractMedia
|
||||
{
|
||||
protected static $global;
|
||||
|
||||
protected $path;
|
||||
|
||||
protected $standard_exif = ['FileSize', 'MimeType', 'height', 'width'];
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @param array $media_order
|
||||
*/
|
||||
public function __construct($path, array $media_order = null)
|
||||
{
|
||||
$this->path = $path;
|
||||
$this->media_order = $media_order;
|
||||
|
||||
$this->__wakeup();
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize static variables on unserialize.
|
||||
*/
|
||||
public function __wakeup()
|
||||
{
|
||||
if (!isset(static::$global)) {
|
||||
// Add fallback to global media.
|
||||
static::$global = new GlobalMedia();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $offset
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return parent::offsetExists($offset) ?: isset(static::$global[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $offset
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return parent::offsetGet($offset) ?: static::$global[$offset];
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize class.
|
||||
*/
|
||||
protected function init()
|
||||
{
|
||||
$config = Grav::instance()['config'];
|
||||
$locator = Grav::instance()['locator'];
|
||||
$exif_reader = isset(Grav::instance()['exif']) ? Grav::instance()['exif']->getReader() : false;
|
||||
$media_types = array_keys(Grav::instance()['config']->get('media.types'));
|
||||
|
||||
// Handle special cases where page doesn't exist in filesystem.
|
||||
if (!is_dir($this->path)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$iterator = new \FilesystemIterator($this->path, \FilesystemIterator::UNIX_PATHS | \FilesystemIterator::SKIP_DOTS);
|
||||
|
||||
$media = [];
|
||||
|
||||
/** @var \DirectoryIterator $info */
|
||||
foreach ($iterator as $path => $info) {
|
||||
// Ignore folders and Markdown files.
|
||||
if (!$info->isFile() || $info->getExtension() === 'md' || $info->getFilename()[0] === '.') {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find out what type we're dealing with
|
||||
list($basename, $ext, $type, $extra) = $this->getFileParts($info->getFilename());
|
||||
|
||||
if (!in_array(strtolower($ext), $media_types)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($type === 'alternative') {
|
||||
$media["{$basename}.{$ext}"][$type][$extra] = [ 'file' => $path, 'size' => $info->getSize() ];
|
||||
} else {
|
||||
$media["{$basename}.{$ext}"][$type] = [ 'file' => $path, 'size' => $info->getSize() ];
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($media as $name => $types) {
|
||||
// First prepare the alternatives in case there is no base medium
|
||||
if (!empty($types['alternative'])) {
|
||||
foreach ($types['alternative'] as $ratio => &$alt) {
|
||||
$alt['file'] = MediumFactory::fromFile($alt['file']);
|
||||
|
||||
if (!$alt['file']) {
|
||||
unset($types['alternative'][$ratio]);
|
||||
} else {
|
||||
$alt['file']->set('size', $alt['size']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$file_path = null;
|
||||
|
||||
// Create the base medium
|
||||
if (empty($types['base'])) {
|
||||
if (!isset($types['alternative'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$max = max(array_keys($types['alternative']));
|
||||
$medium = $types['alternative'][$max]['file'];
|
||||
$file_path = $medium->path();
|
||||
$medium = MediumFactory::scaledFromMedium($medium, $max, 1)['file'];
|
||||
} else {
|
||||
$medium = MediumFactory::fromFile($types['base']['file']);
|
||||
$medium && $medium->set('size', $types['base']['size']);
|
||||
$file_path = $medium->path();
|
||||
}
|
||||
|
||||
if (empty($medium)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// metadata file
|
||||
$meta_path = $file_path . '.meta.yaml';
|
||||
|
||||
if (file_exists($meta_path)) {
|
||||
$types['meta']['file'] = $meta_path;
|
||||
} elseif ($file_path && $medium->get('mime') === 'image/jpeg' && empty($types['meta']) && $config->get('system.media.auto_metadata_exif') && $exif_reader) {
|
||||
|
||||
$meta = $exif_reader->read($file_path);
|
||||
|
||||
if ($meta) {
|
||||
$meta_data = $meta->getData();
|
||||
$meta_trimmed = array_diff_key($meta_data, array_flip($this->standard_exif));
|
||||
if ($meta_trimmed) {
|
||||
if ($locator->isStream($meta_path)) {
|
||||
$file = File::instance($locator->findResource($meta_path, true, true));
|
||||
} else {
|
||||
$file = File::instance($meta_path);
|
||||
}
|
||||
$file->save(Yaml::dump($meta_trimmed));
|
||||
$types['meta']['file'] = $meta_path;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($types['meta'])) {
|
||||
$medium->addMetaFile($types['meta']['file']);
|
||||
}
|
||||
|
||||
if (!empty($types['thumb'])) {
|
||||
// We will not turn it into medium yet because user might never request the thumbnail
|
||||
// not wasting any resources on that, maybe we should do this for medium in general?
|
||||
$medium->set('thumbnails.page', $types['thumb']['file']);
|
||||
}
|
||||
|
||||
// Build missing alternatives
|
||||
if (!empty($types['alternative'])) {
|
||||
$alternatives = $types['alternative'];
|
||||
$max = max(array_keys($alternatives));
|
||||
|
||||
for ($i=$max; $i > 1; $i--) {
|
||||
if (isset($alternatives[$i])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$types['alternative'][$i] = MediumFactory::scaledFromMedium($alternatives[$max]['file'], $max, $i);
|
||||
}
|
||||
|
||||
foreach ($types['alternative'] as $altMedium) {
|
||||
if ($altMedium['file'] != $medium) {
|
||||
$altWidth = $altMedium['file']->get('width');
|
||||
$medWidth = $medium->get('width');
|
||||
if ($altWidth && $medWidth) {
|
||||
$ratio = $altWidth / $medWidth;
|
||||
$medium->addAlternative($ratio, $altMedium['file']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->add($name, $medium);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable accessing the media path
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function path()
|
||||
{
|
||||
return $this->path;
|
||||
}
|
||||
}
|
||||
210
system/src/Grav/Common/Page/Medium/AbstractMedia.php
Normal file
210
system/src/Grav/Common/Page/Medium/AbstractMedia.php
Normal file
@@ -0,0 +1,210 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.Page
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
use Grav\Common\Getters;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Media\Interfaces\MediaCollectionInterface;
|
||||
use Grav\Common\Media\Interfaces\MediaObjectInterface;
|
||||
use Grav\Common\Utils;
|
||||
|
||||
abstract class AbstractMedia extends Getters implements MediaCollectionInterface
|
||||
{
|
||||
protected $gettersVariable = 'instances';
|
||||
|
||||
protected $instances = [];
|
||||
protected $images = [];
|
||||
protected $videos = [];
|
||||
protected $audios = [];
|
||||
protected $files = [];
|
||||
protected $media_order;
|
||||
|
||||
/**
|
||||
* Get medium by filename.
|
||||
*
|
||||
* @param string $filename
|
||||
* @return Medium|null
|
||||
*/
|
||||
public function get($filename)
|
||||
{
|
||||
return $this->offsetGet($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call object as function to get medium by filename.
|
||||
*
|
||||
* @param string $filename
|
||||
* @return mixed
|
||||
*/
|
||||
public function __invoke($filename)
|
||||
{
|
||||
return $this->offsetGet($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $offset
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
$object = parent::offsetGet($offset);
|
||||
|
||||
// It would be nice if previous image modification would not affect the later ones.
|
||||
//$object = $object ? clone($object) : null;
|
||||
|
||||
return $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all media.
|
||||
*
|
||||
* @return array|MediaObjectInterface[]
|
||||
*/
|
||||
public function all()
|
||||
{
|
||||
$this->instances = $this->orderMedia($this->instances);
|
||||
|
||||
return $this->instances;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all image media.
|
||||
*
|
||||
* @return array|MediaObjectInterface[]
|
||||
*/
|
||||
public function images()
|
||||
{
|
||||
$this->images = $this->orderMedia($this->images);
|
||||
return $this->images;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all video media.
|
||||
*
|
||||
* @return array|MediaObjectInterface[]
|
||||
*/
|
||||
public function videos()
|
||||
{
|
||||
$this->videos = $this->orderMedia($this->videos);
|
||||
return $this->videos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all audio media.
|
||||
*
|
||||
* @return array|MediaObjectInterface[]
|
||||
*/
|
||||
public function audios()
|
||||
{
|
||||
$this->audios = $this->orderMedia($this->audios);
|
||||
return $this->audios;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of all file media.
|
||||
*
|
||||
* @return array|MediaObjectInterface[]
|
||||
*/
|
||||
public function files()
|
||||
{
|
||||
$this->files = $this->orderMedia($this->files);
|
||||
return $this->files;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @param MediaObjectInterface $file
|
||||
*/
|
||||
protected function add($name, $file)
|
||||
{
|
||||
$this->instances[$name] = $file;
|
||||
switch ($file->type) {
|
||||
case 'image':
|
||||
$this->images[$name] = $file;
|
||||
break;
|
||||
case 'video':
|
||||
$this->videos[$name] = $file;
|
||||
break;
|
||||
case 'audio':
|
||||
$this->audios[$name] = $file;
|
||||
break;
|
||||
default:
|
||||
$this->files[$name] = $file;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Order the media based on the page's media_order
|
||||
*
|
||||
* @param $media
|
||||
* @return array
|
||||
*/
|
||||
protected function orderMedia($media)
|
||||
{
|
||||
if (null === $this->media_order) {
|
||||
$page = Grav::instance()['pages']->get($this->path);
|
||||
|
||||
if ($page && isset($page->header()->media_order)) {
|
||||
$this->media_order = array_map('trim', explode(',', $page->header()->media_order));
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($this->media_order) && is_array($this->media_order)) {
|
||||
$media = Utils::sortArrayByArray($media, $this->media_order);
|
||||
} else {
|
||||
ksort($media, SORT_NATURAL | SORT_FLAG_CASE);
|
||||
}
|
||||
|
||||
return $media;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get filename, extension and meta part.
|
||||
*
|
||||
* @param string $filename
|
||||
* @return array
|
||||
*/
|
||||
protected function getFileParts($filename)
|
||||
{
|
||||
if (preg_match('/(.*)@(\d+)x\.(.*)$/', $filename, $matches)) {
|
||||
$name = $matches[1];
|
||||
$extension = $matches[3];
|
||||
$extra = (int) $matches[2];
|
||||
$type = 'alternative';
|
||||
|
||||
if ($extra === 1) {
|
||||
$type = 'base';
|
||||
$extra = null;
|
||||
}
|
||||
} else {
|
||||
$fileParts = explode('.', $filename);
|
||||
|
||||
$name = array_shift($fileParts);
|
||||
$extension = null;
|
||||
$extra = null;
|
||||
$type = 'base';
|
||||
|
||||
while (($part = array_shift($fileParts)) !== null) {
|
||||
if ($part !== 'meta' && $part !== 'thumb') {
|
||||
if (null !== $extension) {
|
||||
$name .= '.' . $extension;
|
||||
}
|
||||
$extension = $part;
|
||||
} else {
|
||||
$type = $part;
|
||||
$extra = '.' . $part . '.' . implode('.', $fileParts);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array($name, $extension, $type, $extra);
|
||||
}
|
||||
}
|
||||
153
system/src/Grav/Common/Page/Medium/AudioMedium.php
Normal file
153
system/src/Grav/Common/Page/Medium/AudioMedium.php
Normal file
@@ -0,0 +1,153 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.Page
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
class AudioMedium extends Medium
|
||||
{
|
||||
use StaticResizeTrait;
|
||||
|
||||
/**
|
||||
* Parsedown element for source display mode
|
||||
*
|
||||
* @param array $attributes
|
||||
* @param boolean $reset
|
||||
* @return array
|
||||
*/
|
||||
protected function sourceParsedownElement(array $attributes, $reset = true)
|
||||
{
|
||||
$location = $this->url($reset);
|
||||
|
||||
return [
|
||||
'name' => 'audio',
|
||||
'text' => '<source src="' . $location . '">Your browser does not support the audio tag.',
|
||||
'attributes' => $attributes
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to set or remove the HTML5 default controls
|
||||
*
|
||||
* @param bool $display
|
||||
* @return $this
|
||||
*/
|
||||
public function controls($display = true)
|
||||
{
|
||||
if($display)
|
||||
{
|
||||
$this->attributes['controls'] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
unset($this->attributes['controls']);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to set the preload behaviour
|
||||
*
|
||||
* @param $preload
|
||||
* @return $this
|
||||
*/
|
||||
public function preload($preload)
|
||||
{
|
||||
$validPreloadAttrs = array('auto','metadata','none');
|
||||
|
||||
if (in_array($preload, $validPreloadAttrs))
|
||||
{
|
||||
$this->attributes['preload'] = $preload;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to set the controlsList behaviour
|
||||
* Separate multiple values with a hyphen
|
||||
*
|
||||
* @param $controlsList
|
||||
* @return $this
|
||||
*/
|
||||
public function controlsList($controlsList)
|
||||
{
|
||||
$controlsList = str_replace('-', ' ', $controlsList);
|
||||
$this->attributes['controlsList'] = $controlsList;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to set the muted attribute
|
||||
*
|
||||
* @param bool $status
|
||||
* @return $this
|
||||
*/
|
||||
public function muted($status = false)
|
||||
{
|
||||
if($status)
|
||||
{
|
||||
$this->attributes['muted'] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
unset($this->attributes['muted']);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to set the loop attribute
|
||||
*
|
||||
* @param bool $status
|
||||
* @return $this
|
||||
*/
|
||||
public function loop($status = false)
|
||||
{
|
||||
if($status)
|
||||
{
|
||||
$this->attributes['loop'] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
unset($this->attributes['loop']);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to set the autoplay attribute
|
||||
*
|
||||
* @param bool $status
|
||||
* @return $this
|
||||
*/
|
||||
public function autoplay($status = false)
|
||||
{
|
||||
if($status)
|
||||
{
|
||||
$this->attributes['autoplay'] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
unset($this->attributes['autoplay']);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reset medium.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
parent::reset();
|
||||
|
||||
$this->attributes['controls'] = true;
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
117
system/src/Grav/Common/Page/Medium/GlobalMedia.php
Normal file
117
system/src/Grav/Common/Page/Medium/GlobalMedia.php
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.Page
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
|
||||
class GlobalMedia extends AbstractMedia
|
||||
{
|
||||
/**
|
||||
* @param mixed $offset
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function offsetExists($offset)
|
||||
{
|
||||
return parent::offsetExists($offset) ?: !empty($this->resolveStream($offset));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed $offset
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function offsetGet($offset)
|
||||
{
|
||||
return parent::offsetGet($offset) ?: $this->addMedium($offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
* @return string|null
|
||||
*/
|
||||
protected function resolveStream($filename)
|
||||
{
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = Grav::instance()['locator'];
|
||||
|
||||
return $locator->isStream($filename) ? ($locator->findResource($filename) ?: null) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $stream
|
||||
* @return Medium|null
|
||||
*/
|
||||
protected function addMedium($stream)
|
||||
{
|
||||
$filename = $this->resolveStream($stream);
|
||||
if (!$filename) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$path = dirname($filename);
|
||||
list($basename, $ext,, $extra) = $this->getFileParts(basename($filename));
|
||||
$medium = MediumFactory::fromFile($filename);
|
||||
|
||||
if (empty($medium)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$medium->set('size', filesize($filename));
|
||||
$scale = (int) ($extra ?: 1);
|
||||
|
||||
if ($scale !== 1) {
|
||||
$altMedium = $medium;
|
||||
|
||||
// Create scaled down regular sized image.
|
||||
$medium = MediumFactory::scaledFromMedium($altMedium, $scale, 1)['file'];
|
||||
|
||||
if (empty($medium)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Add original sized image as alternative.
|
||||
$medium->addAlternative($scale, $altMedium['file']);
|
||||
|
||||
// Locate or generate smaller retina images.
|
||||
for ($i = $scale-1; $i > 1; $i--) {
|
||||
$altFilename = "{$path}/{$basename}@{$i}x.{$ext}";
|
||||
|
||||
if (file_exists($altFilename)) {
|
||||
$scaled = MediumFactory::fromFile($altFilename);
|
||||
} else {
|
||||
$scaled = MediumFactory::scaledFromMedium($altMedium, $scale, $i)['file'];
|
||||
}
|
||||
|
||||
if ($scaled) {
|
||||
$medium->addAlternative($i, $scaled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$meta = "{$path}/{$basename}.{$ext}.yaml";
|
||||
if (file_exists($meta)) {
|
||||
$medium->addMetaFile($meta);
|
||||
}
|
||||
$meta = "{$path}/{$basename}.{$ext}.meta.yaml";
|
||||
if (file_exists($meta)) {
|
||||
$medium->addMetaFile($meta);
|
||||
}
|
||||
|
||||
$thumb = "{$path}/{$basename}.thumb.{$ext}";
|
||||
if (file_exists($thumb)) {
|
||||
$medium->set('thumbnails.page', $thumb);
|
||||
}
|
||||
|
||||
$this->add($stream, $medium);
|
||||
|
||||
return $medium;
|
||||
}
|
||||
}
|
||||
110
system/src/Grav/Common/Page/Medium/ImageFile.php
Normal file
110
system/src/Grav/Common/Page/Medium/ImageFile.php
Normal file
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.Page
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use Gregwar\Image\Exceptions\GenerationError;
|
||||
use Gregwar\Image\Image;
|
||||
use Gregwar\Image\Source;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
|
||||
class ImageFile extends Image
|
||||
{
|
||||
public function __destruct()
|
||||
{
|
||||
$this->getAdapter()->deinit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear previously applied operations
|
||||
*/
|
||||
public function clearOperations()
|
||||
{
|
||||
$this->operations = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* This is the same as the Gregwar Image class except this one fires a Grav Event on creation of new cached file
|
||||
*
|
||||
* @param string $type the image type
|
||||
* @param int $quality the quality (for JPEG)
|
||||
* @param bool $actual
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function cacheFile($type = 'jpg', $quality = 80, $actual = false)
|
||||
{
|
||||
if ($type === 'guess') {
|
||||
$type = $this->guessType();
|
||||
}
|
||||
|
||||
if (!$this->forceCache && !count($this->operations) && $type === $this->guessType()) {
|
||||
return $this->getFilename($this->getFilePath());
|
||||
}
|
||||
|
||||
// Computes the hash
|
||||
$this->hash = $this->getHash($type, $quality);
|
||||
|
||||
// Generates the cache file
|
||||
$cacheFile = '';
|
||||
|
||||
if (!$this->prettyName || $this->prettyPrefix) {
|
||||
$cacheFile .= $this->hash;
|
||||
}
|
||||
|
||||
if ($this->prettyPrefix) {
|
||||
$cacheFile .= '-';
|
||||
}
|
||||
|
||||
if ($this->prettyName) {
|
||||
$cacheFile .= $this->prettyName;
|
||||
}
|
||||
|
||||
$cacheFile .= '.' . $type;
|
||||
|
||||
// If the files does not exists, save it
|
||||
$image = $this;
|
||||
|
||||
// Target file should be younger than all the current image
|
||||
// dependencies
|
||||
$conditions = array(
|
||||
'younger-than' => $this->getDependencies()
|
||||
);
|
||||
|
||||
// The generating function
|
||||
$generate = function ($target) use ($image, $type, $quality) {
|
||||
$result = $image->save($target, $type, $quality);
|
||||
|
||||
if ($result !== $target) {
|
||||
throw new GenerationError($result);
|
||||
}
|
||||
|
||||
Grav::instance()->fireEvent('onImageMediumSaved', new Event(['image' => $target]));
|
||||
};
|
||||
|
||||
// Asking the cache for the cacheFile
|
||||
try {
|
||||
$perms = Grav::instance()['config']->get('system.images.cache_perms', '0755');
|
||||
$perms = octdec($perms);
|
||||
$file = $this->getCacheSystem()->setDirectoryMode($perms)->getOrCreateFile($cacheFile, $conditions, $generate, $actual);
|
||||
} catch (GenerationError $e) {
|
||||
$file = $e->getNewFile();
|
||||
}
|
||||
|
||||
// Nulling the resource
|
||||
$this->getAdapter()->setSource(new Source\File($file));
|
||||
$this->getAdapter()->deinit();
|
||||
|
||||
if ($actual) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
return $this->getFilename($file);
|
||||
}
|
||||
}
|
||||
652
system/src/Grav/Common/Page/Medium/ImageMedium.php
Normal file
652
system/src/Grav/Common/Page/Medium/ImageMedium.php
Normal file
@@ -0,0 +1,652 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.Page
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
use Grav\Common\Data\Blueprint;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Utils;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
|
||||
class ImageMedium extends Medium
|
||||
{
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $thumbnailTypes = ['page', 'media', 'default'];
|
||||
|
||||
/**
|
||||
* @var ImageFile
|
||||
*/
|
||||
protected $image;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $format = 'guess';
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $quality;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
protected $default_quality;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
protected $debug_watermarked = false;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public static $magic_actions = [
|
||||
'resize', 'forceResize', 'cropResize', 'crop', 'zoomCrop',
|
||||
'negate', 'brightness', 'contrast', 'grayscale', 'emboss',
|
||||
'smooth', 'sharp', 'edge', 'colorize', 'sepia', 'enableProgressive',
|
||||
'rotate', 'flip', 'fixOrientation', 'gaussianBlur'
|
||||
];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
public static $magic_resize_actions = [
|
||||
'resize' => [0, 1],
|
||||
'forceResize' => [0, 1],
|
||||
'cropResize' => [0, 1],
|
||||
'crop' => [0, 1, 2, 3],
|
||||
'zoomCrop' => [0, 1]
|
||||
];
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $sizes = '100vw';
|
||||
|
||||
/**
|
||||
* Construct.
|
||||
*
|
||||
* @param array $items
|
||||
* @param Blueprint $blueprint
|
||||
*/
|
||||
public function __construct($items = [], Blueprint $blueprint = null)
|
||||
{
|
||||
parent::__construct($items, $blueprint);
|
||||
|
||||
$config = Grav::instance()['config'];
|
||||
|
||||
if (filesize($this->get('filepath')) === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$image_info = getimagesize($this->get('filepath'));
|
||||
$this->def('width', $image_info[0]);
|
||||
$this->def('height', $image_info[1]);
|
||||
$this->def('mime', $image_info['mime']);
|
||||
$this->def('debug', $config->get('system.images.debug'));
|
||||
|
||||
$this->set('thumbnails.media', $this->get('filepath'));
|
||||
|
||||
$this->default_quality = $config->get('system.images.default_image_quality', 85);
|
||||
|
||||
$this->reset();
|
||||
|
||||
if ($config->get('system.images.cache_all', false)) {
|
||||
$this->cache();
|
||||
}
|
||||
}
|
||||
|
||||
public function __destruct()
|
||||
{
|
||||
unset($this->image);
|
||||
}
|
||||
|
||||
public function __clone()
|
||||
{
|
||||
$this->image = $this->image ? clone $this->image : null;
|
||||
|
||||
parent::__clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add meta file for the medium.
|
||||
*
|
||||
* @param $filepath
|
||||
* @return $this
|
||||
*/
|
||||
public function addMetaFile($filepath)
|
||||
{
|
||||
parent::addMetaFile($filepath);
|
||||
|
||||
// Apply filters in meta file
|
||||
$this->reset();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear out the alternatives
|
||||
*/
|
||||
public function clearAlternatives()
|
||||
{
|
||||
$this->alternatives = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return PATH to image.
|
||||
*
|
||||
* @param bool $reset
|
||||
* @return string path to image
|
||||
*/
|
||||
public function path($reset = true)
|
||||
{
|
||||
$output = $this->saveImage();
|
||||
|
||||
if ($reset) {
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return URL to image.
|
||||
*
|
||||
* @param bool $reset
|
||||
* @return string
|
||||
*/
|
||||
public function url($reset = true)
|
||||
{
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = Grav::instance()['locator'];
|
||||
$image_path = $locator->findResource('cache://images', true);
|
||||
$image_dir = $locator->findResource('cache://images', false);
|
||||
$saved_image_path = $this->saveImage();
|
||||
|
||||
$output = preg_replace('|^' . preg_quote(GRAV_ROOT, '|') . '|', '', $saved_image_path);
|
||||
|
||||
if ($locator->isStream($output)) {
|
||||
$output = $locator->findResource($output, false);
|
||||
}
|
||||
|
||||
if (Utils::startsWith($output, $image_path)) {
|
||||
$output = '/' . $image_dir . preg_replace('|^' . preg_quote($image_path, '|') . '|', '', $output);
|
||||
}
|
||||
|
||||
if ($reset) {
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
return trim(Grav::instance()['base_url'] . '/' . ltrim($output . $this->querystring() . $this->urlHash(), '/'), '\\');
|
||||
}
|
||||
|
||||
/**
|
||||
* Simply processes with no extra methods. Useful for triggering events.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function cache()
|
||||
{
|
||||
if (!$this->image) {
|
||||
$this->image();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return srcset string for this Medium and its alternatives.
|
||||
*
|
||||
* @param bool $reset
|
||||
* @return string
|
||||
*/
|
||||
public function srcset($reset = true)
|
||||
{
|
||||
if (empty($this->alternatives)) {
|
||||
if ($reset) {
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
$srcset = [];
|
||||
foreach ($this->alternatives as $ratio => $medium) {
|
||||
$srcset[] = $medium->url($reset) . ' ' . $medium->get('width') . 'w';
|
||||
}
|
||||
$srcset[] = str_replace(' ', '%20', $this->url($reset)) . ' ' . $this->get('width') . 'w';
|
||||
|
||||
return implode(', ', $srcset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the ability to override the Inmage's Pretty name stored in cache
|
||||
*
|
||||
* @param $name
|
||||
*/
|
||||
public function setImagePrettyName($name)
|
||||
{
|
||||
$this->set('prettyname', $name);
|
||||
if ($this->image) {
|
||||
$this->image->setPrettyName($name);
|
||||
}
|
||||
}
|
||||
|
||||
public function getImagePrettyName()
|
||||
{
|
||||
if ($this->get('prettyname')) {
|
||||
return $this->get('prettyname');
|
||||
}
|
||||
|
||||
$basename = $this->get('basename');
|
||||
if (preg_match('/[a-z0-9]{40}-(.*)/', $basename, $matches)) {
|
||||
$basename = $matches[1];
|
||||
}
|
||||
return $basename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate alternative image widths, using either an array of integers, or
|
||||
* a min width, a max width, and a step parameter to fill out the necessary
|
||||
* widths. Existing image alternatives won't be overwritten.
|
||||
*
|
||||
* @param int|int[] $min_width
|
||||
* @param int [$max_width=2500]
|
||||
* @param int [$step=200]
|
||||
* @return $this
|
||||
*/
|
||||
public function derivatives($min_width, $max_width = 2500, $step = 200) {
|
||||
if (!empty($this->alternatives)) {
|
||||
$max = max(array_keys($this->alternatives));
|
||||
$base = $this->alternatives[$max];
|
||||
} else {
|
||||
$base = $this;
|
||||
}
|
||||
|
||||
$widths = [];
|
||||
|
||||
if (func_num_args() === 1) {
|
||||
foreach ((array) func_get_arg(0) as $width) {
|
||||
if ($width < $base->get('width')) {
|
||||
$widths[] = $width;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$max_width = min($max_width, $base->get('width'));
|
||||
|
||||
for ($width = $min_width; $width < $max_width; $width = $width + $step) {
|
||||
$widths[] = $width;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($widths as $width) {
|
||||
// Only generate image alternatives that don't already exist
|
||||
if (array_key_exists((int) $width, $this->alternatives)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$derivative = MediumFactory::fromFile($base->get('filepath'));
|
||||
|
||||
// It's possible that MediumFactory::fromFile returns null if the
|
||||
// original image file no longer exists and this class instance was
|
||||
// retrieved from the page cache
|
||||
if (null !== $derivative) {
|
||||
$index = 2;
|
||||
$alt_widths = array_keys($this->alternatives);
|
||||
sort($alt_widths);
|
||||
|
||||
foreach ($alt_widths as $i => $key) {
|
||||
if ($width > $key) {
|
||||
$index += max($i, 1);
|
||||
}
|
||||
}
|
||||
|
||||
$basename = preg_replace('/(@\d+x){0,1}$/', "@{$width}w", $base->get('basename'), 1);
|
||||
$derivative->setImagePrettyName($basename);
|
||||
|
||||
$ratio = $base->get('width') / $width;
|
||||
$height = $derivative->get('height') / $ratio;
|
||||
|
||||
$derivative->resize($width, $height);
|
||||
$derivative->set('width', $width);
|
||||
$derivative->set('height', $height);
|
||||
|
||||
$this->addAlternative($ratio, $derivative);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parsedown element for source display mode
|
||||
*
|
||||
* @param array $attributes
|
||||
* @param boolean $reset
|
||||
* @return array
|
||||
*/
|
||||
public function sourceParsedownElement(array $attributes, $reset = true)
|
||||
{
|
||||
empty($attributes['src']) && $attributes['src'] = $this->url(false);
|
||||
|
||||
$srcset = $this->srcset($reset);
|
||||
if ($srcset) {
|
||||
empty($attributes['srcset']) && $attributes['srcset'] = $srcset;
|
||||
$attributes['sizes'] = $this->sizes();
|
||||
}
|
||||
|
||||
return [ 'name' => 'img', 'attributes' => $attributes ];
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset image.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
parent::reset();
|
||||
|
||||
if ($this->image) {
|
||||
$this->image();
|
||||
$this->querystring('');
|
||||
$this->filter();
|
||||
$this->clearAlternatives();
|
||||
}
|
||||
|
||||
$this->format = 'guess';
|
||||
$this->quality = $this->default_quality;
|
||||
|
||||
$this->debug_watermarked = false;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn the current Medium into a Link
|
||||
*
|
||||
* @param boolean $reset
|
||||
* @param array $attributes
|
||||
* @return Link
|
||||
*/
|
||||
public function link($reset = true, array $attributes = [])
|
||||
{
|
||||
$attributes['href'] = $this->url(false);
|
||||
$srcset = $this->srcset(false);
|
||||
if ($srcset) {
|
||||
$attributes['data-srcset'] = $srcset;
|
||||
}
|
||||
|
||||
return parent::link($reset, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn the current Medium into a Link with lightbox enabled
|
||||
*
|
||||
* @param int $width
|
||||
* @param int $height
|
||||
* @param boolean $reset
|
||||
* @return Link
|
||||
*/
|
||||
public function lightbox($width = null, $height = null, $reset = true)
|
||||
{
|
||||
if ($this->mode !== 'source') {
|
||||
$this->display('source');
|
||||
}
|
||||
|
||||
if ($width && $height) {
|
||||
$this->cropResize($width, $height);
|
||||
}
|
||||
|
||||
return parent::lightbox($width, $height, $reset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets or gets the quality of the image
|
||||
*
|
||||
* @param int $quality 0-100 quality
|
||||
* @return Medium
|
||||
*/
|
||||
public function quality($quality = null)
|
||||
{
|
||||
if ($quality) {
|
||||
if (!$this->image) {
|
||||
$this->image();
|
||||
}
|
||||
|
||||
$this->quality = $quality;
|
||||
return $this;
|
||||
}
|
||||
|
||||
return $this->quality;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets image output format.
|
||||
*
|
||||
* @param string $format
|
||||
* @return $this
|
||||
*/
|
||||
public function format($format)
|
||||
{
|
||||
if (!$this->image) {
|
||||
$this->image();
|
||||
}
|
||||
|
||||
$this->format = $format;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set or get sizes parameter for srcset media action
|
||||
*
|
||||
* @param string $sizes
|
||||
* @return string
|
||||
*/
|
||||
public function sizes($sizes = null)
|
||||
{
|
||||
|
||||
if ($sizes) {
|
||||
$this->sizes = $sizes;
|
||||
return $this;
|
||||
}
|
||||
|
||||
return empty($this->sizes) ? '100vw' : $this->sizes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to set the width attribute from Markdown or Twig
|
||||
* Examples: 
|
||||
* 
|
||||
* 
|
||||
* 
|
||||
* {{ page.media['myimg.png'].width().height().html }}
|
||||
* {{ page.media['myimg.png'].resize(100,200).width(100).height(200).html }}
|
||||
*
|
||||
* @param mixed $value A value or 'auto' or empty to use the width of the image
|
||||
* @return $this
|
||||
*/
|
||||
public function width($value = 'auto')
|
||||
{
|
||||
if (!$value || $value === 'auto')
|
||||
$this->attributes['width'] = $this->get('width');
|
||||
else
|
||||
$this->attributes['width'] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to set the height attribute from Markdown or Twig
|
||||
* Examples: 
|
||||
* 
|
||||
* 
|
||||
* 
|
||||
* {{ page.media['myimg.png'].width().height().html }}
|
||||
* {{ page.media['myimg.png'].resize(100,200).width(100).height(200).html }}
|
||||
*
|
||||
* @param mixed $value A value or 'auto' or empty to use the height of the image
|
||||
* @return $this
|
||||
*/
|
||||
public function height($value = 'auto')
|
||||
{
|
||||
if (!$value || $value === 'auto')
|
||||
$this->attributes['height'] = $this->get('height');
|
||||
else
|
||||
$this->attributes['height'] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Forward the call to the image processing method.
|
||||
*
|
||||
* @param string $method
|
||||
* @param mixed $args
|
||||
* @return $this|mixed
|
||||
*/
|
||||
public function __call($method, $args)
|
||||
{
|
||||
if ($method === 'cropZoom') {
|
||||
$method = 'zoomCrop';
|
||||
}
|
||||
|
||||
if (!\in_array($method, self::$magic_actions, true)) {
|
||||
return parent::__call($method, $args);
|
||||
}
|
||||
|
||||
// Always initialize image.
|
||||
if (!$this->image) {
|
||||
$this->image();
|
||||
}
|
||||
|
||||
try {
|
||||
call_user_func_array([$this->image, $method], $args);
|
||||
|
||||
foreach ($this->alternatives as $medium) {
|
||||
if (!$medium->image) {
|
||||
$medium->image();
|
||||
}
|
||||
|
||||
$args_copy = $args;
|
||||
|
||||
// regular image: resize 400x400 -> 200x200
|
||||
// --> @2x: resize 800x800->400x400
|
||||
if (isset(self::$magic_resize_actions[$method])) {
|
||||
foreach (self::$magic_resize_actions[$method] as $param) {
|
||||
if (isset($args_copy[$param])) {
|
||||
$args_copy[$param] *= $medium->get('ratio');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
call_user_func_array([$medium, $method], $args_copy);
|
||||
}
|
||||
} catch (\BadFunctionCallException $e) {
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets medium image, resets image manipulation operations.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
protected function image()
|
||||
{
|
||||
$locator = Grav::instance()['locator'];
|
||||
|
||||
$file = $this->get('filepath');
|
||||
|
||||
// Use existing cache folder or if it doesn't exist, create it.
|
||||
$cacheDir = $locator->findResource('cache://images', true) ?: $locator->findResource('cache://images', true, true);
|
||||
|
||||
// Make sure we free previous image.
|
||||
unset($this->image);
|
||||
|
||||
$this->image = ImageFile::open($file)
|
||||
->setCacheDir($cacheDir)
|
||||
->setActualCacheDir($cacheDir)
|
||||
->setPrettyName($this->getImagePrettyName());
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the image with cache.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function saveImage()
|
||||
{
|
||||
if (!$this->image) {
|
||||
return parent::path(false);
|
||||
}
|
||||
|
||||
$this->filter();
|
||||
|
||||
if (isset($this->result)) {
|
||||
return $this->result;
|
||||
}
|
||||
|
||||
if (!$this->debug_watermarked && $this->get('debug')) {
|
||||
$ratio = $this->get('ratio');
|
||||
if (!$ratio) {
|
||||
$ratio = 1;
|
||||
}
|
||||
|
||||
$locator = Grav::instance()['locator'];
|
||||
$overlay = $locator->findResource("system://assets/responsive-overlays/{$ratio}x.png") ?: $locator->findResource('system://assets/responsive-overlays/unknown.png');
|
||||
$this->image->merge(ImageFile::open($overlay));
|
||||
}
|
||||
|
||||
return $this->image->cacheFile($this->format, $this->quality);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter image by using user defined filter parameters.
|
||||
*
|
||||
* @param string $filter Filter to be used.
|
||||
*/
|
||||
public function filter($filter = 'image.filters.default')
|
||||
{
|
||||
$filters = (array) $this->get($filter, []);
|
||||
foreach ($filters as $params) {
|
||||
$params = (array) $params;
|
||||
$method = array_shift($params);
|
||||
$this->__call($method, $params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the image higher quality version
|
||||
*
|
||||
* @return ImageMedium the alternative version with higher quality
|
||||
*/
|
||||
public function higherQualityAlternative()
|
||||
{
|
||||
if ($this->alternatives) {
|
||||
$max = reset($this->alternatives);
|
||||
foreach($this->alternatives as $alternative)
|
||||
{
|
||||
if($alternative->quality() > $max->quality())
|
||||
{
|
||||
$max = $alternative;
|
||||
}
|
||||
}
|
||||
|
||||
return $max;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
||||
70
system/src/Grav/Common/Page/Medium/Link.php
Normal file
70
system/src/Grav/Common/Page/Medium/Link.php
Normal file
@@ -0,0 +1,70 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.Page
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
class Link implements RenderableInterface
|
||||
{
|
||||
use ParsedownHtmlTrait;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $attributes = [];
|
||||
protected $source;
|
||||
|
||||
/**
|
||||
* Construct.
|
||||
* @param array $attributes
|
||||
* @param Medium $medium
|
||||
*/
|
||||
public function __construct(array $attributes, Medium $medium)
|
||||
{
|
||||
$this->attributes = $attributes;
|
||||
$this->source = $medium->reset()->thumbnail('auto')->display('thumbnail');
|
||||
$this->source->linked = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an element (is array) that can be rendered by the Parsedown engine
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $alt
|
||||
* @param string $class
|
||||
* @param string $id
|
||||
* @param boolean $reset
|
||||
* @return array
|
||||
*/
|
||||
public function parsedownElement($title = null, $alt = null, $class = null, $id = null, $reset = true)
|
||||
{
|
||||
$innerElement = $this->source->parsedownElement($title, $alt, $class, $id, $reset);
|
||||
|
||||
return [
|
||||
'name' => 'a',
|
||||
'attributes' => $this->attributes,
|
||||
'handler' => is_string($innerElement) ? 'line' : 'element',
|
||||
'text' => $innerElement
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Forward the call to the source element
|
||||
*
|
||||
* @param string $method
|
||||
* @param mixed $args
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call($method, $args)
|
||||
{
|
||||
$this->source = call_user_func_array(array($this->source, $method), $args);
|
||||
|
||||
// Don't start nesting links, if user has multiple link calls in his
|
||||
// actions, we will drop the previous links.
|
||||
return $this->source instanceof Link ? $this->source : $this;
|
||||
}
|
||||
}
|
||||
596
system/src/Grav/Common/Page/Medium/Medium.php
Normal file
596
system/src/Grav/Common/Page/Medium/Medium.php
Normal file
@@ -0,0 +1,596 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.Page
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
use Grav\Common\File\CompiledYamlFile;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Data\Data;
|
||||
use Grav\Common\Data\Blueprint;
|
||||
use Grav\Common\Media\Interfaces\MediaObjectInterface;
|
||||
|
||||
class Medium extends Data implements RenderableInterface, MediaObjectInterface
|
||||
{
|
||||
use ParsedownHtmlTrait;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $mode = 'source';
|
||||
|
||||
/**
|
||||
* @var Medium
|
||||
*/
|
||||
protected $_thumbnail = null;
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $thumbnailTypes = [ 'page', 'default' ];
|
||||
|
||||
protected $thumbnailType = null;
|
||||
|
||||
/**
|
||||
* @var Medium[]
|
||||
*/
|
||||
protected $alternatives = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $attributes = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $styleAttributes = [];
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected $metadata = [];
|
||||
|
||||
/**
|
||||
* Construct.
|
||||
*
|
||||
* @param array $items
|
||||
* @param Blueprint $blueprint
|
||||
*/
|
||||
public function __construct($items = [], Blueprint $blueprint = null)
|
||||
{
|
||||
parent::__construct($items, $blueprint);
|
||||
|
||||
if (Grav::instance()['config']->get('system.media.enable_media_timestamp', true)) {
|
||||
$this->querystring('&' . Grav::instance()['cache']->getKey());
|
||||
}
|
||||
|
||||
$this->def('mime', 'application/octet-stream');
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
public function __clone()
|
||||
{
|
||||
// Allows future compatibility as parent::__clone() works.
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a copy of this media object
|
||||
*
|
||||
* @return Medium
|
||||
*/
|
||||
public function copy()
|
||||
{
|
||||
return clone $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return just metadata from the Medium object
|
||||
*
|
||||
* @return Data
|
||||
*/
|
||||
public function meta()
|
||||
{
|
||||
return new Data($this->items);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this medium exists or not
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function exists()
|
||||
{
|
||||
$path = $this->get('filepath');
|
||||
if (file_exists($path)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array containing just the metadata
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function metadata()
|
||||
{
|
||||
return $this->metadata;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add meta file for the medium.
|
||||
*
|
||||
* @param $filepath
|
||||
*/
|
||||
public function addMetaFile($filepath)
|
||||
{
|
||||
$this->metadata = (array)CompiledYamlFile::instance($filepath)->content();
|
||||
$this->merge($this->metadata);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add alternative Medium to this Medium.
|
||||
*
|
||||
* @param $ratio
|
||||
* @param Medium $alternative
|
||||
*/
|
||||
public function addAlternative($ratio, Medium $alternative)
|
||||
{
|
||||
if (!is_numeric($ratio) || $ratio === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
$alternative->set('ratio', $ratio);
|
||||
$width = $alternative->get('width');
|
||||
|
||||
$this->alternatives[$width] = $alternative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return string representation of the object (html).
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function __toString()
|
||||
{
|
||||
return $this->html();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return PATH to file.
|
||||
*
|
||||
* @param bool $reset
|
||||
* @return string path to file
|
||||
*/
|
||||
public function path($reset = true)
|
||||
{
|
||||
if ($reset) {
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
return $this->get('filepath');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the relative path to file
|
||||
*
|
||||
* @param bool $reset
|
||||
* @return mixed
|
||||
*/
|
||||
public function relativePath($reset = true)
|
||||
{
|
||||
if ($reset) {
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
return str_replace(GRAV_ROOT, '', $this->get('filepath'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return URL to file.
|
||||
*
|
||||
* @param bool $reset
|
||||
* @return string
|
||||
*/
|
||||
public function url($reset = true)
|
||||
{
|
||||
$output = preg_replace('|^' . preg_quote(GRAV_ROOT, '|') . '|', '', $this->get('filepath'));
|
||||
|
||||
$locator = Grav::instance()['locator'];
|
||||
if ($locator->isStream($output)) {
|
||||
$output = $locator->findResource($output, false);
|
||||
}
|
||||
|
||||
if ($reset) {
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
return trim(Grav::instance()['base_url'] . '/' . ltrim($output . $this->querystring() . $this->urlHash(), '/'), '\\');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get/set querystring for the file's url
|
||||
*
|
||||
* @param string $querystring
|
||||
* @param boolean $withQuestionmark
|
||||
* @return string
|
||||
*/
|
||||
public function querystring($querystring = null, $withQuestionmark = true)
|
||||
{
|
||||
if (!is_null($querystring)) {
|
||||
$this->set('querystring', ltrim($querystring, '?&'));
|
||||
|
||||
foreach ($this->alternatives as $alt) {
|
||||
$alt->querystring($querystring, $withQuestionmark);
|
||||
}
|
||||
}
|
||||
|
||||
$querystring = $this->get('querystring', '');
|
||||
|
||||
if ($withQuestionmark && !empty($querystring)) {
|
||||
return '?' . $querystring;
|
||||
} else {
|
||||
return $querystring;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get/set hash for the file's url
|
||||
*
|
||||
* @param string $hash
|
||||
* @param boolean $withHash
|
||||
* @return string
|
||||
*/
|
||||
public function urlHash($hash = null, $withHash = true)
|
||||
{
|
||||
if ($hash) {
|
||||
$this->set('urlHash', ltrim($hash, '#'));
|
||||
}
|
||||
|
||||
$hash = $this->get('urlHash', '');
|
||||
|
||||
if ($withHash && !empty($hash)) {
|
||||
return '#' . $hash;
|
||||
} else {
|
||||
return $hash;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an element (is array) that can be rendered by the Parsedown engine
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $alt
|
||||
* @param string $class
|
||||
* @param string $id
|
||||
* @param boolean $reset
|
||||
* @return array
|
||||
*/
|
||||
public function parsedownElement($title = null, $alt = null, $class = null, $id = null, $reset = true)
|
||||
{
|
||||
$attributes = $this->attributes;
|
||||
|
||||
$style = '';
|
||||
foreach ($this->styleAttributes as $key => $value) {
|
||||
if (is_numeric($key)) // Special case for inline style attributes, refer to style() method
|
||||
$style .= $value;
|
||||
else
|
||||
$style .= $key . ': ' . $value . ';';
|
||||
}
|
||||
if ($style) {
|
||||
$attributes['style'] = $style;
|
||||
}
|
||||
|
||||
if (empty($attributes['title'])) {
|
||||
if (!empty($title)) {
|
||||
$attributes['title'] = $title;
|
||||
} elseif (!empty($this->items['title'])) {
|
||||
$attributes['title'] = $this->items['title'];
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($attributes['alt'])) {
|
||||
if (!empty($alt)) {
|
||||
$attributes['alt'] = $alt;
|
||||
} elseif (!empty($this->items['alt'])) {
|
||||
$attributes['alt'] = $this->items['alt'];
|
||||
} elseif (!empty($this->items['alt_text'])) {
|
||||
$attributes['alt'] = $this->items['alt_text'];
|
||||
} else {
|
||||
$attributes['alt'] = '';
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($attributes['class'])) {
|
||||
if (!empty($class)) {
|
||||
$attributes['class'] = $class;
|
||||
} elseif (!empty($this->items['class'])) {
|
||||
$attributes['class'] = $this->items['class'];
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($attributes['id'])) {
|
||||
if (!empty($id)) {
|
||||
$attributes['id'] = $id;
|
||||
} elseif (!empty($this->items['id'])) {
|
||||
$attributes['id'] = $this->items['id'];
|
||||
}
|
||||
}
|
||||
|
||||
switch ($this->mode) {
|
||||
case 'text':
|
||||
$element = $this->textParsedownElement($attributes, false);
|
||||
break;
|
||||
case 'thumbnail':
|
||||
$element = $this->getThumbnail()->sourceParsedownElement($attributes, false);
|
||||
break;
|
||||
case 'source':
|
||||
$element = $this->sourceParsedownElement($attributes, false);
|
||||
break;
|
||||
}
|
||||
|
||||
if ($reset) {
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
$this->display('source');
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parsedown element for source display mode
|
||||
*
|
||||
* @param array $attributes
|
||||
* @param boolean $reset
|
||||
* @return array
|
||||
*/
|
||||
protected function sourceParsedownElement(array $attributes, $reset = true)
|
||||
{
|
||||
return $this->textParsedownElement($attributes, $reset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parsedown element for text display mode
|
||||
*
|
||||
* @param array $attributes
|
||||
* @param boolean $reset
|
||||
* @return array
|
||||
*/
|
||||
protected function textParsedownElement(array $attributes, $reset = true)
|
||||
{
|
||||
$text = empty($attributes['title']) ? empty($attributes['alt']) ? $this->get('filename') : $attributes['alt'] : $attributes['title'];
|
||||
|
||||
$element = [
|
||||
'name' => 'p',
|
||||
'attributes' => $attributes,
|
||||
'text' => $text
|
||||
];
|
||||
|
||||
if ($reset) {
|
||||
$this->reset();
|
||||
}
|
||||
|
||||
return $element;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset medium.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
$this->attributes = [];
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch display mode.
|
||||
*
|
||||
* @param string $mode
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function display($mode = 'source')
|
||||
{
|
||||
if ($this->mode === $mode) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
$this->mode = $mode;
|
||||
|
||||
return $mode === 'thumbnail' ? ($this->getThumbnail() ? $this->getThumbnail()->reset() : null) : $this->reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to determine if this media item has a thumbnail or not
|
||||
*
|
||||
* @param string $type;
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function thumbnailExists($type = 'page')
|
||||
{
|
||||
$thumbs = $this->get('thumbnails');
|
||||
if (isset($thumbs[$type])) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch thumbnail.
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function thumbnail($type = 'auto')
|
||||
{
|
||||
if ($type !== 'auto' && !in_array($type, $this->thumbnailTypes)) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
if ($this->thumbnailType !== $type) {
|
||||
$this->_thumbnail = null;
|
||||
}
|
||||
|
||||
$this->thumbnailType = $type;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Turn the current Medium into a Link
|
||||
*
|
||||
* @param boolean $reset
|
||||
* @param array $attributes
|
||||
* @return Link
|
||||
*/
|
||||
public function link($reset = true, array $attributes = [])
|
||||
{
|
||||
if ($this->mode !== 'source') {
|
||||
$this->display('source');
|
||||
}
|
||||
|
||||
foreach ($this->attributes as $key => $value) {
|
||||
empty($attributes['data-' . $key]) && $attributes['data-' . $key] = $value;
|
||||
}
|
||||
|
||||
empty($attributes['href']) && $attributes['href'] = $this->url();
|
||||
|
||||
return new Link($attributes, $this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn the current Medium into a Link with lightbox enabled
|
||||
*
|
||||
* @param int $width
|
||||
* @param int $height
|
||||
* @param boolean $reset
|
||||
* @return Link
|
||||
*/
|
||||
public function lightbox($width = null, $height = null, $reset = true)
|
||||
{
|
||||
$attributes = ['rel' => 'lightbox'];
|
||||
|
||||
if ($width && $height) {
|
||||
$attributes['data-width'] = $width;
|
||||
$attributes['data-height'] = $height;
|
||||
}
|
||||
|
||||
return $this->link($reset, $attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a class to the element from Markdown or Twig
|
||||
* Example:  or 
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function classes()
|
||||
{
|
||||
$classes = func_get_args();
|
||||
if (!empty($classes)) {
|
||||
$this->attributes['class'] = implode(',', (array)$classes);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an id to the element from Markdown or Twig
|
||||
* Example: 
|
||||
*
|
||||
* @param $id
|
||||
* @return $this
|
||||
*/
|
||||
public function id($id)
|
||||
{
|
||||
if (is_string($id)) {
|
||||
$this->attributes['id'] = trim($id);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to add an inline style attribute from Markdown or Twig
|
||||
* Example: 
|
||||
*
|
||||
* @param string $style
|
||||
* @return $this
|
||||
*/
|
||||
public function style($style)
|
||||
{
|
||||
$this->styleAttributes[] = rtrim($style, ';') . ';';
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow any action to be called on this medium from twig or markdown
|
||||
*
|
||||
* @param string $method
|
||||
* @param mixed $args
|
||||
* @return $this
|
||||
*/
|
||||
public function __call($method, $args)
|
||||
{
|
||||
$qs = $method;
|
||||
if (count($args) > 1 || (count($args) == 1 && !empty($args[0]))) {
|
||||
$qs .= '=' . implode(',', array_map(function ($a) {
|
||||
if (is_array($a)) {
|
||||
$a = '[' . implode(',', $a) . ']';
|
||||
}
|
||||
return rawurlencode($a);
|
||||
}, $args));
|
||||
}
|
||||
|
||||
if (!empty($qs)) {
|
||||
$this->querystring($this->querystring(null, false) . '&' . $qs);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the thumbnail Medium object
|
||||
*
|
||||
* @return ThumbnailImageMedium
|
||||
*/
|
||||
protected function getThumbnail()
|
||||
{
|
||||
if (!$this->_thumbnail) {
|
||||
$types = $this->thumbnailTypes;
|
||||
|
||||
if ($this->thumbnailType !== 'auto') {
|
||||
array_unshift($types, $this->thumbnailType);
|
||||
}
|
||||
|
||||
foreach ($types as $type) {
|
||||
$thumb = $this->get('thumbnails.' . $type, false);
|
||||
|
||||
if ($thumb) {
|
||||
$thumb = $thumb instanceof ThumbnailImageMedium ? $thumb : MediumFactory::fromFile($thumb, ['type' => 'thumbnail']);
|
||||
$thumb->parent = $this;
|
||||
}
|
||||
|
||||
if ($thumb) {
|
||||
$this->_thumbnail = $thumb;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->_thumbnail;
|
||||
}
|
||||
|
||||
}
|
||||
146
system/src/Grav/Common/Page/Medium/MediumFactory.php
Normal file
146
system/src/Grav/Common/Page/Medium/MediumFactory.php
Normal file
@@ -0,0 +1,146 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.Page
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Data\Blueprint;
|
||||
|
||||
class MediumFactory
|
||||
{
|
||||
/**
|
||||
* Create Medium from a file
|
||||
*
|
||||
* @param string $file
|
||||
* @param array $params
|
||||
* @return Medium
|
||||
*/
|
||||
public static function fromFile($file, array $params = [])
|
||||
{
|
||||
if (!file_exists($file)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$parts = pathinfo($file);
|
||||
$path = $parts['dirname'];
|
||||
$filename = $parts['basename'];
|
||||
$ext = $parts['extension'];
|
||||
$basename = $parts['filename'];
|
||||
|
||||
$config = Grav::instance()['config'];
|
||||
|
||||
$media_params = $config->get("media.types." . strtolower($ext));
|
||||
if (!$media_params) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$params += $media_params;
|
||||
|
||||
// Add default settings for undefined variables.
|
||||
$params += $config->get('media.types.defaults');
|
||||
$params += [
|
||||
'type' => 'file',
|
||||
'thumb' => 'media/thumb.png',
|
||||
'mime' => 'application/octet-stream',
|
||||
'filepath' => $file,
|
||||
'filename' => $filename,
|
||||
'basename' => $basename,
|
||||
'extension' => $ext,
|
||||
'path' => $path,
|
||||
'modified' => filemtime($file),
|
||||
'thumbnails' => []
|
||||
];
|
||||
|
||||
$locator = Grav::instance()['locator'];
|
||||
|
||||
$file = $locator->findResource("image://{$params['thumb']}");
|
||||
if ($file) {
|
||||
$params['thumbnails']['default'] = $file;
|
||||
}
|
||||
|
||||
return static::fromArray($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create Medium from array of parameters
|
||||
*
|
||||
* @param array $items
|
||||
* @param Blueprint|null $blueprint
|
||||
* @return Medium
|
||||
*/
|
||||
public static function fromArray(array $items = [], Blueprint $blueprint = null)
|
||||
{
|
||||
$type = isset($items['type']) ? $items['type'] : null;
|
||||
|
||||
switch ($type) {
|
||||
case 'image':
|
||||
return new ImageMedium($items, $blueprint);
|
||||
break;
|
||||
case 'thumbnail':
|
||||
return new ThumbnailImageMedium($items, $blueprint);
|
||||
break;
|
||||
case 'animated':
|
||||
case 'vector':
|
||||
return new StaticImageMedium($items, $blueprint);
|
||||
break;
|
||||
case 'video':
|
||||
return new VideoMedium($items, $blueprint);
|
||||
break;
|
||||
case 'audio':
|
||||
return new AudioMedium($items, $blueprint);
|
||||
break;
|
||||
default:
|
||||
return new Medium($items, $blueprint);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new ImageMedium by scaling another ImageMedium object.
|
||||
*
|
||||
* @param ImageMedium $medium
|
||||
* @param int $from
|
||||
* @param int $to
|
||||
* @return Medium|array
|
||||
*/
|
||||
public static function scaledFromMedium($medium, $from, $to)
|
||||
{
|
||||
if (! $medium instanceof ImageMedium) {
|
||||
return $medium;
|
||||
}
|
||||
|
||||
if ($to > $from) {
|
||||
return $medium;
|
||||
}
|
||||
|
||||
$ratio = $to / $from;
|
||||
$width = $medium->get('width') * $ratio;
|
||||
$height = $medium->get('height') * $ratio;
|
||||
|
||||
$prev_basename = $medium->get('basename');
|
||||
$basename = str_replace('@'.$from.'x', '@'.$to.'x', $prev_basename);
|
||||
|
||||
$debug = $medium->get('debug');
|
||||
$medium->set('debug', false);
|
||||
$medium->setImagePrettyName($basename);
|
||||
|
||||
$file = $medium->resize($width, $height)->path();
|
||||
|
||||
$medium->set('debug', $debug);
|
||||
$medium->setImagePrettyName($prev_basename);
|
||||
|
||||
$size = filesize($file);
|
||||
|
||||
$medium = self::fromFile($file);
|
||||
if ($medium) {
|
||||
$medium->set('size', $size);
|
||||
}
|
||||
|
||||
return ['file' => $medium, 'size' => $size];
|
||||
}
|
||||
}
|
||||
40
system/src/Grav/Common/Page/Medium/ParsedownHtmlTrait.php
Normal file
40
system/src/Grav/Common/Page/Medium/ParsedownHtmlTrait.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.Page
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
use Grav\Common\Markdown\Parsedown;
|
||||
|
||||
trait ParsedownHtmlTrait
|
||||
{
|
||||
/**
|
||||
* @var \Grav\Common\Markdown\Parsedown
|
||||
*/
|
||||
protected $parsedown = null;
|
||||
|
||||
/**
|
||||
* Return HTML markup from the medium.
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $alt
|
||||
* @param string $class
|
||||
* @param string $id
|
||||
* @param bool $reset
|
||||
* @return string
|
||||
*/
|
||||
public function html($title = null, $alt = null, $class = null, $id = null, $reset = true)
|
||||
{
|
||||
$element = $this->parsedownElement($title, $alt, $class, $id, $reset);
|
||||
|
||||
if (!$this->parsedown) {
|
||||
$this->parsedown = new Parsedown(null, null);
|
||||
}
|
||||
|
||||
return $this->parsedown->elementToHtml($element);
|
||||
}
|
||||
}
|
||||
35
system/src/Grav/Common/Page/Medium/RenderableInterface.php
Normal file
35
system/src/Grav/Common/Page/Medium/RenderableInterface.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.Page
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
interface RenderableInterface
|
||||
{
|
||||
/**
|
||||
* Return HTML markup from the medium.
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $alt
|
||||
* @param string $class
|
||||
* @param bool $reset
|
||||
* @return string
|
||||
*/
|
||||
public function html($title = null, $alt = null, $class = null, $reset = true);
|
||||
|
||||
/**
|
||||
* Return Parsedown Element from the medium.
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $alt
|
||||
* @param string $class
|
||||
* @param string $id
|
||||
* @param bool $reset
|
||||
* @return string
|
||||
*/
|
||||
public function parsedownElement($title = null, $alt = null, $class = null, $id = null, $reset = true);
|
||||
}
|
||||
28
system/src/Grav/Common/Page/Medium/StaticImageMedium.php
Normal file
28
system/src/Grav/Common/Page/Medium/StaticImageMedium.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.Page
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
class StaticImageMedium extends Medium
|
||||
{
|
||||
use StaticResizeTrait;
|
||||
|
||||
/**
|
||||
* Parsedown element for source display mode
|
||||
*
|
||||
* @param array $attributes
|
||||
* @param boolean $reset
|
||||
* @return array
|
||||
*/
|
||||
protected function sourceParsedownElement(array $attributes, $reset = true)
|
||||
{
|
||||
empty($attributes['src']) && $attributes['src'] = $this->url($reset);
|
||||
|
||||
return [ 'name' => 'img', 'attributes' => $attributes ];
|
||||
}
|
||||
}
|
||||
27
system/src/Grav/Common/Page/Medium/StaticResizeTrait.php
Normal file
27
system/src/Grav/Common/Page/Medium/StaticResizeTrait.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.Page
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
trait StaticResizeTrait
|
||||
{
|
||||
/**
|
||||
* Resize media by setting attributes
|
||||
*
|
||||
* @param int $width
|
||||
* @param int $height
|
||||
* @return $this
|
||||
*/
|
||||
public function resize($width = null, $height = null)
|
||||
{
|
||||
$this->styleAttributes['width'] = $width . 'px';
|
||||
$this->styleAttributes['height'] = $height . 'px';
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
130
system/src/Grav/Common/Page/Medium/ThumbnailImageMedium.php
Normal file
130
system/src/Grav/Common/Page/Medium/ThumbnailImageMedium.php
Normal file
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.Page
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
class ThumbnailImageMedium extends ImageMedium
|
||||
{
|
||||
/**
|
||||
* @var Medium
|
||||
*/
|
||||
public $parent = null;
|
||||
|
||||
/**
|
||||
* @var boolean
|
||||
*/
|
||||
public $linked = false;
|
||||
|
||||
/**
|
||||
* Return srcset string for this Medium and its alternatives.
|
||||
*
|
||||
* @param bool $reset
|
||||
* @return string
|
||||
*/
|
||||
public function srcset($reset = true)
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an element (is array) that can be rendered by the Parsedown engine
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $alt
|
||||
* @param string $class
|
||||
* @param string $id
|
||||
* @param boolean $reset
|
||||
* @return array
|
||||
*/
|
||||
public function parsedownElement($title = null, $alt = null, $class = null, $id = null, $reset = true)
|
||||
{
|
||||
return $this->bubble('parsedownElement', [$title, $alt, $class, $id, $reset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return HTML markup from the medium.
|
||||
*
|
||||
* @param string $title
|
||||
* @param string $alt
|
||||
* @param string $class
|
||||
* @param string $id
|
||||
* @param bool $reset
|
||||
* @return string
|
||||
*/
|
||||
public function html($title = null, $alt = null, $class = null, $id = null, $reset = true)
|
||||
{
|
||||
return $this->bubble('html', [$title, $alt, $class, $id, $reset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch display mode.
|
||||
*
|
||||
* @param string $mode
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function display($mode = 'source')
|
||||
{
|
||||
return $this->bubble('display', [$mode], false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch thumbnail.
|
||||
*
|
||||
* @param string $type
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function thumbnail($type = 'auto')
|
||||
{
|
||||
$this->bubble('thumbnail', [$type], false);
|
||||
return $this->bubble('getThumbnail', [], false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn the current Medium into a Link
|
||||
*
|
||||
* @param boolean $reset
|
||||
* @param array $attributes
|
||||
* @return Link
|
||||
*/
|
||||
public function link($reset = true, array $attributes = [])
|
||||
{
|
||||
return $this->bubble('link', [$reset, $attributes], false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Turn the current Medium into a Link with lightbox enabled
|
||||
*
|
||||
* @param int $width
|
||||
* @param int $height
|
||||
* @param boolean $reset
|
||||
* @return Link
|
||||
*/
|
||||
public function lightbox($width = null, $height = null, $reset = true)
|
||||
{
|
||||
return $this->bubble('lightbox', [$width, $height, $reset], false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bubble a function call up to either the superclass function or the parent Medium instance
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $arguments
|
||||
* @param boolean $testLinked
|
||||
* @return Medium
|
||||
*/
|
||||
protected function bubble($method, array $arguments = [], $testLinked = true)
|
||||
{
|
||||
if (!$testLinked || $this->linked) {
|
||||
return $this->parent ? call_user_func_array(array($this->parent, $method), $arguments) : $this;
|
||||
}
|
||||
|
||||
return call_user_func_array(array($this, 'parent::' . $method), $arguments);
|
||||
}
|
||||
}
|
||||
144
system/src/Grav/Common/Page/Medium/VideoMedium.php
Normal file
144
system/src/Grav/Common/Page/Medium/VideoMedium.php
Normal file
@@ -0,0 +1,144 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.Page
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Page\Medium;
|
||||
|
||||
class VideoMedium extends Medium
|
||||
{
|
||||
use StaticResizeTrait;
|
||||
|
||||
/**
|
||||
* Parsedown element for source display mode
|
||||
*
|
||||
* @param array $attributes
|
||||
* @param boolean $reset
|
||||
* @return array
|
||||
*/
|
||||
protected function sourceParsedownElement(array $attributes, $reset = true)
|
||||
{
|
||||
$location = $this->url($reset);
|
||||
|
||||
return [
|
||||
'name' => 'video',
|
||||
'text' => '<source src="' . $location . '">Your browser does not support the video tag.',
|
||||
'attributes' => $attributes
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to set or remove the HTML5 default controls
|
||||
*
|
||||
* @param bool $display
|
||||
* @return $this
|
||||
*/
|
||||
public function controls($display = true)
|
||||
{
|
||||
if($display) {
|
||||
$this->attributes['controls'] = true;
|
||||
} else {
|
||||
unset($this->attributes['controls']);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to set the video's poster image
|
||||
*
|
||||
* @param $urlImage
|
||||
* @return $this
|
||||
*/
|
||||
public function poster($urlImage)
|
||||
{
|
||||
$this->attributes['poster'] = $urlImage;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to set the loop attribute
|
||||
*
|
||||
* @param bool $status
|
||||
* @return $this
|
||||
*/
|
||||
public function loop($status = false)
|
||||
{
|
||||
if($status) {
|
||||
$this->attributes['loop'] = true;
|
||||
} else {
|
||||
unset($this->attributes['loop']);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to set the autoplay attribute
|
||||
*
|
||||
* @param bool $status
|
||||
* @return $this
|
||||
*/
|
||||
public function autoplay($status = false)
|
||||
{
|
||||
if($status) {
|
||||
$this->attributes['autoplay'] = true;
|
||||
} else {
|
||||
unset($this->attributes['autoplay']);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to set the playsinline attribute
|
||||
*
|
||||
* @param bool $status
|
||||
* @return $this
|
||||
*/
|
||||
public function playsinline($status = false)
|
||||
{
|
||||
if($status) {
|
||||
$this->attributes['playsinline'] = true;
|
||||
} else {
|
||||
unset($this->attributes['playsinline']);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows to set the muted attribute
|
||||
*
|
||||
* @param bool $status
|
||||
* @return $this
|
||||
*/
|
||||
public function muted($status = false)
|
||||
{
|
||||
if($status) {
|
||||
$this->attributes['muted'] = true;
|
||||
} else {
|
||||
unset($this->attributes['muted']);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset medium.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function reset()
|
||||
{
|
||||
parent::reset();
|
||||
|
||||
$this->attributes['controls'] = true;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
2994
system/src/Grav/Common/Page/Page.php
Normal file
2994
system/src/Grav/Common/Page/Page.php
Normal file
File diff suppressed because it is too large
Load Diff
1378
system/src/Grav/Common/Page/Pages.php
Normal file
1378
system/src/Grav/Common/Page/Pages.php
Normal file
File diff suppressed because it is too large
Load Diff
140
system/src/Grav/Common/Page/Types.php
Normal file
140
system/src/Grav/Common/Page/Types.php
Normal file
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
/**
|
||||
* @package Grav.Common.Page
|
||||
*
|
||||
* @copyright Copyright (C) 2015 - 2018 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Page;
|
||||
|
||||
use Grav\Common\Filesystem\Folder;
|
||||
use Grav\Common\Grav;
|
||||
use RocketTheme\Toolbox\ArrayTraits\ArrayAccess;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Constructor;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Countable;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Export;
|
||||
use RocketTheme\Toolbox\ArrayTraits\Iterator;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
|
||||
class Types implements \ArrayAccess, \Iterator, \Countable
|
||||
{
|
||||
use ArrayAccess, Constructor, Iterator, Countable, Export;
|
||||
|
||||
protected $items;
|
||||
protected $systemBlueprints;
|
||||
|
||||
public function register($type, $blueprint = null)
|
||||
{
|
||||
if (!isset($this->items[$type])) {
|
||||
$this->items[$type] = [];
|
||||
} elseif (!$blueprint) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$blueprint && $this->systemBlueprints) {
|
||||
$blueprint = isset($this->systemBlueprints[$type]) ? $this->systemBlueprints[$type] : $this->systemBlueprints['default'];
|
||||
}
|
||||
|
||||
if ($blueprint) {
|
||||
array_unshift($this->items[$type], $blueprint);
|
||||
}
|
||||
}
|
||||
|
||||
public function scanBlueprints($uri)
|
||||
{
|
||||
if (!is_string($uri)) {
|
||||
throw new \InvalidArgumentException('First parameter must be URI');
|
||||
}
|
||||
|
||||
if (!$this->systemBlueprints) {
|
||||
$this->systemBlueprints = $this->findBlueprints('blueprints://pages');
|
||||
|
||||
// Register default by default.
|
||||
$this->register('default');
|
||||
|
||||
$this->register('external');
|
||||
}
|
||||
|
||||
foreach ($this->findBlueprints($uri) as $type => $blueprint) {
|
||||
$this->register($type, $blueprint);
|
||||
}
|
||||
}
|
||||
|
||||
public function scanTemplates($uri)
|
||||
{
|
||||
if (!is_string($uri)) {
|
||||
throw new \InvalidArgumentException('First parameter must be URI');
|
||||
}
|
||||
|
||||
$options = [
|
||||
'compare' => 'Filename',
|
||||
'pattern' => '|\.html\.twig$|',
|
||||
'filters' => [
|
||||
'value' => '|\.html\.twig$|'
|
||||
],
|
||||
'value' => 'Filename',
|
||||
'recursive' => false
|
||||
];
|
||||
|
||||
foreach (Folder::all($uri, $options) as $type) {
|
||||
$this->register($type);
|
||||
}
|
||||
|
||||
$modular_uri = rtrim($uri, '/') . '/modular';
|
||||
if (is_dir($modular_uri)) {
|
||||
foreach (Folder::all($modular_uri, $options) as $type) {
|
||||
$this->register('modular/' . $type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function pageSelect()
|
||||
{
|
||||
$list = [];
|
||||
foreach ($this->items as $name => $file) {
|
||||
if (strpos($name, '/')) {
|
||||
continue;
|
||||
}
|
||||
$list[$name] = ucfirst(strtr($name, '_', ' '));
|
||||
}
|
||||
ksort($list);
|
||||
return $list;
|
||||
}
|
||||
|
||||
public function modularSelect()
|
||||
{
|
||||
$list = [];
|
||||
foreach ($this->items as $name => $file) {
|
||||
if (strpos($name, 'modular/') !== 0) {
|
||||
continue;
|
||||
}
|
||||
$list[$name] = trim(ucfirst(strtr(basename($name), '_', ' ')));
|
||||
}
|
||||
ksort($list);
|
||||
return $list;
|
||||
}
|
||||
|
||||
private function findBlueprints($uri)
|
||||
{
|
||||
$options = [
|
||||
'compare' => 'Filename',
|
||||
'pattern' => '|\.yaml$|',
|
||||
'filters' => [
|
||||
'key' => '|\.yaml$|'
|
||||
],
|
||||
'key' => 'SubPathName',
|
||||
'value' => 'PathName',
|
||||
];
|
||||
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = Grav::instance()['locator'];
|
||||
if ($locator->isStream($uri)) {
|
||||
$options['value'] = 'Url';
|
||||
}
|
||||
|
||||
$list = Folder::all($uri, $options);
|
||||
|
||||
return $list;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user