manager: revamp assets management page (#11491)

This commit is contained in:
Frédéric Péters 2017-12-24 16:58:10 +01:00
parent ee04f9f1e5
commit be2a10e460
6 changed files with 208 additions and 99 deletions

View File

@ -188,52 +188,103 @@ p#redirection {
float: right;
}
div#assets-thumbs {
width: 50%;
float: left;
#assets-browser {
display: flex;
}
div#asset-show {
width: 48%;
float: right;
padding-top: 1em;
#assets-browser #assets-listing {
width: 80%;
}
div#assets-thumbs ul {
-moz-column-width: 100px;
list-style: none;
#assets-browser #assets-listing table th,
#assets-browser #assets-listing table td {
text-align: left;
padding-left: 1ex;
}
div#assets-thumbs ul li {
padding: 5px;
#assets-browser #assets-listing table td.image {
padding: 0;
}
div#assets-thumbs a {
#assets-browser #assets-listing table td.actions {
width: 3em;
}
#assets-browser #assets-listing table td.actions a.delete {
display: inline-block;
border: none;
opacity: 0.8;
overflow: hidden;
width: 30px;
height: 30px;
line-height: 30px;
}
div#assets-thumbs a:hover {
opacity: 1.0;
#assets-browser #assets-listing table td.actions a.delete::before {
text-align: center;
font-family: FontAwesome;
content: "\f057"; /* remove-sign */
display: inline-block;
width: 30px;
}
div#assets-thumbs img {
padding: 5px;
border: 1px solid #aaa;
#assets-browser tr::before {
font-family: FontAwesome;
vertical-align: middle;
text-align: center;
display: table-cell;
width: 2em;
border: 1px solid #bcbcbc;
content: "";
}
div#asset-img {
#assets-browser td img {
max-width: 50px;
max-height: 50px;
}
#assets-browser tr.asset-ext-html::before {
content: "\f0f6"; /* -file-text-o */
}
#assets-browser tr.asset-ext-png::before,
#assets-browser tr.asset-ext-gif::before,
#assets-browser tr.asset-ext-jpg::before,
#assets-browser tr.asset-ext-jpeg::before {
content: "\f1c5"; /* -file-image-o */
}
#assets-browser tr.asset-ext-mp3::before,
#assets-browser tr.asset-ext-ogg::before,
#assets-browser tr.asset-ext-flac::before,
#assets-browser tr.asset-ext-wav::before {
content: "\f1c7"; /* -file-audio-o */
}
#assets-browser tr.asset-ext-pdf::before {
content: "\f1c1"; /* -file-pdf-o */
}
div#asset-preview {
width: 20%;
box-sizing: border-box;
padding: 15px;
text-align: center;
}
div#asset-img img {
max-width: 90%;
padding: 5px;
border: 1px solid #aaa;
div#asset-preview:empty::before {
font-family: FontAwesome;
color: #ddd;
font-size: 100px;
content: "\f03e"; /* picture-o */
width: 100%;
display: block;
}
div#asset-cmds {
text-align: right;
div#asset-preview img {
padding: 5px;
background: white;
border: 1px solid #aaa;
max-width: 100%;
}
ul.multisort {

View File

@ -198,22 +198,16 @@ $(function() {
$(style).appendTo('head');
$(this).addClass('untoggled');
});
$('#assets-thumbs a').on('click', function() {
$('#asset-img').empty().append($('<img src="' + $(this).attr('href') + '"/>'));
$('#asset-cmds').show();
return false;
});
$('#asset-delete').on('click', function() {
var img_src = $('#asset-img img').attr('src');
var thumb_item = $('#assets-thumbs a[href="' + img_src + '"]').parent();
var img_orig = $(thumb_item).data('orig');
$(thumb_item).hide();
$('#asset-img').empty();
$('#asset-cmds').hide();
$.ajax({
url: $(this).data('asset-delete-url'),
data: {'img_orig': img_orig}
});
$('#assets-browser table tr').on('hover mouseenter', function() {
var $img = $(this).find('img');
if ($img.data('href')) {
$('#asset-preview').empty().append($('<img src="' + $img.data('href') + '"/>'));
} else {
$('#asset-preview').empty();
}
return true;
}).on('mouseleave', function() {
$('#asset-preview').empty();
});
$('.manager-add-new-cell a').on('click', function() {
$(this).next().toggle();

View File

@ -0,0 +1,17 @@
{% extends "combo/manager_base.html" %}
{% load i18n %}
{% block appbar %}
<h2>{% trans 'Delete Asset' %}</h2>
{% endblock %}
{% block content %}
<form method="post">
{% csrf_token %}
{% blocktrans %}Are you sure you want to delete this?{% endblocktrans %}
<div class="buttons">
<button class="delete-button">{% trans 'Delete' %}</button>
<a class="cancel" href="{% url 'combo-manager-assets' %}">{% trans 'Cancel' %}</a>
</div>
</form>
{% endblock %}

View File

@ -12,7 +12,7 @@
{% block content %}
{% if not files %}
{% if not object_list %}
<div class="big-msg-info">
{% blocktrans %}
This site doesn't have any asset yet. You can add some directly when editing
@ -23,31 +23,57 @@
{% else %}
<div id="assets-browser">
<div id="assets-thumbs" class="navigation">
<ul>
{% for file in files %}
<li data-orig="{{ file.orig }}">
<a {% if file.is_image %}class="thumb"{% endif %} href="{{ file.src }}">
{% if file.is_image %}
<img src="{{ file.thumb }}" style="max-width: 75px;"/>
{% else %}
{{ file.thumb }}
{% endif %}
</a>
</li>
{% endfor %}
</ul>
<div id="assets-listing">
<table class="main">
<thead>
<tr>
<th>{% trans "Filename" %}</th>
<th>{% trans "Size" %}</th>
<th></th>
<th></th>
</tr>
</thead>
<tbody>
{% for asset in object_list %}
<tr class="{{ asset.css_classes }}">
<td><a href="{{ asset.src }}">{{ asset.filename }}</a></td>
<td>{{ asset.size|filesizeformat }}</td>
<td class="image">{% if asset.is_image %}<img data-href="{{ asset.src }}" src="{{ asset.thumb }}"/>{% endif %}</td>
<td class="actions">
<a href="{% url 'combo-manager-asset-delete' %}?img={{asset.filepath|iriencode}}"
class="delete" rel="popup">{% trans 'Delete' %}</a>
</td>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div id="asset-preview"></div>
</div>
<div id="asset-show">
<div id="asset-img">
</div>
<div id="asset-cmds" style="display: none;">
<button data-asset-delete-url="{% url 'combo-manager-asset-delete' %}"
id="asset-delete">{% trans 'Delete' %}</button>
</div>
</div>
{% if is_paginated %}
<p class="paginator">
{% if page_obj.has_previous %}
<a href="?page={{ page_obj.previous_page_number }}&q={{ query|iriencode }}">&lt;&lt;</a>
{% else %}
<span>&lt;&lt;</span>
{% endif %}
&nbsp;
<span class="current">
{{ page_obj.number }} / {{ page_obj.paginator.num_pages }}
</span>
&nbsp;
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}&q={{ query|iriencode }}">&gt;&gt;</a>
{% else %}
<span>&gt;&gt;</span>
{% endif %}
</div>
{% endif %}
{% endif %}
{% endblock %}

View File

@ -381,40 +381,61 @@ def page_get_additional_label(request, page_pk, cell_reference):
return response
class Assets(TemplateView):
template_name = 'combo/manager_assets.html'
class Asset(object):
def __init__(self, filepath):
self.filepath = filepath
self.filename = os.path.basename(filepath)
self.src = ckeditor.utils.get_media_url(filepath)
def get_context_data(self, **kwargs):
context = super(Assets, self).get_context_data(**kwargs)
context['files'] = []
for filename in ckeditor.views.get_image_files(self.request.user):
src = ckeditor.utils.get_media_url(filename)
if getattr(settings, 'CKEDITOR_IMAGE_BACKEND', None):
thumb = ckeditor.utils.get_media_url(
ckeditor.utils.get_thumb_filename(filename))
else:
thumb = src
context['files'].append({
'thumb': thumb,
'src': src,
'is_image': ckeditor.views.is_image(src),
'orig': filename,
})
return context
def css_classes(self):
extension = os.path.splitext(self.filepath)[-1].strip('.')
if extension:
return 'asset-ext-%s' % extension
return ''
def size(self):
return os.stat(default_storage.path(self.filepath)).st_size
def thumb(self):
if getattr(settings, 'CKEDITOR_IMAGE_BACKEND', None):
thumb = ckeditor.utils.get_media_url(
ckeditor.utils.get_thumb_filename(self.filepath))
else:
thumb = self.src
return thumb
def is_image(self):
return ckeditor.views.is_image(self.src)
class Assets(ListView):
template_name = 'combo/manager_assets.html'
paginate_by = 10
def get_queryset(self):
files = [Asset(x) for x in ckeditor.views.get_image_files(self.request.user)]
files.sort(key=lambda x: getattr(x, 'filename'))
return files
assets = Assets.as_view()
def asset_delete(request):
img_orig = request.GET['img_orig']
if '..' in img_orig:
raise PermissionDenied() # better safe than sorry
base_path = settings.CKEDITOR_UPLOAD_PATH
if getattr(settings, 'CKEDITOR_RESTRICT_BY_USER', False):
base_path = os.path.join(base_path, request.user.username)
if not img_orig.startswith(base_path):
raise PermissionDenied()
default_storage.delete(img_orig)
return HttpResponse(status=204)
class AssetDelete(TemplateView):
template_name = 'combo/manager_asset_confirm_delete.html'
def post(self, request):
img_orig = request.GET['img']
if '..' in img_orig:
raise PermissionDenied() # better safe than sorry
base_path = settings.CKEDITOR_UPLOAD_PATH
if getattr(settings, 'CKEDITOR_RESTRICT_BY_USER', False):
base_path = os.path.join(base_path, request.user.username)
if not img_orig.startswith(base_path):
raise PermissionDenied()
default_storage.delete(img_orig)
return redirect(reverse('combo-manager-assets'))
asset_delete = AssetDelete.as_view()
def menu_json(request):

View File

@ -580,10 +580,10 @@ def test_asset_management(app, admin_user):
resp = app.get('/manage/assets/')
assert 'have any asset yet.' not in resp.body
app.get('/manage/assets/delete?img_orig=%s' % filepath, status=204)
assert not os.path.exists(default_storage.path(filepath))
app.get('/manage/assets/delete?img_orig=/foo.png', status=403)
resp = resp.click('Delete')
assert 'Are you sure you want to delete' in resp.body
resp = resp.form.submit().follow()
assert 'have any asset yet.' in resp.body
def test_menu_json(app, admin_user):
app.get('/manage/menu.json', status=302)