mirror of
https://github.com/getgrav/grav-plugin-admin.git
synced 2025-11-02 11:26:04 +01:00
Feature/upload improvements (#617)
* various improvements.. needs cleanup * more progress - supports deeply nested + pages * Getting close now! * more progress! * some cleanup * use data[_json] to store page-based upload * Smarter logic to get nested form fields * some refactoring/cleanup * Fixed issue with removing multiple files in pages * Refactor and support `destination: page@:/images` and `destination: self@` syntax for file fields * Prettifying the upload field * Handling Files API to better represent the selected files in the input field * Better plurarl string * Fixed harcoded height for input field * revamped CSS!!! * `fancy: false` turns off fancy styling * Create folder if not exists * Add support for @theme/theme@ destination * Fixed create directory functionality to take into account resolved paths * Don't allow @self on page to be uploaded to if not created * added field languages * css tweaks * language integration
This commit is contained in:
@@ -233,9 +233,11 @@ class Admin
|
|||||||
*
|
*
|
||||||
* @return Page
|
* @return Page
|
||||||
*/
|
*/
|
||||||
public function page($route = false)
|
public function page($route = false, $path = null)
|
||||||
{
|
{
|
||||||
$path = $this->route;
|
if (!$path) {
|
||||||
|
$path = $this->route;
|
||||||
|
}
|
||||||
|
|
||||||
if ($route && !$path) {
|
if ($route && !$path) {
|
||||||
$path = '/';
|
$path = '/';
|
||||||
@@ -285,88 +287,57 @@ class Admin
|
|||||||
$post = isset($_POST['data']) ? $_POST['data'] : [];
|
$post = isset($_POST['data']) ? $_POST['data'] : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
switch ($type) {
|
/** @var UniformResourceLocator $locator */
|
||||||
case 'configuration':
|
$locator = $this->grav['locator'];
|
||||||
case 'system':
|
$filename = $locator->findResource("config://{$type}.yaml", true, true);
|
||||||
$type = 'system';
|
$file = CompiledYamlFile::instance($filename);
|
||||||
$blueprints = $this->blueprints("config/{$type}");
|
|
||||||
$config = $this->grav['config'];
|
|
||||||
$obj = new Data\Data($config->get('system'), $blueprints);
|
|
||||||
$obj->merge($post);
|
|
||||||
$file = CompiledYamlFile::instance($this->grav['locator']->findResource("config://{$type}.yaml"));
|
|
||||||
$obj->file($file);
|
|
||||||
$data[$type] = $obj;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'settings':
|
if (preg_match('|plugins/|', $type)) {
|
||||||
case 'site':
|
/** @var Plugins $plugins */
|
||||||
$type = 'site';
|
$plugins = $this->grav['plugins'];
|
||||||
$blueprints = $this->blueprints("config/{$type}");
|
$obj = $plugins->get(preg_replace('|plugins/|', '', $type));
|
||||||
$config = $this->grav['config'];
|
|
||||||
$obj = new Data\Data($config->get('site'), $blueprints);
|
|
||||||
$obj->merge($post);
|
|
||||||
$file = CompiledYamlFile::instance($this->grav['locator']->findResource("config://{$type}.yaml"));
|
|
||||||
$obj->file($file);
|
|
||||||
$data[$type] = $obj;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'login':
|
if (!$obj) { return []; }
|
||||||
$data[$type] = null;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
$obj->merge($post);
|
||||||
/** @var UniformResourceLocator $locator */
|
$obj->file($file);
|
||||||
$locator = $this->grav['locator'];
|
|
||||||
$filename = $locator->findResource("config://{$type}.yaml", true, true);
|
|
||||||
$file = CompiledYamlFile::instance($filename);
|
|
||||||
|
|
||||||
if (preg_match('|plugins/|', $type)) {
|
$data[$type] = $obj;
|
||||||
/** @var Plugins $plugins */
|
} elseif (preg_match('|themes/|', $type)) {
|
||||||
$plugins = $this->grav['plugins'];
|
/** @var Themes $themes */
|
||||||
$obj = $plugins->get(preg_replace('|plugins/|', '', $type));
|
$themes = $this->grav['themes'];
|
||||||
|
$obj = $themes->get(preg_replace('|themes/|', '', $type));
|
||||||
|
|
||||||
if (!$obj) { return []; }
|
if (!$obj) { return []; }
|
||||||
|
|
||||||
$obj->merge($post);
|
$obj->merge($post);
|
||||||
$obj->file($file);
|
$obj->file($file);
|
||||||
|
|
||||||
$data[$type] = $obj;
|
$data[$type] = $obj;
|
||||||
} elseif (preg_match('|themes/|', $type)) {
|
} elseif (preg_match('|users/|', $type)) {
|
||||||
/** @var Themes $themes */
|
$obj = User::load(preg_replace('|users/|', '', $type));
|
||||||
$themes = $this->grav['themes'];
|
$obj->merge($post);
|
||||||
$obj = $themes->get(preg_replace('|themes/|', '', $type));
|
|
||||||
|
|
||||||
if (!$obj) { return []; }
|
$data[$type] = $obj;
|
||||||
|
} elseif (preg_match('|user/|', $type)) {
|
||||||
|
$obj = User::load(preg_replace('|user/|', '', $type));
|
||||||
|
$obj->merge($post);
|
||||||
|
|
||||||
$obj->merge($post);
|
$data[$type] = $obj;
|
||||||
$obj->file($file);
|
} elseif (preg_match('|config/|', $type)) {
|
||||||
|
$type = preg_replace('|config/|', '', $type);
|
||||||
$data[$type] = $obj;
|
$blueprints = $this->blueprints("config/{$type}");
|
||||||
} elseif (preg_match('|users/|', $type)) {
|
$config = $this->grav['config'];
|
||||||
$obj = User::load(preg_replace('|users/|', '', $type));
|
$obj = new Data\Data($config->get($type, []), $blueprints);
|
||||||
$obj->merge($post);
|
$obj->merge($post);
|
||||||
|
// FIXME: We shouldn't allow user to change configuration files in system folder!
|
||||||
$data[$type] = $obj;
|
$filename = $this->grav['locator']->findResource("config://{$type}.yaml")
|
||||||
} elseif (preg_match('|user/|', $type)) {
|
?: $this->grav['locator']->findResource("config://{$type}.yaml", true, true);
|
||||||
$obj = User::load(preg_replace('|user/|', '', $type));
|
$file = CompiledYamlFile::instance($filename);
|
||||||
$obj->merge($post);
|
$obj->file($file);
|
||||||
|
$data[$type] = $obj;
|
||||||
$data[$type] = $obj;
|
} else {
|
||||||
} elseif (preg_match('|config/|', $type)) {
|
throw new \RuntimeException("Data type '{$type}' doesn't exist!");
|
||||||
$type = preg_replace('|config/|', '', $type);
|
|
||||||
$blueprints = $this->blueprints("config/{$type}");
|
|
||||||
$config = $this->grav['config'];
|
|
||||||
$obj = new Data\Data($config->get($type, []), $blueprints);
|
|
||||||
$obj->merge($post);
|
|
||||||
// FIXME: We shouldn't allow user to change configuration files in system folder!
|
|
||||||
$filename = $this->grav['locator']->findResource("config://{$type}.yaml")
|
|
||||||
?: $this->grav['locator']->findResource("config://{$type}.yaml", true, true);
|
|
||||||
$file = CompiledYamlFile::instance($filename);
|
|
||||||
$obj->file($file);
|
|
||||||
$data[$type] = $obj;
|
|
||||||
} else {
|
|
||||||
throw new \RuntimeException("Data type '{$type}' doesn't exist!");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $data[$type];
|
return $data[$type];
|
||||||
@@ -1180,4 +1151,63 @@ class Admin
|
|||||||
{
|
{
|
||||||
$this->permissions = array_merge($this->permissions, $permissions);
|
$this->permissions = array_merge($this->permissions, $permissions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function findFormFields($type, $fields, $found_fields = [])
|
||||||
|
{
|
||||||
|
foreach ($fields as $key => $field) {
|
||||||
|
|
||||||
|
if (isset($field['type']) && $field['type'] == $type) {
|
||||||
|
$found_fields[$key] = $field;
|
||||||
|
} elseif (isset($field['fields'])) {
|
||||||
|
$result = $this->findFormFields($type, $field['fields'], $found_fields);
|
||||||
|
if (!empty($result)) {
|
||||||
|
$found_fields = array_merge($found_fields, $result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $found_fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPagePathFromToken($path)
|
||||||
|
{
|
||||||
|
$path_parts = pathinfo($path);
|
||||||
|
|
||||||
|
$basename = '';
|
||||||
|
if (isset($path_parts['extension'])) {
|
||||||
|
$basename = '/'.$path_parts['basename'];
|
||||||
|
$path = $path_parts['dirname'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$regex = '/(@self|self@)|((?:@page|page@):(?:.*))|((?:@theme|theme@):(?:.*))/';
|
||||||
|
preg_match($regex, $path, $matches);
|
||||||
|
|
||||||
|
if ($matches) {
|
||||||
|
if ($matches[1]) {
|
||||||
|
// self@
|
||||||
|
$page = $this->page(true);
|
||||||
|
} elseif ($matches[2]) {
|
||||||
|
// page@
|
||||||
|
$parts = explode(':', $path);
|
||||||
|
$route = $parts[1];
|
||||||
|
$page = $this->grav['page']->find($route);
|
||||||
|
} elseif ($matches[3]) {
|
||||||
|
// theme@
|
||||||
|
$parts = explode(':', $path);
|
||||||
|
$route = $parts[1];
|
||||||
|
$theme = str_replace(ROOT_DIR, '', $this->grav['locator']->findResource("theme://"));
|
||||||
|
return $theme . $route . $basename;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return $path . $basename;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$page) {
|
||||||
|
throw new \RuntimeException('Page route not found: ' . $path);
|
||||||
|
}
|
||||||
|
|
||||||
|
$path = str_replace($matches[0], rtrim($page->relativePagePath(), '/'), $path);
|
||||||
|
|
||||||
|
return $path . $basename;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,11 +85,11 @@ class AdminController
|
|||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param Grav $grav
|
* @param Grav $grav
|
||||||
* @param string $view
|
* @param string $view
|
||||||
* @param string $task
|
* @param string $task
|
||||||
* @param string $route
|
* @param string $route
|
||||||
* @param array $post
|
* @param array $post
|
||||||
*/
|
*/
|
||||||
public function __construct(Grav $grav, $view, $task, $route, $post)
|
public function __construct(Grav $grav, $view, $task, $route, $post)
|
||||||
{
|
{
|
||||||
@@ -123,15 +123,14 @@ class AdminController
|
|||||||
$nonce = $this->grav['uri']->param('admin-nonce');
|
$nonce = $this->grav['uri']->param('admin-nonce');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$nonce || !Utils::verifyNonce($nonce, 'admin-form'))
|
if (!$nonce || !Utils::verifyNonce($nonce, 'admin-form')) {
|
||||||
{
|
|
||||||
if ($this->task == 'addmedia') {
|
if ($this->task == 'addmedia') {
|
||||||
|
|
||||||
$message = sprintf($this->admin->translate('PLUGIN_ADMIN.FILE_TOO_LARGE', null, true), ini_get('post_max_size'));
|
$message = sprintf($this->admin->translate('PLUGIN_ADMIN.FILE_TOO_LARGE', null, true), ini_get('post_max_size'));
|
||||||
|
|
||||||
//In this case it's more likely that the image is too big than POST can handle. Show message
|
//In this case it's more likely that the image is too big than POST can handle. Show message
|
||||||
$this->admin->json_response = [
|
$this->admin->json_response = [
|
||||||
'status' => 'error',
|
'status' => 'error',
|
||||||
'message' => $message
|
'message' => $message
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -140,7 +139,7 @@ class AdminController
|
|||||||
|
|
||||||
$this->admin->setMessage($this->admin->translate('PLUGIN_ADMIN.INVALID_SECURITY_TOKEN'), 'error');
|
$this->admin->setMessage($this->admin->translate('PLUGIN_ADMIN.INVALID_SECURITY_TOKEN'), 'error');
|
||||||
$this->admin->json_response = [
|
$this->admin->json_response = [
|
||||||
'status' => 'error',
|
'status' => 'error',
|
||||||
'message' => $this->admin->translate('PLUGIN_ADMIN.INVALID_SECURITY_TOKEN')
|
'message' => $this->admin->translate('PLUGIN_ADMIN.INVALID_SECURITY_TOKEN')
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -154,7 +153,7 @@ class AdminController
|
|||||||
$this->admin->setMessage($this->admin->translate('PLUGIN_ADMIN.INVALID_SECURITY_TOKEN'),
|
$this->admin->setMessage($this->admin->translate('PLUGIN_ADMIN.INVALID_SECURITY_TOKEN'),
|
||||||
'error');
|
'error');
|
||||||
$this->admin->json_response = [
|
$this->admin->json_response = [
|
||||||
'status' => 'error',
|
'status' => 'error',
|
||||||
'message' => $this->admin->translate('PLUGIN_ADMIN.INVALID_SECURITY_TOKEN')
|
'message' => $this->admin->translate('PLUGIN_ADMIN.INVALID_SECURITY_TOKEN')
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -166,7 +165,7 @@ class AdminController
|
|||||||
$this->admin->setMessage($this->admin->translate('PLUGIN_ADMIN.INVALID_SECURITY_TOKEN'),
|
$this->admin->setMessage($this->admin->translate('PLUGIN_ADMIN.INVALID_SECURITY_TOKEN'),
|
||||||
'error');
|
'error');
|
||||||
$this->admin->json_response = [
|
$this->admin->json_response = [
|
||||||
'status' => 'error',
|
'status' => 'error',
|
||||||
'message' => $this->admin->translate('PLUGIN_ADMIN.INVALID_SECURITY_TOKEN')
|
'message' => $this->admin->translate('PLUGIN_ADMIN.INVALID_SECURITY_TOKEN')
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -283,7 +282,7 @@ class AdminController
|
|||||||
$language = $this->grav['user']->authenticated ? $this->grav['user']->language : ($this->grav['language']->getLanguage() ?: 'en');
|
$language = $this->grav['user']->authenticated ? $this->grav['user']->language : ($this->grav['language']->getLanguage() ?: 'en');
|
||||||
|
|
||||||
$this->admin->session()->invalidate()->start();
|
$this->admin->session()->invalidate()->start();
|
||||||
$this->setRedirect('/logout/lang:'.$language);
|
$this->setRedirect('/logout/lang:' . $language);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -464,7 +463,7 @@ class AdminController
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($result) {
|
if ($result) {
|
||||||
$this->admin->json_response = ['status' => 'success', 'dependencies' => $dependencies, 'message' => $this->admin->translate(is_string($result) ? $result :'PLUGIN_ADMIN.UNINSTALL_SUCCESSFUL')];
|
$this->admin->json_response = ['status' => 'success', 'dependencies' => $dependencies, 'message' => $this->admin->translate(is_string($result) ? $result : 'PLUGIN_ADMIN.UNINSTALL_SUCCESSFUL')];
|
||||||
} else {
|
} else {
|
||||||
$this->admin->json_response = ['status' => 'error', 'message' => $this->admin->translate('PLUGIN_ADMIN.UNINSTALL_FAILED')];
|
$this->admin->json_response = ['status' => 'error', 'message' => $this->admin->translate('PLUGIN_ADMIN.UNINSTALL_FAILED')];
|
||||||
}
|
}
|
||||||
@@ -651,12 +650,12 @@ class AdminController
|
|||||||
$results = Cache::clearCache($clear);
|
$results = Cache::clearCache($clear);
|
||||||
if (count($results) > 0) {
|
if (count($results) > 0) {
|
||||||
$this->admin->json_response = [
|
$this->admin->json_response = [
|
||||||
'status' => 'success',
|
'status' => 'success',
|
||||||
'message' => $this->admin->translate('PLUGIN_ADMIN.CACHE_CLEARED') . ' <br />' . $this->admin->translate('PLUGIN_ADMIN.METHOD') . ': ' . $clear . ''
|
'message' => $this->admin->translate('PLUGIN_ADMIN.CACHE_CLEARED') . ' <br />' . $this->admin->translate('PLUGIN_ADMIN.METHOD') . ': ' . $clear . ''
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
$this->admin->json_response = [
|
$this->admin->json_response = [
|
||||||
'status' => 'error',
|
'status' => 'error',
|
||||||
'message' => $this->admin->translate('PLUGIN_ADMIN.ERROR_CLEARING_CACHE')
|
'message' => $this->admin->translate('PLUGIN_ADMIN.ERROR_CLEARING_CACHE')
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -696,7 +695,7 @@ class AdminController
|
|||||||
$backup = ZipBackup::backup();
|
$backup = ZipBackup::backup();
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$this->admin->json_response = [
|
$this->admin->json_response = [
|
||||||
'status' => 'error',
|
'status' => 'error',
|
||||||
'message' => $this->admin->translate('PLUGIN_ADMIN.AN_ERROR_OCCURRED') . '. ' . $e->getMessage()
|
'message' => $this->admin->translate('PLUGIN_ADMIN.AN_ERROR_OCCURRED') . '. ' . $e->getMessage()
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -708,18 +707,18 @@ class AdminController
|
|||||||
'/') . '/task' . $param_sep . 'backup/download' . $param_sep . $download . '/admin-nonce' . $param_sep . Utils::getNonce('admin-form');
|
'/') . '/task' . $param_sep . 'backup/download' . $param_sep . $download . '/admin-nonce' . $param_sep . Utils::getNonce('admin-form');
|
||||||
|
|
||||||
$log->content([
|
$log->content([
|
||||||
'time' => time(),
|
'time' => time(),
|
||||||
'location' => $backup
|
'location' => $backup
|
||||||
]);
|
]);
|
||||||
$log->save();
|
$log->save();
|
||||||
|
|
||||||
$this->admin->json_response = [
|
$this->admin->json_response = [
|
||||||
'status' => 'success',
|
'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>',
|
'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' => [
|
'toastr' => [
|
||||||
'timeOut' => 0,
|
'timeOut' => 0,
|
||||||
'extendedTimeOut' => 0,
|
'extendedTimeOut' => 0,
|
||||||
'closeButton' => true
|
'closeButton' => true
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -838,7 +837,7 @@ class AdminController
|
|||||||
}
|
}
|
||||||
|
|
||||||
$this->admin->json_response = [
|
$this->admin->json_response = [
|
||||||
'status' => 'success',
|
'status' => 'success',
|
||||||
'message' => $this->admin->translate('PLUGIN_ADMIN.PAGES_FILTERED'),
|
'message' => $this->admin->translate('PLUGIN_ADMIN.PAGES_FILTERED'),
|
||||||
'results' => $results
|
'results' => $results
|
||||||
];
|
];
|
||||||
@@ -860,7 +859,7 @@ class AdminController
|
|||||||
|
|
||||||
if (!$page) {
|
if (!$page) {
|
||||||
$this->admin->json_response = [
|
$this->admin->json_response = [
|
||||||
'status' => 'error',
|
'status' => 'error',
|
||||||
'message' => $this->admin->translate('PLUGIN_ADMIN.NO_PAGE_FOUND')
|
'message' => $this->admin->translate('PLUGIN_ADMIN.NO_PAGE_FOUND')
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -894,7 +893,7 @@ class AdminController
|
|||||||
|
|
||||||
if (!isset($_FILES['file']['error']) || is_array($_FILES['file']['error'])) {
|
if (!isset($_FILES['file']['error']) || is_array($_FILES['file']['error'])) {
|
||||||
$this->admin->json_response = [
|
$this->admin->json_response = [
|
||||||
'status' => 'error',
|
'status' => 'error',
|
||||||
'message' => $this->admin->translate('PLUGIN_ADMIN.INVALID_PARAMETERS')
|
'message' => $this->admin->translate('PLUGIN_ADMIN.INVALID_PARAMETERS')
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -907,7 +906,7 @@ class AdminController
|
|||||||
break;
|
break;
|
||||||
case UPLOAD_ERR_NO_FILE:
|
case UPLOAD_ERR_NO_FILE:
|
||||||
$this->admin->json_response = [
|
$this->admin->json_response = [
|
||||||
'status' => 'error',
|
'status' => 'error',
|
||||||
'message' => $this->admin->translate('PLUGIN_ADMIN.NO_FILES_SENT')
|
'message' => $this->admin->translate('PLUGIN_ADMIN.NO_FILES_SENT')
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -915,14 +914,14 @@ class AdminController
|
|||||||
case UPLOAD_ERR_INI_SIZE:
|
case UPLOAD_ERR_INI_SIZE:
|
||||||
case UPLOAD_ERR_FORM_SIZE:
|
case UPLOAD_ERR_FORM_SIZE:
|
||||||
$this->admin->json_response = [
|
$this->admin->json_response = [
|
||||||
'status' => 'error',
|
'status' => 'error',
|
||||||
'message' => $this->admin->translate('PLUGIN_ADMIN.EXCEEDED_FILESIZE_LIMIT')
|
'message' => $this->admin->translate('PLUGIN_ADMIN.EXCEEDED_FILESIZE_LIMIT')
|
||||||
];
|
];
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
default:
|
default:
|
||||||
$this->admin->json_response = [
|
$this->admin->json_response = [
|
||||||
'status' => 'error',
|
'status' => 'error',
|
||||||
'message' => $this->admin->translate('PLUGIN_ADMIN.UNKNOWN_ERRORS')
|
'message' => $this->admin->translate('PLUGIN_ADMIN.UNKNOWN_ERRORS')
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -933,7 +932,7 @@ class AdminController
|
|||||||
// You should also check filesize here.
|
// You should also check filesize here.
|
||||||
if ($grav_limit > 0 && $_FILES['file']['size'] > $grav_limit) {
|
if ($grav_limit > 0 && $_FILES['file']['size'] > $grav_limit) {
|
||||||
$this->admin->json_response = [
|
$this->admin->json_response = [
|
||||||
'status' => 'error',
|
'status' => 'error',
|
||||||
'message' => $this->admin->translate('PLUGIN_ADMIN.EXCEEDED_GRAV_FILESIZE_LIMIT')
|
'message' => $this->admin->translate('PLUGIN_ADMIN.EXCEEDED_GRAV_FILESIZE_LIMIT')
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -952,7 +951,7 @@ class AdminController
|
|||||||
// If not a supported type, return
|
// If not a supported type, return
|
||||||
if (!$fileExt || !$config->get("media.{$fileExt}")) {
|
if (!$fileExt || !$config->get("media.{$fileExt}")) {
|
||||||
$this->admin->json_response = [
|
$this->admin->json_response = [
|
||||||
'status' => 'error',
|
'status' => 'error',
|
||||||
'message' => $this->admin->translate('PLUGIN_ADMIN.UNSUPPORTED_FILE_TYPE') . ': ' . $fileExt
|
'message' => $this->admin->translate('PLUGIN_ADMIN.UNSUPPORTED_FILE_TYPE') . ': ' . $fileExt
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -964,7 +963,7 @@ class AdminController
|
|||||||
sprintf('%s/%s', $page->path(), $_FILES['file']['name']))
|
sprintf('%s/%s', $page->path(), $_FILES['file']['name']))
|
||||||
) {
|
) {
|
||||||
$this->admin->json_response = [
|
$this->admin->json_response = [
|
||||||
'status' => 'error',
|
'status' => 'error',
|
||||||
'message' => $this->admin->translate('PLUGIN_ADMIN.FAILED_TO_MOVE_UPLOADED_FILE')
|
'message' => $this->admin->translate('PLUGIN_ADMIN.FAILED_TO_MOVE_UPLOADED_FILE')
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -973,7 +972,7 @@ class AdminController
|
|||||||
|
|
||||||
Cache::clearCache();
|
Cache::clearCache();
|
||||||
$this->admin->json_response = [
|
$this->admin->json_response = [
|
||||||
'status' => 'success',
|
'status' => 'success',
|
||||||
'message' => $this->admin->translate('PLUGIN_ADMIN.FILE_UPLOADED_SUCCESSFULLY')
|
'message' => $this->admin->translate('PLUGIN_ADMIN.FILE_UPLOADED_SUCCESSFULLY')
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -995,7 +994,7 @@ class AdminController
|
|||||||
|
|
||||||
if (!$page) {
|
if (!$page) {
|
||||||
$this->admin->json_response = [
|
$this->admin->json_response = [
|
||||||
'status' => 'error',
|
'status' => 'error',
|
||||||
'message' => $this->admin->translate('PLUGIN_ADMIN.NO_PAGE_FOUND')
|
'message' => $this->admin->translate('PLUGIN_ADMIN.NO_PAGE_FOUND')
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -1010,12 +1009,12 @@ class AdminController
|
|||||||
if (unlink($targetPath)) {
|
if (unlink($targetPath)) {
|
||||||
Cache::clearCache();
|
Cache::clearCache();
|
||||||
$this->admin->json_response = [
|
$this->admin->json_response = [
|
||||||
'status' => 'success',
|
'status' => 'success',
|
||||||
'message' => $this->admin->translate('PLUGIN_ADMIN.FILE_DELETED') . ': ' . $filename
|
'message' => $this->admin->translate('PLUGIN_ADMIN.FILE_DELETED') . ': ' . $filename
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
$this->admin->json_response = [
|
$this->admin->json_response = [
|
||||||
'status' => 'error',
|
'status' => 'error',
|
||||||
'message' => $this->admin->translate('PLUGIN_ADMIN.FILE_COULD_NOT_BE_DELETED') . ': ' . $filename
|
'message' => $this->admin->translate('PLUGIN_ADMIN.FILE_COULD_NOT_BE_DELETED') . ': ' . $filename
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -1043,12 +1042,12 @@ class AdminController
|
|||||||
if ($deletedResponsiveImage) {
|
if ($deletedResponsiveImage) {
|
||||||
Cache::clearCache();
|
Cache::clearCache();
|
||||||
$this->admin->json_response = [
|
$this->admin->json_response = [
|
||||||
'status' => 'success',
|
'status' => 'success',
|
||||||
'message' => $this->admin->translate('PLUGIN_ADMIN.FILE_DELETED') . ': ' . $filename
|
'message' => $this->admin->translate('PLUGIN_ADMIN.FILE_DELETED') . ': ' . $filename
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
$this->admin->json_response = [
|
$this->admin->json_response = [
|
||||||
'status' => 'error',
|
'status' => 'error',
|
||||||
'message' => $this->admin->translate('PLUGIN_ADMIN.FILE_NOT_FOUND') . ': ' . $filename
|
'message' => $this->admin->translate('PLUGIN_ADMIN.FILE_NOT_FOUND') . ': ' . $filename
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -1056,7 +1055,7 @@ class AdminController
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$this->admin->json_response = [
|
$this->admin->json_response = [
|
||||||
'status' => 'error',
|
'status' => 'error',
|
||||||
'message' => $this->admin->translate('PLUGIN_ADMIN.NO_FILE_FOUND')
|
'message' => $this->admin->translate('PLUGIN_ADMIN.NO_FILE_FOUND')
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
@@ -1080,7 +1079,7 @@ class AdminController
|
|||||||
|
|
||||||
if (!$page) {
|
if (!$page) {
|
||||||
$this->admin->json_response = [
|
$this->admin->json_response = [
|
||||||
'status' => 'error',
|
'status' => 'error',
|
||||||
'message' => $this->admin->translate('PLUGIN_ADMIN.NO_PAGE_FOUND')
|
'message' => $this->admin->translate('PLUGIN_ADMIN.NO_PAGE_FOUND')
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -1216,15 +1215,15 @@ class AdminController
|
|||||||
|
|
||||||
if ($result) {
|
if ($result) {
|
||||||
$this->admin->json_response = [
|
$this->admin->json_response = [
|
||||||
'status' => 'success',
|
'status' => 'success',
|
||||||
'type' => 'updategrav',
|
'type' => 'updategrav',
|
||||||
'version' => $version,
|
'version' => $version,
|
||||||
'message' => $this->admin->translate('PLUGIN_ADMIN.GRAV_WAS_SUCCESSFULLY_UPDATED_TO') . ' ' . $version
|
'message' => $this->admin->translate('PLUGIN_ADMIN.GRAV_WAS_SUCCESSFULLY_UPDATED_TO') . ' ' . $version
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
$this->admin->json_response = [
|
$this->admin->json_response = [
|
||||||
'status' => 'error',
|
'status' => 'error',
|
||||||
'type' => 'updategrav',
|
'type' => 'updategrav',
|
||||||
'version' => GRAV_VERSION,
|
'version' => GRAV_VERSION,
|
||||||
'message' => $this->admin->translate('PLUGIN_ADMIN.GRAV_UPDATE_FAILED') . ' <br>' . Installer::lastErrorMsg()
|
'message' => $this->admin->translate('PLUGIN_ADMIN.GRAV_UPDATE_FAILED') . ' <br>' . Installer::lastErrorMsg()
|
||||||
];
|
];
|
||||||
@@ -1265,87 +1264,74 @@ class AdminController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param $field
|
||||||
* @return array
|
* @return array
|
||||||
*/
|
*/
|
||||||
private function cleanFilesData()
|
private function cleanFilesData($field)
|
||||||
{
|
{
|
||||||
/** @var Page $page */
|
/** @var Page $page */
|
||||||
$page = null;
|
$page = null;
|
||||||
$cleanFiles = [];
|
$cleanFiles = [];
|
||||||
|
|
||||||
$type = trim("{$this->view}/{$this->admin->route}", '/');
|
|
||||||
$data = $this->admin->data($type, $this->post);
|
|
||||||
|
|
||||||
$blueprints = $data->blueprints();
|
|
||||||
|
|
||||||
if (!isset($blueprints['form']['fields'])) {
|
|
||||||
throw new \RuntimeException('Blueprints missing form fields definition');
|
|
||||||
}
|
|
||||||
$blueprint = $blueprints['form']['fields'];
|
|
||||||
|
|
||||||
$file = $_FILES['data'];
|
$file = $_FILES['data'];
|
||||||
|
|
||||||
foreach ((array)$file['error'] as $index => $errors) {
|
$errors = (array) Utils::getDotNotation($file['error'], $field['name']);
|
||||||
$errors = !is_array($errors) ? [$errors] : $errors;
|
|
||||||
|
|
||||||
foreach($errors as $multiple_index => $error) {
|
foreach ($errors as $index => $error) {
|
||||||
if ($error == UPLOAD_ERR_OK) {
|
if ($error == UPLOAD_ERR_OK) {
|
||||||
if (is_array($file['name'][$index])) {
|
|
||||||
$tmp_name = $file['tmp_name'][$index][$multiple_index];
|
|
||||||
$name = $file['name'][$index][$multiple_index];
|
|
||||||
$type = $file['type'][$index][$multiple_index];
|
|
||||||
$size = $file['size'][$index][$multiple_index];
|
|
||||||
} else {
|
|
||||||
$tmp_name = $file['tmp_name'][$index];
|
|
||||||
$name = $file['name'][$index];
|
|
||||||
$type = $file['type'][$index];
|
|
||||||
$size = $file['size'][$index];
|
|
||||||
}
|
|
||||||
|
|
||||||
$destination = Folder::getRelativePath(rtrim($blueprint[$index]['destination'], '/'));
|
$fieldname = $field['name'];
|
||||||
|
|
||||||
if (!$this->match_in_array($type, $blueprint[$index]['accept'])) {
|
// Deal with multiple files
|
||||||
throw new \RuntimeException('File "' . $name . '" is not an accepted MIME type.');
|
if (isset($field['multiple']) && $field['multiple'] == true) {
|
||||||
}
|
$fieldname = $fieldname . ".$index";
|
||||||
|
}
|
||||||
|
|
||||||
if (Utils::startsWith($destination, '@page:')) {
|
$tmp_name = Utils::getDotNotation($file['tmp_name'], $fieldname);
|
||||||
$parts = explode(':', $destination);
|
$name = Utils::getDotNotation($file['name'], $fieldname);
|
||||||
$route = $parts[1];
|
$type = Utils::getDotNotation($file['type'], $fieldname);
|
||||||
$page = $this->grav['page']->find($route);
|
$size = Utils::getDotNotation($file['size'], $fieldname);
|
||||||
|
|
||||||
if (!$page) {
|
$original_destination = null ;
|
||||||
throw new \RuntimeException('Unable to upload file to destination. Page route not found.');
|
$destination = Folder::getRelativePath(rtrim($field['destination'], '/'));
|
||||||
}
|
|
||||||
|
|
||||||
$destination = $page->relativePagePath();
|
if (!$this->match_in_array($type, $field['accept'])) {
|
||||||
} else {
|
throw new \RuntimeException('File "' . $name . '" is not an accepted MIME type.');
|
||||||
if ($destination == '@self') {
|
}
|
||||||
$page = $this->admin->page(true);
|
|
||||||
$destination = $page->relativePagePath();
|
|
||||||
} else {
|
|
||||||
Folder::mkdir($destination);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (move_uploaded_file($tmp_name, "$destination/$name")) {
|
if (isset($field['random_name']) && $field['random_name'] === true) {
|
||||||
$path = $page ? $this->grav['uri']->convertUrl($page, $page->route() . '/' . $name) : $destination . '/' . $name;
|
$path_parts = pathinfo($name);
|
||||||
$fileData = [
|
$name = Utils::generateRandomString(15) . '.' . $path_parts['extension'];
|
||||||
'name' => $name,
|
}
|
||||||
'path' => $path,
|
|
||||||
'type' => $type,
|
|
||||||
'size' => $size,
|
|
||||||
'file' => $destination . '/' . $name,
|
|
||||||
'route' => $page ? $path : null
|
|
||||||
];
|
|
||||||
|
|
||||||
$cleanFiles[$index][$path] = $fileData;
|
$resolved_destination = $this->admin->getPagePathFromToken($destination);
|
||||||
} else {
|
$upload_path = $resolved_destination . '/' . $name;
|
||||||
throw new \RuntimeException("Unable to upload file(s) to $destination/$name");
|
|
||||||
}
|
// Create dir if need be
|
||||||
|
if (!is_dir($resolved_destination)) {
|
||||||
|
Folder::mkdir($resolved_destination);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (move_uploaded_file($tmp_name, $upload_path)) {
|
||||||
|
$path = $destination . '/' . $name;
|
||||||
|
$fileData = [
|
||||||
|
'name' => $name,
|
||||||
|
'path' => $path,
|
||||||
|
'type' => $type,
|
||||||
|
'size' => $size,
|
||||||
|
'file' => $destination . '/' . $name,
|
||||||
|
'route' => $page ? $path : null
|
||||||
|
];
|
||||||
|
|
||||||
|
$cleanFiles[$field['name']][$path] = $fileData;
|
||||||
} else {
|
} else {
|
||||||
if ($error != UPLOAD_ERR_NO_FILE) {
|
throw new \RuntimeException("Unable to upload file(s) to $destination/$name");
|
||||||
throw new \RuntimeException("Unable to upload file(s) - Error: ".$file['name'][$index].": ".$this->upload_errors[$error]);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
} else {
|
||||||
|
if ($error != UPLOAD_ERR_NO_FILE) {
|
||||||
|
throw new \RuntimeException("Unable to upload file(s) - Error: " . $field['name'] . ": " . $this->upload_errors[$error]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1354,7 +1340,7 @@ class AdminController
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $needle
|
* @param string $needle
|
||||||
* @param array|string $haystack
|
* @param array|string $haystack
|
||||||
*
|
*
|
||||||
* @return bool
|
* @return bool
|
||||||
@@ -1382,16 +1368,60 @@ class AdminController
|
|||||||
if (!isset($_FILES['data'])) {
|
if (!isset($_FILES['data'])) {
|
||||||
return $obj;
|
return $obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
$cleanFiles = $this->cleanFilesData();
|
|
||||||
|
|
||||||
foreach ($cleanFiles as $key => $data) {
|
$blueprints = $obj->blueprints();
|
||||||
$obj->set($key, $data);
|
|
||||||
|
if (!isset($blueprints['form']['fields'])) {
|
||||||
|
throw new \RuntimeException('Blueprints missing form fields definition');
|
||||||
|
}
|
||||||
|
$fields = $blueprints['form']['fields'];
|
||||||
|
|
||||||
|
$found_files = $this->findFields('file', $fields);
|
||||||
|
|
||||||
|
foreach ($found_files as $key => $data) {
|
||||||
|
if ($this->view == 'pages') {
|
||||||
|
$keys = explode('.', preg_replace('/^header./', '', $key));
|
||||||
|
$init_key = array_shift($keys);
|
||||||
|
if (count($keys) > 0) {
|
||||||
|
$new_data = isset($obj->header()->$init_key) ? $obj->header()->$init_key: [];
|
||||||
|
Utils::setDotNotation($new_data, implode('.', $keys), $data);
|
||||||
|
} else {
|
||||||
|
$new_data = $data;
|
||||||
|
}
|
||||||
|
$obj->modifyHeader($init_key, $new_data);
|
||||||
|
} else {
|
||||||
|
$obj->set($key, $data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $obj;
|
return $obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function findFields($type, $fields, $found = [])
|
||||||
|
{
|
||||||
|
foreach ($fields as $key => $field) {
|
||||||
|
|
||||||
|
if (isset($field['type']) && $field['type'] == $type) {
|
||||||
|
$file_field = $this->cleanFilesData($field);
|
||||||
|
} elseif (isset($field['fields'])) {
|
||||||
|
$result = $this->findFields($type, $field['fields'], $found);
|
||||||
|
if (!empty($result)) {
|
||||||
|
$found = array_merge($found, $result);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$file_field = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($file_field) && (!is_array($file_field) || !empty($file_field))) {
|
||||||
|
$found = array_merge($file_field, $found);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $found;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles creating an empty page folder (without markdown file)
|
* Handles creating an empty page folder (without markdown file)
|
||||||
*
|
*
|
||||||
@@ -1493,8 +1523,12 @@ class AdminController
|
|||||||
|
|
||||||
// Find new parent page in order to build the path.
|
// Find new parent page in order to build the path.
|
||||||
$route = !isset($data['route']) ? dirname($this->admin->route) : $data['route'];
|
$route = !isset($data['route']) ? dirname($this->admin->route) : $data['route'];
|
||||||
|
|
||||||
|
/** @var Page $obj */
|
||||||
$obj = $this->admin->page(true);
|
$obj = $this->admin->page(true);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Ensure route is prefixed with a forward slash.
|
// Ensure route is prefixed with a forward slash.
|
||||||
$route = '/' . ltrim($route, '/');
|
$route = '/' . ltrim($route, '/');
|
||||||
|
|
||||||
@@ -1530,6 +1564,8 @@ class AdminController
|
|||||||
$obj = $obj->move($parent);
|
$obj = $obj->move($parent);
|
||||||
$this->preparePage($obj, false, $obj->language());
|
$this->preparePage($obj, false, $obj->language());
|
||||||
|
|
||||||
|
$obj = $this->processFiles($obj);
|
||||||
|
|
||||||
// Reset slug and route. For now we do not support slug twig variable on save.
|
// Reset slug and route. For now we do not support slug twig variable on save.
|
||||||
$obj->slug($original_slug);
|
$obj->slug($original_slug);
|
||||||
|
|
||||||
@@ -1883,11 +1919,11 @@ class AdminController
|
|||||||
/**
|
/**
|
||||||
* Determine if the user can edit media
|
* Determine if the user can edit media
|
||||||
*
|
*
|
||||||
|
* @param string $type
|
||||||
* @return bool True if the media action is allowed
|
* @return bool True if the media action is allowed
|
||||||
*/
|
*/
|
||||||
protected function canEditMedia()
|
protected function canEditMedia($type = 'media')
|
||||||
{
|
{
|
||||||
$type = 'media';
|
|
||||||
if (!$this->authorizeTask('edit media', ['admin.' . $type, 'admin.super'])) {
|
if (!$this->authorizeTask('edit media', ['admin.' . $type, 'admin.super'])) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -1907,6 +1943,7 @@ class AdminController
|
|||||||
}
|
}
|
||||||
|
|
||||||
$filename = base64_decode($this->route);
|
$filename = base64_decode($this->route);
|
||||||
|
|
||||||
$file = File::instance($filename);
|
$file = File::instance($filename);
|
||||||
$resultRemoveMedia = false;
|
$resultRemoveMedia = false;
|
||||||
$resultRemoveMediaMeta = true;
|
$resultRemoveMediaMeta = true;
|
||||||
@@ -1943,31 +1980,58 @@ class AdminController
|
|||||||
protected function taskRemoveFileFromBlueprint()
|
protected function taskRemoveFileFromBlueprint()
|
||||||
{
|
{
|
||||||
$uri = $this->grav['uri'];
|
$uri = $this->grav['uri'];
|
||||||
$this->taskRemoveMedia();
|
$blueprint = base64_decode($uri->param('blueprint'));
|
||||||
|
$path = base64_decode($uri->param('path'));
|
||||||
|
$proute = base64_decode($uri->param('proute'));
|
||||||
|
$type = $uri->param('type');
|
||||||
$field = $uri->param('field');
|
$field = $uri->param('field');
|
||||||
|
|
||||||
$blueprint = $uri->param('blueprint');
|
$this->taskRemoveMedia();
|
||||||
|
|
||||||
$path = base64_decode($uri->param('path'));
|
if ($type == 'pages') {
|
||||||
|
$page = $this->admin->page(true, $proute);
|
||||||
|
$keys = explode('.', preg_replace('/^header./', '', $field));
|
||||||
|
$header = (array) $page->header();
|
||||||
|
$data_path = implode('.', $keys);
|
||||||
|
$data = Utils::getDotNotation($header, $data_path);
|
||||||
|
|
||||||
$files = $this->grav['config']->get($blueprint . '.' . $field);
|
if (isset($data[$path])) {
|
||||||
|
unset($data[$path]);
|
||||||
|
Utils::setDotNotation($header, $data_path, $data);
|
||||||
|
$page->header($header);
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($files as $key => $value) {
|
$page->save();
|
||||||
if ($key == $path) {
|
} else {
|
||||||
unset($files[$key]);
|
$blueprint_prefix = $type == 'config' ? '': $type . '.';
|
||||||
|
$blueprint_name = str_replace('/blueprints', '', str_replace('config/', '', $blueprint));
|
||||||
|
$blueprint_field = $blueprint_prefix . $blueprint_name . '.' . $field;
|
||||||
|
$files = $this->grav['config']->get($blueprint_field);
|
||||||
|
|
||||||
|
foreach ($files as $key => $value) {
|
||||||
|
if ($key == $path) {
|
||||||
|
unset($files[$key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->grav['config']->set($blueprint_field, $files);
|
||||||
|
|
||||||
|
switch ($type) {
|
||||||
|
case 'config':
|
||||||
|
$data = $this->grav['config']->get($blueprint_name);
|
||||||
|
$config = $this->admin->data($blueprint, $data);
|
||||||
|
$config->save();
|
||||||
|
break;
|
||||||
|
case 'themes':
|
||||||
|
Theme::saveConfig($blueprint_name);
|
||||||
|
break;
|
||||||
|
case 'plugins':
|
||||||
|
Plugin::saveConfig($blueprint_name);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//
|
||||||
$this->grav['config']->set($blueprint . '.' . $field, $files);
|
//
|
||||||
|
|
||||||
if (substr($blueprint, 0, 7) == 'plugins') {
|
|
||||||
Plugin::saveConfig(substr($blueprint, 8));
|
|
||||||
}
|
|
||||||
if (substr($blueprint, 0, 6) == 'themes') {
|
|
||||||
Theme::saveConfig(substr($blueprint, 7));
|
|
||||||
}
|
|
||||||
|
|
||||||
$redirect = base64_decode($uri->param('redirect'));
|
$redirect = base64_decode($uri->param('redirect'));
|
||||||
$route = $this->grav['config']->get('plugins.admin.route');
|
$route = $this->grav['config']->get('plugins.admin.route');
|
||||||
|
|
||||||
|
|||||||
@@ -528,8 +528,9 @@ PLUGIN_ADMIN:
|
|||||||
ORDERING_DISABLED_BECAUSE_PARENT_SETTING_ORDER: "Parent setting order, ordering disabled"
|
ORDERING_DISABLED_BECAUSE_PARENT_SETTING_ORDER: "Parent setting order, ordering disabled"
|
||||||
ORDERING_DISABLED_BECAUSE_PAGE_NOT_VISIBLE: "Page is not visible, ordering disabled"
|
ORDERING_DISABLED_BECAUSE_PAGE_NOT_VISIBLE: "Page is not visible, ordering disabled"
|
||||||
ORDERING_DISABLED_BECAUSE_TOO_MANY_SIBLINGS: "Ordering via the admin is unsupported because there are more than 200 siblings"
|
ORDERING_DISABLED_BECAUSE_TOO_MANY_SIBLINGS: "Ordering via the admin is unsupported because there are more than 200 siblings"
|
||||||
CANNOT_ADD_MEDIA_FILES_PAGE_NOT_SAVED: "You cannot add media files until you save the page. Just click 'Save' on top"
|
CANNOT_ADD_MEDIA_FILES_PAGE_NOT_SAVED: "NOTE: You cannot add media files until you save the page. Just click 'Save' on top"
|
||||||
DROP_FILES_HERE_TO_UPLOAD: "Drop files here to upload"
|
CANNOT_ADD_FILES_PAGE_NOT_SAVED: "NOTE: Page must be saved before you can upload files to it."
|
||||||
|
DROP_FILES_HERE_TO_UPLOAD: "Drop your files here or <strong>click in this area</strong>"
|
||||||
INSERT: "Insert"
|
INSERT: "Insert"
|
||||||
UNDO: "Undo"
|
UNDO: "Undo"
|
||||||
REDO: "Redo"
|
REDO: "Redo"
|
||||||
|
|||||||
22
themes/grav/app/forms/fields/files.js
Normal file
22
themes/grav/app/forms/fields/files.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
import $ from 'jquery';
|
||||||
|
import format from '../../utils/formatbytes';
|
||||||
|
|
||||||
|
$('body').on('change', '.form-input-file > input[type="file"]', (event) => {
|
||||||
|
let input = event.target;
|
||||||
|
let files = input.files;
|
||||||
|
let container = $(input).next();
|
||||||
|
|
||||||
|
if (files.length) {
|
||||||
|
let plural = files.length > 1 ? 's' : '';
|
||||||
|
let html = '';
|
||||||
|
html += `${files.length} file${plural} selected`;
|
||||||
|
html += '<ul>';
|
||||||
|
for (let i = 0; i < files.length; i++) {
|
||||||
|
html += `<li>${files[i].name} (${format(files[i].size, 2)})</li>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
html += '</ul>';
|
||||||
|
|
||||||
|
container.html(html);
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -4,6 +4,7 @@ import CollectionsField, { Instance as CollectionsFieldInstance } from './collec
|
|||||||
import DateTimeField, { Instance as DateTimeFieldInstance } from './datetime';
|
import DateTimeField, { Instance as DateTimeFieldInstance } from './datetime';
|
||||||
import EditorField, { Instance as EditorFieldInstance } from './editor';
|
import EditorField, { Instance as EditorFieldInstance } from './editor';
|
||||||
import ColorpickerField, { Instance as ColorpickerFieldInstance } from './colorpicker';
|
import ColorpickerField, { Instance as ColorpickerFieldInstance } from './colorpicker';
|
||||||
|
import './files';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
SelectizeField: {
|
SelectizeField: {
|
||||||
|
|||||||
2
themes/grav/css-compiled/fonts.css
vendored
2
themes/grav/css-compiled/fonts.css
vendored
@@ -1 +1,3 @@
|
|||||||
@import url("//fonts.googleapis.com/css?family=Montserrat:400|Lato:300,400,700|Inconsolata:400,700");body,h5,h6,.badge,.note,.grav-mdeditor-preview,input,select,textarea,button,.selectize-input{font-family:"Lato","Helvetica","Tahoma","Geneva","Arial",sans-serif}h1,h2,h3,h4,#admin-menu li,.form-tabs>label,.label{font-family:"Montserrat","Helvetica","Tahoma","Geneva","Arial",sans-serif}code,kbd,pre,samp,body .CodeMirror{font-family:"Inconsolata","Monaco","Consolas","Lucida Console",monospace !important}
|
@import url("//fonts.googleapis.com/css?family=Montserrat:400|Lato:300,400,700|Inconsolata:400,700");body,h5,h6,.badge,.note,.grav-mdeditor-preview,input,select,textarea,button,.selectize-input{font-family:"Lato","Helvetica","Tahoma","Geneva","Arial",sans-serif}h1,h2,h3,h4,#admin-menu li,.form-tabs>label,.label{font-family:"Montserrat","Helvetica","Tahoma","Geneva","Arial",sans-serif}code,kbd,pre,samp,body .CodeMirror{font-family:"Inconsolata","Monaco","Consolas","Lucida Console",monospace !important}
|
||||||
|
|
||||||
|
/*# sourceMappingURL=fonts.css.map */
|
||||||
|
|||||||
2
themes/grav/css-compiled/nucleus.css
vendored
2
themes/grav/css-compiled/nucleus.css
vendored
File diff suppressed because one or more lines are too long
4
themes/grav/css-compiled/preset.css
vendored
4
themes/grav/css-compiled/preset.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
2
themes/grav/css-compiled/simple-fonts.css
vendored
2
themes/grav/css-compiled/simple-fonts.css
vendored
@@ -1 +1,3 @@
|
|||||||
body,h5,h6,.badge,.note,.grav-mdeditor-preview,input,select,textarea,button,.selectize-input,h1,h2,h3,h4,#admin-menu li,.form-tabs>label,.label{font-family:"Helvetica Neue", "Helvetica", "Tahoma", "Geneva", "Arial", sans-serif}code,kbd,pre,samp,body .CodeMirror{font-family:"Monaco", "Consolas", "Lucida Console", monospace}
|
body,h5,h6,.badge,.note,.grav-mdeditor-preview,input,select,textarea,button,.selectize-input,h1,h2,h3,h4,#admin-menu li,.form-tabs>label,.label{font-family:"Helvetica Neue", "Helvetica", "Tahoma", "Geneva", "Arial", sans-serif}code,kbd,pre,samp,body .CodeMirror{font-family:"Monaco", "Consolas", "Lucida Console", monospace}
|
||||||
|
|
||||||
|
/*# sourceMappingURL=simple-fonts.css.map */
|
||||||
|
|||||||
4
themes/grav/css-compiled/template.css
vendored
4
themes/grav/css-compiled/template.css
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
10
themes/grav/js/admin.min.js
vendored
10
themes/grav/js/admin.min.js
vendored
File diff suppressed because one or more lines are too long
6
themes/grav/js/vendor.min.js
vendored
6
themes/grav/js/vendor.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -640,6 +640,34 @@ form {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-input-file {
|
||||||
|
border: 4px dashed $form-border;
|
||||||
|
|
||||||
|
p {
|
||||||
|
color: $form-field-text;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
color: $primary-accent-fg;
|
||||||
|
background: $primary-accent-bg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-thumbnail-remove {
|
||||||
|
background: $button-bg;
|
||||||
|
|
||||||
|
.fa {
|
||||||
|
color: $button-text;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $button-text;
|
||||||
|
.fa {
|
||||||
|
color: $button-bg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.grav-editor-resizer {
|
.grav-editor-resizer {
|
||||||
|
|||||||
@@ -29,16 +29,33 @@ form {
|
|||||||
padding-left: 1rem;
|
padding-left: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.thumbnail {
|
.file-thumbnail-wrapper {
|
||||||
max-width: 200px;
|
|
||||||
vertical-align: top;
|
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.thumbnail-remove {
|
.file-thumbnail {
|
||||||
|
max-height: 150px;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
font-size: 1.4rem;
|
display: inline-block;
|
||||||
line-height: 1;
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-thumbnail-remove {
|
||||||
|
position: absolute;
|
||||||
|
text-align: center;
|
||||||
|
display: block;
|
||||||
|
top: 3px;
|
||||||
|
right: 3px;
|
||||||
|
width: 25px;
|
||||||
|
height: 25px;
|
||||||
|
border-radius:100%;
|
||||||
|
|
||||||
|
.fa {
|
||||||
|
font-size: 20px;
|
||||||
|
line-height: 25px;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,6 +130,40 @@ form {
|
|||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-input-file {
|
||||||
|
|
||||||
|
position: relative;
|
||||||
|
min-height: 70px;
|
||||||
|
border-radius: $form-border-radius;
|
||||||
|
|
||||||
|
ul {
|
||||||
|
margin: 1rem 0;
|
||||||
|
text-align: left;
|
||||||
|
font-size: 1rem;
|
||||||
|
border-radius: $form-border-radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
display: block;
|
||||||
|
height: 100%;
|
||||||
|
text-align: center;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0.8rem 1rem 0;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
input {
|
||||||
|
position: absolute;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
outline: none;
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
.selectize-dropdown {
|
.selectize-dropdown {
|
||||||
z-index: 100000;
|
z-index: 100000;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,42 +7,62 @@
|
|||||||
{% set uri = global.grav.uri %}
|
{% set uri = global.grav.uri %}
|
||||||
{% set files = global.files %}
|
{% set files = global.files %}
|
||||||
{% set config = global.grav.config %}
|
{% set config = global.grav.config %}
|
||||||
{% set blueprint = (global.plugin ? 'plugins.' : global.theme ? 'themes.' : '') ~ uri.basename %}
|
{% set route = global.context.route() %}
|
||||||
<img class="thumbnail" src="{{ uri.rootUrl == '/' ? '/' : uri.rootUrl ~ '/'}}{{ path }}" alt="{{ path|replace({(files.destination ~ '/'): ''}) }}" />
|
{% set type = global.context.content() ? 'pages' : global.plugin ? 'plugins' : global.theme ? 'themes' : 'config' %}
|
||||||
<a class="thumbnail-remove" href="{{ uri.addNonce(global.base_url_relative ~
|
{% set blueprint_name = global.blueprints.getFilename %}
|
||||||
'/media/' ~ base64_encode(global.base_path ~ '/' ~ path) ~
|
{% if type == 'pages' %}
|
||||||
'/task' ~ config.system.param_sep ~ 'removeFileFromBlueprint' ~
|
{% set blueprint_name = type ~ '/' ~ blueprint_name %}
|
||||||
'/blueprint' ~ config.system.param_sep ~ blueprint ~
|
{% endif %}
|
||||||
'/field' ~ config.system.param_sep ~ files.name ~
|
{% set blueprint = base64_encode(blueprint_name) %}
|
||||||
'/path' ~ config.system.param_sep ~ base64_encode(value.path) ~
|
{% set real_path = global.admin.getPagePathFromToken(path) %}
|
||||||
'/redirect' ~ config.system.param_sep ~ base64_encode(uri.path), 'admin-form', 'admin-nonce') }}">
|
|
||||||
<i class="fa fa-close"></i>
|
{% if type == 'pages' %}
|
||||||
</a>
|
{% include 'forms/fields/hidden/hidden.html.twig' with {field: {name: 'data._json.' ~ global.field.name}, value:{(value.path):value}|raw|json_encode} %}
|
||||||
|
{% endif %}
|
||||||
|
<div class="file-thumbnail-wrapper">
|
||||||
|
<img class="file-thumbnail" src="{{ uri.rootUrl == '/' ? '/' : uri.rootUrl ~ '/'}}{{ real_path }}" alt="{{ path|replace({(files.destination ~ '/'): ''}) }}" />
|
||||||
|
<a class="file-thumbnail-remove" href="{{ uri.addNonce(global.base_url_relative ~
|
||||||
|
'/media/' ~ base64_encode(global.base_path ~ '/' ~ real_path) ~
|
||||||
|
'/task' ~ config.system.param_sep ~ 'removeFileFromBlueprint' ~
|
||||||
|
'/proute' ~ config.system.param_sep ~ base64_encode(route) ~
|
||||||
|
'/blueprint' ~ config.system.param_sep ~ blueprint ~
|
||||||
|
'/type' ~ config.system.param_sep ~ type ~
|
||||||
|
'/field' ~ config.system.param_sep ~ files.name ~
|
||||||
|
'/path' ~ config.system.param_sep ~ base64_encode(value.path) ~
|
||||||
|
'/redirect' ~ config.system.param_sep ~ base64_encode(uri.path), 'admin-form', 'admin-nonce') }}">
|
||||||
|
<i class="fa fa-fw fa-close"></i>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{% block input %}
|
{% block input %}
|
||||||
|
{% set page_can_upload = exists or (type == 'page' and not exists and not (field.destination starts with '@self' or field.destination starts with 'self@')) %}
|
||||||
|
{% if type is not defined or page_can_upload %}
|
||||||
|
|
||||||
|
{% for path, file in value %}
|
||||||
|
{{ _self.preview(path, file, _context) }}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<div class="form-input-wrapper {% if field.fancy is not same as(false) %}form-input-file{% endif %} {{ field.size|default('xlarge') }}">
|
||||||
|
<input
|
||||||
|
{# required attribute structures #}
|
||||||
|
name="{{ (scope ~ field.name)|fieldName ~ (files.multiple ? '[]' : '') }}"
|
||||||
|
{% block input_attributes %}
|
||||||
|
type="file"
|
||||||
|
{% if files.multiple %}multiple="multiple"{% endif %}
|
||||||
|
{% if files.accept %}accept="{{ files.accept|join(',') }}"{% endif %}
|
||||||
|
{% if field.disabled or isDisabledToggleable %}disabled="disabled"{% endif %}
|
||||||
|
{% if field.random_name %}random="true"{% endif %}
|
||||||
|
{{ parent() }}
|
||||||
|
{% endblock %}
|
||||||
|
/>
|
||||||
|
{% if field.fancy is not same as(false) %}
|
||||||
|
<p>{{ "PLUGIN_ADMIN.DROP_FILES_HERE_TO_UPLOAD"|tu|raw }}</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
{% if not plugin and not theme %}
|
|
||||||
The "file" input field cannot be used in Pages Blueprints. It's intended to be used for Plugins and Themes blueprints.
|
|
||||||
Use the "pagemediaselect" type instead.
|
|
||||||
{% else %}
|
{% else %}
|
||||||
{% for path, file in value %}
|
<span class="note">{{ "PLUGIN_ADMIN.CANNOT_ADD_FILES_PAGE_NOT_SAVED"|tu|raw }}</span>
|
||||||
{{ _self.preview(path, file, _context) }}
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
<div class="form-input-wrapper {{ field.size }}">
|
|
||||||
<input
|
|
||||||
{# required attribute structures #}
|
|
||||||
name="{{ (scope ~ field.name)|fieldName ~ (files.multiple ? '[]' : '') }}"
|
|
||||||
{% block input_attributes %}
|
|
||||||
type="file"
|
|
||||||
{% if files.multiple %}multiple="multiple"{% endif %}
|
|
||||||
{% if files.accept %}accept="{{ files.accept|join(',') }}"{% endif %}
|
|
||||||
{% if field.disabled or isDisabledToggleable %}disabled="disabled"{% endif %}
|
|
||||||
{{ parent() }}
|
|
||||||
{% endblock %}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
@@ -14,9 +14,9 @@
|
|||||||
<div class="form-tab">
|
<div class="form-tab">
|
||||||
<div class="form-field">
|
<div class="form-field">
|
||||||
<div class="form-label">
|
<div class="form-label">
|
||||||
<label>
|
<span class="note">
|
||||||
{{ "PLUGIN_ADMIN.CANNOT_ADD_MEDIA_FILES_PAGE_NOT_SAVED"|tu }}
|
{{ "PLUGIN_ADMIN.CANNOT_ADD_MEDIA_FILES_PAGE_NOT_SAVED"|tu }}
|
||||||
</label>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{% set taxonomies = (taxonomies is null ? admin.data('site').taxonomies : taxonomies) %}
|
{% set taxonomies = (taxonomies is null ? admin.data('config/site').taxonomies : taxonomies) %}
|
||||||
{% set parentname = field.name %}
|
{% set parentname = field.name %}
|
||||||
|
|
||||||
{% for name in taxonomies %}
|
{% for name in taxonomies %}
|
||||||
|
|||||||
@@ -25,12 +25,10 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% set modular = context.modular ? 'modular_' : '' %}
|
{% set modular = context.modular ? 'modular_' : '' %}
|
||||||
|
|
||||||
{% set warn = config.plugins.admin.warnings.delete_page %}
|
{% set warn = config.plugins.admin.warnings.delete_page %}
|
||||||
|
|
||||||
{% set admin_lang = admin.session.admin_lang ?: 'en' %}
|
{% set admin_lang = admin.session.admin_lang ?: 'en' %}
|
||||||
|
|
||||||
{% set page_lang = context.language %}
|
{% set page_lang = context.language %}
|
||||||
|
{% set type = 'page' %}
|
||||||
|
|
||||||
{% block stylesheets %}
|
{% block stylesheets %}
|
||||||
{% if mode == 'edit' %}
|
{% if mode == 'edit' %}
|
||||||
|
|||||||
@@ -1,18 +1,14 @@
|
|||||||
{% set form_id = form_id ? form_id : 'blueprints' %}
|
{% set form_id = form_id ? form_id : 'blueprints' %}
|
||||||
{% set scope = scope ?: 'data.' %}
|
{% set scope = scope ?: 'data.' %}
|
||||||
|
|
||||||
{% set multipart = '' %}
|
{% if admin.findFormFields('file', blueprints.fields) %}
|
||||||
{% for field in blueprints.fields %}
|
{% set multipart = ' enctype="multipart/form-data"' %}
|
||||||
{% if field.type == 'file' %}
|
{% endif %}
|
||||||
{% set multipart = ' enctype="multipart/form-data"' %}
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
<form id="{{ form_id }}" method="post" data-grav-form="{{ form_id }}" data-grav-keepalive="true"{{ multipart|raw }}>
|
<form id="{{ form_id }}" method="post" data-grav-form="{{ form_id }}" data-grav-keepalive="true"{{ multipart|raw }}>
|
||||||
{% for field in blueprints.fields %}
|
{% for field in blueprints.fields %}
|
||||||
{% if field.type %}
|
{% if field.type %}
|
||||||
{% set value = field.name ? data.value(field.name) : data.toArray %}
|
{% set value = field.name ? data.value(field.name) : data.toArray %}
|
||||||
|
|
||||||
<div class="block block-{{ field.type }}">
|
<div class="block block-{{ field.type }}">
|
||||||
{% include ["forms/fields/#{field.type}/#{field.type}.html.twig", 'forms/fields/text/text.html.twig'] %}
|
{% include ["forms/fields/#{field.type}/#{field.type}.html.twig", 'forms/fields/text/text.html.twig'] %}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user