mirror of
				https://github.com/getgrav/grav-plugin-admin.git
				synced 2025-10-27 16:26:32 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			360 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			360 lines
		
	
	
		
			9.0 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
| <?php
 | |
| 
 | |
| /**
 | |
|  * @package    Grav\Plugin\Admin
 | |
|  *
 | |
|  * @copyright  Copyright (c) 2015 - 2024 Trilby Media, LLC. All rights reserved.
 | |
|  * @license    MIT License; see LICENSE file for details.
 | |
|  */
 | |
| 
 | |
| declare(strict_types=1);
 | |
| 
 | |
| namespace Grav\Plugin\Admin\Controllers;
 | |
| 
 | |
| use Grav\Common\Config\Config;
 | |
| use Grav\Common\Data\Blueprint;
 | |
| use Grav\Common\Grav;
 | |
| use Grav\Common\Language\Language;
 | |
| use Grav\Common\Page\Interfaces\PageInterface;
 | |
| use Grav\Common\Page\Page;
 | |
| use Grav\Common\Page\Pages;
 | |
| use Grav\Common\Uri;
 | |
| use Grav\Common\User\Interfaces\UserInterface;
 | |
| use Grav\Common\Utils;
 | |
| use Grav\Framework\Controller\Traits\ControllerResponseTrait;
 | |
| use Grav\Framework\RequestHandler\Exception\PageExpiredException;
 | |
| use Grav\Framework\Session\SessionInterface;
 | |
| use Grav\Plugin\Admin\Admin;
 | |
| use Grav\Plugin\Admin\AdminForm;
 | |
| use Psr\Http\Message\ResponseInterface;
 | |
| use Psr\Http\Message\ServerRequestInterface;
 | |
| use RocketTheme\Toolbox\Session\Message;
 | |
| 
 | |
| abstract class AdminController
 | |
