template rapport_dactivitees avec pdf à télécharger

This commit is contained in:
2021-03-23 13:58:22 +01:00
parent 66f835e1ff
commit f8ecbbe597
39 changed files with 526 additions and 205 deletions
+14 -9
View File
@@ -27,7 +27,6 @@ use function is_bool;
use function is_float;
use function is_int;
use function is_string;
use function strlen;
/**
* Class Validation
@@ -239,16 +238,20 @@ class Validation
$value = trim($value);
}
if (isset($params['min']) && strlen($value) < $params['min']) {
$len = mb_strlen($value);
$min = (int)($params['min'] ?? 0);
if ($min && $len < $min) {
return false;
}
if (isset($params['max']) && strlen($value) > $params['max']) {
$max = (int)($params['max'] ?? 0);
if ($max && $len > $max) {
return false;
}
$min = $params['min'] ?? 0;
if (isset($params['step']) && (strlen($value) - $min) % $params['step'] === 0) {
$step = (int)($params['step'] ?? 0);
if ($step && ($len - $min) % $step === 0) {
return false;
}
@@ -271,11 +274,13 @@ class Validation
return '';
}
$value = (string)$value;
if (!empty($params['trim'])) {
$value = trim($value);
}
return (string) $value;
return $value;
}
/**
@@ -332,7 +337,7 @@ class Validation
*/
protected static function filterLower($value, array $params)
{
return strtolower($value);
return mb_strtolower($value);
}
/**
@@ -342,7 +347,7 @@ class Validation
*/
protected static function filterUpper($value, array $params)
{
return strtoupper($value);
return mb_strtoupper($value);
}
@@ -534,7 +539,7 @@ class Validation
*/
protected static function filterNumber($value, array $params, array $field)
{
return (string)(int)$value !== (string)(float)$value ? (float) $value : (int) $value;
return (string)(int)$value !== (string)(float)$value ? (float)$value : (int)$value;
}
/**
@@ -522,12 +522,13 @@ class PageIndex extends FlexPageIndex implements PageCollectionInterface
$tmp = $child->children()->getIndex();
$child_count = $tmp->count();
$count = $filters ? $tmp->filterBy($filters, true)->count() : null;
$route = $child->getRoute();
$payload = [
'item-key' => basename($child->rawRoute() ?? $child->getKey()),
'icon' => $icon,
'title' => htmlspecialchars($child->menu()),
'route' => [
'display' => $child->getRoute()->toString(false) ?: '/',
'display' => ($route ? ($route->toString(false) ?: '/') : null) ?? '',
'raw' => $child->rawRoute(),
],
'modified' => $this->jsDate($child->modified()),
@@ -22,6 +22,7 @@ use Grav\Common\Flex\Types\Pages\Traits\PageTranslateTrait;
use Grav\Common\Language\Language;
use Grav\Common\Page\Interfaces\PageInterface;
use Grav\Common\Page\Pages;
use Grav\Common\User\Interfaces\UserInterface;
use Grav\Common\Utils;
use Grav\Framework\Filesystem\Filesystem;
use Grav\Framework\Flex\FlexObject;
@@ -321,6 +322,28 @@ class PageObject extends FlexPageObject
return $this;
}
/**
* @param UserInterface $user
* @param string $action
* @param string $scope
* @param bool $isMe
* @return bool|null
*/
protected function isAuthorizedOverride(UserInterface $user, string $action, string $scope, bool $isMe): ?bool
{
// Special case: creating a new page means checking parent for its permissions.
if ($action === 'create' && !$this->exists()) {
$parent = $this->parent();
if ($parent && method_exists($parent, 'isAuthorized')) {
return $parent->isAuthorized($action, $scope, $user);
}
return false;
}
return parent::isAuthorizedOverride($user, $action, $scope, $isMe);
}
/**
* @param array $ordering
* @return PageCollection|null
@@ -15,6 +15,7 @@ use Grav\Common\Grav;
use Grav\Common\Page\Interfaces\PageCollectionInterface;
use Grav\Common\Page\Interfaces\PageInterface;
use Grav\Common\Page\Pages;
use Grav\Common\Uri;
use Grav\Common\Utils;
use Grav\Framework\Filesystem\Filesystem;
use RuntimeException;
@@ -97,6 +98,7 @@ trait PageRoutableTrait
public function activeChild(): bool
{
$grav = Grav::instance();
/** @var Uri $uri */
$uri = $grav['uri'];
/** @var Pages $pages */
$pages = $grav['pages'];
+1 -1
View File
@@ -426,7 +426,7 @@ class Grav extends Container
// Clean route for redirect
$route = preg_replace("#^\/[\\\/]+\/#", '/', $route);
if (null !== $code || $code < 300 || $code > 399) {
if ($code < 300 || $code > 399) {
$code = null;
}
@@ -198,6 +198,17 @@ abstract class AbstractMedia implements ExportInterface, MediaCollectionInterfac
}
}
/**
* @param string $name
* @return void
*/
public function hide($name)
{
$this->offsetUnset($name);
unset($this->images[$name], $this->videos[$name], $this->audios[$name], $this->files[$name]);
}
/**
* Create Medium from a file.
*
+12 -10
View File
@@ -2496,21 +2496,23 @@ class Page implements PageInterface
*/
public function activeChild()
{
$uri = Grav::instance()['uri'];
$pages = Grav::instance()['pages'];
$grav = Grav::instance();
/** @var Uri $uri */
$uri = $grav['uri'];
/** @var Pages $pages */
$pages = $grav['pages'];
$uri_path = rtrim(urldecode($uri->path()), '/');
$routes = Grav::instance()['pages']->routes();
$routes = $pages->routes();
if (isset($routes[$uri_path])) {
$page = $pages->find($uri->route());
/** @var PageInterface|null $child_page */
$child_page = $pages->find($uri->route())->parent();
if ($child_page) {
while (!$child_page->root()) {
if ($this->path() === $child_page->path()) {
return true;
}
$child_page = $child_page->parent();
$child_page = $page ? $page->parent() : null;
while ($child_page && !$child_page->root()) {
if ($this->path() === $child_page->path()) {
return true;
}
$child_page = $child_page->parent();
}
}
+116 -73
View File
@@ -880,103 +880,146 @@ class Pages
}
/**
* alias method to return find a page.
* Find a page based on route.
*
* @param string $route The relative URL of the page
* @param bool $all
* @param string $route The route of the page
* @param bool $all If true, return also non-routable pages, otherwise return null if page isn't routable
* @return PageInterface|null
*/
public function find($route, $all = false)
{
return $this->dispatch($route, $all, false);
$route = urldecode((string)$route);
// Fetch page if there's a defined route to it.
$path = $this->routes[$route] ?? null;
$page = null !== $path ? $this->get($path) : null;
// Try without trailing slash
if (null === $page && Utils::endsWith($route, '/')) {
$path = $this->routes[rtrim($route, '/')] ?? null;
$page = null !== $path ? $this->get($path) : null;
}
if (!$all && !isset($this->grav['admin'])) {
if (null === $page || !$page->routable()) {
// If the page cannot be accessed, look for the site wide routes and wildcards.
$page = $this->findSiteBasedRoute($route) ?? $page;
}
}
return $page;
}
/**
* Check site based routes.
*
* @param string $route
* @return PageInterface|null
*/
protected function findSiteBasedRoute($route)
{
/** @var Config $config */
$config = $this->grav['config'];
$site_routes = $config->get('site.routes');
if (!is_array($site_routes)) {
return null;
}
$page = null;
// See if route matches one in the site configuration
$site_route = $site_routes[$route] ?? null;
if ($site_route) {
$page = $this->find($site_route);
} else {
// Use reverse order because of B/C (previously matched multiple and returned the last match).
foreach (array_reverse($site_routes, true) as $pattern => $replace) {
$pattern = '#^' . str_replace('/', '\/', ltrim($pattern, '^')) . '#';
try {
$found = preg_replace($pattern, $replace, $route);
if ($found && $found !== $route) {
$page = $this->find($found);
if ($page) {
return $page;
}
}
} catch (ErrorException $e) {
$this->grav['log']->error('site.routes: ' . $pattern . '-> ' . $e->getMessage());
}
}
}
return $page;
}
/**
* Dispatch URI to a page.
*
* @param string $route The relative URL of the page
* @param bool $all
* @param bool $redirect
* @param bool $all If true, return also non-routable pages, otherwise return null if page isn't routable
* @param bool $redirect If true, allow redirects
* @return PageInterface|null
* @throws Exception
*/
public function dispatch($route, $all = false, $redirect = true)
{
$route = urldecode($route);
$page = $this->find($route, true);
// Fetch page if there's a defined route to it.
$path = $this->routes[$route] ?? null;
$page = null !== $path ? $this->get($path) : null;
// Try without trailing slash
if (!$page && Utils::endsWith($route, '/')) {
$path = $this->routes[rtrim($route, '/')] ?? null;
$page = null !== $path ? $this->get($path) : null;
// If we want all pages or are in admin, return what we already have.
if ($all || isset($this->grav['admin'])) {
return $page;
}
// Are we in the admin? this is important!
$not_admin = !isset($this->grav['admin']);
// If the page cannot be reached, look into site wide redirects, routes + wildcards
if (!$all && $not_admin) {
// If the page is a simple redirect, just do it.
if ($redirect && $page && $page->redirect()) {
$this->grav->redirectLangSafe($page->redirect());
}
// fall back and check site based redirects
if (!$page || !$page->routable()) {
// Redirect to the first child (placeholder page)
if ($redirect && $page && count($children = $page->children()->visible()->routable()->published()) > 0) {
$this->grav->redirectLangSafe($children->first()->route());
if ($page) {
$routable = $page->routable();
if ($redirect) {
if ($page->redirect()) {
// Follow a redirect page.
$this->grav->redirectLangSafe($page->redirect());
}
/** @var Config $config */
$config = $this->grav['config'];
if (!$routable && ($child = $page->children()->visible()->routable()->published()->first()) !== null) {
// Redirect to the first visible child as current page isn't routable.
$this->grav->redirectLangSafe($child->route());
}
}
// See if route matches one in the site configuration
$site_route = $config->get("site.routes.{$route}");
if ($site_route) {
$page = $this->dispatch($site_route, $all, $redirect);
} else {
/** @var Uri $uri */
$uri = $this->grav['uri'];
/** @var \Grav\Framework\Uri\Uri $source_url */
$source_url = $uri->uri(false);
if ($routable) {
return $page;
}
}
// Try Regex style redirects
$site_redirects = $config->get('site.redirects');
if (is_array($site_redirects)) {
foreach ((array)$site_redirects as $pattern => $replace) {
$pattern = ltrim($pattern, '^');
$pattern = '#^' . str_replace('/', '\/', $pattern) . '#';
try {
/** @var string $found */
$found = preg_replace($pattern, $replace, $source_url);
if ($found && $found !== $source_url) {
$this->grav->redirectLangSafe($found);
}
} catch (ErrorException $e) {
$this->grav['log']->error('site.redirects: ' . $pattern . '-> ' . $e->getMessage());
}
}
}
// Try Regex style routes
$site_routes = $config->get('site.routes');
if (is_array($site_routes)) {
foreach ((array)$site_routes as $pattern => $replace) {
$pattern = '#^' . str_replace('/', '\/', ltrim($pattern, '^')) . '#';
try {
/** @var string $found */
$found = preg_replace($pattern, $replace, $source_url);
if ($found && $found !== $source_url) {
$page = $this->dispatch($found, $all, $redirect);
}
} catch (ErrorException $e) {
$this->grav['log']->error('site.routes: ' . $pattern . '-> ' . $e->getMessage());
}
}
$route = urldecode((string)$route);
// The page cannot be reached, look into site wide redirects, routes and wildcards.
$redirectedPage = $this->findSiteBasedRoute($route);
if ($redirectedPage) {
$page = $this->dispatch($redirectedPage->route(), false, $redirect);
}
/** @var Config $config */
$config = $this->grav['config'];
/** @var Uri $uri */
$uri = $this->grav['uri'];
/** @var \Grav\Framework\Uri\Uri $source_url */
$source_url = $uri->uri(false);
// Try Regex style redirects
$site_redirects = $config->get('site.redirects');
if (is_array($site_redirects)) {
foreach ((array)$site_redirects as $pattern => $replace) {
$pattern = ltrim($pattern, '^');
$pattern = '#^' . str_replace('/', '\/', $pattern) . '#';
try {
/** @var string $found */
$found = preg_replace($pattern, $replace, $source_url);
if ($found && $found !== $source_url) {
$this->grav->redirectLangSafe($found);
}
} catch (ErrorException $e) {
$this->grav['log']->error('site.redirects: ' . $pattern . '-> ' . $e->getMessage());
}
}
}
+1 -1
View File
@@ -519,7 +519,7 @@ class Uri
* Return the full uri
*
* @param bool $include_root
* @return mixed
* @return string
*/
public function uri($include_root = true)
{
+112 -33
View File
@@ -12,11 +12,15 @@ namespace Grav\Common;
use DateTime;
use DateTimeZone;
use Exception;
use Grav\Common\Flex\Types\Pages\PageObject;
use Grav\Common\Helpers\Truncator;
use Grav\Common\Page\Interfaces\PageInterface;
use Grav\Common\Markdown\Parsedown;
use Grav\Common\Markdown\ParsedownExtra;
use Grav\Common\Page\Markdown\Excerpts;
use Grav\Common\Page\Pages;
use Grav\Framework\Flex\Flex;
use Grav\Framework\Flex\Interfaces\FlexObjectInterface;
use InvalidArgumentException;
use Negotiation\Accept;
use Negotiation\Negotiator;
@@ -1520,7 +1524,7 @@ abstract class Utils
}
/**
* Get path based on a token
* Get relative page path based on a token.
*
* @param string $path
* @param PageInterface|null $page
@@ -1529,47 +1533,122 @@ abstract class Utils
*/
public static function getPagePathFromToken($path, PageInterface $page = null)
{
$path_parts = pathinfo($path);
$grav = Grav::instance();
return static::getPathFromToken($path, $page);
}
$basename = '';
if (isset($path_parts['extension'])) {
$basename = '/' . $path_parts['basename'];
$path = rtrim($path_parts['dirname'], ':');
/**
* Get relative path based on a token.
*
* Path supports following syntaxes:
*
* 'self@', 'self@/path'
* 'page@:/route', 'page@:/route/filename.ext'
* 'theme@:', 'theme@:/path'
*
* @param string $path
* @param FlexObjectInterface|PageInterface|null $object
* @return string
* @throws RuntimeException
*/
public static function getPathFromToken($path, $object = null)
{
$matches = static::resolveTokenPath($path);
if (null === $matches) {
return $path;
}
$regex = '/(@self|self@)|((?:@page|page@):(?:.*))|((?:@theme|theme@):(?:.*))/';
preg_match($regex, $path, $matches);
$grav = Grav::instance();
if ($matches) {
if ($matches[1]) {
if (null === $page) {
throw new RuntimeException('Page not available for this self@ reference');
switch ($matches[0]) {
case 'self':
if (null === $object) {
throw new RuntimeException(sprintf('Page not available for self@ reference: %s', $path));
}
} elseif ($matches[2]) {
// page@
$parts = explode(':', $path);
$route = $parts[1];
$page = $grav['page']->find($route);
} elseif ($matches[3]) {
// theme@
$parts = explode(':', $path);
$route = $parts[1];
$theme = str_replace(ROOT_DIR, '', $grav['locator']->findResource('theme://'));
return $theme . $route . $basename;
if ($matches[2] === '') {
if ($object->exists()) {
$route = '/' . $matches[1];
if ($object instanceof PageInterface) {
return trim($object->relativePagePath() . $route, '/');
}
$folder = $object->getMediaFolder();
if ($folder) {
return trim($folder . $route, '/');
}
} else {
return '';
}
}
break;
case 'page':
if ($matches[1] === '') {
$route = '/' . $matches[2];
// Exclude filename from the page lookup.
if (pathinfo($route, PATHINFO_EXTENSION)) {
$basename = '/' . basename($route);
$route = \dirname($route);
} else {
$basename = '';
}
$key = trim($route === '/' ? $grav['config']->get('system.home.alias') : $route, '/');
if ($object instanceof PageObject) {
$object = $object->getFlexDirectory()->getObject($key);
} elseif (static::isAdminPlugin()) {
/** @var Flex|null $flex */
$flex = $grav['flex'] ?? null;
$object = $flex ? $flex->getObject($key, 'pages') : null;
} else {
/** @var Pages $pages */
$pages = $grav['pages'];
$object = $pages->find($route);
}
if ($object instanceof PageInterface) {
return trim($object->relativePagePath() . $basename, '/');
}
}
break;
case 'theme':
if ($matches[1] === '') {
$route = '/' . $matches[2];
$theme = $grav['locator']->findResource('theme://', false);
if (false !== $theme) {
return trim($theme . $route, '/');
}
}
break;
}
throw new RuntimeException(sprintf('Token path not found: %s', $path));
}
/**
* Returns [token, route, path] from '@token/route:/path'. Route and path are optional. If pattern does not match, return null.
*
* @param string $path
* @return string[]|null
*/
private static function resolveTokenPath(string $path): ?array
{
if (strpos($path, '@') !== false) {
$regex = '/^(@\w+|\w+@|@\w+@)([^:]*)(.*)$/u';
if (preg_match($regex, $path, $matches)) {
return [
trim($matches[1], '@'),
trim($matches[2], '/'),
trim($matches[3], ':/')
];
}
} else {
return $path . $basename;
}
if (!$page) {
throw new RuntimeException('Page route not found: ' . $path);
}
$path = str_replace($matches[0], rtrim($page->relativePagePath(), '/'), $path);
return $path . $basename;
return null;
}
/**
@@ -420,9 +420,11 @@ trait PageRoutableTrait
return $this->_parentCache;
}
// Use filesystem as \dirname() does not work in Windows because of '/foo' becomes '\'.
$filesystem = Filesystem::getInstance(false);
$directory = $this->getFlexDirectory();
$parentKey = ltrim(dirname("/{$this->getKey()}"), '/');
if ($parentKey) {
$parentKey = ltrim($filesystem->dirname("/{$this->getKey()}"), '/');
if ('' !== $parentKey) {
$parent = $directory->getObject($parentKey);
$language = $this->getLanguage();
if ($language && $parent && method_exists($parent, 'getTranslation')) {
@@ -433,7 +435,7 @@ trait PageRoutableTrait
} else {
$index = $directory->getIndex();
$this->_parentCache = method_exists($index, 'getRoot') ? $index->getRoot() : null;
$this->_parentCache = \is_callable([$index, 'getRoot']) ? $index->getRoot() : null;
}
return $this->_parentCache;
@@ -497,22 +499,22 @@ trait PageRoutableTrait
public function activeChild(): bool
{
$grav = Grav::instance();
/** @var Uri $uri */
$uri = $grav['uri'];
/** @var Pages $pages */
$pages = $grav['pages'];
$uri_path = rtrim(urldecode($uri->path()), '/');
$routes = $pages->routes();
if (isset($routes[$uri_path])) {
/** @var PageInterface $child_page|null */
$child_page = $pages->find($uri->route())->parent();
if (null !== $child_page) {
while (!$child_page->root()) {
if ($this->path() === $child_page->path()) {
return true;
}
/** @var PageInterface $child_page|null */
$child_page = $child_page->parent();
$page = $pages->find($uri->route());
/** @var PageInterface|null $child_page */
$child_page = $page ? $page->parent() : null;
while ($child_page && !$child_page->root()) {
if ($this->path() === $child_page->path()) {
return true;
}
$child_page = $child_page->parent();
}
}
@@ -14,8 +14,10 @@ use Grav\Common\Grav;
use Grav\Common\Media\Interfaces\MediaCollectionInterface;
use Grav\Common\Media\Interfaces\MediaUploadInterface;
use Grav\Common\Media\Traits\MediaTrait;
use Grav\Common\Page\Media;
use Grav\Common\Page\Medium\Medium;
use Grav\Common\Page\Medium\MediumFactory;
use Grav\Common\Utils;
use Grav\Framework\Cache\CacheInterface;
use Grav\Framework\Filesystem\Filesystem;
use Grav\Framework\Flex\FlexDirectory;
@@ -23,6 +25,7 @@ use Grav\Framework\Form\FormFlashFile;
use Psr\Http\Message\UploadedFileInterface;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use RuntimeException;
use function array_key_exists;
use function in_array;
use function is_array;
use function is_callable;
@@ -75,11 +78,40 @@ trait FlexMediaTrait
return $media;
}
/**
* @param string $field
* @return MediaCollectionInterface|null
*/
public function getMediaField(string $field): ?MediaCollectionInterface
{
// Field specific media.
$settings = $this->getFieldSettings($field);
if (!empty($settings['media_field'])) {
$var = 'destination';
} elseif (!empty($settings['media_picker_field'])) {
$var = 'folder';
}
if (empty($var)) {
// Not a media field.
$media = null;
} elseif ($settings['self']) {
// Uses main media.
$media = $this->getMedia();
} else {
// Uses custom media.
$media = new Media($settings[$var]);
$this->addUpdatedMedia($media);
}
return $media;
}
/**
* @param string $field
* @return array|null
*/
protected function getFieldSettings(string $field): ?array
public function getFieldSettings(string $field): ?array
{
if ($field === '') {
return null;
@@ -88,14 +120,32 @@ trait FlexMediaTrait
// Load settings for the field.
$schema = $this->getBlueprint()->schema();
$settings = $field && is_object($schema) ? (array)$schema->getProperty($field) : null;
if (!isset($settings) || !is_array($settings)) {
return null;
}
if (isset($settings['type']) && (in_array($settings['type'], ['avatar', 'file', 'pagemedia']) || !empty($settings['destination']))) {
// Set destination folder.
$type = $settings['type'] ?? '';
// Media field.
if (!empty($settings['media_field']) || array_key_exists('destination', $settings) || in_array($type, ['avatar', 'file', 'pagemedia'], true)) {
$settings['media_field'] = true;
if (empty($settings['destination']) || in_array($settings['destination'], ['@self', 'self@', '@self@'], true)) {
$settings['destination'] = $this->getMediaFolder();
$var = 'destination';
}
// Media picker field.
if (!empty($settings['media_picker_field']) || in_array($type, ['filepicker', 'pagemediaselect'], true)) {
$settings['media_picker_field'] = true;
$var = 'folder';
}
// Set media folder for media fields.
if (isset($var)) {
$folder = $settings[$var] ?? '';
if (in_array(rtrim($folder, '/'), ['', '@self', 'self@', '@self@'], true)) {
$settings[$var] = $this->getMediaFolder();
$settings['self'] = true;
} else {
$settings[$var] = Utils::getPathFromToken($folder, $this);
$settings['self'] = false;
}
}
@@ -115,7 +165,6 @@ trait FlexMediaTrait
return $settings + ['accept' => '*', 'limit' => 1000, 'self' => true];
}
protected function getMediaFields(): array
{
// Load settings for the field.
@@ -206,12 +255,13 @@ trait FlexMediaTrait
*/
public function uploadMediaFile(UploadedFileInterface $uploadedFile, string $filename = null, string $field = null): void
{
$media = $this->getMedia();
$settings = $this->getMediaFieldSettings($field ?? '');
$media = $field ? $this->getMediaField($field) : $this->getMedia();
if (!$media instanceof MediaUploadInterface) {
throw new RuntimeException("Media for {$this->getFlexDirectory()->getFlexType()} doesn't support file uploads.");
}
$settings = $this->getMediaFieldSettings($field ?? '');
$filename = $media->checkUploadedFile($uploadedFile, $filename, $settings);
$media->copyUploadedFile($uploadedFile, $filename, $settings);
$this->clearMediaCache();
@@ -322,13 +372,20 @@ trait FlexMediaTrait
foreach ($this->getUpdatedMedia() as $filename => $upload) {
if (is_array($upload)) {
// Uses new format with [UploadedFileInterface, array].
$upload = $upload[0];
$settings = $upload[1];
if ($settings['destination'] === $media->getPath()) {
$upload = $upload[0];
} else {
$upload = false;
}
}
if ($upload) {
$medium = MediumFactory::fromUploadedFile($upload);
if (false !== $upload) {
$medium = $upload ? MediumFactory::fromUploadedFile($upload) : null;
$updated = true;
if ($medium) {
$updated = true;
$media->add($filename, $medium);
} else {
$media->hide($filename);
}
}
}
@@ -356,7 +413,6 @@ trait FlexMediaTrait
return;
}
// Upload/delete altered files.
/**
* @var string $filename