resp
This commit is contained in:
@@ -90,7 +90,7 @@ trait AssetUtilsTrait
|
||||
}
|
||||
|
||||
$relative_dir = dirname($relative_path);
|
||||
$link = ROOT_DIR . $relative_path;
|
||||
$link = GRAV_ROOT . '/' . $relative_path;
|
||||
}
|
||||
|
||||
// TODO: looks like this is not being used.
|
||||
|
||||
@@ -252,7 +252,7 @@ trait TestingAssetsTrait
|
||||
*/
|
||||
public function addDir($directory, $pattern = self::DEFAULT_REGEX)
|
||||
{
|
||||
$root_dir = rtrim(ROOT_DIR, '/');
|
||||
$root_dir = GRAV_ROOT;
|
||||
|
||||
// Check if $directory is a stream.
|
||||
if (strpos($directory, '://')) {
|
||||
|
||||
@@ -238,6 +238,7 @@ class Validation
|
||||
$value = trim($value);
|
||||
}
|
||||
|
||||
$value = preg_replace("/\r\n|\r/um", "\n", $value);
|
||||
$len = mb_strlen($value);
|
||||
|
||||
$min = (int)($params['min'] ?? 0);
|
||||
@@ -280,7 +281,7 @@ class Validation
|
||||
$value = trim($value);
|
||||
}
|
||||
|
||||
return $value;
|
||||
return preg_replace("/\r\n|\r/um", "\n", $value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -746,6 +746,16 @@ class PageCollection extends FlexPageCollection implements PageCollectionInterfa
|
||||
return $bool ? $this->select($list) : $this->unselect($list);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $languageCode
|
||||
* @param bool|null $fallback
|
||||
* @return PageIndex
|
||||
*/
|
||||
public function withTranslated(string $languageCode = null, bool $fallback = null)
|
||||
{
|
||||
return $this->getIndex()->withTranslated($languageCode, $fallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter pages by given filters.
|
||||
*
|
||||
|
||||
@@ -18,6 +18,7 @@ use Grav\Common\File\CompiledYamlFile;
|
||||
use Grav\Common\Flex\Traits\FlexGravTrait;
|
||||
use Grav\Common\Flex\Traits\FlexIndexTrait;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Language\Language;
|
||||
use Grav\Common\Page\Header;
|
||||
use Grav\Common\Page\Interfaces\PageCollectionInterface;
|
||||
use Grav\Common\Page\Interfaces\PageInterface;
|
||||
@@ -164,6 +165,31 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
|
||||
return $root;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $languageCode
|
||||
* @param bool|null $fallback
|
||||
* @return PageIndex
|
||||
*/
|
||||
public function withTranslated(string $languageCode = null, bool $fallback = null)
|
||||
{
|
||||
if (null === $languageCode) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$entries = $this->translateEntries($this->getEntries(), $languageCode, $fallback);
|
||||
$params = ['language' => $languageCode, 'language_fallback' => $fallback] + $this->getParams();
|
||||
|
||||
return $this->createFrom($entries)->setParams($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
public function getLanguage(): ?string
|
||||
{
|
||||
return $this->_params['language'] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the collection params
|
||||
*
|
||||
@@ -174,6 +200,17 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
|
||||
return $this->_params ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the collection param
|
||||
*
|
||||
* @param string $name
|
||||
* @return mixed
|
||||
*/
|
||||
public function getParam(string $name)
|
||||
{
|
||||
return $this->_params[$name] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set parameters to the Collection
|
||||
*
|
||||
@@ -187,6 +224,20 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a parameter to the Collection
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
* @return $this
|
||||
*/
|
||||
public function setParam(string $name, $value)
|
||||
{
|
||||
$this->_params[$name] = $value;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the collection params
|
||||
*
|
||||
@@ -197,6 +248,15 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
|
||||
return $this->getParams();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
* @see FlexCollectionInterface::getCacheKey()
|
||||
*/
|
||||
public function getCacheKey(): string
|
||||
{
|
||||
return $this->getTypePrefix() . $this->getFlexType() . '.' . sha1(json_encode($this->getKeys()) . $this->getKeyField() . $this->getLanguage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter pages by given filters.
|
||||
*
|
||||
@@ -345,6 +405,96 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
|
||||
return $index;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $entries
|
||||
* @param string $lang
|
||||
* @param bool|null $fallback
|
||||
* @return array
|
||||
*/
|
||||
protected function translateEntries(array $entries, string $lang, bool $fallback = null): array
|
||||
{
|
||||
$languages = $this->getFallbackLanguages($lang, $fallback);
|
||||
foreach ($entries as $key => &$entry) {
|
||||
// Find out which version of the page we should load.
|
||||
$translations = $this->getLanguageTemplates((string)$key);
|
||||
if (!$translations) {
|
||||
// No translations found, is this a folder?
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find a translation.
|
||||
$template = null;
|
||||
foreach ($languages as $code) {
|
||||
if (isset($translations[$code])) {
|
||||
$template = $translations[$code];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// We couldn't find a translation, remove entry from the list.
|
||||
if (!isset($code, $template)) {
|
||||
unset($entries['key']);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Get the main key without template and langauge.
|
||||
[$main_key,] = explode('|', $entry['storage_key'] . '|', 2);
|
||||
|
||||
// Update storage key and language.
|
||||
$entry['storage_key'] = $main_key . '|' . $template . '.' . $code;
|
||||
$entry['lang'] = $code;
|
||||
}
|
||||
unset($entry);
|
||||
|
||||
return $entries;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
protected function getLanguageTemplates(string $key): array
|
||||
{
|
||||
$meta = $this->getMetaData($key);
|
||||
$template = $meta['template'] ?? 'folder';
|
||||
$translations = $meta['markdown'] ?? [];
|
||||
$list = [];
|
||||
foreach ($translations as $code => $search) {
|
||||
if (isset($search[$template])) {
|
||||
// Use main template if possible.
|
||||
$list[$code] = $template;
|
||||
} elseif (!empty($search)) {
|
||||
// Fall back to first matching template.
|
||||
$list[$code] = key($search);
|
||||
}
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|null $languageCode
|
||||
* @param bool|null $fallback
|
||||
* @return array
|
||||
*/
|
||||
protected function getFallbackLanguages(string $languageCode = null, bool $fallback = null): array
|
||||
{
|
||||
$fallback = $fallback ?? true;
|
||||
if (!$fallback && null !== $languageCode) {
|
||||
return [$languageCode];
|
||||
}
|
||||
|
||||
$grav = Grav::instance();
|
||||
|
||||
/** @var Language $language */
|
||||
$language = $grav['language'];
|
||||
$languageCode = $languageCode ?? '';
|
||||
if ($languageCode === '' && $fallback) {
|
||||
return $language->getFallbackLanguages(null, true);
|
||||
}
|
||||
|
||||
return $fallback ? $language->getFallbackLanguages($languageCode, true) : [$languageCode];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $options
|
||||
* @return array
|
||||
|
||||
@@ -474,17 +474,28 @@ class PageStorage extends FolderStorage
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if page folder should be deleted.
|
||||
*
|
||||
* Deleting page can be done either by deleting everything or just a single language.
|
||||
* If key contains the language, delete only it, unless it is the last language.
|
||||
*
|
||||
* @param string $key
|
||||
* @return bool
|
||||
*/
|
||||
protected function canDeleteFolder(string $key): bool
|
||||
{
|
||||
// Return true if there's no language in the key.
|
||||
$keys = $this->extractKeysFromStorageKey($key);
|
||||
if ($keys['lang']) {
|
||||
return false;
|
||||
if (!$keys['lang']) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return true;
|
||||
// Get the main key and reload meta.
|
||||
$key = $this->buildStorageKey($keys);
|
||||
$meta = $this->getObjectMeta($key, true);
|
||||
|
||||
// Return true if there aren't any markdown files left.
|
||||
return empty($meta['markdown'] ?? []);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -22,7 +22,6 @@ use Grav\Common\Grav;
|
||||
use Grav\Common\Media\Interfaces\MediaCollectionInterface;
|
||||
use Grav\Common\Media\Interfaces\MediaUploadInterface;
|
||||
use Grav\Common\Page\Media;
|
||||
use Grav\Common\Page\Medium\Medium;
|
||||
use Grav\Common\Page\Medium\MediumFactory;
|
||||
use Grav\Common\User\Access;
|
||||
use Grav\Common\User\Authentication;
|
||||
@@ -78,16 +77,12 @@ class UserObject extends FlexObject implements UserInterface, Countable
|
||||
|
||||
/** @var array|null */
|
||||
protected $_uploads_original;
|
||||
|
||||
/** @var FileInterface|null */
|
||||
protected $_storage;
|
||||
|
||||
/** @var UserGroupIndex */
|
||||
protected $_groups;
|
||||
|
||||
/** @var Access */
|
||||
protected $_access;
|
||||
|
||||
/** @var array|null */
|
||||
protected $access;
|
||||
|
||||
|
||||
@@ -527,7 +527,7 @@ class GPM extends Iterator
|
||||
$plugins = $this->getRepositoryPlugins();
|
||||
|
||||
if (null === $themes || null === $plugins) {
|
||||
if (!is_writable(ROOT_DIR . '/cache/gpm')) {
|
||||
if (!is_writable(GRAV_ROOT . '/cache/gpm')) {
|
||||
throw new RuntimeException('The cache/gpm folder is not writable. Please check the folder permissions.');
|
||||
}
|
||||
|
||||
|
||||
@@ -135,7 +135,10 @@ class Installer
|
||||
}
|
||||
|
||||
if (!$options['sophisticated']) {
|
||||
if ($options['theme']) {
|
||||
$isTheme = $options['theme'] ?? false;
|
||||
// Make sure that themes are always being copied, even if option was not set!
|
||||
$isTheme = $isTheme || preg_match('|/themes/[^/]+|ui', $install_path);
|
||||
if ($isTheme) {
|
||||
self::copyInstall($extracted, $install_path);
|
||||
} else {
|
||||
self::moveInstall($extracted, $install_path);
|
||||
|
||||
@@ -71,15 +71,6 @@ trait MediaUploadTrait
|
||||
*/
|
||||
public function checkUploadedFile(UploadedFileInterface $uploadedFile, string $filename = null, array $settings = null): string
|
||||
{
|
||||
// Add the defaults to the settings.
|
||||
$settings = $this->getUploadSettings($settings);
|
||||
|
||||
// Destination is always needed (but it can be set in defaults).
|
||||
$self = $settings['self'] ?? false;
|
||||
if (!isset($settings['destination']) && $self === false) {
|
||||
throw new RuntimeException($this->translate('PLUGIN_ADMIN.DESTINATION_NOT_SPECIFIED'), 400);
|
||||
}
|
||||
|
||||
// Check if there is an upload error.
|
||||
switch ($uploadedFile->getError()) {
|
||||
case UPLOAD_ERR_OK:
|
||||
@@ -101,10 +92,38 @@ trait MediaUploadTrait
|
||||
throw new RuntimeException($this->translate('PLUGIN_ADMIN.UNKNOWN_ERRORS'), 400);
|
||||
}
|
||||
|
||||
$metadata = [
|
||||
'filename' => $uploadedFile->getClientFilename(),
|
||||
'mime' => $uploadedFile->getClientMediaType(),
|
||||
'size' => $uploadedFile->getSize(),
|
||||
];
|
||||
|
||||
return $this->checkFileMetadata($metadata, $filename, $settings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that file metadata meets the requirements. Returns new filename.
|
||||
*
|
||||
* @param array $metadata
|
||||
* @param array|null $settings
|
||||
* @return string|null
|
||||
* @throws RuntimeException
|
||||
*/
|
||||
public function checkFileMetadata(array $metadata, string $filename = null, array $settings = null): string
|
||||
{
|
||||
// Add the defaults to the settings.
|
||||
$settings = $this->getUploadSettings($settings);
|
||||
|
||||
// Destination is always needed (but it can be set in defaults).
|
||||
$self = $settings['self'] ?? false;
|
||||
if (!isset($settings['destination']) && $self === false) {
|
||||
throw new RuntimeException($this->translate('PLUGIN_ADMIN.DESTINATION_NOT_SPECIFIED'), 400);
|
||||
}
|
||||
|
||||
if (null === $filename) {
|
||||
// If no filename is given, use the filename from the uploaded file (path is not allowed).
|
||||
$folder = '';
|
||||
$filename = $uploadedFile->getClientFilename() ?? '';
|
||||
$filename = $metadata['filename'] ?? '';
|
||||
} else {
|
||||
// If caller sets the filename, we will accept any custom path.
|
||||
$folder = dirname($filename);
|
||||
@@ -128,7 +147,7 @@ trait MediaUploadTrait
|
||||
$filename = date('YmdHis') . '-' . $filename;
|
||||
}
|
||||
}
|
||||
$filepath = $folder !== '' ? $folder . $filename : $filename;
|
||||
$filepath = $folder . $filename;
|
||||
|
||||
// Check if the filename is allowed.
|
||||
if (!Utils::checkFilename($filename)) {
|
||||
@@ -148,14 +167,14 @@ trait MediaUploadTrait
|
||||
$filesize = $settings['filesize'];
|
||||
if ($filesize) {
|
||||
$max_filesize = $filesize * 1048576;
|
||||
if ($uploadedFile->getSize() > $max_filesize) {
|
||||
if ($metadata['size'] > $max_filesize) {
|
||||
// TODO: use own language string
|
||||
throw new RuntimeException($this->translate('PLUGIN_ADMIN.EXCEEDED_GRAV_FILESIZE_LIMIT'), 400);
|
||||
}
|
||||
} elseif (null === $filesize) {
|
||||
// Check size against the Grav upload limit.
|
||||
$grav_limit = Utils::getUploadLimit();
|
||||
if ($grav_limit > 0 && $uploadedFile->getSize() > $grav_limit) {
|
||||
if ($grav_limit > 0 && $metadata['size'] > $grav_limit) {
|
||||
throw new RuntimeException($this->translate('PLUGIN_ADMIN.EXCEEDED_GRAV_FILESIZE_LIMIT'), 400);
|
||||
}
|
||||
}
|
||||
@@ -165,6 +184,11 @@ trait MediaUploadTrait
|
||||
$errors = [];
|
||||
// Do not trust mime type sent by the browser.
|
||||
$mime = Utils::getMimeByFilename($filename);
|
||||
$mimeTest = $metadata['mime'] ?? $mime;
|
||||
if ($mime !== $mimeTest) {
|
||||
throw new RuntimeException('The mime type does not match to file extension', 400);
|
||||
}
|
||||
|
||||
foreach ((array)$settings['accept'] as $type) {
|
||||
// Force acceptance of any file when star notation
|
||||
if ($type === '*') {
|
||||
|
||||
@@ -95,8 +95,8 @@ class Excerpts
|
||||
*/
|
||||
public function processLinkExcerpt(array $excerpt, string $type = 'link'): array
|
||||
{
|
||||
$grav = Grav::instance();
|
||||
$url = htmlspecialchars_decode(rawurldecode($excerpt['element']['attributes']['href']));
|
||||
|
||||
$url_parts = $this->parseUrl($url);
|
||||
|
||||
// If there is a query, then parse it and build action calls.
|
||||
@@ -114,7 +114,7 @@ class Excerpts
|
||||
);
|
||||
|
||||
// Valid attributes supported.
|
||||
$valid_attributes = Grav::instance()['config']->get('system.pages.markdown.valid_link_attributes');
|
||||
$valid_attributes = $grav['config']->get('system.pages.markdown.valid_link_attributes') ?? [];
|
||||
|
||||
$skip = [];
|
||||
// Unless told to not process, go through actions.
|
||||
@@ -155,9 +155,11 @@ class Excerpts
|
||||
// If scheme isn't http(s)..
|
||||
if (!empty($url_parts['scheme']) && !in_array($url_parts['scheme'], ['http', 'https'])) {
|
||||
// Handle custom streams.
|
||||
if ($type !== 'image' && !empty($url_parts['stream']) && !empty($url_parts['path'])) {
|
||||
$grav = Grav::instance();
|
||||
$url_parts['path'] = $grav['base_url_relative'] . '/' . $this->resolveStream("{$url_parts['scheme']}://{$url_parts['path']}");
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = $grav['locator'];
|
||||
if ($locator->isStream($url)) {
|
||||
$path = $locator->findResource($url, false) ?: $locator->findResource($url, false, true);
|
||||
$url_parts['path'] = $grav['base_url_relative'] . '/' . $path;
|
||||
unset($url_parts['stream'], $url_parts['scheme']);
|
||||
}
|
||||
|
||||
@@ -338,20 +340,4 @@ class Excerpts
|
||||
|
||||
return $url_parts;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @return string
|
||||
*/
|
||||
protected function resolveStream(string $url)
|
||||
{
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = Grav::instance()['locator'];
|
||||
|
||||
if ($locator->isStream($url)) {
|
||||
return $locator->findResource($url, false) ?: $locator->findResource($url, false, true);
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2095,7 +2095,7 @@ class Page implements PageInterface
|
||||
*/
|
||||
public function filePathClean()
|
||||
{
|
||||
return str_replace(ROOT_DIR, '', $this->filePath());
|
||||
return str_replace(GRAV_ROOT . DS, '', $this->filePath());
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -311,7 +311,7 @@ class Plugins extends Iterator
|
||||
}
|
||||
} else {
|
||||
$grav['log']->addWarning(
|
||||
sprintf("Plugin '%s' enabled but not found! Try clearing cache with `bin/grav clear-cache`", $name)
|
||||
sprintf("Plugin '%s' enabled but not found! Try clearing cache with `bin/grav clearcache`", $name)
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -101,7 +101,14 @@ class InitializeProcessor extends ProcessorBase
|
||||
// Load pages.
|
||||
$this->initializePages($config);
|
||||
|
||||
// Initialize URI.
|
||||
// Load accounts (decides class to be used).
|
||||
// TODO: remove in 2.0.
|
||||
$this->container['accounts'];
|
||||
|
||||
// Initialize session.
|
||||
$this->initializeSession($config);
|
||||
|
||||
// Initialize URI (uses session, see issue #3269).
|
||||
$this->initializeUri($config);
|
||||
|
||||
// Grav may return redirect response right away.
|
||||
@@ -115,13 +122,6 @@ class InitializeProcessor extends ProcessorBase
|
||||
}
|
||||
}
|
||||
|
||||
// Load accounts (decides class to be used).
|
||||
// TODO: remove in 2.0.
|
||||
$this->container['accounts'];
|
||||
|
||||
// Initialize session.
|
||||
$this->initializeSession($config);
|
||||
|
||||
$this->stopTimer('_init');
|
||||
|
||||
// Wrap call to next handler so that debugger can profile it.
|
||||
|
||||
@@ -210,7 +210,7 @@ class Security
|
||||
'on_events' => '#(<[^>]+[[a-z\x00-\x20\"\'\/])([\s\/]on|\sxmlns)[a-z].*=>?#iUu',
|
||||
|
||||
// Match javascript:, livescript:, vbscript:, mocha:, feed: and data: protocols
|
||||
'invalid_protocols' => '#(' . implode('|', array_map('preg_quote', $invalid_protocols, ['#'])) . '):.*?#iUu',
|
||||
'invalid_protocols' => '#(' . implode('|', array_map('preg_quote', $invalid_protocols, ['#'])) . '):\S.*?#iUu',
|
||||
|
||||
// Match -moz-bindings
|
||||
'moz_binding' => '#-moz-binding[a-z\x00-\x20]*:#u',
|
||||
|
||||
@@ -10,10 +10,18 @@
|
||||
namespace Grav\Common\Service;
|
||||
|
||||
use Grav\Common\Uri;
|
||||
use JsonException;
|
||||
use Nyholm\Psr7\Factory\Psr17Factory;
|
||||
use Nyholm\Psr7Server\ServerRequestCreator;
|
||||
use Pimple\Container;
|
||||
use Pimple\ServiceProviderInterface;
|
||||
use function explode;
|
||||
use function fopen;
|
||||
use function function_exists;
|
||||
use function in_array;
|
||||
use function is_array;
|
||||
use function strtolower;
|
||||
use function trim;
|
||||
|
||||
/**
|
||||
* Class RequestServiceProvider
|
||||
@@ -36,7 +44,56 @@ class RequestServiceProvider implements ServiceProviderInterface
|
||||
$psr17Factory // StreamFactory
|
||||
);
|
||||
|
||||
return $creator->fromGlobals();
|
||||
$server = $_SERVER;
|
||||
if (false === isset($server['REQUEST_METHOD'])) {
|
||||
$server['REQUEST_METHOD'] = 'GET';
|
||||
}
|
||||
$method = $server['REQUEST_METHOD'];
|
||||
|
||||
$headers = function_exists('getallheaders') ? getallheaders() : $creator::getHeadersFromServer($_SERVER);
|
||||
|
||||
$post = null;
|
||||
if (in_array($method, ['POST', 'PUT', 'PATCH', 'DELETE'])) {
|
||||
foreach ($headers as $headerName => $headerValue) {
|
||||
if ('content-type' !== strtolower($headerName)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$contentType = strtolower(trim(explode(';', $headerValue, 2)[0]));
|
||||
switch ($contentType) {
|
||||
case 'application/x-www-form-urlencoded':
|
||||
case 'multipart/form-data':
|
||||
$post = $_POST;
|
||||
break 2;
|
||||
case 'application/json':
|
||||
case 'application/vnd.api+json':
|
||||
try {
|
||||
$json = file_get_contents('php://input');
|
||||
$post = json_decode($json, true, 512, JSON_THROW_ON_ERROR);
|
||||
if (!is_array($post)) {
|
||||
$post = null;
|
||||
}
|
||||
} catch (JsonException $e) {
|
||||
$post = null;
|
||||
}
|
||||
break 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove _url from ngnix routes.
|
||||
$get = $_GET;
|
||||
unset($get['_url']);
|
||||
if (isset($server['QUERY_STRING'])) {
|
||||
$query = $server['QUERY_STRING'];
|
||||
if (strpos($query, '_url=') !== false) {
|
||||
parse_str($query, $query);
|
||||
unset($query['_url']);
|
||||
$server['QUERY_STRING'] = http_build_query($query);
|
||||
}
|
||||
}
|
||||
|
||||
return $creator->fromArrays($server, $headers, $_COOKIE, $get, $post, $_FILES, fopen('php://input', 'rb') ?: null);
|
||||
};
|
||||
|
||||
$container['route'] = $container->factory(function () {
|
||||
|
||||
@@ -0,0 +1,351 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* @package Grav\Common\Twig
|
||||
*
|
||||
* @copyright Copyright (c) 2015 - 2021 Trilby Media, LLC. All rights reserved.
|
||||
* @license MIT License; see LICENSE file for details.
|
||||
*/
|
||||
|
||||
namespace Grav\Common\Twig\Extension;
|
||||
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Utils;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\TwigFunction;
|
||||
|
||||
/**
|
||||
* Class FilesystemExtension
|
||||
* @package Grav\Common\Twig\Extension
|
||||
*/
|
||||
class FilesystemExtension extends AbstractExtension
|
||||
{
|
||||
/** @var UniformResourceLocator */
|
||||
private $locator;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->locator = Grav::instance()['locator'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TwigFunction[]
|
||||
*/
|
||||
public function getFilters()
|
||||
{
|
||||
return $this->getFunctions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of all functions.
|
||||
*
|
||||
* @return TwigFunction[]
|
||||
*/
|
||||
public function getFunctions()
|
||||
{
|
||||
return [
|
||||
new TwigFunction('file_exists', [$this, 'file_exists']),
|
||||
new TwigFunction('fileatime', [$this, 'fileatime']),
|
||||
new TwigFunction('filectime', [$this, 'filectime']),
|
||||
new TwigFunction('filemtime', [$this, 'filemtime']),
|
||||
new TwigFunction('filesize', [$this, 'filesize']),
|
||||
new TwigFunction('filetype', [$this, 'filetype']),
|
||||
new TwigFunction('is_dir', [$this, 'is_dir']),
|
||||
new TwigFunction('is_file', [$this, 'is_file']),
|
||||
new TwigFunction('is_link', [$this, 'is_link']),
|
||||
new TwigFunction('is_readable', [$this, 'is_readable']),
|
||||
new TwigFunction('is_writable', [$this, 'is_writable']),
|
||||
new TwigFunction('is_writeable', [$this, 'is_writable']),
|
||||
new TwigFunction('lstat', [$this, 'lstat']),
|
||||
new TwigFunction('getimagesize', [$this, 'getimagesize']),
|
||||
new TwigFunction('exif_read_data', [$this, 'exif_read_data']),
|
||||
new TwigFunction('read_exif_data', [$this, 'exif_read_data']),
|
||||
new TwigFunction('exif_imagetype', [$this, 'exif_imagetype']),
|
||||
new TwigFunction('hash_file', [$this, 'hash_file']),
|
||||
new TwigFunction('hash_hmac_file', [$this, 'hash_hmac_file']),
|
||||
new TwigFunction('md5_file', [$this, 'md5_file']),
|
||||
new TwigFunction('sha1_file', [$this, 'sha1_file']),
|
||||
new TwigFunction('get_meta_tags', [$this, 'get_meta_tags']),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
* @return bool
|
||||
*/
|
||||
public function file_exists($filename): bool
|
||||
{
|
||||
if (!$this->checkFilename($filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return file_exists($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
* @return int|false
|
||||
*/
|
||||
public function fileatime($filename)
|
||||
{
|
||||
if (!$this->checkFilename($filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return fileatime($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
* @return int|false
|
||||
*/
|
||||
public function filectime($filename)
|
||||
{
|
||||
if (!$this->checkFilename($filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return filectime($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
* @return int|false
|
||||
*/
|
||||
public function filemtime($filename)
|
||||
{
|
||||
if (!$this->checkFilename($filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return filemtime($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
* @return int|false
|
||||
*/
|
||||
public function filesize($filename)
|
||||
{
|
||||
if (!$this->checkFilename($filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return filesize($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
* @return string|false
|
||||
*/
|
||||
public function filetype($filename)
|
||||
{
|
||||
if (!$this->checkFilename($filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return filetype($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
* @return bool
|
||||
*/
|
||||
public function is_dir($filename): bool
|
||||
{
|
||||
if (!$this->checkFilename($filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return is_dir($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
* @return bool
|
||||
*/
|
||||
public function is_file($filename): bool
|
||||
{
|
||||
if (!$this->checkFilename($filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return is_file($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
* @return bool
|
||||
*/
|
||||
public function is_link($filename): bool
|
||||
{
|
||||
if (!$this->checkFilename($filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return is_link($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
* @return bool
|
||||
*/
|
||||
public function is_readable($filename): bool
|
||||
{
|
||||
if (!$this->checkFilename($filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return is_readable($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
* @return bool
|
||||
*/
|
||||
public function is_writable($filename): bool
|
||||
{
|
||||
if (!$this->checkFilename($filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return is_writable($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
* @return array|false
|
||||
*/
|
||||
public function lstat($filename)
|
||||
{
|
||||
if (!$this->checkFilename($filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return lstat($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
* @return array|false
|
||||
*/
|
||||
public function getimagesize($filename)
|
||||
{
|
||||
if (!$this->checkFilename($filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return getimagesize($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
* @param string|null $required_sections
|
||||
* @param bool $as_arrays
|
||||
* @param bool $read_thumbnail
|
||||
* @return array|false
|
||||
*/
|
||||
public function exif_read_data($file, ?string $required_sections, bool $as_arrays = false, bool $read_thumbnail = false)
|
||||
{
|
||||
if (!Utils::functionExists('exif_read_data') || !$this->checkFilename($file)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return exif_read_data($file, $required_sections, $as_arrays, $read_thumbnail);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
* @return string|false
|
||||
*/
|
||||
public function exif_imagetype($filename)
|
||||
{
|
||||
if (!Utils::functionExists('exif_imagetype') || !$this->checkFilename($filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return @exif_imagetype($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $algo
|
||||
* @param string $filename
|
||||
* @param bool $binary
|
||||
* @return string|false
|
||||
*/
|
||||
public function hash_file(string $algo, string $filename, bool $binary = false)
|
||||
{
|
||||
if (!$this->checkFilename($filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return hash_file($algo, $filename, $binary);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $algo
|
||||
* @param string $data
|
||||
* @param string $key
|
||||
* @param bool $binary
|
||||
* @return string|false
|
||||
*/
|
||||
public function hash_hmac_file(string $algo, string $data, string $key, bool $binary = false)
|
||||
{
|
||||
if (!$this->checkFilename($data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return hash_hmac_file($algo, $data, $key, $binary);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
* @param bool $binary
|
||||
* @return string|false
|
||||
*/
|
||||
public function md5_file($filename, bool $binary = false)
|
||||
{
|
||||
if (!$this->checkFilename($filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return md5_file($filename, $binary);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
* @param bool $binary
|
||||
* @return string|false
|
||||
*/
|
||||
public function sha1_file($filename, bool $binary = false)
|
||||
{
|
||||
if (!$this->checkFilename($filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return sha1_file($filename, $binary);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
* @return array|false
|
||||
*/
|
||||
public function get_meta_tags($filename)
|
||||
{
|
||||
if (!$this->checkFilename($filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return get_meta_tags($filename);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
* @return bool
|
||||
*/
|
||||
private function checkFilename($filename): bool
|
||||
{
|
||||
return is_string($filename) && (!str_contains($filename, '://') || $this->locator->isStream($filename));
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -47,6 +47,6 @@ class TwigNodeMarkdown extends Node implements NodeOutputInterface
|
||||
->write('$lines = explode("\n", $content);' . PHP_EOL)
|
||||
->write('$content = preg_replace(\'/^\' . $matches[0]. \'/\', "", $lines);' . PHP_EOL)
|
||||
->write('$content = join("\n", $content);' . PHP_EOL)
|
||||
->write('echo $this->env->getExtension(\'Grav\Common\Twig\TwigExtension\')->markdownFunction($context, $content);' . PHP_EOL);
|
||||
->write('echo $this->env->getExtension(\'Grav\Common\Twig\Extension\GravExtension\')->markdownFunction($context, $content);' . PHP_EOL);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@ use Grav\Common\Language\Language;
|
||||
use Grav\Common\Language\LanguageCodes;
|
||||
use Grav\Common\Page\Interfaces\PageInterface;
|
||||
use Grav\Common\Page\Pages;
|
||||
use Grav\Common\Twig\Extension\FilesystemExtension;
|
||||
use Grav\Common\Twig\Extension\GravExtension;
|
||||
use Grav\Common\Utils;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
use RocketTheme\Toolbox\Event\Event;
|
||||
use Phive\Twig\Extensions\Deferred\DeferredExtension;
|
||||
@@ -34,6 +37,8 @@ use Twig\Profiler\Profile;
|
||||
use Twig\TwigFilter;
|
||||
use Twig\TwigFunction;
|
||||
use function function_exists;
|
||||
use function in_array;
|
||||
use function is_array;
|
||||
|
||||
/**
|
||||
* Class Twig
|
||||
@@ -154,27 +159,53 @@ class Twig
|
||||
|
||||
$this->twig = new TwigEnvironment($loader_chain, $params);
|
||||
|
||||
if ($config->get('system.twig.undefined_functions')) {
|
||||
$this->twig->registerUndefinedFunctionCallback(function ($name) {
|
||||
$this->twig->registerUndefinedFunctionCallback(function ($name) use ($config) {
|
||||
$allowed = $config->get('system.twig.safe_functions');
|
||||
if (is_array($allowed) && in_array($name, $allowed, true) && function_exists($name)) {
|
||||
return new TwigFunction($name, $name);
|
||||
}
|
||||
if ($config->get('system.twig.undefined_functions')) {
|
||||
if (function_exists($name)) {
|
||||
return new TwigFunction($name, $name);
|
||||
if (!Utils::isDangerousFunction($name)) {
|
||||
user_error("PHP function {$name}() was used as Twig function. This is deprecated in Grav 1.7. Please add it to system configuration: `system.twig.safe_functions`", E_USER_DEPRECATED);
|
||||
|
||||
return new TwigFunction($name, $name);
|
||||
}
|
||||
|
||||
/** @var Debugger $debugger */
|
||||
$debugger = $this->grav['debugger'];
|
||||
$debugger->addException(new RuntimeException("Blocked potentially dangerous PHP function {$name}() being used as Twig function. If you really want to use it, please add it to system configuration: `system.twig.safe_functions`"));
|
||||
}
|
||||
|
||||
return new TwigFunction($name, static function () {
|
||||
});
|
||||
});
|
||||
}
|
||||
return new TwigFunction($name, static function () {});
|
||||
}
|
||||
|
||||
if ($config->get('system.twig.undefined_filters')) {
|
||||
$this->twig->registerUndefinedFilterCallback(function ($name) {
|
||||
return false;
|
||||
});
|
||||
|
||||
$this->twig->registerUndefinedFilterCallback(function ($name) use ($config) {
|
||||
$allowed = $config->get('system.twig.safe_filters');
|
||||
if (is_array($allowed) && in_array($name, $allowed, true) && function_exists($name)) {
|
||||
return new TwigFilter($name, $name);
|
||||
}
|
||||
if ($config->get('system.twig.undefined_filters')) {
|
||||
if (function_exists($name)) {
|
||||
return new TwigFilter($name, $name);
|
||||
if (!Utils::isDangerousFunction($name)) {
|
||||
user_error("PHP function {$name}() used as Twig filter. This is deprecated in Grav 1.7. Please add it to system configuration: `system.twig.safe_filters`", E_USER_DEPRECATED);
|
||||
|
||||
return new TwigFilter($name, $name);
|
||||
}
|
||||
|
||||
/** @var Debugger $debugger */
|
||||
$debugger = $this->grav['debugger'];
|
||||
$debugger->addException(new RuntimeException("Blocked potentially dangerous PHP function {$name}() being used as Twig filter. If you really want to use it, please add it to system configuration: `system.twig.safe_filters`"));
|
||||
}
|
||||
|
||||
return new TwigFilter($name, static function () {
|
||||
});
|
||||
});
|
||||
}
|
||||
return new TwigFilter($name, static function () {});
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
$this->grav->fireEvent('onTwigInitialized');
|
||||
|
||||
@@ -188,7 +219,8 @@ class Twig
|
||||
if ($config->get('system.twig.debug')) {
|
||||
$this->twig->addExtension(new DebugExtension());
|
||||
}
|
||||
$this->twig->addExtension(new TwigExtension());
|
||||
$this->twig->addExtension(new GravExtension());
|
||||
$this->twig->addExtension(new FilesystemExtension());
|
||||
$this->twig->addExtension(new DeferredExtension());
|
||||
$this->twig->addExtension(new StringLoaderExtension());
|
||||
|
||||
@@ -211,7 +243,7 @@ class Twig
|
||||
'assets' => $this->grav['assets'],
|
||||
'taxonomy' => $this->grav['taxonomy'],
|
||||
'browser' => $this->grav['browser'],
|
||||
'base_dir' => rtrim(ROOT_DIR, '/'),
|
||||
'base_dir' => GRAV_ROOT,
|
||||
'home_url' => $pages->homeUrl($active_language),
|
||||
'base_url' => $pages->baseUrl($active_language),
|
||||
'base_url_absolute' => $pages->baseUrl($active_language, true),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -44,7 +44,7 @@ use function strlen;
|
||||
*/
|
||||
abstract class Utils
|
||||
{
|
||||
/** @var array */
|
||||
/** @var array */
|
||||
protected static $nonces = [];
|
||||
|
||||
protected const ROOTURL_REGEX = '{^((?:http[s]?:\/\/[^\/]+)|(?:\/\/[^\/]+))(.*)}';
|
||||
@@ -178,8 +178,8 @@ abstract class Utils
|
||||
/**
|
||||
* Check if the $haystack string starts with the substring $needle
|
||||
*
|
||||
* @param string $haystack
|
||||
* @param string|string[] $needle
|
||||
* @param string $haystack
|
||||
* @param string|string[] $needle
|
||||
* @param bool $case_sensitive
|
||||
* @return bool
|
||||
*/
|
||||
@@ -202,8 +202,8 @@ abstract class Utils
|
||||
/**
|
||||
* Check if the $haystack string ends with the substring $needle
|
||||
*
|
||||
* @param string $haystack
|
||||
* @param string|string[] $needle
|
||||
* @param string $haystack
|
||||
* @param string|string[] $needle
|
||||
* @param bool $case_sensitive
|
||||
* @return bool
|
||||
*/
|
||||
@@ -227,9 +227,9 @@ abstract class Utils
|
||||
/**
|
||||
* Check if the $haystack string contains the substring $needle
|
||||
*
|
||||
* @param string $haystack
|
||||
* @param string|string[] $needle
|
||||
* @param bool $case_sensitive
|
||||
* @param string $haystack
|
||||
* @param string|string[] $needle
|
||||
* @param bool $case_sensitive
|
||||
* @return bool
|
||||
*/
|
||||
public static function contains($haystack, $needle, $case_sensitive = true)
|
||||
@@ -266,19 +266,19 @@ abstract class Utils
|
||||
{
|
||||
$regex = str_replace(
|
||||
array("\*", "\?"), // wildcard chars
|
||||
array('.*','.'), // regexp chars
|
||||
array('.*', '.'), // regexp chars
|
||||
preg_quote($wildcard_pattern, '/')
|
||||
);
|
||||
|
||||
return preg_match('/^'.$regex.'$/is', $haystack);
|
||||
return preg_match('/^' . $regex . '$/is', $haystack);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render simple template filling up the variables in it. If value is not defined, leave it as it was.
|
||||
*
|
||||
* @param string $template Template string
|
||||
* @param array $variables Variables with values
|
||||
* @param array $brackets Optional array of opening and closing brackets or symbols
|
||||
* @param string $template Template string
|
||||
* @param array $variables Variables with values
|
||||
* @param array $brackets Optional array of opening and closing brackets or symbols
|
||||
* @return string Final string filled with values
|
||||
*/
|
||||
public static function simpleTemplate(string $template, array $variables, array $brackets = ['{', '}']): string
|
||||
@@ -376,8 +376,8 @@ abstract class Utils
|
||||
/**
|
||||
* Merge two objects into one.
|
||||
*
|
||||
* @param object $obj1
|
||||
* @param object $obj2
|
||||
* @param object $obj1
|
||||
* @param object $obj2
|
||||
*
|
||||
* @return object
|
||||
*/
|
||||
@@ -415,7 +415,7 @@ abstract class Utils
|
||||
*/
|
||||
public static function arrayRemoveValue(array $search, $value)
|
||||
{
|
||||
foreach ((array) $value as $val) {
|
||||
foreach ((array)$value as $val) {
|
||||
$key = array_search($val, $search);
|
||||
if ($key !== false) {
|
||||
unset($search[$key]);
|
||||
@@ -481,8 +481,8 @@ abstract class Utils
|
||||
/**
|
||||
* Array combine but supports different array lengths
|
||||
*
|
||||
* @param array $arr1
|
||||
* @param array $arr2
|
||||
* @param array $arr1
|
||||
* @param array $arr2
|
||||
* @return array|false
|
||||
*/
|
||||
public static function arrayCombine($arr1, $arr2)
|
||||
@@ -495,7 +495,7 @@ abstract class Utils
|
||||
/**
|
||||
* Array is associative or not
|
||||
*
|
||||
* @param array $arr
|
||||
* @param array $arr
|
||||
* @return bool
|
||||
*/
|
||||
public static function arrayIsAssociative($arr)
|
||||
@@ -517,15 +517,15 @@ abstract class Utils
|
||||
$now = new DateTime();
|
||||
|
||||
$date_formats = [
|
||||
'd-m-Y H:i' => 'd-m-Y H:i (e.g. '.$now->format('d-m-Y H:i').')',
|
||||
'Y-m-d H:i' => 'Y-m-d H:i (e.g. '.$now->format('Y-m-d H:i').')',
|
||||
'm/d/Y h:i a' => 'm/d/Y h:i a (e.g. '.$now->format('m/d/Y h:i a').')',
|
||||
'H:i d-m-Y' => 'H:i d-m-Y (e.g. '.$now->format('H:i d-m-Y').')',
|
||||
'h:i a m/d/Y' => 'h:i a m/d/Y (e.g. '.$now->format('h:i a m/d/Y').')',
|
||||
];
|
||||
'd-m-Y H:i' => 'd-m-Y H:i (e.g. ' . $now->format('d-m-Y H:i') . ')',
|
||||
'Y-m-d H:i' => 'Y-m-d H:i (e.g. ' . $now->format('Y-m-d H:i') . ')',
|
||||
'm/d/Y h:i a' => 'm/d/Y h:i a (e.g. ' . $now->format('m/d/Y h:i a') . ')',
|
||||
'H:i d-m-Y' => 'H:i d-m-Y (e.g. ' . $now->format('H:i d-m-Y') . ')',
|
||||
'h:i a m/d/Y' => 'h:i a m/d/Y (e.g. ' . $now->format('h:i a m/d/Y') . ')',
|
||||
];
|
||||
$default_format = Grav::instance()['config']->get('system.pages.dateformat.default');
|
||||
if ($default_format) {
|
||||
$date_formats = array_merge([$default_format => $default_format.' (e.g. '.$now->format($default_format).')'], $date_formats);
|
||||
$date_formats = array_merge([$default_format => $default_format . ' (e.g. ' . $now->format($default_format) . ')'], $date_formats);
|
||||
}
|
||||
|
||||
return $date_formats;
|
||||
@@ -552,11 +552,11 @@ abstract class Utils
|
||||
/**
|
||||
* Truncate text by number of characters but can cut off words.
|
||||
*
|
||||
* @param string $string
|
||||
* @param int $limit Max number of characters.
|
||||
* @param bool $up_to_break truncate up to breakpoint after char count
|
||||
* @param string $break Break point.
|
||||
* @param string $pad Appended padding to the end of the string.
|
||||
* @param string $string
|
||||
* @param int $limit Max number of characters.
|
||||
* @param bool $up_to_break truncate up to breakpoint after char count
|
||||
* @param string $break Break point.
|
||||
* @param string $pad Appended padding to the end of the string.
|
||||
* @return string
|
||||
*/
|
||||
public static function truncate($string, $limit = 150, $up_to_break = false, $break = ' ', $pad = '…')
|
||||
@@ -582,7 +582,7 @@ abstract class Utils
|
||||
* Truncate text by number of characters in a "word-safe" manor.
|
||||
*
|
||||
* @param string $string
|
||||
* @param int $limit
|
||||
* @param int $limit
|
||||
* @return string
|
||||
*/
|
||||
public static function safeTruncate($string, $limit = 150)
|
||||
@@ -594,9 +594,9 @@ abstract class Utils
|
||||
/**
|
||||
* Truncate HTML by number of characters. not "word-safe"!
|
||||
*
|
||||
* @param string $text
|
||||
* @param int $length in characters
|
||||
* @param string $ellipsis
|
||||
* @param string $text
|
||||
* @param int $length in characters
|
||||
* @param string $ellipsis
|
||||
* @return string
|
||||
*/
|
||||
public static function truncateHtml($text, $length = 100, $ellipsis = '...')
|
||||
@@ -607,9 +607,9 @@ abstract class Utils
|
||||
/**
|
||||
* Truncate HTML by number of characters in a "word-safe" manor.
|
||||
*
|
||||
* @param string $text
|
||||
* @param int $length in words
|
||||
* @param string $ellipsis
|
||||
* @param string $text
|
||||
* @param int $length in words
|
||||
* @param string $ellipsis
|
||||
* @return string
|
||||
*/
|
||||
public static function safeTruncateHtml($text, $length = 25, $ellipsis = '...')
|
||||
@@ -633,8 +633,8 @@ abstract class Utils
|
||||
*
|
||||
* @param string $file the full path to the file to be downloaded
|
||||
* @param bool $force_download as opposed to letting browser choose if to download or render
|
||||
* @param int $sec Throttling, try 0.1 for some speed throttling of downloads
|
||||
* @param int $bytes Size of chunks to send in bytes. Default is 1024
|
||||
* @param int $sec Throttling, try 0.1 for some speed throttling of downloads
|
||||
* @param int $bytes Size of chunks to send in bytes. Default is 1024
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function download($file, $force_download = true, $sec = 0, $bytes = 1024)
|
||||
@@ -645,7 +645,7 @@ abstract class Utils
|
||||
|
||||
$file_parts = pathinfo($file);
|
||||
$mimetype = static::getMimeByExtension($file_parts['extension']);
|
||||
$size = filesize($file); // File size
|
||||
$size = filesize($file); // File size
|
||||
|
||||
// clean all buffers
|
||||
while (ob_get_level()) {
|
||||
@@ -742,7 +742,7 @@ abstract class Utils
|
||||
// Set from uri extension
|
||||
$uri_extension = $uri->extension();
|
||||
if (is_string($uri_extension) && $uri->isValidExtension($uri_extension)) {
|
||||
return($uri_extension);
|
||||
return ($uri_extension);
|
||||
}
|
||||
|
||||
// Use content negotiation via the `accept:` header
|
||||
@@ -1060,7 +1060,7 @@ abstract class Utils
|
||||
|
||||
$pretty_offset = "UTC${offset_prefix}${offset_formatted}";
|
||||
|
||||
$timezone_list[$timezone] = "(${pretty_offset}) ".str_replace('_', ' ', $timezone);
|
||||
$timezone_list[$timezone] = "(${pretty_offset}) " . str_replace('_', ' ', $timezone);
|
||||
}
|
||||
|
||||
return $timezone_list;
|
||||
@@ -1069,11 +1069,11 @@ abstract class Utils
|
||||
/**
|
||||
* Recursively filter an array, filtering values by processing them through the $fn function argument
|
||||
*
|
||||
* @param array $source the Array to filter
|
||||
* @param callable $fn the function to pass through each array item
|
||||
* @param array $source the Array to filter
|
||||
* @param callable $fn the function to pass through each array item
|
||||
* @return array
|
||||
*/
|
||||
public static function arrayFilterRecursive(Array $source, $fn)
|
||||
public static function arrayFilterRecursive(array $source, $fn)
|
||||
{
|
||||
$result = [];
|
||||
foreach ($source as $key => $value) {
|
||||
@@ -1093,15 +1093,15 @@ abstract class Utils
|
||||
/**
|
||||
* Flatten a multi-dimensional associative array into query params.
|
||||
*
|
||||
* @param array $array
|
||||
* @param string $prepend
|
||||
* @param array $array
|
||||
* @param string $prepend
|
||||
* @return array
|
||||
*/
|
||||
public static function arrayToQueryParams($array, $prepend = '')
|
||||
{
|
||||
$results = [];
|
||||
foreach ($array as $key => $value) {
|
||||
$name = $prepend ? $prepend . '[' . $key . ']' : $key;
|
||||
$name = $prepend ? $prepend . '[' . $key . ']' : $key;
|
||||
|
||||
if (is_array($value)) {
|
||||
$results = array_merge($results, static::arrayToQueryParams($value, $name));
|
||||
@@ -1138,8 +1138,8 @@ abstract class Utils
|
||||
/**
|
||||
* Flatten a multi-dimensional associative array into dot notation
|
||||
*
|
||||
* @param array $array
|
||||
* @param string $prepend
|
||||
* @param array $array
|
||||
* @param string $prepend
|
||||
* @return array
|
||||
*/
|
||||
public static function arrayFlattenDotNotation($array, $prepend = '')
|
||||
@@ -1147,9 +1147,9 @@ abstract class Utils
|
||||
$results = array();
|
||||
foreach ($array as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
$results = array_merge($results, static::arrayFlattenDotNotation($value, $prepend.$key.'.'));
|
||||
$results = array_merge($results, static::arrayFlattenDotNotation($value, $prepend . $key . '.'));
|
||||
} else {
|
||||
$results[$prepend.$key] = $value;
|
||||
$results[$prepend . $key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1297,7 +1297,7 @@ abstract class Utils
|
||||
* with reverse proxy setups.
|
||||
*
|
||||
* @param string $action
|
||||
* @param bool $previousTick if true, generates the token for the previous tick (the previous 12 hours)
|
||||
* @param bool $previousTick if true, generates the token for the previous tick (the previous 12 hours)
|
||||
* @return string the nonce string
|
||||
*/
|
||||
private static function generateNonceString($action, $previousTick = false)
|
||||
@@ -1334,8 +1334,8 @@ abstract class Utils
|
||||
* Creates a hashed nonce tied to the passed action. Tied to the current user and time. The nonce for a given
|
||||
* action is the same for 12 hours.
|
||||
*
|
||||
* @param string $action the action the nonce is tied to (e.g. save-user-admin or move-page-homepage)
|
||||
* @param bool $previousTick if true, generates the token for the previous tick (the previous 12 hours)
|
||||
* @param string $action the action the nonce is tied to (e.g. save-user-admin or move-page-homepage)
|
||||
* @param bool $previousTick if true, generates the token for the previous tick (the previous 12 hours)
|
||||
* @return string the nonce
|
||||
*/
|
||||
public static function getNonce($action, $previousTick = false)
|
||||
@@ -1353,7 +1353,7 @@ abstract class Utils
|
||||
/**
|
||||
* Verify the passed nonce for the give action
|
||||
*
|
||||
* @param string|string[] $nonce the nonce to verify
|
||||
* @param string|string[] $nonce the nonce to verify
|
||||
* @param string $action the action to verify the nonce to
|
||||
* @return boolean verified or not
|
||||
*/
|
||||
@@ -1370,9 +1370,7 @@ abstract class Utils
|
||||
}
|
||||
|
||||
//Nonce generated 12-24 hours ago
|
||||
$previousTick = true;
|
||||
|
||||
return $nonce === self::getNonce($action, $previousTick);
|
||||
return $nonce === self::getNonce($action, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1382,11 +1380,7 @@ abstract class Utils
|
||||
*/
|
||||
public static function isAdminPlugin()
|
||||
{
|
||||
if (isset(Grav::instance()['admin'])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return isset(Grav::instance()['admin']);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1440,7 +1434,7 @@ abstract class Utils
|
||||
while (count($keys) > 1) {
|
||||
$key = array_shift($keys);
|
||||
|
||||
if (! isset($array[$key]) || ! is_array($array[$key])) {
|
||||
if (!isset($array[$key]) || !is_array($array[$key])) {
|
||||
$array[$key] = array();
|
||||
}
|
||||
|
||||
@@ -1731,7 +1725,7 @@ abstract class Utils
|
||||
$size *= 1024 ** stripos('bkmgtpezy', $unit[0]);
|
||||
}
|
||||
|
||||
return (int) abs(round($size));
|
||||
return (int)abs(round($size));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1776,7 +1770,7 @@ abstract class Utils
|
||||
public static function processMarkdown($string, $block = true, $page = null)
|
||||
{
|
||||
$grav = Grav::instance();
|
||||
$page = $page ?? $grav['page'] ?? null;
|
||||
$page = $page ?? $grav['page'] ?? null;
|
||||
$defaults = [
|
||||
'markdown' => $grav['config']->get('system.pages.markdown', []),
|
||||
'images' => $grav['config']->get('system.images', [])
|
||||
@@ -1818,12 +1812,12 @@ abstract class Utils
|
||||
$ip = (string)inet_pton($ip);
|
||||
|
||||
// Maximum netmask length = same as packed address
|
||||
$len = 8*strlen($ip);
|
||||
$len = 8 * strlen($ip);
|
||||
if ($prefix > $len) {
|
||||
$prefix = $len;
|
||||
}
|
||||
|
||||
$mask = str_repeat('f', $prefix>>2);
|
||||
$mask = str_repeat('f', $prefix >> 2);
|
||||
|
||||
switch ($prefix & 3) {
|
||||
case 3:
|
||||
@@ -1836,7 +1830,7 @@ abstract class Utils
|
||||
$mask .= '8';
|
||||
break;
|
||||
}
|
||||
$mask = str_pad($mask, $len>>2, '0');
|
||||
$mask = str_pad($mask, $len >> 2, '0');
|
||||
|
||||
// Packed representation of netmask
|
||||
$mask = pack('H*', $mask);
|
||||
@@ -1850,11 +1844,14 @@ abstract class Utils
|
||||
* Wrapper to ensure html, htm in the front of the supported page types
|
||||
*
|
||||
* @param array|null $defaults
|
||||
* @return array|mixed
|
||||
* @return array
|
||||
*/
|
||||
public static function getSupportPageTypes(array $defaults = null)
|
||||
{
|
||||
$types = Grav::instance()['config']->get('system.pages.types', $defaults);
|
||||
if (!is_array($types)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// remove html/htm
|
||||
$types = static::arrayRemoveValue($types, ['html', 'htm']);
|
||||
@@ -1864,4 +1861,244 @@ abstract class Utils
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public static function isDangerousFunction(string $name): bool
|
||||
{
|
||||
static $commandExecutionFunctions = [
|
||||
'exec',
|
||||
'passthru',
|
||||
'system',
|
||||
'shell_exec',
|
||||
'popen',
|
||||
'proc_open',
|
||||
'pcntl_exec',
|
||||
];
|
||||
|
||||
static $codeExecutionFunctions = [
|
||||
'assert',
|
||||
'preg_replace',
|
||||
'create_function',
|
||||
'include',
|
||||
'include_once',
|
||||
'require',
|
||||
'require_once'
|
||||
];
|
||||
|
||||
static $callbackFunctions = [
|
||||
'ob_start' => 0,
|
||||
'array_diff_uassoc' => -1,
|
||||
'array_diff_ukey' => -1,
|
||||
'array_filter' => 1,
|
||||
'array_intersect_uassoc' => -1,
|
||||
'array_intersect_ukey' => -1,
|
||||
'array_map' => 0,
|
||||
'array_reduce' => 1,
|
||||
'array_udiff_assoc' => -1,
|
||||
'array_udiff_uassoc' => [-1, -2],
|
||||
'array_udiff' => -1,
|
||||
'array_uintersect_assoc' => -1,
|
||||
'array_uintersect_uassoc' => [-1, -2],
|
||||
'array_uintersect' => -1,
|
||||
'array_walk_recursive' => 1,
|
||||
'array_walk' => 1,
|
||||
'assert_options' => 1,
|
||||
'uasort' => 1,
|
||||
'uksort' => 1,
|
||||
'usort' => 1,
|
||||
'preg_replace_callback' => 1,
|
||||
'spl_autoload_register' => 0,
|
||||
'iterator_apply' => 1,
|
||||
'call_user_func' => 0,
|
||||
'call_user_func_array' => 0,
|
||||
'register_shutdown_function' => 0,
|
||||
'register_tick_function' => 0,
|
||||
'set_error_handler' => 0,
|
||||
'set_exception_handler' => 0,
|
||||
'session_set_save_handler' => [0, 1, 2, 3, 4, 5],
|
||||
'sqlite_create_aggregate' => [2, 3],
|
||||
'sqlite_create_function' => 2,
|
||||
];
|
||||
|
||||
static $informationDiscosureFunctions = [
|
||||
'phpinfo',
|
||||
'posix_mkfifo',
|
||||
'posix_getlogin',
|
||||
'posix_ttyname',
|
||||
'getenv',
|
||||
'get_current_user',
|
||||
'proc_get_status',
|
||||
'get_cfg_var',
|
||||
'disk_free_space',
|
||||
'disk_total_space',
|
||||
'diskfreespace',
|
||||
'getcwd',
|
||||
'getlastmo',
|
||||
'getmygid',
|
||||
'getmyinode',
|
||||
'getmypid',
|
||||
'getmyuid'
|
||||
];
|
||||
|
||||
static $otherFunctions = [
|
||||
'extract',
|
||||
'parse_str',
|
||||
'putenv',
|
||||
'ini_set',
|
||||
'mail',
|
||||
'header',
|
||||
'proc_nice',
|
||||
'proc_terminate',
|
||||
'proc_close',
|
||||
'pfsockopen',
|
||||
'fsockopen',
|
||||
'apache_child_terminate',
|
||||
'posix_kill',
|
||||
'posix_mkfifo',
|
||||
'posix_setpgid',
|
||||
'posix_setsid',
|
||||
'posix_setuid',
|
||||
];
|
||||
|
||||
if (in_array($name, $commandExecutionFunctions)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (in_array($name, $codeExecutionFunctions)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isset($callbackFunctions[$name])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (in_array($name, $informationDiscosureFunctions)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (in_array($name, $otherFunctions)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return static::isFilesystemFunction($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
* @return bool
|
||||
*/
|
||||
public static function isFilesystemFunction(string $name): bool
|
||||
{
|
||||
static $fileWriteFunctions = [
|
||||
'fopen',
|
||||
'tmpfile',
|
||||
'bzopen',
|
||||
'gzopen',
|
||||
// write to filesystem (partially in combination with reading)
|
||||
'chgrp',
|
||||
'chmod',
|
||||
'chown',
|
||||
'copy',
|
||||
'file_put_contents',
|
||||
'lchgrp',
|
||||
'lchown',
|
||||
'link',
|
||||
'mkdir',
|
||||
'move_uploaded_file',
|
||||
'rename',
|
||||
'rmdir',
|
||||
'symlink',
|
||||
'tempnam',
|
||||
'touch',
|
||||
'unlink',
|
||||
'imagepng',
|
||||
'imagewbmp',
|
||||
'image2wbmp',
|
||||
'imagejpeg',
|
||||
'imagexbm',
|
||||
'imagegif',
|
||||
'imagegd',
|
||||
'imagegd2',
|
||||
'iptcembed',
|
||||
'ftp_get',
|
||||
'ftp_nb_get',
|
||||
];
|
||||
|
||||
static $fileContentFunctions = [
|
||||
'file_get_contents',
|
||||
'file',
|
||||
'filegroup',
|
||||
'fileinode',
|
||||
'fileowner',
|
||||
'fileperms',
|
||||
'glob',
|
||||
'is_executable',
|
||||
'is_uploaded_file',
|
||||
'parse_ini_file',
|
||||
'readfile',
|
||||
'readlink',
|
||||
'realpath',
|
||||
'gzfile',
|
||||
'readgzfile',
|
||||
'stat',
|
||||
'imagecreatefromgif',
|
||||
'imagecreatefromjpeg',
|
||||
'imagecreatefrompng',
|
||||
'imagecreatefromwbmp',
|
||||
'imagecreatefromxbm',
|
||||
'imagecreatefromxpm',
|
||||
'ftp_put',
|
||||
'ftp_nb_put',
|
||||
'hash_update_file',
|
||||
'highlight_file',
|
||||
'show_source',
|
||||
'php_strip_whitespace',
|
||||
];
|
||||
|
||||
static $filesystemFunctions = [
|
||||
// read from filesystem
|
||||
'file_exists',
|
||||
'fileatime',
|
||||
'filectime',
|
||||
'filemtime',
|
||||
'filesize',
|
||||
'filetype',
|
||||
'is_dir',
|
||||
'is_file',
|
||||
'is_link',
|
||||
'is_readable',
|
||||
'is_writable',
|
||||
'is_writeable',
|
||||
'linkinfo',
|
||||
'lstat',
|
||||
//'pathinfo',
|
||||
'getimagesize',
|
||||
'exif_read_data',
|
||||
'read_exif_data',
|
||||
'exif_thumbnail',
|
||||
'exif_imagetype',
|
||||
'hash_file',
|
||||
'hash_hmac_file',
|
||||
'md5_file',
|
||||
'sha1_file',
|
||||
'get_meta_tags',
|
||||
];
|
||||
|
||||
if (in_array($name, $fileWriteFunctions)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (in_array($name, $fileContentFunctions)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (in_array($name, $filesystemFunctions)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,11 +73,11 @@ class InstallCommand extends GravCommand
|
||||
$io = $this->getIO();
|
||||
|
||||
$dependencies_file = '.dependencies';
|
||||
$this->destination = $input->getArgument('destination') ?: GRAV_ROOT;
|
||||
$this->destination = $input->getArgument('destination') ?: GRAV_WEBROOT;
|
||||
|
||||
// fix trailing slash
|
||||
$this->destination = rtrim($this->destination, DS) . DS;
|
||||
$this->user_path = $this->destination . USER_PATH;
|
||||
$this->user_path = $this->destination . GRAV_USER_PATH . DS;
|
||||
if ($local_config_file = $this->loadLocalConfig()) {
|
||||
$io->writeln('Read local config from <cyan>' . $local_config_file . '</cyan>');
|
||||
}
|
||||
|
||||
@@ -600,7 +600,13 @@ class InstallCommand extends GpmCommand
|
||||
try {
|
||||
$output = Response::get($package->zipball_url . $query, [], [$this, 'progress']);
|
||||
} catch (Exception $e) {
|
||||
$error = str_replace("\n", "\n | '- ", $e->getMessage());
|
||||
if (!empty($package->premium) && $e->getCode() === 401) {
|
||||
$message = '<yellow>Unauthorized Premium License Key</yellow>';
|
||||
} else {
|
||||
$message = $e->getMessage();
|
||||
}
|
||||
|
||||
$error = str_replace("\n", "\n | '- ", $message);
|
||||
$io->write("\x0D");
|
||||
// extra white spaces to clear out the buffer properly
|
||||
$io->writeln(' |- Downloading package... <red>error</red> ');
|
||||
|
||||
@@ -112,7 +112,7 @@ class UninstallCommand extends GpmCommand
|
||||
|
||||
unset($this->data['not_found'], $this->data['total']);
|
||||
|
||||
// Plugins need to be initialized in order to make clear-cache to work.
|
||||
// Plugins need to be initialized in order to make clearcache to work.
|
||||
try {
|
||||
$this->initializePlugins();
|
||||
} catch (Throwable $e) {
|
||||
|
||||
@@ -173,6 +173,7 @@ trait ControllerResponseTrait
|
||||
if ($method !== 'GET' && $method !== 'HEAD') {
|
||||
$this->setMessage($message, 'error');
|
||||
$referer = $request->getHeaderLine('Referer');
|
||||
|
||||
return $this->createRedirectResponse($referer, 303);
|
||||
}
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ class FlexForm implements FlexObjectFormInterface, JsonSerializable
|
||||
}
|
||||
$this->setUniqueId($uniqueId);
|
||||
$directory = $object->getFlexDirectory();
|
||||
$this->setFlashLookupFolder($directory->getBlueprint()->get('form/flash_folder') ?? 'tmp://forms/[SESSIONID]');
|
||||
$this->setFlashLookupFolder($options['flash_folder'] ?? $directory->getBlueprint()->get('form/flash_folder') ?? 'tmp://forms/[SESSIONID]');
|
||||
$this->form = $options['form'] ?? null;
|
||||
|
||||
if (!empty($options['reset'])) {
|
||||
|
||||
@@ -196,9 +196,10 @@ class FlexObject implements FlexObjectInterface, FlexAuthorizeInterface
|
||||
/**
|
||||
* Refresh object from the storage.
|
||||
*
|
||||
* @param bool $keepMissing
|
||||
* @return bool True if the object was refreshed
|
||||
*/
|
||||
public function refresh(): bool
|
||||
public function refresh(bool $keepMissing = false): bool
|
||||
{
|
||||
$key = $this->getStorageKey();
|
||||
if ('' === $key) {
|
||||
@@ -216,20 +217,36 @@ class FlexObject implements FlexObjectInterface, FlexAuthorizeInterface
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get current elements (if requested).
|
||||
$current = $keepMissing ? $this->getElements() : [];
|
||||
// Get elements from the filesystem.
|
||||
$elements = $storage->readRows([$key => null])[$key] ?? null;
|
||||
if (null !== $elements || isset($elements['__ERROR'])) {
|
||||
$meta = $elements['_META'] ?? $meta;
|
||||
if (null !== $elements) {
|
||||
$meta = $elements['__META'] ?? $meta;
|
||||
unset($elements['__META']);
|
||||
$this->filterElements($elements);
|
||||
$newKey = $meta['key'] ?? $this->getKey();
|
||||
if ($meta) {
|
||||
$this->setMetaData($meta);
|
||||
}
|
||||
$this->objectConstruct($elements, $newKey);
|
||||
}
|
||||
|
||||
/** @var Debugger $debugger */
|
||||
$debugger = Grav::instance()['debugger'];
|
||||
$debugger->addMessage("Refreshed {$this->getFlexType()} object {$this->getKey()}", 'debug');
|
||||
if ($current) {
|
||||
// Inject back elements which are missing in the filesystem.
|
||||
$data = $this->getBlueprint()->flattenData($current);
|
||||
foreach ($data as $property => $value) {
|
||||
if (strpos($property, '.') === false) {
|
||||
$this->defProperty($property, $value);
|
||||
} else {
|
||||
$this->defNestedProperty($property, $value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @var Debugger $debugger */
|
||||
$debugger = Grav::instance()['debugger'];
|
||||
$debugger->addMessage("Refreshed {$this->getFlexType()} object {$this->getKey()}", 'debug');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -320,14 +320,21 @@ trait PageLegacyTrait
|
||||
|
||||
// Find non-existing key.
|
||||
$parentKey = $parent ? $parent->getKey() : '';
|
||||
$key = trim($parentKey . '/' . basename($this->getKey()), '/');
|
||||
$key = preg_replace('/-\d+$/', '', $key);
|
||||
$i = 1;
|
||||
do {
|
||||
$i++;
|
||||
$test = "{$key}-{$i}";
|
||||
} while ($index->containsKey($test));
|
||||
$key = $test;
|
||||
if ($this instanceof FlexPageObject) {
|
||||
$key = trim($parentKey . '/' . $this->folder(), '/');
|
||||
} else {
|
||||
$key = trim($parentKey . '/' . basename($this->getKey()), '/');
|
||||
}
|
||||
|
||||
if ($index->containsKey($key)) {
|
||||
$key = preg_replace('/\d+$/', '', $key);
|
||||
$i = 1;
|
||||
do {
|
||||
$i++;
|
||||
$test = "{$key}{$i}";
|
||||
} while ($index->containsKey($test));
|
||||
$key = $test;
|
||||
}
|
||||
$folder = basename($key);
|
||||
|
||||
// Get the folder name.
|
||||
|
||||
Reference in New Issue
Block a user