123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679 |
- <?php
- /**
- * @package Grav\Common\Page
- *
- * @copyright Copyright (C) 2015 - 2019 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;
- use Grav\Common\Utils;
- /**
- * Class Medium
- * @package Grav\Common\Page\Medium
- *
- * @property string $mime
- */
- 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 = [];
- /**
- * @var array
- */
- protected $medium_querystring = [];
- protected $timestamp;
- /**
- * 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->timestamp = 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;
- }
- /**
- * Get file modification time for the medium.
- *
- * @return int|null
- */
- public function modified()
- {
- $path = $this->get('filepath');
- if (!file_exists($path)) {
- return null;
- }
- return filemtime($path) ?: null;
- }
- /**
- * @return int
- */
- public function size()
- {
- $path = $this->get('filepath');
- if (!file_exists($path)) {
- return 0;
- }
- return filesize($path) ?: 0;
- }
- /**
- * Set querystring to file modification timestamp (or value provided as a parameter).
- *
- * @param string|int|null $timestamp
- * @return $this
- */
- public function setTimestamp($timestamp = null)
- {
- $this->timestamp = (string)($timestamp ?? $this->modified());
- return $this;
- }
- /**
- * Returns an array containing just the metadata
- *
- * @return array
- */
- public function metadata()
- {
- return $this->metadata;
- }
- /**
- * Add meta file for the medium.
- *
- * @param string $filepath
- */
- public function addMetaFile($filepath)
- {
- $this->metadata = (array)CompiledYamlFile::instance($filepath)->content();
- $this->merge($this->metadata);
- }
- /**
- * Add alternative Medium to this Medium.
- *
- * @param int|float $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)
- {
- $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 str_replace(GRAV_ROOT, '', $output);
- }
- /**
- * 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'] . '/' . $this->urlQuerystring($output), '\\');
- }
- /**
- * Get/set querystring for the file's url
- *
- * @param string $querystring
- * @param bool $withQuestionmark
- * @return string
- */
- public function querystring($querystring = null, $withQuestionmark = true)
- {
- if (null !== $querystring) {
- $this->medium_querystring[] = ltrim($querystring, '?&');
- foreach ($this->alternatives as $alt) {
- $alt->querystring($querystring, $withQuestionmark);
- }
- }
- if (empty($this->medium_querystring)) {
- return '';
- }
- // join the strings
- $querystring = implode('&', $this->medium_querystring);
- // explode all strings
- $query_parts = explode('&', $querystring);
- // Join them again now ensure the elements are unique
- $querystring = implode('&', array_unique($query_parts));
- return $withQuestionmark ? ('?' . $querystring) : $querystring;
- }
- /**
- * Get the URL with full querystring
- *
- * @param string $url
- * @return string
- */
- public function urlQuerystring($url)
- {
- $querystring = $this->querystring();
- if (isset($this->timestamp) && !Utils::contains($querystring, $this->timestamp)) {
- $querystring = empty($querystring) ? ('?' . $this->timestamp) : ($querystring . '&' . $this->timestamp);
- }
- return ltrim($url . $querystring . $this->urlHash(), '/');
- }
- /**
- * Get/set hash for the file's url
- *
- * @param string $hash
- * @param bool $withHash
- * @return string
- */
- public function urlHash($hash = null, $withHash = true)
- {
- if ($hash) {
- $this->set('urlHash', ltrim($hash, '#'));
- }
- $hash = $this->get('urlHash', '');
- return $withHash && !empty($hash) ? '#' . $hash : $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 bool $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;
- default:
- $element = [];
- }
- if ($reset) {
- $this->reset();
- }
- $this->display('source');
- return $element;
- }
- /**
- * Parsedown element for source display mode
- *
- * @param array $attributes
- * @param bool $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 bool $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, true)) {
- return $this;
- }
- if ($this->thumbnailType !== $type) {
- $this->_thumbnail = null;
- }
- $this->thumbnailType = $type;
- return $this;
- }
- /**
- * Turn the current Medium into a Link
- *
- * @param bool $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 bool $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(',', $classes);
- }
- return $this;
- }
- /**
- * Add an id to the element from Markdown or Twig
- * Example: ![Example](myimg.png?id=primary-img)
- *
- * @param string $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) {
- 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;
- }
- }
|