From 4565069f93bfd1c9d54e031036b4f4e23f1d4e70 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 30 Apr 2026 09:36:36 +0000
Subject: [PATCH 1/6] Phase 1-2: Foundation, build system, and non-conflicting
upstream additions
Agent-Logs-Url: https://github.com/pbs/django-filer/sessions/cad30a66-0889-48ba-bb3e-2ab3bf2939b2
Co-authored-by: andreilupuleasa <124138251+andreilupuleasa@users.noreply.github.com>
---
filer/__init__.py | 17 +-
filer/admin/permissionadmin.py | 56 ++++
filer/admin/permissions.py | 48 +++
filer/admin/thumbnailoptionadmin.py | 5 +
filer/admin/views.py | 131 ++++++++
filer/apps.py | 61 ++++
filer/cache.py | 91 ++++++
filer/contrib/__init__.py | 0
filer/contrib/clamav/__init__.py | 0
filer/contrib/django_cms/__init__.py | 0
filer/contrib/django_cms/cms_toolbars.py | 51 ++++
filer/management/commands/filer_check.py | 183 ++++++++++++
.../commands/generate_thumbnails.py | 28 ++
filer/models/abstract.py | 282 ++++++++++++++++++
filer/models/thumbnailoptionmodels.py | 61 ++++
filer/urls.py | 13 +
filer/utils/compatibility.py | 33 ++
filer/validation.py | 115 +++++++
filer/views.py | 22 +-
pyproject.toml | 108 +++++++
20 files changed, 1301 insertions(+), 4 deletions(-)
create mode 100644 filer/admin/permissionadmin.py
create mode 100644 filer/admin/permissions.py
create mode 100644 filer/admin/thumbnailoptionadmin.py
create mode 100644 filer/admin/views.py
create mode 100644 filer/cache.py
create mode 100644 filer/contrib/__init__.py
create mode 100644 filer/contrib/clamav/__init__.py
create mode 100644 filer/contrib/django_cms/__init__.py
create mode 100644 filer/contrib/django_cms/cms_toolbars.py
create mode 100644 filer/management/commands/filer_check.py
create mode 100644 filer/management/commands/generate_thumbnails.py
create mode 100644 filer/models/abstract.py
create mode 100644 filer/models/thumbnailoptionmodels.py
create mode 100644 filer/urls.py
create mode 100644 filer/utils/compatibility.py
create mode 100644 filer/validation.py
create mode 100644 pyproject.toml
diff --git a/filer/__init__.py b/filer/__init__.py
index f705a2ba8..bde226217 100644
--- a/filer/__init__.py
+++ b/filer/__init__.py
@@ -1,3 +1,14 @@
-#-*- coding: utf-8 -*-
-# version string following pep-0440
-__version__ = '0.9.123' # pragma: nocover
+"""
+See PEP 386 (https://www.python.org/dev/peps/pep-0386/)
+
+Release logic:
+ 1. Increase version number (change __version__ below).
+ 2. Check that all changes have been documented in CHANGELOG.rst.
+ 3. git add filer/__init__.py CHANGELOG.rst
+ 4. git commit -m 'Bump to {new version}'
+ 5. git push
+ 6. Assure that all tests pass on CI
+ 7. Create a new release on github.
+"""
+
+__version__ = '3.5.0.pbs.1'
diff --git a/filer/admin/permissionadmin.py b/filer/admin/permissionadmin.py
new file mode 100644
index 000000000..f3b03bd36
--- /dev/null
+++ b/filer/admin/permissionadmin.py
@@ -0,0 +1,56 @@
+from django import VERSION as django_version
+from django.contrib import admin
+from django.contrib.auth import get_user_model
+from django.utils.translation import gettext_lazy as _
+
+from .. import settings
+from ..cache import clear_folder_permission_cache
+
+
+class PermissionAdmin(admin.ModelAdmin):
+ fieldsets = [
+ (None, {'fields': ['type', 'folder']}),
+ (_("Who"), {'fields': ['user', 'group', 'everybody']}),
+ (_("What"), {'fields': ['can_edit', 'can_read', 'can_add_children']}),
+ ]
+ list_filter = ['group']
+ list_display = ['pretty_logical_path', 'who', 'what']
+ search_fields = ['user__username', 'group__name', 'folder__name']
+ autocomplete_fields = ['user', 'group', 'folder']
+
+ class Media:
+ css = {'all': ['filer/css/admin_folderpermissions.css']}
+
+ def get_autocomplete_fields(self, request):
+ """Remove "owner" from autocomplete_fields if User model has no search_fields"""
+
+ autocomplete_fields = super().get_autocomplete_fields(request)
+ if django_version >= (5, 0):
+ user_admin = self.admin_site.get_model_admin(get_user_model())
+ else:
+ user_admin = self.admin_site._registry[get_user_model()]
+ if not user_admin.get_search_fields(request):
+ autocomplete_fields.remove('user')
+ return autocomplete_fields
+
+ def get_queryset(self, request):
+ qs = super().get_queryset(request)
+ return qs.prefetch_related("group", "folder")
+
+ def get_model_perms(self, request):
+ # don't display the permissions admin if permissions are disabled.
+ # This method is easier for testing than not registering the admin at all at import time
+ enable_permissions = settings.FILER_ENABLE_PERMISSIONS and request.user.has_perm('filer.add_folderpermission')
+ return {
+ 'add': enable_permissions,
+ 'change': enable_permissions,
+ 'delete': enable_permissions,
+ }
+
+ def save_model(self, request, obj, form, change):
+ clear_folder_permission_cache(request.user)
+ super().save_model(request, obj, form, change)
+
+ def delete_model(self, request, obj):
+ clear_folder_permission_cache(request.user)
+ super().delete_model(request, obj)
diff --git a/filer/admin/permissions.py b/filer/admin/permissions.py
new file mode 100644
index 000000000..a747990bc
--- /dev/null
+++ b/filer/admin/permissions.py
@@ -0,0 +1,48 @@
+from django import VERSION as django_version
+from django.contrib import admin
+from django.contrib.auth import get_user_model
+from django.urls import reverse
+
+
+class PrimitivePermissionAwareModelAdmin(admin.ModelAdmin):
+ def get_autocomplete_fields(self, request):
+ """Remove "owner" from autocomplete_fields if User model has no search_fields"""
+
+ autocomplete_fields = super().get_autocomplete_fields(request)
+ if django_version >= (5, 0):
+ user_admin = self.admin_site.get_model_admin(get_user_model())
+ else:
+ user_admin = self.admin_site._registry[get_user_model()]
+ if not user_admin.get_search_fields(request) and 'owner' in autocomplete_fields:
+ autocomplete_fields.remove('owner')
+ return autocomplete_fields
+
+ def has_add_permission(self, request):
+ # we don't have a "add" permission... but all adding is handled
+ # by special methods that go around these permissions anyway
+ # TODO: reactivate return False
+ return False
+
+ def has_change_permission(self, request, obj=None):
+ if hasattr(obj, 'has_edit_permission'):
+ if obj.has_edit_permission(request):
+ return True
+ else:
+ return False
+ else:
+ return True
+
+ def has_delete_permission(self, request, obj=None):
+ # we don't have a specific delete permission... so we use change
+ return self.has_change_permission(request, obj)
+
+ def _get_post_url(self, obj):
+ """
+ Needed to retrieve the changelist url as Folder/File can be extended
+ and admin url may change
+ """
+ # Code from django ModelAdmin to determine changelist on the fly
+ opts = obj._meta
+ return reverse('admin:%s_%s_changelist' %
+ (opts.app_label, opts.model_name),
+ current_app=self.admin_site.name)
diff --git a/filer/admin/thumbnailoptionadmin.py b/filer/admin/thumbnailoptionadmin.py
new file mode 100644
index 000000000..9bdf5ff26
--- /dev/null
+++ b/filer/admin/thumbnailoptionadmin.py
@@ -0,0 +1,5 @@
+from django.contrib import admin
+
+
+class ThumbnailOptionAdmin(admin.ModelAdmin):
+ list_display = ('name', 'width', 'height')
diff --git a/filer/admin/views.py b/filer/admin/views.py
new file mode 100644
index 000000000..542cff139
--- /dev/null
+++ b/filer/admin/views.py
@@ -0,0 +1,131 @@
+from django import forms
+from django.contrib import admin
+from django.contrib.admin import widgets
+from django.contrib.auth.decorators import login_required
+from django.core.exceptions import PermissionDenied
+from django.http import HttpResponseRedirect
+from django.http.response import HttpResponseBadRequest
+from django.template.response import TemplateResponse
+from django.utils.translation import gettext_lazy as _
+
+from .. import settings as filer_settings
+from ..models import Clipboard, Folder, FolderRoot, tools
+from .tools import AdminContext, admin_url_params_encoded, popup_status
+
+
+class NewFolderForm(forms.ModelForm):
+ class Meta:
+ model = Folder
+ fields = ('name',)
+ widgets = {
+ 'name': widgets.AdminTextInputWidget,
+ }
+
+
+@login_required
+def make_folder(request, folder_id=None):
+ if not folder_id:
+ folder_id = request.GET.get('parent_id')
+ if not folder_id:
+ folder_id = request.POST.get('parent_id')
+ if folder_id:
+ try:
+ folder = Folder.objects.get(id=folder_id)
+ except Folder.DoesNotExist:
+ raise PermissionDenied
+ else:
+ folder = None
+
+ if request.user.is_superuser:
+ pass
+ elif folder is None:
+ # regular users may not add root folders unless configured otherwise
+ if not filer_settings.FILER_ALLOW_REGULAR_USERS_TO_ADD_ROOT_FOLDERS:
+ raise PermissionDenied
+ elif not folder.has_add_children_permission(request):
+ # the user does not have the permission to add subfolders
+ raise PermissionDenied
+
+ if request.method == 'POST':
+ new_folder_form = NewFolderForm(request.POST)
+ if new_folder_form.is_valid():
+ new_folder = new_folder_form.save(commit=False)
+ if (folder or FolderRoot()).contains_folder(new_folder.name):
+ new_folder_form._errors['name'] = new_folder_form.error_class(
+ [_('Folder with this name already exists.')])
+ else:
+ context = admin.site.each_context(request)
+ new_folder.parent = folder
+ new_folder.owner = request.user
+ new_folder.save()
+ return TemplateResponse(request, 'admin/filer/dismiss_popup.html', context)
+ else:
+ new_folder_form = NewFolderForm()
+
+ context = admin.site.each_context(request)
+ context.update({
+ 'opts': Folder._meta,
+ 'new_folder_form': new_folder_form,
+ 'is_popup': popup_status(request),
+ 'filer_admin_context': AdminContext(request),
+ })
+ return TemplateResponse(request, 'admin/filer/folder/new_folder_form.html', context)
+
+
+@login_required
+def paste_clipboard_to_folder(request):
+ if True:
+ # TODO: cleanly remove Clipboard code if it is no longer needed
+ return HttpResponseBadRequest('not implemented anymore')
+
+ if request.method == 'POST':
+ folder = Folder.objects.get(id=request.POST.get('folder_id'))
+ clipboard = Clipboard.objects.get(id=request.POST.get('clipboard_id'))
+ if folder.has_add_children_permission(request):
+ tools.move_files_from_clipboard_to_folder(clipboard, folder)
+ tools.discard_clipboard(clipboard)
+ else:
+ raise PermissionDenied
+ redirect = request.GET.get('redirect_to', '')
+ if not redirect:
+ redirect = request.POST.get('redirect_to', '')
+ return HttpResponseRedirect(
+ '{}?order_by=-modified_at{}'.format(
+ redirect,
+ admin_url_params_encoded(request, first_separator='&'),
+ )
+ )
+
+
+@login_required
+def discard_clipboard(request):
+ if True:
+ # TODO: cleanly remove Clipboard code if it is no longer needed
+ return HttpResponseBadRequest('not implemented anymore')
+
+ if request.method == 'POST':
+ clipboard = Clipboard.objects.get(id=request.POST.get('clipboard_id'))
+ tools.discard_clipboard(clipboard)
+ return HttpResponseRedirect(
+ '{}{}'.format(
+ request.POST.get('redirect_to', ''),
+ admin_url_params_encoded(request, first_separator='&'),
+ )
+ )
+
+
+@login_required
+def delete_clipboard(request):
+ if True:
+ # TODO: cleanly remove Clipboard code if it is no longer needed
+ return HttpResponseBadRequest('not implemented anymore')
+
+ if request.method == 'POST':
+ clipboard = Clipboard.objects.get(id=request.POST.get('clipboard_id'))
+ tools.delete_clipboard(clipboard)
+ return HttpResponseRedirect(
+ '{}{}'.format(
+ request.POST.get('redirect_to', ''),
+ admin_url_params_encoded(request, first_separator='&'),
+ )
+ )
diff --git a/filer/apps.py b/filer/apps.py
index f92cd7939..ce0dc38a1 100644
--- a/filer/apps.py
+++ b/filer/apps.py
@@ -1,8 +1,69 @@
+import mimetypes
+
from django.apps import AppConfig
+from django.core.exceptions import ImproperlyConfigured
from django.utils.translation import gettext_lazy as _
+
from filer import __version__ as filer_version
class FilerConfig(AppConfig):
+ default_auto_field = 'django.db.models.AutoField'
name = 'filer'
verbose_name = _(f"Filer ({filer_version})")
+
+ def register_optional_heif_supprt(self):
+ try: # pragma: no cover
+ from pillow_heif import register_heif_opener
+
+ from .settings import IMAGE_EXTENSIONS, IMAGE_MIME_TYPES
+
+ # Register with easy_thumbnails
+ register_heif_opener()
+ HEIF_EXTENSIONS = [".heic", ".heics", ".heif", ".heifs", ".hif"]
+ # Add extensions to python mimetypes which filer uses
+ for ext in HEIF_EXTENSIONS:
+ mimetypes.add_type("image/heic", ext)
+ # Mark them as images
+ IMAGE_EXTENSIONS += HEIF_EXTENSIONS
+ IMAGE_MIME_TYPES.append("heic")
+ except (ModuleNotFoundError, ImportError):
+ # No heif support installed
+ pass
+
+ def resolve_validators(self):
+ """Resolve dotted path file validators"""
+
+ import importlib
+
+ from filer.settings import FILE_VALIDATORS, FILER_MIME_TYPE_WHITELIST
+
+ if (
+ not isinstance(FILER_MIME_TYPE_WHITELIST, (list, tuple)) or # noqa W504
+ any(map(lambda x: not isinstance(x, str), FILER_MIME_TYPE_WHITELIST))
+ ): # pragma: no cover
+ raise ImproperlyConfigured(
+ "filer: setting FILER_MIME_TYPE_WHITELIST needs to be a list or tuple of strings"
+ )
+ self.MIME_TYPE_WHITELIST = FILER_MIME_TYPE_WHITELIST
+ self.FILE_VALIDATORS = {}
+ for mime_type, validators in FILE_VALIDATORS.items():
+ functions = []
+ for item in validators:
+ if callable(item): # pragma: no cover
+ functions.append(item)
+ else:
+ split = item.rsplit(".", 1)
+ try:
+ module = importlib.import_module(split[0])
+ functions.append(getattr(module, split[-1]))
+ except (ImportError, ModuleNotFoundError, AttributeError):
+ raise ImproperlyConfigured(f"""filer: could not import validator "{item}".""")
+ self.FILE_VALIDATORS[mime_type] = functions
+
+ def ready(self):
+ # Make webp mime type known to python (needed for python < 3.11)
+ mimetypes.add_type("image/webp", ".webp")
+ #
+ self.resolve_validators()
+ self.register_optional_heif_supprt()
diff --git a/filer/cache.py b/filer/cache.py
new file mode 100644
index 000000000..d9861ac60
--- /dev/null
+++ b/filer/cache.py
@@ -0,0 +1,91 @@
+import typing
+
+from django.core.cache import cache
+from django.db.models import Model
+
+
+UserModel = typing.TypeVar('UserModel', bound=Model)
+
+
+def get_folder_perm_cache_key(user: UserModel, permission: str) -> str:
+ """
+ Generates a unique cache key for a given user and permission.
+
+ The key is a string in the format "filer:perm:", i.e. it does not
+ contain the user id. This will be sufficient for most use cases.
+
+ Patch this method to include the user id in the cache key if necessary, e.g.,
+ for far more than 1,000 admin users to make the cached unit require less memory.
+
+ Parameters:
+ user (UserModel): The user for whom the cache key is being generated.
+ The `user` can be an instance of the default `django.contrib.auth.models.User`
+ or any custom user model specified by `AUTH_USER_MODEL` in the settings.
+ permission (str): The permission for which the cache key is being generated.
+
+ Returns:
+ str: The generated cache key.
+ """
+ return f"filer:perm:{permission}"
+
+
+def get_folder_permission_cache(user: UserModel, permission: str) -> typing.Optional[typing.Set[int]]:
+ """
+ Retrieves the cached folder permissions for a given user and permission.
+
+ If the cache value exists, it returns the permissions for the user.
+ If the cache value does not exist, it returns None.
+
+ Parameters:
+ user (UserModel): The user for whom the permissions are being retrieved.
+ The `user` can be an instance of the default `django.contrib.auth.models.User`
+ or any custom user model specified by `AUTH_USER_MODEL` in the settings.
+ permission (str): The permission for which the permissions are being retrieved.
+
+ Returns:
+ set or None: The permissions for the user, or None if no cache value exists.
+ """
+ cache_value = cache.get(get_folder_perm_cache_key(user, permission))
+ if cache_value:
+ return cache_value.get(user.pk, None)
+ return None
+
+
+def clear_folder_permission_cache(user: UserModel, permission: typing.Optional[str] = None) -> None:
+ """
+ Clears the cached folder permissions for a given user.
+
+ If a specific permission is provided, it clears the cache for that permission only.
+ If no specific permission is provided, it clears the cache for all permissions.
+
+ Parameters:
+ user (UserModel): The user for whom the permissions are being cleared.
+ The `user` can be an instance of the default `django.contrib.auth.models.User`
+ or any custom user model specified by `AUTH_USER_MODEL` in the settings.
+ permission (str, optional): The specific permission to clear. Defaults to None.
+ """
+ if permission is None:
+ for perm in ['can_read', 'can_edit', 'can_add_children']:
+ cache.delete(get_folder_perm_cache_key(user, perm))
+ else:
+ cache.delete(get_folder_perm_cache_key(user, permission))
+
+
+def update_folder_permission_cache(user: UserModel, permission: str, id_list: typing.Set[int]) -> None:
+ """
+ Updates the cached folder permissions for a given user and permission.
+
+ It first retrieves the current permissions from the cache (or an empty dictionary if none exist).
+ Then it updates the permissions for the user with the provided list of IDs.
+ Finally, it sets the updated permissions back into the cache.
+
+ Parameters:
+ user (UserModel): The user for whom the permissions are being updated.
+ The `user` can be an instance of the default `django.contrib.auth.models.User`
+ or any custom user model specified by `AUTH_USER_MODEL` in the settings.
+ permission (str): The permission to update.
+ id_list (set): The set of IDs to set as the new permissions.
+ """
+ perms = cache.get(get_folder_perm_cache_key(user, permission)) or {}
+ perms[user.pk] = set(id_list)
+ cache.set(get_folder_perm_cache_key(user, permission), perms)
diff --git a/filer/contrib/__init__.py b/filer/contrib/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/filer/contrib/clamav/__init__.py b/filer/contrib/clamav/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/filer/contrib/django_cms/__init__.py b/filer/contrib/django_cms/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/filer/contrib/django_cms/cms_toolbars.py b/filer/contrib/django_cms/cms_toolbars.py
new file mode 100644
index 000000000..a42928dd2
--- /dev/null
+++ b/filer/contrib/django_cms/cms_toolbars.py
@@ -0,0 +1,51 @@
+from django.urls import reverse
+from django.utils.encoding import force_str
+from django.utils.translation import gettext_lazy as _
+
+from cms.cms_toolbars import ADMIN_MENU_IDENTIFIER, ADMINISTRATION_BREAK
+from cms.toolbar.items import Break
+from cms.toolbar_base import CMSToolbar
+from cms.toolbar_pool import toolbar_pool
+
+
+SHORTCUTS_BREAK = 'Shortcuts Break'
+
+
+@toolbar_pool.register
+class FilerToolbar(CMSToolbar):
+ """
+ Adds a Filer menu-item into django CMS's "ADMIN" (first) menu.
+ """
+ @staticmethod
+ def get_insert_position(admin_menu, item_name):
+ """
+ Ensures that there is a SHORTCUTS_BREAK and returns a position for an
+ alphabetical position against all items between SHORTCUTS_BREAK, and
+ the ADMINISTRATION_BREAK.
+ """
+ start = admin_menu.find_first(Break, identifier=SHORTCUTS_BREAK)
+ if not start:
+ end = admin_menu.find_first(Break, identifier=ADMINISTRATION_BREAK)
+ admin_menu.add_break(SHORTCUTS_BREAK, position=end.index)
+ start = admin_menu.find_first(Break, identifier=SHORTCUTS_BREAK)
+ end = admin_menu.find_first(Break, identifier=ADMINISTRATION_BREAK)
+
+ items = admin_menu.get_items()[start.index + 1: end.index]
+ for idx, item in enumerate(items):
+ try:
+ if force_str(item_name.lower()) < force_str(item.name.lower()):
+ return idx + start.index + 1
+ except AttributeError:
+ # Some item types do not have a 'name' attribute.
+ pass
+ return end.index
+
+ def populate(self):
+ media_library = _('Media library')
+
+ admin_menu = self.toolbar.get_or_create_menu(ADMIN_MENU_IDENTIFIER)
+ admin_menu.add_sideframe_item(
+ media_library,
+ url=reverse('admin:filer_folder_changelist'),
+ position=self.get_insert_position(admin_menu, media_library)
+ )
diff --git a/filer/management/commands/filer_check.py b/filer/management/commands/filer_check.py
new file mode 100644
index 000000000..2213de57d
--- /dev/null
+++ b/filer/management/commands/filer_check.py
@@ -0,0 +1,183 @@
+import os
+
+from django.core.management.base import BaseCommand
+from django.utils.module_loading import import_string
+
+from PIL import UnidentifiedImageError
+
+from filer import settings as filer_settings
+from filer.models.filemodels import File
+from filer.utils.loader import load_model
+
+
+class Command(BaseCommand):
+ help = "Check for orphaned files, missing file references, and set image dimensions."
+
+ def add_arguments(self, parser):
+ parser.add_argument(
+ '--orphans',
+ action='store_true',
+ dest='orphans',
+ default=False,
+ help="Scan media folders for orphaned files.",
+ )
+ parser.add_argument(
+ '--delete-orphans',
+ action='store_true',
+ dest='delete_orphans',
+ default=False,
+ help="Delete orphaned files from storage.",
+ )
+ parser.add_argument(
+ '--missing',
+ action='store_true',
+ dest='missing',
+ default=False,
+ help="Check file references and report missing files.",
+ )
+ parser.add_argument(
+ '--delete-missing',
+ action='store_true',
+ dest='delete_missing',
+ default=False,
+ help="Delete database entries if files are missing in the media folder.",
+ )
+ parser.add_argument(
+ '--image-dimensions',
+ action='store_true',
+ dest='image_dimensions',
+ default=False,
+ help="Set image dimensions if they are not set.",
+ )
+ parser.add_argument(
+ '--noinput',
+ '--no-input',
+ action='store_false',
+ dest='interactive',
+ default=True,
+ help="Do not prompt the user for any interactive input.",
+ )
+
+ def handle(self, *args, **options):
+ if options['missing']:
+ self.verify_references(options)
+ if options['delete_missing']:
+ if options['interactive']:
+ if input(
+ "\nThis will delete missing file references from the database.\n"
+ "Type 'yes' to continue, or 'no' to cancel: "
+ ) != 'yes':
+ self.stdout.write("Aborted: Missing file references were not deleted.\n")
+ self.stdout.flush()
+ return
+ self.verify_references(options)
+
+ if options['orphans'] or options['delete_orphans']:
+ if options['delete_orphans'] and options['interactive']:
+ if input(
+ "\nThis will delete orphaned files from storage.\n"
+ "Type 'yes' to continue, or 'no' to cancel: "
+ ) != 'yes':
+ self.stdout.write("Aborted: Orphaned files were not deleted.\n")
+ self.stdout.flush()
+ return
+ self.verify_storages(options)
+
+ if options['image_dimensions']:
+ self.image_dimensions(options)
+
+ def verify_references(self, options):
+ """
+ Checks that every file reference in the database exists in storage.
+ If a file is missing, either report it or delete the reference based on the provided options.
+ """
+ for file in File.objects.all():
+ if not file.file.storage.exists(file.file.name):
+ if options['delete_missing']:
+ file.delete()
+ verbose_msg = f"Deleted missing file reference '{file.folder}/{file}' from the database."
+ else:
+ verbose_msg = f"File reference '{file.folder}/{file}' is missing in storage."
+ if options.get('verbosity', 1) > 2:
+ self.stdout.write(verbose_msg + "\n")
+ self.stdout.flush()
+ elif options.get('verbosity'):
+ self.stdout.write(os.path.join(str(file.folder), str(file)) + "\n")
+ self.stdout.flush()
+
+ def verify_storages(self, options):
+ """
+ Scans all storages defined in FILER_STORAGES (e.g., public and private)
+ for orphaned files, then reports or deletes them based on the options.
+ """
+
+ def walk(storage, prefix, label_prefix):
+ # If the directory does not exist, there is nothing to scan
+ if not storage.exists(prefix):
+ return
+ child_dirs, files = storage.listdir(prefix)
+ for filename in files:
+ actual_path = os.path.join(prefix, filename)
+ relfilename = os.path.join(label_prefix, filename)
+ if not File.objects.filter(file=actual_path).exists():
+ if options['delete_orphans']:
+ storage.delete(actual_path)
+ message = f"Deleted orphaned file '{relfilename}'"
+ else:
+ message = f"Found orphaned file '{relfilename}'"
+ if options.get('verbosity', 1) > 2:
+ self.stdout.write(message + "\n")
+ self.stdout.flush()
+ elif options.get('verbosity'):
+ self.stdout.write(relfilename + "\n")
+ self.stdout.flush()
+ for child in child_dirs:
+ walk(storage, os.path.join(prefix, child), os.path.join(label_prefix, child))
+
+ # Loop through each storage configuration (e.g., public, private, etc.)
+ for storage_name, storage_config in filer_settings.FILER_STORAGES.items():
+ storage_settings = storage_config.get('main')
+ if not storage_settings:
+ continue
+ storage = import_string(storage_settings['ENGINE'])()
+ if storage_settings.get('OPTIONS', {}).get('location'):
+ storage.location = storage_settings['OPTIONS']['location']
+ # Set label_prefix: for public and private storages, use their names.
+ label_prefix = storage_name if storage_name in ['public', 'private'] else storage_settings.get('UPLOAD_TO_PREFIX', '')
+ walk(storage, storage_settings.get('UPLOAD_TO_PREFIX', ''), label_prefix)
+
+ def image_dimensions(self, options):
+ """
+ For images without set dimensions (_width == 0 or None), try to read their dimensions
+ and save them, handling SVG files and possible image errors.
+ """
+ from django.db.models import Q
+
+ import easy_thumbnails
+ from easy_thumbnails.VIL import Image as VILImage
+
+ from filer.utils.compatibility import PILImage
+
+ ImageModel = load_model(filer_settings.FILER_IMAGE_MODEL)
+ images_without_dimensions = ImageModel.objects.filter(Q(_width=0) | Q(_width__isnull=True))
+ self.stdout.write(f"Setting dimensions for {images_without_dimensions.count()} images" + "\n")
+ self.stdout.flush()
+ for image in images_without_dimensions:
+ file_holder = image.file_ptr if getattr(image, 'file_ptr', None) else image
+ try:
+ imgfile = file_holder.file
+ imgfile.seek(0)
+ except FileNotFoundError:
+ continue
+ if image.file.name.lower().endswith('.svg'):
+ # For SVG files, use VILImage (invalid SVGs do not throw errors)
+ with VILImage.load(imgfile) as vil_image:
+ image._width, image._height = vil_image.size
+ else:
+ try:
+ with PILImage.open(imgfile) as pil_image:
+ image._width, image._height = pil_image.size
+ image._transparent = easy_thumbnails.utils.is_transparent(pil_image)
+ except UnidentifiedImageError:
+ continue
+ image.save()
diff --git a/filer/management/commands/generate_thumbnails.py b/filer/management/commands/generate_thumbnails.py
new file mode 100644
index 000000000..64bf3df2f
--- /dev/null
+++ b/filer/management/commands/generate_thumbnails.py
@@ -0,0 +1,28 @@
+from django.core.management.base import BaseCommand
+
+from filer.models.imagemodels import Image
+
+
+class Command(BaseCommand):
+
+ def handle(self, *args, **options):
+ """
+ Generates image thumbnails
+
+ NOTE: To keep memory consumption stable avoid iteration over the Image queryset
+ """
+ pks = Image.objects.all().values_list('id', flat=True)
+ total = len(pks)
+ for idx, pk in enumerate(pks):
+ image = None
+ try:
+ image = Image.objects.get(pk=pk)
+ self.stdout.write(u'Processing image {0} / {1} {2}'.format(idx + 1, total, image))
+ self.stdout.flush()
+ image.thumbnails
+ image.icons
+ except IOError as e:
+ self.stderr.write('Failed to generate thumbnails: {0}'.format(str(e)))
+ self.stderr.flush()
+ finally:
+ del image
diff --git a/filer/models/abstract.py b/filer/models/abstract.py
new file mode 100644
index 000000000..e4345896f
--- /dev/null
+++ b/filer/models/abstract.py
@@ -0,0 +1,282 @@
+import logging
+
+from django.conf import settings
+from django.core.checks import Warning
+from django.core.checks import register as register_check
+from django.core.exceptions import ValidationError
+from django.db import models
+from django.utils.functional import cached_property
+from django.utils.translation import gettext_lazy as _
+
+import easy_thumbnails.utils
+from easy_thumbnails.VIL import Image as VILImage
+from PIL.Image import MAX_IMAGE_PIXELS
+
+from .. import settings as filer_settings
+from ..utils.compatibility import PILImage
+from ..utils.filer_easy_thumbnails import FilerThumbnailer
+from ..utils.pil_exif import get_exif_for_file
+from .filemodels import File
+
+
+logger = logging.getLogger(__name__)
+
+
+# We can never exceed the max pixel value set by Pillow's PIL Image MAX_IMAGE_PIXELS
+# as if we allow it, it will fail while thumbnailing (first in the admin thumbnails
+# and then in the page itself.
+# Refer this https://github.com/python-pillow/Pillow/blob/b723e9e62e4706a85f7e44cb42b3d838dae5e546/src/PIL/Image.py#L3148
+FILER_MAX_IMAGE_PIXELS = getattr(settings, "FILER_MAX_IMAGE_PIXELS", MAX_IMAGE_PIXELS)
+if MAX_IMAGE_PIXELS is not None:
+ FILER_MAX_IMAGE_PIXELS = min(FILER_MAX_IMAGE_PIXELS, MAX_IMAGE_PIXELS)
+
+
+@register_check()
+def max_pixel_setting_check(app_configs, **kwargs):
+ if not FILER_MAX_IMAGE_PIXELS:
+ return [
+ Warning(
+ "Both settings.FILER_MAX_IMAGE_PIXELS and PIL.Image.MAX_IMAGE_PIXELS are not set.",
+ hint="Set FILER_MAX_IMAGE_PIXELS to a positive integer value in your settings.py. "
+ "This setting is used to limit the maximum number of pixels an image can have "
+ "to protect your site from memory bombs.",
+ obj=settings,
+ )
+ ]
+ return []
+
+
+class BaseImage(File):
+ SIDEBAR_IMAGE_WIDTH = 210
+ DEFAULT_THUMBNAILS = {
+ 'admin_clipboard_icon': {'size': (32, 32), 'crop': True,
+ 'upscale': True},
+ 'admin_sidebar_preview': {'size': (SIDEBAR_IMAGE_WIDTH, 0), 'upscale': True},
+ 'admin_directory_listing_icon': {'size': (48, 48),
+ 'crop': True, 'upscale': True},
+ 'admin_tiny_icon': {'size': (32, 32), 'crop': True, 'upscale': True},
+ }
+ file_type = 'Image'
+ _icon = "image"
+
+ _height = models.FloatField(
+ null=True,
+ blank=True,
+ )
+
+ _width = models.FloatField(
+ null=True,
+ blank=True,
+ )
+
+ _transparent = models.BooleanField(
+ null=False,
+ default=False,
+ )
+
+ default_alt_text = models.CharField(
+ _("default alt text"),
+ max_length=255,
+ blank=True,
+ null=True,
+ )
+
+ default_caption = models.CharField(
+ _("default caption"),
+ max_length=255,
+ blank=True,
+ null=True,
+ )
+
+ subject_location = models.CharField(
+ _("subject location"),
+ max_length=64,
+ blank=True,
+ default='',
+ )
+
+ file_ptr = models.OneToOneField(
+ to='filer.File',
+ parent_link=True,
+ related_name='%(app_label)s_%(class)s_file',
+ on_delete=models.CASCADE,
+ )
+
+ class Meta:
+ app_label = 'filer'
+ verbose_name = _("image")
+ verbose_name_plural = _("images")
+ abstract = True
+ default_manager_name = 'objects'
+
+ @classmethod
+ def matches_file_type(cls, iname, ifile, mime_type):
+ # source: https://www.freeformatter.com/mime-types-list.html
+ from ..settings import IMAGE_MIME_TYPES
+ maintype, subtype = mime_type.split('/')
+ return maintype == 'image' and subtype in IMAGE_MIME_TYPES
+
+ def file_data_changed(self, post_init=False):
+ attrs_updated = super().file_data_changed(post_init=post_init)
+ if attrs_updated:
+ try:
+ try:
+ imgfile = self.file.file
+ except ValueError:
+ imgfile = self.file_ptr.file
+ imgfile.seek(0)
+ if self.mime_type == 'image/svg+xml':
+ self._width, self._height = VILImage.load(imgfile).size
+ self._transparent = True
+ else:
+ pil_image = PILImage.open(imgfile)
+ self._width, self._height = pil_image.size
+ self._transparent = easy_thumbnails.utils.is_transparent(pil_image)
+ imgfile.seek(0)
+ except Exception:
+ if post_init is False:
+ # in case `imgfile` could not be found, unset dimensions
+ # but only if not initialized by loading a fixture file
+ self._width, self._height = None, None
+ self._transparent = False
+ return attrs_updated
+
+ def clean(self):
+ # We check the Image size and calculate the pixel before
+ # the image gets attached to a folder and saved. We also
+ # send the error msg in the JSON and also post the message
+ # so that they know what is wrong with the image they uploaded
+ if not self.file or not FILER_MAX_IMAGE_PIXELS:
+ return
+
+ if self._width is None or self._height is None:
+ # If image size exceeds Pillow's max image size, Pillow will not return width or height
+ pixels = 2 * FILER_MAX_IMAGE_PIXELS + 1
+ aspect = 16 / 9
+ else:
+ width, height = max(1, self.width), max(1, self.height)
+ pixels: int = width * height
+ aspect: float = width / height
+ res_x: int = int((FILER_MAX_IMAGE_PIXELS * aspect) ** 0.5)
+ res_y: int = int(res_x / aspect)
+ if pixels > 2 * FILER_MAX_IMAGE_PIXELS:
+ msg = _(
+ "Image format not recognized or image size exceeds limit of %(max_pixels)d million "
+ "pixels by a factor of two or more. Before uploading again, check file format or resize "
+ "image to %(width)d x %(height)d resolution or lower."
+ ) % dict(max_pixels=FILER_MAX_IMAGE_PIXELS // 1000000, width=res_x, height=res_y)
+ raise ValidationError(str(msg), code="image_size")
+
+ if pixels > FILER_MAX_IMAGE_PIXELS:
+ msg = _(
+ "Image size (%(pixels)d million pixels) exceeds limit of %(max_pixels)d "
+ "million pixels. Before uploading again, resize image to %(width)d x %(height)d "
+ "resolution or lower."
+ ) % dict(pixels=pixels // 1000000, max_pixels=FILER_MAX_IMAGE_PIXELS // 1000000,
+ width=res_x, height=res_y)
+ raise ValidationError(str(msg), code="image_size")
+
+ def save(self, *args, **kwargs):
+ self.has_all_mandatory_data = self._check_validity()
+ super().save(*args, **kwargs)
+
+ def _check_validity(self):
+ if not self.name:
+ return False
+ return True
+
+ def sidebar_image_ratio(self):
+ if self.width:
+ return float(self.width) / float(self.SIDEBAR_IMAGE_WIDTH)
+ else:
+ return 1.0
+
+ @cached_property
+ def exif(self):
+ try:
+ return get_exif_for_file(self.file)
+ except Exception:
+ return {}
+
+ def has_edit_permission(self, request):
+ return self.has_generic_permission(request, 'edit')
+
+ def has_read_permission(self, request):
+ return self.has_generic_permission(request, 'read')
+
+ def has_add_children_permission(self, request):
+ return self.has_generic_permission(request, 'add_children')
+
+ def has_generic_permission(self, request, permission_type):
+ """
+ Return true if the current user has permission on this
+ image. Return the string 'ALL' if the user has all rights.
+ """
+ user = request.user
+ if not user.is_authenticated:
+ return False
+ elif user.is_superuser:
+ return True
+ elif user == self.owner:
+ return True
+ elif self.folder:
+ return self.folder.has_generic_permission(request, permission_type)
+ else:
+ return False
+
+ @property
+ def label(self):
+ if self.name in ['', None]:
+ return self.original_filename or 'unnamed file'
+ else:
+ return self.name
+
+ @property
+ def width(self):
+ return self._width or 0.0
+
+ @property
+ def height(self):
+ return self._height or 0.0
+
+ def _generate_thumbnails(self, required_thumbnails):
+ _thumbnails = {}
+ for name, opts in required_thumbnails.items():
+ try:
+ opts.update({'subject_location': self.subject_location})
+ thumb = self.file.get_thumbnail(opts)
+ _thumbnails[name] = thumb.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
+ # proper logging configuration
+ if filer_settings.FILER_ENABLE_LOGGING:
+ logger.error('Error while generating thumbnail: %s', e)
+ if filer_settings.FILER_DEBUG:
+ raise
+ return _thumbnails
+
+ @property
+ def icons(self):
+ required_thumbnails = {
+ size: {
+ 'size': (int(size), int(size)),
+ 'crop': True,
+ 'upscale': True,
+ 'subject_location': self.subject_location,
+ }
+ for size in filer_settings.FILER_ADMIN_ICON_SIZES}
+ return self._generate_thumbnails(required_thumbnails)
+
+ @property
+ def thumbnails(self):
+ return self._generate_thumbnails(BaseImage.DEFAULT_THUMBNAILS)
+
+ @property
+ def easy_thumbnails_thumbnailer(self):
+ tn = FilerThumbnailer(
+ file=self.file, name=self.file.name,
+ source_storage=self.file.source_storage,
+ thumbnail_storage=self.file.thumbnail_storage,
+ thumbnail_basedir=self.file.thumbnail_basedir)
+ return tn
diff --git a/filer/models/thumbnailoptionmodels.py b/filer/models/thumbnailoptionmodels.py
new file mode 100644
index 000000000..883df7989
--- /dev/null
+++ b/filer/models/thumbnailoptionmodels.py
@@ -0,0 +1,61 @@
+from django.db import models
+from django.utils.translation import gettext_lazy as _
+
+
+class ThumbnailOption(models.Model):
+ """
+ This class defines the option use to create the thumbnail.
+ """
+ name = models.CharField(
+ _("name"),
+ max_length=100,
+ )
+
+ width = models.IntegerField(
+ _("width"),
+ help_text=_("width in pixel."),
+ )
+
+ height = models.IntegerField(
+ _("height"),
+ help_text=_("height in pixel."),
+ )
+
+ crop = models.BooleanField(
+ _("crop"),
+ default=True,
+ )
+
+ upscale = models.BooleanField(
+ _("upscale"),
+ default=True,
+ )
+
+ class Meta:
+ app_label = 'filer'
+ ordering = ('width', 'height')
+ verbose_name = _("thumbnail option")
+ verbose_name_plural = _("thumbnail options")
+
+ def __str__(self):
+ return f'{self.name} -- {self.width} x {self.height}'
+
+ @property
+ def as_dict(self):
+ """
+ This property returns a dictionary suitable for Thumbnailer.get_thumbnail()
+
+ Sample code:
+ # thumboption_obj is a ThumbnailOption instance
+ # filerimage is a Image instance
+ option_dict = thumboption_obj.as_dict
+ thumbnailer = filerimage.easy_thumbnails_thumbnailer
+ thumb_image = thumbnailer.get_thumbnail(option_dict)
+ """
+ return {
+ 'size': (self.width, self.height),
+ 'width': self.width,
+ 'height': self.height,
+ 'crop': self.crop,
+ 'upscale': self.upscale,
+ }
diff --git a/filer/urls.py b/filer/urls.py
new file mode 100644
index 000000000..f8fccf4a1
--- /dev/null
+++ b/filer/urls.py
@@ -0,0 +1,13 @@
+from django.urls import path
+
+from . import settings as filer_settings
+from . import views
+
+
+urlpatterns = [
+ path(
+ filer_settings.FILER_CANONICAL_URL + '//',
+ views.canonical,
+ name='canonical'
+ ),
+]
diff --git a/filer/utils/compatibility.py b/filer/utils/compatibility.py
new file mode 100644
index 000000000..0c7fd5290
--- /dev/null
+++ b/filer/utils/compatibility.py
@@ -0,0 +1,33 @@
+from django.utils.functional import keep_lazy
+from django.utils.text import Truncator, format_lazy
+
+
+def string_concat(*strings):
+ return format_lazy('{}' * len(strings), *strings)
+
+
+def truncate_words(s, num, end_text='...'):
+ # truncate_words was removed in Django 1.5.
+ truncate = end_text and ' %s' % end_text or ''
+ return Truncator(s).words(num, truncate=truncate)
+
+
+truncate_words = keep_lazy(truncate_words, str)
+
+
+def get_delete_permission(opts):
+ from django.contrib.auth import get_permission_codename
+ return '{}.{}'.format(opts.app_label, get_permission_codename('delete', opts))
+
+
+try:
+ from PIL import ExifTags as PILExifTags
+ from PIL import Image as PILImage
+ from PIL import ImageDraw as PILImageDraw
+except ImportError:
+ try:
+ import ExifTags as PILExifTags # noqa
+ import Image as PILImage # noqa
+ import ImageDraw as PILImageDraw # noqa
+ except ImportError:
+ raise ImportError("The Python Imaging Library was not found.")
diff --git a/filer/validation.py b/filer/validation.py
new file mode 100644
index 000000000..4a34d85d1
--- /dev/null
+++ b/filer/validation.py
@@ -0,0 +1,115 @@
+import typing
+
+from django.apps import apps
+from django.contrib.auth import get_user_model
+from django.core.exceptions import ValidationError
+from django.utils.translation import gettext as _
+
+
+User = get_user_model() # Needed for typing
+
+
+class FileValidationError(ValidationError):
+ pass
+
+
+def deny(file_name: str, file: typing.IO, owner: User, mime_type: str) -> None:
+ file_type = file_name.rsplit(".")[-1]
+ if file_type == file_name:
+ raise FileValidationError(
+ _('File "{file_name}": Upload denied by site security policy').format(file_name=file_name)
+ )
+ raise FileValidationError(
+ _('File "{file_name}": {file_type} upload denied by site security policy').format(
+ file_name=file_name,
+ file_type=file_type.upper()
+ )
+ )
+
+
+def deny_html(file_name: str, file: typing.IO, owner: User, mime_type: str) -> None:
+ """Simple validator that denies all files. Separate for HTML since .html and .htm are both
+ common suffixes for text/html files."""
+ raise FileValidationError(
+ _('File "{file_name}": HTML upload denied by site security policy').format(file_name=file_name)
+ )
+
+
+TRIGGER_XSS_THREAD = (
+ # Part 1: Event attributes that take js code as values
+ # See https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/Events
+ b"onbegin=", b"onend=", b"onrepeat=",
+ b"onabort=", b"onerror=", b"onresize=", b"onscroll=", b"onunload=",
+ b"oncopy=", b"oncut=", b"onpaste=",
+ b"oncancel=", b"oncanplay=", b"oncanplaythrough=", b"onchange=", b"onclick=", b"onclose=", b"oncuechange=", b"ondblclick=",
+ b"ondrag=", b"ondragend=", b"ondragenter=", b"ondragleave=", b"ondragover=", b"ondragstart=", b"ondrop=",
+ b"ondurationchange=", b"onemptied=", b"onended=", b"onerror=", b"onfocus=", b"oninput=", b"oninvalid=",
+ b"onkeydown=", b"onkeypress=", b"onkeyup=", b"onload=", b"onloadeddata=", b"onloadedmetadata=", b"onloadstart=",
+ b"onmousedown=", b"onmouseenter=", b"onmouseleave=", b"onmousemove=", b"onmouseout=", b"onmouseover=", b"onmouseup=",
+ b"onmousewheel=", b"onpause=", b"onplay=", b"onplaying=", b"onprogress=", b"onratechange=", b"onreset=", b"onresize=",
+ b"onscroll=", b"onseeked=", b"onseeking=", b"onselect=", b"onshow=", b"onstalled=", b"onsubmit=", b"onsuspend=",
+ b"ontimeupdate=", b"ontoggle==", b"onvolumechange==", b"onwaiting=",
+ b"onactivate=", b"onfocusin=", b"onfocusout=",
+) + (
+ # Part 2:
+ # Reject base64 obfuscated content
+ b";base64,",
+) + (
+ # Part 3: Obvious scripts
+ # Reject direct tags or javascript: uri
+ b"')
- return response
-
- def delete_view(self, request, object_id, extra_context=None):
- # override delete view since we need to hide already trashed
- # files/folders
- opts = self.model._meta
- obj = self.get_object(request, unquote(object_id))
-
- if obj is None:
- raise Http404(_('%(name)s object with primary key %(key)r '
- 'does not exist.') % {
- 'name': force_str(opts.verbose_name),
- 'key': escape(object_id)})
- if obj.parent:
- redirect_url = reverse('admin:filer-directory_listing',
- kwargs={'folder_id': obj.parent_id})
- else:
- redirect_url = reverse('admin:filer-directory_listing-root')
- redirect_url = "%s%s%s%s" % (redirect_url, popup_param(request),
- selectfolder_param(request, "&"),
- current_site_param(request),)
-
- setattr(request, 'current_dir_list_folder',
- obj.parent or FolderRoot())
-
- response = self.delete_files_or_folders(
- request,
- File.objects.none(),
- Folder.objects.filter(id=obj.id))
-
- if response is None:
- return HttpResponseRedirect(redirect_url)
- return response
+ path('',
+ self.admin_site.admin_view(self.directory_listing),
+ name='filer-directory_listing-root'),
+
+ path('last/',
+ self.admin_site.admin_view(self.directory_listing),
+ {'viewtype': 'last'},
+ name='filer-directory_listing-last'),
+
+ path('/list/',
+ self.admin_site.admin_view(self.directory_listing),
+ name='filer-directory_listing'),
+
+ path('/make_folder/',
+ self.admin_site.admin_view(views.make_folder),
+ name='filer-directory_listing-make_folder'),
+ path('make_folder/',
+ self.admin_site.admin_view(views.make_folder),
+ name='filer-directory_listing-make_root_folder'),
+
+ path('images_with_missing_data/',
+ self.admin_site.admin_view(self.directory_listing),
+ {'viewtype': 'images_with_missing_data'},
+ name='filer-directory_listing-images_with_missing_data'),
+
+ path('unfiled_images/',
+ self.admin_site.admin_view(self.directory_listing),
+ {'viewtype': 'unfiled_images'},
+ name='filer-directory_listing-unfiled_images'),
+ ] + super().get_urls()
# custom views
def directory_listing(self, request, folder_id=None, viewtype=None):
- user = request.user
- clipboard = tools.get_user_clipboard(user)
- file_type = request.GET.get('file_type', None)
+ if not request.user.has_perm("filer.can_use_directory_listing"):
+ raise PermissionDenied()
+ clipboard = tools.get_user_clipboard(request.user)
if viewtype == 'images_with_missing_data':
folder = ImagesWithMissingData()
- folder_file_qs = folder.files
elif viewtype == 'unfiled_images':
- folder = UnfiledImages()
- folder_file_qs = folder.files
- elif folder_id is None:
- folder = FolderRoot()
- folder_file_qs = File.objects.none()
- else:
+ # pass user in the class invocation, so that we can get
+ # access to the current user instance in the class
+ folder = UnsortedImages(user=request.user)
+ elif viewtype == 'last':
+ last_folder_id = request.session.get('filer_last_folder_id')
try:
- folder = Folder.objects.get(id=folder_id)
- if not self.can_view_folder_content(request, folder):
- raise PermissionDenied
- except Folder.DoesNotExist:
- raise Http404
- if file_type == 'image':
- folder_file_qs = Image.objects.filter(folder=folder)
+ self.get_queryset(request).get(id=last_folder_id)
+ except self.model.DoesNotExist:
+ url = reverse('admin:filer-directory_listing-root')
+ url = f"{url}{admin_url_params_encoded(request)}"
else:
- folder_file_qs = File.objects.filter(folder=folder)
-
- if file_type == 'image':
- all_file_qs = Image.objects
+ url = reverse('admin:filer-directory_listing', kwargs={'folder_id': last_folder_id})
+ url = f"{url}{admin_url_params_encoded(request)}"
+ return HttpResponseRedirect(url)
+ elif folder_id is None:
+ folder = FolderRoot()
else:
- all_file_qs = File.objects
-
- setattr(request, 'current_dir_list_folder', folder)
- # search
- q = request.GET.get('q', None)
- if q:
- search_terms = q.split(" ")
+ folder = get_object_or_404(self.get_queryset(request), id=folder_id)
+ request.session['filer_last_folder_id'] = folder_id
+
+ list_type = get_directory_listing_type(request) or settings.FILER_FOLDER_ADMIN_DEFAULT_LIST_TYPE
+ if list_type == TABLE_LIST_TYPE:
+ # Prefetch thumbnails for table view
+ size = f"{FILER_TABLE_ICON_SIZE}x{FILER_TABLE_ICON_SIZE}"
+ size_x2 = f"{2 * FILER_TABLE_ICON_SIZE}x{2 * FILER_TABLE_ICON_SIZE}"
else:
- search_terms = []
- q = ''
+ # Prefetch thumbnails for thumbnail view
+ size = f"{FILER_THUMBNAIL_ICON_SIZE}x{FILER_THUMBNAIL_ICON_SIZE}"
+ size_x2 = f"{2 * FILER_THUMBNAIL_ICON_SIZE}x{2 * FILER_THUMBNAIL_ICON_SIZE}"
# Check actions to see if any are available on this changelist
- # do not let any actions available if we're in search view since
- # there is no way to detect the current folder
- actions = {}
- if not search_terms:
- actions = self.get_actions(request)
+ actions = self.get_actions(request)
# Remove action checkboxes if there aren't any actions available.
list_display = list(self.list_display)
@@ -304,77 +297,127 @@ def directory_listing(self, request, folder_id=None, viewtype=None):
except ValueError:
pass
+ # search
+ q = request.GET.get('q')
+ if q:
+ search_terms = urlunquote(q).split(' ')
+ search_mode = True
+ else:
+ search_terms = []
+ q = ''
+ search_mode = False
+ # Limit search results to current folder.
limit_search_to_folder = request.GET.get('limit_search_to_folder',
False) in (True, 'on')
- current_site = request.GET.get('current_site', None)
- _filter_folders = partial(folders_available, current_site, request.user)
- _filter_files = partial(files_available, current_site, request.user)
if len(search_terms) > 0:
if folder and limit_search_to_folder and not folder.is_root:
- descendants = folder.get_descendants(
- include_self=True).filter(deleted_at__isnull=True)
- folder_qs = _filter_folders(descendants.exclude(id=folder.id))
- file_qs = _filter_files(all_file_qs.filter(folder__in=descendants))
+ desc_folder_ids = folder.get_descendants_ids()
+ # Do not include current folder itself in search results.
+ folder_qs = Folder.objects.filter(pk__in=desc_folder_ids)
+ # Limit search results to files in the current folder or any
+ # nested folder.
+ file_qs = File.objects.filter(folder_id__in=desc_folder_ids + [folder.pk])
else:
- folder_qs = _filter_folders(Folder.objects.all())
- file_qs = _filter_files(all_file_qs)
-
- def folder_search_qs(qs, terms=[]):
- for term in terms:
- qs = qs.filter(Q(name__icontains=term) |
- Q(owner__username__icontains=term) |
- Q(owner__first_name__icontains=term) |
- Q(owner__last_name__icontains=term))
- return qs
-
- def file_search_qs(qs, terms=[]):
- for term in terms:
- qs = qs.filter(Q(name__icontains=term) |
- Q(description__icontains=term) |
- Q(original_filename__icontains=term) |
- Q(owner__username__icontains=term) |
- Q(owner__first_name__icontains=term) |
- Q(owner__last_name__icontains=term))
- return qs
-
- folder_qs = folder_search_qs(folder_qs, search_terms)
- file_qs = file_search_qs(file_qs, search_terms)
+ folder_qs = self.get_queryset(request)
+ file_qs = File.objects.all()
+ folder_qs = self.filter_folder(folder_qs, search_terms).prefetch_related("children", "all_files")
+ file_qs = self.filter_file(file_qs, search_terms)
+
show_result_count = True
else:
- folder_qs = _filter_folders(folder.children.all())
- file_qs = _filter_files(folder_file_qs)
+ folder_qs = folder.children.all()
+ file_qs = folder.files.all()
show_result_count = False
- folder_qs = folder_qs.order_by('name')
- file_qs = file_qs.order_by('name')
- if show_result_count:
- show_result_count = {
- 'files_found': file_qs.count(),
- 'folders_found': folder_qs.count(),
+ folder_qs = folder_qs.order_by('name').select_related("owner")
+ order_by = request.GET.get('order_by', None)
+ order_by_annotation = None
+ if order_by is None:
+ order_by_annotation = Lower(Coalesce(
+ Case(
+ When(name__exact='', then=None),
+ When(name__isnull=False, then='name')
+ ),
+ 'original_filename'
+ ))
+
+ order_by = order_by.split(',') if order_by else []
+ order_by = [field for field in order_by
+ if re.sub(r'^-', '', field) in self.order_by_file_fields]
+ if len(order_by) > 0:
+ file_qs = file_qs.order_by(*order_by)
+ elif order_by_annotation:
+ file_qs = file_qs.order_by(order_by_annotation)
+
+ if folder.is_root and not search_mode:
+ virtual_items = folder.virtual_folders
+ else:
+ virtual_items = []
+
+ perms = FolderPermission.objects.get_read_id_list(request.user)
+ if perms != 'All':
+ file_qs = file_qs.filter(
+ models.Q(folder__id__in=perms)
+ | models.Q(folder_id__isnull=True)
+ | models.Q(owner=request.user)
+ )
+ folder_qs = folder_qs.filter(models.Q(id__in=perms) | models.Q(owner=request.user))
+ root_exclude_kwargs = {'parent__isnull': False, 'parent__id__in': perms}
+ else:
+ root_exclude_kwargs = {'parent__isnull': False}
+ if folder.is_root:
+ folder_qs = folder_qs.exclude(**root_exclude_kwargs)
+
+ # Annotate thumbnail status
+ thumbnail_qs = (
+ Thumbnail.objects
+ .filter(
+ source__name=OuterRef("file"),
+ modified__gte=F("source__modified"),
+ )
+ .exclude(name__contains="upscale") # TODO: Check WHY not used by directory listing
+ .order_by("-modified")
+ )
+ file_qs = file_qs.annotate(
+ thumbnail_name=Subquery(thumbnail_qs.filter(name__contains=f"__{size}_").values_list("name")[:1]),
+ thumbnailx2_name=Subquery(thumbnail_qs.filter(name__contains=f"__{size_x2}_").values_list("name")[:1])
+ ).select_related("owner")
+
+ try:
+ permissions = {
+ 'has_edit_permission': folder.has_edit_permission(request),
+ 'has_read_permission': folder.has_read_permission(request),
+ 'has_add_children_permission':
+ folder.has_add_children_permission(request),
}
+ except: # noqa
+ permissions = {}
- items = MultiMoldelQuerysetChain([folder_qs, file_qs])
+ items = list(itertools.chain(folder_qs, file_qs))
paginator = Paginator(items, FILER_PAGINATE_BY)
# Are we moving to clipboard?
if request.method == 'POST' and '_save' not in request.POST:
- for f in file_qs:
+ # TODO: Refactor/remove clipboard parts
+ for f in folder_qs:
if "move-to-clipboard-%d" % (f.id,) in request.POST:
- if (f.is_readonly_for_user(user) or
- f.is_restricted_for_user(user)):
+ clipboard = tools.get_user_clipboard(request.user)
+ if f.has_edit_permission(request):
+ tools.move_file_to_clipboard([f], clipboard)
+ return HttpResponseRedirect(request.get_full_path())
+ else:
raise PermissionDenied
- tools.move_file_to_clipboard(request, [f], clipboard)
- return HttpResponseRedirect(request.get_full_path())
selected = request.POST.getlist(helpers.ACTION_CHECKBOX_NAME)
# Actions with no confirmation
- if (actions and request.method == 'POST' and
- 'index' in request.POST and '_save' not in request.POST):
+ if (
+ actions and request.method == 'POST'
+ and 'index' in request.POST
+ and '_save' not in request.POST
+ ):
if selected:
- response = self.response_action(request,
- files_queryset=file_qs,
- folders_queryset=folder_qs)
+ response = self.response_action(request, files_queryset=file_qs, folders_queryset=folder_qs)
if response:
return response
else:
@@ -383,71 +426,127 @@ def file_search_qs(qs, terms=[]):
self.message_user(request, msg)
# Actions with confirmation
- if (actions and request.method == 'POST' and
- helpers.ACTION_CHECKBOX_NAME in request.POST and
- 'index' not in request.POST and '_save' not in request.POST):
+ if (
+ actions and request.method == 'POST'
+ and helpers.ACTION_CHECKBOX_NAME in request.POST
+ and 'index' not in request.POST
+ and '_save' not in request.POST
+ ):
if selected:
- response = self.response_action(request,
- files_queryset=file_qs,
- folders_queryset=folder_qs)
+ response = self.response_action(request, files_queryset=file_qs, folders_queryset=folder_qs)
if response:
return response
# Build the action form and populate it with available actions.
if actions:
action_form = self.action_form(auto_id=None)
- action_form.fields['action'].choices = \
- self.get_action_choices(request)
+ action_form.fields['action'].choices = self.get_action_choices(request)
else:
action_form = None
- selection_note_all = ngettext_lazy(
- '%(total_count)s selected',
- 'All %(total_count)s selected',
- paginator.count
- )
- # Make sure page request is an int. If not, deliver first page.
- try:
- page = int(request.GET.get('page', '1'))
- except ValueError:
- page = 1
+ selection_note_all = ngettext_lazy('%(total_count)s selected',
+ 'All %(total_count)s selected', paginator.count)
# If page request (9999) is out of range, deliver last page of results.
try:
- paginated_items = paginator.page(page)
- except (EmptyPage, InvalidPage):
+ paginated_items = paginator.page(request.GET.get('page', 1))
+ except PageNotAnInteger:
+ paginated_items = paginator.page(1)
+ except EmptyPage:
paginated_items = paginator.page(paginator.num_pages)
- context = self.admin_site.each_context(request)
+
+ context = self.admin_site.each_context(request)
context.update({
- 'folder': folder,
- 'user_clipboard': clipboard,
- 'clipboard_files': clipboard.files.distinct(),
- 'current_site': get_param_from_request(request, 'current_site'),
- 'paginator': paginator,
- 'paginated_items': paginated_items,
- 'current_url': request.path,
- 'title': 'Directory listing for %s' % folder.name,
- 'search_string': ' '.join(search_terms),
- 'q': q,
- 'show_result_count': show_result_count,
- 'limit_search_to_folder': limit_search_to_folder,
- 'is_popup': popup_status(request),
- 'select_folder': selectfolder_status(request),
- # needed in the admin/base.html template for logout links
- 'root_path': reverse('admin:index'),
- 'action_form': action_form,
- 'actions_on_top': self.actions_on_top,
- 'actions_on_bottom': self.actions_on_bottom,
- 'actions_selection_counter': self.actions_selection_counter,
- 'selection_note': _('0 of %(cnt)s selected') % {
- 'cnt': len(paginated_items.object_list)},
- 'selection_note_all': selection_note_all % {
- 'total_count': paginator.count},
- 'media': self.media,
- 'file_type': file_type,
- })
- response = render(request, 'admin/filer/folder/directory_listing.html', context)
- return response
+ 'folder': folder,
+ 'clipboard_files': File.objects.filter(
+ in_clipboards__clipboarditem__clipboard__user=request.user
+ ).distinct(),
+ 'paginator': paginator,
+ 'paginated_items': paginated_items,
+ 'virtual_items': virtual_items,
+ 'uploader_connections': settings.FILER_UPLOADER_CONNECTIONS,
+ 'max_files': settings.FILER_UPLOADER_MAX_FILES,
+ 'max_filesize': settings.FILER_UPLOADER_MAX_FILE_SIZE,
+ 'permissions': permissions,
+ 'permstest': userperms_for_request(folder, request),
+ 'current_url': request.path,
+ 'title': _('Directory listing for %(folder_name)s') % {'folder_name': folder.name},
+ 'search_string': ' '.join(search_terms),
+ 'q': urlquote(q),
+ 'show_result_count': show_result_count,
+ 'folder_children': folder_qs,
+ 'folder_files': file_qs,
+ 'thumbnail_size': FILER_TABLE_ICON_SIZE if list_type == TABLE_LIST_TYPE else FILER_THUMBNAIL_ICON_SIZE,
+ 'limit_search_to_folder': limit_search_to_folder,
+ 'is_popup': popup_status(request),
+ 'filer_admin_context': AdminContext(request),
+ # needed in the admin/base.html template for logout links
+ 'root_path': reverse('admin:index'),
+ 'action_form': action_form,
+ 'actions_on_top': self.actions_on_top,
+ 'actions_on_bottom': self.actions_on_bottom,
+ 'actions_selection_counter': self.actions_selection_counter,
+ 'selection_note': _('0 of %(cnt)s selected') % {'cnt': len(paginated_items.object_list)},
+ 'selection_note_all': selection_note_all % {'total_count': paginator.count},
+ 'list_type': list_type,
+ 'list_type_template': settings.FILER_FOLDER_ADMIN_LIST_TYPE_SWITCHER_SETTINGS[list_type]['template'],
+ 'media': self.media,
+ 'enable_permissions': settings.FILER_ENABLE_PERMISSIONS,
+ '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"),
+ })
+ return TemplateResponse(request, self.directory_listing_template, context)
+
+ def filter_folder(self, qs, terms=()):
+ # Source: https://github.com/django/django/blob/1.7.1/django/contrib/admin/options.py#L939-L947 flake8: noqa
+ def construct_search(field_name):
+ if field_name.startswith('^'):
+ return "%s__istartswith" % field_name[1:]
+ elif field_name.startswith('='):
+ return "%s__iexact" % field_name[1:]
+ elif field_name.startswith('@'):
+ return "%s__search" % field_name[1:]
+ else:
+ return "%s__icontains" % field_name
+
+ for term in terms:
+ filters = models.Q()
+ for filter_ in self.search_fields:
+ filters |= models.Q(**{construct_search(filter_): term})
+ for filter_ in self.get_owner_filter_lookups():
+ filters |= models.Q(**{filter_: term})
+ qs = qs.filter(filters)
+ return qs
+
+ def filter_file(self, qs, terms=()):
+ for term in terms:
+ filters = (models.Q(name__icontains=term)
+ | models.Q(description__icontains=term)
+ | models.Q(original_filename__icontains=term))
+ for filter_ in self.get_owner_filter_lookups():
+ filters |= models.Q(**{filter_: term})
+ qs = qs.filter(filters)
+ return qs
+
+ @property
+ def owner_search_fields(self):
+ """
+ Returns all the fields that are CharFields except for password from the
+ User model. For the built-in User model, that means username,
+ first_name, last_name, and email.
+ """
+ from django.contrib.auth import get_user_model
+ User = get_user_model()
+
+ return [
+ field.name for field in User._meta.fields
+ if isinstance(field, models.CharField) and field.name != 'password'
+ ]
+
+ def get_owner_filter_lookups(self):
+ return [
+ f'owner__{field}__icontains'
+ for field in self.owner_search_fields
+ ]
def response_action(self, request, files_queryset, folders_queryset):
"""
@@ -492,8 +591,8 @@ def response_action(self, request, files_queryset, folders_queryset):
# the action explicitly on all objects.
selected = request.POST.getlist(helpers.ACTION_CHECKBOX_NAME)
if not selected and not select_across:
- # Reminder that something needs to be selected or
- # nothing will happen
+ # Reminder that something needs to be selected or nothing
+ # will happen
msg = _("Items must be selected in order to perform "
"actions on them. No items have been changed.")
self.message_user(request, msg)
@@ -529,162 +628,196 @@ def response_action(self, request, files_queryset, folders_queryset):
return None
def get_actions(self, request):
- actions = super(FolderAdmin, self).get_actions(request)
+ if settings.FILER_ENABLE_PERMISSIONS:
+ actions = OrderedDict()
+ actions['files_set_public'] = self.get_action('files_set_public')
+ actions['files_set_private'] = self.get_action('files_set_private')
+ actions.update(super().get_actions(request))
+ else:
+ actions = super().get_actions(request)
+
+ if 'delete_selected' in actions:
+ del actions['delete_selected']
+ return actions
+
+ def move_to_clipboard(self, request, files_queryset, folders_queryset):
+ """
+ Action which moves the selected files and files in selected folders
+ to clipboard.
+ """
- def pop_actions(*actions_to_remove):
- for action in actions_to_remove:
- actions.pop(action, None)
+ if not self.has_change_permission(request):
+ raise PermissionDenied
- pop_actions('delete_selected')
+ if request.method != 'POST':
+ return None
- if not self.has_delete_permission(request, None):
- pop_actions(*self.actions_affecting_position)
+ clipboard = tools.get_user_clipboard(request.user)
- current_folder = getattr(request, 'current_dir_list_folder', None)
- if not current_folder:
- return actions
+ check_files_edit_permissions(request, files_queryset)
+ check_folder_edit_permissions(request, folders_queryset)
- if current_folder.is_root:
- pop_actions('extract_files')
+ # TODO: Display a confirmation page if moving more than X files to
+ # clipboard?
- if (not current_folder.is_root and
- current_folder.is_readonly_for_user(request.user)):
- return {}
+ # We define it like that so that we can modify it inside the
+ # move_files function
+ files_count = [0]
- if isinstance(current_folder, UnfiledImages):
- pop_actions('enable_restriction', 'copy_files_and_folders',
- 'disable_restriction')
- return actions
+ def move_files(files):
+ files_count[0] += tools.move_file_to_clipboard(files, clipboard)
- # actions are available for descendants not for current folder
- if not (current_folder.can_change_restricted(request.user) and
- not current_folder.restricted):
- pop_actions('enable_restriction', 'disable_restriction')
+ def move_folders(folders):
+ for f in folders:
+ move_files(f.files)
+ move_folders(f.children.all())
- if (actions and current_folder.is_restricted_for_user(request.user)):
- # allow only copy
- if 'copy_files_and_folders' in actions:
- return {'copy_files_and_folders':
- actions['copy_files_and_folders']}
+ move_files(files_queryset)
+ move_folders(folders_queryset)
- return actions
+ self.message_user(request, _("Successfully moved %(count)d files to "
+ "clipboard.") % {"count": files_count[0]})
- def move_to_clipboard(self, request, files_queryset, folders_queryset):
+ return None
+
+ move_to_clipboard.short_description = _("Move selected files to clipboard")
+
+ def files_set_public_or_private(self, request, set_public, files_queryset,
+ folders_queryset):
"""
- Action which moves the selected files to clipboard.
+ Action which enables or disables permissions for selected files and
+ files in selected folders to clipboard (set them private or public).
"""
- if request.method != 'POST':
- return None
- if not has_multi_file_action_permission(
- request, files_queryset,
- Folder.objects.none()):
+ if not self.has_change_permission(request):
raise PermissionDenied
- clipboard = tools.get_user_clipboard(request.user)
+ permissions_enabled = settings.FILER_ENABLE_PERMISSIONS
+
+ if request.method != 'POST' or not permissions_enabled:
+ return None
+
+ check_files_edit_permissions(request, files_queryset)
+ check_folder_edit_permissions(request, folders_queryset)
+
# We define it like that so that we can modify it inside the
- # move_files function
+ # set_files function
files_count = [0]
- def move_files(files):
- files_count[0] += tools.move_file_to_clipboard(
- request, files, clipboard)
+ def set_files(files):
+ for f in files:
+ if f.is_public != set_public:
+ f.is_public = set_public
+ f.save()
+ files_count[0] += 1
- move_files(files_queryset)
- if files_count[0] > 0:
- self.message_user(request,
- _("Successfully moved %(count)d files to clipboard.") % {
- "count": files_count[0], })
+ def set_folders(folders):
+ for f in folders:
+ set_files(f.files)
+ set_folders(f.children.all())
+
+ set_files(files_queryset)
+ set_folders(folders_queryset)
+
+ if set_public:
+ self.message_user(request, _("Successfully disabled permissions for %(count)d files.") % {"count": files_count[0], })
else:
- self.message_user(request,
- _("No files were moved to clipboard."))
+ self.message_user(request, _("Successfully enabled permissions for %(count)d files.") % {"count": files_count[0], })
+
return None
- move_to_clipboard.short_description = gettext_lazy(
- "Move selected files to clipboard")
-
- def _get_unique_items(self, deletable_items, unique_items, depth=5):
- count = 0
- if depth < 0:
- return count
- for elem in deletable_items:
- if isinstance(elem, (list, tuple)):
- count += self._get_unique_items(elem, unique_items, depth-1)
- elif isinstance(elem, str):
- match = ELEM_ID.match(elem)
- elem_id = match and match.group('file_id')
- if elem_id and elem_id not in unique_items:
- unique_items.append(elem_id)
- count += 1
- return count
-
- def delete_files_or_folders(self, request,
- files_queryset, folders_queryset):
+ def files_set_private(self, request, files_queryset, folders_queryset):
+ return self.files_set_public_or_private(request, False, files_queryset,
+ folders_queryset)
+
+ files_set_private.short_description = _("Enable permissions for selected files")
+
+ def files_set_public(self, request, files_queryset, folders_queryset):
+ return self.files_set_public_or_private(request, True, files_queryset,
+ folders_queryset)
+
+ files_set_public.short_description = _("Disable permissions for selected files")
+
+ def delete_files_or_folders(self, request, files_queryset, folders_queryset):
"""
Action which deletes the selected files and/or folders.
This action first displays a confirmation page whichs shows all the
- deleteable files and/or folders, or, if the user has no permission
- on one of the related childs (foreignkeys), a "permission denied"
- message.
+ deletable files and/or folders, or, if the user has no permission on
+ one of the related childs (foreignkeys), a "permission denied" message.
- Next, it delets all selected files and/or folders and redirects back
- to the folder.
+ Next, it deletes all selected files and/or folders and redirects back to
+ the folder.
"""
+ opts = self.model._meta
+ app_label = opts.app_label
# Check that the user has delete permission for the actual model
if not self.has_delete_permission(request):
raise PermissionDenied
- if not has_multi_file_action_permission(
- request, files_queryset, folders_queryset):
- raise PermissionDenied
-
- opts = self.model._meta
- app_label = opts.app_label
-
current_folder = self._get_current_action_folder(
request, files_queryset, folders_queryset)
all_protected = []
+ # Populate deletable_objects, a data structure of all related objects
+ # that will also be deleted. Hopefully this also checks for necessary
+ # permissions.
+ # TODO: Check if permissions are really verified
using = router.db_for_write(self.model)
- deletable_files, perms_needed_files, protected_files = \
- get_deleted_objects(
- files_queryset, files_queryset.model._meta,
- request.user, self.admin_site, using)
- files_count = self._get_unique_items(deletable_files, unique_items=[])
- deletable_folders, perms_needed_folders, protected_folders = \
- get_deleted_objects(
- folders_queryset, folders_queryset.model._meta,
- request.user, self.admin_site, using)
- folders_count = self._get_unique_items(deletable_folders, unique_items=[])
+ deletable_files, model_count_files, perms_needed_files, protected_files = get_deleted_objects(files_queryset, files_queryset.model._meta, request.user, self.admin_site, using)
+ deletable_folders, model_count_folder, perms_needed_folders, protected_folders = get_deleted_objects(folders_queryset, folders_queryset.model._meta, request.user, self.admin_site, using)
all_protected.extend(protected_files)
all_protected.extend(protected_folders)
all_deletable_objects = [deletable_files, deletable_folders]
all_perms_needed = perms_needed_files.union(perms_needed_folders)
- # The user has already confirmed the deletion.
- # Do the deletion and return a None to display the change list
- # view again.
+ # The user has already confirmed the deletion. Do the deletion and
+ # return a None to display the change list view again.
if request.POST.get('post'):
if all_perms_needed:
raise PermissionDenied
- total_count = files_count + folders_count
- if total_count:
+ n = files_queryset.count() + folders_queryset.count()
+ if n:
# delete all explicitly selected files
- for file_obj in files_queryset:
- self.log_deletion(request, file_obj, force_str(file_obj))
- file_obj.delete()
+ if DJANGO_VERSION >= (5, 1):
+ self.log_deletions(request, files_queryset)
+ # Still need to delete files individually (not only the database entries)
+ for f in files_queryset:
+ f.delete()
+ else:
+ for f in files_queryset:
+ self.log_deletion(request, f, force_str(f))
+ f.delete()
+ # delete all files in all selected folders and their children
+ # This would happen automatically by ways of the delete
+ # cascade, but then the individual .delete() methods won't be
+ # called and the files won't be deleted from the filesystem.
+ folder_ids = set()
+ for folder in folders_queryset:
+ folder_ids.add(folder.id)
+ folder_ids.update(folder.get_descendants_ids())
+ if DJANGO_VERSION >= (5, 1):
+ qs = File.objects.filter(folder__in=folder_ids)
+ self.log_deletions(request, qs)
+ # Still need to delete files individually (not only the database entries)
+ for f in qs:
+ f.delete()
+ else:
+ for f in File.objects.filter(folder__in=folder_ids):
+ self.log_deletion(request, f, force_str(f))
+ f.delete()
# delete all folders
- for file_id in folders_queryset.values_list('id', flat=True):
- file_obj = Folder.objects.get(id=file_id)
- self.log_deletion(request, file_obj, force_str(file_obj))
- file_obj.delete()
- self.message_user(request,
- _("Successfully deleted %(count)d files "
- "and/or folders.") % {"count": total_count, })
+ if DJANGO_VERSION >= (5, 1):
+ self.log_deletions(request, files_queryset)
+ folders_queryset.delete()
+ else:
+ for f in folders_queryset:
+ self.log_deletion(request, f, force_str(f))
+ f.delete()
+ self.message_user(request, _("Successfully deleted %(count)d files and/or folders.") % {"count": n, })
# Return None to display the change list page again.
return None
@@ -693,7 +826,8 @@ def delete_files_or_folders(self, request,
else:
title = _("Are you sure?")
- context = {
+ context = self.admin_site.each_context(request)
+ context.update({
"title": title,
"instance": current_folder,
"breadcrumbs_action": _("Delete files and/or folders"),
@@ -704,17 +838,20 @@ def delete_files_or_folders(self, request,
"protected": all_protected,
"opts": opts,
'is_popup': popup_status(request),
- 'select_folder': selectfolder_status(request),
+ 'filer_admin_context': AdminContext(request),
"root_path": reverse('admin:index'),
"app_label": app_label,
"action_checkbox_name": helpers.ACTION_CHECKBOX_NAME,
- }
- context.update(self.admin_site.each_context(request))
+ })
+
# Display the destination folder selection page
- return render(request, "admin/filer/delete_selected_files_confirmation.html", context)
+ return TemplateResponse(
+ request,
+ "admin/filer/delete_selected_files_confirmation.html",
+ context
+ )
- delete_files_or_folders.short_description = gettext_lazy(
- "Delete selected files and/or folders")
+ delete_files_or_folders.short_description = _("Delete selected files and/or folders")
# Copied from django.contrib.admin.util
def _format_callback(self, obj, user, admin_site, perms_needed):
@@ -726,297 +863,236 @@ def _format_callback(self, obj, user, admin_site, perms_needed):
opts.app_label,
opts.object_name.lower()),
None, (quote(obj._get_pk_val()),))
- get_permission_codename('delete', opts)
- p = '%s.%s' % (opts.app_label,
- get_permission_codename('delete', opts))
- # Also check permissions on individual objects
- if not user.has_perm(p, obj) and not user.has_perm(p):
+ p = get_delete_permission(opts)
+ if not user.has_perm(p):
perms_needed.add(opts.verbose_name)
# Display a link to the admin page.
- return mark_safe('%s: %s' %
- (escape(capfirst(opts.verbose_name)),
- admin_url,
- escape(obj.actual_name)))
+ return format_html('{}: {}', escape(capfirst(opts.verbose_name)), admin_url, escape(obj))
else:
# Don't display link to edit, because it either has no
# admin or is edited inline.
- return '%s: %s' % (capfirst(opts.verbose_name),
- force_str(obj.actual_name))
-
- def _get_current_action_folder(self, request, files_qs, folders_qs):
- current_folder = getattr(request, 'current_dir_list_folder', None)
- if current_folder:
- return current_folder
-
- if files_qs:
- return files_qs[0].folder
- elif folders_qs:
- return folders_qs[0].parent
+ return f'{capfirst(opts.verbose_name)}: {force_str(obj)}'
+
+ def _check_copy_perms(self, request, files_queryset, folders_queryset):
+ try:
+ check_files_read_permissions(request, files_queryset)
+ check_folder_read_permissions(request, folders_queryset)
+ except PermissionDenied:
+ return True
+ return False
+
+ def _check_move_perms(self, request, files_queryset, folders_queryset):
+ try:
+ check_files_read_permissions(request, files_queryset)
+ check_folder_read_permissions(request, folders_queryset)
+ check_files_edit_permissions(request, files_queryset)
+ check_folder_edit_permissions(request, folders_queryset)
+ except PermissionDenied:
+ return True
+ return False
+
+ def _get_current_action_folder(self, request, files_queryset,
+ folders_queryset):
+ if files_queryset:
+ return files_queryset[0].folder
+ elif folders_queryset:
+ return folders_queryset[0].parent
else:
return None
def _list_folders_to_copy_or_move(self, request, folders):
for fo in folders:
- yield self._format_callback(
- fo, request.user, self.admin_site, set())
- children = list(self._list_folders_to_copy_or_move(
- request, fo.children.all()))
- children.extend([self._format_callback(
- f, request.user, self.admin_site, set())
- for f in sorted(fo.files)])
+ yield self._format_callback(fo, request.user, self.admin_site, set())
+ children = list(self._list_folders_to_copy_or_move(request, fo.children.all()))
+ children.extend([self._format_callback(f, request.user, self.admin_site, set()) for f in sorted(fo.files)])
if children:
yield children
- def _list_all_to_copy_or_move(self, request,
- files_queryset, folders_queryset):
- to_copy_or_move = list(self._list_folders_to_copy_or_move(
- request, folders_queryset))
- to_copy_or_move.extend([self._format_callback(
- f, request.user, self.admin_site, set())
- for f in sorted(files_queryset)])
+ def _list_all_to_copy_or_move(self, request, files_queryset, folders_queryset):
+ to_copy_or_move = list(self._list_folders_to_copy_or_move(request, folders_queryset))
+ to_copy_or_move.extend([self._format_callback(f, request.user, self.admin_site, set()) for f in sorted(files_queryset)])
return to_copy_or_move
- def _move_files_and_folders_impl(self, files_queryset, folders_queryset,
- destination):
- for f in files_queryset:
- f.folder = destination
- f.save()
- for f_id in folders_queryset.values_list('id', flat=True):
- f = Folder.objects.get(id=f_id)
- f.parent = destination
- f.save()
-
- def _as_folder(self, request_data, param):
- try:
- return Folder.objects.get(id=int(request_data.get(param, None)))
- except (Folder.DoesNotExist, ValueError, TypeError):
- return None
+ def _list_all_destination_folders_recursive(self, request, folders_queryset, current_folder, folders, allow_self, level):
+ for fo in folders:
+ if not allow_self and fo in folders_queryset:
+ # We do not allow moving to selected folders or their descendants
+ continue
- def _clean_destination(self, request, current_folder,
- selected_folders):
- destination = self._as_folder(request.POST, 'destination')
- if not destination:
- raise PermissionDenied
- # check destination permissions
- if not is_valid_destination(request, destination):
- raise PermissionDenied
- # don't allow copy/move from folder to the same folder
- if (hasattr(current_folder, 'pk') and
- destination.pk == current_folder.pk):
- raise PermissionDenied
- # don't allow selected folders to be copied/moved inside
- # themselves or inside any of their descendants
- for folder in selected_folders:
- destination_in_selected = folder.get_descendants(include_self=True).filter(id=destination.pk).exists()
- if destination_in_selected:
- raise PermissionDenied
- return destination
-
- def destination_folders(self, request):
- all_required = all((
- request.method == 'GET',
- is_ajax(request),
- request.user.is_authenticated,
- 'parent' in request.GET
- ))
- if not all_required:
- raise PermissionDenied
+ if not fo.has_read_permission(request):
+ continue
- def _valid_candidates(request, candidates_qs, selected):
- # exclude orphaned/core/shared/restricted or any selected folders
- current_site = request.GET.get('current_site', None)
- return folders_available(current_site, request.user, candidates_qs) \
- .valid_destinations(request.user) \
- .unrestricted(request.user) \
- .exclude(id__in=selected)
-
- current_folder = self._as_folder(request.GET, 'current_folder')
- parent = self._as_folder(request.GET, 'parent')
- selected_ids = [_f for _f in [f_id or None for f_id in json.loads(request.GET.get('selected_folders') or '[]')] if _f]
- candidates = Folder.objects.filter(parent=parent)
- fancytree_candidates = []
- for folder in _valid_candidates(request, candidates, selected_ids):
- has_children = _valid_candidates(
- request, Folder.objects.filter(parent=folder), selected_ids
- ).exists()
- # don't allow move/copy files&folders to itself
- disabled = current_folder and current_folder.pk == folder.pk
- fancytree_candidates.append({
- 'title': folder.name,
- 'key': "%d" % folder.pk,
- 'folder': has_children,
- 'lazy': has_children,
- 'hideCheckbox': disabled,
- 'unselectable': disabled,
- 'icon': folder.icons.get('32', '')
- })
-
- return HttpResponse(
- json.dumps(fancytree_candidates), content_type="application/json")
-
- def move_files_and_folders(self, request,
- selected_files, selected_folders):
- opts = self.model._meta
- app_label = opts.app_label
+ # We do not allow copying/moving back to the folder itself
+ enabled = (allow_self or fo != current_folder) and fo.has_add_children_permission(request)
+ yield (fo, (mark_safe((" " * level) + force_str(fo)), enabled))
+ yield from self._list_all_destination_folders_recursive(request, folders_queryset, current_folder, fo.children.all(), allow_self, level + 1)
- if not has_multi_file_action_permission(request, selected_files, selected_folders):
- messages.error(request, "You are not allowed to move some of the "\
- "files and folders you selected.")
- return
+ def _list_all_destination_folders(self, request, folders_queryset, current_folder, allow_self):
+ root_folders = self.get_queryset(request).filter(parent__isnull=True).order_by('name')
+ return list(self._list_all_destination_folders_recursive(request, folders_queryset, current_folder, root_folders, allow_self, 0))
- if selected_folders.filter(parent=None).exists():
- messages.error(request, "To prevent potential problems, users "
- "are not allowed to move root folders. You may copy folders "
- "and files.")
- return
+ def _move_files_and_folders_impl(self, files_queryset, folders_queryset, destination):
+ files_queryset.update(folder=destination)
+ folders_queryset.update(parent=destination)
- current_folder = self._get_current_action_folder(
- request, selected_files, selected_folders)
- to_move = self._list_all_to_copy_or_move(
- request, selected_files, selected_folders)
+ def move_files_and_folders(self, request, files_queryset, folders_queryset):
+ opts = self.model._meta
+ app_label = opts.app_label
+
+ current_folder = self._get_current_action_folder(request, files_queryset, folders_queryset)
+ perms_needed = self._check_move_perms(request, files_queryset, folders_queryset)
+ to_move = self._list_all_to_copy_or_move(request, files_queryset, folders_queryset)
+ folders = self._list_all_destination_folders(request, folders_queryset, current_folder, False)
if request.method == 'POST' and request.POST.get('post'):
+ if perms_needed:
+ raise PermissionDenied
try:
- destination = self._clean_destination(
- request, current_folder, selected_folders)
- except PermissionDenied:
- messages.error(request, "The destination was not valid so the selected "\
- "files and folders were not moved. Please try again.")
- return
-
- # all folders need to belong to the same site as the
- # destination site folder
- sites_from_folders = \
- set(selected_folders.values_list('site_id', flat=True)) | \
- set(selected_files.exclude(folder__isnull=True).\
- values_list('folder__site_id', flat=True))
-
- if (sites_from_folders and
- None in sites_from_folders):
- messages.error(request, "Some of the selected files/folders "
- "do not belong to any site. Folders need to be assigned "
- "to a site before you can move files/folders from it.")
- return
- elif len(sites_from_folders) > 1:
- # it gets here if selection is made through a search view
- messages.error(request, "You cannot move files/folders that "
- "belong to several sites. Select files/folders that "
- "belong to only one site.")
- return
- elif (sites_from_folders and
- sites_from_folders.pop() != destination.site.id):
- messages.error(request, "Selected files/folders need to "
- "belong to the same site as the destination folder.")
- return
-
- if not self._are_candidate_names_valid(
- request, selected_files, selected_folders, destination):
- return
-
+ destination = self.get_queryset(request).get(pk=request.POST.get('destination'))
+ except self.model.DoesNotExist:
+ raise PermissionDenied
+ folders_dict = dict(folders)
+ if destination not in folders_dict or not folders_dict[destination][1]:
+ raise PermissionDenied
# We count only topmost files and folders here
- n = selected_files.count() + selected_folders.count()
- if n:
- self._move_files_and_folders_impl(
- selected_files, selected_folders, destination)
- self.message_user(request,
- _("Successfully moved %(count)d files and/or "
- "folders to folder '%(destination)s'.") % {
- "count": n,
- "destination": destination,
- })
+ n = files_queryset.count() + folders_queryset.count()
+ conflicting_names = [folder.name for folder in self.get_queryset(request).filter(parent=destination, name__in=folders_queryset.values('name'))]
+ if conflicting_names:
+ messages.error(request, _("Folders with names %s already exist at the selected "
+ "destination") % ", ".join(conflicting_names))
+ elif n:
+ self._move_files_and_folders_impl(files_queryset, folders_queryset, destination)
+ self.message_user(request, _("Successfully moved %(count)d files and/or folders to folder '%(destination)s'.") % {
+ "count": n,
+ "destination": destination,
+ })
return None
- context = {
+ context = self.admin_site.each_context(request)
+ context.update({
"title": _("Move files and/or folders"),
"instance": current_folder,
"breadcrumbs_action": _("Move files and/or folders"),
"to_move": to_move,
- "files_queryset": selected_files,
- "folders_queryset": selected_folders,
+ "destination_folders": folders,
+ "files_queryset": files_queryset,
+ "folders_queryset": folders_queryset,
+ "perms_lacking": perms_needed,
"opts": opts,
"root_path": reverse('admin:index'),
"app_label": app_label,
"action_checkbox_name": helpers.ACTION_CHECKBOX_NAME,
- }
- context.update(self.admin_site.each_context(request))
+ })
+
# Display the destination folder selection page
- return render(request, "admin/filer/folder/choose_move_destination.html", context)
+ return TemplateResponse(request, "admin/filer/folder/choose_move_destination.html", context)
- move_files_and_folders.short_description = gettext_lazy(
- "Move selected files and/or folders")
+ move_files_and_folders.short_description = _("Move selected files and/or folders")
- def extract_files(self, request, files_queryset, folder_queryset):
- success_format = "Successfully extracted archive {}."
+ def _rename_file(self, file_obj, form_data, counter, global_counter):
+ original_basename, original_extension = os.path.splitext(file_obj.original_filename)
+ if file_obj.name:
+ current_basename, current_extension = os.path.splitext(file_obj.name)
+ else:
+ current_basename = ""
+ current_extension = ""
+ file_obj.name = form_data['rename_format'] % {
+ 'original_filename': file_obj.original_filename,
+ 'original_basename': original_basename,
+ 'original_extension': original_extension,
+ 'current_filename': file_obj.name or "",
+ 'current_basename': current_basename,
+ 'current_extension': current_extension,
+ 'current_folder': getattr(file_obj.folder, 'name', ''),
+ 'counter': counter + 1, # 1-based
+ 'global_counter': global_counter + 1, # 1-based
+ }
+ file_obj.save()
- files_queryset = files_queryset.filter(
- polymorphic_ctype=ContentType.objects.get_for_model(Archive).id)
- # cannot extract in unfiled files folder
- if files_queryset.filter(folder__isnull=True).exists():
- raise PermissionDenied
+ def _rename_files(self, files, form_data, global_counter):
+ n = 0
+ for f in sorted(files):
+ self._rename_file(f, form_data, n, global_counter + n)
+ n += 1
+ return n
- if not has_multi_file_action_permission(request, files_queryset,
- Folder.objects.none()):
- raise PermissionDenied
+ def _rename_folder(self, folder, form_data, global_counter):
+ return self._rename_files_impl(folder.files.all(), folder.children.all(), form_data, global_counter)
- def is_valid_archive(filer_file):
- is_valid = filer_file.is_valid()
- if not is_valid:
- error_format = "{} is not a valid zip file"
- message = error_format.format(filer_file.clean_actual_name)
- messages.error(request, _(message))
- return is_valid
-
- def has_collisions(filer_file):
- collisions = filer_file.collisions()
- if collisions:
- error_format = "Files/Folders from {archive} with names:"
- error_format += "{names} already exist."
- names = ", ".join(collisions)
- archive = filer_file.clean_actual_name
- message = error_format.format(
- archive=archive,
- names=names,
- )
- messages.error(request, _(message))
- return len(collisions) > 0
-
- for f in files_queryset:
- if not is_valid_archive(f) or has_collisions(f):
- continue
- f.extract()
- message = success_format.format(f.actual_name)
- self.message_user(request, _(message))
- for err_msg in f.extract_errors:
- messages.warning(
- request,
- _("%s: %s" % (f.actual_name, err_msg))
- )
-
- extract_files.short_description = gettext_lazy(
- "Extract selected zip files")
+ def _rename_files_impl(self, files_queryset, folders_queryset, form_data, global_counter):
+ n = 0
+
+ for f in folders_queryset:
+ n += self._rename_folder(f, form_data, global_counter + n)
+
+ n += self._rename_files(files_queryset, form_data, global_counter + n)
+
+ return n
+
+ def rename_files(self, request, files_queryset, folders_queryset):
+ opts = self.model._meta
+ app_label = opts.app_label
+
+ current_folder = self._get_current_action_folder(request, files_queryset, folders_queryset)
+ perms_needed = self._check_move_perms(request, files_queryset, folders_queryset)
+ to_rename = self._list_all_to_copy_or_move(request, files_queryset, folders_queryset)
+
+ if request.method == 'POST' and request.POST.get('post'):
+ if perms_needed:
+ raise PermissionDenied
+ form = RenameFilesForm(request.POST)
+ if form.is_valid():
+ if files_queryset.count() + folders_queryset.count():
+ n = self._rename_files_impl(files_queryset, folders_queryset, form.cleaned_data, 0)
+ self.message_user(request, _("Successfully renamed %(count)d files.") % {
+ "count": n,
+ })
+ return None
+ else:
+ form = RenameFilesForm()
+
+ context = self.admin_site.each_context(request)
+ context.update({
+ "title": _("Rename files"),
+ "instance": current_folder,
+ "breadcrumbs_action": _("Rename files"),
+ "to_rename": to_rename,
+ "rename_form": form,
+ "files_queryset": files_queryset,
+ "folders_queryset": folders_queryset,
+ "perms_lacking": perms_needed,
+ "opts": opts,
+ "root_path": reverse('admin:index'),
+ "app_label": app_label,
+ "action_checkbox_name": helpers.ACTION_CHECKBOX_NAME,
+ })
+
+ # Display the rename format selection page
+ return TemplateResponse(request, "admin/filer/folder/choose_rename_format.html", context)
+
+ rename_files.short_description = _("Rename files")
+
+ def _generate_new_filename(self, filename, suffix):
+ basename, extension = os.path.splitext(filename)
+ return basename + suffix + extension
def _copy_file(self, file_obj, destination, suffix, overwrite):
if overwrite:
- # Not yet implemented as we have to find a portable
- # (for different storage backends) way to overwrite files
+ # Not yet implemented as we have to find a portable (for different storage backends) way to overwrite files
raise NotImplementedError
- # We are assuming here that we are operating on an already saved
- # database objects with current database state available
+ # We are assuming here that we are operating on an already saved database objects with current database state available
+
+ filename = self._generate_new_filename(file_obj.file.name, suffix)
# Due to how inheritance works, we have to set both pk and id to None
file_obj.pk = None
file_obj.id = None
- file_obj.restricted = False
+ file_obj.save()
file_obj.folder = destination
- # add suffix to actual name
- if file_obj.name in ('', None):
- file_obj.original_filename = self._generate_name(
- file_obj.original_filename, suffix)
- else:
- file_obj.name = self._generate_name(file_obj.name, suffix)
- new_path = file_obj.file.field.upload_to(file_obj, file_obj.actual_name)
- file_obj.file = file_obj._copy_file(new_path)
+ file_obj._file_data_changed_hint = False # no need to update size, sha1, etc.
+ file_obj.file = file_obj._copy_file(filename)
+ file_obj.original_filename = self._generate_new_filename(file_obj.original_filename, suffix)
file_obj.save()
def _copy_files(self, files, destination, suffix, overwrite):
@@ -1024,331 +1100,161 @@ def _copy_files(self, files, destination, suffix, overwrite):
self._copy_file(f, destination, suffix, overwrite)
return len(files)
+ def _get_available_name(self, destination, name):
+ count = itertools.count(1)
+ original = name
+ while destination.contains_folder(name):
+ name = f"{original}_{next(count)}"
+ return name
+
def _copy_folder(self, folder, destination, suffix, overwrite):
if overwrite:
- # Not yet implemented as we have to find a portable
- # (for different storage backends) way to overwrite files
+ # Not yet implemented as we have to find a portable (for different storage backends) way to overwrite files
raise NotImplementedError
- foldername = self._generate_name(folder.name, suffix)
+ # TODO: Should we also allow not to overwrite the folder if it exists, but just copy into it?
+
+ # TODO: Is this a race-condition? Would this be a problem?
+ foldername = self._get_available_name(destination, folder.name)
+
old_folder = Folder.objects.get(pk=folder.pk)
- # Due to how inheritance works, we have to set both pk and id to None
- # lft and rght need to be reset since otherwise will see this node
- # as 'already set up for insertion' and will not recalculate tree
- # values
- folder.pk = folder.id = folder.lft = folder.rght = None
- folder.restricted = False
- folder.name = foldername
- folder.parent = destination
- folder.save()
-
- return 1 + self._copy_files_and_folders_impl(
- old_folder.files.all(), old_folder.children.all(),
- folder, suffix, overwrite)
-
- def _copy_files_and_folders_impl(self, files_queryset, folders_queryset,
- destination, suffix, overwrite):
+ folder, _ = Folder.objects.get_or_create(
+ name=foldername,
+ owner=old_folder.owner,
+ parent=destination,
+ )
+
+ for perm in FolderPermission.objects.filter(folder=old_folder):
+ perm.pk = None
+ perm.id = None
+ perm.folder = folder
+ perm.save()
+ return 1 + self._copy_files_and_folders_impl(old_folder.files.all(), old_folder.children.all(), folder, suffix, overwrite)
+
+ def _copy_files_and_folders_impl(self, files_queryset, folders_queryset, destination, suffix, overwrite):
n = self._copy_files(files_queryset, destination, suffix, overwrite)
- for f_id in folders_queryset.values_list('id', flat=True):
- f = Folder.objects.get(id=f_id)
- destination = Folder.objects.get(id=destination.id)
+ for f in folders_queryset:
n += self._copy_folder(f, destination, suffix, overwrite)
return n
- def _generate_name(self, filename, suffix):
- if not suffix:
- return filename
- basename, extension = os.path.splitext(filename)
- return basename + suffix + extension
-
- def _are_candidate_names_valid(
- self, request, file_qs, folder_qs, destination, suffix=None):
- candidate_folder_names = [self._generate_name(name, suffix)
- for name in folder_qs.values_list(
- 'name', flat=True)]
- candidate_file_names = [
- self._generate_name(file_obj.actual_name, suffix)
- for file_obj in file_qs]
-
- existing_names = [f.actual_name
- for f in destination.entries_with_names(
- candidate_folder_names + candidate_file_names)]
-
- if existing_names:
- messages.error(request,
- _("File or folders with names %s already exist at the "
- "selected destination") % ", ".join(existing_names))
- return False
- return True
-
- def copy_files_and_folders(self, request,
- files_queryset, folders_queryset):
+ def copy_files_and_folders(self, request, files_queryset, folders_queryset):
opts = self.model._meta
app_label = opts.app_label
- current_folder = self._get_current_action_folder(
- request, files_queryset, folders_queryset)
- to_copy = self._list_all_to_copy_or_move(
- request, files_queryset, folders_queryset)
+ current_folder = self._get_current_action_folder(request, files_queryset, folders_queryset)
+ perms_needed = self._check_copy_perms(request, files_queryset, folders_queryset)
+ to_copy = self._list_all_to_copy_or_move(request, files_queryset, folders_queryset)
+ folders = self._list_all_destination_folders(request, folders_queryset, current_folder, False)
if request.method == 'POST' and request.POST.get('post'):
+ if perms_needed:
+ raise PermissionDenied
form = CopyFilesAndFoldersForm(request.POST)
if form.is_valid():
try:
- destination = self._clean_destination(
- request, current_folder, folders_queryset)
- except PermissionDenied:
- messages.error(request,
- _("The selected destination was not valid, so the selected "\
- "files and folders were not copied. Please try again."))
- return None
-
- suffix = form.cleaned_data['suffix']
- if not self._are_candidate_names_valid(
- request, files_queryset, folders_queryset,
- destination, suffix): return
-
+ destination = self.get_queryset(request).get(pk=request.POST.get('destination'))
+ except self.model.DoesNotExist:
+ raise PermissionDenied
+ folders_dict = dict(folders)
+ if destination not in folders_dict or not folders_dict[destination][1]:
+ raise PermissionDenied
if files_queryset.count() + folders_queryset.count():
- # We count all files and folders here (recursivelly)
- n = self._copy_files_and_folders_impl(
- files_queryset, folders_queryset, destination,
- suffix, False)
- self.message_user(request,
- _("Successfully copied %(count)d files and/or "
- "folders to folder '%(destination)s'.") % {
- "count": n,
- "destination": destination,
- })
+ # We count all files and folders here (recursively)
+ n = self._copy_files_and_folders_impl(files_queryset, folders_queryset, destination, form.cleaned_data['suffix'], False)
+ self.message_user(request, _("Successfully copied %(count)d files and/or folders to folder '%(destination)s'.") % {
+ "count": n,
+ "destination": destination,
+ })
return None
else:
form = CopyFilesAndFoldersForm()
try:
- selected_destination_folder = \
- int(request.POST.get('destination', 0))
+ selected_destination_folder = int(request.POST.get('destination', 0))
except ValueError:
if current_folder:
selected_destination_folder = current_folder.pk
else:
selected_destination_folder = 0
- context = {
+
+ context = self.admin_site.each_context(request)
+ context.update({
"title": _("Copy files and/or folders"),
"instance": current_folder,
"breadcrumbs_action": _("Copy files and/or folders"),
"to_copy": to_copy,
+ "destination_folders": folders,
"selected_destination_folder": selected_destination_folder,
"copy_form": form,
"files_queryset": files_queryset,
"folders_queryset": folders_queryset,
+ "perms_lacking": perms_needed,
"opts": opts,
"root_path": reverse('admin:index'),
"app_label": app_label,
"action_checkbox_name": helpers.ACTION_CHECKBOX_NAME,
- }
- context.update(self.admin_site.each_context(request))
- # Display the destination folder selection page
- return render(request, "admin/filer/folder/choose_copy_destination.html", context)
-
- copy_files_and_folders.short_description = gettext_lazy(
- "Copy selected files and/or folders")
-
- def files_toggle_restriction(self, request, restriction,
- files_qs, folders_qs):
- """
- Action which enables or disables restriction for files/folders.
- """
- if request.method != 'POST':
- return None
- # cannot restrict/unrestrict unfiled files
- unfiled_files = files_qs.filter(folder__isnull=True)
- if unfiled_files.exists():
- messages.warning(request, _("Some of the selected files do not have parents: %s, "
- "so their rights cannot be changed.") %
- ', '.join([str(unfiled_file) for unfiled_file in unfiled_files.all()]))
- return None
-
- if not has_multi_file_action_permission(request, files_qs, folders_qs):
- messages.warning(request, _("You are not allowed to modify the restrictions on "\
- "the selected files and folders."))
- return None
-
- count = [0]
-
- def set_files_or_folders(filer_obj):
- for f in filer_obj:
- if f.restricted != restriction:
- f.restricted = restriction
- f.save()
- count[0] += 1
-
- set_files_or_folders(files_qs)
- set_files_or_folders(folders_qs)
- count = count[0]
- if restriction:
- self.message_user(request,
- _("Successfully enabled restriction for %(count)d files "
- "and/or folders.") % {"count": count,})
- else:
- self.message_user(request,
- _("Successfully disabled restriction for %(count)d files "
- "and/or folders.") % {"count": count,})
-
- return None
-
- def enable_restriction(self, request, files_qs, folders_qs):
- return self.files_toggle_restriction(
- request, True, files_qs, folders_qs)
-
- enable_restriction.short_description = gettext_lazy(
- "Enable restriction for selected and/or folders")
-
- def disable_restriction(self, request, files_qs, folders_qs):
- return self.files_toggle_restriction(
- request, False, files_qs, folders_qs)
-
- disable_restriction.short_description = gettext_lazy(
- "Disable restriction for selected and/or folders")
-
-'''
- def _rename_file(self, file_obj, form_data, counter, global_counter):
- original_basename, original_extension = os.path.splitext(
- file_obj.original_filename)
- if file_obj.name:
- current_basename, current_extension = os.path.splitext(
- file_obj.name)
- else:
- current_basename = ""
- current_extension = ""
- file_obj.name = form_data['rename_format'] % {
- 'original_filename': file_obj.original_filename,
- 'original_basename': original_basename,
- 'original_extension': original_extension,
- 'current_filename': file_obj.name or "",
- 'current_basename': current_basename,
- 'current_extension': current_extension,
- 'current_folder': file_obj.folder.name,
- 'counter': counter + 1, # 1-based
- 'global_counter': global_counter + 1, # 1-based
- }
- file_obj.save()
-
- def _rename_files(self, files, form_data, global_counter):
- n = 0
- for f in sorted(files):
- self._rename_file(f, form_data, n, global_counter + n)
- n += 1
- return n
-
- def _rename_folder(self, folder, form_data, global_counter):
- return self._rename_files_impl(
- folder.files.all(), folder.children.all(),
- form_data, global_counter)
-
- def _rename_files_impl(self, files_queryset, folders_queryset,
- form_data, global_counter):
- n = 0
-
- for f in folders_queryset:
- n += self._rename_folder(f, form_data, global_counter + n)
-
- n += self._rename_files(files_queryset, form_data, global_counter + n)
-
- return n
-
- def rename_files(self, request, files_queryset, folders_queryset):
- # this logic needs to be suplimented with folder type permission layer
- opts = self.model._meta
- app_label = opts.app_label
-
- current_folder = self._get_current_action_folder(
- request, files_queryset, folders_queryset)
- to_rename = self._list_all_to_copy_or_move(
- request, files_queryset, folders_queryset)
-
- if request.method == 'POST' and request.POST.get('post'):
- form = RenameFilesForm(request.POST)
- if form.is_valid():
- if files_queryset.count() + folders_queryset.count():
- n = self._rename_files_impl(
- files_queryset, folders_queryset,
- form.cleaned_data, 0)
- self.message_user(request,
- _("Successfully renamed %(count)d files.") % {
- "count": n,
- })
- return None
- else:
- form = RenameFilesForm()
+ })
- context = {
- "title": _("Rename files"),
- "instance": current_folder,
- "breadcrumbs_action": _("Rename files"),
- "to_rename": to_rename,
- "rename_form": form,
- "files_queryset": files_queryset,
- "folders_queryset": folders_queryset,
- "opts": opts,
- "root_path": reverse('admin:index'),
- "app_label": app_label,
- "action_checkbox_name": helpers.ACTION_CHECKBOX_NAME,
- }
+ # Display the destination folder selection page
+ return TemplateResponse(request, "admin/filer/folder/choose_copy_destination.html", context)
- # Display the rename format selection page
- return render(request, "admin/filer/folder/choose_rename_format.html", context=context)
+ copy_files_and_folders.short_description = _("Copy selected files and/or folders")
- rename_files.short_description = gettext_lazy("Rename files")
+ def _check_resize_perms(self, request, files_queryset, folders_queryset):
+ try:
+ check_files_read_permissions(request, files_queryset)
+ check_folder_read_permissions(request, folders_queryset)
+ check_files_edit_permissions(request, files_queryset)
+ except PermissionDenied:
+ return True
+ return False
def _list_folders_to_resize(self, request, folders):
for fo in folders:
- children = list(self._list_folders_to_resize(
- request, fo.children.all()))
- children.extend([self._format_callback(
- f, request.user, self.admin_site, set())
- for f in sorted(fo.files)
- if isinstance(f, Image)])
+ children = list(self._list_folders_to_resize(request, fo.children.all()))
+ children.extend([self._format_callback(f, request.user, self.admin_site, set()) for f in sorted(fo.files) if isinstance(f, Image)])
if children:
- yield self._format_callback(
- fo, request.user, self.admin_site, set())
+ yield self._format_callback(fo, request.user, self.admin_site, set())
yield children
def _list_all_to_resize(self, request, files_queryset, folders_queryset):
- to_resize = list(self._list_folders_to_resize(
- request, folders_queryset))
- to_resize.extend([self._format_callback(
- f, request.user, self.admin_site, set())
- for f in sorted(files_queryset)
- if isinstance(f, Image)])
+ to_resize = list(self._list_folders_to_resize(request, folders_queryset))
+ to_resize.extend([self._format_callback(f, request.user, self.admin_site, set()) for f in sorted(files_queryset) if isinstance(f, Image)])
return to_resize
- def _new_subject_location(self, original_width, original_height,
- new_width, new_height, x, y, crop):
- # TODO: We could probably do better
- return (round(new_width / 2), round(new_height / 2))
+ def _new_subject_location(self, original_width, original_height, new_width, new_height, x, y, crop):
+ # TODO: We could probably do even better, but this method knows nothing
+ # about actual thumbnailing algorithm details.
+ # It's better to reset subject location to the central point of the new
+ # image if the image is being cropped. The originally specified subject
+ # location could be outside of the new image.
+ if crop:
+ return int(new_width / 2), int(new_height / 2)
+ else:
+ # Calculate scaling factor of the new image compared to old.
+ scale = min(new_width / original_width, new_height / original_height)
+ return int(scale * x), int(scale * y)
def _resize_image(self, image, form_data):
original_width = float(image.width)
original_height = float(image.height)
- thumbnailer = FilerActionThumbnailer(
- file=image.file.file,
- name=image.file.name,
- source_storage=image.file.source_storage,
- thumbnail_storage=image.file.source_storage)
+ thumbnailer = FilerActionThumbnailer(file=image.file, name=image.file.name, source_storage=image.file.source_storage, thumbnail_storage=image.file.source_storage)
# This should overwrite the original image
new_image = thumbnailer.get_thumbnail({
- 'size': (form_data['width'], form_data['height']),
+ 'size': tuple(int(form_data[d] or 0) for d in ('width', 'height')),
'crop': form_data['crop'],
'upscale': form_data['upscale'],
'subject_location': image.subject_location,
})
- from django.db.models.fields.files import ImageFieldFile
image.file.file = new_image.file
- image.generate_sha1()
- image.save() # Also gets new width and height
+ # Since only file data was changed, there is no way for file field to know about the change.
+ # To update size, sha1, width and height fields let's call file_data_changed callback directly.
+ image.file_data_changed()
+ image.save()
subject_location = normalize_subject_location(image.subject_location)
if subject_location:
@@ -1357,9 +1263,7 @@ def _resize_image(self, image, form_data):
y = float(y)
new_width = float(image.width)
new_height = float(image.height)
- (new_x, new_y) = self._new_subject_location(
- original_width, original_height, new_width, new_height,
- x, y, form_data['crop'])
+ (new_x, new_y) = self._new_subject_location(original_width, original_height, new_width, new_height, x, y, form_data['crop'])
image.subject_location = "%d,%d" % (new_x, new_y)
image.save()
@@ -1372,11 +1276,9 @@ def _resize_images(self, files, form_data):
return n
def _resize_folder(self, folder, form_data):
- return self._resize_images_impl(
- folder.files.all(), folder.children.all(), form_data)
+ return self._resize_images_impl(folder.files.all(), folder.children.all(), form_data)
- def _resize_images_impl(self, files_queryset,
- folders_queryset, form_data):
+ def _resize_images_impl(self, files_queryset, folders_queryset, form_data):
n = self._resize_images(files_queryset, form_data)
for f in folders_queryset:
@@ -1388,115 +1290,45 @@ def resize_images(self, request, files_queryset, folders_queryset):
opts = self.model._meta
app_label = opts.app_label
- current_folder = self._get_current_action_folder(
- request, files_queryset, folders_queryset)
- to_resize = self._list_all_to_resize(
- request, files_queryset, folders_queryset)
+ current_folder = self._get_current_action_folder(request, files_queryset, folders_queryset)
+ perms_needed = self._check_resize_perms(request, files_queryset, folders_queryset)
+ to_resize = self._list_all_to_resize(request, files_queryset, folders_queryset)
if request.method == 'POST' and request.POST.get('post'):
+ if perms_needed:
+ raise PermissionDenied
form = ResizeImagesForm(request.POST)
if form.is_valid():
if form.cleaned_data.get('thumbnail_option'):
- form.cleaned_data['width'] = \
- form.cleaned_data['thumbnail_option'].width
- form.cleaned_data['height'] = \
- form.cleaned_data['thumbnail_option'].height
- form.cleaned_data['crop'] = \
- form.cleaned_data['thumbnail_option'].crop
- form.cleaned_data['upscale'] = \
- form.cleaned_data['thumbnail_option'].upscale
+ form.cleaned_data['width'] = form.cleaned_data['thumbnail_option'].width
+ form.cleaned_data['height'] = form.cleaned_data['thumbnail_option'].height
+ form.cleaned_data['crop'] = form.cleaned_data['thumbnail_option'].crop
+ form.cleaned_data['upscale'] = form.cleaned_data['thumbnail_option'].upscale
if files_queryset.count() + folders_queryset.count():
- # We count all files here (recursivelly)
- n = self._resize_images_impl(
- files_queryset, folders_queryset, form.cleaned_data)
- self.message_user(request,
- _("Successfully resized %(count)d images.") % {
- "count": n,
- })
+ # We count all files here (recursively)
+ n = self._resize_images_impl(files_queryset, folders_queryset, form.cleaned_data)
+ self.message_user(request, _("Successfully resized %(count)d images.") % {"count": n, })
return None
else:
form = ResizeImagesForm()
- context = {
+ context = self.admin_site.each_context(request)
+ context.update({
"title": _("Resize images"),
"instance": current_folder,
"breadcrumbs_action": _("Resize images"),
"to_resize": to_resize,
"resize_form": form,
- "cmsplugin_enabled": ('cmsplugin_filer_image'
- in django_settings.INSTALLED_APPS),
"files_queryset": files_queryset,
"folders_queryset": folders_queryset,
+ "perms_lacking": perms_needed,
"opts": opts,
"root_path": reverse('admin:index'),
"app_label": app_label,
"action_checkbox_name": helpers.ACTION_CHECKBOX_NAME,
- }
+ })
# Display the resize options page
- return render(
- request,
- "admin/filer/folder/choose_images_resize_options.html",
- context=context,
- )
-
- resize_images.short_description = gettext_lazy("Resize selected images")
-
- def files_set_public_or_private(self, request, set_public,
- files_queryset, folders_queryset):
- """
- Action which enables or disables permissions for selected
- files and files in selected folders to clipboard
- (set them private or public).
- """
-
- if not self.has_change_permission(request):
- raise PermissionDenied
-
- if request.method != 'POST':
- return None
-
- # We define it like that so that we can modify it inside the
- # set_files function
- files_count = [0]
-
- def set_files(files):
- for f in files:
- if f.is_public != set_public:
- f.is_public = set_public
- f.save()
- files_count[0] += 1
-
- def set_folders(folders):
- for f in folders:
- set_files(f.files)
- set_folders(f.children.all())
-
- set_files(files_queryset)
- set_folders(folders_queryset)
-
- if set_public:
- self.message_user(request,
- _("Successfully disabled permissions for %(count)d files.") % {
- "count": files_count[0], })
- else:
- self.message_user(request,
- _("Successfully enabled permissions for %(count)d files.") % {
- "count": files_count[0], })
-
- return None
-
- def files_set_private(self, request, files_queryset, folders_queryset):
- return self.files_set_public_or_private(
- request, False, files_queryset, folders_queryset)
-
- files_set_private.short_description = gettext_lazy(
- "Enable permissions for selected files")
-
- def files_set_public(self, request, files_queryset, folders_queryset):
- return self.files_set_public_or_private(
- request, True, files_queryset, folders_queryset)
+ return TemplateResponse(request, "admin/filer/folder/choose_images_resize_options.html", context)
- files_set_public.short_description = gettext_lazy(
- "Disable permissions for selected files")
-'''
+ resize_images.short_description = _("Resize selected images")
diff --git a/filer/admin/forms.py b/filer/admin/forms.py
index 52925c0d9..1a35e4125 100644
--- a/filer/admin/forms.py
+++ b/filer/admin/forms.py
@@ -1,40 +1,38 @@
from django import forms
-from django.db import models
from django.contrib.admin import widgets
-from filer.utils.files import get_valid_filename
-from django.utils.translation import gettext as _
+from django.contrib.admin.helpers import AdminForm
from django.core.exceptions import ValidationError
-from django.conf import settings
+from django.db import models
+from django.utils.translation import gettext as _
+from ..models import ThumbnailOption
+from ..utils.files import get_valid_filename
-if 'cmsplugin_filer_image' in settings.INSTALLED_APPS:
- from cmsplugin_filer_image.models import ThumbnailOption
+class WithFieldsetMixin:
+ def get_fieldsets(self):
+ return getattr(self, "fieldsets", [
+ (None, {"fields": [field for field in self.fields]})
+ ])
-class AsPWithHelpMixin(object):
- def as_p_with_help(self):
- "Returns this form rendered as HTML s with help text formated for admin."
- return self._html_output(
- normal_row='
%(label)s %(field)s
%(help_text)s',
- error_row='%s',
- row_ender='
',
- help_text_html='%s
',
- errors_on_separate_row=True)
+ def admin_form(self):
+ "Returns a class contains the Admin fieldset to show form as admin form"
+ return AdminForm(self, self.get_fieldsets(), {})
-class CopyFilesAndFoldersForm(forms.Form, AsPWithHelpMixin):
+class CopyFilesAndFoldersForm(forms.Form):
suffix = forms.CharField(required=False, help_text=_("Suffix which will be appended to filenames of copied files."))
# TODO: We have to find a way to overwrite files with different storage backends first.
- #overwrite_files = forms.BooleanField(required=False, help_text=_("Overwrite a file if there already exists a file with the same filename?"))
+ # overwrite_files = forms.BooleanField(required=False, help_text=_("Overwrite a file if there already exists a file with the same filename?"))
def clean_suffix(self):
- valid = get_valid_filename(self.cleaned_data['suffix'])
+ valid = get_valid_filename(self.cleaned_data['suffix']) if self.cleaned_data['suffix'] else ""
if valid != self.cleaned_data['suffix']:
raise forms.ValidationError(_('Suffix should be a valid, simple and lowercase filename part, like "%(valid)s".') % {'valid': valid})
return self.cleaned_data['suffix']
-class RenameFilesForm(forms.Form, AsPWithHelpMixin):
+class RenameFilesForm(WithFieldsetMixin, forms.Form):
rename_format = forms.CharField(required=True)
def clean_rename_format(self):
@@ -57,9 +55,20 @@ def clean_rename_format(self):
return self.cleaned_data['rename_format']
-class ResizeImagesForm(forms.Form, AsPWithHelpMixin):
- if 'cmsplugin_filer_image' in settings.INSTALLED_APPS:
- thumbnail_option = models.ForeignKey(ThumbnailOption, null=True, blank=True, verbose_name=_("thumbnail option")).formfield()
+class ResizeImagesForm(WithFieldsetMixin, forms.Form):
+ fieldsets = ((None, {"fields": (
+ "thumbnail_option",
+ ("width", "height"),
+ ("crop", "upscale"))}),)
+
+ thumbnail_option = models.ForeignKey(
+ ThumbnailOption,
+ null=True,
+ blank=True,
+ verbose_name=_("thumbnail option"),
+ on_delete=models.CASCADE,
+ ).formfield()
+
width = models.PositiveIntegerField(_("width"), null=True, blank=True).formfield(widget=widgets.AdminIntegerFieldWidget)
height = models.PositiveIntegerField(_("height"), null=True, blank=True).formfield(widget=widgets.AdminIntegerFieldWidget)
crop = models.BooleanField(_("crop"), default=True).formfield()
@@ -67,8 +76,5 @@ class ResizeImagesForm(forms.Form, AsPWithHelpMixin):
def clean(self):
if not (self.cleaned_data.get('thumbnail_option') or ((self.cleaned_data.get('width') or 0) + (self.cleaned_data.get('height') or 0))):
- if 'cmsplugin_filer_image' in settings.INSTALLED_APPS:
- raise ValidationError(_('Thumbnail option or resize parameters must be choosen.'))
- else:
- raise ValidationError(_('Resize parameters must be choosen.'))
+ raise ValidationError(_('Thumbnail option or resize parameters must be choosen.'))
return self.cleaned_data
diff --git a/filer/admin/imageadmin.py b/filer/admin/imageadmin.py
index 96fd494f3..aec3d41f9 100644
--- a/filer/admin/imageadmin.py
+++ b/filer/admin/imageadmin.py
@@ -1,63 +1,121 @@
-#-*- coding: utf-8 -*-
from django import forms
-from django.utils.translation import gettext as _
-from django.shortcuts import render
-from django.http import Http404
-from django.urls import re_path
-from filer.admin.fileadmin import FileAdmin
-from filer.models import Image
-from filer.views import (popup_status, selectfolder_status)
+from django.shortcuts import get_object_or_404
+from django.template.response import TemplateResponse
+from django.urls import path
+from django.utils.translation import gettext as _
+from django.utils.translation import gettext_lazy
+from ..settings import FILER_IMAGE_MODEL
+from ..thumbnail_processors import normalize_subject_location
+from ..utils.compatibility import string_concat
+from ..utils.loader import load_model
+from .fileadmin import FileAdmin, FileAdminChangeFrom
-class ImageAdminForm(forms.ModelForm):
+
+Image = load_model(FILER_IMAGE_MODEL)
+
+
+class ImageAdminForm(FileAdminChangeFrom):
subject_location = forms.CharField(
- max_length=64, required=False,
- label=_('Subject location'),
- help_text=_('Location of the main subject of the scene.'))
+ max_length=64, required=False,
+ label=_('Subject location'),
+ help_text=_('Location of the main subject of the scene. '
+ 'Format: "x,y".'))
def sidebar_image_ratio(self):
if self.instance:
# this is very important. It forces the value to be returned as a
- # string and always with a "." as seperator. If the conversion
+ # string and always with a "." as separator. If the conversion
# from float to string is done in the template, the locale will
# be used and in some cases there would be a "," instead of ".".
# javascript would parse that to an integer.
- return "%.6F" % self.instance.sidebar_image_ratio()
+ return '%.6F' % self.instance.sidebar_image_ratio()
else:
return ''
+ def _set_previous_subject_location(self, cleaned_data):
+ subject_location = self.instance.subject_location
+ cleaned_data['subject_location'] = subject_location
+ self.data = self.data.copy()
+ self.data['subject_location'] = subject_location
+
+ def clean_subject_location(self):
+ """
+ Validate subject_location preserving last saved value.
+
+ Last valid value of the subject_location field is shown to the user
+ for subject location widget to receive valid coordinates on field
+ validation errors.
+ """
+ subject_location = self.cleaned_data['subject_location']
+ if not subject_location:
+ # if supplied subject location is empty, do not check it
+ return subject_location
+
+ # use thumbnail's helper function to check the format
+ coordinates = normalize_subject_location(subject_location)
+
+ if not coordinates:
+ err_msg = gettext_lazy('Invalid subject location format. ')
+ err_code = 'invalid_subject_format'
+
+ elif (
+ coordinates[0] > self.instance.width > 0
+ or coordinates[1] > self.instance.height > 0
+ ):
+ err_msg = gettext_lazy(
+ 'Subject location is outside of the image. ')
+ err_code = 'subject_out_of_bounds'
+ else:
+ return subject_location
+
+ self._set_previous_subject_location(self.cleaned_data)
+ raise forms.ValidationError(
+ string_concat(
+ err_msg,
+ gettext_lazy('Your input: "{subject_location}". '.format(
+ subject_location=subject_location)),
+ 'Previous value is restored.'),
+ code=err_code)
+
class Meta:
model = Image
exclude = ()
class ImageAdmin(FileAdmin):
+ change_form_template = 'admin/filer/image/change_form.html'
form = ImageAdminForm
def get_urls(self):
- urls = super(ImageAdmin, self).get_urls()
- url_patterns = [
- re_path(r'^(?P\d+)/full_size_preview/$',
- self.admin_site.admin_view(self.full_size_preview),
- name='filer-image-preview'),
+ return super().get_urls() + [
+ path("expand/",
+ self.admin_site.admin_view(self.expand_view),
+ name=f"{self.opts.app_label}_{self.opts.model_name}_expand")
]
- url_patterns.extend(urls)
- return url_patterns
-
- def full_size_preview(self, request, file_id):
- try:
- image = Image.objects.get(id=file_id)
- except Image.DoesNotExist:
- raise Http404
-
- return render(request, 'admin/filer/image/full_size_preview.html', {
- 'image': image,
- 'current_site': request.GET.get('current_site', None),
- 'is_popup': popup_status(request),
- 'select_folder': selectfolder_status(request),
- })
+
+ def expand_view(self, request, file_id):
+ image = get_object_or_404(self.model, pk=file_id)
+ return TemplateResponse(
+ request,
+ "admin/filer/image/expand.html",
+ context={
+ "original_url": image.url
+ },
+ )
+
+
+if FILER_IMAGE_MODEL == 'filer.Image':
+ extra_main_fields = ('author', 'default_alt_text', 'default_caption',)
+else:
+ extra_main_fields = ('default_alt_text', 'default_caption',)
ImageAdmin.fieldsets = ImageAdmin.build_fieldsets(
- extra_main_fields=('default_alt_text', 'default_caption', 'default_credit'),
- extra_fieldsets=()
+ extra_main_fields=extra_main_fields,
+ extra_fieldsets=(
+ (_('Subject location'), {
+ 'fields': ('subject_location',),
+ 'classes': ('collapse',),
+ }),
+ )
)
diff --git a/filer/admin/patched/__init__.py b/filer/admin/patched/__init__.py
index 53f7f2286..e69de29bb 100644
--- a/filer/admin/patched/__init__.py
+++ b/filer/admin/patched/__init__.py
@@ -1,2 +0,0 @@
-#-*- coding: utf-8 -*-
-
diff --git a/filer/admin/patched/admin_utils.py b/filer/admin/patched/admin_utils.py
index ec986f68a..84602d5e5 100644
--- a/filer/admin/patched/admin_utils.py
+++ b/filer/admin/patched/admin_utils.py
@@ -1,31 +1,34 @@
-# -*- coding: utf-8 -*-
"""
Copy of ``django.contrib.admin.utils.get_deleted_objects`` and a subclass of
-``django.contrib.admin.utils.NestedObjects`` that work with djongo_polymorphic querysets.
-Ultimatly these should go directly into django_polymorphic or, in a more generic way, into django itself.
+``django.contrib.admin.utils.NestedObjects`` that work with django-polymorphic
+querysets.
+Ultimately these should go directly into django-polymorphic or, in a more
+generic way, into Django itself.
-This code has been copied from
-django 1.4. ``get_deleted_objects`` and ``NestedObjects`` have not changed compared to 1.3.1.
+This code has been copied from Django 1.9.4.
-At all locations where something has been changed, there are inline comments in the code.
+At all locations where something has been changed, there are inline comments
+in the code.
"""
-from django.contrib.admin.utils import NestedObjects, quote
+from collections import defaultdict
+
+from django import VERSION as DJANGO_VERSION
+from django.contrib.admin.utils import quote
from django.contrib.auth import get_permission_codename
-from django.utils.text import capfirst
-from django.utils.html import format_html
+from django.db import models
+from django.db.models.deletion import Collector
from django.urls import NoReverseMatch, reverse
from django.utils.encoding import force_str
-
+from django.utils.html import format_html
+from django.utils.text import capfirst
def get_deleted_objects(objs, opts, user, admin_site, using):
"""
Find all objects related to ``objs`` that should also be deleted. ``objs``
- must be a homogenous iterable of objects (e.g. a QuerySet).
-
+ must be a homogeneous iterable of objects (e.g. a QuerySet).
Returns a nested list of strings suitable for display in the
template with the ``unordered_list`` filter.
-
"""
# --- begin patch ---
collector = PolymorphicAwareNestedObjects(using=using)
@@ -53,8 +56,7 @@ def format_callback(obj):
p = '%s.%s' % (opts.app_label,
get_permission_codename('delete', opts))
- # Additional change: also check for individual object permission
- if not user.has_perm(p, obj) and not user.has_perm(p):
+ if not user.has_perm(p):
perms_needed.add(opts.verbose_name)
# Display a link to the admin page.
return format_html('{}: {}',
@@ -69,11 +71,45 @@ def format_callback(obj):
to_delete = collector.nested(format_callback)
protected = [format_callback(obj) for obj in collector.protected]
+ model_count = {model._meta.verbose_name_plural: len(objs) for model, objs in collector.model_objs.items()}
- return to_delete, perms_needed, protected
+ return to_delete, model_count, perms_needed, protected
-class PolymorphicAwareNestedObjects(NestedObjects):
+class NestedObjects(Collector):
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ self.edges = {} # {from_instance: [to_instances]}
+ self.protected = set()
+ self.model_objs = defaultdict(set)
+
+ def add_edge(self, source, target):
+ self.edges.setdefault(source, []).append(target)
+
+ def collect(self, objs, source=None, source_attr=None, **kwargs):
+ for obj in objs:
+ if source_attr and not source_attr.endswith('+'):
+ related_name = source_attr % {
+ 'class': source._meta.model_name,
+ 'app_label': source._meta.app_label,
+ }
+ self.add_edge(getattr(obj, related_name), obj)
+ else:
+ self.add_edge(None, obj)
+ self.model_objs[obj._meta.model].add(obj)
+ try:
+ return super().collect(objs, source_attr=source_attr, **kwargs)
+ except models.ProtectedError as e:
+ self.protected.update(e.protected_objects)
+
+ if DJANGO_VERSION >= (3, 1):
+ def related_objects(self, related_model, related_fields, objs):
+ qs = super().related_objects(related_model, related_fields, objs)
+ return qs.select_related(*[related.name for related in related_fields])
+ else:
+ def related_objects(self, related, objs):
+ qs = super().related_objects(related, objs)
+ return qs.select_related(related.field.name)
def _nested(self, obj, seen, format_callback):
if obj in seen:
@@ -82,21 +118,37 @@ def _nested(self, obj, seen, format_callback):
children = []
for child in self.edges.get(obj, ()):
children.extend(self._nested(child, seen, format_callback))
- if hasattr(obj, 'is_in_trash') and obj.is_in_trash():
- ret = []
+ if format_callback:
+ ret = [format_callback(obj)]
else:
- if format_callback:
- ret = [format_callback(obj)]
- else:
- ret = [obj]
+ ret = [obj]
if children:
ret.append(children)
return ret
+ def nested(self, format_callback=None):
+ """
+ Return the graph as a nested list.
+ """
+ seen = set()
+ roots = []
+ for root in self.edges.get(None, ()):
+ roots.extend(self._nested(root, seen, format_callback))
+ return roots
+
+ def can_fast_delete(self, *args, **kwargs):
+ """
+ We always want to load the objects into memory so that we can display
+ them to the user in confirm page.
+ """
+ return False
+
+
+class PolymorphicAwareNestedObjects(NestedObjects):
def collect(self, objs, source_attr=None, **kwargs):
if hasattr(objs, 'non_polymorphic'):
- # .filter() is needed, because there may already be
- # cached polymorphic results in the queryset
+ # .filter() is needed, because there may already be cached
+ # polymorphic results in the queryset
objs = objs.non_polymorphic().filter()
- return super(PolymorphicAwareNestedObjects, self).collect(
+ return super().collect(
objs, source_attr=source_attr, **kwargs)
diff --git a/filer/admin/tools.py b/filer/admin/tools.py
index 1995b905b..d7eeceebf 100644
--- a/filer/admin/tools.py
+++ b/filer/admin/tools.py
@@ -1,9 +1,128 @@
-#-*- coding: utf-8 -*-
-from django.conf import settings
+from django.conf import settings as django_settings
+from django.contrib.admin.options import IS_POPUP_VAR
+from django.core.exceptions import PermissionDenied
from django.db.models import Q
-from filer.utils.cms_roles import *
-from filer.models.foldermodels import Folder
+from django.utils.http import urlencode
+from .. import settings
+from ..utils.cms_roles import get_sites_for_user
+from ..models.foldermodels import Folder
+
+
+ALLOWED_PICK_TYPES = ('folder', 'file')
+
+
+def check_files_edit_permissions(request, files):
+ for f in files:
+ if not f.has_edit_permission(request):
+ raise PermissionDenied
+
+
+def check_folder_edit_permissions(request, folders):
+ for f in folders:
+ if not f.has_edit_permission(request):
+ raise PermissionDenied
+ check_files_edit_permissions(request, f.files)
+ check_folder_edit_permissions(request, f.children.all())
+
+
+def check_files_read_permissions(request, files):
+ for f in files:
+ if not f.has_read_permission(request):
+ raise PermissionDenied
+
+
+def check_folder_read_permissions(request, folders):
+ for f in folders:
+ if not f.has_read_permission(request):
+ raise PermissionDenied
+ check_files_read_permissions(request, f.files)
+ check_folder_read_permissions(request, f.children.all())
+
+
+def userperms_for_request(item, request):
+ r = []
+ ps = ['read', 'edit', 'add_children']
+ for p in ps:
+ attr = "has_%s_permission" % p
+ if hasattr(item, attr):
+ x = getattr(item, attr)(request)
+ if x:
+ r.append(p)
+ return r
+
+
+def popup_status(request):
+ return (
+ IS_POPUP_VAR in request.GET
+ or 'pop' in request.GET
+ or IS_POPUP_VAR in request.POST
+ or 'pop' in request.POST
+ )
+
+
+def popup_pick_type(request):
+ pick_type = request.GET.get('_pick', request.POST.get('_pick'))
+ if pick_type in ALLOWED_PICK_TYPES:
+ return pick_type
+ 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:
+ return
+ return list_type
+
+
+def admin_url_params(request, params=None):
+ params = params or {}
+ if popup_status(request):
+ params[IS_POPUP_VAR] = '1'
+ 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
+ return params
+
+
+def admin_url_params_encoded(request, first_separator='?', params=None):
+ params = urlencode(
+ sorted(admin_url_params(request, params=params).items())
+ )
+ if not params:
+ return ''
+ return f'{first_separator}{params}'
+
+
+class AdminContext(dict):
+ def __init__(self, request):
+ super().__init__()
+ self.update(admin_url_params(request))
+
+ def __missing__(self, key):
+ if key == 'popup':
+ return self.get(IS_POPUP_VAR, False) == '1'
+ elif key == 'pick':
+ return self.get('_pick', '')
+ elif key.startswith('pick_'):
+ return self.get('_pick', '') == key.split('pick_')[1]
+
+ def __getattr__(self, name):
+ if name in ('popup', 'pick') or name.startswith('pick_'):
+ return self.get(name)
+ raise AttributeError
+
+
+# --- PBS-specific functions ---
def is_valid_destination(request, folder):
user = request.user
@@ -33,104 +152,22 @@ def _filter_available_sites(current_site, user):
def folders_available(current_site, user, folders_qs):
"""
- Returns a queryset with folders that current user can see
- * core folders
- * shared folders
- * only site folders with sites available to the user
- * site admins can also see site folder files with no site assigned
-
- * current_site param is passed only with cms plugin change form so this
- will restrict visible files/folder for the ones that belong to that
- site for all users, even superusers
+ Returns a queryset with folders that current user can see.
"""
- if user.is_superuser and not current_site:
- return folders_qs.distinct()
-
- available_sites = _filter_available_sites(current_site, user)
-
- sites_q = Q(Q(folder_type=Folder.CORE_FOLDER) |
- Q(site__in=available_sites) |
- Q(shared__in=available_sites))
-
- if (getattr(settings, 'FILER_INCLUDE_SITELESS_FOLDERS', True) and
- has_admin_role(user) and not current_site):
- sites_q |= Q(site__pk__isnull=True)
-
- return folders_qs.filter(sites_q).distinct()
-
-
-def files_available(current_site, user, files_qs):
- """
- Returns a queryset with files that current user can see:
- * core folder files
- * shared folder files
- * files from 'unfiled files'
- * only site folder files with sites available to the user
- * site admins can also see site folder files with no site assigned
-
- * current_site param is passed only with cms plugin change form so this
- will restrict visible files/folder for the ones that belong to that
- site for all users, even superusers
- """
- # never show unfiled files from other users clipboard
- unfiled_in_clipboard = Q(Q(folder__isnull=True) &
- ~Q(clipboarditem__isnull=True))
-
- if user.is_superuser and not current_site:
- return files_qs.exclude(unfiled_in_clipboard).distinct()
-
available_sites = _filter_available_sites(current_site, user)
+ if not available_sites:
+ return folders_qs.none()
- sites_q = Q(Q(folder__folder_type=Folder.CORE_FOLDER) |
- Q(folder__site__in=available_sites) |
- Q(folder__shared__in=available_sites))
-
- if not current_site:
- sites_q |= Q(folder__isnull=True)
- if (getattr(settings, 'FILER_INCLUDE_SITELESS_FOLDERS', True) and
- has_admin_role(user)):
- sites_q |= Q(folder__site__isnull=True)
- else:
- # never show unfiled in popup
- sites_q &= Q(folder__isnull=False)
-
- return files_qs.exclude(unfiled_in_clipboard).filter(sites_q).distinct()
+ core_folders = Q(folder_type=Folder.CORE_FOLDER)
+ shared_folders = Q(shared__in=available_sites)
+ accessible_site_folders = Q(site__in=available_sites)
-
-def has_multi_file_action_permission(request, files, folders):
- # unfiled files can be moved/deleted so better to just exclude them
- # from checking permissions for them
- files = files.exclude(folder__isnull=True)
- user = request.user
-
- if files.readonly(user).exists() or folders.readonly(user).exists():
- return False
if user.is_superuser:
- return True
-
- if files.restricted(user).exists():
- return False
-
- if folders.restricted_descendants(user).exists():
- return False
-
- # only superusers can move/delete files/folders with no site ownership
- if (files.filter(folder__site__isnull=True).exists() or
- folders.filter(site__isnull=True).exists()):
- return False
-
- _exists_root_folders = folders.filter(parent__isnull=True).exists()
- if _exists_root_folders:
- if not has_admin_role(user):
- return False
- # allow site admins to move/delete root files/folders that belong
- # to the site where is admin
- sites_allowed = [s.id for s in get_admin_sites_for_user(user)]
+ # superusers can also see folders with no site assigned
+ no_site_folders = Q(site__isnull=True) & ~core_folders
+ visible = (core_folders | shared_folders |
+ accessible_site_folders | no_site_folders)
else:
- sites_allowed = get_sites_for_user(user)
-
- if (files.exclude(folder__site__in=sites_allowed).exists() or
- folders.exclude(site__in=sites_allowed).exists()):
- return False
+ visible = core_folders | shared_folders | accessible_site_folders
- return True
+ return folders_qs.filter(visible).distinct()
diff --git a/filer/fields/file.py b/filer/fields/file.py
index e43b56871..0223440b9 100644
--- a/filer/fields/file.py
+++ b/filer/fields/file.py
@@ -1,121 +1,101 @@
-#-*- coding: utf-8 -*-
+import logging
+import warnings
+
from django import forms
-from django.contrib.admin.widgets import ForeignKeyRawIdWidget
-from django.contrib.admin import widgets as django_widgets
from django.contrib.admin.sites import site
-from django.urls import reverse
+from django.contrib.admin.widgets import ForeignKeyRawIdWidget
+from django.core.exceptions import ObjectDoesNotExist
from django.db import models
from django.template.loader import render_to_string
+from django.urls import reverse
+from django.utils.http import urlencode
from django.utils.safestring import mark_safe
-from filer.models import File
-from filer import settings as filer_settings
-from filer.utils.model_label import get_model_label
-try:
- from django.utils.text import truncate_words
-except ImportError:
- from django.template.defaultfilters import truncatewords as truncate_words
+from .. import settings as filer_settings
+from ..models import File
+from ..settings import ICON_CSS_LIB
+from ..utils.compatibility import truncate_words
+from ..utils.model_label import get_model_label
+
-import logging
logger = logging.getLogger(__name__)
+
class AdminFileWidget(ForeignKeyRawIdWidget):
choices = None
- def __init__(self, *args, **kwargs):
- super(AdminFileWidget, self).__init__(*args, **kwargs)
- self.set_defaults()
-
- def set_defaults(self):
- self.custom_preview_width = None
- self.search_label = None
- self.remove_label = None
-
- def render(self, name, value, attrs=None):
+ def render(self, name, value, attrs=None, renderer=None):
obj = self.obj_for_value(value)
- css_id = attrs.get('id', 'id_image_x')
- css_id_thumbnail_img = "%s_thumbnail_img" % css_id
- css_id_description_txt = "%s_description_txt" % css_id
- css_id_link_to_file = "%s_link_to_file" % css_id
+ 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()
+ 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
# proper logging configuration
if filer_settings.FILER_ENABLE_LOGGING:
- logger.error('Error while rendering file widget: %s',e)
+ logger.error("Error while rendering file widget: %s", e)
if filer_settings.FILER_DEBUG:
- raise e
+ raise
if not related_url:
- related_url = reverse('admin:filer-directory_listing-root')
+ related_url = reverse("admin:filer-directory_listing-last")
params = self.url_parameters()
+ params["_pick"] = "file"
if params:
- lookup_url = '?' + '&'.join(
- ['%s=%s' % (k, v) for k, v in list(params.items())])
+ lookup_url = "?" + urlencode(sorted(params.items()))
else:
- lookup_url = ''
- if not 'class' in attrs:
+ lookup_url = ""
+ # Filer image is never autocomplete widget; Django's popup js fails if it finds "data-context="available-source"
+ self.attrs.pop("data-context", None)
+ if "class" not in attrs:
# The JavaScript looks for this hook.
- attrs['class'] = 'vForeignKeyRawIdAdminField'
+ attrs["class"] = "vForeignKeyRawIdAdminField"
# rendering the super for ForeignKeyRawIdWidget on purpose here because
# we only need the input and none of the other stuff that
# ForeignKeyRawIdWidget adds
hidden_input = super(ForeignKeyRawIdWidget, self).render(
- name, value, attrs)
- filer_static_prefix = filer_settings.FILER_STATICMEDIA_PREFIX
- if not filer_static_prefix[-1] == '/':
- filer_static_prefix += '/'
+ name, value, attrs
+ ) # grandparent super
context = {
- 'hidden_input': hidden_input,
- 'lookup_url': '%s%s' % (related_url, lookup_url),
- 'thumb_id': css_id_thumbnail_img,
- 'span_id': css_id_description_txt,
- 'link_id': css_id_link_to_file,
- 'object': obj,
- 'lookup_name': name,
- 'filer_static_prefix': filer_static_prefix,
- 'clear_id': '%s_clear' % css_id,
- 'id': css_id,
- 'search_label': self.search_label,
- 'remove_label': self.remove_label,
+ "hidden_input": hidden_input,
+ "lookup_url": f"{related_url}{lookup_url}",
+ "change_url": change_url,
+ "object": obj,
+ "lookup_name": name,
+ "id": css_id,
+ "admin_icon_delete": ("admin/img/icon-deletelink.svg"),
}
-
- template = 'admin/filer/widgets/admin_file.html'
- if self.custom_preview_width:
- self.update_context_for_custom_preview(context, obj)
- template = 'admin/filer/widgets/admin_file_custom.html'
-
- html = render_to_string(template, context)
+ html = render_to_string("admin/filer/widgets/admin_file.html", context)
return mark_safe(html)
def label_for_value(self, value):
obj = self.obj_for_value(value)
- return ' %s' % truncate_words(obj, 14)
+ return " %s" % truncate_words(obj, 14)
def obj_for_value(self, value):
- try:
- key = self.remote_field.get_related_field().name
- obj = self.remote_field.model.objects.get(**{key: value})
- except:
+ if value:
+ try:
+ # the next line may never bee reached
+ key = self.rel.get_related_field().name
+ obj = self.rel.model._default_manager.get(**{key: value})
+ except ObjectDoesNotExist:
+ obj = None
+ else:
obj = None
return obj
- def update_context_for_custom_preview(self, context, obj):
- context['custom_preview'] = True
- context['custom_preview_width'] = self.custom_preview_width
- context['custom_preview_image'] = self.get_custom_preview_image(obj=obj)
- return context
-
- def get_custom_preview_image(self, obj):
- return obj.icons['32'] if obj else None
-
class Media:
- js = (filer_settings.FILER_STATICMEDIA_PREFIX + 'js/popup_handling.js',)
+ css = {
+ "all": ("filer/css/admin_filer.css",) + ICON_CSS_LIB,
+ }
+ js = ("filer/js/dist/admin-file-widget.bundle.js",)
class AdminFileFormField(forms.ModelChoiceField):
@@ -127,8 +107,8 @@ def __init__(self, rel, queryset, to_field_name, *args, **kwargs):
self.to_field_name = to_field_name
self.max_value = None
self.min_value = None
- kwargs.pop('widget', None)
- super(AdminFileFormField, self).__init__(queryset, widget=self.widget(rel, site), *args, **kwargs)
+ kwargs.pop("widget", None)
+ super().__init__(queryset, widget=self.widget(rel, site), *args, **kwargs)
def widget_attrs(self, widget):
widget.required = self.required
@@ -139,29 +119,19 @@ class FilerFileField(models.ForeignKey):
default_form_class = AdminFileFormField
default_model_class = File
- def __init__(self, to=None, *args, **kwargs):
- kwargs['to'] = get_model_label(self.default_model_class)
- super(FilerFileField, self).__init__(*args, **kwargs)
+ def __init__(self, **kwargs):
+ to = kwargs.pop("to", None)
+ dfl = get_model_label(self.default_model_class)
+ if to and get_model_label(to).lower() != dfl.lower():
+ msg = "In {}: ForeignKey must point to {}; instead passed {}"
+ warnings.warn(msg.format(self.__class__.__name__, dfl, to), SyntaxWarning)
+ kwargs["to"] = dfl # hard-code `to` to model `filer.File`
+ super().__init__(**kwargs)
def formfield(self, **kwargs):
- # This is a fairly standard way to set up some defaults
- # while letting the caller override them.
defaults = {
- 'form_class': self.default_form_class,
- 'rel': self.remote_field,
+ "form_class": self.default_form_class,
+ "rel": self.remote_field,
}
defaults.update(kwargs)
- return super(FilerFileField, self).formfield(**defaults)
-
- def south_field_triple(self):
- "Returns a suitable description of this field for South."
- # We'll just introspect ourselves, since we inherit.
- from south.modelsinspector import introspector
- field_class = "django.db.models.fields.related.ForeignKey"
- args, kwargs = introspector(self)
- # That's our definition!
- return (field_class, args, kwargs)
-
-
-class NonClearableFileInput(django_widgets.AdminFileWidget):
- template_with_clear = ''
+ return super().formfield(**defaults)
diff --git a/filer/fields/folder.py b/filer/fields/folder.py
index a1bf9f88a..5a6dc450a 100644
--- a/filer/fields/folder.py
+++ b/filer/fields/folder.py
@@ -1,35 +1,35 @@
-#-*- coding: utf-8 -*-
-from django.template.loader import render_to_string
+import warnings
from django import forms
-from django.contrib.admin.widgets import ForeignKeyRawIdWidget
from django.contrib.admin.sites import site
-from django.urls import reverse
+from django.contrib.admin.widgets import ForeignKeyRawIdWidget
+from django.core.exceptions import ObjectDoesNotExist
from django.db import models
+from django.template.loader import render_to_string
+from django.urls import reverse
+from django.utils.http import urlencode
from django.utils.safestring import mark_safe
-from filer.models import Folder
-from filer.settings import FILER_STATICMEDIA_PREFIX
-from filer.utils.model_label import get_model_label
-try:
- from django.utils.text import truncate_words
-except ImportError:
- from django.template.defaultfilters import truncatewords as truncate_words
+from ..models import Folder
+from ..settings import ICON_CSS_LIB
+from ..utils.compatibility import truncate_words
+from ..utils.model_label import get_model_label
class AdminFolderWidget(ForeignKeyRawIdWidget):
choices = None
+ input_type = "hidden"
+ is_hidden = False
-
- def render(self, name, value, attrs=None):
+ def render(self, name, value, attrs=None, renderer=None):
obj = self.obj_for_value(value)
- css_id = attrs.get('id')
+ css_id = attrs.get("id")
css_id_folder = "%s_folder" % css_id
css_id_description_txt = "%s_description_txt" % css_id
- required = self.attrs
if attrs is None:
attrs = {}
related_url = None
+
if value:
try:
folder = Folder.objects.get(pk=value)
@@ -37,52 +37,57 @@ def render(self, name, value, attrs=None):
except Exception:
pass
if not related_url:
- related_url = reverse('admin:filer-directory_listing-root')
+ related_url = reverse("admin:filer-directory_listing-last")
params = self.url_parameters()
- params['select_folder'] = 1
+ params["_pick"] = "folder"
if params:
- url = '?' + '&'.join(
- ['%s=%s' % (k, v) for k, v in list(params.items())])
+ url = "?" + urlencode(sorted(params.items()))
else:
- url = ''
- if not 'class' in attrs:
+ url = ""
+ if "class" not in attrs:
# The JavaScript looks for this hook.
- attrs['class'] = 'vForeignKeyRawIdAdminField'
+ attrs["class"] = "vForeignKeyRawIdAdminField"
super_attrs = attrs.copy()
hidden_input = super(ForeignKeyRawIdWidget, self).render(
- name, value, super_attrs)
+ name, value, super_attrs
+ ) # grandparent super
# TODO: "id_" is hard-coded here. This should instead use the correct
# API to determine the ID dynamically.
context = {
- 'hidden_input': hidden_input,
- 'lookup_url': '%s%s' % (related_url, url),
- 'lookup_name': name,
- 'span_id': css_id_description_txt,
- 'object': obj,
- 'clear_id': '%s_clear' % css_id,
- 'descid': css_id_description_txt,
- 'noimg': '%sicons/nofile_32x32.png' % FILER_STATICMEDIA_PREFIX,
- 'foldid': css_id_folder,
- 'id': css_id,
- }
- html = render_to_string('admin/filer/widgets/admin_folder.html', context)
+ "hidden_input": hidden_input,
+ "lookup_url": f"{related_url}{url}",
+ "lookup_name": name,
+ "span_id": css_id_description_txt,
+ "object": obj,
+ "clear_id": "%s_clear" % css_id,
+ "descid": css_id_description_txt,
+ "foldid": css_id_folder,
+ "id": css_id,
+ }
+ html = render_to_string("admin/filer/widgets/admin_folder.html", context)
return mark_safe(html)
def label_for_value(self, value):
obj = self.obj_for_value(value)
- return ' %s' % truncate_words(obj, 14)
+ return " %s" % truncate_words(obj, 14)
def obj_for_value(self, value):
+ if not value:
+ return None
try:
- key = self.remote_field.get_related_field().name
- obj = self.remote_field.model.objects.get(**{key: value})
- except:
+ key = self.rel.get_related_field().name
+ obj = self.rel.model._default_manager.get(**{key: value})
+ except ObjectDoesNotExist:
obj = None
return obj
class Media:
- js = (FILER_STATICMEDIA_PREFIX + 'js/popup_handling.js',)
+ css = {"all": ("filer/css/admin_filer.css",) + ICON_CSS_LIB}
+ js = (
+ "filer/js/addons/popup_handling.js",
+ "filer/js/widgets/admin-folder-widget.js",
+ )
class AdminFolderFormField(forms.ModelChoiceField):
@@ -91,11 +96,13 @@ class AdminFolderFormField(forms.ModelChoiceField):
def __init__(self, rel, queryset, to_field_name, *args, **kwargs):
self.rel = rel
self.queryset = queryset
+ self.limit_choices_to = kwargs.pop("limit_choices_to", None)
self.to_field_name = to_field_name
self.max_value = None
self.min_value = None
- kwargs.pop('widget', None)
- super(AdminFolderFormField, self).__init__(queryset, widget=self.widget(rel, site), *args, **kwargs)
+ kwargs.pop("widget", None)
+ kwargs.pop("blank", None)
+ forms.Field.__init__(self, widget=self.widget(rel, site), *args, **kwargs)
def widget_attrs(self, widget):
widget.required = self.required
@@ -107,24 +114,22 @@ class FilerFolderField(models.ForeignKey):
default_model_class = Folder
def __init__(self, **kwargs):
- kwargs['to'] = get_model_label(self.default_model_class)
- super(FilerFolderField, self).__init__(**kwargs)
+ # We hard-code the `to` argument for ForeignKey.__init__
+ dfl = get_model_label(self.default_model_class)
+ if "to" in kwargs.keys(): # pragma: no cover
+ old_to = get_model_label(kwargs.pop("to"))
+ if old_to.lower() != dfl.lower():
+ msg = "{} can only be a ForeignKey to {}; {} passed".format(
+ self.__class__.__name__, dfl, old_to
+ )
+ warnings.warn(msg, SyntaxWarning)
+ kwargs["to"] = dfl
+ super().__init__(**kwargs)
def formfield(self, **kwargs):
- # This is a fairly standard way to set up some defaults
- # while letting the caller override them.
defaults = {
- 'form_class': self.default_form_class,
- 'rel': self.remote_field,
+ "form_class": self.default_form_class,
+ "rel": self.remote_field,
}
defaults.update(kwargs)
- return super(FilerFolderField, self).formfield(**defaults)
-
- def south_field_triple(self):
- "Returns a suitable description of this field for South."
- # We'll just introspect ourselves, since we inherit.
- from south.modelsinspector import introspector
- field_class = "django.db.models.fields.related.ForeignKey"
- args, kwargs = introspector(self)
- # That's our definition!
- return (field_class, args, kwargs)
+ return super().formfield(**defaults)
diff --git a/filer/fields/image.py b/filer/fields/image.py
index c9eabe90d..d1ff162bb 100644
--- a/filer/fields/image.py
+++ b/filer/fields/image.py
@@ -1,13 +1,9 @@
-#-*- coding: utf-8 -*-
-from filer.fields.file import AdminFileWidget, AdminFileFormField, \
- FilerFileField
-from filer.models import Image
+from .. import settings
+from .file import AdminFileFormField, AdminFileWidget, FilerFileField
class AdminImageWidget(AdminFileWidget):
-
- def get_custom_preview_image(self, obj):
- return obj.url if obj else None
+ pass
class AdminImageFormField(AdminFileFormField):
@@ -16,4 +12,4 @@ class AdminImageFormField(AdminFileFormField):
class FilerImageField(FilerFileField):
default_form_class = AdminImageFormField
- default_model_class = Image
+ default_model_class = settings.FILER_IMAGE_MODEL
diff --git a/filer/fields/multistorage_file.py b/filer/fields/multistorage_file.py
index 9be171cdf..99f730403 100644
--- a/filer/fields/multistorage_file.py
+++ b/filer/fields/multistorage_file.py
@@ -1,10 +1,16 @@
-#-*- coding: utf-8 -*-
-from easy_thumbnails import fields as easy_thumbnails_fields, \
- files as easy_thumbnails_files
+import base64
+import hashlib
+import warnings
+from io import BytesIO
-from filer import settings as filer_settings
-from filer.utils.filer_easy_thumbnails import ThumbnailerNameMixin
-from filer.utils.cdn import get_cdn_url
+from django.core.files.base import ContentFile
+from django.db.models.fields.files import FileDescriptor
+
+from easy_thumbnails import fields as easy_thumbnails_fields
+from easy_thumbnails import files as easy_thumbnails_files
+
+from .. import settings as filer_settings
+from ..utils.filer_easy_thumbnails import ThumbnailerNameMixin
STORAGES = {
@@ -33,18 +39,30 @@ def generate_filename_multistorage(instance, filename):
return upload_to
-class CdnAwareThumbnailFile(object):
-
- def __init__(self, thumbnail_file, filer_file):
- self._thumbnail_file = thumbnail_file
- self._filer_file = filer_file
-
- def __getattr__(self, attr_name):
- return getattr(self._thumbnail_file, attr_name)
-
- @property
- def url(self):
- return get_cdn_url(self._filer_file, self._thumbnail_file.url)
+class MultiStorageFileDescriptor(FileDescriptor):
+ """
+ This is rather similar to Django's ImageFileDescriptor.
+ It calls _data_changed on model instance when new
+ value is set. The callback is supposed to update fields which
+ are related to file data (like size, checksum, etc.).
+ When this is called from model __init__ (prev_assigned=False),
+ it does nothing because related fields might not have values yet.
+ In such case data_changed callback should be called at the end of model __init__
+ (File.__init__ in this case).
+ """
+ def __set__(self, instance, value):
+ prev_assigned = self.field.name in instance.__dict__
+ previous_file = instance.__dict__.get(self.field.name)
+ super().__set__(instance, value)
+
+ # To prevent recalculating file data related attributes when we are instantiating
+ # an object from the database, update only if the field had a value before this assignment.
+ # To prevent recalculating upon reassignment of the same file, update only if value is
+ # different from the previous one.
+ if prev_assigned and value != previous_file:
+ callback_attr = f'{self.field.name}_data_changed'
+ if hasattr(instance, callback_attr):
+ getattr(instance, callback_attr)()
class MultiStorageFieldFile(ThumbnailerNameMixin,
@@ -52,7 +70,7 @@ class MultiStorageFieldFile(ThumbnailerNameMixin,
def __init__(self, instance, field, name):
"""
This is a little weird, but I couldn't find a better solution.
- Thumbnailer.__init__ is called first for proper object inizialization.
+ Thumbnailer.__init__ is called first for proper object initialisation.
Then we override some attributes defined at runtime with properties.
We cannot simply call super().__init__ because filer Field objects
doesn't have a storage attribute.
@@ -97,41 +115,63 @@ def _thumbnail_base_dir(self):
else:
return self.thumbnail_options['private'].get('base_dir', '')
- def _get_url(self):
- if self.instance.is_in_trash():
- return ''
- url = super(MultiStorageFieldFile, self).url
- return get_cdn_url(self.instance, url)
- url = property(_get_url)
-
def save(self, name, content, save=True):
- content.seek(0) # Ensure we upload the whole file
- super(MultiStorageFieldFile, self).save(name, content, save)
-
- def get_thumbnail(self, opts, save=True, generate=None):
- if self.instance.is_in_trash():
- return None
- thumbnail = super(MultiStorageFieldFile, self).get_thumbnail(opts, save, generate)
- return CdnAwareThumbnailFile(thumbnail, self.instance)
+ content.seek(0) # Ensure we upload the whole file
+ super().save(name, content, save)
- def get_thumbnails(self, *args, **kwargs):
- if self.instance.is_in_trash():
- return []
- return super(MultiStorageFieldFile, self).get_thumbnails(
- *args, **kwargs)
+ def exists(self):
+ """
+ Returns ``True`` if underlying file exists in storage.
+ """
+ return self.name and self.storage.exists(self.name)
class MultiStorageFileField(easy_thumbnails_fields.ThumbnailerField):
attr_class = MultiStorageFieldFile
+ descriptor_class = MultiStorageFileDescriptor
def __init__(self, verbose_name=None, name=None,
storages=None, thumbnail_storages=None, thumbnail_options=None, **kwargs):
- # fix for makemigrations
- kwargs.pop("upload_to", None)
+ if 'upload_to' in kwargs: # pragma: no cover
+ upload_to = kwargs.pop("upload_to")
+ if upload_to != generate_filename_multistorage:
+ warnings.warn("MultiStorageFileField can handle only File objects;"
+ "%s passed" % upload_to, SyntaxWarning)
self.storages = storages or STORAGES
self.thumbnail_storages = thumbnail_storages or THUMBNAIL_STORAGES
self.thumbnail_options = thumbnail_options or THUMBNAIL_OPTIONS
super(easy_thumbnails_fields.ThumbnailerField, self).__init__(
- verbose_name=verbose_name, name=name,
- upload_to=generate_filename_multistorage,
- storage=None, **kwargs)
+ verbose_name=verbose_name, name=name,
+ upload_to=generate_filename_multistorage,
+ storage=None, **kwargs) # grandparent super
+
+ def value_to_string(self, obj):
+ value = super().value_to_string(obj)
+ if not filer_settings.FILER_DUMP_PAYLOAD:
+ return value
+ try:
+ payload_file = BytesIO(self.storage.open(value).read())
+ sha = hashlib.sha1()
+ sha.update(payload_file.read())
+ if sha.hexdigest() != obj.sha1:
+ warnings.warn('The checksum for "%s" diverges. Check for file consistency!' % obj.original_filename)
+ payload_file.seek(0)
+ encoded_string = base64.b64encode(payload_file.read()).decode('utf-8')
+ return value, encoded_string
+ except OSError:
+ warnings.warn(f'The payload for "{obj.original_filename}" is missing. No such file on disk: {self.storage.location}!')
+ return value
+
+ def to_python(self, value):
+ if isinstance(value, list) and len(value) == 2 and isinstance(value[0], str):
+ filename, payload = value
+ try:
+ payload = base64.b64decode(payload)
+ except TypeError:
+ pass
+ else:
+ if self.storage.exists(filename):
+ self.storage.delete(filename)
+ self.storage.save(filename, ContentFile(payload))
+ return filename
+ return value
diff --git a/filer/server/backends/base.py b/filer/server/backends/base.py
index 6a843ea0e..5e811ee4e 100644
--- a/filer/server/backends/base.py
+++ b/filer/server/backends/base.py
@@ -1,18 +1,14 @@
-#-*- coding: utf-8 -*-
-from django.utils.encoding import smart_str
-import mimetypes
import os
+from django.utils.encoding import smart_str
+
-class ServerBase(object):
+class ServerBase:
"""
Server classes define a way to serve a Django File object.
Warning: this API is EXPERIMENTAL and may change at any time.
"""
- def get_mimetype(self, path):
- return mimetypes.guess_type(path)[0] or 'application/octet-stream'
-
def default_headers(self, **kwargs):
self.save_as_header(**kwargs)
self.size_header(**kwargs)
@@ -21,24 +17,29 @@ def save_as_header(self, response, **kwargs):
"""
* if save_as is False the header will not be added
* if save_as is a filename, it will be used in the header
- * if save_as is None the filename will be determined from the file path
+ * if save_as is True or None the filename will be determined from the
+ file path
"""
save_as = kwargs.get('save_as', None)
- if save_as == False:
+ if save_as is False:
return
file_obj = kwargs.get('file_obj', None)
- filename = None
- if save_as:
- filename = save_as
- else:
+ if save_as is True or save_as is None:
filename = os.path.basename(file_obj.path)
- response['Content-Disposition'] = smart_str('attachment; filename=%s' % filename)
+ else:
+ filename = save_as
+ response['Content-Disposition'] = smart_str(
+ 'attachment; filename=%s' % filename)
def size_header(self, response, **kwargs):
size = kwargs.get('size', None)
- #file = kwargs.get('file', None)
+ # file = kwargs.get('file', None)
if size:
response['Content-Length'] = size
- # we should not do this, because it accesses the file. and that might be an expensive operation.
+ # we should not do this, because it accesses the file. and that might
+ # be an expensive operation.
# elif file and file.size is not None:
# response['Content-Length'] = file.size
+
+ def serve(self, request, filer_file, **kwargs):
+ raise NotImplementedError(".serve() must be overridden")
diff --git a/filer/server/backends/default.py b/filer/server/backends/default.py
index 172ac1871..2aed251d4 100644
--- a/filer/server/backends/default.py
+++ b/filer/server/backends/default.py
@@ -1,10 +1,11 @@
-#-*- coding: utf-8 -*-
import os
import stat
+
from django.http import Http404, HttpResponse, HttpResponseNotModified
from django.utils.http import http_date
from django.views.static import was_modified_since
-from filer.server.backends.base import ServerBase
+
+from .base import ServerBase
class DefaultServer(ServerBase):
@@ -14,19 +15,19 @@ class DefaultServer(ServerBase):
This will only work for files that can be accessed in the local filesystem.
"""
- def serve(self, request, file_obj, **kwargs):
- fullpath = file_obj.path
+ def serve(self, request, filer_file, **kwargs):
+ fullpath = filer_file.path
# the following code is largely borrowed from `django.views.static.serve`
# and django-filetransfers: filetransfers.backends.default
if not os.path.exists(fullpath):
raise Http404('"%s" does not exist' % fullpath)
# Respect the If-Modified-Since header.
statobj = os.stat(fullpath)
- mimetype = self.get_mimetype(fullpath)
+ response_params = {'content_type': filer_file.mime_type}
if not was_modified_since(request.META.get('HTTP_IF_MODIFIED_SINCE'),
statobj[stat.ST_MTIME]):
- return HttpResponseNotModified(content_type=mimetype)
- response = HttpResponse(open(fullpath, 'rb').read(), content_type=mimetype)
+ return HttpResponseNotModified(**response_params)
+ response = HttpResponse(open(fullpath, 'rb').read(), **response_params)
response["Last-Modified"] = http_date(statobj[stat.ST_MTIME])
- self.default_headers(request=request, response=response, file_obj=file_obj, **kwargs)
+ self.default_headers(request=request, response=response, file_obj=filer_file.file, **kwargs)
return response
diff --git a/filer/server/backends/nginx.py b/filer/server/backends/nginx.py
index d0b6e9adf..03a7ff17f 100644
--- a/filer/server/backends/nginx.py
+++ b/filer/server/backends/nginx.py
@@ -1,6 +1,6 @@
-#-*- coding: utf-8 -*-
from django.http import HttpResponse
-from filer.server.backends.base import ServerBase
+
+from .base import ServerBase
class NginxXAccelRedirectServer(ServerBase):
@@ -18,11 +18,10 @@ def __init__(self, location, nginx_location):
def get_nginx_location(self, path):
return path.replace(self.location, self.nginx_location)
- def serve(self, request, file_obj, **kwargs):
- # we should not use get_mimetype() here, because it tries to access the file in the filesystem.
- #response = HttpResponse(content_type=self.get_mimetype(file.path))
+ def serve(self, request, filer_file, **kwargs):
response = HttpResponse()
- nginx_path = self.get_nginx_location(file_obj.path)
+ response['Content-Type'] = filer_file.mime_type
+ nginx_path = self.get_nginx_location(filer_file.path)
response['X-Accel-Redirect'] = nginx_path
- self.default_headers(request=request, response=response, file_obj=file_obj, **kwargs)
+ self.default_headers(request=request, response=response, file_obj=filer_file.file, **kwargs)
return response
diff --git a/filer/server/backends/xsendfile.py b/filer/server/backends/xsendfile.py
index cd3316f64..476a87f7b 100644
--- a/filer/server/backends/xsendfile.py
+++ b/filer/server/backends/xsendfile.py
@@ -1,17 +1,17 @@
-#-*- coding: utf-8 -*-
from django.http import HttpResponse
-from filer.server.backends.base import ServerBase
+
+from .base import ServerBase
class ApacheXSendfileServer(ServerBase):
- def serve(self, request, file_obj, **kwargs):
+ def serve(self, request, filer_file, **kwargs):
response = HttpResponse()
- response['X-Sendfile'] = file_obj.path
+ response['X-Sendfile'] = filer_file.path
# This is needed for lighttpd, hopefully this will
# not be needed after this is fixed:
# http://redmine.lighttpd.net/issues/2076
- response['Content-Type'] = self.get_mimetype(file_obj.path)
+ response['Content-Type'] = filer_file.mime_type
- self.default_headers(request=request, response=response, file_obj=file_obj, **kwargs)
+ self.default_headers(request=request, response=response, file_obj=filer_file.file, **kwargs)
return response
diff --git a/filer/server/main_server_urls.py b/filer/server/main_server_urls.py
index bb555c1a3..232c1fe2a 100644
--- a/filer/server/main_server_urls.py
+++ b/filer/server/main_server_urls.py
@@ -1,8 +1,8 @@
-#-*- coding: utf-8 -*-
from django.urls import re_path
from . import views
+
urlpatterns = [
- re_path(r'^(?P.*)$', views.serve_protected_file)
+ re_path(r'^(?P.*)$', views.serve_protected_file),
]
diff --git a/filer/server/thumbnails_server_urls.py b/filer/server/thumbnails_server_urls.py
index 072f51cac..7d2cb0395 100644
--- a/filer/server/thumbnails_server_urls.py
+++ b/filer/server/thumbnails_server_urls.py
@@ -1,8 +1,8 @@
-#-*- coding: utf-8 -*-
from django.urls import re_path
from . import views
+
urlpatterns = [
- re_path(r'^(?P.*)$', views.serve_protected_thumbnail)
+ re_path(r'^(?P.*)$', views.serve_protected_thumbnail),
]
diff --git a/filer/server/urls.py b/filer/server/urls.py
index 14a1a0f4d..8f758dcc9 100644
--- a/filer/server/urls.py
+++ b/filer/server/urls.py
@@ -1,8 +1,12 @@
-#-*- coding: utf-8 -*-
-from django.urls import re_path, include
-from filer import settings as filer_settings
+from django.urls import include, re_path
-urlpatterns = [
- re_path(r'^' + filer_settings.FILER_PRIVATEMEDIA_STORAGE.base_url.lstrip('/'), include('filer.server.main_server_urls')),
- re_path(r'^' + filer_settings.FILER_PRIVATEMEDIA_THUMBNAIL_STORAGE.base_url.lstrip('/'), include('filer.server.thumbnails_server_urls'))
-]
+from .. import settings as filer_settings
+
+
+if not filer_settings.FILER_0_8_COMPATIBILITY_MODE:
+ urlpatterns = [
+ re_path(r'^' + filer_settings.FILER_PRIVATEMEDIA_STORAGE.base_url.lstrip('/'), include('filer.server.main_server_urls')),
+ re_path(r'^' + filer_settings.FILER_PRIVATEMEDIA_THUMBNAIL_STORAGE.base_url.lstrip('/'), include('filer.server.thumbnails_server_urls')),
+ ]
+else:
+ urlpatterns = []
diff --git a/filer/server/views.py b/filer/server/views.py
index ea26c9def..90b394abb 100644
--- a/filer/server/views.py
+++ b/filer/server/views.py
@@ -1,14 +1,20 @@
-#-*- coding: utf-8 -*-
+from django.conf import settings
+from django.core.exceptions import PermissionDenied
from django.http import Http404
+from django.views.decorators.cache import never_cache
+
from easy_thumbnails.files import ThumbnailFile
-from filer import settings as filer_settings
-from filer.models import File
-from filer.utils.filer_easy_thumbnails import thumbnail_to_original_filename
+
+from .. import settings as filer_settings
+from ..models import File
+from ..utils.filer_easy_thumbnails import thumbnail_to_original_filename
+
server = filer_settings.FILER_PRIVATEMEDIA_SERVER
thumbnail_server = filer_settings.FILER_PRIVATEMEDIA_THUMBNAIL_SERVER
+@never_cache
def serve_protected_file(request, path):
"""
Serve protected files to authenticated users with read permissions.
@@ -17,9 +23,15 @@ def serve_protected_file(request, path):
file_obj = File.objects.get(file=path, is_public=False)
except File.DoesNotExist:
raise Http404('File not found')
- return server.serve(request, file_obj=file_obj.file, save_as=False)
+ if not file_obj.has_read_permission(request):
+ if settings.DEBUG:
+ raise PermissionDenied
+ else:
+ raise Http404('File not found')
+ return server.serve(request, file_obj, save_as=False)
+@never_cache
def serve_protected_thumbnail(request, path):
"""
Serve protected thumbnails to authenticated users.
@@ -32,8 +44,14 @@ def serve_protected_thumbnail(request, path):
file_obj = File.objects.get(file=source_path, is_public=False)
except File.DoesNotExist:
raise Http404('File not found')
+ if not file_obj.has_read_permission(request):
+ if settings.DEBUG:
+ raise PermissionDenied
+ else:
+ raise Http404('File not found')
try:
thumbnail = ThumbnailFile(name=path, storage=file_obj.file.thumbnail_storage)
+ thumbnail.mime_type = file_obj.mime_type
return thumbnail_server.serve(request, thumbnail, save_as=False)
except Exception:
raise Http404('File not found')
diff --git a/filer/templatetags/filer_admin_tags.py b/filer/templatetags/filer_admin_tags.py
index 8dd08a542..8ef9bd9fe 100644
--- a/filer/templatetags/filer_admin_tags.py
+++ b/filer/templatetags/filer_admin_tags.py
@@ -1,13 +1,32 @@
-from django.conf import settings
+from math import ceil
+
+from django.contrib.staticfiles.storage import staticfiles_storage
+from django.core.files.storage import FileSystemStorage
from django.template import Library
-from packaging.version import parse
-import django
-import pytz
-import datetime
-import filer
+from django.templatetags.static import static
+from django.urls import reverse
+from django.utils.html import escapejs, format_html_join
+from django.utils.safestring import mark_safe
+from django.utils.translation import gettext_lazy as _
+
+from easy_thumbnails.engine import NoSourceGenerator
+from easy_thumbnails.exceptions import InvalidImageFormatError
+from easy_thumbnails.files import get_thumbnailer
+from easy_thumbnails.options import ThumbnailOptions
+
+from filer import settings
+from filer.admin.tools import admin_url_params, admin_url_params_encoded
+from filer.models.imagemodels import BaseImage
+from filer.settings import (
+ DEFERRED_THUMBNAIL_SIZES, FILER_MAX_SVG_THUMBNAIL_SIZE, FILER_TABLE_ICON_SIZE, FILER_THUMBNAIL_ICON_SIZE,
+)
+
register = Library()
+assignment_tag = getattr(register, 'assignment_tag', register.simple_tag)
+
+
def filer_actions(context):
"""
Track the number of times the action field has been rendered on the page,
@@ -15,121 +34,196 @@ def filer_actions(context):
"""
context['action_index'] = context.get('action_index', -1) + 1
return context
-filer_actions = register.inclusion_tag("admin/filer/actions.html", takes_context=True)(filer_actions)
-if parse(django.get_version()) < parse('1.4'):
- ADMIN_ICON_BASE = "%sadmin/img/admin/" % settings.STATIC_URL
-else:
- ADMIN_ICON_BASE = "%sadmin/img/" % settings.STATIC_URL
-ADMIN_CSS_BASE = "%sadmin/css/" % settings.STATIC_URL
-ADMIN_JS_BASE = "%sadmin/js/" % settings.STATIC_URL
+filer_actions = register.inclusion_tag(
+ "admin/filer/actions.html", takes_context=True)(filer_actions)
+
+
+@register.inclusion_tag(
+ 'admin/filer/folder/list_type_switcher.html',
+ takes_context=True,
+)
+def filer_folder_list_type_switcher(context):
+ choice_list = settings.FILER_FOLDER_ADMIN_LIST_TYPE_CHOICES
+ current_list_type = context['list_type']
+ # This solution is user-friendly when there's only 2 choices
+ # If there will be more list types then please change this switcher to more
+ # proper widget (like for e.g. select list)
+ next_list_type = next(
+ iter(choice_list[choice_list.index(current_list_type) + 1:]),
+ choice_list[0],
+ )
+
+ context['url'] = admin_url_params_encoded(
+ context['request'],
+ params={'_list_type': next_list_type},
+ )
+ context['tooltip_text'] = settings.FILER_FOLDER_ADMIN_LIST_TYPE_SWITCHER_SETTINGS[next_list_type]['tooltip_text']
+ context['icon'] = settings.FILER_FOLDER_ADMIN_LIST_TYPE_SWITCHER_SETTINGS[next_list_type]['icon']
+ return context
+
-@register.simple_tag
-def admin_icon_base():
- return ADMIN_ICON_BASE
+@register.simple_tag(takes_context=True)
+def filer_admin_context_url_params(context, first_separator='?'):
+ return admin_url_params_encoded(
+ context['request'], first_separator=first_separator)
-@register.simple_tag
-def admin_css_base():
- return ADMIN_CSS_BASE
-@register.simple_tag
-def admin_js_base():
- return ADMIN_JS_BASE
+@register.simple_tag(takes_context=True)
+def filer_admin_context_hidden_formfields(context):
+ request = context.get('request')
+ return format_html_join(
+ '\n',
+ '',
+ admin_url_params(request).items(),
+ )
+
+
+@assignment_tag(takes_context=True)
+def filer_has_permission(context, item, action):
+ """Does the current user (taken from the request in the context) have
+ permission to do the given action on the given item.
-@register.inclusion_tag('admin/filer/submit_line.html', takes_context=True)
-def submit_row(context):
- """
- Displays the row of buttons for delete and save.
"""
- opts = context['opts']
- change = context['change']
- is_popup = context['is_popup']
- save_as = context['save_as']
- ctx = {
- 'opts': opts,
- 'obj': context.get('original'),
- 'show_delete_link': not is_popup and context['has_delete_permission'] and change and context.get('show_delete', True),
- 'show_save_as_new': not is_popup and change and save_as,
- 'show_save_and_add_another': context['has_add_permission'] and not is_popup and (not save_as or context['add']),
- 'show_save_and_continue': not is_popup and context['has_change_permission'],
- 'is_popup': is_popup,
- 'show_save': True,
- 'preserved_filters': context.get('preserved_filters'),
+ permission_method_name = f'has_{action}_permission'
+ permission_method = getattr(item, permission_method_name, None)
+ request = context.get('request')
+
+ if not permission_method or not request:
+ return False
+ # Call the permission method.
+ # This amounts to calling `item.has_X_permission(request)`
+ return permission_method(request)
+
+
+def file_icon_context(file, detail, width, height):
+ mime_maintype, mime_subtype = file.mime_maintype, file.mime_subtype
+ context = {
+ 'mime_maintype': mime_maintype,
+ 'mime_type': file.mime_type,
}
- if context.get('original') is not None:
- ctx['original'] = context['original']
- return ctx
+ height, width, context = get_aspect_ratio_and_download_url(context, detail, file, height, width)
+ # returned context if icon is not available
+ not_available_context = {
+ 'icon_url': staticfiles_storage.url('filer/icons/file-missing.svg'),
+ 'alt_text': _("File is missing"),
+ 'width': width,
+ 'height': width, # The icon is a square
+ }
+ # Check if file exists for performance reasons (only on FileSystemStorage)
+ if file.file and isinstance(file.file.source_storage, FileSystemStorage) and not file.file.exists():
+ return not_available_context
+
+ add_styles = {}
+ if isinstance(file, BaseImage):
+ thumbnailer = get_thumbnailer(file)
+
+ # SVG files may contain multiple vector graphics, and width and height are not available for them. If file does
+ # not have width or height just ignore the thumbnail icon. Otherwise, continue with the standard procedure.
+ if file.width == 0.0 or file.height == 0.0:
+ icon_url = staticfiles_storage.url('filer/icons/file-unknown.svg')
+ else:
+ if detail:
+ opts = {'size': (width, height), 'upscale': True}
+ else:
+ opts = {'size': (width, height), 'crop': True}
+ thumbnail_options = ThumbnailOptions(opts)
+ # Optimize directory listing:
+ if width == height and width in DEFERRED_THUMBNAIL_SIZES and hasattr(file, "thumbnail_name"):
+ # Get name of thumbnail from easy-thumbnail
+ configured_name = thumbnailer.get_thumbnail_name(thumbnail_options, transparent=file._transparent)
+ # If the name was annotated: Thumbnail exists and we can use it
+ if configured_name == file.thumbnail_name:
+ icon_url = file.file.thumbnail_storage.url(configured_name)
+ if mime_subtype != 'svg+xml' and file.thumbnailx2_name:
+ context['highres_url'] = file.file.thumbnail_storage.url(file.thumbnailx2_name)
+ elif mime_subtype == 'svg+xml':
+ if file.size < FILER_MAX_SVG_THUMBNAIL_SIZE:
+ icon_url = file.url
+ else:
+ # Only display a generic image icon
+ # Better solution: Render a bitmap thumbnail from SVG
+ icon_url = staticfiles_storage.url('filer/icons/file-picture.svg')
+ add_styles = {
+ "object-fit": "cover", # Mimic the behavior of the thumbnail
+ }
+ else: # Probably does not exist, defer creation
+ icon_url = reverse("admin:filer_file_fileicon", args=(file.pk, width))
+ context['alt_text'] = file.default_alt_text
+ else:
+ # Try creating thumbnails / take existing ones
+ try:
+ icon_url = thumbnailer.get_thumbnail(thumbnail_options).url
+ context['alt_text'] = file.default_alt_text
+ if mime_subtype != 'svg+xml':
+ thumbnail_options['size'] = 2 * width, 2 * height
+ context['highres_url'] = thumbnailer.get_thumbnail(thumbnail_options).url
+ except (InvalidImageFormatError, NoSourceGenerator):
+ # This is caught by file.exists() for file storage systems
+ # For remote storage systems we catch the error to avoid second trip
+ # to the storage system
+ return not_available_context
+ elif mime_maintype in ['audio', 'font', 'video']:
+ icon_url = staticfiles_storage.url(f'filer/icons/file-{mime_maintype}.svg')
+ height = width # icon is a square
+ elif mime_maintype == 'application' and mime_subtype in ['zip', 'pdf']:
+ icon_url = staticfiles_storage.url(f'filer/icons/file-{mime_subtype}.svg')
+ height = width # icon is a square
+ else:
+ icon_url = staticfiles_storage.url('filer/icons/file-unknown.svg')
+ height = width # icon is a square
+ context.update(width=width, height=height, icon_url=icon_url)
+ if add_styles:
+ styles = "; ".join(f"{k}: {v}" for k, v in add_styles.items())
+ context.update(add_styles=f'style="{styles}"')
+ return context
+def get_aspect_ratio_and_download_url(context, detail, file, height, width):
+ # Get download_url and aspect ratio right for detail view
+ if detail:
+ context['download_url'] = file.url
+ if isinstance(file, BaseImage):
+ # only check for file width, if the file
+ # is actually an image and not on other files
+ # because they don't really have width or height
+ if file.width:
+ width, height = 210, ceil(210 / file.width * file.height)
+ context['sidebar_image_ratio'] = '%.6f' % (file.width / 210)
+ return height, width, context
-@register.simple_tag(takes_context=True)
-def get_popup_params(context, sep='?'):
- is_popup = context.get('is_popup', False)
- select_folder = context.get('select_folder', False)
- current_site = context.get('current_site', False)
- file_type = context.get('file_type', None)
- params = ''
- if is_popup:
- params += '%s_popup=1' % sep
- if select_folder:
- params += '&select_folder=1'
- if current_site:
- params += '¤t_site=%s' % current_site
- if file_type:
- params += '&file_type=%s' % file_type
-
- return params
-
-
-@register.filter
-def is_restricted_for_user(filer_obj, user):
- return (filer_obj.is_readonly_for_user(user) or
- filer_obj.is_restricted_for_user(user))
-
-
-@register.filter
-def is_readonly_for_user(filer_obj, user):
- return filer_obj.is_readonly_for_user(user)
-
-
-@register.filter
-def can_change_folder(folder, user):
- return folder.has_change_permission(user)
-
-
-@register.filter
-def pretty_display(filer_obj):
- if isinstance(filer_obj, filer.models.Folder):
- return '/%s' % '/'.join(filer_obj.get_ancestors(include_self=True)
- .values_list('name', flat=True))
- if isinstance(filer_obj, filer.models.File):
- name = filer_obj.actual_name
- if not filer_obj.folder_id:
- return '/%s' % name
- return '/%s' % '/'.join(
- list(filer.models.Folder.all_objects.get(id=filer_obj.folder_id)
- .get_ancestors(include_self=True)
- .values_list('name', flat=True)) + [name])
- return ''
-
-
-def _build_timezone_dict():
- offset_with_timezone = {}
- for zone in pytz.common_timezones:
- timezone_obj = pytz.timezone(zone)
- zone_offset = datetime.datetime.now(timezone_obj).strftime("%z")
- offset_with_timezone[zone_offset] = timezone_obj
- return offset_with_timezone
-
-_OFFSETS_WITH_TIMEZONES = _build_timezone_dict()
-@register.simple_tag(takes_context=True)
-def client_timezone(context, date):
- gmt_offset = context.get('request').COOKIES.get('gmt_offset', None)
- if not gmt_offset:
- return date
- client_timezone = _OFFSETS_WITH_TIMEZONES.get(gmt_offset, None)
- if not client_timezone:
- return date
- return date.astimezone(client_timezone)
+@register.inclusion_tag('admin/filer/templatetags/file_icon.html')
+def file_icon(file, detail=False, size=None):
+ """
+ This templatetag returns a rendered `
+ to be used for rendering thumbnails of files in the directory listing or in the corresponding detail
+ views for that image.
+ """
+ if size:
+ width, height = (int(s) for s in size.split('x'))
+ elif detail == "thumbnail":
+ width, height = FILER_THUMBNAIL_ICON_SIZE, FILER_THUMBNAIL_ICON_SIZE
+ elif detail is True:
+ width, height = 2 * FILER_TABLE_ICON_SIZE, 2 * FILER_TABLE_ICON_SIZE
+ else:
+ width, height = FILER_TABLE_ICON_SIZE, FILER_TABLE_ICON_SIZE
+ return file_icon_context(file, detail is True, width, height)
+
+
+@register.simple_tag
+def file_icon_url(file):
+ # Cache since it is called repeatedly by templates
+ if not hasattr(file, "_file_icon_url_cache"):
+ context = file_icon_context(file, False, 2 * FILER_TABLE_ICON_SIZE, 2 * FILER_TABLE_ICON_SIZE)
+ file._file_icon_url_cache = escapejs(context.get('highres_url', context['icon_url']))
+ return file._file_icon_url_cache
+
+
+@register.simple_tag
+def icon_css_library():
+ html = ""
+ for lib in settings.ICON_CSS_LIB:
+ html += f''
+ return mark_safe(html)
diff --git a/filer/templatetags/filer_image_tags.py b/filer/templatetags/filer_image_tags.py
index 78a58d716..5588521f1 100644
--- a/filer/templatetags/filer_image_tags.py
+++ b/filer/templatetags/filer_image_tags.py
@@ -1,12 +1,17 @@
-#-*- coding: utf-8 -*-
-from django.template import Library
import re
+from django.template import Library
+
+
register = Library()
RE_SIZE = re.compile(r'(\d+)x(\d+)$')
+def percentage(part, whole):
+ return 100 * float(part) / float(whole)
+
+
def _recalculate_size(size, index, divisor=0, padding=0,
keep_aspect_ratio=False):
new_one = size[index]
@@ -15,8 +20,8 @@ def _recalculate_size(size, index, divisor=0, padding=0,
if padding:
new_one = new_one - padding
if keep_aspect_ratio:
- new_two = int(float(new_one) * \
- float(size[int(not index)]) / size[index])
+ new_two = int(
+ float(new_one) * float(size[int(not index)]) / size[index])
else:
new_two = int(size[int(not index)])
@@ -56,6 +61,8 @@ def extra_padding_x(original_size, padding):
Reduce the width of `original_size` by `padding`
"""
return _resize(original_size, 0, padding=padding)
+
+
extra_padding_x = register.filter(extra_padding_x)
@@ -65,6 +72,8 @@ def extra_padding_x_keep_ratio(original_size, padding):
ratio.
"""
return _resize(original_size, 0, padding=padding, keep_aspect_ratio=True)
+
+
extra_padding_x_keep_ratio = register.filter(extra_padding_x_keep_ratio)
@@ -73,6 +82,8 @@ def extra_padding_y(original_size, padding):
Reduce the height of `original_size` by `padding`
"""
return _resize(original_size, 1, padding=padding)
+
+
extra_padding_y = register.filter(extra_padding_y)
@@ -82,16 +93,22 @@ def extra_padding_y_keep_ratio(original_size, padding):
ratio.
"""
return _resize(original_size, 1, padding=padding, keep_aspect_ratio=True)
+
+
extra_padding_y_keep_ratio = register.filter(extra_padding_y_keep_ratio)
def divide_x_by(original_size, divisor):
return _resize(original_size, 0, divisor=divisor)
+
+
devide_x_by = register.filter(divide_x_by)
def divide_y_by(original_size, divisor):
return _resize(original_size, 1, divisor=divisor)
+
+
devide_y_by = register.filter(divide_y_by)
@@ -99,4 +116,24 @@ def divide_xy_by(original_size, divisor):
size = divide_x_by(original_size, divisor=divisor)
size = divide_y_by(size, divisor=divisor)
return size
+
+
divide_xy_by = register.filter(divide_xy_by)
+
+
+def get_css_position(image):
+ if not image or not image.subject_location:
+ return '50% 50%'
+
+ x, y = image.subject_location.split(',')
+ width = image.width
+ height = image.height
+
+ coords = '{}% {}%'.format(
+ percentage(x, width),
+ percentage(y, height)
+ )
+ return coords
+
+
+get_css_position = register.filter(get_css_position)
diff --git a/filer/templatetags/filer_tags.py b/filer/templatetags/filer_tags.py
index f481c50e0..faaba6109 100644
--- a/filer/templatetags/filer_tags.py
+++ b/filer/templatetags/filer_tags.py
@@ -1,7 +1,8 @@
-#-*- coding: utf-8 -*-
-from django.template import Library
import math
+from django.template import Library
+
+
register = Library()
# The templatetag below is copied from sorl.thumbnail
@@ -49,6 +50,7 @@ def filesize(bytes, format='auto1024'):
elif format not in ('auto1024', 'auto1000',
'auto1024long', 'auto1000long'):
return bytes
+
# Check for valid bytes
try:
bytes = int(bytes)
@@ -82,12 +84,12 @@ def filesize(bytes, format='auto1024'):
unit = filesize_long_formats.get(unit, '')
if base == 1024 and unit:
unit = '%sbi' % unit[:2]
- unit = '%sbyte%s' % (unit, bytes != '1' and 's' or '')
+ unit = '{}byte{}'.format(unit, bytes != '1' and 's' or '')
else:
- unit = '%s%s' % (base == 1024 and unit.upper() or unit,
+ unit = '{}{}'.format(base == 1024 and unit.upper() or unit,
base == 1024 and 'iB' or 'B')
- return '%s %s' % (bytes, unit)
+ return f'{bytes} {unit}'
if bytes == 0:
return bytes
@@ -99,4 +101,6 @@ def filesize(bytes, format='auto1024'):
elif format_len == 3:
bytes = bytes >> (10 * (base - 1))
return bytes / 1024.0
+
+
register.filter(filesize)
diff --git a/filer/templatetags/filermedia.py b/filer/templatetags/filermedia.py
deleted file mode 100644
index 84a7f60f7..000000000
--- a/filer/templatetags/filermedia.py
+++ /dev/null
@@ -1,16 +0,0 @@
-#-*- coding: utf-8 -*-
-from django.template import Library
-
-register = Library()
-
-
-def filer_staticmedia_prefix():
- """
- Returns the string contained in the setting FILER_STATICMEDIA_PREFIX.
- """
- try:
- from filer import settings
- except ImportError:
- return ''
- return settings.FILER_STATICMEDIA_PREFIX
-filer_staticmedia_prefix = register.simple_tag(filer_staticmedia_prefix)
diff --git a/filer/thumbnail_processors.py b/filer/thumbnail_processors.py
index a06b4ef73..04fc43789 100644
--- a/filer/thumbnail_processors.py
+++ b/filer/thumbnail_processors.py
@@ -1,16 +1,18 @@
-#-*- coding: utf-8 -*-
import re
+
+from easy_thumbnails import processors
+
+from .settings import FILER_SUBJECT_LOCATION_IMAGE_DEBUG, FILER_WHITESPACE_COLOR
+
+
try:
- from PIL import Image
- from PIL import ImageDraw
+ from PIL import Image, ImageDraw
except ImportError:
try:
import Image
import ImageDraw
except ImportError:
raise ImportError("The Python Imaging Library was not found.")
-from easy_thumbnails import processors
-from filer.settings import FILER_SUBJECT_LOCATION_IMAGE_DEBUG
RE_SUBJECT_LOCATION = re.compile(r'^(\d+),(\d+)$')
@@ -30,12 +32,13 @@ def normalize_subject_location(subject_location):
def scale_and_crop_with_subject_location(im, size, subject_location=False,
- crop=False, upscale=False, **kwargs):
+ zoom=None, crop=False, upscale=False,
+ **kwargs):
"""
Like ``easy_thumbnails.processors.scale_and_crop``, but will use the
coordinates in ``subject_location`` to make sure that that part of the
image is in the center or at least somewhere on the cropped image.
- Please not that this does *not* work correctly if the image has been
+ Please note that this does *not* work correctly if the image has been
resized by a previous processor (e.g ``autocrop``).
``crop`` needs to be set for this to work, but any special cropping
@@ -44,15 +47,15 @@ def scale_and_crop_with_subject_location(im, size, subject_location=False,
subject_location = normalize_subject_location(subject_location)
if not (subject_location and crop):
# use the normal scale_and_crop
- return processors.scale_and_crop(im, size, crop=crop,
+ return processors.scale_and_crop(im, size, zoom=zoom, crop=crop,
upscale=upscale, **kwargs)
# for here on we have a subject_location and cropping is on
# --snip-- this is a copy and paste of the first few
# lines of ``scale_and_crop``
- source_x, source_y = [float(v) for v in im.size]
- target_x, target_y = [float(v) for v in size]
+ source_x, source_y = (float(v) for v in im.size)
+ target_x, target_y = (float(v) for v in size)
if crop or not target_x or not target_y:
scale = max(target_x / source_x, target_y / source_y)
@@ -65,16 +68,27 @@ def scale_and_crop_with_subject_location(im, size, subject_location=False,
elif not target_y:
target_y = source_y * scale
+ if zoom:
+ if not crop:
+ target_x = round(source_x * scale)
+ target_y = round(source_y * scale)
+ scale *= (100 + int(zoom)) / 100.0
+
if scale < 1.0 or (scale > 1.0 and upscale):
- im = im.resize((int(source_x * scale), int(source_y * scale)),
- resample=Image.ANTIALIAS)
+ try:
+ im = im.resize((int(source_x * scale), int(source_y * scale)),
+ resample=Image.LANCZOS)
+ except AttributeError: # pragma: no cover
+ im = im.resize((int(source_x * scale), int(source_y * scale)),
+ resample=Image.ANTIALIAS)
+
# --endsnip-- begin real code
# ===============================
# subject location aware cropping
# ===============================
# res_x, res_y: the resolution of the possibly already resized image
- res_x, res_y = [float(v) for v in im.size]
+ res_x, res_y = (float(v) for v in im.size)
# subj_x, subj_y: the position of the subject (maybe already re-scaled)
subj_x = res_x * float(subject_location[0]) / source_x
@@ -110,10 +124,37 @@ def scale_and_crop_with_subject_location(im, size, subject_location=False,
if ex or ey:
crop_box = ((int(tex), int(tey), int(tfx), int(tfy)))
if FILER_SUBJECT_LOCATION_IMAGE_DEBUG:
- # draw elipse on focal point for Debugging
+ # draw ellipse on focal point for Debugging
draw = ImageDraw.Draw(im)
esize = 10
draw.ellipse(((subj_x - esize, subj_y - esize),
(subj_x + esize, subj_y + esize)), outline="#FF0000")
im = im.crop(crop_box)
return im
+
+
+def whitespace(image, size, whitespace=False, whitespace_color=None, **kwargs):
+ if not whitespace:
+ return image
+
+ if whitespace_color is None:
+ whitespace_color = FILER_WHITESPACE_COLOR
+ if whitespace_color is None:
+ whitespace_color = '#fff'
+
+ old_image = image
+ source_x, source_y = image.size
+ target_x, target_y = size
+
+ image = Image.new('RGBA', (target_x, target_y), whitespace_color)
+ if source_x < target_x and source_y < target_y: # whitespace all around
+ image.paste(old_image, (
+ (target_x - source_x) / 2, (target_y - source_y) / 2))
+ elif source_x < target_x: # whitespace on top and bottom only
+ image.paste(old_image, ((target_x - source_x) / 2, 0))
+ elif source_y < target_y: # whitespace on sides only
+ image.paste(old_image, (0, (target_y - source_y) / 2))
+ else: # no whitespace needed
+ image = old_image
+
+ return image
diff --git a/filer/utils/filer_easy_thumbnails.py b/filer/utils/filer_easy_thumbnails.py
index 2ee1f7d0d..00b2f4b43 100644
--- a/filer/utils/filer_easy_thumbnails.py
+++ b/filer/utils/filer_easy_thumbnails.py
@@ -1,8 +1,9 @@
-#-*- coding: utf-8 -*-
-from easy_thumbnails.files import Thumbnailer
import os
import re
+from easy_thumbnails.files import Thumbnailer
+
+
# match the source filename using `__` as the seperator. ``opts_and_ext`` is non
# greedy so it should match the last occurence of `__`.
# in ``ThumbnailerNameMixin.get_thumbnail_name`` we ensure that there is no `__`
@@ -17,20 +18,24 @@ def thumbnail_to_original_filename(thumbnail_name):
return None
-class ThumbnailerNameMixin(object):
+class ThumbnailerNameMixin:
thumbnail_basedir = ''
thumbnail_subdir = ''
thumbnail_prefix = ''
- def get_thumbnail_name(self, thumbnail_options, transparent=False,
- high_resolution=False):
+ def get_thumbnail_name(self, thumbnail_options, transparent=False):
"""
A version of ``Thumbnailer.get_thumbnail_name`` that produces a
reproducible thumbnail name that can be converted back to the original
filename.
"""
path, source_filename = os.path.split(self.name)
- if transparent:
+ source_extension = os.path.splitext(source_filename)[1][1:].lower()
+ preserve_extensions = self.thumbnail_preserve_extensions
+ if preserve_extensions is True or source_extension == 'svg' or \
+ isinstance(preserve_extensions, (list, tuple)) and source_extension in preserve_extensions:
+ extension = source_extension
+ elif transparent:
extension = self.thumbnail_transparency_extension
else:
extension = self.thumbnail_extension
@@ -38,35 +43,36 @@ def get_thumbnail_name(self, thumbnail_options, transparent=False,
thumbnail_options = thumbnail_options.copy()
size = tuple(thumbnail_options.pop('size'))
+ initial_opts = ['{}x{}'.format(*size)]
quality = thumbnail_options.pop('quality', self.thumbnail_quality)
- initial_opts = ['%sx%s' % size, 'q%s' % quality]
+ if extension == 'jpg':
+ initial_opts.append(f'q{quality}')
+ elif extension == 'svg':
+ thumbnail_options.pop('subsampling', None)
+ thumbnail_options.pop('upscale', None)
opts = list(thumbnail_options.items())
opts.sort() # Sort the options so the file name is consistent.
- opts = ['%s' % (v is not True and '%s-%s' % (k, v) or k)
+ opts = ['{}'.format(v is not True and f'{k}-{v}' or k)
for k, v in opts if v]
-
all_opts = '_'.join(initial_opts + opts)
basedir = self.thumbnail_basedir
subdir = self.thumbnail_subdir
- #make sure our magic delimiter is not used in all_opts
+ # make sure our magic delimiter is not used in all_opts
all_opts = all_opts.replace('__', '_')
- if high_resolution:
- all_opts += '@2x'
- filename = '%s__%s.%s' % (source_filename, all_opts, extension)
+ filename = f'{source_filename}__{all_opts}.{extension}'
return os.path.join(basedir, path, subdir, filename)
-class ActionThumbnailerMixin(object):
+class ActionThumbnailerMixin:
thumbnail_basedir = ''
thumbnail_subdir = ''
thumbnail_prefix = ''
- def get_thumbnail_name(self, thumbnail_options, transparent=False,
- high_resolution=False):
+ def get_thumbnail_name(self, thumbnail_options, transparent=False):
"""
A version of ``Thumbnailer.get_thumbnail_name`` that returns the original
filename to resize.
@@ -85,7 +91,7 @@ def thumbnail_exists(self, thumbnail_name):
class FilerThumbnailer(ThumbnailerNameMixin, Thumbnailer):
def __init__(self, *args, **kwargs):
self.thumbnail_basedir = kwargs.pop('thumbnail_basedir', '')
- super(FilerThumbnailer, self).__init__(*args, **kwargs)
+ super().__init__(*args, **kwargs)
class FilerActionThumbnailer(ActionThumbnailerMixin, Thumbnailer):
diff --git a/filer/utils/files.py b/filer/utils/files.py
index 74d1c9d3e..4af97c241 100644
--- a/filer/utils/files.py
+++ b/filer/utils/files.py
@@ -1,53 +1,152 @@
-#-*- coding: utf-8 -*-
-import os
import mimetypes
+import os
+import uuid
+from django.http.multipartparser import ChunkIter, SkipFile, StopFutureHandlers, StopUpload, exhaust
+from django.template.defaultfilters import slugify as slugify_django
+from django.utils.encoding import force_str
from django.utils.text import get_valid_filename as get_valid_filename_django
-from django.template.defaultfilters import slugify
-from django.core.files.uploadedfile import SimpleUploadedFile
-
-from filer.settings import FILER_FILE_MODELS
-from filer.utils.loader import load_object
-from filer.utils.is_ajax import is_ajax
-import filetype
-
class UploadException(Exception):
pass
def handle_upload(request):
- if not request.method == "POST":
- raise UploadException("AJAX request not valid: must be POST")
- if is_ajax(request):
+ if not request.method == 'POST':
+ raise UploadException("XMLHttpRequest not valid: must be POST")
+ if request.headers.get('x-requested-with') == 'XMLHttpRequest':
# the file is stored raw in the request
is_raw = True
filename = request.GET.get('qqfile', False) or request.GET.get('filename', False) or ''
- if hasattr(request, 'body'):
- # raw_post_data was depreciated in django 1.4:
- # https://docs.djangoproject.com/en/dev/releases/1.4/#httprequest-raw-post-data-renamed-to-httprequest-body
- data = request.body
- elif hasattr(request, 'raw_post_data'):
- # fallback for django 1.3
- data = request.raw_post_data
+
+ try:
+ content_length = int(request.headers['content-length'])
+ except (IndexError, TypeError, ValueError):
+ content_length = None
+
+ if content_length < 0:
+ # This means we shouldn't continue...raise an error.
+ raise UploadException("Invalid content length: %r" % content_length)
+
+ mime_type = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
+
+ upload_handlers = request.upload_handlers
+ for handler in upload_handlers:
+ handler.handle_raw_input(request,
+ request.META,
+ content_length,
+ None,
+ None)
+ pass
+
+ # For compatibility with low-level network APIs (with 32-bit integers),
+ # the chunk size should be < 2^31, but still divisible by 4.
+ possible_sizes = [x.chunk_size for x in upload_handlers if x.chunk_size]
+ chunk_size = min([2 ** 31 - 4] + possible_sizes)
+
+ stream = ChunkIter(request, chunk_size)
+ counters = [0] * len(upload_handlers)
+
+ try:
+ for handler in upload_handlers:
+ try:
+ handler.new_file(None, filename,
+ None, content_length, None)
+ except StopFutureHandlers:
+ break
+
+ for chunk in stream:
+ for i, handler in enumerate(upload_handlers):
+ chunk_length = len(chunk)
+ chunk = handler.receive_data_chunk(chunk,
+ counters[i])
+ counters[i] += chunk_length
+ if chunk is None:
+ # If the chunk received by the handler is None, then don't continue.
+ break
+
+ except SkipFile:
+ # Just use up the rest of this file...
+ exhaust(stream)
+ except StopUpload as e:
+ if not e.connection_reset:
+ exhaust(request)
else:
- raise UploadException("Request is not valid: there is no request body.")
- mime_type = mimetypes.guess_type(filename)[0] or "text/plain"
- upload = SimpleUploadedFile(name=filename, content=data, content_type=mime_type)
+ # Make sure that the request data is all fed
+ exhaust(request)
+
+ # Signal that the upload has completed.
+ for handler in upload_handlers:
+ retval = handler.upload_complete()
+ if retval:
+ break
+
+ for i, handler in enumerate(upload_handlers):
+ file_obj = handler.file_complete(counters[i])
+ if file_obj:
+ upload = file_obj
+ break
else:
if len(request.FILES) == 1:
- # FILES is a dictionary in Django but Ajax Upload gives the uploaded file an
- # ID based on a random number, so it cannot be guessed here in the code.
- # Rather than editing Ajax Upload to pass the ID in the querystring, note that
- # each upload is a separate request so FILES should only have one entry.
- # Thus, we can just grab the first (and only) value in the dict.
- is_raw = False
- upload = list(request.FILES.values())[0]
- filename = upload.name
+ upload, filename, is_raw, mime_type = handle_request_files_upload(request)
else:
- raise UploadException("AJAX request not valid: Bad Upload")
- return upload, filename, is_raw
+ raise UploadException("XMLHttpRequest request not valid: Bad Upload")
+ return upload, filename, is_raw, mime_type
+
+
+def handle_request_files_upload(request):
+ """
+ Handle request.FILES if len(request.FILES) == 1.
+ Returns tuple(upload, filename, is_raw, mime_type) where upload is file itself.
+ """
+ # FILES is a dictionary in Django but Ajax Upload gives the uploaded file
+ # an ID based on a random number, so it cannot be guessed here in the code.
+ # Rather than editing Ajax Upload to pass the ID in the querystring,
+ # note that each upload is a separate request so FILES should only
+ # have one entry.
+ # Thus, we can just grab the first (and only) value in the dict.
+ is_raw = False
+ upload = list(request.FILES.values())[0]
+ filename = upload.name
+ _, iext = os.path.splitext(filename)
+ mime_type = upload.content_type.lower()
+ extensions = mimetypes.guess_all_extensions(mime_type)
+ if mime_type != 'application/octet-stream' and extensions and iext.lower() not in extensions:
+ msg = "MIME-Type '{mimetype}' does not correspond to file extension of {filename}."
+ raise UploadException(msg.format(mimetype=mime_type, filename=filename))
+ return upload, filename, is_raw, mime_type
+
+
+def slugify(string):
+ return slugify_django(force_str(string))
+
+
+def _ensure_safe_length(filename, max_length=155, random_suffix_length=16):
+ """
+ Ensures that the filename does not exceed the maximum allowed length.
+ If it does, the function truncates the filename and appends a random hexadecimal
+ suffix of length `random_suffix_length` to ensure uniqueness and compliance with
+ database constraints - even after markers for a thumbnail are added.
+
+ Parameters:
+ filename (str): The filename to check.
+ max_length (int): The maximum allowed length for the filename.
+ random_suffix_length (int): The length of the random suffix to append.
+
+ Returns:
+ str: The safe filename.
+
+
+ Reference issue: https://github.com/django-cms/django-filer/issues/1270
+ """
+
+ if len(filename) <= max_length:
+ return filename
+
+ keep_length = max_length - random_suffix_length
+ random_suffix = uuid.uuid4().hex[:random_suffix_length]
+ return filename[:keep_length] + random_suffix
def get_valid_filename(s):
@@ -55,42 +154,53 @@ def get_valid_filename(s):
like the regular get_valid_filename, but also slugifies away
umlauts and stuff.
"""
- if not s:
- return ''
s = get_valid_filename_django(s)
filename, ext = os.path.splitext(s)
filename = slugify(filename)
ext = slugify(ext)
if ext:
- return "%s.%s" % (filename, ext)
+ valid_filename = "{}.{}".format(filename, ext)
else:
- return "%s" % (filename,)
+ valid_filename = "{}".format(filename)
+
+ # Ensure the filename meets the maximum length requirements.
+ return _ensure_safe_length(valid_filename)
+# PBS-specific: file type matching and filename utilities
+
def matching_file_subtypes(filename, file_pointer, request):
"""
Returns a list of valid subtypes for a given file.
"""
+ from ..settings import FILER_FILE_MODELS
+ from .loader import load_object
+
types = list(map(load_object, FILER_FILE_MODELS))
def _match_subtype(subtype):
- is_match = subtype.matches_file_type(filename, file_pointer, request)
- return is_match
+ return subtype.matches_file_type(filename, file_pointer, request)
type_matches = list(filter(_match_subtype, types))
return type_matches
def truncate_filename(upload, maxlen=None):
"""
- Return truncated filename
- Pre-extension filename will be less than or equals maxlen(if passed)
+ Return truncated filename.
+ Pre-extension filename will be less than or equals maxlen (if passed).
"""
+ try:
+ import filetype as filetype_lib
+ except ImportError:
+ filetype_lib = None
+
title, extension = os.path.splitext(upload.name)
if not extension.lstrip('.'):
- guessed = filetype.guess_extension(upload) or ''
- # filetype reads bytes from the upload; seek back for later use
- if hasattr(upload, 'seek'):
- upload.seek(0)
+ guessed = ''
+ if filetype_lib is not None:
+ guessed = filetype_lib.guess_extension(upload) or ''
+ if hasattr(upload, 'seek'):
+ upload.seek(0)
else:
guessed = ''
filename = '{title}.{ext}'.format(title=title[:maxlen],
diff --git a/filer/utils/generate_filename.py b/filer/utils/generate_filename.py
index 14b6f8874..5d107ce35 100644
--- a/filer/utils/generate_filename.py
+++ b/filer/utils/generate_filename.py
@@ -1,20 +1,28 @@
-import datetime
import os
-import filer
-
-from filer.utils.files import get_valid_filename
+import uuid
from django.core.exceptions import ObjectDoesNotExist
from django.core.files.uploadedfile import UploadedFile
-from django.utils.encoding import smart_str
+from django.utils.encoding import force_str
+from django.utils.timezone import now
+
+import filer
+
+from .files import get_valid_filename
def by_date(instance, filename):
- datepart = str(datetime.datetime.now().strftime(smart_str("%Y/%m/%d")))
+ datepart = force_str(now().strftime("%Y/%m/%d"))
return os.path.join(datepart, get_valid_filename(filename))
-class prefixed_factory(object):
+def randomized(instance, filename):
+ uuid_str = str(uuid.uuid4())
+ return os.path.join(uuid_str[0:2], uuid_str[2:4], uuid_str,
+ get_valid_filename(filename))
+
+
+class prefixed_factory:
def __init__(self, upload_to, prefix):
self.upload_to = upload_to
self.prefix = prefix
@@ -29,6 +37,8 @@ def __call__(self, instance, filename):
return os.path.join(self.prefix, upload_to_str)
+# PBS-specific: path-based filename generation
+
def _is_in_memory(file_):
return isinstance(file_, UploadedFile)
@@ -61,8 +71,6 @@ def by_path(instance, filename):
def get_trash_path(instance):
path = [filer.settings.FILER_TRASH_PREFIX]
- # enforce uniqueness by using file's pk
path.append("%s" % instance.pk)
- # add folder path
path.append(instance.pretty_logical_path.strip('/'))
return os.path.join(*path)
diff --git a/filer/utils/is_ajax.py b/filer/utils/is_ajax.py
deleted file mode 100644
index 63166d793..000000000
--- a/filer/utils/is_ajax.py
+++ /dev/null
@@ -1,3 +0,0 @@
-def is_ajax(request):
- """Checks if a request is ajax."""
- return request.headers.get('x-requested-with') == 'XMLHttpRequest'
diff --git a/filer/utils/loader.py b/filer/utils/loader.py
index 89a841ae3..b75f8f4d6 100644
--- a/filer/utils/loader.py
+++ b/filer/utils/loader.py
@@ -1,4 +1,3 @@
-#-*- coding: utf-8 -*-
"""
This function is snatched from
https://github.com/ojii/django-load/blob/3058ab9d9d4875589638cc45e84b59e7e1d7c9c3/django_load/core.py#L49
@@ -8,6 +7,7 @@
or method.
"""
+
from importlib import import_module
@@ -30,13 +30,20 @@ def load_object(import_path):
return import_path
if '.' not in import_path:
raise TypeError(
- "'import_path' argument to 'django_load.core.load_object' " +\
- "must contain at least one dot.")
+ "'import_path' argument to 'django_load.core.load_object' must "
+ "contain at least one dot.")
module_name, object_name = import_path.rsplit('.', 1)
module = import_module(module_name)
return getattr(module, object_name)
+def load_model(model_name):
+ from django.apps import apps
+
+ model_name_tuple = model_name.split('.')
+ return apps.get_model(*model_name_tuple)
+
+
def storage_factory(klass, location, base_url):
"""
This factory returns an instance of the storage class provided.
diff --git a/filer/utils/model_label.py b/filer/utils/model_label.py
index f488cf67f..93cc66809 100644
--- a/filer/utils/model_label.py
+++ b/filer/utils/model_label.py
@@ -1,15 +1,17 @@
def get_model_label(model):
"""
Take a model class or model label and return its model label.
+
>>> get_model_label(MyModel)
"myapp.MyModel"
+
>>> get_model_label("myapp.MyModel")
"myapp.MyModel"
"""
if isinstance(model, str):
return model
else:
- return "%s.%s" % (
+ return "{}.{}".format(
model._meta.app_label,
model.__name__
)
diff --git a/filer/utils/multi_model_qs.py b/filer/utils/multi_model_qs.py
deleted file mode 100644
index e87227ab5..000000000
--- a/filer/utils/multi_model_qs.py
+++ /dev/null
@@ -1,49 +0,0 @@
-from functools import reduce
-
-
-class MultiMoldelQuerysetChain(object):
- """
- Allows passing a list of different models querysets to a paginator without
- transforming them into one list.
- """
-
- def __init__(self, qsets):
- self._qsets = qsets
- self._offsets = []
- offset = 0
- for q in qsets:
- self._offsets.append(offset)
- offset += q.count()
- self.length = offset
-
- def __getitem__(self, key):
- if isinstance(key, slice):
- return self._get_slice(key)
- elif isinstance(key, int):
- return self._get_item(key)
- else:
- raise TypeError("QuerysetChain index must be int or slice not {}".format(type(key)))
-
- def _get_item(self, key):
- if key < 0:
- raise IndexError("index out of bounds {}".format(key))
- for index, offset in enumerate(self._offsets):
- qset = self._qsets[index]
- if key-offset < qset.count():
- return qset[key-offset]
- raise IndexError("index out of bounds {}".format(key))
-
- def _get_slice(self, key):
- assert key.start is not None
- assert key.stop is not None
- assert key.step is None
- slices = (self.slice(qs, key.start-offset, key.stop-offset)
- for qs, offset in zip(self._qsets, self._offsets))
- return reduce(lambda s1, s2: s1+s2, slices)
-
- def slice(self, qset, start, stop):
- start, stop = max(start, 0), max(stop, 0)
- return list(qset[start:stop])
-
- def __len__(self):
- return self.length
diff --git a/filer/utils/pil_exif.py b/filer/utils/pil_exif.py
index 0d0077b77..f0280e4aa 100644
--- a/filer/utils/pil_exif.py
+++ b/filer/utils/pil_exif.py
@@ -1,35 +1,29 @@
-#-*- coding: utf-8 -*-
-try:
- from PIL import Image
- from PIL import ExifTags
-except ImportError:
- try:
- import Image
- import ExifTags
- except ImportError:
- raise ImportError("The Python Imaging Library was not found.")
+from django.core.files.storage import default_storage as storage
+
+from ..utils.compatibility import PILExifTags, PILImage
def get_exif(im):
try:
exif_raw = im._getexif() or {}
- except:
+ except Exception:
+ # Not available? Return empty dict
return {}
ret = {}
for tag, value in list(exif_raw.items()):
- decoded = ExifTags.TAGS.get(tag, tag)
+ decoded = PILExifTags.TAGS.get(tag, tag)
ret[decoded] = value
return ret
def get_exif_for_file(file_obj):
- im = Image.open(file_obj, 'r')
+ im = PILImage.open(storage.open(file_obj.name), 'r')
return get_exif(im)
def get_subject_location(exif_data):
try:
r = (int(exif_data['SubjectLocation'][0]), int(exif_data['SubjectLocation'][1]),)
- except:
+ except KeyError:
r = None
return r
diff --git a/filer/utils/recursive_dictionary.py b/filer/utils/recursive_dictionary.py
index cd49eaa3b..01ad34be0 100644
--- a/filer/utils/recursive_dictionary.py
+++ b/filer/utils/recursive_dictionary.py
@@ -1,5 +1,3 @@
-#-*- coding: utf-8 -*-
-
# https://gist.github.com/114831
# recursive_dictionary.py
# Created 2009-05-20 by Jannis Andrija Schnitzer.
@@ -24,8 +22,10 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
+
__author__ = 'jannis@itisme.org (Jannis Andrija Schnitzer)'
+
class RecursiveDictionary(dict):
"""RecursiveDictionary provides the methods rec_update and iter_rec_update
that can be used to update member dictionaries rather than overwriting
@@ -41,11 +41,11 @@ def rec_update(self, other, **third):
{'foo': {'baz': 36, 'bar': 42}}
"""
try:
- iterator = list(other.items())
+ iterator = other.items()
except AttributeError:
iterator = other
self.iter_rec_update(iterator)
- self.iter_rec_update(list(third.items()))
+ self.iter_rec_update(third.items())
def iter_rec_update(self, iterator):
for (key, value) in iterator:
@@ -65,7 +65,7 @@ class RecursiveDictionaryWithExcludes(RecursiveDictionary):
"""
def __init__(self, *args, **kwargs):
self.rec_excluded_keys = kwargs.pop('rec_excluded_keys', ())
- super(RecursiveDictionaryWithExcludes, self).__init__(*args, **kwargs)
+ super().__init__(*args, **kwargs)
def iter_rec_update(self, iterator):
for (key, value) in iterator:
@@ -76,4 +76,4 @@ def iter_rec_update(self, iterator):
rec_excluded_keys=self.rec_excluded_keys)
self[key].rec_update(value)
else:
- self[key] = value
\ No newline at end of file
+ self[key] = value
diff --git a/filer/utils/zip.py b/filer/utils/zip.py
deleted file mode 100644
index e6b1e3b29..000000000
--- a/filer/utils/zip.py
+++ /dev/null
@@ -1,26 +0,0 @@
-#-*- coding: utf-8 -*-
-#import zipfile
-# zipfile.open() is only available in Python 2.6, so we use the future version
-from django.core.files.uploadedfile import SimpleUploadedFile
-from zipfile import ZipFile
-
-
-def unzip(file_obj):
- """
- Take a path to a zipfile and checks if it is a valid zip file
- and returns...
- """
- files = []
- # TODO: implement try-except here
- zip = ZipFile(file_obj)
- bad_file = zip.testzip()
- if bad_file:
- raise Exception('"%s" in the .zip archive is corrupt.' % bad_file)
- infolist = zip.infolist()
- for zipinfo in infolist:
- if zipinfo.filename.startswith('__'): # do not process meta files
- continue
- file_obj = SimpleUploadedFile(name=zipinfo.filename, content=zip.read(zipinfo))
- files.append((file_obj, zipinfo.filename))
- zip.close()
- return files
From 848acee4a607f0662233cba32db7ba32dc467d2c Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 30 Apr 2026 09:53:30 +0000
Subject: [PATCH 4/6] Phase 8: Generate migration 0008 for upstream schema
changes (ThumbnailOption, FolderPermission, mime_type, etc.)
Agent-Logs-Url: https://github.com/pbs/django-filer/sessions/cad30a66-0889-48ba-bb3e-2ab3bf2939b2
Co-authored-by: andreilupuleasa <124138251+andreilupuleasa@users.noreply.github.com>
---
=5.1 | 518 ++++++++++++++++++
filer/admin/common_admin.py | 4 +-
filer/admin/tools.py | 42 ++
filer/fields/file.py | 8 +
...nailoption_alter_image_options_and_more.py | 137 +++++
filer/utils/multi_model_qs.py | 49 ++
6 files changed, 756 insertions(+), 2 deletions(-)
create mode 100644 =5.1
create mode 100644 filer/migrations/0008_thumbnailoption_alter_image_options_and_more.py
create mode 100644 filer/utils/multi_model_qs.py
diff --git a/=5.1 b/=5.1
new file mode 100644
index 000000000..7488124a1
--- /dev/null
+++ b/=5.1
@@ -0,0 +1,518 @@
+Defaulting to user installation because normal site-packages is not writeable
+Collecting django
+ Downloading django-6.0.4-py3-none-any.whl.metadata (3.9 kB)
+Collecting django-mptt
+ Downloading django_mptt-0.18.0-py3-none-any.whl.metadata (5.3 kB)
+Collecting django-polymorphic
+ Downloading django_polymorphic-4.11.2-py3-none-any.whl.metadata (7.7 kB)
+Collecting django-js-asset
+ Downloading django_js_asset-3.1.2-py3-none-any.whl.metadata (6.4 kB)
+Collecting easy-thumbnails
+ Downloading easy_thumbnails-2.10.1-py3-none-any.whl.metadata (15 kB)
+Collecting Unidecode
+ Downloading Unidecode-1.4.0-py3-none-any.whl.metadata (13 kB)
+Collecting filetype
+ Downloading filetype-1.2.0-py2.py3-none-any.whl.metadata (6.5 kB)
+Collecting svglib
+ Downloading svglib-1.6.0-py3-none-any.whl.metadata (11 kB)
+Collecting Pillow
+ Downloading pillow-12.2.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.metadata (8.8 kB)
+Collecting asgiref>=3.9.1 (from django)
+ Downloading asgiref-3.11.1-py3-none-any.whl.metadata (9.3 kB)
+Collecting sqlparse>=0.5.0 (from django)
+ Downloading sqlparse-0.5.5-py3-none-any.whl.metadata (4.7 kB)
+Collecting typing-extensions>=4.12.0 (from django-polymorphic)
+ Downloading typing_extensions-4.15.0-py3-none-any.whl.metadata (3.3 kB)
+Collecting cssselect2>=0.2.0 (from svglib)
+ Downloading cssselect2-0.9.0-py3-none-any.whl.metadata (2.9 kB)
+Collecting lxml>=6.0.0 (from svglib)
+ Downloading lxml-6.1.0-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl.metadata (4.0 kB)
+Collecting reportlab>=4.4.3 (from svglib)
+ Downloading reportlab-4.5.0-py3-none-any.whl.metadata (1.7 kB)
+Collecting rlpycairo>=0.4.0 (from svglib)
+ Downloading rlpycairo-0.4.0-py3-none-any.whl.metadata (4.9 kB)
+Collecting tinycss2>=0.6.0 (from svglib)
+ Downloading tinycss2-1.5.1-py3-none-any.whl.metadata (3.0 kB)
+Collecting webencodings (from cssselect2>=0.2.0->svglib)
+ Downloading webencodings-0.5.1-py2.py3-none-any.whl.metadata (2.1 kB)
+Collecting charset-normalizer (from reportlab>=4.4.3->svglib)
+ Downloading charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl.metadata (40 kB)
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 40.9/40.9 kB 14.8 MB/s eta 0:00:00
+Collecting pycairo>=1.20.0 (from rlpycairo>=0.4.0->svglib)
+ Downloading pycairo-1.29.0.tar.gz (665 kB)
+ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 665.9/665.9 kB 23.9 MB/s eta 0:00:00
+ Installing build dependencies: started
+ Installing build dependencies: finished with status 'done'
+ Getting requirements to build wheel: started
+ Getting requirements to build wheel: finished with status 'done'
+ Preparing metadata (pyproject.toml): started
+ Preparing metadata (pyproject.toml): finished with status 'error'
+ error: subprocess-exited-with-error
+
+ × Preparing metadata (pyproject.toml) did not run successfully.
+ │ exit code: 1
+ ╰─> [454 lines of output]
+ + meson setup /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667 /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b -Dbuildtype=release -Db_ndebug=if-release -Db_vscrt=md -Dwheel=true -Dtests=false --native-file=/tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-python-native-file.ini
+ The Meson build system
+ Version: 1.11.1
+ Source dir: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667
+ Build dir: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b
+ Build type: native build
+ Project name: pycairo
+ Project version: 1.29.0
+ C compiler for the host machine: cc (gcc 13.3.0 "cc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0")
+ C linker for the host machine: cc ld.bfd 2.42
+ Host machine cpu family: x86_64
+ Host machine cpu: x86_64
+ Program python3 found: YES (/usr/bin/python3)
+ Compiler for C supports arguments -Wall: YES
+ Compiler for C supports arguments -Warray-bounds: YES
+ Compiler for C supports arguments -Wcast-align: YES
+ Compiler for C supports arguments -Wconversion: YES
+ Compiler for C supports arguments -Wextra: YES
+ Compiler for C supports arguments -Wformat=2: YES
+ Compiler for C supports arguments -Wformat-nonliteral: YES
+ Compiler for C supports arguments -Wformat-security: YES
+ Compiler for C supports arguments -Wimplicit-function-declaration: YES
+ Compiler for C supports arguments -Winit-self: YES
+ Compiler for C supports arguments -Wmissing-format-attribute: YES
+ Compiler for C supports arguments -Wmissing-noreturn: YES
+ Compiler for C supports arguments -Wnested-externs: YES
+ Compiler for C supports arguments -Wold-style-definition: YES
+ Compiler for C supports arguments -Wpacked: YES
+ Compiler for C supports arguments -Wpointer-arith: YES
+ Compiler for C supports arguments -Wreturn-type: YES
+ Compiler for C supports arguments -Wshadow: YES
+ Compiler for C supports arguments -Wsign-compare: YES
+ Compiler for C supports arguments -Wstrict-aliasing: YES
+ Compiler for C supports arguments -Wundef: YES
+ Compiler for C supports arguments -Wunused-but-set-variable: YES
+ Compiler for C supports arguments -Wswitch-default: YES
+ Compiler for C supports arguments -Wno-missing-field-initializers: YES
+ Compiler for C supports arguments -Wno-unused-parameter: YES
+ Compiler for C supports arguments -fno-strict-aliasing: YES
+ Compiler for C supports arguments -fvisibility=hidden: YES
+ Found pkg-config: YES (/usr/bin/pkg-config) 1.8.1
+ Found CMake: /usr/local/bin/cmake (3.31.6)
+ Run-time dependency cairo found: NO (tried pkg-config and cmake)
+
+ ../cairo/meson.build:31:12: ERROR: Dependency "cairo" not found (tried pkg-config and cmake)
+
+ A full log can be found at /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-logs/meson-log.txt
+ ::group::==== CI platform detected, click here for meson-log.txt contents. ====
+ Build started at 2026-04-30T09:49:25.394376
+ Main binary: /usr/bin/python3
+ Build Options: -Dbuildtype=release -Db_ndebug=if-release -Db_vscrt=md -Dwheel=true -Dtests=false --native-file=/tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-python-native-file.ini
+ Python system: Linux
+ The Meson build system
+ Version: 1.11.1
+ Source dir: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667
+ Build dir: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b
+ Build type: native build
+ Project name: pycairo
+ Project version: 1.29.0
+ -----------
+ Detecting compiler via: `cc --version` -> 0
+ stdout:
+ cc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0
+ Copyright (C) 2023 Free Software Foundation, Inc.
+ This is free software; see the source for copying conditions. There is NO
+ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ -----------
+ Running command: -cpp -x c -E -dM -
+ -----
+ -----------
+ Detecting linker via: `cc -Wl,--version` -> 0
+ stdout:
+ GNU ld (GNU Binutils for Ubuntu) 2.42
+ Copyright (C) 2024 Free Software Foundation, Inc.
+ This program is free software; you may redistribute it under the terms of
+ the GNU General Public License version 3 or (at your option) a later version.
+ This program has absolutely no warranty.
+ -----------
+ stderr:
+ collect2 version 13.3.0
+ /usr/bin/ld -plugin /usr/libexec/gcc/x86_64-linux-gnu/13/liblto_plugin.so -plugin-opt=/usr/libexec/gcc/x86_64-linux-gnu/13/lto-wrapper -plugin-opt=-fresolution=/tmp/ccxnZMDU.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -z now -z relro /usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/13/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/13 -L/usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/13/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/13/../../.. --version -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state /usr/lib/gcc/x86_64-linux-gnu/13/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu/crtn.o
+ -----------
+ Sanity check compiler command line: cc -D_FILE_OFFSET_BITS=64 -o sanity_check_for_c.exe sanity_check_for_c.c -D_FILE_OFFSET_BITS=64
+ Sanity check compile stdout:
+
+ -----
+ Sanity check compile stderr:
+
+ -----
+ Sanity check built target output for host machine c compiler
+ -- Running test binary command: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/sanity_check_for_c.exe
+ -----------
+ Sanity check: `/tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/sanity_check_for_c.exe` -> 0
+ -- stdout:
+
+ -- stderr:
+
+ -- returncode: 0
+ C compiler for the host machine: cc (gcc 13.3.0 "cc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0")
+ C linker for the host machine: cc ld.bfd 2.42
+ -----------
+ Detecting archiver via: `gcc-ar --version` -> 0
+ stdout:
+ GNU ar (GNU Binutils for Ubuntu) 2.42
+ Copyright (C) 2024 Free Software Foundation, Inc.
+ This program is free software; you may redistribute it under the terms of
+ the GNU General Public License version 3 or (at your option) any later version.
+ This program has absolutely no warranty.
+ -----------
+ -----------
+ Detecting compiler via: `cc --version` -> 0
+ stdout:
+ cc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0
+ Copyright (C) 2023 Free Software Foundation, Inc.
+ This is free software; see the source for copying conditions. There is NO
+ warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ -----------
+ Running command: -cpp -x c -E -dM -
+ -----
+ -----------
+ Detecting linker via: `cc -Wl,--version` -> 0
+ stdout:
+ GNU ld (GNU Binutils for Ubuntu) 2.42
+ Copyright (C) 2024 Free Software Foundation, Inc.
+ This program is free software; you may redistribute it under the terms of
+ the GNU General Public License version 3 or (at your option) a later version.
+ This program has absolutely no warranty.
+ -----------
+ stderr:
+ collect2 version 13.3.0
+ /usr/bin/ld -plugin /usr/libexec/gcc/x86_64-linux-gnu/13/liblto_plugin.so -plugin-opt=/usr/libexec/gcc/x86_64-linux-gnu/13/lto-wrapper -plugin-opt=-fresolution=/tmp/ccxwrsCc.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -z now -z relro /usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/13/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/13 -L/usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/13/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/13/../../.. --version -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state /usr/lib/gcc/x86_64-linux-gnu/13/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu/crtn.o
+ -----------
+ Sanity check compiler command line: cc -D_FILE_OFFSET_BITS=64 -o sanity_check_for_c.exe sanity_check_for_c.c -D_FILE_OFFSET_BITS=64
+ Sanity check compile stdout:
+
+ -----
+ Sanity check compile stderr:
+
+ -----
+ Sanity check built target output for build machine c compiler
+ -- Running test binary command: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/sanity_check_for_c.exe
+ -----------
+ Sanity check: `/tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/sanity_check_for_c.exe` -> 0
+ -- stdout:
+
+ -- stderr:
+
+ -- returncode: 0
+ C compiler for the build machine: cc (gcc 13.3.0 "cc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0")
+ C linker for the build machine: cc ld.bfd 2.42
+ Build machine cpu family: x86_64
+ Build machine cpu: x86_64
+ Host machine cpu family: x86_64
+ Host machine cpu: x86_64
+ Target machine cpu family: x86_64
+ Target machine cpu: x86_64
+ Program python3 found: YES (/usr/bin/python3)
+ Running compile:
+ Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpq10ufb7l
+ Code:
+ extern int i;
+ int i;
+
+ -----------
+ Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpq10ufb7l/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpq10ufb7l/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wall` -> 0
+ Compiler for C supports arguments -Wall: YES
+ Running compile:
+ Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp8qts2n5l
+ Code:
+ extern int i;
+ int i;
+
+ -----------
+ Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp8qts2n5l/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp8qts2n5l/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Warray-bounds` -> 0
+ Compiler for C supports arguments -Warray-bounds: YES
+ Running compile:
+ Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpg8nrnjvb
+ Code:
+ extern int i;
+ int i;
+
+ -----------
+ Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpg8nrnjvb/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpg8nrnjvb/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wcast-align` -> 0
+ Compiler for C supports arguments -Wcast-align: YES
+ Running compile:
+ Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpahi5_xug
+ Code:
+ extern int i;
+ int i;
+
+ -----------
+ Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpahi5_xug/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpahi5_xug/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wconversion` -> 0
+ Compiler for C supports arguments -Wconversion: YES
+ Running compile:
+ Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpvetn92u9
+ Code:
+ extern int i;
+ int i;
+
+ -----------
+ Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpvetn92u9/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpvetn92u9/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wextra` -> 0
+ Compiler for C supports arguments -Wextra: YES
+ Running compile:
+ Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp591nhuze
+ Code:
+ extern int i;
+ int i;
+
+ -----------
+ Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp591nhuze/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp591nhuze/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wformat=2` -> 0
+ Compiler for C supports arguments -Wformat=2: YES
+ Running compile:
+ Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpr5lad1fi
+ Code:
+ extern int i;
+ int i;
+
+ -----------
+ Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpr5lad1fi/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpr5lad1fi/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wformat-nonliteral` -> 0
+ Compiler for C supports arguments -Wformat-nonliteral: YES
+ Running compile:
+ Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpduqdxvjc
+ Code:
+ extern int i;
+ int i;
+
+ -----------
+ Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpduqdxvjc/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpduqdxvjc/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wformat-security` -> 0
+ Compiler for C supports arguments -Wformat-security: YES
+ Running compile:
+ Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpr2cg9qgk
+ Code:
+ extern int i;
+ int i;
+
+ -----------
+ Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpr2cg9qgk/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpr2cg9qgk/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wimplicit-function-declaration` -> 0
+ Compiler for C supports arguments -Wimplicit-function-declaration: YES
+ Running compile:
+ Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpv65xtow9
+ Code:
+ extern int i;
+ int i;
+
+ -----------
+ Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpv65xtow9/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpv65xtow9/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Winit-self` -> 0
+ Compiler for C supports arguments -Winit-self: YES
+ Running compile:
+ Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp82mly3km
+ Code:
+ extern int i;
+ int i;
+
+ -----------
+ Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp82mly3km/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp82mly3km/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wmissing-format-attribute` -> 0
+ Compiler for C supports arguments -Wmissing-format-attribute: YES
+ Running compile:
+ Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpfaswcrni
+ Code:
+ extern int i;
+ int i;
+
+ -----------
+ Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpfaswcrni/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpfaswcrni/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wmissing-noreturn` -> 0
+ Compiler for C supports arguments -Wmissing-noreturn: YES
+ Running compile:
+ Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmps5n97jul
+ Code:
+ extern int i;
+ int i;
+
+ -----------
+ Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmps5n97jul/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmps5n97jul/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wnested-externs` -> 0
+ Compiler for C supports arguments -Wnested-externs: YES
+ Running compile:
+ Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpkcvs47pl
+ Code:
+ extern int i;
+ int i;
+
+ -----------
+ Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpkcvs47pl/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpkcvs47pl/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wold-style-definition` -> 0
+ Compiler for C supports arguments -Wold-style-definition: YES
+ Running compile:
+ Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp089e1gxr
+ Code:
+ extern int i;
+ int i;
+
+ -----------
+ Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp089e1gxr/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp089e1gxr/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wpacked` -> 0
+ Compiler for C supports arguments -Wpacked: YES
+ Running compile:
+ Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp5vsuim0z
+ Code:
+ extern int i;
+ int i;
+
+ -----------
+ Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp5vsuim0z/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp5vsuim0z/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wpointer-arith` -> 0
+ Compiler for C supports arguments -Wpointer-arith: YES
+ Running compile:
+ Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmppw3tn4mm
+ Code:
+ extern int i;
+ int i;
+
+ -----------
+ Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmppw3tn4mm/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmppw3tn4mm/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wreturn-type` -> 0
+ Compiler for C supports arguments -Wreturn-type: YES
+ Running compile:
+ Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpdyednray
+ Code:
+ extern int i;
+ int i;
+
+ -----------
+ Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpdyednray/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpdyednray/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wshadow` -> 0
+ Compiler for C supports arguments -Wshadow: YES
+ Running compile:
+ Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmppzs14kt9
+ Code:
+ extern int i;
+ int i;
+
+ -----------
+ Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmppzs14kt9/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmppzs14kt9/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wsign-compare` -> 0
+ Compiler for C supports arguments -Wsign-compare: YES
+ Running compile:
+ Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpim1xzoce
+ Code:
+ extern int i;
+ int i;
+
+ -----------
+ Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpim1xzoce/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpim1xzoce/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wstrict-aliasing` -> 0
+ Compiler for C supports arguments -Wstrict-aliasing: YES
+ Running compile:
+ Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp2rcs89un
+ Code:
+ extern int i;
+ int i;
+
+ -----------
+ Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp2rcs89un/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp2rcs89un/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wundef` -> 0
+ Compiler for C supports arguments -Wundef: YES
+ Running compile:
+ Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpye9rfxe9
+ Code:
+ extern int i;
+ int i;
+
+ -----------
+ Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpye9rfxe9/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpye9rfxe9/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wunused-but-set-variable` -> 0
+ Compiler for C supports arguments -Wunused-but-set-variable: YES
+ Running compile:
+ Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpgt0j7j7o
+ Code:
+ extern int i;
+ int i;
+
+ -----------
+ Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpgt0j7j7o/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpgt0j7j7o/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wswitch-default` -> 0
+ Compiler for C supports arguments -Wswitch-default: YES
+ Running compile:
+ Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpuy8j9o8g
+ Code:
+ extern int i;
+ int i;
+
+ -----------
+ Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpuy8j9o8g/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpuy8j9o8g/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wmissing-field-initializers -Wno-missing-field-initializers` -> 0
+ Compiler for C supports arguments -Wno-missing-field-initializers: YES
+ Running compile:
+ Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp206a43mw
+ Code:
+ extern int i;
+ int i;
+
+ -----------
+ Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp206a43mw/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp206a43mw/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wunused-parameter -Wno-unused-parameter` -> 0
+ Compiler for C supports arguments -Wno-unused-parameter: YES
+ Running compile:
+ Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp98e0ru46
+ Code:
+ extern int i;
+ int i;
+
+ -----------
+ Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp98e0ru46/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp98e0ru46/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -fno-strict-aliasing` -> 0
+ Compiler for C supports arguments -fno-strict-aliasing: YES
+ Running compile:
+ Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpz771h1xa
+ Code:
+ extern int i;
+ int i;
+
+ -----------
+ Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpz771h1xa/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpz771h1xa/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -fvisibility=hidden` -> 0
+ Compiler for C supports arguments -fvisibility=hidden: YES
+ Pkg-config binary missing from cross or native file, or env var undefined.
+ Trying a default Pkg-config fallback at pkg-config
+ Found pkg-config: YES (/usr/bin/pkg-config) 1.8.1
+ Determining dependency 'cairo' with pkg-config executable '/usr/bin/pkg-config'
+ env[PKG_CONFIG_PATH]:
+ env[PKG_CONFIG]: /usr/bin/pkg-config
+ -----------
+ Called: `/usr/bin/pkg-config --modversion cairo` -> 1
+ stderr:
+ Package cairo was not found in the pkg-config search path.
+ Perhaps you should add the directory containing `cairo.pc'
+ to the PKG_CONFIG_PATH environment variable
+ Package 'cairo', required by 'virtual:world', not found
+ -----------
+ CMake binary for host machine is not cached
+ CMake binary missing from cross or native file, or env var undefined.
+ Trying a default CMake fallback at cmake
+ Found CMake: /usr/local/bin/cmake (3.31.6)
+ Extracting basic cmake information
+ CMake Toolchain: Calling CMake once to generate the compiler state
+ Calling CMake (['/usr/local/bin/cmake']) in /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/__CMake_compiler_info__ with:
+ - "--trace-expand"
+ - "--trace-format=json-v1"
+ - "--no-warn-unused-cli"
+ - "--trace-redirect=cmake_trace.txt"
+ - "-G"
+ - "Ninja"
+ - "-DCMAKE_TOOLCHAIN_FILE=/tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/__CMake_compiler_info__/CMakeMesonTempToolchainFile.cmake"
+ - "."
+ CMake trace warning: add_executable() non imported executables are not supported
+ CMake TRACE: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/__CMake_compiler_info__/CMakeFiles/CMakeScratch/TryCompile-5br5r6/CMakeLists.txt:22 add_executable(['cmTC_68a78'])
+ CMake trace warning: target_link_libraries() TARGET cmTC_68a78 not found
+ CMake TRACE: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/__CMake_compiler_info__/CMakeFiles/CMakeScratch/TryCompile-5br5r6/CMakeLists.txt:28 target_link_libraries(['cmTC_68a78', ''])
+ !meson_ci!/ci_include "/tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/cmake_cairo/CMakeMesonToolchainFile.cmake"
+ Try CMake generator: auto
+ !meson_ci!/ci_include "/tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/cmake_cairo/CMakeLists.txt"
+ Calling CMake (['/usr/local/bin/cmake']) in /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/cmake_cairo with:
+ - "--trace-expand"
+ - "--trace-format=json-v1"
+ - "--no-warn-unused-cli"
+ - "--trace-redirect=cmake_trace.txt"
+ - "-DCMAKE_TOOLCHAIN_FILE=/tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/cmake_cairo/CMakeMesonToolchainFile.cmake"
+ - "."
+ -- Module search paths: ['/', '/opt', '/usr', '/usr/local']
+ -- CMake root: /usr/local/share/cmake-3.31
+ -- CMake architectures: ['x86_64-linux-gnu']
+ -- CMake lib search paths: ['lib', 'lib32', 'lib64', 'libx32', 'share', '', 'lib/x86_64-linux-gnu']
+ Preliminary CMake check failed. Aborting.
+ Run-time dependency cairo found: NO (tried pkg-config and cmake)
+
+ ../cairo/meson.build:31:12: ERROR: Dependency "cairo" not found (tried pkg-config and cmake)
+
+ ::endgroup::
+
+ [end of output]
+
+ note: This error originates from a subprocess, and is likely not a problem with pip.
+error: metadata-generation-failed
+
+× Encountered error while generating package metadata.
+╰─> See above for output.
+
+note: This is an issue with the package mentioned above, not pip.
+hint: See above for details.
diff --git a/filer/admin/common_admin.py b/filer/admin/common_admin.py
index c6ee96971..4e34716cc 100644
--- a/filer/admin/common_admin.py
+++ b/filer/admin/common_admin.py
@@ -7,8 +7,8 @@
from django.http import HttpResponseRedirect
from filer.models import Folder, File
-from filer.admin.tools import (has_admin_role, has_role_on_site,
- has_multi_file_action_permission)
+from filer.utils.cms_roles import has_admin_role, has_role_on_site
+from filer.admin.tools import has_multi_file_action_permission
from filer.views import (popup_param, selectfolder_param, popup_status,
selectfolder_status, current_site_param,
get_param_from_request)
diff --git a/filer/admin/tools.py b/filer/admin/tools.py
index d7eeceebf..66a75dd8c 100644
--- a/filer/admin/tools.py
+++ b/filer/admin/tools.py
@@ -171,3 +171,45 @@ def folders_available(current_site, user, folders_qs):
visible = core_folders | shared_folders | accessible_site_folders
return folders_qs.filter(visible).distinct()
+
+
+def has_multi_file_action_permission(request, files, folders):
+ """PBS: Check permissions for multi-file actions (move/copy/delete)."""
+ from ..utils.cms_roles import (
+ has_admin_role,
+ get_admin_sites_for_user,
+ get_sites_for_user,
+ )
+ # unfiled files can be moved/deleted so better to just exclude them
+ files = files.exclude(folder__isnull=True)
+ user = request.user
+
+ if files.readonly(user).exists() or folders.readonly(user).exists():
+ return False
+ if user.is_superuser:
+ return True
+
+ if files.restricted(user).exists():
+ return False
+
+ if folders.restricted_descendants(user).exists():
+ return False
+
+ # only superusers can move/delete files/folders with no site ownership
+ if (files.filter(folder__site__isnull=True).exists() or
+ folders.filter(site__isnull=True).exists()):
+ return False
+
+ _exists_root_folders = folders.filter(parent__isnull=True).exists()
+ if _exists_root_folders:
+ if not has_admin_role(user):
+ return False
+ sites_allowed = [s.id for s in get_admin_sites_for_user(user)]
+ else:
+ sites_allowed = get_sites_for_user(user)
+
+ if (files.exclude(folder__site__in=sites_allowed).exists() or
+ folders.exclude(site__in=sites_allowed).exists()):
+ return False
+
+ return True
diff --git a/filer/fields/file.py b/filer/fields/file.py
index 0223440b9..7fb4bf6b8 100644
--- a/filer/fields/file.py
+++ b/filer/fields/file.py
@@ -135,3 +135,11 @@ def formfield(self, **kwargs):
}
defaults.update(kwargs)
return super().formfield(**defaults)
+
+
+# PBS-specific: widget that doesn't show clear checkbox
+from django.contrib.admin import widgets as django_widgets
+
+
+class NonClearableFileInput(django_widgets.AdminFileWidget):
+ template_with_clear = ''
diff --git a/filer/migrations/0008_thumbnailoption_alter_image_options_and_more.py b/filer/migrations/0008_thumbnailoption_alter_image_options_and_more.py
new file mode 100644
index 000000000..f6196990c
--- /dev/null
+++ b/filer/migrations/0008_thumbnailoption_alter_image_options_and_more.py
@@ -0,0 +1,137 @@
+# Generated by Django 5.1.15 on 2026-04-30 04:52
+
+import django.db.models.deletion
+import filer.fields.multistorage_file
+import filer.models.filemodels
+from django.conf import settings
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('auth', '0012_alter_user_first_name_max_length'),
+ ('contenttypes', '0002_remove_content_type_name'),
+ ('filer', '0007_filer_file_size'),
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='ThumbnailOption',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('name', models.CharField(max_length=100, verbose_name='name')),
+ ('width', models.IntegerField(help_text='width in pixel.', verbose_name='width')),
+ ('height', models.IntegerField(help_text='height in pixel.', verbose_name='height')),
+ ('crop', models.BooleanField(default=True, verbose_name='crop')),
+ ('upscale', models.BooleanField(default=True, verbose_name='upscale')),
+ ],
+ options={
+ 'verbose_name': 'thumbnail option',
+ 'verbose_name_plural': 'thumbnail options',
+ 'ordering': ('width', 'height'),
+ },
+ ),
+ migrations.AlterModelOptions(
+ name='image',
+ options={'default_manager_name': 'objects', 'verbose_name': 'image', 'verbose_name_plural': 'images'},
+ ),
+ migrations.RemoveField(
+ model_name='image',
+ name='default_credit',
+ ),
+ migrations.AddField(
+ model_name='file',
+ name='mime_type',
+ field=models.CharField(default='application/octet-stream', help_text='MIME type of uploaded content', max_length=255, validators=[filer.models.filemodels.mimetype_validator]),
+ ),
+ migrations.AddField(
+ model_name='image',
+ name='_transparent',
+ field=models.BooleanField(default=False),
+ ),
+ migrations.AlterField(
+ model_name='file',
+ name='_file_size',
+ field=models.BigIntegerField(blank=True, null=True, verbose_name='file size'),
+ ),
+ migrations.AlterField(
+ model_name='file',
+ name='file',
+ field=filer.fields.multistorage_file.MultiStorageFileField(blank=True, db_index=True, max_length=1024, null=True, upload_to=filer.fields.multistorage_file.generate_filename_multistorage, verbose_name='file'),
+ ),
+ migrations.AlterField(
+ model_name='file',
+ name='owner',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='owned_%(class)ss', to=settings.AUTH_USER_MODEL, verbose_name='owner'),
+ ),
+ migrations.AlterField(
+ model_name='file',
+ name='polymorphic_ctype',
+ field=models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_%(app_label)s.%(class)s_set+', to='contenttypes.contenttype'),
+ ),
+ migrations.AlterField(
+ model_name='folder',
+ name='level',
+ field=models.PositiveIntegerField(editable=False),
+ ),
+ migrations.AlterField(
+ model_name='folder',
+ name='lft',
+ field=models.PositiveIntegerField(editable=False),
+ ),
+ migrations.AlterField(
+ model_name='folder',
+ name='rght',
+ field=models.PositiveIntegerField(editable=False),
+ ),
+ migrations.AlterField(
+ model_name='image',
+ name='_height',
+ field=models.FloatField(blank=True, null=True),
+ ),
+ migrations.AlterField(
+ model_name='image',
+ name='_width',
+ field=models.FloatField(blank=True, null=True),
+ ),
+ migrations.AlterField(
+ model_name='image',
+ name='default_alt_text',
+ field=models.CharField(blank=True, max_length=255, null=True, verbose_name='default alt text'),
+ ),
+ migrations.AlterField(
+ model_name='image',
+ name='default_caption',
+ field=models.CharField(blank=True, max_length=255, null=True, verbose_name='default caption'),
+ ),
+ migrations.AlterField(
+ model_name='image',
+ name='file_ptr',
+ field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, related_name='%(app_label)s_%(class)s_file', serialize=False, to='filer.file'),
+ ),
+ migrations.AlterField(
+ model_name='image',
+ name='subject_location',
+ field=models.CharField(blank=True, default='', max_length=64, verbose_name='subject location'),
+ ),
+ migrations.CreateModel(
+ name='FolderPermission',
+ fields=[
+ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('type', models.SmallIntegerField(choices=[(0, 'all items'), (1, 'this item only'), (2, 'this item and all children')], default=0, verbose_name='type')),
+ ('everybody', models.BooleanField(default=False, verbose_name='everybody')),
+ ('can_read', models.SmallIntegerField(blank=True, choices=[(None, 'inherit'), (1, 'allow'), (0, 'deny')], default=None, null=True, verbose_name='can read')),
+ ('can_edit', models.SmallIntegerField(blank=True, choices=[(None, 'inherit'), (1, 'allow'), (0, 'deny')], default=None, null=True, verbose_name='can edit')),
+ ('can_add_children', models.SmallIntegerField(blank=True, choices=[(None, 'inherit'), (1, 'allow'), (0, 'deny')], default=None, null=True, verbose_name='can add children')),
+ ('folder', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='filer.folder', verbose_name='folder')),
+ ('group', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='filer_folder_permissions', to='auth.group', verbose_name='group')),
+ ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='filer_folder_permissions', to=settings.AUTH_USER_MODEL, verbose_name='user')),
+ ],
+ options={
+ 'verbose_name': 'folder permission',
+ 'verbose_name_plural': 'folder permissions',
+ },
+ ),
+ ]
diff --git a/filer/utils/multi_model_qs.py b/filer/utils/multi_model_qs.py
new file mode 100644
index 000000000..e87227ab5
--- /dev/null
+++ b/filer/utils/multi_model_qs.py
@@ -0,0 +1,49 @@
+from functools import reduce
+
+
+class MultiMoldelQuerysetChain(object):
+ """
+ Allows passing a list of different models querysets to a paginator without
+ transforming them into one list.
+ """
+
+ def __init__(self, qsets):
+ self._qsets = qsets
+ self._offsets = []
+ offset = 0
+ for q in qsets:
+ self._offsets.append(offset)
+ offset += q.count()
+ self.length = offset
+
+ def __getitem__(self, key):
+ if isinstance(key, slice):
+ return self._get_slice(key)
+ elif isinstance(key, int):
+ return self._get_item(key)
+ else:
+ raise TypeError("QuerysetChain index must be int or slice not {}".format(type(key)))
+
+ def _get_item(self, key):
+ if key < 0:
+ raise IndexError("index out of bounds {}".format(key))
+ for index, offset in enumerate(self._offsets):
+ qset = self._qsets[index]
+ if key-offset < qset.count():
+ return qset[key-offset]
+ raise IndexError("index out of bounds {}".format(key))
+
+ def _get_slice(self, key):
+ assert key.start is not None
+ assert key.stop is not None
+ assert key.step is None
+ slices = (self.slice(qs, key.start-offset, key.stop-offset)
+ for qs, offset in zip(self._qsets, self._offsets))
+ return reduce(lambda s1, s2: s1+s2, slices)
+
+ def slice(self, qset, start, stop):
+ start, stop = max(start, 0), max(stop, 0)
+ return list(qset[start:stop])
+
+ def __len__(self):
+ return self.length
From ea8f30039fce6324c741c71303ec732737e389af Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Thu, 30 Apr 2026 09:59:44 +0000
Subject: [PATCH 5/6] Phase 9-10: Adopt upstream templates/static/locale, fix
import issues, restore needed PBS utils
Agent-Logs-Url: https://github.com/pbs/django-filer/sessions/cad30a66-0889-48ba-bb3e-2ab3bf2939b2
Co-authored-by: andreilupuleasa <124138251+andreilupuleasa@users.noreply.github.com>
---
=5.1 | 518 -------
filer/locale/ar/LC_MESSAGES/django.mo | Bin 0 -> 520 bytes
filer/locale/ar/LC_MESSAGES/django.po | 1245 +++++++++++++++
filer/locale/bg/LC_MESSAGES/django.mo | Bin 0 -> 440 bytes
filer/locale/bg/LC_MESSAGES/django.po | 1224 +++++++++++++++
filer/locale/ca/LC_MESSAGES/django.mo | Bin 0 -> 16627 bytes
filer/locale/ca/LC_MESSAGES/django.po | 1261 ++++++++++++++++
filer/locale/cs/LC_MESSAGES/django.mo | Bin 0 -> 16442 bytes
filer/locale/cs/LC_MESSAGES/django.po | 1265 ++++++++++++++++
filer/locale/de/LC_MESSAGES/django.mo | Bin 0 -> 21636 bytes
filer/locale/de/LC_MESSAGES/django.po | 1244 +++++++++++++++
filer/locale/en/LC_MESSAGES/django.mo | Bin 458 -> 15724 bytes
filer/locale/en/LC_MESSAGES/django.po | 1341 +++++++++++------
filer/locale/es/LC_MESSAGES/django.mo | Bin 0 -> 20427 bytes
filer/locale/es/LC_MESSAGES/django.po | 1296 ++++++++++++++++
filer/locale/et/LC_MESSAGES/django.mo | Bin 479 -> 5456 bytes
filer/locale/et/LC_MESSAGES/django.po | 1150 +++++++++-----
filer/locale/eu/LC_MESSAGES/django.mo | Bin 0 -> 11064 bytes
filer/locale/eu/LC_MESSAGES/django.po | 1241 +++++++++++++++
filer/locale/fa/LC_MESSAGES/django.mo | Bin 0 -> 21668 bytes
filer/locale/fa/LC_MESSAGES/django.po | 1045 +++++++++++++
filer/locale/fi/LC_MESSAGES/django.mo | Bin 0 -> 16713 bytes
filer/locale/fi/LC_MESSAGES/django.po | 1259 ++++++++++++++++
filer/locale/fr/LC_MESSAGES/django.mo | Bin 16758 -> 22139 bytes
filer/locale/fr/LC_MESSAGES/django.po | 1292 ++++++++++------
filer/locale/gl/LC_MESSAGES/django.mo | Bin 0 -> 3830 bytes
filer/locale/gl/LC_MESSAGES/django.po | 1228 +++++++++++++++
filer/locale/he/LC_MESSAGES/django.mo | Bin 0 -> 14347 bytes
filer/locale/he/LC_MESSAGES/django.po | 1249 +++++++++++++++
filer/locale/hr/LC_MESSAGES/django.mo | Bin 0 -> 13374 bytes
filer/locale/hr/LC_MESSAGES/django.po | 1265 ++++++++++++++++
filer/locale/hu/LC_MESSAGES/django.mo | Bin 0 -> 16819 bytes
filer/locale/hu/LC_MESSAGES/django.po | 1258 ++++++++++++++++
filer/locale/it/LC_MESSAGES/django.mo | Bin 16237 -> 17229 bytes
filer/locale/it/LC_MESSAGES/django.po | 1189 +++++++++------
filer/locale/ja/LC_MESSAGES/django.mo | Bin 0 -> 432 bytes
filer/locale/ja/LC_MESSAGES/django.po | 1219 +++++++++++++++
filer/locale/ja_JP/LC_MESSAGES/django.mo | Bin 0 -> 446 bytes
filer/locale/ja_JP/LC_MESSAGES/django.po | 1219 +++++++++++++++
filer/locale/lt/LC_MESSAGES/django.mo | Bin 0 -> 17336 bytes
filer/locale/lt/LC_MESSAGES/django.po | 1273 ++++++++++++++++
filer/locale/lt_LT/LC_MESSAGES/django.mo | Bin 0 -> 590 bytes
filer/locale/lt_LT/LC_MESSAGES/django.po | 1236 +++++++++++++++
filer/locale/nb/LC_MESSAGES/django.mo | Bin 0 -> 12700 bytes
filer/locale/nb/LC_MESSAGES/django.po | 1256 +++++++++++++++
filer/locale/nl_NL/LC_MESSAGES/django.mo | Bin 0 -> 21260 bytes
filer/locale/nl_NL/LC_MESSAGES/django.po | 1245 +++++++++++++++
filer/locale/nn/LC_MESSAGES/django.mo | Bin 0 -> 12594 bytes
filer/locale/nn/LC_MESSAGES/django.po | 1253 +++++++++++++++
filer/locale/pl/LC_MESSAGES/django.mo | Bin 4616 -> 17155 bytes
filer/locale/pl/LC_MESSAGES/django.po | 1299 ++++++++++------
filer/locale/pt/LC_MESSAGES/django.mo | Bin 0 -> 492 bytes
filer/locale/pt/LC_MESSAGES/django.po | 1230 +++++++++++++++
filer/locale/pt_BR/LC_MESSAGES/django.mo | Bin 5630 -> 17504 bytes
filer/locale/pt_BR/LC_MESSAGES/django.po | 1284 ++++++++++------
filer/locale/ru/LC_MESSAGES/django.mo | Bin 0 -> 21137 bytes
filer/locale/ru/LC_MESSAGES/django.po | 1263 ++++++++++++++++
filer/locale/sk/LC_MESSAGES/django.mo | Bin 0 -> 512 bytes
filer/locale/sk/LC_MESSAGES/django.po | 1235 +++++++++++++++
filer/locale/tr/LC_MESSAGES/django.mo | Bin 0 -> 7360 bytes
filer/locale/tr/LC_MESSAGES/django.po | 1230 +++++++++++++++
filer/locale/vi_VN/LC_MESSAGES/django.mo | Bin 0 -> 18089 bytes
filer/locale/vi_VN/LC_MESSAGES/django.po | 1252 +++++++++++++++
filer/locale/zh-Hans/LC_MESSAGES/django.mo | Bin 0 -> 15264 bytes
filer/locale/zh-Hans/LC_MESSAGES/django.po | 1208 +++++++++++++++
filer/locale/zh/LC_MESSAGES/django.mo | Bin 0 -> 431 bytes
filer/locale/zh/LC_MESSAGES/django.po | 1219 +++++++++++++++
filer/locale/zh_CN/LC_MESSAGES/django.mo | Bin 0 -> 445 bytes
filer/locale/zh_CN/LC_MESSAGES/django.po | 1219 +++++++++++++++
filer/locale/zh_TW/LC_MESSAGES/django.mo | Bin 0 -> 446 bytes
filer/locale/zh_TW/LC_MESSAGES/django.po | 1219 +++++++++++++++
.../filer/css/admin_filer.cms.icons.css | 1 +
filer/static/filer/css/admin_filer.css | 3 +
.../static/filer/css/admin_filer.fa.icons.css | 4 +
.../filer/css/admin_folderpermissions.css | 6 +
.../css/maps/admin_filer.cms.icons.css.map | 1 +
.../static/filer/css/maps/admin_filer.css.map | 1 +
.../css/maps/admin_filer.fa.icons.css.map | 1 +
filer/static/filer/fonts/FontAwesome.otf | Bin 0 -> 106260 bytes
.../filer/fonts/django-filer-iconfont.eot | Bin 0 -> 3860 bytes
.../filer/fonts/django-filer-iconfont.svg | 51 +
.../filer/fonts/django-filer-iconfont.ttf | Bin 0 -> 3640 bytes
.../filer/fonts/django-filer-iconfont.woff | Bin 0 -> 2224 bytes
.../filer/fonts/django-filer-iconfont.woff2 | Bin 0 -> 1680 bytes
.../filer/fonts/fontawesome-webfont.eot | Bin 0 -> 68875 bytes
.../filer/fonts/fontawesome-webfont.svg | 640 ++++++++
.../filer/fonts/fontawesome-webfont.ttf | Bin 0 -> 138204 bytes
.../filer/fonts/fontawesome-webfont.woff | Bin 0 -> 81284 bytes
.../filer/fonts/fontawesome-webfont.woff2 | Bin 0 -> 64464 bytes
filer/static/filer/fonts/src/arrow-down.svg | 41 +
filer/static/filer/fonts/src/caret-down.svg | 3 +
.../static/filer/fonts/src/chevron-right.svg | 3 +
filer/static/filer/fonts/src/download.svg | 4 +
filer/static/filer/fonts/src/expand.svg | 3 +
filer/static/filer/fonts/src/link.svg | 4 +
.../static/filer/fonts/src/move-to-folder.svg | 3 +
filer/static/filer/fonts/src/picture.svg | 4 +
.../filer/fonts/src/remove-selection.svg | 3 +
filer/static/filer/fonts/src/select.svg | 3 +
filer/static/filer/fonts/src/th-large.svg | 3 +
filer/static/filer/fonts/src/th-list.svg | 5 +
filer/static/filer/fonts/src/upload.svg | 3 +
filer/static/filer/icons/cloud-up.svg | 8 +
filer/static/filer/icons/file-audio.svg | 11 +
filer/static/filer/icons/file-empty.svg | 8 +
filer/static/filer/icons/file-font.svg | 11 +
filer/static/filer/icons/file-missing.svg | 84 ++
filer/static/filer/icons/file-pdf.svg | 11 +
filer/static/filer/icons/file-picture.svg | 11 +
filer/static/filer/icons/file-unknown.svg | 69 +
filer/static/filer/icons/file-video.svg | 8 +
filer/static/filer/icons/file-zip.svg | 14 +
filer/static/filer/icons/folder-dropdown.svg | 85 ++
filer/static/filer/icons/folder-root.svg | 10 +
filer/static/filer/icons/folder-unfiled.svg | 77 +
filer/static/filer/icons/folder.svg | 9 +
filer/static/filer/img/button-bg.gif | Bin 0 -> 224 bytes
filer/static/filer/img/icon_changelink.gif | Bin 0 -> 1178 bytes
filer/static/filer/img/icon_deletelink.gif | Bin 0 -> 1200 bytes
.../static/filer/js/addons/copy-move-files.js | 32 +
filer/static/filer/js/addons/dropdown-menu.js | 249 +++
filer/static/filer/js/addons/dropzone.init.js | 234 +++
.../filer/js/addons/filer_popup_response.js | 22 +
filer/static/filer/js/addons/focal-point.js | 253 ++++
.../static/filer/js/addons/popup_handling.js | 137 ++
.../static/filer/js/addons/table-dropzone.js | 282 ++++
filer/static/filer/js/addons/toggler.js | 82 +
filer/static/filer/js/addons/tooltip.js | 40 +
filer/static/filer/js/addons/upload-button.js | 242 +++
filer/static/filer/js/addons/widget.js | 56 +
filer/static/filer/js/base.js | 417 +++++
.../filer/js/widgets/admin-file-widget.js | 30 +
.../filer/js/widgets/admin-folder-widget.js | 64 +
filer/templates/admin/filer/actions.html | 38 +-
filer/templates/admin/filer/base_site.html | 19 +-
filer/templates/admin/filer/breadcrumbs.html | 55 +-
filer/templates/admin/filer/change_form.html | 86 +-
.../admin/filer/delete_confirmation.html | 39 +-
.../delete_selected_files_confirmation.html | 80 +-
.../templates/admin/filer/dismiss_popup.html | 8 +
.../admin/filer/file/change_form.html | 23 +
.../admin/filer/file/popup_response.html | 10 +
.../admin/filer/folder/change_form.html | 51 +-
.../filer/folder/choose_copy_destination.html | 86 +-
.../folder/choose_images_resize_options.html | 64 +-
.../filer/folder/choose_move_destination.html | 85 +-
.../filer/folder/choose_rename_format.html | 104 +-
.../admin/filer/folder/directory_listing.html | 341 +++--
.../filer/folder/directory_table_list.html | 256 ++++
.../folder/directory_thumbnail_list.html | 228 +++
.../admin/filer/folder/legacy_listing.html | 265 ++++
.../filer/folder/list_type_switcher.html | 1 +
.../admin/filer/folder/new_folder_form.html | 67 +-
.../admin/filer/image/change_form.html | 45 +-
filer/templates/admin/filer/image/expand.html | 28 +
.../admin/filer/templatetags/file_icon.html | 38 +
.../filer/tools/clipboard/clipboard.html | 110 +-
.../tools/clipboard/clipboard_item_rows.html | 32 +-
.../admin/filer/tools/detail_info.html | 55 +
.../admin/filer/tools/search_form.html | 50 +-
.../admin/filer/widgets/admin_file.html | 100 +-
.../admin/filer/widgets/admin_folder.html | 44 +-
filer/tests/foo_file.jpg | Bin 0 -> 11200 bytes
filer/tests/image_name | Bin 0 -> 11200 bytes
filer/tests/new_one.jpg | Bin 0 -> 1119 bytes
filer/tests/new_three.jpg | Bin 0 -> 1119 bytes
...sttesttesttesttesttesttesttesttesttest.jpg | Bin 0 -> 11200 bytes
filer/utils/files.py | 6 +-
filer/utils/zip.py | 26 +
test.zip | Bin 0 -> 2538 bytes
170 files changed, 45820 insertions(+), 3895 deletions(-)
delete mode 100644 =5.1
create mode 100644 filer/locale/ar/LC_MESSAGES/django.mo
create mode 100644 filer/locale/ar/LC_MESSAGES/django.po
create mode 100644 filer/locale/bg/LC_MESSAGES/django.mo
create mode 100644 filer/locale/bg/LC_MESSAGES/django.po
create mode 100644 filer/locale/ca/LC_MESSAGES/django.mo
create mode 100644 filer/locale/ca/LC_MESSAGES/django.po
create mode 100644 filer/locale/cs/LC_MESSAGES/django.mo
create mode 100644 filer/locale/cs/LC_MESSAGES/django.po
create mode 100644 filer/locale/de/LC_MESSAGES/django.mo
create mode 100644 filer/locale/de/LC_MESSAGES/django.po
create mode 100644 filer/locale/es/LC_MESSAGES/django.mo
create mode 100644 filer/locale/es/LC_MESSAGES/django.po
create mode 100644 filer/locale/eu/LC_MESSAGES/django.mo
create mode 100644 filer/locale/eu/LC_MESSAGES/django.po
create mode 100644 filer/locale/fa/LC_MESSAGES/django.mo
create mode 100644 filer/locale/fa/LC_MESSAGES/django.po
create mode 100644 filer/locale/fi/LC_MESSAGES/django.mo
create mode 100644 filer/locale/fi/LC_MESSAGES/django.po
create mode 100644 filer/locale/gl/LC_MESSAGES/django.mo
create mode 100644 filer/locale/gl/LC_MESSAGES/django.po
create mode 100644 filer/locale/he/LC_MESSAGES/django.mo
create mode 100644 filer/locale/he/LC_MESSAGES/django.po
create mode 100644 filer/locale/hr/LC_MESSAGES/django.mo
create mode 100644 filer/locale/hr/LC_MESSAGES/django.po
create mode 100644 filer/locale/hu/LC_MESSAGES/django.mo
create mode 100644 filer/locale/hu/LC_MESSAGES/django.po
create mode 100644 filer/locale/ja/LC_MESSAGES/django.mo
create mode 100644 filer/locale/ja/LC_MESSAGES/django.po
create mode 100644 filer/locale/ja_JP/LC_MESSAGES/django.mo
create mode 100644 filer/locale/ja_JP/LC_MESSAGES/django.po
create mode 100644 filer/locale/lt/LC_MESSAGES/django.mo
create mode 100644 filer/locale/lt/LC_MESSAGES/django.po
create mode 100644 filer/locale/lt_LT/LC_MESSAGES/django.mo
create mode 100644 filer/locale/lt_LT/LC_MESSAGES/django.po
create mode 100644 filer/locale/nb/LC_MESSAGES/django.mo
create mode 100644 filer/locale/nb/LC_MESSAGES/django.po
create mode 100644 filer/locale/nl_NL/LC_MESSAGES/django.mo
create mode 100644 filer/locale/nl_NL/LC_MESSAGES/django.po
create mode 100644 filer/locale/nn/LC_MESSAGES/django.mo
create mode 100644 filer/locale/nn/LC_MESSAGES/django.po
create mode 100644 filer/locale/pt/LC_MESSAGES/django.mo
create mode 100644 filer/locale/pt/LC_MESSAGES/django.po
create mode 100644 filer/locale/ru/LC_MESSAGES/django.mo
create mode 100644 filer/locale/ru/LC_MESSAGES/django.po
create mode 100644 filer/locale/sk/LC_MESSAGES/django.mo
create mode 100644 filer/locale/sk/LC_MESSAGES/django.po
create mode 100644 filer/locale/tr/LC_MESSAGES/django.mo
create mode 100644 filer/locale/tr/LC_MESSAGES/django.po
create mode 100644 filer/locale/vi_VN/LC_MESSAGES/django.mo
create mode 100644 filer/locale/vi_VN/LC_MESSAGES/django.po
create mode 100644 filer/locale/zh-Hans/LC_MESSAGES/django.mo
create mode 100644 filer/locale/zh-Hans/LC_MESSAGES/django.po
create mode 100644 filer/locale/zh/LC_MESSAGES/django.mo
create mode 100644 filer/locale/zh/LC_MESSAGES/django.po
create mode 100644 filer/locale/zh_CN/LC_MESSAGES/django.mo
create mode 100644 filer/locale/zh_CN/LC_MESSAGES/django.po
create mode 100644 filer/locale/zh_TW/LC_MESSAGES/django.mo
create mode 100644 filer/locale/zh_TW/LC_MESSAGES/django.po
create mode 100644 filer/static/filer/css/admin_filer.cms.icons.css
create mode 100644 filer/static/filer/css/admin_filer.css
create mode 100644 filer/static/filer/css/admin_filer.fa.icons.css
create mode 100644 filer/static/filer/css/admin_folderpermissions.css
create mode 100644 filer/static/filer/css/maps/admin_filer.cms.icons.css.map
create mode 100644 filer/static/filer/css/maps/admin_filer.css.map
create mode 100644 filer/static/filer/css/maps/admin_filer.fa.icons.css.map
create mode 100644 filer/static/filer/fonts/FontAwesome.otf
create mode 100644 filer/static/filer/fonts/django-filer-iconfont.eot
create mode 100644 filer/static/filer/fonts/django-filer-iconfont.svg
create mode 100644 filer/static/filer/fonts/django-filer-iconfont.ttf
create mode 100644 filer/static/filer/fonts/django-filer-iconfont.woff
create mode 100644 filer/static/filer/fonts/django-filer-iconfont.woff2
create mode 100644 filer/static/filer/fonts/fontawesome-webfont.eot
create mode 100644 filer/static/filer/fonts/fontawesome-webfont.svg
create mode 100644 filer/static/filer/fonts/fontawesome-webfont.ttf
create mode 100644 filer/static/filer/fonts/fontawesome-webfont.woff
create mode 100644 filer/static/filer/fonts/fontawesome-webfont.woff2
create mode 100644 filer/static/filer/fonts/src/arrow-down.svg
create mode 100644 filer/static/filer/fonts/src/caret-down.svg
create mode 100644 filer/static/filer/fonts/src/chevron-right.svg
create mode 100644 filer/static/filer/fonts/src/download.svg
create mode 100644 filer/static/filer/fonts/src/expand.svg
create mode 100644 filer/static/filer/fonts/src/link.svg
create mode 100644 filer/static/filer/fonts/src/move-to-folder.svg
create mode 100644 filer/static/filer/fonts/src/picture.svg
create mode 100644 filer/static/filer/fonts/src/remove-selection.svg
create mode 100644 filer/static/filer/fonts/src/select.svg
create mode 100644 filer/static/filer/fonts/src/th-large.svg
create mode 100644 filer/static/filer/fonts/src/th-list.svg
create mode 100644 filer/static/filer/fonts/src/upload.svg
create mode 100644 filer/static/filer/icons/cloud-up.svg
create mode 100644 filer/static/filer/icons/file-audio.svg
create mode 100644 filer/static/filer/icons/file-empty.svg
create mode 100644 filer/static/filer/icons/file-font.svg
create mode 100644 filer/static/filer/icons/file-missing.svg
create mode 100644 filer/static/filer/icons/file-pdf.svg
create mode 100644 filer/static/filer/icons/file-picture.svg
create mode 100644 filer/static/filer/icons/file-unknown.svg
create mode 100644 filer/static/filer/icons/file-video.svg
create mode 100644 filer/static/filer/icons/file-zip.svg
create mode 100644 filer/static/filer/icons/folder-dropdown.svg
create mode 100644 filer/static/filer/icons/folder-root.svg
create mode 100644 filer/static/filer/icons/folder-unfiled.svg
create mode 100644 filer/static/filer/icons/folder.svg
create mode 100644 filer/static/filer/img/button-bg.gif
create mode 100644 filer/static/filer/img/icon_changelink.gif
create mode 100644 filer/static/filer/img/icon_deletelink.gif
create mode 100644 filer/static/filer/js/addons/copy-move-files.js
create mode 100644 filer/static/filer/js/addons/dropdown-menu.js
create mode 100644 filer/static/filer/js/addons/dropzone.init.js
create mode 100644 filer/static/filer/js/addons/filer_popup_response.js
create mode 100644 filer/static/filer/js/addons/focal-point.js
create mode 100644 filer/static/filer/js/addons/popup_handling.js
create mode 100644 filer/static/filer/js/addons/table-dropzone.js
create mode 100644 filer/static/filer/js/addons/toggler.js
create mode 100644 filer/static/filer/js/addons/tooltip.js
create mode 100644 filer/static/filer/js/addons/upload-button.js
create mode 100644 filer/static/filer/js/addons/widget.js
create mode 100644 filer/static/filer/js/base.js
create mode 100644 filer/static/filer/js/widgets/admin-file-widget.js
create mode 100644 filer/static/filer/js/widgets/admin-folder-widget.js
create mode 100644 filer/templates/admin/filer/dismiss_popup.html
create mode 100644 filer/templates/admin/filer/file/change_form.html
create mode 100644 filer/templates/admin/filer/file/popup_response.html
create mode 100644 filer/templates/admin/filer/folder/directory_table_list.html
create mode 100644 filer/templates/admin/filer/folder/directory_thumbnail_list.html
create mode 100644 filer/templates/admin/filer/folder/legacy_listing.html
create mode 100644 filer/templates/admin/filer/folder/list_type_switcher.html
create mode 100644 filer/templates/admin/filer/image/expand.html
create mode 100644 filer/templates/admin/filer/templatetags/file_icon.html
create mode 100644 filer/templates/admin/filer/tools/detail_info.html
create mode 100644 filer/tests/foo_file.jpg
create mode 100644 filer/tests/image_name
create mode 100644 filer/tests/new_one.jpg
create mode 100644 filer/tests/new_three.jpg
create mode 100644 filer/tests/testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttest.jpg
create mode 100644 filer/utils/zip.py
create mode 100644 test.zip
diff --git a/=5.1 b/=5.1
deleted file mode 100644
index 7488124a1..000000000
--- a/=5.1
+++ /dev/null
@@ -1,518 +0,0 @@
-Defaulting to user installation because normal site-packages is not writeable
-Collecting django
- Downloading django-6.0.4-py3-none-any.whl.metadata (3.9 kB)
-Collecting django-mptt
- Downloading django_mptt-0.18.0-py3-none-any.whl.metadata (5.3 kB)
-Collecting django-polymorphic
- Downloading django_polymorphic-4.11.2-py3-none-any.whl.metadata (7.7 kB)
-Collecting django-js-asset
- Downloading django_js_asset-3.1.2-py3-none-any.whl.metadata (6.4 kB)
-Collecting easy-thumbnails
- Downloading easy_thumbnails-2.10.1-py3-none-any.whl.metadata (15 kB)
-Collecting Unidecode
- Downloading Unidecode-1.4.0-py3-none-any.whl.metadata (13 kB)
-Collecting filetype
- Downloading filetype-1.2.0-py2.py3-none-any.whl.metadata (6.5 kB)
-Collecting svglib
- Downloading svglib-1.6.0-py3-none-any.whl.metadata (11 kB)
-Collecting Pillow
- Downloading pillow-12.2.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl.metadata (8.8 kB)
-Collecting asgiref>=3.9.1 (from django)
- Downloading asgiref-3.11.1-py3-none-any.whl.metadata (9.3 kB)
-Collecting sqlparse>=0.5.0 (from django)
- Downloading sqlparse-0.5.5-py3-none-any.whl.metadata (4.7 kB)
-Collecting typing-extensions>=4.12.0 (from django-polymorphic)
- Downloading typing_extensions-4.15.0-py3-none-any.whl.metadata (3.3 kB)
-Collecting cssselect2>=0.2.0 (from svglib)
- Downloading cssselect2-0.9.0-py3-none-any.whl.metadata (2.9 kB)
-Collecting lxml>=6.0.0 (from svglib)
- Downloading lxml-6.1.0-cp312-cp312-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl.metadata (4.0 kB)
-Collecting reportlab>=4.4.3 (from svglib)
- Downloading reportlab-4.5.0-py3-none-any.whl.metadata (1.7 kB)
-Collecting rlpycairo>=0.4.0 (from svglib)
- Downloading rlpycairo-0.4.0-py3-none-any.whl.metadata (4.9 kB)
-Collecting tinycss2>=0.6.0 (from svglib)
- Downloading tinycss2-1.5.1-py3-none-any.whl.metadata (3.0 kB)
-Collecting webencodings (from cssselect2>=0.2.0->svglib)
- Downloading webencodings-0.5.1-py2.py3-none-any.whl.metadata (2.1 kB)
-Collecting charset-normalizer (from reportlab>=4.4.3->svglib)
- Downloading charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl.metadata (40 kB)
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 40.9/40.9 kB 14.8 MB/s eta 0:00:00
-Collecting pycairo>=1.20.0 (from rlpycairo>=0.4.0->svglib)
- Downloading pycairo-1.29.0.tar.gz (665 kB)
- ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 665.9/665.9 kB 23.9 MB/s eta 0:00:00
- Installing build dependencies: started
- Installing build dependencies: finished with status 'done'
- Getting requirements to build wheel: started
- Getting requirements to build wheel: finished with status 'done'
- Preparing metadata (pyproject.toml): started
- Preparing metadata (pyproject.toml): finished with status 'error'
- error: subprocess-exited-with-error
-
- × Preparing metadata (pyproject.toml) did not run successfully.
- │ exit code: 1
- ╰─> [454 lines of output]
- + meson setup /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667 /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b -Dbuildtype=release -Db_ndebug=if-release -Db_vscrt=md -Dwheel=true -Dtests=false --native-file=/tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-python-native-file.ini
- The Meson build system
- Version: 1.11.1
- Source dir: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667
- Build dir: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b
- Build type: native build
- Project name: pycairo
- Project version: 1.29.0
- C compiler for the host machine: cc (gcc 13.3.0 "cc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0")
- C linker for the host machine: cc ld.bfd 2.42
- Host machine cpu family: x86_64
- Host machine cpu: x86_64
- Program python3 found: YES (/usr/bin/python3)
- Compiler for C supports arguments -Wall: YES
- Compiler for C supports arguments -Warray-bounds: YES
- Compiler for C supports arguments -Wcast-align: YES
- Compiler for C supports arguments -Wconversion: YES
- Compiler for C supports arguments -Wextra: YES
- Compiler for C supports arguments -Wformat=2: YES
- Compiler for C supports arguments -Wformat-nonliteral: YES
- Compiler for C supports arguments -Wformat-security: YES
- Compiler for C supports arguments -Wimplicit-function-declaration: YES
- Compiler for C supports arguments -Winit-self: YES
- Compiler for C supports arguments -Wmissing-format-attribute: YES
- Compiler for C supports arguments -Wmissing-noreturn: YES
- Compiler for C supports arguments -Wnested-externs: YES
- Compiler for C supports arguments -Wold-style-definition: YES
- Compiler for C supports arguments -Wpacked: YES
- Compiler for C supports arguments -Wpointer-arith: YES
- Compiler for C supports arguments -Wreturn-type: YES
- Compiler for C supports arguments -Wshadow: YES
- Compiler for C supports arguments -Wsign-compare: YES
- Compiler for C supports arguments -Wstrict-aliasing: YES
- Compiler for C supports arguments -Wundef: YES
- Compiler for C supports arguments -Wunused-but-set-variable: YES
- Compiler for C supports arguments -Wswitch-default: YES
- Compiler for C supports arguments -Wno-missing-field-initializers: YES
- Compiler for C supports arguments -Wno-unused-parameter: YES
- Compiler for C supports arguments -fno-strict-aliasing: YES
- Compiler for C supports arguments -fvisibility=hidden: YES
- Found pkg-config: YES (/usr/bin/pkg-config) 1.8.1
- Found CMake: /usr/local/bin/cmake (3.31.6)
- Run-time dependency cairo found: NO (tried pkg-config and cmake)
-
- ../cairo/meson.build:31:12: ERROR: Dependency "cairo" not found (tried pkg-config and cmake)
-
- A full log can be found at /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-logs/meson-log.txt
- ::group::==== CI platform detected, click here for meson-log.txt contents. ====
- Build started at 2026-04-30T09:49:25.394376
- Main binary: /usr/bin/python3
- Build Options: -Dbuildtype=release -Db_ndebug=if-release -Db_vscrt=md -Dwheel=true -Dtests=false --native-file=/tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-python-native-file.ini
- Python system: Linux
- The Meson build system
- Version: 1.11.1
- Source dir: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667
- Build dir: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b
- Build type: native build
- Project name: pycairo
- Project version: 1.29.0
- -----------
- Detecting compiler via: `cc --version` -> 0
- stdout:
- cc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0
- Copyright (C) 2023 Free Software Foundation, Inc.
- This is free software; see the source for copying conditions. There is NO
- warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- -----------
- Running command: -cpp -x c -E -dM -
- -----
- -----------
- Detecting linker via: `cc -Wl,--version` -> 0
- stdout:
- GNU ld (GNU Binutils for Ubuntu) 2.42
- Copyright (C) 2024 Free Software Foundation, Inc.
- This program is free software; you may redistribute it under the terms of
- the GNU General Public License version 3 or (at your option) a later version.
- This program has absolutely no warranty.
- -----------
- stderr:
- collect2 version 13.3.0
- /usr/bin/ld -plugin /usr/libexec/gcc/x86_64-linux-gnu/13/liblto_plugin.so -plugin-opt=/usr/libexec/gcc/x86_64-linux-gnu/13/lto-wrapper -plugin-opt=-fresolution=/tmp/ccxnZMDU.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -z now -z relro /usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/13/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/13 -L/usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/13/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/13/../../.. --version -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state /usr/lib/gcc/x86_64-linux-gnu/13/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu/crtn.o
- -----------
- Sanity check compiler command line: cc -D_FILE_OFFSET_BITS=64 -o sanity_check_for_c.exe sanity_check_for_c.c -D_FILE_OFFSET_BITS=64
- Sanity check compile stdout:
-
- -----
- Sanity check compile stderr:
-
- -----
- Sanity check built target output for host machine c compiler
- -- Running test binary command: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/sanity_check_for_c.exe
- -----------
- Sanity check: `/tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/sanity_check_for_c.exe` -> 0
- -- stdout:
-
- -- stderr:
-
- -- returncode: 0
- C compiler for the host machine: cc (gcc 13.3.0 "cc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0")
- C linker for the host machine: cc ld.bfd 2.42
- -----------
- Detecting archiver via: `gcc-ar --version` -> 0
- stdout:
- GNU ar (GNU Binutils for Ubuntu) 2.42
- Copyright (C) 2024 Free Software Foundation, Inc.
- This program is free software; you may redistribute it under the terms of
- the GNU General Public License version 3 or (at your option) any later version.
- This program has absolutely no warranty.
- -----------
- -----------
- Detecting compiler via: `cc --version` -> 0
- stdout:
- cc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0
- Copyright (C) 2023 Free Software Foundation, Inc.
- This is free software; see the source for copying conditions. There is NO
- warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
- -----------
- Running command: -cpp -x c -E -dM -
- -----
- -----------
- Detecting linker via: `cc -Wl,--version` -> 0
- stdout:
- GNU ld (GNU Binutils for Ubuntu) 2.42
- Copyright (C) 2024 Free Software Foundation, Inc.
- This program is free software; you may redistribute it under the terms of
- the GNU General Public License version 3 or (at your option) a later version.
- This program has absolutely no warranty.
- -----------
- stderr:
- collect2 version 13.3.0
- /usr/bin/ld -plugin /usr/libexec/gcc/x86_64-linux-gnu/13/liblto_plugin.so -plugin-opt=/usr/libexec/gcc/x86_64-linux-gnu/13/lto-wrapper -plugin-opt=-fresolution=/tmp/ccxwrsCc.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -z now -z relro /usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/13/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/13 -L/usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/13/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/13/../../.. --version -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state /usr/lib/gcc/x86_64-linux-gnu/13/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/13/../../../x86_64-linux-gnu/crtn.o
- -----------
- Sanity check compiler command line: cc -D_FILE_OFFSET_BITS=64 -o sanity_check_for_c.exe sanity_check_for_c.c -D_FILE_OFFSET_BITS=64
- Sanity check compile stdout:
-
- -----
- Sanity check compile stderr:
-
- -----
- Sanity check built target output for build machine c compiler
- -- Running test binary command: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/sanity_check_for_c.exe
- -----------
- Sanity check: `/tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/sanity_check_for_c.exe` -> 0
- -- stdout:
-
- -- stderr:
-
- -- returncode: 0
- C compiler for the build machine: cc (gcc 13.3.0 "cc (Ubuntu 13.3.0-6ubuntu2~24.04.1) 13.3.0")
- C linker for the build machine: cc ld.bfd 2.42
- Build machine cpu family: x86_64
- Build machine cpu: x86_64
- Host machine cpu family: x86_64
- Host machine cpu: x86_64
- Target machine cpu family: x86_64
- Target machine cpu: x86_64
- Program python3 found: YES (/usr/bin/python3)
- Running compile:
- Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpq10ufb7l
- Code:
- extern int i;
- int i;
-
- -----------
- Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpq10ufb7l/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpq10ufb7l/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wall` -> 0
- Compiler for C supports arguments -Wall: YES
- Running compile:
- Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp8qts2n5l
- Code:
- extern int i;
- int i;
-
- -----------
- Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp8qts2n5l/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp8qts2n5l/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Warray-bounds` -> 0
- Compiler for C supports arguments -Warray-bounds: YES
- Running compile:
- Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpg8nrnjvb
- Code:
- extern int i;
- int i;
-
- -----------
- Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpg8nrnjvb/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpg8nrnjvb/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wcast-align` -> 0
- Compiler for C supports arguments -Wcast-align: YES
- Running compile:
- Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpahi5_xug
- Code:
- extern int i;
- int i;
-
- -----------
- Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpahi5_xug/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpahi5_xug/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wconversion` -> 0
- Compiler for C supports arguments -Wconversion: YES
- Running compile:
- Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpvetn92u9
- Code:
- extern int i;
- int i;
-
- -----------
- Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpvetn92u9/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpvetn92u9/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wextra` -> 0
- Compiler for C supports arguments -Wextra: YES
- Running compile:
- Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp591nhuze
- Code:
- extern int i;
- int i;
-
- -----------
- Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp591nhuze/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp591nhuze/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wformat=2` -> 0
- Compiler for C supports arguments -Wformat=2: YES
- Running compile:
- Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpr5lad1fi
- Code:
- extern int i;
- int i;
-
- -----------
- Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpr5lad1fi/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpr5lad1fi/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wformat-nonliteral` -> 0
- Compiler for C supports arguments -Wformat-nonliteral: YES
- Running compile:
- Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpduqdxvjc
- Code:
- extern int i;
- int i;
-
- -----------
- Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpduqdxvjc/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpduqdxvjc/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wformat-security` -> 0
- Compiler for C supports arguments -Wformat-security: YES
- Running compile:
- Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpr2cg9qgk
- Code:
- extern int i;
- int i;
-
- -----------
- Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpr2cg9qgk/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpr2cg9qgk/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wimplicit-function-declaration` -> 0
- Compiler for C supports arguments -Wimplicit-function-declaration: YES
- Running compile:
- Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpv65xtow9
- Code:
- extern int i;
- int i;
-
- -----------
- Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpv65xtow9/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpv65xtow9/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Winit-self` -> 0
- Compiler for C supports arguments -Winit-self: YES
- Running compile:
- Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp82mly3km
- Code:
- extern int i;
- int i;
-
- -----------
- Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp82mly3km/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp82mly3km/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wmissing-format-attribute` -> 0
- Compiler for C supports arguments -Wmissing-format-attribute: YES
- Running compile:
- Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpfaswcrni
- Code:
- extern int i;
- int i;
-
- -----------
- Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpfaswcrni/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpfaswcrni/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wmissing-noreturn` -> 0
- Compiler for C supports arguments -Wmissing-noreturn: YES
- Running compile:
- Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmps5n97jul
- Code:
- extern int i;
- int i;
-
- -----------
- Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmps5n97jul/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmps5n97jul/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wnested-externs` -> 0
- Compiler for C supports arguments -Wnested-externs: YES
- Running compile:
- Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpkcvs47pl
- Code:
- extern int i;
- int i;
-
- -----------
- Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpkcvs47pl/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpkcvs47pl/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wold-style-definition` -> 0
- Compiler for C supports arguments -Wold-style-definition: YES
- Running compile:
- Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp089e1gxr
- Code:
- extern int i;
- int i;
-
- -----------
- Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp089e1gxr/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp089e1gxr/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wpacked` -> 0
- Compiler for C supports arguments -Wpacked: YES
- Running compile:
- Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp5vsuim0z
- Code:
- extern int i;
- int i;
-
- -----------
- Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp5vsuim0z/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp5vsuim0z/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wpointer-arith` -> 0
- Compiler for C supports arguments -Wpointer-arith: YES
- Running compile:
- Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmppw3tn4mm
- Code:
- extern int i;
- int i;
-
- -----------
- Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmppw3tn4mm/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmppw3tn4mm/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wreturn-type` -> 0
- Compiler for C supports arguments -Wreturn-type: YES
- Running compile:
- Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpdyednray
- Code:
- extern int i;
- int i;
-
- -----------
- Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpdyednray/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpdyednray/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wshadow` -> 0
- Compiler for C supports arguments -Wshadow: YES
- Running compile:
- Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmppzs14kt9
- Code:
- extern int i;
- int i;
-
- -----------
- Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmppzs14kt9/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmppzs14kt9/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wsign-compare` -> 0
- Compiler for C supports arguments -Wsign-compare: YES
- Running compile:
- Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpim1xzoce
- Code:
- extern int i;
- int i;
-
- -----------
- Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpim1xzoce/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpim1xzoce/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wstrict-aliasing` -> 0
- Compiler for C supports arguments -Wstrict-aliasing: YES
- Running compile:
- Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp2rcs89un
- Code:
- extern int i;
- int i;
-
- -----------
- Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp2rcs89un/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp2rcs89un/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wundef` -> 0
- Compiler for C supports arguments -Wundef: YES
- Running compile:
- Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpye9rfxe9
- Code:
- extern int i;
- int i;
-
- -----------
- Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpye9rfxe9/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpye9rfxe9/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wunused-but-set-variable` -> 0
- Compiler for C supports arguments -Wunused-but-set-variable: YES
- Running compile:
- Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpgt0j7j7o
- Code:
- extern int i;
- int i;
-
- -----------
- Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpgt0j7j7o/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpgt0j7j7o/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wswitch-default` -> 0
- Compiler for C supports arguments -Wswitch-default: YES
- Running compile:
- Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpuy8j9o8g
- Code:
- extern int i;
- int i;
-
- -----------
- Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpuy8j9o8g/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpuy8j9o8g/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wmissing-field-initializers -Wno-missing-field-initializers` -> 0
- Compiler for C supports arguments -Wno-missing-field-initializers: YES
- Running compile:
- Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp206a43mw
- Code:
- extern int i;
- int i;
-
- -----------
- Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp206a43mw/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp206a43mw/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -Wunused-parameter -Wno-unused-parameter` -> 0
- Compiler for C supports arguments -Wno-unused-parameter: YES
- Running compile:
- Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp98e0ru46
- Code:
- extern int i;
- int i;
-
- -----------
- Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp98e0ru46/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmp98e0ru46/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -fno-strict-aliasing` -> 0
- Compiler for C supports arguments -fno-strict-aliasing: YES
- Running compile:
- Working directory: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpz771h1xa
- Code:
- extern int i;
- int i;
-
- -----------
- Command line: `cc /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpz771h1xa/testfile.c -o /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/tmpz771h1xa/output.obj -c -D_FILE_OFFSET_BITS=64 -O0 -U_FORTIFY_SOURCE -fvisibility=hidden` -> 0
- Compiler for C supports arguments -fvisibility=hidden: YES
- Pkg-config binary missing from cross or native file, or env var undefined.
- Trying a default Pkg-config fallback at pkg-config
- Found pkg-config: YES (/usr/bin/pkg-config) 1.8.1
- Determining dependency 'cairo' with pkg-config executable '/usr/bin/pkg-config'
- env[PKG_CONFIG_PATH]:
- env[PKG_CONFIG]: /usr/bin/pkg-config
- -----------
- Called: `/usr/bin/pkg-config --modversion cairo` -> 1
- stderr:
- Package cairo was not found in the pkg-config search path.
- Perhaps you should add the directory containing `cairo.pc'
- to the PKG_CONFIG_PATH environment variable
- Package 'cairo', required by 'virtual:world', not found
- -----------
- CMake binary for host machine is not cached
- CMake binary missing from cross or native file, or env var undefined.
- Trying a default CMake fallback at cmake
- Found CMake: /usr/local/bin/cmake (3.31.6)
- Extracting basic cmake information
- CMake Toolchain: Calling CMake once to generate the compiler state
- Calling CMake (['/usr/local/bin/cmake']) in /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/__CMake_compiler_info__ with:
- - "--trace-expand"
- - "--trace-format=json-v1"
- - "--no-warn-unused-cli"
- - "--trace-redirect=cmake_trace.txt"
- - "-G"
- - "Ninja"
- - "-DCMAKE_TOOLCHAIN_FILE=/tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/__CMake_compiler_info__/CMakeMesonTempToolchainFile.cmake"
- - "."
- CMake trace warning: add_executable() non imported executables are not supported
- CMake TRACE: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/__CMake_compiler_info__/CMakeFiles/CMakeScratch/TryCompile-5br5r6/CMakeLists.txt:22 add_executable(['cmTC_68a78'])
- CMake trace warning: target_link_libraries() TARGET cmTC_68a78 not found
- CMake TRACE: /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/__CMake_compiler_info__/CMakeFiles/CMakeScratch/TryCompile-5br5r6/CMakeLists.txt:28 target_link_libraries(['cmTC_68a78', ''])
- !meson_ci!/ci_include "/tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/cmake_cairo/CMakeMesonToolchainFile.cmake"
- Try CMake generator: auto
- !meson_ci!/ci_include "/tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/cmake_cairo/CMakeLists.txt"
- Calling CMake (['/usr/local/bin/cmake']) in /tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/cmake_cairo with:
- - "--trace-expand"
- - "--trace-format=json-v1"
- - "--no-warn-unused-cli"
- - "--trace-redirect=cmake_trace.txt"
- - "-DCMAKE_TOOLCHAIN_FILE=/tmp/pip-install-zbza_hym/pycairo_ded28bf9d04b454bae7dcc031f1c1667/.mesonpy-cdely51b/meson-private/cmake_cairo/CMakeMesonToolchainFile.cmake"
- - "."
- -- Module search paths: ['/', '/opt', '/usr', '/usr/local']
- -- CMake root: /usr/local/share/cmake-3.31
- -- CMake architectures: ['x86_64-linux-gnu']
- -- CMake lib search paths: ['lib', 'lib32', 'lib64', 'libx32', 'share', '', 'lib/x86_64-linux-gnu']
- Preliminary CMake check failed. Aborting.
- Run-time dependency cairo found: NO (tried pkg-config and cmake)
-
- ../cairo/meson.build:31:12: ERROR: Dependency "cairo" not found (tried pkg-config and cmake)
-
- ::endgroup::
-
- [end of output]
-
- note: This error originates from a subprocess, and is likely not a problem with pip.
-error: metadata-generation-failed
-
-× Encountered error while generating package metadata.
-╰─> See above for output.
-
-note: This is an issue with the package mentioned above, not pip.
-hint: See above for details.
diff --git a/filer/locale/ar/LC_MESSAGES/django.mo b/filer/locale/ar/LC_MESSAGES/django.mo
new file mode 100644
index 0000000000000000000000000000000000000000..ec06f66ea7a49b0588c034e22c86c99f699e3568
GIT binary patch
literal 520
zcmYjMO-}+b5Y^~ukDfiuL5)Vswjc_{`UxZ?5{W_YWw$IPrQLLk!5`w^@+bIPoJs;t
z^3vD5d2eQZ4)(uxuyzrLh$F-v;sOz?h4@CXjc@GP)X2NJ$^seWXx9$k=g6Mla-5eduT)T^SUII%Rc2uL)}aYw{*Nr&!6etO6N(
zX)7ax^Bf**p^SsBwi<4x+De1Nl$n?JGZz+8n_A`;x9GFQQWlDgm0Y09%Zbk5bQVI%
z(^QsaEyPegRjX)7bN!)Ry55{jDeZbgY?O8FzjvbWi^^SIUAJ4RXK#_DBJ-BRE1zj*_zgo)+0S;l-`O4oSdxrjbPEy*lLXZ6WeVRFHvlElAT|mM3Z>{
literal 0
HcmV?d00001
diff --git a/filer/locale/ar/LC_MESSAGES/django.po b/filer/locale/ar/LC_MESSAGES/django.po
new file mode 100644
index 000000000..5469f0334
--- /dev/null
+++ b/filer/locale/ar/LC_MESSAGES/django.po
@@ -0,0 +1,1245 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# Translators:
+# Translators:
+msgid ""
+msgstr ""
+"Project-Id-Version: django Filer\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2023-09-30 18:10+0200\n"
+"PO-Revision-Date: 2012-07-13 15:50+0000\n"
+"Last-Translator: Angelo Dini \n"
+"Language-Team: Arabic (http://app.transifex.com/divio/django-filer/language/"
+"ar/)\n"
+"Language: ar\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 "
+"&& n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
+
+#: admin/clipboardadmin.py:17
+msgid "You do not have permission to upload files."
+msgstr ""
+
+#: admin/clipboardadmin.py:18
+msgid "Can't find folder to upload. Please refresh and try again"
+msgstr ""
+
+#: admin/clipboardadmin.py:20
+msgid "Can't use this folder, Permission Denied. Please select another folder."
+msgstr ""
+
+#: admin/fileadmin.py:73
+msgid "Advanced"
+msgstr ""
+
+#: admin/fileadmin.py:188
+msgid "canonical URL"
+msgstr ""
+
+#: admin/folderadmin.py:411 admin/folderadmin.py:582
+msgid ""
+"Items must be selected in order to perform actions on them. No items have "
+"been changed."
+msgstr ""
+
+#: admin/folderadmin.py:434
+#, python-format
+msgid "%(total_count)s selected"
+msgid_plural "All %(total_count)s selected"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+#: admin/folderadmin.py:460
+#, python-format
+msgid "Directory listing for %(folder_name)s"
+msgstr ""
+
+#: admin/folderadmin.py:475
+#, python-format
+msgid "0 of %(cnt)s selected"
+msgstr ""
+
+#: admin/folderadmin.py:612
+msgid "No action selected."
+msgstr ""
+
+#: admin/folderadmin.py:664
+#, python-format
+msgid "Successfully moved %(count)d files to clipboard."
+msgstr ""
+
+#: admin/folderadmin.py:669
+msgid "Move selected files to clipboard"
+msgstr ""
+
+#: admin/folderadmin.py:709
+#, python-format
+msgid "Successfully disabled permissions for %(count)d files."
+msgstr ""
+
+#: admin/folderadmin.py:711
+#, python-format
+msgid "Successfully enabled permissions for %(count)d files."
+msgstr ""
+
+#: admin/folderadmin.py:719
+msgid "Enable permissions for selected files"
+msgstr ""
+
+#: admin/folderadmin.py:725
+msgid "Disable permissions for selected files"
+msgstr ""
+
+#: admin/folderadmin.py:789
+#, python-format
+msgid "Successfully deleted %(count)d files and/or folders."
+msgstr ""
+
+#: admin/folderadmin.py:794
+msgid "Cannot delete files and/or folders"
+msgstr ""
+
+#: admin/folderadmin.py:796
+msgid "Are you sure?"
+msgstr ""
+
+#: admin/folderadmin.py:802
+msgid "Delete files and/or folders"
+msgstr ""
+
+#: admin/folderadmin.py:823
+msgid "Delete selected files and/or folders"
+msgstr ""
+
+#: admin/folderadmin.py:930
+#, python-format
+msgid "Folders with names %s already exist at the selected destination"
+msgstr ""
+
+#: admin/folderadmin.py:934
+#, python-format
+msgid ""
+"Successfully moved %(count)d files and/or folders to folder "
+"'%(destination)s'."
+msgstr ""
+
+#: admin/folderadmin.py:942 admin/folderadmin.py:944
+msgid "Move files and/or folders"
+msgstr ""
+
+#: admin/folderadmin.py:959
+msgid "Move selected files and/or folders"
+msgstr ""
+
+#: admin/folderadmin.py:1016
+#, python-format
+msgid "Successfully renamed %(count)d files."
+msgstr ""
+
+#: admin/folderadmin.py:1025 admin/folderadmin.py:1027
+#: admin/folderadmin.py:1042
+msgid "Rename files"
+msgstr ""
+
+#: admin/folderadmin.py:1137
+#, python-format
+msgid ""
+"Successfully copied %(count)d files and/or folders to folder "
+"'%(destination)s'."
+msgstr ""
+
+#: admin/folderadmin.py:1155 admin/folderadmin.py:1157
+msgid "Copy files and/or folders"
+msgstr ""
+
+#: admin/folderadmin.py:1174
+msgid "Copy selected files and/or folders"
+msgstr ""
+
+#: admin/folderadmin.py:1279
+#, python-format
+msgid "Successfully resized %(count)d images."
+msgstr ""
+
+#: admin/folderadmin.py:1286 admin/folderadmin.py:1288
+msgid "Resize images"
+msgstr ""
+
+#: admin/folderadmin.py:1303
+msgid "Resize selected images"
+msgstr ""
+
+#: admin/forms.py:24
+msgid "Suffix which will be appended to filenames of copied files."
+msgstr ""
+
+#: admin/forms.py:31
+#, python-format
+msgid ""
+"Suffix should be a valid, simple and lowercase filename part, like "
+"\"%(valid)s\"."
+msgstr ""
+
+#: admin/forms.py:52
+#, python-format
+msgid "Unknown rename format value key \"%(key)s\"."
+msgstr ""
+
+#: admin/forms.py:54
+#, python-format
+msgid "Invalid rename format: %(error)s."
+msgstr ""
+
+#: admin/forms.py:68 models/thumbnailoptionmodels.py:37
+msgid "thumbnail option"
+msgstr ""
+
+#: admin/forms.py:72 models/thumbnailoptionmodels.py:15
+msgid "width"
+msgstr ""
+
+#: admin/forms.py:73 models/thumbnailoptionmodels.py:20
+msgid "height"
+msgstr ""
+
+#: admin/forms.py:74 models/thumbnailoptionmodels.py:25
+msgid "crop"
+msgstr ""
+
+#: admin/forms.py:75 models/thumbnailoptionmodels.py:30
+msgid "upscale"
+msgstr ""
+
+#: admin/forms.py:79
+msgid "Thumbnail option or resize parameters must be choosen."
+msgstr ""
+
+#: admin/imageadmin.py:20 admin/imageadmin.py:112
+msgid "Subject location"
+msgstr ""
+
+#: admin/imageadmin.py:21
+msgid "Location of the main subject of the scene. Format: \"x,y\"."
+msgstr ""
+
+#: admin/imageadmin.py:59
+msgid "Invalid subject location format. "
+msgstr ""
+
+#: admin/imageadmin.py:67
+msgid "Subject location is outside of the image. "
+msgstr ""
+
+#: admin/imageadmin.py:76
+#, python-brace-format
+msgid "Your input: \"{subject_location}\". "
+msgstr ""
+
+#: admin/permissionadmin.py:10 models/foldermodels.py:380
+msgid "Who"
+msgstr ""
+
+#: admin/permissionadmin.py:11 models/foldermodels.py:401
+msgid "What"
+msgstr ""
+
+#: admin/views.py:55
+msgid "Folder with this name already exists."
+msgstr ""
+
+#: apps.py:11 templates/admin/filer/breadcrumbs.html:5
+#: templates/admin/filer/folder/change_form.html:7
+#: templates/admin/filer/folder/directory_listing.html:33
+msgid "Filer"
+msgstr ""
+
+#: contrib/django_cms/cms_toolbars.py:44
+msgid "Media library"
+msgstr ""
+
+#: models/abstract.py:62
+msgid "default alt text"
+msgstr ""
+
+#: models/abstract.py:69
+msgid "default caption"
+msgstr ""
+
+#: models/abstract.py:76
+msgid "subject location"
+msgstr ""
+
+#: models/abstract.py:91
+msgid "image"
+msgstr ""
+
+#: models/abstract.py:92
+msgid "images"
+msgstr ""
+
+#: models/abstract.py:148
+#, python-format
+msgid ""
+"Image format not recognized or image size exceeds limit of %(max_pixels)d "
+"million pixels by a factor of two or more. Before uploading again, check "
+"file format or resize image to %(width)d x %(height)d resolution or lower."
+msgstr ""
+
+#: models/abstract.py:156
+#, python-format
+msgid ""
+"Image size (%(pixels)d million pixels) exceeds limit of %(max_pixels)d "
+"million pixels. Before uploading again, resize image to %(width)d x "
+"%(height)d resolution or lower."
+msgstr ""
+
+#: models/clipboardmodels.py:11 models/foldermodels.py:289
+msgid "user"
+msgstr ""
+
+#: models/clipboardmodels.py:17 models/filemodels.py:157
+msgid "files"
+msgstr ""
+
+#: models/clipboardmodels.py:24 models/clipboardmodels.py:50
+msgid "clipboard"
+msgstr ""
+
+#: models/clipboardmodels.py:25
+msgid "clipboards"
+msgstr ""
+
+#: models/clipboardmodels.py:44 models/filemodels.py:74
+#: models/filemodels.py:156
+msgid "file"
+msgstr ""
+
+#: models/clipboardmodels.py:56
+msgid "clipboard item"
+msgstr ""
+
+#: models/clipboardmodels.py:57
+msgid "clipboard items"
+msgstr ""
+
+#: models/filemodels.py:66 templates/admin/filer/folder/change_form.html:8
+#: templates/admin/filer/folder/directory_listing.html:102
+#: templates/admin/filer/folder/new_folder_form.html:4
+#: templates/admin/filer/folder/new_folder_form.html:7
+#: templates/admin/filer/tools/clipboard/clipboard.html:27
+msgid "folder"
+msgstr ""
+
+#: models/filemodels.py:81
+msgid "file size"
+msgstr ""
+
+#: models/filemodels.py:87
+msgid "sha1"
+msgstr ""
+
+#: models/filemodels.py:94
+msgid "has all mandatory data"
+msgstr ""
+
+#: models/filemodels.py:100
+msgid "original filename"
+msgstr ""
+
+#: models/filemodels.py:110 models/foldermodels.py:102
+#: models/thumbnailoptionmodels.py:10
+msgid "name"
+msgstr ""
+
+#: models/filemodels.py:116
+msgid "description"
+msgstr ""
+
+#: models/filemodels.py:125 models/foldermodels.py:108
+msgid "owner"
+msgstr ""
+
+#: models/filemodels.py:129 models/foldermodels.py:116
+msgid "uploaded at"
+msgstr ""
+
+#: models/filemodels.py:134 models/foldermodels.py:126
+msgid "modified at"
+msgstr ""
+
+#: models/filemodels.py:140
+msgid "Permissions disabled"
+msgstr ""
+
+#: models/filemodels.py:141
+msgid ""
+"Disable any permission checking for this file. File will be publicly "
+"accessible to anyone."
+msgstr ""
+
+#: models/foldermodels.py:94
+msgid "parent"
+msgstr ""
+
+#: models/foldermodels.py:121
+msgid "created at"
+msgstr ""
+
+#: models/foldermodels.py:136 templates/admin/filer/breadcrumbs.html:6
+#: templates/admin/filer/folder/directory_listing.html:35
+msgid "Folder"
+msgstr ""
+
+#: models/foldermodels.py:137
+#: templates/admin/filer/folder/directory_thumbnail_list.html:12
+msgid "Folders"
+msgstr ""
+
+#: models/foldermodels.py:260
+msgid "all items"
+msgstr ""
+
+#: models/foldermodels.py:261
+msgid "this item only"
+msgstr ""
+
+#: models/foldermodels.py:262
+msgid "this item and all children"
+msgstr ""
+
+#: models/foldermodels.py:266
+msgid "inherit"
+msgstr ""
+
+#: models/foldermodels.py:267
+msgid "allow"
+msgstr ""
+
+#: models/foldermodels.py:268
+msgid "deny"
+msgstr ""
+
+#: models/foldermodels.py:280
+msgid "type"
+msgstr ""
+
+#: models/foldermodels.py:297
+msgid "group"
+msgstr ""
+
+#: models/foldermodels.py:304
+msgid "everybody"
+msgstr ""
+
+#: models/foldermodels.py:309
+msgid "can read"
+msgstr ""
+
+#: models/foldermodels.py:317
+msgid "can edit"
+msgstr ""
+
+#: models/foldermodels.py:325
+msgid "can add children"
+msgstr ""
+
+#: models/foldermodels.py:333
+msgid "folder permission"
+msgstr ""
+
+#: models/foldermodels.py:334
+msgid "folder permissions"
+msgstr ""
+
+#: models/foldermodels.py:348
+msgid "Folder cannot be selected with type \"all items\"."
+msgstr ""
+
+#: models/foldermodels.py:350
+msgid "Folder has to be selected when type is not \"all items\"."
+msgstr ""
+
+#: models/foldermodels.py:352
+msgid "User or group cannot be selected together with \"everybody\"."
+msgstr ""
+
+#: models/foldermodels.py:354
+msgid "At least one of user, group, or \"everybody\" has to be selected."
+msgstr ""
+
+#: models/foldermodels.py:360
+msgid "All Folders"
+msgstr ""
+
+#: models/foldermodels.py:362
+msgid "Logical Path"
+msgstr ""
+
+#: models/foldermodels.py:371
+#, python-brace-format
+msgid "User: {user}"
+msgstr ""
+
+#: models/foldermodels.py:373
+#, python-brace-format
+msgid "Group: {group}"
+msgstr ""
+
+#: models/foldermodels.py:375
+msgid "Everybody"
+msgstr ""
+
+#: models/foldermodels.py:388 templates/admin/filer/widgets/admin_file.html:45
+msgid "Edit"
+msgstr ""
+
+#: models/foldermodels.py:389
+msgid "Read"
+msgstr ""
+
+#: models/foldermodels.py:390
+msgid "Add children"
+msgstr ""
+
+#: models/imagemodels.py:18
+msgid "date taken"
+msgstr ""
+
+#: models/imagemodels.py:25
+msgid "author"
+msgstr ""
+
+#: models/imagemodels.py:32
+msgid "must always publish author credit"
+msgstr ""
+
+#: models/imagemodels.py:37
+msgid "must always publish copyright"
+msgstr ""
+
+#: models/thumbnailoptionmodels.py:16
+msgid "width in pixel."
+msgstr ""
+
+#: models/thumbnailoptionmodels.py:21
+msgid "height in pixel."
+msgstr ""
+
+#: models/thumbnailoptionmodels.py:38
+msgid "thumbnail options"
+msgstr ""
+
+#: models/virtualitems.py:46
+#: templates/admin/filer/folder/directory_table_list.html:4
+#: templates/admin/filer/folder/directory_table_list.html:170
+#: templates/admin/filer/folder/directory_thumbnail_list.html:5
+#: templates/admin/filer/folder/directory_thumbnail_list.html:146
+#: templates/admin/filer/tools/clipboard/clipboard.html:27
+msgid "Unsorted Uploads"
+msgstr ""
+
+#: models/virtualitems.py:73
+msgid "files with missing metadata"
+msgstr ""
+
+#: models/virtualitems.py:87 templates/admin/filer/folder/change_form.html:8
+#: templates/admin/filer/folder/directory_listing.html:102
+msgid "root"
+msgstr ""
+
+#: settings.py:273
+msgid "Show table view"
+msgstr ""
+
+#: settings.py:278
+msgid "Show thumbnail view"
+msgstr ""
+
+#: templates/admin/filer/actions.html:5
+msgid "Run the selected action"
+msgstr ""
+
+#: templates/admin/filer/actions.html:5
+msgid "Go"
+msgstr ""
+
+#: templates/admin/filer/actions.html:14
+#: templates/admin/filer/folder/directory_table_list.html:235
+#: templates/admin/filer/folder/directory_thumbnail_list.html:210
+msgid "Click here to select the objects across all pages"
+msgstr ""
+
+#: templates/admin/filer/actions.html:14
+#, python-format
+msgid "Select all %(total_count)s files and/or folders"
+msgstr ""
+
+#: templates/admin/filer/actions.html:16
+#: templates/admin/filer/folder/directory_table_list.html:237
+#: templates/admin/filer/folder/directory_thumbnail_list.html:212
+msgid "Clear selection"
+msgstr ""
+
+#: templates/admin/filer/breadcrumbs.html:4
+#: templates/admin/filer/folder/directory_listing.html:28
+msgid "Go back to admin homepage"
+msgstr ""
+
+#: templates/admin/filer/breadcrumbs.html:4
+#: templates/admin/filer/folder/change_form.html:6
+#: templates/admin/filer/folder/directory_listing.html:29
+msgid "Home"
+msgstr ""
+
+#: templates/admin/filer/breadcrumbs.html:5
+#: templates/admin/filer/folder/directory_listing.html:32
+msgid "Go back to Filer app"
+msgstr ""
+
+#: templates/admin/filer/breadcrumbs.html:6
+#: templates/admin/filer/folder/directory_listing.html:35
+msgid "Go back to root folder"
+msgstr ""
+
+#: templates/admin/filer/breadcrumbs.html:8
+#: templates/admin/filer/breadcrumbs.html:18
+#: templates/admin/filer/folder/change_form.html:10
+#: templates/admin/filer/folder/directory_listing.html:39
+#: templates/admin/filer/folder/directory_listing.html:47
+#, python-format
+msgid "Go back to '%(folder_name)s' folder"
+msgstr ""
+
+#: templates/admin/filer/change_form.html:26
+msgid "Duplicates"
+msgstr ""
+
+#: templates/admin/filer/delete_selected_files_confirmation.html:11
+msgid ""
+"Deleting the selected files and/or folders would result in deleting related "
+"objects, but your account doesn't have permission to delete the following "
+"types of objects:"
+msgstr ""
+
+#: templates/admin/filer/delete_selected_files_confirmation.html:19
+msgid ""
+"Deleting the selected files and/or folders would require deleting the "
+"following protected related objects:"
+msgstr ""
+
+#: templates/admin/filer/delete_selected_files_confirmation.html:27
+msgid ""
+"Are you sure you want to delete the selected files and/or folders? All of "
+"the following objects and their related items will be deleted:"
+msgstr ""
+
+#: templates/admin/filer/delete_selected_files_confirmation.html:46
+#: templates/admin/filer/folder/choose_copy_destination.html:64
+#: templates/admin/filer/folder/choose_move_destination.html:72
+msgid "No, take me back"
+msgstr ""
+
+#: templates/admin/filer/delete_selected_files_confirmation.html:47
+msgid "Yes, I'm sure"
+msgstr ""
+
+#: templates/admin/filer/file/change_form.html:35
+#: templates/admin/filer/folder/change_form.html:21
+#: templates/admin/filer/image/change_form.html:35
+msgid "History"
+msgstr ""
+
+#: templates/admin/filer/file/change_form.html:37
+#: templates/admin/filer/folder/change_form.html:23
+#: templates/admin/filer/image/change_form.html:37
+msgid "View on site"
+msgstr ""
+
+#: templates/admin/filer/folder/change_form.html:6
+#: templates/admin/filer/folder/change_form.html:7
+#: templates/admin/filer/folder/change_form.html:8
+#: templates/admin/filer/folder/directory_listing.html:102
+msgid "Go back to"
+msgstr ""
+
+#: templates/admin/filer/folder/change_form.html:6
+msgid "admin homepage"
+msgstr ""
+
+#: templates/admin/filer/folder/change_form.html:37
+#: templates/admin/filer/folder/directory_listing.html:95
+#: templates/admin/filer/folder/directory_listing.html:103
+#: templates/admin/filer/folder/directory_table_list.html:29
+#: templates/admin/filer/folder/directory_table_list.html:59
+#: templates/admin/filer/folder/directory_table_list.html:181
+#: templates/admin/filer/folder/directory_thumbnail_list.html:27
+#: templates/admin/filer/folder/directory_thumbnail_list.html:61
+#: templates/admin/filer/folder/directory_thumbnail_list.html:156
+msgid "Folder Icon"
+msgstr ""
+
+#: templates/admin/filer/folder/choose_copy_destination.html:23
+msgid ""
+"Your account doesn't have permissions to copy all of the selected files and/"
+"or folders."
+msgstr ""
+
+#: templates/admin/filer/folder/choose_copy_destination.html:25
+#: templates/admin/filer/folder/choose_copy_destination.html:31
+#: templates/admin/filer/folder/choose_copy_destination.html:37
+#: templates/admin/filer/folder/choose_move_destination.html:37
+#: templates/admin/filer/folder/choose_move_destination.html:43
+#: templates/admin/filer/folder/choose_move_destination.html:49
+msgid "Take me back"
+msgstr ""
+
+#: templates/admin/filer/folder/choose_copy_destination.html:29
+#: templates/admin/filer/folder/choose_move_destination.html:41
+msgid "There are no destination folders available."
+msgstr ""
+
+#: templates/admin/filer/folder/choose_copy_destination.html:35
+msgid "There are no files and/or folders available to copy."
+msgstr ""
+
+#: templates/admin/filer/folder/choose_copy_destination.html:40
+msgid ""
+"The following files and/or folders will be copied to a destination folder "
+"(retaining their tree structure):"
+msgstr ""
+
+#: templates/admin/filer/folder/choose_copy_destination.html:54
+#: templates/admin/filer/folder/choose_move_destination.html:64
+msgid "Destination folder:"
+msgstr ""
+
+#: templates/admin/filer/folder/choose_copy_destination.html:65
+#: templates/admin/filer/folder/choose_copy_destination.html:68
+#: templates/admin/filer/folder/directory_listing.html:178
+msgid "Copy"
+msgstr ""
+
+#: templates/admin/filer/folder/choose_copy_destination.html:67
+msgid "It is not allowed to copy files into same folder"
+msgstr ""
+
+#: templates/admin/filer/folder/choose_images_resize_options.html:15
+msgid ""
+"Your account doesn't have permissions to resize all of the selected images."
+msgstr ""
+
+#: templates/admin/filer/folder/choose_images_resize_options.html:18
+msgid "There are no images available to resize."
+msgstr ""
+
+#: templates/admin/filer/folder/choose_images_resize_options.html:20
+msgid "The following images will be resized:"
+msgstr ""
+
+#: templates/admin/filer/folder/choose_images_resize_options.html:32
+msgid "Choose an existing thumbnail option or enter resize parameters:"
+msgstr ""
+
+#: templates/admin/filer/folder/choose_images_resize_options.html:36
+msgid ""
+"Warning: Images will be resized in-place and originals will be lost. Maybe "
+"first make a copy of them to retain the originals."
+msgstr ""
+
+#: templates/admin/filer/folder/choose_images_resize_options.html:39
+msgid "Resize"
+msgstr ""
+
+#: templates/admin/filer/folder/choose_move_destination.html:35
+msgid ""
+"Your account doesn't have permissions to move all of the selected files and/"
+"or folders."
+msgstr ""
+
+#: templates/admin/filer/folder/choose_move_destination.html:47
+msgid "There are no files and/or folders available to move."
+msgstr ""
+
+#: templates/admin/filer/folder/choose_move_destination.html:52
+msgid ""
+"The following files and/or folders will be moved to a destination folder "
+"(retaining their tree structure):"
+msgstr ""
+
+#: templates/admin/filer/folder/choose_move_destination.html:73
+#: templates/admin/filer/folder/choose_move_destination.html:76
+#: templates/admin/filer/folder/directory_listing.html:181
+msgid "Move"
+msgstr ""
+
+#: templates/admin/filer/folder/choose_move_destination.html:75
+msgid "It is not allowed to move files into same folder"
+msgstr ""
+
+#: templates/admin/filer/folder/choose_rename_format.html:15
+msgid ""
+"Your account doesn't have permissions to rename all of the selected files."
+msgstr ""
+
+#: templates/admin/filer/folder/choose_rename_format.html:18
+msgid "There are no files available to rename."
+msgstr ""
+
+#: templates/admin/filer/folder/choose_rename_format.html:20
+msgid ""
+"The following files will be renamed (they will stay in their folders and "
+"keep original filename, only displayed filename will be changed):"
+msgstr ""
+
+#: templates/admin/filer/folder/choose_rename_format.html:59
+msgid "Rename"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_listing.html:94
+msgid "Go back to the parent folder"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_listing.html:131
+msgid "Change current folder details"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_listing.html:131
+msgid "Change"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_listing.html:141
+msgid "Click here to run search for entered phrase"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_listing.html:144
+msgid "Search"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_listing.html:151
+msgid "Close"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_listing.html:153
+msgid "Limit"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_listing.html:158
+msgid "Check it to limit the search to current folder"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_listing.html:159
+msgid "Limit the search to current folder"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_listing.html:175
+msgid "Delete"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_listing.html:203
+msgid "Adds a new Folder"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_listing.html:206
+msgid "New Folder"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_listing.html:211
+#: templates/admin/filer/folder/directory_listing.html:218
+#: templates/admin/filer/folder/directory_listing.html:221
+#: templates/admin/filer/folder/directory_listing.html:228
+#: templates/admin/filer/folder/directory_listing.html:235
+msgid "Upload Files"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_listing.html:233
+msgid "You have to select a folder first"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:16
+msgid "Name"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:17
+#: templates/admin/filer/tools/detail_info.html:50
+msgid "Owner"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:18
+#: templates/admin/filer/tools/detail_info.html:34
+msgid "Size"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:19
+msgid "Action"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:28
+#: templates/admin/filer/folder/directory_table_list.html:35
+#: templates/admin/filer/folder/directory_table_list.html:58
+#: templates/admin/filer/folder/directory_table_list.html:65
+#: templates/admin/filer/folder/directory_thumbnail_list.html:26
+#: templates/admin/filer/folder/directory_thumbnail_list.html:32
+#: templates/admin/filer/folder/directory_thumbnail_list.html:60
+#: templates/admin/filer/folder/directory_thumbnail_list.html:66
+#, python-format
+msgid "Change '%(item_label)s' folder details"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:76
+#: templates/admin/filer/tools/search_form.html:5
+#, python-format
+msgid "%(counter)s folder"
+msgid_plural "%(counter)s folders"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+#: templates/admin/filer/folder/directory_table_list.html:77
+#: templates/admin/filer/tools/search_form.html:6
+#, python-format
+msgid "%(counter)s file"
+msgid_plural "%(counter)s files"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+#: templates/admin/filer/folder/directory_table_list.html:83
+msgid "Change folder details"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:84
+msgid "Remove folder"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:96
+#: templates/admin/filer/folder/directory_table_list.html:104
+#: templates/admin/filer/folder/directory_table_list.html:119
+#: templates/admin/filer/folder/directory_thumbnail_list.html:95
+#: templates/admin/filer/folder/directory_thumbnail_list.html:104
+#: templates/admin/filer/folder/directory_thumbnail_list.html:119
+msgid "Select this file"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:107
+#: templates/admin/filer/folder/directory_table_list.html:122
+#: templates/admin/filer/folder/directory_table_list.html:154
+#: templates/admin/filer/folder/directory_thumbnail_list.html:107
+#: templates/admin/filer/folder/directory_thumbnail_list.html:122
+#, python-format
+msgid "Change '%(item_label)s' details"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:131
+#: templates/admin/filer/folder/directory_thumbnail_list.html:130
+msgid "Permissions"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:131
+#: templates/admin/filer/folder/directory_thumbnail_list.html:130
+msgid "disabled"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:131
+#: templates/admin/filer/folder/directory_thumbnail_list.html:130
+msgid "enabled"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:144
+msgid "URL copied to clipboard"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:147
+#, python-format
+msgid "Canonical url '%(item_label)s'"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:151
+#, python-format
+msgid "Download '%(item_label)s'"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:155
+msgid "Remove file"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:163
+#: templates/admin/filer/folder/directory_thumbnail_list.html:138
+msgid "Drop files here or use the \"Upload Files\" button"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:178
+#: templates/admin/filer/folder/directory_thumbnail_list.html:153
+msgid "Drop your file to upload into:"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:188
+#: templates/admin/filer/folder/directory_thumbnail_list.html:163
+msgid "Upload"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:200
+#: templates/admin/filer/folder/directory_thumbnail_list.html:175
+msgid "cancel"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:204
+#: templates/admin/filer/folder/directory_thumbnail_list.html:179
+msgid "Upload success!"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:208
+#: templates/admin/filer/folder/directory_thumbnail_list.html:183
+msgid "Upload canceled!"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:215
+#: templates/admin/filer/folder/directory_thumbnail_list.html:190
+msgid "previous"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:220
+#: templates/admin/filer/folder/directory_thumbnail_list.html:195
+#, python-format
+msgid "Page %(number)s of %(num_pages)s."
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:225
+#: templates/admin/filer/folder/directory_thumbnail_list.html:200
+msgid "next"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:235
+#: templates/admin/filer/folder/directory_thumbnail_list.html:210
+#, python-format
+msgid "Select all %(total_count)s"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_thumbnail_list.html:15
+#: templates/admin/filer/folder/directory_thumbnail_list.html:80
+msgid "Select all"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_thumbnail_list.html:77
+msgid "Files"
+msgstr ""
+
+#: templates/admin/filer/folder/new_folder_form.html:4
+#: templates/admin/filer/folder/new_folder_form.html:7
+msgid "Add new"
+msgstr ""
+
+#: templates/admin/filer/folder/new_folder_form.html:4
+msgid "Django site admin"
+msgstr ""
+
+#: templates/admin/filer/folder/new_folder_form.html:15
+msgid "Please correct the error below."
+msgid_plural "Please correct the errors below."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+msgstr[4] ""
+msgstr[5] ""
+
+#: templates/admin/filer/folder/new_folder_form.html:30
+msgid "Save"
+msgstr ""
+
+#: templates/admin/filer/templatetags/file_icon.html:9
+msgid "Your browser does not support audio."
+msgstr ""
+
+#: templates/admin/filer/templatetags/file_icon.html:14
+msgid "Your browser does not support video."
+msgstr ""
+
+#: templates/admin/filer/tools/clipboard/clipboard.html:9
+msgid "Clipboard"
+msgstr ""
+
+#: templates/admin/filer/tools/clipboard/clipboard.html:21
+msgid "Paste all items here"
+msgstr ""
+
+#: templates/admin/filer/tools/clipboard/clipboard.html:27
+msgid "Move all clipboard files to"
+msgstr ""
+
+#: templates/admin/filer/tools/clipboard/clipboard.html:27
+msgid "Empty Clipboard"
+msgstr ""
+
+#: templates/admin/filer/tools/clipboard/clipboard.html:50
+msgid "the clipboard is empty"
+msgstr ""
+
+#: templates/admin/filer/tools/clipboard/clipboard_item_rows.html:16
+msgid "upload failed"
+msgstr ""
+
+#: templates/admin/filer/tools/detail_info.html:11
+msgid "Download"
+msgstr ""
+
+#: templates/admin/filer/tools/detail_info.html:16
+msgid "Expand"
+msgstr ""
+
+#: templates/admin/filer/tools/detail_info.html:21
+#: templates/admin/filer/widgets/admin_file.html:32
+#: templatetags/filer_admin_tags.py:108
+msgid "File is missing"
+msgstr ""
+
+#: templates/admin/filer/tools/detail_info.html:30
+msgid "Type"
+msgstr ""
+
+#: templates/admin/filer/tools/detail_info.html:38
+msgid "File-size"
+msgstr ""
+
+#: templates/admin/filer/tools/detail_info.html:42
+msgid "Modified"
+msgstr ""
+
+#: templates/admin/filer/tools/detail_info.html:46
+msgid "Created"
+msgstr ""
+
+#: templates/admin/filer/tools/search_form.html:5
+msgid "found"
+msgstr ""
+
+#: templates/admin/filer/tools/search_form.html:5
+msgid "and"
+msgstr ""
+
+#: templates/admin/filer/tools/search_form.html:7
+msgid "cancel search"
+msgstr ""
+
+#: templates/admin/filer/widgets/admin_file.html:10
+#: templates/admin/filer/widgets/admin_file.html:39
+#: templates/admin/filer/widgets/admin_folder.html:18
+msgid "Clear"
+msgstr ""
+
+#: templates/admin/filer/widgets/admin_file.html:22
+msgid "or drop your file here"
+msgstr ""
+
+#: templates/admin/filer/widgets/admin_file.html:35
+msgid "no file selected"
+msgstr ""
+
+#: templates/admin/filer/widgets/admin_file.html:50
+#: templates/admin/filer/widgets/admin_folder.html:14
+msgid "Lookup"
+msgstr ""
+
+#: templates/admin/filer/widgets/admin_file.html:51
+msgid "Choose File"
+msgstr ""
+
+#: templates/admin/filer/widgets/admin_folder.html:16
+msgid "Choose Folder"
+msgstr ""
+
+#: validation.py:20
+#, python-brace-format
+msgid "File \"{file_name}\": Upload denied by site security policy"
+msgstr ""
+
+#: validation.py:23
+#, python-brace-format
+msgid "File \"{file_name}\": {file_type} upload denied by site security policy"
+msgstr ""
+
+#: validation.py:34
+#, python-brace-format
+msgid "File \"{file_name}\": HTML upload denied by site security policy"
+msgstr ""
+
+#: validation.py:72
+#, python-brace-format
+msgid ""
+"File \"{file_name}\": Rejected due to potential cross site scripting "
+"vulnerability"
+msgstr ""
+
+#: validation.py:84
+#, python-brace-format
+msgid "File \"{file_name}\": SVG file format not recognized"
+msgstr ""
+
+#~ msgid "Resize parameters must be choosen."
+#~ msgstr "Resize parameters must be choosen."
+
+#~ msgid "Choose resize parameters:"
+#~ msgstr "Choose resize parameters:"
+
+#~ msgid "Open file"
+#~ msgstr "Open file"
+
+#~ msgid "Full size preview"
+#~ msgstr "Full size preview"
+
+#~ msgid "Add Description"
+#~ msgstr "Add Description"
+
+#~ msgid "Author"
+#~ msgstr "Author"
+
+#~ msgid "Add Alt-Text"
+#~ msgstr "Add Alt-Text"
+
+#~ msgid "Add caption"
+#~ msgstr "Add caption"
+
+#~ msgid "Add Tags"
+#~ msgstr "Add Tags"
+
+#~ msgid "Change File"
+#~ msgstr "Change File"
+
+#~ msgid "none selected"
+#~ msgstr "none selected"
+
+#~ msgid "Add Folder"
+#~ msgstr "Add Folder"
+
+#~ msgid "Subject Location"
+#~ msgstr "Subject location"
+
+#~ msgid ""
+#~ "\n"
+#~ " %(counter)s file"
+#~ msgid_plural ""
+#~ "%(counter)s files\n"
+#~ " "
+#~ msgstr[0] "15c0f2d28d6dab1af1f6d94906beb627_pl_0"
+#~ msgstr[1] "15c0f2d28d6dab1af1f6d94906beb627_pl_1"
+#~ msgstr[2] "15c0f2d28d6dab1af1f6d94906beb627_pl_2"
+#~ msgstr[3] "15c0f2d28d6dab1af1f6d94906beb627_pl_3"
+#~ msgstr[4] "15c0f2d28d6dab1af1f6d94906beb627_pl_4"
+#~ msgstr[5] "15c0f2d28d6dab1af1f6d94906beb627_pl_5"
+
+#~ msgid "Upload File"
+#~ msgstr "Upload File"
diff --git a/filer/locale/bg/LC_MESSAGES/django.mo b/filer/locale/bg/LC_MESSAGES/django.mo
new file mode 100644
index 0000000000000000000000000000000000000000..24974f0a5bf82ffb4ee81e7ed8ca9b169887934d
GIT binary patch
literal 440
zcmYL^!A=4(5Qa5++M{O=W8wj#rMnm+7S|{O35i5PR_|rETS~fZ(-wjk-@@1PS!^YN
zKl#!&o&TSi-;48~lc3YUW#Bq+7Pt*W^#kAG^C6tE=AgONYgT$RtEGog@zmMP@xGV^LMmhTM-RtL_H(WO|vda?@@LKSJ6n=~kSqt2tk
zQspG|+7ftCoJkEMp#(hELQxr-F}XI+EA1O9WT({4Q}8Lip`4+D(T2cqBRRE#Drl{|
zHw0s9OvlGuQL|n2;7V+T#{1z>9f!v;JEq_z$L&KVfhA97)7kjAa@I+cSFJo#VcIVf
z*70oTv5{1$0hB9h9rO7|F+u%fUF=>ni^i(dl~9~Oe<^%2mm5nZnrPcN0?Pb9UEUqQ
P{+hQGT;-6p2g%7Fj4gws
literal 0
HcmV?d00001
diff --git a/filer/locale/bg/LC_MESSAGES/django.po b/filer/locale/bg/LC_MESSAGES/django.po
new file mode 100644
index 000000000..88b4a3899
--- /dev/null
+++ b/filer/locale/bg/LC_MESSAGES/django.po
@@ -0,0 +1,1224 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# Translators:
+# Translators:
+msgid ""
+msgstr ""
+"Project-Id-Version: django Filer\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2023-09-30 18:10+0200\n"
+"PO-Revision-Date: 2012-07-13 15:50+0000\n"
+"Last-Translator: Angelo Dini \n"
+"Language-Team: Bulgarian (http://app.transifex.com/divio/django-filer/"
+"language/bg/)\n"
+"Language: bg\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: admin/clipboardadmin.py:17
+msgid "You do not have permission to upload files."
+msgstr ""
+
+#: admin/clipboardadmin.py:18
+msgid "Can't find folder to upload. Please refresh and try again"
+msgstr ""
+
+#: admin/clipboardadmin.py:20
+msgid "Can't use this folder, Permission Denied. Please select another folder."
+msgstr ""
+
+#: admin/fileadmin.py:73
+msgid "Advanced"
+msgstr ""
+
+#: admin/fileadmin.py:188
+msgid "canonical URL"
+msgstr ""
+
+#: admin/folderadmin.py:411 admin/folderadmin.py:582
+msgid ""
+"Items must be selected in order to perform actions on them. No items have "
+"been changed."
+msgstr ""
+
+#: admin/folderadmin.py:434
+#, python-format
+msgid "%(total_count)s selected"
+msgid_plural "All %(total_count)s selected"
+msgstr[0] ""
+msgstr[1] ""
+
+#: admin/folderadmin.py:460
+#, python-format
+msgid "Directory listing for %(folder_name)s"
+msgstr ""
+
+#: admin/folderadmin.py:475
+#, python-format
+msgid "0 of %(cnt)s selected"
+msgstr ""
+
+#: admin/folderadmin.py:612
+msgid "No action selected."
+msgstr ""
+
+#: admin/folderadmin.py:664
+#, python-format
+msgid "Successfully moved %(count)d files to clipboard."
+msgstr ""
+
+#: admin/folderadmin.py:669
+msgid "Move selected files to clipboard"
+msgstr ""
+
+#: admin/folderadmin.py:709
+#, python-format
+msgid "Successfully disabled permissions for %(count)d files."
+msgstr ""
+
+#: admin/folderadmin.py:711
+#, python-format
+msgid "Successfully enabled permissions for %(count)d files."
+msgstr ""
+
+#: admin/folderadmin.py:719
+msgid "Enable permissions for selected files"
+msgstr ""
+
+#: admin/folderadmin.py:725
+msgid "Disable permissions for selected files"
+msgstr ""
+
+#: admin/folderadmin.py:789
+#, python-format
+msgid "Successfully deleted %(count)d files and/or folders."
+msgstr ""
+
+#: admin/folderadmin.py:794
+msgid "Cannot delete files and/or folders"
+msgstr ""
+
+#: admin/folderadmin.py:796
+msgid "Are you sure?"
+msgstr ""
+
+#: admin/folderadmin.py:802
+msgid "Delete files and/or folders"
+msgstr ""
+
+#: admin/folderadmin.py:823
+msgid "Delete selected files and/or folders"
+msgstr ""
+
+#: admin/folderadmin.py:930
+#, python-format
+msgid "Folders with names %s already exist at the selected destination"
+msgstr ""
+
+#: admin/folderadmin.py:934
+#, python-format
+msgid ""
+"Successfully moved %(count)d files and/or folders to folder "
+"'%(destination)s'."
+msgstr ""
+
+#: admin/folderadmin.py:942 admin/folderadmin.py:944
+msgid "Move files and/or folders"
+msgstr ""
+
+#: admin/folderadmin.py:959
+msgid "Move selected files and/or folders"
+msgstr ""
+
+#: admin/folderadmin.py:1016
+#, python-format
+msgid "Successfully renamed %(count)d files."
+msgstr ""
+
+#: admin/folderadmin.py:1025 admin/folderadmin.py:1027
+#: admin/folderadmin.py:1042
+msgid "Rename files"
+msgstr ""
+
+#: admin/folderadmin.py:1137
+#, python-format
+msgid ""
+"Successfully copied %(count)d files and/or folders to folder "
+"'%(destination)s'."
+msgstr ""
+
+#: admin/folderadmin.py:1155 admin/folderadmin.py:1157
+msgid "Copy files and/or folders"
+msgstr ""
+
+#: admin/folderadmin.py:1174
+msgid "Copy selected files and/or folders"
+msgstr ""
+
+#: admin/folderadmin.py:1279
+#, python-format
+msgid "Successfully resized %(count)d images."
+msgstr ""
+
+#: admin/folderadmin.py:1286 admin/folderadmin.py:1288
+msgid "Resize images"
+msgstr ""
+
+#: admin/folderadmin.py:1303
+msgid "Resize selected images"
+msgstr ""
+
+#: admin/forms.py:24
+msgid "Suffix which will be appended to filenames of copied files."
+msgstr ""
+
+#: admin/forms.py:31
+#, python-format
+msgid ""
+"Suffix should be a valid, simple and lowercase filename part, like "
+"\"%(valid)s\"."
+msgstr ""
+
+#: admin/forms.py:52
+#, python-format
+msgid "Unknown rename format value key \"%(key)s\"."
+msgstr ""
+
+#: admin/forms.py:54
+#, python-format
+msgid "Invalid rename format: %(error)s."
+msgstr ""
+
+#: admin/forms.py:68 models/thumbnailoptionmodels.py:37
+msgid "thumbnail option"
+msgstr ""
+
+#: admin/forms.py:72 models/thumbnailoptionmodels.py:15
+msgid "width"
+msgstr ""
+
+#: admin/forms.py:73 models/thumbnailoptionmodels.py:20
+msgid "height"
+msgstr ""
+
+#: admin/forms.py:74 models/thumbnailoptionmodels.py:25
+msgid "crop"
+msgstr ""
+
+#: admin/forms.py:75 models/thumbnailoptionmodels.py:30
+msgid "upscale"
+msgstr ""
+
+#: admin/forms.py:79
+msgid "Thumbnail option or resize parameters must be choosen."
+msgstr ""
+
+#: admin/imageadmin.py:20 admin/imageadmin.py:112
+msgid "Subject location"
+msgstr ""
+
+#: admin/imageadmin.py:21
+msgid "Location of the main subject of the scene. Format: \"x,y\"."
+msgstr ""
+
+#: admin/imageadmin.py:59
+msgid "Invalid subject location format. "
+msgstr ""
+
+#: admin/imageadmin.py:67
+msgid "Subject location is outside of the image. "
+msgstr ""
+
+#: admin/imageadmin.py:76
+#, python-brace-format
+msgid "Your input: \"{subject_location}\". "
+msgstr ""
+
+#: admin/permissionadmin.py:10 models/foldermodels.py:380
+msgid "Who"
+msgstr ""
+
+#: admin/permissionadmin.py:11 models/foldermodels.py:401
+msgid "What"
+msgstr ""
+
+#: admin/views.py:55
+msgid "Folder with this name already exists."
+msgstr ""
+
+#: apps.py:11 templates/admin/filer/breadcrumbs.html:5
+#: templates/admin/filer/folder/change_form.html:7
+#: templates/admin/filer/folder/directory_listing.html:33
+msgid "Filer"
+msgstr ""
+
+#: contrib/django_cms/cms_toolbars.py:44
+msgid "Media library"
+msgstr ""
+
+#: models/abstract.py:62
+msgid "default alt text"
+msgstr ""
+
+#: models/abstract.py:69
+msgid "default caption"
+msgstr ""
+
+#: models/abstract.py:76
+msgid "subject location"
+msgstr ""
+
+#: models/abstract.py:91
+msgid "image"
+msgstr ""
+
+#: models/abstract.py:92
+msgid "images"
+msgstr ""
+
+#: models/abstract.py:148
+#, python-format
+msgid ""
+"Image format not recognized or image size exceeds limit of %(max_pixels)d "
+"million pixels by a factor of two or more. Before uploading again, check "
+"file format or resize image to %(width)d x %(height)d resolution or lower."
+msgstr ""
+
+#: models/abstract.py:156
+#, python-format
+msgid ""
+"Image size (%(pixels)d million pixels) exceeds limit of %(max_pixels)d "
+"million pixels. Before uploading again, resize image to %(width)d x "
+"%(height)d resolution or lower."
+msgstr ""
+
+#: models/clipboardmodels.py:11 models/foldermodels.py:289
+msgid "user"
+msgstr ""
+
+#: models/clipboardmodels.py:17 models/filemodels.py:157
+msgid "files"
+msgstr ""
+
+#: models/clipboardmodels.py:24 models/clipboardmodels.py:50
+msgid "clipboard"
+msgstr ""
+
+#: models/clipboardmodels.py:25
+msgid "clipboards"
+msgstr ""
+
+#: models/clipboardmodels.py:44 models/filemodels.py:74
+#: models/filemodels.py:156
+msgid "file"
+msgstr ""
+
+#: models/clipboardmodels.py:56
+msgid "clipboard item"
+msgstr ""
+
+#: models/clipboardmodels.py:57
+msgid "clipboard items"
+msgstr ""
+
+#: models/filemodels.py:66 templates/admin/filer/folder/change_form.html:8
+#: templates/admin/filer/folder/directory_listing.html:102
+#: templates/admin/filer/folder/new_folder_form.html:4
+#: templates/admin/filer/folder/new_folder_form.html:7
+#: templates/admin/filer/tools/clipboard/clipboard.html:27
+msgid "folder"
+msgstr ""
+
+#: models/filemodels.py:81
+msgid "file size"
+msgstr ""
+
+#: models/filemodels.py:87
+msgid "sha1"
+msgstr ""
+
+#: models/filemodels.py:94
+msgid "has all mandatory data"
+msgstr ""
+
+#: models/filemodels.py:100
+msgid "original filename"
+msgstr ""
+
+#: models/filemodels.py:110 models/foldermodels.py:102
+#: models/thumbnailoptionmodels.py:10
+msgid "name"
+msgstr ""
+
+#: models/filemodels.py:116
+msgid "description"
+msgstr ""
+
+#: models/filemodels.py:125 models/foldermodels.py:108
+msgid "owner"
+msgstr ""
+
+#: models/filemodels.py:129 models/foldermodels.py:116
+msgid "uploaded at"
+msgstr ""
+
+#: models/filemodels.py:134 models/foldermodels.py:126
+msgid "modified at"
+msgstr ""
+
+#: models/filemodels.py:140
+msgid "Permissions disabled"
+msgstr ""
+
+#: models/filemodels.py:141
+msgid ""
+"Disable any permission checking for this file. File will be publicly "
+"accessible to anyone."
+msgstr ""
+
+#: models/foldermodels.py:94
+msgid "parent"
+msgstr ""
+
+#: models/foldermodels.py:121
+msgid "created at"
+msgstr ""
+
+#: models/foldermodels.py:136 templates/admin/filer/breadcrumbs.html:6
+#: templates/admin/filer/folder/directory_listing.html:35
+msgid "Folder"
+msgstr ""
+
+#: models/foldermodels.py:137
+#: templates/admin/filer/folder/directory_thumbnail_list.html:12
+msgid "Folders"
+msgstr ""
+
+#: models/foldermodels.py:260
+msgid "all items"
+msgstr ""
+
+#: models/foldermodels.py:261
+msgid "this item only"
+msgstr ""
+
+#: models/foldermodels.py:262
+msgid "this item and all children"
+msgstr ""
+
+#: models/foldermodels.py:266
+msgid "inherit"
+msgstr ""
+
+#: models/foldermodels.py:267
+msgid "allow"
+msgstr ""
+
+#: models/foldermodels.py:268
+msgid "deny"
+msgstr ""
+
+#: models/foldermodels.py:280
+msgid "type"
+msgstr ""
+
+#: models/foldermodels.py:297
+msgid "group"
+msgstr ""
+
+#: models/foldermodels.py:304
+msgid "everybody"
+msgstr ""
+
+#: models/foldermodels.py:309
+msgid "can read"
+msgstr ""
+
+#: models/foldermodels.py:317
+msgid "can edit"
+msgstr ""
+
+#: models/foldermodels.py:325
+msgid "can add children"
+msgstr ""
+
+#: models/foldermodels.py:333
+msgid "folder permission"
+msgstr ""
+
+#: models/foldermodels.py:334
+msgid "folder permissions"
+msgstr ""
+
+#: models/foldermodels.py:348
+msgid "Folder cannot be selected with type \"all items\"."
+msgstr ""
+
+#: models/foldermodels.py:350
+msgid "Folder has to be selected when type is not \"all items\"."
+msgstr ""
+
+#: models/foldermodels.py:352
+msgid "User or group cannot be selected together with \"everybody\"."
+msgstr ""
+
+#: models/foldermodels.py:354
+msgid "At least one of user, group, or \"everybody\" has to be selected."
+msgstr ""
+
+#: models/foldermodels.py:360
+msgid "All Folders"
+msgstr ""
+
+#: models/foldermodels.py:362
+msgid "Logical Path"
+msgstr ""
+
+#: models/foldermodels.py:371
+#, python-brace-format
+msgid "User: {user}"
+msgstr ""
+
+#: models/foldermodels.py:373
+#, python-brace-format
+msgid "Group: {group}"
+msgstr ""
+
+#: models/foldermodels.py:375
+msgid "Everybody"
+msgstr ""
+
+#: models/foldermodels.py:388 templates/admin/filer/widgets/admin_file.html:45
+msgid "Edit"
+msgstr ""
+
+#: models/foldermodels.py:389
+msgid "Read"
+msgstr ""
+
+#: models/foldermodels.py:390
+msgid "Add children"
+msgstr ""
+
+#: models/imagemodels.py:18
+msgid "date taken"
+msgstr ""
+
+#: models/imagemodels.py:25
+msgid "author"
+msgstr ""
+
+#: models/imagemodels.py:32
+msgid "must always publish author credit"
+msgstr ""
+
+#: models/imagemodels.py:37
+msgid "must always publish copyright"
+msgstr ""
+
+#: models/thumbnailoptionmodels.py:16
+msgid "width in pixel."
+msgstr ""
+
+#: models/thumbnailoptionmodels.py:21
+msgid "height in pixel."
+msgstr ""
+
+#: models/thumbnailoptionmodels.py:38
+msgid "thumbnail options"
+msgstr ""
+
+#: models/virtualitems.py:46
+#: templates/admin/filer/folder/directory_table_list.html:4
+#: templates/admin/filer/folder/directory_table_list.html:170
+#: templates/admin/filer/folder/directory_thumbnail_list.html:5
+#: templates/admin/filer/folder/directory_thumbnail_list.html:146
+#: templates/admin/filer/tools/clipboard/clipboard.html:27
+msgid "Unsorted Uploads"
+msgstr ""
+
+#: models/virtualitems.py:73
+msgid "files with missing metadata"
+msgstr ""
+
+#: models/virtualitems.py:87 templates/admin/filer/folder/change_form.html:8
+#: templates/admin/filer/folder/directory_listing.html:102
+msgid "root"
+msgstr ""
+
+#: settings.py:273
+msgid "Show table view"
+msgstr ""
+
+#: settings.py:278
+msgid "Show thumbnail view"
+msgstr ""
+
+#: templates/admin/filer/actions.html:5
+msgid "Run the selected action"
+msgstr ""
+
+#: templates/admin/filer/actions.html:5
+msgid "Go"
+msgstr ""
+
+#: templates/admin/filer/actions.html:14
+#: templates/admin/filer/folder/directory_table_list.html:235
+#: templates/admin/filer/folder/directory_thumbnail_list.html:210
+msgid "Click here to select the objects across all pages"
+msgstr ""
+
+#: templates/admin/filer/actions.html:14
+#, python-format
+msgid "Select all %(total_count)s files and/or folders"
+msgstr ""
+
+#: templates/admin/filer/actions.html:16
+#: templates/admin/filer/folder/directory_table_list.html:237
+#: templates/admin/filer/folder/directory_thumbnail_list.html:212
+msgid "Clear selection"
+msgstr ""
+
+#: templates/admin/filer/breadcrumbs.html:4
+#: templates/admin/filer/folder/directory_listing.html:28
+msgid "Go back to admin homepage"
+msgstr ""
+
+#: templates/admin/filer/breadcrumbs.html:4
+#: templates/admin/filer/folder/change_form.html:6
+#: templates/admin/filer/folder/directory_listing.html:29
+msgid "Home"
+msgstr ""
+
+#: templates/admin/filer/breadcrumbs.html:5
+#: templates/admin/filer/folder/directory_listing.html:32
+msgid "Go back to Filer app"
+msgstr ""
+
+#: templates/admin/filer/breadcrumbs.html:6
+#: templates/admin/filer/folder/directory_listing.html:35
+msgid "Go back to root folder"
+msgstr ""
+
+#: templates/admin/filer/breadcrumbs.html:8
+#: templates/admin/filer/breadcrumbs.html:18
+#: templates/admin/filer/folder/change_form.html:10
+#: templates/admin/filer/folder/directory_listing.html:39
+#: templates/admin/filer/folder/directory_listing.html:47
+#, python-format
+msgid "Go back to '%(folder_name)s' folder"
+msgstr ""
+
+#: templates/admin/filer/change_form.html:26
+msgid "Duplicates"
+msgstr ""
+
+#: templates/admin/filer/delete_selected_files_confirmation.html:11
+msgid ""
+"Deleting the selected files and/or folders would result in deleting related "
+"objects, but your account doesn't have permission to delete the following "
+"types of objects:"
+msgstr ""
+
+#: templates/admin/filer/delete_selected_files_confirmation.html:19
+msgid ""
+"Deleting the selected files and/or folders would require deleting the "
+"following protected related objects:"
+msgstr ""
+
+#: templates/admin/filer/delete_selected_files_confirmation.html:27
+msgid ""
+"Are you sure you want to delete the selected files and/or folders? All of "
+"the following objects and their related items will be deleted:"
+msgstr ""
+
+#: templates/admin/filer/delete_selected_files_confirmation.html:46
+#: templates/admin/filer/folder/choose_copy_destination.html:64
+#: templates/admin/filer/folder/choose_move_destination.html:72
+msgid "No, take me back"
+msgstr ""
+
+#: templates/admin/filer/delete_selected_files_confirmation.html:47
+msgid "Yes, I'm sure"
+msgstr ""
+
+#: templates/admin/filer/file/change_form.html:35
+#: templates/admin/filer/folder/change_form.html:21
+#: templates/admin/filer/image/change_form.html:35
+msgid "History"
+msgstr ""
+
+#: templates/admin/filer/file/change_form.html:37
+#: templates/admin/filer/folder/change_form.html:23
+#: templates/admin/filer/image/change_form.html:37
+msgid "View on site"
+msgstr ""
+
+#: templates/admin/filer/folder/change_form.html:6
+#: templates/admin/filer/folder/change_form.html:7
+#: templates/admin/filer/folder/change_form.html:8
+#: templates/admin/filer/folder/directory_listing.html:102
+msgid "Go back to"
+msgstr ""
+
+#: templates/admin/filer/folder/change_form.html:6
+msgid "admin homepage"
+msgstr ""
+
+#: templates/admin/filer/folder/change_form.html:37
+#: templates/admin/filer/folder/directory_listing.html:95
+#: templates/admin/filer/folder/directory_listing.html:103
+#: templates/admin/filer/folder/directory_table_list.html:29
+#: templates/admin/filer/folder/directory_table_list.html:59
+#: templates/admin/filer/folder/directory_table_list.html:181
+#: templates/admin/filer/folder/directory_thumbnail_list.html:27
+#: templates/admin/filer/folder/directory_thumbnail_list.html:61
+#: templates/admin/filer/folder/directory_thumbnail_list.html:156
+msgid "Folder Icon"
+msgstr ""
+
+#: templates/admin/filer/folder/choose_copy_destination.html:23
+msgid ""
+"Your account doesn't have permissions to copy all of the selected files and/"
+"or folders."
+msgstr ""
+
+#: templates/admin/filer/folder/choose_copy_destination.html:25
+#: templates/admin/filer/folder/choose_copy_destination.html:31
+#: templates/admin/filer/folder/choose_copy_destination.html:37
+#: templates/admin/filer/folder/choose_move_destination.html:37
+#: templates/admin/filer/folder/choose_move_destination.html:43
+#: templates/admin/filer/folder/choose_move_destination.html:49
+msgid "Take me back"
+msgstr ""
+
+#: templates/admin/filer/folder/choose_copy_destination.html:29
+#: templates/admin/filer/folder/choose_move_destination.html:41
+msgid "There are no destination folders available."
+msgstr ""
+
+#: templates/admin/filer/folder/choose_copy_destination.html:35
+msgid "There are no files and/or folders available to copy."
+msgstr ""
+
+#: templates/admin/filer/folder/choose_copy_destination.html:40
+msgid ""
+"The following files and/or folders will be copied to a destination folder "
+"(retaining their tree structure):"
+msgstr ""
+
+#: templates/admin/filer/folder/choose_copy_destination.html:54
+#: templates/admin/filer/folder/choose_move_destination.html:64
+msgid "Destination folder:"
+msgstr ""
+
+#: templates/admin/filer/folder/choose_copy_destination.html:65
+#: templates/admin/filer/folder/choose_copy_destination.html:68
+#: templates/admin/filer/folder/directory_listing.html:178
+msgid "Copy"
+msgstr ""
+
+#: templates/admin/filer/folder/choose_copy_destination.html:67
+msgid "It is not allowed to copy files into same folder"
+msgstr ""
+
+#: templates/admin/filer/folder/choose_images_resize_options.html:15
+msgid ""
+"Your account doesn't have permissions to resize all of the selected images."
+msgstr ""
+
+#: templates/admin/filer/folder/choose_images_resize_options.html:18
+msgid "There are no images available to resize."
+msgstr ""
+
+#: templates/admin/filer/folder/choose_images_resize_options.html:20
+msgid "The following images will be resized:"
+msgstr ""
+
+#: templates/admin/filer/folder/choose_images_resize_options.html:32
+msgid "Choose an existing thumbnail option or enter resize parameters:"
+msgstr ""
+
+#: templates/admin/filer/folder/choose_images_resize_options.html:36
+msgid ""
+"Warning: Images will be resized in-place and originals will be lost. Maybe "
+"first make a copy of them to retain the originals."
+msgstr ""
+
+#: templates/admin/filer/folder/choose_images_resize_options.html:39
+msgid "Resize"
+msgstr ""
+
+#: templates/admin/filer/folder/choose_move_destination.html:35
+msgid ""
+"Your account doesn't have permissions to move all of the selected files and/"
+"or folders."
+msgstr ""
+
+#: templates/admin/filer/folder/choose_move_destination.html:47
+msgid "There are no files and/or folders available to move."
+msgstr ""
+
+#: templates/admin/filer/folder/choose_move_destination.html:52
+msgid ""
+"The following files and/or folders will be moved to a destination folder "
+"(retaining their tree structure):"
+msgstr ""
+
+#: templates/admin/filer/folder/choose_move_destination.html:73
+#: templates/admin/filer/folder/choose_move_destination.html:76
+#: templates/admin/filer/folder/directory_listing.html:181
+msgid "Move"
+msgstr ""
+
+#: templates/admin/filer/folder/choose_move_destination.html:75
+msgid "It is not allowed to move files into same folder"
+msgstr ""
+
+#: templates/admin/filer/folder/choose_rename_format.html:15
+msgid ""
+"Your account doesn't have permissions to rename all of the selected files."
+msgstr ""
+
+#: templates/admin/filer/folder/choose_rename_format.html:18
+msgid "There are no files available to rename."
+msgstr ""
+
+#: templates/admin/filer/folder/choose_rename_format.html:20
+msgid ""
+"The following files will be renamed (they will stay in their folders and "
+"keep original filename, only displayed filename will be changed):"
+msgstr ""
+
+#: templates/admin/filer/folder/choose_rename_format.html:59
+msgid "Rename"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_listing.html:94
+msgid "Go back to the parent folder"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_listing.html:131
+msgid "Change current folder details"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_listing.html:131
+msgid "Change"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_listing.html:141
+msgid "Click here to run search for entered phrase"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_listing.html:144
+msgid "Search"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_listing.html:151
+msgid "Close"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_listing.html:153
+msgid "Limit"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_listing.html:158
+msgid "Check it to limit the search to current folder"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_listing.html:159
+msgid "Limit the search to current folder"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_listing.html:175
+msgid "Delete"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_listing.html:203
+msgid "Adds a new Folder"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_listing.html:206
+msgid "New Folder"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_listing.html:211
+#: templates/admin/filer/folder/directory_listing.html:218
+#: templates/admin/filer/folder/directory_listing.html:221
+#: templates/admin/filer/folder/directory_listing.html:228
+#: templates/admin/filer/folder/directory_listing.html:235
+msgid "Upload Files"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_listing.html:233
+msgid "You have to select a folder first"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:16
+msgid "Name"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:17
+#: templates/admin/filer/tools/detail_info.html:50
+msgid "Owner"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:18
+#: templates/admin/filer/tools/detail_info.html:34
+msgid "Size"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:19
+msgid "Action"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:28
+#: templates/admin/filer/folder/directory_table_list.html:35
+#: templates/admin/filer/folder/directory_table_list.html:58
+#: templates/admin/filer/folder/directory_table_list.html:65
+#: templates/admin/filer/folder/directory_thumbnail_list.html:26
+#: templates/admin/filer/folder/directory_thumbnail_list.html:32
+#: templates/admin/filer/folder/directory_thumbnail_list.html:60
+#: templates/admin/filer/folder/directory_thumbnail_list.html:66
+#, python-format
+msgid "Change '%(item_label)s' folder details"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:76
+#: templates/admin/filer/tools/search_form.html:5
+#, python-format
+msgid "%(counter)s folder"
+msgid_plural "%(counter)s folders"
+msgstr[0] ""
+msgstr[1] ""
+
+#: templates/admin/filer/folder/directory_table_list.html:77
+#: templates/admin/filer/tools/search_form.html:6
+#, python-format
+msgid "%(counter)s file"
+msgid_plural "%(counter)s files"
+msgstr[0] ""
+msgstr[1] ""
+
+#: templates/admin/filer/folder/directory_table_list.html:83
+msgid "Change folder details"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:84
+msgid "Remove folder"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:96
+#: templates/admin/filer/folder/directory_table_list.html:104
+#: templates/admin/filer/folder/directory_table_list.html:119
+#: templates/admin/filer/folder/directory_thumbnail_list.html:95
+#: templates/admin/filer/folder/directory_thumbnail_list.html:104
+#: templates/admin/filer/folder/directory_thumbnail_list.html:119
+msgid "Select this file"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:107
+#: templates/admin/filer/folder/directory_table_list.html:122
+#: templates/admin/filer/folder/directory_table_list.html:154
+#: templates/admin/filer/folder/directory_thumbnail_list.html:107
+#: templates/admin/filer/folder/directory_thumbnail_list.html:122
+#, python-format
+msgid "Change '%(item_label)s' details"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:131
+#: templates/admin/filer/folder/directory_thumbnail_list.html:130
+msgid "Permissions"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:131
+#: templates/admin/filer/folder/directory_thumbnail_list.html:130
+msgid "disabled"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:131
+#: templates/admin/filer/folder/directory_thumbnail_list.html:130
+msgid "enabled"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:144
+msgid "URL copied to clipboard"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:147
+#, python-format
+msgid "Canonical url '%(item_label)s'"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:151
+#, python-format
+msgid "Download '%(item_label)s'"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:155
+msgid "Remove file"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:163
+#: templates/admin/filer/folder/directory_thumbnail_list.html:138
+msgid "Drop files here or use the \"Upload Files\" button"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:178
+#: templates/admin/filer/folder/directory_thumbnail_list.html:153
+msgid "Drop your file to upload into:"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:188
+#: templates/admin/filer/folder/directory_thumbnail_list.html:163
+msgid "Upload"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:200
+#: templates/admin/filer/folder/directory_thumbnail_list.html:175
+msgid "cancel"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:204
+#: templates/admin/filer/folder/directory_thumbnail_list.html:179
+msgid "Upload success!"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:208
+#: templates/admin/filer/folder/directory_thumbnail_list.html:183
+msgid "Upload canceled!"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:215
+#: templates/admin/filer/folder/directory_thumbnail_list.html:190
+msgid "previous"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:220
+#: templates/admin/filer/folder/directory_thumbnail_list.html:195
+#, python-format
+msgid "Page %(number)s of %(num_pages)s."
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:225
+#: templates/admin/filer/folder/directory_thumbnail_list.html:200
+msgid "next"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:235
+#: templates/admin/filer/folder/directory_thumbnail_list.html:210
+#, python-format
+msgid "Select all %(total_count)s"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_thumbnail_list.html:15
+#: templates/admin/filer/folder/directory_thumbnail_list.html:80
+msgid "Select all"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_thumbnail_list.html:77
+msgid "Files"
+msgstr ""
+
+#: templates/admin/filer/folder/new_folder_form.html:4
+#: templates/admin/filer/folder/new_folder_form.html:7
+msgid "Add new"
+msgstr ""
+
+#: templates/admin/filer/folder/new_folder_form.html:4
+msgid "Django site admin"
+msgstr ""
+
+#: templates/admin/filer/folder/new_folder_form.html:15
+msgid "Please correct the error below."
+msgid_plural "Please correct the errors below."
+msgstr[0] ""
+msgstr[1] ""
+
+#: templates/admin/filer/folder/new_folder_form.html:30
+msgid "Save"
+msgstr ""
+
+#: templates/admin/filer/templatetags/file_icon.html:9
+msgid "Your browser does not support audio."
+msgstr ""
+
+#: templates/admin/filer/templatetags/file_icon.html:14
+msgid "Your browser does not support video."
+msgstr ""
+
+#: templates/admin/filer/tools/clipboard/clipboard.html:9
+msgid "Clipboard"
+msgstr ""
+
+#: templates/admin/filer/tools/clipboard/clipboard.html:21
+msgid "Paste all items here"
+msgstr ""
+
+#: templates/admin/filer/tools/clipboard/clipboard.html:27
+msgid "Move all clipboard files to"
+msgstr ""
+
+#: templates/admin/filer/tools/clipboard/clipboard.html:27
+msgid "Empty Clipboard"
+msgstr ""
+
+#: templates/admin/filer/tools/clipboard/clipboard.html:50
+msgid "the clipboard is empty"
+msgstr ""
+
+#: templates/admin/filer/tools/clipboard/clipboard_item_rows.html:16
+msgid "upload failed"
+msgstr ""
+
+#: templates/admin/filer/tools/detail_info.html:11
+msgid "Download"
+msgstr ""
+
+#: templates/admin/filer/tools/detail_info.html:16
+msgid "Expand"
+msgstr ""
+
+#: templates/admin/filer/tools/detail_info.html:21
+#: templates/admin/filer/widgets/admin_file.html:32
+#: templatetags/filer_admin_tags.py:108
+msgid "File is missing"
+msgstr ""
+
+#: templates/admin/filer/tools/detail_info.html:30
+msgid "Type"
+msgstr ""
+
+#: templates/admin/filer/tools/detail_info.html:38
+msgid "File-size"
+msgstr ""
+
+#: templates/admin/filer/tools/detail_info.html:42
+msgid "Modified"
+msgstr ""
+
+#: templates/admin/filer/tools/detail_info.html:46
+msgid "Created"
+msgstr ""
+
+#: templates/admin/filer/tools/search_form.html:5
+msgid "found"
+msgstr ""
+
+#: templates/admin/filer/tools/search_form.html:5
+msgid "and"
+msgstr ""
+
+#: templates/admin/filer/tools/search_form.html:7
+msgid "cancel search"
+msgstr ""
+
+#: templates/admin/filer/widgets/admin_file.html:10
+#: templates/admin/filer/widgets/admin_file.html:39
+#: templates/admin/filer/widgets/admin_folder.html:18
+msgid "Clear"
+msgstr ""
+
+#: templates/admin/filer/widgets/admin_file.html:22
+msgid "or drop your file here"
+msgstr ""
+
+#: templates/admin/filer/widgets/admin_file.html:35
+msgid "no file selected"
+msgstr ""
+
+#: templates/admin/filer/widgets/admin_file.html:50
+#: templates/admin/filer/widgets/admin_folder.html:14
+msgid "Lookup"
+msgstr ""
+
+#: templates/admin/filer/widgets/admin_file.html:51
+msgid "Choose File"
+msgstr ""
+
+#: templates/admin/filer/widgets/admin_folder.html:16
+msgid "Choose Folder"
+msgstr ""
+
+#: validation.py:20
+#, python-brace-format
+msgid "File \"{file_name}\": Upload denied by site security policy"
+msgstr ""
+
+#: validation.py:23
+#, python-brace-format
+msgid "File \"{file_name}\": {file_type} upload denied by site security policy"
+msgstr ""
+
+#: validation.py:34
+#, python-brace-format
+msgid "File \"{file_name}\": HTML upload denied by site security policy"
+msgstr ""
+
+#: validation.py:72
+#, python-brace-format
+msgid ""
+"File \"{file_name}\": Rejected due to potential cross site scripting "
+"vulnerability"
+msgstr ""
+
+#: validation.py:84
+#, python-brace-format
+msgid "File \"{file_name}\": SVG file format not recognized"
+msgstr ""
+
+#~ msgid "Resize parameters must be choosen."
+#~ msgstr "Resize parameters must be choosen."
+
+#~ msgid "Choose resize parameters:"
+#~ msgstr "Choose resize parameters:"
+
+#~ msgid "Open file"
+#~ msgstr "Open file"
+
+#~ msgid "Full size preview"
+#~ msgstr "Full size preview"
+
+#~ msgid "Add Description"
+#~ msgstr "Add Description"
+
+#~ msgid "Author"
+#~ msgstr "Author"
+
+#~ msgid "Add Alt-Text"
+#~ msgstr "Add Alt-Text"
+
+#~ msgid "Add caption"
+#~ msgstr "Add caption"
+
+#~ msgid "Add Tags"
+#~ msgstr "Add Tags"
+
+#~ msgid "Change File"
+#~ msgstr "Change File"
+
+#~ msgid "none selected"
+#~ msgstr "none selected"
+
+#~ msgid "Add Folder"
+#~ msgstr "Add Folder"
+
+#~ msgid "Subject Location"
+#~ msgstr "Subject location"
+
+#~ msgid ""
+#~ "\n"
+#~ " %(counter)s file"
+#~ msgid_plural ""
+#~ "%(counter)s files\n"
+#~ " "
+#~ msgstr[0] "15c0f2d28d6dab1af1f6d94906beb627_pl_0"
+#~ msgstr[1] "15c0f2d28d6dab1af1f6d94906beb627_pl_1"
+
+#~ msgid "Upload File"
+#~ msgstr "Upload File"
diff --git a/filer/locale/ca/LC_MESSAGES/django.mo b/filer/locale/ca/LC_MESSAGES/django.mo
new file mode 100644
index 0000000000000000000000000000000000000000..abddc619575884a06874ee0593f2be31f0a95d9d
GIT binary patch
literal 16627
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
literal 0
HcmV?d00001
diff --git a/filer/locale/ca/LC_MESSAGES/django.po b/filer/locale/ca/LC_MESSAGES/django.po
new file mode 100644
index 000000000..4ba43842f
--- /dev/null
+++ b/filer/locale/ca/LC_MESSAGES/django.po
@@ -0,0 +1,1261 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# Translators:
+# Translators:
+# Roger Pons , 2013-2014,2016
+msgid ""
+msgstr ""
+"Project-Id-Version: django Filer\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2023-09-30 18:10+0200\n"
+"PO-Revision-Date: 2012-07-13 15:50+0000\n"
+"Last-Translator: Roger Pons , 2013-2014,2016\n"
+"Language-Team: Catalan (http://app.transifex.com/divio/django-filer/language/"
+"ca/)\n"
+"Language: ca\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: admin/clipboardadmin.py:17
+msgid "You do not have permission to upload files."
+msgstr ""
+
+#: admin/clipboardadmin.py:18
+msgid "Can't find folder to upload. Please refresh and try again"
+msgstr ""
+
+#: admin/clipboardadmin.py:20
+msgid "Can't use this folder, Permission Denied. Please select another folder."
+msgstr ""
+
+#: admin/fileadmin.py:73
+msgid "Advanced"
+msgstr "Avançat"
+
+#: admin/fileadmin.py:188
+msgid "canonical URL"
+msgstr "URL canònica"
+
+#: admin/folderadmin.py:411 admin/folderadmin.py:582
+msgid ""
+"Items must be selected in order to perform actions on them. No items have "
+"been changed."
+msgstr ""
+"Cal seleccionar els elements en ordre per tal de realitzar accions sobre "
+"ells. No s'ha modificat cap element."
+
+#: admin/folderadmin.py:434
+#, python-format
+msgid "%(total_count)s selected"
+msgid_plural "All %(total_count)s selected"
+msgstr[0] "%(total_count)s seleccionat"
+msgstr[1] "Tots %(total_count)s seleccionats"
+
+#: admin/folderadmin.py:460
+#, python-format
+msgid "Directory listing for %(folder_name)s"
+msgstr ""
+
+#: admin/folderadmin.py:475
+#, python-format
+msgid "0 of %(cnt)s selected"
+msgstr "0 de %(cnt)s seleccionats"
+
+#: admin/folderadmin.py:612
+msgid "No action selected."
+msgstr "No s'ha seleccionat cap acció."
+
+#: admin/folderadmin.py:664
+#, python-format
+msgid "Successfully moved %(count)d files to clipboard."
+msgstr "S'han mogut %(count)d fitxers al portapapers."
+
+#: admin/folderadmin.py:669
+msgid "Move selected files to clipboard"
+msgstr "Moure els fitxers seleccionats al portapapers."
+
+#: admin/folderadmin.py:709
+#, python-format
+msgid "Successfully disabled permissions for %(count)d files."
+msgstr "S'han desactivat els permisos de %(count)d fitxers."
+
+#: admin/folderadmin.py:711
+#, python-format
+msgid "Successfully enabled permissions for %(count)d files."
+msgstr "S'han activat els permisos de %(count)d fitxers."
+
+#: admin/folderadmin.py:719
+msgid "Enable permissions for selected files"
+msgstr "Activar els permisos dels fitxers seleccionats"
+
+#: admin/folderadmin.py:725
+msgid "Disable permissions for selected files"
+msgstr "Desactivaer els permisos dels fitxers seleccionats"
+
+#: admin/folderadmin.py:789
+#, python-format
+msgid "Successfully deleted %(count)d files and/or folders."
+msgstr "S'han esborrat %(count)d fitxers i/o carpetes."
+
+#: admin/folderadmin.py:794
+msgid "Cannot delete files and/or folders"
+msgstr "No es poden esborrar els fitxers i/o carpetes"
+
+#: admin/folderadmin.py:796
+msgid "Are you sure?"
+msgstr "Esteu segur?"
+
+#: admin/folderadmin.py:802
+msgid "Delete files and/or folders"
+msgstr "Esborrar fitxers i/o carpetes"
+
+#: admin/folderadmin.py:823
+msgid "Delete selected files and/or folders"
+msgstr "Esborrar els fitxers i/o carpetes seleccionats"
+
+#: admin/folderadmin.py:930
+#, python-format
+msgid "Folders with names %s already exist at the selected destination"
+msgstr "Ja existeixen carpetes amb els noms %s a la destinació seleccionada"
+
+#: admin/folderadmin.py:934
+#, python-format
+msgid ""
+"Successfully moved %(count)d files and/or folders to folder "
+"'%(destination)s'."
+msgstr ""
+"S'han mogut %(count)d fitxers i/o carpetes a la carpeta '%(destination)s'"
+
+#: admin/folderadmin.py:942 admin/folderadmin.py:944
+msgid "Move files and/or folders"
+msgstr "Moure fitxers i/o carpetes"
+
+#: admin/folderadmin.py:959
+msgid "Move selected files and/or folders"
+msgstr "Moure els fitxers i/o carpetes seleccionats"
+
+#: admin/folderadmin.py:1016
+#, python-format
+msgid "Successfully renamed %(count)d files."
+msgstr "S'ha canviat el nom de %(count)d fitxers."
+
+#: admin/folderadmin.py:1025 admin/folderadmin.py:1027
+#: admin/folderadmin.py:1042
+msgid "Rename files"
+msgstr "Canviar el nom a fitxers"
+
+#: admin/folderadmin.py:1137
+#, python-format
+msgid ""
+"Successfully copied %(count)d files and/or folders to folder "
+"'%(destination)s'."
+msgstr ""
+"S'han copiat %(count)d fitxeres i/o carpetes a la carpeta '%(destination)s'."
+
+#: admin/folderadmin.py:1155 admin/folderadmin.py:1157
+msgid "Copy files and/or folders"
+msgstr "Copiar fitxers i/o carpetes"
+
+#: admin/folderadmin.py:1174
+msgid "Copy selected files and/or folders"
+msgstr "Copiar els fitxers i/o carpetes seleccionats"
+
+#: admin/folderadmin.py:1279
+#, python-format
+msgid "Successfully resized %(count)d images."
+msgstr "S'han redimensionat %(count)d imatges."
+
+#: admin/folderadmin.py:1286 admin/folderadmin.py:1288
+msgid "Resize images"
+msgstr "Redimensionar imatges"
+
+#: admin/folderadmin.py:1303
+msgid "Resize selected images"
+msgstr "Redimensionar les imatges seleccionades"
+
+#: admin/forms.py:24
+msgid "Suffix which will be appended to filenames of copied files."
+msgstr "Sufix que serà afegit als noms de fitxer dels fitxers copiats"
+
+#: admin/forms.py:31
+#, python-format
+msgid ""
+"Suffix should be a valid, simple and lowercase filename part, like "
+"\"%(valid)s\"."
+msgstr ""
+"El sufix ha de ser una part de nom de fitxer vàlida, simple i en minúscules, "
+"com ara \"%(valid)s\"."
+
+#: admin/forms.py:52
+#, python-format
+msgid "Unknown rename format value key \"%(key)s\"."
+msgstr "Format de renombrat amb valor de clau \"%(key)s\" desconegut."
+
+#: admin/forms.py:54
+#, python-format
+msgid "Invalid rename format: %(error)s."
+msgstr "Format de renombrat no vàlid: %(error)s"
+
+#: admin/forms.py:68 models/thumbnailoptionmodels.py:37
+msgid "thumbnail option"
+msgstr "opció de miniatures"
+
+#: admin/forms.py:72 models/thumbnailoptionmodels.py:15
+msgid "width"
+msgstr "ample"
+
+#: admin/forms.py:73 models/thumbnailoptionmodels.py:20
+msgid "height"
+msgstr "alçada"
+
+#: admin/forms.py:74 models/thumbnailoptionmodels.py:25
+msgid "crop"
+msgstr "retallar"
+
+#: admin/forms.py:75 models/thumbnailoptionmodels.py:30
+msgid "upscale"
+msgstr "ampliar"
+
+#: admin/forms.py:79
+msgid "Thumbnail option or resize parameters must be choosen."
+msgstr "Heu de triar una opció de miniatura o paràmetres de redimensionat."
+
+#: admin/imageadmin.py:20 admin/imageadmin.py:112
+msgid "Subject location"
+msgstr "Lloc del subjecte."
+
+#: admin/imageadmin.py:21
+msgid "Location of the main subject of the scene. Format: \"x,y\"."
+msgstr "Ubicació del subjecte principal de l'escena. Format: \"x,y\"."
+
+#: admin/imageadmin.py:59
+msgid "Invalid subject location format. "
+msgstr "Foirmat erroni de la ubicació del subjecte."
+
+#: admin/imageadmin.py:67
+msgid "Subject location is outside of the image. "
+msgstr "La ubicació del subjecte està fora de la imatge."
+
+#: admin/imageadmin.py:76
+#, python-brace-format
+msgid "Your input: \"{subject_location}\". "
+msgstr "La teva entrada: \"{subject_location}\"."
+
+#: admin/permissionadmin.py:10 models/foldermodels.py:380
+msgid "Who"
+msgstr ""
+
+#: admin/permissionadmin.py:11 models/foldermodels.py:401
+msgid "What"
+msgstr ""
+
+#: admin/views.py:55
+msgid "Folder with this name already exists."
+msgstr "Ja existeix una carpeta amb aquest nom."
+
+#: apps.py:11 templates/admin/filer/breadcrumbs.html:5
+#: templates/admin/filer/folder/change_form.html:7
+#: templates/admin/filer/folder/directory_listing.html:33
+msgid "Filer"
+msgstr "Filer"
+
+#: contrib/django_cms/cms_toolbars.py:44
+msgid "Media library"
+msgstr "Biblioteca de medis."
+
+#: models/abstract.py:62
+msgid "default alt text"
+msgstr "text alternatiu per defecte"
+
+#: models/abstract.py:69
+msgid "default caption"
+msgstr "títol per defecte"
+
+#: models/abstract.py:76
+msgid "subject location"
+msgstr "lloc del subjecte"
+
+#: models/abstract.py:91
+msgid "image"
+msgstr "imatge"
+
+#: models/abstract.py:92
+msgid "images"
+msgstr "imatges"
+
+#: models/abstract.py:148
+#, python-format
+msgid ""
+"Image format not recognized or image size exceeds limit of %(max_pixels)d "
+"million pixels by a factor of two or more. Before uploading again, check "
+"file format or resize image to %(width)d x %(height)d resolution or lower."
+msgstr ""
+
+#: models/abstract.py:156
+#, python-format
+msgid ""
+"Image size (%(pixels)d million pixels) exceeds limit of %(max_pixels)d "
+"million pixels. Before uploading again, resize image to %(width)d x "
+"%(height)d resolution or lower."
+msgstr ""
+
+#: models/clipboardmodels.py:11 models/foldermodels.py:289
+msgid "user"
+msgstr "usuari"
+
+#: models/clipboardmodels.py:17 models/filemodels.py:157
+msgid "files"
+msgstr "fitxers"
+
+#: models/clipboardmodels.py:24 models/clipboardmodels.py:50
+msgid "clipboard"
+msgstr "portapapers"
+
+#: models/clipboardmodels.py:25
+msgid "clipboards"
+msgstr "portapapers"
+
+#: models/clipboardmodels.py:44 models/filemodels.py:74
+#: models/filemodels.py:156
+msgid "file"
+msgstr "fitxer"
+
+#: models/clipboardmodels.py:56
+msgid "clipboard item"
+msgstr "element del portapapers"
+
+#: models/clipboardmodels.py:57
+msgid "clipboard items"
+msgstr "elements del portapapers"
+
+#: models/filemodels.py:66 templates/admin/filer/folder/change_form.html:8
+#: templates/admin/filer/folder/directory_listing.html:102
+#: templates/admin/filer/folder/new_folder_form.html:4
+#: templates/admin/filer/folder/new_folder_form.html:7
+#: templates/admin/filer/tools/clipboard/clipboard.html:27
+msgid "folder"
+msgstr "carpeta"
+
+#: models/filemodels.py:81
+msgid "file size"
+msgstr "tamany del fitxer"
+
+#: models/filemodels.py:87
+msgid "sha1"
+msgstr "sha1"
+
+#: models/filemodels.py:94
+msgid "has all mandatory data"
+msgstr "té totes les dades obligatòries"
+
+#: models/filemodels.py:100
+msgid "original filename"
+msgstr "nom de fitxer original"
+
+#: models/filemodels.py:110 models/foldermodels.py:102
+#: models/thumbnailoptionmodels.py:10
+msgid "name"
+msgstr "nom"
+
+#: models/filemodels.py:116
+msgid "description"
+msgstr "descripció"
+
+#: models/filemodels.py:125 models/foldermodels.py:108
+msgid "owner"
+msgstr "propietari"
+
+#: models/filemodels.py:129 models/foldermodels.py:116
+msgid "uploaded at"
+msgstr "pujat a"
+
+#: models/filemodels.py:134 models/foldermodels.py:126
+msgid "modified at"
+msgstr "modificat a"
+
+#: models/filemodels.py:140
+msgid "Permissions disabled"
+msgstr "Permisos desactivats"
+
+#: models/filemodels.py:141
+msgid ""
+"Disable any permission checking for this file. File will be publicly "
+"accessible to anyone."
+msgstr ""
+"Desactiveu tots els permisos d'aquest fitxer. Aquest serà accessible de "
+"forma pública per a tothom."
+
+#: models/foldermodels.py:94
+msgid "parent"
+msgstr ""
+
+#: models/foldermodels.py:121
+msgid "created at"
+msgstr "creat a"
+
+#: models/foldermodels.py:136 templates/admin/filer/breadcrumbs.html:6
+#: templates/admin/filer/folder/directory_listing.html:35
+msgid "Folder"
+msgstr "Carpeta"
+
+#: models/foldermodels.py:137
+#: templates/admin/filer/folder/directory_thumbnail_list.html:12
+msgid "Folders"
+msgstr "Carpetes"
+
+#: models/foldermodels.py:260
+msgid "all items"
+msgstr "tots els elements"
+
+#: models/foldermodels.py:261
+msgid "this item only"
+msgstr "només aquest element"
+
+#: models/foldermodels.py:262
+msgid "this item and all children"
+msgstr "aquest elements i els seus fills"
+
+#: models/foldermodels.py:266
+msgid "inherit"
+msgstr ""
+
+#: models/foldermodels.py:267
+msgid "allow"
+msgstr "permetre"
+
+#: models/foldermodels.py:268
+msgid "deny"
+msgstr "denegar"
+
+#: models/foldermodels.py:280
+msgid "type"
+msgstr "tipus"
+
+#: models/foldermodels.py:297
+msgid "group"
+msgstr "group"
+
+#: models/foldermodels.py:304
+msgid "everybody"
+msgstr "tothom"
+
+#: models/foldermodels.py:309
+msgid "can read"
+msgstr "pot llegir"
+
+#: models/foldermodels.py:317
+msgid "can edit"
+msgstr "pot editar"
+
+#: models/foldermodels.py:325
+msgid "can add children"
+msgstr "pot afegir fills"
+
+#: models/foldermodels.py:333
+msgid "folder permission"
+msgstr "permís de carpeta"
+
+#: models/foldermodels.py:334
+msgid "folder permissions"
+msgstr "permisos de carpeta"
+
+#: models/foldermodels.py:348
+msgid "Folder cannot be selected with type \"all items\"."
+msgstr ""
+
+#: models/foldermodels.py:350
+msgid "Folder has to be selected when type is not \"all items\"."
+msgstr ""
+
+#: models/foldermodels.py:352
+msgid "User or group cannot be selected together with \"everybody\"."
+msgstr ""
+
+#: models/foldermodels.py:354
+msgid "At least one of user, group, or \"everybody\" has to be selected."
+msgstr ""
+
+#: models/foldermodels.py:360
+msgid "All Folders"
+msgstr ""
+
+#: models/foldermodels.py:362
+msgid "Logical Path"
+msgstr ""
+
+#: models/foldermodels.py:371
+#, python-brace-format
+msgid "User: {user}"
+msgstr ""
+
+#: models/foldermodels.py:373
+#, python-brace-format
+msgid "Group: {group}"
+msgstr ""
+
+#: models/foldermodels.py:375
+msgid "Everybody"
+msgstr ""
+
+#: models/foldermodels.py:388 templates/admin/filer/widgets/admin_file.html:45
+msgid "Edit"
+msgstr ""
+
+#: models/foldermodels.py:389
+msgid "Read"
+msgstr ""
+
+#: models/foldermodels.py:390
+msgid "Add children"
+msgstr ""
+
+#: models/imagemodels.py:18
+msgid "date taken"
+msgstr "data agafada"
+
+#: models/imagemodels.py:25
+msgid "author"
+msgstr "autor"
+
+#: models/imagemodels.py:32
+msgid "must always publish author credit"
+msgstr "cal publicar sempre crèdits d'autor"
+
+#: models/imagemodels.py:37
+msgid "must always publish copyright"
+msgstr "cal publicar sempre els drets d'autor"
+
+#: models/thumbnailoptionmodels.py:16
+msgid "width in pixel."
+msgstr "ample en píxels."
+
+#: models/thumbnailoptionmodels.py:21
+msgid "height in pixel."
+msgstr "alt en píxels"
+
+#: models/thumbnailoptionmodels.py:38
+msgid "thumbnail options"
+msgstr "opcions de miniatura"
+
+#: models/virtualitems.py:46
+#: templates/admin/filer/folder/directory_table_list.html:4
+#: templates/admin/filer/folder/directory_table_list.html:170
+#: templates/admin/filer/folder/directory_thumbnail_list.html:5
+#: templates/admin/filer/folder/directory_thumbnail_list.html:146
+#: templates/admin/filer/tools/clipboard/clipboard.html:27
+msgid "Unsorted Uploads"
+msgstr "Pujades sense ordre"
+
+#: models/virtualitems.py:73
+msgid "files with missing metadata"
+msgstr "fitxer als que els falten metadades"
+
+#: models/virtualitems.py:87 templates/admin/filer/folder/change_form.html:8
+#: templates/admin/filer/folder/directory_listing.html:102
+msgid "root"
+msgstr "arrel"
+
+#: settings.py:273
+msgid "Show table view"
+msgstr ""
+
+#: settings.py:278
+msgid "Show thumbnail view"
+msgstr ""
+
+#: templates/admin/filer/actions.html:5
+msgid "Run the selected action"
+msgstr "Executar l'acció seleccionada"
+
+#: templates/admin/filer/actions.html:5
+msgid "Go"
+msgstr "Anar"
+
+#: templates/admin/filer/actions.html:14
+#: templates/admin/filer/folder/directory_table_list.html:235
+#: templates/admin/filer/folder/directory_thumbnail_list.html:210
+msgid "Click here to select the objects across all pages"
+msgstr ""
+"Cliqueu aquí per seleccionar els objectes a través de totes les pàgines"
+
+#: templates/admin/filer/actions.html:14
+#, python-format
+msgid "Select all %(total_count)s files and/or folders"
+msgstr "Seleccionar tots els %(total_count)s fitixers i/o carpetes"
+
+#: templates/admin/filer/actions.html:16
+#: templates/admin/filer/folder/directory_table_list.html:237
+#: templates/admin/filer/folder/directory_thumbnail_list.html:212
+msgid "Clear selection"
+msgstr "Netejar la selecció"
+
+#: templates/admin/filer/breadcrumbs.html:4
+#: templates/admin/filer/folder/directory_listing.html:28
+msgid "Go back to admin homepage"
+msgstr "Tornar a la pàgina principal d'administració"
+
+#: templates/admin/filer/breadcrumbs.html:4
+#: templates/admin/filer/folder/change_form.html:6
+#: templates/admin/filer/folder/directory_listing.html:29
+msgid "Home"
+msgstr "Inici"
+
+#: templates/admin/filer/breadcrumbs.html:5
+#: templates/admin/filer/folder/directory_listing.html:32
+msgid "Go back to Filer app"
+msgstr "Tornar a l'aplicació Filer"
+
+#: templates/admin/filer/breadcrumbs.html:6
+#: templates/admin/filer/folder/directory_listing.html:35
+msgid "Go back to root folder"
+msgstr "Tornar a la carpeta arrel"
+
+#: templates/admin/filer/breadcrumbs.html:8
+#: templates/admin/filer/breadcrumbs.html:18
+#: templates/admin/filer/folder/change_form.html:10
+#: templates/admin/filer/folder/directory_listing.html:39
+#: templates/admin/filer/folder/directory_listing.html:47
+#, python-format
+msgid "Go back to '%(folder_name)s' folder"
+msgstr "Tornar a la carpeta '%(folder_name)s'"
+
+#: templates/admin/filer/change_form.html:26
+msgid "Duplicates"
+msgstr "Duplicats"
+
+#: templates/admin/filer/delete_selected_files_confirmation.html:11
+msgid ""
+"Deleting the selected files and/or folders would result in deleting related "
+"objects, but your account doesn't have permission to delete the following "
+"types of objects:"
+msgstr ""
+"Esborrar els fitxers seleccionats provocarà l'esborrar d'objectes "
+"relacionats, però el teu compte no té permisos per esborrar els següents "
+"tipus d'objectes:"
+
+#: templates/admin/filer/delete_selected_files_confirmation.html:19
+msgid ""
+"Deleting the selected files and/or folders would require deleting the "
+"following protected related objects:"
+msgstr ""
+"Esborrer els fitxers i/o carpetes seleccionades provocarà l'esborrat dels "
+"següents objectes protegits relacionats:"
+
+#: templates/admin/filer/delete_selected_files_confirmation.html:27
+msgid ""
+"Are you sure you want to delete the selected files and/or folders? All of "
+"the following objects and their related items will be deleted:"
+msgstr ""
+"Segur que voleu esborrar els fitxers i/o carpetes seleccionades? Els "
+"següents objectes i els seus elements relacionats seran esborrats:"
+
+#: templates/admin/filer/delete_selected_files_confirmation.html:46
+#: templates/admin/filer/folder/choose_copy_destination.html:64
+#: templates/admin/filer/folder/choose_move_destination.html:72
+msgid "No, take me back"
+msgstr "No, porta'm enrere."
+
+#: templates/admin/filer/delete_selected_files_confirmation.html:47
+msgid "Yes, I'm sure"
+msgstr "Sí, estic segur"
+
+#: templates/admin/filer/file/change_form.html:35
+#: templates/admin/filer/folder/change_form.html:21
+#: templates/admin/filer/image/change_form.html:35
+msgid "History"
+msgstr "Història"
+
+#: templates/admin/filer/file/change_form.html:37
+#: templates/admin/filer/folder/change_form.html:23
+#: templates/admin/filer/image/change_form.html:37
+msgid "View on site"
+msgstr "Veure al lloc"
+
+#: templates/admin/filer/folder/change_form.html:6
+#: templates/admin/filer/folder/change_form.html:7
+#: templates/admin/filer/folder/change_form.html:8
+#: templates/admin/filer/folder/directory_listing.html:102
+msgid "Go back to"
+msgstr "Tornar a"
+
+#: templates/admin/filer/folder/change_form.html:6
+msgid "admin homepage"
+msgstr "pàgina principal d'administració"
+
+#: templates/admin/filer/folder/change_form.html:37
+#: templates/admin/filer/folder/directory_listing.html:95
+#: templates/admin/filer/folder/directory_listing.html:103
+#: templates/admin/filer/folder/directory_table_list.html:29
+#: templates/admin/filer/folder/directory_table_list.html:59
+#: templates/admin/filer/folder/directory_table_list.html:181
+#: templates/admin/filer/folder/directory_thumbnail_list.html:27
+#: templates/admin/filer/folder/directory_thumbnail_list.html:61
+#: templates/admin/filer/folder/directory_thumbnail_list.html:156
+msgid "Folder Icon"
+msgstr "Icona de la carpeta"
+
+#: templates/admin/filer/folder/choose_copy_destination.html:23
+msgid ""
+"Your account doesn't have permissions to copy all of the selected files and/"
+"or folders."
+msgstr "Sense permisos per copiar tots els fitxers i/o carpetes seleccionats."
+
+#: templates/admin/filer/folder/choose_copy_destination.html:25
+#: templates/admin/filer/folder/choose_copy_destination.html:31
+#: templates/admin/filer/folder/choose_copy_destination.html:37
+#: templates/admin/filer/folder/choose_move_destination.html:37
+#: templates/admin/filer/folder/choose_move_destination.html:43
+#: templates/admin/filer/folder/choose_move_destination.html:49
+msgid "Take me back"
+msgstr "Porta'm enrere"
+
+#: templates/admin/filer/folder/choose_copy_destination.html:29
+#: templates/admin/filer/folder/choose_move_destination.html:41
+msgid "There are no destination folders available."
+msgstr "No hi ha carpetes de destí disponibles."
+
+#: templates/admin/filer/folder/choose_copy_destination.html:35
+msgid "There are no files and/or folders available to copy."
+msgstr "No hi ha fitxers i/o carpetes disponibles."
+
+#: templates/admin/filer/folder/choose_copy_destination.html:40
+msgid ""
+"The following files and/or folders will be copied to a destination folder "
+"(retaining their tree structure):"
+msgstr ""
+"Els següents fitxers i/o carpetes seran copiats a una carpeta de destí "
+"(mantenint la seva estructura d'arbre):"
+
+#: templates/admin/filer/folder/choose_copy_destination.html:54
+#: templates/admin/filer/folder/choose_move_destination.html:64
+msgid "Destination folder:"
+msgstr "Carpeta de destí:"
+
+#: templates/admin/filer/folder/choose_copy_destination.html:65
+#: templates/admin/filer/folder/choose_copy_destination.html:68
+#: templates/admin/filer/folder/directory_listing.html:178
+msgid "Copy"
+msgstr "Copiar"
+
+#: templates/admin/filer/folder/choose_copy_destination.html:67
+msgid "It is not allowed to copy files into same folder"
+msgstr "No està permès copiar fitxers a la mateixa carpeta"
+
+#: templates/admin/filer/folder/choose_images_resize_options.html:15
+msgid ""
+"Your account doesn't have permissions to resize all of the selected images."
+msgstr ""
+"El vostre compte no té permisos per redimensionar totes les imatges "
+"seleccionades."
+
+#: templates/admin/filer/folder/choose_images_resize_options.html:18
+msgid "There are no images available to resize."
+msgstr "No hi ha imatges disponibles per redimensionar."
+
+#: templates/admin/filer/folder/choose_images_resize_options.html:20
+msgid "The following images will be resized:"
+msgstr "Les següents imatges seran redimensionades:"
+
+#: templates/admin/filer/folder/choose_images_resize_options.html:32
+msgid "Choose an existing thumbnail option or enter resize parameters:"
+msgstr ""
+"Trieu una opció de miniatura existent o especifiqueu paràmetres de "
+"redimensionat:"
+
+#: templates/admin/filer/folder/choose_images_resize_options.html:36
+msgid ""
+"Warning: Images will be resized in-place and originals will be lost. Maybe "
+"first make a copy of them to retain the originals."
+msgstr ""
+"Avís: les imatges seran redimensionades al mateix lloc i les originals es "
+"perdran. Considereu realitzar abans una còpia d'aquestes per mantenir els "
+"originals."
+
+#: templates/admin/filer/folder/choose_images_resize_options.html:39
+msgid "Resize"
+msgstr "Redimensionar"
+
+#: templates/admin/filer/folder/choose_move_destination.html:35
+msgid ""
+"Your account doesn't have permissions to move all of the selected files and/"
+"or folders."
+msgstr ""
+"El vostre compte no té permisos per moure tots els fitxers i/o carpetes "
+"seleccionats."
+
+#: templates/admin/filer/folder/choose_move_destination.html:47
+msgid "There are no files and/or folders available to move."
+msgstr "No hi ha fitxers i/o carpetes disponibles per moure."
+
+#: templates/admin/filer/folder/choose_move_destination.html:52
+msgid ""
+"The following files and/or folders will be moved to a destination folder "
+"(retaining their tree structure):"
+msgstr ""
+"Els següents fitxers i/o carpetes seran moguts a una carpeta de destí "
+"(mantenint la seva estructura d'arbre):"
+
+#: templates/admin/filer/folder/choose_move_destination.html:73
+#: templates/admin/filer/folder/choose_move_destination.html:76
+#: templates/admin/filer/folder/directory_listing.html:181
+msgid "Move"
+msgstr "Moure"
+
+#: templates/admin/filer/folder/choose_move_destination.html:75
+msgid "It is not allowed to move files into same folder"
+msgstr "No està permès moure fitxers a la mateixa carpeta"
+
+#: templates/admin/filer/folder/choose_rename_format.html:15
+msgid ""
+"Your account doesn't have permissions to rename all of the selected files."
+msgstr ""
+"El vostre compte no té permisos per canviar el nom de tots els fitxers "
+"seleccionats."
+
+#: templates/admin/filer/folder/choose_rename_format.html:18
+msgid "There are no files available to rename."
+msgstr "No hi ha fitxers per canviar el nom disponibles."
+
+#: templates/admin/filer/folder/choose_rename_format.html:20
+msgid ""
+"The following files will be renamed (they will stay in their folders and "
+"keep original filename, only displayed filename will be changed):"
+msgstr ""
+"En canviarà el nom dels següents fitxers (seguiran estant a les seves "
+"carpetes i mantindran el seu nom de fitxers original, només serà modificat "
+"el nom que es mostrarà):"
+
+#: templates/admin/filer/folder/choose_rename_format.html:59
+msgid "Rename"
+msgstr "Canviar el nom"
+
+#: templates/admin/filer/folder/directory_listing.html:94
+msgid "Go back to the parent folder"
+msgstr "Tornar a la carpeta pare"
+
+#: templates/admin/filer/folder/directory_listing.html:131
+msgid "Change current folder details"
+msgstr "Modificar els detalls de la carpeta actual"
+
+#: templates/admin/filer/folder/directory_listing.html:131
+msgid "Change"
+msgstr "Modificar"
+
+#: templates/admin/filer/folder/directory_listing.html:141
+msgid "Click here to run search for entered phrase"
+msgstr "Cliqueu aquí per cercar la frase introduïda"
+
+#: templates/admin/filer/folder/directory_listing.html:144
+msgid "Search"
+msgstr "Cercar"
+
+#: templates/admin/filer/folder/directory_listing.html:151
+msgid "Close"
+msgstr "Tancar"
+
+#: templates/admin/filer/folder/directory_listing.html:153
+msgid "Limit"
+msgstr "Limitar"
+
+#: templates/admin/filer/folder/directory_listing.html:158
+msgid "Check it to limit the search to current folder"
+msgstr "Marqueu per limitar la cercar a la carpeta actual"
+
+#: templates/admin/filer/folder/directory_listing.html:159
+msgid "Limit the search to current folder"
+msgstr "Limitar la cerca a la carpeta actual"
+
+#: templates/admin/filer/folder/directory_listing.html:175
+msgid "Delete"
+msgstr "Esborrar"
+
+#: templates/admin/filer/folder/directory_listing.html:203
+msgid "Adds a new Folder"
+msgstr "Afegir una Carpeta nova"
+
+#: templates/admin/filer/folder/directory_listing.html:206
+msgid "New Folder"
+msgstr "Carpeta nova"
+
+#: templates/admin/filer/folder/directory_listing.html:211
+#: templates/admin/filer/folder/directory_listing.html:218
+#: templates/admin/filer/folder/directory_listing.html:221
+#: templates/admin/filer/folder/directory_listing.html:228
+#: templates/admin/filer/folder/directory_listing.html:235
+msgid "Upload Files"
+msgstr "Pujar Arxius"
+
+#: templates/admin/filer/folder/directory_listing.html:233
+msgid "You have to select a folder first"
+msgstr "Cal que abans seleccioneu una carpeta"
+
+#: templates/admin/filer/folder/directory_table_list.html:16
+msgid "Name"
+msgstr "Nom"
+
+#: templates/admin/filer/folder/directory_table_list.html:17
+#: templates/admin/filer/tools/detail_info.html:50
+msgid "Owner"
+msgstr "Propietari"
+
+#: templates/admin/filer/folder/directory_table_list.html:18
+#: templates/admin/filer/tools/detail_info.html:34
+msgid "Size"
+msgstr "Mida"
+
+#: templates/admin/filer/folder/directory_table_list.html:19
+msgid "Action"
+msgstr "Acció"
+
+#: templates/admin/filer/folder/directory_table_list.html:28
+#: templates/admin/filer/folder/directory_table_list.html:35
+#: templates/admin/filer/folder/directory_table_list.html:58
+#: templates/admin/filer/folder/directory_table_list.html:65
+#: templates/admin/filer/folder/directory_thumbnail_list.html:26
+#: templates/admin/filer/folder/directory_thumbnail_list.html:32
+#: templates/admin/filer/folder/directory_thumbnail_list.html:60
+#: templates/admin/filer/folder/directory_thumbnail_list.html:66
+#, python-format
+msgid "Change '%(item_label)s' folder details"
+msgstr "Modificar els detalls de la carpeta '%(item_label)s'"
+
+#: templates/admin/filer/folder/directory_table_list.html:76
+#: templates/admin/filer/tools/search_form.html:5
+#, python-format
+msgid "%(counter)s folder"
+msgid_plural "%(counter)s folders"
+msgstr[0] "%(counter)s fitxer"
+msgstr[1] "%(counter)s fitxers"
+
+#: templates/admin/filer/folder/directory_table_list.html:77
+#: templates/admin/filer/tools/search_form.html:6
+#, python-format
+msgid "%(counter)s file"
+msgid_plural "%(counter)s files"
+msgstr[0] "%(counter)s fitxer"
+msgstr[1] "%(counter)s fitxers"
+
+#: templates/admin/filer/folder/directory_table_list.html:83
+msgid "Change folder details"
+msgstr "Modifcar detalls de carpeta"
+
+#: templates/admin/filer/folder/directory_table_list.html:84
+msgid "Remove folder"
+msgstr "Esborrar carpeta"
+
+#: templates/admin/filer/folder/directory_table_list.html:96
+#: templates/admin/filer/folder/directory_table_list.html:104
+#: templates/admin/filer/folder/directory_table_list.html:119
+#: templates/admin/filer/folder/directory_thumbnail_list.html:95
+#: templates/admin/filer/folder/directory_thumbnail_list.html:104
+#: templates/admin/filer/folder/directory_thumbnail_list.html:119
+msgid "Select this file"
+msgstr "Seleccionar aquest fitxer"
+
+#: templates/admin/filer/folder/directory_table_list.html:107
+#: templates/admin/filer/folder/directory_table_list.html:122
+#: templates/admin/filer/folder/directory_table_list.html:154
+#: templates/admin/filer/folder/directory_thumbnail_list.html:107
+#: templates/admin/filer/folder/directory_thumbnail_list.html:122
+#, python-format
+msgid "Change '%(item_label)s' details"
+msgstr "Modificar els detalls de '%(item_label)s'"
+
+#: templates/admin/filer/folder/directory_table_list.html:131
+#: templates/admin/filer/folder/directory_thumbnail_list.html:130
+msgid "Permissions"
+msgstr "Permisos"
+
+#: templates/admin/filer/folder/directory_table_list.html:131
+#: templates/admin/filer/folder/directory_thumbnail_list.html:130
+msgid "disabled"
+msgstr "desactivat"
+
+#: templates/admin/filer/folder/directory_table_list.html:131
+#: templates/admin/filer/folder/directory_thumbnail_list.html:130
+msgid "enabled"
+msgstr "activat"
+
+#: templates/admin/filer/folder/directory_table_list.html:144
+#, fuzzy
+#| msgid "Move selected files to clipboard"
+msgid "URL copied to clipboard"
+msgstr "Moure els fitxers seleccionats al portapapers."
+
+#: templates/admin/filer/folder/directory_table_list.html:147
+#, python-format
+msgid "Canonical url '%(item_label)s'"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:151
+#, python-format
+msgid "Download '%(item_label)s'"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:155
+msgid "Remove file"
+msgstr "Esborrar fitxer"
+
+#: templates/admin/filer/folder/directory_table_list.html:163
+#: templates/admin/filer/folder/directory_thumbnail_list.html:138
+msgid "Drop files here or use the \"Upload Files\" button"
+msgstr "Deixeu anar fitxers aquí o utilitzeu el botó \"Pujar Fitxers\""
+
+#: templates/admin/filer/folder/directory_table_list.html:178
+#: templates/admin/filer/folder/directory_thumbnail_list.html:153
+msgid "Drop your file to upload into:"
+msgstr "Deixeu anar el fitxer a pujar a:"
+
+#: templates/admin/filer/folder/directory_table_list.html:188
+#: templates/admin/filer/folder/directory_thumbnail_list.html:163
+msgid "Upload"
+msgstr "Pujar"
+
+#: templates/admin/filer/folder/directory_table_list.html:200
+#: templates/admin/filer/folder/directory_thumbnail_list.html:175
+msgid "cancel"
+msgstr "cancel·lar"
+
+#: templates/admin/filer/folder/directory_table_list.html:204
+#: templates/admin/filer/folder/directory_thumbnail_list.html:179
+msgid "Upload success!"
+msgstr "Pujada correcta!"
+
+#: templates/admin/filer/folder/directory_table_list.html:208
+#: templates/admin/filer/folder/directory_thumbnail_list.html:183
+msgid "Upload canceled!"
+msgstr "Pujada cancel·lada!"
+
+#: templates/admin/filer/folder/directory_table_list.html:215
+#: templates/admin/filer/folder/directory_thumbnail_list.html:190
+msgid "previous"
+msgstr "anterior"
+
+#: templates/admin/filer/folder/directory_table_list.html:220
+#: templates/admin/filer/folder/directory_thumbnail_list.html:195
+#, python-format
+msgid "Page %(number)s of %(num_pages)s."
+msgstr "Pàgina %(number)s de %(num_pages)s."
+
+#: templates/admin/filer/folder/directory_table_list.html:225
+#: templates/admin/filer/folder/directory_thumbnail_list.html:200
+msgid "next"
+msgstr "següent"
+
+#: templates/admin/filer/folder/directory_table_list.html:235
+#: templates/admin/filer/folder/directory_thumbnail_list.html:210
+#, python-format
+msgid "Select all %(total_count)s"
+msgstr "Seleccionar tots/es %(total_count)s"
+
+#: templates/admin/filer/folder/directory_thumbnail_list.html:15
+#: templates/admin/filer/folder/directory_thumbnail_list.html:80
+msgid "Select all"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_thumbnail_list.html:77
+msgid "Files"
+msgstr ""
+
+#: templates/admin/filer/folder/new_folder_form.html:4
+#: templates/admin/filer/folder/new_folder_form.html:7
+msgid "Add new"
+msgstr "Afegir nou"
+
+#: templates/admin/filer/folder/new_folder_form.html:4
+msgid "Django site admin"
+msgstr ""
+
+#: templates/admin/filer/folder/new_folder_form.html:15
+msgid "Please correct the error below."
+msgid_plural "Please correct the errors below."
+msgstr[0] "Corregiu el següent error."
+msgstr[1] "Corregiu els següents errors."
+
+#: templates/admin/filer/folder/new_folder_form.html:30
+msgid "Save"
+msgstr "Desar"
+
+#: templates/admin/filer/templatetags/file_icon.html:9
+msgid "Your browser does not support audio."
+msgstr ""
+
+#: templates/admin/filer/templatetags/file_icon.html:14
+msgid "Your browser does not support video."
+msgstr ""
+
+#: templates/admin/filer/tools/clipboard/clipboard.html:9
+msgid "Clipboard"
+msgstr "Portapapers"
+
+#: templates/admin/filer/tools/clipboard/clipboard.html:21
+msgid "Paste all items here"
+msgstr "Enganxar aquí tots els elements"
+
+#: templates/admin/filer/tools/clipboard/clipboard.html:27
+msgid "Move all clipboard files to"
+msgstr "Moure tots els fitxer del portapapers a"
+
+#: templates/admin/filer/tools/clipboard/clipboard.html:27
+msgid "Empty Clipboard"
+msgstr "Buidar Porta-retalls"
+
+#: templates/admin/filer/tools/clipboard/clipboard.html:50
+msgid "the clipboard is empty"
+msgstr "el portapapers és buït"
+
+#: templates/admin/filer/tools/clipboard/clipboard_item_rows.html:16
+msgid "upload failed"
+msgstr "ha fallat la pujada"
+
+#: templates/admin/filer/tools/detail_info.html:11
+msgid "Download"
+msgstr ""
+
+#: templates/admin/filer/tools/detail_info.html:16
+msgid "Expand"
+msgstr ""
+
+#: templates/admin/filer/tools/detail_info.html:21
+#: templates/admin/filer/widgets/admin_file.html:32
+#: templatetags/filer_admin_tags.py:108
+msgid "File is missing"
+msgstr ""
+
+#: templates/admin/filer/tools/detail_info.html:30
+msgid "Type"
+msgstr "Tipus"
+
+#: templates/admin/filer/tools/detail_info.html:38
+msgid "File-size"
+msgstr "MIda de fitxer"
+
+#: templates/admin/filer/tools/detail_info.html:42
+msgid "Modified"
+msgstr "Modificat"
+
+#: templates/admin/filer/tools/detail_info.html:46
+msgid "Created"
+msgstr "Creat"
+
+#: templates/admin/filer/tools/search_form.html:5
+msgid "found"
+msgstr "trobat"
+
+#: templates/admin/filer/tools/search_form.html:5
+msgid "and"
+msgstr "i"
+
+#: templates/admin/filer/tools/search_form.html:7
+msgid "cancel search"
+msgstr "cancel·lar la cerca"
+
+#: templates/admin/filer/widgets/admin_file.html:10
+#: templates/admin/filer/widgets/admin_file.html:39
+#: templates/admin/filer/widgets/admin_folder.html:18
+msgid "Clear"
+msgstr "Netejar"
+
+#: templates/admin/filer/widgets/admin_file.html:22
+msgid "or drop your file here"
+msgstr "o deixar anar aquí el fitxer"
+
+#: templates/admin/filer/widgets/admin_file.html:35
+msgid "no file selected"
+msgstr "no s'ha seleccionat cap fitxer"
+
+#: templates/admin/filer/widgets/admin_file.html:50
+#: templates/admin/filer/widgets/admin_folder.html:14
+msgid "Lookup"
+msgstr "Cercar"
+
+#: templates/admin/filer/widgets/admin_file.html:51
+msgid "Choose File"
+msgstr "Escollir Fitxer"
+
+#: templates/admin/filer/widgets/admin_folder.html:16
+msgid "Choose Folder"
+msgstr ""
+
+#: validation.py:20
+#, python-brace-format
+msgid "File \"{file_name}\": Upload denied by site security policy"
+msgstr ""
+
+#: validation.py:23
+#, python-brace-format
+msgid "File \"{file_name}\": {file_type} upload denied by site security policy"
+msgstr ""
+
+#: validation.py:34
+#, python-brace-format
+msgid "File \"{file_name}\": HTML upload denied by site security policy"
+msgstr ""
+
+#: validation.py:72
+#, python-brace-format
+msgid ""
+"File \"{file_name}\": Rejected due to potential cross site scripting "
+"vulnerability"
+msgstr ""
+
+#: validation.py:84
+#, python-brace-format
+msgid "File \"{file_name}\": SVG file format not recognized"
+msgstr ""
+
+#~ msgid "Resize parameters must be choosen."
+#~ msgstr "Resize parameters must be choosen."
+
+#~ msgid "Choose resize parameters:"
+#~ msgstr "Choose resize parameters:"
+
+#~ msgid "Open file"
+#~ msgstr "Open file"
+
+#~ msgid "Full size preview"
+#~ msgstr "Full size preview"
+
+#~ msgid "Add Description"
+#~ msgstr "Add Description"
+
+#~ msgid "Author"
+#~ msgstr "Author"
+
+#~ msgid "Add Alt-Text"
+#~ msgstr "Add Alt-Text"
+
+#~ msgid "Add caption"
+#~ msgstr "Add caption"
+
+#~ msgid "Add Tags"
+#~ msgstr "Add Tags"
+
+#~ msgid "Change File"
+#~ msgstr "Change File"
+
+#~ msgid "none selected"
+#~ msgstr "none selected"
+
+#~ msgid "Add Folder"
+#~ msgstr "Add Folder"
+
+#~ msgid "Subject Location"
+#~ msgstr "Subject location"
+
+#~ msgid ""
+#~ "\n"
+#~ " %(counter)s file"
+#~ msgid_plural ""
+#~ "%(counter)s files\n"
+#~ " "
+#~ msgstr[0] "15c0f2d28d6dab1af1f6d94906beb627_pl_0"
+#~ msgstr[1] "15c0f2d28d6dab1af1f6d94906beb627_pl_1"
+
+#~ msgid "Upload File"
+#~ msgstr "Upload File"
diff --git a/filer/locale/cs/LC_MESSAGES/django.mo b/filer/locale/cs/LC_MESSAGES/django.mo
new file mode 100644
index 0000000000000000000000000000000000000000..c73f2c7d9f66a1ae47f96ccc6cb4f2b9aa7cfdb9
GIT binary patch
literal 16442
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
literal 0
HcmV?d00001
diff --git a/filer/locale/cs/LC_MESSAGES/django.po b/filer/locale/cs/LC_MESSAGES/django.po
new file mode 100644
index 000000000..e56d59634
--- /dev/null
+++ b/filer/locale/cs/LC_MESSAGES/django.po
@@ -0,0 +1,1265 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+#
+# Translators:
+# Translators:
+# Translators:
+# Jakub Dorňák , 2020
+# Mirek Simek , 2016
+msgid ""
+msgstr ""
+"Project-Id-Version: django Filer\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2023-09-30 18:10+0200\n"
+"PO-Revision-Date: 2012-07-13 15:50+0000\n"
+"Last-Translator: Jakub Dorňák , 2020\n"
+"Language-Team: Czech (http://app.transifex.com/divio/django-filer/language/"
+"cs/)\n"
+"Language: cs\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n "
+"<= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n"
+
+#: admin/clipboardadmin.py:17
+msgid "You do not have permission to upload files."
+msgstr ""
+
+#: admin/clipboardadmin.py:18
+msgid "Can't find folder to upload. Please refresh and try again"
+msgstr ""
+
+#: admin/clipboardadmin.py:20
+msgid "Can't use this folder, Permission Denied. Please select another folder."
+msgstr ""
+
+#: admin/fileadmin.py:73
+msgid "Advanced"
+msgstr "Pokročilé"
+
+#: admin/fileadmin.py:188
+msgid "canonical URL"
+msgstr "kanonický odkaz"
+
+#: admin/folderadmin.py:411 admin/folderadmin.py:582
+msgid ""
+"Items must be selected in order to perform actions on them. No items have "
+"been changed."
+msgstr "Nejdříve vyberte položku, která má být změněna."
+
+#: admin/folderadmin.py:434
+#, python-format
+msgid "%(total_count)s selected"
+msgid_plural "All %(total_count)s selected"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+#: admin/folderadmin.py:460
+#, python-format
+msgid "Directory listing for %(folder_name)s"
+msgstr "Výpis složky %(folder_name)s"
+
+#: admin/folderadmin.py:475
+#, python-format
+msgid "0 of %(cnt)s selected"
+msgstr "žádná položka z %(cnt)s není vybrána"
+
+#: admin/folderadmin.py:612
+msgid "No action selected."
+msgstr "Nebyla vybrána žádná operace."
+
+#: admin/folderadmin.py:664
+#, python-format
+msgid "Successfully moved %(count)d files to clipboard."
+msgstr "Soubory (%(count)d) byly přesunuty do schránky."
+
+#: admin/folderadmin.py:669
+msgid "Move selected files to clipboard"
+msgstr "Přesunout vybrané soubory do schránky"
+
+#: admin/folderadmin.py:709
+#, python-format
+msgid "Successfully disabled permissions for %(count)d files."
+msgstr "Pro vybrané soubory (%(count)d) byla deaktivována oprávnění."
+
+#: admin/folderadmin.py:711
+#, python-format
+msgid "Successfully enabled permissions for %(count)d files."
+msgstr "Pro vybrané soubory (%(count)d) byla aktivována oprávnění."
+
+#: admin/folderadmin.py:719
+msgid "Enable permissions for selected files"
+msgstr "Aktivovat oprávnění pro vybrané soubory"
+
+#: admin/folderadmin.py:725
+msgid "Disable permissions for selected files"
+msgstr "Deaktivovat oprávnění pro vybrané soubory"
+
+#: admin/folderadmin.py:789
+#, python-format
+msgid "Successfully deleted %(count)d files and/or folders."
+msgstr "Vybrané soubory a/nebo složky (%(count)d) byly smazány."
+
+#: admin/folderadmin.py:794
+msgid "Cannot delete files and/or folders"
+msgstr "Nemohu smazat soubory a/nebo složky"
+
+#: admin/folderadmin.py:796
+msgid "Are you sure?"
+msgstr "Opravdu provést?"
+
+#: admin/folderadmin.py:802
+msgid "Delete files and/or folders"
+msgstr "Smazat soubory a/nebo složky"
+
+#: admin/folderadmin.py:823
+msgid "Delete selected files and/or folders"
+msgstr "Smazat vybrané soubory a/nebo složky"
+
+#: admin/folderadmin.py:930
+#, python-format
+msgid "Folders with names %s already exist at the selected destination"
+msgstr "Složky pojmenované %s již v cílové složce existují"
+
+#: admin/folderadmin.py:934
+#, python-format
+msgid ""
+"Successfully moved %(count)d files and/or folders to folder "
+"'%(destination)s'."
+msgstr ""
+"Vybrané soubory a/nebo složky (%(count)d) byly přesunuty do složky "
+"'%(destination)s'."
+
+#: admin/folderadmin.py:942 admin/folderadmin.py:944
+msgid "Move files and/or folders"
+msgstr "Přesunout soubory a/nebo složky"
+
+#: admin/folderadmin.py:959
+msgid "Move selected files and/or folders"
+msgstr "Přesunout vybrané soubory a/nebo složky"
+
+#: admin/folderadmin.py:1016
+#, python-format
+msgid "Successfully renamed %(count)d files."
+msgstr "Soubory (%(count)d) vyly přejmenovány."
+
+#: admin/folderadmin.py:1025 admin/folderadmin.py:1027
+#: admin/folderadmin.py:1042
+msgid "Rename files"
+msgstr "Přejmenovat soubory"
+
+#: admin/folderadmin.py:1137
+#, python-format
+msgid ""
+"Successfully copied %(count)d files and/or folders to folder "
+"'%(destination)s'."
+msgstr ""
+"Soubory a/nebo složky (%(count)d) byly zkopírovány do složky "
+"'%(destination)s'."
+
+#: admin/folderadmin.py:1155 admin/folderadmin.py:1157
+msgid "Copy files and/or folders"
+msgstr "Kopírovat soubory a/nebo složky"
+
+#: admin/folderadmin.py:1174
+msgid "Copy selected files and/or folders"
+msgstr "Kopírovat vybrané soubory a/nebo složky"
+
+#: admin/folderadmin.py:1279
+#, python-format
+msgid "Successfully resized %(count)d images."
+msgstr "Velikost obrázků (%(count)d) byla změněna."
+
+#: admin/folderadmin.py:1286 admin/folderadmin.py:1288
+msgid "Resize images"
+msgstr "Změnit velikost obrázků"
+
+#: admin/folderadmin.py:1303
+msgid "Resize selected images"
+msgstr "Změnit velikost vybraných obrázků"
+
+#: admin/forms.py:24
+msgid "Suffix which will be appended to filenames of copied files."
+msgstr "Koncovka, která bude přidána ke jménům kopírovaných souborů."
+
+#: admin/forms.py:31
+#, python-format
+msgid ""
+"Suffix should be a valid, simple and lowercase filename part, like "
+"\"%(valid)s\"."
+msgstr ""
+"Koncovka by měla být jednoduchá součást názvu souboru malými písmeny, "
+"například \"%(valid)s\"."
+
+#: admin/forms.py:52
+#, python-format
+msgid "Unknown rename format value key \"%(key)s\"."
+msgstr "Neznámý formát klíče pro přejmenování \"%(key)s\"."
+
+#: admin/forms.py:54
+#, python-format
+msgid "Invalid rename format: %(error)s."
+msgstr "Špatný formát přejmenování: %(error)s."
+
+#: admin/forms.py:68 models/thumbnailoptionmodels.py:37
+msgid "thumbnail option"
+msgstr "volby náhledu"
+
+#: admin/forms.py:72 models/thumbnailoptionmodels.py:15
+msgid "width"
+msgstr "šířka"
+
+#: admin/forms.py:73 models/thumbnailoptionmodels.py:20
+msgid "height"
+msgstr "výška"
+
+#: admin/forms.py:74 models/thumbnailoptionmodels.py:25
+msgid "crop"
+msgstr "oříznout"
+
+#: admin/forms.py:75 models/thumbnailoptionmodels.py:30
+msgid "upscale"
+msgstr "zvětšit"
+
+#: admin/forms.py:79
+msgid "Thumbnail option or resize parameters must be choosen."
+msgstr ""
+"Musíte vybrat předdefinované volby náhledu nebo nastavit jednotlivé "
+"parametry."
+
+#: admin/imageadmin.py:20 admin/imageadmin.py:112
+msgid "Subject location"
+msgstr "Pozice objektu"
+
+#: admin/imageadmin.py:21
+msgid "Location of the main subject of the scene. Format: \"x,y\"."
+msgstr "Pozice hlavního předmětu fotografie. Formát \"x,y\"."
+
+#: admin/imageadmin.py:59
+msgid "Invalid subject location format. "
+msgstr "Špatný formát pozice objektu. "
+
+#: admin/imageadmin.py:67
+msgid "Subject location is outside of the image. "
+msgstr "Zadaná pozice objektu je mimo obrázek. "
+
+#: admin/imageadmin.py:76
+#, python-brace-format
+msgid "Your input: \"{subject_location}\". "
+msgstr "Vaše zadání: \"{subject_location}\". "
+
+#: admin/permissionadmin.py:10 models/foldermodels.py:380
+msgid "Who"
+msgstr ""
+
+#: admin/permissionadmin.py:11 models/foldermodels.py:401
+msgid "What"
+msgstr ""
+
+#: admin/views.py:55
+msgid "Folder with this name already exists."
+msgstr "Složka s tímto názvem již existuje."
+
+#: apps.py:11 templates/admin/filer/breadcrumbs.html:5
+#: templates/admin/filer/folder/change_form.html:7
+#: templates/admin/filer/folder/directory_listing.html:33
+msgid "Filer"
+msgstr "Správce souborů"
+
+#: contrib/django_cms/cms_toolbars.py:44
+msgid "Media library"
+msgstr "Knihovna médií"
+
+#: models/abstract.py:62
+msgid "default alt text"
+msgstr "výchozí alternativní text"
+
+#: models/abstract.py:69
+msgid "default caption"
+msgstr "výchozí popisek"
+
+#: models/abstract.py:76
+msgid "subject location"
+msgstr "poloha objektu"
+
+#: models/abstract.py:91
+msgid "image"
+msgstr "obrázek"
+
+#: models/abstract.py:92
+msgid "images"
+msgstr "obrázky"
+
+#: models/abstract.py:148
+#, python-format
+msgid ""
+"Image format not recognized or image size exceeds limit of %(max_pixels)d "
+"million pixels by a factor of two or more. Before uploading again, check "
+"file format or resize image to %(width)d x %(height)d resolution or lower."
+msgstr ""
+
+#: models/abstract.py:156
+#, python-format
+msgid ""
+"Image size (%(pixels)d million pixels) exceeds limit of %(max_pixels)d "
+"million pixels. Before uploading again, resize image to %(width)d x "
+"%(height)d resolution or lower."
+msgstr ""
+
+#: models/clipboardmodels.py:11 models/foldermodels.py:289
+msgid "user"
+msgstr "uživatel"
+
+#: models/clipboardmodels.py:17 models/filemodels.py:157
+msgid "files"
+msgstr "soubory"
+
+#: models/clipboardmodels.py:24 models/clipboardmodels.py:50
+msgid "clipboard"
+msgstr "schránka"
+
+#: models/clipboardmodels.py:25
+msgid "clipboards"
+msgstr "schránky"
+
+#: models/clipboardmodels.py:44 models/filemodels.py:74
+#: models/filemodels.py:156
+msgid "file"
+msgstr "soubor"
+
+#: models/clipboardmodels.py:56
+msgid "clipboard item"
+msgstr "položka schránky"
+
+#: models/clipboardmodels.py:57
+msgid "clipboard items"
+msgstr "položky schránky"
+
+#: models/filemodels.py:66 templates/admin/filer/folder/change_form.html:8
+#: templates/admin/filer/folder/directory_listing.html:102
+#: templates/admin/filer/folder/new_folder_form.html:4
+#: templates/admin/filer/folder/new_folder_form.html:7
+#: templates/admin/filer/tools/clipboard/clipboard.html:27
+msgid "folder"
+msgstr "složka"
+
+#: models/filemodels.py:81
+msgid "file size"
+msgstr "velikost souboru"
+
+#: models/filemodels.py:87
+msgid "sha1"
+msgstr "sha1"
+
+#: models/filemodels.py:94
+msgid "has all mandatory data"
+msgstr "má všechna povinná data"
+
+#: models/filemodels.py:100
+msgid "original filename"
+msgstr "původní název souboru"
+
+#: models/filemodels.py:110 models/foldermodels.py:102
+#: models/thumbnailoptionmodels.py:10
+msgid "name"
+msgstr "název"
+
+#: models/filemodels.py:116
+msgid "description"
+msgstr "popis"
+
+#: models/filemodels.py:125 models/foldermodels.py:108
+msgid "owner"
+msgstr "vlastník"
+
+#: models/filemodels.py:129 models/foldermodels.py:116
+msgid "uploaded at"
+msgstr "nahráno"
+
+#: models/filemodels.py:134 models/foldermodels.py:126
+msgid "modified at"
+msgstr "změněno"
+
+#: models/filemodels.py:140
+msgid "Permissions disabled"
+msgstr "Oprávnění neaktivní"
+
+#: models/filemodels.py:141
+msgid ""
+"Disable any permission checking for this file. File will be publicly "
+"accessible to anyone."
+msgstr ""
+"Deaktivovat veškerá oprávnění pro tento soubor. Soubor bude komukoli veřejně "
+"dostupný."
+
+#: models/foldermodels.py:94
+msgid "parent"
+msgstr ""
+
+#: models/foldermodels.py:121
+msgid "created at"
+msgstr "vytvořeno"
+
+#: models/foldermodels.py:136 templates/admin/filer/breadcrumbs.html:6
+#: templates/admin/filer/folder/directory_listing.html:35
+msgid "Folder"
+msgstr "Složka"
+
+#: models/foldermodels.py:137
+#: templates/admin/filer/folder/directory_thumbnail_list.html:12
+msgid "Folders"
+msgstr "Složky"
+
+#: models/foldermodels.py:260
+msgid "all items"
+msgstr "všechny položky"
+
+#: models/foldermodels.py:261
+msgid "this item only"
+msgstr "pouze tato položka"
+
+#: models/foldermodels.py:262
+msgid "this item and all children"
+msgstr "tato položka včetně všech potomků"
+
+#: models/foldermodels.py:266
+msgid "inherit"
+msgstr ""
+
+#: models/foldermodels.py:267
+msgid "allow"
+msgstr "povolit"
+
+#: models/foldermodels.py:268
+msgid "deny"
+msgstr "zakázat"
+
+#: models/foldermodels.py:280
+msgid "type"
+msgstr "typ"
+
+#: models/foldermodels.py:297
+msgid "group"
+msgstr "skupina"
+
+#: models/foldermodels.py:304
+msgid "everybody"
+msgstr "kdokoli"
+
+#: models/foldermodels.py:309
+msgid "can read"
+msgstr "smí číst"
+
+#: models/foldermodels.py:317
+msgid "can edit"
+msgstr "smí upravit"
+
+#: models/foldermodels.py:325
+msgid "can add children"
+msgstr "smí přidávat potomky"
+
+#: models/foldermodels.py:333
+msgid "folder permission"
+msgstr "oprávnění složky"
+
+#: models/foldermodels.py:334
+msgid "folder permissions"
+msgstr "oprávnění složky"
+
+#: models/foldermodels.py:348
+msgid "Folder cannot be selected with type \"all items\"."
+msgstr ""
+
+#: models/foldermodels.py:350
+msgid "Folder has to be selected when type is not \"all items\"."
+msgstr ""
+
+#: models/foldermodels.py:352
+msgid "User or group cannot be selected together with \"everybody\"."
+msgstr ""
+
+#: models/foldermodels.py:354
+msgid "At least one of user, group, or \"everybody\" has to be selected."
+msgstr ""
+
+#: models/foldermodels.py:360
+msgid "All Folders"
+msgstr ""
+
+#: models/foldermodels.py:362
+msgid "Logical Path"
+msgstr ""
+
+#: models/foldermodels.py:371
+#, python-brace-format
+msgid "User: {user}"
+msgstr ""
+
+#: models/foldermodels.py:373
+#, python-brace-format
+msgid "Group: {group}"
+msgstr ""
+
+#: models/foldermodels.py:375
+msgid "Everybody"
+msgstr ""
+
+#: models/foldermodels.py:388 templates/admin/filer/widgets/admin_file.html:45
+msgid "Edit"
+msgstr ""
+
+#: models/foldermodels.py:389
+msgid "Read"
+msgstr ""
+
+#: models/foldermodels.py:390
+msgid "Add children"
+msgstr ""
+
+#: models/imagemodels.py:18
+msgid "date taken"
+msgstr "datum pořízení"
+
+#: models/imagemodels.py:25
+msgid "author"
+msgstr "autor"
+
+#: models/imagemodels.py:32
+msgid "must always publish author credit"
+msgstr "autor musí být vždy uveden"
+
+#: models/imagemodels.py:37
+msgid "must always publish copyright"
+msgstr "copyright musí být vždy uvedeno"
+
+#: models/thumbnailoptionmodels.py:16
+msgid "width in pixel."
+msgstr "šířka v pixelech."
+
+#: models/thumbnailoptionmodels.py:21
+msgid "height in pixel."
+msgstr "výška v pixelech."
+
+#: models/thumbnailoptionmodels.py:38
+msgid "thumbnail options"
+msgstr "volby náhledu"
+
+#: models/virtualitems.py:46
+#: templates/admin/filer/folder/directory_table_list.html:4
+#: templates/admin/filer/folder/directory_table_list.html:170
+#: templates/admin/filer/folder/directory_thumbnail_list.html:5
+#: templates/admin/filer/folder/directory_thumbnail_list.html:146
+#: templates/admin/filer/tools/clipboard/clipboard.html:27
+msgid "Unsorted Uploads"
+msgstr "Nezařazené"
+
+#: models/virtualitems.py:73
+msgid "files with missing metadata"
+msgstr "soubory s chybějícími informacemi"
+
+#: models/virtualitems.py:87 templates/admin/filer/folder/change_form.html:8
+#: templates/admin/filer/folder/directory_listing.html:102
+msgid "root"
+msgstr "kořenová složka"
+
+#: settings.py:273
+msgid "Show table view"
+msgstr ""
+
+#: settings.py:278
+msgid "Show thumbnail view"
+msgstr ""
+
+#: templates/admin/filer/actions.html:5
+msgid "Run the selected action"
+msgstr "Provést vybranou operaci"
+
+#: templates/admin/filer/actions.html:5
+msgid "Go"
+msgstr "Provést"
+
+#: templates/admin/filer/actions.html:14
+#: templates/admin/filer/folder/directory_table_list.html:235
+#: templates/admin/filer/folder/directory_thumbnail_list.html:210
+msgid "Click here to select the objects across all pages"
+msgstr "Klikněte sem pro výběr všechny položky na všech stránkách výpisu"
+
+#: templates/admin/filer/actions.html:14
+#, python-format
+msgid "Select all %(total_count)s files and/or folders"
+msgstr "Vybrat všechny soubory a/nebo složky (%(total_count)s)"
+
+#: templates/admin/filer/actions.html:16
+#: templates/admin/filer/folder/directory_table_list.html:237
+#: templates/admin/filer/folder/directory_thumbnail_list.html:212
+msgid "Clear selection"
+msgstr "Zrušit výběr"
+
+#: templates/admin/filer/breadcrumbs.html:4
+#: templates/admin/filer/folder/directory_listing.html:28
+msgid "Go back to admin homepage"
+msgstr "Přejít zpět na hlavní stránku administrace"
+
+#: templates/admin/filer/breadcrumbs.html:4
+#: templates/admin/filer/folder/change_form.html:6
+#: templates/admin/filer/folder/directory_listing.html:29
+msgid "Home"
+msgstr "Hlavní stránka"
+
+#: templates/admin/filer/breadcrumbs.html:5
+#: templates/admin/filer/folder/directory_listing.html:32
+msgid "Go back to Filer app"
+msgstr "Přejít zpět do Správce souborů"
+
+#: templates/admin/filer/breadcrumbs.html:6
+#: templates/admin/filer/folder/directory_listing.html:35
+msgid "Go back to root folder"
+msgstr "Přejít zpět do kořenové složky"
+
+#: templates/admin/filer/breadcrumbs.html:8
+#: templates/admin/filer/breadcrumbs.html:18
+#: templates/admin/filer/folder/change_form.html:10
+#: templates/admin/filer/folder/directory_listing.html:39
+#: templates/admin/filer/folder/directory_listing.html:47
+#, python-format
+msgid "Go back to '%(folder_name)s' folder"
+msgstr "Přejít zpět do složky '%(folder_name)s'"
+
+#: templates/admin/filer/change_form.html:26
+msgid "Duplicates"
+msgstr "Duplicity"
+
+#: templates/admin/filer/delete_selected_files_confirmation.html:11
+msgid ""
+"Deleting the selected files and/or folders would result in deleting related "
+"objects, but your account doesn't have permission to delete the following "
+"types of objects:"
+msgstr ""
+"Smazání vybraných souborů a/nebo složek bude mít za následek smazání všech "
+"přidružených objektů, nicméně ke smazání následujících objektů nemáte "
+"oprávnění:"
+
+#: templates/admin/filer/delete_selected_files_confirmation.html:19
+msgid ""
+"Deleting the selected files and/or folders would require deleting the "
+"following protected related objects:"
+msgstr ""
+"Smazání vybraných souborů a/nebo složek bude mít za následek smazání "
+"následujících přidružených objektů:"
+
+#: templates/admin/filer/delete_selected_files_confirmation.html:27
+msgid ""
+"Are you sure you want to delete the selected files and/or folders? All of "
+"the following objects and their related items will be deleted:"
+msgstr ""
+"Opravdu chcete smazat vybrané soubory a složky? Všechny následující objekty "
+"budou smazány:"
+
+#: templates/admin/filer/delete_selected_files_confirmation.html:46
+#: templates/admin/filer/folder/choose_copy_destination.html:64
+#: templates/admin/filer/folder/choose_move_destination.html:72
+msgid "No, take me back"
+msgstr "Ne, chci zpátky"
+
+#: templates/admin/filer/delete_selected_files_confirmation.html:47
+msgid "Yes, I'm sure"
+msgstr "Ano"
+
+#: templates/admin/filer/file/change_form.html:35
+#: templates/admin/filer/folder/change_form.html:21
+#: templates/admin/filer/image/change_form.html:35
+msgid "History"
+msgstr "Historie"
+
+#: templates/admin/filer/file/change_form.html:37
+#: templates/admin/filer/folder/change_form.html:23
+#: templates/admin/filer/image/change_form.html:37
+msgid "View on site"
+msgstr "Zobrazit na webu"
+
+#: templates/admin/filer/folder/change_form.html:6
+#: templates/admin/filer/folder/change_form.html:7
+#: templates/admin/filer/folder/change_form.html:8
+#: templates/admin/filer/folder/directory_listing.html:102
+msgid "Go back to"
+msgstr "Vrátit se do"
+
+#: templates/admin/filer/folder/change_form.html:6
+msgid "admin homepage"
+msgstr "hlavní stránka administrace"
+
+#: templates/admin/filer/folder/change_form.html:37
+#: templates/admin/filer/folder/directory_listing.html:95
+#: templates/admin/filer/folder/directory_listing.html:103
+#: templates/admin/filer/folder/directory_table_list.html:29
+#: templates/admin/filer/folder/directory_table_list.html:59
+#: templates/admin/filer/folder/directory_table_list.html:181
+#: templates/admin/filer/folder/directory_thumbnail_list.html:27
+#: templates/admin/filer/folder/directory_thumbnail_list.html:61
+#: templates/admin/filer/folder/directory_thumbnail_list.html:156
+msgid "Folder Icon"
+msgstr "Ikona složky"
+
+#: templates/admin/filer/folder/choose_copy_destination.html:23
+msgid ""
+"Your account doesn't have permissions to copy all of the selected files and/"
+"or folders."
+msgstr "Nemáte oprávnění kopírovat vybrané soubory a/nebo složky."
+
+#: templates/admin/filer/folder/choose_copy_destination.html:25
+#: templates/admin/filer/folder/choose_copy_destination.html:31
+#: templates/admin/filer/folder/choose_copy_destination.html:37
+#: templates/admin/filer/folder/choose_move_destination.html:37
+#: templates/admin/filer/folder/choose_move_destination.html:43
+#: templates/admin/filer/folder/choose_move_destination.html:49
+msgid "Take me back"
+msgstr "Zpět"
+
+#: templates/admin/filer/folder/choose_copy_destination.html:29
+#: templates/admin/filer/folder/choose_move_destination.html:41
+msgid "There are no destination folders available."
+msgstr "Nejsou k dispozici žádné cílové složky."
+
+#: templates/admin/filer/folder/choose_copy_destination.html:35
+msgid "There are no files and/or folders available to copy."
+msgstr "Žádné soubory ani složky není možno kopírovat."
+
+#: templates/admin/filer/folder/choose_copy_destination.html:40
+msgid ""
+"The following files and/or folders will be copied to a destination folder "
+"(retaining their tree structure):"
+msgstr ""
+"Následující soubory a/nebo složky budou zkopírovány do cílové slžoky (jejich "
+"stromová struktura bude zachována):"
+
+#: templates/admin/filer/folder/choose_copy_destination.html:54
+#: templates/admin/filer/folder/choose_move_destination.html:64
+msgid "Destination folder:"
+msgstr "Cílová složka:"
+
+#: templates/admin/filer/folder/choose_copy_destination.html:65
+#: templates/admin/filer/folder/choose_copy_destination.html:68
+#: templates/admin/filer/folder/directory_listing.html:178
+msgid "Copy"
+msgstr "Kopírovat"
+
+#: templates/admin/filer/folder/choose_copy_destination.html:67
+msgid "It is not allowed to copy files into same folder"
+msgstr "Není možné kopírovat soubory do stejné složky"
+
+#: templates/admin/filer/folder/choose_images_resize_options.html:15
+msgid ""
+"Your account doesn't have permissions to resize all of the selected images."
+msgstr "Nemáte oprávnění k změně velikosti vybraných obrázků."
+
+#: templates/admin/filer/folder/choose_images_resize_options.html:18
+msgid "There are no images available to resize."
+msgstr "Žádné obrázky ke změně velikosti."
+
+#: templates/admin/filer/folder/choose_images_resize_options.html:20
+msgid "The following images will be resized:"
+msgstr "Následujícím obrázkům bude změněna velikost:"
+
+#: templates/admin/filer/folder/choose_images_resize_options.html:32
+msgid "Choose an existing thumbnail option or enter resize parameters:"
+msgstr ""
+"Vyberte existující volby náhledu nebo zadejte parametry změny velikosti:"
+
+#: templates/admin/filer/folder/choose_images_resize_options.html:36
+msgid ""
+"Warning: Images will be resized in-place and originals will be lost. Maybe "
+"first make a copy of them to retain the originals."
+msgstr ""
+"Varování: Bude změněna velikost obrázků a původní obrázky budou přepsány. "
+"Pokud chcete zachovat obrázky v původní velikosti, nejdříve vytvořte jejich "
+"kopie."
+
+#: templates/admin/filer/folder/choose_images_resize_options.html:39
+msgid "Resize"
+msgstr "Změnit velikost"
+
+#: templates/admin/filer/folder/choose_move_destination.html:35
+msgid ""
+"Your account doesn't have permissions to move all of the selected files and/"
+"or folders."
+msgstr "Nemáte oprávnění k přesunu vybraných souborů nebo složek."
+
+#: templates/admin/filer/folder/choose_move_destination.html:47
+msgid "There are no files and/or folders available to move."
+msgstr "Žádné soubory k přesunutí."
+
+#: templates/admin/filer/folder/choose_move_destination.html:52
+msgid ""
+"The following files and/or folders will be moved to a destination folder "
+"(retaining their tree structure):"
+msgstr ""
+"Následující soubory a/nebo složky budou přesunyty do cílové složky (jejich "
+"stromová struktura bude zachována)"
+
+#: templates/admin/filer/folder/choose_move_destination.html:73
+#: templates/admin/filer/folder/choose_move_destination.html:76
+#: templates/admin/filer/folder/directory_listing.html:181
+msgid "Move"
+msgstr "Přesunout"
+
+#: templates/admin/filer/folder/choose_move_destination.html:75
+msgid "It is not allowed to move files into same folder"
+msgstr "Není možné přesouvat soubory do stejné složky"
+
+#: templates/admin/filer/folder/choose_rename_format.html:15
+msgid ""
+"Your account doesn't have permissions to rename all of the selected files."
+msgstr "Nemáte oprávnění přejmenovat všechny vybrané soubory."
+
+#: templates/admin/filer/folder/choose_rename_format.html:18
+msgid "There are no files available to rename."
+msgstr "Žádné soubory k přejmenování."
+
+#: templates/admin/filer/folder/choose_rename_format.html:20
+msgid ""
+"The following files will be renamed (they will stay in their folders and "
+"keep original filename, only displayed filename will be changed):"
+msgstr ""
+"Následující soubory budou přejmenovány (změněno bude pouze zobrazované jméno)"
+
+#: templates/admin/filer/folder/choose_rename_format.html:59
+msgid "Rename"
+msgstr "Přejmenovat"
+
+#: templates/admin/filer/folder/directory_listing.html:94
+msgid "Go back to the parent folder"
+msgstr "Přejít zpět do nadřazené složky"
+
+#: templates/admin/filer/folder/directory_listing.html:131
+msgid "Change current folder details"
+msgstr "Upravit aktuální složku"
+
+#: templates/admin/filer/folder/directory_listing.html:131
+msgid "Change"
+msgstr "Upravit"
+
+#: templates/admin/filer/folder/directory_listing.html:141
+msgid "Click here to run search for entered phrase"
+msgstr "Klikněte sem pro vyhledání zadaného výrazu"
+
+#: templates/admin/filer/folder/directory_listing.html:144
+msgid "Search"
+msgstr "Hledat"
+
+#: templates/admin/filer/folder/directory_listing.html:151
+msgid "Close"
+msgstr "Zavřít"
+
+#: templates/admin/filer/folder/directory_listing.html:153
+msgid "Limit"
+msgstr "Omezit"
+
+#: templates/admin/filer/folder/directory_listing.html:158
+msgid "Check it to limit the search to current folder"
+msgstr "Zaškrtněte pro omezení hledání na aktuální složku"
+
+#: templates/admin/filer/folder/directory_listing.html:159
+msgid "Limit the search to current folder"
+msgstr "Omezit hledání na aktuální složku"
+
+#: templates/admin/filer/folder/directory_listing.html:175
+msgid "Delete"
+msgstr "Smazat"
+
+#: templates/admin/filer/folder/directory_listing.html:203
+msgid "Adds a new Folder"
+msgstr "Přidá novou složku"
+
+#: templates/admin/filer/folder/directory_listing.html:206
+msgid "New Folder"
+msgstr "Nová složka"
+
+#: templates/admin/filer/folder/directory_listing.html:211
+#: templates/admin/filer/folder/directory_listing.html:218
+#: templates/admin/filer/folder/directory_listing.html:221
+#: templates/admin/filer/folder/directory_listing.html:228
+#: templates/admin/filer/folder/directory_listing.html:235
+msgid "Upload Files"
+msgstr "Nahrát soubory"
+
+#: templates/admin/filer/folder/directory_listing.html:233
+msgid "You have to select a folder first"
+msgstr "Nejdříve musíte vybrat složku"
+
+#: templates/admin/filer/folder/directory_table_list.html:16
+msgid "Name"
+msgstr "Název"
+
+#: templates/admin/filer/folder/directory_table_list.html:17
+#: templates/admin/filer/tools/detail_info.html:50
+msgid "Owner"
+msgstr "Vlastník"
+
+#: templates/admin/filer/folder/directory_table_list.html:18
+#: templates/admin/filer/tools/detail_info.html:34
+msgid "Size"
+msgstr "Velikost"
+
+#: templates/admin/filer/folder/directory_table_list.html:19
+msgid "Action"
+msgstr "Akce"
+
+#: templates/admin/filer/folder/directory_table_list.html:28
+#: templates/admin/filer/folder/directory_table_list.html:35
+#: templates/admin/filer/folder/directory_table_list.html:58
+#: templates/admin/filer/folder/directory_table_list.html:65
+#: templates/admin/filer/folder/directory_thumbnail_list.html:26
+#: templates/admin/filer/folder/directory_thumbnail_list.html:32
+#: templates/admin/filer/folder/directory_thumbnail_list.html:60
+#: templates/admin/filer/folder/directory_thumbnail_list.html:66
+#, python-format
+msgid "Change '%(item_label)s' folder details"
+msgstr "Upravit podrobnosti složky '%(item_label)s'"
+
+#: templates/admin/filer/folder/directory_table_list.html:76
+#: templates/admin/filer/tools/search_form.html:5
+#, python-format
+msgid "%(counter)s folder"
+msgid_plural "%(counter)s folders"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+#: templates/admin/filer/folder/directory_table_list.html:77
+#: templates/admin/filer/tools/search_form.html:6
+#, python-format
+msgid "%(counter)s file"
+msgid_plural "%(counter)s files"
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+#: templates/admin/filer/folder/directory_table_list.html:83
+msgid "Change folder details"
+msgstr "Upravit podrobnosti složky"
+
+#: templates/admin/filer/folder/directory_table_list.html:84
+msgid "Remove folder"
+msgstr "Smazat složku"
+
+#: templates/admin/filer/folder/directory_table_list.html:96
+#: templates/admin/filer/folder/directory_table_list.html:104
+#: templates/admin/filer/folder/directory_table_list.html:119
+#: templates/admin/filer/folder/directory_thumbnail_list.html:95
+#: templates/admin/filer/folder/directory_thumbnail_list.html:104
+#: templates/admin/filer/folder/directory_thumbnail_list.html:119
+msgid "Select this file"
+msgstr "Vybrat tento soubor"
+
+#: templates/admin/filer/folder/directory_table_list.html:107
+#: templates/admin/filer/folder/directory_table_list.html:122
+#: templates/admin/filer/folder/directory_table_list.html:154
+#: templates/admin/filer/folder/directory_thumbnail_list.html:107
+#: templates/admin/filer/folder/directory_thumbnail_list.html:122
+#, python-format
+msgid "Change '%(item_label)s' details"
+msgstr "Upravit podrobnosti '%(item_label)s'"
+
+#: templates/admin/filer/folder/directory_table_list.html:131
+#: templates/admin/filer/folder/directory_thumbnail_list.html:130
+msgid "Permissions"
+msgstr "Oprávnění"
+
+#: templates/admin/filer/folder/directory_table_list.html:131
+#: templates/admin/filer/folder/directory_thumbnail_list.html:130
+msgid "disabled"
+msgstr "neaktivní"
+
+#: templates/admin/filer/folder/directory_table_list.html:131
+#: templates/admin/filer/folder/directory_thumbnail_list.html:130
+msgid "enabled"
+msgstr "aktivní"
+
+#: templates/admin/filer/folder/directory_table_list.html:144
+#, fuzzy
+#| msgid "Move selected files to clipboard"
+msgid "URL copied to clipboard"
+msgstr "Přesunout vybrané soubory do schránky"
+
+#: templates/admin/filer/folder/directory_table_list.html:147
+#, python-format
+msgid "Canonical url '%(item_label)s'"
+msgstr "Kanonický odkaz '%(item_label)s'"
+
+#: templates/admin/filer/folder/directory_table_list.html:151
+#, python-format
+msgid "Download '%(item_label)s'"
+msgstr "Stáhnout '%(item_label)s'"
+
+#: templates/admin/filer/folder/directory_table_list.html:155
+msgid "Remove file"
+msgstr "Smazat soubor"
+
+#: templates/admin/filer/folder/directory_table_list.html:163
+#: templates/admin/filer/folder/directory_thumbnail_list.html:138
+msgid "Drop files here or use the \"Upload Files\" button"
+msgstr "Přetáhněte soubory sem nebo klikněte na tlačítko \"Nahrát soubory\""
+
+#: templates/admin/filer/folder/directory_table_list.html:178
+#: templates/admin/filer/folder/directory_thumbnail_list.html:153
+msgid "Drop your file to upload into:"
+msgstr "Přetáhněte sem soubory, které chcete nahrát do:"
+
+#: templates/admin/filer/folder/directory_table_list.html:188
+#: templates/admin/filer/folder/directory_thumbnail_list.html:163
+msgid "Upload"
+msgstr "Nahrát"
+
+#: templates/admin/filer/folder/directory_table_list.html:200
+#: templates/admin/filer/folder/directory_thumbnail_list.html:175
+msgid "cancel"
+msgstr "zrušit"
+
+#: templates/admin/filer/folder/directory_table_list.html:204
+#: templates/admin/filer/folder/directory_thumbnail_list.html:179
+msgid "Upload success!"
+msgstr "Nahrávání se zdařilo!"
+
+#: templates/admin/filer/folder/directory_table_list.html:208
+#: templates/admin/filer/folder/directory_thumbnail_list.html:183
+msgid "Upload canceled!"
+msgstr "Nahrávání bylo zrušeno!"
+
+#: templates/admin/filer/folder/directory_table_list.html:215
+#: templates/admin/filer/folder/directory_thumbnail_list.html:190
+msgid "previous"
+msgstr "předchozí"
+
+#: templates/admin/filer/folder/directory_table_list.html:220
+#: templates/admin/filer/folder/directory_thumbnail_list.html:195
+#, python-format
+msgid "Page %(number)s of %(num_pages)s."
+msgstr "Stránka %(number)s z %(num_pages)s."
+
+#: templates/admin/filer/folder/directory_table_list.html:225
+#: templates/admin/filer/folder/directory_thumbnail_list.html:200
+msgid "next"
+msgstr "následující"
+
+#: templates/admin/filer/folder/directory_table_list.html:235
+#: templates/admin/filer/folder/directory_thumbnail_list.html:210
+#, python-format
+msgid "Select all %(total_count)s"
+msgstr "Vybrat všechny (%(total_count)s)"
+
+#: templates/admin/filer/folder/directory_thumbnail_list.html:15
+#: templates/admin/filer/folder/directory_thumbnail_list.html:80
+msgid "Select all"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_thumbnail_list.html:77
+msgid "Files"
+msgstr ""
+
+#: templates/admin/filer/folder/new_folder_form.html:4
+#: templates/admin/filer/folder/new_folder_form.html:7
+msgid "Add new"
+msgstr "Přidat"
+
+#: templates/admin/filer/folder/new_folder_form.html:4
+msgid "Django site admin"
+msgstr "Hlavní stránka administrace"
+
+#: templates/admin/filer/folder/new_folder_form.html:15
+msgid "Please correct the error below."
+msgid_plural "Please correct the errors below."
+msgstr[0] ""
+msgstr[1] ""
+msgstr[2] ""
+msgstr[3] ""
+
+#: templates/admin/filer/folder/new_folder_form.html:30
+msgid "Save"
+msgstr "Uložit"
+
+#: templates/admin/filer/templatetags/file_icon.html:9
+msgid "Your browser does not support audio."
+msgstr ""
+
+#: templates/admin/filer/templatetags/file_icon.html:14
+msgid "Your browser does not support video."
+msgstr ""
+
+#: templates/admin/filer/tools/clipboard/clipboard.html:9
+msgid "Clipboard"
+msgstr "Schtánka"
+
+#: templates/admin/filer/tools/clipboard/clipboard.html:21
+msgid "Paste all items here"
+msgstr "Vložit všechny položky sem"
+
+#: templates/admin/filer/tools/clipboard/clipboard.html:27
+msgid "Move all clipboard files to"
+msgstr "Přesunout všechny soubory ve schránce do"
+
+#: templates/admin/filer/tools/clipboard/clipboard.html:27
+msgid "Empty Clipboard"
+msgstr "Vyprázdnit schránku"
+
+#: templates/admin/filer/tools/clipboard/clipboard.html:50
+msgid "the clipboard is empty"
+msgstr "schránka je prázdná"
+
+#: templates/admin/filer/tools/clipboard/clipboard_item_rows.html:16
+msgid "upload failed"
+msgstr "nahrávání selhalo"
+
+#: templates/admin/filer/tools/detail_info.html:11
+msgid "Download"
+msgstr ""
+
+#: templates/admin/filer/tools/detail_info.html:16
+msgid "Expand"
+msgstr ""
+
+#: templates/admin/filer/tools/detail_info.html:21
+#: templates/admin/filer/widgets/admin_file.html:32
+#: templatetags/filer_admin_tags.py:108
+msgid "File is missing"
+msgstr ""
+
+#: templates/admin/filer/tools/detail_info.html:30
+msgid "Type"
+msgstr "Typ"
+
+#: templates/admin/filer/tools/detail_info.html:38
+msgid "File-size"
+msgstr "Velikost souboru"
+
+#: templates/admin/filer/tools/detail_info.html:42
+msgid "Modified"
+msgstr "Změněno"
+
+#: templates/admin/filer/tools/detail_info.html:46
+msgid "Created"
+msgstr "Vytvořeno"
+
+#: templates/admin/filer/tools/search_form.html:5
+msgid "found"
+msgstr "nalezeno"
+
+#: templates/admin/filer/tools/search_form.html:5
+msgid "and"
+msgstr "a"
+
+#: templates/admin/filer/tools/search_form.html:7
+msgid "cancel search"
+msgstr "zrušit hledání"
+
+#: templates/admin/filer/widgets/admin_file.html:10
+#: templates/admin/filer/widgets/admin_file.html:39
+#: templates/admin/filer/widgets/admin_folder.html:18
+msgid "Clear"
+msgstr "Vyčistit"
+
+#: templates/admin/filer/widgets/admin_file.html:22
+msgid "or drop your file here"
+msgstr "přetáhněte soubory sem"
+
+#: templates/admin/filer/widgets/admin_file.html:35
+msgid "no file selected"
+msgstr "žádný soubor nebyl vybrán"
+
+#: templates/admin/filer/widgets/admin_file.html:50
+#: templates/admin/filer/widgets/admin_folder.html:14
+msgid "Lookup"
+msgstr "Vyhledat"
+
+#: templates/admin/filer/widgets/admin_file.html:51
+msgid "Choose File"
+msgstr "Vybrat soubor"
+
+#: templates/admin/filer/widgets/admin_folder.html:16
+msgid "Choose Folder"
+msgstr ""
+
+#: validation.py:20
+#, python-brace-format
+msgid "File \"{file_name}\": Upload denied by site security policy"
+msgstr ""
+
+#: validation.py:23
+#, python-brace-format
+msgid "File \"{file_name}\": {file_type} upload denied by site security policy"
+msgstr ""
+
+#: validation.py:34
+#, python-brace-format
+msgid "File \"{file_name}\": HTML upload denied by site security policy"
+msgstr ""
+
+#: validation.py:72
+#, python-brace-format
+msgid ""
+"File \"{file_name}\": Rejected due to potential cross site scripting "
+"vulnerability"
+msgstr ""
+
+#: validation.py:84
+#, python-brace-format
+msgid "File \"{file_name}\": SVG file format not recognized"
+msgstr ""
+
+#~ msgid "Resize parameters must be choosen."
+#~ msgstr "Resize parameters must be choosen."
+
+#~ msgid "Choose resize parameters:"
+#~ msgstr "Choose resize parameters:"
+
+#~ msgid "Open file"
+#~ msgstr "Open file"
+
+#~ msgid "Full size preview"
+#~ msgstr "Full size preview"
+
+#~ msgid "Add Description"
+#~ msgstr "Add Description"
+
+#~ msgid "Author"
+#~ msgstr "Author"
+
+#~ msgid "Add Alt-Text"
+#~ msgstr "Add Alt-Text"
+
+#~ msgid "Add caption"
+#~ msgstr "Add caption"
+
+#~ msgid "Add Tags"
+#~ msgstr "Add Tags"
+
+#~ msgid "Change File"
+#~ msgstr "Change File"
+
+#~ msgid "none selected"
+#~ msgstr "none selected"
+
+#~ msgid "Add Folder"
+#~ msgstr "Add Folder"
+
+#~ msgid "Subject Location"
+#~ msgstr "Subject location"
+
+#~ msgid ""
+#~ "\n"
+#~ " %(counter)s file"
+#~ msgid_plural ""
+#~ "%(counter)s files\n"
+#~ " "
+#~ msgstr[0] "15c0f2d28d6dab1af1f6d94906beb627_pl_0"
+#~ msgstr[1] "15c0f2d28d6dab1af1f6d94906beb627_pl_1"
+#~ msgstr[2] "15c0f2d28d6dab1af1f6d94906beb627_pl_2"
+#~ msgstr[3] "15c0f2d28d6dab1af1f6d94906beb627_pl_3"
+
+#~ msgid "Upload File"
+#~ msgstr "Upload File"
diff --git a/filer/locale/de/LC_MESSAGES/django.mo b/filer/locale/de/LC_MESSAGES/django.mo
new file mode 100644
index 0000000000000000000000000000000000000000..0f9415701524ef6334539b54b4f404bfae420c68
GIT binary patch
literal 21636
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 n4AZt`Q2+#w3
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 g_#&~1$-!6!#ZFE#
zR<;XEj&7n6WQn-4U?_rOxPV%xJ8!)@%cd&Pp|Cq#87gjUEWSGvt_Em8R#)ID4js!8jTGgf(;6DkN8e-FZZ&!i^@N(u^;*FnO
z$KH~7t8e3}?mj}weKVQ#2CkmsJiEgv!Xm}OIC|M1>mHVhEw;XrzapPFSjVv&Z_hQp
zNE5bJrvC}$2IPy83V@$I_7=gt%ZLwl<>xB*j);(%sIu6C)8Ch`^>dZyJJ#JqCfOf-
zGMK71dg!Hq?bQQsjvyH4utxL7gTbcbfWbaJmh$pX7t
zx_BPhcFz=wv=E~57+rTw!2gHdTgN(1YyO4$Dolv{aHXad0WRWM!{n}jYWXdj{U&Pd9GV~NrpUfvRn-BhJfmSr|_
z=g=}Z*04K3C(V=yjBD2Jq^^*BQ>~6m56ry>zI9Weg>Ps$+9fIG5}xi$p#>vA_1rH|
zb`Cl)F}za>x{y;i@0g7gInPv)pT%qVMUbJ8)(>NSqs;LUy<8K>4B$
gr)x-$-N+Gk?Ik`%, 2016-2017
+# Fabian Braun , 2023
+# Jannik Vieten , 2016-2017
+# Mitja Martini , 2012
+# Mitja Martini , 2012
+# Peter Wischer , 2018
+# Stefan Foulis , 2016
+msgid ""
+msgstr ""
+"Project-Id-Version: django Filer\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2023-09-30 18:10+0200\n"
+"PO-Revision-Date: 2012-07-13 15:50+0000\n"
+"Last-Translator: Fabian Braun , 2023\n"
+"Language-Team: German (http://app.transifex.com/divio/django-filer/language/de/)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: de\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+#: admin/clipboardadmin.py:17
+#| msgid ""
+#| "ccount doesn't have permissions to rename all of the selected files."
+msgid "You do not have permission to upload files."
+msgstr "Du hast keine Berechtigungen, um Dateien hochzuladen."
+
+#: admin/clipboardadmin.py:18
+msgid "Can't find folder to upload. Please refresh and try again"
+msgstr "Ich kann den Zielordner nicht finden. Bitter lade die Seite neu und versuche es noch einmal."
+
+#: admin/clipboardadmin.py:20
+msgid ""
+"Can't use this folder, Permission Denied. Please select another folder."
+msgstr "Zugriff auf diesen Ordner verweigert. Bitte wähle einen anderen Ordner."
+
+#: admin/fileadmin.py:73
+msgid "Advanced"
+msgstr "Fortgeschritten"
+
+#: admin/fileadmin.py:188
+msgid "canonical URL"
+msgstr "Kanonische URL"
+
+#: admin/folderadmin.py:411 admin/folderadmin.py:582
+msgid ""
+"Items must be selected in order to perform actions on them. No items have "
+"been changed."
+msgstr "Einträge müssen ausgewählt werden, um Aktionen darauf auszuführen. Keine Einträge wurden geändert."
+
+#: admin/folderadmin.py:434
+#, python-format
+msgid "%(total_count)s selected"
+msgid_plural "All %(total_count)s selected"
+msgstr[0] "%(total_count)s ausgewählt"
+msgstr[1] "Alle %(total_count)s ausgewählt"
+
+#: admin/folderadmin.py:460
+#, python-format
+msgid "Directory listing for %(folder_name)s"
+msgstr "Verzeichnis Auflistung für %(folder_name)s"
+
+#: admin/folderadmin.py:475
+#, python-format
+msgid "0 of %(cnt)s selected"
+msgstr "0 von %(cnt)s ausgewählt"
+
+#: admin/folderadmin.py:612
+msgid "No action selected."
+msgstr "Keine Aktion ausgewählt."
+
+#: admin/folderadmin.py:664
+#, python-format
+msgid "Successfully moved %(count)d files to clipboard."
+msgstr "%(count)d Dateien wurden erfolgreich in die Zwischenablage gelegt."
+
+#: admin/folderadmin.py:669
+msgid "Move selected files to clipboard"
+msgstr "Ausgewählte Dateien in die Zwischenablage legen"
+
+#: admin/folderadmin.py:709
+#, python-format
+msgid "Successfully disabled permissions for %(count)d files."
+msgstr "Berechtigungen für %(count)d Dateien erfolgreich deaktiviert."
+
+#: admin/folderadmin.py:711
+#, python-format
+msgid "Successfully enabled permissions for %(count)d files."
+msgstr "Berechtigungen für %(count)d Dateien erfolgreich aktiviert."
+
+#: admin/folderadmin.py:719
+msgid "Enable permissions for selected files"
+msgstr "Berechtigungen für ausgewählte Dateien aktivieren"
+
+#: admin/folderadmin.py:725
+msgid "Disable permissions for selected files"
+msgstr "Berechtigungen für ausgewählte Dateien deaktivieren"
+
+#: admin/folderadmin.py:789
+#, python-format
+msgid "Successfully deleted %(count)d files and/or folders."
+msgstr "%(count)d Dateien und/oder Ordner wurden erfolgreich gelöscht."
+
+#: admin/folderadmin.py:794
+msgid "Cannot delete files and/or folders"
+msgstr "Dateien und/oder Ordner können nicht gelöscht werden"
+
+#: admin/folderadmin.py:796
+msgid "Are you sure?"
+msgstr "Bist Du sicher?"
+
+#: admin/folderadmin.py:802
+msgid "Delete files and/or folders"
+msgstr "Lösche Dateien und/oder Ordner"
+
+#: admin/folderadmin.py:823
+msgid "Delete selected files and/or folders"
+msgstr "Lösche ausgewählte Dateien und/oder Ordner"
+
+#: admin/folderadmin.py:930
+#, python-format
+msgid "Folders with names %s already exist at the selected destination"
+msgstr "Die Ordner mit den Namen %s existieren bereits am ausgewählten Zielort"
+
+#: admin/folderadmin.py:934
+#, python-format
+msgid ""
+"Successfully moved %(count)d files and/or folders to folder "
+"'%(destination)s'."
+msgstr "%(count)d Dateien und/oder Ordner wurden erfolgreich in den Ordner '%(destination)s' verschoben."
+
+#: admin/folderadmin.py:942 admin/folderadmin.py:944
+msgid "Move files and/or folders"
+msgstr "Verschiebe Dateien und/oder Ordner"
+
+#: admin/folderadmin.py:959
+msgid "Move selected files and/or folders"
+msgstr "Ausgewählte Dateien und/oder Ordner verschieben"
+
+#: admin/folderadmin.py:1016
+#, python-format
+msgid "Successfully renamed %(count)d files."
+msgstr "%(count)d Dateien wurden erfolgreich umbenannt."
+
+#: admin/folderadmin.py:1025 admin/folderadmin.py:1027
+#: admin/folderadmin.py:1042
+msgid "Rename files"
+msgstr "Dateien umbenennen"
+
+#: admin/folderadmin.py:1137
+#, python-format
+msgid ""
+"Successfully copied %(count)d files and/or folders to folder "
+"'%(destination)s'."
+msgstr "%(count)d Dateien und/oder Ordner wurden erfolgreich in den Ordner '%(destination)s' kopiert."
+
+#: admin/folderadmin.py:1155 admin/folderadmin.py:1157
+msgid "Copy files and/or folders"
+msgstr "Kopiere Dateien und/oder Ordner"
+
+#: admin/folderadmin.py:1174
+msgid "Copy selected files and/or folders"
+msgstr "Ausgewählte Dateien und/oder Ordner kopieren"
+
+#: admin/folderadmin.py:1279
+#, python-format
+msgid "Successfully resized %(count)d images."
+msgstr "Die Bildgrösse von %(count)d Bildern wurde erfolgreich geändert."
+
+#: admin/folderadmin.py:1286 admin/folderadmin.py:1288
+msgid "Resize images"
+msgstr "Bildgrössen verändern"
+
+#: admin/folderadmin.py:1303
+msgid "Resize selected images"
+msgstr "Die Bildgrössen der ausgewählten Bilder verändern"
+
+#: admin/forms.py:24
+msgid "Suffix which will be appended to filenames of copied files."
+msgstr "Die Dateiendung, welche an die Dateinamen der kopierten Dateien angehängt wird."
+
+#: admin/forms.py:31
+#, python-format
+msgid ""
+"Suffix should be a valid, simple and lowercase filename part, like "
+"\"%(valid)s\"."
+msgstr "Die Dateiendung sollte ein gültiger, einfacher und kleingeschriebener Teil eines Dateinamens sein, wie zum Beispiel \"%(valid)s\"."
+
+#: admin/forms.py:52
+#, python-format
+msgid "Unknown rename format value key \"%(key)s\"."
+msgstr "Unbekannter Schlüssel für das Umbenennungs-Format: %(key)s."
+
+#: admin/forms.py:54
+#, python-format
+msgid "Invalid rename format: %(error)s."
+msgstr "Ungültiges Umbenennungs-Format: %(error)s."
+
+#: admin/forms.py:68 models/thumbnailoptionmodels.py:37
+msgid "thumbnail option"
+msgstr "Vorschaubild Optionen"
+
+#: admin/forms.py:72 models/thumbnailoptionmodels.py:15
+msgid "width"
+msgstr "Breite"
+
+#: admin/forms.py:73 models/thumbnailoptionmodels.py:20
+msgid "height"
+msgstr "Höhe"
+
+#: admin/forms.py:74 models/thumbnailoptionmodels.py:25
+msgid "crop"
+msgstr "Beschneiden"
+
+#: admin/forms.py:75 models/thumbnailoptionmodels.py:30
+msgid "upscale"
+msgstr "Vergrössern"
+
+#: admin/forms.py:79
+msgid "Thumbnail option or resize parameters must be choosen."
+msgstr "Vorschaubild Optionen oder Parameter zur Grössenänderung müssen ausgewählt werden."
+
+#: admin/imageadmin.py:20 admin/imageadmin.py:112
+msgid "Subject location"
+msgstr "Ort des Hauptinhalts"
+
+#: admin/imageadmin.py:21
+msgid "Location of the main subject of the scene. Format: \"x,y\"."
+msgstr "Position des Hauptinhalts des Bildes. Format: \"x,y\"."
+
+#: admin/imageadmin.py:59
+msgid "Invalid subject location format. "
+msgstr "Ungültiges Format zur Positionsangabe."
+
+#: admin/imageadmin.py:67
+msgid "Subject location is outside of the image. "
+msgstr "Angegebene Position des Hauptinhalts liegt außerhalb des Bildes."
+
+#: admin/imageadmin.py:76
+#, python-brace-format
+msgid "Your input: \"{subject_location}\". "
+msgstr "Deine Eingabe: \"{subject_location}\". "
+
+#: admin/permissionadmin.py:10 models/foldermodels.py:380
+msgid "Who"
+msgstr "Wer"
+
+#: admin/permissionadmin.py:11 models/foldermodels.py:401
+msgid "What"
+msgstr "Was"
+
+#: admin/views.py:55
+msgid "Folder with this name already exists."
+msgstr "Dieser Ordnername wird bereits verwendet."
+
+#: apps.py:11 templates/admin/filer/breadcrumbs.html:5
+#: templates/admin/filer/folder/change_form.html:7
+#: templates/admin/filer/folder/directory_listing.html:33
+msgid "Filer"
+msgstr "Filer"
+
+#: contrib/django_cms/cms_toolbars.py:44
+msgid "Media library"
+msgstr "Medienbibliothek"
+
+#: models/abstract.py:62
+msgid "default alt text"
+msgstr "Standard Alt-Text"
+
+#: models/abstract.py:69
+msgid "default caption"
+msgstr "Standard Bildlegende"
+
+#: models/abstract.py:76
+msgid "subject location"
+msgstr "Ort des Hauptinhalts"
+
+#: models/abstract.py:91
+msgid "image"
+msgstr "Bild"
+
+#: models/abstract.py:92
+msgid "images"
+msgstr "Bilder"
+
+#: models/abstract.py:148
+#, python-format
+msgid ""
+"Image format not recognized or image size exceeds limit of %(max_pixels)d "
+"million pixels by a factor of two or more. Before uploading again, check "
+"file format or resize image to %(width)d x %(height)d resolution or lower."
+msgstr "Bildformat nicht erkannt oder Bildgröße überschreitet den Grenzwert von %(max_pixels)d Millionen Pixeln um einen Faktor 2 oder mehr. Vor erneutem Hochladen prüfen Sie das Dateiformat oder verkleinern Sie das Bild auf %(width)d x %(height)d oder weniger."
+
+#: models/abstract.py:156
+#, python-format
+msgid ""
+"Image size (%(pixels)d million pixels) exceeds limit of %(max_pixels)d "
+"million pixels. Before uploading again, resize image to %(width)d x "
+"%(height)d resolution or lower."
+msgstr "Bildgröße (%(pixels)d Millionen Pixel) überschreitet den Grenzwert von %(max_pixels)d Millionen Pixeln. Vor erneutem Hochladen verkleinern Sie das Bild auf %(width)d x %(height)d oder weniger."
+
+#: models/clipboardmodels.py:11 models/foldermodels.py:289
+msgid "user"
+msgstr "Benutzer"
+
+#: models/clipboardmodels.py:17 models/filemodels.py:157
+msgid "files"
+msgstr "Dateien"
+
+#: models/clipboardmodels.py:24 models/clipboardmodels.py:50
+msgid "clipboard"
+msgstr "Zwischenablage"
+
+#: models/clipboardmodels.py:25
+msgid "clipboards"
+msgstr "Zwischenablagen"
+
+#: models/clipboardmodels.py:44 models/filemodels.py:74
+#: models/filemodels.py:156
+msgid "file"
+msgstr "Datei"
+
+#: models/clipboardmodels.py:56
+msgid "clipboard item"
+msgstr "Eintrag der Zwischenablage"
+
+#: models/clipboardmodels.py:57
+msgid "clipboard items"
+msgstr "Einträge der Zwischenablage"
+
+#: models/filemodels.py:66 templates/admin/filer/folder/change_form.html:8
+#: templates/admin/filer/folder/directory_listing.html:102
+#: templates/admin/filer/folder/new_folder_form.html:4
+#: templates/admin/filer/folder/new_folder_form.html:7
+#: templates/admin/filer/tools/clipboard/clipboard.html:27
+msgid "folder"
+msgstr "Ordner"
+
+#: models/filemodels.py:81
+msgid "file size"
+msgstr "Dateigrösse"
+
+#: models/filemodels.py:87
+msgid "sha1"
+msgstr "sha1"
+
+#: models/filemodels.py:94
+msgid "has all mandatory data"
+msgstr "hat alle Pflichtinhalte"
+
+#: models/filemodels.py:100
+msgid "original filename"
+msgstr "ursprünglicher Dateiname"
+
+#: models/filemodels.py:110 models/foldermodels.py:102
+#: models/thumbnailoptionmodels.py:10
+msgid "name"
+msgstr "Name"
+
+#: models/filemodels.py:116
+msgid "description"
+msgstr "Beschreibung"
+
+#: models/filemodels.py:125 models/foldermodels.py:108
+msgid "owner"
+msgstr "Besitzer"
+
+#: models/filemodels.py:129 models/foldermodels.py:116
+msgid "uploaded at"
+msgstr "Hochgeladen am"
+
+#: models/filemodels.py:134 models/foldermodels.py:126
+msgid "modified at"
+msgstr "Verändert am"
+
+#: models/filemodels.py:140
+msgid "Permissions disabled"
+msgstr "Berechtigungen deaktiviert"
+
+#: models/filemodels.py:141
+msgid ""
+"Disable any permission checking for this file. File will be publicly "
+"accessible to anyone."
+msgstr "Zugriffskontrolle für diese Datei deaktivieren. Die Datei wird für jeden öffentlich zugreifbar sein."
+
+#: models/foldermodels.py:94
+msgid "parent"
+msgstr "Übergeordneter Ordner"
+
+#: models/foldermodels.py:121
+msgid "created at"
+msgstr "Angelegt am"
+
+#: models/foldermodels.py:136 templates/admin/filer/breadcrumbs.html:6
+#: templates/admin/filer/folder/directory_listing.html:35
+msgid "Folder"
+msgstr "Ordner"
+
+#: models/foldermodels.py:137
+#: templates/admin/filer/folder/directory_thumbnail_list.html:12
+msgid "Folders"
+msgstr "Ordner"
+
+#: models/foldermodels.py:260
+msgid "all items"
+msgstr "alle Einträge"
+
+#: models/foldermodels.py:261
+msgid "this item only"
+msgstr "nur dieser Eintrag"
+
+#: models/foldermodels.py:262
+msgid "this item and all children"
+msgstr "dieser Eintrag und alle darunter liegenden Einträge"
+
+#: models/foldermodels.py:266
+msgid "inherit"
+msgstr "vererben"
+
+#: models/foldermodels.py:267
+msgid "allow"
+msgstr "erlauben"
+
+#: models/foldermodels.py:268
+msgid "deny"
+msgstr "verbieten"
+
+#: models/foldermodels.py:280
+msgid "type"
+msgstr "Typ"
+
+#: models/foldermodels.py:297
+msgid "group"
+msgstr "Gruppe"
+
+#: models/foldermodels.py:304
+msgid "everybody"
+msgstr "Jeder"
+
+#: models/foldermodels.py:309
+msgid "can read"
+msgstr "kann lesen"
+
+#: models/foldermodels.py:317
+msgid "can edit"
+msgstr "kann ändern"
+
+#: models/foldermodels.py:325
+msgid "can add children"
+msgstr "kann Kinder hinzufügen"
+
+#: models/foldermodels.py:333
+msgid "folder permission"
+msgstr "Ordnerberechtigung"
+
+#: models/foldermodels.py:334
+msgid "folder permissions"
+msgstr "Ordnerberechtigungen"
+
+#: models/foldermodels.py:348
+msgid "Folder cannot be selected with type \"all items\"."
+msgstr "Für den Typ \"Alle Einträge\" kann kein Ordner ausgewählt werden. "
+
+#: models/foldermodels.py:350
+msgid "Folder has to be selected when type is not \"all items\"."
+msgstr "Für alle Typen außer \"Alle Einträge\" muss ein Ordner ausgewählt werden."
+
+#: models/foldermodels.py:352
+msgid "User or group cannot be selected together with \"everybody\"."
+msgstr "Zusammen mit \"Jeder\" kann kein Benutzer oder eine Gruppe ausgewählt werden."
+
+#: models/foldermodels.py:354
+msgid "At least one of user, group, or \"everybody\" has to be selected."
+msgstr "Mindestens ein Eintrag muss gewählt werden: \"Benutzer\", \"Gruppe\" oder \"Jeder\"."
+
+#: models/foldermodels.py:360
+#| msgid "Folders"
+msgid "All Folders"
+msgstr "Alle Ordner"
+
+#: models/foldermodels.py:362
+msgid "Logical Path"
+msgstr "Logischer Pfad"
+
+#: models/foldermodels.py:371
+#, python-brace-format
+msgid "User: {user}"
+msgstr "Nutzer: {user}"
+
+#: models/foldermodels.py:373
+#, python-brace-format
+msgid "Group: {group}"
+msgstr "Gruppe: {group}"
+
+#: models/foldermodels.py:375
+#| msgid "everybody"
+msgid "Everybody"
+msgstr "Jede(r)"
+
+#: models/foldermodels.py:388 templates/admin/filer/widgets/admin_file.html:45
+msgid "Edit"
+msgstr "Edit"
+
+#: models/foldermodels.py:389
+msgid "Read"
+msgstr "Lesen"
+
+#: models/foldermodels.py:390
+#| msgid "can add children"
+msgid "Add children"
+msgstr "Unterordner hinzufügen"
+
+#: models/imagemodels.py:18
+msgid "date taken"
+msgstr "Aufgenommen am"
+
+#: models/imagemodels.py:25
+msgid "author"
+msgstr "Autor"
+
+#: models/imagemodels.py:32
+msgid "must always publish author credit"
+msgstr "Nennung des Autoren ist Pflicht"
+
+#: models/imagemodels.py:37
+msgid "must always publish copyright"
+msgstr "Veröffentlichung des Copyright ist Pflicht"
+
+#: models/thumbnailoptionmodels.py:16
+msgid "width in pixel."
+msgstr "Breite in Pixel."
+
+#: models/thumbnailoptionmodels.py:21
+msgid "height in pixel."
+msgstr "Höhe in Pixel."
+
+#: models/thumbnailoptionmodels.py:38
+msgid "thumbnail options"
+msgstr "Vorschaubild-Optionen"
+
+#: models/virtualitems.py:46
+#: templates/admin/filer/folder/directory_table_list.html:4
+#: templates/admin/filer/folder/directory_table_list.html:170
+#: templates/admin/filer/folder/directory_thumbnail_list.html:5
+#: templates/admin/filer/folder/directory_thumbnail_list.html:146
+#: templates/admin/filer/tools/clipboard/clipboard.html:27
+msgid "Unsorted Uploads"
+msgstr "Nicht sortierte Uploads"
+
+#: models/virtualitems.py:73
+msgid "files with missing metadata"
+msgstr "Dateien mit fehlenden Metadaten"
+
+#: models/virtualitems.py:87 templates/admin/filer/folder/change_form.html:8
+#: templates/admin/filer/folder/directory_listing.html:102
+msgid "root"
+msgstr "Start"
+
+#: settings.py:273
+msgid "Show table view"
+msgstr "Tabellenansicht"
+
+#: settings.py:278
+#| msgid "thumbnail option"
+msgid "Show thumbnail view"
+msgstr "Vorschauansicht"
+
+#: templates/admin/filer/actions.html:5
+msgid "Run the selected action"
+msgstr "Die ausgewählte Aktion ausführen"
+
+#: templates/admin/filer/actions.html:5
+msgid "Go"
+msgstr "Los"
+
+#: templates/admin/filer/actions.html:14
+#: templates/admin/filer/folder/directory_table_list.html:235
+#: templates/admin/filer/folder/directory_thumbnail_list.html:210
+msgid "Click here to select the objects across all pages"
+msgstr "Hier klicken, um die Objekte über alle Seiten hinweg auszuwählen"
+
+#: templates/admin/filer/actions.html:14
+#, python-format
+msgid "Select all %(total_count)s files and/or folders"
+msgstr "Alle %(total_count)s Dateien und/oder Ordner auswählen"
+
+#: templates/admin/filer/actions.html:16
+#: templates/admin/filer/folder/directory_table_list.html:237
+#: templates/admin/filer/folder/directory_thumbnail_list.html:212
+msgid "Clear selection"
+msgstr "Auswahl aufheben"
+
+#: templates/admin/filer/breadcrumbs.html:4
+#: templates/admin/filer/folder/directory_listing.html:28
+msgid "Go back to admin homepage"
+msgstr "Zur Admin Startseite zurück gehen"
+
+#: templates/admin/filer/breadcrumbs.html:4
+#: templates/admin/filer/folder/change_form.html:6
+#: templates/admin/filer/folder/directory_listing.html:29
+msgid "Home"
+msgstr "Startseite"
+
+#: templates/admin/filer/breadcrumbs.html:5
+#: templates/admin/filer/folder/directory_listing.html:32
+msgid "Go back to Filer app"
+msgstr "Zur Filer App zurück gehen"
+
+#: templates/admin/filer/breadcrumbs.html:6
+#: templates/admin/filer/folder/directory_listing.html:35
+msgid "Go back to root folder"
+msgstr "Zum Root Ordner zurück gehen"
+
+#: templates/admin/filer/breadcrumbs.html:8
+#: templates/admin/filer/breadcrumbs.html:18
+#: templates/admin/filer/folder/change_form.html:10
+#: templates/admin/filer/folder/directory_listing.html:39
+#: templates/admin/filer/folder/directory_listing.html:47
+#, python-format
+msgid "Go back to '%(folder_name)s' folder"
+msgstr "Zum Ordner '%(folder_name)s' zurück gehen"
+
+#: templates/admin/filer/change_form.html:26
+msgid "Duplicates"
+msgstr "Duplikate"
+
+#: templates/admin/filer/delete_selected_files_confirmation.html:11
+msgid ""
+"Deleting the selected files and/or folders would result in deleting related "
+"objects, but your account doesn't have permission to delete the following "
+"types of objects:"
+msgstr "Beim Löschen der ausgewählten Dateien und/oder Ordner, würden verbundene Objekte mitgelöscht. Dein Benutzerkonto hat jedoch nicht die erforderlichen Berechtigungen, um die folgenden Objekttypen zu löschen:"
+
+#: templates/admin/filer/delete_selected_files_confirmation.html:19
+msgid ""
+"Deleting the selected files and/or folders would require deleting the "
+"following protected related objects:"
+msgstr "Das Löschen der folgenden ausgewählten Dateien und/oder Ordner würde das Löschen der folgenden geschützten verknüpften Objekte erfordern:"
+
+#: templates/admin/filer/delete_selected_files_confirmation.html:27
+msgid ""
+"Are you sure you want to delete the selected files and/or folders? All of "
+"the following objects and their related items will be deleted:"
+msgstr "Bist Du sicher, dass Du die ausgewählten Dateien und/oder Ordner löschen willst? Alle der folgenden Objekte und ihre verbundenen Einträge werden gelöscht:"
+
+#: templates/admin/filer/delete_selected_files_confirmation.html:46
+#: templates/admin/filer/folder/choose_copy_destination.html:64
+#: templates/admin/filer/folder/choose_move_destination.html:72
+msgid "No, take me back"
+msgstr "Nein, zurück gehen"
+
+#: templates/admin/filer/delete_selected_files_confirmation.html:47
+msgid "Yes, I'm sure"
+msgstr "Ja, ich bin sicher"
+
+#: templates/admin/filer/file/change_form.html:35
+#: templates/admin/filer/folder/change_form.html:21
+#: templates/admin/filer/image/change_form.html:35
+msgid "History"
+msgstr "Historie"
+
+#: templates/admin/filer/file/change_form.html:37
+#: templates/admin/filer/folder/change_form.html:23
+#: templates/admin/filer/image/change_form.html:37
+msgid "View on site"
+msgstr "Auf der Website ansehen"
+
+#: templates/admin/filer/folder/change_form.html:6
+#: templates/admin/filer/folder/change_form.html:7
+#: templates/admin/filer/folder/change_form.html:8
+#: templates/admin/filer/folder/directory_listing.html:102
+msgid "Go back to"
+msgstr "Gehe zurück zu"
+
+#: templates/admin/filer/folder/change_form.html:6
+msgid "admin homepage"
+msgstr "Admin Startseite"
+
+#: templates/admin/filer/folder/change_form.html:37
+#: templates/admin/filer/folder/directory_listing.html:95
+#: templates/admin/filer/folder/directory_listing.html:103
+#: templates/admin/filer/folder/directory_table_list.html:29
+#: templates/admin/filer/folder/directory_table_list.html:59
+#: templates/admin/filer/folder/directory_table_list.html:181
+#: templates/admin/filer/folder/directory_thumbnail_list.html:27
+#: templates/admin/filer/folder/directory_thumbnail_list.html:61
+#: templates/admin/filer/folder/directory_thumbnail_list.html:156
+msgid "Folder Icon"
+msgstr "Ordner Icon"
+
+#: templates/admin/filer/folder/choose_copy_destination.html:23
+msgid ""
+"Your account doesn't have permissions to copy all of the selected files "
+"and/or folders."
+msgstr "Dein Benutzerkonto hat nicht die Berechtigungen, um alle der ausgewählten Dateien und/oder Ordner zu kopieren."
+
+#: templates/admin/filer/folder/choose_copy_destination.html:25
+#: templates/admin/filer/folder/choose_copy_destination.html:31
+#: templates/admin/filer/folder/choose_copy_destination.html:37
+#: templates/admin/filer/folder/choose_move_destination.html:37
+#: templates/admin/filer/folder/choose_move_destination.html:43
+#: templates/admin/filer/folder/choose_move_destination.html:49
+msgid "Take me back"
+msgstr "Zurück gehen"
+
+#: templates/admin/filer/folder/choose_copy_destination.html:29
+#: templates/admin/filer/folder/choose_move_destination.html:41
+msgid "There are no destination folders available."
+msgstr "Es gibt keine verwendbaren Zielordner."
+
+#: templates/admin/filer/folder/choose_copy_destination.html:35
+msgid "There are no files and/or folders available to copy."
+msgstr "Es gibt keine Dateien und/oder Ordner zum Kopieren."
+
+#: templates/admin/filer/folder/choose_copy_destination.html:40
+msgid ""
+"The following files and/or folders will be copied to a destination folder "
+"(retaining their tree structure):"
+msgstr "Die folgenden Dateien und/oder Ordner werden in einen Zielordner kopiert (unter Beibehaltung ihrer Ordnerstruktur):"
+
+#: templates/admin/filer/folder/choose_copy_destination.html:54
+#: templates/admin/filer/folder/choose_move_destination.html:64
+msgid "Destination folder:"
+msgstr "Zielordner"
+
+#: templates/admin/filer/folder/choose_copy_destination.html:65
+#: templates/admin/filer/folder/choose_copy_destination.html:68
+#: templates/admin/filer/folder/directory_listing.html:178
+msgid "Copy"
+msgstr "Kopieren"
+
+#: templates/admin/filer/folder/choose_copy_destination.html:67
+msgid "It is not allowed to copy files into same folder"
+msgstr "Es ist nicht erlaubt Dateien in den selben Ordner zu kopieren"
+
+#: templates/admin/filer/folder/choose_images_resize_options.html:15
+msgid ""
+"Your account doesn't have permissions to resize all of the selected images."
+msgstr "Dein Benutzerkonto hat nicht die erforderlichen Berechtigungen, um die Grösse jedes der ausgewählten Bilder zu verändern."
+
+#: templates/admin/filer/folder/choose_images_resize_options.html:18
+msgid "There are no images available to resize."
+msgstr "Es gibt keine Bilder auf denen die Größenänderung anwendbar ist."
+
+#: templates/admin/filer/folder/choose_images_resize_options.html:20
+msgid "The following images will be resized:"
+msgstr "Die Grösse der folgenden Bilder wird verändert:"
+
+#: templates/admin/filer/folder/choose_images_resize_options.html:32
+msgid "Choose an existing thumbnail option or enter resize parameters:"
+msgstr "Wähle eine vorhandene Vorschaubild Option aus oder gib die Parameter für die Bildgrössenänderung an:"
+
+#: templates/admin/filer/folder/choose_images_resize_options.html:36
+msgid ""
+"Warning: Images will be resized in-place and originals will be lost. Maybe "
+"first make a copy of them to retain the originals."
+msgstr "Warnung: Die Bilder werden in-place verändert. Die Originale gehen dabei verloren. Lege bei Bedarf eine Kopie der Originale an."
+
+#: templates/admin/filer/folder/choose_images_resize_options.html:39
+msgid "Resize"
+msgstr "Bildgrösse verändern"
+
+#: templates/admin/filer/folder/choose_move_destination.html:35
+msgid ""
+"Your account doesn't have permissions to move all of the selected files "
+"and/or folders."
+msgstr "Dein Benutzerkonto hat nicht die erforderlichen Berechtigungen, um alle ausgewählten Dateien und/oder Ordner zu verschieben."
+
+#: templates/admin/filer/folder/choose_move_destination.html:47
+msgid "There are no files and/or folders available to move."
+msgstr "Es gibt keine verschiebbaren Dateien und/oder Ordner."
+
+#: templates/admin/filer/folder/choose_move_destination.html:52
+msgid ""
+"The following files and/or folders will be moved to a destination folder "
+"(retaining their tree structure):"
+msgstr "Die folgenden Dateien und/oder Ordner werden in ein Zielordner verschoben (unter Beibehaltung ihrer Ordnerstruktur):"
+
+#: templates/admin/filer/folder/choose_move_destination.html:73
+#: templates/admin/filer/folder/choose_move_destination.html:76
+#: templates/admin/filer/folder/directory_listing.html:181
+msgid "Move"
+msgstr "Verschieben"
+
+#: templates/admin/filer/folder/choose_move_destination.html:75
+msgid "It is not allowed to move files into same folder"
+msgstr "Es ist nicht erlaubt Dateien in den selben Ordner zu verschieben"
+
+#: templates/admin/filer/folder/choose_rename_format.html:15
+msgid ""
+"Your account doesn't have permissions to rename all of the selected files."
+msgstr "Dein Benutzerkonto hat nicht die erforderlichen Berechtigungen, um alle ausgewählten Dateien umzubenennen."
+
+#: templates/admin/filer/folder/choose_rename_format.html:18
+msgid "There are no files available to rename."
+msgstr "Es sind keine Dateien, die umbenannt werden können."
+
+#: templates/admin/filer/folder/choose_rename_format.html:20
+msgid ""
+"The following files will be renamed (they will stay in their folders and "
+"keep original filename, only displayed filename will be changed):"
+msgstr "Die folgenden Dateien werden umbenannt (sie bleiben in ihren Ordnern und behalten ihren originalen Dateinamen, nur dier angezeigte Dateiname wird geändert):"
+
+#: templates/admin/filer/folder/choose_rename_format.html:59
+msgid "Rename"
+msgstr "Umbenennen"
+
+#: templates/admin/filer/folder/directory_listing.html:94
+msgid "Go back to the parent folder"
+msgstr "Gehe zurück zum übergeordneten Ordner"
+
+#: templates/admin/filer/folder/directory_listing.html:131
+msgid "Change current folder details"
+msgstr "Details des aktuellen Ordners ändern"
+
+#: templates/admin/filer/folder/directory_listing.html:131
+msgid "Change"
+msgstr "Ändern"
+
+#: templates/admin/filer/folder/directory_listing.html:141
+msgid "Click here to run search for entered phrase"
+msgstr "Hier klicken, um nach dem eingegebenen Text zu suchen"
+
+#: templates/admin/filer/folder/directory_listing.html:144
+msgid "Search"
+msgstr "Suchen"
+
+#: templates/admin/filer/folder/directory_listing.html:151
+msgid "Close"
+msgstr "Schliessen"
+
+#: templates/admin/filer/folder/directory_listing.html:153
+msgid "Limit"
+msgstr "Limit"
+
+#: templates/admin/filer/folder/directory_listing.html:158
+msgid "Check it to limit the search to current folder"
+msgstr "Aktivieren, um die Suche auf den aktuellen Ordner zu beschränken"
+
+#: templates/admin/filer/folder/directory_listing.html:159
+msgid "Limit the search to current folder"
+msgstr "Suche auf aktuellen Ordner beschränken"
+
+#: templates/admin/filer/folder/directory_listing.html:175
+msgid "Delete"
+msgstr "Löschen"
+
+#: templates/admin/filer/folder/directory_listing.html:203
+msgid "Adds a new Folder"
+msgstr "Fügt einen neuen Ordner hinzu"
+
+#: templates/admin/filer/folder/directory_listing.html:206
+msgid "New Folder"
+msgstr "Neuer Ordner"
+
+#: templates/admin/filer/folder/directory_listing.html:211
+#: templates/admin/filer/folder/directory_listing.html:218
+#: templates/admin/filer/folder/directory_listing.html:221
+#: templates/admin/filer/folder/directory_listing.html:228
+#: templates/admin/filer/folder/directory_listing.html:235
+msgid "Upload Files"
+msgstr "Dateien hochladen"
+
+#: templates/admin/filer/folder/directory_listing.html:233
+msgid "You have to select a folder first"
+msgstr "Du musst zuerst einen Ordner auswählen"
+
+#: templates/admin/filer/folder/directory_table_list.html:16
+msgid "Name"
+msgstr "Name"
+
+#: templates/admin/filer/folder/directory_table_list.html:17
+#: templates/admin/filer/tools/detail_info.html:50
+msgid "Owner"
+msgstr "Besitzer"
+
+#: templates/admin/filer/folder/directory_table_list.html:18
+#: templates/admin/filer/tools/detail_info.html:34
+msgid "Size"
+msgstr "Grösse"
+
+#: templates/admin/filer/folder/directory_table_list.html:19
+msgid "Action"
+msgstr "Aktion"
+
+#: templates/admin/filer/folder/directory_table_list.html:28
+#: templates/admin/filer/folder/directory_table_list.html:35
+#: templates/admin/filer/folder/directory_table_list.html:58
+#: templates/admin/filer/folder/directory_table_list.html:65
+#: templates/admin/filer/folder/directory_thumbnail_list.html:26
+#: templates/admin/filer/folder/directory_thumbnail_list.html:32
+#: templates/admin/filer/folder/directory_thumbnail_list.html:60
+#: templates/admin/filer/folder/directory_thumbnail_list.html:66
+#, python-format
+msgid "Change '%(item_label)s' folder details"
+msgstr "Ändere Details des Ordners '%(item_label)s'"
+
+#: templates/admin/filer/folder/directory_table_list.html:76
+#: templates/admin/filer/tools/search_form.html:5
+#, python-format
+msgid "%(counter)s folder"
+msgid_plural "%(counter)s folders"
+msgstr[0] "%(counter)s Ordner"
+msgstr[1] "%(counter)s Ordner"
+
+#: templates/admin/filer/folder/directory_table_list.html:77
+#: templates/admin/filer/tools/search_form.html:6
+#, python-format
+msgid "%(counter)s file"
+msgid_plural "%(counter)s files"
+msgstr[0] "%(counter)s Datei"
+msgstr[1] "%(counter)s Dateien"
+
+#: templates/admin/filer/folder/directory_table_list.html:83
+msgid "Change folder details"
+msgstr "Ordner-Details ändern"
+
+#: templates/admin/filer/folder/directory_table_list.html:84
+msgid "Remove folder"
+msgstr "Ordner löschen"
+
+#: templates/admin/filer/folder/directory_table_list.html:96
+#: templates/admin/filer/folder/directory_table_list.html:104
+#: templates/admin/filer/folder/directory_table_list.html:119
+#: templates/admin/filer/folder/directory_thumbnail_list.html:95
+#: templates/admin/filer/folder/directory_thumbnail_list.html:104
+#: templates/admin/filer/folder/directory_thumbnail_list.html:119
+msgid "Select this file"
+msgstr "Diese Datei auswählen"
+
+#: templates/admin/filer/folder/directory_table_list.html:107
+#: templates/admin/filer/folder/directory_table_list.html:122
+#: templates/admin/filer/folder/directory_table_list.html:154
+#: templates/admin/filer/folder/directory_thumbnail_list.html:107
+#: templates/admin/filer/folder/directory_thumbnail_list.html:122
+#, python-format
+msgid "Change '%(item_label)s' details"
+msgstr "Ändere Details von '%(item_label)s'"
+
+#: templates/admin/filer/folder/directory_table_list.html:131
+#: templates/admin/filer/folder/directory_thumbnail_list.html:130
+msgid "Permissions"
+msgstr "Berechtigungen"
+
+#: templates/admin/filer/folder/directory_table_list.html:131
+#: templates/admin/filer/folder/directory_thumbnail_list.html:130
+msgid "disabled"
+msgstr "deaktiviert"
+
+#: templates/admin/filer/folder/directory_table_list.html:131
+#: templates/admin/filer/folder/directory_thumbnail_list.html:130
+msgid "enabled"
+msgstr "aktiviert"
+
+#: templates/admin/filer/folder/directory_table_list.html:144
+#| msgid "Move selected files to clipboard"
+msgid "URL copied to clipboard"
+msgstr "URL in Zwischenablage kopiert"
+
+#: templates/admin/filer/folder/directory_table_list.html:147
+#, python-format
+#| msgid "Change '%(item_label)s' details"
+msgid "Canonical url '%(item_label)s'"
+msgstr "Kanonische URL '%(item_label)s'"
+
+#: templates/admin/filer/folder/directory_table_list.html:151
+#, python-format
+#| msgid "Change '%(item_label)s' details"
+msgid "Download '%(item_label)s'"
+msgstr "'%(item_label)s' herunterladen"
+
+#: templates/admin/filer/folder/directory_table_list.html:155
+msgid "Remove file"
+msgstr "Datei löschen"
+
+#: templates/admin/filer/folder/directory_table_list.html:163
+#: templates/admin/filer/folder/directory_thumbnail_list.html:138
+msgid "Drop files here or use the \"Upload Files\" button"
+msgstr "Dateien hier ablegen oder den \"Datei hochladen\" Button benutzen."
+
+#: templates/admin/filer/folder/directory_table_list.html:178
+#: templates/admin/filer/folder/directory_thumbnail_list.html:153
+msgid "Drop your file to upload into:"
+msgstr "Dateien hier ablegen um sie in den folgenden Ordner abzulegen:"
+
+#: templates/admin/filer/folder/directory_table_list.html:188
+#: templates/admin/filer/folder/directory_thumbnail_list.html:163
+msgid "Upload"
+msgstr "Hochladen"
+
+#: templates/admin/filer/folder/directory_table_list.html:200
+#: templates/admin/filer/folder/directory_thumbnail_list.html:175
+msgid "cancel"
+msgstr "Abbrechen"
+
+#: templates/admin/filer/folder/directory_table_list.html:204
+#: templates/admin/filer/folder/directory_thumbnail_list.html:179
+msgid "Upload success!"
+msgstr "Upload erfolgreich!"
+
+#: templates/admin/filer/folder/directory_table_list.html:208
+#: templates/admin/filer/folder/directory_thumbnail_list.html:183
+msgid "Upload canceled!"
+msgstr "Upload abgebrochen!"
+
+#: templates/admin/filer/folder/directory_table_list.html:215
+#: templates/admin/filer/folder/directory_thumbnail_list.html:190
+msgid "previous"
+msgstr "vorige"
+
+#: templates/admin/filer/folder/directory_table_list.html:220
+#: templates/admin/filer/folder/directory_thumbnail_list.html:195
+#, python-format
+msgid "Page %(number)s of %(num_pages)s."
+msgstr "Seite %(number)s von %(num_pages)s."
+
+#: templates/admin/filer/folder/directory_table_list.html:225
+#: templates/admin/filer/folder/directory_thumbnail_list.html:200
+msgid "next"
+msgstr "nächste"
+
+#: templates/admin/filer/folder/directory_table_list.html:235
+#: templates/admin/filer/folder/directory_thumbnail_list.html:210
+#, python-format
+msgid "Select all %(total_count)s"
+msgstr "Alle %(total_count)s auswählen"
+
+#: templates/admin/filer/folder/directory_thumbnail_list.html:15
+#: templates/admin/filer/folder/directory_thumbnail_list.html:80
+#| msgid "Select this file"
+msgid "Select all"
+msgstr "Alle auswählen"
+
+#: templates/admin/filer/folder/directory_thumbnail_list.html:77
+#| msgid "Filer"
+msgid "Files"
+msgstr "Dateien"
+
+#: templates/admin/filer/folder/new_folder_form.html:4
+#: templates/admin/filer/folder/new_folder_form.html:7
+msgid "Add new"
+msgstr "Hinzufügen"
+
+#: templates/admin/filer/folder/new_folder_form.html:4
+msgid "Django site admin"
+msgstr "Django Seiten-Administration"
+
+#: templates/admin/filer/folder/new_folder_form.html:15
+msgid "Please correct the error below."
+msgid_plural "Please correct the errors below."
+msgstr[0] "Bitte korrigiere den unten genannten Fehler."
+msgstr[1] "Bitte korrigiere die unten genannten Fehler."
+
+#: templates/admin/filer/folder/new_folder_form.html:30
+msgid "Save"
+msgstr "Sichern"
+
+#: templates/admin/filer/templatetags/file_icon.html:9
+msgid "Your browser does not support audio."
+msgstr "Dein Browser unterstützt kein Audio."
+
+#: templates/admin/filer/templatetags/file_icon.html:14
+msgid "Your browser does not support video."
+msgstr "Dein Browser unterstützt kein Video."
+
+#: templates/admin/filer/tools/clipboard/clipboard.html:9
+msgid "Clipboard"
+msgstr "Zwischenablage"
+
+#: templates/admin/filer/tools/clipboard/clipboard.html:21
+msgid "Paste all items here"
+msgstr "Alle Einträge hier einfügen"
+
+#: templates/admin/filer/tools/clipboard/clipboard.html:27
+msgid "Move all clipboard files to"
+msgstr "Bewege alle Dateien in der Zwischenablage nach"
+
+#: templates/admin/filer/tools/clipboard/clipboard.html:27
+msgid "Empty Clipboard"
+msgstr "Zwischenablage leeren"
+
+#: templates/admin/filer/tools/clipboard/clipboard.html:50
+msgid "the clipboard is empty"
+msgstr "die Zwischenablage ist leer"
+
+#: templates/admin/filer/tools/clipboard/clipboard_item_rows.html:16
+msgid "upload failed"
+msgstr "das Hochladen schlug fehl"
+
+#: templates/admin/filer/tools/detail_info.html:11
+msgid "Download"
+msgstr "Herunterladen"
+
+#: templates/admin/filer/tools/detail_info.html:16
+msgid "Expand"
+msgstr "Vergrößern"
+
+#: templates/admin/filer/tools/detail_info.html:21
+#: templates/admin/filer/widgets/admin_file.html:32
+#: templatetags/filer_admin_tags.py:108
+#| msgid "file missing"
+msgid "File is missing"
+msgstr "Datei fehlt"
+
+#: templates/admin/filer/tools/detail_info.html:30
+msgid "Type"
+msgstr "Typ"
+
+#: templates/admin/filer/tools/detail_info.html:38
+msgid "File-size"
+msgstr "Dateigrösse"
+
+#: templates/admin/filer/tools/detail_info.html:42
+msgid "Modified"
+msgstr "Verändert am"
+
+#: templates/admin/filer/tools/detail_info.html:46
+msgid "Created"
+msgstr "Erstellt am"
+
+#: templates/admin/filer/tools/search_form.html:5
+msgid "found"
+msgstr "gefunden"
+
+#: templates/admin/filer/tools/search_form.html:5
+msgid "and"
+msgstr "und"
+
+#: templates/admin/filer/tools/search_form.html:7
+msgid "cancel search"
+msgstr "Suche abbrechen"
+
+#: templates/admin/filer/widgets/admin_file.html:10
+#: templates/admin/filer/widgets/admin_file.html:39
+#: templates/admin/filer/widgets/admin_folder.html:18
+msgid "Clear"
+msgstr "Zurücksetzen"
+
+#: templates/admin/filer/widgets/admin_file.html:22
+msgid "or drop your file here"
+msgstr "oder die Datei hierhin ziehen"
+
+#: templates/admin/filer/widgets/admin_file.html:35
+msgid "no file selected"
+msgstr "keine Datei ausgewählt"
+
+#: templates/admin/filer/widgets/admin_file.html:50
+#: templates/admin/filer/widgets/admin_folder.html:14
+msgid "Lookup"
+msgstr "Nachschlagen"
+
+#: templates/admin/filer/widgets/admin_file.html:51
+msgid "Choose File"
+msgstr "Datei auswählen"
+
+#: templates/admin/filer/widgets/admin_folder.html:16
+#| msgid "Choose File"
+msgid "Choose Folder"
+msgstr "Ordner auswählen"
+
+#: validation.py:20
+#, python-brace-format
+msgid "File \"{file_name}\": Upload denied by site security policy"
+msgstr "Datei \"{file_name}\": Die Sicherheitseinstellung der Seite haben die Datei zurückgewiesen."
+
+#: validation.py:23
+#, python-brace-format
+msgid "File \"{file_name}\": {file_type} upload denied by site security policy"
+msgstr "Datei \"{file_name}\": Die Sicherheitseinstellung weisen {file_type}-Dateien zurück."
+
+#: validation.py:34
+#, python-brace-format
+msgid "File \"{file_name}\": HTML upload denied by site security policy"
+msgstr "Datei \"{file_name}\": Die Sicherheitseinstellung weisen HTML-Dateien zurück."
+
+#: validation.py:72
+#, python-brace-format
+msgid ""
+"File \"{file_name}\": Rejected due to potential cross site scripting "
+"vulnerability"
+msgstr "Datei \"{file_name}\": Zurückgewiesen, da eine \"Cross Site Scripting\"-Attacke nicht ausgeschlossen werden kann."
+
+#: validation.py:84
+#, python-brace-format
+msgid "File \"{file_name}\": SVG file format not recognized"
+msgstr "Datei \"{file_name}\": SVG-Format nicht erkannt"
+
+#~ msgid "Resize parameters must be choosen."
+#~ msgstr "Resize parameters must be choosen."
+
+#~ msgid "Choose resize parameters:"
+#~ msgstr "Choose resize parameters:"
+
+#~ msgid "Open file"
+#~ msgstr "Open file"
+
+#~ msgid "Full size preview"
+#~ msgstr "Full size preview"
+
+#~ msgid "Add Description"
+#~ msgstr "Add Description"
+
+#~ msgid "Author"
+#~ msgstr "Author"
+
+#~ msgid "Add Alt-Text"
+#~ msgstr "Add Alt-Text"
+
+#~ msgid "Add caption"
+#~ msgstr "Add caption"
+
+#~ msgid "Add Tags"
+#~ msgstr "Add Tags"
+
+#~ msgid "Change File"
+#~ msgstr "Change File"
+
+#~ msgid "none selected"
+#~ msgstr "none selected"
+
+#~ msgid "Add Folder"
+#~ msgstr "Add Folder"
+
+#~ msgid "Subject Location"
+#~ msgstr "Subject location"
+
+#~ msgid ""
+#~ "\n"
+#~ " %(counter)s file"
+#~ msgid_plural ""
+#~ "%(counter)s files\n"
+#~ " "
+#~ msgstr[0] "15c0f2d28d6dab1af1f6d94906beb627_pl_0"
+#~ msgstr[1] "15c0f2d28d6dab1af1f6d94906beb627_pl_1"
+
+#~ msgid "Upload File"
+#~ msgstr "Upload File"
diff --git a/filer/locale/en/LC_MESSAGES/django.mo b/filer/locale/en/LC_MESSAGES/django.mo
index 11a1682b6234eda7558e71b08dc2e644d7167e7e..4f8792c0612b0ca8ac1345f94ed252f54afddbad 100644
GIT binary patch
literal 15724
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
J`(XDc{{bkXdFucG
diff --git a/filer/locale/en/LC_MESSAGES/django.po b/filer/locale/en/LC_MESSAGES/django.po
index 449ccce4c..fbcc67bdc 100644
--- a/filer/locale/en/LC_MESSAGES/django.po
+++ b/filer/locale/en/LC_MESSAGES/django.po
@@ -1,483 +1,643 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
-# FIRST AUTHOR , YEAR.
#
+# Translators:
+# Translators:
msgid ""
msgstr ""
-"Project-Id-Version: django-filer 0.9\n"
+"Project-Id-Version: django Filer\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-07-13 19:10+0200\n"
-"PO-Revision-Date: 2012-09-03 20:10+0100\n"
-"Last-Translator: Stefan Foulis \n"
-"Language-Team: en \n"
+"POT-Creation-Date: 2023-09-30 18:10+0200\n"
+"PO-Revision-Date: 2016-06-17 14:16+0000\n"
+"Last-Translator: Angelo Dini \n"
+"Language-Team: English (http://www.transifex.com/divio/django-filer/language/"
+"en/)\n"
"Language: en\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-#: views.py:103
-msgid "Folder with this name already exists."
+#: admin/clipboardadmin.py:17
+#, fuzzy
+#| msgid ""
+#| "Your account doesn't have permissions to rename all of the selected files."
+msgid "You do not have permission to upload files."
msgstr ""
+"Your account doesn't have permissions to rename all of the selected files."
-#: admin/fileadmin.py:41
-msgid "Advanced"
+#: admin/clipboardadmin.py:18
+msgid "Can't find folder to upload. Please refresh and try again"
msgstr ""
-#: admin/folderadmin.py:324 admin/folderadmin.py:427
+#: admin/clipboardadmin.py:20
+msgid "Can't use this folder, Permission Denied. Please select another folder."
+msgstr ""
+
+#: admin/fileadmin.py:73
+msgid "Advanced"
+msgstr "Advanced"
+
+#: admin/fileadmin.py:188
+msgid "canonical URL"
+msgstr "canonical URL"
+
+#: admin/folderadmin.py:411 admin/folderadmin.py:582
msgid ""
"Items must be selected in order to perform actions on them. No items have "
"been changed."
msgstr ""
+"Items must be selected in order to perform actions on them. No items have "
+"been changed."
-#: admin/folderadmin.py:344
+#: admin/folderadmin.py:434
#, python-format
msgid "%(total_count)s selected"
msgid_plural "All %(total_count)s selected"
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "%(total_count)s selected"
+msgstr[1] "All %(total_count)s selected"
-#: admin/folderadmin.py:377
+#: admin/folderadmin.py:460
#, python-format
-msgid "0 of %(cnt)s selected"
+msgid "Directory listing for %(folder_name)s"
msgstr ""
-#: admin/folderadmin.py:456
+#: admin/folderadmin.py:475
+#, python-format
+msgid "0 of %(cnt)s selected"
+msgstr "0 of %(cnt)s selected"
+
+#: admin/folderadmin.py:612
msgid "No action selected."
-msgstr ""
+msgstr "No action selected."
-#: admin/folderadmin.py:497
+#: admin/folderadmin.py:664
#, python-format
msgid "Successfully moved %(count)d files to clipboard."
-msgstr ""
+msgstr "Successfully moved %(count)d files to clipboard."
-#: admin/folderadmin.py:503
+#: admin/folderadmin.py:669
msgid "Move selected files to clipboard"
-msgstr ""
+msgstr "Move selected files to clipboard"
-#: admin/folderadmin.py:537
+#: admin/folderadmin.py:709
#, python-format
msgid "Successfully disabled permissions for %(count)d files."
-msgstr ""
+msgstr "Successfully disabled permissions for %(count)d files."
-#: admin/folderadmin.py:541
+#: admin/folderadmin.py:711
#, python-format
msgid "Successfully enabled permissions for %(count)d files."
-msgstr ""
+msgstr "Successfully enabled permissions for %(count)d files."
-#: admin/folderadmin.py:550
+#: admin/folderadmin.py:719
msgid "Enable permissions for selected files"
-msgstr ""
+msgstr "Enable permissions for selected files"
-#: admin/folderadmin.py:555
+#: admin/folderadmin.py:725
msgid "Disable permissions for selected files"
-msgstr ""
+msgstr "Disable permissions for selected files"
-#: admin/folderadmin.py:623
+#: admin/folderadmin.py:789
#, python-format
msgid "Successfully deleted %(count)d files and/or folders."
-msgstr ""
+msgstr "Successfully deleted %(count)d files and/or folders."
-#: admin/folderadmin.py:630
+#: admin/folderadmin.py:794
msgid "Cannot delete files and/or folders"
-msgstr ""
+msgstr "Cannot delete files and/or folders"
-#: admin/folderadmin.py:632
+#: admin/folderadmin.py:796
msgid "Are you sure?"
-msgstr ""
+msgstr "Are you sure?"
-#: admin/folderadmin.py:637
+#: admin/folderadmin.py:802
msgid "Delete files and/or folders"
-msgstr ""
+msgstr "Delete files and/or folders"
-#: admin/folderadmin.py:656
+#: admin/folderadmin.py:823
msgid "Delete selected files and/or folders"
-msgstr ""
+msgstr "Delete selected files and/or folders"
+
+#: admin/folderadmin.py:930
+#, python-format
+msgid "Folders with names %s already exist at the selected destination"
+msgstr "Folders with names %s already exist at the selected destination"
-#: admin/folderadmin.py:771
+#: admin/folderadmin.py:934
#, python-format
msgid ""
"Successfully moved %(count)d files and/or folders to folder "
"'%(destination)s'."
msgstr ""
+"Successfully moved %(count)d files and/or folders to folder "
+"'%(destination)s'."
-#: admin/folderadmin.py:778 admin/folderadmin.py:780
+#: admin/folderadmin.py:942 admin/folderadmin.py:944
msgid "Move files and/or folders"
-msgstr ""
+msgstr "Move files and/or folders"
-#: admin/folderadmin.py:797
+#: admin/folderadmin.py:959
msgid "Move selected files and/or folders"
-msgstr ""
+msgstr "Move selected files and/or folders"
-#: admin/folderadmin.py:854
+#: admin/folderadmin.py:1016
#, python-format
msgid "Successfully renamed %(count)d files."
-msgstr ""
+msgstr "Successfully renamed %(count)d files."
-#: admin/folderadmin.py:862 admin/folderadmin.py:864 admin/folderadmin.py:881
+#: admin/folderadmin.py:1025 admin/folderadmin.py:1027
+#: admin/folderadmin.py:1042
msgid "Rename files"
-msgstr ""
+msgstr "Rename files"
-#: admin/folderadmin.py:975
+#: admin/folderadmin.py:1137
#, python-format
msgid ""
"Successfully copied %(count)d files and/or folders to folder "
"'%(destination)s'."
msgstr ""
+"Successfully copied %(count)d files and/or folders to folder "
+"'%(destination)s'."
-#: admin/folderadmin.py:989 admin/folderadmin.py:991
+#: admin/folderadmin.py:1155 admin/folderadmin.py:1157
msgid "Copy files and/or folders"
-msgstr ""
+msgstr "Copy files and/or folders"
-#: admin/folderadmin.py:1010
+#: admin/folderadmin.py:1174
msgid "Copy selected files and/or folders"
-msgstr ""
+msgstr "Copy selected files and/or folders"
-#: admin/folderadmin.py:1105
+#: admin/folderadmin.py:1279
#, python-format
msgid "Successfully resized %(count)d images."
-msgstr ""
+msgstr "Successfully resized %(count)d images."
-#: admin/folderadmin.py:1113 admin/folderadmin.py:1115
+#: admin/folderadmin.py:1286 admin/folderadmin.py:1288
msgid "Resize images"
-msgstr ""
+msgstr "Resize images"
-#: admin/folderadmin.py:1133
+#: admin/folderadmin.py:1303
msgid "Resize selected images"
-msgstr ""
+msgstr "Resize selected images"
-#: admin/forms.py:26
+#: admin/forms.py:24
msgid "Suffix which will be appended to filenames of copied files."
-msgstr ""
+msgstr "Suffix which will be appended to filenames of copied files."
-#: admin/forms.py:33
+#: admin/forms.py:31
#, python-format
msgid ""
"Suffix should be a valid, simple and lowercase filename part, like "
"\"%(valid)s\"."
msgstr ""
+"Suffix should be a valid, simple and lowercase filename part, like "
+"\"%(valid)s\"."
-#: admin/forms.py:54
+#: admin/forms.py:52
#, python-format
msgid "Unknown rename format value key \"%(key)s\"."
-msgstr ""
+msgstr "Unknown rename format value key \"%(key)s\"."
-#: admin/forms.py:56
+#: admin/forms.py:54
#, python-format
msgid "Invalid rename format: %(error)s."
-msgstr ""
+msgstr "Invalid rename format: %(error)s."
-#: admin/forms.py:62
+#: admin/forms.py:68 models/thumbnailoptionmodels.py:37
msgid "thumbnail option"
-msgstr ""
+msgstr "thumbnail option"
-#: admin/forms.py:63
+#: admin/forms.py:72 models/thumbnailoptionmodels.py:15
msgid "width"
-msgstr ""
+msgstr "width"
-#: admin/forms.py:64
+#: admin/forms.py:73 models/thumbnailoptionmodels.py:20
msgid "height"
-msgstr ""
+msgstr "height"
-#: admin/forms.py:65
+#: admin/forms.py:74 models/thumbnailoptionmodels.py:25
msgid "crop"
-msgstr ""
+msgstr "crop"
-#: admin/forms.py:66
+#: admin/forms.py:75 models/thumbnailoptionmodels.py:30
msgid "upscale"
-msgstr ""
+msgstr "upscale"
-#: admin/forms.py:71
+#: admin/forms.py:79
msgid "Thumbnail option or resize parameters must be choosen."
+msgstr "Thumbnail option or resize parameters must be choosen."
+
+#: admin/imageadmin.py:20 admin/imageadmin.py:112
+msgid "Subject location"
+msgstr "Subject location"
+
+#: admin/imageadmin.py:21
+msgid "Location of the main subject of the scene. Format: \"x,y\"."
+msgstr "Location of the main subject of the scene. Format: \"x,y\"."
+
+#: admin/imageadmin.py:59
+msgid "Invalid subject location format. "
+msgstr "Invalid subject location format. "
+
+#: admin/imageadmin.py:67
+msgid "Subject location is outside of the image. "
+msgstr "Subject location is outside of the image. "
+
+#: admin/imageadmin.py:76
+#, python-brace-format
+msgid "Your input: \"{subject_location}\". "
+msgstr "Your input: \"{subject_location}\". "
+
+#: admin/permissionadmin.py:10 models/foldermodels.py:380
+msgid "Who"
msgstr ""
-#: admin/forms.py:73
-msgid "Resize parameters must be choosen."
+#: admin/permissionadmin.py:11 models/foldermodels.py:401
+msgid "What"
msgstr ""
-#: admin/imageadmin.py:12
-msgid "Subject location"
+#: admin/views.py:55
+msgid "Folder with this name already exists."
+msgstr "Folder with this name already exists."
+
+#: apps.py:11 templates/admin/filer/breadcrumbs.html:5
+#: templates/admin/filer/folder/change_form.html:7
+#: templates/admin/filer/folder/directory_listing.html:33
+msgid "Filer"
+msgstr "Filer"
+
+#: contrib/django_cms/cms_toolbars.py:44
+msgid "Media library"
msgstr ""
-#: admin/imageadmin.py:13
-msgid "Location of the main subject of the scene."
+#: models/abstract.py:62
+msgid "default alt text"
+msgstr "default alt text"
+
+#: models/abstract.py:69
+msgid "default caption"
+msgstr "default caption"
+
+#: models/abstract.py:76
+msgid "subject location"
+msgstr "subject location"
+
+#: models/abstract.py:91
+msgid "image"
+msgstr "image"
+
+#: models/abstract.py:92
+msgid "images"
+msgstr "images"
+
+#: models/abstract.py:148
+#, python-format
+msgid ""
+"Image format not recognized or image size exceeds limit of %(max_pixels)d "
+"million pixels by a factor of two or more. Before uploading again, check "
+"file format or resize image to %(width)d x %(height)d resolution or lower."
msgstr ""
-#: models/clipboardmodels.py:9 models/foldermodels.py:236
-msgid "user"
+#: models/abstract.py:156
+#, python-format
+msgid ""
+"Image size (%(pixels)d million pixels) exceeds limit of %(max_pixels)d "
+"million pixels. Before uploading again, resize image to %(width)d x "
+"%(height)d resolution or lower."
msgstr ""
-#: models/clipboardmodels.py:11 models/filemodels.py:287
+#: models/clipboardmodels.py:11 models/foldermodels.py:289
+msgid "user"
+msgstr "user"
+
+#: models/clipboardmodels.py:17 models/filemodels.py:157
msgid "files"
-msgstr ""
+msgstr "files"
-#: models/clipboardmodels.py:34 models/clipboardmodels.py:40
+#: models/clipboardmodels.py:24 models/clipboardmodels.py:50
msgid "clipboard"
-msgstr ""
+msgstr "clipboard"
-#: models/clipboardmodels.py:35
+#: models/clipboardmodels.py:25
msgid "clipboards"
-msgstr ""
+msgstr "clipboards"
-#: models/clipboardmodels.py:39 models/filemodels.py:35
-#: models/filemodels.py:286
+#: models/clipboardmodels.py:44 models/filemodels.py:74
+#: models/filemodels.py:156
msgid "file"
-msgstr ""
+msgstr "file"
-#: models/clipboardmodels.py:44
+#: models/clipboardmodels.py:56
msgid "clipboard item"
-msgstr ""
+msgstr "clipboard item"
-#: models/clipboardmodels.py:45
+#: models/clipboardmodels.py:57
msgid "clipboard items"
-msgstr ""
+msgstr "clipboard items"
-#: models/filemodels.py:33 templates/admin/filer/folder/change_form.html:9
-#: templates/admin/filer/folder/directory_listing.html:74
+#: models/filemodels.py:66 templates/admin/filer/folder/change_form.html:8
+#: templates/admin/filer/folder/directory_listing.html:102
#: templates/admin/filer/folder/new_folder_form.html:4
-#: templates/admin/filer/tools/clipboard/clipboard.html:26
+#: templates/admin/filer/folder/new_folder_form.html:7
+#: templates/admin/filer/tools/clipboard/clipboard.html:27
msgid "folder"
-msgstr ""
+msgstr "folder"
-#: models/filemodels.py:36
+#: models/filemodels.py:81
msgid "file size"
-msgstr ""
+msgstr "file size"
-#: models/filemodels.py:38
+#: models/filemodels.py:87
msgid "sha1"
-msgstr ""
+msgstr "sha1"
-#: models/filemodels.py:40
+#: models/filemodels.py:94
msgid "has all mandatory data"
-msgstr ""
+msgstr "has all mandatory data"
-#: models/filemodels.py:42
+#: models/filemodels.py:100
msgid "original filename"
-msgstr ""
+msgstr "original filename"
-#: models/filemodels.py:44 models/foldermodels.py:99
+#: models/filemodels.py:110 models/foldermodels.py:102
+#: models/thumbnailoptionmodels.py:10
msgid "name"
-msgstr ""
+msgstr "name"
-#: models/filemodels.py:46
+#: models/filemodels.py:116
msgid "description"
-msgstr ""
+msgstr "description"
-#: models/filemodels.py:50
+#: models/filemodels.py:125 models/foldermodels.py:108
msgid "owner"
-msgstr "uploaded by"
+msgstr "owner"
-#: models/filemodels.py:52 models/foldermodels.py:105
+#: models/filemodels.py:129 models/foldermodels.py:116
msgid "uploaded at"
-msgstr ""
+msgstr "uploaded at"
-#: models/filemodels.py:53 models/foldermodels.py:108
+#: models/filemodels.py:134 models/foldermodels.py:126
msgid "modified at"
-msgstr ""
+msgstr "modified at"
-#: models/filemodels.py:57
+#: models/filemodels.py:140
msgid "Permissions disabled"
+msgstr "Permissions disabled"
+
+#: models/filemodels.py:141
+msgid ""
+"Disable any permission checking for this file. File will be publicly "
+"accessible to anyone."
msgstr ""
+"Disable any permission checking for this file. File will be publicly "
+"accessible to anyone."
-#: models/filemodels.py:58
-msgid "Disable any permission checking for this "
+#: models/foldermodels.py:94
+msgid "parent"
msgstr ""
-#: models/foldermodels.py:107
+#: models/foldermodels.py:121
msgid "created at"
-msgstr ""
+msgstr "created at"
-#: models/foldermodels.py:211
-#: templates/admin/filer/widgets/admin_folder.html:3
-#: templates/admin/filer/widgets/admin_folder.html:5
+#: models/foldermodels.py:136 templates/admin/filer/breadcrumbs.html:6
+#: templates/admin/filer/folder/directory_listing.html:35
msgid "Folder"
-msgstr ""
+msgstr "Folder"
-#: models/foldermodels.py:212
+#: models/foldermodels.py:137
+#: templates/admin/filer/folder/directory_thumbnail_list.html:12
msgid "Folders"
-msgstr ""
+msgstr "Folders"
-#: models/foldermodels.py:227
+#: models/foldermodels.py:260
msgid "all items"
-msgstr ""
+msgstr "all items"
-#: models/foldermodels.py:228
+#: models/foldermodels.py:261
msgid "this item only"
-msgstr ""
+msgstr "this item only"
-#: models/foldermodels.py:229
+#: models/foldermodels.py:262
msgid "this item and all children"
+msgstr "this item and all children"
+
+#: models/foldermodels.py:266
+msgid "inherit"
msgstr ""
-#: models/foldermodels.py:233
+#: models/foldermodels.py:267
+msgid "allow"
+msgstr "allow"
+
+#: models/foldermodels.py:268
+msgid "deny"
+msgstr "deny"
+
+#: models/foldermodels.py:280
msgid "type"
-msgstr ""
+msgstr "type"
-#: models/foldermodels.py:239
+#: models/foldermodels.py:297
msgid "group"
-msgstr ""
+msgstr "group"
-#: models/foldermodels.py:240
+#: models/foldermodels.py:304
msgid "everybody"
-msgstr ""
-
-#: models/foldermodels.py:242
-msgid "can edit"
-msgstr ""
+msgstr "everybody"
-#: models/foldermodels.py:243
+#: models/foldermodels.py:309
msgid "can read"
-msgstr ""
+msgstr "can read"
-#: models/foldermodels.py:244
+#: models/foldermodels.py:317
+msgid "can edit"
+msgstr "can edit"
+
+#: models/foldermodels.py:325
msgid "can add children"
-msgstr ""
+msgstr "can add children"
-#: models/foldermodels.py:273
+#: models/foldermodels.py:333
msgid "folder permission"
-msgstr ""
+msgstr "folder permission"
-#: models/foldermodels.py:274
+#: models/foldermodels.py:334
msgid "folder permissions"
-msgstr ""
+msgstr "folder permissions"
-#: models/imagemodels.py:39
-msgid "date taken"
+#: models/foldermodels.py:348
+msgid "Folder cannot be selected with type \"all items\"."
msgstr ""
-#: models/imagemodels.py:42
-msgid "default alt text"
+#: models/foldermodels.py:350
+msgid "Folder has to be selected when type is not \"all items\"."
msgstr ""
-#: models/imagemodels.py:43
-msgid "default caption"
+#: models/foldermodels.py:352
+msgid "User or group cannot be selected together with \"everybody\"."
msgstr ""
-#: models/imagemodels.py:45 templates/image_filer/image.html:6
-msgid "author"
+#: models/foldermodels.py:354
+msgid "At least one of user, group, or \"everybody\" has to be selected."
msgstr ""
-#: models/imagemodels.py:47
-msgid "must always publish author credit"
-msgstr ""
+#: models/foldermodels.py:360
+#, fuzzy
+#| msgid "Folders"
+msgid "All Folders"
+msgstr "Folders"
-#: models/imagemodels.py:48
-msgid "must always publish copyright"
+#: models/foldermodels.py:362
+msgid "Logical Path"
msgstr ""
-#: models/imagemodels.py:50
-msgid "subject location"
+#: models/foldermodels.py:371
+#, python-brace-format
+msgid "User: {user}"
msgstr ""
-#: models/imagemodels.py:200
-msgid "image"
+#: models/foldermodels.py:373
+#, python-brace-format
+msgid "Group: {group}"
msgstr ""
-#: models/imagemodels.py:201
-msgid "images"
+#: models/foldermodels.py:375
+#, fuzzy
+#| msgid "everybody"
+msgid "Everybody"
+msgstr "everybody"
+
+#: models/foldermodels.py:388 templates/admin/filer/widgets/admin_file.html:45
+msgid "Edit"
msgstr ""
-#: models/virtualitems.py:45
-#: templates/admin/filer/tools/clipboard/clipboard.html:26
-msgid "unfiled files"
+#: models/foldermodels.py:389
+msgid "Read"
msgstr ""
-#: models/virtualitems.py:59
+#: models/foldermodels.py:390
+#, fuzzy
+#| msgid "can add children"
+msgid "Add children"
+msgstr "can add children"
+
+#: models/imagemodels.py:18
+msgid "date taken"
+msgstr "date taken"
+
+#: models/imagemodels.py:25
+msgid "author"
+msgstr "author"
+
+#: models/imagemodels.py:32
+msgid "must always publish author credit"
+msgstr "must always publish author credit"
+
+#: models/imagemodels.py:37
+msgid "must always publish copyright"
+msgstr "must always publish copyright"
+
+#: models/thumbnailoptionmodels.py:16
+msgid "width in pixel."
+msgstr "width in pixel."
+
+#: models/thumbnailoptionmodels.py:21
+msgid "height in pixel."
+msgstr "height in pixel."
+
+#: models/thumbnailoptionmodels.py:38
+msgid "thumbnail options"
+msgstr "thumbnail options"
+
+#: models/virtualitems.py:46
+#: templates/admin/filer/folder/directory_table_list.html:4
+#: templates/admin/filer/folder/directory_table_list.html:170
+#: templates/admin/filer/folder/directory_thumbnail_list.html:5
+#: templates/admin/filer/folder/directory_thumbnail_list.html:146
+#: templates/admin/filer/tools/clipboard/clipboard.html:27
+msgid "Unsorted Uploads"
+msgstr "Unsorted Uploads"
+
+#: models/virtualitems.py:73
msgid "files with missing metadata"
-msgstr ""
+msgstr "files with missing metadata"
-#: models/virtualitems.py:73 templates/admin/filer/breadcrumbs.html:6
-#: templates/admin/filer/folder/change_form.html:9
-#: templates/admin/filer/folder/directory_listing.html:74
-#: templates/image_filer/image_export_form.html:10
+#: models/virtualitems.py:87 templates/admin/filer/folder/change_form.html:8
+#: templates/admin/filer/folder/directory_listing.html:102
msgid "root"
+msgstr "root"
+
+#: settings.py:273
+msgid "Show table view"
msgstr ""
-#: templates/admin/filer/actions.html:4
+#: settings.py:278
+#, fuzzy
+#| msgid "thumbnail option"
+msgid "Show thumbnail view"
+msgstr "thumbnail option"
+
+#: templates/admin/filer/actions.html:5
msgid "Run the selected action"
-msgstr ""
+msgstr "Run the selected action"
-#: templates/admin/filer/actions.html:4
+#: templates/admin/filer/actions.html:5
msgid "Go"
-msgstr ""
+msgstr "Go"
-#: templates/admin/filer/actions.html:11
+#: templates/admin/filer/actions.html:14
+#: templates/admin/filer/folder/directory_table_list.html:235
+#: templates/admin/filer/folder/directory_thumbnail_list.html:210
msgid "Click here to select the objects across all pages"
-msgstr ""
+msgstr "Click here to select the objects across all pages"
-#: templates/admin/filer/actions.html:11
+#: templates/admin/filer/actions.html:14
#, python-format
msgid "Select all %(total_count)s files and/or folders"
-msgstr ""
+msgstr "Select all %(total_count)s files and/or folders"
-#: templates/admin/filer/actions.html:13
+#: templates/admin/filer/actions.html:16
+#: templates/admin/filer/folder/directory_table_list.html:237
+#: templates/admin/filer/folder/directory_thumbnail_list.html:212
msgid "Clear selection"
-msgstr ""
+msgstr "Clear selection"
#: templates/admin/filer/breadcrumbs.html:4
+#: templates/admin/filer/folder/directory_listing.html:28
msgid "Go back to admin homepage"
-msgstr ""
+msgstr "Go back to admin homepage"
#: templates/admin/filer/breadcrumbs.html:4
-#: templates/admin/filer/folder/change_form.html:7
-#: templates/image_filer/image_export_form.html:7
+#: templates/admin/filer/folder/change_form.html:6
+#: templates/admin/filer/folder/directory_listing.html:29
msgid "Home"
-msgstr ""
+msgstr "Home"
#: templates/admin/filer/breadcrumbs.html:5
+#: templates/admin/filer/folder/directory_listing.html:32
msgid "Go back to Filer app"
-msgstr ""
-
-#: templates/admin/filer/breadcrumbs.html:5
-#: templates/admin/filer/folder/change_form.html:8
-#: templates/image_filer/image_export_form.html:8
-msgid "Filer"
-msgstr ""
+msgstr "Go back to Filer app"
#: templates/admin/filer/breadcrumbs.html:6
-#: templates/image_filer/image_export_form.html:10
+#: templates/admin/filer/folder/directory_listing.html:35
msgid "Go back to root folder"
-msgstr ""
+msgstr "Go back to root folder"
#: templates/admin/filer/breadcrumbs.html:8
-#: templates/admin/filer/breadcrumbs.html:11
-#: templates/admin/filer/folder/change_form.html:11
-#: templates/image_filer/image_export_form.html:12
-#: templates/image_filer/image_export_form.html:14
+#: templates/admin/filer/breadcrumbs.html:18
+#: templates/admin/filer/folder/change_form.html:10
+#: templates/admin/filer/folder/directory_listing.html:39
+#: templates/admin/filer/folder/directory_listing.html:47
#, python-format
msgid "Go back to '%(folder_name)s' folder"
-msgstr ""
+msgstr "Go back to '%(folder_name)s' folder"
-#: templates/admin/filer/change_form.html:28
-msgid "duplicates"
-msgstr ""
-
-#: templates/admin/filer/delete_confirmation.html:13
-#, python-format
-msgid ""
-"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting "
-"related objects, but your account doesn't have permission to delete the "
-"following types of objects:"
-msgstr ""
-
-#: templates/admin/filer/delete_confirmation.html:21
-#, python-format
-msgid ""
-"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the "
-"following protected related objects:"
-msgstr ""
-
-#: templates/admin/filer/delete_confirmation.html:29
-#, python-format
-msgid ""
-"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? "
-"All of the following related items will be deleted:"
-msgstr ""
-
-#: templates/admin/filer/delete_confirmation.html:34
-#: templates/admin/filer/delete_selected_files_confirmation.html:45
-msgid "Yes, I'm sure"
-msgstr ""
+#: templates/admin/filer/change_form.html:26
+msgid "Duplicates"
+msgstr "Duplicates"
#: templates/admin/filer/delete_selected_files_confirmation.html:11
msgid ""
@@ -485,399 +645,636 @@ msgid ""
"objects, but your account doesn't have permission to delete the following "
"types of objects:"
msgstr ""
+"Deleting the selected files and/or folders would result in deleting related "
+"objects, but your account doesn't have permission to delete the following "
+"types of objects:"
#: templates/admin/filer/delete_selected_files_confirmation.html:19
msgid ""
"Deleting the selected files and/or folders would require deleting the "
"following protected related objects:"
msgstr ""
+"Deleting the selected files and/or folders would require deleting the "
+"following protected related objects:"
#: templates/admin/filer/delete_selected_files_confirmation.html:27
msgid ""
"Are you sure you want to delete the selected files and/or folders? All of "
"the following objects and their related items will be deleted:"
msgstr ""
+"Are you sure you want to delete the selected files and/or folders? All of "
+"the following objects and their related items will be deleted:"
-#: templates/admin/filer/folder/change_form.html:7
-#: templates/admin/filer/folder/change_form.html:8
-#: templates/admin/filer/folder/change_form.html:9
-#: templates/admin/filer/folder/directory_listing.html:74
-#: templates/image_filer/image_export_form.html:7
-#: templates/image_filer/image_export_form.html:8
-#: templates/image_filer/image_export_form.html:16
-msgid "Go back to"
-msgstr ""
+#: templates/admin/filer/delete_selected_files_confirmation.html:46
+#: templates/admin/filer/folder/choose_copy_destination.html:64
+#: templates/admin/filer/folder/choose_move_destination.html:72
+msgid "No, take me back"
+msgstr "No, take me back"
-#: templates/admin/filer/folder/change_form.html:7
-#: templates/image_filer/image_export_form.html:7
-msgid "admin homepage"
-msgstr ""
+#: templates/admin/filer/delete_selected_files_confirmation.html:47
+msgid "Yes, I'm sure"
+msgstr "Yes, I'm sure"
-#: templates/admin/filer/folder/change_form.html:22
-#: templates/admin/filer/image/change_form.html:9
+#: templates/admin/filer/file/change_form.html:35
+#: templates/admin/filer/folder/change_form.html:21
+#: templates/admin/filer/image/change_form.html:35
msgid "History"
-msgstr ""
+msgstr "History"
+#: templates/admin/filer/file/change_form.html:37
#: templates/admin/filer/folder/change_form.html:23
-#: templates/admin/filer/image/change_form.html:10
+#: templates/admin/filer/image/change_form.html:37
msgid "View on site"
-msgstr ""
+msgstr "View on site"
-#: templates/admin/filer/folder/change_form.html:35
-#: templates/admin/filer/folder/directory_listing.html:75
-#: templates/admin/filer/folder/directory_listing.html:85
-#: templates/admin/filer/folder/directory_table.html:23
+#: templates/admin/filer/folder/change_form.html:6
+#: templates/admin/filer/folder/change_form.html:7
+#: templates/admin/filer/folder/change_form.html:8
+#: templates/admin/filer/folder/directory_listing.html:102
+msgid "Go back to"
+msgstr "Go back to"
+
+#: templates/admin/filer/folder/change_form.html:6
+msgid "admin homepage"
+msgstr "admin homepage"
+
+#: templates/admin/filer/folder/change_form.html:37
+#: templates/admin/filer/folder/directory_listing.html:95
+#: templates/admin/filer/folder/directory_listing.html:103
+#: templates/admin/filer/folder/directory_table_list.html:29
+#: templates/admin/filer/folder/directory_table_list.html:59
+#: templates/admin/filer/folder/directory_table_list.html:181
+#: templates/admin/filer/folder/directory_thumbnail_list.html:27
+#: templates/admin/filer/folder/directory_thumbnail_list.html:61
+#: templates/admin/filer/folder/directory_thumbnail_list.html:156
msgid "Folder Icon"
-msgstr ""
+msgstr "Folder Icon"
-#: templates/admin/filer/folder/choose_copy_destination.html:12
+#: templates/admin/filer/folder/choose_copy_destination.html:23
msgid ""
"Your account doesn't have permissions to copy all of the selected files and/"
"or folders."
msgstr ""
+"Your account doesn't have permissions to copy all of the selected files and/"
+"or folders."
-#: templates/admin/filer/folder/choose_copy_destination.html:15
-#: templates/admin/filer/folder/choose_move_destination.html:15
+#: templates/admin/filer/folder/choose_copy_destination.html:25
+#: templates/admin/filer/folder/choose_copy_destination.html:31
+#: templates/admin/filer/folder/choose_copy_destination.html:37
+#: templates/admin/filer/folder/choose_move_destination.html:37
+#: templates/admin/filer/folder/choose_move_destination.html:43
+#: templates/admin/filer/folder/choose_move_destination.html:49
+msgid "Take me back"
+msgstr "Take me back"
+
+#: templates/admin/filer/folder/choose_copy_destination.html:29
+#: templates/admin/filer/folder/choose_move_destination.html:41
msgid "There are no destination folders available."
-msgstr ""
+msgstr "There are no destination folders available."
-#: templates/admin/filer/folder/choose_copy_destination.html:18
+#: templates/admin/filer/folder/choose_copy_destination.html:35
msgid "There are no files and/or folders available to copy."
-msgstr ""
+msgstr "There are no files and/or folders available to copy."
-#: templates/admin/filer/folder/choose_copy_destination.html:20
+#: templates/admin/filer/folder/choose_copy_destination.html:40
msgid ""
"The following files and/or folders will be copied to a destination folder "
"(retaining their tree structure):"
msgstr ""
+"The following files and/or folders will be copied to a destination folder "
+"(retaining their tree structure):"
-#: templates/admin/filer/folder/choose_copy_destination.html:32
-#: templates/admin/filer/folder/choose_move_destination.html:32
+#: templates/admin/filer/folder/choose_copy_destination.html:54
+#: templates/admin/filer/folder/choose_move_destination.html:64
msgid "Destination folder:"
-msgstr ""
+msgstr "Destination folder:"
-#: templates/admin/filer/folder/choose_copy_destination.html:39
+#: templates/admin/filer/folder/choose_copy_destination.html:65
+#: templates/admin/filer/folder/choose_copy_destination.html:68
+#: templates/admin/filer/folder/directory_listing.html:178
msgid "Copy"
-msgstr ""
+msgstr "Copy"
+
+#: templates/admin/filer/folder/choose_copy_destination.html:67
+msgid "It is not allowed to copy files into same folder"
+msgstr "It is not allowed to copy files into same folder"
-#: templates/admin/filer/folder/choose_images_resize_options.html:12
+#: templates/admin/filer/folder/choose_images_resize_options.html:15
msgid ""
"Your account doesn't have permissions to resize all of the selected images."
msgstr ""
+"Your account doesn't have permissions to resize all of the selected images."
-#: templates/admin/filer/folder/choose_images_resize_options.html:15
+#: templates/admin/filer/folder/choose_images_resize_options.html:18
msgid "There are no images available to resize."
-msgstr ""
+msgstr "There are no images available to resize."
-#: templates/admin/filer/folder/choose_images_resize_options.html:17
+#: templates/admin/filer/folder/choose_images_resize_options.html:20
msgid "The following images will be resized:"
-msgstr ""
-
-#: templates/admin/filer/folder/choose_images_resize_options.html:30
-msgid "Choose an existing thumbnail option or enter resize parameters:"
-msgstr ""
+msgstr "The following images will be resized:"
#: templates/admin/filer/folder/choose_images_resize_options.html:32
-msgid "Choose resize parameters:"
-msgstr ""
+msgid "Choose an existing thumbnail option or enter resize parameters:"
+msgstr "Choose an existing thumbnail option or enter resize parameters:"
-#: templates/admin/filer/folder/choose_images_resize_options.html:35
+#: templates/admin/filer/folder/choose_images_resize_options.html:36
msgid ""
"Warning: Images will be resized in-place and originals will be lost. Maybe "
"first make a copy of them to retain the originals."
msgstr ""
+"Warning: Images will be resized in-place and originals will be lost. Maybe "
+"first make a copy of them to retain the originals."
-#: templates/admin/filer/folder/choose_images_resize_options.html:36
+#: templates/admin/filer/folder/choose_images_resize_options.html:39
msgid "Resize"
-msgstr ""
+msgstr "Resize"
-#: templates/admin/filer/folder/choose_move_destination.html:12
+#: templates/admin/filer/folder/choose_move_destination.html:35
msgid ""
"Your account doesn't have permissions to move all of the selected files and/"
"or folders."
msgstr ""
+"Your account doesn't have permissions to move all of the selected files and/"
+"or folders."
-#: templates/admin/filer/folder/choose_move_destination.html:18
+#: templates/admin/filer/folder/choose_move_destination.html:47
msgid "There are no files and/or folders available to move."
-msgstr ""
+msgstr "There are no files and/or folders available to move."
-#: templates/admin/filer/folder/choose_move_destination.html:20
+#: templates/admin/filer/folder/choose_move_destination.html:52
msgid ""
"The following files and/or folders will be moved to a destination folder "
"(retaining their tree structure):"
msgstr ""
+"The following files and/or folders will be moved to a destination folder "
+"(retaining their tree structure):"
-#: templates/admin/filer/folder/choose_move_destination.html:38
+#: templates/admin/filer/folder/choose_move_destination.html:73
+#: templates/admin/filer/folder/choose_move_destination.html:76
+#: templates/admin/filer/folder/directory_listing.html:181
msgid "Move"
-msgstr ""
+msgstr "Move"
+
+#: templates/admin/filer/folder/choose_move_destination.html:75
+msgid "It is not allowed to move files into same folder"
+msgstr "It is not allowed to move files into same folder"
-#: templates/admin/filer/folder/choose_rename_format.html:12
+#: templates/admin/filer/folder/choose_rename_format.html:15
msgid ""
"Your account doesn't have permissions to rename all of the selected files."
msgstr ""
+"Your account doesn't have permissions to rename all of the selected files."
-#: templates/admin/filer/folder/choose_rename_format.html:15
+#: templates/admin/filer/folder/choose_rename_format.html:18
msgid "There are no files available to rename."
-msgstr ""
+msgstr "There are no files available to rename."
-#: templates/admin/filer/folder/choose_rename_format.html:17
+#: templates/admin/filer/folder/choose_rename_format.html:20
msgid ""
"The following files will be renamed (they will stay in their folders and "
"keep original filename, only displayed filename will be changed):"
msgstr ""
+"The following files will be renamed (they will stay in their folders and "
+"keep original filename, only displayed filename will be changed):"
-#: templates/admin/filer/folder/choose_rename_format.html:54
+#: templates/admin/filer/folder/choose_rename_format.html:59
msgid "Rename"
-msgstr ""
+msgstr "Rename"
-#: templates/admin/filer/folder/directory_listing.html:66
-msgid "Adds a new Folder"
-msgstr ""
+#: templates/admin/filer/folder/directory_listing.html:94
+msgid "Go back to the parent folder"
+msgstr "Go back to the parent folder"
-#: templates/admin/filer/folder/directory_listing.html:66
-#: templates/image_filer/include/export_dialog.html:34
-msgid "New Folder"
-msgstr ""
+#: templates/admin/filer/folder/directory_listing.html:131
+msgid "Change current folder details"
+msgstr "Change current folder details"
-#: templates/admin/filer/folder/directory_listing.html:67
-msgid "upload files"
-msgstr ""
+#: templates/admin/filer/folder/directory_listing.html:131
+msgid "Change"
+msgstr "Change"
-#: templates/admin/filer/folder/directory_listing.html:67
-msgid "Upload"
-msgstr ""
+#: templates/admin/filer/folder/directory_listing.html:141
+msgid "Click here to run search for entered phrase"
+msgstr "Click here to run search for entered phrase"
-#: templates/admin/filer/folder/directory_listing.html:74
-msgid "Go back to the parent folder"
-msgstr ""
+#: templates/admin/filer/folder/directory_listing.html:144
+msgid "Search"
+msgstr "Search"
-#: templates/admin/filer/folder/directory_listing.html:76
-#: templates/admin/filer/folder/directory_table.html:29
-#: templates/admin/filer/tools/search_form.html:18
-#, python-format
-msgid "1 folder"
-msgid_plural "%(counter)s folders"
-msgstr[0] ""
-msgstr[1] ""
+#: templates/admin/filer/folder/directory_listing.html:151
+msgid "Close"
+msgstr "Close"
-#: templates/admin/filer/folder/directory_listing.html:76
-#: templates/admin/filer/folder/directory_table.html:29
-#: templates/admin/filer/tools/search_form.html:19
-#, python-format
-msgid "1 file"
-msgid_plural "%(counter)s files"
-msgstr[0] ""
-msgstr[1] ""
+#: templates/admin/filer/folder/directory_listing.html:153
+msgid "Limit"
+msgstr "Limit"
-#: templates/admin/filer/folder/directory_listing.html:80
-msgid "Change current folder details"
-msgstr ""
+#: templates/admin/filer/folder/directory_listing.html:158
+msgid "Check it to limit the search to current folder"
+msgstr "Check it to limit the search to current folder"
-#: templates/admin/filer/folder/directory_listing.html:80
-#: templates/admin/filer/folder/directory_table.html:27
-#: templates/admin/filer/folder/directory_table.html:43
-msgid "Change"
-msgstr ""
+#: templates/admin/filer/folder/directory_listing.html:159
+msgid "Limit the search to current folder"
+msgstr "Limit the search to current folder"
+
+#: templates/admin/filer/folder/directory_listing.html:175
+msgid "Delete"
+msgstr "Delete"
+
+#: templates/admin/filer/folder/directory_listing.html:203
+msgid "Adds a new Folder"
+msgstr "Adds a new Folder"
+
+#: templates/admin/filer/folder/directory_listing.html:206
+msgid "New Folder"
+msgstr "New Folder"
+
+#: templates/admin/filer/folder/directory_listing.html:211
+#: templates/admin/filer/folder/directory_listing.html:218
+#: templates/admin/filer/folder/directory_listing.html:221
+#: templates/admin/filer/folder/directory_listing.html:228
+#: templates/admin/filer/folder/directory_listing.html:235
+msgid "Upload Files"
+msgstr "Upload Files"
-#: templates/admin/filer/folder/directory_table.html:13
+#: templates/admin/filer/folder/directory_listing.html:233
+msgid "You have to select a folder first"
+msgstr "You have to select a folder first"
+
+#: templates/admin/filer/folder/directory_table_list.html:16
msgid "Name"
-msgstr ""
+msgstr "Name"
-#: templates/admin/filer/folder/directory_table.html:23
-#: templates/admin/filer/folder/directory_table.html:27
-#: templates/admin/filer/folder/directory_table.html:29
+#: templates/admin/filer/folder/directory_table_list.html:17
+#: templates/admin/filer/tools/detail_info.html:50
+msgid "Owner"
+msgstr "Owner"
+
+#: templates/admin/filer/folder/directory_table_list.html:18
+#: templates/admin/filer/tools/detail_info.html:34
+msgid "Size"
+msgstr "Size"
+
+#: templates/admin/filer/folder/directory_table_list.html:19
+msgid "Action"
+msgstr "Action"
+
+#: templates/admin/filer/folder/directory_table_list.html:28
+#: templates/admin/filer/folder/directory_table_list.html:35
+#: templates/admin/filer/folder/directory_table_list.html:58
+#: templates/admin/filer/folder/directory_table_list.html:65
+#: templates/admin/filer/folder/directory_thumbnail_list.html:26
+#: templates/admin/filer/folder/directory_thumbnail_list.html:32
+#: templates/admin/filer/folder/directory_thumbnail_list.html:60
+#: templates/admin/filer/folder/directory_thumbnail_list.html:66
#, python-format
msgid "Change '%(item_label)s' folder details"
-msgstr ""
+msgstr "Change '%(item_label)s' folder details"
-#: templates/admin/filer/folder/directory_table.html:30
-#: templates/admin/filer/folder/directory_table.html:45
-msgid "Owner"
-msgstr "Uploaded by"
+#: templates/admin/filer/folder/directory_table_list.html:76
+#: templates/admin/filer/tools/search_form.html:5
+#, python-format
+msgid "%(counter)s folder"
+msgid_plural "%(counter)s folders"
+msgstr[0] "%(counter)s folder"
+msgstr[1] "%(counter)s folders"
-#: templates/admin/filer/folder/directory_table.html:37
+#: templates/admin/filer/folder/directory_table_list.html:77
+#: templates/admin/filer/tools/search_form.html:6
+#, python-format
+msgid "%(counter)s file"
+msgid_plural "%(counter)s files"
+msgstr[0] "%(counter)s file"
+msgstr[1] "%(counter)s files"
+
+#: templates/admin/filer/folder/directory_table_list.html:83
+msgid "Change folder details"
+msgstr "Change folder details"
+
+#: templates/admin/filer/folder/directory_table_list.html:84
+msgid "Remove folder"
+msgstr "Remove folder"
+
+#: templates/admin/filer/folder/directory_table_list.html:96
+#: templates/admin/filer/folder/directory_table_list.html:104
+#: templates/admin/filer/folder/directory_table_list.html:119
+#: templates/admin/filer/folder/directory_thumbnail_list.html:95
+#: templates/admin/filer/folder/directory_thumbnail_list.html:104
+#: templates/admin/filer/folder/directory_thumbnail_list.html:119
msgid "Select this file"
-msgstr ""
+msgstr "Select this file"
-#: templates/admin/filer/folder/directory_table.html:39
-#: templates/admin/filer/folder/directory_table.html:43
-#: templates/admin/filer/folder/directory_table.html:44
+#: templates/admin/filer/folder/directory_table_list.html:107
+#: templates/admin/filer/folder/directory_table_list.html:122
+#: templates/admin/filer/folder/directory_table_list.html:154
+#: templates/admin/filer/folder/directory_thumbnail_list.html:107
+#: templates/admin/filer/folder/directory_thumbnail_list.html:122
#, python-format
msgid "Change '%(item_label)s' details"
-msgstr ""
+msgstr "Change '%(item_label)s' details"
-#: templates/admin/filer/folder/directory_table.html:42
-#, python-format
-msgid "Delete '%(item_label)s'"
-msgstr ""
-
-#: templates/admin/filer/folder/directory_table.html:42
-msgid "Delete"
-msgstr ""
-
-#: templates/admin/filer/folder/directory_table.html:46
+#: templates/admin/filer/folder/directory_table_list.html:131
+#: templates/admin/filer/folder/directory_thumbnail_list.html:130
msgid "Permissions"
-msgstr ""
+msgstr "Permissions"
-#: templates/admin/filer/folder/directory_table.html:46
+#: templates/admin/filer/folder/directory_table_list.html:131
+#: templates/admin/filer/folder/directory_thumbnail_list.html:130
msgid "disabled"
-msgstr ""
+msgstr "disabled"
-#: templates/admin/filer/folder/directory_table.html:46
+#: templates/admin/filer/folder/directory_table_list.html:131
+#: templates/admin/filer/folder/directory_thumbnail_list.html:130
msgid "enabled"
-msgstr ""
+msgstr "enabled"
+
+#: templates/admin/filer/folder/directory_table_list.html:144
+#, fuzzy
+#| msgid "Move selected files to clipboard"
+msgid "URL copied to clipboard"
+msgstr "Move selected files to clipboard"
+
+#: templates/admin/filer/folder/directory_table_list.html:147
+#, fuzzy, python-format
+#| msgid "Change '%(item_label)s' details"
+msgid "Canonical url '%(item_label)s'"
+msgstr "Change '%(item_label)s' details"
+
+#: templates/admin/filer/folder/directory_table_list.html:151
+#, fuzzy, python-format
+#| msgid "Change '%(item_label)s' details"
+msgid "Download '%(item_label)s'"
+msgstr "Change '%(item_label)s' details"
+
+#: templates/admin/filer/folder/directory_table_list.html:155
+msgid "Remove file"
+msgstr "Remove file"
+
+#: templates/admin/filer/folder/directory_table_list.html:163
+#: templates/admin/filer/folder/directory_thumbnail_list.html:138
+msgid "Drop files here or use the \"Upload Files\" button"
+msgstr "Drop files here or use the \"Upload Files\" button"
+
+#: templates/admin/filer/folder/directory_table_list.html:178
+#: templates/admin/filer/folder/directory_thumbnail_list.html:153
+msgid "Drop your file to upload into:"
+msgstr "Drop your file to upload into:"
+
+#: templates/admin/filer/folder/directory_table_list.html:188
+#: templates/admin/filer/folder/directory_thumbnail_list.html:163
+msgid "Upload"
+msgstr "Upload"
-#: templates/admin/filer/folder/directory_table.html:50
-msgid "Move to clipboard"
-msgstr ""
+#: templates/admin/filer/folder/directory_table_list.html:200
+#: templates/admin/filer/folder/directory_thumbnail_list.html:175
+msgid "cancel"
+msgstr "cancel"
-#: templates/admin/filer/folder/directory_table.html:58
-msgid "there are no files or subfolders"
-msgstr ""
+#: templates/admin/filer/folder/directory_table_list.html:204
+#: templates/admin/filer/folder/directory_thumbnail_list.html:179
+msgid "Upload success!"
+msgstr "Upload success!"
+
+#: templates/admin/filer/folder/directory_table_list.html:208
+#: templates/admin/filer/folder/directory_thumbnail_list.html:183
+msgid "Upload canceled!"
+msgstr "Upload canceled!"
-#: templates/admin/filer/folder/directory_table.html:65
+#: templates/admin/filer/folder/directory_table_list.html:215
+#: templates/admin/filer/folder/directory_thumbnail_list.html:190
msgid "previous"
-msgstr ""
+msgstr "previous"
-#: templates/admin/filer/folder/directory_table.html:69
+#: templates/admin/filer/folder/directory_table_list.html:220
+#: templates/admin/filer/folder/directory_thumbnail_list.html:195
#, python-format
-msgid ""
-"\n"
-" Page %(number)s of %(num_pages)s.\n"
-" "
-msgstr ""
+msgid "Page %(number)s of %(num_pages)s."
+msgstr "Page %(number)s of %(num_pages)s."
-#: templates/admin/filer/folder/directory_table.html:75
+#: templates/admin/filer/folder/directory_table_list.html:225
+#: templates/admin/filer/folder/directory_thumbnail_list.html:200
msgid "next"
-msgstr ""
+msgstr "next"
+
+#: templates/admin/filer/folder/directory_table_list.html:235
+#: templates/admin/filer/folder/directory_thumbnail_list.html:210
+#, python-format
+msgid "Select all %(total_count)s"
+msgstr "Select all %(total_count)s"
+
+#: templates/admin/filer/folder/directory_thumbnail_list.html:15
+#: templates/admin/filer/folder/directory_thumbnail_list.html:80
+#, fuzzy
+#| msgid "Select this file"
+msgid "Select all"
+msgstr "Select this file"
+
+#: templates/admin/filer/folder/directory_thumbnail_list.html:77
+#, fuzzy
+#| msgid "Filer"
+msgid "Files"
+msgstr "Filer"
#: templates/admin/filer/folder/new_folder_form.html:4
+#: templates/admin/filer/folder/new_folder_form.html:7
msgid "Add new"
+msgstr "Add new"
+
+#: templates/admin/filer/folder/new_folder_form.html:4
+msgid "Django site admin"
msgstr ""
-#: templates/admin/filer/folder/new_folder_form.html:13
+#: templates/admin/filer/folder/new_folder_form.html:15
msgid "Please correct the error below."
msgid_plural "Please correct the errors below."
-msgstr[0] ""
-msgstr[1] ""
+msgstr[0] "Please correct the error below."
+msgstr[1] "Please correct the errors below."
-#: templates/admin/filer/folder/new_folder_form.html:28
+#: templates/admin/filer/folder/new_folder_form.html:30
msgid "Save"
-msgstr ""
+msgstr "Save"
-#: templates/admin/filer/image/change_form.html:21
-msgid "Full size preview"
+#: templates/admin/filer/templatetags/file_icon.html:9
+msgid "Your browser does not support audio."
msgstr ""
-#: templates/admin/filer/tools/search_form.html:8
-#: templates/admin/filer/tools/search_form.html:14
-msgid "Search"
+#: templates/admin/filer/templatetags/file_icon.html:14
+msgid "Your browser does not support video."
msgstr ""
-#: templates/admin/filer/tools/search_form.html:13
-msgid "Enter your search phrase here"
-msgstr ""
+#: templates/admin/filer/tools/clipboard/clipboard.html:9
+msgid "Clipboard"
+msgstr "Clipboard"
-#: templates/admin/filer/tools/search_form.html:14
-msgid "Click here to"
-msgstr ""
+#: templates/admin/filer/tools/clipboard/clipboard.html:21
+msgid "Paste all items here"
+msgstr "Paste all items here"
-#: templates/admin/filer/tools/search_form.html:14
-msgid "run search for entered phrase"
-msgstr ""
+#: templates/admin/filer/tools/clipboard/clipboard.html:27
+msgid "Move all clipboard files to"
+msgstr "Move all clipboard files to"
+
+#: templates/admin/filer/tools/clipboard/clipboard.html:27
+msgid "Empty Clipboard"
+msgstr "Empty Clipboard"
-#: templates/admin/filer/tools/search_form.html:15
-msgid "Check it to"
+#: templates/admin/filer/tools/clipboard/clipboard.html:50
+msgid "the clipboard is empty"
+msgstr "the clipboard is empty"
+
+#: templates/admin/filer/tools/clipboard/clipboard_item_rows.html:16
+msgid "upload failed"
+msgstr "upload failed"
+
+#: templates/admin/filer/tools/detail_info.html:11
+msgid "Download"
msgstr ""
-#: templates/admin/filer/tools/search_form.html:15
-#: templates/admin/filer/tools/search_form.html:16
-msgid "limit the search to current folder"
+#: templates/admin/filer/tools/detail_info.html:16
+msgid "Expand"
msgstr ""
-#: templates/admin/filer/tools/search_form.html:18
+#: templates/admin/filer/tools/detail_info.html:21
+#: templates/admin/filer/widgets/admin_file.html:32
+#: templatetags/filer_admin_tags.py:108
+#, fuzzy
+#| msgid "file missing"
+msgid "File is missing"
+msgstr "file missing"
+
+#: templates/admin/filer/tools/detail_info.html:30
+msgid "Type"
+msgstr "Type"
+
+#: templates/admin/filer/tools/detail_info.html:38
+msgid "File-size"
+msgstr "File-size"
+
+#: templates/admin/filer/tools/detail_info.html:42
+msgid "Modified"
+msgstr "Modified"
+
+#: templates/admin/filer/tools/detail_info.html:46
+msgid "Created"
+msgstr "Created"
+
+#: templates/admin/filer/tools/search_form.html:5
msgid "found"
-msgstr ""
+msgstr "found"
-#: templates/admin/filer/tools/search_form.html:18
+#: templates/admin/filer/tools/search_form.html:5
msgid "and"
-msgstr ""
+msgstr "and"
-#: templates/admin/filer/tools/search_form.html:19
+#: templates/admin/filer/tools/search_form.html:7
msgid "cancel search"
-msgstr ""
+msgstr "cancel search"
-#: templates/admin/filer/tools/upload_button_js.html:24
-#: templates/admin/filer/widgets/admin_file.html:7
-#: templates/admin/filer/widgets/admin_file.html:8
-msgid "file missing"
-msgstr ""
+#: templates/admin/filer/widgets/admin_file.html:10
+#: templates/admin/filer/widgets/admin_file.html:39
+#: templates/admin/filer/widgets/admin_folder.html:18
+msgid "Clear"
+msgstr "Clear"
-#: templates/admin/filer/tools/clipboard/clipboard.html:7
-msgid "Clipboard"
-msgstr ""
+#: templates/admin/filer/widgets/admin_file.html:22
+msgid "or drop your file here"
+msgstr "or drop your file here"
-#: templates/admin/filer/tools/clipboard/clipboard.html:19
-msgid "Paste all items here"
-msgstr ""
+#: templates/admin/filer/widgets/admin_file.html:35
+msgid "no file selected"
+msgstr "no file selected"
-#: templates/admin/filer/tools/clipboard/clipboard.html:26
-msgid "discard"
-msgstr ""
+#: templates/admin/filer/widgets/admin_file.html:50
+#: templates/admin/filer/widgets/admin_folder.html:14
+msgid "Lookup"
+msgstr "Lookup"
-#: templates/admin/filer/tools/clipboard/clipboard.html:26
-msgid "Move all clipboard files to"
-msgstr ""
+#: templates/admin/filer/widgets/admin_file.html:51
+msgid "Choose File"
+msgstr "Choose File"
-#: templates/admin/filer/tools/clipboard/clipboard.html:43
-msgid "the clipboard is empty"
-msgstr ""
+#: templates/admin/filer/widgets/admin_folder.html:16
+#, fuzzy
+#| msgid "Choose File"
+msgid "Choose Folder"
+msgstr "Choose File"
-#: templates/admin/filer/tools/clipboard/clipboard_item_rows.html:11
-msgid "upload failed"
+#: validation.py:20
+#, python-brace-format
+msgid "File \"{file_name}\": Upload denied by site security policy"
msgstr ""
-#: templates/admin/filer/widgets/admin_file.html:11
-msgid "no file selected"
+#: validation.py:23
+#, python-brace-format
+msgid "File \"{file_name}\": {file_type} upload denied by site security policy"
msgstr ""
-#: templates/admin/filer/widgets/admin_file.html:14
-#: templates/admin/filer/widgets/admin_file.html:15
-#: templates/admin/filer/widgets/admin_folder.html:7
-#: templates/admin/filer/widgets/admin_folder.html:8
-msgid "Lookup"
+#: validation.py:34
+#, python-brace-format
+msgid "File \"{file_name}\": HTML upload denied by site security policy"
msgstr ""
-#: templates/admin/filer/widgets/admin_file.html:17
-#: templates/admin/filer/widgets/admin_folder.html:10
-msgid "Clear"
+#: validation.py:72
+#, python-brace-format
+msgid ""
+"File \"{file_name}\": Rejected due to potential cross site scripting "
+"vulnerability"
msgstr ""
-#: templates/admin/filer/widgets/admin_folder.html:5
-msgid "none selected"
+#: validation.py:84
+#, python-brace-format
+msgid "File \"{file_name}\": SVG file format not recognized"
msgstr ""
-#: templates/image_filer/folder.html:3
-#, python-format
-msgid "Folder '%(folder_label)s' files"
-msgstr ""
+#~ msgid "Resize parameters must be choosen."
+#~ msgstr "Resize parameters must be choosen."
-#: templates/image_filer/folder.html:6
-msgid "File name"
-msgstr ""
+#~ msgid "Choose resize parameters:"
+#~ msgstr "Choose resize parameters:"
-#: templates/image_filer/image_export_form.html:17
-msgid "export"
-msgstr ""
+#~ msgid "Open file"
+#~ msgstr "Open file"
-#: templates/image_filer/image_export_form.html:27
-msgid "download image"
-msgstr ""
+#~ msgid "Full size preview"
+#~ msgstr "Full size preview"
-#: templates/image_filer/include/export_dialog.html:13
-msgid "Folder name already taken."
-msgstr ""
+#~ msgid "Add Description"
+#~ msgstr "Add Description"
-#: templates/image_filer/include/export_dialog.html:15
-msgid "Submit"
-msgstr ""
+#~ msgid "Author"
+#~ msgstr "Author"
+
+#~ msgid "Add Alt-Text"
+#~ msgstr "Add Alt-Text"
+
+#~ msgid "Add caption"
+#~ msgstr "Add caption"
+
+#~ msgid "Add Tags"
+#~ msgstr "Add Tags"
+
+#~ msgid "Change File"
+#~ msgstr "Change File"
+
+#~ msgid "none selected"
+#~ msgstr "none selected"
+
+#~ msgid "Add Folder"
+#~ msgstr "Add Folder"
+
+#, fuzzy
+#~| msgid "Subject location"
+#~ msgid "Subject Location"
+#~ msgstr "Subject location"
+
+#~ msgid ""
+#~ "\n"
+#~ " %(counter)s file"
+#~ msgid_plural ""
+#~ "%(counter)s files\n"
+#~ " "
+#~ msgstr[0] "15c0f2d28d6dab1af1f6d94906beb627_pl_0"
+#~ msgstr[1] "15c0f2d28d6dab1af1f6d94906beb627_pl_1"
+
+#~ msgid "Upload File"
+#~ msgstr "Upload File"
diff --git a/filer/locale/es/LC_MESSAGES/django.mo b/filer/locale/es/LC_MESSAGES/django.mo
new file mode 100644
index 0000000000000000000000000000000000000000..f11285aee5c1ce06cf160d9784a5ba98c27c6add
GIT binary patch
literal 20427
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ϝ(g`JOBYYt|bE
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, 2016
+# David , 2015
+# Jason Gass Martinez , 2016
+# Luis Zárate , 2019
+# Manuel E. Gutierrez , 2013,2015
+# Manuel E. Gutierrez , 2013
+# Pablo, 2015
+# Pablo, 2015
+msgid ""
+msgstr ""
+"Project-Id-Version: django Filer\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2023-09-30 18:10+0200\n"
+"PO-Revision-Date: 2012-07-13 15:50+0000\n"
+"Last-Translator: Biel Frontera, 2023\n"
+"Language-Team: Spanish (http://app.transifex.com/divio/django-filer/language/"
+"es/)\n"
+"Language: es\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? "
+"1 : 2;\n"
+
+#: admin/clipboardadmin.py:17
+msgid "You do not have permission to upload files."
+msgstr "No tienes autorización para subir ficheros. "
+
+#: admin/clipboardadmin.py:18
+msgid "Can't find folder to upload. Please refresh and try again"
+msgstr ""
+"No se ha encontrado la carpeta donde guardar el fichero. Por favor, refresca "
+"la página y vuelve a probar."
+
+#: admin/clipboardadmin.py:20
+msgid "Can't use this folder, Permission Denied. Please select another folder."
+msgstr ""
+"No se puede utilizar esta carpeta: permiso denegado. Por favor, selecciona "
+"otra carpeta."
+
+#: admin/fileadmin.py:73
+msgid "Advanced"
+msgstr "Avanzado"
+
+#: admin/fileadmin.py:188
+msgid "canonical URL"
+msgstr "URL canónica"
+
+#: admin/folderadmin.py:411 admin/folderadmin.py:582
+msgid ""
+"Items must be selected in order to perform actions on them. No items have "
+"been changed."
+msgstr ""
+"Los elementos deben estar seleccionados para efectuar acciones sobre ellos. "
+"Ningún elemento ha sido modificado."
+
+#: admin/folderadmin.py:434
+#, python-format
+msgid "%(total_count)s selected"
+msgid_plural "All %(total_count)s selected"
+msgstr[0] " %(total_count)s seleccionado"
+msgstr[1] "Todos los %(total_count)s seleccionados"
+msgstr[2] "Todos los %(total_count)s seleccionados"
+
+#: admin/folderadmin.py:460
+#, python-format
+msgid "Directory listing for %(folder_name)s"
+msgstr "Listado de directorio para %(folder_name)s"
+
+#: admin/folderadmin.py:475
+#, python-format
+msgid "0 of %(cnt)s selected"
+msgstr "0 de %(cnt)s seleccionados"
+
+#: admin/folderadmin.py:612
+msgid "No action selected."
+msgstr "Ninguna acción seleccionada."
+
+#: admin/folderadmin.py:664
+#, python-format
+msgid "Successfully moved %(count)d files to clipboard."
+msgstr "%(count)d archivos movidos con éxito al clipboard."
+
+#: admin/folderadmin.py:669
+msgid "Move selected files to clipboard"
+msgstr "Mover archivos selecionados al clipboard."
+
+#: admin/folderadmin.py:709
+#, python-format
+msgid "Successfully disabled permissions for %(count)d files."
+msgstr "Permisos para %(count)d ficheros deshabilitados con éxito."
+
+#: admin/folderadmin.py:711
+#, python-format
+msgid "Successfully enabled permissions for %(count)d files."
+msgstr "Permisos para %(count)d ficheros habilitados con éxito."
+
+#: admin/folderadmin.py:719
+msgid "Enable permissions for selected files"
+msgstr "Habilitar permisos para los archivos seleccionados."
+
+#: admin/folderadmin.py:725
+msgid "Disable permissions for selected files"
+msgstr "Deshabilitar permisos para los archivos seleccionados."
+
+#: admin/folderadmin.py:789
+#, python-format
+msgid "Successfully deleted %(count)d files and/or folders."
+msgstr "Eliminados %(count)d ficheros y/o directorios con éxito."
+
+#: admin/folderadmin.py:794
+msgid "Cannot delete files and/or folders"
+msgstr "No es posible eliminar ficheros y/o directorios"
+
+#: admin/folderadmin.py:796
+msgid "Are you sure?"
+msgstr "¿Estás seguro?"
+
+#: admin/folderadmin.py:802
+msgid "Delete files and/or folders"
+msgstr "Eliminar ficheros y/o directorios"
+
+#: admin/folderadmin.py:823
+msgid "Delete selected files and/or folders"
+msgstr "Eliminar ficheros y/o directorios seleccionados"
+
+#: admin/folderadmin.py:930
+#, python-format
+msgid "Folders with names %s already exist at the selected destination"
+msgstr "Las carpetas con los nombres %s ya existen en el destino seleccionado"
+
+#: admin/folderadmin.py:934
+#, python-format
+msgid ""
+"Successfully moved %(count)d files and/or folders to folder "
+"'%(destination)s'."
+msgstr ""
+"Movidos con éxito %(count)d ficheros y/o directorios al directorio "
+"'%(destination)s'."
+
+#: admin/folderadmin.py:942 admin/folderadmin.py:944
+msgid "Move files and/or folders"
+msgstr "Mover ficheros y/o directorios"
+
+#: admin/folderadmin.py:959
+msgid "Move selected files and/or folders"
+msgstr "Mover ficheros y/o directorios seleccionados"
+
+#: admin/folderadmin.py:1016
+#, python-format
+msgid "Successfully renamed %(count)d files."
+msgstr "Cambiado el nombre de %(count)d archivos con éxito."
+
+#: admin/folderadmin.py:1025 admin/folderadmin.py:1027
+#: admin/folderadmin.py:1042
+msgid "Rename files"
+msgstr "Cambiar el nombre de los archivos"
+
+#: admin/folderadmin.py:1137
+#, python-format
+msgid ""
+"Successfully copied %(count)d files and/or folders to folder "
+"'%(destination)s'."
+msgstr ""
+"Copiados con éxito %(count)d ficheros y/o directorios al directorio "
+"'%(destination)s'."
+
+#: admin/folderadmin.py:1155 admin/folderadmin.py:1157
+msgid "Copy files and/or folders"
+msgstr "Copiar ficheros y/o directorios"
+
+#: admin/folderadmin.py:1174
+msgid "Copy selected files and/or folders"
+msgstr "Copiar ficheros y/o directorios seleccionados"
+
+#: admin/folderadmin.py:1279
+#, python-format
+msgid "Successfully resized %(count)d images."
+msgstr "Cambiado correctamente el tamaño de %(count)d imágenes."
+
+#: admin/folderadmin.py:1286 admin/folderadmin.py:1288
+msgid "Resize images"
+msgstr "Cambiar el tamaño de imágenes."
+
+#: admin/folderadmin.py:1303
+msgid "Resize selected images"
+msgstr "Cambiar el tamaño de imágenes seleccionadas."
+
+#: admin/forms.py:24
+msgid "Suffix which will be appended to filenames of copied files."
+msgstr ""
+"Sufijo que se añadirá al nombre de los archivos de los archivos copiados."
+
+#: admin/forms.py:31
+#, python-format
+msgid ""
+"Suffix should be a valid, simple and lowercase filename part, like "
+"\"%(valid)s\"."
+msgstr ""
+"El sufijo debe ser una parte válida de un nombre de fichero, simple y en "
+"minúsculas, como \"%(valid)s\"."
+
+#: admin/forms.py:52
+#, python-format
+msgid "Unknown rename format value key \"%(key)s\"."
+msgstr ""
+"Formato de cambio de nombre con valor de clave \"%(key)s\" desconocido."
+
+#: admin/forms.py:54
+#, python-format
+msgid "Invalid rename format: %(error)s."
+msgstr "Formato de cambio de nombre no válido: %(error)s."
+
+#: admin/forms.py:68 models/thumbnailoptionmodels.py:37
+msgid "thumbnail option"
+msgstr "opción de miniatura"
+
+#: admin/forms.py:72 models/thumbnailoptionmodels.py:15
+msgid "width"
+msgstr "ancho"
+
+#: admin/forms.py:73 models/thumbnailoptionmodels.py:20
+msgid "height"
+msgstr "alto"
+
+#: admin/forms.py:74 models/thumbnailoptionmodels.py:25
+msgid "crop"
+msgstr "recortar"
+
+#: admin/forms.py:75 models/thumbnailoptionmodels.py:30
+msgid "upscale"
+msgstr "ampliar"
+
+#: admin/forms.py:79
+msgid "Thumbnail option or resize parameters must be choosen."
+msgstr ""
+"Se debe elegir una opción de miniatura o unos parámetros para el cambio de "
+"tamaño."
+
+#: admin/imageadmin.py:20 admin/imageadmin.py:112
+msgid "Subject location"
+msgstr "Localización del sujeto."
+
+#: admin/imageadmin.py:21
+msgid "Location of the main subject of the scene. Format: \"x,y\"."
+msgstr "Ubicación del tema principal en la escena. Formato \"x,y\""
+
+#: admin/imageadmin.py:59
+msgid "Invalid subject location format. "
+msgstr "Formato de la ubicación del tema inválido"
+
+#: admin/imageadmin.py:67
+msgid "Subject location is outside of the image. "
+msgstr "La ubicación del tema está fuera de la imagen"
+
+#: admin/imageadmin.py:76
+#, python-brace-format
+msgid "Your input: \"{subject_location}\". "
+msgstr "Tu entrada: \"{subject_location}\"."
+
+#: admin/permissionadmin.py:10 models/foldermodels.py:380
+msgid "Who"
+msgstr "Quién"
+
+#: admin/permissionadmin.py:11 models/foldermodels.py:401
+msgid "What"
+msgstr "Qué"
+
+#: admin/views.py:55
+msgid "Folder with this name already exists."
+msgstr "Ya existe un directorio con este nombre."
+
+#: apps.py:11 templates/admin/filer/breadcrumbs.html:5
+#: templates/admin/filer/folder/change_form.html:7
+#: templates/admin/filer/folder/directory_listing.html:33
+msgid "Filer"
+msgstr "Filer"
+
+#: contrib/django_cms/cms_toolbars.py:44
+msgid "Media library"
+msgstr "Biblioteca multimedia"
+
+#: models/abstract.py:62
+msgid "default alt text"
+msgstr "texto alternativo por defecto"
+
+#: models/abstract.py:69
+msgid "default caption"
+msgstr "leyenda por defecto"
+
+#: models/abstract.py:76
+msgid "subject location"
+msgstr "localización del sujeto"
+
+#: models/abstract.py:91
+msgid "image"
+msgstr "imagen"
+
+#: models/abstract.py:92
+msgid "images"
+msgstr "imágenes"
+
+#: models/abstract.py:148
+#, python-format
+msgid ""
+"Image format not recognized or image size exceeds limit of %(max_pixels)d "
+"million pixels by a factor of two or more. Before uploading again, check "
+"file format or resize image to %(width)d x %(height)d resolution or lower."
+msgstr ""
+
+#: models/abstract.py:156
+#, python-format
+msgid ""
+"Image size (%(pixels)d million pixels) exceeds limit of %(max_pixels)d "
+"million pixels. Before uploading again, resize image to %(width)d x "
+"%(height)d resolution or lower."
+msgstr ""
+
+#: models/clipboardmodels.py:11 models/foldermodels.py:289
+msgid "user"
+msgstr "usuario"
+
+#: models/clipboardmodels.py:17 models/filemodels.py:157
+msgid "files"
+msgstr "archivos"
+
+#: models/clipboardmodels.py:24 models/clipboardmodels.py:50
+msgid "clipboard"
+msgstr "portapapeles"
+
+#: models/clipboardmodels.py:25
+msgid "clipboards"
+msgstr "portapapeles"
+
+#: models/clipboardmodels.py:44 models/filemodels.py:74
+#: models/filemodels.py:156
+msgid "file"
+msgstr "archivo"
+
+#: models/clipboardmodels.py:56
+msgid "clipboard item"
+msgstr "elemento del portapapeles"
+
+#: models/clipboardmodels.py:57
+msgid "clipboard items"
+msgstr "elementos del portapapeles"
+
+#: models/filemodels.py:66 templates/admin/filer/folder/change_form.html:8
+#: templates/admin/filer/folder/directory_listing.html:102
+#: templates/admin/filer/folder/new_folder_form.html:4
+#: templates/admin/filer/folder/new_folder_form.html:7
+#: templates/admin/filer/tools/clipboard/clipboard.html:27
+msgid "folder"
+msgstr "carpeta"
+
+#: models/filemodels.py:81
+msgid "file size"
+msgstr "tamaño del archivo"
+
+#: models/filemodels.py:87
+msgid "sha1"
+msgstr "sha1"
+
+#: models/filemodels.py:94
+msgid "has all mandatory data"
+msgstr "tiene todos los datos obligatorios"
+
+#: models/filemodels.py:100
+msgid "original filename"
+msgstr "nombre del archivo original"
+
+#: models/filemodels.py:110 models/foldermodels.py:102
+#: models/thumbnailoptionmodels.py:10
+msgid "name"
+msgstr "nombre"
+
+#: models/filemodels.py:116
+msgid "description"
+msgstr "descripción"
+
+#: models/filemodels.py:125 models/foldermodels.py:108
+msgid "owner"
+msgstr "propietario"
+
+#: models/filemodels.py:129 models/foldermodels.py:116
+msgid "uploaded at"
+msgstr "subido a"
+
+#: models/filemodels.py:134 models/foldermodels.py:126
+msgid "modified at"
+msgstr "modificado el"
+
+#: models/filemodels.py:140
+msgid "Permissions disabled"
+msgstr "Permisos desactivados"
+
+#: models/filemodels.py:141
+msgid ""
+"Disable any permission checking for this file. File will be publicly "
+"accessible to anyone."
+msgstr ""
+"Desactiva cualquier comprobación de permiso para este archivo. El archivo "
+"será accesible públicamente para todos."
+
+#: models/foldermodels.py:94
+msgid "parent"
+msgstr "Padre"
+
+#: models/foldermodels.py:121
+msgid "created at"
+msgstr "creado el"
+
+#: models/foldermodels.py:136 templates/admin/filer/breadcrumbs.html:6
+#: templates/admin/filer/folder/directory_listing.html:35
+msgid "Folder"
+msgstr "Carpeta"
+
+#: models/foldermodels.py:137
+#: templates/admin/filer/folder/directory_thumbnail_list.html:12
+msgid "Folders"
+msgstr "Carpetas"
+
+#: models/foldermodels.py:260
+msgid "all items"
+msgstr "todos los elementos"
+
+#: models/foldermodels.py:261
+msgid "this item only"
+msgstr "sólo este elemento"
+
+#: models/foldermodels.py:262
+msgid "this item and all children"
+msgstr "este elemento y todos los hijos"
+
+#: models/foldermodels.py:266
+msgid "inherit"
+msgstr "hereda"
+
+#: models/foldermodels.py:267
+msgid "allow"
+msgstr "permitir"
+
+#: models/foldermodels.py:268
+msgid "deny"
+msgstr "denegar"
+
+#: models/foldermodels.py:280
+msgid "type"
+msgstr "tipo"
+
+#: models/foldermodels.py:297
+msgid "group"
+msgstr "grupo"
+
+#: models/foldermodels.py:304
+msgid "everybody"
+msgstr "todos"
+
+#: models/foldermodels.py:309
+msgid "can read"
+msgstr "puede leer"
+
+#: models/foldermodels.py:317
+msgid "can edit"
+msgstr "puede editar"
+
+#: models/foldermodels.py:325
+msgid "can add children"
+msgstr "puede añadir hijos"
+
+#: models/foldermodels.py:333
+msgid "folder permission"
+msgstr "permiso de la carpeta"
+
+#: models/foldermodels.py:334
+msgid "folder permissions"
+msgstr "permisos de la carpeta"
+
+#: models/foldermodels.py:348
+msgid "Folder cannot be selected with type \"all items\"."
+msgstr ""
+"La carpeta no se puede seleccionar con el tipo \"todos los elementos\"."
+
+#: models/foldermodels.py:350
+msgid "Folder has to be selected when type is not \"all items\"."
+msgstr ""
+"La carpeta se tiene que seleccionar cuando el tipo no es \"todos los "
+"elementos\"."
+
+#: models/foldermodels.py:352
+msgid "User or group cannot be selected together with \"everybody\"."
+msgstr "Usuario y grupo no se pueden seleccionar a la vez con \"todos\"."
+
+#: models/foldermodels.py:354
+msgid "At least one of user, group, or \"everybody\" has to be selected."
+msgstr "Al menos se debe seleccionar un usuario, un grupo o \"todos\"."
+
+#: models/foldermodels.py:360
+msgid "All Folders"
+msgstr "Todas las carpetas"
+
+#: models/foldermodels.py:362
+msgid "Logical Path"
+msgstr "Path lógico"
+
+#: models/foldermodels.py:371
+#, python-brace-format
+msgid "User: {user}"
+msgstr "Usuario: {user}"
+
+#: models/foldermodels.py:373
+#, python-brace-format
+msgid "Group: {group}"
+msgstr "Grupo: {group}"
+
+#: models/foldermodels.py:375
+msgid "Everybody"
+msgstr "Todos"
+
+#: models/foldermodels.py:388 templates/admin/filer/widgets/admin_file.html:45
+msgid "Edit"
+msgstr "Editar"
+
+#: models/foldermodels.py:389
+msgid "Read"
+msgstr "Leer"
+
+#: models/foldermodels.py:390
+msgid "Add children"
+msgstr "Añadir hijos"
+
+#: models/imagemodels.py:18
+msgid "date taken"
+msgstr "fecha"
+
+#: models/imagemodels.py:25
+msgid "author"
+msgstr "autor"
+
+#: models/imagemodels.py:32
+msgid "must always publish author credit"
+msgstr "siempre debes dar crédito al autor"
+
+#: models/imagemodels.py:37
+msgid "must always publish copyright"
+msgstr "siempre debes publicar los derechos de autor"
+
+#: models/thumbnailoptionmodels.py:16
+msgid "width in pixel."
+msgstr "Ancho en pixel."
+
+#: models/thumbnailoptionmodels.py:21
+msgid "height in pixel."
+msgstr "Alto en pixel."
+
+#: models/thumbnailoptionmodels.py:38
+msgid "thumbnail options"
+msgstr "Opciones de miniatura"
+
+#: models/virtualitems.py:46
+#: templates/admin/filer/folder/directory_table_list.html:4
+#: templates/admin/filer/folder/directory_table_list.html:170
+#: templates/admin/filer/folder/directory_thumbnail_list.html:5
+#: templates/admin/filer/folder/directory_thumbnail_list.html:146
+#: templates/admin/filer/tools/clipboard/clipboard.html:27
+msgid "Unsorted Uploads"
+msgstr "Subidas desordenadas"
+
+#: models/virtualitems.py:73
+msgid "files with missing metadata"
+msgstr "archivos con metadatos perdidos"
+
+#: models/virtualitems.py:87 templates/admin/filer/folder/change_form.html:8
+#: templates/admin/filer/folder/directory_listing.html:102
+msgid "root"
+msgstr "raíz"
+
+#: settings.py:273
+msgid "Show table view"
+msgstr "Muestra la vista de tabla"
+
+#: settings.py:278
+msgid "Show thumbnail view"
+msgstr "Muestra la vista de miniaturas"
+
+#: templates/admin/filer/actions.html:5
+msgid "Run the selected action"
+msgstr "Ejecutar la acción seleccionada"
+
+#: templates/admin/filer/actions.html:5
+msgid "Go"
+msgstr "Continuar"
+
+#: templates/admin/filer/actions.html:14
+#: templates/admin/filer/folder/directory_table_list.html:235
+#: templates/admin/filer/folder/directory_thumbnail_list.html:210
+msgid "Click here to select the objects across all pages"
+msgstr ""
+"Haz clic aquí para seleccionar los objetos a través de todas las páginas"
+
+#: templates/admin/filer/actions.html:14
+#, python-format
+msgid "Select all %(total_count)s files and/or folders"
+msgstr "Seleccionar los %(total_count)s archivos y/o carpetas"
+
+#: templates/admin/filer/actions.html:16
+#: templates/admin/filer/folder/directory_table_list.html:237
+#: templates/admin/filer/folder/directory_thumbnail_list.html:212
+msgid "Clear selection"
+msgstr "Limpiar la selección"
+
+#: templates/admin/filer/breadcrumbs.html:4
+#: templates/admin/filer/folder/directory_listing.html:28
+msgid "Go back to admin homepage"
+msgstr "Volver a la página de inicio de admin"
+
+#: templates/admin/filer/breadcrumbs.html:4
+#: templates/admin/filer/folder/change_form.html:6
+#: templates/admin/filer/folder/directory_listing.html:29
+msgid "Home"
+msgstr "Inicio"
+
+#: templates/admin/filer/breadcrumbs.html:5
+#: templates/admin/filer/folder/directory_listing.html:32
+msgid "Go back to Filer app"
+msgstr "Volver a la app Filer"
+
+#: templates/admin/filer/breadcrumbs.html:6
+#: templates/admin/filer/folder/directory_listing.html:35
+msgid "Go back to root folder"
+msgstr "Volver a la carpeta raíz"
+
+#: templates/admin/filer/breadcrumbs.html:8
+#: templates/admin/filer/breadcrumbs.html:18
+#: templates/admin/filer/folder/change_form.html:10
+#: templates/admin/filer/folder/directory_listing.html:39
+#: templates/admin/filer/folder/directory_listing.html:47
+#, python-format
+msgid "Go back to '%(folder_name)s' folder"
+msgstr "Volver a la carpeta '%(folder_name)s'"
+
+#: templates/admin/filer/change_form.html:26
+msgid "Duplicates"
+msgstr "Duplicados"
+
+#: templates/admin/filer/delete_selected_files_confirmation.html:11
+msgid ""
+"Deleting the selected files and/or folders would result in deleting related "
+"objects, but your account doesn't have permission to delete the following "
+"types of objects:"
+msgstr ""
+"Borrar los archivos y/o carpetas seleccionados borraría los objetos "
+"seleccionados, pero tu cuenta no tiene permiso para borrar los siguientes "
+"tipos de objetos:"
+
+#: templates/admin/filer/delete_selected_files_confirmation.html:19
+msgid ""
+"Deleting the selected files and/or folders would require deleting the "
+"following protected related objects:"
+msgstr ""
+"Borrar los archivos y/o carpetas requeriría borrar los siguientes objetos "
+"relacionados protegidos:"
+
+#: templates/admin/filer/delete_selected_files_confirmation.html:27
+msgid ""
+"Are you sure you want to delete the selected files and/or folders? All of "
+"the following objects and their related items will be deleted:"
+msgstr ""
+"¿Estás seguro de que quieres borrar los archivos y/o carpetas seleccionados? "
+"Los siguientes objetos y sus elementos relacionados serán borrados:"
+
+#: templates/admin/filer/delete_selected_files_confirmation.html:46
+#: templates/admin/filer/folder/choose_copy_destination.html:64
+#: templates/admin/filer/folder/choose_move_destination.html:72
+msgid "No, take me back"
+msgstr "No, ir atrás"
+
+#: templates/admin/filer/delete_selected_files_confirmation.html:47
+msgid "Yes, I'm sure"
+msgstr "Sí, estoy seguro"
+
+#: templates/admin/filer/file/change_form.html:35
+#: templates/admin/filer/folder/change_form.html:21
+#: templates/admin/filer/image/change_form.html:35
+msgid "History"
+msgstr "Histórico"
+
+#: templates/admin/filer/file/change_form.html:37
+#: templates/admin/filer/folder/change_form.html:23
+#: templates/admin/filer/image/change_form.html:37
+msgid "View on site"
+msgstr "Ver en la página"
+
+#: templates/admin/filer/folder/change_form.html:6
+#: templates/admin/filer/folder/change_form.html:7
+#: templates/admin/filer/folder/change_form.html:8
+#: templates/admin/filer/folder/directory_listing.html:102
+msgid "Go back to"
+msgstr "Volver a"
+
+#: templates/admin/filer/folder/change_form.html:6
+msgid "admin homepage"
+msgstr "página de inicio de la administración"
+
+#: templates/admin/filer/folder/change_form.html:37
+#: templates/admin/filer/folder/directory_listing.html:95
+#: templates/admin/filer/folder/directory_listing.html:103
+#: templates/admin/filer/folder/directory_table_list.html:29
+#: templates/admin/filer/folder/directory_table_list.html:59
+#: templates/admin/filer/folder/directory_table_list.html:181
+#: templates/admin/filer/folder/directory_thumbnail_list.html:27
+#: templates/admin/filer/folder/directory_thumbnail_list.html:61
+#: templates/admin/filer/folder/directory_thumbnail_list.html:156
+msgid "Folder Icon"
+msgstr "Icono de la Carpeta"
+
+#: templates/admin/filer/folder/choose_copy_destination.html:23
+msgid ""
+"Your account doesn't have permissions to copy all of the selected files and/"
+"or folders."
+msgstr ""
+"Tu cuenta no tiene permisos para copiar todos los archivos y/o carpetas "
+"seleccionados."
+
+#: templates/admin/filer/folder/choose_copy_destination.html:25
+#: templates/admin/filer/folder/choose_copy_destination.html:31
+#: templates/admin/filer/folder/choose_copy_destination.html:37
+#: templates/admin/filer/folder/choose_move_destination.html:37
+#: templates/admin/filer/folder/choose_move_destination.html:43
+#: templates/admin/filer/folder/choose_move_destination.html:49
+msgid "Take me back"
+msgstr "Volver atrás"
+
+#: templates/admin/filer/folder/choose_copy_destination.html:29
+#: templates/admin/filer/folder/choose_move_destination.html:41
+msgid "There are no destination folders available."
+msgstr "No hay carpetas de destino disponibles."
+
+#: templates/admin/filer/folder/choose_copy_destination.html:35
+msgid "There are no files and/or folders available to copy."
+msgstr "No hay archivos y/o carpetas disponibles para copiar."
+
+#: templates/admin/filer/folder/choose_copy_destination.html:40
+msgid ""
+"The following files and/or folders will be copied to a destination folder "
+"(retaining their tree structure):"
+msgstr ""
+"Los siguientes archivos y/o carpetas serán copiados a una carpeta de destino "
+"(manteniendo su estructura en árbol):"
+
+#: templates/admin/filer/folder/choose_copy_destination.html:54
+#: templates/admin/filer/folder/choose_move_destination.html:64
+msgid "Destination folder:"
+msgstr "Carpeta de destino:"
+
+#: templates/admin/filer/folder/choose_copy_destination.html:65
+#: templates/admin/filer/folder/choose_copy_destination.html:68
+#: templates/admin/filer/folder/directory_listing.html:178
+msgid "Copy"
+msgstr "Copiar"
+
+#: templates/admin/filer/folder/choose_copy_destination.html:67
+msgid "It is not allowed to copy files into same folder"
+msgstr "No está permitido copiar los archivos dentro de la misma carpeta"
+
+#: templates/admin/filer/folder/choose_images_resize_options.html:15
+msgid ""
+"Your account doesn't have permissions to resize all of the selected images."
+msgstr ""
+"Tu cuenta no tiene permisos para cambiar el tamaño de todas las imágenes "
+"seleccionadas."
+
+#: templates/admin/filer/folder/choose_images_resize_options.html:18
+msgid "There are no images available to resize."
+msgstr "No hay imágenes disponibles a las que cambiarles el tamaño."
+
+#: templates/admin/filer/folder/choose_images_resize_options.html:20
+msgid "The following images will be resized:"
+msgstr "Se les cambiará el tamaño a las siguientes imágenes:"
+
+#: templates/admin/filer/folder/choose_images_resize_options.html:32
+msgid "Choose an existing thumbnail option or enter resize parameters:"
+msgstr ""
+"Elige una opción de miniatura existente o introduce parámetros para el "
+"cambio de tamaño:"
+
+#: templates/admin/filer/folder/choose_images_resize_options.html:36
+msgid ""
+"Warning: Images will be resized in-place and originals will be lost. Maybe "
+"first make a copy of them to retain the originals."
+msgstr ""
+"Aviso: se cambiará el tamaño de las imágenes en el mismo sitio y los "
+"originales se perderán. Considera realizar una copia de aquellas para "
+"conservar los originales."
+
+#: templates/admin/filer/folder/choose_images_resize_options.html:39
+msgid "Resize"
+msgstr "Cambiar de tamaño"
+
+#: templates/admin/filer/folder/choose_move_destination.html:35
+msgid ""
+"Your account doesn't have permissions to move all of the selected files and/"
+"or folders."
+msgstr ""
+"Tu cuenta no tiene permisos para mover todos los archivos y/o carpetas "
+"seleccionados."
+
+#: templates/admin/filer/folder/choose_move_destination.html:47
+msgid "There are no files and/or folders available to move."
+msgstr "No hay archivos y/o carpetas disponibles para mover."
+
+#: templates/admin/filer/folder/choose_move_destination.html:52
+msgid ""
+"The following files and/or folders will be moved to a destination folder "
+"(retaining their tree structure):"
+msgstr ""
+"Los siguientes archivos y/o directorios serán movidos a una carpeta de "
+"destino (manteniendo su estructura de árbol):"
+
+#: templates/admin/filer/folder/choose_move_destination.html:73
+#: templates/admin/filer/folder/choose_move_destination.html:76
+#: templates/admin/filer/folder/directory_listing.html:181
+msgid "Move"
+msgstr "Mover"
+
+#: templates/admin/filer/folder/choose_move_destination.html:75
+msgid "It is not allowed to move files into same folder"
+msgstr "No está permitido mover los archivos dentro de la misma carpeta"
+
+#: templates/admin/filer/folder/choose_rename_format.html:15
+msgid ""
+"Your account doesn't have permissions to rename all of the selected files."
+msgstr ""
+"Tu cuenta no tiene permisos para renombrar todos los objetos seleccionados."
+
+#: templates/admin/filer/folder/choose_rename_format.html:18
+msgid "There are no files available to rename."
+msgstr "No hay archivos disponibles a los que cambiar el nombre."
+
+#: templates/admin/filer/folder/choose_rename_format.html:20
+msgid ""
+"The following files will be renamed (they will stay in their folders and "
+"keep original filename, only displayed filename will be changed):"
+msgstr ""
+"Los siguientes archivos serán renombrados (se quedarán en sus carpetas y "
+"mantendrán su nombre original, solo los nombres mostrados serán cambiados):"
+
+#: templates/admin/filer/folder/choose_rename_format.html:59
+msgid "Rename"
+msgstr "Cambiar el nombre"
+
+#: templates/admin/filer/folder/directory_listing.html:94
+msgid "Go back to the parent folder"
+msgstr "Volver a la carpeta padre"
+
+#: templates/admin/filer/folder/directory_listing.html:131
+msgid "Change current folder details"
+msgstr "Cambiar los detalles de la carpeta actual"
+
+#: templates/admin/filer/folder/directory_listing.html:131
+msgid "Change"
+msgstr "Cambiar"
+
+#: templates/admin/filer/folder/directory_listing.html:141
+msgid "Click here to run search for entered phrase"
+msgstr "Haz clic para correr la búsqueda del texto ingresado"
+
+#: templates/admin/filer/folder/directory_listing.html:144
+msgid "Search"
+msgstr "Buscar"
+
+#: templates/admin/filer/folder/directory_listing.html:151
+msgid "Close"
+msgstr "Cerrar"
+
+#: templates/admin/filer/folder/directory_listing.html:153
+msgid "Limit"
+msgstr "Limite"
+
+#: templates/admin/filer/folder/directory_listing.html:158
+msgid "Check it to limit the search to current folder"
+msgstr "Comprueba el límite de búsqueda en la carpeta actual"
+
+#: templates/admin/filer/folder/directory_listing.html:159
+msgid "Limit the search to current folder"
+msgstr "Limitar busqueda a la carpeta actual"
+
+#: templates/admin/filer/folder/directory_listing.html:175
+msgid "Delete"
+msgstr "Borrar"
+
+#: templates/admin/filer/folder/directory_listing.html:203
+msgid "Adds a new Folder"
+msgstr "Añade una nueva Carpeta"
+
+#: templates/admin/filer/folder/directory_listing.html:206
+msgid "New Folder"
+msgstr "Carpeta Nueva"
+
+#: templates/admin/filer/folder/directory_listing.html:211
+#: templates/admin/filer/folder/directory_listing.html:218
+#: templates/admin/filer/folder/directory_listing.html:221
+#: templates/admin/filer/folder/directory_listing.html:228
+#: templates/admin/filer/folder/directory_listing.html:235
+msgid "Upload Files"
+msgstr "Subir archivos."
+
+#: templates/admin/filer/folder/directory_listing.html:233
+msgid "You have to select a folder first"
+msgstr "Tiene que seleccionar primero una carpeta"
+
+#: templates/admin/filer/folder/directory_table_list.html:16
+msgid "Name"
+msgstr "Nombre"
+
+#: templates/admin/filer/folder/directory_table_list.html:17
+#: templates/admin/filer/tools/detail_info.html:50
+msgid "Owner"
+msgstr "Propietario"
+
+#: templates/admin/filer/folder/directory_table_list.html:18
+#: templates/admin/filer/tools/detail_info.html:34
+msgid "Size"
+msgstr "Tamaño"
+
+#: templates/admin/filer/folder/directory_table_list.html:19
+msgid "Action"
+msgstr "Acción"
+
+#: templates/admin/filer/folder/directory_table_list.html:28
+#: templates/admin/filer/folder/directory_table_list.html:35
+#: templates/admin/filer/folder/directory_table_list.html:58
+#: templates/admin/filer/folder/directory_table_list.html:65
+#: templates/admin/filer/folder/directory_thumbnail_list.html:26
+#: templates/admin/filer/folder/directory_thumbnail_list.html:32
+#: templates/admin/filer/folder/directory_thumbnail_list.html:60
+#: templates/admin/filer/folder/directory_thumbnail_list.html:66
+#, python-format
+msgid "Change '%(item_label)s' folder details"
+msgstr "Cambiar los detalles de la carpeta '%(item_label)s'"
+
+#: templates/admin/filer/folder/directory_table_list.html:76
+#: templates/admin/filer/tools/search_form.html:5
+#, python-format
+msgid "%(counter)s folder"
+msgid_plural "%(counter)s folders"
+msgstr[0] "%(counter)s carpeta"
+msgstr[1] "%(counter)s carpetas"
+msgstr[2] "%(counter)s carpetas"
+
+#: templates/admin/filer/folder/directory_table_list.html:77
+#: templates/admin/filer/tools/search_form.html:6
+#, python-format
+msgid "%(counter)s file"
+msgid_plural "%(counter)s files"
+msgstr[0] "%(counter)s archivo"
+msgstr[1] "%(counter)s archivos"
+msgstr[2] "%(counter)s archivos"
+
+#: templates/admin/filer/folder/directory_table_list.html:83
+msgid "Change folder details"
+msgstr "Cambiar los detalles de la carpeta"
+
+#: templates/admin/filer/folder/directory_table_list.html:84
+msgid "Remove folder"
+msgstr "Eliminar carpeta"
+
+#: templates/admin/filer/folder/directory_table_list.html:96
+#: templates/admin/filer/folder/directory_table_list.html:104
+#: templates/admin/filer/folder/directory_table_list.html:119
+#: templates/admin/filer/folder/directory_thumbnail_list.html:95
+#: templates/admin/filer/folder/directory_thumbnail_list.html:104
+#: templates/admin/filer/folder/directory_thumbnail_list.html:119
+msgid "Select this file"
+msgstr "Seleccionar este archivo"
+
+#: templates/admin/filer/folder/directory_table_list.html:107
+#: templates/admin/filer/folder/directory_table_list.html:122
+#: templates/admin/filer/folder/directory_table_list.html:154
+#: templates/admin/filer/folder/directory_thumbnail_list.html:107
+#: templates/admin/filer/folder/directory_thumbnail_list.html:122
+#, python-format
+msgid "Change '%(item_label)s' details"
+msgstr "Cambiar los detalles de '%(item_label)s'"
+
+#: templates/admin/filer/folder/directory_table_list.html:131
+#: templates/admin/filer/folder/directory_thumbnail_list.html:130
+msgid "Permissions"
+msgstr "Permisos"
+
+#: templates/admin/filer/folder/directory_table_list.html:131
+#: templates/admin/filer/folder/directory_thumbnail_list.html:130
+msgid "disabled"
+msgstr "desactivado"
+
+#: templates/admin/filer/folder/directory_table_list.html:131
+#: templates/admin/filer/folder/directory_thumbnail_list.html:130
+msgid "enabled"
+msgstr "activado"
+
+#: templates/admin/filer/folder/directory_table_list.html:144
+#, fuzzy
+#| msgid "Move selected files to clipboard"
+msgid "URL copied to clipboard"
+msgstr "Mover archivos selecionados al clipboard."
+
+#: templates/admin/filer/folder/directory_table_list.html:147
+#, python-format
+msgid "Canonical url '%(item_label)s'"
+msgstr "Url canónica '%(item_label)s'"
+
+#: templates/admin/filer/folder/directory_table_list.html:151
+#, python-format
+msgid "Download '%(item_label)s'"
+msgstr "Descargar '%(item_label)s'"
+
+#: templates/admin/filer/folder/directory_table_list.html:155
+msgid "Remove file"
+msgstr "Eliminar archivo"
+
+#: templates/admin/filer/folder/directory_table_list.html:163
+#: templates/admin/filer/folder/directory_thumbnail_list.html:138
+msgid "Drop files here or use the \"Upload Files\" button"
+msgstr "Suelta los archivos aquí o usa el botón \"Subir archivos\""
+
+#: templates/admin/filer/folder/directory_table_list.html:178
+#: templates/admin/filer/folder/directory_thumbnail_list.html:153
+msgid "Drop your file to upload into:"
+msgstr "Suelta el archivo a subir dentro:"
+
+#: templates/admin/filer/folder/directory_table_list.html:188
+#: templates/admin/filer/folder/directory_thumbnail_list.html:163
+msgid "Upload"
+msgstr "Subir"
+
+#: templates/admin/filer/folder/directory_table_list.html:200
+#: templates/admin/filer/folder/directory_thumbnail_list.html:175
+msgid "cancel"
+msgstr "cancelar"
+
+#: templates/admin/filer/folder/directory_table_list.html:204
+#: templates/admin/filer/folder/directory_thumbnail_list.html:179
+msgid "Upload success!"
+msgstr "Subida exitosa!"
+
+#: templates/admin/filer/folder/directory_table_list.html:208
+#: templates/admin/filer/folder/directory_thumbnail_list.html:183
+msgid "Upload canceled!"
+msgstr "Subida cancelada!"
+
+#: templates/admin/filer/folder/directory_table_list.html:215
+#: templates/admin/filer/folder/directory_thumbnail_list.html:190
+msgid "previous"
+msgstr "anterior"
+
+#: templates/admin/filer/folder/directory_table_list.html:220
+#: templates/admin/filer/folder/directory_thumbnail_list.html:195
+#, python-format
+msgid "Page %(number)s of %(num_pages)s."
+msgstr "Página %(number)s de %(num_pages)s."
+
+#: templates/admin/filer/folder/directory_table_list.html:225
+#: templates/admin/filer/folder/directory_thumbnail_list.html:200
+msgid "next"
+msgstr "siguiente"
+
+#: templates/admin/filer/folder/directory_table_list.html:235
+#: templates/admin/filer/folder/directory_thumbnail_list.html:210
+#, python-format
+msgid "Select all %(total_count)s"
+msgstr "Seleccionar todo %(total_count)s"
+
+#: templates/admin/filer/folder/directory_thumbnail_list.html:15
+#: templates/admin/filer/folder/directory_thumbnail_list.html:80
+msgid "Select all"
+msgstr "Selecciona todas"
+
+#: templates/admin/filer/folder/directory_thumbnail_list.html:77
+msgid "Files"
+msgstr "Ficheros"
+
+#: templates/admin/filer/folder/new_folder_form.html:4
+#: templates/admin/filer/folder/new_folder_form.html:7
+msgid "Add new"
+msgstr "Añadir nuevo"
+
+#: templates/admin/filer/folder/new_folder_form.html:4
+msgid "Django site admin"
+msgstr "Sitio administrativo de Django"
+
+#: templates/admin/filer/folder/new_folder_form.html:15
+msgid "Please correct the error below."
+msgid_plural "Please correct the errors below."
+msgstr[0] "Por favor, corrige el error indicado abajo."
+msgstr[1] "Por favor, corrige los errores indicados abajo."
+msgstr[2] "Por favor, corrige los errores indicados abajo."
+
+#: templates/admin/filer/folder/new_folder_form.html:30
+msgid "Save"
+msgstr "Guardar"
+
+#: templates/admin/filer/templatetags/file_icon.html:9
+msgid "Your browser does not support audio."
+msgstr "El navegador no soporta audio."
+
+#: templates/admin/filer/templatetags/file_icon.html:14
+msgid "Your browser does not support video."
+msgstr "El navegador no soporta vídeo."
+
+#: templates/admin/filer/tools/clipboard/clipboard.html:9
+msgid "Clipboard"
+msgstr "Portapapeles"
+
+#: templates/admin/filer/tools/clipboard/clipboard.html:21
+msgid "Paste all items here"
+msgstr "Pegar aquí todos los elementos"
+
+#: templates/admin/filer/tools/clipboard/clipboard.html:27
+msgid "Move all clipboard files to"
+msgstr "Mover todos los archivos del portapapeles a"
+
+#: templates/admin/filer/tools/clipboard/clipboard.html:27
+msgid "Empty Clipboard"
+msgstr "Portapapeles vacío"
+
+#: templates/admin/filer/tools/clipboard/clipboard.html:50
+msgid "the clipboard is empty"
+msgstr "el portapapeles está vacío"
+
+#: templates/admin/filer/tools/clipboard/clipboard_item_rows.html:16
+msgid "upload failed"
+msgstr "fallo en la subida"
+
+#: templates/admin/filer/tools/detail_info.html:11
+msgid "Download"
+msgstr "Descarga"
+
+#: templates/admin/filer/tools/detail_info.html:16
+msgid "Expand"
+msgstr "Expande"
+
+#: templates/admin/filer/tools/detail_info.html:21
+#: templates/admin/filer/widgets/admin_file.html:32
+#: templatetags/filer_admin_tags.py:108
+msgid "File is missing"
+msgstr "Fichero no encontrado"
+
+#: templates/admin/filer/tools/detail_info.html:30
+msgid "Type"
+msgstr "Tipo"
+
+#: templates/admin/filer/tools/detail_info.html:38
+msgid "File-size"
+msgstr "Tamaño del archivo"
+
+#: templates/admin/filer/tools/detail_info.html:42
+msgid "Modified"
+msgstr "Modificado"
+
+#: templates/admin/filer/tools/detail_info.html:46
+msgid "Created"
+msgstr "Creado"
+
+#: templates/admin/filer/tools/search_form.html:5
+msgid "found"
+msgstr "encontrado"
+
+#: templates/admin/filer/tools/search_form.html:5
+msgid "and"
+msgstr "y"
+
+#: templates/admin/filer/tools/search_form.html:7
+msgid "cancel search"
+msgstr "cancelar la búsqueda"
+
+#: templates/admin/filer/widgets/admin_file.html:10
+#: templates/admin/filer/widgets/admin_file.html:39
+#: templates/admin/filer/widgets/admin_folder.html:18
+msgid "Clear"
+msgstr "Limpiar"
+
+#: templates/admin/filer/widgets/admin_file.html:22
+msgid "or drop your file here"
+msgstr "o suelta aquí tu archivo"
+
+#: templates/admin/filer/widgets/admin_file.html:35
+msgid "no file selected"
+msgstr "ningún archivo seleccionado"
+
+#: templates/admin/filer/widgets/admin_file.html:50
+#: templates/admin/filer/widgets/admin_folder.html:14
+msgid "Lookup"
+msgstr "Buscar"
+
+#: templates/admin/filer/widgets/admin_file.html:51
+msgid "Choose File"
+msgstr "Selecciona el archivo"
+
+#: templates/admin/filer/widgets/admin_folder.html:16
+msgid "Choose Folder"
+msgstr "Escoge una carpeta"
+
+#: validation.py:20
+#, python-brace-format
+msgid "File \"{file_name}\": Upload denied by site security policy"
+msgstr ""
+"Fichero \"{file_name}\": carga denegada por políticas de seguridad del sitio "
+"web"
+
+#: validation.py:23
+#, python-brace-format
+msgid "File \"{file_name}\": {file_type} upload denied by site security policy"
+msgstr ""
+"Fichero \"{file_name}\": carga del tipo {file_type} denegada por políticas "
+"de seguridad del sitio web"
+
+#: validation.py:34
+#, python-brace-format
+msgid "File \"{file_name}\": HTML upload denied by site security policy"
+msgstr ""
+"Fichero \"{file_name}\": carga de HTML denegada por políticas de seguridad "
+"del sitio web"
+
+#: validation.py:72
+#, python-brace-format
+msgid ""
+"File \"{file_name}\": Rejected due to potential cross site scripting "
+"vulnerability"
+msgstr ""
+"Fichero \"{file_name}\": rechazado por posible vulnerabilidad XSS (Cross-"
+"site scripting)"
+
+#: validation.py:84
+#, python-brace-format
+msgid "File \"{file_name}\": SVG file format not recognized"
+msgstr ""
+
+#~ msgid "Resize parameters must be choosen."
+#~ msgstr "Resize parameters must be choosen."
+
+#~ msgid "Choose resize parameters:"
+#~ msgstr "Choose resize parameters:"
+
+#~ msgid "Open file"
+#~ msgstr "Open file"
+
+#~ msgid "Full size preview"
+#~ msgstr "Full size preview"
+
+#~ msgid "Add Description"
+#~ msgstr "Add Description"
+
+#~ msgid "Author"
+#~ msgstr "Author"
+
+#~ msgid "Add Alt-Text"
+#~ msgstr "Add Alt-Text"
+
+#~ msgid "Add caption"
+#~ msgstr "Add caption"
+
+#~ msgid "Add Tags"
+#~ msgstr "Add Tags"
+
+#~ msgid "Change File"
+#~ msgstr "Change File"
+
+#~ msgid "none selected"
+#~ msgstr "none selected"
+
+#~ msgid "Add Folder"
+#~ msgstr "Add Folder"
+
+#~ msgid "Subject Location"
+#~ msgstr "Subject location"
+
+#~ msgid ""
+#~ "\n"
+#~ " %(counter)s file"
+#~ msgid_plural ""
+#~ "%(counter)s files\n"
+#~ " "
+#~ msgstr[0] "15c0f2d28d6dab1af1f6d94906beb627_pl_0"
+#~ msgstr[1] "15c0f2d28d6dab1af1f6d94906beb627_pl_1"
+#~ msgstr[2] "15c0f2d28d6dab1af1f6d94906beb627_pl_2"
+
+#~ msgid "Upload File"
+#~ msgstr "Upload File"
diff --git a/filer/locale/et/LC_MESSAGES/django.mo b/filer/locale/et/LC_MESSAGES/django.mo
index 982b54f44df7a47882b40525cabfad812d0d56e1..6f688e0c099224139a935521e87e2e96d92f75de 100644
GIT binary patch
literal 5456
zcmZ{mYm8l06~{NWii}02f{23b(&xb3d9*yrlv?_j(lVWPXr~1fTld_v=iW2-+;gty
zG3_X*2~neg7$PJ@iN=UAAs9Xo_+n#BhQyG7UkD@yLe%&OF%W_YejpNm|8vefYT@Lr
z^V_ep_S$Q$z4v{6?dnGiPlQ~F+`GycHN5sQUOdAW8FLLBft%nIybyj9UIOoj7sE%O
zKKUj51bhO16g~?-1hH@C)#5xB;%|Gv*370{NM7Ueb30
zGQ`Y4&C`SWehzB>`=Ipx0Dc5M1o@eVd3_fC9BRGKLXCR~%AQx@$Kjvr`WrRhg8a?5L3)kQ2Kuh<>%+1^t}Kz{*}7^DwKVHhqCKmQ0sUH
zu7#^HM*Wx9yb5aEdMH0#Tet71+jm3Fe*kKpshW2~jXw#M2MN?X--FWsD3rZFgZl2*
zP~)D2ipv6&A6|#j^Cr}|_aQ&CicZaY3DkP8g6iK0rTNVl8{r?I^t=gW$GfofJ4qs50%iZzb^Us%@q3`=8HbAFWZmC^
z(m!AGJ5cNRKGZq%V<`VT1vTIEQ0sdIDlY%5+y4Wle>FzyytxR39vKC73RZ#Zy
zLw&!s?%xBYcLK5`a}sKN56ZrKYu;b;hfw}{2x|OKp}zkO)V#lgvgbJ{J<45eoPY8~6^_FJIr*$w62aj1PW1Ev2ysP7(x
z()%!!zQ-U_n_ocX%QH~xcnNCWzd?QXE>t}J3zhFzTvpA$rRE5f-J?+R-3r-WrituD
zlvAe=J-1abmqU%!b3+9)3KefX*CX1Si_akn1IPhHw&)o_%%MBRlEh!eXOU2C>ORN$_?d#Lv|tSkXsQwhdByh3k+F$UVqi$Ok>Q)2O^O8@Du*BF=rV=e8tK;L{Ii%FON{
zu(3ZaX4a`__gABLr{2ydh0Ti8?=pMaE^c{{B$>DSHRs;QyR>@S%tyYNhe=F5>`f;w
z4Ja|x>`i*}W^d|U?gO)rVY#nSTF#_#FqEWrrkpif?5h;|;6nx_r{gGbf!(}uOPKra
zsmM+Hs6X3m_7%M-Y%*D9#=5_8oc&YP1nB_@2Zw@3~Zzhr;oC#U~L^9{iVMo*9Wk(-QtSfz2
zd(j7j=5R7#bJy{<>q|4tk<&3IPr8=38@I$oce;#yGE>z~_2|g@vq3ZI({7k$L?Sas
zeV6&F1X(Osu|4Wz8Bw8fF_qFc#~daeBeKmlP*kmpBBsfc+(oC#yz9>vyYhBeXW%h8
zc51%o&GDW>Q^Pi0+(b>VZm}UNnoXZ&>&%G|A0)BOh@iRKX9M=&ruExy-q9^n&bXi(
z#A$g+gpm(
z`7Ih-u*g=L6g|`SVXK|jusEg{p7GJ33A=Jm`OZwY&NG=?1~~R-C?#ctm$J*S+X5D&
z-Oav}fo-39n!{EYyQpj`KWCSbbiLHig-MZ_tnEgOQhkNPGV8U?XeJR9AMNKdW18(S
z3fK}Wx{^53NU}F}VPuoag}IW3ZL9q}Lp#o_YV3X%QuMN>B8H#J7K*^wJYZy3IAn5XZM%ksul>f$VN?CMc_d%|&OPlREbbO!in>sH3Z
zt%6uIro8Kp+A+3&95QH2JI{NgLqo3D8_cC5obhJ{n@M*l2yyFBWoJWSNQa_Y$&k;7
z`j=FWTA%k#9Gn9``x&u?+&5ZlHU&yU}xRX9ADPc
zFAXb|=0%l}A295Y^U8AfAvaLRxgZRdjkVFTb^GyK$Qf6ptbf1aZpKSLm2YRmPQq1G
zRk$+3m{!M$3MsGl0SFPx~OOscq1qx&k-VrdRB((!3AOn
zg(*LzkeRuK$D>(C`kp2JPHB_z_rkAiD3@VMf0iMtGrJ0xsRCN1s%2hcU$qh5vZl3r
zrSv$~5}94mWg7amVr3X~yEW%ib{pOd;Qg>4=s
z)tRAw?&T1NM=b(ejxTj3RWjpr
z)`PG+3rpLp#ioslmG%!~$Il~U9
zBQL!)F+Ei`BsDSDO2M_bBtI_`s6!*8Wb%DJR+;kha=j9efthKk6?)0}x%ve~`B|yS
bCB^y$lYjHcOjhBK;)WSwrI0$ghF=c=%Xvs<
diff --git a/filer/locale/et/LC_MESSAGES/django.po b/filer/locale/et/LC_MESSAGES/django.po
index ba48e6a20..44b47474a 100644
--- a/filer/locale/et/LC_MESSAGES/django.po
+++ b/filer/locale/et/LC_MESSAGES/django.po
@@ -3,483 +3,623 @@
# This file is distributed under the same license as the PACKAGE package.
#
# Translators:
+# Translators:
+# Translators:
+# Johan Viirok, 2022
+# Martin , 2016
+# Rivo Zängov , 2013
msgid ""
msgstr ""
-"Project-Id-Version: django-filer\n"
+"Project-Id-Version: django Filer\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2012-07-13 19:10+0200\n"
-"PO-Revision-Date: 2012-09-03 20:11+0100\n"
-"Last-Translator: Stefan Foulis \n"
-"Language-Team: Estonian (http://www.transifex.com/projects/p/django-filer/"
+"POT-Creation-Date: 2023-09-30 18:10+0200\n"
+"PO-Revision-Date: 2012-07-13 15:50+0000\n"
+"Last-Translator: Johan Viirok, 2022\n"
+"Language-Team: Estonian (http://app.transifex.com/divio/django-filer/"
"language/et/)\n"
+"Language: et\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"Language: et\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-#: views.py:103
-msgid "Folder with this name already exists."
+#: admin/clipboardadmin.py:17
+msgid "You do not have permission to upload files."
msgstr ""
-#: admin/fileadmin.py:41
+#: admin/clipboardadmin.py:18
+msgid "Can't find folder to upload. Please refresh and try again"
+msgstr ""
+
+#: admin/clipboardadmin.py:20
+msgid "Can't use this folder, Permission Denied. Please select another folder."
+msgstr ""
+
+#: admin/fileadmin.py:73
msgid "Advanced"
msgstr ""
-#: admin/folderadmin.py:324 admin/folderadmin.py:427
+#: admin/fileadmin.py:188
+msgid "canonical URL"
+msgstr ""
+
+#: admin/folderadmin.py:411 admin/folderadmin.py:582
msgid ""
"Items must be selected in order to perform actions on them. No items have "
"been changed."
msgstr ""
-#: admin/folderadmin.py:344
+#: admin/folderadmin.py:434
#, python-format
msgid "%(total_count)s selected"
msgid_plural "All %(total_count)s selected"
msgstr[0] ""
msgstr[1] ""
-#: admin/folderadmin.py:377
+#: admin/folderadmin.py:460
+#, python-format
+msgid "Directory listing for %(folder_name)s"
+msgstr ""
+
+#: admin/folderadmin.py:475
#, python-format
msgid "0 of %(cnt)s selected"
msgstr ""
-#: admin/folderadmin.py:456
+#: admin/folderadmin.py:612
msgid "No action selected."
-msgstr ""
+msgstr "Ühtegi tegevust pole valitud."
-#: admin/folderadmin.py:497
+#: admin/folderadmin.py:664
#, python-format
msgid "Successfully moved %(count)d files to clipboard."
msgstr ""
-#: admin/folderadmin.py:503
+#: admin/folderadmin.py:669
msgid "Move selected files to clipboard"
msgstr ""
-#: admin/folderadmin.py:537
+#: admin/folderadmin.py:709
#, python-format
msgid "Successfully disabled permissions for %(count)d files."
msgstr ""
-#: admin/folderadmin.py:541
+#: admin/folderadmin.py:711
#, python-format
msgid "Successfully enabled permissions for %(count)d files."
msgstr ""
-#: admin/folderadmin.py:550
+#: admin/folderadmin.py:719
msgid "Enable permissions for selected files"
msgstr ""
-#: admin/folderadmin.py:555
+#: admin/folderadmin.py:725
msgid "Disable permissions for selected files"
msgstr ""
-#: admin/folderadmin.py:623
+#: admin/folderadmin.py:789
#, python-format
msgid "Successfully deleted %(count)d files and/or folders."
msgstr ""
-#: admin/folderadmin.py:630
+#: admin/folderadmin.py:794
msgid "Cannot delete files and/or folders"
msgstr ""
-#: admin/folderadmin.py:632
+#: admin/folderadmin.py:796
msgid "Are you sure?"
-msgstr ""
+msgstr "Oled sa kindel?"
-#: admin/folderadmin.py:637
+#: admin/folderadmin.py:802
msgid "Delete files and/or folders"
-msgstr ""
+msgstr "Kustuta failid ja/või kaustad"
-#: admin/folderadmin.py:656
+#: admin/folderadmin.py:823
msgid "Delete selected files and/or folders"
+msgstr "Kustuta valitud failid ja/või kaustad"
+
+#: admin/folderadmin.py:930
+#, python-format
+msgid "Folders with names %s already exist at the selected destination"
msgstr ""
-#: admin/folderadmin.py:771
+#: admin/folderadmin.py:934
#, python-format
msgid ""
"Successfully moved %(count)d files and/or folders to folder "
"'%(destination)s'."
msgstr ""
-#: admin/folderadmin.py:778 admin/folderadmin.py:780
+#: admin/folderadmin.py:942 admin/folderadmin.py:944
msgid "Move files and/or folders"
msgstr ""
-#: admin/folderadmin.py:797
+#: admin/folderadmin.py:959
msgid "Move selected files and/or folders"
msgstr ""
-#: admin/folderadmin.py:854
+#: admin/folderadmin.py:1016
#, python-format
msgid "Successfully renamed %(count)d files."
msgstr ""
-#: admin/folderadmin.py:862 admin/folderadmin.py:864 admin/folderadmin.py:881
+#: admin/folderadmin.py:1025 admin/folderadmin.py:1027
+#: admin/folderadmin.py:1042
msgid "Rename files"
-msgstr ""
+msgstr "Nimeta failid ümber"
-#: admin/folderadmin.py:975
+#: admin/folderadmin.py:1137
#, python-format
msgid ""
"Successfully copied %(count)d files and/or folders to folder "
"'%(destination)s'."
msgstr ""
-#: admin/folderadmin.py:989 admin/folderadmin.py:991
+#: admin/folderadmin.py:1155 admin/folderadmin.py:1157
msgid "Copy files and/or folders"
msgstr ""
-#: admin/folderadmin.py:1010
+#: admin/folderadmin.py:1174
msgid "Copy selected files and/or folders"
msgstr ""
-#: admin/folderadmin.py:1105
+#: admin/folderadmin.py:1279
#, python-format
msgid "Successfully resized %(count)d images."
msgstr ""
-#: admin/folderadmin.py:1113 admin/folderadmin.py:1115
+#: admin/folderadmin.py:1286 admin/folderadmin.py:1288
msgid "Resize images"
msgstr ""
-#: admin/folderadmin.py:1133
+#: admin/folderadmin.py:1303
msgid "Resize selected images"
msgstr ""
-#: admin/forms.py:26
+#: admin/forms.py:24
msgid "Suffix which will be appended to filenames of copied files."
msgstr ""
-#: admin/forms.py:33
+#: admin/forms.py:31
#, python-format
msgid ""
"Suffix should be a valid, simple and lowercase filename part, like "
"\"%(valid)s\"."
msgstr ""
-#: admin/forms.py:54
+#: admin/forms.py:52
#, python-format
msgid "Unknown rename format value key \"%(key)s\"."
msgstr ""
-#: admin/forms.py:56
+#: admin/forms.py:54
#, python-format
msgid "Invalid rename format: %(error)s."
msgstr ""
-#: admin/forms.py:62
+#: admin/forms.py:68 models/thumbnailoptionmodels.py:37
msgid "thumbnail option"
-msgstr ""
+msgstr "pisipildi valikud"
-#: admin/forms.py:63
+#: admin/forms.py:72 models/thumbnailoptionmodels.py:15
msgid "width"
-msgstr ""
+msgstr "laius"
-#: admin/forms.py:64
+#: admin/forms.py:73 models/thumbnailoptionmodels.py:20
msgid "height"
-msgstr ""
+msgstr "kõrgus"
-#: admin/forms.py:65
+#: admin/forms.py:74 models/thumbnailoptionmodels.py:25
msgid "crop"
-msgstr ""
+msgstr "lõika"
-#: admin/forms.py:66
+#: admin/forms.py:75 models/thumbnailoptionmodels.py:30
msgid "upscale"
-msgstr ""
+msgstr "suurenda"
-#: admin/forms.py:71
+#: admin/forms.py:79
msgid "Thumbnail option or resize parameters must be choosen."
msgstr ""
-#: admin/forms.py:73
-msgid "Resize parameters must be choosen."
+#: admin/imageadmin.py:20 admin/imageadmin.py:112
+msgid "Subject location"
msgstr ""
-#: admin/imageadmin.py:12
-msgid "Subject location"
+#: admin/imageadmin.py:21
+msgid "Location of the main subject of the scene. Format: \"x,y\"."
msgstr ""
-#: admin/imageadmin.py:13
-msgid "Location of the main subject of the scene."
+#: admin/imageadmin.py:59
+msgid "Invalid subject location format. "
msgstr ""
-#: models/clipboardmodels.py:9 models/foldermodels.py:236
-msgid "user"
+#: admin/imageadmin.py:67
+msgid "Subject location is outside of the image. "
msgstr ""
-#: models/clipboardmodels.py:11 models/filemodels.py:287
-msgid "files"
+#: admin/imageadmin.py:76
+#, python-brace-format
+msgid "Your input: \"{subject_location}\". "
msgstr ""
-#: models/clipboardmodels.py:34 models/clipboardmodels.py:40
-msgid "clipboard"
+#: admin/permissionadmin.py:10 models/foldermodels.py:380
+msgid "Who"
msgstr ""
-#: models/clipboardmodels.py:35
-msgid "clipboards"
+#: admin/permissionadmin.py:11 models/foldermodels.py:401
+msgid "What"
msgstr ""
-#: models/clipboardmodels.py:39 models/filemodels.py:35
-#: models/filemodels.py:286
-msgid "file"
+#: admin/views.py:55
+msgid "Folder with this name already exists."
msgstr ""
-#: models/clipboardmodels.py:44
-msgid "clipboard item"
+#: apps.py:11 templates/admin/filer/breadcrumbs.html:5
+#: templates/admin/filer/folder/change_form.html:7
+#: templates/admin/filer/folder/directory_listing.html:33
+msgid "Filer"
+msgstr "Filer"
+
+#: contrib/django_cms/cms_toolbars.py:44
+msgid "Media library"
msgstr ""
-#: models/clipboardmodels.py:45
-msgid "clipboard items"
+#: models/abstract.py:62
+msgid "default alt text"
+msgstr ""
+
+#: models/abstract.py:69
+msgid "default caption"
+msgstr ""
+
+#: models/abstract.py:76
+msgid "subject location"
+msgstr ""
+
+#: models/abstract.py:91
+msgid "image"
+msgstr "pilt"
+
+#: models/abstract.py:92
+msgid "images"
+msgstr "pildid"
+
+#: models/abstract.py:148
+#, python-format
+msgid ""
+"Image format not recognized or image size exceeds limit of %(max_pixels)d "
+"million pixels by a factor of two or more. Before uploading again, check "
+"file format or resize image to %(width)d x %(height)d resolution or lower."
+msgstr ""
+
+#: models/abstract.py:156
+#, python-format
+msgid ""
+"Image size (%(pixels)d million pixels) exceeds limit of %(max_pixels)d "
+"million pixels. Before uploading again, resize image to %(width)d x "
+"%(height)d resolution or lower."
msgstr ""
-#: models/filemodels.py:33 templates/admin/filer/folder/change_form.html:9
-#: templates/admin/filer/folder/directory_listing.html:74
+#: models/clipboardmodels.py:11 models/foldermodels.py:289
+msgid "user"
+msgstr "kasutaja"
+
+#: models/clipboardmodels.py:17 models/filemodels.py:157
+msgid "files"
+msgstr "failid"
+
+#: models/clipboardmodels.py:24 models/clipboardmodels.py:50
+msgid "clipboard"
+msgstr "lõikalaud"
+
+#: models/clipboardmodels.py:25
+msgid "clipboards"
+msgstr "lõikelauad"
+
+#: models/clipboardmodels.py:44 models/filemodels.py:74
+#: models/filemodels.py:156
+msgid "file"
+msgstr "fail"
+
+#: models/clipboardmodels.py:56
+msgid "clipboard item"
+msgstr "lõikelaua kirje"
+
+#: models/clipboardmodels.py:57
+msgid "clipboard items"
+msgstr "lõikelaua kirjed"
+
+#: models/filemodels.py:66 templates/admin/filer/folder/change_form.html:8
+#: templates/admin/filer/folder/directory_listing.html:102
#: templates/admin/filer/folder/new_folder_form.html:4
-#: templates/admin/filer/tools/clipboard/clipboard.html:26
+#: templates/admin/filer/folder/new_folder_form.html:7
+#: templates/admin/filer/tools/clipboard/clipboard.html:27
msgid "folder"
-msgstr ""
+msgstr "kaust"
-#: models/filemodels.py:36
+#: models/filemodels.py:81
msgid "file size"
-msgstr ""
+msgstr "failisuurus"
-#: models/filemodels.py:38
+#: models/filemodels.py:87
msgid "sha1"
-msgstr ""
+msgstr "sha1"
-#: models/filemodels.py:40
+#: models/filemodels.py:94
msgid "has all mandatory data"
msgstr ""
-#: models/filemodels.py:42
+#: models/filemodels.py:100
msgid "original filename"
-msgstr ""
+msgstr "algne failinimi"
-#: models/filemodels.py:44 models/foldermodels.py:99
+#: models/filemodels.py:110 models/foldermodels.py:102
+#: models/thumbnailoptionmodels.py:10
msgid "name"
-msgstr ""
+msgstr "nimi"
-#: models/filemodels.py:46
+#: models/filemodels.py:116
msgid "description"
-msgstr ""
+msgstr "kirjeldus"
-#: models/filemodels.py:50
+#: models/filemodels.py:125 models/foldermodels.py:108
msgid "owner"
-msgstr ""
+msgstr "omanik"
-#: models/filemodels.py:52 models/foldermodels.py:105
+#: models/filemodels.py:129 models/foldermodels.py:116
msgid "uploaded at"
-msgstr ""
+msgstr "üles laaditud"
-#: models/filemodels.py:53 models/foldermodels.py:108
+#: models/filemodels.py:134 models/foldermodels.py:126
msgid "modified at"
-msgstr ""
+msgstr "muudetud"
-#: models/filemodels.py:57
+#: models/filemodels.py:140
msgid "Permissions disabled"
msgstr ""
-#: models/filemodels.py:58
-msgid "Disable any permission checking for this "
+#: models/filemodels.py:141
+msgid ""
+"Disable any permission checking for this file. File will be publicly "
+"accessible to anyone."
+msgstr ""
+
+#: models/foldermodels.py:94
+msgid "parent"
msgstr ""
-#: models/foldermodels.py:107
+#: models/foldermodels.py:121
msgid "created at"
-msgstr ""
+msgstr "loodud"
-#: models/foldermodels.py:211
-#: templates/admin/filer/widgets/admin_folder.html:3
-#: templates/admin/filer/widgets/admin_folder.html:5
+#: models/foldermodels.py:136 templates/admin/filer/breadcrumbs.html:6
+#: templates/admin/filer/folder/directory_listing.html:35
msgid "Folder"
-msgstr ""
+msgstr "Kaust"
-#: models/foldermodels.py:212
+#: models/foldermodels.py:137
+#: templates/admin/filer/folder/directory_thumbnail_list.html:12
msgid "Folders"
-msgstr ""
+msgstr "Kaustad"
-#: models/foldermodels.py:227
+#: models/foldermodels.py:260
msgid "all items"
-msgstr ""
+msgstr "kõik kirjed"
-#: models/foldermodels.py:228
+#: models/foldermodels.py:261
msgid "this item only"
-msgstr ""
+msgstr "ainult see kirje"
-#: models/foldermodels.py:229
+#: models/foldermodels.py:262
msgid "this item and all children"
+msgstr "see kirje ja kõik alamkirjed"
+
+#: models/foldermodels.py:266
+msgid "inherit"
msgstr ""
-#: models/foldermodels.py:233
+#: models/foldermodels.py:267
+msgid "allow"
+msgstr "luba"
+
+#: models/foldermodels.py:268
+msgid "deny"
+msgstr "keeldu"
+
+#: models/foldermodels.py:280
msgid "type"
-msgstr ""
+msgstr "tüüp"
-#: models/foldermodels.py:239
+#: models/foldermodels.py:297
msgid "group"
-msgstr ""
+msgstr "grupp"
-#: models/foldermodels.py:240
+#: models/foldermodels.py:304
msgid "everybody"
-msgstr ""
+msgstr "kõik"
+
+#: models/foldermodels.py:309
+msgid "can read"
+msgstr "saab lugeda"
-#: models/foldermodels.py:242
+#: models/foldermodels.py:317
msgid "can edit"
+msgstr "saab mutua"
+
+#: models/foldermodels.py:325
+msgid "can add children"
msgstr ""
-#: models/foldermodels.py:243
-msgid "can read"
+#: models/foldermodels.py:333
+msgid "folder permission"
+msgstr "kausta õigus"
+
+#: models/foldermodels.py:334
+msgid "folder permissions"
+msgstr "kausta õigused"
+
+#: models/foldermodels.py:348
+msgid "Folder cannot be selected with type \"all items\"."
msgstr ""
-#: models/foldermodels.py:244
-msgid "can add children"
+#: models/foldermodels.py:350
+msgid "Folder has to be selected when type is not \"all items\"."
msgstr ""
-#: models/foldermodels.py:273
-msgid "folder permission"
+#: models/foldermodels.py:352
+msgid "User or group cannot be selected together with \"everybody\"."
msgstr ""
-#: models/foldermodels.py:274
-msgid "folder permissions"
+#: models/foldermodels.py:354
+msgid "At least one of user, group, or \"everybody\" has to be selected."
msgstr ""
-#: models/imagemodels.py:39
-msgid "date taken"
+#: models/foldermodels.py:360
+msgid "All Folders"
msgstr ""
-#: models/imagemodels.py:42
-msgid "default alt text"
+#: models/foldermodels.py:362
+msgid "Logical Path"
msgstr ""
-#: models/imagemodels.py:43
-msgid "default caption"
+#: models/foldermodels.py:371
+#, python-brace-format
+msgid "User: {user}"
msgstr ""
-#: models/imagemodels.py:45 templates/image_filer/image.html:6
-msgid "author"
+#: models/foldermodels.py:373
+#, python-brace-format
+msgid "Group: {group}"
msgstr ""
-#: models/imagemodels.py:47
-msgid "must always publish author credit"
+#: models/foldermodels.py:375
+msgid "Everybody"
msgstr ""
-#: models/imagemodels.py:48
-msgid "must always publish copyright"
+#: models/foldermodels.py:388 templates/admin/filer/widgets/admin_file.html:45
+msgid "Edit"
msgstr ""
-#: models/imagemodels.py:50
-msgid "subject location"
+#: models/foldermodels.py:389
+msgid "Read"
msgstr ""
-#: models/imagemodels.py:200
-msgid "image"
+#: models/foldermodels.py:390
+msgid "Add children"
msgstr ""
-#: models/imagemodels.py:201
-msgid "images"
+#: models/imagemodels.py:18
+msgid "date taken"
+msgstr ""
+
+#: models/imagemodels.py:25
+msgid "author"
+msgstr "autor"
+
+#: models/imagemodels.py:32
+msgid "must always publish author credit"
msgstr ""
-#: models/virtualitems.py:45
-#: templates/admin/filer/tools/clipboard/clipboard.html:26
-msgid "unfiled files"
+#: models/imagemodels.py:37
+msgid "must always publish copyright"
msgstr ""
-#: models/virtualitems.py:59
+#: models/thumbnailoptionmodels.py:16
+msgid "width in pixel."
+msgstr "laius pikslites"
+
+#: models/thumbnailoptionmodels.py:21
+msgid "height in pixel."
+msgstr "kõrgus pikslites"
+
+#: models/thumbnailoptionmodels.py:38
+msgid "thumbnail options"
+msgstr ""
+
+#: models/virtualitems.py:46
+#: templates/admin/filer/folder/directory_table_list.html:4
+#: templates/admin/filer/folder/directory_table_list.html:170
+#: templates/admin/filer/folder/directory_thumbnail_list.html:5
+#: templates/admin/filer/folder/directory_thumbnail_list.html:146
+#: templates/admin/filer/tools/clipboard/clipboard.html:27
+msgid "Unsorted Uploads"
+msgstr ""
+
+#: models/virtualitems.py:73
msgid "files with missing metadata"
msgstr ""
-#: models/virtualitems.py:73 templates/admin/filer/breadcrumbs.html:6
-#: templates/admin/filer/folder/change_form.html:9
-#: templates/admin/filer/folder/directory_listing.html:74
-#: templates/image_filer/image_export_form.html:10
+#: models/virtualitems.py:87 templates/admin/filer/folder/change_form.html:8
+#: templates/admin/filer/folder/directory_listing.html:102
msgid "root"
msgstr ""
-#: templates/admin/filer/actions.html:4
+#: settings.py:273
+msgid "Show table view"
+msgstr ""
+
+#: settings.py:278
+msgid "Show thumbnail view"
+msgstr ""
+
+#: templates/admin/filer/actions.html:5
msgid "Run the selected action"
msgstr ""
-#: templates/admin/filer/actions.html:4
+#: templates/admin/filer/actions.html:5
msgid "Go"
-msgstr ""
+msgstr "Mine"
-#: templates/admin/filer/actions.html:11
+#: templates/admin/filer/actions.html:14
+#: templates/admin/filer/folder/directory_table_list.html:235
+#: templates/admin/filer/folder/directory_thumbnail_list.html:210
msgid "Click here to select the objects across all pages"
msgstr ""
-#: templates/admin/filer/actions.html:11
+#: templates/admin/filer/actions.html:14
#, python-format
msgid "Select all %(total_count)s files and/or folders"
msgstr ""
-#: templates/admin/filer/actions.html:13
+#: templates/admin/filer/actions.html:16
+#: templates/admin/filer/folder/directory_table_list.html:237
+#: templates/admin/filer/folder/directory_thumbnail_list.html:212
msgid "Clear selection"
-msgstr ""
+msgstr "Tühista valik"
#: templates/admin/filer/breadcrumbs.html:4
+#: templates/admin/filer/folder/directory_listing.html:28
msgid "Go back to admin homepage"
msgstr ""
#: templates/admin/filer/breadcrumbs.html:4
-#: templates/admin/filer/folder/change_form.html:7
-#: templates/image_filer/image_export_form.html:7
+#: templates/admin/filer/folder/change_form.html:6
+#: templates/admin/filer/folder/directory_listing.html:29
msgid "Home"
-msgstr ""
+msgstr "Koduleht"
#: templates/admin/filer/breadcrumbs.html:5
+#: templates/admin/filer/folder/directory_listing.html:32
msgid "Go back to Filer app"
msgstr ""
-#: templates/admin/filer/breadcrumbs.html:5
-#: templates/admin/filer/folder/change_form.html:8
-#: templates/image_filer/image_export_form.html:8
-msgid "Filer"
-msgstr ""
-
#: templates/admin/filer/breadcrumbs.html:6
-#: templates/image_filer/image_export_form.html:10
+#: templates/admin/filer/folder/directory_listing.html:35
msgid "Go back to root folder"
-msgstr ""
+msgstr "Tagasi peakausta"
#: templates/admin/filer/breadcrumbs.html:8
-#: templates/admin/filer/breadcrumbs.html:11
-#: templates/admin/filer/folder/change_form.html:11
-#: templates/image_filer/image_export_form.html:12
-#: templates/image_filer/image_export_form.html:14
+#: templates/admin/filer/breadcrumbs.html:18
+#: templates/admin/filer/folder/change_form.html:10
+#: templates/admin/filer/folder/directory_listing.html:39
+#: templates/admin/filer/folder/directory_listing.html:47
#, python-format
msgid "Go back to '%(folder_name)s' folder"
msgstr ""
-#: templates/admin/filer/change_form.html:28
-msgid "duplicates"
-msgstr ""
-
-#: templates/admin/filer/delete_confirmation.html:13
-#, python-format
-msgid ""
-"Deleting the %(object_name)s '%(escaped_object)s' would result in deleting "
-"related objects, but your account doesn't have permission to delete the "
-"following types of objects:"
-msgstr ""
-
-#: templates/admin/filer/delete_confirmation.html:21
-#, python-format
-msgid ""
-"Deleting the %(object_name)s '%(escaped_object)s' would require deleting the "
-"following protected related objects:"
-msgstr ""
-
-#: templates/admin/filer/delete_confirmation.html:29
-#, python-format
-msgid ""
-"Are you sure you want to delete the %(object_name)s \"%(escaped_object)s\"? "
-"All of the following related items will be deleted:"
-msgstr ""
-
-#: templates/admin/filer/delete_confirmation.html:34
-#: templates/admin/filer/delete_selected_files_confirmation.html:45
-msgid "Yes, I'm sure"
-msgstr ""
+#: templates/admin/filer/change_form.html:26
+msgid "Duplicates"
+msgstr "Duplikaadid"
#: templates/admin/filer/delete_selected_files_confirmation.html:11
msgid ""
@@ -500,386 +640,588 @@ msgid ""
"the following objects and their related items will be deleted:"
msgstr ""
-#: templates/admin/filer/folder/change_form.html:7
-#: templates/admin/filer/folder/change_form.html:8
-#: templates/admin/filer/folder/change_form.html:9
-#: templates/admin/filer/folder/directory_listing.html:74
-#: templates/image_filer/image_export_form.html:7
-#: templates/image_filer/image_export_form.html:8
-#: templates/image_filer/image_export_form.html:16
-msgid "Go back to"
-msgstr ""
+#: templates/admin/filer/delete_selected_files_confirmation.html:46
+#: templates/admin/filer/folder/choose_copy_destination.html:64
+#: templates/admin/filer/folder/choose_move_destination.html:72
+msgid "No, take me back"
+msgstr "Ei, vii mind tagasi"
-#: templates/admin/filer/folder/change_form.html:7
-#: templates/image_filer/image_export_form.html:7
-msgid "admin homepage"
-msgstr ""
+#: templates/admin/filer/delete_selected_files_confirmation.html:47
+msgid "Yes, I'm sure"
+msgstr "Jah, olen kindel"
-#: templates/admin/filer/folder/change_form.html:22
-#: templates/admin/filer/image/change_form.html:9
+#: templates/admin/filer/file/change_form.html:35
+#: templates/admin/filer/folder/change_form.html:21
+#: templates/admin/filer/image/change_form.html:35
msgid "History"
-msgstr ""
+msgstr "Ajalugu"
+#: templates/admin/filer/file/change_form.html:37
#: templates/admin/filer/folder/change_form.html:23
-#: templates/admin/filer/image/change_form.html:10
+#: templates/admin/filer/image/change_form.html:37
msgid "View on site"
-msgstr ""
+msgstr "Vaata saidil"
+
+#: templates/admin/filer/folder/change_form.html:6
+#: templates/admin/filer/folder/change_form.html:7
+#: templates/admin/filer/folder/change_form.html:8
+#: templates/admin/filer/folder/directory_listing.html:102
+msgid "Go back to"
+msgstr "Mine tagasi"
-#: templates/admin/filer/folder/change_form.html:35
-#: templates/admin/filer/folder/directory_listing.html:75
-#: templates/admin/filer/folder/directory_listing.html:85
-#: templates/admin/filer/folder/directory_table.html:23
+#: templates/admin/filer/folder/change_form.html:6
+msgid "admin homepage"
+msgstr "admini koduleht"
+
+#: templates/admin/filer/folder/change_form.html:37
+#: templates/admin/filer/folder/directory_listing.html:95
+#: templates/admin/filer/folder/directory_listing.html:103
+#: templates/admin/filer/folder/directory_table_list.html:29
+#: templates/admin/filer/folder/directory_table_list.html:59
+#: templates/admin/filer/folder/directory_table_list.html:181
+#: templates/admin/filer/folder/directory_thumbnail_list.html:27
+#: templates/admin/filer/folder/directory_thumbnail_list.html:61
+#: templates/admin/filer/folder/directory_thumbnail_list.html:156
msgid "Folder Icon"
-msgstr ""
+msgstr "Kausta ikoon"
-#: templates/admin/filer/folder/choose_copy_destination.html:12
+#: templates/admin/filer/folder/choose_copy_destination.html:23
msgid ""
"Your account doesn't have permissions to copy all of the selected files and/"
"or folders."
msgstr ""
-#: templates/admin/filer/folder/choose_copy_destination.html:15
-#: templates/admin/filer/folder/choose_move_destination.html:15
+#: templates/admin/filer/folder/choose_copy_destination.html:25
+#: templates/admin/filer/folder/choose_copy_destination.html:31
+#: templates/admin/filer/folder/choose_copy_destination.html:37
+#: templates/admin/filer/folder/choose_move_destination.html:37
+#: templates/admin/filer/folder/choose_move_destination.html:43
+#: templates/admin/filer/folder/choose_move_destination.html:49
+msgid "Take me back"
+msgstr ""
+
+#: templates/admin/filer/folder/choose_copy_destination.html:29
+#: templates/admin/filer/folder/choose_move_destination.html:41
msgid "There are no destination folders available."
msgstr ""
-#: templates/admin/filer/folder/choose_copy_destination.html:18
+#: templates/admin/filer/folder/choose_copy_destination.html:35
msgid "There are no files and/or folders available to copy."
msgstr ""
-#: templates/admin/filer/folder/choose_copy_destination.html:20
+#: templates/admin/filer/folder/choose_copy_destination.html:40
msgid ""
"The following files and/or folders will be copied to a destination folder "
"(retaining their tree structure):"
msgstr ""
-#: templates/admin/filer/folder/choose_copy_destination.html:32
-#: templates/admin/filer/folder/choose_move_destination.html:32
+#: templates/admin/filer/folder/choose_copy_destination.html:54
+#: templates/admin/filer/folder/choose_move_destination.html:64
msgid "Destination folder:"
msgstr ""
-#: templates/admin/filer/folder/choose_copy_destination.html:39
+#: templates/admin/filer/folder/choose_copy_destination.html:65
+#: templates/admin/filer/folder/choose_copy_destination.html:68
+#: templates/admin/filer/folder/directory_listing.html:178
msgid "Copy"
+msgstr "Kopeeri"
+
+#: templates/admin/filer/folder/choose_copy_destination.html:67
+msgid "It is not allowed to copy files into same folder"
msgstr ""
-#: templates/admin/filer/folder/choose_images_resize_options.html:12
+#: templates/admin/filer/folder/choose_images_resize_options.html:15
msgid ""
"Your account doesn't have permissions to resize all of the selected images."
msgstr ""
-#: templates/admin/filer/folder/choose_images_resize_options.html:15
+#: templates/admin/filer/folder/choose_images_resize_options.html:18
msgid "There are no images available to resize."
msgstr ""
-#: templates/admin/filer/folder/choose_images_resize_options.html:17
+#: templates/admin/filer/folder/choose_images_resize_options.html:20
msgid "The following images will be resized:"
msgstr ""
-#: templates/admin/filer/folder/choose_images_resize_options.html:30
-msgid "Choose an existing thumbnail option or enter resize parameters:"
-msgstr ""
-
#: templates/admin/filer/folder/choose_images_resize_options.html:32
-msgid "Choose resize parameters:"
+msgid "Choose an existing thumbnail option or enter resize parameters:"
msgstr ""
-#: templates/admin/filer/folder/choose_images_resize_options.html:35
+#: templates/admin/filer/folder/choose_images_resize_options.html:36
msgid ""
"Warning: Images will be resized in-place and originals will be lost. Maybe "
"first make a copy of them to retain the originals."
msgstr ""
-#: templates/admin/filer/folder/choose_images_resize_options.html:36
+#: templates/admin/filer/folder/choose_images_resize_options.html:39
msgid "Resize"
-msgstr ""
+msgstr "Muuda suurust"
-#: templates/admin/filer/folder/choose_move_destination.html:12
+#: templates/admin/filer/folder/choose_move_destination.html:35
msgid ""
"Your account doesn't have permissions to move all of the selected files and/"
"or folders."
msgstr ""
-#: templates/admin/filer/folder/choose_move_destination.html:18
+#: templates/admin/filer/folder/choose_move_destination.html:47
msgid "There are no files and/or folders available to move."
msgstr ""
-#: templates/admin/filer/folder/choose_move_destination.html:20
+#: templates/admin/filer/folder/choose_move_destination.html:52
msgid ""
"The following files and/or folders will be moved to a destination folder "
"(retaining their tree structure):"
msgstr ""
-#: templates/admin/filer/folder/choose_move_destination.html:38
+#: templates/admin/filer/folder/choose_move_destination.html:73
+#: templates/admin/filer/folder/choose_move_destination.html:76
+#: templates/admin/filer/folder/directory_listing.html:181
msgid "Move"
+msgstr "Liiguta"
+
+#: templates/admin/filer/folder/choose_move_destination.html:75
+msgid "It is not allowed to move files into same folder"
msgstr ""
-#: templates/admin/filer/folder/choose_rename_format.html:12
+#: templates/admin/filer/folder/choose_rename_format.html:15
msgid ""
"Your account doesn't have permissions to rename all of the selected files."
msgstr ""
-#: templates/admin/filer/folder/choose_rename_format.html:15
+#: templates/admin/filer/folder/choose_rename_format.html:18
msgid "There are no files available to rename."
msgstr ""
-#: templates/admin/filer/folder/choose_rename_format.html:17
+#: templates/admin/filer/folder/choose_rename_format.html:20
msgid ""
"The following files will be renamed (they will stay in their folders and "
"keep original filename, only displayed filename will be changed):"
msgstr ""
-#: templates/admin/filer/folder/choose_rename_format.html:54
+#: templates/admin/filer/folder/choose_rename_format.html:59
msgid "Rename"
-msgstr ""
+msgstr "Nimeta ümber"
-#: templates/admin/filer/folder/directory_listing.html:66
-msgid "Adds a new Folder"
+#: templates/admin/filer/folder/directory_listing.html:94
+msgid "Go back to the parent folder"
+msgstr "Mine tagasi peamisesse kausta"
+
+#: templates/admin/filer/folder/directory_listing.html:131
+msgid "Change current folder details"
msgstr ""
-#: templates/admin/filer/folder/directory_listing.html:66
-#: templates/image_filer/include/export_dialog.html:34
-msgid "New Folder"
+#: templates/admin/filer/folder/directory_listing.html:131
+msgid "Change"
+msgstr "Muuda"
+
+#: templates/admin/filer/folder/directory_listing.html:141
+msgid "Click here to run search for entered phrase"
msgstr ""
-#: templates/admin/filer/folder/directory_listing.html:67
-msgid "upload files"
+#: templates/admin/filer/folder/directory_listing.html:144
+msgid "Search"
+msgstr "Otsi"
+
+#: templates/admin/filer/folder/directory_listing.html:151
+msgid "Close"
+msgstr "Sulge"
+
+#: templates/admin/filer/folder/directory_listing.html:153
+msgid "Limit"
msgstr ""
-#: templates/admin/filer/folder/directory_listing.html:67
-msgid "Upload"
+#: templates/admin/filer/folder/directory_listing.html:158
+msgid "Check it to limit the search to current folder"
msgstr ""
-#: templates/admin/filer/folder/directory_listing.html:74
-msgid "Go back to the parent folder"
+#: templates/admin/filer/folder/directory_listing.html:159
+msgid "Limit the search to current folder"
msgstr ""
-#: templates/admin/filer/folder/directory_listing.html:76
-#: templates/admin/filer/folder/directory_table.html:29
-#: templates/admin/filer/tools/search_form.html:18
-#, python-format
-msgid "1 folder"
-msgid_plural "%(counter)s folders"
-msgstr[0] ""
-msgstr[1] ""
+#: templates/admin/filer/folder/directory_listing.html:175
+msgid "Delete"
+msgstr "Kustuta"
-#: templates/admin/filer/folder/directory_listing.html:76
-#: templates/admin/filer/folder/directory_table.html:29
-#: templates/admin/filer/tools/search_form.html:19
-#, python-format
-msgid "1 file"
-msgid_plural "%(counter)s files"
-msgstr[0] ""
-msgstr[1] ""
+#: templates/admin/filer/folder/directory_listing.html:203
+msgid "Adds a new Folder"
+msgstr "Lisab uue kausta"
-#: templates/admin/filer/folder/directory_listing.html:80
-msgid "Change current folder details"
+#: templates/admin/filer/folder/directory_listing.html:206
+msgid "New Folder"
+msgstr "Uus kaust"
+
+#: templates/admin/filer/folder/directory_listing.html:211
+#: templates/admin/filer/folder/directory_listing.html:218
+#: templates/admin/filer/folder/directory_listing.html:221
+#: templates/admin/filer/folder/directory_listing.html:228
+#: templates/admin/filer/folder/directory_listing.html:235
+msgid "Upload Files"
msgstr ""
-#: templates/admin/filer/folder/directory_listing.html:80
-#: templates/admin/filer/folder/directory_table.html:27
-#: templates/admin/filer/folder/directory_table.html:43
-msgid "Change"
+#: templates/admin/filer/folder/directory_listing.html:233
+msgid "You have to select a folder first"
msgstr ""
-#: templates/admin/filer/folder/directory_table.html:13
+#: templates/admin/filer/folder/directory_table_list.html:16
msgid "Name"
+msgstr "Nimi"
+
+#: templates/admin/filer/folder/directory_table_list.html:17
+#: templates/admin/filer/tools/detail_info.html:50
+msgid "Owner"
+msgstr "Omanik"
+
+#: templates/admin/filer/folder/directory_table_list.html:18
+#: templates/admin/filer/tools/detail_info.html:34
+msgid "Size"
+msgstr "Suurus"
+
+#: templates/admin/filer/folder/directory_table_list.html:19
+msgid "Action"
msgstr ""
-#: templates/admin/filer/folder/directory_table.html:23
-#: templates/admin/filer/folder/directory_table.html:27
-#: templates/admin/filer/folder/directory_table.html:29
+#: templates/admin/filer/folder/directory_table_list.html:28
+#: templates/admin/filer/folder/directory_table_list.html:35
+#: templates/admin/filer/folder/directory_table_list.html:58
+#: templates/admin/filer/folder/directory_table_list.html:65
+#: templates/admin/filer/folder/directory_thumbnail_list.html:26
+#: templates/admin/filer/folder/directory_thumbnail_list.html:32
+#: templates/admin/filer/folder/directory_thumbnail_list.html:60
+#: templates/admin/filer/folder/directory_thumbnail_list.html:66
#, python-format
msgid "Change '%(item_label)s' folder details"
msgstr ""
-#: templates/admin/filer/folder/directory_table.html:30
-#: templates/admin/filer/folder/directory_table.html:45
-msgid "Owner"
+#: templates/admin/filer/folder/directory_table_list.html:76
+#: templates/admin/filer/tools/search_form.html:5
+#, python-format
+msgid "%(counter)s folder"
+msgid_plural "%(counter)s folders"
+msgstr[0] "%(counter)s kaust"
+msgstr[1] "%(counter)s kausta"
+
+#: templates/admin/filer/folder/directory_table_list.html:77
+#: templates/admin/filer/tools/search_form.html:6
+#, python-format
+msgid "%(counter)s file"
+msgid_plural "%(counter)s files"
+msgstr[0] "%(counter)s fail"
+msgstr[1] "%(counter)s faili"
+
+#: templates/admin/filer/folder/directory_table_list.html:83
+msgid "Change folder details"
msgstr ""
-#: templates/admin/filer/folder/directory_table.html:37
+#: templates/admin/filer/folder/directory_table_list.html:84
+msgid "Remove folder"
+msgstr "Eemalda kaust"
+
+#: templates/admin/filer/folder/directory_table_list.html:96
+#: templates/admin/filer/folder/directory_table_list.html:104
+#: templates/admin/filer/folder/directory_table_list.html:119
+#: templates/admin/filer/folder/directory_thumbnail_list.html:95
+#: templates/admin/filer/folder/directory_thumbnail_list.html:104
+#: templates/admin/filer/folder/directory_thumbnail_list.html:119
msgid "Select this file"
-msgstr ""
+msgstr "Vali see fail"
-#: templates/admin/filer/folder/directory_table.html:39
-#: templates/admin/filer/folder/directory_table.html:43
-#: templates/admin/filer/folder/directory_table.html:44
+#: templates/admin/filer/folder/directory_table_list.html:107
+#: templates/admin/filer/folder/directory_table_list.html:122
+#: templates/admin/filer/folder/directory_table_list.html:154
+#: templates/admin/filer/folder/directory_thumbnail_list.html:107
+#: templates/admin/filer/folder/directory_thumbnail_list.html:122
#, python-format
msgid "Change '%(item_label)s' details"
msgstr ""
-#: templates/admin/filer/folder/directory_table.html:42
-#, python-format
-msgid "Delete '%(item_label)s'"
-msgstr ""
-
-#: templates/admin/filer/folder/directory_table.html:42
-msgid "Delete"
-msgstr ""
-
-#: templates/admin/filer/folder/directory_table.html:46
+#: templates/admin/filer/folder/directory_table_list.html:131
+#: templates/admin/filer/folder/directory_thumbnail_list.html:130
msgid "Permissions"
-msgstr ""
+msgstr "Õigused"
-#: templates/admin/filer/folder/directory_table.html:46
+#: templates/admin/filer/folder/directory_table_list.html:131
+#: templates/admin/filer/folder/directory_thumbnail_list.html:130
msgid "disabled"
-msgstr ""
+msgstr "välja lülitatud"
-#: templates/admin/filer/folder/directory_table.html:46
+#: templates/admin/filer/folder/directory_table_list.html:131
+#: templates/admin/filer/folder/directory_thumbnail_list.html:130
msgid "enabled"
+msgstr "sisse lülitatud"
+
+#: templates/admin/filer/folder/directory_table_list.html:144
+msgid "URL copied to clipboard"
msgstr ""
-#: templates/admin/filer/folder/directory_table.html:50
-msgid "Move to clipboard"
+#: templates/admin/filer/folder/directory_table_list.html:147
+#, python-format
+msgid "Canonical url '%(item_label)s'"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_table_list.html:151
+#, python-format
+msgid "Download '%(item_label)s'"
+msgstr "Lae alla '%(item_label)s'"
+
+#: templates/admin/filer/folder/directory_table_list.html:155
+msgid "Remove file"
+msgstr "Eemalda fail"
+
+#: templates/admin/filer/folder/directory_table_list.html:163
+#: templates/admin/filer/folder/directory_thumbnail_list.html:138
+msgid "Drop files here or use the \"Upload Files\" button"
msgstr ""
-#: templates/admin/filer/folder/directory_table.html:58
-msgid "there are no files or subfolders"
+#: templates/admin/filer/folder/directory_table_list.html:178
+#: templates/admin/filer/folder/directory_thumbnail_list.html:153
+msgid "Drop your file to upload into:"
msgstr ""
-#: templates/admin/filer/folder/directory_table.html:65
+#: templates/admin/filer/folder/directory_table_list.html:188
+#: templates/admin/filer/folder/directory_thumbnail_list.html:163
+msgid "Upload"
+msgstr "Laadi üles"
+
+#: templates/admin/filer/folder/directory_table_list.html:200
+#: templates/admin/filer/folder/directory_thumbnail_list.html:175
+msgid "cancel"
+msgstr "tühista"
+
+#: templates/admin/filer/folder/directory_table_list.html:204
+#: templates/admin/filer/folder/directory_thumbnail_list.html:179
+msgid "Upload success!"
+msgstr "Üleslaadimine õnnestus!"
+
+#: templates/admin/filer/folder/directory_table_list.html:208
+#: templates/admin/filer/folder/directory_thumbnail_list.html:183
+msgid "Upload canceled!"
+msgstr "Üleslaadimine tühistatud!"
+
+#: templates/admin/filer/folder/directory_table_list.html:215
+#: templates/admin/filer/folder/directory_thumbnail_list.html:190
msgid "previous"
-msgstr ""
+msgstr "eelmine"
-#: templates/admin/filer/folder/directory_table.html:69
+#: templates/admin/filer/folder/directory_table_list.html:220
+#: templates/admin/filer/folder/directory_thumbnail_list.html:195
#, python-format
-msgid ""
-"\n"
-" Page %(number)s of %(num_pages)s.\n"
-" "
-msgstr ""
+msgid "Page %(number)s of %(num_pages)s."
+msgstr "Lehekülg %(number)s / %(num_pages)s."
-#: templates/admin/filer/folder/directory_table.html:75
+#: templates/admin/filer/folder/directory_table_list.html:225
+#: templates/admin/filer/folder/directory_thumbnail_list.html:200
msgid "next"
+msgstr "järgmine"
+
+#: templates/admin/filer/folder/directory_table_list.html:235
+#: templates/admin/filer/folder/directory_thumbnail_list.html:210
+#, python-format
+msgid "Select all %(total_count)s"
+msgstr "Vali kõik %(total_count)s"
+
+#: templates/admin/filer/folder/directory_thumbnail_list.html:15
+#: templates/admin/filer/folder/directory_thumbnail_list.html:80
+msgid "Select all"
+msgstr ""
+
+#: templates/admin/filer/folder/directory_thumbnail_list.html:77
+msgid "Files"
msgstr ""
#: templates/admin/filer/folder/new_folder_form.html:4
+#: templates/admin/filer/folder/new_folder_form.html:7
msgid "Add new"
+msgstr "Lisa uus"
+
+#: templates/admin/filer/folder/new_folder_form.html:4
+msgid "Django site admin"
msgstr ""
-#: templates/admin/filer/folder/new_folder_form.html:13
+#: templates/admin/filer/folder/new_folder_form.html:15
msgid "Please correct the error below."
msgid_plural "Please correct the errors below."
msgstr[0] ""
msgstr[1] ""
-#: templates/admin/filer/folder/new_folder_form.html:28
+#: templates/admin/filer/folder/new_folder_form.html:30
msgid "Save"
-msgstr ""
+msgstr "Salvesta"
-#: templates/admin/filer/image/change_form.html:21
-msgid "Full size preview"
+#: templates/admin/filer/templatetags/file_icon.html:9
+msgid "Your browser does not support audio."
msgstr ""
-#: templates/admin/filer/tools/search_form.html:8
-#: templates/admin/filer/tools/search_form.html:14
-msgid "Search"
+#: templates/admin/filer/templatetags/file_icon.html:14
+msgid "Your browser does not support video."
msgstr ""
-#: templates/admin/filer/tools/search_form.html:13
-msgid "Enter your search phrase here"
+#: templates/admin/filer/tools/clipboard/clipboard.html:9
+msgid "Clipboard"
+msgstr "Lõikelaud"
+
+#: templates/admin/filer/tools/clipboard/clipboard.html:21
+msgid "Paste all items here"
msgstr ""
-#: templates/admin/filer/tools/search_form.html:14
-msgid "Click here to"
+#: templates/admin/filer/tools/clipboard/clipboard.html:27
+msgid "Move all clipboard files to"
msgstr ""
-#: templates/admin/filer/tools/search_form.html:14
-msgid "run search for entered phrase"
+#: templates/admin/filer/tools/clipboard/clipboard.html:27
+msgid "Empty Clipboard"
+msgstr "Tühjenda lõikelaud"
+
+#: templates/admin/filer/tools/clipboard/clipboard.html:50
+msgid "the clipboard is empty"
+msgstr "lõikelaud on tühi"
+
+#: templates/admin/filer/tools/clipboard/clipboard_item_rows.html:16
+msgid "upload failed"
+msgstr "üleslaadimine ebaõnnestus"
+
+#: templates/admin/filer/tools/detail_info.html:11
+msgid "Download"
msgstr ""
-#: templates/admin/filer/tools/search_form.html:15
-msgid "Check it to"
+#: templates/admin/filer/tools/detail_info.html:16
+msgid "Expand"
msgstr ""
-#: templates/admin/filer/tools/search_form.html:15
-#: templates/admin/filer/tools/search_form.html:16
-msgid "limit the search to current folder"
+#: templates/admin/filer/tools/detail_info.html:21
+#: templates/admin/filer/widgets/admin_file.html:32
+#: templatetags/filer_admin_tags.py:108
+msgid "File is missing"
msgstr ""
-#: templates/admin/filer/tools/search_form.html:18
+#: templates/admin/filer/tools/detail_info.html:30
+msgid "Type"
+msgstr "Tüüp"
+
+#: templates/admin/filer/tools/detail_info.html:38
+msgid "File-size"
+msgstr "Faili suurus"
+
+#: templates/admin/filer/tools/detail_info.html:42
+msgid "Modified"
+msgstr "Muudetud"
+
+#: templates/admin/filer/tools/detail_info.html:46
+msgid "Created"
+msgstr "Loodud"
+
+#: templates/admin/filer/tools/search_form.html:5
msgid "found"
-msgstr ""
+msgstr "leitud"
-#: templates/admin/filer/tools/search_form.html:18
+#: templates/admin/filer/tools/search_form.html:5
msgid "and"
-msgstr ""
+msgstr "ja"
-#: templates/admin/filer/tools/search_form.html:19
+#: templates/admin/filer/tools/search_form.html:7
msgid "cancel search"
-msgstr ""
+msgstr "tühista otsing"
-#: templates/admin/filer/tools/upload_button_js.html:24
-#: templates/admin/filer/widgets/admin_file.html:7
-#: templates/admin/filer/widgets/admin_file.html:8
-msgid "file missing"
-msgstr ""
+#: templates/admin/filer/widgets/admin_file.html:10
+#: templates/admin/filer/widgets/admin_file.html:39
+#: templates/admin/filer/widgets/admin_folder.html:18
+msgid "Clear"
+msgstr "Tühjenda"
-#: templates/admin/filer/tools/clipboard/clipboard.html:7
-msgid "Clipboard"
-msgstr ""
+#: templates/admin/filer/widgets/admin_file.html:22
+msgid "or drop your file here"
+msgstr "või lohista fail siia"
-#: templates/admin/filer/tools/clipboard/clipboard.html:19
-msgid "Paste all items here"
-msgstr ""
+#: templates/admin/filer/widgets/admin_file.html:35
+msgid "no file selected"
+msgstr "ühtegi faili pole valitud"
-#: templates/admin/filer/tools/clipboard/clipboard.html:26
-msgid "discard"
+#: templates/admin/filer/widgets/admin_file.html:50
+#: templates/admin/filer/widgets/admin_folder.html:14
+msgid "Lookup"
msgstr ""
-#: templates/admin/filer/tools/clipboard/clipboard.html:26
-msgid "Move all clipboard files to"
-msgstr ""
+#: templates/admin/filer/widgets/admin_file.html:51
+msgid "Choose File"
+msgstr "Vali fail"
-#: templates/admin/filer/tools/clipboard/clipboard.html:43
-msgid "the clipboard is empty"
+#: templates/admin/filer/widgets/admin_folder.html:16
+msgid "Choose Folder"
msgstr ""
-#: templates/admin/filer/tools/clipboard/clipboard_item_rows.html:11
-msgid "upload failed"
+#: validation.py:20
+#, python-brace-format
+msgid "File \"{file_name}\": Upload denied by site security policy"
msgstr ""
-#: templates/admin/filer/widgets/admin_file.html:11
-msgid "no file selected"
+#: validation.py:23
+#, python-brace-format
+msgid "File \"{file_name}\": {file_type} upload denied by site security policy"
msgstr ""
-#: templates/admin/filer/widgets/admin_file.html:14
-#: templates/admin/filer/widgets/admin_file.html:15
-#: templates/admin/filer/widgets/admin_folder.html:7
-#: templates/admin/filer/widgets/admin_folder.html:8
-msgid "Lookup"
+#: validation.py:34
+#, python-brace-format
+msgid "File \"{file_name}\": HTML upload denied by site security policy"
msgstr ""
-#: templates/admin/filer/widgets/admin_file.html:17
-#: templates/admin/filer/widgets/admin_folder.html:10
-msgid "Clear"
+#: validation.py:72
+#, python-brace-format
+msgid ""
+"File \"{file_name}\": Rejected due to potential cross site scripting "
+"vulnerability"
msgstr ""
-#: templates/admin/filer/widgets/admin_folder.html:5
-msgid "none selected"
+#: validation.py:84
+#, python-brace-format
+msgid "File \"{file_name}\": SVG file format not recognized"
msgstr ""
-#: templates/image_filer/folder.html:3
-#, python-format
-msgid "Folder '%(folder_label)s' files"
-msgstr ""
+#~ msgid "Resize parameters must be choosen."
+#~ msgstr "Resize parameters must be choosen."
-#: templates/image_filer/folder.html:6
-msgid "File name"
-msgstr ""
+#~ msgid "Choose resize parameters:"
+#~ msgstr "Choose resize parameters:"
-#: templates/image_filer/image_export_form.html:17
-msgid "export"
-msgstr ""
+#~ msgid "Open file"
+#~ msgstr "Open file"
-#: templates/image_filer/image_export_form.html:27
-msgid "download image"
-msgstr ""
+#~ msgid "Full size preview"
+#~ msgstr "Full size preview"
-#: templates/image_filer/include/export_dialog.html:13
-msgid "Folder name already taken."
-msgstr ""
+#~ msgid "Add Description"
+#~ msgstr "Add Description"
-#: templates/image_filer/include/export_dialog.html:15
-msgid "Submit"
-msgstr ""
+#~ msgid "Author"
+#~ msgstr "Author"
+
+#~ msgid "Add Alt-Text"
+#~ msgstr "Add Alt-Text"
+
+#~ msgid "Add caption"
+#~ msgstr "Add caption"
+
+#~ msgid "Add Tags"
+#~ msgstr "Add Tags"
+
+#~ msgid "Change File"
+#~ msgstr "Change File"
+
+#~ msgid "none selected"
+#~ msgstr "none selected"
+
+#~ msgid "Add Folder"
+#~ msgstr "Add Folder"
+
+#~ msgid "Subject Location"
+#~ msgstr "Subject location"
+
+#~ msgid ""
+#~ "\n"
+#~ " %(counter)s file"
+#~ msgid_plural ""
+#~ "%(counter)s files\n"
+#~ " "
+#~ msgstr[0] "15c0f2d28d6dab1af1f6d94906beb627_pl_0"
+#~ msgstr[1] "15c0f2d28d6dab1af1f6d94906beb627_pl_1"
+
+#~ msgid "Upload File"
+#~ msgstr "Upload File"
diff --git a/filer/locale/eu/LC_MESSAGES/django.mo b/filer/locale/eu/LC_MESSAGES/django.mo
new file mode 100644
index 0000000000000000000000000000000000000000..0c1dfafb69c392d5872efea85a89c95bca4fca89
GIT binary patch
literal 11064
zcmb7}d5|2{ea9c>w7|qb97AyI#~~zvc2*LH@d^QwkkBFt36KydIQY$U?@Tj2-9sN*
z?fAkmV89oI%2gyOhm^}zsWL9drYhx-a#a#LsW_Fg{YUISd{W6DsoYg5@IQ9)`M&Ns
zc6L{SuKmrYkJs<`UGKf#A65q-*9Wcp3aORQ+Fos{ilde*)7fj_eyH~|xE=l#)bn4!>)_Qm+YRt=%_ranuD=F1!dIc%JD<*93wJ^F
zw+A)O0)7~N3`+0+2k(N%7$mM?UWD?iFT*q8UqHS8b*OQE6Uwi@SMw)O`ubO>_g;sp
z=Zs4$e|Qub+pq_a8#}
z%b!5sUx;bVHzB4l--jyqZ#Dl1GIaAgRQU^WGCh9>)b-_1{kRg!Pa9Bic^Inwq-GA)
z|C3PdJq=aQ3sC+4W2o|fTJuX#<-ZEm{x{&S!XH5C?e+TkIo#BH?}XCtyP*1WEtEcP
zs-NEq)!uIS_k6Yj~@J^_DzYAyJkD$hX0fUu4
zT>=~M7N|Jy!aLz-q5APXD7*a_ZiD{?_5PLbuAXm#ircMF^&F}1f4si`$@>0FPgHY}L9#lX72)++~9;*D0
zp~m|f+y^hfh_b`cnvcWHTz>`5!=J!w;I&s*_P(cP2@~%BKHLJ&TVFkYAKb+C`{8@x
zC*V!+>rnN+RzJUrO4a^BsB*sp&xHR7`2m$0wll
z^a4~rUaIeZ3(B8<3RTa~p!)xFxDH-Ou&Te8LFJn(pxT**XTV1g>0O8HKa2Q&>jp;)
zIf)RKKED9J{&^673Xv_ILv|u<(=vV^=9kq_v=9sQx4=<0L%i4OTygnSS=
zfjouWi)4t7MdUNccH|ah7t%#Ois |