| {
 | |
|     use ControllerResponseTrait {
 | |
|         createRedirectResponse as traitCreateRedirectResponse;
 | |
|         getErrorJson as traitGetErrorJson;
 | |
|     }
 | |
| 
 | |
|     /** @var string */
 | |
|     protected $nonce_action = 'admin-form';
 | |
|     /** @var string */
 | |
|     protected $nonce_name = 'admin-nonce';
 | |
|     /** @var Grav */
 | |
|     protected $grav;
 | |
|     /** @var PageInterface */
 | |
|     protected $page;
 | |
|     /** @var AdminForm|null */
 | |
|     protected $form;
 | |
| 
 | |
|     public function __construct(Grav $grav)
 | |
|     {
 | |
|         $this->grav = $grav;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @return PageInterface|null
 | |
|      */
 | |
|     public function getPage(): ?PageInterface
 | |
|     {
 | |
|         return $this->page;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Get currently active form.
 | |
|      *
 | |
|      * @return AdminForm|null
 | |
|      */
 | |
|     public function getActiveForm(): ?AdminForm
 | |
|     {
 | |
|         if (null === $this->form) {
 | |
|             $post = $this->getPost();
 | |
| 
 | |
|             $active = $post['__form-name__'] ?? null;
 | |
| 
 | |
|             $this->form = $active ? $this->getForm($active) : null;
 | |
|         }
 | |
| 
 | |
|         return $this->form;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Get a form.
 | |
|      *
 | |
|      * @param string $name
 | |
|      * @param array $options
 | |
|      * @return AdminForm|null
 | |
|      */
 | |
|     public function getForm(string $name, array $options = []): ?AdminForm
 | |
|     {
 | |
|         $post = $this->getPost();
 | |
|         $page = $this->getPage();
 | |
|         $forms = $page ? $page->forms() : [];
 | |
|         $blueprint = $forms[$name] ?? null;
 | |
|         if (null === $blueprint) {
 | |
|             return null;
 | |
|         }
 | |
| 
 | |
|         $active = $post['__form-name__'] ?? null;
 | |
|         $unique_id = $active && $active === $name ? ($post['__unique_form_id__'] ?? null) : null;
 | |
| 
 | |
|         $options += [
 | |
|             'unique_id' => $unique_id,
 | |
|             'blueprint' => new Blueprint(null, ['form' => $blueprint]),
 | |
|             'submit_method' => $this->getFormSubmitMethod($name),
 | |
|             'nonce_name' => $this->nonce_name,
 | |
|             'nonce_action' => $this->nonce_action,
 | |
|         ];
 | |
| 
 | |
|         return new AdminForm($name, $options);
 | |
|     }
 | |
| 
 | |
|     abstract protected function getFormSubmitMethod(string $name): callable;
 | |
| 
 | |
|     /**
 | |
|      * @param string $route
 | |
|      * @param string|null $lang
 | |
|      * @return string
 | |
|      */
 | |
|     public function getAdminUrl(string $route, ?string $lang = null): string
 | |
|     {
 | |
|         /** @var Pages $pages */
 | |
|         $pages = $this->grav['pages'];
 | |
|         $admin = $this->getAdmin();
 | |
| 
 | |
|         return $pages->baseUrl($lang) . $admin->base . $route;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @param string $route
 | |
|      * @param string|null $lang
 | |
|      * @return string
 | |
|      */
 | |
|     public function getAbsoluteAdminUrl(string $route, ?string $lang = null): string
 | |
|     {
 | |
|         /** @var Pages $pages */
 | |
|         $pages = $this->grav['pages'];
 | |
|         $admin = $this->getAdmin();
 | |
| 
 | |
|         return $pages->baseUrl($lang, true) . $admin->base . $route;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Get session.
 | |
|      *
 | |
|      * @return SessionInterface
 | |
|      */
 | |
|     public function getSession(): SessionInterface
 | |
|     {
 | |
|         return $this->grav['session'];
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @return Admin
 | |
|      */
 | |
|     protected function getAdmin(): Admin
 | |
|     {
 | |
|         return $this->grav['admin'];
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @return UserInterface
 | |
|      */
 | |
|     protected function getUser(): UserInterface
 | |
|     {
 | |
|         return $this->getAdmin()->user;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @return ServerRequestInterface
 | |
|      */
 | |
|     public function getRequest(): ServerRequestInterface
 | |
|     {
 | |
|         return $this->getAdmin()->request;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @return array
 | |
|      */
 | |
|     public function getPost(): array
 | |
|     {
 | |
|         return (array)($this->getRequest()->getParsedBody() ?? []);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Translate a string.
 | |
|      *
 | |
|      * @param string $string
 | |
|      * @param mixed ...$args
 | |
|      * @return string
 | |
|      */
 | |
|     public function translate(string $string, ...$args): string
 | |
|     {
 | |
|         /** @var Language $language */
 | |
|         $language = $this->grav['language'];
 | |
| 
 | |
|         array_unshift($args, $string);
 | |
| 
 | |
|         return $language->translate($args);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Set message to be shown in the admin.
 | |
|      *
 | |
|      * @param string $message
 | |
|      * @param string $type
 | |
|      * @return $this
 | |
|      */
 | |
|     public function setMessage(string $message, string $type = 'info'): AdminController
 | |
|     {
 | |
|         /** @var Message $messages */
 | |
|         $messages = $this->grav['messages'];
 | |
|         $messages->add($message, $type);
 | |
| 
 | |
|         return $this;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @return Config
 | |
|      */
 | |
|     protected function getConfig(): Config
 | |
|     {
 | |
|         return $this->grav['config'];
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Check if request nonce is valid.
 | |
|      *
 | |
|      * @return void
 | |
|      * @throws PageExpiredException  If nonce is not valid.
 | |
|      */
 | |
|     protected function checkNonce(): void
 | |
|     {
 | |
|         $nonce = null;
 | |
| 
 | |
|         $nonce_name = $this->form ? $this->form->getNonceName() : $this->nonce_name;
 | |
|         $nonce_action = $this->form ? $this->form->getNonceAction() : $this->nonce_action;
 | |
| 
 | |
|         if (\in_array(strtoupper($this->getRequest()->getMethod()), ['POST', 'PUT', 'PATCH', 'DELETE'])) {
 | |
|             $post = $this->getPost();
 | |
|             $nonce = $post[$nonce_name] ?? null;
 | |
|         }
 | |
| 
 | |
|         /** @var Uri $uri */
 | |
|         $uri = $this->grav['uri'];
 | |
|         if (!$nonce) {
 | |
|             $nonce = $uri->param($nonce_name);
 | |
|         }
 | |
| 
 | |
|         if (!$nonce) {
 | |
|             $nonce = $uri->query($nonce_name);
 | |
|         }
 | |
| 
 | |
|         if (!$nonce || !Utils::verifyNonce($nonce, $nonce_action)) {
 | |
|             throw new PageExpiredException($this->getRequest());
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Return the best matching mime type for the request.
 | |
|      *
 | |
|      * @param  string[] $compare
 | |
|      * @return string|null
 | |
|      */
 | |
|     protected function getAccept(array $compare): ?string
 | |
|     {
 | |
|         $accepted = [];
 | |
|         foreach ($this->getRequest()->getHeader('Accept') as $accept) {
 | |
|             foreach (explode(',', $accept) as $item) {
 | |
|                 if (!$item) {
 | |
|                     continue;
 | |
|                 }
 | |
| 
 | |
|                 $split = explode(';q=', $item);
 | |
|                 $mime = array_shift($split);
 | |
|                 $priority = array_shift($split) ?? 1.0;
 | |
| 
 | |
|                 $accepted[$mime] = $priority;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         arsort($accepted);
 | |
| 
 | |
|         // TODO: add support for image/* etc
 | |
|         $list = array_intersect($compare, array_keys($accepted));
 | |
|         if (!$list && (isset($accepted['*/*']) || isset($accepted['*']))) {
 | |
|             return reset($compare) ?: null;
 | |
|         }
 | |
| 
 | |
|         return reset($list) ?: null;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @param string $template
 | |
|      * @return PageInterface
 | |
|      */
 | |
|     protected function createPage(string $template): PageInterface
 | |
|     {
 | |
|         $page = new Page();
 | |
| 
 | |
|         // Plugins may not have the correct Cache-Control header set, force no-store for the proxies.
 | |
|         $page->expires(0);
 | |
| 
 | |
|         $filename = "plugin://admin/pages/admin/{$template}.md";
 | |
|         if (!file_exists($filename)) {
 | |
|             throw new \RuntimeException(sprintf('Creating admin page %s failed: not found', $template));
 | |
|         }
 | |
| 
 | |
|         Admin::DEBUG && Admin::addDebugMessage("Admin page: {$template}");
 | |
| 
 | |
|         $page->init(new \SplFileInfo($filename));
 | |
|         $page->slug($template);
 | |
| 
 | |
|         return $page;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @param string|null $url
 | |
|      * @param int|null $code
 | |
|      * @return ResponseInterface
 | |
|      */
 | |
|     protected function createRedirectResponse(?string $url = null, ?int $code = null): ResponseInterface
 | |
|     {
 | |
|         $request = $this->getRequest();
 | |
| 
 | |
|         if (null === $url || '' === $url) {
 | |
|             $url = (string)$request->getUri();
 | |
|         } elseif (mb_strpos($url, '/') === 0) {
 | |
|             $url = $this->getAbsoluteAdminUrl($url);
 | |
|         }
 | |
| 
 | |
|         if (null === $code) {
 | |
|             if (in_array($request->getMethod(), ['GET', 'HEAD'])) {
 | |
|                 $code = 302;
 | |
|             } else {
 | |
|                 $code = 303;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         return $this->traitCreateRedirectResponse($url, $code);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @param \Throwable $e
 | |
|      * @return array
 | |
|      */
 | |
|     protected function getErrorJson(\Throwable $e): array
 | |
|     {
 | |
|         $json = $this->traitGetErrorJson($e);
 | |
|         $code = $e->getCode();
 | |
|         if ($code === 401) {
 | |
|             $json['redirect'] = $this->getAbsoluteAdminUrl('/');
 | |
|         }
 | |
| 
 | |
|         return $json;
 | |
|     }
 | |
| 
 | |
| 
 | |
| }
 |