BaseAsset.php 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268
  1. <?php
  2. /**
  3. * @package Grav\Common\Assets
  4. *
  5. * @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
  6. * @license MIT License; see LICENSE file for details.
  7. */
  8. namespace Grav\Common\Assets;
  9. use Grav\Common\Assets\Traits\AssetUtilsTrait;
  10. use Grav\Common\Config\Config;
  11. use Grav\Common\Grav;
  12. use Grav\Common\Uri;
  13. use Grav\Common\Utils;
  14. use Grav\Framework\Object\PropertyObject;
  15. use RocketTheme\Toolbox\File\File;
  16. use SplFileInfo;
  17. /**
  18. * Class BaseAsset
  19. * @package Grav\Common\Assets
  20. */
  21. abstract class BaseAsset extends PropertyObject
  22. {
  23. use AssetUtilsTrait;
  24. protected const CSS_ASSET = true;
  25. protected const JS_ASSET = false;
  26. /** @var string|false */
  27. protected $asset;
  28. /** @var string */
  29. protected $asset_type;
  30. /** @var int */
  31. protected $order;
  32. /** @var string */
  33. protected $group;
  34. /** @var string */
  35. protected $position;
  36. /** @var int */
  37. protected $priority;
  38. /** @var array */
  39. protected $attributes = [];
  40. /** @var string */
  41. protected $timestamp;
  42. /** @var int|false */
  43. protected $modified;
  44. /** @var bool */
  45. protected $remote;
  46. /** @var string */
  47. protected $query = '';
  48. // Private Bits
  49. /** @var bool */
  50. private $css_rewrite = false;
  51. /** @var bool */
  52. private $css_minify = false;
  53. /**
  54. * @return string
  55. */
  56. abstract function render();
  57. /**
  58. * BaseAsset constructor.
  59. * @param array $elements
  60. * @param string|null $key
  61. */
  62. public function __construct(array $elements = [], $key = null)
  63. {
  64. $base_config = [
  65. 'group' => 'head',
  66. 'position' => 'pipeline',
  67. 'priority' => 10,
  68. 'modified' => null,
  69. 'asset' => null
  70. ];
  71. // Merge base defaults
  72. $elements = array_merge($base_config, $elements);
  73. parent::__construct($elements, $key);
  74. }
  75. /**
  76. * @param string|false $asset
  77. * @param array $options
  78. * @return $this|false
  79. */
  80. public function init($asset, $options)
  81. {
  82. if (!$asset) {
  83. return false;
  84. }
  85. $config = Grav::instance()['config'];
  86. $uri = Grav::instance()['uri'];
  87. // set attributes
  88. foreach ($options as $key => $value) {
  89. if ($this->hasProperty($key)) {
  90. $this->setProperty($key, $value);
  91. } else {
  92. $this->attributes[$key] = $value;
  93. }
  94. }
  95. // Force priority to be an int
  96. $this->priority = (int) $this->priority;
  97. // Do some special stuff for CSS/JS (not inline)
  98. if (!Utils::startsWith($this->getType(), 'inline')) {
  99. $this->base_url = rtrim($uri->rootUrl($config->get('system.absolute_urls')), '/') . '/';
  100. $this->remote = static::isRemoteLink($asset);
  101. // Move this to render?
  102. if (!$this->remote) {
  103. $asset_parts = parse_url($asset);
  104. if (isset($asset_parts['query'])) {
  105. $this->query = $asset_parts['query'];
  106. unset($asset_parts['query']);
  107. $asset = Uri::buildUrl($asset_parts);
  108. }
  109. $locator = Grav::instance()['locator'];
  110. if ($locator->isStream($asset)) {
  111. $path = $locator->findResource($asset, true);
  112. } else {
  113. $path = GRAV_WEBROOT . $asset;
  114. }
  115. // If local file is missing return
  116. if ($path === false) {
  117. return false;
  118. }
  119. $file = new SplFileInfo($path);
  120. $asset = $this->buildLocalLink($file->getPathname());
  121. $this->modified = $file->isFile() ? $file->getMTime() : false;
  122. }
  123. }
  124. $this->asset = $asset;
  125. return $this;
  126. }
  127. /**
  128. * @return string|false
  129. */
  130. public function getAsset()
  131. {
  132. return $this->asset;
  133. }
  134. /**
  135. * @return bool
  136. */
  137. public function getRemote()
  138. {
  139. return $this->remote;
  140. }
  141. /**
  142. * @param string $position
  143. * @return $this
  144. */
  145. public function setPosition($position)
  146. {
  147. $this->position = $position;
  148. return $this;
  149. }
  150. /**
  151. * Receive asset location and return the SRI integrity hash
  152. *
  153. * @param string $input
  154. * @return string
  155. */
  156. public static function integrityHash($input)
  157. {
  158. $grav = Grav::instance();
  159. $uri = $grav['uri'];
  160. $assetsConfig = $grav['config']->get('system.assets');
  161. if (!self::isRemoteLink($input) && !empty($assetsConfig['enable_asset_sri']) && $assetsConfig['enable_asset_sri']) {
  162. $input = preg_replace('#^' . $uri->rootUrl() . '#', '', $input);
  163. $asset = File::instance(GRAV_WEBROOT . $input);
  164. if ($asset->exists()) {
  165. $dataToHash = $asset->content();
  166. $hash = hash('sha256', $dataToHash, true);
  167. $hash_base64 = base64_encode($hash);
  168. return ' integrity="sha256-' . $hash_base64 . '"';
  169. }
  170. }
  171. return '';
  172. }
  173. /**
  174. *
  175. * Get the last modification time of asset
  176. *
  177. * @param string $asset the asset string reference
  178. *
  179. * @return string the last modifcation time or false on error
  180. */
  181. // protected function getLastModificationTime($asset)
  182. // {
  183. // $file = GRAV_WEBROOT . $asset;
  184. // if (Grav::instance()['locator']->isStream($asset)) {
  185. // $file = $this->buildLocalLink($asset, true);
  186. // }
  187. //
  188. // return file_exists($file) ? filemtime($file) : false;
  189. // }
  190. /**
  191. *
  192. * Build local links including grav asset shortcodes
  193. *
  194. * @param string $asset the asset string reference
  195. *
  196. * @return string|false the final link url to the asset
  197. */
  198. protected function buildLocalLink($asset)
  199. {
  200. if ($asset) {
  201. return $this->base_url . ltrim(Utils::replaceFirstOccurrence(GRAV_WEBROOT, '', $asset), '/');
  202. }
  203. return false;
  204. }
  205. /**
  206. * Implements JsonSerializable interface.
  207. *
  208. * @return array
  209. */
  210. public function jsonSerialize()
  211. {
  212. return ['type' => $this->getType(), 'elements' => $this->getElements()];
  213. }
  214. /**
  215. * Placeholder for AssetUtilsTrait method
  216. *
  217. * @param string $file
  218. * @param string $dir
  219. * @param bool $local
  220. * @return string
  221. */
  222. protected function cssRewrite($file, $dir, $local)
  223. {
  224. return '';
  225. }
  226. }