Merge branch 'master' into feature/settings-menu

This commit is contained in:
Matt Westcott 2014-09-26 16:05:03 +01:00
commit a8a46b45f5
15 changed files with 71 additions and 205 deletions

View file

@ -8,6 +8,9 @@ Changelog
* Page copy operations now also copy the page revision history
* Page models now support a 'parent_page_types' property in addition to 'subpage types', to restrict the types of page they can be created under
* 'register_snippet' can now be invoked as a decorator
* Project template updated to Django 1.7
* Fix: 'wagtail start' command now works on Windows
* Fix: The external image URL generator no longer stores generated images in Django's cache
0.6 (11.09.2014)
~~~~~~~~~~~~~~~~

View file

@ -64,7 +64,6 @@ You will now be able to run the following command to set up an initial file stru
cd myprojectname
pip install -r requirements.txt
python manage.py syncdb
python manage.py migrate
python manage.py runserver

View file

@ -21,10 +21,14 @@ Minor features
* Page copy operations now also copy the page revision history.
* Page models now support a ``parent_page_types`` property in addition to ``subpage types``, to restrict the types of page they can be created under.
* ``register_snippet`` can now be invoked as a decorator.
* The project template (used when running ``wagtail start``) has been updated to Django 1.7.
Bug fixes
~~~~~~~~~
* The 'wagtail start' command now works on Windows and other environments where the ``django-admin.py`` executable is not readily accessible.
* The external image URL generator no longer stores generated images in Django's cache; this was an unintentional side-effect of setting cache control headers.
Upgrade considerations
======================

View file

