Files
intotheeast-com-content/plugins/api/classes/Api/AdminProxy.php
T

290 lines
8.1 KiB
PHP

<?php
declare(strict_types=1);
namespace Grav\Plugin\Api;
use Grav\Common\Data\Blueprints;
use Grav\Common\Grav;
use Grav\Common\Language\Language;
use Grav\Common\Page\Interfaces\PageInterface;
use Grav\Common\Page\Pages;
use Grav\Common\User\Interfaces\UserInterface;
/**
* Lightweight admin proxy registered as $grav['admin'] during API requests.
*
* Grav core checks `isset($grav['admin'])` in multiple places to alter
* behavior: page routing/visibility, Flex authorization scope, blueprint
* field handling, and event firing. Without this proxy, API-driven changes
* operate in "site" scope rather than "admin" scope, causing subtle bugs:
*
* - Non-routable/hidden pages invisible to API (Pages.php:1047)
* - Flex onAdminSave/AfterSave events don't fire (FlexGravTrait.php:60)
* - Blueprint edit mode not set (Page.php:1261)
* - Flex authorization uses 'site' scope instead of 'admin' (FlexObject.php)
* - Plugins checking isAdmin() return false
*
* This class implements the minimum interface that Grav core actually calls
* on $grav['admin'], without pulling in the full admin plugin dependency.
*/
class AdminProxy
{
/** @var string Admin base route (not applicable for API, but required by getRouteDetails) */
public string $base = '';
/** @var string Current location segment */
public string $location = '';
/** @var string Current route */
public string $route = '';
/** @var UserInterface The authenticated API user */
public UserInterface $user;
/** @var bool Whether multilang is enabled */
public bool $multilang = false;
/** @var string Active language */
public string $language = '';
/** @var string[] Enabled languages */
public array $languages_enabled = [];
/** @var array<int, array{message: string, scope: string}> Queued temp messages */
public array $temp_messages = [];
private Grav $grav;
private ?Blueprints $blueprintsLoader = null;
/** @var array<string, PageInterface|null> Page cache */
private array $pages = [];
public function __construct(Grav $grav, UserInterface $user)
{
$this->grav = $grav;
$this->user = $user;
/** @var Language $language */
$language = $grav['language'];
$this->multilang = $language->enabled();
if ($this->multilang) {
$this->language = $language->getActive() ?? '';
$this->languages_enabled = (array) $grav['config']->get('system.languages.supported', []);
}
}
/**
* Register this proxy as $grav['admin'].
*/
public function register(): void
{
$this->grav['admin'] = $this;
}
/**
* Get the current admin page (used by Pages.php and Plugin.php).
*
* In API context there's no "current admin page" being edited, so this
* returns the page at the current route if set, or null.
*/
public function page($route = false, $path = null): ?PageInterface
{
if (!$path) {
$path = $this->route;
}
if ($route && !$path) {
$path = '/';
}
if (!$path) {
return null;
}
if (!isset($this->pages[$path])) {
$this->pages[$path] = $this->getPage($path);
}
return $this->pages[$path];
}
/**
* Find a page by path (used by Pages.php for parent resolution).
*/
public function getPage(string $path): ?PageInterface
{
$pages = self::enablePages();
if ($path && $path[0] !== '/') {
$path = "/{$path}";
}
$path = urldecode($path);
return $path ? $pages->find($path, true) : $pages->root();
}
/**
* Return route details as [base, location, route] tuple.
*
* Used by Pages.php and AccountsServiceProvider.php to determine
* which admin section is active. For API requests, we return empty
* values since there's no admin page navigation happening.
*/
public function getRouteDetails(): array
{
return [$this->base, $this->location, $this->route];
}
/**
* Load a blueprint by type (used by Flex PageObject).
*/
public function blueprints(string $type)
{
if ($this->blueprintsLoader === null) {
$this->blueprintsLoader = new Blueprints('blueprints://');
}
return $this->blueprintsLoader->get($type);
}
/**
* Translate a string using Grav's language system.
*
* This is a static method in the real Admin class, but core calls it
* on the instance via $grav['admin']->translate().
*
* @param array|string $args
* @param array|string|null $languages
* @return string
*/
public static function translate($args, $languages = null): string
{
$grav = Grav::instance();
if (is_array($args)) {
$lookup = array_shift($args);
} else {
$lookup = $args;
$args = [];
}
if (!$languages) {
if ($grav['config']->get('system.languages.translations_fallback', true)) {
$languages = $grav['language']->getFallbackLanguages();
} else {
$languages = (array) $grav['language']->getDefault();
}
if (isset($grav['user']) && $grav['user']->authenticated) {
$languages = [$grav['user']->language];
}
} else {
$languages = (array) $languages;
}
foreach ((array) $languages as $lang) {
$translation = $grav['language']->getTranslation($lang, $lookup, true);
if (!$translation) {
$language = $grav['language']->getDefault() ?: 'en';
$translation = $grav['language']->getTranslation($language, $lookup, true);
}
if (!$translation) {
$translation = $grav['language']->getTranslation('en', $lookup, true);
}
if ($translation) {
if (count($args) >= 1) {
return vsprintf($translation, $args);
}
return $translation;
}
}
return $lookup;
}
/**
* Add a flash message to the session queue.
*
* Mirrors Admin::setMessage(). Admin-aware plugins routinely call this from
* onAdminSave/onAdminAfterSave handlers (e.g. to report generated image
* derivatives). The core `messages` service always resolves — returning a
* transient Messages instance when there's no active session — so queuing
* here is harmless under the API and simply discarded after the request.
*
* @param string $msg
* @param string $type
* @return void
*/
public function setMessage($msg, $type = 'info'): void
{
$messages = $this->grav['messages'];
$messages->add($msg, $type);
}
/**
* Fetch and clear messages from the session queue.
*
* Mirrors Admin::messages().
*
* @param string|null $type
* @return array
*/
public function messages($type = null): array
{
$messages = $this->grav['messages'];
return $messages->fetch($type);
}
/**
* Queue a temporary message.
*
* Mirrors Admin::addTempMessage().
*
* @param string $msg
* @param string $type
* @return void
*/
public function addTempMessage($msg, $type): void
{
$this->temp_messages[] = ['message' => $msg, 'scope' => $type];
}
/**
* Return queued temporary messages.
*
* Mirrors Admin::getTempMessages().
*
* @return array
*/
public function getTempMessages(): array
{
return $this->temp_messages;
}
/**
* Enable and return the Pages service.
*
* Mirrors Admin::enablePages() — ensures pages are initialized
* (they are disabled by default during API requests for performance).
*/
public static function enablePages(): Pages
{
static $pages;
if ($pages) {
return $pages;
}
$grav = Grav::instance();
/** @var Pages $pages */
$pages = $grav['pages'];
$pages->enablePages();
return $pages;
}
}