Selaa lähdekoodia

template rapport_dactivitees avec pdf à télécharger

ouidade 3 vuotta sitten
vanhempi
commit
f8ecbbe597
39 muutettua tiedostoa jossa 523 lisäystä ja 203 poistoa
  1. 21 0
      CHANGELOG.md
  2. 1 1
      SECURITY.md
  3. 18 18
      composer.lock
  4. 1 1
      system/defines.php
  5. 14 9
      system/src/Grav/Common/Data/Validation.php
  6. 2 1
      system/src/Grav/Common/Flex/Types/Pages/PageIndex.php
  7. 23 0
      system/src/Grav/Common/Flex/Types/Pages/PageObject.php
  8. 2 0
      system/src/Grav/Common/Flex/Types/Pages/Traits/PageRoutableTrait.php
  9. 1 1
      system/src/Grav/Common/Grav.php
  10. 11 0
      system/src/Grav/Common/Page/Medium/AbstractMedia.php
  11. 12 10
      system/src/Grav/Common/Page/Page.php
  12. 114 71
      system/src/Grav/Common/Page/Pages.php
  13. 1 1
      system/src/Grav/Common/Uri.php
  14. 112 33
      system/src/Grav/Common/Utils.php
  15. 14 12
      system/src/Grav/Framework/Flex/Pages/Traits/PageRoutableTrait.php
  16. 69 13
      system/src/Grav/Framework/Flex/Traits/FlexMediaTrait.php
  17. 0 1
      user/blueprints/pages/modular/personnes.yaml
  18. 0 1
      user/blueprints/pages/modular/programmes.yaml
  19. 48 0
      user/blueprints/pages/modular/rapport_dactivitees.yaml
  20. 1 1
      user/config/site.yaml
  21. 1 1
      user/config/system.yaml
  22. BIN
      user/pages/01.home/02._programmes/logo_coubertin.png
  23. 2 2
      user/pages/01.home/02._programmes/programmes.md
  24. BIN
      user/pages/01.home/03._rapports-dactivitees/190410_events2event.pdf
  25. 0 0
      user/pages/01.home/03._rapports-dactivitees/images.jpeg
  26. 19 0
      user/pages/01.home/03._rapports-dactivitees/rapport_dactivitees.md
  27. 0 13
      user/pages/01.home/03._rapports-dactivites/showcase.md
  28. 1 0
      user/pages/01.home/04._gouvernance/01._conseil-dadministration/02._membres-du-conseil-dadministration/personnes.md
  29. 0 0
      user/pages/01.home/04._gouvernance/01._conseil-dadministration/02._membres-du-conseil-dadministration/photo_butlen.jpeg
  30. 0 0
      user/pages/01.home/04._gouvernance/02._equipe-epau/Alain Maugard_TROUVE.jpg
  31. 0 0
      user/pages/01.home/04._gouvernance/02._equipe-epau/personnes.md
  32. BIN
      user/pages/01.home/04._gouvernance/1612260288596.jpg
  33. BIN
      user/pages/01.home/04._gouvernance/aurelie-cousi-nommee-aux-fonctions-de-directrice.jpg
  34. BIN
      user/pages/01.home/04._gouvernance/catherine-chevillot-nouvelle-presidente-de-cite.JPG
  35. 0 1
      user/pages/01.home/04._gouvernance/gouvernance.md
  36. 1 1
      user/pages/01.home/modular.md
  37. 5 8
      user/themes/epau-antimatter/templates/modular/personnes.html.twig
  38. 4 3
      user/themes/epau-antimatter/templates/modular/programmes.html.twig
  39. 25 0
      user/themes/epau-antimatter/templates/modular/rapport_dactivitees.html.twig

+ 21 - 0
CHANGELOG.md

