mirror of
				https://github.com/getgrav/grav-plugin-admin.git
				synced 2025-11-03 20:05:53 +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;
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
}
 |