Skouter mortgage estimates. Web application with view written in PHP and Vue, but controller and models in Go.
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350
  1. <?php
  2. namespace Grav\Plugin;
  3. use Composer\Autoload\ClassLoader;
  4. use Grav\Common\Cache;
  5. use Grav\Common\Data\Data;
  6. use Grav\Common\Debugger;
  7. use Grav\Common\File\CompiledYamlFile;
  8. use Grav\Common\Grav;
  9. use Grav\Common\Helpers\LogViewer;
  10. use Grav\Common\Inflector;
  11. use Grav\Common\Language\Language;
  12. use Grav\Common\Page\Interfaces\PageInterface;
  13. use Grav\Common\Page\Page;
  14. use Grav\Common\Page\Pages;
  15. use Grav\Common\Plugin;
  16. use Grav\Common\Plugins;
  17. use Grav\Common\Processors\Events\RequestHandlerEvent;
  18. use Grav\Common\Session;
  19. use Grav\Common\Twig\Twig;
  20. use Grav\Common\Uri;
  21. use Grav\Common\User\Interfaces\UserInterface;
  22. use Grav\Common\Utils;
  23. use Grav\Common\Yaml;
  24. use Grav\Events\PermissionsRegisterEvent;
  25. use Grav\Framework\Acl\PermissionsReader;
  26. use Grav\Framework\Psr7\Response;
  27. use Grav\Framework\Session\Exceptions\SessionException;
  28. use Grav\Plugin\Admin\Admin;
  29. use Grav\Plugin\Admin\AdminFormFactory;
  30. use Grav\Plugin\Admin\Popularity;
  31. use Grav\Plugin\Admin\Router;
  32. use Grav\Plugin\Admin\Themes;
  33. use Grav\Plugin\Admin\AdminController;
  34. use Grav\Plugin\Admin\Twig\AdminTwigExtension;
  35. use Grav\Plugin\Admin\WhiteLabel;
  36. use Grav\Plugin\Form\Form;
  37. use Grav\Plugin\Form\Forms;
  38. use Grav\Plugin\Login\Login;
  39. use Pimple\Container;
  40. use Psr\Http\Message\ResponseInterface;
  41. use RocketTheme\Toolbox\Event\Event;
  42. use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
  43. /**
  44. * Class AdminPlugin
  45. * @package Grav\Plugin\Admin
  46. */
  47. class AdminPlugin extends Plugin
  48. {
  49. public $features = [
  50. 'blueprints' => 1000,
  51. ];
  52. /** @var bool */
  53. protected $active = false;
  54. /** @var string */
  55. protected $template;
  56. /** @var string */
  57. protected $theme;
  58. /** @var string */
  59. protected $route;
  60. /** @var string */
  61. protected $admin_route;
  62. /** @var Uri */
  63. protected $uri;
  64. /** @var Admin */
  65. protected $admin;
  66. /** @var Session */
  67. protected $session;
  68. /** @var Popularity */
  69. protected $popularity;
  70. /** @var string */
  71. protected $base;
  72. /** @var string */
  73. protected $version;
  74. /**
  75. * @return array
  76. */
  77. public static function getSubscribedEvents()
  78. {
  79. return [
  80. 'onPluginsInitialized' => [
  81. ['setup', 100000],
  82. ['onPluginsInitialized', 1001]
  83. ],
  84. 'onRequestHandlerInit' => [
  85. ['onRequestHandlerInit', 100000]
  86. ],
  87. 'onFormRegisterTypes' => ['onFormRegisterTypes', 0],
  88. 'onPageInitialized' => ['onPageInitialized', 0],
  89. 'onShutdown' => ['onShutdown', 1000],
  90. PermissionsRegisterEvent::class => ['onRegisterPermissions', 1000],
  91. ];
  92. }
  93. /**
  94. * Get list of form field types specified in this plugin. Only special types needs to be listed.
  95. *
  96. * @return array
  97. */
  98. public function getFormFieldTypes()
  99. {
  100. return [
  101. 'column' => [
  102. 'input@' => false
  103. ],
  104. 'columns' => [
  105. 'input@' => false
  106. ],
  107. 'fieldset' => [
  108. 'input@' => false
  109. ],
  110. 'section' => [
  111. 'input@' => false
  112. ],
  113. 'list' => [
  114. 'array' => true
  115. ],
  116. 'elements' => [
  117. 'input@' => true
  118. ],
  119. 'element' => [
  120. 'input@' => false
  121. ],
  122. 'file' => [
  123. 'array' => true,
  124. 'media_field' => true,
  125. 'validate' => [
  126. 'type' => 'ignore'
  127. ]
  128. ],
  129. 'pagemedia' => [
  130. 'array' => true,
  131. 'media_field' => true,
  132. 'validate' => [
  133. 'type' => 'ignore'
  134. ]
  135. ],
  136. 'filepicker' => [
  137. 'media_picker_field' => true
  138. ],
  139. 'pagemediaselect' => [
  140. 'media_picker_field' => true
  141. ],
  142. 'permissions' => [
  143. 'ignore_empty' => true,
  144. 'validate' => [
  145. 'type' => 'array'
  146. ],
  147. 'filter' => [
  148. 'type' => 'flatten_array',
  149. 'value_type' => 'bool',
  150. ]
  151. ],
  152. 'acl_picker' => [
  153. 'ignore_empty' => true,
  154. 'validate' => [
  155. 'type' => 'array'
  156. ],
  157. 'filter' => [
  158. 'type' => 'array',
  159. 'key_type' => 'string',
  160. 'value_type' => 'bool',
  161. ]
  162. ],
  163. 'taxonomy' => [
  164. 'multiple' => true,
  165. 'validate' => [
  166. 'type' => 'array'
  167. ]
  168. ]
  169. ];
  170. }
  171. /**
  172. * @return ClassLoader
  173. */
  174. public function autoload(): ClassLoader
  175. {
  176. return require __DIR__ . '/vendor/autoload.php';
  177. }
  178. /**
  179. * @param Event $event
  180. * @return void
  181. */
  182. public function onFormRegisterTypes(Event $event): void
  183. {
  184. /** @var Forms $forms */
  185. $forms = $event['forms'];
  186. $forms->registerType('admin', new AdminFormFactory());
  187. }
  188. /**
  189. * [onPluginsInitialized:100000]
  190. *
  191. * If the admin path matches, initialize the Login plugin configuration and set the admin
  192. * as active.
  193. *
  194. * @return void
  195. */
  196. public function setup()
  197. {
  198. // Only enable admin if it has a route.
  199. $route = $this->config->get('plugins.admin.route');
  200. if (!$route) {
  201. return;
  202. }
  203. /** @var Uri uri */
  204. $this->uri = $this->grav['uri'];
  205. $this->base = '/' . trim($route, '/');
  206. $this->admin_route = rtrim($this->grav['pages']->base(), '/') . $this->base;
  207. $inAdmin = $this->isAdminPath();
  208. // If no users found, go to register.
  209. if (!$inAdmin && !Admin::doAnyUsersExist()) {
  210. $this->grav->redirect($this->admin_route);
  211. }
  212. // Only setup admin if we're inside the admin path.
  213. if ($inAdmin) {
  214. $this->setupAdmin();
  215. }
  216. }
  217. /**
  218. * [onPluginsInitialized:1001]
  219. *
  220. * If the admin plugin is set as active, initialize the admin
  221. *
  222. * @return void
  223. */
  224. public function onPluginsInitialized()
  225. {
  226. // Only activate admin if we're inside the admin path.
  227. if ($this->active) {
  228. $this->initializeAdmin();
  229. }
  230. // Always initialize popularity.
  231. $this->popularity = new Popularity();
  232. }
  233. /**
  234. * [onRequestHandlerInit:100000]
  235. *
  236. * @param RequestHandlerEvent $event
  237. * @return void
  238. */
  239. public function onRequestHandlerInit(RequestHandlerEvent $event)
  240. {
  241. // Store this version.
  242. $this->version = $this->getBlueprint()->get('version');
  243. $route = $event->getRoute();
  244. $base = $route->getRoute(0, 1);
  245. if ($base === $this->base) {
  246. /** @var Debugger $debugger */
  247. $debugger = $this->grav['debugger'];
  248. $debugger->addMessage('Admin v' . $this->version);
  249. $event->addMiddleware('admin_router', new Router($this->grav, $this->admin));
  250. }
  251. }
  252. /**
  253. * @param Event $event
  254. * @return void
  255. */
  256. public function onAdminControllerInit(Event $event): void
  257. {
  258. $eventController = $event['controller'];
  259. // Blacklist login related views.
  260. $loginViews = ['login', 'forgot', 'register', 'reset'];
  261. $eventController->blacklist_views = array_merge($eventController->blacklist_views, $loginViews);
  262. }
  263. /**
  264. * Force compile during save if admin plugin save
  265. *
  266. * @param Event $event
  267. * @return void
  268. */
  269. public function onAdminSave(Event $event)
  270. {
  271. $obj = $event['object'];
  272. if ($obj instanceof Data
  273. && ($blueprint = $obj->blueprints()) && $blueprint && $blueprint->getFilename() === 'admin/blueprints') {
  274. [$status, $msg] = $this->grav['admin-whitelabel']->compilePresetScss($obj);
  275. if (!$status) {
  276. $this->grav['messages']->add($msg, 'error');
  277. }
  278. }
  279. }
  280. /**
  281. * [onPageInitialized:0]
  282. *
  283. * @return void
  284. */
  285. public function onPageInitialized()
  286. {
  287. $template = $this->uri->param('tmpl');
  288. if ($template) {
  289. /** @var PageInterface $page */
  290. $page = $this->grav['page'];
  291. $page->template($template);
  292. }
  293. }
  294. /**
  295. * [onShutdown:1000]
  296. *
  297. * Handles the shutdown
  298. *
  299. * @return void
  300. */
  301. public function onShutdown()
  302. {
  303. if ($this->active) {
  304. //only activate when Admin is active
  305. if ($this->admin->shouldLoadAdditionalFilesInBackground()) {
  306. $this->admin->loadAdditionalFilesInBackground();
  307. }
  308. } elseif ($this->popularity && $this->config->get('plugins.admin.popularity.enabled')) {
  309. //if popularity is enabled, track non-admin hits
  310. $this->popularity->trackHit();
  311. }
  312. }
  313. /**
  314. * [onAdminDashboard:0]
  315. *
  316. * @return void
  317. */
  318. public function onAdminDashboard()
  319. {
  320. $lang = $this->grav['language'];
  321. $this->grav['twig']->plugins_hooked_dashboard_widgets_top[] = [
  322. 'name' => $lang->translate('PLUGIN_ADMIN.MAINTENANCE'),
  323. 'template' => 'dashboard-maintenance',
  324. ];
  325. $this->grav['twig']->plugins_hooked_dashboard_widgets_top[] = [
  326. 'name' => $lang->translate('PLUGIN_ADMIN.VIEWS_STATISTICS'),
  327. 'template' => 'dashboard-statistics',
  328. ];
  329. $this->grav['twig']->plugins_hooked_dashboard_widgets_top[] = [
  330. 'name' => $lang->translate('PLUGIN_ADMIN.NOTIFICATIONS'),
  331. 'template' => 'dashboard-notifications',
  332. ];
  333. $this->grav['twig']->plugins_hooked_dashboard_widgets_top[] = [
  334. 'name' => $lang->translate('PLUGIN_ADMIN.NEWS_FEED'),
  335. 'template' => 'dashboard-feed',
  336. ];
  337. $this->grav['twig']->plugins_hooked_dashboard_widgets_main[] = [
  338. 'name' => $lang->translate('PLUGIN_ADMIN.LATEST_PAGE_UPDATES'),
  339. 'template' => 'dashboard-pages',
  340. ];
  341. }
  342. /**
  343. * [onAdminTools:0]
  344. *
  345. * Provide the tools for the Tools page, currently only direct install
  346. *
  347. * @return void
  348. */
  349. public function onAdminTools(Event $event)
  350. {
  351. $event['tools'] = array_merge($event['tools'], [
  352. 'backups' => [['admin.maintenance', 'admin.super'], 'PLUGIN_ADMIN.BACKUPS'],
  353. 'scheduler' => [['admin.super'], 'PLUGIN_ADMIN.SCHEDULER'],
  354. 'logs' => [['admin.super'], 'PLUGIN_ADMIN.LOGS'],
  355. 'reports' => [['admin.super'], 'PLUGIN_ADMIN.REPORTS'],
  356. 'direct-install' => [['admin.super'], 'PLUGIN_ADMIN.DIRECT_INSTALL'],
  357. ]);
  358. }
  359. /**
  360. * Sets longer path to the home page allowing us to have list of pages when we enter to pages section.
  361. *
  362. * @return void
  363. */
  364. public function onPagesInitialized()
  365. {
  366. $config = $this->config;
  367. // Force SSL with redirect if required
  368. if ($config->get('system.force_ssl')) {
  369. if (!isset($_SERVER['HTTPS']) || strtolower($_SERVER['HTTPS']) !== 'on') {
  370. Admin::DEBUG && Admin::addDebugMessage('Admin SSL forced on, redirect');
  371. $url = 'https://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
  372. $this->grav->redirect($url);
  373. }
  374. }
  375. $this->session = $this->grav['session'];
  376. // set session variable if it's passed via the url
  377. if (!$this->session->user->authorize('admin.super') || $this->uri->param('mode') === 'normal') {
  378. $this->session->expert = false;
  379. } elseif ($this->uri->param('mode') === 'expert') {
  380. $this->session->expert = true;
  381. } else {
  382. // set the default if not set before
  383. $this->session->expert = $this->session->expert ?? false;
  384. }
  385. // make sure page is not frozen!
  386. unset($this->grav['page']);
  387. // Call the controller if it has been set.
  388. $adminParams = $this->admin->request->getAttribute('admin');
  389. $page = null;
  390. if (isset($adminParams['controller'])) {
  391. $controllerParams = $adminParams['controller'];
  392. $class = $controllerParams['class'];
  393. if (!class_exists($class)) {
  394. throw new \RuntimeException(sprintf('Admin controller %s does not exist', $class));
  395. }
  396. /** @var \Grav\Plugin\Admin\Controllers\AdminController $controller */
  397. $controller = new $class($this->grav);
  398. $method = $controllerParams['method'];
  399. $params = $controllerParams['params'] ?? [];
  400. if (!is_callable([$controller, $method])) {
  401. throw new \RuntimeException(sprintf('Admin controller method %s() does not exist', $method));
  402. }
  403. /** @var ResponseInterface $response */
  404. $response = $controller->{$method}(...$params);
  405. if ($response->getStatusCode() !== 418) {
  406. $this->grav->close($response);
  407. }
  408. $page = $controller->getPage();
  409. if (!$page) {
  410. throw new \RuntimeException('Not Found', 404);
  411. }
  412. $this->grav['page'] = $page;
  413. $this->admin->form = $controller->getActiveForm();
  414. $legacyController = false;
  415. } else {
  416. $legacyController = true;
  417. }
  418. /** @var UserInterface $user */
  419. $user = $this->grav['user'];
  420. // Replace page service with admin.
  421. if (empty($this->grav['page'])) {
  422. $this->grav['page'] = function () use ($user) {
  423. $page = new Page();
  424. // Plugins may not have the correct Cache-Control header set, force no-store for the proxies.
  425. $page->expires(0);
  426. if ($user->authorize('admin.login')) {
  427. $event = new Event(['page' => $page]);
  428. $event = $this->grav->fireEvent('onAdminPage', $event);
  429. /** @var PageInterface $page */
  430. $page = $event['page'];
  431. if ($page->slug()) {
  432. Admin::DEBUG && Admin::addDebugMessage('Admin page: from event');
  433. return $page;
  434. }
  435. }
  436. // Look in the pages provided by the Admin plugin itself
  437. if (file_exists(__DIR__ . "/pages/admin/{$this->template}.md")) {
  438. Admin::DEBUG && Admin::addDebugMessage("Admin page: {$this->template}");
  439. $page->init(new \SplFileInfo(__DIR__ . "/pages/admin/{$this->template}.md"));
  440. $page->slug(Utils::basename($this->template));
  441. return $page;
  442. }
  443. /** @var UniformResourceLocator $locator */
  444. $locator = $this->grav['locator'];
  445. // If not provided by Admin, lookup pages added by other plugins
  446. /** @var Plugins $plugins */
  447. $plugins = $this->grav['plugins'];
  448. foreach ($plugins as $plugin) {
  449. if ($this->config->get("plugins.{$plugin->name}.enabled") !== true) {
  450. continue;
  451. }
  452. $path = $locator->findResource("plugins://{$plugin->name}/admin/pages/{$this->template}.md");
  453. if ($path) {
  454. Admin::DEBUG && Admin::addDebugMessage("Admin page: plugin {$plugin->name}/{$this->template}");
  455. $page->init(new \SplFileInfo($path));
  456. $page->slug(Utils::basename($this->template));
  457. return $page;
  458. }
  459. }
  460. return null;
  461. };
  462. }
  463. if (empty($this->grav['page'])) {
  464. if ($user->authenticated) {
  465. Admin::DEBUG && Admin::addDebugMessage('Admin page: fire onPageNotFound event');
  466. $event = new Event(['page' => null]);
  467. $event->page = null;
  468. $event = $this->grav->fireEvent('onPageNotFound', $event);
  469. /** @var PageInterface $page */
  470. $page = $event->page;
  471. if (!$page || !$page->routable()) {
  472. Admin::DEBUG && Admin::addDebugMessage('Admin page: 404 Not Found');
  473. $error_file = $this->grav['locator']->findResource('plugins://admin/pages/admin/error.md');
  474. $page = new Page();
  475. $page->init(new \SplFileInfo($error_file));
  476. $page->slug(Utils::basename($this->route));
  477. $page->routable(true);
  478. }
  479. unset($this->grav['page']);
  480. $this->grav['page'] = $page;
  481. } else {
  482. Admin::DEBUG && Admin::addDebugMessage('Admin page: login');
  483. // Not Found and not logged in: Display login page.
  484. $login_file = $this->grav['locator']->findResource('plugins://admin/pages/admin/login.md');
  485. $page = new Page();
  486. $page->init(new \SplFileInfo($login_file));
  487. $page->slug(Utils::basename($this->route));
  488. unset($this->grav['page']);
  489. $this->grav['page'] = $page;
  490. }
  491. }
  492. if ($legacyController) {
  493. // Handle tasks.
  494. $this->admin->task = $task = $this->grav['task'] ?? $this->grav['action'];
  495. if ($task) {
  496. Admin::DEBUG && Admin::addDebugMessage("Admin task: {$task}");
  497. // Make local copy of POST.
  498. $post = $this->grav['uri']->post();
  499. $this->initializeController($task, $post);
  500. } elseif ($this->template === 'logs' && $this->route) {
  501. // Display RAW error message.
  502. $response = new Response(200, [], $this->admin->logEntry());
  503. $this->grav->close($response);
  504. }
  505. }
  506. // Explicitly set a timestamp on assets
  507. $this->grav['assets']->setTimestamp(substr(md5(GRAV_VERSION . $this->grav['config']->checksum()), 0, 10));
  508. }
  509. /**
  510. * Handles initializing the assets
  511. *
  512. * @return void
  513. */
  514. public function onAssetsInitialized()
  515. {
  516. // Disable Asset pipelining
  517. $assets = $this->grav['assets'];
  518. $assets->setJsPipeline(false);
  519. $assets->setCssPipeline(false);
  520. }
  521. /**
  522. * Add twig paths to plugin templates.
  523. *
  524. * @return void
  525. */
  526. public function onTwigTemplatePaths()
  527. {
  528. $twig_paths = [];
  529. $this->grav->fireEvent('onAdminTwigTemplatePaths', new Event(['paths' => &$twig_paths]));
  530. $twig_paths[] = __DIR__ . '/themes/' . $this->theme . '/templates';
  531. $this->grav['twig']->twig_paths = $twig_paths;
  532. }
  533. /**
  534. * Set all twig variables for generating output.
  535. *
  536. * @return void
  537. */
  538. public function onTwigSiteVariables()
  539. {
  540. /** @var Twig $twig */
  541. $twig = $this->grav['twig'];
  542. /** @var PageInterface $page */
  543. $page = $this->grav['page'];
  544. $twig->twig_vars['location'] = $this->template;
  545. $twig->twig_vars['nav_route'] = trim($this->template . '/' . $this->route, '/') . '/';
  546. $twig->twig_vars['base_url_relative_frontend'] = $twig->twig_vars['base_url_relative'] ?: '/';
  547. $twig->twig_vars['admin_route'] = trim($this->admin_route, '/');
  548. $twig->twig_vars['template_route'] = $this->template;
  549. $twig->twig_vars['current_route'] = '/' . $twig->twig_vars['admin_route'] . '/' . $this->template . '/' . $this->route;
  550. $twig->twig_vars['base_url_relative'] = $twig->twig_vars['base_url_simple'] . '/' . $twig->twig_vars['admin_route'];
  551. $twig->twig_vars['current_url'] = rtrim($twig->twig_vars['base_url_relative'] . '/' . $this->template . '/' . $this->route, '/');
  552. $theme_url = '/' . ltrim($this->grav['locator']->findResource('plugin://admin/themes/' . $this->theme,
  553. false), '/');
  554. $twig->twig_vars['theme_url'] = $theme_url;
  555. $twig->twig_vars['base_url'] = $twig->twig_vars['base_url_relative'];
  556. $twig->twig_vars['base_path'] = GRAV_ROOT;
  557. $twig->twig_vars['admin'] = $this->admin;
  558. $twig->twig_vars['user'] = $this->admin->user;
  559. $twig->twig_vars['admin_version'] = $this->version;
  560. $twig->twig_vars['logviewer'] = new LogViewer();
  561. $twig->twig_vars['form_max_filesize'] = Utils::getUploadLimit() / 1024 / 1024;
  562. // Start white label functionality
  563. $twig->twig_vars['whitelabel_presets'] = $this->getPresets();
  564. $custom_logo = $this->config->get('plugins.admin.whitelabel.logo_custom', false);
  565. $custom_login_logo = $this->config->get('plugins.admin.whitelabel.logo_login', false);
  566. $custom_footer = $this->config->get('plugins.admin.whitelabel.custom_footer', false);
  567. if ($custom_logo && is_array($custom_logo)) {
  568. $custom_logo = array_keys($custom_logo);
  569. $path = array_shift($custom_logo);
  570. $twig->twig_vars['custom_admin_logo'] = $path;
  571. }
  572. if ($custom_login_logo && is_array($custom_login_logo)) {
  573. $custom_login_logo = array_keys($custom_login_logo);
  574. $path = array_shift($custom_login_logo);
  575. $twig->twig_vars['custom_login_logo'] = $path;
  576. }
  577. if ($custom_footer) {
  578. $footer = Utils::processMarkdown($custom_footer);
  579. $twig->twig_vars['custom_admin_footer'] = $footer;
  580. }
  581. $custom_css = $this->config->get('plugins.admin.whitelabel.custom_css', false);
  582. if ($custom_css) {
  583. $this->grav['assets']->addInlineCss($custom_css);
  584. }
  585. // End white label functionality
  586. $fa_icons_file = CompiledYamlFile::instance($this->grav['locator']->findResource('plugin://admin/themes/grav/templates/forms/fields/iconpicker/icons' . YAML_EXT));
  587. $fa_icons = $fa_icons_file->content();
  588. $fa_icons = array_map(function ($icon) {
  589. //only pick used values
  590. return ['id' => $icon['id'], 'unicode' => $icon['unicode']];
  591. }, $fa_icons['icons']);
  592. $twig->twig_vars['fa_icons'] = $fa_icons;
  593. // add form if it exists in the page
  594. $header = $page->header();
  595. $forms = [];
  596. if (isset($header->forms)) foreach ($header->forms as $key => $form) {
  597. $forms[$key] = new Form($page, null, $form);
  598. }
  599. $twig->twig_vars['forms'] = $forms;
  600. // preserve form validation
  601. if ($this->admin->form) {
  602. $twig->twig_vars['form'] = $this->admin->form;
  603. } elseif (!isset($twig->twig_vars['form'])) {
  604. if (isset($header->form)) {
  605. $twig->twig_vars['form'] = new Form($page);
  606. } elseif (isset($header->forms)) {
  607. $twig->twig_vars['form'] = new Form($page, null, reset($header->forms));
  608. }
  609. }
  610. // Gather all nav items
  611. $this->grav['twig']->plugins_hooked_nav = [];
  612. $this->grav->fireEvent('onAdminMenu');
  613. uasort($this->grav['twig']->plugins_hooked_nav, function ($a, $b) {
  614. $ac = $a['priority'] ?? 0;
  615. $bc = $b['priority'] ?? 0;
  616. return $bc <=> $ac;
  617. });
  618. // Gather Plugin-hooked dashboard items
  619. $this->grav->fireEvent('onAdminDashboard');
  620. switch ($this->template) {
  621. case 'dashboard':
  622. $twig->twig_vars['popularity'] = $this->popularity;
  623. break;
  624. }
  625. $flashData = $this->grav['session']->getFlashCookieObject(Admin::TMP_COOKIE_NAME);
  626. if (isset($flashData->message)) {
  627. $this->grav['messages']->add($flashData->message, $flashData->status);
  628. }
  629. }
  630. /**
  631. * Add images to twig template paths to allow inclusion of SVG files
  632. *
  633. * @return void
  634. */
  635. public function onTwigLoader()
  636. {
  637. /** @var Twig $twig */
  638. $twig = $this->grav['twig'];
  639. /** @var UniformResourceLocator $locator */
  640. $locator = Grav::instance()['locator'];
  641. $theme_paths = $locator->findResources('plugins://admin/themes/' . $this->theme . '/images');
  642. foreach($theme_paths as $images_path) {
  643. $twig->addPath($images_path, 'admin-images');
  644. }
  645. }
  646. /**
  647. * Add the Admin Twig Extensions
  648. *
  649. * @return void
  650. */
  651. public function onTwigExtensions()
  652. {
  653. /** @var Twig $twig */
  654. $twig = $this->grav['twig'];
  655. $twig->twig->addExtension(new AdminTwigExtension);
  656. }
  657. /**
  658. * @param Event $event
  659. * @return void
  660. */
  661. public function onAdminAfterSave(Event $event)
  662. {
  663. // Special case to redirect after changing the admin route to avoid 'breaking'
  664. $obj = $event['object'];
  665. if (null !== $obj && method_exists($obj, 'blueprints')) {
  666. $blueprint = $obj->blueprints()->getFilename();
  667. if ($blueprint === 'admin/blueprints' && isset($obj->route) && $this->admin_route !== $obj->route) {
  668. $redirect = preg_replace('/^' . str_replace('/','\/',$this->admin_route) . '/',$obj->route,$this->uri->path());
  669. $this->grav->redirect($redirect);
  670. }
  671. }
  672. }
  673. /**
  674. * Convert some types where we want to process out of the standard config path
  675. *
  676. * @param Event $e
  677. * @return void
  678. */
  679. public function onAdminData(Event $e)
  680. {
  681. $type = $e['type'] ?? null;
  682. switch ($type) {
  683. case 'config':
  684. $e['type'] = $this->admin->authorize(['admin.configuration.system', 'admin.super']) ? 'config/system' : 'config/site';
  685. break;
  686. case 'tools/scheduler':
  687. $e['type'] = 'config/scheduler';
  688. break;
  689. case 'tools':
  690. case 'tools/backups':
  691. $e['type'] = 'config/backups';
  692. break;
  693. }
  694. }
  695. /**
  696. * @return void
  697. */
  698. public function onOutputGenerated()
  699. {
  700. // Clear flash objects for previously uploaded files whenever the user switches page or reloads
  701. // ignoring any JSON / extension call
  702. if ($this->admin->task !== 'save' && empty($this->uri->extension())) {
  703. // Discard any previously uploaded files session and remove all uploaded files.
  704. if ($flash = $this->session->getFlashObject('files-upload')) {
  705. $flash = new \RecursiveIteratorIterator(new \RecursiveArrayIterator($flash));
  706. foreach ($flash as $key => $value) {
  707. if ($key !== 'tmp_name') {
  708. continue;
  709. }
  710. @unlink($value);
  711. }
  712. }
  713. }
  714. }
  715. /**
  716. * Initial stab at registering permissions (WIP)
  717. *
  718. * @param PermissionsRegisterEvent $event
  719. * @return void
  720. */
  721. public function onRegisterPermissions(PermissionsRegisterEvent $event): void
  722. {
  723. $actions = PermissionsReader::fromYaml("plugin://{$this->name}/permissions.yaml");
  724. $permissions = $event->permissions;
  725. $permissions->addActions($actions);
  726. }
  727. /**
  728. * @return void
  729. */
  730. public function onAdminMenu()
  731. {
  732. /** @var Twig $twig */
  733. $twig = $this->grav['twig'];
  734. // Dashboard
  735. $twig->plugins_hooked_nav['PLUGIN_ADMIN.DASHBOARD'] = [
  736. 'route' => 'dashboard',
  737. 'icon' => 'fa-th',
  738. 'authorize' => ['admin.login', 'admin.super'],
  739. 'priority' => 10
  740. ];
  741. // Configuration
  742. $twig->plugins_hooked_nav['PLUGIN_ADMIN.CONFIGURATION'] = [
  743. 'route' => 'config',
  744. 'icon' => 'fa-wrench',
  745. 'authorize' => [
  746. 'admin.configuration.system',
  747. 'admin.configuration.site',
  748. 'admin.configuration.media',
  749. 'admin.configuration.security',
  750. 'admin.configuration.info',
  751. 'admin.super'],
  752. 'priority' => 9
  753. ];
  754. // Pages
  755. $count = new Container(['count' => function () { return $this->admin->pagesCount(); }]);
  756. $twig->plugins_hooked_nav['PLUGIN_ADMIN.PAGES'] = [
  757. 'route' => 'pages',
  758. 'icon' => 'fa-file-text-o',
  759. 'authorize' => ['admin.pages', 'admin.super'],
  760. 'badge' => $count,
  761. 'priority' => 5
  762. ];
  763. // Plugins
  764. $count = new Container(['updates' => 0, 'count' => function () { return count($this->admin->plugins()); }]);
  765. $twig->plugins_hooked_nav['PLUGIN_ADMIN.PLUGINS'] = [
  766. 'route' => 'plugins',
  767. 'icon' => 'fa-plug',
  768. 'authorize' => ['admin.plugins', 'admin.super'],
  769. 'badge' => $count,
  770. 'priority' => -8
  771. ];
  772. // Themes
  773. $count = new Container(['updates' => 0, 'count' => function () { return count($this->admin->themes()); }]);
  774. $twig->plugins_hooked_nav['PLUGIN_ADMIN.THEMES'] = [
  775. 'route' => 'themes',
  776. 'icon' => 'fa-tint',
  777. 'authorize' => ['admin.themes', 'admin.super'],
  778. 'badge' => $count,
  779. 'priority' => -9
  780. ];
  781. // Tools
  782. $twig->plugins_hooked_nav['PLUGIN_ADMIN.TOOLS'] = [
  783. 'route' => 'tools',
  784. 'icon' => 'fa-briefcase',
  785. 'authorize' => $this->admin::toolsPermissions(),
  786. 'priority' => -10
  787. ];
  788. }
  789. /**
  790. * Check if the current route is under the admin path
  791. *
  792. * @return bool
  793. */
  794. public function isAdminPath()
  795. {
  796. $route = $this->uri->route();
  797. return $route === $this->base || 0 === strpos($route, $this->base . '/');
  798. }
  799. /**
  800. * Helper function to replace Pages::Types() and to provide an event to manipulate the data
  801. *
  802. * Dispatches 'onAdminPageTypes' event with 'types' data member which is a reference to the data
  803. *
  804. * @param bool $keysOnly
  805. * @return array
  806. */
  807. public static function pagesTypes(bool $keysOnly = false)
  808. {
  809. $types = Pages::types();
  810. // First filter by configuration
  811. $hideTypes = (array)Grav::instance()['config']->get('plugins.admin.hide_page_types');
  812. foreach ($hideTypes as $hide) {
  813. if (isset($types[$hide])) {
  814. unset($types[$hide]);
  815. } else {
  816. foreach ($types as $key => $type) {
  817. $match = preg_match('#' . $hide . '#i', $key);
  818. if ($match) {
  819. unset($types[$key]);
  820. }
  821. }
  822. }
  823. }
  824. // Allow manipulating of the data by event
  825. $e = new Event(['types' => &$types]);
  826. Grav::instance()->fireEvent('onAdminPageTypes', $e);
  827. return $keysOnly ? array_keys($types) : $types;
  828. }
  829. /**
  830. * Helper function to replace Pages::modularTypes() and to provide an event to manipulate the data
  831. *
  832. * Dispatches 'onAdminModularPageTypes' event with 'types' data member which is a reference to the data
  833. *
  834. * @param bool $keysOnly
  835. * @return array
  836. */
  837. public static function pagesModularTypes(bool $keysOnly = false)
  838. {
  839. $types = Pages::modularTypes();
  840. // First filter by configuration
  841. $hideTypes = (array)Grav::instance()['config']->get('plugins.admin.hide_modular_page_types');
  842. foreach ($hideTypes as $hide) {
  843. if (isset($types[$hide])) {
  844. unset($types[$hide]);
  845. } else {
  846. foreach ($types as $key => $type) {
  847. $match = preg_match('#' . $hide . '#i', $key);
  848. if ($match) {
  849. unset($types[$key]);
  850. }
  851. }
  852. }
  853. }
  854. // Allow manipulating of the data by event
  855. $e = new Event(['types' => &$types]);
  856. Grav::instance()->fireEvent('onAdminModularPageTypes', $e);
  857. return $keysOnly ? array_keys($types) : $types;
  858. }
  859. /**
  860. * Validate a value. Currently validates
  861. *
  862. * - 'user' for username format and username availability.
  863. * - 'password1' for password format
  864. * - 'password2' for equality to password1
  865. *
  866. * @param string $type The field type
  867. * @param string $value The field value
  868. * @param string $extra Any extra value required
  869. *
  870. * @return bool
  871. */
  872. protected function validate($type, $value, $extra = '')
  873. {
  874. /** @var Login $login */
  875. $login = $this->grav['login'];
  876. return $login->validateField($type, $value, $extra);
  877. }
  878. /**
  879. * @param string $task
  880. * @param array|null $post
  881. * @return void
  882. */
  883. protected function initializeController($task, $post = null): void
  884. {
  885. Admin::DEBUG && Admin::addDebugMessage('Admin controller: execute');
  886. $controller = new AdminController();
  887. $controller->initialize($this->grav, $this->template, $task, $this->route, $post);
  888. $controller->execute();
  889. $controller->redirect();
  890. }
  891. /**
  892. * @return void
  893. */
  894. protected function setupAdmin()
  895. {
  896. // Set cache based on admin_cache option.
  897. /** @var Cache $cache */
  898. $cache = $this->grav['cache'];
  899. $cache->setEnabled($this->config->get('plugins.admin.cache_enabled'));
  900. /** @var Pages $pages */
  901. $pages = $this->grav['pages'];
  902. // Disable frontend pages in admin.
  903. $pages->disablePages();
  904. // Force file hash checks to fix caching on moved and deleted pages.
  905. $pages->setCheckMethod('hash');
  906. /** @var Session $session */
  907. $session = $this->grav['session'];
  908. // Make sure that the session has been initialized.
  909. try {
  910. $session->init();
  911. } catch (SessionException $e) {
  912. $session->init();
  913. $message = 'Session corruption detected, restarting session...';
  914. /** @var Debugger $debugger */
  915. $debugger = $this->grav['debugger'];
  916. $debugger->addMessage($message);
  917. $this->grav['messages']->add($message, 'error');
  918. }
  919. $this->active = true;
  920. }
  921. /**
  922. * Initialize the admin.
  923. *
  924. * @return void
  925. * @throws \RuntimeException
  926. */
  927. protected function initializeAdmin()
  928. {
  929. // Check for required plugins
  930. if (!$this->grav['config']->get('plugins.login.enabled') || !$this->grav['config']->get('plugins.form.enabled') || !$this->grav['config']->get('plugins.email.enabled')) {
  931. throw new \RuntimeException('One of the required plugins is missing or not enabled');
  932. }
  933. /** @var Cache $cache */
  934. $cache = $this->grav['cache'];
  935. // Have a unique Admin-only Cache key
  936. $cache_key = $cache->getKey();
  937. $cache->setKey($cache_key . '$');
  938. /** @var Session $session */
  939. $session = $this->grav['session'];
  940. /** @var Language $language */
  941. $language = $this->grav['language'];
  942. /** @var UniformResourceLocator $locator */
  943. $locator = $this->grav['locator'];
  944. // Turn on Twig autoescaping
  945. if ($this->uri->param('task') !== 'processmarkdown') {
  946. $this->grav['twig']->setAutoescape(true);
  947. }
  948. // Initialize Admin Language if needed
  949. if ($language->enabled() && empty($session->admin_lang)) {
  950. $session->admin_lang = $language->getLanguage();
  951. }
  952. // Decide admin template and route.
  953. $path = trim(substr($this->uri->route(), strlen($this->base)), '/');
  954. if (empty($this->template)) {
  955. $this->template = 'dashboard';
  956. }
  957. // Can't access path directly...
  958. if ($path && $path !== 'register') {
  959. $array = explode('/', $path, 2);
  960. $this->template = array_shift($array);
  961. $this->route = array_shift($array);
  962. }
  963. // Initialize admin class (also registers it to Grav services).
  964. $this->admin = new Admin($this->grav, $this->admin_route, $this->template, $this->route);
  965. // Get theme for admin
  966. $this->theme = $this->config->get('plugins.admin.theme', 'grav');
  967. // Replace themes service with admin.
  968. $this->grav['themes'] = function () {
  969. return new Themes($this->grav);
  970. };
  971. // Initialize white label functionality
  972. $this->grav['admin-whitelabel'] = new WhiteLabel();
  973. // Compile a missing preset.css file
  974. $preset_css = 'asset://admin-preset.css';
  975. $preset_path = $this->grav['locator']->findResource($preset_css);
  976. if (!$preset_path) {
  977. $this->grav['admin-whitelabel']->compilePresetScss($this->config->get('plugins.admin.whitelabel'));
  978. }
  979. // These events are needed for login.
  980. $this->enable([
  981. 'onTwigExtensions' => ['onTwigExtensions', 1000],
  982. 'onPagesInitialized' => ['onPagesInitialized', 1000],
  983. 'onTwigLoader' => ['onTwigLoader', 1000],
  984. 'onTwigTemplatePaths' => ['onTwigTemplatePaths', 1000],
  985. 'onTwigSiteVariables' => ['onTwigSiteVariables', 1000],
  986. 'onAssetsInitialized' => ['onAssetsInitialized', 1000],
  987. ]);
  988. // Do not do more if user isn't logged in.
  989. if (!$this->admin->user->authorize('admin.login')) {
  990. return;
  991. }
  992. // These events are not needed during login.
  993. $this->enable([
  994. 'onAdminControllerInit' => ['onAdminControllerInit', 1001],
  995. 'onAdminDashboard' => ['onAdminDashboard', 0],
  996. 'onAdminMenu' => ['onAdminMenu', 1000],
  997. 'onAdminTools' => ['onAdminTools', 0],
  998. 'onAdminSave' => ['onAdminSave', 0],
  999. 'onAdminAfterSave' => ['onAdminAfterSave', 0],
  1000. 'onAdminData' => ['onAdminData', 0],
  1001. 'onOutputGenerated' => ['onOutputGenerated', 0],
  1002. ]);
  1003. // Double check we have system.yaml, site.yaml etc
  1004. $config_path = $locator->findResource('user://config');
  1005. foreach ($this->admin::configurations() as $config_file) {
  1006. if ($config_file === 'info') {
  1007. continue;
  1008. }
  1009. $config_file = "{$config_path}/{$config_file}.yaml";
  1010. if (!file_exists($config_file)) {
  1011. touch($config_file);
  1012. }
  1013. }
  1014. $assets = $this->grav['assets'];
  1015. $translations = 'this.GravAdmin = this.GravAdmin || {}; if (!this.GravAdmin.translations) this.GravAdmin.translations = {}; ' . PHP_EOL . 'this.GravAdmin.translations.PLUGIN_ADMIN = {';
  1016. // Enable language translations
  1017. $translations_actual_state = $this->config->get('system.languages.translations');
  1018. $this->config->set('system.languages.translations', true);
  1019. $strings = [
  1020. 'EVERYTHING_UP_TO_DATE',
  1021. 'UPDATES_ARE_AVAILABLE',
  1022. 'IS_AVAILABLE_FOR_UPDATE',
  1023. 'AND',
  1024. 'IS_NOW_AVAILABLE',
  1025. 'CURRENT',
  1026. 'UPDATE_GRAV_NOW',
  1027. 'TASK_COMPLETED',
  1028. 'UPDATE',
  1029. 'UPDATING_PLEASE_WAIT',
  1030. 'GRAV_SYMBOLICALLY_LINKED',
  1031. 'OF_YOUR',
  1032. 'OF_THIS',
  1033. 'HAVE_AN_UPDATE_AVAILABLE',
  1034. 'UPDATE_AVAILABLE',
  1035. 'UPDATES_AVAILABLE',
  1036. 'FULLY_UPDATED',
  1037. 'DAYS',
  1038. 'PAGE_MODES',
  1039. 'PAGE_TYPES',
  1040. 'ACCESS_LEVELS',
  1041. 'NOTHING_TO_SAVE',
  1042. 'FILE_UNSUPPORTED',
  1043. 'FILE_ERROR_ADD',
  1044. 'FILE_ERROR_UPLOAD',
  1045. 'DROP_FILES_HERE_TO_UPLOAD',
  1046. 'DELETE',
  1047. 'UNSET',
  1048. 'INSERT',
  1049. 'METADATA',
  1050. 'VIEW',
  1051. 'UNDO',
  1052. 'REDO',
  1053. 'HEADERS',
  1054. 'BOLD',
  1055. 'ITALIC',
  1056. 'STRIKETHROUGH',
  1057. 'SUMMARY_DELIMITER',
  1058. 'LINK',
  1059. 'IMAGE',
  1060. 'BLOCKQUOTE',
  1061. 'UNORDERED_LIST',
  1062. 'ORDERED_LIST',
  1063. 'EDITOR',
  1064. 'PREVIEW',
  1065. 'FULLSCREEN',
  1066. 'MODULE',
  1067. 'NON_MODULE',
  1068. 'VISIBLE',
  1069. 'NON_VISIBLE',
  1070. 'ROUTABLE',
  1071. 'NON_ROUTABLE',
  1072. 'PUBLISHED',
  1073. 'NON_PUBLISHED',
  1074. 'PLUGINS',
  1075. 'THEMES',
  1076. 'ALL',
  1077. 'FROM',
  1078. 'TO',
  1079. 'DROPZONE_CANCEL_UPLOAD',
  1080. 'DROPZONE_CANCEL_UPLOAD_CONFIRMATION',
  1081. 'DROPZONE_DEFAULT_MESSAGE',
  1082. 'DROPZONE_FALLBACK_MESSAGE',
  1083. 'DROPZONE_FALLBACK_TEXT',
  1084. 'DROPZONE_FILE_TOO_BIG',
  1085. 'DROPZONE_INVALID_FILE_TYPE',
  1086. 'DROPZONE_MAX_FILES_EXCEEDED',
  1087. 'DROPZONE_REMOVE_FILE',
  1088. 'DROPZONE_RESPONSE_ERROR'
  1089. ];
  1090. foreach ($strings as $string) {
  1091. $separator = (end($strings) === $string) ? '' : ',';
  1092. $translations .= '"' . $string . '": "' . htmlspecialchars($this->admin::translate('PLUGIN_ADMIN.' . $string)) . '"' . $separator;
  1093. }
  1094. $translations .= '};';
  1095. $translations .= 'this.GravAdmin.translations.PLUGIN_FORM = {';
  1096. $strings = ['RESOLUTION_MIN', 'RESOLUTION_MAX'];
  1097. foreach ($strings as $string) {
  1098. $separator = (end($strings) === $string) ? '' : ',';
  1099. $translations .= '"' . $string . '": "' . $this->admin::translate('PLUGIN_FORM.' . $string) . '"' . $separator;
  1100. }
  1101. $translations .= '};';
  1102. $translations .= 'this.GravAdmin.translations.GRAV_CORE = {';
  1103. $strings = [
  1104. 'NICETIME.SECOND',
  1105. 'NICETIME.MINUTE',
  1106. 'NICETIME.HOUR',
  1107. 'NICETIME.DAY',
  1108. 'NICETIME.WEEK',
  1109. 'NICETIME.MONTH',
  1110. 'NICETIME.YEAR',
  1111. 'CRON.EVERY',
  1112. 'CRON.EVERY_HOUR',
  1113. 'CRON.EVERY_MINUTE',
  1114. 'CRON.EVERY_DAY_OF_WEEK',
  1115. 'CRON.EVERY_DAY_OF_MONTH',
  1116. 'CRON.EVERY_MONTH',
  1117. 'CRON.TEXT_PERIOD',
  1118. 'CRON.TEXT_MINS',
  1119. 'CRON.TEXT_TIME',
  1120. 'CRON.TEXT_DOW',
  1121. 'CRON.TEXT_MONTH',
  1122. 'CRON.TEXT_DOM',
  1123. 'CRON.ERROR1',
  1124. 'CRON.ERROR2',
  1125. 'CRON.ERROR3',
  1126. 'CRON.ERROR4',
  1127. 'MONTHS_OF_THE_YEAR',
  1128. 'DAYS_OF_THE_WEEK'
  1129. ];
  1130. foreach ($strings as $string) {
  1131. $separator = (end($strings) === $string) ? '' : ',';
  1132. $translations .= '"' . $string . '": ' . json_encode($this->admin::translate('GRAV.'.$string)) . $separator;
  1133. }
  1134. $translations .= '};';
  1135. // set the actual translations state back
  1136. $this->config->set('system.languages.translations', $translations_actual_state);
  1137. $assets->addInlineJs($translations);
  1138. // Fire even to register permissions from other plugins
  1139. $this->grav->fireEvent('onAdminRegisterPermissions', new Event(['admin' => $this->admin]));
  1140. }
  1141. /**
  1142. * @return array
  1143. */
  1144. public static function themeOptions()
  1145. {
  1146. static $options;
  1147. if (null === $options) {
  1148. $options = [];
  1149. $theme_files = glob(__dir__ . '/themes/grav/css/codemirror/themes/*.css');
  1150. foreach ($theme_files as $theme_file) {
  1151. $theme = Utils::basename(Utils::basename($theme_file, '.css'));
  1152. $options[$theme] = Inflector::titleize($theme);
  1153. }
  1154. }
  1155. return $options;
  1156. }
  1157. /**
  1158. * @return array
  1159. */
  1160. public function getPresets()
  1161. {
  1162. $filename = $this->grav['locator']->findResource('plugin://admin/presets.yaml', false);
  1163. $file = CompiledYamlFile::instance($filename);
  1164. $presets = (array)$file->content();
  1165. $custom_presets = $this->config->get('plugins.admin.whitelabel.custom_presets');
  1166. if (isset($custom_presets)) {
  1167. $custom_presets = Yaml::parse($custom_presets);
  1168. if (is_array($custom_presets)) {
  1169. if (isset($custom_presets['name'], $custom_presets['colors'], $custom_presets['accents'])) {
  1170. $preset = [Inflector::hyphenize($custom_presets['name']) => $custom_presets];
  1171. $presets = $preset + $presets;
  1172. } else {
  1173. foreach ($custom_presets as $value) {
  1174. if (isset($value['name'], $value['colors'], $value['accents'])) {
  1175. $preset = [Inflector::hyphenize($value['name']) => $value];
  1176. $presets = $preset + $presets;
  1177. }
  1178. }
  1179. }
  1180. }
  1181. }
  1182. return $presets;
  1183. }
  1184. }