@@ -1,3 +1,24 @@
+# v1.7.9
+## 03/19/2021
+
+1. [](#new)
+    * Added `Media::hide()` method to hide files from media
+    * Added `Utils::getPathFromToken()` method which works also with `Flex Objects`
+    * Added `FlexMediaTrait::getMediaField()`, which can be used to access custom media set in the blueprint fields
+    * Added `FlexMediaTrait::getFieldSettings()`, which can be used to get media field settings
+1. [](#improved)
+    * Method `Utils::getPagePathFromToken()` now calls the more generic `Utils::getPathFromToken()`
+    * Updated `SECURITY.md` to use security@getgrav.org
+1. [](#bugfix)
+    * Fixed broken media upload in `Flex` with `@self/path`, `@page` and `@theme` destinations [#3275](https://github.com/getgrav/grav/issues/3275)
+    * Fixed media fields excluding newly deleted files before saving the object
+    * Fixed method `$pages->find()` should never redirect [#3266](https://github.com/getgrav/grav/pull/3266)
+    * Fixed `Page::activeChild()` throwing an error [#3276](https://github.com/getgrav/grav/issues/3276)
+    * Fixed `Flex Page` CRUD ACL when creating a new page (needs Flex Objects plugin update) [grav-plugin-flex-objects#115](https://github.com/trilbymedia/grav-plugin-flex-objects/issues/115)
+    * Fixed the list of pages not showing up in admin [#3280](https://github.com/getgrav/grav/issues/3280)
+    * Fixed text field min/max validation for UTF8 characters [#3281](https://github.com/getgrav/grav/issues/3281)
+    * Fixed redirects using wrong redirect code
+
 # v1.7.8
 ## 03/17/2021
 

+ 1 - 1
SECURITY.md

@@ -18,4 +18,4 @@ If you cannot update to the latest stable version available because, for example
 
 ## Reporting a Vulnerability
 
-Please contact contact@getgrav.org with a detailed explaination of the security issue found and we will work with you to get it resolved as fast as possible.
+Please contact security@getgrav.org with a detailed explaination of the security issue found and we will work with you to get it resolved as fast as possible.

+ 18 - 18
composer.lock

@@ -381,16 +381,16 @@
         },
         {
             "name": "donatj/phpuseragentparser",
-            "version": "v1.3.0",
+            "version": "v1.4.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/donatj/PhpUserAgent.git",
-                "reference": "f9a521726b2ce4c5173281ceaab5a02c05b691ef"
+                "reference": "246c1cf0a44f07168c702203bf30d5f48f17bab0"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/donatj/PhpUserAgent/zipball/f9a521726b2ce4c5173281ceaab5a02c05b691ef",
-                "reference": "f9a521726b2ce4c5173281ceaab5a02c05b691ef",
+                "url": "https://api.github.com/repos/donatj/PhpUserAgent/zipball/246c1cf0a44f07168c702203bf30d5f48f17bab0",
+                "reference": "246c1cf0a44f07168c702203bf30d5f48f17bab0",
                 "shasum": ""
             },
             "require": {
@@ -433,7 +433,7 @@
             ],
             "support": {
                 "issues": "https://github.com/donatj/PhpUserAgent/issues",
-                "source": "https://github.com/donatj/PhpUserAgent/tree/v1.3.0"
+                "source": "https://github.com/donatj/PhpUserAgent/tree/v1.4.0"
             },
             "funding": [
                 {
@@ -445,7 +445,7 @@
                     "type": "github"
                 }
             ],
-            "time": "2021-02-18T04:30:49+00:00"
+            "time": "2021-03-16T16:25:14+00:00"
         },
         {
             "name": "dragonmantank/cron-expression",
@@ -642,16 +642,16 @@
         },
         {
             "name": "filp/whoops",
-            "version": "2.9.2",
+            "version": "2.10.0",
             "source": {
                 "type": "git",
                 "url": "https://github.com/filp/whoops.git",
-                "reference": "df7933820090489623ce0be5e85c7e693638e536"
+                "reference": "6ecda5217bf048088b891f7403b262906be5a957"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/filp/whoops/zipball/df7933820090489623ce0be5e85c7e693638e536",
-                "reference": "df7933820090489623ce0be5e85c7e693638e536",
+                "url": "https://api.github.com/repos/filp/whoops/zipball/6ecda5217bf048088b891f7403b262906be5a957",
+                "reference": "6ecda5217bf048088b891f7403b262906be5a957",
                 "shasum": ""
             },
             "require": {
@@ -701,7 +701,7 @@
             ],
             "support": {
                 "issues": "https://github.com/filp/whoops/issues",
-                "source": "https://github.com/filp/whoops/tree/2.9.2"
+                "source": "https://github.com/filp/whoops/tree/2.10.0"
             },
             "funding": [
                 {
@@ -709,7 +709,7 @@
                     "type": "github"
                 }
             ],
-            "time": "2021-01-24T12:00:00+00:00"
+            "time": "2021-03-16T12:00:00+00:00"
         },
         {
             "name": "gregwar/cache",
@@ -4929,16 +4929,16 @@
         },
         {
             "name": "phpunit/phpunit",
-            "version": "9.5.2",
+            "version": "9.5.3",
             "source": {
                 "type": "git",
                 "url": "https://github.com/sebastianbergmann/phpunit.git",
-                "reference": "f661659747f2f87f9e72095bb207bceb0f151cb4"
+                "reference": "27241ac75fc37ecf862b6e002bf713b6566cbe41"
             },
             "dist": {
                 "type": "zip",
-                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/f661659747f2f87f9e72095bb207bceb0f151cb4",
-                "reference": "f661659747f2f87f9e72095bb207bceb0f151cb4",
+                "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/27241ac75fc37ecf862b6e002bf713b6566cbe41",
+                "reference": "27241ac75fc37ecf862b6e002bf713b6566cbe41",
                 "shasum": ""
             },
             "require": {
@@ -5016,7 +5016,7 @@
             ],
             "support": {
                 "issues": "https://github.com/sebastianbergmann/phpunit/issues",
-                "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.2"
+                "source": "https://github.com/sebastianbergmann/phpunit/tree/9.5.3"
             },
             "funding": [
                 {
@@ -5028,7 +5028,7 @@
                     "type": "github"
                 }
             ],
-            "time": "2021-02-02T14:45:58+00:00"
+            "time": "2021-03-17T07:30:34+00:00"
         },
         {
             "name": "psr/http-client",

+ 1 - 1
system/defines.php

@@ -8,7 +8,7 @@
 
 // Some standard defines
 define('GRAV', true);
-define('GRAV_VERSION', '1.7.8');
+define('GRAV_VERSION', '1.7.9');
 define('GRAV_SCHEMA', '1.7.0_2020-11-20_1');
 define('GRAV_TESTING', false);
 

+ 14 - 9
system/src/Grav/Common/Data/Validation.php

@@ -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;
     }
 
     /**

+ 2 - 1
system/src/Grav/Common/Flex/Types/Pages/PageIndex.php

@@ -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()),

+ 23 - 0
system/src/Grav/Common/Flex/Types/Pages/PageObject.php

@@ -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

+ 2 - 0
system/src/Grav/Common/Flex/Types/Pages/Traits/PageRoutableTrait.php

@@ -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
system/src/Grav/Common/Grav.php

@@ -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;
         }
 

+ 11 - 0
system/src/Grav/Common/Page/Medium/AbstractMedia.php

@@ -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
system/src/Grav/Common/Page/Page.php

@@ -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();
             }
         }
 

+ 114 - 71
system/src/Grav/Common/Page/Pages.php

@@ -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 ($page) {
+            $routable = $page->routable();
+            if ($redirect) {
+                if ($page->redirect()) {
+                    // Follow a redirect page.
+                    $this->grav->redirectLangSafe($page->redirect());
+                }
+
+                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());
+                }
+            }
 
-        // 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());
+            if ($routable) {
+                return $page;
             }
+        }
 
-            // 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());
-                }
+        $route = urldecode((string)$route);
 
-                /** @var Config $config */
-                $config = $this->grav['config'];
+        // 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);
+        }
 
-                // 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);
-
-                    // 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());
-                            }
-                        }
-                    }
+        /** @var Config $config */
+        $config = $this->grav['config'];
 
-                    // 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());
-                            }
-                        }
+        /** @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
system/src/Grav/Common/Uri.php

@@ -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
system/src/Grav/Common/Utils.php

@@ -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;
-            }
-        } else {
-            return $path . $basename;
-        }
 
-        if (!$page) {
-            throw new RuntimeException('Page route not found: ' . $path);
+                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;
         }
 
-        $path = str_replace($matches[0], rtrim($page->relativePagePath(), '/'), $path);
+        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], ':/')
+                ];
+            }
+        }
 
-        return $path . $basename;
+        return null;
     }
 
     /**

+ 14 - 12
system/src/Grav/Framework/Flex/Pages/Traits/PageRoutableTrait.php

@@ -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();
             }
         }
 

+ 69 - 13
system/src/Grav/Framework/Flex/Traits/FlexMediaTrait.php

@@ -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

+ 0 - 1
user/blueprints/pages/modular/personnes.yaml

@@ -42,4 +42,3 @@ form:
                 .portrait:
                   type: filepicker
                   label: Portrait
-                  folder: 'user/pages/01.home/04._gouvernance'

+ 0 - 1
user/blueprints/pages/modular/programmes.yaml

@@ -41,4 +41,3 @@ form:
                   name: logo_du_programme
                   type: filepicker
                   label: Logo
-                  folder: 'user/pages/01.home/02._programmes'

+ 48 - 0
user/blueprints/pages/modular/rapport_dactivitees.yaml

@@ -0,0 +1,48 @@
+title: Rapport d'activitées
+'@extends': default
+
+form:
+  fields:
+    tabs:
+      fields:
+        advanced:
+          fields:
+            columns:
+              fields:
+                column1:
+                  fields:
+                    name:
+                      default: modular/rapport_dactivitees
+                      '@data-options': '\Grav\Common\Page\Pages::modularTypes'
+            overrides:
+              fields:
+                header.template:
+                  default: modular/rapport_dactivitees
+                  '@data-options': '\Grav\Common\Page\Pages::modularTypes'
+        rapports:
+          type: tab
+          title: Rapports d'activitées
+          fields:
+            header.rapports:
+              name: rapports
+              type: list
+              label: Rapports d'activitées
+
+              fields:
+                .titre_du_rapport:
+                  type: text
+                  label: Titre du rapport
+                .texte_de_presentation:
+                  type: markdown
+                  label: Texte de présentation du rapport
+                  validate:
+                    type: textarea
+                .couverture:
+                  name: couv_du_rapport
+                  type: filepicker
+                  label: Image de couverture
+                .pdf:
+                  type: filepicker
+                  label: pdf à télécharger
+                  accept:
+                    - .pdf

+ 1 - 1
user/config/site.yaml

@@ -1,7 +1,7 @@
 title: 'GIP EPAU'
 default_lang: fr
 author:
-  name: 'Ouidade Soussi'
+  name: 'Ouidade Soussi Chiadmi'
   email: ouidade@figureslibres.io
 taxonomies:
   - category

+ 1 - 1
user/config/system.yaml

@@ -96,7 +96,7 @@ pages:
 cache:
   enabled: true
   check:
-    method: file
+    method: hash
   driver: auto
   prefix: g
   purge_at: '0 4 * * *'

BIN
user/pages/01.home/02._programmes/logo_coubertin.png


+ 2 - 2
user/pages/01.home/02._programmes/programmes.md

@@ -17,10 +17,10 @@ programmes:
     -
         nom_du_programme: Coubertin
         texte_de_presentation: 'Le programme de recherche-action Coubertin construit le récit, au fil de l’eau, de la conception des ouvrages et des opérations d’aménagement des Jeux Olympiques et Paralympiques de Paris 2024. À travers une observation embarquée au sein de la SOLIDEO, l’équipe de chercheurs analyse la production architecturale et urbaine et sa capacité à transformer les pratiques d’aménagement.'
-        logo: null
+        logo: logo_coubertin.png
 visible: true
 debugger: true
-media_order: 'popsu.png,forum_solution.png,europan_france.jpg'
+media_order: 'popsu.png,forum_solution.png,europan_france.jpg,logo_coubertin.png'
 process:
     markdown: true
     twig: false

BIN
user/pages/01.home/03._rapports-dactivitees/190410_events2event.pdf


+ 0 - 0
user/pages/01.home/03._rapports-dactivites/images.jpeg → user/pages/01.home/03._rapports-dactivitees/images.jpeg


+ 19 - 0
user/pages/01.home/03._rapports-dactivitees/rapport_dactivitees.md

@@ -0,0 +1,19 @@
+---
+title: 'Rapports d''activitées'
+body_classes: modular
+media_order: 'images.jpeg,190410_events2event.pdf'
+visible: true
+debugger: true
+rapports:
+    -
+        titre_du_rapport: 'rapport d''activitées POPSU 2019'
+        texte_de_presentation: "#### # ozMIHEFMJBZNDKCJbnW NX;, \r\noipouepuoaiejhrglnk\r\n_loremnksjdqs _\r\n"
+        couverture: images.jpeg
+        pdf: 190410_events2event.pdf
+template: modular/rapport_dactivitees
+process:
+    markdown: true
+    twig: false
+---
+
+###  Rapports d’activitées

+ 0 - 13
user/pages/01.home/03._rapports-dactivites/showcase.md

@@ -1,13 +0,0 @@
----
-title: 'Rapports d''activités'
-body_classes: modular
-media_order: images.jpeg
-visible: true
-debugger: true
----
-
-###  Rapports d’activités de l’Europe des projets architecturaux et urbains 
-
-
-[Lien pour télécharger]() 
-![]images.jpeg(http://)+ images couv

+ 1 - 0
user/pages/01.home/04._gouvernance/01._conseil-dadministration/02._membres-du-conseil-dadministration/personnes.md

@@ -10,6 +10,7 @@ personnes:
         portrait: photo_butlen.jpeg
 debugger: true
 template: modular/personnes
+media_order: photo_butlen.jpeg
 ---
 
 ### Membres du conseil d'administration

+ 0 - 0
user/pages/01.home/04._gouvernance/photo_butlen.jpeg → user/pages/01.home/04._gouvernance/01._conseil-dadministration/02._membres-du-conseil-dadministration/photo_butlen.jpeg


+ 0 - 0
user/pages/01.home/04._gouvernance/Alain Maugard_TROUVE.jpg → user/pages/01.home/04._gouvernance/02._equipe-epau/Alain Maugard_TROUVE.jpg


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
user/pages/01.home/04._gouvernance/02._equipe-epau/personnes.md


BIN
user/pages/01.home/04._gouvernance/1612260288596.jpg


BIN
user/pages/01.home/04._gouvernance/aurelie-cousi-nommee-aux-fonctions-de-directrice.jpg


BIN
user/pages/01.home/04._gouvernance/catherine-chevillot-nouvelle-presidente-de-cite.JPG


+ 0 - 1
user/pages/01.home/04._gouvernance/gouvernance.md

@@ -15,7 +15,6 @@ content:
 admin:
     children_display_order: collection
 debugger: true
-media_order: '1612260288596.jpg,Alain Maugard_TROUVE.jpg,aurelie-cousi-nommee-aux-fonctions-de-directrice.jpg,catherine-chevillot-nouvelle-presidente-de-cite.JPG,photo_butlen.jpeg'
 ---
 
 # Gouvernance

+ 1 - 1
user/pages/01.home/modular.md

@@ -11,7 +11,7 @@ content:
         custom:
             - _accueil
             - _programmes
-            - _rapports-dactivites
+            - _rapports-dactivitees
             - _gouvernance
             - _contact
 media_order: 'jeux-olypiques-paris-2024-village-athle-lot-e2.jpg,Logotype_GIP_EPAU.svg.png'

+ 5 - 8
user/themes/epau-antimatter/templates/modular/personnes.html.twig

@@ -3,12 +3,7 @@
     <div class="feature-items">
     {% for personne in page.header.personnes %}
            <div class="feature">
-            {% if personne.icon %}
-            <i class="fa fa-fw fa-{{ personne.icon }}"></i>
-            <div class="feature-content icon-offset">
-            {% else %}
-            <div class="feature-content">
-            {% endif %}
+
             {% if personne.nom %}
             <h5>{{ personne.nom }}</h5>
             {% endif %}
@@ -16,11 +11,13 @@
             <h6>{{ personne.fonction }}</h6>
             {% endif %}
             {% if personne.biographie %}
-            <p>{{ personne.biographie }}</p>
+            <p>{{ personne.biographie|markdown }}</p>
             {% endif %}
             {% if personne.portrait %}
-            <img src="/user/pages/01.home/04._gouvernance/{{personne.portrait}}" alt="portrait de {{personne.nom}}" />
+            <img src="{{page.media[personne.portrait].url|e }}" alt="portrait de {{personne.nom}}" />
+
             {% endif %}
+
             </div>
         </div>
     {% endfor %}

+ 4 - 3
user/themes/epau-antimatter/templates/modular/programmes.html.twig

@@ -3,17 +3,18 @@
 
     <div class="">
     {% for programme in page.header.programmes %}
+
         <div class="">
             <div class="">
             {% if programme.nom_du_programme %}
             <h4>{{ programme.nom_du_programme }}</h4>
             {% endif %}
             {% if programme.texte_de_presentation %}
-            <p>{{ programme.texte_de_presentation }}</p>
+            <p>{{ programme.texte_de_presentation|markdown }}</p>
             {% endif %}
             {% if programme.logo %}
-            <img src="/user/pages/01.home/02._programmes/{{programme.logo}} " alt="logo du programme {{programme.nom_du_programme}}" />
-            {{ dump(programme.logo) }}
+            <img src="{{page.media[programme.logo].url|e }}" alt="logo du programme {{programme.nom_du_programme}}" />
+
 
             {% endif %}
             </div>

+ 25 - 0
user/themes/epau-antimatter/templates/modular/rapport_dactivitees.html.twig

@@ -0,0 +1,25 @@
+<div class="modular-row {{ page.header.class}}">
+    {{ content|raw }}
+
+    <div class="">
+    {% for rapport in page.header.rapports %}
+
+        <div class="">
+            <div class="">
+            {% if rapport.titre_du_rapport %}
+            <h4>{{ rapport.titre_du_rapport }}</h4>
+            {% endif %}
+            {% if rapport.texte_de_presentation %}
+            <p>{{ rapport.texte_de_presentation|markdown }}</p>
+            {% endif %}
+            {% if rapport.couverture and rapport.pdf %}
+            <a href="{{page.media[rapport.pdf].url|e }}" {{rapport.titre_du_rapport}}>
+              <img src="{{page.media[rapport.couverture].url|e }}" alt="couverture du {{rapport.titre_du_rapport}}" />
+            </a>
+            {% endif %}
+            {{ dump(rapport)}}
+            </div>
+        </div>
+    {% endfor %}
+    </div>
+</div>

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä