mirror of
https://github.com/getgrav/grav-plugin-admin.git
synced 2025-10-27 16:26:32 +01:00
Introduce the mediapicker field (#1125)
Added a new `mediapicker` form field which allows to select a media from any page + Provided an option to control how parent select field displays
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
# v1.5.0-rc.3
|
||||
## 05/xx/2017
|
||||
|
||||
1. [](#new)
|
||||
* Added a new `mediapicker` form field which allows to select a media from any page [#1125](https://github.com/getgrav/grav-plugin-admin/pull/1125)
|
||||
1. [](#improved)
|
||||
* Various form styling improvements
|
||||
* Provided an option to control how parent select field displays
|
||||
|
||||
# v1.5.0-rc.2
|
||||
## 05/22/2017
|
||||
@@ -14,7 +17,7 @@
|
||||
* Use new unified `Utils::getPagePathFromToken()` method rather
|
||||
1. [](#bugfix)
|
||||
* Fix for undefined `include_metadata` error
|
||||
|
||||
|
||||
|
||||
# v1.5.0-rc.1
|
||||
## 05/16/2017
|
||||
|
||||
21
admin.php
21
admin.php
@@ -84,7 +84,11 @@ class AdminPlugin extends Plugin
|
||||
{
|
||||
if (!Grav::instance()['config']->get('plugins.admin-pro.enabled')) {
|
||||
return [
|
||||
'onPluginsInitialized' => [['setup', 100000], ['onPluginsInitialized', 1001]],
|
||||
'onPluginsInitialized' => [
|
||||
['setup', 100000],
|
||||
['onPluginsInitialized', 1001]
|
||||
],
|
||||
'onPageInitialized' => ['onPageInitialized', 0],
|
||||
'onShutdown' => ['onShutdown', 1000],
|
||||
'onFormProcessed' => ['onFormProcessed', 0],
|
||||
'onAdminDashboard' => ['onAdminDashboard', 0],
|
||||
@@ -95,6 +99,17 @@ class AdminPlugin extends Plugin
|
||||
return [];
|
||||
}
|
||||
|
||||
public function onPageInitialized()
|
||||
{
|
||||
$page = $this->grav['page'];
|
||||
|
||||
$template = $this->grav['uri']->param('tmpl');
|
||||
|
||||
if ($template) {
|
||||
$page->template($template);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the admin path matches, initialize the Login plugin configuration and set the admin
|
||||
* as active.
|
||||
@@ -516,6 +531,10 @@ class AdminPlugin extends Plugin
|
||||
$this->popularity->trackHit();
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->grav['admin']->shouldLoadAdditionalFilesInBackground()) {
|
||||
$this->grav['admin']->loadAdditionalFilesInBackground();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -17,6 +17,8 @@ widgets:
|
||||
dashboard-notifications: true
|
||||
dashboard-feed: true
|
||||
dashboard-pages: true
|
||||
pages:
|
||||
show_parents: both
|
||||
session:
|
||||
timeout: 1800
|
||||
warnings:
|
||||
|
||||
@@ -136,6 +136,16 @@ form:
|
||||
frontend_tab: Separate tab (always the same)
|
||||
_self: Current tab
|
||||
|
||||
pages.show_parents:
|
||||
type: select
|
||||
size: medium
|
||||
label: Parent dropdown
|
||||
highlight: 1
|
||||
options:
|
||||
both: Show slug and folder
|
||||
folder: Show folder
|
||||
fullpath: Show fullpath
|
||||
|
||||
google_fonts:
|
||||
type: toggle
|
||||
label: Use Google Fonts
|
||||
|
||||
@@ -24,6 +24,8 @@ form:
|
||||
.mime:
|
||||
type: text
|
||||
label: PLUGIN_ADMIN.MIME_TYPE
|
||||
validate:
|
||||
type: lower
|
||||
.image:
|
||||
type: textarea
|
||||
yaml: true
|
||||
|
||||
@@ -32,6 +32,8 @@ define('LOGIN_REDIRECT_COOKIE', 'grav-login-redirect');
|
||||
|
||||
class Admin
|
||||
{
|
||||
const MEDIA_PAGINATION_INTERVAL = 20;
|
||||
|
||||
/**
|
||||
* @var Grav
|
||||
*/
|
||||
@@ -95,6 +97,16 @@ class Admin
|
||||
*/
|
||||
protected $permissions;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $load_additional_files_in_background = false;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
protected $loading_additional_files_in_background = false;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
@@ -532,6 +544,28 @@ class Admin
|
||||
?: $this->grav['locator']->findResource("config://{$type}.yaml", true, true);
|
||||
$file = CompiledYamlFile::instance($filename);
|
||||
$obj->file($file);
|
||||
$data[$type] = $obj;
|
||||
} elseif (preg_match('|media-manager/|', $type)) {
|
||||
$filename = base64_decode(preg_replace('|media-manager/|', '', $type));
|
||||
|
||||
$file = File::instance($filename);
|
||||
|
||||
$obj = new \StdClass();
|
||||
$obj->title = $file->basename();
|
||||
$obj->path = $file->filename();
|
||||
$obj->file = $file;
|
||||
$obj->page = $this->grav['pages']->get(dirname($obj->path));
|
||||
|
||||
$filename = pathinfo($obj->title)['filename'];
|
||||
$filename = str_replace(['@3x', '@2x'], '', $filename);
|
||||
if (isset(pathinfo($obj->title)['extension'])) {
|
||||
$filename .= '.' . pathinfo($obj->title)['extension'];
|
||||
}
|
||||
|
||||
if ($obj->page && isset($obj->page->media()[$filename])) {
|
||||
$obj->metadata = new Data($obj->page->media()[$filename]->metadata());
|
||||
}
|
||||
|
||||
$data[$type] = $obj;
|
||||
} else {
|
||||
throw new \RuntimeException("Data type '{$type}' doesn't exist!");
|
||||
@@ -540,6 +574,17 @@ class Admin
|
||||
return $data[$type];
|
||||
}
|
||||
|
||||
protected function hasErrorMessage()
|
||||
{
|
||||
$msgs = $this->grav['messages']->all();
|
||||
foreach ($msgs as $msg) {
|
||||
if (isset($msg['scope']) && $msg['scope'] === 'error') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns blueprints for the given type.
|
||||
*
|
||||
@@ -1373,4 +1418,260 @@ class Admin
|
||||
return [$this->base, $this->location, $this->route];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the files list
|
||||
*
|
||||
* @todo allow pagination
|
||||
* @return array
|
||||
*/
|
||||
public function files($filtered = true, $page_index = 0)
|
||||
{
|
||||
$param_type = $this->grav['uri']->param('type');
|
||||
$param_date = $this->grav['uri']->param('date');
|
||||
$param_page = $this->grav['uri']->param('page');
|
||||
$param_page = str_replace('\\', '/', $param_page);
|
||||
|
||||
$files_cache_key = 'media-manager-files';
|
||||
|
||||
if ($param_type) {
|
||||
$files_cache_key .= "-{$param_type}";
|
||||
}
|
||||
if ($param_date) {
|
||||
$files_cache_key .= "-{$param_date}";
|
||||
}
|
||||
if ($param_page) {
|
||||
$files_cache_key .= "-{$param_page}";
|
||||
}
|
||||
|
||||
$page_files = null;
|
||||
|
||||
$cache_enabled = $this->grav['config']->get('plugins.admin.cache_enabled');
|
||||
if (!$cache_enabled) {
|
||||
$this->grav['cache']->setEnabled(true);
|
||||
}
|
||||
|
||||
$page_files = $this->grav['cache']->fetch(md5($files_cache_key));
|
||||
|
||||
if (!$cache_enabled) {
|
||||
$this->grav['cache']->setEnabled(false);
|
||||
}
|
||||
|
||||
if (!$page_files) {
|
||||
$page_files = [];
|
||||
$pages = $this->grav['pages'];
|
||||
|
||||
if ($param_page) {
|
||||
$page = $pages->dispatch($param_page);
|
||||
|
||||
$page_files = $this->getFiles('images', $page, $page_files, $filtered);
|
||||
$page_files = $this->getFiles('videos', $page, $page_files, $filtered);
|
||||
$page_files = $this->getFiles('audios', $page, $page_files, $filtered);
|
||||
$page_files = $this->getFiles('files', $page, $page_files, $filtered);
|
||||
} else {
|
||||
$allPages = $pages->all();
|
||||
|
||||
if ($allPages) foreach ($allPages as $page) {
|
||||
$page_files = $this->getFiles('images', $page, $page_files, $filtered);
|
||||
$page_files = $this->getFiles('videos', $page, $page_files, $filtered);
|
||||
$page_files = $this->getFiles('audios', $page, $page_files, $filtered);
|
||||
$page_files = $this->getFiles('files', $page, $page_files, $filtered);
|
||||
}
|
||||
}
|
||||
|
||||
if (count($page_files) >= self::MEDIA_PAGINATION_INTERVAL) {
|
||||
$this->shouldLoadAdditionalFilesInBackground(true);
|
||||
}
|
||||
|
||||
if (!$cache_enabled) {
|
||||
$this->grav['cache']->setEnabled(true);
|
||||
}
|
||||
$this->grav['cache']->save(md5($files_cache_key), $page_files, 600); //cache for 10 minutes
|
||||
if (!$cache_enabled) {
|
||||
$this->grav['cache']->setEnabled(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (count($page_files) >= self::MEDIA_PAGINATION_INTERVAL) {
|
||||
$page_files = array_slice($page_files, $page_index * self::MEDIA_PAGINATION_INTERVAL, self::MEDIA_PAGINATION_INTERVAL);
|
||||
}
|
||||
|
||||
return $page_files;
|
||||
}
|
||||
|
||||
public function shouldLoadAdditionalFilesInBackground($status = null)
|
||||
{
|
||||
if ($status) {
|
||||
$this->load_additional_files_in_background = true;
|
||||
}
|
||||
|
||||
return $this->load_additional_files_in_background;
|
||||
}
|
||||
|
||||
public function loadAdditionalFilesInBackground($status = null)
|
||||
{
|
||||
if (!$this->loading_additional_files_in_background) {
|
||||
$this->loading_additional_files_in_background = true;
|
||||
$this->files(false, false);
|
||||
$this->shouldLoadAdditionalFilesInBackground(false);
|
||||
$this->loading_additional_files_in_background = false;
|
||||
}
|
||||
}
|
||||
|
||||
private function getFiles($type, $page, $page_files, $filtered)
|
||||
{
|
||||
$page_files = $this->getMediaOfType($type, $page, $page_files);
|
||||
|
||||
if ($filtered) {
|
||||
$page_files = $this->filterByType($page_files);
|
||||
$page_files = $this->filterByDate($page_files);
|
||||
}
|
||||
|
||||
return $page_files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all the media of a type ('images' | 'audios' | 'videos' | 'files')
|
||||
*
|
||||
* @param string $type
|
||||
* @param Page\Page $page
|
||||
* @param array $files
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function getMediaOfType($type, $page, $page_files) {
|
||||
if ($page) {
|
||||
|
||||
// $path = $page->path();
|
||||
$media = $page->media();
|
||||
$mediaOfType = $media->$type();
|
||||
|
||||
foreach($mediaOfType as $title => $file) {
|
||||
$page_files[] = [
|
||||
'title' => $title,
|
||||
'type' => $type,
|
||||
'page_route' => $page->route(),
|
||||
'file' => $file->higherQualityAlternative()
|
||||
];
|
||||
}
|
||||
|
||||
return $page_files;
|
||||
} else {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter media by type
|
||||
*
|
||||
* @param array $filesFiltered
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function filterByType($filesFiltered)
|
||||
{
|
||||
$filter_type = $this->grav['uri']->param('type');
|
||||
if (!$filter_type) {
|
||||
return $filesFiltered;
|
||||
}
|
||||
|
||||
$filesFiltered = array_filter($filesFiltered, function ($file) use ($filter_type) {
|
||||
return $file['type'] == $filter_type;
|
||||
});
|
||||
|
||||
return $filesFiltered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter media by date
|
||||
*
|
||||
* @param array $filesFiltered
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function filterByDate($filesFiltered)
|
||||
{
|
||||
$filter_date = $this->grav['uri']->param('date');
|
||||
if (!$filter_date) {
|
||||
return $filesFiltered;
|
||||
}
|
||||
|
||||
$year = substr($filter_date, 0, 4);
|
||||
$month = substr($filter_date, 5, 2);
|
||||
|
||||
$filesFilteredByDate = [];
|
||||
|
||||
foreach($filesFiltered as $file) {
|
||||
$filedate = $this->fileDate($file['file']);
|
||||
$fileYear = $filedate->format('Y');
|
||||
$fileMonth = $filedate->format('m');
|
||||
|
||||
if ($fileYear == $year && $fileMonth == $month) {
|
||||
$filesFilteredByDate[] = $file;
|
||||
}
|
||||
}
|
||||
|
||||
return $filesFilteredByDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the DateTime object representation of a file modified date
|
||||
*
|
||||
* @param File $file
|
||||
*
|
||||
* @return DateTime
|
||||
*/
|
||||
private function fileDate($file) {
|
||||
$datetime = new \DateTime();
|
||||
$datetime->setTimestamp($file->toArray()['modified']);
|
||||
return $datetime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the files dates list to be used in the Media Files filter
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function filesDates()
|
||||
{
|
||||
$files = $this->files(false);
|
||||
$dates = [];
|
||||
|
||||
foreach ($files as $file) {
|
||||
$datetime = $this->fileDate($file['file']);
|
||||
$year = $datetime->format('Y');
|
||||
$month = $datetime->format('m');
|
||||
|
||||
if (!isset($dates[$year])) {
|
||||
$dates[$year] = [];
|
||||
}
|
||||
|
||||
if (!isset($dates[$year][$month])) {
|
||||
$dates[$year][$month] = 1;
|
||||
} else {
|
||||
$dates[$year][$month]++;
|
||||
}
|
||||
}
|
||||
|
||||
return $dates;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the pages list to be used in the Media Files filter
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function pages()
|
||||
{
|
||||
$pages = $this->grav['pages']->all();
|
||||
|
||||
$pagesWithFiles = [];
|
||||
if ($pages) foreach ($pages as $page) {
|
||||
if (count($page->media()->all())) {
|
||||
$pagesWithFiles[] = $page;
|
||||
}
|
||||
}
|
||||
|
||||
return $pagesWithFiles;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -924,7 +924,7 @@ class AdminBaseController
|
||||
$fileParts = pathinfo($filename);
|
||||
|
||||
foreach (scandir($fileParts['dirname']) as $file) {
|
||||
$regex_pattern = "/" . preg_quote($fileParts['filename']) . "@\d+x\." . $fileParts['extension'] . "$|" . preg_quote($fileParts['basename']) . ".meta.yaml$/";
|
||||
$regex_pattern = "/" . preg_quote($fileParts['filename']) . "@\d+x\." . $fileParts['extension'] . "(?:\.meta\.yaml)?$|" . preg_quote($fileParts['basename']) . "\.meta\.yaml$/";
|
||||
if (preg_match($regex_pattern, $file)) {
|
||||
$path = $fileParts['dirname'] . '/' . $file;
|
||||
@unlink($path);
|
||||
|
||||
@@ -1520,8 +1520,21 @@ class AdminController extends AdminBaseController
|
||||
$media_list = [];
|
||||
$media = new Media($page->path());
|
||||
|
||||
$include_metadata = Grav::instance()['config']->get('system.media.auto_metadata_exif', false);
|
||||
|
||||
foreach ($media->all() as $name => $medium) {
|
||||
$media_list[$name] = ['url' => $medium->display($medium->get('extension') === 'svg' ? 'source' : 'thumbnail')->cropZoom(400, 300)->url(), 'size' => $medium->get('size')];
|
||||
|
||||
$metadata = [];
|
||||
|
||||
if ($include_metadata) {
|
||||
$img_metadata = $medium->metadata();
|
||||
if ($img_metadata) {
|
||||
$metadata = $img_metadata;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$media_list[$name] = ['url' => $medium->display($medium->get('extension') === 'svg' ? 'source' : 'thumbnail')->cropZoom(400, 300)->url(), 'size' => $medium->get('size'), 'metadata' => $metadata];
|
||||
}
|
||||
$this->admin->json_response = ['status' => 'success', 'results' => $media_list];
|
||||
|
||||
@@ -1631,13 +1644,28 @@ class AdminController extends AdminBaseController
|
||||
}
|
||||
|
||||
// reinitialize media to trigger availability
|
||||
$page->media();
|
||||
$media = $page->media();
|
||||
|
||||
// Add metadata if needed
|
||||
$include_metadata = Grav::instance()['config']->get('system.media.auto_metadata_exif', false);
|
||||
$filename = $fileParts['basename'];
|
||||
$filename = str_replace(['@3x', '@2x'], '', $filename);
|
||||
|
||||
$metadata = [];
|
||||
|
||||
if ($include_metadata && isset($media[$filename])) {
|
||||
$img_metadata = $media[$filename]->metadata();
|
||||
if ($img_metadata) {
|
||||
$metadata = $img_metadata;
|
||||
}
|
||||
}
|
||||
|
||||
$this->grav->fireEvent('onAdminAfterAddMedia', new Event(['page' => $page]));
|
||||
|
||||
$this->admin->json_response = [
|
||||
'status' => 'success',
|
||||
'message' => $this->admin->translate('PLUGIN_ADMIN.FILE_UPLOADED_SUCCESSFULLY')
|
||||
'message' => $this->admin->translate('PLUGIN_ADMIN.FILE_UPLOADED_SUCCESSFULLY'),
|
||||
'metadata' => $metadata,
|
||||
];
|
||||
|
||||
return true;
|
||||
@@ -1697,7 +1725,7 @@ class AdminController extends AdminBaseController
|
||||
|
||||
// Remove Extra Files
|
||||
foreach (scandir($page->path()) as $file) {
|
||||
if (preg_match("/{$fileParts['filename']}@\d+x\.{$fileParts['extension']}$|{$filename}.meta.yaml$/", $file)) {
|
||||
if (preg_match("/{$fileParts['filename']}@\d+x\.{$fileParts['extension']}(?:\.meta\.yaml)?$|{$filename}\.meta\.yaml$/", $file)) {
|
||||
$result = unlink($page->path() . '/' . $file);
|
||||
|
||||
if (!$result) {
|
||||
|
||||
@@ -6,6 +6,7 @@ import DateTimeField, { Instance as DateTimeFieldInstance } from './datetime';
|
||||
import EditorField, { Instance as EditorFieldInstance } from './editor';
|
||||
import ColorpickerField, { Instance as ColorpickerFieldInstance } from './colorpicker';
|
||||
import FilesField, { Instance as FilesFieldInstance } from './files';
|
||||
import MediapickerField, { Instance as MediapickerInstance } from './mediapicker';
|
||||
import SelectUniqueField, { Instance as SelectUniqueInstance } from './selectunique';
|
||||
|
||||
export default {
|
||||
@@ -44,6 +45,10 @@ export default {
|
||||
SelectUniqueField: {
|
||||
SelectUniqueField,
|
||||
Instance: SelectUniqueInstance
|
||||
},
|
||||
MediapickerField: {
|
||||
MediapickerField,
|
||||
Instance: MediapickerInstance
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
49
themes/grav/app/forms/fields/mediapicker.js
Normal file
49
themes/grav/app/forms/fields/mediapicker.js
Normal file
@@ -0,0 +1,49 @@
|
||||
import $ from 'jquery';
|
||||
import Scrollbar from '../../utils/scrollbar';
|
||||
|
||||
$(function() {
|
||||
var modal = '';
|
||||
|
||||
var treescroll = new Scrollbar('.pages-list-container .mediapicker-scroll', { autoshow: true });
|
||||
var thumbscroll = new Scrollbar('.thumbs-list-container .mediapicker-scroll', { autoshow: true });
|
||||
|
||||
// Thumb Resizer
|
||||
$('.media-container .media-range').on('input change', function() {
|
||||
var cards = $('.media-container div.card-item');
|
||||
var width = $(this).val() + 'px';
|
||||
cards.each(function() {
|
||||
$(this).css('width', width);
|
||||
});
|
||||
});
|
||||
|
||||
$(document).on('opened', '.remodal', function() {
|
||||
setTimeout(function() {
|
||||
treescroll.update();
|
||||
thumbscroll.update();
|
||||
}, 10);
|
||||
});
|
||||
|
||||
$('body').on('click', '[data-mediapicker-modal-trigger]', function() {
|
||||
var modal_identifier = $(this).data('grav-mediapicker-unique-identifier');
|
||||
var modal_element = $('body').find('[data-remodal-unique-identifier="' + modal_identifier + '"]');
|
||||
modal = $.remodal.lookup[modal_element.data('remodal')];
|
||||
modal.open();
|
||||
|
||||
// load all media
|
||||
modal_element.find('.js__files').trigger('fillView');
|
||||
});
|
||||
|
||||
/* handle media modal click actions */
|
||||
$('body').on('click', '[data-remodal-mediapicker] .media-container.in-modal .admin-media-details a', (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
var val = $(event.target).parents('.js__media-element').data('file-url');
|
||||
var string = val.replace(/ /g, '%20');
|
||||
|
||||
var modal_identifier = $(event.target).parents('[data-remodal-mediapicker]').data('remodal-unique-identifier');
|
||||
$('body').find('[data-grav-mediapicker-unique-identifier="' + modal_identifier + '"] input').val(string);
|
||||
|
||||
modal.close();
|
||||
});
|
||||
});
|
||||
@@ -8,6 +8,7 @@ import Forms from './forms';
|
||||
import Scrollbar, { Instance as contentScrollbar } from './utils/scrollbar';
|
||||
import './plugins';
|
||||
import './themes';
|
||||
import { Filter as MediaFilter, Instance as MediaFilterInstance} from './media';
|
||||
|
||||
// bootstrap jQuery extensions
|
||||
import 'bootstrap/js/transition';
|
||||
@@ -65,5 +66,9 @@ export default {
|
||||
Sidebar: {
|
||||
Sidebar,
|
||||
Instance: sidebar
|
||||
},
|
||||
MediaFilter: {
|
||||
MediaFilter,
|
||||
Instance: MediaFilterInstance
|
||||
}
|
||||
};
|
||||
|
||||
219
themes/grav/app/media/index.js
Normal file
219
themes/grav/app/media/index.js
Normal file
@@ -0,0 +1,219 @@
|
||||
import $ from 'jquery';
|
||||
import { config, uri_params } from 'grav-config';
|
||||
import request from '../utils/request';
|
||||
|
||||
export default class Filter {
|
||||
constructor() {
|
||||
this.URI = `${config.base_url_relative}/media-manager/`;
|
||||
}
|
||||
|
||||
filter(name, value) {
|
||||
let filtered = [];
|
||||
let keys = Object.keys(uri_params);
|
||||
if (!~keys.indexOf(name)) { keys.push(name); }
|
||||
|
||||
keys.forEach((key) => {
|
||||
let filter = Filter.cleanValue(key === name ? value : uri_params[key]);
|
||||
if (filter !== '*') {
|
||||
filtered.push(`${key}${config.param_sep}${filter}`);
|
||||
}
|
||||
});
|
||||
|
||||
global.location = this.URI + filtered.join('/');
|
||||
}
|
||||
|
||||
static cleanValue(value) {
|
||||
return encodeURIComponent(value.replace('/', '\\'));
|
||||
}
|
||||
}
|
||||
|
||||
export let Instance = new Filter();
|
||||
var isLoading = false;
|
||||
|
||||
var filters = {};
|
||||
var global_index = 1;
|
||||
var files_ended = false;
|
||||
const MEDIA_PAGINATION_INTERVAL = 20;
|
||||
|
||||
/* handle changing file type / date filter */
|
||||
$('body').on('change', '.thumbs-list-container select.filter', (event) => {
|
||||
let target = $(event.currentTarget);
|
||||
let filterName = target.data('name');
|
||||
let filterValue = target.val();
|
||||
|
||||
if (filterValue) {
|
||||
filters[filterName] = filterValue;
|
||||
} else {
|
||||
delete filters[filterName];
|
||||
}
|
||||
|
||||
filterFiles();
|
||||
});
|
||||
|
||||
/* initialize media uploader */
|
||||
if ($('.thumbs-list-container .dropzone')[0]) {
|
||||
$('.thumbs-list-container .dropzone')[0].dropzone.on('queuecomplete', function() {
|
||||
let body = {};
|
||||
if (filters.page) { body.page = filters.page; }
|
||||
if (filters.date) { body.date = filters.date; }
|
||||
if (filters.type) { body.type = filters.type; }
|
||||
|
||||
$('.dropzone')[0].dropzone.files.forEach(function(file) { file.previewElement.remove(); });
|
||||
$('.dropzone').first().removeClass('dz-started');
|
||||
|
||||
request(config.base_url_relative + '/media-manager.json/task:clearMediaCache', { method: 'post', body }, () => {
|
||||
filterFiles();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/* handle loading media */
|
||||
var loadMedia = function loadMedia(filters, callback) {
|
||||
var url = config.base_url_relative + '/media.json/tmpl:media-list-content/index:' + global_index;
|
||||
|
||||
if (filters.page) {
|
||||
url += '/page:' + (filters.page).split('/').join('%5C');
|
||||
}
|
||||
if (filters.type && filters.type !== '*') {
|
||||
url += '/type:' + filters.type;
|
||||
}
|
||||
if (filters.date && filters.date !== '*') {
|
||||
url += '/date:' + filters.date;
|
||||
}
|
||||
|
||||
if (!isLoading) {
|
||||
isLoading = true;
|
||||
|
||||
$('.spinning-wheel').show();
|
||||
$.get(url, function(content) {
|
||||
$('.js__files').append(content);
|
||||
$('.spinning-wheel').hide();
|
||||
isLoading = false;
|
||||
global_index++;
|
||||
|
||||
callback(content);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var cleanFilesList = function cleanFilesList() {
|
||||
$('.js__files .card-item').remove();
|
||||
};
|
||||
|
||||
var resetActiveStateInSidebar = function resetActiveStateInSidebar() {
|
||||
$('.pages-list-container .row').removeClass('active'); // clear active state in sidebar
|
||||
};
|
||||
|
||||
var showEmptyState = function showEmptyState() {
|
||||
$('.thumbs-list-container').append('<p class="card-item empty-space">No media found</p>');
|
||||
};
|
||||
|
||||
var filterFiles = function filterFiles() {
|
||||
cleanFilesList();
|
||||
global_index = 0;
|
||||
files_ended = false;
|
||||
$('.empty-space').remove();
|
||||
loadMedia(filters, function(content) {
|
||||
if (!$(content).length) {
|
||||
showEmptyState();
|
||||
} else {
|
||||
if (!filters.page && (!filters.date || filters.date === '*') && (!filters.type || filters.type === '*')) {
|
||||
$('.js__files').trigger('fillView');
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/* handle changing page */
|
||||
$('body').on('click', '.pages-list-container .js__page-link', (event) => {
|
||||
var page = $(event.target).data('page');
|
||||
filters['page'] = page;
|
||||
|
||||
$('.media-list-title .page-indicator').html(page); // set indication
|
||||
$('.js__reset-pages-filter').removeClass('hidden'); // activate reset pages icon
|
||||
resetActiveStateInSidebar();
|
||||
$(event.target).parents('.row').addClass('active'); // set active state in sidebar
|
||||
$('.js__file-uploader').removeClass('hidden');
|
||||
|
||||
// customize processing URL, as the page changes dynamically
|
||||
if ($('.dropzone')[0]) {
|
||||
$('.dropzone')[0].dropzone.on('processing', function(file) {
|
||||
this.options.url = `${config.base_url_relative}/media-manager${page}.json/task${config.param_sep}addmedia`;
|
||||
});
|
||||
}
|
||||
|
||||
$('.js__button-clear-media-cache').addClass('hidden');
|
||||
filterFiles();
|
||||
|
||||
disableInfiniteScrolling(); // only infinite scroll on main list, not inside single pages
|
||||
});
|
||||
|
||||
/* handle clearing page filter */
|
||||
$('body').on('click', '.js__reset-pages-filter', (event) => {
|
||||
$('.media-list-title .page-indicator').html('All Pages'); // set indication
|
||||
cleanFilesList();
|
||||
resetActiveStateInSidebar();
|
||||
$('.js__reset-pages-filter').addClass('hidden'); // remove reset pages icon
|
||||
$('.js__file-uploader').addClass('hidden');
|
||||
$('.js__button-clear-media-cache').removeClass('hidden');
|
||||
delete filters['page'];
|
||||
|
||||
filterFiles();
|
||||
});
|
||||
|
||||
/* handle infinite loading */
|
||||
var enableInfiniteScrolling = function enableInfiniteScrolling() {
|
||||
$('.spinning-wheel').hide();
|
||||
var view = $('.mediapicker-scroll');
|
||||
view.scroll(function() {
|
||||
if (($(this).scrollTop() + $(this).innerHeight() + 100) >= $(this)[0].scrollHeight) {
|
||||
fillView();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var loadNextBatch = function loadNextBatch(callback) {
|
||||
if (files_ended) {
|
||||
return;
|
||||
}
|
||||
|
||||
loadMedia({}, function(content) {
|
||||
if (!$(content).length || ((content.split('card-item').length - 1) < MEDIA_PAGINATION_INTERVAL)) {
|
||||
files_ended = true;
|
||||
} else {
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var fillView = function fillView() {
|
||||
if (!$('.js__files').find('.card-item').last().offset()) {
|
||||
setTimeout(function() {
|
||||
// retry later
|
||||
fillView();
|
||||
}, 300);
|
||||
|
||||
return;
|
||||
}
|
||||
if ($('.js__files').find('.card-item').last().offset().top < $('.media-container').height()) {
|
||||
loadNextBatch(function() {
|
||||
fillView();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/* disable infinite loading */
|
||||
var disableInfiniteScrolling = function disableInfiniteScrolling() {
|
||||
$('.spinning-wheel').hide();
|
||||
$('.content-wrapper').unbind('scroll');
|
||||
};
|
||||
|
||||
$('.js__files').on('fillView', function(event) {
|
||||
// the first batch got the max number of media files, try loading more
|
||||
if (($('.js__files')[0].innerHTML.split('card-item').length - 1) === MEDIA_PAGINATION_INTERVAL) {
|
||||
fillView();
|
||||
enableInfiniteScrolling();
|
||||
}
|
||||
});
|
||||
2
themes/grav/css-compiled/fonts.css
vendored
2
themes/grav/css-compiled/fonts.css
vendored
@@ -1,3 +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 */
|
||||
/*# sourceMappingURL=../css-compiled/fonts.css.map */
|
||||
@@ -1 +1,11 @@
|
||||
{"version":3,"file":"fonts.css","sources":["fonts.scss","configuration/fonts/_support.scss"],"sourcesContent":["$fonts-default: 'Lato' !default;\n$fonts-header: 'Montserrat' !default;\n$fonts-mono: 'Inconsolata' !default;\n\n$font-definitions: (\n Montserrat: '400',\n Lato: '300,400,700',\n Inconsolata: '400,700'\n);\n\n@import \"configuration/fonts/support\";\n\n\n\n\n","@function str-replace($string, $search, $replace: '') {\n $index: str-index($string, $search);\n\n @if $index {\n @return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);\n }\n\n @return $string;\n}\n\n@function admin-font-faces($fonts) {\n $url: \"//fonts.googleapis.com/css?family=\";\n $nb: 0;\n\n @each $fontname, $weights in $fonts {\n\n @if $fontname == $fonts-default or\n $fontname == $fonts-header or\n $fontname == $fonts-mono {\n\n $nb: $nb + 1;\n $nb-word: 0;\n\n $fontname: str-replace(\"#{$fontname}\", \" \", \"+\");\n\n $url: $url + $fontname;\n\n @if $weights != null {\n $url: $url + \":\" + $weights;\n }\n\n @if $nb < 3 {\n $url: $url + \"|\";\n }\n }\n }\n\n @return $url;\n}\n\n@mixin body-fonts($font) {\n body, h5, h6,\n .badge, .note, .grav-mdeditor-preview,\n input, select, textarea, button, .selectize-input {\n font-family: \"#{$font}\", \"Helvetica\", \"Tahoma\", \"Geneva\", \"Arial\", sans-serif;\n }\n}\n\n@mixin header-fonts($font) {\n h1, h2, h3, h4,\n #admin-menu li, .form-tabs > label, .label {\n font-family: \"#{$font}\", \"Helvetica\", \"Tahoma\", \"Geneva\", \"Arial\", sans-serif;\n }\n}\n\n@mixin mono-fonts($font) {\n code, kbd, pre, samp,\n body .CodeMirror {\n font-family: \"#{$font}\", \"Monaco\", \"Consolas\", \"Lucida Console\", monospace !important;\n }\n}\n$font-url: admin-font-faces($font-definitions);\n\n@import url(\"#{$font-url}\");\n\n@include body-fonts($fonts-default);\n\n@include header-fonts($fonts-header);\n\n@include mono-fonts($fonts-mono);\n\n\n\n\n\n"],"names":[],"mappings":"AC+DA,OAAO,CAAC,4FAAI,CAtBR,AAAA,IAAI,CAAE,AAAA,EAAE,CAAE,AAAA,EAAE,CACZ,AAAA,MAAM,CAAE,AAAA,KAAK,CAAE,AAAA,sBAAsB,CACrC,AAAA,KAAK,CAAE,AAAA,MAAM,CAAE,AAAA,QAAQ,CAAE,AAAA,MAAM,CAAE,AAAA,gBAAgB,AAAC,CAC9C,WAAW,CAAE,MAAU,CAAE,WAAW,CAAE,QAAQ,CAAE,QAAQ,CAAE,OAAO,CAAE,UAAU,CAChF,AAID,AAAA,EAAE,CAAE,AAAA,EAAE,CAAE,AAAA,EAAE,CAAE,AAAA,EAAE,CACd,AAAY,WAAD,CAAC,EAAE,CAAE,AAAa,UAAH,CAAG,KAAK,CAAE,AAAA,MAAM,AAAC,CACvC,WAAW,CAAE,YAAU,CAAE,WAAW,CAAE,QAAQ,CAAE,QAAQ,CAAE,OAAO,CAAE,UAAU,CAChF,AAID,AAAA,IAAI,CAAE,AAAA,GAAG,CAAE,AAAA,GAAG,CAAE,AAAA,IAAI,CACpB,AAAK,IAAD,CAAC,WAAW,AAAC,CACb,WAAW,CAAE,aAAU,CAAE,QAAQ,CAAE,UAAU,CAAE,gBAAgB,CAAE,SAAS,CAAC,UAAU,CACxF"}
|
||||
{
|
||||
"version": 3,
|
||||
"file": "../scss/fonts.css",
|
||||
"sources": [
|
||||
"../scss/fonts.scss",
|
||||
"../hdr0",
|
||||
"../scss/configuration/fonts/_support.scss"
|
||||
],
|
||||
"mappings": "AE+DA,OAAO,CAAC,4FAAI,CAtBR,AAAA,IAAI,CAAE,AAAA,EAAE,CAAE,AAAA,EAAE,CACZ,AAAA,MAAM,CAAE,AAAA,KAAK,CAAE,AAAA,sBAAsB,CACrC,AAAA,KAAK,CAAE,AAAA,MAAM,CAAE,AAAA,QAAQ,CAAE,AAAA,MAAM,CAAE,AAAA,gBAAgB,AAAC,CAC9C,WAAW,CAAE,MAAU,CAAE,WAAW,CAAE,QAAQ,CAAE,QAAQ,CAAE,OAAO,CAAE,UAAU,CAChF,AAID,AAAA,EAAE,CAAE,AAAA,EAAE,CAAE,AAAA,EAAE,CAAE,AAAA,EAAE,CACd,AAAY,WAAD,CAAC,EAAE,CAAE,AAAa,UAAH,CAAG,KAAK,CAAE,AAAA,MAAM,AAAC,CACvC,WAAW,CAAE,YAAU,CAAE,WAAW,CAAE,QAAQ,CAAE,QAAQ,CAAE,OAAO,CAAE,UAAU,CAChF,AAID,AAAA,IAAI,CAAE,AAAA,GAAG,CAAE,AAAA,GAAG,CAAE,AAAA,IAAI,CACpB,AAAK,IAAD,CAAC,WAAW,AAAC,CACb,WAAW,CAAE,aAAU,CAAE,QAAQ,CAAE,UAAU,CAAE,gBAAgB,CAAE,SAAS,CAAC,UAAU,CACxF",
|
||||
"names": []
|
||||
}
|
||||
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
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,3 +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 */
|
||||
/*# sourceMappingURL=../css-compiled/simple-fonts.css.map */
|
||||
@@ -1 +1,10 @@
|
||||
{"version":3,"file":"simple-fonts.css","sources":["simple-fonts.scss"],"sourcesContent":["body, h5, h6,\n.badge, .note, .grav-mdeditor-preview,\ninput, select, textarea, button, .selectize-input,\nh1, h2, h3, h4,\n#admin-menu li, .form-tabs > label, .label {\n font-family: \"Helvetica Neue\", \"Helvetica\", \"Tahoma\", \"Geneva\", \"Arial\", sans-serif;\n}\ncode, kbd, pre, samp,\nbody .CodeMirror {\n font-family: \"Monaco\", \"Consolas\", \"Lucida Console\", monospace;\n}\n"],"names":[],"mappings":"AAAA,AAAA,IAAI,CAAE,AAAA,EAAE,CAAE,AAAA,EAAE,CACZ,AAAA,MAAM,CAAE,AAAA,KAAK,CAAE,AAAA,sBAAsB,CACrC,AAAA,KAAK,CAAE,AAAA,MAAM,CAAE,AAAA,QAAQ,CAAE,AAAA,MAAM,CAAE,AAAA,gBAAgB,CACjD,AAAA,EAAE,CAAE,AAAA,EAAE,CAAE,AAAA,EAAE,CAAE,AAAA,EAAE,CACd,AAAY,WAAD,CAAC,EAAE,CAAE,AAAa,UAAH,CAAG,KAAK,CAAE,AAAA,MAAM,AAAC,CACvC,WAAW,CAAE,sEAAsE,CACtF,AACD,AAAA,IAAI,CAAE,AAAA,GAAG,CAAE,AAAA,GAAG,CAAE,AAAA,IAAI,CACpB,AAAK,IAAD,CAAC,WAAW,AAAC,CACb,WAAW,CAAE,iDAAiD,CACjE"}
|
||||
{
|
||||
"version": 3,
|
||||
"file": "../scss/simple-fonts.css",
|
||||
"sources": [
|
||||
"../scss/simple-fonts.scss",
|
||||
"../hdr0"
|
||||
],
|
||||
"mappings": "AAAA,AAAA,IAAI,CAAE,AAAA,EAAE,CAAE,AAAA,EAAE,CACZ,AAAA,MAAM,CAAE,AAAA,KAAK,CAAE,AAAA,sBAAsB,CACrC,AAAA,KAAK,CAAE,AAAA,MAAM,CAAE,AAAA,QAAQ,CAAE,AAAA,MAAM,CAAE,AAAA,gBAAgB,CACjD,AAAA,EAAE,CAAE,AAAA,EAAE,CAAE,AAAA,EAAE,CAAE,AAAA,EAAE,CACd,AAAY,WAAD,CAAC,EAAE,CAAE,AAAa,UAAH,CAAG,KAAK,CAAE,AAAA,MAAM,AAAC,CACvC,WAAW,CAAE,sEAAuE,CACvF,AACD,AAAA,IAAI,CAAE,AAAA,GAAG,CAAE,AAAA,GAAG,CAAE,AAAA,IAAI,CACpB,AAAK,IAAD,CAAC,WAAW,AAAC,CACb,WAAW,CAAE,iDAAkD,CAClE",
|
||||
"names": []
|
||||
}
|
||||
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
Binary file not shown.
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 6.6 KiB |
32
themes/grav/js/admin.min.js
vendored
32
themes/grav/js/admin.min.js
vendored
File diff suppressed because one or more lines are too long
53
themes/grav/js/vendor.min.js
vendored
53
themes/grav/js/vendor.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -86,6 +86,9 @@
|
||||
// Notifications
|
||||
@import "template/notifications";
|
||||
|
||||
// Media
|
||||
@import "template/media";
|
||||
|
||||
// Custom
|
||||
@import "template/custom";
|
||||
|
||||
|
||||
@@ -638,3 +638,4 @@ textarea.frontmatter {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
310
themes/grav/scss/template/_media.scss
Normal file
310
themes/grav/scss/template/_media.scss
Normal file
@@ -0,0 +1,310 @@
|
||||
/* Media */
|
||||
$dark-text: #333;
|
||||
|
||||
.pages-list-container {
|
||||
width: 30%;
|
||||
flex: none!important;
|
||||
padding: 0!important;
|
||||
|
||||
.pages-list {
|
||||
width: 100%;
|
||||
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
&.depth-0 .row {
|
||||
padding-left: .5rem;
|
||||
}
|
||||
|
||||
.depth-1 .row {
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
.page-item {
|
||||
.page-link {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.row.active {
|
||||
background-color: rgba(#00a6cf, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.media-page-dropzone {
|
||||
width: 100%;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.media-container {
|
||||
&.size-2-3 {
|
||||
flex: none!important;
|
||||
width: 70%;
|
||||
}
|
||||
|
||||
|
||||
.filters {
|
||||
padding-bottom: 30px;
|
||||
width: 100%;
|
||||
|
||||
|
||||
.filter-wrapper {
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
|
||||
&:after {
|
||||
margin-top: -25px;
|
||||
border: 0px none;
|
||||
content: "\f078";
|
||||
font-family: "FontAwesome";
|
||||
right: 12px;
|
||||
top: 50%;
|
||||
line-height: 0;
|
||||
color: #9BA2A6;
|
||||
pointer-events: none;
|
||||
float: right;
|
||||
padding-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.filter {
|
||||
option {
|
||||
padding: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.files {
|
||||
padding-left: 30px;
|
||||
padding-right: 30px;
|
||||
|
||||
}
|
||||
|
||||
.admin-media-title {
|
||||
|
||||
color: $dark-text;
|
||||
margin: 0px;
|
||||
font-size: .8rem;
|
||||
line-height: 1.3;
|
||||
|
||||
}
|
||||
|
||||
.card-item {
|
||||
width: 49%;
|
||||
|
||||
&.empty-space {
|
||||
|
||||
background: rgba(255,255,255,0.4);
|
||||
border: 1px solid rgba(0,0,0,0.1) !important;
|
||||
|
||||
color: rgba(0,0,0,0.3);
|
||||
|
||||
max-width: none;
|
||||
display: block;
|
||||
text-align: center;
|
||||
|
||||
top: 20%;
|
||||
height: 10rem !important;
|
||||
width: 50% !important;
|
||||
margin: 0 auto !important;
|
||||
font-size: 2rem;
|
||||
line-height: 8rem;
|
||||
transform: translateY(-50%);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.media-details {
|
||||
text-align: center;
|
||||
img {
|
||||
width: 50%;
|
||||
}
|
||||
h2 {
|
||||
margin: 0;
|
||||
font-size: 1.8rem;
|
||||
}
|
||||
h5 {
|
||||
color: #9BA2A6;
|
||||
font-size: 1.1rem;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
h1 {
|
||||
padding: 0 2rem 0.5rem!important;
|
||||
}
|
||||
|
||||
.admin-form-wrapper {
|
||||
width: 100%;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.spinning-wheel {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Media in-page modal */
|
||||
|
||||
.media-container.in-modal {
|
||||
h1 {
|
||||
font-size: 30px!important;
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.pages-list li {
|
||||
list-style-type: none;
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
//Mediapicker field
|
||||
.remodal.remodal-mediapicker {
|
||||
max-width: 70vw;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.media-container {
|
||||
height: 70vh;
|
||||
|
||||
|
||||
.grid {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
|
||||
&.in-modal {
|
||||
|
||||
.gm-scrollbar {
|
||||
background: rgba(255,255,255,0.2);
|
||||
|
||||
.thumb {
|
||||
background-color: rgba(0,0,0,0.2) !important;
|
||||
&:hover, &.active {
|
||||
background-color: rgba(0,0,0,0.3) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
h5 {
|
||||
border-bottom: 1px solid #ddd;
|
||||
padding: 0.5rem 15px;
|
||||
margin: 0;
|
||||
height: 55px;
|
||||
}
|
||||
|
||||
.filter-wrapper {
|
||||
float: right;
|
||||
margin-right: 5px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.filter-date {
|
||||
width: 150px;
|
||||
|
||||
}
|
||||
|
||||
.filter-type {
|
||||
width: 100px;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.pages-list-container {
|
||||
position: relative;
|
||||
|
||||
.pages-list {
|
||||
border-top: 0;
|
||||
padding-right: 10px;
|
||||
font-size: 90%;
|
||||
|
||||
.row {
|
||||
transition: none !important;
|
||||
line-height: 2rem;
|
||||
}
|
||||
|
||||
@for $i from 1 to 10 {
|
||||
.depth-#{$i} .row {
|
||||
padding-left: 1rem * ($i + 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.mediapicker-scroll {
|
||||
position: absolute;
|
||||
top: 55px;
|
||||
bottom: 3px;
|
||||
height: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
.thumbs-list-container {
|
||||
position: relative;
|
||||
|
||||
.media-range {
|
||||
position: absolute;
|
||||
right: 15px;
|
||||
bottom: 5px;
|
||||
}
|
||||
|
||||
.mediapicker-scroll {
|
||||
position: absolute;
|
||||
top: 55px;
|
||||
bottom: 30px;
|
||||
height: inherit;
|
||||
}
|
||||
|
||||
.gm-scrollbar.-horizontal {
|
||||
display: none;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#admin-media {
|
||||
|
||||
padding: 15px;
|
||||
height: 100%;
|
||||
|
||||
align-items: flex-start;
|
||||
align-content: flex-start;
|
||||
|
||||
.card-item {
|
||||
width: 100px;
|
||||
padding: 2px;
|
||||
margin: 0 5px 5px 0;
|
||||
border: 0;
|
||||
|
||||
img {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.admin-media-details {
|
||||
position: relative;
|
||||
|
||||
.admin-media-title {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
background: rgba(0,0,0,0.3);
|
||||
color: #fff;
|
||||
font-size: 10px;
|
||||
overflow: hidden;
|
||||
line-height: 2;
|
||||
text-indent: 2px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@ $ir_slider_height: 20px;
|
||||
$ir_counter_width: 60px;
|
||||
$ir_margin: 10px 0;
|
||||
|
||||
input[type=range] {
|
||||
input[type=range].rangefield {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
-webkit-appearance: none;
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
{% extends "forms/fields/text/text.html.twig" %}
|
||||
|
||||
{% set originalValue = value %}
|
||||
{% set value = (value is null ? field.default : value) %}
|
||||
|
||||
{% set unique_identifier = random_string() %}
|
||||
|
||||
{% block global_attributes %}
|
||||
{{ parent() }}
|
||||
data-mediapicker-modal-trigger
|
||||
data-grav-mediapicker-unique-identifier="{{ unique_identifier }}"
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block contents %}
|
||||
{{ parent() }}
|
||||
<div class="remodal remodal-mediapicker" data-remodal-mediapicker data-remodal-unique-identifier="{{ unique_identifier }}" data-remodal-options="hashTracking: false">
|
||||
{% include 'partials/media-list-wrapper.html.twig' with { is_modal: true } %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -2,7 +2,17 @@
|
||||
|
||||
{% block input %}
|
||||
{% set last_page_route = admin.page.getLastPageRoute %}
|
||||
{% set defaults = {show_root:true, show_all:true, show_slug:true, default:last_page_route} %}
|
||||
{% set show_slug_val = true %}
|
||||
{% set show_fullpath_val = false %}
|
||||
|
||||
{% set show_parents = config.get('plugins.admin.pages.show_parents') %}
|
||||
{% if show_parents == 'folder' %}
|
||||
{% set show_slug_val = false %}
|
||||
{% elseif show_parents == 'fullpath' %}
|
||||
{% set show_fullpath_val = true %}
|
||||
{% endif %}
|
||||
|
||||
{% set defaults = {show_root:true, show_all:true, show_slug:show_slug_val, show_fullpath:show_fullpath_val, default:last_page_route} %}
|
||||
{% set field = field|merge(defaults) %}
|
||||
{{ parent() }}
|
||||
{% endblock %}
|
||||
|
||||
18
themes/grav/templates/media-list-content.html.twig
Normal file
18
themes/grav/templates/media-list-content.html.twig
Normal file
@@ -0,0 +1,18 @@
|
||||
{% set index = uri.param('index') %}
|
||||
|
||||
{% for file in admin.files(true, index) %}
|
||||
{% set the_file = file.file %}
|
||||
{% if the_file.display() is not empty %}
|
||||
<div class="card-item">
|
||||
<div class="admin-media-details">
|
||||
<a href="{% if is_modal %}#{% else %}{{ base_url_relative }}/media-manager/{{base64_encode(the_file.filepath)}}{% endif %}" class="js__media-element" data-file-url="{{ file.page_route ~ '/' ~ file.title }}">
|
||||
{% set thumbnail = the_file.display(the_file.extension == 'svg' ? 'source' : 'thumbnail') %}
|
||||
{% if thumbnail %}
|
||||
<img src="{{thumbnail.cropZoom(250, 250).url}}" />
|
||||
<h4 class="admin-media-title">{{ file.title }}</h4>
|
||||
{% endif %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
16
themes/grav/templates/partials/media-list-wrapper.html.twig
Normal file
16
themes/grav/templates/partials/media-list-wrapper.html.twig
Normal file
@@ -0,0 +1,16 @@
|
||||
<div class="grid media-container {% if is_modal %}in-modal{% endif %}">
|
||||
{% set default_site_lang = grav.config.system.languages|first|first %}
|
||||
|
||||
{% include 'partials/media-list-wrapper__sidebar.html.twig' %}
|
||||
|
||||
<div class="thumbs-list-container block size-3-4">
|
||||
{% include 'partials/media-list-wrapper__list__filters.html.twig' %}
|
||||
<h5 class="media-list-title"><span class="page-indicator">All Pages</span> <a class="hidden js__reset-pages-filter" href="#"><i class="fa fa-fw fa-times"></i></a></h5>
|
||||
|
||||
<div class="mediapicker-scroll">
|
||||
{% include 'partials/media-list-wrapper__list.html.twig' with { is_modal: is_modal } %}
|
||||
</div>
|
||||
|
||||
<input name="thumb-size" class="media-range" type="range" min="50" max="250" value="100" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,23 @@
|
||||
<div id="admin-media" class="files js__files card-row grid fixed-blocks pure-g">
|
||||
{% if not is_modal %}
|
||||
{% include 'partials/media-list-wrapper__list__dropzone.html.twig' ignore missing %}
|
||||
{% endif %}
|
||||
|
||||
{% if admin.files is empty %}
|
||||
<div class="empty-state">
|
||||
{% if (uri.param('type') or uri.param('date')) %}
|
||||
<h2>Filtering by {{ uri.param('type') }} {{ uri.param('date') }}</h2>
|
||||
{% endif %}
|
||||
|
||||
<h2>No media files found</h2>
|
||||
|
||||
<p>You need to add media to a page in order to display it here.</p>
|
||||
</div>
|
||||
{% else %}
|
||||
{% include 'media-list-content.html.twig' with { is_modal: is_modal } %} {# not a partial as used by AJAX #}
|
||||
{% endif %}
|
||||
|
||||
{{ nonce_field('admin-form', 'admin-nonce')|raw }}
|
||||
</div>
|
||||
|
||||
{% include 'partials/spinning-wheel.html.twig' %}
|
||||
@@ -0,0 +1,28 @@
|
||||
<div>
|
||||
<div class="filter-wrapper filter-type">
|
||||
<select class="filter js__filter" data-name="type" data-grav-selectize>
|
||||
<option value="*" {% if not grav.uri.param('type') %}selected{% endif %}>All Files</option>
|
||||
<option value="images" {% if grav.uri.param('type') == 'images' %}selected{% endif %}>Images</option>
|
||||
<option value="videos"{% if grav.uri.param('type') == 'videos' %}selected{% endif %}>Videos</option>
|
||||
<option value="audios"{% if grav.uri.param('type') == 'audios' %}selected{% endif %}>Audio</option>
|
||||
<option value="files"{% if grav.uri.param('type') == 'files' %}selected{% endif %}>Files</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="filter-wrapper filter-date">
|
||||
<select class="filter js__filter" data-name="date" data-grav-selectize>
|
||||
<option value="*">All Dates</option>
|
||||
{% set theDate = date() %}
|
||||
{% for year, months in admin.filesDates %}
|
||||
<optgroup label="{{year}}">
|
||||
{% for month, number in months %}
|
||||
<option value="{{year}}-{{month}}" {% if grav.uri.param('date') == year~'-'~month %}selected{% endif %}>
|
||||
{{theDate.setDate(theDate.format('Y'), month, theDate.format('d'))|date('M')}}
|
||||
({{number}})</option>
|
||||
{% endfor %}
|
||||
</optgroup>
|
||||
{% endfor %}
|
||||
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,46 @@
|
||||
<div class="pages-list-container clear block size-1-4">
|
||||
<h5>{{ "PLUGIN_ADMIN.PAGES"|tu|e }}</h5>
|
||||
<div class="mediapicker-scroll">
|
||||
<ul class="pages-list depth-0">
|
||||
{{ _self.loop(pages, 0, _context) }}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% macro loop(page, depth, twig_vars) %}
|
||||
{% set separator = twig_vars['config'].system.param_sep %}
|
||||
{% set base_url = twig_vars['base_url_relative'] %}
|
||||
{% set base_url_simple = twig_vars['base_url_simple'] %}
|
||||
{% set admin_route = twig_vars['admin_route'] %}
|
||||
{% set admin_lang = twig_vars['admin_lang'] %}
|
||||
{% set warn = twig_vars['warn'] %}
|
||||
|
||||
{% for p in page.children() %}
|
||||
{% set page_route = p.rawRoute|trim('/') %}
|
||||
{% if p.language and p.language != admin_lang %}
|
||||
{% set page_url = base_url_simple ~ '/' ~ p.language ~ '/' ~ admin_route ~ '/pages/' ~ page_route %}
|
||||
{% else %}
|
||||
{% set page_url = base_url ~ '/pages/' ~ page_route %}
|
||||
{% endif %}
|
||||
|
||||
<li class="page-item" data-nav-id="{{ p.route }}">
|
||||
<div class="row">
|
||||
<span {{ p.children(0).count > 0 ? 'data-toggle="children"' : ''}} class="hint--bottom">
|
||||
<i class="page-icon fa fa-fw fa-circle-o {{ p.children(0).count > 0 ? 'children-closed' : ''}} {{ p.modular ? 'modular' : (not p.routable ? 'not-routable' : (not p.visible ? 'not-visible' : (not p.page ? 'folder' : ''))) }}"></i>
|
||||
</span>
|
||||
|
||||
<span data-hint="{{ p.header.routes.default ?: p.route }}" class="hint--bottom">
|
||||
<a data-page="{{ p.route }}" class="js__page-link page-link" href="#">{{ p.title }}</a>
|
||||
</span>
|
||||
|
||||
<span class="page-home">{{ p.home ? '<i class="fa fa-home"></i>' }}</span>
|
||||
</div>
|
||||
{% if p.children().count > 0 %}
|
||||
|
||||
<ul class="depth-{{ depth + 1 }}" style="display:none;">
|
||||
{{ _self.loop(p, depth + 1, twig_vars) }}
|
||||
</ul>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% endmacro %}
|
||||
3
themes/grav/templates/partials/spinning-wheel.html.twig
Normal file
3
themes/grav/templates/partials/spinning-wheel.html.twig
Normal file
@@ -0,0 +1,3 @@
|
||||
<div class="spinning-wheel" style="display: none">
|
||||
{{ "PLUGIN_ADMIN.LOADING"|tu|e }} <i class="fa fa-spinner fa-spin"></i>
|
||||
</div>
|
||||
Reference in New Issue
Block a user