Projet

Général

Profil

0001-review-backoffice-homepage-and-announce-management-1.patch

Serghei Mihai (congés, retour 15/05), 19 septembre 2016 18:08

Télécharger (19,9 ko)

Voir les différences:

Subject: [PATCH] review backoffice homepage and announce management (#12908)

 corbo/forms.py                                     |   3 +-
 corbo/manage_urls.py                               |   8 +-
 corbo/models.py                                    |   8 +-
 corbo/static/css/corbo.css                         | 167 ++-------------------
 corbo/templates/corbo/announce_confirm_delete.html |   2 +
 corbo/templates/corbo/announce_form.html           |  15 +-
 corbo/templates/corbo/category_confirm_delete.html |   2 +
 corbo/templates/corbo/category_detail.html         |  44 ++++++
 corbo/templates/corbo/category_form.html           |   1 +
 corbo/templates/corbo/manage.html                  | 120 ++++-----------
 corbo/views.py                                     |  68 +++++----
 11 files changed, 158 insertions(+), 280 deletions(-)
 create mode 100644 corbo/templates/corbo/category_detail.html
corbo/forms.py
13 13
            'publication_time': forms.TextInput(attrs={'class': 'datetimepicker',
14 14
                                                       'readonly': True}),
15 15
            'expiration_time': forms.TextInput(attrs={'class': 'datetimepicker',
16
                                                      'readonly': True})
16
                                                      'readonly': True}),
17
            'category': forms.HiddenInput()
17 18
        }
18 19

  
19 20
    def save(self, *args, **kwargs):
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
    menu_json
5 6

  
6 7
urlpatterns = patterns('',
7 8
            url(r'^$', manage, name='manage'),
8
            url(r'^announce/add$', add_announce, name='add_announce'),
9
            url(r'^category/(?P<pk>\d+)/announce/$', add_announce,
10
                name='add_announce'),
9 11
            url(r'^announce/edit/(?P<pk>\d+)$', edit_announce,
10 12
                name='edit_announce'),
11 13
            url(r'^announce/delete/(?P<pk>\d+)$', delete_announce,
12 14
                name='delete_announce'),
15
            url(r'^category/(?P<pk>\d+)/$', view_category,
16
                name='view_category'),
13 17
            url(r'^category/add$', add_category,
14 18
                name='add_category'),
15 19
            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_count(self):
35
        return self.announce_set.all().count()
36

  
37
    def get_subscriptions_count(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'))
......
54 60
        return False
55 61

  
56 62
    def is_published(self):
57
        if self.is_expired():
58
            return False
59 63
        if self.publication_time:
60 64
            return self.publication_time <= timezone.now()
61 65
        return False
corbo/static/css/corbo.css
1
.icon:before, .icon:after {
2
    font-family: FontAwesome;
3
}
4

  
5

  
6 1
body {
7 2
    margin: 0;
8 3
}
9 4

  
10 5
a:link, a:visited, a:hover {
11 6
    text-decoration: none;
12
    color: #000;
13 7
}
14 8

  
15 9
header h1 {
......
25 19
    text-align: center;
26 20
}
27 21

  
28
div#content {
29
    padding: 0 10px;
30
}
31

  
32
ul.announces {
33
    margin: 0;
34
    padding: 0;
35
}
36

  
37
ul.announces li, ul.auth li, #management ul li {
22
ul.auth li {
38 23
    list-style-type: none;
39 24
    padding: .1em .5em;
40 25
}
41 26

  
42
li.bright {
43
    background: #fff;
44
}
45

  
46
li.bluesky {
47
    background: #d3d8e8 !important;
48
}
49

  
50
div.pub_time {
51
    float: right;
52
    font: bold Arial .8em;
53
}
54

  
55 27
div.clear {
56 28
    clear: both;
57 29
}
58 30

  
59
ul.pagination {
60
    font-size: .8em;
61
    margin: 0;
62
    padding: 0;
63
}
64

  
65
#management ul.pagination li {
66
    display: inline;
67
    margin: 0 2px;
68
}
69

  
70
ul.pagination li.current {
71
    border: 0;
72
}
73

  
74
ul.pagination a {
75
    color: #000;
76
}
77

  
78
ul.pagination li.next a:before {
79
    content: '\f105';
80
}
81

  
82
ul.pagination li.prev a:before {
83
    content: '\f104';
84
}
85

  
86 31
ul.auth {
87 32
    border-radius: .2em;
88 33
    -moz-border-radius: .2em;
......
142 87
    margin-left: .4em;
143 88
}
144 89

  
145
#management {
146
    margin: 1em 0 .5em 0;
