Skip to content

Commit 95cdf42

Browse files
corentinbettiolMateusz Kamycki
andauthored
Add thumbnail view for folder list admin view (updated) (#1255)
* Add thumbnail view for folder list admin view * Cherry-pick changes from PR 1076 add thumbnail view for folder list admin view cherry-pick changes from #1076 fix small details (js tests, isort, flake8) maybe a solution to #723 ? * Add trans tags to directory_thumbnail_list.html template * Fix select all from all pages functionality on folder view * Fixes after code review * (re) compile css * MAINT: update css for thumbnail view * MAINT: use svg images * FIX: replace old img tag by file_icon templatetag Co-authored-by: Mateusz Kamycki <mateusz.kamycki@gmail.com>
1 parent 672d00b commit 95cdf42

File tree

17 files changed

+519
-14
lines changed

17 files changed

+519
-14
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,4 @@ pep8.txt
4242
share/
4343
.python-version
4444
data
45+
local.sqlite

filer/admin/folderadmin.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@
3535
from .permissions import PrimitivePermissionAwareModelAdmin
3636
from .tools import (
3737
AdminContext, admin_url_params_encoded, check_files_edit_permissions, check_files_read_permissions,
38-
check_folder_edit_permissions, check_folder_read_permissions, popup_status, userperms_for_request,
38+
check_folder_edit_permissions, check_folder_read_permissions, get_directory_listing_type, popup_status,
39+
userperms_for_request,
3940
)
4041

4142

@@ -406,6 +407,7 @@ def directory_listing(self, request, folder_id=None, viewtype=None):
406407
except EmptyPage:
407408
paginated_items = paginator.page(paginator.num_pages)
408409

410+
list_type = get_directory_listing_type(request) or settings.FILER_FOLDER_ADMIN_DEFAULT_LIST_TYPE
409411
context = self.admin_site.each_context(request)
410412
context.update({
411413
'folder': folder,
@@ -436,6 +438,8 @@ def directory_listing(self, request, folder_id=None, viewtype=None):
436438
'actions_selection_counter': self.actions_selection_counter,
437439
'selection_note': _('0 of %(cnt)s selected') % {'cnt': len(paginated_items.object_list)},
438440
'selection_note_all': selection_note_all % {'total_count': paginator.count},
441+
'list_type': list_type,
442+
'list_type_template': settings.FILER_FOLDER_ADMIN_LIST_TYPE_SWITCHER_SETTINGS[list_type]['template'],
439443
'media': self.media,
440444
'enable_permissions': settings.FILER_ENABLE_PERMISSIONS,
441445
'can_make_folder': request.user.is_superuser or (folder.is_root and settings.FILER_ALLOW_REGULAR_USERS_TO_ADD_ROOT_FOLDERS) or permissions.get("has_add_children_permission"),

filer/admin/tools.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
from django.core.exceptions import PermissionDenied
33
from django.utils.http import urlencode
44

5+
from .. import settings
6+
57

68
ALLOWED_PICK_TYPES = ('folder', 'file')
79

@@ -64,6 +66,13 @@ def popup_pick_type(request):
6466
return None
6567

6668

69+
def get_directory_listing_type(request):
70+
list_type = request.GET.get('_list_type', None)
71+
if list_type not in settings.FILER_FOLDER_ADMIN_LIST_TYPE_CHOICES:
72+
return
73+
return list_type
74+
75+
6776
def admin_url_params(request, params=None):
6877
"""
6978
given a request, looks at GET and POST values to determine which params
@@ -75,6 +84,9 @@ def admin_url_params(request, params=None):
7584
pick_type = popup_pick_type(request)
7685
if pick_type:
7786
params['_pick'] = pick_type
87+
list_type = get_directory_listing_type(request)
88+
if list_type and '_list_type' not in params.keys():
89+
params['_list_type'] = list_type
7890
return params
7991

8092

filer/private/sass/components/_base.scss

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,3 +139,15 @@ body {
139139
box-shadow: 0 0 10px -2px rgba(black, 0.2);
140140
background-color: $white;
141141
}
142+
143+
.navigator .actions span.all,
144+
.navigator .actions span.clear,
145+
.navigator .actions span.question {
146+
font-size: 13px;
147+
margin: 0 0.5em;
148+
display: none;
149+
}
150+
151+
#all-items-action-toggle {
152+
display: none !important;
153+
}

filer/private/sass/components/_navigator.scss

Lines changed: 137 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,19 @@ body {
2121
border-bottom: solid 2px $color-primary;
2222
pointer-events: none;
2323
z-index: 100;
24+
display: none;
2425
}
25-
26+
.thumbnail-drag-hover-border {
27+
border: solid 2px $color-primary;
28+
}
29+
.filebrowser .navigator-list,
2630
.filebrowser .navigator-table {
2731
// required for django CMS <= 3.1 #673
2832
width: 100%;
2933
margin: 0;
3034
border-top: solid 1px $gray-lighter !important;
3135
border-collapse: collapse !important;
36+
.navigator-header,
3237
thead th,
3338
tbody td {
3439
text-align: left;
@@ -45,6 +50,7 @@ body {
4550
color: $gray-darkest !important;
4651
}
4752
}
53+
.navigator-body,
4854
.unfiled td {
4955
padding: 12px 5px !important;
5056
background-color: $gray-super-light !important;
@@ -284,6 +290,7 @@ body {
284290
width: calc(100% - 30px);
285291
}
286292
}
293+
287294
.navigator-tools {
288295
@include clearfix;
289296
white-space: nowrap;
@@ -340,6 +347,23 @@ body {
340347
border-left: solid 1px $gray-lighter;
341348
}
342349
}
350+
.filer-list-type-switcher-wrapper {
351+
display: inline-block;
352+
line-height: 34px;
353+
vertical-align: middle;
354+
padding: 0 15px;
355+
border-left: 1px solid $gray-lighter;
356+
border-right: 1px solid $gray-lighter;
357+
margin-right: 10px;
358+
a {
359+
font-size: 17px;
360+
color: $gray !important;
361+
cursor: pointer;
362+
}
363+
@media screen and (max-width: $screen-tablet) {
364+
margin-top: 10px;
365+
}
366+
}
343367
}
344368
@media screen and (max-width: $screen-tablet) {
345369
.navigator-top-nav {
@@ -682,3 +706,115 @@ body {
682706
display: table-cell;
683707
vertical-align: middle;
684708
}
709+
710+
.filebrowser .navigator-thumbnail-list {
711+
overflow: hidden;
712+
.navigator-list {
713+
border-top: 0 !important;
714+
}
715+
.navigator-thumbnail-list-header {
716+
& > * {
717+
display: inline-block;
718+
text-transform: uppercase;
719+
margin: 0;
720+
padding: 0;
721+
padding-left: 10px;
722+
}
723+
.navigator-checkbox {
724+
float: right;
725+
text-transform: initial;
726+
color: $color-primary;
727+
input[type="checkbox"] {
728+
margin-left: 5px;
729+
vertical-align: middle;
730+
}
731+
}
732+
}
733+
.navigator-body {
734+
@include clearfix;
735+
padding: 0 !important;
736+
}
737+
.thumbnail-item {
738+
float: left;
739+
display: inline-block;
740+
padding: 10px;
741+
width: 125px;
742+
height: 125px;
743+
border: 1px solid $gray-lighter;
744+
margin: 16px 12px;
745+
background-color: $white;
746+
position: relative;
747+
overflow: hidden;
748+
.thumbnail-file-item-box {
749+
padding: 10px;
750+
width: 125px;
751+
height: 125px;
752+
border: 1px solid $gray-lighter;
753+
margin: 16px 12px;
754+
background-color: $white;
755+
&:hover {
756+
background-color: #f1faff;
757+
}
758+
}
759+
.navigator-checkbox {
760+
position: absolute;
761+
top: 5px;
762+
left: 5px;
763+
input {
764+
margin: 0;
765+
vertical-align: top;
766+
}
767+
}
768+
.item-thumbnail,
769+
.item-icon {
770+
height: 50%;
771+
width: 50%;
772+
margin: 10px auto;
773+
margin-bottom: 18px;
774+
a {
775+
display: block;
776+
height: 100%;
777+
width: 100%;
778+
}
779+
img {
780+
width: 100%;
781+
height: 100%;
782+
}
783+
}
784+
.item-name {
785+
background: transparent;
786+
text-align: center;
787+
word-break: break-word;
788+
}
789+
}
790+
.thumbnail-virtual-item {
791+
background-color: initial;
792+
}
793+
.thumbnail-folder-item {
794+
&:hover {
795+
background-color: #f1faff;
796+
}
797+
}
798+
.thumbnail-file-item {
799+
float: none;
800+
width: 147px;
801+
height: 200px;
802+
border: 0;
803+
padding: 0;
804+
background-color: transparent;
805+
.thumbnail-file-item-box {
806+
float: none;
807+
margin: 0;
808+
margin-bottom: 5px;
809+
}
810+
.item-thumbnail {
811+
margin: 0;
812+
height: 100%;
813+
width: 100%;
814+
}
815+
.item-name {
816+
position: relative;
817+
word-break: break-word;
818+
}
819+
}
820+
}

filer/settings.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from django.conf import settings
55
from django.core.exceptions import ImproperlyConfigured
66
from django.core.files.storage import get_storage_class
7+
from django.utils.translation import ugettext_lazy as _
78

89
from .utils.loader import load_object
910
from .utils.recursive_dictionary import RecursiveDictionaryWithExcludes
@@ -250,3 +251,26 @@ def update_server_settings(settings, defaults, s, t):
250251
FILER_DUMP_PAYLOAD = getattr(settings, 'FILER_DUMP_PAYLOAD', False) # Whether the filer shall dump the files payload
251252

252253
FILER_CANONICAL_URL = getattr(settings, 'FILER_CANONICAL_URL', 'canonical/')
254+
255+
TABLE_LIST_TYPE = 'tb'
256+
THUMBNAIL_LIST_TYPE = 'th'
257+
FILER_FOLDER_ADMIN_LIST_TYPE_CHOICES = (
258+
TABLE_LIST_TYPE,
259+
THUMBNAIL_LIST_TYPE,
260+
)
261+
FILER_FOLDER_ADMIN_DEFAULT_LIST_TYPE = getattr(settings, 'FILER_FOLDER_ADMIN_DEFAULT_LIST_TYPE', TABLE_LIST_TYPE)
262+
if FILER_FOLDER_ADMIN_DEFAULT_LIST_TYPE not in FILER_FOLDER_ADMIN_LIST_TYPE_CHOICES:
263+
FILER_FOLDER_ADMIN_DEFAULT_LIST_TYPE = TABLE_LIST_TYPE
264+
265+
FILER_FOLDER_ADMIN_LIST_TYPE_SWITCHER_SETTINGS = {
266+
TABLE_LIST_TYPE: {
267+
'icon': 'th-list',
268+
'tooltip_text': _('Show table view'),
269+
'template': 'admin/filer/folder/directory_table_list.html',
270+
},
271+
THUMBNAIL_LIST_TYPE: {
272+
'icon': 'th-large',
273+
'tooltip_text': _('Show thumbnail view'),
274+
'template': 'admin/filer/folder/directory_thumbnail_list.html',
275+
},
276+
}

filer/static/filer/css/admin_filer.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

filer/static/filer/css/maps/admin_filer.css.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

filer/static/filer/js/addons/table-dropzone.js

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,12 +129,16 @@ if (django.jQuery) {
129129
var folderTitle = $(dragEvent.target).closest(dropzoneSelector).data('folder-name');
130130
var dropzoneFolder = dropzone.hasClass('js-filer-dropzone-folder');
131131
var dropzoneBoundingRect = dropzone[0].getBoundingClientRect();
132-
var borderSize = $('.drag-hover-border').css('border-top-width');
132+
var topBorderSize = $('.drag-hover-border').css('border-top-width');
133+
var leftBorderSize = $('.drag-hover-border').css('border-left-width');
133134
var dropzonePosition = {
134135
top: dropzoneBoundingRect.top,
135136
bottom: dropzoneBoundingRect.bottom,
136-
width: dropzoneBoundingRect.width,
137-
height: dropzoneBoundingRect.height - (parseInt(borderSize, 10) * 2)
137+
left: dropzoneBoundingRect.left,
138+
right: dropzoneBoundingRect.right,
139+
width: dropzoneBoundingRect.width - parseInt(leftBorderSize, 10) * 2,
140+
height: dropzoneBoundingRect.height - parseInt(topBorderSize, 10) * 2,
141+
display: 'block'
138142
};
139143
if (dropzoneFolder) {
140144
dragHoverBorder.css(dropzonePosition);

filer/static/filer/js/base.js

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,11 @@ Cl.mediator = new Mediator();
8383

8484
// show counter if file is selected
8585
(function () {
86-
var navigatorTable = $('.navigator-table').find('tr');
86+
var navigatorTable = $('.navigator-table, .navigator-list').find('tr, .list-item');
8787
var actionList = $('.actions-wrapper');
88-
var actionSelect = $('.action-select, #action-toggle, .actions .clear a');
88+
var actionSelect = $(
89+
'.action-select, #action-toggle, #files-action-toggle, #folders-action-toggle, .actions .clear a'
90+
);
8991

9092
// timeout is needed to wait until table row has class selected.
9193
setTimeout(function () {
@@ -120,7 +122,7 @@ Cl.mediator = new Mediator();
120122
var valueDelete = 'delete_files_or_folders';
121123
var valueCopy = 'copy_files_and_folders';
122124
var valueMove = 'move_files_and_folders';
123-
var navigatorTable = $('.navigator-table').find('tr');
125+
var navigatorTable = $('.navigator-table, .navigator-list').find('tr, .list-item');
124126

125127
// triggers delete copy and move actions on separate buttons
126128
function actionsButton(optionValue, actionButton) {
@@ -206,5 +208,57 @@ Cl.mediator = new Mediator();
206208
});
207209

208210
}());
211+
// thumbnail folder admin view
212+
(function () {
213+
$(document).ready(function () {
214+
var $actionEls = $('.navigator-list .list-item input.action-select'),
215+
foldersActionCheckboxes = '.navigator-list .navigator-folders-body .list-item input.action-select',
216+
filesActionCheckboxes = '.navigator-list .navigator-files-body .list-item input.action-select',
217+
$allFilesToggle = $('#files-action-toggle'),
218+
$allFoldersToggle = $('#folders-action-toggle');
219+
220+
if ($actionEls.length > 0) {
221+
$actionEls.actions({
222+
allToggle: '#all-items-action-toggle'
223+
});
224+
}
225+
226+
$allFoldersToggle.on('click', function () {
227+
if (!!$(this).prop('checked')) {
228+
$(foldersActionCheckboxes).filter(':not(:checked)').trigger('click');
229+
} else {
230+
$(foldersActionCheckboxes).filter(':checked').trigger('click');
231+
}
232+
});
233+
$allFilesToggle.on('click', function () {
234+
if (!!$(this).prop('checked')) {
235+
$(filesActionCheckboxes).filter(':not(:checked)').trigger('click');
236+
} else {
237+
$(filesActionCheckboxes).filter(':checked').trigger('click');
238+
}
239+
});
240+
$actionEls.on('click', function () {
241+
if (!$(this).prop('checked')) {
242+
if (!!$(filesActionCheckboxes).filter(':not(:checked)').length) {
243+
$allFilesToggle.prop('checked', false);
244+
}
245+
if (!!$(foldersActionCheckboxes).filter(':not(:checked)').length) {
246+
$allFoldersToggle.prop('checked', false);
247+
}
248+
} else {
249+
if (!$(filesActionCheckboxes).filter(':not(:checked)').length) {
250+
$allFilesToggle.prop('checked', true);
251+
}
252+
if (!$(foldersActionCheckboxes).filter(':not(:checked)').length) {
253+
$allFoldersToggle.prop('checked', true);
254+
}
255+
}
256+
});
257+
$('.navigator .actions .clear a').on('click', function () {
258+
$allFoldersToggle.prop('checked', false);
259+
$allFilesToggle.prop('checked', false);
260+
});
261+
});
262+
})();
209263
});
210264
})(djQuery);

0 commit comments

Comments
 (0)