mirror of
https://github.com/wagtail/wagtail.git
synced 2024-12-01 11:41:20 +01:00
Merge branch 'kaedroho-python3-take2'
This commit is contained in:
commit
4f92311b37
@ -6,11 +6,6 @@ python:
|
||||
env:
|
||||
- DJANGO_VERSION=Django==1.6.5
|
||||
#- DJANGO_VERSION=Django==1.7.0
|
||||
matrix:
|
||||
allow_failures:
|
||||
- python: 3.4
|
||||
#- env: DJANGO_VERSION=Django==1.7.0
|
||||
fast_finish: true
|
||||
# Services
|
||||
services:
|
||||
- redis-server
|
||||
|
46
setup.py
46
setup.py
@ -1,5 +1,8 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import sys
|
||||
|
||||
|
||||
try:
|
||||
from setuptools import setup, find_packages
|
||||
except ImportError:
|
||||
@ -16,6 +19,31 @@ except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
PY3 = sys.version_info[0] == 3
|
||||
|
||||
|
||||
install_requires = [
|
||||
"Django>=1.6.2,<1.7",
|
||||
"South>=0.8.4",
|
||||
"django-compressor>=1.3",
|
||||
"django-libsass>=0.1",
|
||||
"django-modelcluster>=0.1",
|
||||
"django-taggit==0.11.2",
|
||||
"django-treebeard==2.0",
|
||||
"Pillow>=2.3.0",
|
||||
"beautifulsoup4>=4.3.2",
|
||||
"lxml>=3.3.0",
|
||||
"Unidecode>=0.04.14",
|
||||
"six==1.7.3",
|
||||
]
|
||||
|
||||
|
||||
if not PY3:
|
||||
install_requires += [
|
||||
"unicodecsv>=0.9.4"
|
||||
]
|
||||
|
||||
|
||||
setup(
|
||||
name='wagtail',
|
||||
version='0.3.1',
|
||||
@ -37,23 +65,11 @@ setup(
|
||||
'Programming Language :: Python :: 2',
|
||||
'Programming Language :: Python :: 2.6',
|
||||
'Programming Language :: Python :: 2.7',
|
||||
'Programming Language :: Python :: 3.3',
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Framework :: Django',
|
||||
'Topic :: Internet :: WWW/HTTP :: Site Management',
|
||||
],
|
||||
install_requires=[
|
||||
"Django>=1.6.2,<1.7",
|
||||
"South>=0.8.4",
|
||||
"django-compressor>=1.3",
|
||||
"django-libsass>=0.1",
|
||||
"django-modelcluster>=0.1",
|
||||
"django-taggit==0.11.2",
|
||||
"django-treebeard==2.0",
|
||||
"Pillow>=2.3.0",
|
||||
"beautifulsoup4>=4.3.2",
|
||||
"lxml>=3.3.0",
|
||||
'unicodecsv>=0.9.4',
|
||||
'Unidecode>=0.04.14',
|
||||
"BeautifulSoup==3.2.1", # django-compressor gets confused if we have lxml but not BS3 installed
|
||||
],
|
||||
install_requires=install_requires,
|
||||
zip_safe=False,
|
||||
)
|
||||
|
43
tox.ini
43
tox.ini
@ -1,15 +1,17 @@
|
||||
[deps]
|
||||
dj16=
|
||||
Django>=1.6,<1.7
|
||||
pyelasticsearch==0.6.1
|
||||
elasticutils==0.8.2
|
||||
elasticsearch==1.0.0
|
||||
mock==1.0.1
|
||||
|
||||
[tox]
|
||||
envlist =
|
||||
py26-dj16-postgres,
|
||||
py26-dj16-sqlite,
|
||||
py27-dj16-postgres,
|
||||
py27-dj16-sqlite
|
||||
py27-dj16-sqlite,
|
||||
py33-dj16-postgres,
|
||||
py34-dj16-postgres
|
||||
|
||||
# mysql not currently supported
|
||||
# (wagtail.wagtailimages.tests.TestImageEditView currently fails with a
|
||||
@ -17,6 +19,11 @@ envlist =
|
||||
# py26-dj16-mysql
|
||||
# py27-dj16-mysql
|
||||
|
||||
# South fails with sqlite on python3, because it tries to use DryRunMigrator which uses iteritems
|
||||
# py33-dj16-sqlite,
|
||||
# py34-dj16-sqlite
|
||||
|
||||
|
||||
[testenv]
|
||||
commands=./runtests.py
|
||||
|
||||
@ -67,3 +74,33 @@ deps =
|
||||
setenv =
|
||||
DATABASE_ENGINE=django.db.backends.mysql
|
||||
DATABASE_USER=wagtail
|
||||
|
||||
[testenv:py33-dj16-postgres]
|
||||
basepython=python3.3
|
||||
deps =
|
||||
{[deps]dj16}
|
||||
psycopg2==2.5.2
|
||||
setenv =
|
||||
DATABASE_ENGINE=django.db.backends.postgresql_psycopg2
|
||||
|
||||
[testenv:py33-dj16-sqlite]
|
||||
basepython=python3.3
|
||||
deps =
|
||||
{[deps]dj16}
|
||||
setenv =
|
||||
DATABASE_ENGINE=django.db.backends.sqlite3
|
||||
|
||||
[testenv:py34-dj16-postgres]
|
||||
basepython=python3.4
|
||||
deps =
|
||||
{[deps]dj16}
|
||||
psycopg2==2.5.2
|
||||
setenv =
|
||||
DATABASE_ENGINE=django.db.backends.postgresql_psycopg2
|
||||
|
||||
[testenv:py34-dj16-sqlite]
|
||||
basepython=python3.4
|
||||
deps =
|
||||
{[deps]dj16}
|
||||
setenv =
|
||||
DATABASE_ENGINE=django.db.backends.sqlite3
|
||||
|
@ -1,6 +1,9 @@
|
||||
from django.db import models
|
||||
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
|
||||
from modelcluster.fields import ParentalKey
|
||||
|
||||
from wagtail.wagtailcore.models import Page, Orderable
|
||||
from wagtail.wagtailcore.fields import RichTextField
|
||||
from wagtail.wagtailadmin.edit_handlers import FieldPanel, MultiFieldPanel, InlinePanel, PageChooserPanel
|
||||
@ -259,6 +262,7 @@ FormPage.content_panels = [
|
||||
|
||||
# Snippets
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Advert(models.Model):
|
||||
url = models.URLField(null=True, blank=True)
|
||||
text = models.CharField(max_length=255)
|
||||
@ -268,7 +272,7 @@ class Advert(models.Model):
|
||||
FieldPanel('text'),
|
||||
]
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.text
|
||||
|
||||
|
||||
@ -281,18 +285,20 @@ register_snippet(Advert)
|
||||
# to ensure specific [in]correct register ordering
|
||||
|
||||
# AlphaSnippet is registered during TestSnippetOrdering
|
||||
@python_2_unicode_compatible
|
||||
class AlphaSnippet(models.Model):
|
||||
text = models.CharField(max_length=255)
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.text
|
||||
|
||||
|
||||
# ZuluSnippet is registered during TestSnippetOrdering
|
||||
@python_2_unicode_compatible
|
||||
class ZuluSnippet(models.Model):
|
||||
text = models.CharField(max_length=255)
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.text
|
||||
|
||||
|
||||
|
@ -2,6 +2,9 @@ import copy
|
||||
import re
|
||||
import datetime
|
||||
|
||||
from six import string_types
|
||||
from six import text_type
|
||||
|
||||
from taggit.forms import TagWidget
|
||||
from modelcluster.forms import ClusterForm, ClusterFormMetaclass
|
||||
|
||||
@ -245,7 +248,7 @@ class EditHandler(object):
|
||||
"""
|
||||
rendered_fields = self.rendered_fields()
|
||||
missing_fields_html = [
|
||||
unicode(self.form[field_name])
|
||||
text_type(self.form[field_name])
|
||||
for field_name in self.form.fields
|
||||
if field_name not in rendered_fields
|
||||
]
|
||||
@ -483,7 +486,7 @@ class BasePageChooserPanel(BaseChooserPanel):
|
||||
def target_content_type(cls):
|
||||
if cls._target_content_type is None:
|
||||
if cls.page_type:
|
||||
if isinstance(cls.page_type, basestring):
|
||||
if isinstance(cls.page_type, string_types):
|
||||
# translate the passed model name into an actual model class
|
||||
from django.db.models import get_model
|
||||
try:
|
||||
|
@ -1,3 +1,5 @@
|
||||
from six import text_type
|
||||
|
||||
from django.utils.text import slugify
|
||||
from django.utils.html import format_html
|
||||
|
||||
@ -7,7 +9,7 @@ class MenuItem(object):
|
||||
self.label = label
|
||||
self.url = url
|
||||
self.classnames = classnames
|
||||
self.name = (name or slugify(unicode(label)))
|
||||
self.name = (name or slugify(text_type(label)))
|
||||
self.order = order
|
||||
|
||||
def render_html(self):
|
||||
|
@ -8,9 +8,11 @@
|
||||
### <img src="{% gravatar_url sometemplatevariable %}">
|
||||
### just make sure to update the "default" image path below
|
||||
|
||||
import urllib
|
||||
import hashlib
|
||||
|
||||
from six import b
|
||||
from six.moves.urllib.parse import urlencode
|
||||
|
||||
from django import template
|
||||
|
||||
register = template.Library()
|
||||
@ -30,8 +32,8 @@ class GravatarUrlNode(template.Node):
|
||||
default = "blank"
|
||||
size = int(self.size) * 2 # requested at retina size by default and scaled down at point of use with css
|
||||
|
||||
gravatar_url = "//www.gravatar.com/avatar/" + hashlib.md5(email.lower()).hexdigest() + "?"
|
||||
gravatar_url += urllib.urlencode({'s': str(size), 'd': default})
|
||||
gravatar_url = "//www.gravatar.com/avatar/" + hashlib.md5(b(email.lower())).hexdigest() + "?"
|
||||
gravatar_url += urlencode({'s': str(size), 'd': default})
|
||||
|
||||
return gravatar_url
|
||||
|
||||
|
@ -343,7 +343,7 @@ def edit(request, page_id):
|
||||
edit_handler = edit_handler_class(instance=page, form=form)
|
||||
errors_debug = (
|
||||
repr(edit_handler.form.errors)
|
||||
+ repr([(name, formset.errors) for (name, formset) in edit_handler.form.formsets.iteritems() if formset.errors])
|
||||
+ repr([(name, formset.errors) for (name, formset) in edit_handler.form.formsets.items() if formset.errors])
|
||||
)
|
||||
else:
|
||||
form = form_class(instance=page)
|
||||
|
@ -1,3 +1,5 @@
|
||||
from __future__ import print_function
|
||||
|
||||
import datetime
|
||||
import json
|
||||
from optparse import make_option
|
||||
@ -31,7 +33,7 @@ class Command(BaseCommand):
|
||||
def handle(self, *args, **options):
|
||||
dryrun = False
|
||||
if options['dryrun']:
|
||||
print "Will do a dry run."
|
||||
print("Will do a dry run.")
|
||||
dryrun = True
|
||||
|
||||
# 1. get all expired pages with live = True
|
||||
@ -41,17 +43,17 @@ class Command(BaseCommand):
|
||||
)
|
||||
if dryrun:
|
||||
if expired_pages:
|
||||
print "Expired pages to be deactivated:"
|
||||
print "Expiry datetime\t\tSlug\t\tName"
|
||||
print "---------------\t\t----\t\t----"
|
||||
print("Expired pages to be deactivated:")
|
||||
print("Expiry datetime\t\tSlug\t\tName")
|
||||
print("---------------\t\t----\t\t----")
|
||||
for ep in expired_pages:
|
||||
print "{0}\t{1}\t{2}".format(
|
||||
print("{0}\t{1}\t{2}".format(
|
||||
ep.expire_at.strftime("%Y-%m-%d %H:%M"),
|
||||
ep.slug,
|
||||
ep.title
|
||||
)
|
||||
))
|
||||
else:
|
||||
print "No expired pages to be deactivated found."
|
||||
print("No expired pages to be deactivated found.")
|
||||
else:
|
||||
expired_pages.update(expired=True, live=False)
|
||||
|
||||
@ -62,22 +64,22 @@ class Command(BaseCommand):
|
||||
) if revision_date_expired(r)
|
||||
]
|
||||
if dryrun:
|
||||
print "---------------------------------"
|
||||
print("---------------------------------")
|
||||
if expired_revs:
|
||||
print "Expired revisions to be dropped from moderation queue:"
|
||||
print "Expiry datetime\t\tSlug\t\tName"
|
||||
print "---------------\t\t----\t\t----"
|
||||
print("Expired revisions to be dropped from moderation queue:")
|
||||
print("Expiry datetime\t\tSlug\t\tName")
|
||||
print("---------------\t\t----\t\t----")
|
||||
for er in expired_revs:
|
||||
rev_data = json.loads(er.content_json)
|
||||
print "{0}\t{1}\t{2}".format(
|
||||
print("{0}\t{1}\t{2}".format(
|
||||
dateparse.parse_datetime(
|
||||
rev_data.get('expire_at')
|
||||
).strftime("%Y-%m-%d %H:%M"),
|
||||
rev_data.get('slug'),
|
||||
rev_data.get('title')
|
||||
)
|
||||
))
|
||||
else:
|
||||
print "No expired revision to be dropped from moderation."
|
||||
print("No expired revision to be dropped from moderation.")
|
||||
else:
|
||||
for er in expired_revs:
|
||||
er.submitted_for_moderation = False
|
||||
@ -88,20 +90,20 @@ class Command(BaseCommand):
|
||||
approved_go_live_at__lt=timezone.now()
|
||||
)
|
||||
if dryrun:
|
||||
print "---------------------------------"
|
||||
print("---------------------------------")
|
||||
if revs_for_publishing:
|
||||
print "Revisions to be published:"
|
||||
print "Go live datetime\t\tSlug\t\tName"
|
||||
print "---------------\t\t\t----\t\t----"
|
||||
print("Revisions to be published:")
|
||||
print("Go live datetime\t\tSlug\t\tName")
|
||||
print("---------------\t\t\t----\t\t----")
|
||||
for rp in revs_for_publishing:
|
||||
rev_data = json.loads(rp.content_json)
|
||||
print "{0}\t\t{1}\t{2}".format(
|
||||
print("{0}\t\t{1}\t{2}".format(
|
||||
rp.approved_go_live_at.strftime("%Y-%m-%d %H:%M"),
|
||||
rev_data.get('slug'),
|
||||
rev_data.get('title')
|
||||
)
|
||||
))
|
||||
else:
|
||||
print "No pages to go live."
|
||||
print("No pages to go live.")
|
||||
else:
|
||||
for rp in revs_for_publishing:
|
||||
# just run publish for the revision -- since the approved go
|
||||
|
@ -1,7 +1,10 @@
|
||||
from StringIO import StringIO
|
||||
from urlparse import urlparse
|
||||
import warnings
|
||||
|
||||
import six
|
||||
from six import string_types
|
||||
from six import StringIO
|
||||
from six.moves.urllib.parse import urlparse
|
||||
|
||||
from modelcluster.models import ClusterableModel
|
||||
|
||||
from django.db import models, connection, transaction
|
||||
@ -19,6 +22,7 @@ from django.utils.translation import ugettext
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
|
||||
from treebeard.mp_tree import MP_Node
|
||||
|
||||
@ -33,6 +37,7 @@ class SiteManager(models.Manager):
|
||||
return self.get(hostname=hostname, port=port)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Site(models.Model):
|
||||
hostname = models.CharField(max_length=255, db_index=True)
|
||||
port = models.IntegerField(default=80, help_text=_("Set this to something other than 80 if you need a specific port number to appear in URLs (e.g. development on port 8000). Does not affect request handling (so port forwarding still works)."))
|
||||
@ -45,7 +50,7 @@ class Site(models.Model):
|
||||
def natural_key(self):
|
||||
return (self.hostname, self.port)
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.hostname + ("" if self.port == 80 else (":%d" % self.port)) + (" [default]" if self.is_default_site else "")
|
||||
|
||||
@staticmethod
|
||||
@ -255,9 +260,8 @@ class PageBase(models.base.ModelBase):
|
||||
PAGE_MODEL_CLASSES.append(cls)
|
||||
|
||||
|
||||
class Page(MP_Node, ClusterableModel, Indexed):
|
||||
__metaclass__ = PageBase
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Page(six.with_metaclass(PageBase, MP_Node, ClusterableModel, Indexed)):
|
||||
title = models.CharField(max_length=255, help_text=_("The page title as you'd like it to be seen by the public"))
|
||||
slug = models.SlugField(help_text=_("The name of the page as it will appear in URLs e.g http://domain.com/blog/[my-slug]/"))
|
||||
# TODO: enforce uniqueness on slug field per parent (will have to be done at the Django
|
||||
@ -300,7 +304,7 @@ class Page(MP_Node, ClusterableModel, Indexed):
|
||||
# created as
|
||||
self.content_type = ContentType.objects.get_for_model(self)
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
is_abstract = True # don't offer Page in the list of page types a superuser can create
|
||||
@ -536,7 +540,7 @@ class Page(MP_Node, ClusterableModel, Indexed):
|
||||
else:
|
||||
res = []
|
||||
for page_type in cls.subpage_types:
|
||||
if isinstance(page_type, basestring):
|
||||
if isinstance(page_type, string_types):
|
||||
try:
|
||||
app_label, model_name = page_type.split(".")
|
||||
except ValueError:
|
||||
@ -770,6 +774,7 @@ class SubmittedRevisionsManager(models.Manager):
|
||||
return super(SubmittedRevisionsManager, self).get_queryset().filter(submitted_for_moderation=True)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class PageRevision(models.Model):
|
||||
page = models.ForeignKey('Page', related_name='revisions')
|
||||
submitted_for_moderation = models.BooleanField(default=False)
|
||||
@ -827,7 +832,7 @@ class PageRevision(models.Model):
|
||||
self.submitted_for_moderation = False
|
||||
page.revisions.update(submitted_for_moderation=False)
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return '"' + unicode(self.page) + '" at ' + unicode(self.created_at)
|
||||
|
||||
|
||||
|
@ -166,8 +166,8 @@ class DbWhitelister(Whitelister):
|
||||
def clean(cls, html):
|
||||
if not cls.has_loaded_custom_whitelist_rules:
|
||||
for fn in hooks.get_hooks('construct_whitelister_element_rules'):
|
||||
cls.element_rules = dict(
|
||||
cls.element_rules.items() + fn().items())
|
||||
cls.element_rules = cls.element_rules.copy()
|
||||
cls.element_rules.update(fn())
|
||||
cls.has_loaded_custom_whitelist_rules = True
|
||||
|
||||
return super(DbWhitelister, cls).clean(html)
|
||||
|
@ -1,6 +1,7 @@
|
||||
from StringIO import StringIO
|
||||
from datetime import timedelta
|
||||
|
||||
from six import StringIO
|
||||
|
||||
from django.test import TestCase, Client
|
||||
from django.http import HttpRequest, Http404
|
||||
from django.core import management
|
||||
|
@ -1,4 +1,4 @@
|
||||
from StringIO import StringIO
|
||||
from six import StringIO
|
||||
|
||||
from django.test import TestCase, Client
|
||||
from django.http import HttpRequest, Http404
|
||||
|
@ -1,4 +1,4 @@
|
||||
from StringIO import StringIO
|
||||
from six import StringIO
|
||||
|
||||
from django.test import TestCase, Client
|
||||
from django.http import HttpRequest, Http404
|
||||
|
@ -1,4 +1,4 @@
|
||||
from StringIO import StringIO
|
||||
from six import StringIO
|
||||
|
||||
from django.test import TestCase, Client
|
||||
from django.http import HttpRequest, Http404
|
||||
|
@ -2,8 +2,9 @@
|
||||
A generic HTML whitelisting engine, designed to accommodate subclassing to override
|
||||
specific rules.
|
||||
"""
|
||||
from six.moves.urllib.parse import urlparse
|
||||
|
||||
from bs4 import BeautifulSoup, NavigableString, Tag
|
||||
from urlparse import urlparse
|
||||
|
||||
|
||||
ALLOWED_URL_SCHEMES = ['', 'http', 'https', 'ftp', 'mailto', 'tel']
|
||||
@ -28,7 +29,7 @@ def attribute_rule(allowed_attrs):
|
||||
* if the lookup returns a truthy value, keep the attribute; if falsy, drop it
|
||||
"""
|
||||
def fn(tag):
|
||||
for attr, val in tag.attrs.items():
|
||||
for attr, val in list(tag.attrs.items()):
|
||||
rule = allowed_attrs.get(attr)
|
||||
if rule:
|
||||
if callable(rule):
|
||||
@ -82,7 +83,7 @@ class Whitelister(object):
|
||||
attributes"""
|
||||
doc = BeautifulSoup(html, 'lxml')
|
||||
cls.clean_node(doc, doc)
|
||||
return unicode(doc)
|
||||
return doc.decode()
|
||||
|
||||
@classmethod
|
||||
def clean_node(cls, doc, node):
|
||||
|
@ -9,10 +9,12 @@ from django.dispatch import Signal
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
|
||||
from wagtail.wagtailadmin.taggable import TagSearchable
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Document(models.Model, TagSearchable):
|
||||
title = models.CharField(max_length=255, verbose_name=_('Title'))
|
||||
file = models.FileField(upload_to='documents' , verbose_name=_('File'))
|
||||
@ -30,7 +32,7 @@ class Document(models.Model, TagSearchable):
|
||||
},
|
||||
}
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
@property
|
||||
|
@ -1,10 +1,14 @@
|
||||
from six import b
|
||||
|
||||
from django.test import TestCase
|
||||
from wagtail.wagtaildocs import models
|
||||
from wagtail.tests.utils import WagtailTestUtils
|
||||
|
||||
from django.contrib.auth.models import User, Group, Permission
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.core.files.base import ContentFile
|
||||
|
||||
from wagtail.wagtaildocs import models
|
||||
from wagtail.tests.utils import WagtailTestUtils
|
||||
|
||||
# TODO: Test serve view
|
||||
|
||||
|
||||
@ -112,7 +116,7 @@ class TestDocumentAddView(TestCase, WagtailTestUtils):
|
||||
|
||||
def test_post(self):
|
||||
# Build a fake file
|
||||
fake_file = ContentFile("A boring example document")
|
||||
fake_file = ContentFile(b("A boring example document"))
|
||||
fake_file.name = 'test.txt'
|
||||
|
||||
# Submit
|
||||
@ -134,7 +138,7 @@ class TestDocumentEditView(TestCase, WagtailTestUtils):
|
||||
self.login()
|
||||
|
||||
# Build a fake file
|
||||
fake_file = ContentFile("A boring example document")
|
||||
fake_file = ContentFile(b("A boring example document"))
|
||||
fake_file.name = 'test.txt'
|
||||
|
||||
# Create a document to edit
|
||||
@ -147,7 +151,7 @@ class TestDocumentEditView(TestCase, WagtailTestUtils):
|
||||
|
||||
def test_post(self):
|
||||
# Build a fake file
|
||||
fake_file = ContentFile("A boring example document")
|
||||
fake_file = ContentFile(b("A boring example document"))
|
||||
fake_file.name = 'test.txt'
|
||||
|
||||
# Submit title change
|
||||
@ -272,7 +276,7 @@ class TestDocumentChooserUploadView(TestCase, WagtailTestUtils):
|
||||
|
||||
def test_post(self):
|
||||
# Build a fake file
|
||||
fake_file = ContentFile("A boring example document")
|
||||
fake_file = ContentFile(b("A boring example document"))
|
||||
fake_file.name = 'test.txt'
|
||||
|
||||
# Submit
|
||||
|
@ -1,4 +1,6 @@
|
||||
import sys
|
||||
from datetime import datetime
|
||||
import json
|
||||
|
||||
try:
|
||||
from importlib import import_module
|
||||
@ -6,13 +8,20 @@ except ImportError:
|
||||
# for Python 2.6, fall back on django.utils.importlib (deprecated as of Django 1.7)
|
||||
from django.utils.importlib import import_module
|
||||
|
||||
|
||||
# Needs to be imported like this to allow @patch to work in tests
|
||||
from six.moves.urllib import request as urllib_request
|
||||
|
||||
from six.moves.urllib.request import Request
|
||||
from six.moves.urllib.error import URLError
|
||||
from six.moves.urllib.parse import urlencode
|
||||
|
||||
from django.conf import settings
|
||||
from datetime import datetime
|
||||
from django.utils import six
|
||||
|
||||
from wagtail.wagtailembeds.oembed_providers import get_oembed_provider
|
||||
from wagtail.wagtailembeds.models import Embed
|
||||
import urllib2, urllib
|
||||
import json
|
||||
|
||||
|
||||
|
||||
class EmbedNotFoundException(Exception): pass
|
||||
@ -99,11 +108,11 @@ def oembed(url, max_width=None):
|
||||
params['maxwidth'] = max_width
|
||||
|
||||
# Perform request
|
||||
request = urllib2.Request(provider + '?' + urllib.urlencode(params))
|
||||
request = Request(provider + '?' + urlencode(params))
|
||||
request.add_header('User-agent', 'Mozilla/5.0')
|
||||
try:
|
||||
r = urllib2.urlopen(request)
|
||||
except urllib2.URLError:
|
||||
r = urllib_request.urlopen(request)
|
||||
except URLError:
|
||||
raise EmbedNotFoundException
|
||||
oembed = json.loads(r.read())
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
from django.db import models
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
|
||||
|
||||
EMBED_TYPES = (
|
||||
@ -9,6 +10,7 @@ EMBED_TYPES = (
|
||||
)
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Embed(models.Model):
|
||||
url = models.URLField()
|
||||
max_width = models.SmallIntegerField(null=True, blank=True)
|
||||
@ -25,5 +27,5 @@ class Embed(models.Model):
|
||||
class Meta:
|
||||
unique_together = ('url', 'max_width')
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.url
|
||||
|
@ -1,5 +1,8 @@
|
||||
from six.moves.urllib.request import urlopen
|
||||
import six.moves.urllib.request
|
||||
from six.moves.urllib.error import URLError
|
||||
|
||||
from mock import patch
|
||||
import urllib2
|
||||
|
||||
try:
|
||||
import embedly
|
||||
@ -217,12 +220,12 @@ class TestOembed(TestCase):
|
||||
self.assertRaises(EmbedNotFoundException, wagtail_oembed, "foo")
|
||||
|
||||
def test_oembed_invalid_request(self):
|
||||
config = {'side_effect': urllib2.URLError('foo')}
|
||||
with patch.object(urllib2, 'urlopen', **config) as urlopen:
|
||||
config = {'side_effect': URLError('foo')}
|
||||
with patch.object(six.moves.urllib.request, 'urlopen', **config) as urlopen:
|
||||
self.assertRaises(EmbedNotFoundException, wagtail_oembed,
|
||||
"http://www.youtube.com/watch/")
|
||||
|
||||
@patch('urllib2.urlopen')
|
||||
@patch('six.moves.urllib.request.urlopen')
|
||||
@patch('json.loads')
|
||||
def test_oembed_photo_request(self, loads, urlopen) :
|
||||
urlopen.return_value = self.dummy_response
|
||||
@ -233,7 +236,7 @@ class TestOembed(TestCase):
|
||||
self.assertEqual(result['html'], '<img src="http://www.example.com" />')
|
||||
loads.assert_called_with("foo")
|
||||
|
||||
@patch('urllib2.urlopen')
|
||||
@patch('six.moves.urllib.request.urlopen')
|
||||
@patch('json.loads')
|
||||
def test_oembed_return_values(self, loads, urlopen):
|
||||
urlopen.return_value = self.dummy_response
|
||||
@ -268,7 +271,7 @@ class TestEmbedFilter(TestCase):
|
||||
return "foo"
|
||||
self.dummy_response = DummyResponse()
|
||||
|
||||
@patch('urllib2.urlopen')
|
||||
@patch('six.moves.urllib.request.urlopen')
|
||||
@patch('json.loads')
|
||||
def test_valid_embed(self, loads, urlopen):
|
||||
urlopen.return_value = self.dummy_response
|
||||
@ -277,7 +280,7 @@ class TestEmbedFilter(TestCase):
|
||||
result = embed_filter('http://www.youtube.com/watch/')
|
||||
self.assertEqual(result, '<img src="http://www.example.com" />')
|
||||
|
||||
@patch('urllib2.urlopen')
|
||||
@patch('six.moves.urllib.request.urlopen')
|
||||
@patch('json.loads')
|
||||
def test_render_filter(self, loads, urlopen):
|
||||
urlopen.return_value = self.dummy_response
|
||||
@ -288,7 +291,7 @@ class TestEmbedFilter(TestCase):
|
||||
result = temp.render(context)
|
||||
self.assertEqual(result, '<img src="http://www.example.com" />')
|
||||
|
||||
@patch('urllib2.urlopen')
|
||||
@patch('six.moves.urllib.request.urlopen')
|
||||
@patch('json.loads')
|
||||
def test_render_filter_nonexistent_type(self, loads, urlopen):
|
||||
urlopen.return_value = self.dummy_response
|
||||
@ -307,7 +310,7 @@ class TestEmbedlyFilter(TestEmbedFilter):
|
||||
return "foo"
|
||||
self.dummy_response = DummyResponse()
|
||||
|
||||
@patch('urllib2.urlopen')
|
||||
@patch('six.moves.urllib.request.urlopen')
|
||||
@patch('json.loads')
|
||||
def test_valid_embed(self, loads, urlopen):
|
||||
urlopen.return_value = self.dummy_response
|
||||
@ -316,7 +319,7 @@ class TestEmbedlyFilter(TestEmbedFilter):
|
||||
result = embedly_filter('http://www.youtube.com/watch/')
|
||||
self.assertEqual(result, '<img src="http://www.example.com" />')
|
||||
|
||||
@patch('urllib2.urlopen')
|
||||
@patch('six.moves.urllib.request.urlopen')
|
||||
@patch('json.loads')
|
||||
def test_render_filter(self, loads, urlopen):
|
||||
urlopen.return_value = self.dummy_response
|
||||
@ -327,7 +330,7 @@ class TestEmbedlyFilter(TestEmbedFilter):
|
||||
result = temp.render(context)
|
||||
self.assertEqual(result, '<img src="http://www.example.com" />')
|
||||
|
||||
@patch('urllib2.urlopen')
|
||||
@patch('six.moves.urllib.request.urlopen')
|
||||
@patch('json.loads')
|
||||
def test_render_filter_nonexistent_type(self, loads, urlopen):
|
||||
urlopen.return_value = self.dummy_response
|
||||
|
@ -1,11 +1,15 @@
|
||||
import json
|
||||
import re
|
||||
|
||||
from six import text_type
|
||||
|
||||
from unidecode import unidecode
|
||||
|
||||
from django.db import models
|
||||
from django.shortcuts import render
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.text import slugify
|
||||
|
||||
from unidecode import unidecode
|
||||
import json
|
||||
import re
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
|
||||
from wagtail.wagtailcore.models import Page, Orderable, UserPagePermissionsProxy, get_page_types
|
||||
from wagtail.wagtailadmin.edit_handlers import FieldPanel
|
||||
@ -32,6 +36,7 @@ FORM_FIELD_CHOICES = (
|
||||
HTML_EXTENSION_RE = re.compile(r"(.*)\.html")
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class FormSubmission(models.Model):
|
||||
"""Data for a Form submission."""
|
||||
|
||||
@ -43,7 +48,7 @@ class FormSubmission(models.Model):
|
||||
def get_data(self):
|
||||
return json.loads(self.form_data)
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.form_data
|
||||
|
||||
|
||||
@ -73,7 +78,7 @@ class AbstractFormField(Orderable):
|
||||
# unidecode will return an ascii string while slugify wants a
|
||||
# unicode string on the other hand, slugify returns a safe-string
|
||||
# which will be converted to a normal str
|
||||
return str(slugify(unicode(unidecode(self.label))))
|
||||
return str(slugify(text_type(unidecode(self.label))))
|
||||
|
||||
panels = [
|
||||
FieldPanel('label'),
|
||||
|
@ -258,5 +258,5 @@ class TestFormsSubmissions(TestCase):
|
||||
|
||||
# Check response
|
||||
self.assertEqual(response.status_code, 200)
|
||||
data_line = response.content.split("\n")[1]
|
||||
data_line = response.content.decode().split("\n")[1]
|
||||
self.assertTrue('new@example.com' in data_line)
|
||||
|
@ -1,5 +1,11 @@
|
||||
import datetime
|
||||
import unicodecsv
|
||||
|
||||
try:
|
||||
import unicodecsv as csv
|
||||
using_unicodecsv = True
|
||||
except ImportError:
|
||||
import csv
|
||||
using_unicodecsv = False
|
||||
|
||||
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||||
from django.core.exceptions import PermissionDenied
|
||||
@ -65,7 +71,11 @@ def list_submissions(request, page_id):
|
||||
# return a CSV instead
|
||||
response = HttpResponse(content_type='text/csv; charset=utf-8')
|
||||
response['Content-Disposition'] = 'attachment;filename=export.csv'
|
||||
writer = unicodecsv.writer(response, encoding='utf-8')
|
||||
|
||||
if using_unicodecsv:
|
||||
writer = csv.writer(response, encoding='utf-8')
|
||||
else:
|
||||
writer = csv.writer(response)
|
||||
|
||||
header_row = ['Submission date'] + [label for name, label in data_fields]
|
||||
|
||||
|
@ -8,11 +8,15 @@ except ImportError:
|
||||
# for Python 2.6, fall back on django.utils.importlib (deprecated as of Django 1.7)
|
||||
from django.utils.importlib import import_module
|
||||
|
||||
from django.utils import six
|
||||
import sys
|
||||
from django.conf import settings
|
||||
|
||||
from base import InvalidImageBackendError
|
||||
from django.utils import six
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
|
||||
|
||||
class InvalidImageBackendError(ImproperlyConfigured):
|
||||
pass
|
||||
|
||||
# Pinched from django 1.7 source code.
|
||||
# TODO: Replace this with "from django.utils.module_loading import import_string"
|
||||
|
@ -1,9 +1,4 @@
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
|
||||
|
||||
class InvalidImageBackendError(ImproperlyConfigured):
|
||||
pass
|
||||
|
||||
|
||||
class BaseImageBackend(object):
|
||||
|
@ -1,7 +1,8 @@
|
||||
import StringIO
|
||||
import os.path
|
||||
import re
|
||||
|
||||
from six import BytesIO
|
||||
|
||||
from taggit.managers import TaggableManager
|
||||
|
||||
from django.core.files import File
|
||||
@ -13,6 +14,7 @@ from django.utils.safestring import mark_safe
|
||||
from django.utils.html import escape, format_html_join
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
|
||||
from unidecode import unidecode
|
||||
|
||||
@ -21,6 +23,7 @@ from wagtail.wagtailimages.backends import get_image_backend
|
||||
from .utils import validate_image_format
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class AbstractImage(models.Model, TagSearchable):
|
||||
title = models.CharField(max_length=255, verbose_name=_('Title') )
|
||||
|
||||
@ -54,7 +57,7 @@ class AbstractImage(models.Model, TagSearchable):
|
||||
},
|
||||
}
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
def get_rendition(self, filter):
|
||||
@ -208,7 +211,7 @@ class Filter(models.Model):
|
||||
|
||||
image = method(image, self.method_arg)
|
||||
|
||||
output = StringIO.StringIO()
|
||||
output = BytesIO()
|
||||
backend.save_image(image, output, file_format)
|
||||
|
||||
# and then close the input file
|
||||
|
@ -18,11 +18,11 @@ from wagtail.wagtailimages.backends import get_image_backend
|
||||
from wagtail.wagtailimages.backends.pillow import PillowBackend
|
||||
|
||||
def get_test_image_file():
|
||||
from StringIO import StringIO
|
||||
from six import BytesIO
|
||||
from PIL import Image
|
||||
from django.core.files.images import ImageFile
|
||||
|
||||
f = StringIO()
|
||||
f = BytesIO()
|
||||
image = Image.new('RGB', (640, 480), 'white')
|
||||
image.save(f, 'PNG')
|
||||
return ImageFile(f, name='test.png')
|
||||
|
@ -1,6 +1,6 @@
|
||||
from django import http
|
||||
|
||||
import models
|
||||
from wagtail.wagtailredirects import models
|
||||
|
||||
|
||||
# Originally pinched from: https://github.com/django/django/blob/master/django/contrib/redirects/middleware.py
|
||||
|
@ -3,7 +3,7 @@ from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from wagtail.wagtailadmin.edit_handlers import FieldPanel, MultiFieldPanel, PageChooserPanel
|
||||
|
||||
from urlparse import urlparse
|
||||
from six.moves.urllib.parse import urlparse
|
||||
|
||||
|
||||
class Redirect(models.Model):
|
||||
|
@ -8,7 +8,7 @@ from django.views.decorators.vary import vary_on_headers
|
||||
from wagtail.wagtailadmin.edit_handlers import ObjectList
|
||||
from wagtail.wagtailadmin.forms import SearchForm
|
||||
|
||||
import models
|
||||
from wagtail.wagtailredirects import models
|
||||
|
||||
|
||||
REDIRECT_EDIT_HANDLER = ObjectList(models.Redirect.content_panels)
|
||||
|
@ -1,3 +1,3 @@
|
||||
from indexed import Indexed
|
||||
from signal_handlers import register_signal_handlers
|
||||
from backends import get_search_backend
|
||||
from wagtail.wagtailsearch.indexed import Indexed
|
||||
from wagtail.wagtailsearch.signal_handlers import register_signal_handlers
|
||||
from wagtail.wagtailsearch.backends import get_search_backend
|
@ -8,10 +8,15 @@ except ImportError:
|
||||
# for Python 2.6, fall back on django.utils.importlib (deprecated as of Django 1.7)
|
||||
from django.utils.importlib import import_module
|
||||
|
||||
from django.utils import six
|
||||
import sys
|
||||
|
||||
from django.utils import six
|
||||
from django.conf import settings
|
||||
from base import InvalidSearchBackendError
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
|
||||
|
||||
class InvalidSearchBackendError(ImproperlyConfigured):
|
||||
pass
|
||||
|
||||
|
||||
# Pinched from django 1.7 source code.
|
||||
|
@ -1,13 +1,8 @@
|
||||
from django.db import models
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
|
||||
from wagtail.wagtailsearch.indexed import Indexed
|
||||
|
||||
|
||||
class InvalidSearchBackendError(ImproperlyConfigured):
|
||||
pass
|
||||
|
||||
|
||||
class BaseSearch(object):
|
||||
def __init__(self, params):
|
||||
pass
|
||||
|
@ -338,10 +338,11 @@ class ElasticSearch(BaseSearch):
|
||||
indexed_fields = model.indexed_get_indexed_fields()
|
||||
|
||||
# Make field list
|
||||
fields = dict({
|
||||
fields = {
|
||||
"pk": dict(type="string", index="not_analyzed", store="yes"),
|
||||
"content_type": dict(type="string"),
|
||||
}.items() + indexed_fields.items())
|
||||
}
|
||||
fields.update(indexed_fields)
|
||||
|
||||
# Put mapping
|
||||
self.es.indices.put_mapping(index=self.es_index, doc_type=content_type, body={
|
||||
|
@ -1,7 +1,8 @@
|
||||
from django import forms
|
||||
from django.forms.models import inlineformset_factory
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
import models
|
||||
|
||||
from wagtail.wagtailsearch import models
|
||||
|
||||
|
||||
class QueryForm(forms.Form):
|
||||
@ -48,7 +49,7 @@ class EditorsPickFormSet(EditorsPickFormSetBase):
|
||||
# Check there is at least one non-deleted form.
|
||||
non_deleted_forms = self.total_form_count()
|
||||
non_empty_forms = 0
|
||||
for i in xrange(0, self.total_form_count()):
|
||||
for i in range(0, self.total_form_count()):
|
||||
form = self.forms[i]
|
||||
if self.can_delete and self._should_delete_form(form):
|
||||
non_deleted_forms -= 1
|
||||
|
@ -1,3 +1,5 @@
|
||||
from six import string_types
|
||||
|
||||
from django.db import models
|
||||
|
||||
|
||||
@ -35,21 +37,27 @@ class Indexed(object):
|
||||
def indexed_get_indexed_fields(cls):
|
||||
# Get indexed fields for this class as dictionary
|
||||
indexed_fields = cls.indexed_fields
|
||||
if isinstance(indexed_fields, tuple):
|
||||
indexed_fields = list(indexed_fields)
|
||||
if isinstance(indexed_fields, basestring):
|
||||
indexed_fields = [indexed_fields]
|
||||
if isinstance(indexed_fields, list):
|
||||
indexed_fields = dict((field, dict(type="string")) for field in indexed_fields)
|
||||
if not isinstance(indexed_fields, dict):
|
||||
raise ValueError()
|
||||
if isinstance(indexed_fields, dict):
|
||||
# Make sure we have a copy to prevent us accidentally changing the configuration
|
||||
indexed_fields = indexed_fields.copy()
|
||||
else:
|
||||
# Convert to dict
|
||||
if isinstance(indexed_fields, tuple):
|
||||
indexed_fields = list(indexed_fields)
|
||||
if isinstance(indexed_fields, string_types):
|
||||
indexed_fields = [indexed_fields]
|
||||
if isinstance(indexed_fields, list):
|
||||
indexed_fields = dict((field, dict(type="string")) for field in indexed_fields)
|
||||
if not isinstance(indexed_fields, dict):
|
||||
raise ValueError()
|
||||
|
||||
# Get indexed fields for parent class
|
||||
parent = cls.indexed_get_parent(require_model=False)
|
||||
if parent:
|
||||
# Add parent fields into this list
|
||||
parent_indexed_fields = parent.indexed_get_indexed_fields()
|
||||
indexed_fields = dict(parent_indexed_fields.items() + indexed_fields.items())
|
||||
parent_indexed_fields = parent.indexed_get_indexed_fields().copy()
|
||||
parent_indexed_fields.update(indexed_fields)
|
||||
indexed_fields = parent_indexed_fields
|
||||
return indexed_fields
|
||||
|
||||
def indexed_get_document_id(self):
|
||||
|
@ -2,11 +2,13 @@ import datetime
|
||||
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
|
||||
from wagtail.wagtailsearch.indexed import Indexed
|
||||
from wagtail.wagtailsearch.utils import normalise_query_string, MAX_QUERY_STRING_LENGTH
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class Query(models.Model):
|
||||
query_string = models.CharField(max_length=MAX_QUERY_STRING_LENGTH, unique=True)
|
||||
|
||||
@ -23,7 +25,7 @@ class Query(models.Model):
|
||||
daily_hits.hits = models.F('hits') + 1
|
||||
daily_hits.save()
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.query_string
|
||||
|
||||
@property
|
||||
|
@ -1,12 +1,14 @@
|
||||
from six import StringIO
|
||||
|
||||
from django.test import TestCase
|
||||
from django.test.utils import override_settings
|
||||
from django.conf import settings
|
||||
from django.core import management
|
||||
|
||||
from wagtail.tests.utils import unittest
|
||||
from wagtail.wagtailsearch import models, get_search_backend
|
||||
from wagtail.wagtailsearch.backends.db import DBSearch
|
||||
from wagtail.wagtailsearch.backends import InvalidSearchBackendError
|
||||
from StringIO import StringIO
|
||||
|
||||
|
||||
# Register wagtailsearch signal handlers
|
||||
@ -19,7 +21,7 @@ class BackendTests(object):
|
||||
|
||||
def setUp(self):
|
||||
# Search WAGTAILSEARCH_BACKENDS for an entry that uses the given backend path
|
||||
for (backend_name, backend_conf) in settings.WAGTAILSEARCH_BACKENDS.iteritems():
|
||||
for backend_name, backend_conf in settings.WAGTAILSEARCH_BACKENDS.items():
|
||||
if backend_conf['BACKEND'] == self.backend_path:
|
||||
self.backend = get_search_backend(backend_name)
|
||||
break
|
||||
|
@ -1,4 +1,4 @@
|
||||
from StringIO import StringIO
|
||||
from six import StringIO
|
||||
|
||||
from django.test import TestCase
|
||||
from django.core import management
|
||||
|
@ -1 +1 @@
|
||||
from frontend import search
|
||||
from wagtail.wagtailsearch.views.frontend import search
|
@ -1,5 +1,7 @@
|
||||
import json
|
||||
|
||||
from six import text_type
|
||||
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.contrib.auth.decorators import permission_required
|
||||
|
||||
@ -35,7 +37,7 @@ def chosen(request, content_type_app_name, content_type_model_name, id):
|
||||
|
||||
snippet_json = json.dumps({
|
||||
'id': item.id,
|
||||
'string': unicode(item),
|
||||
'string': text_type(item),
|
||||
})
|
||||
|
||||
return render_modal_workflow(
|
||||
|
@ -1,8 +1,10 @@
|
||||
from django.db import models
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
|
||||
|
||||
@python_2_unicode_compatible
|
||||
class UserProfile(models.Model):
|
||||
user = models.OneToOneField(settings.AUTH_USER_MODEL)
|
||||
|
||||
@ -25,5 +27,5 @@ class UserProfile(models.Model):
|
||||
def get_for_user(cls, user):
|
||||
return cls.objects.get_or_create(user=user)[0]
|
||||
|
||||
def __unicode__(self):
|
||||
def __str__(self):
|
||||
return self.user.username
|
||||
|
Loading…
Reference in New Issue
Block a user