147
}
148

  
149
#management ul {
150
    margin: 1em 0 0 0;
90
ul.objects-list.single-links li .actions a {
91
    color: #999;
92
    display: inline;
151 93
    padding: 0;
152 94
}
153 95

  
154
#management ul li {
155
    padding: 5px;
156
    border: 1px solid #aaa;
157
    background: #eee;
158
    margin:2px 0;
159
    border-radius: .3em;
160
    position: relative;
161
}
162

  
163
#management h4 {
164
    margin: .4em 0;
165
}
166

  
167
.title {
168
    font-size: .9em;
169
    font-weight: bold;
170
}
171

  
172
.preview {
173
    padding-left: 10px;
174
    font-size: .8em;
175
    color: #777;
176
}
177

  
178
.actions {
179
    position: absolute;
180
    top: 2px;
181
    right: 5px;
96
.actions a:hover {
97
    color: #333;
182 98
}
183 99

  
184 100
.status {
......
190 106
    width: 20px;
191 107
}
192 108

  
193
.edit:before {
194
    content: '\f044';
195
}
196

  
197
.delete:before {
198
    content: '\f1f8';
199
}
200

  
201
.status .icon {
202
    color: #888;
203
}
204

  
205
.status .published:before {
206
    content: '\f1ea';
207
}
208

  
209
.status .unpublished:before {
210
    content: '\f06a';
211
}
212

  
213
.status .expired:before {
214
    content: '\f0f6';
109
li.unpublished {
110
    background: #f8f8fe;
215 111
}
216 112

  
217 113
.announce {
......
220 116
}
221 117

  
222 118
#management .datetime {
223
    width: 15%;
224
    float: right;
225
    margin-right: 35px;
119
    position: absolute;
120
    top: 2px;
121
    right: 2px;
226 122
}
227 123

  
228 124
.empty {
......
232 128
    margin: 0 auto;
233 129
}
234 130

  
235
.categories {
236
    float: right;
237
}
238

  
239
.categories select {
240
    border: 1px solid #aaa;
241
    display: inline;
242
}
243

  
244
.categories a, #management > span a {
245
    color: #000;
246
    padding: 1px 3px;
247
    margin: 0;
248
    font-weight: bold;
249
    font-size: .75em;
250
}
251

  
252
a.add:before {
253
    content: '\f067';
254
    padding-right:3px;
255
}
256

  
257 131
form ul li {
258 132
    list-style-type: none;
259 133
    margin: 5px 0;
260 134
}
261 135

  
262
li.datetime:after {
263
    margin-left: 5px;
264
    font-family: FontAwesome;
265
    content: '\f073';
266
    color: #555;
267
}
268

  
269 136
form ul li label {
270 137
    display: block;
271 138
}
272 139

  
273
#id_transport_channel li, #id_transport_channel label {
274
    display: inline;
275
}
276

  
277 140
.content {
278 141
    width: 50%;
279 142
    margin: auto;
......
283 146
    background: #e6db74;
284 147
    border: 1px solid #aaa;
285 148
    padding: 5px;
286
}
287

  
288
.info {
289
    background: #a6e22e;
290
    border: 1px solid #aaa;
291
    padding: 5px;
292
}
149
}
corbo/templates/corbo/announce_confirm_delete.html
1 1
{% extends "corbo/manage.html" %}
2 2
{% load i18n %}
3
{% block appbar %}
4
{% endblock %}
3 5
{% block content %}
4 6
<form method="post">
5 7
  <p>
