restored dashboard from usable

This commit is contained in:
Bachir Soussi Chiadmi
2018-10-25 16:18:42 +02:00
commit abc70f9e12
454 changed files with 46195 additions and 0 deletions
+549
View File
@@ -0,0 +1,549 @@
<?php
namespace Grav\Common\Page;
use Grav\Common\Grav;
use Grav\Common\Iterator;
use Grav\Common\Utils;
/**
* Collection of Pages.
*
* @author RocketTheme
* @license MIT
*/
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;
}
/**
*
* Create a copy of this collection
*
* @return static
*/
public function copy()
{
return new static($this->items, $this->params, $this->pages);
}
/**
* 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;
}
/**
* Remove item from the list.
*
* @param Page|string|null $key
*
* @return $this|void
* @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
*
* @return $this
*/
public function order($by, $dir = 'asc', $manual = null)
{
$this->items = $this->pages->sortCollection($this, $by, $dir, $manual);
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 Page Item in the array the the current position.
*/
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) : strtotime("now +1000 years");
$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 && $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;
}
}
+15
View File
@@ -0,0 +1,15 @@
<?php
namespace Grav\Common\Page;
use RocketTheme\Toolbox\ArrayTraits\Constructor;
use RocketTheme\Toolbox\ArrayTraits\NestedArrayAccess;
/**
* Class Header
* @package Grav\Common\Page
*/
class Header implements \ArrayAccess
{
use NestedArrayAccess, Constructor;
}
+258
View File
@@ -0,0 +1,258 @@
<?php
namespace Grav\Common\Page;
use Grav\Common\Getters;
use Grav\Common\GravTrait;
use Grav\Common\Page\Medium\Medium;
use Grav\Common\Page\Medium\MediumFactory;
/**
* Media is a holder object that contains references to the media of page. This object is created and
* populated during the getMedia() method in the Pages object
*
* @author RocketTheme
* @license MIT
*/
class Media extends Getters
{
use GravTrait;
protected $gettersVariable = 'instances';
protected $path;
protected $instances = [];
protected $images = [];
protected $videos = [];
protected $audios = [];
protected $files = [];
/**
* @param $path
*/
public function __construct($path)
{
// Handle special cases where page doesn't exist in filesystem.
if (!is_dir($path)) {
return;
}
$this->path = $path;
$iterator = new \FilesystemIterator($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->getBasename() === '.DS_Store') {
continue;
}
// Find out what type we're dealing with
list($basename, $ext, $type, $extra) = $this->getFileParts($info->getFilename());
$media["{$basename}.{$ext}"] = isset($media["{$basename}.{$ext}"]) ? $media["{$basename}.{$ext}"] : [];
if ($type === 'alternative') {
$media["{$basename}.{$ext}"][$type] = isset($media["{$basename}.{$ext}"][$type]) ? $media["{$basename}.{$ext}"][$type] : [];
$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']);
}
}
}
// Create the base medium
if (!empty($types['base'])) {
$medium = MediumFactory::fromFile($types['base']['file']);
$medium && $medium->set('size', $types['base']['size']);
} else if (!empty($types['alternative'])) {
$altMedium = reset($types['alternative']);
$ratio = key($types['alternative']);
$altMedium = $altMedium['file'];
$medium = MediumFactory::scaledFromMedium($altMedium, $ratio, 1)['file'];
}
if (!$medium) {
continue;
}
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=2; $i < $max; $i++) {
if (isset($alternatives[$i])) {
continue;
}
$types['alternative'][$i] = MediumFactory::scaledFromMedium($alternatives[$max]['file'], $max, $i);
}
foreach ($types['alternative'] as $ratio => $altMedium) {
$medium->addAlternative($ratio, $altMedium['file']);
}
}
$this->add($name, $medium);
}
}
/**
* Get medium by filename.
*
* @param string $filename
* @return Medium|null
*/
public function get($filename)
{
return isset($this->instances[$filename]) ? $this->instances[$filename] : null;
}
/**
* Get a list of all media.
*
* @return array|Medium[]
*/
public function all()
{
ksort($this->instances, SORT_NATURAL | SORT_FLAG_CASE);
return $this->instances;
}
/**
* Get a list of all image media.
*
* @return array|Medium[]
*/
public function images()
{
ksort($this->images, SORT_NATURAL | SORT_FLAG_CASE);
return $this->images;
}
/**
* Get a list of all video media.
*
* @return array|Medium[]
*/
public function videos()
{
ksort($this->videos, SORT_NATURAL | SORT_FLAG_CASE);
return $this->videos;
}
/**
* Get a list of all audio media.
*
* @return array|Medium[]
*/
public function audios()
{
ksort($this->audios, SORT_NATURAL | SORT_FLAG_CASE);
return $this->audios;
}
/**
* Get a list of all file media.
*
* @return array|Medium[]
*/
public function files()
{
ksort($this->files, SORT_NATURAL | SORT_FLAG_CASE);
return $this->files;
}
/**
* @internal
*/
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;
}
}
/**
* Get filename, extension and meta part.
*
* @param string $filename
* @return array
*/
protected function getFileParts($filename)
{
$fileParts = explode('.', $filename);
$name = array_shift($fileParts);
$type = 'base';
$extra = null;
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 {
$extension = null;
while (($part = array_shift($fileParts)) !== null) {
if ($part != 'meta' && $part != 'thumb') {
if (isset($extension)) {
$name .= '.' . $extension;
}
$extension = $part;
} else {
$type = $part;
$extra = '.' . $part . '.' . implode('.', $fileParts);
break;
}
}
}
return array($name, $extension, $type, $extra);
}
}
@@ -0,0 +1,38 @@
<?php
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
];
}
/**
* Reset medium.
*
* @return $this
*/
public function reset()
{
parent::reset();
$this->attributes['controls'] = true;
return $this;
}
}
@@ -0,0 +1,96 @@
<?php
namespace Grav\Common\Page\Medium;
use Grav\Common\GravTrait;
use Gregwar\Image\Exceptions\GenerationError;
use RocketTheme\Toolbox\Event\Event;
use Gregwar\Image\Image;
class ImageFile extends Image
{
use GravTrait;
/**
* 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 mixed|string
*/
public function cacheFile($type = 'jpg', $quality = 80, $actual = false)
{
if ($type == 'guess') {
$type = $this->guessType();
}
if (!count($this->operations) && $type == $this->guessType() && !$this->forceCache) {
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);
}
self::getGrav()->fireEvent('onImageMediumSaved', new Event(['image' => $target]));
};
// Asking the cache for the cacheFile
try {
$perms = self::getGrav()['config']->get('system.images.cache_perms', '0755');
$perms = octdec($perms);
$file = $this->cache->setDirectoryMode($perms)->getOrCreateFile($cacheFile, $conditions, $generate, $actual);
} catch (GenerationError $e) {
$file = $e->getNewFile();
}
if ($actual) {
return $file;
} else {
return $this->getFilename($file);
}
}
}
@@ -0,0 +1,555 @@
<?php
namespace Grav\Common\Page\Medium;
use Grav\Common\Utils;
use Grav\Common\Data\Blueprint;
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'
];
/**
* @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 array
*/
protected $derivatives = [];
/**
* @var string
*/
protected $sizes = '100vw';
/**
* Construct.
*
* @param array $items
* @param Blueprint $blueprint
*/
public function __construct($items = [], Blueprint $blueprint = null)
{
parent::__construct($items, $blueprint);
$config = self::$grav['config'];
$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();
}
}
/**
* 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;
}
/**
* 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)
{
$image_path = self::$grav['locator']->findResource('cache://images', true);
$image_dir = self::$grav['locator']->findResource('cache://images', false);
$saved_image_path = $this->saveImage();
$output = preg_replace('|^' . preg_quote(GRAV_ROOT) . '|', '', $saved_image_path);
if (Utils::startsWith($output, $image_path)) {
$output = '/' . $image_dir . preg_replace('|^' . preg_quote($image_path) . '|', '', $output);
}
if ($reset) {
$this->reset();
}
return self::$grav['base_url'] . $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) && empty($this->derivatives)) {
if ($reset) {
$this->reset();
}
return '';
}
if (!empty($this->derivatives)) {
asort($this->derivatives);
foreach ($this->derivatives as $url => $width) {
$srcset[] = $url . ' ' . $width . 'w';
}
$srcset[] = $this->url($reset) . ' ' . $this->get('width') . 'w';
}
else {
$srcset = [ $this->url($reset) . ' ' . $this->get('width') . 'w' ];
foreach ($this->alternatives as $ratio => $medium) {
$srcset[] = $medium->url($reset) . ' ' . $medium->get('width') . 'w';
}
}
return implode(', ', $srcset);
}
/**
* Generate derivatives
*
* @param int $min_width
* @param int $max_width
* @param int $step
* @return $this
*/
public function derivatives($min_width, $max_width, $step = 200) {
$width = $min_width;
// Do not upscale images.
if ($max_width > $this->get('width')) {
$max_width = $this->get('width');
}
while ($width <= $max_width) {
$ratio = $width / $this->get('width');
$derivative = MediumFactory::scaledFromMedium($this, 1, $ratio);
if (is_array($derivative)) {
$this->addDerivative($derivative['file']);
}
$width += $step;
}
return $this;
}
/**
* Add a derivative
*
* @param ImageMedium $image
*/
public function addDerivative(ImageMedium $image) {
$this->derivatives[$image->url()] = $image->get('width');
}
/**
* 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->image->clearOperations(); // Clear previously applied operations
$this->querystring('');
$this->filter();
}
$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 $this
*/
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: ![Example](myimg.png?width=200&height=400)
* ![Example](myimg.png?resize=100,200&width=100&height=200)
* ![Example](myimg.png?width=auto&height=auto)
* ![Example](myimg.png?width&height)
* {{ 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: ![Example](myimg.png?width=200&height=400)
* ![Example](myimg.png?resize=100,200&width=100&height=200)
* ![Example](myimg.png?width=auto&height=auto)
* ![Example](myimg.png?width&height)
* {{ 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)) {
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 $ratio => $medium) {
$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] = (int) $args_copy[$param] * $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 = self::$grav['locator'];
$file = $this->get('filepath');
$cacheDir = $locator->findResource('cache://images', true);
$this->image = ImageFile::open($file)
->setCacheDir($cacheDir)
->setActualCacheDir($cacheDir)
->setPrettyName(basename($this->get('basename')));
$this->filter();
return $this;
}
/**
* Save the image with cache.
*
* @return mixed|string
*/
protected function saveImage()
{
if (!$this->image) {
return parent::path(false);
}
if (isset($this->result)) {
return $this->result;
}
if ($this->get('debug') && !$this->debug_watermarked) {
$ratio = $this->get('ratio');
if (!$ratio) {
$ratio = 1;
}
$locator = self::$grav['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;
} else {
return $this;
}
}
}
@@ -0,0 +1,65 @@
<?php
namespace Grav\Common\Page\Medium;
use Grav\Common\GravTrait;
class Link implements RenderableInterface
{
use GravTrait;
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 boolean $reset
* @return array
*/
public function parsedownElement($title = null, $alt = null, $class = null, $reset = true)
{
$innerElement = $this->source->parsedownElement($title, $alt, $class, $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 $this|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;
}
}
@@ -0,0 +1,494 @@
<?php
namespace Grav\Common\Page\Medium;
use Grav\Common\File\CompiledYamlFile;
use Grav\Common\GravTrait;
use Grav\Common\Data\Data;
use Grav\Common\Data\Blueprint;
/**
* The Medium is a general class for multimedia objects in Grav pages, specific implementations will derive from
*
* @author Grav
* @license MIT
*
*/
class Medium extends Data implements RenderableInterface
{
use GravTrait;
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 = [];
/**
* Construct.
*
* @param array $items
* @param Blueprint $blueprint
*/
public function __construct($items = [], Blueprint $blueprint = null)
{
parent::__construct($items, $blueprint);
if (self::getGrav()['config']->get('system.media.enable_media_timestamp', true)) {
$this->querystring('&' . self::getGrav()['cache']->getKey());
}
$this->def('mime', 'application/octet-stream');
$this->reset();
}
/**
* Return just metadata from the Medium object
*
* @return $this
*/
public function meta()
{
return new Data($this->items);
}
/**
* Add meta file for the medium.
*
* @param $filepath
*/
public function addMetaFile($filepath)
{
$this->merge(CompiledYamlFile::instance($filepath)->content());
}
/**
* 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);
$this->alternatives[(float) $ratio] = $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 URL to file.
*
* @param bool $reset
* @return string
*/
public function url($reset = true)
{
$output = preg_replace('|^' . preg_quote(GRAV_ROOT) . '|', '', $this->get('filepath'));
if ($reset) {
$this->reset();
}
return self::$grav['base_url'] . $output . $this->querystring() . $this->urlHash();
}
/**
* Get/set querystring for the file's url
*
* @param string $hash
* @param boolean $withHash
* @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 boolean $reset
* @return array
*/
public function parsedownElement($title = null, $alt = null, $class = 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'];
}
}
if (empty($attributes['class'])) {
if (!empty($class)) {
$attributes['class'] = $class;
} elseif (!empty($this->items['class'])) {
$attributes['class'] = $this->items['class'];
}
}
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()->reset() : $this->reset();
}
/**
* 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: ![Example](myimg.png?classes=float-left) or ![Example](myimg.png?classes=myclass1,myclass2)
*
* @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: ![Example](myimg.png?id=primary-img)
*
* @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: ![Example](myimg.png?style=float:left)
*
* @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) { return urlencode($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;
}
}
@@ -0,0 +1,148 @@
<?php
namespace Grav\Common\Page\Medium;
use Grav\Common\GravTrait;
use Grav\Common\Data\Blueprint;
/**
* MediumFactory can be used to more easily create various Medium objects from files or arrays, it should
* contain most logic for instantiating a Medium object.
*
* @author Grav
* @license MIT
*
*/
class MediumFactory
{
use GravTrait;
/**
* 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;
}
$path = dirname($file);
$filename = basename($file);
$parts = explode('.', $filename);
$ext = array_pop($parts);
$basename = implode('.', $parts);
$config = self::getGrav()['config'];
$media_params = $config->get("media.".strtolower($ext));
if (!$media_params) {
return null;
}
$params += $media_params;
// Add default settings for undefined variables.
$params += $config->get('media.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 = self::getGrav()['locator'];
$lookup = $locator->findResources('image://');
foreach ($lookup as $lookupPath) {
if (is_file($lookupPath . '/' . $params['thumb'])) {
$params['thumbnails']['default'] = $lookupPath . '/' . $params['thumb'];
break;
}
}
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
*/
public static function scaledFromMedium($medium, $from, $to)
{
if (! $medium instanceof ImageMedium) {
return $medium;
}
if ($to > $from) {
return $medium;
}
$ratio = $to / $from;
$width = (int) ($medium->get('width') * $ratio);
$height = (int) ($medium->get('height') * $ratio);
$basename = $medium->get('basename');
$basename = str_replace('@'.$from.'x', '@'.$to.'x', $basename);
$debug = $medium->get('debug');
$medium->set('debug', false);
$file = $medium->resize($width, $height)->path();
$medium->set('debug', $debug);
$size = filesize($file);
$medium = self::fromFile($file);
$medium->set('size', $size);
return ['file' => $medium, 'size' => $size];
}
}
@@ -0,0 +1,31 @@
<?php
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 $class
* @param bool $reset
* @return string
*/
public function html($title = null, $alt = null, $class = null, $reset = true)
{
$element = $this->parsedownElement($title, $alt, $class, $reset);
if (!$this->parsedown) {
$this->parsedown = new Parsedown(null, null);
}
return $this->parsedown->elementToHtml($element);
}
}
@@ -0,0 +1,34 @@
<?php
namespace Grav\Common\Page\Medium;
/**
* Renderable Medium objects can be rendered to HTML markup and Parsedown objects
*
* @author Grav
* @license MIT
*
*/
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 bool $reset
* @return string
*/
public function parsedownElement($title = null, $alt = null, $class = null, $reset = true);
}
@@ -0,0 +1,28 @@
<?php
namespace Grav\Common\Page\Medium;
/**
* The Image medium holds information related to an individual image. These are then stored in the Media object.
*
* @author Grav
* @license MIT
*
*/
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' => 'image', 'attributes' => $attributes ];
}
}
@@ -0,0 +1,20 @@
<?php
namespace Grav\Common\Page\Medium;
trait StaticResizeTrait
{
/**
* Resize media by setting attributes
*
* @param int $width
* @param int $height
* @return Medium
*/
public function resize($width = null, $height = null)
{
$this->styleAttributes['width'] = $width . 'px';
$this->styleAttributes['height'] = $height . 'px';
return $this;
}
}
@@ -0,0 +1,121 @@
<?php
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 boolean $reset
* @return array
*/
public function parsedownElement($title = null, $alt = null, $class = null, $reset = true)
{
return $this->bubble('parsedownElement', [$title, $alt, $class, $reset]);
}
/**
* 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 $this->bubble('html', [$title, $alt, $class, $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;
} else {
return call_user_func_array(array($this, 'parent::' . $method), $arguments);
}
}
}
@@ -0,0 +1,38 @@
<?php
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
];
}
/**
* Reset medium.
*
* @return $this
*/
public function reset()
{
parent::reset();
$this->attributes['controls'] = true;
return $this;
}
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+113
View File
@@ -0,0 +1,113 @@
<?php
namespace Grav\Common\Page;
use Grav\Common\Filesystem\Folder;
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;
class Types implements \ArrayAccess, \Iterator, \Countable
{
use ArrayAccess, Constructor, Iterator, Countable, Export;
protected $items;
protected $systemBlueprints;
public function register($type, $blueprint = null)
{
if (!$blueprint && $this->systemBlueprints && isset($this->systemBlueprints[$type])) {
$useBlueprint = $this->systemBlueprints[$type];
} else {
$useBlueprint = $blueprint;
}
if ($blueprint || empty($this->items[$type])) {
$this->items[$type] = $useBlueprint;
}
}
public function scanBlueprints($paths)
{
$this->items = $this->findBlueprints($paths) + $this->items;
}
public function scanTemplates($paths)
{
$options = [
'compare' => 'Filename',
'pattern' => '|\.html\.twig$|',
'filters' => [
'value' => '|\.html\.twig$|'
],
'value' => 'Filename',
'recursive' => false
];
if (!$this->systemBlueprints) {
$this->systemBlueprints = $this->findBlueprints('blueprints://pages');
}
// register default by default
$this->register('default');
foreach ((array) $paths as $path) {
foreach (Folder::all($path, $options) as $type) {
$this->register($type);
}
$modular_path = rtrim($path, '/') . '/modular';
if (file_exists($modular_path)) {
foreach (Folder::all($modular_path, $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($paths)
{
$options = [
'compare' => 'Filename',
'pattern' => '|\.yaml$|',
'filters' => [
'key' => '|\.yaml$|'
],
'key' => 'SubPathName',
'value' => 'PathName',
];
$list = [];
foreach ((array) $paths as $path) {
$list += Folder::all($path, $options);
}
return $list;
}
}