mirror of
https://github.com/Hopiu/wagtail.git
synced 2026-05-12 09:13:14 +00:00
Merge branch 'feature/streamfield' into feature/streamfield-frontend
This commit is contained in:
commit
160f38bbbb
45 changed files with 453 additions and 170 deletions
|
|
@ -9,12 +9,14 @@ Changelog
|
|||
* Added thousands separator for counters on dashboard
|
||||
* Added contextual links to admin notification messages
|
||||
* When copying pages, it is now possible to specify a place to copy to (Timo Rieber)
|
||||
* FieldPanel now accepts an optional 'widget' parameter to override the field's default form widget (Alejandro Giacometti)
|
||||
|
||||
|
||||
0.8.5 (xx.xx.20xx)
|
||||
~~~~~~~~~~~~~~~~~~
|
||||
* Fix: On adding a new page, the available page types are ordered by the displayed verbose name
|
||||
* Fix: Active admin submenus were not properly closed when activating another
|
||||
* Fix: get_sitemap_urls is now called on the specific page class so it can now be overridden (Jerel Unruh)
|
||||
|
||||
|
||||
0.8.4 (04.12.2014)
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ Contributors
|
|||
* Robert Rollins
|
||||
* linibou
|
||||
* Timo Rieber
|
||||
* Jerel Unruh
|
||||
|
||||
Translators
|
||||
===========
|
||||
|
|
|
|||
|
|
@ -23,8 +23,10 @@ A "panel" is the basic editing block in Wagtail. Wagtail will automatically pick
|
|||
|
||||
There are four basic types of panels:
|
||||
|
||||
``FieldPanel( field_name, classname=None )``
|
||||
This is the panel used for basic Django field types. ``field_name`` is the name of the class property used in your model definition. ``classname`` is a string of optional CSS classes given to the panel which are used in formatting and scripted interactivity. By default, panels are formatted as inset fields. The CSS class ``full`` can be used to format the panel so it covers the full width of the Wagtail page editor. The CSS class ``title`` can be used to mark a field as the source for auto-generated slug strings.
|
||||
``FieldPanel( field_name, classname=None, widget=None )``
|
||||
This is the panel used for basic Django field types. ``field_name`` is the name of the class property used in your model definition. ``classname`` is a string of optional CSS classes given to the panel which are used in formatting and scripted interactivity. By default, panels are formatted as inset fields. The CSS class ``full`` can be used to format the panel so it covers the full width of the Wagtail page editor. The CSS class ``title`` can be used to mark a field as the source for auto-generated slug strings. The optional ``widget`` parameter allows you to specify a `django form widget`_ to use instead of the default widget for this field type.
|
||||
|
||||
.. _django form widget: https://docs.djangoproject.com/en/dev/ref/forms/widgets/
|
||||
|
||||
``MultiFieldPanel( children, heading="", classname=None )``
|
||||
This panel condenses several ``FieldPanel`` s or choosers, from a list or tuple, under a single ``heading`` string.
|
||||
|
|
|
|||
|
|
@ -15,3 +15,4 @@ Bug fixes
|
|||
|
||||
* On adding a new page, the available page types are ordered by the displayed verbose name
|
||||
* Active admin submenus were not properly closed when activating another
|
||||
* ``get_sitemap_urls`` is now called on the specific page class so it can now be overridden
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ Minor features
|
|||
* Added thousands separator for counters on dashboard
|
||||
* Added contextual links to admin notification messages
|
||||
* When copying pages, it is now possible to specify a place to copy to
|
||||
* ``FieldPanel`` now accepts an optional ``widget`` parameter to override the field's default form widget
|
||||
|
||||
|
||||
Bug fixes
|
||||
|
|
|
|||
1
setup.py
1
setup.py
|
|
@ -40,6 +40,7 @@ install_requires = [
|
|||
"Unidecode>=0.04.14",
|
||||
"six>=1.7.0",
|
||||
'requests>=2.0.0',
|
||||
"Willow==0.1",
|
||||
]
|
||||
|
||||
|
||||
|
|
|
|||
1
tox.ini
1
tox.ini
|
|
@ -17,6 +17,7 @@ base =
|
|||
python-dateutil==2.2
|
||||
pytz==2014.7
|
||||
Embedly
|
||||
Willow==0.1
|
||||
coverage
|
||||
|
||||
dj17 =
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ class Sitemap(object):
|
|||
|
||||
def get_urls(self):
|
||||
for page in self.get_pages():
|
||||
for url in page.get_sitemap_urls():
|
||||
for url in page.specific.get_sitemap_urls():
|
||||
yield url
|
||||
|
||||
def render(self):
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from django.test import TestCase
|
|||
from django.core.cache import cache
|
||||
|
||||
from wagtail.wagtailcore.models import Page, PageViewRestriction, Site
|
||||
from wagtail.tests.models import SimplePage
|
||||
from wagtail.tests.models import SimplePage, EventIndex
|
||||
|
||||
from .sitemap_generator import Sitemap
|
||||
|
||||
|
|
@ -47,6 +47,20 @@ class TestSitemapGenerator(TestCase):
|
|||
self.assertIn('http://localhost/', urls) # Homepage
|
||||
self.assertIn('http://localhost/hello-world/', urls) # Child page
|
||||
|
||||
def test_get_urls_uses_specific(self):
|
||||
# Add an event page which has an extra url in the sitemap
|
||||
events_page = self.home_page.add_child(instance=EventIndex(
|
||||
title="Events",
|
||||
slug='events',
|
||||
live=True,
|
||||
))
|
||||
|
||||
sitemap = Sitemap(self.site)
|
||||
urls = [url['location'] for url in sitemap.get_urls()]
|
||||
|
||||
self.assertIn('http://localhost/events/', urls) # Main view
|
||||
self.assertIn('http://localhost/events/past/', urls) # Sub view
|
||||
|
||||
def test_render(self):
|
||||
sitemap = Sitemap(self.site)
|
||||
xml = sitemap.render()
|
||||
|
|
|
|||
|
|
@ -230,6 +230,8 @@
|
|||
<section id="buttons">
|
||||
<h2>Buttons</h2>
|
||||
|
||||
<p>Buttons must have interaction possible (i.e be an input or button element) to get a suitable hover cursor</p>
|
||||
|
||||
<a href="#" class="button">button</a>
|
||||
|
||||
<a href="#" class="button button-secondary">button-secondary</a>
|
||||
|
|
@ -250,8 +252,6 @@
|
|||
|
||||
<div class="button no bicolor icon icon-cog">button on a div</div>
|
||||
|
||||
<p>Buttons must have interaction possible (i.e be an input or button element) to get a suitable hover cursor</p>
|
||||
|
||||
<button>button</button>
|
||||
|
||||
<button class="button-small">small button</button>
|
||||
|
|
@ -261,6 +261,14 @@
|
|||
<input type="submit" class="bicolor icon icon-plus" value="bicolor only supported on button elements" />
|
||||
|
||||
<button class="icon icon-view">button</button>
|
||||
|
||||
<button class="icon text-replace yes icon-tick">A proper button</button>
|
||||
<a href="#" class="button icon text-replace white icon-cog">A link button</a>
|
||||
<span class="button icon text-replace no icon-bin">A non-link button</span>
|
||||
|
||||
<button class="button-small icon text-replace white icon-tick">A proper button</button>
|
||||
<a href="#" class="button button-small icon text-replace white icon-cog">A link button</a>
|
||||
<span class="button button-small icon text-replace white icon-bin">A non-link button</span>
|
||||
</section>
|
||||
|
||||
<section id="dropdowns">
|
||||
|
|
|
|||
|
|
@ -306,6 +306,15 @@ class EventIndex(Page):
|
|||
for path in super(EventIndex, self).get_static_site_paths():
|
||||
yield path
|
||||
|
||||
def get_sitemap_urls(self):
|
||||
# Add past events url to sitemap
|
||||
return super(EventIndex, self).get_sitemap_urls() + [
|
||||
{
|
||||
'location': self.full_url + 'past/',
|
||||
'lastmod': self.latest_revision_created_at
|
||||
}
|
||||
]
|
||||
|
||||
EventIndex.content_panels = [
|
||||
FieldPanel('title', classname="full title"),
|
||||
FieldPanel('intro', classname="full"),
|
||||
|
|
|
|||
|
|
@ -336,10 +336,12 @@ class BaseStructBlock(Block):
|
|||
for child_rendering in child_renderings
|
||||
])
|
||||
|
||||
|
||||
# Can these be rendered with a template?
|
||||
if self.label:
|
||||
return format_html("<label>{0}</label> <ul>{1}</ul>", self.label, list_items)
|
||||
return format_html('<div class="struct-block"><label>{0}</label> <ul>{1}</ul></div>', self.label, list_items)
|
||||
else:
|
||||
return format_html("<ul>{0}</ul>", list_items)
|
||||
return format_html('<div class="struct-block"><ul>{0}</ul></div>', list_items)
|
||||
|
||||
def value_from_datadict(self, data, files, prefix):
|
||||
return dict([
|
||||
|
|
|
|||
|
|
@ -362,6 +362,15 @@ def MultiFieldPanel(children, heading="", classname=""):
|
|||
|
||||
|
||||
class BaseFieldPanel(EditHandler):
|
||||
|
||||
@classmethod
|
||||
def widget_overrides(cls):
|
||||
"""check if a specific widget has been defined for this field"""
|
||||
if hasattr(cls, 'widget'):
|
||||
return {cls.field_name: cls.widget}
|
||||
else:
|
||||
return {}
|
||||
|
||||
def __init__(self, instance=None, form=None):
|
||||
super(BaseFieldPanel, self).__init__(instance=instance, form=form)
|
||||
self.bound_field = self.form[self.field_name]
|
||||
|
|
@ -408,11 +417,16 @@ class BaseFieldPanel(EditHandler):
|
|||
return [self.field_name]
|
||||
|
||||
|
||||
def FieldPanel(field_name, classname=""):
|
||||
return type(str('_FieldPanel'), (BaseFieldPanel,), {
|
||||
def FieldPanel(field_name, classname="", widget=None):
|
||||
base = {
|
||||
'field_name': field_name,
|
||||
'classname': classname,
|
||||
})
|
||||
}
|
||||
|
||||
if widget:
|
||||
base['widget'] = widget
|
||||
|
||||
return type(str('_FieldPanel'), (BaseFieldPanel,), base)
|
||||
|
||||
|
||||
class BaseRichTextFieldPanel(BaseFieldPanel):
|
||||
|
|
@ -632,6 +646,20 @@ Page.settings_panels = [
|
|||
from wagtail.wagtailadmin.blocks import StreamBlock
|
||||
|
||||
class BaseStreamFieldPanel(BaseFieldPanel):
|
||||
|
||||
def classes(self):
|
||||
classes = super(BaseStreamFieldPanel, self).classes()
|
||||
classes.append("stream-field")
|
||||
|
||||
# BaseFieldPanel is essentially for single fields, which are rendered on the front end
|
||||
# with the assumption that the label (singular) will always be promoted to the full-width
|
||||
# divider bar thing.
|
||||
# This results in all the other labels being promoted similarly, so it's better not to
|
||||
# treat this as a single field, and remove the "single-field" class.
|
||||
classes.remove("single-field")
|
||||
|
||||
return classes
|
||||
|
||||
@classmethod
|
||||
def widget_overrides(cls):
|
||||
return {cls.field_name: widgets.StreamWidget(block_def=cls.block_def)}
|
||||
|
|
|
|||
|
|
@ -225,13 +225,16 @@ a.tag:hover{
|
|||
}
|
||||
}
|
||||
|
||||
hr{
|
||||
border:1px solid $color-grey-4;
|
||||
border-width:1px 0 0;
|
||||
margin:1.5em 0;
|
||||
}
|
||||
|
||||
/* general image style */
|
||||
img{
|
||||
max-width:100%;
|
||||
height:auto;
|
||||
border: 3px solid $color-grey-4;
|
||||
|
||||
}
|
||||
|
||||
/* make a block-level element inline */
|
||||
|
|
|
|||
|
|
@ -221,9 +221,16 @@ input[type=submit], input[type=reset], input[type=button], .button, button{
|
|||
background-color:transparent;
|
||||
}
|
||||
|
||||
&.icon.text-replace:before{
|
||||
font-size:auto;
|
||||
}
|
||||
/* Buttons which are only an icon */
|
||||
&.icon.text-replace{
|
||||
font-size:0; /* unavoidable duplication of setting in icons.scss */
|
||||
width:1.8rem;
|
||||
height:1.8rem;
|
||||
|
||||
&:before{
|
||||
line-height:1.7em;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover{
|
||||
background-color: $color-button-hover;
|
||||
|
|
@ -382,9 +389,7 @@ input[type=submit], input[type=reset], input[type=button], button{
|
|||
@include border-radius(2px);
|
||||
|
||||
li{
|
||||
background-color: $color-teal;
|
||||
float:left;
|
||||
cursor:pointer;
|
||||
margin-right:1px;
|
||||
|
||||
&:last-child{
|
||||
|
|
@ -392,24 +397,6 @@ input[type=submit], input[type=reset], input[type=button], button{
|
|||
}
|
||||
}
|
||||
|
||||
.icon{
|
||||
padding:0.3em;
|
||||
}
|
||||
|
||||
.icon:before{
|
||||
line-height:2em;
|
||||
width:2em;
|
||||
}
|
||||
.icon:hover{
|
||||
background-color:$color-teal-darker;
|
||||
|
||||
&:before{
|
||||
color:white;
|
||||
}
|
||||
}
|
||||
.icon-bin:hover{
|
||||
background-color:$color-red;
|
||||
}
|
||||
.disabled{
|
||||
display:none;
|
||||
}
|
||||
|
|
@ -803,8 +790,7 @@ input[type=submit], input[type=reset], input[type=button], .button, button{
|
|||
.boolean_field &,
|
||||
.choice_field &,
|
||||
.model_choice_field &,
|
||||
.image_field &,
|
||||
.file_field &{
|
||||
.image_field &{
|
||||
padding-top:0;
|
||||
}
|
||||
}
|
||||
|
|
@ -824,9 +810,23 @@ input[type=submit], input[type=reset], input[type=button], .button, button{
|
|||
height: 3em;
|
||||
line-height:3em;
|
||||
|
||||
&.icon.text-replace{
|
||||
width:2.2rem;
|
||||
height:2.2rem;
|
||||
&:before{
|
||||
line-height:2.1em;
|
||||
}
|
||||
}
|
||||
|
||||
&.button-small{
|
||||
height:2.3em;
|
||||
line-height:2.2em;
|
||||
&.icon.text-replace{
|
||||
height:1.8rem;
|
||||
width:1.8rem;
|
||||
|
||||
&:before{
|
||||
line-height:1.7em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.bicolor{
|
||||
|
|
@ -845,7 +845,8 @@ input[type=submit], input[type=reset], input[type=button], .button, button{
|
|||
&:before{
|
||||
line-height:1.85em;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Special styles to counteract Firefox's completely unwarranted assumptions about button styles */
|
||||
|
|
|
|||
|
|
@ -353,6 +353,11 @@ ul.listing{
|
|||
font-size:1em;
|
||||
opacity:0.7;
|
||||
}
|
||||
|
||||
&.images img{
|
||||
@include transition(border-color 0.2s ease);
|
||||
border: 3px solid transparent;
|
||||
}
|
||||
}
|
||||
ul.listing{
|
||||
border-top:1px dashed $color-input-border;
|
||||
|
|
|
|||
|
|
@ -66,6 +66,9 @@
|
|||
content:"w";
|
||||
margin-right:0.5em;
|
||||
font-size:1.2em;
|
||||
font-weight:normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -58,6 +58,22 @@ kbd{
|
|||
padding:0.3em 0.5em;
|
||||
}
|
||||
|
||||
dl, dt, dd{
|
||||
padding:0;
|
||||
margin:0;
|
||||
}
|
||||
dl{
|
||||
margin-top:1em;
|
||||
}
|
||||
dt{
|
||||
color:darken($color-grey-3, 5%);
|
||||
text-transform:uppercase;
|
||||
font-size:0.9em;
|
||||
}
|
||||
dd{
|
||||
margin-bottom:1em;
|
||||
}
|
||||
|
||||
/* Help text formatters */
|
||||
|
||||
.help-block{
|
||||
|
|
|
|||
|
|
@ -314,6 +314,13 @@ footer, .logo{
|
|||
@include column(12);
|
||||
}
|
||||
|
||||
.divider-before{
|
||||
border-left:1px solid $color-grey-4;
|
||||
}
|
||||
.divider-after{
|
||||
border-right:1px solid $color-grey-4;
|
||||
}
|
||||
|
||||
.row{
|
||||
@include row();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"metadata": {
|
||||
"name": "Wagtail 1",
|
||||
"lastOpened": 1410881728324,
|
||||
"name": "Wagtail",
|
||||
"lastOpened": 1420801397108,
|
||||
"created": 1405597423787
|
||||
},
|
||||
"iconSets": [
|
||||
|
|
@ -8564,20 +8564,23 @@
|
|||
"minorVersion": 0
|
||||
},
|
||||
"metrics": {
|
||||
"emSize": 512,
|
||||
"emSize": 1024,
|
||||
"baseline": 6.25,
|
||||
"whitespace": 50
|
||||
},
|
||||
"showMetrics": false,
|
||||
"showMetrics": true,
|
||||
"showMetadata": false,
|
||||
"showVersion": false
|
||||
"showVersion": false,
|
||||
"includeMetadata": false
|
||||
},
|
||||
"imagePref": {},
|
||||
"historySize": 100,
|
||||
"showCodes": true,
|
||||
"search": "",
|
||||
"gridSize": 16,
|
||||
"showGrid": true
|
||||
"showGrid": true,
|
||||
"showQuickUse2": true,
|
||||
"showSVGs": true
|
||||
},
|
||||
"externalSets": []
|
||||
}
|
||||
Binary file not shown.
File diff suppressed because one or more lines are too long
|
Before Width: | Height: | Size: 60 KiB After Width: | Height: | Size: 62 KiB |
Binary file not shown.
Binary file not shown.
|
|
@ -110,6 +110,8 @@
|
|||
margin:0;
|
||||
cursor:pointer;
|
||||
background-color:$color-salmon;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -184,6 +186,80 @@
|
|||
}
|
||||
}
|
||||
|
||||
&.stream-field {
|
||||
> fieldset{
|
||||
@include column(12);
|
||||
padding-left:0;
|
||||
padding-right:0;
|
||||
}
|
||||
|
||||
/* Object controls */
|
||||
.stream-controls{
|
||||
position:absolute;
|
||||
z-index:1;
|
||||
right:1em;
|
||||
top:1em;
|
||||
color:white;
|
||||
overflow:hidden;
|
||||
@include border-radius(2px);
|
||||
|
||||
li{
|
||||
background-color: $color-teal;
|
||||
float:left;
|
||||
cursor:pointer;
|
||||
margin-right:1px;
|
||||
|
||||
&:last-child{
|
||||
margin-right:0;
|
||||
}
|
||||
}
|
||||
|
||||
.disabled{
|
||||
display:none;
|
||||
}
|
||||
}
|
||||
|
||||
.fields > li > .field > label{
|
||||
display:none;
|
||||
}
|
||||
.sequence{
|
||||
@include clearfix;
|
||||
margin:1em 0;
|
||||
border:1px solid lighten($color-grey-4, 3%);
|
||||
padding:0 1.5em;
|
||||
}
|
||||
.sequence-member{
|
||||
position:relative;
|
||||
padding:1em 1.5em;
|
||||
border-bottom:1px solid lighten($color-grey-4, 3%);
|
||||
margin:0 -1.5em;
|
||||
|
||||
.inner > .struct-block > label{
|
||||
display:block;
|
||||
width:100%;
|
||||
float:none;
|
||||
}
|
||||
}
|
||||
.sequence-inner{
|
||||
@include column(10);
|
||||
|
||||
&:nth-of-type(1){
|
||||
@include column(12);
|
||||
padding:0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.struct-block > ul > li{ /* duplicates forms.scss ln.568 */
|
||||
@include clearfix();
|
||||
padding-top:0.5em;
|
||||
padding-bottom:1.2em;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
/* special panel for the publishing fields, requires a bit more pizzazz */
|
||||
&.publishing{
|
||||
h2:before{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{% load wagtailadmin_tags %}
|
||||
<div class="field {{ field|fieldtype }}">
|
||||
{{ field.label_tag }}
|
||||
{{ label_tag }}
|
||||
<div class="field-content">
|
||||
<div class="input">
|
||||
{{ widget }}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
{% extends "wagtailadmin/block_forms/sequence_member.html" %}
|
||||
{% block header_controls %}
|
||||
<button type="button" id="{{ prefix }}-delete">delete</button>
|
||||
<button type="button" id="{{ prefix }}-delete" class="icon text-replace no icon-bin">delete</button>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,15 @@
|
|||
{# Common HTML structure shared by list and stream blocks #}
|
||||
|
||||
{% if label %}<label>{{ label }}</label>{% endif %}
|
||||
<input type="hidden" name="{{ prefix }}-count" id="{{ prefix }}-count" value="{{ list_members_html|length }}">
|
||||
{% block header %}{% endblock %}
|
||||
<ul id="{{ prefix }}-list">
|
||||
{% for list_member_html in list_members_html %}
|
||||
{{ list_member_html }}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% block footer %}{% endblock %}
|
||||
<div class="sequence">
|
||||
<input type="hidden" name="{{ prefix }}-count" id="{{ prefix }}-count" value="{{ list_members_html|length }}">
|
||||
|
||||
{% if label %}<label>{{ label }}</label>{% endif %}
|
||||
{% block header %}{% endblock %}
|
||||
<div class="sequence-inner">
|
||||
<ul id="{{ prefix }}-list" class="sequence">
|
||||
{% for list_member_html in list_members_html %}
|
||||
{{ list_member_html }}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% block footer %}{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,8 +1,14 @@
|
|||
<li id="{{ prefix }}-container">
|
||||
<li id="{{ prefix }}-container" class="sequence-member">
|
||||
<input type="hidden" id="{{ prefix }}-deleted" name="{{ prefix }}-deleted" value="">
|
||||
<input type="hidden" id="{{ prefix }}-order" name="{{ prefix }}-order" value="{{ index }}">
|
||||
|
||||
{% block hidden_fields %}{% endblock %}
|
||||
{% block header_controls %}{% endblock %}
|
||||
<div>{{ child.render_form }}</div>
|
||||
|
||||
<div class="stream-controls">
|
||||
{% block header_controls %}{% endblock %}
|
||||
</div>
|
||||
|
||||
<div class="inner">{{ child.render_form }}</div>
|
||||
|
||||
{% block footer_controls %}{% endblock %}
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block header_controls %}
|
||||
<button type="button" id="{{ prefix }}-delete">delete</button>
|
||||
<button type="button" id="{{ prefix }}-delete" class="stream-control icon text-replace no icon-bin">delete</button>
|
||||
{% endblock %}
|
||||
|
||||
{% block footer_controls %}
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@
|
|||
<li id="inline_child_{{ child.form.prefix }}">
|
||||
<ul class="controls">
|
||||
{% if can_order %}
|
||||
<li class="icon text-replace white icon-order-up inline-child-move-up" id="{{ child.form.prefix }}-move-up">{% trans "Move up" %}</li>
|
||||
<li class="icon text-replace white icon-order-down inline-child-move-down" id="{{ child.form.prefix }}-move-down">{% trans "Move down" %}</li>
|
||||
<li><button class="icon text-replace white icon-order-up inline-child-move-up" id="{{ child.form.prefix }}-move-up" title="{% trans 'Move up' %}">{% trans "Move up" %}</button></li>
|
||||
<li><button class="icon text-replace white icon-order-down inline-child-move-down" id="{{ child.form.prefix }}-move-down" title="{% trans 'Move down' %}">{% trans "Move down" %}</button></li>
|
||||
{% endif %}
|
||||
<li class="icon text-replace white icon-bin" id="{{ child.form.DELETE.id_for_label }}-button">{% trans "Delete" %}</li>
|
||||
<li><button class="icon text-replace white icon-bin" id="{{ child.form.DELETE.id_for_label }}-button" title="{% trans 'Delete' %}">{% trans "Delete" %}</button></li>
|
||||
</ul>
|
||||
{{ child.render_form_content }}
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
{% load i18n %}
|
||||
|
||||
{% block titletag %}{% blocktrans with page_type=content_type.model_class.get_verbose_name %}New {{ page_type }}{% endblocktrans %}{% endblock %}
|
||||
{% block bodyclass %}menu-explorer page-editor create{% endblock %}
|
||||
{% block bodyclass %}menu-explorer page-editor create model-{{ content_type.model }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
|
|
@ -66,4 +66,10 @@
|
|||
{% endblock %}
|
||||
{% block extra_js %}
|
||||
{% include "wagtailadmin/pages/_editor_js.html" %}
|
||||
|
||||
<script>
|
||||
$(function(){
|
||||
$('#page-edit-form .tab-content section.active input').first().focus();
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
{% load wagtailadmin_tags %}
|
||||
{% load gravatar %}
|
||||
{% load i18n %}
|
||||
{% block titletag %}{% blocktrans with title=page.title %}Editing {{ title }}{% endblocktrans %}{% endblock %}
|
||||
{% block bodyclass %}menu-explorer page-editor{% endblock %}
|
||||
{% block titletag %}{% blocktrans with title=page.title page_type=content_type.model_class.get_verbose_name %}Editing {{ page_type }}: {{ title }}{% endblocktrans %}{% endblock %}
|
||||
{% block bodyclass %}menu-explorer page-editor model-{{ content_type.model }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% page_permissions page as page_perms %}
|
||||
|
|
@ -12,7 +12,7 @@
|
|||
|
||||
<div class="row row-flush">
|
||||
<div class="left col9">
|
||||
<h1 class="icon icon-doc-empty-inverse">{% blocktrans with title=page.title %}Editing <span>{{ title }}</span>{% endblocktrans %}</h1>
|
||||
<h1 class="icon icon-doc-empty-inverse">{% blocktrans with title=page.title page_type=content_type.model_class.get_verbose_name %}Editing {{ page_type }} <span>{{ title }}</span>{% endblocktrans %}</h1>
|
||||
</div>
|
||||
<div class="right col3">
|
||||
{% trans "Status" %}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
{% load wagtailadmin_tags %}
|
||||
<div class="field {{ field|fieldtype }} {{ field_classes }}">
|
||||
<div class="field {{ field|fieldtype }} {{ field|widgettype }} {{ field_classes }}">
|
||||
{{ field.label_tag }}
|
||||
<div class="field-content">
|
||||
<div class="input {{ input_classes }} ">
|
||||
|
|
|
|||
|
|
@ -65,6 +65,14 @@ def fieldtype(bound_field):
|
|||
return ""
|
||||
|
||||
|
||||
@register.filter
|
||||
def widgettype(bound_field):
|
||||
try:
|
||||
return camelcase_to_underscore(bound_field.field.widget.__class__.__name__)
|
||||
except AttributeError:
|
||||
return ""
|
||||
|
||||
|
||||
@register.filter
|
||||
def meta_description(model):
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -255,6 +255,8 @@ def edit(request, page_id):
|
|||
page = get_object_or_404(Page, id=page_id).get_latest_revision_as_page()
|
||||
parent = page.get_parent()
|
||||
|
||||
content_type = ContentType.objects.get_for_model(page)
|
||||
|
||||
page_perms = page.permissions_for_user(request.user)
|
||||
if not page_perms.can_edit():
|
||||
raise PermissionDenied
|
||||
|
|
@ -373,6 +375,7 @@ def edit(request, page_id):
|
|||
|
||||
return render(request, 'wagtailadmin/pages/edit.html', {
|
||||
'page': page,
|
||||
'content_type': content_type,
|
||||
'edit_handler': edit_handler,
|
||||
'errors_debug': errors_debug,
|
||||
'preview_modes': page.preview_modes,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{% extends "wagtailadmin/shared/field_as_li.html" %}
|
||||
{% extends "wagtailadmin/shared/field.html" %}
|
||||
{% load i18n %}
|
||||
{% block form_field %}
|
||||
<a href="{{ document.url }}" class="icon icon-doc-full-inverse">{{ document.file }}</a><br /><br />
|
||||
<a href="{{ document.url }}" class="icon icon-doc-full-inverse">{{ document.filename }}</a><br /><br />
|
||||
{% trans "Change document:" %}
|
||||
{{ field }}
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
{% load wagtailadmin_tags %}
|
||||
<li class="{% if field.field.required %}required{% endif %} {{ wrapper_classes }} {{ li_classes }} {% if field.errors %}error{% endif %}">
|
||||
{% include "wagtaildocs/documents/_file_field.html" %}
|
||||
</li>
|
||||
|
|
@ -15,20 +15,32 @@
|
|||
{% trans "Editing" as editing_str %}
|
||||
{% include "wagtailadmin/shared/header.html" with title=editing_str subtitle=document.title icon="doc-full-inverse" usage_object=document %}
|
||||
|
||||
<div class="nice-padding">
|
||||
<form action="{% url 'wagtaildocs_edit_document' document.id %}" method="POST" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
<ul class="fields">
|
||||
{% for field in form %}
|
||||
{% if field.name == 'file' %}
|
||||
{% include "wagtaildocs/documents/_file_field.html" %}
|
||||
{% else %}
|
||||
{% include "wagtailadmin/shared/field_as_li.html" %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<li><input type="submit" value="{% trans 'Save' %}" /> <a href="{% url 'wagtaildocs_delete_document' document.id %}" class="button button-secondary no">{% trans "Delete document" %}</a></li>
|
||||
</ul>
|
||||
</form>
|
||||
<div class="row row-flush nice-padding">
|
||||
|
||||
<div class="col10 divider-after">
|
||||
<form action="{% url 'wagtaildocs_edit_document' document.id %}" method="POST" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
<ul class="fields">
|
||||
{% for field in form %}
|
||||
{% if field.name == 'file' %}
|
||||
{% include "wagtaildocs/documents/_file_field_as_li.html" %}
|
||||
{% else %}
|
||||
{% include "wagtailadmin/shared/field_as_li.html" %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<li><input type="submit" value="{% trans 'Save' %}" /> <a href="{% url 'wagtaildocs_delete_document' document.id %}" class="button button-secondary no">{% trans "Delete document" %}</a></li>
|
||||
</ul>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col2">
|
||||
<dl>
|
||||
{% if document.file %}
|
||||
<dt>{% trans "Filesize" %}</dt>
|
||||
<dd>{{ document.file.size|filesizeformat }}</dd>
|
||||
{% endif %}
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import re
|
|||
from six import BytesIO, text_type
|
||||
|
||||
from taggit.managers import TaggableManager
|
||||
from willow.image import Image as WillowImage
|
||||
|
||||
from django.core.files import File
|
||||
from django.core.exceptions import ImproperlyConfigured, ObjectDoesNotExist
|
||||
|
|
@ -80,6 +81,19 @@ class AbstractImage(models.Model, TagSearchable):
|
|||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
def get_willow_image(self):
|
||||
try:
|
||||
image_file = self.file.file # triggers a call to self.storage.open, so IOErrors from missing files will be raised at this point
|
||||
except IOError as e:
|
||||
# re-throw this as a SourceImageIOError so that calling code can distinguish
|
||||
# these from IOErrors elsewhere in the process
|
||||
raise SourceImageIOError(text_type(e))
|
||||
|
||||
image_file.open('rb')
|
||||
image_file.seek(0)
|
||||
|
||||
return WillowImage.open(image_file)
|
||||
|
||||
def get_rect(self):
|
||||
return Rect(0, 0, self.width, self.height)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
{% extends "wagtailadmin/shared/field_as_li.html" %}
|
||||
{% load i18n %}
|
||||
{% extends "wagtailadmin/shared/field.html" %}
|
||||
{% load i18n wagtailimages_tags %}
|
||||
{% block form_field %}
|
||||
<span class="icon icon-image">{{ image.filename }}</span><br /><br />
|
||||
{% image image original as original_image %}
|
||||
|
||||
{% trans "Change image:" %}
|
||||
<a href="{{ original_image.url }}" class="icon icon-image">{{ image.filename }}</a> ({{ original_image.width }}x{{ original_image.height}})<br /><br />
|
||||
|
||||
{% trans "Change image file:" %}
|
||||
{{ field }}
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,4 @@
|
|||
{% load wagtailadmin_tags %}
|
||||
<li class="{% if field.field.required %}required{% endif %} {{ wrapper_classes }} {{ li_classes }} {% if field.errors %}error{% endif %}">
|
||||
{% include "wagtailimages/images/_file_field.html" %}
|
||||
</li>
|
||||
|
|
@ -28,14 +28,14 @@
|
|||
|
||||
<div class="row row-flush nice-padding">
|
||||
|
||||
<div class="col7">
|
||||
<div class="col5">
|
||||
<form action="{% url 'wagtailimages_edit_image' image.id %}" method="POST" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
<ul class="fields">
|
||||
{% for field in form %}
|
||||
|
||||
{% if field.name == 'file' %}
|
||||
{% include "wagtailimages/images/_file_field.html" %}
|
||||
{% include "wagtailimages/images/_file_field_as_li.html" %}
|
||||
{% elif field.is_hidden %}
|
||||
{{ field }}
|
||||
{% else %}
|
||||
|
|
@ -47,7 +47,7 @@
|
|||
</ul>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col5">
|
||||
<div class="col5 divider-after">
|
||||
<h2 class="label">{% trans "Focal point (optional)" %}</h2>
|
||||
<p>{% trans "To define this image's most important region, drag a box over the image below." %} {% if image.focal_point %}({% trans "Current focal point shown" %}){% endif %}</p>
|
||||
|
||||
|
|
@ -64,10 +64,22 @@
|
|||
|
||||
<div class="current-focal-point-indicator{% if not image.focal_point %} hidden{% endif %}"></div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<div class="col2 ">
|
||||
{% if url_generator_enabled %}
|
||||
<a href="{% url 'wagtailimages_url_generator' image.id %}" class="button bicolor icon icon-link">{% trans "URL Generator" %}</a>
|
||||
<hr />
|
||||
{% endif %}
|
||||
|
||||
{% image image original as original_image %}
|
||||
|
||||
<dl>
|
||||
<dt>{% trans "Max dimensions" %}</dt>
|
||||
<dd>{{ original_image.width }}x{{ original_image.height }}</dd>
|
||||
<dt>{% trans "Filesize" %}</dt>
|
||||
<dd>{{ image.file.size|filesizeformat }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import unittest
|
||||
from willow.image import Image as WillowImage
|
||||
|
||||
from django.test import TestCase
|
||||
from django.core.urlresolvers import reverse
|
||||
|
|
@ -13,7 +14,7 @@ from django.db import connection
|
|||
from wagtail.tests.utils import WagtailTestUtils, test_concurrently
|
||||
from wagtail.wagtailcore.models import Page
|
||||
from wagtail.tests.models import EventPage, EventPageCarouselItem
|
||||
from wagtail.wagtailimages.models import Rendition, Filter
|
||||
from wagtail.wagtailimages.models import Rendition, Filter, SourceImageIOError
|
||||
from wagtail.wagtailimages.backends import get_image_backend
|
||||
from wagtail.wagtailimages.backends.pillow import PillowBackend
|
||||
from wagtail.wagtailimages.rect import Rect
|
||||
|
|
@ -268,6 +269,29 @@ class TestGetUsage(TestCase):
|
|||
self.assertTrue(issubclass(Page, type(self.image.get_usage()[0])))
|
||||
|
||||
|
||||
class TestGetWillowImage(TestCase):
|
||||
fixtures = ['test.json']
|
||||
|
||||
def setUp(self):
|
||||
self.image = Image.objects.create(
|
||||
title="Test image",
|
||||
file=get_test_image_file(),
|
||||
)
|
||||
|
||||
def test_willow_image_object_returned(self):
|
||||
willow_image = self.image.get_willow_image()
|
||||
|
||||
self.assertIsInstance(willow_image, WillowImage)
|
||||
|
||||
def test_with_missing_image(self):
|
||||
# Image id=1 in test fixtures has a missing image file
|
||||
bad_image = Image.objects.get(id=1)
|
||||
|
||||
# Attempting to get the Willow image for images without files
|
||||
# should raise a SourceImageIOError
|
||||
self.assertRaises(SourceImageIOError, bad_image.get_willow_image)
|
||||
|
||||
|
||||
class TestIssue573(TestCase):
|
||||
"""
|
||||
This tests for a bug which causes filename limit on Renditions to be reached
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
{% load i18n %}
|
||||
<li id="inline_child_{{ form.prefix }}"{% if form.DELETE.value %} style="display: none;"{% endif %}>
|
||||
<ul class="controls">
|
||||
<li class="icon text-replace icon-order-up inline-child-move-up" id="{{ form.prefix }}-move-up">{% trans "Move up" %}</li>
|
||||
<li class="icon text-replace icon-order-down inline-child-move-down" id="{{ form.prefix }}-move-down">{% trans "Move down" %}</li>
|
||||
<li class="icon text-replace icon-bin" id="{{ form.DELETE.id_for_label }}-button">{% trans "Delete" %}</li>
|
||||
<li><button class="icon text-replace icon-order-up inline-child-move-up" id="{{ form.prefix }}-move-up">{% trans "Move up" %}</button></li>
|
||||
<li><button class="icon text-replace icon-order-down inline-child-move-down" id="{{ form.prefix }}-move-down">{% trans "Move down" %}</button></li>
|
||||
<li><button class="icon text-replace icon-bin" id="{{ form.DELETE.id_for_label }}-button">{% trans "Delete" %}</button></li>
|
||||
</ul>
|
||||
|
||||
<fieldset>
|
||||
|
|
|
|||
Loading…
Reference in a new issue