@ -33,7 +33,7 @@ install_requires = [
"django-compressor>=1.4",
"django-libsass>=0.2",
"django-modelcluster>=0.4",
"django-taggit==0.12.1",
"django-taggit==0.12.2",
"django-treebeard==2.0",
"Pillow>=2.3.0",
"beautifulsoup4>=4.3.2",
@ -62,7 +62,7 @@ setup(
license='BSD',
long_description=open('README.rst').read(),
classifiers=[
'Development Status :: 4 - Beta',
'Development Status :: 5 - Production/Stable',
'Environment :: Web Environment',
'Intended Audience :: Developers',
'License :: OSI Approved :: BSD License',

View file

@ -2,11 +2,11 @@
from __future__ import print_function, absolute_import
import os
import subprocess
import errno
import sys
from optparse import OptionParser
from django.core.management import ManagementUtility
def create_project(parser, options, args):
@ -44,15 +44,15 @@ def create_project(parser, options, args):
template_path = os.path.join(wagtail_path, 'project_template')
# Call django-admin startproject
result = subprocess.call([
utility = ManagementUtility([
'django-admin.py', 'startproject',
'--template=' + template_path,
'--name=Vagrantfile', '--ext=html,rst',
project_name
])
utility.execute()
if result == 0:
print("Success! %(project_name)s is created" % {'project_name': project_name})
print("Success! %(project_name)s is created" % {'project_name': project_name})
COMMANDS = {

View file

@ -1,87 +1,24 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from south.utils import datetime_utils as datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
from django.db import models, migrations
class Migration(SchemaMigration):
class Migration(migrations.Migration):
def forwards(self, orm):
# Adding model 'HomePage'
db.create_table('core_homepage', (
('page_ptr', self.gf('django.db.models.fields.related.OneToOneField')(to=orm['wagtailcore.Page'], unique=True, primary_key=True)),
))
db.send_create_signal('core', ['HomePage'])
dependencies = [
('wagtailcore', '0002_initial_data'),
]
def backwards(self, orm):
# Deleting model 'HomePage'
db.delete_table('core_homepage')
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'user_set'", 'blank': 'True', 'to': "orm['auth.Group']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'user_set'", 'blank': 'True', 'to': "orm['auth.Permission']"}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'core.homepage': {
'Meta': {'object_name': 'HomePage', '_ormbases': ['wagtailcore.Page']},
'page_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['wagtailcore.Page']", 'unique': 'True', 'primary_key': 'True'})
},
'wagtailcore.page': {
'Meta': {'object_name': 'Page'},
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'pages'", 'to': "orm['contenttypes.ContentType']"}),
'depth': ('django.db.models.fields.PositiveIntegerField', [], {}),
'expire_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'expired': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'go_live_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'has_unpublished_changes': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'live': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'numchild': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'owned_pages'", 'null': 'True', 'to': "orm['auth.User']"}),
'path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
'search_description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'seo_title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'show_in_menus': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'url_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'})
}
}
complete_apps = ['core']
operations = [
migrations.CreateModel(
name='HomePage',
fields=[
('page_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='wagtailcore.Page')),
],
options={
'abstract': False,
},
bases=('wagtailcore.page',),
),
]

View file

@ -1,114 +1,45 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from south.utils import datetime_utils as datetime
from south.db import db
from south.v2 import DataMigration
from django.db import models, connection
from django.db.transaction import set_autocommit
from django.db import models, migrations
class Migration(DataMigration):
def create_homepage(apps, schema_editor):
# Get models
ContentType = apps.get_model('contenttypes.ContentType')
Page = apps.get_model('wagtailcore.Page')
Site = apps.get_model('wagtailcore.Site')
HomePage = apps.get_model('core.HomePage')
depends_on = (
('wagtailcore', '0002_initial_data'),
# Delete the default homepage
Page.objects.get(id=2).delete()
# Create content type for homepage model
homepage_content_type, created = ContentType.objects.get_or_create(
model='homepage', app_label='core', defaults={'name': 'Homepage'})
# Create a new homepage
homepage = HomePage.objects.create(
title="Homepage",
slug='home',
content_type=homepage_content_type,
path='00010001',
depth=2,
numchild=0,
url_path='/home/',
)
def forwards(self, orm):
if connection.vendor == 'sqlite':
set_autocommit(True)
# Create a site with the new homepage set as the root
Site.objects.create(
hostname='localhost', root_page=homepage, is_default_site=True)
orm['wagtailcore.Page'].objects.get(id=2).delete()
homepage_content_type, created = orm['contenttypes.contenttype'].objects.get_or_create(
model='homepage', app_label='core', defaults={'name': 'Homepage'})
class Migration(migrations.Migration):
homepage = orm['core.HomePage'].objects.create(
title="Homepage",
slug='home',
content_type=homepage_content_type,
path='00010001',
depth=2,
numchild=0,
url_path='/home/',
)
dependencies = [
('core', '0001_initial'),
]
orm['wagtailcore.site'].objects.create(
hostname='localhost', root_page=homepage, is_default_site=True)
def backwards(self, orm):
pass
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'user_set'", 'blank': 'True', 'to': "orm['auth.Group']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'user_set'", 'blank': 'True', 'to': "orm['auth.Permission']"}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'core.homepage': {
'Meta': {'object_name': 'HomePage', '_ormbases': ['wagtailcore.Page']},
'page_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['wagtailcore.Page']", 'unique': 'True', 'primary_key': 'True'})
},
'wagtailcore.page': {
'Meta': {'object_name': 'Page'},
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'pages'", 'to': "orm['contenttypes.ContentType']"}),
'depth': ('django.db.models.fields.PositiveIntegerField', [], {}),
'expire_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'expired': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'go_live_at': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}),
'has_unpublished_changes': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'live': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'numchild': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'owner': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'owned_pages'", 'null': 'True', 'to': "orm['auth.User']"}),
'path': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
'search_description': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'seo_title': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
'show_in_menus': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50'}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'url_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'})
},
'wagtailcore.site': {
'Meta': {'unique_together': "(('hostname', 'port'),)", 'object_name': 'Site'},
'hostname': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_default_site': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'port': ('django.db.models.fields.IntegerField', [], {'default': '80'}),
'root_page': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'sites_rooted_here'", 'to': "orm['wagtailcore.Page']"})
}
}
complete_apps = ['core']
symmetrical = True
operations = [
migrations.RunPython(create_homepage),
]

View file

@ -3,7 +3,6 @@
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />

View file

@ -5,7 +5,6 @@
<!--[if IE 7]> <html class="no-js lt-ie9 lt-ie8"> <![endif]-->
<!--[if IE 8]> <html class="no-js lt-ie9"> <![endif]-->
<!--[if gt IE 8]><!--> <html class="no-js"> <!--<![endif]-->
<html>
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />

View file

@ -38,7 +38,6 @@ INSTALLED_APPS = (
'django.contrib.messages',
'django.contrib.staticfiles',
'south',
'compressor',
'taggit',
'modelcluster',

View file

@ -1,6 +1,5 @@
# Minimal requirements
Django>=1.6.2,<1.7
South==1.0.0
Django>=1.7,<1.8
wagtail==0.6
# Recommended components (require additional setup):

View file

@ -22,8 +22,7 @@ chmod a+x $PROJECT_DIR/manage.py
# Run syncdb/migrate/update_index
su - vagrant -c "$PYTHON $PROJECT_DIR/manage.py syncdb --noinput && \
$PYTHON $PROJECT_DIR/manage.py migrate --noinput && \
su - vagrant -c "$PYTHON $PROJECT_DIR/manage.py migrate --noinput && \
$PYTHON $PROJECT_DIR/manage.py update_index"

View file

@ -122,7 +122,7 @@ function InlinePanel(opts) {
var self = {};
self.setHasContent = function(){
if($('li:visible', self.formsUl).length){
if($('> li', self.formsUl).not(".deleted").length){
self.formsUl.parent().removeClass('empty');
}else{
self.formsUl.parent().addClass('empty');
@ -139,7 +139,7 @@ function InlinePanel(opts) {
$('#' + deleteInputId + '-button').click(function() {
/* set 'deleted' form field to true */
$('#' + deleteInputId).val('1');
$('#' + childId).slideUp(function() {
$('#' + childId).addClass('deleted').slideUp(function() {
self.updateMoveButtonDisabledStates();
self.setHasContent();
});
@ -191,8 +191,8 @@ function InlinePanel(opts) {
/* Hide container on page load if it is marked as deleted. Remove the error
message so that it doesn't count towards the number of errors on the tab at the
top of the page. */
if ( $('#' + deleteInputId).val() === "1" ) {
$('#' + childId).hide(0, function() {
if ($('#' + deleteInputId).val() === "1" ) {
$('#' + childId).addClass('deleted').hide(0, function() {
self.updateMoveButtonDisabledStates();
self.setHasContent();
});

View file

@ -783,9 +783,6 @@ class TestFrontendServeView(TestCase):
self.assertEqual(response['Cache-Control'].split('=')[0], 'max-age')
self.assertTrue(int(response['Cache-Control'].split('=')[1]) > datetime.timedelta(days=30).seconds)
self.assertIn('Expires', response)
self.assertTrue(dateutil.parser.parse(response['Expires']) > timezone.now() + datetime.timedelta(days=30))
def test_get_invalid_signature(self):
"""
Test that an invalid signature returns a 403 response

View file

@ -1,13 +1,13 @@
from django.shortcuts import get_object_or_404
from django.http import HttpResponse
from django.core.exceptions import PermissionDenied
from django.views.decorators.cache import cache_page
from django.views.decorators.cache import cache_control
from wagtail.wagtailimages.models import get_image_model, Filter
from wagtail.wagtailimages.utils.crypto import verify_signature
@cache_page(60 * 60 * 24 * 60) # Cache for 60 days
@cache_control(max_age=60*60*24*60) # Cache for 60 days
def serve(request, signature, image_id, filter_spec):
image = get_object_or_404(get_image_model(), id=image_id)