Skip to content

Commit

Permalink
feat: open edit file popup from file widget (#1301)
Browse files Browse the repository at this point in the history
* feat: open edit file popup from file widget

* fix: rename popup_response.js to filer_popup_response.js to ensure not overriding django's file

* fix: minor

* chore: add edit from widget tests

* fix: clean-up file widget style

* fix: mobile devices styling

* fix: hide edit file button when no file selected

* fix: lookup icon hidden on recent djangocms

* style: format

* fix: missing updated compiled css

* fix: jscs and jshint complaints

* fix: legacyize code

* fix: restore const

* fix: legacyize code again

* fix: better ellipsis cut width + simplify css

* fix: change choose and replace buttons icon

* test: add initial AdminFileWidget test

* fix: consistent filer dropzone buttons margin left
  • Loading branch information
fabien-michel committed Jun 19, 2023
1 parent 76c69f1 commit ed8f8a8
Show file tree
Hide file tree
Showing 16 changed files with 193 additions and 44 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.rst
Expand Up @@ -2,6 +2,11 @@
CHANGELOG
=========

unreleased
==================

* Add an edit button to the file widget which open edit file popup

Unpublished
===========

Expand Down Expand Up @@ -121,7 +126,6 @@ Unpublished
* Fix #1247: Not owned files in unfiled folder can not be listed if perms are ON.
* Fix #1184: OSError close file before deletion on file move.


2.0.2 (2020-09-10)
==================

Expand Down
9 changes: 8 additions & 1 deletion filer/admin/fileadmin.py
Expand Up @@ -69,6 +69,7 @@ def response_change(self, request, obj):
and '_continue' not in request.POST
and '_saveasnew' not in request.POST
and '_addanother' not in request.POST
and '_edit_from_widget' not in request.POST
):
# Popup in pick mode or normal mode. In both cases we want to go
# back to the folder list view after save. And not the useless file
Expand All @@ -84,7 +85,13 @@ def response_change(self, request, obj):
admin_url_params_encoded(request),
)
return HttpResponseRedirect(url)
return super().response_change(request, obj)

# Add media to context to allow default django js inclusions in django/filer/base_site.html ({{ media.js }})
# This is required by popup_handling.js used in popup_response
template_response = super().response_change(request, obj)
if hasattr(template_response, 'context_data'):
template_response.context_data["media"] = self.media
return template_response

def render_change_form(self, request, context, add=False, change=False,
form_url='', obj=None):
Expand Down
6 changes: 6 additions & 0 deletions filer/admin/tools.py
Expand Up @@ -66,6 +66,10 @@ def popup_pick_type(request):
return None


def edit_from_widget(request):
return request.GET.get('_edit_from_widget') == '1'


