Files
Grav-Admin-Plugin/classes/plugin/AdminController.php

2803 lines
88 KiB
PHP
Raw Normal View History

2014-08-05 13:06:38 -07:00
<?php
namespace Grav\Plugin\Admin;
2014-08-05 13:06:38 -07:00
2018-09-10 21:41:02 -06:00
use Grav\Common\Backup\Backups;
use Grav\Common\Cache;
2014-10-01 22:28:16 +03:00
use Grav\Common\Config\Config;
use Grav\Common\Debugger;
use Grav\Common\File\CompiledYamlFile;
2014-08-05 13:06:38 -07:00
use Grav\Common\Filesystem\Folder;
use Grav\Common\Flex\Types\Pages\PageIndex;
2017-02-17 16:01:14 -07:00
use Grav\Common\GPM\GPM as GravGPM;
use Grav\Common\GPM\Installer;
use Grav\Common\Grav;
2014-08-05 13:06:38 -07:00
use Grav\Common\Data;
2020-04-30 13:27:10 -06:00
use Grav\Common\Helpers\Excerpts;
use Grav\Common\Language\Language;
use Grav\Common\Page\Interfaces\PageInterface;
2016-07-18 15:42:38 -06:00
use Grav\Common\Page\Media;
use Grav\Common\Page\Medium\ImageMedium;
2018-05-10 10:14:18 +03:00
use Grav\Common\Page\Medium\Medium;
2016-01-21 09:46:38 +02:00
use Grav\Common\Page\Page;
2015-10-21 19:46:51 +02:00
use Grav\Common\Page\Pages;
use Grav\Common\Page\Collection;
2018-09-30 17:44:50 -06:00
use Grav\Common\Security;
use Grav\Common\User\Interfaces\UserCollectionInterface;
use Grav\Common\User\Interfaces\UserInterface;
2015-04-27 13:37:22 +02:00
use Grav\Common\Utils;
2019-06-03 13:17:16 +03:00
use Grav\Framework\Psr7\Response;
2019-08-23 12:54:41 +03:00
use Grav\Framework\RequestHandler\Exception\RequestException;
use Grav\Plugin\Login\TwoFactorAuth\TwoFactorAuth;
2018-08-22 12:45:39 -06:00
use Grav\Common\Yaml;
2019-02-08 17:17:05 -07:00
use PicoFeed\Parser\MalformedXmlException;
2019-08-23 12:54:41 +03:00
use Psr\Http\Message\ResponseInterface;
use RocketTheme\Toolbox\Event\Event;
use RocketTheme\Toolbox\File\File;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use Twig\Loader\FilesystemLoader;
2018-08-22 12:45:39 -06:00
2014-08-05 13:06:38 -07:00
2016-02-12 10:16:28 +01:00
/**
* Class AdminController
*
2016-02-12 10:16:28 +01:00
* @package Grav\Plugin
*/
class AdminController extends AdminBaseController
2014-08-05 13:06:38 -07:00
{
/**
2021-01-08 18:57:41 +02:00
* @param Grav|null $grav
* @param string|null $view
* @param string|null $task
* @param string|null $route
* @param array|null $post
2014-08-05 13:06:38 -07:00
*/
public function initialize(Grav $grav = null, $view = null, $task = null, $route = null, $post = null)
2014-08-05 13:06:38 -07:00
{
$this->grav = $grav;
2014-08-05 13:06:38 -07:00
$this->view = $view;
2018-12-05 08:20:38 +02:00
$this->task = $task ?: 'display';
if (isset($post['data'])) {
$this->data = $this->getPost($post['data']);
unset($post['data']);
} else {
// Backwards compatibility for Form plugin <= 1.2
$this->data = $this->getPost($post);
}
$this->post = $this->getPost($post);
2014-08-05 13:06:38 -07:00
$this->route = $route;
$this->admin = $this->grav['admin'];
$this->grav->fireEvent('onAdminControllerInit', new Event(['controller' => &$this]));
2014-08-05 13:06:38 -07:00
}
// GENERAL TASKS
/**
* Keep alive
2021-01-08 18:57:41 +02:00
*
* @return void
*/
2021-01-08 18:57:41 +02:00
protected function taskKeepAlive(): void
{
$response = new Response(200);
2019-08-23 12:54:41 +03:00
$this->close($response);
}
/**
* Clear the cache.
*
* @return bool True if the action was performed.
*/
protected function taskClearCache()
{
if (!$this->authorizeTask('clear cache', ['admin.cache', 'admin.super', 'admin.maintenance'])) {
return false;
}
// get optional cleartype param
$clear_type = $this->grav['uri']->param('cleartype');
if ($clear_type) {
$clear = $clear_type;
} else {
$clear = 'standard';
}
if ($clear === 'purge') {
$msg = Cache::purgeJob();
$this->admin->json_response = [
'status' => 'success',
'message' => $msg,
];
} else {
$results = Cache::clearCache($clear);
if (count($results) > 0) {
$this->admin->json_response = [
'status' => 'success',
'message' => $this->admin::translate('PLUGIN_ADMIN.CACHE_CLEARED') . ' <br />' . $this->admin::translate('PLUGIN_ADMIN.METHOD') . ': ' . $clear . ''
];
} else {
$this->admin->json_response = [
'status' => 'error',
'message' => $this->admin::translate('PLUGIN_ADMIN.ERROR_CLEARING_CACHE')
];
}
}
return true;
}
/**
* Handles form and saves the input data if its valid.
*
* @return bool True if the action was performed.
*/
public function taskSave()
{
if (!$this->authorizeTask('save', $this->dataPermissions())) {
return false;
}
$this->grav['twig']->twig_vars['current_form_data'] = (array)$this->data;
switch ($this->view) {
case 'pages':
2021-03-19 18:06:24 +02:00
return $this->savePage();
case 'user':
2021-03-19 18:06:24 +02:00
return $this->saveUser();
default:
2021-03-19 18:06:24 +02:00
return $this->saveDefault();
}
}
2021-01-08 18:57:41 +02:00
/**
* @return bool
*/
2021-03-19 18:06:24 +02:00
protected function saveDefault()
{
// Handle standard data types.
$type = $this->getDataType();
$obj = $this->admin->getConfigurationData($type, $this->data);
try {
$obj->validate();
} catch (\Exception $e) {
/** @var Debugger $debugger */
$debugger = $this->grav['debugger'];
$debugger->addException($e);
$this->admin->setMessage($e->getMessage(), 'error');
return false;
}
$obj->filter(false, true);
$obj = $this->storeFiles($obj);
if ($obj) {
// Event to manipulate data before saving the object
$this->grav->fireEvent('onAdminSave', new Event(['object' => &$obj]));
$obj->save();
$this->admin->setMessage($this->admin::translate('PLUGIN_ADMIN.SUCCESSFULLY_SAVED'), 'info');
$this->grav->fireEvent('onAdminAfterSave', new Event(['object' => $obj]));
}
// Force configuration reload.
/** @var Config $config */
$config = $this->grav['config'];
$config->reload();
2019-06-14 12:19:31 +03:00
Cache::clearCache('invalidate');
return true;
}
// USER TASKS
/**
* Handle logout.
*
* @return bool True if the action was performed.
*/
protected function taskLogout()
{
if (!$this->authorizeTask('logout', ['admin.login'])) {
return false;
}
$this->admin->logout($this->data, $this->post);
return true;
}
/**
* @return bool
*/
public function taskRegenerate2FASecret()
{
if (!$this->authorizeTask('regenerate 2FA Secret', ['admin.login'])) {
return false;
}
try {
/** @var UserInterface $user */
2018-05-10 11:51:27 +03:00
$user = $this->grav['user'];
/** @var TwoFactorAuth $twoFa */
$twoFa = $this->grav['login']->twoFactorAuth();
2018-05-10 12:13:04 +03:00
$secret = $twoFa->createSecret();
$image = $twoFa->getQrImageData($user->username, $secret);
$user->set('twofa_secret', $secret);
2018-05-10 11:51:27 +03:00
2019-07-01 20:43:53 +03:00
// TODO: data user can also use save, but please test it before removing this code.
if ($user instanceof \Grav\Common\User\DataUser\User) {
// Save secret into the user file.
$file = $user->file();
if ($file->exists()) {
$content = (array)$file->content();
$content['twofa_secret'] = $secret;
$file->save($content);
$file->free();
}
} else {
$user->save();
}
2018-05-10 11:51:27 +03:00
$this->admin->json_response = ['status' => 'success', 'image' => $image, 'secret' => preg_replace('|(\w{4})|', '\\1 ', $secret)];
} catch (\Exception $e) {
/** @var Debugger $debugger */
$debugger = $this->grav['debugger'];
$debugger->addException($e);
$this->admin->json_response = ['status' => 'error', 'message' => $e->getMessage()];
return false;
}
return true;
}
2015-07-30 12:20:25 +02:00
/**
* Save user account.
*
* Called by more general save task.
*
* @return bool
2015-07-30 12:20:25 +02:00
*/
2021-03-19 18:06:24 +02:00
protected function saveUser()
2014-08-05 13:06:38 -07:00
{
/** @var UserCollectionInterface $users */
$users = $this->grav['accounts'];
$user = $users->load($this->admin->route);
if (!$this->admin->authorize(['admin.super', 'admin.users'])) {
// no user file or not admin.super or admin.users
if ($user->username !== $this->grav['user']->username) {
$this->admin->setMessage($this->admin::translate('PLUGIN_ADMIN.INSUFFICIENT_PERMISSIONS_FOR_TASK') . ' save.','error');
return false;
}
2014-08-05 13:06:38 -07:00
}
/** @var Data\Blueprint $blueprint */
$blueprint = $user->blueprints();
$data = $blueprint->processForm($this->admin->cleanUserPost((array)$this->data));
$data = new Data\Data($data, $blueprint);
try {
$data->validate();
$data->filter();
} catch (\Exception $e) {
/** @var Debugger $debugger */
$debugger = $this->grav['debugger'];
$debugger->addException($e);
$this->admin->setMessage($e->getMessage(), 'error');
return false;
}
$user->update($data->toArray());
2014-08-05 13:06:38 -07:00
$user = $this->storeFiles($user);
if ($user) {
// Event to manipulate data before saving the object
$this->grav->fireEvent('onAdminSave', new Event(['object' => &$user]));
$user->save();
$this->admin->setMessage($this->admin::translate('PLUGIN_ADMIN.SUCCESSFULLY_SAVED'), 'info');
$this->grav->fireEvent('onAdminAfterSave', new Event(['object' => $user]));
}
if ($user->username === $this->grav['user']->username) {
/** @var UserCollectionInterface $users */
$users = $this->grav['accounts'];
//Editing current user. Reload user object
$this->grav['user']->undef('avatar');
$this->grav['user']->merge($users->load($this->admin->route)->toArray());
}
return true;
2014-08-05 13:06:38 -07:00
}
// DASHBOARD TASKS
/**
* Get Notifications
*
2021-01-08 18:57:41 +02:00
* @return void
*/
2021-01-08 18:57:41 +02:00
protected function taskGetNotifications(): void
2016-01-10 17:17:04 +01:00
{
if (!$this->authorizeTask('dashboard', ['admin.login', 'admin.super'])) {
$this->sendJsonResponse(['status' => 'error', 'message' => 'unauthorized']);
}
// do we need to force a reload
$refresh = $this->data['refresh'] === 'true';
$filter = $this->data['filter'] ?? '';
$filter_types = !empty($filter) ? array_map('trim', explode(',', $filter)) : [];
try {
$notifications = $this->admin->getNotifications($refresh);
$notification_data = [];
foreach ($notifications as $type => $type_notifications) {
if ($filter_types && in_array($type, $filter_types, true)) {
$twig_template = 'partials/notification-' . $type . '-block.html.twig';
$notification_data[$type] = $this->grav['twig']->processTemplate($twig_template, ['notifications' => $type_notifications]);
}
}
$json_response = [
'status' => 'success',
'notifications' => $notification_data
];
} catch (\Exception $e) {
/** @var Debugger $debugger */
$debugger = $this->grav['debugger'];
$debugger->addException($e);
$json_response = ['status' => 'error', 'message' => $e->getMessage()];
}
$this->sendJsonResponse($json_response);
}
2014-08-05 13:06:38 -07:00
/**
* Hide notifications.
2014-08-05 13:06:38 -07:00
*
* @return bool True if the action was performed.
*/
protected function taskHideNotification()
2014-08-05 13:06:38 -07:00
{
if (!$this->authorizeTask('hide notification', ['admin.login'])) {
return false;
}
2014-08-05 13:06:38 -07:00
$notification_id = $this->grav['uri']->param('notification_id');
if (!$notification_id) {
$this->admin->json_response = [
'status' => 'error'
];
return false;
}
2014-08-05 13:06:38 -07:00
$filename = $this->grav['locator']->findResource('user://data/notifications/' . $this->grav['user']->username . YAML_EXT, true, true);
$file = CompiledYamlFile::instance($filename);
$data = (array)$file->content();
$date = new \DateTime();
$data[$notification_id] = $date->format('r');
$file->save($data);
$this->admin->json_response = [
'status' => 'success'
];
2014-08-05 13:06:38 -07:00
return true;
}
/**
* Get Newsfeeds
2021-01-08 18:57:41 +02:00
*
* @return void
*/
2021-01-08 18:57:41 +02:00
protected function taskGetNewsFeed(): void
{
if (!$this->authorizeTask('dashboard', ['admin.login', 'admin.super'])) {
$this->sendJsonResponse(['status' => 'error', 'message' => 'unauthorized']);
}
$refresh = $this->data['refresh'] === 'true' ? true : false;
try {
$feed = $this->admin->getFeed($refresh);
$feed_data = $this->grav['twig']->processTemplate('partials/feed-block.html.twig', ['feed' => $feed]);
$json_response = [
'status' => 'success',
'feed_data' => $feed_data
];
} catch (MalformedXmlException $e) {
/** @var Debugger $debugger */
$debugger = $this->grav['debugger'];
$debugger->addException($e);
$json_response = ['status' => 'error', 'message' => $e->getMessage()];
}
$this->sendJsonResponse($json_response);
}
// BACKUP TASKS
/**
2020-12-01 09:37:58 +02:00
* Handle the backup action
*
* @return bool True if the action was performed.
*/
protected function taskBackup()
{
$param_sep = $this->grav['config']->get('system.param_sep', ':');
if (!$this->authorizeTask('backup', ['admin.maintenance', 'admin.super'])) {
return false;
}
$download = $this->grav['uri']->param('download');
try {
if ($download) {
$filename = basename(base64_decode(urldecode($download)));
$file = $this->grav['locator']->findResource("backup://{$filename}", true);
if (!$file || !Utils::endsWith($filename, '.zip', false)) {
header('HTTP/1.1 401 Unauthorized');
exit();
}
Utils::download($file, true);
}
$id = $this->grav['uri']->param('id', 0);
$backup = Backups::backup($id);
} catch (\Exception $e) {
/** @var Debugger $debugger */
$debugger = $this->grav['debugger'];
$debugger->addException($e);
$this->admin->json_response = [
'status' => 'error',
'message' => $this->admin::translate('PLUGIN_ADMIN.AN_ERROR_OCCURRED') . '. ' . $e->getMessage()
];
return true;
}
$download = urlencode(base64_encode($backup));
$url = rtrim($this->grav['uri']->rootUrl(false), '/') . '/' . trim($this->admin->base,
'/') . '/task' . $param_sep . 'backup/download' . $param_sep . $download . '/admin-nonce' . $param_sep . Utils::getNonce('admin-form');
$this->admin->json_response = [
'status' => 'success',
'message' => $this->admin::translate('PLUGIN_ADMIN.YOUR_BACKUP_IS_READY_FOR_DOWNLOAD') . '. <a href="' . $url . '" class="button">' . $this->admin::translate('PLUGIN_ADMIN.DOWNLOAD_BACKUP') . '</a>',
'toastr' => [
'timeOut' => 0,
'extendedTimeOut' => 0,
'closeButton' => true
]
];
return true;
}
/**
* Handle delete backup action
*
* @return bool
*/
protected function taskBackupDelete()
{
if (!$this->authorizeTask('backup', ['admin.maintenance', 'admin.super'])) {
return false;
}
$backup = $this->grav['uri']->param('backup', null);
if (null !== $backup) {
$filename = basename(base64_decode(urldecode($backup)));
$file = $this->grav['locator']->findResource("backup://{$filename}", true);
if ($file && Utils::endsWith($filename, '.zip', false)) {
unlink($file);
$this->admin->json_response = [
'status' => 'success',
'message' => $this->admin::translate('PLUGIN_ADMIN.BACKUP_DELETED'),
'toastr' => [
'closeButton' => true
]
];
return true;
}
}
$this->admin->json_response = [
'status' => 'error',
'message' => $this->admin::translate('PLUGIN_ADMIN.BACKUP_NOT_FOUND'),
];
return true;
}
// PLUGIN / THEME TASKS
/**
* Enable a plugin.
*
* Route: /plugins
*
* @return bool True if the action was performed.
*/
public function taskEnable()
{
if (!$this->authorizeTask('enable plugin', ['admin.plugins', 'admin.super'])) {
return false;
}
if ($this->view !== 'plugins') {
return false;
}
// Filter value and save it.
$this->post = ['enabled' => true];
$obj = $this->prepareData($this->post);
$obj->save();
$this->post = ['_redirect' => 'plugins'];
$this->admin->setMessage($this->admin::translate('PLUGIN_ADMIN.SUCCESSFULLY_ENABLED_PLUGIN'), 'info');
2019-06-14 12:19:31 +03:00
Cache::clearCache('invalidate');
return true;
}
/**
* Disable a plugin.
*
* Route: /plugins
*
* @return bool True if the action was performed.
*/
public function taskDisable()
{
if (!$this->authorizeTask('disable plugin', ['admin.plugins', 'admin.super'])) {
return false;
}
if ($this->view !== 'plugins') {
return false;
}
// Filter value and save it.
$this->post = ['enabled' => false];
$obj = $this->prepareData($this->post);
$obj->save();
$this->post = ['_redirect' => 'plugins'];
$this->admin->setMessage($this->admin::translate('PLUGIN_ADMIN.SUCCESSFULLY_DISABLED_PLUGIN'), 'info');
2019-06-14 12:19:31 +03:00
Cache::clearCache('invalidate');
return true;
}
/**
* Set the default theme.
*
* Route: /themes
*
* @return bool True if the action was performed.
*/
public function taskActivate()
{
if (!$this->authorizeTask('activate theme', ['admin.themes', 'admin.super'])) {
return false;
}
if ($this->view !== 'themes') {
return false;
}
$this->post = ['_redirect' => 'themes'];
// Make sure theme exists (throws exception)
$name = $this->route;
$this->grav['themes']->get($name);
// Store system configuration.
$system = $this->admin->getConfigurationData('config/system');
$system->set('pages.theme', $name);
$system->save();
// Force configuration reload and save.
/** @var Config $config */
$config = $this->grav['config'];
$config->reload()->save();
$config->set('system.pages.theme', $name);
2016-07-07 18:55:52 +02:00
2018-12-05 08:20:38 +02:00
$this->admin->setMessage($this->admin::translate('PLUGIN_ADMIN.SUCCESSFULLY_CHANGED_THEME'), 'info');
2019-06-14 12:19:31 +03:00
Cache::clearCache('invalidate');
return true;
}
// INSTALL & UPGRADE
/**
* Handles updating Grav
*
* @return bool False if user has no permissions.
*/
public function taskUpdategrav()
{
if (!$this->authorizeTask('install grav', ['admin.super'])) {
return false;
}
$gpm = Gpm::GPM();
$version = $gpm->grav->getVersion();
$result = Gpm::selfupgrade();
if ($result) {
$json_response = [
'status' => 'success',
'type' => 'updategrav',
'version' => $version,
2018-12-05 08:20:38 +02:00
'message' => $this->admin::translate('PLUGIN_ADMIN.GRAV_WAS_SUCCESSFULLY_UPDATED_TO') . ' ' . $version
];
} else {
$json_response = [
'status' => 'error',
'type' => 'updategrav',
'version' => GRAV_VERSION,
2018-12-05 08:20:38 +02:00
'message' => $this->admin::translate('PLUGIN_ADMIN.GRAV_UPDATE_FAILED') . ' <br>' . Installer::lastErrorMsg()
];
}
$this->sendJsonResponse($json_response);
return true;
}
/**
* Handles uninstalling plugins and themes
*
* Route: /plugins
* Route: /themes
*
* @deprecated
*
* @return bool True if the action was performed
*/
public function taskUninstall()
{
$type = $this->view === 'plugins' ? 'plugins' : 'themes';
if (!$this->authorizeTask('uninstall ' . $type, ['admin.' . $type, 'admin.super'])) {
return false;
}
$package = $this->route;
$result = Gpm::uninstall($package, []);
if ($result) {
2018-12-05 08:20:38 +02:00
$this->admin->setMessage($this->admin::translate('PLUGIN_ADMIN.UNINSTALL_SUCCESSFUL'), 'info');
} else {
2018-12-05 08:20:38 +02:00
$this->admin->setMessage($this->admin::translate('PLUGIN_ADMIN.UNINSTALL_FAILED'), 'error');
}
$this->post = ['_redirect' => $this->view];
return true;
}
/**
* Toggle the gpm.releases setting
2021-01-08 18:57:41 +02:00
*
* @return bool
*/
protected function taskGpmRelease()
{
2020-02-03 13:52:58 +02:00
if (!$this->authorizeTask('configuration', ['admin.configuration.system', 'admin.super'])) {
return false;
}
// Default release state
$release = 'stable';
$reload = false;
// Get the testing release value if set
if ($this->post['release'] === 'testing') {
$release = 'testing';
}
$config = $this->grav['config'];
$current_release = $config->get('system.gpm.releases');
// If the releases setting is different, save it in the system config
if ($current_release !== $release) {
$data = new Data\Data($config->get('system'));
$data->set('gpm.releases', $release);
// Get the file location
$file = CompiledYamlFile::instance($this->grav['locator']->findResource('config://system.yaml'));
$data->file($file);
// Save the configuration
$data->save();
$config->reload();
$reload = true;
}
$this->admin->json_response = ['status' => 'success', 'reload' => $reload];
return true;
}
/**
* Get update status from GPM
2021-01-08 18:57:41 +02:00
*
* @return bool
*/
protected function taskGetUpdates()
{
if (!$this->authorizeTask('dashboard', ['admin.login', 'admin.super'])) {
return false;
}
$data = $this->post;
$flush = !empty($data['flush']);
if (isset($this->grav['session'])) {
$this->grav['session']->close();
}
try {
$gpm = new GravGPM($flush);
$resources_updates = $gpm->getUpdatable();
foreach ($resources_updates as $key => $update) {
if (!is_iterable($update)) {
continue;
}
foreach ($update as $slug => $item) {
$resources_updates[$key][$slug] = $item;
}
}
if ($gpm->grav !== null) {
$grav_updates = [
'isUpdatable' => $gpm->grav->isUpdatable(),
'assets' => $gpm->grav->getAssets(),
'version' => GRAV_VERSION,
'available' => $gpm->grav->getVersion(),
'date' => $gpm->grav->getDate(),
'isSymlink' => $gpm->grav->isSymlink()
];
$this->admin->json_response = [
'status' => 'success',
'payload' => [
'resources' => $resources_updates,
'grav' => $grav_updates,
'installed' => $gpm->countInstalled(),
'flushed' => $flush
]
];
} else {
$this->admin->json_response = ['status' => 'error', 'message' => 'Cannot connect to the GPM'];
return false;
}
} catch (\Exception $e) {
/** @var Debugger $debugger */
$debugger = $this->grav['debugger'];
$debugger->addException($e);
$this->admin->json_response = ['status' => 'error', 'message' => $e->getMessage()];
return false;
}
return true;
}
/**
* Handle getting a new package dependencies needed to be installed
*
* @return bool
*/
protected function taskGetPackagesDependencies()
{
$data = $this->post;
$packages = isset($data['packages']) ? explode(',', $data['packages']) : '';
$packages = (array)$packages;
try {
$this->admin->checkPackagesCanBeInstalled($packages);
$dependencies = $this->admin->getDependenciesNeededToInstall($packages);
} catch (\Exception $e) {
/** @var Debugger $debugger */
$debugger = $this->grav['debugger'];
$debugger->addException($e);
$this->admin->json_response = ['status' => 'error', 'message' => $e->getMessage()];
return false;
}
$this->admin->json_response = ['status' => 'success', 'dependencies' => $dependencies];
return true;
}
2021-01-08 18:57:41 +02:00
/**
* @return bool
*/
protected function taskInstallDependenciesOfPackages()
{
$data = $this->post;
$packages = isset($data['packages']) ? explode(',', $data['packages']) : '';
$packages = (array)$packages;
$type = $data['type'] ?? '';
if (!$this->authorizeTask('install ' . $type, ['admin.' . $type, 'admin.super'])) {
$this->admin->json_response = [
'status' => 'error',
'message' => $this->admin::translate('PLUGIN_ADMIN.INSUFFICIENT_PERMISSIONS_FOR_TASK')
];
return false;
}
try {
$dependencies = $this->admin->getDependenciesNeededToInstall($packages);
} catch (\Exception $e) {
/** @var Debugger $debugger */
$debugger = $this->grav['debugger'];
$debugger->addException($e);
$this->admin->json_response = ['status' => 'error', 'message' => $e->getMessage()];
return false;
}
$result = Gpm::install(array_keys($dependencies), ['theme' => $type === 'theme']);
if ($result) {
$this->admin->json_response = ['status' => 'success', 'message' => 'Dependencies installed successfully'];
} else {
$this->admin->json_response = [
'status' => 'error',
'message' => $this->admin::translate('PLUGIN_ADMIN.INSTALLATION_FAILED')
];
}
2018-09-30 15:26:19 -06:00
return true;
}
2018-09-29 21:18:45 -06:00
2021-01-08 18:57:41 +02:00
/**
* @param bool $reinstall
* @return bool
*/
protected function taskInstallPackage($reinstall = false)
{
$data = $this->post;
$package = $data['package'] ?? '';
$type = $data['type'] ?? '';
if (!$this->authorizeTask('install ' . $type, ['admin.' . $type, 'admin.super'])) {
$this->admin->json_response = [
'status' => 'error',
'message' => $this->admin::translate('PLUGIN_ADMIN.INSUFFICIENT_PERMISSIONS_FOR_TASK')
];
return false;
}
2017-04-14 11:24:48 -06:00
try {
$result = Gpm::install($package, ['theme' => $type === 'theme']);
} catch (\Exception $e) {
/** @var Debugger $debugger */
$debugger = $this->grav['debugger'];
$debugger->addException($e);
2020-09-03 22:28:47 -06:00
$msg = $e->getMessage();
$msg = Utils::contains($msg, '401 Unauthorized') ? "ERROR: License key for this resource is invalid." : $msg;
2020-09-03 22:28:47 -06:00
$msg = Utils::contains($msg, '404 Not Found') ? "ERROR: Resource not found" : $msg;
$this->admin->json_response = ['status' => 'error', 'message' => $msg];
return false;
}
if ($result) {
$this->admin->json_response = [
'status' => 'success',
'message' => $this->admin::translate(is_string($result) ? $result : sprintf($this->admin::translate($reinstall ?: 'PLUGIN_ADMIN.PACKAGE_X_REINSTALLED_SUCCESSFULLY',
null), $package))
];
} else {
$this->admin->json_response = [
'status' => 'error',
'message' => $this->admin::translate($reinstall ?: 'PLUGIN_ADMIN.INSTALLATION_FAILED')
];
}
2016-07-07 18:55:52 +02:00
return true;
}
2017-03-21 15:35:33 -06:00
/**
* Handle removing a package
2021-01-08 18:57:41 +02:00
*
* @return void
*/
2021-01-08 18:57:41 +02:00
protected function taskRemovePackage(): void
{
$data = $this->post;
$package = $data['package'] ?? '';
$type = $data['type'] ?? '';
2020-02-04 11:22:27 +02:00
$result = false;
if (!$this->authorizeTask('uninstall ' . $type, ['admin.' . $type, 'admin.super'])) {
$json_response = [
'status' => 'error',
'message' => $this->admin::translate('PLUGIN_ADMIN.INSUFFICIENT_PERMISSIONS_FOR_TASK')
];
$this->sendJsonResponse($json_response, 403);
}
2016-07-07 18:55:52 +02:00
//check if there are packages that have this as a dependency. Abort and show which ones
$dependent_packages = $this->admin->getPackagesThatDependOnPackage($package);
if (count($dependent_packages) > 0) {
if (count($dependent_packages) > 1) {
$message = 'The installed packages <cyan>' . implode('</cyan>, <cyan>',
$dependent_packages) . '</cyan> depends on this package. Please remove those first.';
} else {
$message = 'The installed package <cyan>' . implode('</cyan>, <cyan>',
$dependent_packages) . '</cyan> depends on this package. Please remove it first.';
}
$json_response = ['status' => 'error', 'message' => $message];
$this->sendJsonResponse($json_response, 200);
}
2020-02-04 11:22:27 +02:00
$dependencies = false;
try {
$dependencies = $this->admin->dependenciesThatCanBeRemovedWhenRemoving($package);
$result = Gpm::uninstall($package, []);
} catch (\Exception $e) {
/** @var Debugger $debugger */
$debugger = $this->grav['debugger'];
$debugger->addException($e);
$json_response = ['status' => 'error', 'message' => $e->getMessage()];
$this->sendJsonResponse($json_response, 200);
}
if ($result) {
$json_response = [
'status' => 'success',
'dependencies' => $dependencies,
'message' => $this->admin::translate(is_string($result) ? $result : 'PLUGIN_ADMIN.UNINSTALL_SUCCESSFUL')
];
$this->sendJsonResponse($json_response, 200);
}
$json_response = [
'status' => 'error',
'message' => $this->admin::translate('PLUGIN_ADMIN.UNINSTALL_FAILED')
];
$this->sendJsonResponse($json_response, 200);
}
/**
* Handle reinstalling a package
2021-01-08 18:57:41 +02:00
*
* @return void
*/
protected function taskReinstallPackage()
{
$data = $this->post;
$slug = $data['slug'] ?? '';
$type = $data['type'] ?? '';
$package_name = $data['package_name'] ?? '';
$current_version = $data['current_version'] ?? '';
if (!$this->authorizeTask('install ' . $type, ['admin.' . $type, 'admin.super'])) {
$json_response = [
'status' => 'error',
'message' => $this->admin::translate('PLUGIN_ADMIN.INSUFFICIENT_PERMISSIONS_FOR_TASK')
];
$this->sendJsonResponse($json_response, 403);
}
$url = "https://getgrav.org/download/{$type}s/$slug/$current_version";
$result = Gpm::directInstall($url);
if ($result === true) {
$this->admin->json_response = [
'status' => 'success',
'message' => $this->admin::translate(sprintf($this->admin::translate('PLUGIN_ADMIN.PACKAGE_X_REINSTALLED_SUCCESSFULLY',
null), $package_name))
];
} else {
$this->admin->json_response = [
'status' => 'error',
'message' => $this->admin::translate('PLUGIN_ADMIN.REINSTALLATION_FAILED')
];
}
}
2015-07-30 12:20:25 +02:00
/**
* Handle direct install.
2021-01-08 18:57:41 +02:00
*
* @return bool
2015-07-30 12:20:25 +02:00
*/
protected function taskDirectInstall()
2015-04-27 13:37:22 +02:00
{
if (!$this->authorizeTask('install', ['admin.super'])) {
return false;
}
$file_path = $this->data['file_path'] ?? null;
if (isset($_FILES['uploaded_file'])) {
// Check $_FILES['file']['error'] value.
switch ($_FILES['uploaded_file']['error']) {
case UPLOAD_ERR_OK:
break;
case UPLOAD_ERR_NO_FILE:
$this->admin->setMessage($this->admin::translate('PLUGIN_ADMIN.NO_FILES_SENT'), 'error');
return false;
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
$this->admin->setMessage($this->admin::translate('PLUGIN_ADMIN.EXCEEDED_FILESIZE_LIMIT'), 'error');
return false;
case UPLOAD_ERR_NO_TMP_DIR:
$this->admin->setMessage($this->admin::translate('PLUGIN_ADMIN.UPLOAD_ERR_NO_TMP_DIR'), 'error');
return false;
default:
$this->admin->setMessage($this->admin::translate('PLUGIN_ADMIN.UNKNOWN_ERRORS'), 'error');
return false;
}
$file_name = $_FILES['uploaded_file']['name'];
$file_path = $_FILES['uploaded_file']['tmp_name'];
2014-09-22 15:49:53 -06:00
// Handle bad filenames.
if (!Utils::checkFilename($file_name)) {
$this->admin->json_response = [
'status' => 'error',
'message' => $this->admin::translate('PLUGIN_ADMIN.UNKNOWN_ERRORS')
];
2016-03-05 12:11:13 +01:00
return false;
}
2014-09-22 17:13:19 -06:00
}
$result = Gpm::directInstall($file_path);
if ($result === true) {
$this->admin->setMessage($this->admin::translate('PLUGIN_ADMIN.INSTALLATION_SUCCESSFUL'), 'info');
} else {
$this->admin->setMessage($this->admin::translate('PLUGIN_ADMIN.INSTALLATION_FAILED') . ': ' . $result,
'error');
}
$this->setRedirect('/tools');
2014-09-22 15:49:53 -06:00
return true;
}
// PAGE TASKS
/**
* Handles creating an empty page folder (without markdown file)
*
* @return bool True if the action was performed.
*/
public function taskSaveNewFolder()
{
if (!$this->authorizeTask('save', $this->dataPermissions())) {
2016-01-21 09:46:38 +02:00
return false;
2014-10-07 15:32:07 -06:00
}
2014-09-22 16:35:11 -06:00
$data = (array)$this->data;
2016-03-05 12:11:13 +01:00
$folder = $data['folder'] ?? '';
if ($folder === '' || mb_strpos($folder, '/') !== false) {
throw new \RuntimeException('Creating folder failed: bad folder name', 400);
}
if ($data['route'] === '' || $data['route'] === '/') {
$path = $this->grav['locator']->findResource('page://');
} else {
2019-08-23 20:45:51 +03:00
$pages = $this->admin::enablePages();
$page = $pages->find($data['route']);
if (!$page) {
return false;
}
$path = $page->path();
}
2016-03-05 12:11:13 +01:00
$orderOfNewFolder = static::getNextOrderInFolder($path);
$new_path = $path . '/' . $orderOfNewFolder . '.' . $folder;
Folder::create($new_path);
Cache::clearCache('invalidate');
2016-03-05 12:11:13 +01:00
$this->grav->fireEvent('onAdminAfterSaveAs', new Event(['path' => $new_path]));
$this->admin->setMessage($this->admin::translate('PLUGIN_ADMIN.SUCCESSFULLY_SAVED'), 'info');
2014-09-22 16:35:11 -06:00
$this->setRedirect($this->admin->getAdminRoute("/{$this->view}")->toString());
2016-03-05 12:11:13 +01:00
return true;
}
2021-01-08 18:57:41 +02:00
/**
* @return bool
*/
2021-03-19 18:06:24 +02:00
protected function savePage()
{
$reorder = true;
$data = (array)$this->data;
$this->grav['twig']->twig_vars['current_form_data'] = $data;
2019-08-23 20:45:51 +03:00
$pages = $this->admin::enablePages();
// Find new parent page in order to build the path.
$path = trim($data['route'] ?? dirname($this->admin->route), '/');
if ($path === '.') {
$path = '';
}
/** @var PageInterface $obj */
$obj = $this->admin->page(true);
$folder = $data['folder'] ?? null;
if ($folder === '' || mb_strpos($folder, '/') !== false) {
throw new \RuntimeException('Saving page failed: bad folder name', 400);
}
if (!isset($data['folder']) || !$data['folder']) {
$data['folder'] = $obj->slug();
$this->data['folder'] = $obj->slug();
2019-02-08 17:17:05 -07:00
}
// Check for valid frontmatter
if (isset($data['frontmatter']) && !$this->checkValidFrontmatter($data['frontmatter'])) {
$this->admin->setMessage($this->admin::translate('PLUGIN_ADMIN.INVALID_FRONTMATTER_COULD_NOT_SAVE'),
'error');
return false;
2019-02-08 17:17:05 -07:00
}
// XSS Checks for page content
$xss_whitelist = $this->grav['config']->get('security.xss_whitelist', 'admin.super');
if (!$this->admin->authorize($xss_whitelist)) {
$check_what = ['header' => $data['header'] ?? '', 'frontmatter' => $data['frontmatter'] ?? '', 'content' => $data['content'] ?? ''];
$results = Security::detectXssFromArray($check_what);
if (!empty($results)) {
$this->admin->setMessage('<i class="fa fa-ban"></i> ' . $this->admin::translate('PLUGIN_ADMIN.XSS_ONSAVE_ISSUE'),
'error');
return false;
}
}
if ($path !== '') {
// First try to get page by its path.
$parent = $pages->get(GRAV_ROOT . '/' . $path);
if (!$parent) {
// Fall back using the route.
$route = '/' . preg_replace(PageIndex::PAGE_ROUTE_REGEX, '/', $path);
$parent = $pages->find($route, true);
if (!$parent) {
throw new \RuntimeException('New parent page cannot be resolved!');
}
}
} else {
$parent = $pages->root();
}
$original_order = (int)trim($obj->order(), '.');
2019-02-08 17:17:05 -07:00
try {
// Change parent if needed and initialize move (might be needed also on ordering/folder change).
$obj = $obj->move($parent);
$this->preparePage($obj, false, $obj->language());
$obj->validate();
2017-02-26 19:36:01 +01:00
} catch (\Exception $e) {
/** @var Debugger $debugger */
$debugger = $this->grav['debugger'];
$debugger->addException($e);
$this->admin->setMessage($e->getMessage(), 'error');
return false;
}
$obj->filter();
2014-10-07 15:32:07 -06:00
// rename folder based on visible
if ($original_order === 1000) {
// increment order to force reshuffle
$obj->order($original_order + 1);
}
if (isset($data['order']) && !empty($data['order'])) {
$reorder = explode(',', $data['order']);
}
// add or remove numeric prefix based on ordering value
if (isset($data['ordering'])) {
if ($data['ordering'] && !$obj->order()) {
$obj->order(static::getNextOrderInFolder($obj->parent()->path()));
$reorder = false;
} elseif (!$data['ordering'] && $obj->order()) {
$obj->folder($obj->slug());
}
}
$obj = $this->storeFiles($obj);
if ($obj) {
// Event to manipulate data before saving the object
// DEPRECATED: page
$this->grav->fireEvent('onAdminSave', new Event(['object' => &$obj, 'page' => &$obj]));
2019-06-14 12:19:31 +03:00
$obj->save($reorder);
2019-06-14 12:19:31 +03:00
Cache::clearCache('invalidate');
$this->admin->setMessage($this->admin::translate('PLUGIN_ADMIN.SUCCESSFULLY_SAVED'), 'info');
// DEPRECATED: page
$this->grav->fireEvent('onAdminAfterSave', new Event(['object' => $obj, 'page' => $obj]));
}
if (method_exists($obj, 'unsetRouteSlug')) {
$obj->unsetRouteSlug();
}
$multilang = $this->isMultilang();
if ($multilang && !$obj->language()) {
$obj->language($this->admin->getLanguage());
}
$admin_route = $this->admin->base;
$route = $obj->rawRoute();
$redirect_url = ($multilang ? '/' . $obj->language() : '') . $admin_route . '/' . $this->view . $route;
$this->setRedirect($redirect_url);
return true;
}
2016-03-05 12:11:13 +01:00
/**
* Save page as a new copy.
*
* Route: /pages
*
* @return bool True if the action was performed.
* @throws \RuntimeException
*/
protected function taskCopy()
{
if (!$this->authorizeTask('copy page', ['admin.pages', 'admin.super'])) {
return false;
}
// Only applies to pages.
if ($this->view !== 'pages') {
return false;
}
try {
2019-08-23 20:45:51 +03:00
$pages = $this->admin::enablePages();
// Get the current page.
$original_page = $this->admin->page(true);
// Find new parent page in order to build the path.
$parent = $original_page->parent() ?: $pages->root();
// Make a copy of the current page and fill the updated information into it.
$page = $original_page->copy($parent);
$order = 0;
if ($page->order()) {
$order = $this->getNextOrderInFolder($page->parent()->path());
}
// Make sure the header is loaded in case content was set through raw() (expert mode)
$page->header();
if ($page->order()) {
$page->order($order);
}
$folder = $this->findFirstAvailable('folder', $page);
$slug = $this->findFirstAvailable('slug', $page);
$page->path($page->parent()->path() . DS . $page->order() . $folder);
$page->route($page->parent()->route() . '/' . $slug);
$page->rawRoute($page->parent()->rawRoute() . '/' . $slug);
// Append progressive number to the copied page title
$match = preg_split('/(\d+)(?!.*\d)/', $original_page->title(), 2, PREG_SPLIT_DELIM_CAPTURE);
$header = $page->header();
if (!isset($match[1])) {
$header->title = $match[0] . ' 2';
} else {
$header->title = $match[0] . ((int)$match[1] + 1);
}
$page->header($header);
$page->save(false);
$redirect = $this->view . $page->rawRoute();
$header = $page->header();
if (isset($header->slug)) {
$match = preg_split('/-(\d+)$/', $header->slug, 2, PREG_SPLIT_DELIM_CAPTURE);
$header->slug = $match[0] . '-' . (isset($match[1]) ? (int)$match[1] + 1 : 2);
}
$page->header($header);
$page->save();
2019-06-14 12:19:31 +03:00
Cache::clearCache('invalidate');
// DEPRECATED: page
$this->grav->fireEvent('onAdminAfterSave', new Event(['object' => $page, 'page' => $page]));
// Enqueue message and redirect to new location.
$this->admin->setMessage($this->admin::translate('PLUGIN_ADMIN.SUCCESSFULLY_COPIED'), 'info');
$this->setRedirect($redirect);
} catch (\Exception $e) {
throw new \RuntimeException('Copying page failed on error: ' . $e->getMessage());
}
return true;
}
2015-07-30 12:20:25 +02:00
/**
* Reorder pages.
2015-07-30 12:20:25 +02:00
*
* Route: /pages
*
* @return bool True if the action was performed.
2015-07-30 12:20:25 +02:00
*/
protected function taskReorder()
2014-09-22 17:13:19 -06:00
{
if (!$this->authorizeTask('reorder pages', ['admin.pages', 'admin.super'])) {
return false;
}
// Only applies to pages.
if ($this->view !== 'pages') {
2016-01-21 09:46:38 +02:00
return false;
}
$this->admin->setMessage($this->admin::translate('PLUGIN_ADMIN.REORDERING_WAS_SUCCESSFUL'), 'info');
2014-09-22 17:27:48 -06:00
return true;
}
/**
* Delete page.
*
* Route: /pages
*
* @return bool True if the action was performed.
* @throws \RuntimeException
*/
protected function taskDelete()
{
if (!$this->authorizeTask('delete page', ['admin.pages', 'admin.super'])) {
2014-10-01 22:28:16 +03:00
return false;
2014-09-22 17:27:48 -06:00
}
2014-09-22 17:13:19 -06:00
// Only applies to pages.
if ($this->view !== 'pages') {
return false;
2014-09-22 17:27:48 -06:00
}
2015-07-30 12:20:25 +02:00
try {
$page = $this->admin->page();
2016-03-05 12:11:13 +01:00
if (count($page->translatedLanguages()) > 1) {
$page->file()->delete();
} else {
Folder::delete($page->path());
}
// DEPRECATED: page
$this->grav->fireEvent('onAdminAfterDelete', new Event(['object' => $page, 'page' => $page]));
Cache::clearCache('invalidate');
// Set redirect to pages list.
$redirect = 'pages';
$this->admin->setMessage($this->admin::translate('PLUGIN_ADMIN.SUCCESSFULLY_DELETED'), 'info');
$this->setRedirect($redirect);
} catch (\Exception $e) {
throw new \RuntimeException('Deleting page failed on error: ' . $e->getMessage());
}
return true;
}
/**
* Switch the content language. Optionally redirect to a different page.
*
* Route: /pages
2021-01-08 18:57:41 +02:00
*
* @return bool
*/
protected function taskSwitchlanguage()
{
if (!$this->authorizeTask('switch language', ['admin.pages', 'admin.super'])) {
return false;
2015-08-06 15:16:22 +02:00
}
$data = (array)$this->data;
$language = $data['lang'] ?? $this->grav['uri']->param('lang');
if (isset($data['redirect'])) {
$redirect = '/pages/' . $data['redirect'];
} else {
$redirect = '/pages';
}
2015-04-13 21:37:12 +02:00
if ($language) {
$this->grav['session']->admin_lang = $language ?: 'en';
2015-04-13 21:37:12 +02:00
}
$this->admin->setMessage($this->admin::translate('PLUGIN_ADMIN.SUCCESSFULLY_SWITCHED_LANGUAGE'), 'info');
$this->setRedirect($this->admin->getAdminRoute($redirect)->toString());
2015-04-13 21:37:12 +02:00
return true;
}
/**
* Save the current page in a different language. Automatically switches to that language.
2016-01-15 12:57:09 +01:00
*
* @return bool True if the action was performed.
2016-01-15 12:57:09 +01:00
*/
protected function taskSaveas()
2016-01-15 12:57:09 +01:00
{
if (!$this->authorizeTask('save', $this->dataPermissions())) {
return false;
}
2016-01-15 12:57:09 +01:00
/** @var Language $language */
$language = $this->grav['language'];
2015-04-13 21:37:12 +02:00
$data = (array)$this->data;
$lang = $data['lang'] ?? null;
if ($lang) {
$this->grav['session']->admin_lang = $lang ?: 'en';
}
$uri = $this->grav['uri'];
$obj = $this->admin->page($uri->route());
$this->preparePage($obj, false, $lang);
$file = $obj->file();
if ($file) {
$filename = $this->determineFilenameIncludingLanguage($obj->name(), $lang);
2016-01-15 12:57:09 +01:00
$path = $obj->path() . DS . $filename;
$aFile = File::instance($path);
$aFile->save();
2018-09-11 16:12:46 -06:00
$aPage = new Page();
$aPage->init(new \SplFileInfo($path), $lang . '.md');
$aPage->header($obj->header());
$aPage->rawMarkdown($obj->rawMarkdown());
$aPage->template($obj->template());
$aPage->validate();
$aPage->filter();
2018-09-11 16:12:46 -06:00
// DEPRECATED: page
$this->grav->fireEvent('onAdminSave', new Event(['object' => $aPage, 'page' => &$aPage]));
2019-06-14 12:19:31 +03:00
$aPage->save();
2019-06-14 12:19:31 +03:00
Cache::clearCache('invalidate');
// DEPRECATED: page
$this->grav->fireEvent('onAdminAfterSave', new Event(['object' => $aPage, 'page' => $aPage]));
2018-09-11 16:12:46 -06:00
}
$this->admin->setMessage($this->admin::translate('PLUGIN_ADMIN.SUCCESSFULLY_SWITCHED_LANGUAGE'), 'info');
// TODO: better multilanguage support needed.
$this->setRedirect($language->getLanguageURLPrefix($lang) . $uri->route());
2018-09-11 16:12:46 -06:00
2016-01-15 12:57:09 +01:00
return true;
}
2019-06-02 14:44:06 -06:00
/**
* Continue to the new page.
2019-06-02 14:44:06 -06:00
*
* @return bool True if the action was performed.
2019-06-02 14:44:06 -06:00
*/
public function taskContinue()
2019-06-02 14:44:06 -06:00
{
$data = (array)$this->data;
2019-06-02 14:44:06 -06:00
if ($this->view === 'users') {
$username = strip_tags(strtolower($data['username']));
$this->setRedirect("{$this->view}/{$username}");
return true;
}
2019-06-02 14:44:06 -06:00
if ($this->view === 'groups') {
$this->setRedirect("{$this->view}/{$data['groupname']}");
return true;
}
if ($this->view !== 'pages') {
return false;
}
2019-05-28 19:53:10 +02:00
$route = $data['route'] !== '/' ? $data['route'] : '';
2020-03-03 15:01:17 +02:00
$folder = $data['folder'] ?? null;
$title = $data['title'] ?? null;
// Handle @slugify-{field} value, automatically slugifies the specified field
2020-03-03 15:01:17 +02:00
if (null !== $folder && 0 === strpos($folder, '@slugify-')) {
$folder = \Grav\Plugin\Admin\Utils::slug($data[substr($folder, 9)] ?? '');
}
if (!$folder) {
$folder = \Grav\Plugin\Admin\Utils::slug($title) ?: '';
2019-05-28 19:53:10 +02:00
}
$folder = ltrim($folder, '_');
if ($folder === '' || mb_strpos($folder, '/') !== false) {
throw new \RuntimeException('Creating page failed: bad folder name', 400);
}
if (!empty($data['modular'])) {
$folder = '_' . $folder;
}
2020-03-03 15:01:17 +02:00
$data['folder'] = $folder;
2020-03-03 15:01:17 +02:00
$path = $route . '/' . $folder;
$this->admin->session()->{$path} = $data;
2019-06-02 14:44:06 -06:00
// Store the name and route of a page, to be used pre-filled defaults of the form in the future
$this->admin->session()->lastPageName = $data['name'];
$this->admin->session()->lastPageRoute = $data['route'];
2019-06-13 09:55:39 -06:00
$this->setRedirect("{$this->view}/" . ltrim($path, '/'));
return true;
}
2019-05-28 19:53:10 +02:00
/**
* $data['route'] = $this->grav['uri']->param('route');
* $data['sortby'] = $this->grav['uri']->param('sortby', null);
* $data['filters'] = $this->grav['uri']->param('filters', null);
* $data['page'] $this->grav['uri']->param('page', true);
* $data['base'] = $this->grav['uri']->param('base');
* $initial = (bool) $this->grav['uri']->param('initial');
*
2019-08-23 12:54:41 +03:00
* @return ResponseInterface
* @throws RequestException
*/
2019-08-23 12:54:41 +03:00
protected function taskGetLevelListing(): ResponseInterface
{
2019-08-23 12:54:41 +03:00
$this->checkTaskAuthorization('save', $this->dataPermissions());
2019-05-28 19:53:10 +02:00
2019-08-23 12:54:41 +03:00
$request = $this->getRequest();
$data = $request->getParsedBody();
if (!isset($data['field'])) {
throw new RequestException($request, 'Bad Request', 400);
}
// Base64 decode the route
$data['route'] = isset($data['route']) ? base64_decode($data['route']) : null;
2019-08-23 12:54:41 +03:00
$initial = $data['initial'] ?? null;
if ($initial) {
$data['leaf_route'] = $data['route'];
$data['route'] = null;
$data['level'] = 1;
2019-05-28 19:53:10 +02:00
}
2019-08-23 12:58:56 +03:00
[$status, $message, $response,] = $this->getLevelListing($data);
2019-08-23 12:54:41 +03:00
$json = [
'status' => $status,
'message' => $this->admin::translate($message ?? 'PLUGIN_ADMIN.NO_ROUTE_PROVIDED'),
'data' => array_values($response)
];
2019-08-23 12:54:41 +03:00
return $this->createJsonResponse($json, 200);
2019-05-28 19:53:10 +02:00
}
2021-01-08 18:57:41 +02:00
/**
* @return bool
*/
2017-03-05 17:45:10 -07:00
protected function taskGetChildTypes()
{
if (!$this->authorizeTask('get childtypes', ['admin.pages', 'admin.super'])) {
2018-05-10 10:14:18 +03:00
return false;
2017-03-05 17:45:10 -07:00
}
$data = $this->post;
2018-12-05 08:20:38 +02:00
$rawroute = $data['rawroute'] ?? null;
2017-03-05 17:45:10 -07:00
if ($rawroute) {
2019-08-23 20:45:51 +03:00
$pages = $this->admin::enablePages();
/** @var PageInterface $page */
$page = $pages->find($rawroute);
2017-03-05 17:45:10 -07:00
if ($page) {
$child_type = $page->childType();
if ($child_type !== '') {
2017-03-05 17:45:10 -07:00
$this->admin->json_response = [
'status' => 'success',
'child_type' => $child_type
];
return true;
}
}
}
$this->admin->json_response = [
'status' => 'success',
'child_type' => '',
// 'message' => $this->admin::translate('PLUGIN_ADMIN.NO_CHILD_TYPE')
2017-03-05 17:45:10 -07:00
];
return true;
}
2016-02-12 10:16:28 +01:00
/**
* Handles filtering the page by modular/visible/routable in the pages list.
2021-01-08 18:57:41 +02:00
*
* @return void
*/
protected function taskFilterPages()
{
if (!$this->authorizeTask('filter pages', ['admin.pages', 'admin.super'])) {
return;
}
$data = $this->post;
[WIP] Ajax Files Upload (#748) * Reworked the `file` field. All files get uploaded via Ajax and are stored upon Save This improves the Save task tremendously as now there is no longer the need of waiting for the files to finish uploading. Fully backward compatible, `file` field now includes also a `limit` and `filesize` option in the blueprints. The former determines how many files are allowed to be uploaded when in combination with `multiple: true` (default: 10), the latter determines the file size limit (in MB) allowed for each file (default: 5MB) * Added support for `accept: [‘*’]` to allow any file type * Minor tweaks in the comments and messages * Delete any orphan file when discarding the uploaded files session * Minor optimization * Fixed issue with `_json` elements where nested fields merging would get stored in an unexpected way * Potential fix for wrong order of value in Datetime * Fixed nested fields for files * Fixed tmp streams * Minor cleanup * Update JSON data when removing a file. Implemented task to remove files that haven’t been saved yet, from the flash object session * Ensure temporary files are deleted when removing un-saved files from the flash object session * Fixed wrong reference of HTML file field when clicking on the drop zone area to pick a file * Added JSON template for pages * fix a CSS issue in page order * More CSS fixes * Trigger file field mutation when adding or removing a file * Recompiled JS * Removed twig templates that are no longer needed * Fixed issue with nested header fields in a page, not properly merging data * [internal] Fixed issue with collections not capable of handling both param and dot notations at the same time * Reorganized FileField structure to be more consistent with the other fields * Added support for dynamically created file fields (ie, autoinitialization on new lists items) * Added translationable strings for file uploads errors * Added translasions for all Dropzone available strings * Changed default values
2016-08-29 11:12:09 -07:00
$flags = !empty($data['flags']) ? array_map('strtolower', explode(',', $data['flags'])) : [];
$queries = !empty($data['query']) ? explode(',', $data['query']) : [];
2019-08-23 20:45:51 +03:00
$pages = $this->admin::enablePages();
/** @var Collection $collection */
$collection = $pages->all();
if (count($flags)) {
// Filter by state
$pageStates = [
'modular',
'nonmodular',
'visible',
'nonvisible',
'routable',
'nonroutable',
'published',
'nonpublished'
[WIP] Ajax Files Upload (#748) * Reworked the `file` field. All files get uploaded via Ajax and are stored upon Save This improves the Save task tremendously as now there is no longer the need of waiting for the files to finish uploading. Fully backward compatible, `file` field now includes also a `limit` and `filesize` option in the blueprints. The former determines how many files are allowed to be uploaded when in combination with `multiple: true` (default: 10), the latter determines the file size limit (in MB) allowed for each file (default: 5MB) * Added support for `accept: [‘*’]` to allow any file type * Minor tweaks in the comments and messages * Delete any orphan file when discarding the uploaded files session * Minor optimization * Fixed issue with `_json` elements where nested fields merging would get stored in an unexpected way * Potential fix for wrong order of value in Datetime * Fixed nested fields for files * Fixed tmp streams * Minor cleanup * Update JSON data when removing a file. Implemented task to remove files that haven’t been saved yet, from the flash object session * Ensure temporary files are deleted when removing un-saved files from the flash object session * Fixed wrong reference of HTML file field when clicking on the drop zone area to pick a file * Added JSON template for pages * fix a CSS issue in page order * More CSS fixes * Trigger file field mutation when adding or removing a file * Recompiled JS * Removed twig templates that are no longer needed * Fixed issue with nested header fields in a page, not properly merging data * [internal] Fixed issue with collections not capable of handling both param and dot notations at the same time * Reorganized FileField structure to be more consistent with the other fields * Added support for dynamically created file fields (ie, autoinitialization on new lists items) * Added translationable strings for file uploads errors * Added translasions for all Dropzone available strings * Changed default values
2016-08-29 11:12:09 -07:00
];
if (count(array_intersect($pageStates, $flags)) > 0) {
2018-05-10 10:14:18 +03:00
if (in_array('modular', $flags, true)) {
$collection = $collection->modular();
}
[WIP] Ajax Files Upload (#748) * Reworked the `file` field. All files get uploaded via Ajax and are stored upon Save This improves the Save task tremendously as now there is no longer the need of waiting for the files to finish uploading. Fully backward compatible, `file` field now includes also a `limit` and `filesize` option in the blueprints. The former determines how many files are allowed to be uploaded when in combination with `multiple: true` (default: 10), the latter determines the file size limit (in MB) allowed for each file (default: 5MB) * Added support for `accept: [‘*’]` to allow any file type * Minor tweaks in the comments and messages * Delete any orphan file when discarding the uploaded files session * Minor optimization * Fixed issue with `_json` elements where nested fields merging would get stored in an unexpected way * Potential fix for wrong order of value in Datetime * Fixed nested fields for files * Fixed tmp streams * Minor cleanup * Update JSON data when removing a file. Implemented task to remove files that haven’t been saved yet, from the flash object session * Ensure temporary files are deleted when removing un-saved files from the flash object session * Fixed wrong reference of HTML file field when clicking on the drop zone area to pick a file * Added JSON template for pages * fix a CSS issue in page order * More CSS fixes * Trigger file field mutation when adding or removing a file * Recompiled JS * Removed twig templates that are no longer needed * Fixed issue with nested header fields in a page, not properly merging data * [internal] Fixed issue with collections not capable of handling both param and dot notations at the same time * Reorganized FileField structure to be more consistent with the other fields * Added support for dynamically created file fields (ie, autoinitialization on new lists items) * Added translationable strings for file uploads errors * Added translasions for all Dropzone available strings * Changed default values
2016-08-29 11:12:09 -07:00
2018-05-10 10:14:18 +03:00
if (in_array('nonmodular', $flags, true)) {
$collection = $collection->nonModular();
}
[WIP] Ajax Files Upload (#748) * Reworked the `file` field. All files get uploaded via Ajax and are stored upon Save This improves the Save task tremendously as now there is no longer the need of waiting for the files to finish uploading. Fully backward compatible, `file` field now includes also a `limit` and `filesize` option in the blueprints. The former determines how many files are allowed to be uploaded when in combination with `multiple: true` (default: 10), the latter determines the file size limit (in MB) allowed for each file (default: 5MB) * Added support for `accept: [‘*’]` to allow any file type * Minor tweaks in the comments and messages * Delete any orphan file when discarding the uploaded files session * Minor optimization * Fixed issue with `_json` elements where nested fields merging would get stored in an unexpected way * Potential fix for wrong order of value in Datetime * Fixed nested fields for files * Fixed tmp streams * Minor cleanup * Update JSON data when removing a file. Implemented task to remove files that haven’t been saved yet, from the flash object session * Ensure temporary files are deleted when removing un-saved files from the flash object session * Fixed wrong reference of HTML file field when clicking on the drop zone area to pick a file * Added JSON template for pages * fix a CSS issue in page order * More CSS fixes * Trigger file field mutation when adding or removing a file * Recompiled JS * Removed twig templates that are no longer needed * Fixed issue with nested header fields in a page, not properly merging data * [internal] Fixed issue with collections not capable of handling both param and dot notations at the same time * Reorganized FileField structure to be more consistent with the other fields * Added support for dynamically created file fields (ie, autoinitialization on new lists items) * Added translationable strings for file uploads errors * Added translasions for all Dropzone available strings * Changed default values
2016-08-29 11:12:09 -07:00
2018-05-10 10:14:18 +03:00
if (in_array('visible', $flags, true)) {
$collection = $collection->visible();
}
[WIP] Ajax Files Upload (#748) * Reworked the `file` field. All files get uploaded via Ajax and are stored upon Save This improves the Save task tremendously as now there is no longer the need of waiting for the files to finish uploading. Fully backward compatible, `file` field now includes also a `limit` and `filesize` option in the blueprints. The former determines how many files are allowed to be uploaded when in combination with `multiple: true` (default: 10), the latter determines the file size limit (in MB) allowed for each file (default: 5MB) * Added support for `accept: [‘*’]` to allow any file type * Minor tweaks in the comments and messages * Delete any orphan file when discarding the uploaded files session * Minor optimization * Fixed issue with `_json` elements where nested fields merging would get stored in an unexpected way * Potential fix for wrong order of value in Datetime * Fixed nested fields for files * Fixed tmp streams * Minor cleanup * Update JSON data when removing a file. Implemented task to remove files that haven’t been saved yet, from the flash object session * Ensure temporary files are deleted when removing un-saved files from the flash object session * Fixed wrong reference of HTML file field when clicking on the drop zone area to pick a file * Added JSON template for pages * fix a CSS issue in page order * More CSS fixes * Trigger file field mutation when adding or removing a file * Recompiled JS * Removed twig templates that are no longer needed * Fixed issue with nested header fields in a page, not properly merging data * [internal] Fixed issue with collections not capable of handling both param and dot notations at the same time * Reorganized FileField structure to be more consistent with the other fields * Added support for dynamically created file fields (ie, autoinitialization on new lists items) * Added translationable strings for file uploads errors * Added translasions for all Dropzone available strings * Changed default values
2016-08-29 11:12:09 -07:00
2018-05-10 10:14:18 +03:00
if (in_array('nonvisible', $flags, true)) {
$collection = $collection->nonVisible();
}
[WIP] Ajax Files Upload (#748) * Reworked the `file` field. All files get uploaded via Ajax and are stored upon Save This improves the Save task tremendously as now there is no longer the need of waiting for the files to finish uploading. Fully backward compatible, `file` field now includes also a `limit` and `filesize` option in the blueprints. The former determines how many files are allowed to be uploaded when in combination with `multiple: true` (default: 10), the latter determines the file size limit (in MB) allowed for each file (default: 5MB) * Added support for `accept: [‘*’]` to allow any file type * Minor tweaks in the comments and messages * Delete any orphan file when discarding the uploaded files session * Minor optimization * Fixed issue with `_json` elements where nested fields merging would get stored in an unexpected way * Potential fix for wrong order of value in Datetime * Fixed nested fields for files * Fixed tmp streams * Minor cleanup * Update JSON data when removing a file. Implemented task to remove files that haven’t been saved yet, from the flash object session * Ensure temporary files are deleted when removing un-saved files from the flash object session * Fixed wrong reference of HTML file field when clicking on the drop zone area to pick a file * Added JSON template for pages * fix a CSS issue in page order * More CSS fixes * Trigger file field mutation when adding or removing a file * Recompiled JS * Removed twig templates that are no longer needed * Fixed issue with nested header fields in a page, not properly merging data * [internal] Fixed issue with collections not capable of handling both param and dot notations at the same time * Reorganized FileField structure to be more consistent with the other fields * Added support for dynamically created file fields (ie, autoinitialization on new lists items) * Added translationable strings for file uploads errors * Added translasions for all Dropzone available strings * Changed default values
2016-08-29 11:12:09 -07:00
2018-05-10 10:14:18 +03:00
if (in_array('routable', $flags, true)) {
$collection = $collection->routable();
}
[WIP] Ajax Files Upload (#748) * Reworked the `file` field. All files get uploaded via Ajax and are stored upon Save This improves the Save task tremendously as now there is no longer the need of waiting for the files to finish uploading. Fully backward compatible, `file` field now includes also a `limit` and `filesize` option in the blueprints. The former determines how many files are allowed to be uploaded when in combination with `multiple: true` (default: 10), the latter determines the file size limit (in MB) allowed for each file (default: 5MB) * Added support for `accept: [‘*’]` to allow any file type * Minor tweaks in the comments and messages * Delete any orphan file when discarding the uploaded files session * Minor optimization * Fixed issue with `_json` elements where nested fields merging would get stored in an unexpected way * Potential fix for wrong order of value in Datetime * Fixed nested fields for files * Fixed tmp streams * Minor cleanup * Update JSON data when removing a file. Implemented task to remove files that haven’t been saved yet, from the flash object session * Ensure temporary files are deleted when removing un-saved files from the flash object session * Fixed wrong reference of HTML file field when clicking on the drop zone area to pick a file * Added JSON template for pages * fix a CSS issue in page order * More CSS fixes * Trigger file field mutation when adding or removing a file * Recompiled JS * Removed twig templates that are no longer needed * Fixed issue with nested header fields in a page, not properly merging data * [internal] Fixed issue with collections not capable of handling both param and dot notations at the same time * Reorganized FileField structure to be more consistent with the other fields * Added support for dynamically created file fields (ie, autoinitialization on new lists items) * Added translationable strings for file uploads errors * Added translasions for all Dropzone available strings * Changed default values
2016-08-29 11:12:09 -07:00
2018-05-10 10:14:18 +03:00
if (in_array('nonroutable', $flags, true)) {
$collection = $collection->nonRoutable();
}
[WIP] Ajax Files Upload (#748) * Reworked the `file` field. All files get uploaded via Ajax and are stored upon Save This improves the Save task tremendously as now there is no longer the need of waiting for the files to finish uploading. Fully backward compatible, `file` field now includes also a `limit` and `filesize` option in the blueprints. The former determines how many files are allowed to be uploaded when in combination with `multiple: true` (default: 10), the latter determines the file size limit (in MB) allowed for each file (default: 5MB) * Added support for `accept: [‘*’]` to allow any file type * Minor tweaks in the comments and messages * Delete any orphan file when discarding the uploaded files session * Minor optimization * Fixed issue with `_json` elements where nested fields merging would get stored in an unexpected way * Potential fix for wrong order of value in Datetime * Fixed nested fields for files * Fixed tmp streams * Minor cleanup * Update JSON data when removing a file. Implemented task to remove files that haven’t been saved yet, from the flash object session * Ensure temporary files are deleted when removing un-saved files from the flash object session * Fixed wrong reference of HTML file field when clicking on the drop zone area to pick a file * Added JSON template for pages * fix a CSS issue in page order * More CSS fixes * Trigger file field mutation when adding or removing a file * Recompiled JS * Removed twig templates that are no longer needed * Fixed issue with nested header fields in a page, not properly merging data * [internal] Fixed issue with collections not capable of handling both param and dot notations at the same time * Reorganized FileField structure to be more consistent with the other fields * Added support for dynamically created file fields (ie, autoinitialization on new lists items) * Added translationable strings for file uploads errors * Added translasions for all Dropzone available strings * Changed default values
2016-08-29 11:12:09 -07:00
2018-05-10 10:14:18 +03:00
if (in_array('published', $flags, true)) {
$collection = $collection->published();
}
[WIP] Ajax Files Upload (#748) * Reworked the `file` field. All files get uploaded via Ajax and are stored upon Save This improves the Save task tremendously as now there is no longer the need of waiting for the files to finish uploading. Fully backward compatible, `file` field now includes also a `limit` and `filesize` option in the blueprints. The former determines how many files are allowed to be uploaded when in combination with `multiple: true` (default: 10), the latter determines the file size limit (in MB) allowed for each file (default: 5MB) * Added support for `accept: [‘*’]` to allow any file type * Minor tweaks in the comments and messages * Delete any orphan file when discarding the uploaded files session * Minor optimization * Fixed issue with `_json` elements where nested fields merging would get stored in an unexpected way * Potential fix for wrong order of value in Datetime * Fixed nested fields for files * Fixed tmp streams * Minor cleanup * Update JSON data when removing a file. Implemented task to remove files that haven’t been saved yet, from the flash object session * Ensure temporary files are deleted when removing un-saved files from the flash object session * Fixed wrong reference of HTML file field when clicking on the drop zone area to pick a file * Added JSON template for pages * fix a CSS issue in page order * More CSS fixes * Trigger file field mutation when adding or removing a file * Recompiled JS * Removed twig templates that are no longer needed * Fixed issue with nested header fields in a page, not properly merging data * [internal] Fixed issue with collections not capable of handling both param and dot notations at the same time * Reorganized FileField structure to be more consistent with the other fields * Added support for dynamically created file fields (ie, autoinitialization on new lists items) * Added translationable strings for file uploads errors * Added translasions for all Dropzone available strings * Changed default values
2016-08-29 11:12:09 -07:00
2018-05-10 10:14:18 +03:00
if (in_array('nonpublished', $flags, true)) {
$collection = $collection->nonPublished();
}
[WIP] Ajax Files Upload (#748) * Reworked the `file` field. All files get uploaded via Ajax and are stored upon Save This improves the Save task tremendously as now there is no longer the need of waiting for the files to finish uploading. Fully backward compatible, `file` field now includes also a `limit` and `filesize` option in the blueprints. The former determines how many files are allowed to be uploaded when in combination with `multiple: true` (default: 10), the latter determines the file size limit (in MB) allowed for each file (default: 5MB) * Added support for `accept: [‘*’]` to allow any file type * Minor tweaks in the comments and messages * Delete any orphan file when discarding the uploaded files session * Minor optimization * Fixed issue with `_json` elements where nested fields merging would get stored in an unexpected way * Potential fix for wrong order of value in Datetime * Fixed nested fields for files * Fixed tmp streams * Minor cleanup * Update JSON data when removing a file. Implemented task to remove files that haven’t been saved yet, from the flash object session * Ensure temporary files are deleted when removing un-saved files from the flash object session * Fixed wrong reference of HTML file field when clicking on the drop zone area to pick a file * Added JSON template for pages * fix a CSS issue in page order * More CSS fixes * Trigger file field mutation when adding or removing a file * Recompiled JS * Removed twig templates that are no longer needed * Fixed issue with nested header fields in a page, not properly merging data * [internal] Fixed issue with collections not capable of handling both param and dot notations at the same time * Reorganized FileField structure to be more consistent with the other fields * Added support for dynamically created file fields (ie, autoinitialization on new lists items) * Added translationable strings for file uploads errors * Added translasions for all Dropzone available strings * Changed default values
2016-08-29 11:12:09 -07:00
}
foreach ($pageStates as $pageState) {
2018-05-10 10:14:18 +03:00
if (($pageState = array_search($pageState, $flags, true)) !== false) {
unset($flags[$pageState]);
}
[WIP] Ajax Files Upload (#748) * Reworked the `file` field. All files get uploaded via Ajax and are stored upon Save This improves the Save task tremendously as now there is no longer the need of waiting for the files to finish uploading. Fully backward compatible, `file` field now includes also a `limit` and `filesize` option in the blueprints. The former determines how many files are allowed to be uploaded when in combination with `multiple: true` (default: 10), the latter determines the file size limit (in MB) allowed for each file (default: 5MB) * Added support for `accept: [‘*’]` to allow any file type * Minor tweaks in the comments and messages * Delete any orphan file when discarding the uploaded files session * Minor optimization * Fixed issue with `_json` elements where nested fields merging would get stored in an unexpected way * Potential fix for wrong order of value in Datetime * Fixed nested fields for files * Fixed tmp streams * Minor cleanup * Update JSON data when removing a file. Implemented task to remove files that haven’t been saved yet, from the flash object session * Ensure temporary files are deleted when removing un-saved files from the flash object session * Fixed wrong reference of HTML file field when clicking on the drop zone area to pick a file * Added JSON template for pages * fix a CSS issue in page order * More CSS fixes * Trigger file field mutation when adding or removing a file * Recompiled JS * Removed twig templates that are no longer needed * Fixed issue with nested header fields in a page, not properly merging data * [internal] Fixed issue with collections not capable of handling both param and dot notations at the same time * Reorganized FileField structure to be more consistent with the other fields * Added support for dynamically created file fields (ie, autoinitialization on new lists items) * Added translationable strings for file uploads errors * Added translasions for all Dropzone available strings * Changed default values
2016-08-29 11:12:09 -07:00
}
// Filter by page type
2018-05-10 10:14:18 +03:00
if ($flags) {
$types = [];
[WIP] Ajax Files Upload (#748) * Reworked the `file` field. All files get uploaded via Ajax and are stored upon Save This improves the Save task tremendously as now there is no longer the need of waiting for the files to finish uploading. Fully backward compatible, `file` field now includes also a `limit` and `filesize` option in the blueprints. The former determines how many files are allowed to be uploaded when in combination with `multiple: true` (default: 10), the latter determines the file size limit (in MB) allowed for each file (default: 5MB) * Added support for `accept: [‘*’]` to allow any file type * Minor tweaks in the comments and messages * Delete any orphan file when discarding the uploaded files session * Minor optimization * Fixed issue with `_json` elements where nested fields merging would get stored in an unexpected way * Potential fix for wrong order of value in Datetime * Fixed nested fields for files * Fixed tmp streams * Minor cleanup * Update JSON data when removing a file. Implemented task to remove files that haven’t been saved yet, from the flash object session * Ensure temporary files are deleted when removing un-saved files from the flash object session * Fixed wrong reference of HTML file field when clicking on the drop zone area to pick a file * Added JSON template for pages * fix a CSS issue in page order * More CSS fixes * Trigger file field mutation when adding or removing a file * Recompiled JS * Removed twig templates that are no longer needed * Fixed issue with nested header fields in a page, not properly merging data * [internal] Fixed issue with collections not capable of handling both param and dot notations at the same time * Reorganized FileField structure to be more consistent with the other fields * Added support for dynamically created file fields (ie, autoinitialization on new lists items) * Added translationable strings for file uploads errors * Added translasions for all Dropzone available strings * Changed default values
2016-08-29 11:12:09 -07:00
$pageTypes = array_keys(Pages::pageTypes());
foreach ($pageTypes as $pageType) {
2018-12-05 08:20:38 +02:00
if (($pageKey = array_search($pageType, $flags, true)) !== false) {
$types[] = $pageType;
unset($flags[$pageKey]);
}
}
[WIP] Ajax Files Upload (#748) * Reworked the `file` field. All files get uploaded via Ajax and are stored upon Save This improves the Save task tremendously as now there is no longer the need of waiting for the files to finish uploading. Fully backward compatible, `file` field now includes also a `limit` and `filesize` option in the blueprints. The former determines how many files are allowed to be uploaded when in combination with `multiple: true` (default: 10), the latter determines the file size limit (in MB) allowed for each file (default: 5MB) * Added support for `accept: [‘*’]` to allow any file type * Minor tweaks in the comments and messages * Delete any orphan file when discarding the uploaded files session * Minor optimization * Fixed issue with `_json` elements where nested fields merging would get stored in an unexpected way * Potential fix for wrong order of value in Datetime * Fixed nested fields for files * Fixed tmp streams * Minor cleanup * Update JSON data when removing a file. Implemented task to remove files that haven’t been saved yet, from the flash object session * Ensure temporary files are deleted when removing un-saved files from the flash object session * Fixed wrong reference of HTML file field when clicking on the drop zone area to pick a file * Added JSON template for pages * fix a CSS issue in page order * More CSS fixes * Trigger file field mutation when adding or removing a file * Recompiled JS * Removed twig templates that are no longer needed * Fixed issue with nested header fields in a page, not properly merging data * [internal] Fixed issue with collections not capable of handling both param and dot notations at the same time * Reorganized FileField structure to be more consistent with the other fields * Added support for dynamically created file fields (ie, autoinitialization on new lists items) * Added translationable strings for file uploads errors * Added translasions for all Dropzone available strings * Changed default values
2016-08-29 11:12:09 -07:00
if (count($types)) {
$collection = $collection->ofOneOfTheseTypes($types);
}
}
[WIP] Ajax Files Upload (#748) * Reworked the `file` field. All files get uploaded via Ajax and are stored upon Save This improves the Save task tremendously as now there is no longer the need of waiting for the files to finish uploading. Fully backward compatible, `file` field now includes also a `limit` and `filesize` option in the blueprints. The former determines how many files are allowed to be uploaded when in combination with `multiple: true` (default: 10), the latter determines the file size limit (in MB) allowed for each file (default: 5MB) * Added support for `accept: [‘*’]` to allow any file type * Minor tweaks in the comments and messages * Delete any orphan file when discarding the uploaded files session * Minor optimization * Fixed issue with `_json` elements where nested fields merging would get stored in an unexpected way * Potential fix for wrong order of value in Datetime * Fixed nested fields for files * Fixed tmp streams * Minor cleanup * Update JSON data when removing a file. Implemented task to remove files that haven’t been saved yet, from the flash object session * Ensure temporary files are deleted when removing un-saved files from the flash object session * Fixed wrong reference of HTML file field when clicking on the drop zone area to pick a file * Added JSON template for pages * fix a CSS issue in page order * More CSS fixes * Trigger file field mutation when adding or removing a file * Recompiled JS * Removed twig templates that are no longer needed * Fixed issue with nested header fields in a page, not properly merging data * [internal] Fixed issue with collections not capable of handling both param and dot notations at the same time * Reorganized FileField structure to be more consistent with the other fields * Added support for dynamically created file fields (ie, autoinitialization on new lists items) * Added translationable strings for file uploads errors * Added translasions for all Dropzone available strings * Changed default values
2016-08-29 11:12:09 -07:00
// Filter by page type
2018-05-10 10:14:18 +03:00
if ($flags) {
$accessLevels = $flags;
$collection = $collection->ofOneOfTheseAccessLevels($accessLevels);
}
[WIP] Ajax Files Upload (#748) * Reworked the `file` field. All files get uploaded via Ajax and are stored upon Save This improves the Save task tremendously as now there is no longer the need of waiting for the files to finish uploading. Fully backward compatible, `file` field now includes also a `limit` and `filesize` option in the blueprints. The former determines how many files are allowed to be uploaded when in combination with `multiple: true` (default: 10), the latter determines the file size limit (in MB) allowed for each file (default: 5MB) * Added support for `accept: [‘*’]` to allow any file type * Minor tweaks in the comments and messages * Delete any orphan file when discarding the uploaded files session * Minor optimization * Fixed issue with `_json` elements where nested fields merging would get stored in an unexpected way * Potential fix for wrong order of value in Datetime * Fixed nested fields for files * Fixed tmp streams * Minor cleanup * Update JSON data when removing a file. Implemented task to remove files that haven’t been saved yet, from the flash object session * Ensure temporary files are deleted when removing un-saved files from the flash object session * Fixed wrong reference of HTML file field when clicking on the drop zone area to pick a file * Added JSON template for pages * fix a CSS issue in page order * More CSS fixes * Trigger file field mutation when adding or removing a file * Recompiled JS * Removed twig templates that are no longer needed * Fixed issue with nested header fields in a page, not properly merging data * [internal] Fixed issue with collections not capable of handling both param and dot notations at the same time * Reorganized FileField structure to be more consistent with the other fields * Added support for dynamically created file fields (ie, autoinitialization on new lists items) * Added translationable strings for file uploads errors * Added translasions for all Dropzone available strings * Changed default values
2016-08-29 11:12:09 -07:00
}
if (!empty($queries)) {
foreach ($collection as $page) {
foreach ($queries as $query) {
$query = trim($query);
2018-12-05 08:20:38 +02:00
if (stripos($page->getRawContent(), $query) === false
&& stripos($page->title(), $query) === false
&& stripos($page->folder(), $query) === false
&& stripos($page->slug(), \Grav\Plugin\Admin\Utils::slug($query)) === false
) {
$collection->remove($page);
}
}
[WIP] Ajax Files Upload (#748) * Reworked the `file` field. All files get uploaded via Ajax and are stored upon Save This improves the Save task tremendously as now there is no longer the need of waiting for the files to finish uploading. Fully backward compatible, `file` field now includes also a `limit` and `filesize` option in the blueprints. The former determines how many files are allowed to be uploaded when in combination with `multiple: true` (default: 10), the latter determines the file size limit (in MB) allowed for each file (default: 5MB) * Added support for `accept: [‘*’]` to allow any file type * Minor tweaks in the comments and messages * Delete any orphan file when discarding the uploaded files session * Minor optimization * Fixed issue with `_json` elements where nested fields merging would get stored in an unexpected way * Potential fix for wrong order of value in Datetime * Fixed nested fields for files * Fixed tmp streams * Minor cleanup * Update JSON data when removing a file. Implemented task to remove files that haven’t been saved yet, from the flash object session * Ensure temporary files are deleted when removing un-saved files from the flash object session * Fixed wrong reference of HTML file field when clicking on the drop zone area to pick a file * Added JSON template for pages * fix a CSS issue in page order * More CSS fixes * Trigger file field mutation when adding or removing a file * Recompiled JS * Removed twig templates that are no longer needed * Fixed issue with nested header fields in a page, not properly merging data * [internal] Fixed issue with collections not capable of handling both param and dot notations at the same time * Reorganized FileField structure to be more consistent with the other fields * Added support for dynamically created file fields (ie, autoinitialization on new lists items) * Added translationable strings for file uploads errors * Added translasions for all Dropzone available strings * Changed default values
2016-08-29 11:12:09 -07:00
}
}
$results = [];
foreach ($collection as $path => $page) {
$results[] = $page->route();
}
[WIP] Ajax Files Upload (#748) * Reworked the `file` field. All files get uploaded via Ajax and are stored upon Save This improves the Save task tremendously as now there is no longer the need of waiting for the files to finish uploading. Fully backward compatible, `file` field now includes also a `limit` and `filesize` option in the blueprints. The former determines how many files are allowed to be uploaded when in combination with `multiple: true` (default: 10), the latter determines the file size limit (in MB) allowed for each file (default: 5MB) * Added support for `accept: [‘*’]` to allow any file type * Minor tweaks in the comments and messages * Delete any orphan file when discarding the uploaded files session * Minor optimization * Fixed issue with `_json` elements where nested fields merging would get stored in an unexpected way * Potential fix for wrong order of value in Datetime * Fixed nested fields for files * Fixed tmp streams * Minor cleanup * Update JSON data when removing a file. Implemented task to remove files that haven’t been saved yet, from the flash object session * Ensure temporary files are deleted when removing un-saved files from the flash object session * Fixed wrong reference of HTML file field when clicking on the drop zone area to pick a file * Added JSON template for pages * fix a CSS issue in page order * More CSS fixes * Trigger file field mutation when adding or removing a file * Recompiled JS * Removed twig templates that are no longer needed * Fixed issue with nested header fields in a page, not properly merging data * [internal] Fixed issue with collections not capable of handling both param and dot notations at the same time * Reorganized FileField structure to be more consistent with the other fields * Added support for dynamically created file fields (ie, autoinitialization on new lists items) * Added translationable strings for file uploads errors * Added translasions for all Dropzone available strings * Changed default values
2016-08-29 11:12:09 -07:00
$this->admin->json_response = [
'status' => 'success',
2018-12-05 08:20:38 +02:00
'message' => $this->admin::translate('PLUGIN_ADMIN.PAGES_FILTERED'),
'results' => $results
[WIP] Ajax Files Upload (#748) * Reworked the `file` field. All files get uploaded via Ajax and are stored upon Save This improves the Save task tremendously as now there is no longer the need of waiting for the files to finish uploading. Fully backward compatible, `file` field now includes also a `limit` and `filesize` option in the blueprints. The former determines how many files are allowed to be uploaded when in combination with `multiple: true` (default: 10), the latter determines the file size limit (in MB) allowed for each file (default: 5MB) * Added support for `accept: [‘*’]` to allow any file type * Minor tweaks in the comments and messages * Delete any orphan file when discarding the uploaded files session * Minor optimization * Fixed issue with `_json` elements where nested fields merging would get stored in an unexpected way * Potential fix for wrong order of value in Datetime * Fixed nested fields for files * Fixed tmp streams * Minor cleanup * Update JSON data when removing a file. Implemented task to remove files that haven’t been saved yet, from the flash object session * Ensure temporary files are deleted when removing un-saved files from the flash object session * Fixed wrong reference of HTML file field when clicking on the drop zone area to pick a file * Added JSON template for pages * fix a CSS issue in page order * More CSS fixes * Trigger file field mutation when adding or removing a file * Recompiled JS * Removed twig templates that are no longer needed * Fixed issue with nested header fields in a page, not properly merging data * [internal] Fixed issue with collections not capable of handling both param and dot notations at the same time * Reorganized FileField structure to be more consistent with the other fields * Added support for dynamically created file fields (ie, autoinitialization on new lists items) * Added translationable strings for file uploads errors * Added translasions for all Dropzone available strings * Changed default values
2016-08-29 11:12:09 -07:00
];
$this->admin->collection = $collection;
[WIP] Ajax Files Upload (#748) * Reworked the `file` field. All files get uploaded via Ajax and are stored upon Save This improves the Save task tremendously as now there is no longer the need of waiting for the files to finish uploading. Fully backward compatible, `file` field now includes also a `limit` and `filesize` option in the blueprints. The former determines how many files are allowed to be uploaded when in combination with `multiple: true` (default: 10), the latter determines the file size limit (in MB) allowed for each file (default: 5MB) * Added support for `accept: [‘*’]` to allow any file type * Minor tweaks in the comments and messages * Delete any orphan file when discarding the uploaded files session * Minor optimization * Fixed issue with `_json` elements where nested fields merging would get stored in an unexpected way * Potential fix for wrong order of value in Datetime * Fixed nested fields for files * Fixed tmp streams * Minor cleanup * Update JSON data when removing a file. Implemented task to remove files that haven’t been saved yet, from the flash object session * Ensure temporary files are deleted when removing un-saved files from the flash object session * Fixed wrong reference of HTML file field when clicking on the drop zone area to pick a file * Added JSON template for pages * fix a CSS issue in page order * More CSS fixes * Trigger file field mutation when adding or removing a file * Recompiled JS * Removed twig templates that are no longer needed * Fixed issue with nested header fields in a page, not properly merging data * [internal] Fixed issue with collections not capable of handling both param and dot notations at the same time * Reorganized FileField structure to be more consistent with the other fields * Added support for dynamically created file fields (ie, autoinitialization on new lists items) * Added translationable strings for file uploads errors * Added translasions for all Dropzone available strings * Changed default values
2016-08-29 11:12:09 -07:00
}
/**
* Process the page Markdown
*
* @return bool True if the action was performed.
*/
protected function taskProcessMarkdown()
{
if (!$this->authorizeTask('process markdown', ['admin.pages', 'admin.super'])) {
return false;
}
try {
$page = $this->admin->page(true);
if (!$page) {
$this->admin->json_response = [
'status' => 'error',
'message' => $this->admin::translate('PLUGIN_ADMIN.NO_PAGE_FOUND')
];
return false;
}
$this->preparePage($page, true);
$page->header();
$page->templateFormat('html');
// Add theme template paths to Twig loader
$template_paths = $this->grav['locator']->findResources('theme://templates');
$this->grav['twig']->twig->getLoader()->addLoader(new FilesystemLoader($template_paths));
$html = $page->content();
$this->admin->json_response = ['status' => 'success', 'preview' => $html];
} catch (\Exception $e) {
/** @var Debugger $debugger */
$debugger = $this->grav['debugger'];
$debugger->addException($e);
$this->admin->json_response = ['status' => 'error', 'message' => $e->getMessage()];
return false;
}
return true;
}
// MEDIA TASKS
[WIP] Ajax Files Upload (#748) * Reworked the `file` field. All files get uploaded via Ajax and are stored upon Save This improves the Save task tremendously as now there is no longer the need of waiting for the files to finish uploading. Fully backward compatible, `file` field now includes also a `limit` and `filesize` option in the blueprints. The former determines how many files are allowed to be uploaded when in combination with `multiple: true` (default: 10), the latter determines the file size limit (in MB) allowed for each file (default: 5MB) * Added support for `accept: [‘*’]` to allow any file type * Minor tweaks in the comments and messages * Delete any orphan file when discarding the uploaded files session * Minor optimization * Fixed issue with `_json` elements where nested fields merging would get stored in an unexpected way * Potential fix for wrong order of value in Datetime * Fixed nested fields for files * Fixed tmp streams * Minor cleanup * Update JSON data when removing a file. Implemented task to remove files that haven’t been saved yet, from the flash object session * Ensure temporary files are deleted when removing un-saved files from the flash object session * Fixed wrong reference of HTML file field when clicking on the drop zone area to pick a file * Added JSON template for pages * fix a CSS issue in page order * More CSS fixes * Trigger file field mutation when adding or removing a file * Recompiled JS * Removed twig templates that are no longer needed * Fixed issue with nested header fields in a page, not properly merging data * [internal] Fixed issue with collections not capable of handling both param and dot notations at the same time * Reorganized FileField structure to be more consistent with the other fields * Added support for dynamically created file fields (ie, autoinitialization on new lists items) * Added translationable strings for file uploads errors * Added translasions for all Dropzone available strings * Changed default values
2016-08-29 11:12:09 -07:00
/**
* Determines the file types allowed to be uploaded
[WIP] Ajax Files Upload (#748) * Reworked the `file` field. All files get uploaded via Ajax and are stored upon Save This improves the Save task tremendously as now there is no longer the need of waiting for the files to finish uploading. Fully backward compatible, `file` field now includes also a `limit` and `filesize` option in the blueprints. The former determines how many files are allowed to be uploaded when in combination with `multiple: true` (default: 10), the latter determines the file size limit (in MB) allowed for each file (default: 5MB) * Added support for `accept: [‘*’]` to allow any file type * Minor tweaks in the comments and messages * Delete any orphan file when discarding the uploaded files session * Minor optimization * Fixed issue with `_json` elements where nested fields merging would get stored in an unexpected way * Potential fix for wrong order of value in Datetime * Fixed nested fields for files * Fixed tmp streams * Minor cleanup * Update JSON data when removing a file. Implemented task to remove files that haven’t been saved yet, from the flash object session * Ensure temporary files are deleted when removing un-saved files from the flash object session * Fixed wrong reference of HTML file field when clicking on the drop zone area to pick a file * Added JSON template for pages * fix a CSS issue in page order * More CSS fixes * Trigger file field mutation when adding or removing a file * Recompiled JS * Removed twig templates that are no longer needed * Fixed issue with nested header fields in a page, not properly merging data * [internal] Fixed issue with collections not capable of handling both param and dot notations at the same time * Reorganized FileField structure to be more consistent with the other fields * Added support for dynamically created file fields (ie, autoinitialization on new lists items) * Added translationable strings for file uploads errors * Added translasions for all Dropzone available strings * Changed default values
2016-08-29 11:12:09 -07:00
*
2020-11-30 17:25:53 +02:00
* Used by pagemedia field.
*
[WIP] Ajax Files Upload (#748) * Reworked the `file` field. All files get uploaded via Ajax and are stored upon Save This improves the Save task tremendously as now there is no longer the need of waiting for the files to finish uploading. Fully backward compatible, `file` field now includes also a `limit` and `filesize` option in the blueprints. The former determines how many files are allowed to be uploaded when in combination with `multiple: true` (default: 10), the latter determines the file size limit (in MB) allowed for each file (default: 5MB) * Added support for `accept: [‘*’]` to allow any file type * Minor tweaks in the comments and messages * Delete any orphan file when discarding the uploaded files session * Minor optimization * Fixed issue with `_json` elements where nested fields merging would get stored in an unexpected way * Potential fix for wrong order of value in Datetime * Fixed nested fields for files * Fixed tmp streams * Minor cleanup * Update JSON data when removing a file. Implemented task to remove files that haven’t been saved yet, from the flash object session * Ensure temporary files are deleted when removing un-saved files from the flash object session * Fixed wrong reference of HTML file field when clicking on the drop zone area to pick a file * Added JSON template for pages * fix a CSS issue in page order * More CSS fixes * Trigger file field mutation when adding or removing a file * Recompiled JS * Removed twig templates that are no longer needed * Fixed issue with nested header fields in a page, not properly merging data * [internal] Fixed issue with collections not capable of handling both param and dot notations at the same time * Reorganized FileField structure to be more consistent with the other fields * Added support for dynamically created file fields (ie, autoinitialization on new lists items) * Added translationable strings for file uploads errors * Added translasions for all Dropzone available strings * Changed default values
2016-08-29 11:12:09 -07:00
* @return bool True if the action was performed.
*/
protected function taskListmedia()
[WIP] Ajax Files Upload (#748) * Reworked the `file` field. All files get uploaded via Ajax and are stored upon Save This improves the Save task tremendously as now there is no longer the need of waiting for the files to finish uploading. Fully backward compatible, `file` field now includes also a `limit` and `filesize` option in the blueprints. The former determines how many files are allowed to be uploaded when in combination with `multiple: true` (default: 10), the latter determines the file size limit (in MB) allowed for each file (default: 5MB) * Added support for `accept: [‘*’]` to allow any file type * Minor tweaks in the comments and messages * Delete any orphan file when discarding the uploaded files session * Minor optimization * Fixed issue with `_json` elements where nested fields merging would get stored in an unexpected way * Potential fix for wrong order of value in Datetime * Fixed nested fields for files * Fixed tmp streams * Minor cleanup * Update JSON data when removing a file. Implemented task to remove files that haven’t been saved yet, from the flash object session * Ensure temporary files are deleted when removing un-saved files from the flash object session * Fixed wrong reference of HTML file field when clicking on the drop zone area to pick a file * Added JSON template for pages * fix a CSS issue in page order * More CSS fixes * Trigger file field mutation when adding or removing a file * Recompiled JS * Removed twig templates that are no longer needed * Fixed issue with nested header fields in a page, not properly merging data * [internal] Fixed issue with collections not capable of handling both param and dot notations at the same time * Reorganized FileField structure to be more consistent with the other fields * Added support for dynamically created file fields (ie, autoinitialization on new lists items) * Added translationable strings for file uploads errors * Added translasions for all Dropzone available strings * Changed default values
2016-08-29 11:12:09 -07:00
{
if (!$this->authorizeTask('list media', ['admin.pages', 'admin.super'])) {
[WIP] Ajax Files Upload (#748) * Reworked the `file` field. All files get uploaded via Ajax and are stored upon Save This improves the Save task tremendously as now there is no longer the need of waiting for the files to finish uploading. Fully backward compatible, `file` field now includes also a `limit` and `filesize` option in the blueprints. The former determines how many files are allowed to be uploaded when in combination with `multiple: true` (default: 10), the latter determines the file size limit (in MB) allowed for each file (default: 5MB) * Added support for `accept: [‘*’]` to allow any file type * Minor tweaks in the comments and messages * Delete any orphan file when discarding the uploaded files session * Minor optimization * Fixed issue with `_json` elements where nested fields merging would get stored in an unexpected way * Potential fix for wrong order of value in Datetime * Fixed nested fields for files * Fixed tmp streams * Minor cleanup * Update JSON data when removing a file. Implemented task to remove files that haven’t been saved yet, from the flash object session * Ensure temporary files are deleted when removing un-saved files from the flash object session * Fixed wrong reference of HTML file field when clicking on the drop zone area to pick a file * Added JSON template for pages * fix a CSS issue in page order * More CSS fixes * Trigger file field mutation when adding or removing a file * Recompiled JS * Removed twig templates that are no longer needed * Fixed issue with nested header fields in a page, not properly merging data * [internal] Fixed issue with collections not capable of handling both param and dot notations at the same time * Reorganized FileField structure to be more consistent with the other fields * Added support for dynamically created file fields (ie, autoinitialization on new lists items) * Added translationable strings for file uploads errors * Added translasions for all Dropzone available strings * Changed default values
2016-08-29 11:12:09 -07:00
return false;
}
$media = $this->getMedia();
if (!$media) {
$this->admin->json_response = [
'status' => 'error',
2018-12-05 08:20:38 +02:00
'message' => $this->admin::translate('PLUGIN_ADMIN.NO_PAGE_FOUND')
];
[WIP] Ajax Files Upload (#748) * Reworked the `file` field. All files get uploaded via Ajax and are stored upon Save This improves the Save task tremendously as now there is no longer the need of waiting for the files to finish uploading. Fully backward compatible, `file` field now includes also a `limit` and `filesize` option in the blueprints. The former determines how many files are allowed to be uploaded when in combination with `multiple: true` (default: 10), the latter determines the file size limit (in MB) allowed for each file (default: 5MB) * Added support for `accept: [‘*’]` to allow any file type * Minor tweaks in the comments and messages * Delete any orphan file when discarding the uploaded files session * Minor optimization * Fixed issue with `_json` elements where nested fields merging would get stored in an unexpected way * Potential fix for wrong order of value in Datetime * Fixed nested fields for files * Fixed tmp streams * Minor cleanup * Update JSON data when removing a file. Implemented task to remove files that haven’t been saved yet, from the flash object session * Ensure temporary files are deleted when removing un-saved files from the flash object session * Fixed wrong reference of HTML file field when clicking on the drop zone area to pick a file * Added JSON template for pages * fix a CSS issue in page order * More CSS fixes * Trigger file field mutation when adding or removing a file * Recompiled JS * Removed twig templates that are no longer needed * Fixed issue with nested header fields in a page, not properly merging data * [internal] Fixed issue with collections not capable of handling both param and dot notations at the same time * Reorganized FileField structure to be more consistent with the other fields * Added support for dynamically created file fields (ie, autoinitialization on new lists items) * Added translationable strings for file uploads errors * Added translasions for all Dropzone available strings * Changed default values
2016-08-29 11:12:09 -07:00
return false;
[WIP] Ajax Files Upload (#748) * Reworked the `file` field. All files get uploaded via Ajax and are stored upon Save This improves the Save task tremendously as now there is no longer the need of waiting for the files to finish uploading. Fully backward compatible, `file` field now includes also a `limit` and `filesize` option in the blueprints. The former determines how many files are allowed to be uploaded when in combination with `multiple: true` (default: 10), the latter determines the file size limit (in MB) allowed for each file (default: 5MB) * Added support for `accept: [‘*’]` to allow any file type * Minor tweaks in the comments and messages * Delete any orphan file when discarding the uploaded files session * Minor optimization * Fixed issue with `_json` elements where nested fields merging would get stored in an unexpected way * Potential fix for wrong order of value in Datetime * Fixed nested fields for files * Fixed tmp streams * Minor cleanup * Update JSON data when removing a file. Implemented task to remove files that haven’t been saved yet, from the flash object session * Ensure temporary files are deleted when removing un-saved files from the flash object session * Fixed wrong reference of HTML file field when clicking on the drop zone area to pick a file * Added JSON template for pages * fix a CSS issue in page order * More CSS fixes * Trigger file field mutation when adding or removing a file * Recompiled JS * Removed twig templates that are no longer needed * Fixed issue with nested header fields in a page, not properly merging data * [internal] Fixed issue with collections not capable of handling both param and dot notations at the same time * Reorganized FileField structure to be more consistent with the other fields * Added support for dynamically created file fields (ie, autoinitialization on new lists items) * Added translationable strings for file uploads errors * Added translasions for all Dropzone available strings * Changed default values
2016-08-29 11:12:09 -07:00
}
$media_list = [];
2018-05-10 10:14:18 +03:00
/**
* @var string $name
* @var Medium|ImageMedium $medium
2018-05-10 10:14:18 +03:00
*/
foreach ($media->all() as $name => $medium) {
$metadata = [];
$img_metadata = $medium->metadata();
if ($img_metadata) {
$metadata = $img_metadata;
}
// Get original name
/** @var ImageMedium $source */
$source = method_exists($medium, 'higherQualityAlternative') ? $medium->higherQualityAlternative() : null;
2018-12-05 08:20:38 +02:00
$media_list[$name] = [
'url' => $medium->display($medium->get('extension') === 'svg' ? 'source' : 'thumbnail')->cropZoom(400, 300)->url(),
'size' => $medium->get('size'),
'metadata' => $metadata,
'original' => $source ? $source->get('filename') : null
2018-12-05 08:20:38 +02:00
];
[WIP] Ajax Files Upload (#748) * Reworked the `file` field. All files get uploaded via Ajax and are stored upon Save This improves the Save task tremendously as now there is no longer the need of waiting for the files to finish uploading. Fully backward compatible, `file` field now includes also a `limit` and `filesize` option in the blueprints. The former determines how many files are allowed to be uploaded when in combination with `multiple: true` (default: 10), the latter determines the file size limit (in MB) allowed for each file (default: 5MB) * Added support for `accept: [‘*’]` to allow any file type * Minor tweaks in the comments and messages * Delete any orphan file when discarding the uploaded files session * Minor optimization * Fixed issue with `_json` elements where nested fields merging would get stored in an unexpected way * Potential fix for wrong order of value in Datetime * Fixed nested fields for files * Fixed tmp streams * Minor cleanup * Update JSON data when removing a file. Implemented task to remove files that haven’t been saved yet, from the flash object session * Ensure temporary files are deleted when removing un-saved files from the flash object session * Fixed wrong reference of HTML file field when clicking on the drop zone area to pick a file * Added JSON template for pages * fix a CSS issue in page order * More CSS fixes * Trigger file field mutation when adding or removing a file * Recompiled JS * Removed twig templates that are no longer needed * Fixed issue with nested header fields in a page, not properly merging data * [internal] Fixed issue with collections not capable of handling both param and dot notations at the same time * Reorganized FileField structure to be more consistent with the other fields * Added support for dynamically created file fields (ie, autoinitialization on new lists items) * Added translationable strings for file uploads errors * Added translasions for all Dropzone available strings * Changed default values
2016-08-29 11:12:09 -07:00
}
$this->admin->json_response = ['status' => 'success', 'results' => $media_list];
[WIP] Ajax Files Upload (#748) * Reworked the `file` field. All files get uploaded via Ajax and are stored upon Save This improves the Save task tremendously as now there is no longer the need of waiting for the files to finish uploading. Fully backward compatible, `file` field now includes also a `limit` and `filesize` option in the blueprints. The former determines how many files are allowed to be uploaded when in combination with `multiple: true` (default: 10), the latter determines the file size limit (in MB) allowed for each file (default: 5MB) * Added support for `accept: [‘*’]` to allow any file type * Minor tweaks in the comments and messages * Delete any orphan file when discarding the uploaded files session * Minor optimization * Fixed issue with `_json` elements where nested fields merging would get stored in an unexpected way * Potential fix for wrong order of value in Datetime * Fixed nested fields for files * Fixed tmp streams * Minor cleanup * Update JSON data when removing a file. Implemented task to remove files that haven’t been saved yet, from the flash object session * Ensure temporary files are deleted when removing un-saved files from the flash object session * Fixed wrong reference of HTML file field when clicking on the drop zone area to pick a file * Added JSON template for pages * fix a CSS issue in page order * More CSS fixes * Trigger file field mutation when adding or removing a file * Recompiled JS * Removed twig templates that are no longer needed * Fixed issue with nested header fields in a page, not properly merging data * [internal] Fixed issue with collections not capable of handling both param and dot notations at the same time * Reorganized FileField structure to be more consistent with the other fields * Added support for dynamically created file fields (ie, autoinitialization on new lists items) * Added translationable strings for file uploads errors * Added translasions for all Dropzone available strings * Changed default values
2016-08-29 11:12:09 -07:00
return true;
}
2014-08-05 13:06:38 -07:00
/**
2020-11-30 17:25:53 +02:00
* Handles adding a media file to a page.
*
* Used by pagemedia field.
2014-08-05 13:06:38 -07:00
*
* @return bool True if the action was performed.
*/
protected function taskAddmedia()
2014-08-05 13:06:38 -07:00
{
if (!$this->authorizeTask('add media', ['admin.pages', 'admin.super'])) {
2016-01-21 09:46:38 +02:00
return false;
}
/** @var Config $config */
2016-01-21 09:46:38 +02:00
$config = $this->grav['config'];
if (empty($_FILES)) {
$this->admin->json_response = [
'status' => 'error',
'message' => $this->admin::translate('PLUGIN_ADMIN.EXCEEDED_POSTMAX_LIMIT')
];
return false;
}
if (!isset($_FILES['file']['error']) || is_array($_FILES['file']['error'])) {
$this->admin->json_response = [
'status' => 'error',
2018-12-05 08:20:38 +02:00
'message' => $this->admin::translate('PLUGIN_ADMIN.INVALID_PARAMETERS')
];
return false;
}
// Check $_FILES['file']['error'] value.
switch ($_FILES['file']['error']) {
case UPLOAD_ERR_OK:
break;
case UPLOAD_ERR_NO_FILE:
$this->admin->json_response = [
'status' => 'error',
2018-12-05 08:20:38 +02:00
'message' => $this->admin::translate('PLUGIN_ADMIN.NO_FILES_SENT')
];
2016-03-05 12:11:13 +01:00
return false;
case UPLOAD_ERR_INI_SIZE:
case UPLOAD_ERR_FORM_SIZE:
$this->admin->json_response = [
'status' => 'error',
2018-12-05 08:20:38 +02:00
'message' => $this->admin::translate('PLUGIN_ADMIN.EXCEEDED_FILESIZE_LIMIT')
];
2016-07-07 18:55:52 +02:00
return false;
case UPLOAD_ERR_NO_TMP_DIR:
$this->admin->json_response = [
'status' => 'error',
2018-12-05 08:20:38 +02:00
'message' => $this->admin::translate('PLUGIN_ADMIN.UPLOAD_ERR_NO_TMP_DIR')
];
2016-07-07 18:55:52 +02:00
return false;
default:
$this->admin->json_response = [
'status' => 'error',
2018-12-05 08:20:38 +02:00
'message' => $this->admin::translate('PLUGIN_ADMIN.UNKNOWN_ERRORS')
];
return false;
2015-08-05 18:10:15 -06:00
}
2018-10-04 15:42:59 +03:00
$filename = $_FILES['file']['name'];
// Handle bad filenames.
if (!Utils::checkFilename($filename)) {
$this->admin->json_response = [
'status' => 'error',
2018-12-05 08:20:38 +02:00
'message' => sprintf($this->admin::translate('PLUGIN_ADMIN.FILEUPLOAD_UNABLE_TO_UPLOAD'),
2018-10-04 15:42:59 +03:00
$filename, 'Bad filename')
];
return false;
}
// You should also check filesize here.
$grav_limit = Utils::getUploadLimit();
if ($grav_limit > 0 && $_FILES['file']['size'] > $grav_limit) {
$this->admin->json_response = [
'status' => 'error',
2018-12-05 08:20:38 +02:00
'message' => $this->admin::translate('PLUGIN_ADMIN.EXCEEDED_GRAV_FILESIZE_LIMIT')
];
[WIP] Ajax Files Upload (#748) * Reworked the `file` field. All files get uploaded via Ajax and are stored upon Save This improves the Save task tremendously as now there is no longer the need of waiting for the files to finish uploading. Fully backward compatible, `file` field now includes also a `limit` and `filesize` option in the blueprints. The former determines how many files are allowed to be uploaded when in combination with `multiple: true` (default: 10), the latter determines the file size limit (in MB) allowed for each file (default: 5MB) * Added support for `accept: [‘*’]` to allow any file type * Minor tweaks in the comments and messages * Delete any orphan file when discarding the uploaded files session * Minor optimization * Fixed issue with `_json` elements where nested fields merging would get stored in an unexpected way * Potential fix for wrong order of value in Datetime * Fixed nested fields for files * Fixed tmp streams * Minor cleanup * Update JSON data when removing a file. Implemented task to remove files that haven’t been saved yet, from the flash object session * Ensure temporary files are deleted when removing un-saved files from the flash object session * Fixed wrong reference of HTML file field when clicking on the drop zone area to pick a file * Added JSON template for pages * fix a CSS issue in page order * More CSS fixes * Trigger file field mutation when adding or removing a file * Recompiled JS * Removed twig templates that are no longer needed * Fixed issue with nested header fields in a page, not properly merging data * [internal] Fixed issue with collections not capable of handling both param and dot notations at the same time * Reorganized FileField structure to be more consistent with the other fields * Added support for dynamically created file fields (ie, autoinitialization on new lists items) * Added translationable strings for file uploads errors * Added translasions for all Dropzone available strings * Changed default values
2016-08-29 11:12:09 -07:00
return false;
[WIP] Ajax Files Upload (#748) * Reworked the `file` field. All files get uploaded via Ajax and are stored upon Save This improves the Save task tremendously as now there is no longer the need of waiting for the files to finish uploading. Fully backward compatible, `file` field now includes also a `limit` and `filesize` option in the blueprints. The former determines how many files are allowed to be uploaded when in combination with `multiple: true` (default: 10), the latter determines the file size limit (in MB) allowed for each file (default: 5MB) * Added support for `accept: [‘*’]` to allow any file type * Minor tweaks in the comments and messages * Delete any orphan file when discarding the uploaded files session * Minor optimization * Fixed issue with `_json` elements where nested fields merging would get stored in an unexpected way * Potential fix for wrong order of value in Datetime * Fixed nested fields for files * Fixed tmp streams * Minor cleanup * Update JSON data when removing a file. Implemented task to remove files that haven’t been saved yet, from the flash object session * Ensure temporary files are deleted when removing un-saved files from the flash object session * Fixed wrong reference of HTML file field when clicking on the drop zone area to pick a file * Added JSON template for pages * fix a CSS issue in page order * More CSS fixes * Trigger file field mutation when adding or removing a file * Recompiled JS * Removed twig templates that are no longer needed * Fixed issue with nested header fields in a page, not properly merging data * [internal] Fixed issue with collections not capable of handling both param and dot notations at the same time * Reorganized FileField structure to be more consistent with the other fields * Added support for dynamically created file fields (ie, autoinitialization on new lists items) * Added translationable strings for file uploads errors * Added translasions for all Dropzone available strings * Changed default values
2016-08-29 11:12:09 -07:00
}
2014-08-05 13:06:38 -07:00
// Check extension
2018-10-04 15:42:59 +03:00
$extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
2014-10-07 12:12:21 +03:00
// If not a supported type, return
2018-10-04 15:42:59 +03:00
if (!$extension || !$config->get("media.types.{$extension}")) {
$this->admin->json_response = [
'status' => 'error',
2018-12-05 08:20:38 +02:00
'message' => $this->admin::translate('PLUGIN_ADMIN.UNSUPPORTED_FILE_TYPE') . ': ' . $extension
];
return false;
}
2015-12-29 19:31:02 +01:00
$page = $this->admin->page($this->route);
$media = $page ? $this->getMedia($page) : null;
if (!$media) {
$this->admin->json_response = [
'status' => 'error',
2018-12-05 08:20:38 +02:00
'message' => $this->admin::translate('PLUGIN_ADMIN.NO_PAGE_FOUND')
];
return false;
}
2018-05-22 10:58:35 +03:00
/** @var UniformResourceLocator $locator */
$locator = $this->grav['locator'];
$path = $media->getPath();
2018-05-22 10:58:35 +03:00
if ($locator->isStream($path)) {
$path = $locator->findResource($path, true, true);
}
2019-09-03 12:18:20 -06:00
// Special Sanitization for SVG
if (Utils::contains($extension, 'svg', false)) {
Security::sanitizeSVG($_FILES['file']['tmp_name']);
}
// Upload it
2018-12-05 08:20:38 +02:00
if (!move_uploaded_file($_FILES['file']['tmp_name'], sprintf('%s/%s', $path, $filename))) {
$this->admin->json_response = [
'status' => 'error',
2018-12-05 08:20:38 +02:00
'message' => $this->admin::translate('PLUGIN_ADMIN.FAILED_TO_MOVE_UPLOADED_FILE')
];
2015-12-29 19:31:02 +01:00
return false;
2014-08-05 13:06:38 -07:00
}
2019-06-14 12:19:31 +03:00
Cache::clearCache('invalidate');
// Add metadata if needed
$include_metadata = Grav::instance()['config']->get('system.media.auto_metadata_exif', false);
2018-10-04 15:42:59 +03:00
$basename = str_replace(['@3x', '@2x'], '', pathinfo($filename, PATHINFO_BASENAME));
$metadata = [];
2018-10-04 15:42:59 +03:00
if ($include_metadata && isset($media[$basename])) {
$img_metadata = $media[$basename]->metadata();
if ($img_metadata) {
$metadata = $img_metadata;
}
}
2017-05-12 12:54:08 -06:00
// DEPRECATED: page
$this->grav->fireEvent('onAdminAfterAddMedia', new Event(['object' => $page, 'page' => $page]));
$this->admin->json_response = [
'status' => 'success',
2018-12-05 08:20:38 +02:00
'message' => $this->admin::translate('PLUGIN_ADMIN.FILE_UPLOADED_SUCCESSFULLY'),
'metadata' => $metadata,
];
2014-08-05 13:06:38 -07:00
return true;
}
2021-01-08 18:57:41 +02:00
/**
* @return bool
*/
2020-04-19 18:14:12 -06:00
protected function taskCompileScss()
2020-04-18 17:45:11 -06:00
{
2020-05-07 21:48:33 -06:00
if (!$this->authorizeTask('compile scss', ['admin.super'])) {
2020-04-18 17:45:11 -06:00
return false;
}
$default_scheme = $this->grav['config']->get('plugins.admin.whitelabel.color_scheme');
2020-05-07 21:54:12 -06:00
$preview = $this->post['preview'] ?? false;
$data = ['color_scheme' => $this->data['whitelabel']['color_scheme'] ?? $default_scheme];
$output_file = $preview ? 'admin-preset.css' : 'admin-preset__tmp.css';
2020-04-18 17:45:11 -06:00
$options = [
'input' => 'plugin://admin/themes/grav/scss/preset.scss',
'output' => 'asset://' .$output_file
];
[$compile_status, $msg] = $this->grav['admin-whitelabel']->compilePresetScss($data, $options);
2020-04-18 17:45:11 -06:00
$json_response = [
2020-04-19 18:14:12 -06:00
'status' => $compile_status ? 'success' : 'error',
'message' => ($preview ? 'Preview ' : 'SCSS ') . $msg,
2020-04-18 17:45:11 -06:00
'files' => [
'color_scheme' => Utils::url($options['output'])
2020-04-18 17:45:11 -06:00
]
];
echo json_encode($json_response);
exit;
}
2021-01-08 18:57:41 +02:00
/**
* @return bool
*/
protected function taskExportScss()
{
if (!$this->authorizeTask('compile scss', ['admin.pages', 'admin.super'])) {
return false;
}
$data = ['color_scheme' => $this->data['whitelabel']['color_scheme'] ?? null];
2020-05-07 21:44:22 +01:00
$name = empty($this->data['whitelabel']['color_scheme']['name']) ? 'admin-theme-export' : \Grav\Plugin\Admin\Utils::slug($this->data['whitelabel']['color_scheme']['name']);
2020-05-07 21:37:56 +01:00
2020-05-07 21:44:22 +01:00
$location = 'asset://' . $name . '.yaml';
[$status, $msg] = $this->grav['admin-whitelabel']->exportPresetScsss($data, $location);
$json_response = [
2020-05-07 21:37:56 +01:00
'status' => $status ? 'success' : 'error',
'message' => $msg,
'files' => [
'download' => Utils::url($location)
]
];
echo json_encode($json_response);
exit;
}
2014-08-05 13:06:38 -07:00
/**
2020-11-30 17:25:53 +02:00
* Handles deleting a media file from a page.
*
* Used by pagemedia field.
2014-08-05 13:06:38 -07:00
*
* @return bool True if the action was performed.
*/
protected function taskDelmedia()
2014-08-05 13:06:38 -07:00
{
if (!$this->authorizeTask('delete media', ['admin.pages', 'admin.super'])) {
return false;
}
$page = $this->admin->page($this->route);
$media = $page ? $this->getMedia($page) : null;
if (null === $media) {
$this->admin->json_response = [
'status' => 'error',
2018-12-05 08:20:38 +02:00
'message' => $this->admin::translate('PLUGIN_ADMIN.NO_PAGE_FOUND')
];
return false;
2014-10-10 15:25:07 +03:00
}
$filename = !empty($this->post['filename']) ? basename($this->post['filename']) : null;
2016-03-05 12:11:13 +01:00
2018-10-04 15:42:59 +03:00
// Handle bad filenames.
if (!$filename || !Utils::checkFilename($filename)) {
$this->admin->json_response = [
'status' => 'error',
2018-12-05 08:20:38 +02:00
'message' => $this->admin::translate('PLUGIN_ADMIN.NO_FILE_FOUND')
];
2015-10-20 16:36:39 +02:00
2014-08-05 13:06:38 -07:00
return false;
}
2018-05-23 13:01:58 +03:00
/** @var UniformResourceLocator $locator */
$locator = $this->grav['locator'];
$targetPath = $media->getPath() . '/' . $filename;
2018-05-23 13:01:58 +03:00
if ($locator->isStream($targetPath)) {
$targetPath = $locator->findResource($targetPath, true, true);
}
2017-02-26 19:36:01 +01:00
$fileParts = pathinfo($filename);
$found = false;
if (file_exists($targetPath)) {
2017-02-26 19:36:01 +01:00
$found = true;
$result = unlink($targetPath);
if (!$result) {
$this->admin->json_response = [
'status' => 'error',
2018-12-05 08:20:38 +02:00
'message' => $this->admin::translate('PLUGIN_ADMIN.FILE_COULD_NOT_BE_DELETED') . ': ' . $filename
];
return false;
}
2014-10-10 11:57:57 +03:00
}
2014-08-05 13:06:38 -07:00
// Remove Extra Files
foreach (scandir($media->getPath(), SCANDIR_SORT_NONE) as $file) {
if (preg_match("/{$fileParts['filename']}@\d+x\.{$fileParts['extension']}(?:\.meta\.yaml)?$|{$filename}\.meta\.yaml$/", $file)) {
$targetPath = $media->getPath() . '/' . $file;
if ($locator->isStream($targetPath)) {
$targetPath = $locator->findResource($targetPath, true, true);
}
$result = unlink($targetPath);
if (!$result) {
$this->admin->json_response = [
'status' => 'error',
'message' => $this->admin::translate('PLUGIN_ADMIN.FILE_COULD_NOT_BE_DELETED') . ': ' . $filename
];
return false;
}
$found = true;
}
}
2019-06-14 12:19:31 +03:00
Cache::clearCache('invalidate');
if (!$found) {
$this->admin->json_response = [
'status' => 'error',
'message' => $this->admin::translate('PLUGIN_ADMIN.FILE_NOT_FOUND') . ': ' . $filename
];
return false;
}
// DEPRECATED: page
$this->grav->fireEvent('onAdminAfterDelMedia', new Event(['object' => $page, 'page' => $page, 'media' => $media, 'filename' => $filename]));
$this->admin->json_response = [
'status' => 'success',
'message' => $this->admin::translate('PLUGIN_ADMIN.FILE_DELETED') . ': ' . $filename
];
return true;
}
2021-01-08 18:57:41 +02:00
/**
* @param array $data
* @return array
*/
2019-08-23 12:54:41 +03:00
protected function getLevelListing($data)
{
// Valid types are dir|file|link
$default_filters = ['type'=> ['root', 'dir'], 'name' => null, 'extension' => null];
2019-08-23 20:45:51 +03:00
$pages = $this->admin::enablePages();
$page_instances = $pages->instances();
$is_page = $data['page'] ?? true;
$route = $data['route'] ?? null;
$leaf_route = $data['leaf_route'] ?? null;
$sortby = $data['sortby'] ?? 'filename';
$order = $data['order'] ?? SORT_ASC;
$initial = $data['initial'] ?? null;
$filters = isset($data['filters']) ? $default_filters + json_decode($data['filters']) : $default_filters;
$filter_type = (array) $filters['type'];
$status = 'error';
$msg = null;
$response = [];
$children = null;
$sub_route = null;
$extra = null;
$root = false;
// Handle leaf_route
2019-08-23 20:45:51 +03:00
if ($leaf_route && $route !== $leaf_route) {
$nodes = explode('/', $leaf_route);
2019-08-23 20:45:51 +03:00
$sub_route = '/' . implode('/', array_slice($nodes, 1, $data['level']++));
$data['route'] = $sub_route;
2019-08-23 12:58:56 +03:00
[$status, $msg, $children, $extra] = $this->getLevelListing($data);
}
// Handle no route, assume page tree root
if (!$route) {
$is_page = false;
$route = $this->grav['locator']->findResource('page://', true);
$root = true;
}
if ($is_page) {
// Try the path
/** @var PageInterface $page */
$page = $pages->get(GRAV_ROOT . $route);
// Try a real route (like homepage)
if (is_null($page)) {
$page = $pages->find($route);
}
$path = $page ? $page->path() : null;
} else {
// Try a physical path
if (!Utils::startsWith($route, GRAV_ROOT)) {
$try_path = GRAV_ROOT . $route;
} else {
$try_path = $route;
}
$path = file_exists($try_path) ? $try_path : null;
}
$blueprintsData = $this->admin->page(true);
if (null !== $blueprintsData) {
if (method_exists($blueprintsData, 'blueprints')) {
$settings = $blueprintsData->blueprints()->schema()->getProperty($data['field']);
} elseif (method_exists($blueprintsData, 'getBlueprint')) {
$settings = $blueprintsData->getBlueprint()->schema()->getProperty($data['field']);
}
$filters = array_merge([], $filters, ($settings['filters'] ?? []));
$filter_type = $filters['type'] ?? $filter_type;
}
if ($path) {
$status = 'success';
$msg = 'PLUGIN_ADMIN.PAGE_ROUTE_FOUND';
foreach (new \DirectoryIterator($path) as $fileInfo) {
$fileName = $fileInfo->getFilename();
2019-06-24 11:22:38 -06:00
$filePath = str_replace('\\', '/', $fileInfo->getPathname());
if (($fileInfo->isDot() && $fileName !== '.' && $initial) || (Utils::startsWith($fileName, '.') && strlen($fileName) > 1)) {
continue;
}
if ($fileInfo->isDot()) {
if ($root) {
$payload = [
'name' => '<root>',
'value' => '',
'item-key' => '',
'filename' => '.',
'extension' => '',
'type' => 'root',
'modified' => $fileInfo->getMTime(),
'size' => 0,
2020-03-06 17:10:32 -07:00
'has-children' => false
];
} else {
continue;
}
} else {
2019-06-24 11:22:38 -06:00
$file_page = $page_instances[$filePath] ?? null;
$file_path = Utils::replaceFirstOccurrence(GRAV_ROOT, '', $filePath);
$type = $fileInfo->getType();
2020-05-22 10:32:30 -06:00
$child_path = $file_page ? $file_page->path() : $filePath;
$count_children = Folder::countChildren($child_path);
$payload = [
'name' => $file_page ? $file_page->title() : $fileName,
'value' => $file_page ? $file_page->rawRoute() : $file_path,
'item-key' => basename($file_page ? $file_page->route() : $file_path),
'filename' => $fileName,
'extension' => $type === 'dir' ? '' : $fileInfo->getExtension(),
'type' => $type,
'modified' => $fileInfo->getMTime(),
2020-05-22 10:32:30 -06:00
'size' => $count_children,
'symlink' => false,
2020-05-22 10:32:30 -06:00
'has-children' => $count_children > 0
];
}
2019-06-24 11:22:38 -06:00
// Fix for symlink
if ($payload['type'] === 'link') {
$payload['symlink'] = true;
$physical_path = $fileInfo->getRealPath();
$payload['type'] = is_dir($physical_path) ? 'dir' : 'file';
2019-06-24 11:22:38 -06:00
}
// filter types
if ($filters['type']) {
if (!in_array($payload['type'], $filter_type)) {
continue;
}
}
// Simple filter for name or extension
if (($filters['name'] && Utils::contains($payload['basename'], $filters['name'])) ||
($filters['extension'] && Utils::contains($payload['extension'], $filters['extension']))) {
continue;
}
// Add children if any
2019-08-23 20:45:51 +03:00
if ($filePath === $extra && is_array($children)) {
$payload['children'] = array_values($children);
}
$response[] = $payload;
}
} else {
$msg = 'PLUGIN_ADMIN.PAGE_ROUTE_NOT_FOUND';
}
// Sorting
$response = Utils::sortArrayByKey($response, $sortby, $order);
$temp_array = [];
foreach ($response as $index => $item) {
$temp_array[$item['type']][$index] = $item;
}
$sorted = Utils::sortArrayByArray($temp_array, $filter_type);
$response = Utils::arrayFlatten($sorted);
2014-08-05 13:06:38 -07:00
return [$status, $this->admin::translate($msg ?? 'PLUGIN_ADMIN.NO_ROUTE_PROVIDED'), $response, $path];
2014-08-05 13:06:38 -07:00
}
/**
* Get page media.
*
* @param PageInterface|null $page
* @return Media|null
*/
public function getMedia(PageInterface $page = null)
{
if ($this->view !== 'media') {
return null;
}
$page = $page ?? $this->admin->page($this->route);
if (!$page) {
return null;
}
$this->uri = $this->uri ?? $this->grav['uri'];
2016-07-07 18:55:52 +02:00
$field = (string)$this->uri->post('field', '');
$order = $this->uri->post('order') ?: null;
2020-12-01 09:51:43 +02:00
if ($order && is_string($order)) {
$order = array_map('trim', explode(',', $order));
}
$blueprints = $page->blueprints();
$settings = $this->getMediaFieldSettings($blueprints, $field);
$path = $settings['destination'] ?? $page->path();
2016-07-07 18:55:52 +02:00
return $path ? new Media($path, $order) : null;
}
/**
* @param Data\Blueprint|null $blueprint
* @param string $field
* @return array|null
*/
protected function getMediaFieldSettings(?Data\Blueprint $blueprint, string $field): ?array
{
$schema = $blueprint ? $blueprint->schema() : null;
if (!$schema || $field === '') {
return null;
}
2016-07-07 18:55:52 +02:00
$settings = is_object($schema) ? (array)$schema->getProperty($field) : null;
if (null === $settings) {
return null;
}
2016-07-07 18:55:52 +02:00
if (empty($settings['destination']) || \in_array($settings['destination'], ['@self', 'self@', '@self@'], true)) {
unset($settings['destination']);
}
2016-07-07 18:55:52 +02:00
return $settings + ['accept' => '*', 'limit' => 1000];
}
2016-07-07 18:55:52 +02:00
/**
* @return string
*/
protected function getDataType()
{
return trim("{$this->view}/{$this->admin->route}", '/');
}
/**
* Gets the configuration data for a given view & post
*
* @param array $data
* @return object
*/
protected function prepareData(array $data)
{
$type = $this->getDataType();
return $this->admin->data($type, $data);
}
/**
* Prepare a page to be stored: update its folder, name, template, header and content
*
* @param PageInterface $page
* @param bool $clean_header
* @param string $languageCode
2021-01-08 18:57:41 +02:00
* @return void
*/
2021-01-08 18:57:41 +02:00
protected function preparePage(PageInterface $page, $clean_header = false, $languageCode = ''): void
{
$input = (array)$this->data;
2019-08-23 20:45:51 +03:00
$this->admin::enablePages();
2018-05-10 10:14:18 +03:00
if (isset($input['folder']) && $input['folder'] !== $page->value('folder')) {
$order = $page->value('order');
$ordering = $order ? sprintf('%02d.', $order) : '';
$page->folder($ordering . $input['folder']);
}
if (isset($input['name']) && !empty($input['name'])) {
2018-05-10 10:14:18 +03:00
$type = strtolower($input['name']);
$page->template($type);
$name = preg_replace('|.*/|', '', $type);
/** @var Language $language */
$language = $this->grav['language'];
if ($language->enabled()) {
$languageCode = $languageCode ?: $language->getLanguage();
if ($languageCode) {
$isDefault = $languageCode === $language->getDefault();
$includeLang = !$isDefault || (bool)$this->grav['config']->get('system.languages.include_default_lang_file_extension', true);
if (!$includeLang) {
// Check if the language specific file exists; use it if it does.
$includeLang = file_exists("{$page->path()}/{$name}.{$languageCode}.md");
}
// Keep existing .md file if we're updating default language, otherwise always append the language.
if ($includeLang) {
$name .= '.' . $languageCode;
}
}
}
$name .= '.md';
$page->name($name);
}
// Special case for Expert mode: build the raw, unset content
2018-05-10 10:14:18 +03:00
if (isset($input['frontmatter'], $input['content'])) {
$page->raw("---\n" . (string)$input['frontmatter'] . "\n---\n" . (string)$input['content']);
unset($input['content']);
// Handle header normally
} elseif (isset($input['header'])) {
$header = $input['header'];
foreach ($header as $key => $value) {
2018-05-10 10:14:18 +03:00
if ($key === 'metadata' && is_array($header[$key])) {
foreach ($header['metadata'] as $key2 => $value2) {
if (isset($input['toggleable_header']['metadata'][$key2]) && !$input['toggleable_header']['metadata'][$key2]) {
$header['metadata'][$key2] = '';
}
}
2018-05-10 10:14:18 +03:00
} elseif ($key === 'taxonomy' && is_array($header[$key])) {
foreach ($header[$key] as $taxkey => $taxonomy) {
2018-12-05 08:20:38 +02:00
if (is_array($taxonomy) && \count($taxonomy) === 1 && trim($taxonomy[0]) === '') {
unset($header[$key][$taxkey]);
}
}
} else {
if (isset($input['toggleable_header'][$key]) && !$input['toggleable_header'][$key]) {
$header[$key] = null;
}
}
}
if ($clean_header) {
$header = Utils::arrayFilterRecursive($header, function ($k, $v) {
return !(null === $v || $v === '');
});
}
$page->header((object)$header);
$page->frontmatter(Yaml::dump((array)$page->header(), 20));
}
// Fill content last because it also renders the output.
if (isset($input['content'])) {
$page->rawMarkdown((string)$input['content']);
}
}
/**
* Find the first available $item ('slug' | 'folder') for a page
* Used when copying a page, to determine the first available slot
*
* @param string $item
* @param PageInterface $page
* @return string The first available slot
*/
protected function findFirstAvailable($item, PageInterface $page)
{
$parent = $page->parent();
if (!$parent || !$parent->children()) {
2018-12-05 08:20:38 +02:00
return $page->{$item}();
}
$withoutPrefix = function ($string) {
$match = preg_split('/^[0-9]+\./u', $string, 2, PREG_SPLIT_DELIM_CAPTURE);
2018-12-05 08:20:38 +02:00
return $match[1] ?? $match[0];
};
$withoutPostfix = function ($string) {
$match = preg_split('/-(\d+)$/', $string, 2, PREG_SPLIT_DELIM_CAPTURE);
return $match[0];
};
/* $appendedNumber = function ($string) {
$match = preg_split('/-(\d+)$/', $string, 2, PREG_SPLIT_DELIM_CAPTURE);
$append = (isset($match[1]) ? (int)$match[1] + 1 : 2);
return $append;
};*/
$highest = 1;
$siblings = $parent->children();
$findCorrectAppendedNumber = function ($item, $page_item, $highest) use (
$siblings,
&$findCorrectAppendedNumber,
&$withoutPrefix
) {
foreach ($siblings as $sibling) {
2018-12-05 08:20:38 +02:00
if ($withoutPrefix($sibling->{$item}()) == ($highest === 1 ? $page_item : $page_item . '-' . $highest)) {
$highest = $findCorrectAppendedNumber($item, $page_item, $highest + 1);
2014-08-05 13:06:38 -07:00
return $highest;
}
}
return $highest;
};
$base = $withoutPrefix($withoutPostfix($page->$item()));
2014-08-05 13:06:38 -07:00
$return = $base;
$highest = $findCorrectAppendedNumber($item, $base, $highest);
if ($highest > 1) {
$return .= '-' . $highest;
2014-08-05 13:06:38 -07:00
}
return $return;
2014-08-05 13:06:38 -07:00
}
/**
* @param string $frontmatter
* @return bool
2014-08-05 13:06:38 -07:00
*/
public function checkValidFrontmatter($frontmatter)
2014-08-05 13:06:38 -07:00
{
try {
Yaml::parse($frontmatter);
} catch (\RuntimeException $e) {
2014-08-05 13:06:38 -07:00
return false;
}
return true;
}
/**
* The what should be the new filename when saving as a new language
2014-08-05 13:06:38 -07:00
*
* @param string $current_filename the current file name, including .md. Example: default.en.md
* @param string $language The new language it will be saved as. Example: 'it' or 'en-GB'.
* @return string The new filename. Example: 'default.it'
*/
public function determineFilenameIncludingLanguage($current_filename, $language)
2016-01-10 17:17:04 +01:00
{
$ext = '.md';
$filename = substr($current_filename, 0, -strlen($ext));
$languages_enabled = $this->grav['config']->get('system.languages.supported', []);
2015-08-21 18:01:20 +02:00
$parts = explode('.', trim($filename, '.'));
$lang = array_pop($parts);
2015-08-21 18:01:20 +02:00
if ($lang === $language) {
return $filename . $ext;
2015-08-21 18:01:20 +02:00
}
if (in_array($lang, $languages_enabled, true)) {
$filename = implode('.', $parts);
}
return $filename . '.' . $language . $ext;
}
/**
* Get the next available ordering number in a folder
*
* @param string $path
* @return string the correct order string to prepend
*/
public static function getNextOrderInFolder($path)
{
$files = Folder::all($path, ['recursive' => false]);
$highestOrder = 0;
foreach ($files as $file) {
preg_match(PAGE_ORDER_PREFIX_REGEX, $file, $order);
if (isset($order[0])) {
$theOrder = (int)trim($order[0], '.');
} else {
$theOrder = 0;
}
if ($theOrder >= $highestOrder) {
$highestOrder = $theOrder;
2018-10-04 15:42:59 +03:00
}
}
$orderOfNewFolder = $highestOrder + 1;
if ($orderOfNewFolder < 10) {
$orderOfNewFolder = '0' . $orderOfNewFolder;
}
return $orderOfNewFolder;
}
2020-04-30 13:27:10 -06:00
2021-01-08 18:57:41 +02:00
/**
* @return ResponseInterface
*/
2020-04-30 13:27:10 -06:00
protected function taskConvertUrls(): ResponseInterface
{
$request = $this->getRequest();
$data = $request->getParsedBody();
$converted_links = [];
$converted_images = [];
$status = 'success';
$message = 'All links converted';
$data['route'] = isset($data['route']) ? base64_decode($data['route']) : null;
$data['data'] = json_decode($data['data'] ?? '{}', true);
// use the route if passed, else use current page in admin as reference
$page_route = $data['route'] ?? $this->admin->page(true);
/** @var PageInterface */
$pages = $this->admin::enablePages();
$page = $pages->find($page_route);
if (!$page) {
throw new RequestException($request,'Page Not Found', 404);
}
if (!isset($data['data'])) {
throw new RequestException($request, 'Bad Request', 400);
}
foreach ($data['data']['a'] ?? [] as $link) {
$converted_links[$link] = Excerpts::processLinkHtml($link, $page);
}
foreach ($data['data']['img'] ?? [] as $image) {
$converted_images[$image] = Excerpts::processImageHtml($image, $page);
}
$json = [
'status' => $status,
'message' => $message,
'data' => ['links' => $converted_links, 'images' => $converted_images]
];
return $this->createJsonResponse($json, 200);
}
2014-08-05 13:06:38 -07:00
}