template rapport_dactivitees avec pdf à télécharger
This commit is contained in:
@@ -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'];
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -519,7 +519,7 @@ class Uri
|
||||
* Return the full uri
|
||||
*
|
||||
* @param bool $include_root
|
||||
* @return mixed
|
||||
* @return string
|
||||
*/
|
||||
public function uri($include_root = true)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user