def get_directory_listing_type(request):
list_type = request.GET.get('_list_type', None)
if list_type not in settings.FILER_FOLDER_ADMIN_LIST_TYPE_CHOICES:
Expand All @@ -84,6 +88,8 @@ def admin_url_params(request, params=None):
pick_type = popup_pick_type(request)
if pick_type:
params['_pick'] = pick_type
if edit_from_widget(request):
params['_edit_from_widget'] = '1'
list_type = get_directory_listing_type(request)
if list_type and '_list_type' not in params.keys():
params['_list_type'] = list_type
Expand Down
3 changes: 3 additions & 0 deletions filer/fields/file.py
Expand Up @@ -28,10 +28,12 @@ def render(self, name, value, attrs=None, renderer=None):
obj = self.obj_for_value(value)
css_id = attrs.get('id', 'id_image_x')
related_url = None
change_url = ''
if value:
try:
file_obj = File.objects.get(pk=value)
related_url = file_obj.logical_folder.get_admin_directory_listing_url_path()
change_url = file_obj.get_admin_change_url()
except Exception as e:
# catch exception and manage it. We can re-raise it for debugging
# purposes and/or just logging it, provided user configured
Expand All @@ -58,6 +60,7 @@ def render(self, name, value, attrs=None, renderer=None):
context = {
'hidden_input': hidden_input,
'lookup_url': '%s%s' % (related_url, lookup_url),
'change_url': change_url,
'object': obj,
'lookup_name': name,
'id': css_id,
Expand Down
59 changes: 30 additions & 29 deletions filer/private/sass/components/_drag-and-drop.scss
Expand Up @@ -170,6 +170,9 @@ form .form-row {
width: auto !important;
height: auto !important;
padding: 10px 20px !important;
margin-top: 24px;
margin-left: 10px;
text-align: center !important;
cursor: pointer;
.fa {
color: $white;
Expand All @@ -181,11 +184,15 @@ form .form-row {
display: none;
}
.choose-file,
.replace-file,
.edit-file {
color: $white;
margin: 0;
}
.edit-file {
.replace-file {
display: none;
}
&.edit {
display: none;
}
&.related-lookup-change {
Expand All @@ -208,16 +215,17 @@ form .form-row {
.choose-file {
display: none;
}
.edit-file {
.replace-file {
display: block;
}
&.lookup {
display: block !important;
}
&.edit {
display: block;
}
}
}
// makes sure that filer clear button has correct size #669
.filerClearer {
width: 36px !important;
height: 36px !important;
}
.filerFile {
position: absolute;
top: 9px;
Expand All @@ -228,11 +236,11 @@ form .form-row {
background-color: $white;
}
// make sure that text crops if there is not enough space #670
span:not(:empty):not(.choose-file):not(.edit-file) {
span:not(:empty):not(.choose-file):not(.replace-file):not(.edit-file) {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
width: calc(100% - 200px);
width: calc(100% - 260px);
}
// required for django CMS <= 3.1 #673
img {
Expand All @@ -249,10 +257,6 @@ form .form-row {
border-radius: $border-radius-base;
}
}
.related-lookup {
text-align: center !important;
margin-top: 24px;
}
// required for django CMS <= 3.1
a {
box-sizing: border-box;
Expand All @@ -275,23 +279,20 @@ form .form-row {
.filerClearer {
@include button-variant($btn-default-color, $btn-default-bgcolor, $btn-default-border, true);
float: right;
// make sure that clear button has correct size #272
width: 10px;
height: 10px;
margin: 24px 0 0 5px !important;
padding: 12px !important;
border: solid 1px $gray-lighter;
border-radius: $border-radius-base;
padding: 12px;
margin: 24px 0 0 10px;
width: 36px;
height: 36px;
cursor: pointer;
&:before {
color: $gray-light !important;
vertical-align: middle;
margin: 0;
&:focus {
background-color: $white;
}
@media screen and (max-width: 1024px) {
margin-left: 15px !important;
span {
text-align: center;
line-height: 24px;
}
}

}
&.filer-dropzone-mobile {
.filerFile {
Expand All @@ -310,13 +311,13 @@ form .form-row {
@media screen and (max-width: $screen-tablet-filer) {
.description_text {
text-overflow: ellipsis;
width: calc(100% - 200px);
width: calc(100% - 250px);
overflow: hidden;
height: 20px;
}
}
>span:not(.choose-file):not(.edit-file), .dz-name {
width: calc(100% - 200px);
>span:not(.choose-file):not(.replace-file):not(.edit-file), .dz-name {
width: calc(100% - 250px);
}
}
}
Expand Down
5 changes: 2 additions & 3 deletions filer/static/filer/css/admin_filer.css

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion filer/static/filer/css/maps/admin_filer.css.map

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions filer/static/filer/js/addons/dropzone.init.js
Expand Up @@ -22,6 +22,7 @@ djQuery(function ($) {
var dropzones = $(dropzoneSelector);
var messageSelector = '.js-filer-dropzone-message';
var lookupButtonSelector = '.js-related-lookup';
var editButtonSelector = '.js-related-edit';
var progressSelector = '.js-filer-dropzone-progress';
var previewImageWrapperSelector = '.js-img-wrapper';
var filerClearerSelector = '.filerClearer';
Expand Down Expand Up @@ -56,6 +57,7 @@ djQuery(function ($) {
var inputId = dropzone.find(fileIdInputSelector);
var isImage = inputId.is('[name="image"]');
var lookupButton = dropzone.find(lookupButtonSelector);
var editButton = dropzone.find(editButtonSelector);
var message = dropzone.find(messageSelector);
var clearButton = dropzone.find(filerClearerSelector);
var fileChoose = dropzone.find(fileChooseSelector);
Expand Down Expand Up @@ -99,6 +101,7 @@ djQuery(function ($) {
this.removeAllFiles(true);
fileChoose.hide();
lookupButton.addClass('related-lookup-change');
editButton.addClass('related-lookup-change');
message.addClass(hiddenClass);
dropzone.removeClass(dragHoverClass);
dropzone.addClass(objectAttachedClass);
Expand Down Expand Up @@ -144,6 +147,7 @@ djQuery(function ($) {
dropzone.removeClass(objectAttachedClass);
inputId.val('');
lookupButton.removeClass('related-lookup-change');
editButton.removeClass('related-lookup-change');
message.removeClass(hiddenClass);
inputId.trigger('change');
}
Expand Down
17 changes: 17 additions & 0 deletions filer/static/filer/js/addons/filer_popup_response.js
@@ -0,0 +1,17 @@
/*global opener */
(function () {
'use strict';
var initData = JSON.parse(document.getElementById('django-admin-popup-response-constants').dataset.popupResponse);
switch (initData.action) {
case 'change':
// Specific function for file editing popup opened from widget
opener.dismissRelatedImageLookupPopup(window, initData.new_value, null, initData.obj, null);
break;
case 'delete':
opener.dismissDeleteRelatedObjectPopup(window, initData.value);
break;
default:
opener.dismissAddRelatedObjectPopup(window, initData.value, initData.obj);
break;
}
})();
19 changes: 16 additions & 3 deletions filer/static/filer/js/addons/popup_handling.js
Expand Up @@ -19,10 +19,17 @@ if (django.jQuery) {
document.location.reload();
win.close();
};
window.dismissRelatedImageLookupPopup = function (win, chosenId, chosenThumbnailUrl, chosenDescriptionTxt) {
window.dismissRelatedImageLookupPopup = function (
win,
chosenId,
chosenThumbnailUrl,
chosenDescriptionTxt,
chosenAdminChangeUrl
) {
var id = windowname_to_id(win.name);
var lookup = $('#' + id);
var container = lookup.closest('.filerFile');
var edit = container.find('.edit');
var image = container.find('.thumbnail_img');
var descriptionText = container.find('.description_text');
var clearer = container.find('.filerClearer');
Expand All @@ -32,11 +39,17 @@ if (django.jQuery) {

element.val(chosenId);
element.closest('.js-filer-dropzone').addClass('js-object-attached');
image.attr('src', chosenThumbnailUrl).removeClass('hidden');
image.removeAttr('srcset'); // would be nicer, but much more complicate to also replace 'srcset'
if (chosenThumbnailUrl) {
image.attr('src', chosenThumbnailUrl).removeClass('hidden');
image.removeAttr('srcset'); // would be nicer, but much more complicate to also replace 'srcset'
}
descriptionText.text(chosenDescriptionTxt);
clearer.removeClass('hidden');
lookup.addClass('related-lookup-change');
edit.addClass('related-lookup-change');
if (chosenAdminChangeUrl) {
edit.attr('href', chosenAdminChangeUrl + '?_edit_from_widget=1');
}
dropzoneMessage.addClass('hidden');

if (oldId !== chosenId) {
Expand Down
2 changes: 2 additions & 0 deletions filer/static/filer/js/addons/widget.js
Expand Up @@ -16,6 +16,7 @@ djQuery(function ($) {
var thumbnail = container.find('.thumbnail_img');
var description = container.find('.description_text');
var addImageButton = container.find('.lookup');
var editImageButton = container.find('.edit');
var dropzoneMessage = container.siblings('.dz-message');
var hiddenClass = 'hidden';

Expand All @@ -24,6 +25,7 @@ djQuery(function ($) {
thumbnail.addClass(hiddenClass);
thumbnail.parent('a').removeAttr('href');
addImageButton.removeClass('related-lookup-change');
editImageButton.removeClass('related-lookup-change');
dropzoneMessage.removeClass(hiddenClass);
description.empty();
};
Expand Down
10 changes: 10 additions & 0 deletions filer/templates/admin/filer/file/popup_response.html
@@ -0,0 +1,10 @@
{% extends "admin/filer/base_site.html" %}
{% load static %}

{% block extrahead %}
{{ block.super }}
<script id="django-admin-popup-response-constants"
src="{% static "filer/js/addons/filer_popup_response.js" %}"
data-popup-response="{{ popup_response_data }}">
</script>
{% endblock %}
6 changes: 3 additions & 3 deletions filer/templates/admin/filer/folder/directory_table_list.html
Expand Up @@ -92,15 +92,15 @@
<tr>
<td class="column-checkbox">
{% if is_popup and filer_admin_context.pick_file %}
<a class="insertlink insertlinkButton" href="#" onclick="opener.dismissRelatedImageLookupPopup(window, '{{ file.id|unlocalize }}', '{% file_icon_url file %}', '{{ file.label|escapejs }}'); return false;"
<a class="insertlink insertlinkButton" href="#" onclick="opener.dismissRelatedImageLookupPopup(window, '{{ file.id|unlocalize }}', '{% file_icon_url file %}', '{{ file.label|escapejs }}', '{{ file.get_admin_change_url }}'); return false;"
title="{% trans 'Select this file' %}">&nbsp;</a>
{% elif action_form and not is_popup %}
<input type="checkbox" class="action-select" value="file-{{ item.pk|safe }}" name="_selected_action">
{% endif %}
</td>
<td class="column-icon">
{% if is_popup and filer_admin_context.pick_file %}
<a href="#" onclick="opener.dismissRelatedImageLookupPopup(window, {{ file.id|unlocalize }}, '{% file_icon_url file %}', '{{ file.label|escapejs }}'); return false;"
<a href="#" onclick="opener.dismissRelatedImageLookupPopup(window, {{ file.id|unlocalize }}, '{% file_icon_url file %}', '{{ file.label|escapejs }}', '{{ file.get_admin_change_url }}'); return false;"
title="{% trans 'Select this file' %}">
{% elif has_change_permission %}
<a href="{{ file.get_admin_change_url }}{% filer_admin_context_url_params %}"
Expand All @@ -115,7 +115,7 @@
<div>
<strong>
{% if is_popup and filer_admin_context.pick_file %}
<a href="#" onclick="opener.dismissRelatedImageLookupPopup(window, {{ file.id|unlocalize }}, '{% file_icon_url file %}', '{{ file.label|escapejs }}'); return false;"
<a href="#" onclick="opener.dismissRelatedImageLookupPopup(window, {{ file.id|unlocalize }}, '{% file_icon_url file %}', '{{ file.label|escapejs }}', '{{ file.get_admin_change_url }}'); return false;"
title="{% trans 'Select this file' %}">
{% elif has_change_permission %}
<a href="{{ file.get_admin_change_url }}{% filer_admin_context_url_params %}"
Expand Down
9 changes: 7 additions & 2 deletions filer/templates/admin/filer/widgets/admin_file.html
Expand Up @@ -40,10 +40,15 @@
width="10" height="10" alt="{% trans 'Clear' %}" title="{% trans 'Clear' %}"
data-no-icon-file="{% static 'filer/icons/file-unknown.svg' %}">

<a href="{{ change_url }}?_edit_from_widget=1" class="js-related-edit related-lookup {% if object %}related-lookup-change{% endif %} edit" id="{{ id }}_change"
title="{% trans 'Edit' %}">
<span class="edit-file"><span class="fa fa-pencil"></span></span>
</a>

<a href="{{ lookup_url }}" class="js-related-lookup related-lookup {% if object %}related-lookup-change{% endif %} lookup" id="{{ id }}_lookup"
title="{% trans 'Lookup' %}">
<span class="choose-file"><span class="fa fa-picture-o"></span>{% trans 'Choose File' %}</span>
<span class="edit-file"><span class="fa fa-pencil"></span></span>
<span class="choose-file"><span class="fa fa-search"></span>{% trans 'Choose File' %}</span>
<span class="replace-file"><span class="fa fa-search"></span></span>
</a>

<br>
Expand Down

0 comments on commit ed8f8a8

Please sign in to comment.