corbo/templates/corbo/announce_form.html
1 1
{% extends "corbo/manage.html" %}
2 2
{% load i18n %}
3 3

  
4
{% block breadcrumb %}
5
{{ block.super }}
6
{% if category %}
7
<a href='{% url "view_category" pk=category.pk %}'>{{ category }}</a>
8
{% endif %}
9
{% endblock %}
10

  
4 11
{% block appbar %}
5 12
{% if object %}
6
  <h2>{% trans "Modify Announce" %}</h2>
13
<h2>{% trans "Edit Announce" %}</h2>
14
<a href='{% url "delete_announce" pk=object.pk %}' rel='popup'>{% trans 'Delete' %}</a>
7 15
{% else %}
8
  <h2>{% trans "New Announce" %}</h2>
16
<h2>{% trans "New Announce" %}</h2>
9 17
{% endif %}
18
</h2>
19

  
10 20
{% endblock %}
11 21

  
12 22
{% block content %}
......
16 26
  {{ form.as_p }}
17 27
  <div class="buttons">
18 28
    <button>{% trans "Save" %}</button>
29
    <a class="cancel" href="{% url 'view_category' pk=category.pk %}">{% trans 'Cancel' %}</a>
19 30
  </div>
20 31
  <script type="text/javascript">
21 32
    $(function() {
corbo/templates/corbo/category_confirm_delete.html
1 1
{% extends "corbo/manage.html" %}
2 2
{% load i18n %}
3
{% block appbar %}
4
{% endblock %}
3 5
{% block content %}
4 6
<form method="post">
5 7
  {% csrf_token %}
corbo/templates/corbo/category_detail.html
1
{% extends 'corbo/manage.html' %}
2
{% load i18n %}
3

  
4
{% block breadcrumb %}
5
{{ block.super }}
6
{% if category %}
7
<a href='{% url "view_category" pk=category.pk %}'>{{ category }}</a>
8
{% endif %}
9
{% endblock %}
10

  
11
{% block appbar %}
12
<h2>{{ object.name }}</h2>
13
<a href="{% url 'delete_category' object.id %}" rel="popup">{% trans 'Delete' %}</a>
14
<a href="{% url 'edit_category' object.id %}" rel="popup">{% trans 'Rename' %}</a>
15
<a href="{% url 'add_announce' pk=object.pk %}">{% trans 'New announce' %}</a>
16
{% endblock %}
17

  
18
{% block content %}
19
<div id="management">
20
<ul class='objects-list single-links'>
21
{% for announce in announces %}
22
<li class="{% if not announce.is_published %}unpublished{% endif %}">
23
  <a href="{% url 'edit_announce' announce.id %}">{{ announce.title }}</a>
24
  <div class="datetime">
25
    {% if announce.publication_time %}
26
    {% blocktrans with mtime=announce.publication_time|date:'DATETIME_FORMAT' %}
27
    Published on {{ mtime }}
28
    {% endblocktrans %}
29
    {% endif %}
30
  </div>
31
</li>
32
{% endfor %}
33
</ul>
34

  
35
{% if not announces %}
36
<div class="big-msg-info">
37
  {% blocktrans %}
38
  This category doesn't have any announces yet. Click on "New announce" button in the top
39
  right of the page to add a first one.
40
  {% endblocktrans %}
41
</div>
42
{% endif %}
43

  
44
{% endblock %}
corbo/templates/corbo/category_form.html
15 15
  {{ form.as_p }}
16 16
  <div class="buttons">
17 17
    <button>{% trans "Save" %}</button>
18
    <a class="cancel" href="{% url 'manage' %}">{% trans 'Cancel' %}</a>
18 19
  </div>
19 20
</form>
20 21
{% endblock %}
corbo/templates/corbo/manage.html
1 1
{% extends 'corbo/base.html' %}
2
{% load i18n static %}
3
{% block page-title %}
4
{{ block.super }} :: {% trans "Management" %}
5
{% endblock %}
6

  
2
{% load i18n %}
7 3

  
4
{% block appbar %}
5
<h2>{% trans "Categories" %}</h2>
6
<a href="{% url 'add_category' %}" rel='popup'>{% trans 'New category' %}</a>
7
{% endblock %}
8 8

  
9 9
{% 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 10
<div id="management">
26
  <h4>{% trans "Announces" %}</h4>
27
  <span><a class="icon add" href="{% url "add_announce" %}">{% trans "add announce" %}</a></span>
28
<ul>
29
{% for obj in object_list %}
30
<li class='{% cycle 'bluesky' '' %}'>
31
  <div class="status">
32
    {% if obj.is_published %}
33
    <span class="icon published" title="{% trans "published" %}"></span>
34
    {% endif %}
35
    {% if not obj.is_published %}
36
    {% if obj.is_expired %}
37
    <span class="icon expired" title="{% trans "expired" %}"></span>
38
    {% else %}
39
    <span class="icon unpublished" title="{% trans "not published yet" %}"></span>
40
    {% endif %}
41
    {% endif %}
42
  </div>
43
  <div class="preview datetime">
44
    {% blocktrans with mtime=obj.mtime|date:'DATETIME_FORMAT' %}
45
    Modified on {{ mtime }}
46
    {% endblocktrans %}
47
  </div>
48
  <div class="actions">
49
    <span><a class="icon edit" href="{% url 'edit_announce' obj.id %}"></a></span>
50
    <span><a class="icon delete" href="{% url 'delete_announce' obj.id %}" rel="popup"></a></span>
51
  </div>
52
  <div class="announce">
53
    <div class="title">{{ obj.title }}</div>
54
    <div class="preview">{{ obj.text|safe|truncatechars_html:128 }}</div>
55
  </div>
56
</li>
57
{% empty %}
58
<div class="empty">
59
  {% trans "No announces matching this category" %}
11
  <ul class='objects-list single-links'>
12
    {% for obj in object_list %}
13
    <li class='category'>
14
        <a href="{% url 'view_category' pk=obj.id %}">{{ obj.name }} /
15
          {% blocktrans count announces_number=obj.get_announces_count %}
16
          {{ announces_number }} announce
17
          {% plural %}
18
          {{ announces_number }} announces
19
          {% endblocktrans %} /
20
          {% blocktrans count subscriptions_number=obj.get_subscriptions_count %}
21
          {{ subscriptions_number }} subscription
22
          {% plural %}
23
          {{ subscriptions_number }} subscriptions
24
          {% endblocktrans %}
25
        </a>
26
    </li>
27
    {% empty %}
28
<div class="big-msg-info">
29
  {% blocktrans %}
30
  There are no categories yet. Click on "New category" button in the top
31
  right of the page to add a first one.
32
  {% endblocktrans %}
33
  {% endfor %}
34
  </ul>
60 35
</div>
61
{% endfor %}
62
</ul>
63

  
64
{% if is_paginated %}
65
<ul class="pagination">
66
  {% if page_obj.has_previous %}
67
  <li class="prev">
68
    <a href="?{% if category_id %}category={{ category_id}}&{% endif %}page={{ page_obj.previous_page_number }}" class="icon"></a>
69
  </li>
70
  {% endif %}
71
  <li class="current">
72
    page {{ page_obj.number }} of {{ paginator.num_pages }}
73
  </li>
74
  {% if page_obj.has_next %}
75
  <li class="next">
76
    <a href="?{% if category_id %}category={{ category_id}}&{% endif %}page={{ page_obj.next_page_number }}" class="icon"></a>
77
  </li>
78
  {% endif %}
79
</ul>
80
{% endif %}
81
</div>
82
{% endblock %}
83

  
84
{% block page-end %}
85
<script type="text/javascript">
86
  $(function() {
87
    $('div.categories select').on('change', function() {
88
        var category = $(this).val();
89
        if (category)
90
            window.location.replace('{% url "manage" %}?category=' + category);
91
        else
92
            window.location.replace({% url "manage" %});
93
    })
94
})
95
</script>
96
{% endblock %}
36
  {% 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
......
49 50

  
50 51
homepage = HomepageView.as_view()
51 52

  
53

  
52 54
class AnnounceCreateView(CreateView):
55

  
53 56
    form_class = AnnounceForm
54 57
    template_name = 'corbo/announce_form.html'
55 58

  
56 59
    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})
60
        return reverse('view_category', kwargs={'pk': self.object.category.pk})
61

  
62
    def get_initial(self):
63
        initial = super(AnnounceCreateView, self).get_initial()
64
        initial['category'] = models.Category.objects.get(pk=self.kwargs['pk'])
65
        return initial
66

  
67
    def get_context_data(self, **kwargs):
68
        context = super(AnnounceCreateView, self).get_context_data(**kwargs)
69
        context['category'] = kwargs['form'].initial['category']
70
        return context
61 71

  
62 72
add_announce = AnnounceCreateView.as_view()
63 73

  
74

  
64 75
class AnnounceEditView(UpdateView):
65 76
    model = models.Announce
66 77
    form_class = AnnounceForm
67 78

  
79
    def get_context_data(self, **kwargs):
80
        context = super(AnnounceEditView, self).get_context_data(**kwargs)
81
        category_id = kwargs['form'].initial['category']
82
        context['category'] = models.Category.objects.get(pk=category_id)
83
        return context
84

  
68 85
    def get_success_url(self):
69
        return reverse('manage') + '?' + urlencode({'category': self.object.category.id})
86
        return reverse('view_category', kwargs={'pk': self.object.category.pk})
70 87

  
71 88
edit_announce = AnnounceEditView.as_view()
72 89

  
......
74 91
    model = models.Announce
75 92

  
76 93
    def get_success_url(self):
77
        return self.request.META['HTTP_REFERER'] or \
78
            reverse('manage') + '?' + urlencode({'category': self.object.category.id})
94
        return reverse('view_category', kwargs={'pk': self.object.category.pk})
79 95

  
80 96
delete_announce = AnnounceDeleteView.as_view()
81 97

  
......
92 108

  
93 109
add_category = CategoryCreateView.as_view()
94 110

  
111

  
95 112
class CategoryEditView(UpdateView):
96 113
    form_class = CategoryForm
97 114
    model = models.Category
98 115

  
99 116
    def get_success_url(self):
100
        return self.request.META['HTTP_REFERER'] or \
101
            reverse('manage') + '?' + urlencode({'category': self.object.id})
117
        return reverse('view_category', kwargs={'pk': self.object.pk})
102 118

  
103 119
edit_category = CategoryEditView.as_view()
104 120

  
121

  
122
class CategoryView(DetailView):
123
    model = models.Category
124

  
125
    def get_context_data(self, **kwargs):
126
        context = super(CategoryView, self).get_context_data(**kwargs)
127
        context['announces'] = self.object.announce_set.all()
128
        return context
129

  
130
view_category = CategoryView.as_view()
131

  
132

  
105 133
class CategoryDeleteView(DeleteView):
106 134
    model = models.Category
107 135

  
......
135 163

  
136 164

  
137 165
class ManageView(ListView):
138
    paginate_by = settings.ANNOUNCES_PER_PAGE
139 166
    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
        if self.request.GET.get('category'):
152
            try:
153
                context['category_id'] = int(self.request.GET['category'])
154
            except:
155
                pass
156
        return context
167
    model = models.Category
157 168

  
158 169
manage = ManageView.as_view()
159 170

  
171

  
160 172
class Atom1Feed(DjangoAtom1Feed):
161 173
    def root_attributes(self):
162 174
        attrs = super(Atom1Feed, self).root_attributes()
163
-