0001-review-backoffice-homepage-and-announce-management-1.patch
corbo/manage_urls.py | ||
---|---|---|
1 | 1 |
from django.conf.urls import patterns, include, url |
2 | 2 | |
3 | 3 |
from .views import add_announce, edit_announce, delete_announce, \ |
4 |
add_category, edit_category, delete_category, manage, menu_json |
|
4 |
add_category, edit_category, view_category, delete_category, manage, \ |
|
5 |
add_announce_for_category, menu_json |
|
5 | 6 | |
6 | 7 |
urlpatterns = patterns('', |
7 | 8 |
url(r'^$', manage, name='manage'), |
... | ... | |
10 | 11 |
name='edit_announce'), |
11 | 12 |
url(r'^announce/delete/(?P<pk>\d+)$', delete_announce, |
12 | 13 |
name='delete_announce'), |
14 |
url(r'^category/(?P<pk>\d+)/$', view_category, |
|
15 |
name='view_category'), |
|
16 |
url(r'^category/(?P<pk>\d+)/announce/$', add_announce_for_category, |
|
17 |
name='add_announce_for_category'), |
|
13 | 18 |
url(r'^category/add$', add_category, |
14 | 19 |
name='add_category'), |
15 | 20 |
url(r'^category/edit/(?P<pk>\d+)$', edit_category, |
corbo/models.py | ||
---|---|---|
31 | 31 |
def __unicode__(self): |
32 | 32 |
return self.name |
33 | 33 | |
34 |
def get_announces_number(self): |
|
35 |
return self.announce_set.all().count() |
|
36 | ||
37 |
def get_subscriptions_number(self): |
|
38 |
return self.subscription_set.all().count() |
|
39 | ||
34 | 40 | |
35 | 41 |
class Announce(models.Model): |
36 | 42 |
category = models.ForeignKey('Category', verbose_name=_('category')) |
corbo/static/css/corbo.css | ||
---|---|---|
9 | 9 | |
10 | 10 |
a:link, a:visited, a:hover { |
11 | 11 |
text-decoration: none; |
12 |
color: #000; |
|
13 | 12 |
} |
14 | 13 | |
15 | 14 |
header h1 { |
... | ... | |
154 | 153 |
#management ul li { |
155 | 154 |
padding: 5px; |
156 | 155 |
border: 1px solid #aaa; |
157 |
background: #eee; |
|
158 |
margin:2px 0; |
|
159 |
border-radius: .3em; |
|
160 | 156 |
position: relative; |
161 | 157 |
} |
162 | 158 | |
... | ... | |
164 | 160 |
margin: .4em 0; |
165 | 161 |
} |
166 | 162 | |
167 |
.title { |
|
168 |
font-size: .9em; |
|
169 |
font-weight: bold; |
|
170 |
} |
|
171 | ||
172 | 163 |
.preview { |
173 | 164 |
padding-left: 10px; |
174 | 165 |
font-size: .8em; |
... | ... | |
181 | 172 |
right: 5px; |
182 | 173 |
} |
183 | 174 | |
175 |
ul.objects-list.single-links li .actions a { |
|
176 |
color: #999; |
|
177 |
display: inline; |
|
178 |
padding: 0; |
|
179 |
} |
|
180 | ||
181 |
.actions a:hover { |
|
182 |
color: #333; |
|
183 |
} |
|
184 | ||
184 | 185 |
.status { |
185 | 186 |
position: absolute; |
186 | 187 |
padding: 2px; |
... | ... | |
200 | 201 | |
201 | 202 |
.status .icon { |
202 | 203 |
color: #888; |
204 |
font-size: 0.9em; |
|
203 | 205 |
} |
204 | 206 | |
205 | 207 |
.status .published:before { |
... | ... | |
220 | 222 |
} |
221 | 223 | |
222 | 224 |
#management .datetime { |
223 |
width: 15%; |
|
224 |
float: right; |
|
225 |
margin-right: 35px; |
|
225 |
position: absolute; |
|
226 |
top: 3px; |
|
227 |
right: 0; |
|
228 |
width: 25%; |
|
226 | 229 |
} |
227 | 230 | |
228 | 231 |
.empty { |
... | ... | |
295 | 298 |
clear: both; |
296 | 299 |
} |
297 | 300 | |
298 |
.subscriptions_number { |
|
299 |
font-size: 0.8em; |
|
301 |
.categories-listing { |
|
302 |
-webkit-column-width: 13em; |
|
303 |
-moz-column-width: 13em; |
|
304 |
column-width: 13em; |
|
305 |
} |
|
306 | ||
307 |
.categories-listing div.category { |
|
308 |
border-radius: 0.3em; |
|
309 |
display: table; |
|
310 |
min-width: 10em; |
|
311 |
position: relative; |
|
312 |
padding:6px; |
|
313 |
margin-bottom: 10px; |
|
314 |
background:#f6f6f6; |
|
315 |
color:#505050; |
|
316 |
border: 1px solid #e4e4e4; |
|
317 |
} |
|
318 | ||
319 |
div.title { |
|
320 |
line-height:1.5em; |
|
321 |
} |
|
322 | ||
323 |
div.category .creation_date, div.category .infos { |
|
324 |
font-size: 0.7em; |
|
325 |
} |
|
326 | ||
327 |
.category .infos { |
|
328 |
margin: 5px 0 0 0; |
|
300 | 329 |
float: right; |
301 |
} |
|
330 |
} |
corbo/templates/corbo/category_detail.html | ||
---|---|---|
1 |
{% extends 'corbo/manage.html' %} |
|
2 |
{% load i18n %} |
|
3 | ||
4 |
{% block appbar %} |
|
5 |
<h2>{% trans "Category:" %} {{ object.name }}</h2> |
|
6 |
<a href="{% url 'delete_category' object.id %}" rel="popup">{% trans 'Delete' %}</a> |
|
7 |
<a href="{% url 'edit_category' object.id %}" rel="popup">{% trans 'Rename' %}</a> |
|
8 |
<a href="{% url 'add_announce_for_category' pk=object.pk %}" rel='popup'>{% trans 'New announce' %}</a> |
|
9 |
{% endblock %} |
|
10 | ||
11 |
{% block content %} |
|
12 |
<div id="management"> |
|
13 |
<ul class='objects-list single-links'> |
|
14 |
{% for announce in announces %} |
|
15 |
<li> |
|
16 |
<div class="status"> |
|
17 |
{% if announce.is_published %} |
|
18 |
<span class="icon published" title="{% trans "published" %}"></span> |
|
19 |
{% endif %} |
|
20 |
{% if not announce.is_published %} |
|
21 |
{% if announce.is_expired %} |
|
22 |
<span class="icon expired" title="{% trans "expired" %}"></span> |
|
23 |
{% else %} |
|
24 |
<span class="icon unpublished" title="{% trans "not published yet" %}"></span> |
|
25 |
{% endif %} |
|
26 |
{% endif %} |
|
27 |
</div> |
|
28 |
<a href="{% url 'edit_announce' announce.id %}" rel='popup'>{{ announce.title }}</a> |
|
29 |
<div class="datetime"> |
|
30 |
{% blocktrans with mtime=announce.mtime|date:'DATETIME_FORMAT' %} |
|
31 |
Modified on {{ mtime }} |
|
32 |
{% endblocktrans %} |
|
33 |
</div> |
|
34 |
<div class="actions"> |
|
35 |
<span><a class="icon delete" href="{% url 'delete_announce' announce.id %}" rel="popup"></a></span> |
|
36 |
</div> |
|
37 |
</li> |
|
38 |
{% empty %} |
|
39 |
<div class="empty"> |
|
40 |
{% trans "No announces yet." %} |
|
41 |
</div> |
|
42 |
{% endfor %} |
|
43 |
</ul> |
|
44 |
{% endblock %} |
corbo/templates/corbo/manage.html | ||
---|---|---|
1 | 1 |
{% extends 'corbo/base.html' %} |
2 |
{% load i18n static %}
|
|
2 |
{% load i18n %} |
|
3 | 3 |
{% block page-title %} |
4 | 4 |
{{ block.super }} :: {% trans "Management" %} |
5 | 5 |
{% endblock %} |
6 | 6 | |
7 | ||
7 |
{% block appbar %} |
|
8 |
<h2>{% trans "Categories" %}</h2> |
|
9 |
<a href="{% url 'add_category' %}" rel='popup'>{% trans 'New category' %}</a> |
|
10 |
<a href="{% url 'add_announce' %}" rel='popup'>{% trans 'New announce' %}</a> |
|
11 |
{% endblock %} |
|
8 | 12 | |
9 | 13 |
{% block content %} |
10 |
<div class="categories"> |
|
11 |
<span class="title">{% trans "Category:" %}</span> |
|
12 |
<select> |
|
13 |
<option value=''>{% trans "All" %}</option> |
|
14 |
{% for category in categories %} |
|
15 |
<option value='{{ category.id }}'{% if category_id == category.id %} selected{% endif %}>{{ category }}</option> |
|
16 |
{% endfor %} |
|
17 |
</select> |
|
18 |
<span> |
|
19 |
{% if category_id %} |
|
20 |
<a class="icon edit" href="{% url "edit_category" category_id %}" rel="popup" title="{% trans "edit category" %}"></a> |
|
21 |
<a class="icon delete" href="{% url "delete_category" category_id %}" rel="popup" title="{% trans "delete category" %}"></a> |
|
22 |
{% endif %} |
|
23 |
<a class="icon add" href="{% url "add_category" %}" rel="popup" title="{% trans "add category" %}"></a></span> |
|
24 |
</div> |
|
25 | 14 |
<div id="management"> |
26 |
<h4>{% trans "Announces" %}</h4> |
|
27 |
<span><a class="icon add" href="{% url "add_announce" %}">{% trans "add announce" %}</a></span> |
|
28 |
<div> |
|
29 |
<span class="subscriptions_number"> |
|
30 |
{% blocktrans count n=subscriptions_number %} |
|
31 |
{{ n }} subscription |
|
32 |
{% plural %} |
|
33 |
{{ n }} subscriptions |
|
34 |
{% endblocktrans %} |
|
35 |
</span> |
|
36 |
</div> |
|
37 |
<div class="clear"></div> |
|
38 |
<ul> |
|
39 |
{% for obj in object_list %} |
|
40 |
<li class='{% cycle 'bluesky' '' %}'> |
|
41 |
<div class="status"> |
|
42 |
{% if obj.is_published %} |
|
43 |
<span class="icon published" title="{% trans "published" %}"></span> |
|
44 |
{% endif %} |
|
45 |
{% if not obj.is_published %} |
|
46 |
{% if obj.is_expired %} |
|
47 |
<span class="icon expired" title="{% trans "expired" %}"></span> |
|
48 |
{% else %} |
|
49 |
<span class="icon unpublished" title="{% trans "not published yet" %}"></span> |
|
50 |
{% endif %} |
|
51 |
{% endif %} |
|
52 |
</div> |
|
53 |
<div class="preview datetime"> |
|
54 |
{% blocktrans with mtime=obj.mtime|date:'DATETIME_FORMAT' %} |
|
55 |
Modified on {{ mtime }} |
|
56 |
{% endblocktrans %} |
|
57 |
</div> |
|
58 |
<div class="actions"> |
|
59 |
<span><a class="icon edit" href="{% url 'edit_announce' obj.id %}"></a></span> |
|
60 |
<span><a class="icon delete" href="{% url 'delete_announce' obj.id %}" rel="popup"></a></span> |
|
61 |
</div> |
|
62 |
<div class="announce"> |
|
63 |
<div class="title">{{ obj.title }}</div> |
|
64 |
<div class="preview">{{ obj.text|safe|truncatechars_html:128 }}</div> |
|
65 |
</div> |
|
66 |
</li> |
|
67 |
{% empty %} |
|
68 |
<div class="empty"> |
|
69 |
{% trans "No announces matching this category" %} |
|
70 |
</div> |
|
71 |
{% endfor %} |
|
72 |
</ul> |
|
73 | ||
74 |
{% if is_paginated %} |
|
75 |
<ul class="pagination"> |
|
76 |
{% if page_obj.has_previous %} |
|
77 |
<li class="prev"> |
|
78 |
<a href="?{% if category_id %}category={{ category_id}}&{% endif %}page={{ page_obj.previous_page_number }}" class="icon"></a> |
|
79 |
</li> |
|
80 |
{% endif %} |
|
81 |
<li class="current"> |
|
82 |
page {{ page_obj.number }} of {{ paginator.num_pages }} |
|
83 |
</li> |
|
84 |
{% if page_obj.has_next %} |
|
85 |
<li class="next"> |
|
86 |
<a href="?{% if category_id %}category={{ category_id}}&{% endif %}page={{ page_obj.next_page_number }}" class="icon"></a> |
|
87 |
</li> |
|
88 |
{% endif %} |
|
89 |
</ul> |
|
90 |
{% endif %} |
|
15 |
<ul class='objects-list single-links'> |
|
16 |
{% for obj in object_list %} |
|
17 |
<li class='category'> |
|
18 |
<a href="{% url 'view_category' pk=obj.id %}">{{ obj.name }} / |
|
19 |
{% blocktrans with creation_date=obj.ctime|date:"SHORT_DATETIME_FORMAT" %} |
|
20 |
created on {{ creation_date }} |
|
21 |
{% endblocktrans %} / |
|
22 |
{% blocktrans count announces_number=obj.get_announces_number %} |
|
23 |
{{ announces_number }} announce |
|
24 |
{% plural %} |
|
25 |
{{ announces_number }} announces |
|
26 |
{% endblocktrans %} / |
|
27 |
{% blocktrans count subscriptions_number=obj.get_subscriptions_number %} |
|
28 |
{{ subscriptions_number }} subscription |
|
29 |
{% plural %} |
|
30 |
{{ subscriptions_number }} subscriptions |
|
31 |
{% endblocktrans %} |
|
32 |
</a> |
|
33 |
<div class="actions"> |
|
34 |
<a class="icon edit" href="{% url 'edit_category' obj.id %}" rel="popup"></a> |
|
35 |
<a class="icon delete" href="{% url 'delete_category' obj.id %}" rel="popup"></a> |
|
36 |
</div> |
|
37 |
</li> |
|
38 |
{% empty %} |
|
39 |
<div class="empty"> |
|
40 |
{% trans "No categories yet." %} |
|
41 |
</div> |
|
42 |
{% endfor %} |
|
43 |
</ul> |
|
91 | 44 |
</div> |
92 |
{% endblock %} |
|
93 | ||
94 |
{% block page-end %} |
|
95 |
<script type="text/javascript"> |
|
96 |
$(function() { |
|
97 |
$('div.categories select').on('change', function() { |
|
98 |
var category = $(this).val(); |
|
99 |
if (category) |
|
100 |
window.location.replace('{% url "manage" %}?category=' + category); |
|
101 |
else |
|
102 |
window.location.replace({% url "manage" %}); |
|
103 |
}) |
|
104 |
}) |
|
105 |
</script> |
|
106 |
{% endblock %} |
|
45 |
{% endblock %} |
corbo/views.py | ||
---|---|---|
6 | 6 |
from django.core import signing |
7 | 7 |
from django.core.urlresolvers import reverse |
8 | 8 |
from django.views.generic import CreateView, UpdateView, DeleteView, \ |
9 |
ListView, TemplateView, RedirectView |
|
9 |
ListView, TemplateView, RedirectView, \ |
|
10 |
DetailView |
|
10 | 11 |
from django.contrib.syndication.views import Feed |
11 | 12 |
from django.shortcuts import resolve_url |
12 | 13 |
from django.utils.encoding import force_text |
... | ... | |
54 | 55 |
template_name = 'corbo/announce_form.html' |
55 | 56 | |
56 | 57 |
def get_success_url(self): |
57 |
""" |
|
58 |
redirect to the category page of the new created announce |
|
59 |
""" |
|
60 |
return reverse('manage') + '?' + urlencode({'category': self.object.category.id}) |
|
58 |
return reverse('view_category', kwargs={'pk': self.object.category.pk}) |
|
61 | 59 | |
62 | 60 |
add_announce = AnnounceCreateView.as_view() |
63 | 61 | |
62 | ||
63 |
class AnnounceForCategoryCreateView(AnnounceCreateView): |
|
64 | ||
65 |
def get_initial(self): |
|
66 |
initial = super(AnnounceForCategoryCreateView, self).get_initial() |
|
67 |
initial['category'] = models.Category.objects.get(pk=self.kwargs['pk']) |
|
68 |
return initial |
|
69 | ||
70 |
add_announce_for_category = AnnounceForCategoryCreateView.as_view() |
|
71 | ||
72 | ||
64 | 73 |
class AnnounceEditView(UpdateView): |
65 | 74 |
model = models.Announce |
66 | 75 |
form_class = AnnounceForm |
67 | 76 | |
68 | 77 |
def get_success_url(self): |
69 |
return reverse('manage') + '?' + urlencode({'category': self.object.category.id})
|
|
78 |
return reverse('view_category', kwargs={'pk': self.object.category.pk})
|
|
70 | 79 | |
71 | 80 |
edit_announce = AnnounceEditView.as_view() |
72 | 81 | |
... | ... | |
92 | 101 | |
93 | 102 |
add_category = CategoryCreateView.as_view() |
94 | 103 | |
104 | ||
95 | 105 |
class CategoryEditView(UpdateView): |
96 | 106 |
form_class = CategoryForm |
97 | 107 |
model = models.Category |
98 | 108 | |
99 | 109 |
def get_success_url(self): |
100 |
return self.request.META['HTTP_REFERER'] or \ |
|
101 |
reverse('manage') + '?' + urlencode({'category': self.object.id}) |
|
110 |
return reverse('view_category', kwargs={'pk': self.object.pk}) |
|
102 | 111 | |
103 | 112 |
edit_category = CategoryEditView.as_view() |
104 | 113 | |
114 | ||
115 |
class CategoryView(DetailView): |
|
116 |
model = models.Category |
|
117 | ||
118 |
def get_context_data(self, **kwargs): |
|
119 |
context = super(CategoryView, self).get_context_data(**kwargs) |
|
120 |
context['announces'] = self.object.announce_set.all() |
|
121 |
return context |
|
122 | ||
123 |
view_category = CategoryView.as_view() |
|
124 | ||
125 | ||
105 | 126 |
class CategoryDeleteView(DeleteView): |
106 | 127 |
model = models.Category |
107 | 128 | |
... | ... | |
135 | 156 | |
136 | 157 | |
137 | 158 |
class ManageView(ListView): |
138 |
paginate_by = settings.ANNOUNCES_PER_PAGE |
|
139 | 159 |
template_name = 'corbo/manage.html' |
140 |
model = models.Announce |
|
141 | ||
142 |
def get_queryset(self): |
|
143 |
queryset = super(ManageView, self).get_queryset() |
|
144 |
if self.request.GET.get('category'): |
|
145 |
queryset = queryset.filter(category__id=self.request.GET['category']) |
|
146 |
return queryset |
|
147 | ||
148 |
def get_context_data(self, **kwargs): |
|
149 |
context = super(ManageView, self).get_context_data(**kwargs) |
|
150 |
context['categories'] = models.Category.objects.all().order_by('-ctime') |
|
151 |
context['subscriptions_number'] = models.Subscription.objects.count() |
|
152 |
if self.request.GET.get('category'): |
|
153 |
try: |
|
154 |
context['category_id'] = int(self.request.GET['category']) |
|
155 |
cat = models.Category.objects.get(pk=context['category_id']) |
|
156 |
context['subscriptions_number'] = cat.subscription_set.count() |
|
157 |
except (ValueError, models.Category.DoesNotExist): |
|
158 |
pass |
|
159 |
return context |
|
160 |
model = models.Category |
|
160 | 161 | |
161 | 162 |
manage = ManageView.as_view() |
162 | 163 | |
164 | ||
163 | 165 |
class Atom1Feed(DjangoAtom1Feed): |
164 | 166 |
def root_attributes(self): |
165 | 167 |
attrs = super(Atom1Feed, self).root_attributes() |
166 |
- |