mirror of
https://github.com/getgrav/grav-plugin-admin.git
synced 2025-11-02 19:36:08 +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
|
||||
*/
|
||||
public function page($route = false)
|
||||
public function page($route = false, $path = null)
|
||||
{
|
||||
if (!$path) {
|
||||
$path = $this->route;
|
||||
}
|
||||
|
||||
if ($route && !$path) {
|
||||
$path = '/';
|
||||
@@ -285,36 +287,6 @@ class Admin
|
||||
$post = isset($_POST['data']) ? $_POST['data'] : [];
|
||||
}
|
||||
|
||||
switch ($type) {
|
||||
case 'configuration':
|
||||
case 'system':
|
||||
$type = 'system';
|
||||
$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':
|
||||
case 'site':
|
||||
$type = 'site';
|
||||
$blueprints = $this->blueprints("config/{$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':
|
||||
$data[$type] = null;
|
||||
break;
|
||||
|
||||
default:
|
||||
/** @var UniformResourceLocator $locator */
|
||||
$locator = $this->grav['locator'];
|
||||
$filename = $locator->findResource("config://{$type}.yaml", true, true);
|
||||
@@ -367,7 +339,6 @@ class Admin
|
||||
} else {
|
||||
throw new \RuntimeException("Data type '{$type}' doesn't exist!");
|
||||
}
|
||||
}
|
||||
|
||||
return $data[$type];
|
||||
}
|
||||
@@ -1180,4 +1151,63 @@ class Admin
|
||||
{
|
||||
$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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -123,8 +123,7 @@ class AdminController
|
||||
$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') {
|
||||
|
||||
$message = sprintf($this->admin->translate('PLUGIN_ADMIN.FILE_TOO_LARGE', null, true), ini_get('post_max_size'));
|
||||
@@ -1265,70 +1264,56 @@ class AdminController
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $field
|
||||
* @return array
|
||||
*/
|
||||
private function cleanFilesData()
|
||||
private function cleanFilesData($field)
|
||||
{
|
||||
/** @var Page $page */
|
||||
$page = null;
|
||||
$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'];
|
||||
|
||||
foreach ((array)$file['error'] as $index => $errors) {
|
||||
$errors = !is_array($errors) ? [$errors] : $errors;
|
||||
$errors = (array) Utils::getDotNotation($file['error'], $field['name']);
|
||||
|
||||
foreach($errors as $multiple_index => $error) {
|
||||
foreach ($errors as $index => $error) {
|
||||
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];
|
||||
|
||||
$fieldname = $field['name'];
|
||||
|
||||
// Deal with multiple files
|
||||
if (isset($field['multiple']) && $field['multiple'] == true) {
|
||||
$fieldname = $fieldname . ".$index";
|
||||
}
|
||||
|
||||
$destination = Folder::getRelativePath(rtrim($blueprint[$index]['destination'], '/'));
|
||||
$tmp_name = Utils::getDotNotation($file['tmp_name'], $fieldname);
|
||||
$name = Utils::getDotNotation($file['name'], $fieldname);
|
||||
$type = Utils::getDotNotation($file['type'], $fieldname);
|
||||
$size = Utils::getDotNotation($file['size'], $fieldname);
|
||||
|
||||
if (!$this->match_in_array($type, $blueprint[$index]['accept'])) {
|
||||
$original_destination = null ;
|
||||
$destination = Folder::getRelativePath(rtrim($field['destination'], '/'));
|
||||
|
||||
if (!$this->match_in_array($type, $field['accept'])) {
|
||||
throw new \RuntimeException('File "' . $name . '" is not an accepted MIME type.');
|
||||
}
|
||||
|
||||
if (Utils::startsWith($destination, '@page:')) {
|
||||
$parts = explode(':', $destination);
|
||||
$route = $parts[1];
|
||||
$page = $this->grav['page']->find($route);
|
||||
|
||||
if (!$page) {
|
||||
throw new \RuntimeException('Unable to upload file to destination. Page route not found.');
|
||||
if (isset($field['random_name']) && $field['random_name'] === true) {
|
||||
$path_parts = pathinfo($name);
|
||||
$name = Utils::generateRandomString(15) . '.' . $path_parts['extension'];
|
||||
}
|
||||
|
||||
$destination = $page->relativePagePath();
|
||||
} else {
|
||||
if ($destination == '@self') {
|
||||
$page = $this->admin->page(true);
|
||||
$destination = $page->relativePagePath();
|
||||
} else {
|
||||
Folder::mkdir($destination);
|
||||
}
|
||||
$resolved_destination = $this->admin->getPagePathFromToken($destination);
|
||||
$upload_path = $resolved_destination . '/' . $name;
|
||||
|
||||
// Create dir if need be
|
||||
if (!is_dir($resolved_destination)) {
|
||||
Folder::mkdir($resolved_destination);
|
||||
}
|
||||
|
||||
if (move_uploaded_file($tmp_name, "$destination/$name")) {
|
||||
$path = $page ? $this->grav['uri']->convertUrl($page, $page->route() . '/' . $name) : $destination . '/' . $name;
|
||||
if (move_uploaded_file($tmp_name, $upload_path)) {
|
||||
$path = $destination . '/' . $name;
|
||||
$fileData = [
|
||||
'name' => $name,
|
||||
'path' => $path,
|
||||
@@ -1338,14 +1323,15 @@ class AdminController
|
||||
'route' => $page ? $path : null
|
||||
];
|
||||
|
||||
$cleanFiles[$index][$path] = $fileData;
|
||||
$cleanFiles[$field['name']][$path] = $fileData;
|
||||
} else {
|
||||
throw new \RuntimeException("Unable to upload file(s) to $destination/$name");
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
if ($error != UPLOAD_ERR_NO_FILE) {
|
||||
throw new \RuntimeException("Unable to upload file(s) - Error: ".$file['name'][$index].": ".$this->upload_errors[$error]);
|
||||
}
|
||||
throw new \RuntimeException("Unable to upload file(s) - Error: " . $field['name'] . ": " . $this->upload_errors[$error]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1383,15 +1369,59 @@ class AdminController
|
||||
return $obj;
|
||||
}
|
||||
|
||||
$cleanFiles = $this->cleanFilesData();
|
||||
$blueprints = $obj->blueprints();
|
||||
|
||||
foreach ($cleanFiles as $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;
|
||||
}
|
||||
|
||||
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)
|
||||
*
|
||||
@@ -1493,8 +1523,12 @@ class AdminController
|
||||
|
||||
// Find new parent page in order to build the path.
|
||||
$route = !isset($data['route']) ? dirname($this->admin->route) : $data['route'];
|
||||
|
||||
/** @var Page $obj */
|
||||
$obj = $this->admin->page(true);
|
||||
|
||||
|
||||
|
||||
// Ensure route is prefixed with a forward slash.
|
||||
$route = '/' . ltrim($route, '/');
|
||||
|
||||
@@ -1530,6 +1564,8 @@ class AdminController
|
||||
$obj = $obj->move($parent);
|
||||
$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.
|
||||
$obj->slug($original_slug);
|
||||
|
||||
@@ -1883,11 +1919,11 @@ class AdminController
|
||||
/**
|
||||
* Determine if the user can edit media
|
||||
*
|
||||
* @param string $type
|
||||
* @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'])) {
|
||||
return false;
|
||||
}
|
||||
@@ -1907,6 +1943,7 @@ class AdminController
|
||||
}
|
||||
|
||||
$filename = base64_decode($this->route);
|
||||
|
||||
$file = File::instance($filename);
|
||||
$resultRemoveMedia = false;
|
||||
$resultRemoveMediaMeta = true;
|
||||
@@ -1943,15 +1980,33 @@ class AdminController
|
||||
protected function taskRemoveFileFromBlueprint()
|
||||
{
|
||||
$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');
|
||||
|
||||
$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);
|
||||
}
|
||||
|
||||
$page->save();
|
||||
} else {
|
||||
$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) {
|
||||
@@ -1959,15 +2014,24 @@ class AdminController
|
||||
}
|
||||
}
|
||||
|
||||
$this->grav['config']->set($blueprint . '.' . $field, $files);
|
||||
$this->grav['config']->set($blueprint_field, $files);
|
||||
|
||||
if (substr($blueprint, 0, 7) == 'plugins') {
|
||||
Plugin::saveConfig(substr($blueprint, 8));
|
||||
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;
|
||||
}
|
||||
if (substr($blueprint, 0, 6) == 'themes') {
|
||||
Theme::saveConfig(substr($blueprint, 7));
|
||||
}
|
||||
|
||||
//
|
||||
//
|
||||
$redirect = base64_decode($uri->param('redirect'));
|
||||
$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_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"
|
||||
CANNOT_ADD_MEDIA_FILES_PAGE_NOT_SAVED: "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_MEDIA_FILES_PAGE_NOT_SAVED: "NOTE: You cannot add media files until you save the page. Just click 'Save' on top"
|
||||
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"
|
||||
UNDO: "Undo"
|
||||
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 EditorField, { Instance as EditorFieldInstance } from './editor';
|
||||
import ColorpickerField, { Instance as ColorpickerFieldInstance } from './colorpicker';
|
||||
import './files';
|
||||
|
||||
export default {
|
||||
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}
|
||||
|
||||
/*# 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}
|
||||
|
||||
/*# 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 {
|
||||
|
||||
@@ -29,16 +29,33 @@ form {
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
.thumbnail {
|
||||
max-width: 200px;
|
||||
vertical-align: top;
|
||||
.file-thumbnail-wrapper {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.thumbnail-remove {
|
||||
.file-thumbnail {
|
||||
max-height: 150px;
|
||||
vertical-align: top;
|
||||
font-size: 1.4rem;
|
||||
line-height: 1;
|
||||
display: inline-block;
|
||||
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;
|
||||
}
|
||||
|
||||
.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 {
|
||||
z-index: 100000;
|
||||
}
|
||||
|
||||
@@ -7,31 +7,44 @@
|
||||
{% set uri = global.grav.uri %}
|
||||
{% set files = global.files %}
|
||||
{% set config = global.grav.config %}
|
||||
{% set blueprint = (global.plugin ? 'plugins.' : global.theme ? 'themes.' : '') ~ uri.basename %}
|
||||
<img class="thumbnail" src="{{ uri.rootUrl == '/' ? '/' : uri.rootUrl ~ '/'}}{{ path }}" alt="{{ path|replace({(files.destination ~ '/'): ''}) }}" />
|
||||
<a class="thumbnail-remove" href="{{ uri.addNonce(global.base_url_relative ~
|
||||
'/media/' ~ base64_encode(global.base_path ~ '/' ~ path) ~
|
||||
{% set route = global.context.route() %}
|
||||
{% set type = global.context.content() ? 'pages' : global.plugin ? 'plugins' : global.theme ? 'themes' : 'config' %}
|
||||
{% set blueprint_name = global.blueprints.getFilename %}
|
||||
{% if type == 'pages' %}
|
||||
{% set blueprint_name = type ~ '/' ~ blueprint_name %}
|
||||
{% endif %}
|
||||
{% set blueprint = base64_encode(blueprint_name) %}
|
||||
{% set real_path = global.admin.getPagePathFromToken(path) %}
|
||||
|
||||
{% if type == 'pages' %}
|
||||
{% 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-close"></i>
|
||||
<i class="fa fa-fw fa-close"></i>
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% 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 %}
|
||||
|
||||
{% 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 %}
|
||||
{% for path, file in value %}
|
||||
{{ _self.preview(path, file, _context) }}
|
||||
{% endfor %}
|
||||
|
||||
<div class="form-input-wrapper {{ field.size }}">
|
||||
<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 ? '[]' : '') }}"
|
||||
@@ -40,9 +53,16 @@
|
||||
{% 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>
|
||||
|
||||
{% else %}
|
||||
<span class="note">{{ "PLUGIN_ADMIN.CANNOT_ADD_FILES_PAGE_NOT_SAVED"|tu|raw }}</span>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
<div class="form-tab">
|
||||
<div class="form-field">
|
||||
<div class="form-label">
|
||||
<label>
|
||||
<span class="note">
|
||||
{{ "PLUGIN_ADMIN.CANNOT_ADD_MEDIA_FILES_PAGE_NOT_SAVED"|tu }}
|
||||
</label>
|
||||
</span>
|
||||
</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 %}
|
||||
|
||||
{% for name in taxonomies %}
|
||||
|
||||
@@ -25,12 +25,10 @@
|
||||
{% endif %}
|
||||
|
||||
{% set modular = context.modular ? 'modular_' : '' %}
|
||||
|
||||
{% set warn = config.plugins.admin.warnings.delete_page %}
|
||||
|
||||
{% set admin_lang = admin.session.admin_lang ?: 'en' %}
|
||||
|
||||
{% set page_lang = context.language %}
|
||||
{% set type = 'page' %}
|
||||
|
||||
{% block stylesheets %}
|
||||
{% if mode == 'edit' %}
|
||||
|
||||
@@ -1,18 +1,14 @@
|
||||
{% set form_id = form_id ? form_id : 'blueprints' %}
|
||||
{% set scope = scope ?: 'data.' %}
|
||||
|
||||
{% set multipart = '' %}
|
||||
{% for field in blueprints.fields %}
|
||||
{% if field.type == 'file' %}
|
||||
{% if admin.findFormFields('file', blueprints.fields) %}
|
||||
{% 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 }}>
|
||||
{% for field in blueprints.fields %}
|
||||
{% if field.type %}
|
||||
{% set value = field.name ? data.value(field.name) : data.toArray %}
|
||||
|
||||
<div class="block block-{{ field.type }}">
|
||||
{% include ["forms/fields/#{field.type}/#{field.type}.html.twig", 'forms/fields/text/text.html.twig'] %}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user