diff --git a/.travis.yml b/.travis.yml index 5915da7b7..7330e7df3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,6 +19,8 @@ env: - DJANGO="Django>=1.6,<1.7" EASY_THUMBNAILS="easy-thumbnails>2.0" CUSTOM_IMAGE="filer.test_utils.custom_image.models.Image" - DJANGO="Django>=1.7,<1.8" EASY_THUMBNAILS="easy-thumbnails>2.0" - DJANGO="Django>=1.7,<1.8" EASY_THUMBNAILS="easy-thumbnails>2.0" CUSTOM_IMAGE="filer.test_utils.custom_image.models.Image" + - DJANGO="Django>=1.8,<1.9" EASY_THUMBNAILS="easy-thumbnails>2.0" + - DJANGO="Django>=1.8,<1.9" EASY_THUMBNAILS="easy-thumbnails>2.0" CUSTOM_IMAGE="filer.test_utils.custom_image.models.Image" - DJANGO="https://github.com/django/django/archive/master.zip" EASY_THUMBNAILS="easy-thumbnails>2.0" script: coverage run --rcfile=coverage.rc test_settings.py @@ -33,6 +35,10 @@ matrix: env: DJANGO="Django>=1.7,<1.8" EASY_THUMBNAILS="easy-thumbnails>2.0" - python: 2.6 env: DJANGO="Django>=1.7,<1.8" EASY_THUMBNAILS="easy-thumbnails>2.0" CUSTOM_IMAGE="filer.test_utils.custom_image.models.Image" + - python: 2.6 + env: DJANGO="Django>=1.8,<1.9" EASY_THUMBNAILS="easy-thumbnails>2.0" + - python: 2.6 + env: DJANGO="Django>=1.8,<1.9" EASY_THUMBNAILS="easy-thumbnails>2.0" CUSTOM_IMAGE="filer.test_utils.custom_image.models.Image" - python: 2.6 env: DJANGO="https://github.com/django/django/archive/master.zip" EASY_THUMBNAILS="easy-thumbnails>2.0" - python: 3.3 @@ -46,3 +52,15 @@ matrix: env: DJANGO="https://github.com/django/django/archive/master.zip" EASY_THUMBNAILS="easy-thumbnails>2.0" - python: 3.4 env: DJANGO="https://github.com/django/django/archive/master.zip" EASY_THUMBNAILS="easy-thumbnails>2.0" + - python: 2.7 + env: DJANGO="Django>=1.8,<1.9" EASY_THUMBNAILS="easy-thumbnails>2.0" + - python: 3.3 + env: DJANGO="Django>=1.8,<1.9" EASY_THUMBNAILS="easy-thumbnails>2.0" + - python: 3.4 + env: DJANGO="Django>=1.8,<1.9" EASY_THUMBNAILS="easy-thumbnails>2.0" + - python: 2.7 + env: DJANGO="Django>=1.8,<1.9" EASY_THUMBNAILS="easy-thumbnails>2.0" CUSTOM_IMAGE="filer.test_utils.custom_image.models.Image" + - python: 3.3 + env: DJANGO="Django>=1.8,<1.9" EASY_THUMBNAILS="easy-thumbnails>2.0" CUSTOM_IMAGE="filer.test_utils.custom_image.models.Image" + - python: 3.4 + env: DJANGO="Django>=1.8,<1.9" EASY_THUMBNAILS="easy-thumbnails>2.0" CUSTOM_IMAGE="filer.test_utils.custom_image.models.Image" diff --git a/filer/admin/folderadmin.py b/filer/admin/folderadmin.py index 7e0789a87..177d41acc 100644 --- a/filer/admin/folderadmin.py +++ b/filer/admin/folderadmin.py @@ -1,51 +1,52 @@ #-*- coding: utf-8 -*- from __future__ import unicode_literals +import itertools +import os +import re -from django import forms -from django import template -from django.core.exceptions import ValidationError +from django import forms, template +from django.conf import settings as django_settings +from django.contrib import messages from django.contrib.admin import helpers from django.contrib.admin.util import quote, unquote, capfirst -from django.contrib import messages -from django.utils.http import urlquote -from filer.admin.patched.admin_utils import get_deleted_objects +from django.core.exceptions import ValidationError from django.core.exceptions import PermissionDenied from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from django.core.urlresolvers import reverse from django.db import router, models -from django.db.models import Q from django.http import HttpResponseRedirect, HttpResponse from django.shortcuts import render_to_response, get_object_or_404 from django.template import RequestContext + try: from django.utils.encoding import force_text except ImportError: # Django < 1.5 from django.utils.encoding import force_unicode as force_text from django.utils.html import escape +from django.utils.http import urlquote from django.utils.safestring import mark_safe from django.utils.translation import ugettext as _ from django.utils.translation import ungettext, ugettext_lazy + from filer import settings from filer.admin.forms import (CopyFilesAndFoldersForm, ResizeImagesForm, RenameFilesForm) from filer.admin.permissions import PrimitivePermissionAwareModelAdmin -from filer.views import (popup_status, popup_param, selectfolder_status, - selectfolder_param) -from filer.admin.tools import (userperms_for_request, - check_folder_edit_permissions, - check_files_edit_permissions, - check_files_read_permissions, - check_folder_read_permissions) +from filer.admin.patched.admin_utils import get_deleted_objects +from filer.admin.tools import (userperms_for_request, + check_folder_edit_permissions, + check_files_edit_permissions, + check_files_read_permissions, + check_folder_read_permissions) from filer.models import (Folder, FolderRoot, UnfiledImages, File, tools, ImagesWithMissingData, FolderPermission, Image) from filer.settings import FILER_STATICMEDIA_PREFIX, FILER_PAGINATE_BY -from filer.utils.filer_easy_thumbnails import FilerActionThumbnailer from filer.thumbnail_processors import normalize_subject_location -from django.conf import settings as django_settings -import os -import re -import itertools +from filer.utils.compatibility import get_delete_permission +from filer.utils.filer_easy_thumbnails import FilerActionThumbnailer +from filer.views import (popup_status, popup_param, selectfolder_status, + selectfolder_param) class AddFolderPopupForm(forms.ModelForm): @@ -301,8 +302,8 @@ def directory_listing(self, request, folder_id=None, viewtype=None): perms = FolderPermission.objects.get_read_id_list(request.user) root_exclude_kw = {'parent__isnull': False, 'parent__id__in': perms} if perms != 'All': - file_qs = file_qs.filter(Q(folder__id__in=perms) | Q(owner=request.user)) - folder_qs = folder_qs.filter(Q(id__in=perms) | Q(owner=request.user)) + file_qs = file_qs.filter(models.Q(folder__id__in=perms) | models.Q(owner=request.user)) + folder_qs = folder_qs.filter(models.Q(id__in=perms) | models.Q(owner=request.user)) else: root_exclude_kw.pop('parent__id__in') if folder.is_root: @@ -414,19 +415,19 @@ def directory_listing(self, request, folder_id=None, viewtype=None): def filter_folder(self, qs, terms=[]): for term in terms: - filters = Q(name__icontains=term) + filters = models.Q(name__icontains=term) for filter_ in self.get_owner_filter_lookups(): - filters |= Q(**{filter_: term}) + filters |= models.Q(**{filter_: term}) qs = qs.filter(filters) return qs def filter_file(self, qs, terms=[]): for term in terms: - filters = (Q(name__icontains=term) | - Q(description__icontains=term) | - Q(original_filename__icontains=term)) + 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 |= Q(**{filter_: term}) + filters |= models.Q(**{filter_: term}) qs = qs.filter(filters) return qs @@ -732,8 +733,7 @@ def _format_callback(self, obj, user, admin_site, perms_needed): opts.app_label, opts.object_name.lower()), None, (quote(obj._get_pk_val()),)) - p = '%s.%s' % (opts.app_label, - opts.get_delete_permission()) + p = get_delete_permission(opts) if not user.has_perm(p): perms_needed.add(opts.verbose_name) # Display a link to the admin page. diff --git a/filer/admin/patched/admin_utils.py b/filer/admin/patched/admin_utils.py index 31c087ae5..f3b1d8926 100644 --- a/filer/admin/patched/admin_utils.py +++ b/filer/admin/patched/admin_utils.py @@ -11,6 +11,7 @@ from __future__ import unicode_literals from django.contrib.admin.util import NestedObjects, quote +from django.core.urlresolvers import reverse from django.utils.html import escape from django.utils.safestring import mark_safe from django.utils.text import capfirst @@ -19,7 +20,8 @@ except ImportError: # Django < 1.5 from django.utils.encoding import force_unicode as force_text -from django.core.urlresolvers import reverse + +from filer.utils.compatibility import get_delete_permission def get_deleted_objects(objs, opts, user, admin_site, using): @@ -47,8 +49,7 @@ def format_callback(obj): opts.app_label, opts.object_name.lower()), None, (quote(obj._get_pk_val()),)) - p = '%s.%s' % (opts.app_label, - opts.get_delete_permission()) + p = get_delete_permission(opts) if not user.has_perm(p): perms_needed.add(opts.verbose_name) # Display a link to the admin page. diff --git a/filer/admin/permissions.py b/filer/admin/permissions.py index beb24ee0e..3badc5327 100644 --- a/filer/admin/permissions.py +++ b/filer/admin/permissions.py @@ -2,6 +2,8 @@ from django.contrib import admin from django.core.urlresolvers import reverse +from filer.utils.compatibility import DJANGO_1_7 + class PrimitivePermissionAwareModelAdmin(admin.ModelAdmin): def has_add_permission(self, request): @@ -28,7 +30,10 @@ def _get_post_url(self, obj): and admin url may change """ ## Code borrowed from django ModelAdmin to determine changelist on the fly opts = obj._meta - module_name = opts.module_name + if DJANGO_1_7: + model_name = opts.module_name + else: + model_name = opts.model_name return reverse('admin:%s_%s_changelist' % - (opts.app_label, module_name), + (opts.app_label, model_name), current_app=self.admin_site.name) diff --git a/filer/models/filemodels.py b/filer/models/filemodels.py index cae184608..1978e7d5b 100644 --- a/filer/models/filemodels.py +++ b/filer/models/filemodels.py @@ -1,7 +1,6 @@ #-*- coding: utf-8 -*- from __future__ import unicode_literals -import django import hashlib import os @@ -10,12 +9,14 @@ from django.core.files.base import ContentFile from django.db import models from django.utils.translation import ugettext_lazy as _ + +from polymorphic import PolymorphicModel, PolymorphicManager + +from filer import settings as filer_settings from filer.fields.multistorage_file import MultiStorageFileField from filer.models import mixins -from filer import settings as filer_settings from filer.models.foldermodels import Folder -from filer.utils.compatibility import python_2_unicode_compatible -from polymorphic import PolymorphicModel, PolymorphicManager +from filer.utils.compatibility import python_2_unicode_compatible, DJANGO_1_7 class FileManager(PolymorphicManager): @@ -218,13 +219,13 @@ def __str__(self): return text def get_admin_url_path(self): - if django.VERSION < (1, 7): - module_name = self._meta.module_name + if DJANGO_1_7: + model_name = self._meta.module_name else: - module_name = self._meta.model_name + model_name = self._meta.model_name return urlresolvers.reverse( 'admin:%s_%s_change' % (self._meta.app_label, - module_name,), + model_name,), args=(self.pk,) ) diff --git a/filer/utils/compatibility.py b/filer/utils/compatibility.py index 573ea5d5e..d687ae0d9 100644 --- a/filer/utils/compatibility.py +++ b/filer/utils/compatibility.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- from distutils.version import LooseVersion -import django import sys + +import django from django.utils import six try: @@ -16,6 +17,10 @@ def truncate_words(s, num, end_text='...'): truncate_words = allow_lazy(truncate_words, six.text_type) DJANGO_1_4 = LooseVersion(django.get_version()) < LooseVersion('1.5') +DJANGO_1_5 = LooseVersion(django.get_version()) < LooseVersion('1.6') +DJANGO_1_6 = LooseVersion(django.get_version()) < LooseVersion('1.7') +DJANGO_1_7 = LooseVersion(django.get_version()) < LooseVersion('1.8') +DJANGO_1_8 = LooseVersion(django.get_version()) < LooseVersion('1.9') if not six.PY3: @@ -48,4 +53,14 @@ def python_2_unicode_compatible(klass): return klass except ImportError: force_unicode = lambda s: str(s) - from django.utils.encoding import python_2_unicode_compatible \ No newline at end of file + from django.utils.encoding import python_2_unicode_compatible + + +def get_delete_permission(opts): + try: + from django.contrib.auth import get_permission_codename + return '%s.%s' % (opts.app_label, + get_permission_codename('delete', opts)) + except ImportError: + return '%s.%s' % (opts.app_label, + opts.get_delete_permission()) diff --git a/setup.py b/setup.py index ff392bf15..36aee9006 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ def read(fname): install_requires = ( 'Django>=1.4', 'easy-thumbnails>=1.0', - 'django-mptt>=0.6,<0.6.2', + 'django-mptt>=0.6', 'django_polymorphic>=0.2', 'Unidecode>=0.04', ), diff --git a/tox.ini b/tox.ini index ee4ed297d..b4347cad8 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist=py{26,27}-django{14}, py{26,27,33,34}-django{15,16}, py{27,33,34}-django{17,dev}, py{27,33,34}-django{16,17}-custom +envlist=py{26,27}-django{14}, py{26,27,33,34}-django{15,16}, py{27,33,34}-django{17,18}, py{27,33,34}-django{16,17,18}-custom [testenv:docs] @@ -27,6 +27,7 @@ deps= django16: django<1.7 django16: south django17: django<1.8 + django18: django<1.9 djangodev: git+git://github.com/django/django.git#egg=Django py26: unittest2 py26: argparse @@ -55,3 +56,15 @@ setenv = [testenv:py34-django17-custom] setenv = CUSTOM_IMAGE=filer.test_utils.custom_image.models.Image + +[testenv:py27-django18-custom] +setenv = + CUSTOM_IMAGE=filer.test_utils.custom_image.models.Image + +[testenv:py33-django18-custom] +setenv = + CUSTOM_IMAGE=filer.test_utils.custom_image.models.Image + +[testenv:py34-django18-custom] +setenv = + CUSTOM_IMAGE=filer.test_utils.custom_image.models.Image