0001-remove-alfortville-custom-extension-31454.patch
MANIFEST.in | ||
---|---|---|
12 | 12 |
recursive-include welco/sources/counter/templates *.html |
13 | 13 |
recursive-include welco/sources/phone/templates *.html |
14 | 14 |
recursive-include welco/sources/mail/templates *.html |
15 |
recursive-include welco/contrib/alfortville/templates *.html *.txt |
|
16 | 15 | |
17 | 16 |
include COPYING README |
18 | 17 |
include MANIFEST.in |
welco/contrib/alfortville/__init__.py | ||
---|---|---|
1 |
# welco - multichannel request processing |
|
2 |
# Copyright (C) 2015 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
import django.apps |
|
18 | ||
19 |
class AppConfig(django.apps.AppConfig): |
|
20 |
name = 'welco.contrib.alfortville' |
|
21 | ||
22 |
def get_before_urls(self): |
|
23 |
from . import urls |
|
24 |
return urls.urlpatterns |
|
25 | ||
26 |
default_app_config = 'welco.contrib.alfortville.AppConfig' |
welco/contrib/alfortville/management/commands/send-avis-email.py | ||
---|---|---|
1 |
# welco - multichannel request processing |
|
2 |
# Copyright (C) 2015-2016 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
from django.conf import settings |
|
18 |
from django.contrib.auth import get_user_model |
|
19 |
from django.contrib.contenttypes.models import ContentType |
|
20 |
from django.core.mail import EmailMessage, send_mass_mail |
|
21 |
from django.core.management.base import BaseCommand, CommandError |
|
22 |
from django.template.loader import render_to_string |
|
23 | ||
24 |
from hobo.agent.common.models import Role |
|
25 |
from welco.contrib.alfortville.models import Inbox |
|
26 |
from welco.sources.mail.models import Mail |
|
27 | ||
28 | ||
29 |
class Command(BaseCommand): |
|
30 |
def handle(self, *args, **kwargs): |
|
31 |
ctx = {} |
|
32 |
ctx.update(getattr(settings, 'TEMPLATE_VARS', {})) |
|
33 | ||
34 |
subject = render_to_string(['alfortville/avis-email_subject.txt'], ctx).strip() |
|
35 |
message = render_to_string(['alfortville/avis-email_body.txt'], ctx) |
|
36 |
mails = [] |
|
37 | ||
38 |
relevant_mails = Mail.objects.filter(status='done-dga') |
|
39 |
content_type = ContentType.objects.get_for_model(Mail) |
|
40 | ||
41 |
User = get_user_model() |
|
42 |
for user in User.objects.all(): |
|
43 |
user_roles = [x.uuid for x in Role.objects.filter(user=user)] |
|
44 |
avis = Inbox.objects.filter( |
|
45 |
role_slug__in=user_roles, |
|
46 |
source_type=content_type, |
|
47 |
source_pk__in=[x.id for x in relevant_mails], |
|
48 |
subtype__in=[Inbox.MANDATORY_AVIS, Inbox.AVIS], |
|
49 |
done=False) |
|
50 |
if avis.count() == 0: |
|
51 |
continue |
|
52 |
mails.append((subject, message, settings.DEFAULT_FROM_EMAIL, [user.email])) |
|
53 | ||
54 |
if mails: |
|
55 |
send_mass_mail(mails) |
welco/contrib/alfortville/migrations/0001_initial.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
from __future__ import unicode_literals |
|
3 | ||
4 |
from django.db import models, migrations |
|
5 | ||
6 | ||
7 |
class Migration(migrations.Migration): |
|
8 | ||
9 |
dependencies = [ |
|
10 |
('qualif', '0003_association_comments'), |
|
11 |
] |
|
12 | ||
13 |
operations = [ |
|
14 |
migrations.CreateModel( |
|
15 |
name='Inbox', |
|
16 |
fields=[ |
|
17 |
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), |
|
18 |
('role_slug', models.CharField(max_length=50)), |
|
19 |
('subtype', models.PositiveSmallIntegerField(default=1, choices=[(1, b'info'), (2, b'avis')])), |
|
20 |
('done', models.BooleanField(default=False)), |
|
21 |
('qualif', models.ForeignKey(to='qualif.Association')), |
|
22 |
], |
|
23 |
options={ |
|
24 |
}, |
|
25 |
bases=(models.Model,), |
|
26 |
), |
|
27 |
] |
welco/contrib/alfortville/migrations/0002_auto_20151009_1331.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
from __future__ import unicode_literals |
|
3 | ||
4 |
from django.db import models, migrations |
|
5 | ||
6 | ||
7 |
class Migration(migrations.Migration): |
|
8 | ||
9 |
dependencies = [ |
|
10 |
('contenttypes', '0001_initial'), |
|
11 |
('alfortville', '0001_initial'), |
|
12 |
] |
|
13 | ||
14 |
operations = [ |
|
15 |
migrations.RemoveField( |
|
16 |
model_name='inbox', |
|
17 |
name='qualif', |
|
18 |
), |
|
19 |
migrations.AddField( |
|
20 |
model_name='inbox', |
|
21 |
name='source_pk', |
|
22 |
field=models.PositiveIntegerField(null=True), |
|
23 |
preserve_default=True, |
|
24 |
), |
|
25 |
migrations.AddField( |
|
26 |
model_name='inbox', |
|
27 |
name='source_type', |
|
28 |
field=models.ForeignKey(to='contenttypes.ContentType', null=True), |
|
29 |
preserve_default=True, |
|
30 |
), |
|
31 |
] |
welco/contrib/alfortville/migrations/0003_inbox_comments.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
from __future__ import unicode_literals |
|
3 | ||
4 |
from django.db import models, migrations |
|
5 | ||
6 | ||
7 |
class Migration(migrations.Migration): |
|
8 | ||
9 |
dependencies = [ |
|
10 |
('alfortville', '0002_auto_20151009_1331'), |
|
11 |
] |
|
12 | ||
13 |
operations = [ |
|
14 |
migrations.AddField( |
|
15 |
model_name='inbox', |
|
16 |
name='comments', |
|
17 |
field=models.TextField(verbose_name='Comments', blank=True), |
|
18 |
preserve_default=True, |
|
19 |
), |
|
20 |
] |
welco/contrib/alfortville/migrations/0004_auto_20151016_1523.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
from __future__ import unicode_literals |
|
3 | ||
4 |
from django.db import models, migrations |
|
5 | ||
6 | ||
7 |
class Migration(migrations.Migration): |
|
8 | ||
9 |
dependencies = [ |
|
10 |
('alfortville', '0003_inbox_comments'), |
|
11 |
] |
|
12 | ||
13 |
operations = [ |
|
14 |
migrations.AlterField( |
|
15 |
model_name='inbox', |
|
16 |
name='subtype', |
|
17 |
field=models.PositiveSmallIntegerField(default=1, choices=[(1, b'info'), (2, b'avis'), (3, b'mandatory-avis')]), |
|
18 |
preserve_default=True, |
|
19 |
), |
|
20 |
] |
welco/contrib/alfortville/migrations/0005_auto_20151016_1523.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
from __future__ import unicode_literals |
|
3 | ||
4 |
from django.db import models, migrations |
|
5 | ||
6 | ||
7 |
class Migration(migrations.Migration): |
|
8 | ||
9 |
dependencies = [ |
|
10 |
('alfortville', '0004_auto_20151016_1523'), |
|
11 |
] |
|
12 | ||
13 |
operations = [ |
|
14 |
migrations.AlterField( |
|
15 |
model_name='inbox', |
|
16 |
name='comments', |
|
17 |
field=models.TextField(null=True, verbose_name='Comments', blank=True), |
|
18 |
preserve_default=True, |
|
19 |
), |
|
20 |
] |
welco/contrib/alfortville/migrations/0006_inbox_last_update_timestamp.py | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
from __future__ import unicode_literals |
|
3 | ||
4 |
from django.db import migrations, models |
|
5 | ||
6 | ||
7 |
class Migration(migrations.Migration): |
|
8 | ||
9 |
dependencies = [ |
|
10 |
('alfortville', '0005_auto_20151016_1523'), |
|
11 |
] |
|
12 | ||
13 |
operations = [ |
|
14 |
migrations.AddField( |
|
15 |
model_name='inbox', |
|
16 |
name='last_update_timestamp', |
|
17 |
field=models.DateTimeField(auto_now=True, null=True), |
|
18 |
), |
|
19 |
] |
welco/contrib/alfortville/models.py | ||
---|---|---|
1 |
# welco - multichannel request processing |
|
2 |
# Copyright (C) 2015 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
from django.contrib.contenttypes.models import ContentType |
|
18 |
from django.contrib.contenttypes.fields import GenericForeignKey |
|
19 |
from django.db import models |
|
20 |
from django.utils.translation import ugettext_lazy as _ |
|
21 | ||
22 |
from hobo.agent.common.models import Role |
|
23 | ||
24 | ||
25 |
class Inbox(models.Model): |
|
26 |
INFO = 1 |
|
27 |
AVIS = 2 |
|
28 |
MANDATORY_AVIS = 3 |
|
29 | ||
30 |
role_slug = models.CharField(max_length=50) |
|
31 |
subtype = models.PositiveSmallIntegerField( |
|
32 |
choices=((INFO, 'info'), |
|
33 |
(AVIS, 'avis'), |
|
34 |
(MANDATORY_AVIS, 'mandatory-avis')), |
|
35 |
default=INFO) |
|
36 |
source_type = models.ForeignKey(ContentType, null=True) |
|
37 |
source_pk = models.PositiveIntegerField(null=True) |
|
38 |
source = GenericForeignKey('source_type', 'source_pk') |
|
39 |
done = models.BooleanField(default=False) |
|
40 |
comments = models.TextField(blank=True, null=True, verbose_name=_('Comments')) |
|
41 |
last_update_timestamp = models.DateTimeField(auto_now=True, null=True) |
|
42 | ||
43 |
def role_name(self): |
|
44 |
return Role.objects.get(uuid=self.role_slug).name |
welco/contrib/alfortville/templates/alfortville/avis-email_body.txt | ||
---|---|---|
1 |
Bonjour, |
|
2 | ||
3 |
Vous avez des avis à donner sur des courriers (avis requis et/ou avis |
|
4 |
demandés). |
|
5 | ||
6 |
Pour prendre connaissance des courriers et déposer vos avis, merci de vous |
|
7 |
rendre sur : |
|
8 | ||
9 |
{{ portal_agent_url }} |
welco/contrib/alfortville/templates/alfortville/avis-email_subject.txt | ||
---|---|---|
1 |
Avis attendus sur des courriers |
welco/contrib/alfortville/templates/alfortville/copies.html | ||
---|---|---|
1 |
{% extends "welco/base.html" %} |
|
2 |
{% load i18n static %} |
|
3 | ||
4 |
{% block content %} |
|
5 | ||
6 |
<form method="POST"> |
|
7 |
{% csrf_token %} |
|
8 |
<table class="avis"> |
|
9 |
<thead> |
|
10 |
<tr> |
|
11 |
<td></td> |
|
12 |
<th>Pour information</th> |
|
13 |
<th>Pour avis facultatif</th> |
|
14 |
<th>Pour avis requis</th> |
|
15 |
</tr> |
|
16 |
</thead> |
|
17 |
<tbody> |
|
18 |
{% for role in roles %} |
|
19 |
<tr> |
|
20 |
<th>{{role.text}}</th> |
|
21 |
<td><input name="info" value="{{role.slug}}" type="checkbox" |
|
22 |
{% if role.slug in checked_info %}checked="checked"{% endif %}/></td> |
|
23 |
<td><input name="avis" value="{{role.slug}}" type="checkbox" |
|
24 |
{% if role.slug in checked_avis %}checked="checked"{% endif %}/></td> |
|
25 |
<td><input name="mandatory_avis" value="{{role.slug}}" type="checkbox" |
|
26 |
{% if role.slug in checked_mandatory_avis %}checked="checked"{% endif %}/></td> |
|
27 |
</tr> |
|
28 |
{% endfor %} |
|
29 |
</tbody> |
|
30 |
</table> |
|
31 |
<div class="buttons"> |
|
32 |
<button>{% trans 'Save' %}</button> |
|
33 |
<button class="cancel">{% trans 'Cancel' %}</button> |
|
34 |
</div> |
|
35 |
</form> |
|
36 | ||
37 |
{% endblock %} |
welco/contrib/alfortville/templates/alfortville/dg-table.html | ||
---|---|---|
1 |
{% extends "welco/base.html" %} |
|
2 |
{% load i18n static %} |
|
3 | ||
4 |
{% block bodyargs %}class="mail-dg-table"{% endblock %} |
|
5 | ||
6 |
{% block breadcrumb %} |
|
7 |
{{block.super}} |
|
8 |
<a href=".">{{home_screen_title}}</a> |
|
9 |
{% endblock %} |
|
10 | ||
11 |
{% block content %} |
|
12 | ||
13 |
<form method="post" action="qualif-many"> |
|
14 |
<div class="source-mail all"> |
|
15 |
<div class="cell document top"> |
|
16 |
{% if source.get_queryset %} |
|
17 |
<div> |
|
18 |
<table class="main"> |
|
19 |
<thead> |
|
20 |
<th>{% trans 'Post Date' %}</th> |
|
21 |
<th>{% trans 'Reference' %}</th> |
|
22 |
<th>{% trans 'Subject' %}</th> |
|
23 |
<th>{% trans 'User' %}</th> |
|
24 |
<th>{% trans 'Category' %}</th> |
|
25 |
<th>{% trans 'Related Forms' %}</th> |
|
26 |
<th><input type="checkbox" title="{% trans "Click to (un)toggle all mails" %}" id="click-all-pks"/></th> |
|
27 |
</thead> |
|
28 |
<tbody> |
|
29 |
{% for object in mails %} |
|
30 |
<tr data-mail-id="{{object.id}}"> |
|
31 |
<td class="r">{{object.post_date|date:"d F Y"|lower}}</td> |
|
32 |
<td class="r">{{object.reference|default:'-'}}</td> |
|
33 |
<td class="r">{{object.subject|default:'-'}}</td> |
|
34 |
<td class="r">{{object.contact_name }}</td> |
|
35 |
<td class="r">{{object.categories|join:", " }}</td> |
|
36 |
<td class="r">{% for association in object.associations.all %}{{association.formdef_name}}{% if not forloop.last %}, {% endif %}{% endfor %}</td> |
|
37 |
<td><input type="checkbox" name="pks" value="{{object.id}}"/></td> |
|
38 |
</tr> |
|
39 |
{% endfor %} |
|
40 |
</tbody> |
|
41 |
<script> |
|
42 |
$('td.r').click(function() { |
|
43 |
window.open('../?' + $(this).parent().data('mail-id'), target='_welco_dg'); |
|
44 |
}); |
|
45 |
$('input#click-all-pks').click(function(e) { |
|
46 |
$('input[name="pks"]').prop('checked', $(this).prop('checked')); |
|
47 |
}); |
|
48 |
</script> |
|
49 |
</table> |
|
50 |
</div> |
|
51 |
<div id="dg-transmit"> |
|
52 |
{% csrf_token %} |
|
53 |
<button>Transmettre</button> |
|
54 |
</form> |
|
55 |
{%else %} |
|
56 |
<p class="no-mail-table"> |
|
57 |
{% trans 'There is currently no mail in this list.' %} |
|
58 |
</p> |
|
59 |
{% endif %} |
|
60 |
</div> |
|
61 |
</div> |
|
62 | ||
63 |
{% endblock %} |
welco/contrib/alfortville/templates/alfortville/mail-table-waiting.html | ||
---|---|---|
1 |
{% extends "welco/base.html" %} |
|
2 |
{% load i18n static %} |
|
3 | ||
4 |
{% block bodyargs %}class="mail-table mail-table-waiting"{% endblock %} |
|
5 | ||
6 |
{% block content %} |
|
7 | ||
8 |
<div class="source-mail all"> |
|
9 |
<div class="cell document top"> |
|
10 |
{% if objects %} |
|
11 |
<h2>{% trans 'Pending Mails' %}</h2> |
|
12 |
<div> |
|
13 |
<table class="main"> |
|
14 |
<thead> |
|
15 |
<th>{% trans 'Post Date' %}</th> |
|
16 |
<th>{% trans 'Reference' %}</th> |
|
17 |
<th>{% trans 'Subject' %}</th> |
|
18 |
<th>{% trans 'Related Forms' %}</th> |
|
19 |
<th>{% trans 'Status' %}</th> |
|
20 |
</thead> |
|
21 |
<tbody> |
|
22 |
{% for object in objects %} |
|
23 |
<tr> |
|
24 |
<td>{{object.post_date|default:'-'}}</td> |
|
25 |
<td>{{object.reference|default:'-'}}</td> |
|
26 |
<td>{{object.subject|default:'-'}}</td> |
|
27 |
<td>{% for association in object.associations.all %}{{association.formdef_name}}{% if not forloop.last %}, {% endif %}{% endfor %}</td> |
|
28 |
<td>{% if object.status == 'done-qualif' %}En attente de validation DGS |
|
29 |
{% elif object.status == 'done-dgs' %}En attente de validation DGA |
|
30 |
{% else %}En attente de qualification |
|
31 |
{% endif %}</td> |
|
32 |
</tr> |
|
33 |
{% endfor %} |
|
34 |
</tbody> |
|
35 |
</table> |
|
36 |
</div> |
|
37 |
{%else %} |
|
38 |
<p class="no-mail-table"> |
|
39 |
{% trans 'There is currently no mail in this list.' %} |
|
40 |
</p> |
|
41 |
{% endif %} |
|
42 |
</div> |
|
43 |
</div> |
|
44 | ||
45 |
{% endblock %} |
welco/contrib/alfortville/templates/alfortville/mail-table.html | ||
---|---|---|
1 |
{% extends "welco/base.html" %} |
|
2 |
{% load i18n static %} |
|
3 | ||
4 |
{% block bodyargs %}class="mail-table mail-table-subtype{{subtype}}"{% endblock %} |
|
5 | ||
6 |
{% block content %} |
|
7 | ||
8 |
{% if objects %} |
|
9 |
<div class="source-mail all"> |
|
10 |
<div class="cell document top"> |
|
11 |
<h2>{{title}}</h2> |
|
12 |
<div> |
|
13 |
<div class="mails source"> |
|
14 |
<ul> |
|
15 |
{% for object in objects %} |
|
16 |
<li |
|
17 |
data-source-pk="{{object.source.id}}" |
|
18 |
data-pdf-href="{{object.source.content.url}}"> |
|
19 |
{{object.source.creation_timestamp}} |
|
20 |
{% for association in object.source.associations.all %} |
|
21 |
<br/><span data-formdata-url="{{association.formdata_url}}">{{association.formdef_name}}</span> |
|
22 |
{% endfor %} |
|
23 |
</li> |
|
24 |
{% endfor %} |
|
25 |
</ul> |
|
26 |
</div> |
|
27 |
<div id="postit" class="readonly" style="display: none" |
|
28 |
data-base-url="{% url 'mail-edit-note' %}" |
|
29 |
> |
|
30 |
</div> |
|
31 |
<iframe id="pdf-viewer" src="{% url 'mail-viewer' %}" style="width: 100%; |
|
32 |
height: 100%; border: 0;"> |
|
33 |
</iframe> |
|
34 |
</div> |
|
35 |
</div> |
|
36 | ||
37 |
<div class="cell"> |
|
38 |
</div> |
|
39 | ||
40 |
<div class="cell mark-as-seen"> |
|
41 |
<form method="POST"> |
|
42 |
{% csrf_token %} |
|
43 |
{% if display_comments_field %} |
|
44 |
<textarea name="comments" id="comments-field" placeholder="{% trans "Comments" %}"></textarea> |
|
45 |
{% endif %} |
|
46 |
<input type="hidden" name="object-pk"></input> |
|
47 |
<button>{{ button_text }}</button> |
|
48 |
</form> |
|
49 |
</div> |
|
50 | ||
51 |
</div> |
|
52 | ||
53 |
<script> |
|
54 |
$(function() { |
|
55 |
$('[data-pdf-href]').on('welco:mail-selected', function() { |
|
56 |
$('input[name="object-pk"]').val($(this).data('source-pk')); |
|
57 |
}); |
|
58 |
}); |
|
59 |
</script> |
|
60 |
{% else %} |
|
61 |
<!-- nothing --> |
|
62 | ||
63 |
<div class="source-mail all"> |
|
64 |
<div class="cell document top"> |
|
65 |
<h2>{{title}}</h2> |
|
66 | ||
67 |
<p class="no-mail-table"> |
|
68 |
{% trans 'There is currently no mail in this list.' %} |
|
69 |
</p> |
|
70 | ||
71 |
</div> |
|
72 |
</div> |
|
73 | ||
74 | ||
75 |
{% endif %} |
|
76 | ||
77 |
{% endblock %} |
welco/contrib/alfortville/urls.py | ||
---|---|---|
1 |
# welco - multichannel request processing |
|
2 |
# Copyright (C) 2015 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
from django.conf.urls import url |
|
18 | ||
19 |
from .views import (dgs, dga, copies, copies_ajax, table_info, table_avis, |
|
20 |
table_mandatory_avis, table_waiting, count_dgs, count_dga, count_info, |
|
21 |
count_avis, count_mandatory_avis, dgs_qualif_many, dga_qualif_many) |
|
22 | ||
23 |
urlpatterns = [ |
|
24 |
url(r'^dgs/(?P<table>table/)?$', dgs, name='alfortville-dgs'), |
|
25 |
url(r'^dga/(?P<table>table/)?$', dga, name='alfortville-dga'), |
|
26 |
url(r'^dgs/table/qualif-many$', dgs_qualif_many, name='alfortville-dgs-qualif-many'), |
|
27 |
url(r'^dga/table/qualif-many$', dga_qualif_many, name='alfortville-dga-qualif-many'), |
|
28 |
url(r'^copies/(?P<pk>\w+)/$', copies, name='alfortville-copies'), |
|
29 |
url(r'^ajax/copies/(?P<pk>\w+)/$', copies_ajax, name='alfortville-copies-ajax'), |
|
30 |
url(r'^table/info/$', table_info, name='alfortville-table-info'), |
|
31 |
url(r'^table/avis/$', table_avis, name='alfortville-table-avis'), |
|
32 |
url(r'^table/avis-requis/$', table_mandatory_avis, name='alfortville-table-mandatory-avis'), |
|
33 |
url(r'^table/en-attente/$', table_waiting, name='alfortville-table-waiting'), |
|
34 | ||
35 |
url(r'^ajax/count/dgs/$', count_dgs, name='alfortville-count-dgs'), |
|
36 |
url(r'^ajax/count/dga/$', count_dga, name='alfortville-count-dga'), |
|
37 |
url(r'^ajax/count/info/$', count_info, name='alfortville-count-info'), |
|
38 |
url(r'^ajax/count/avis/$', count_avis, name='alfortville-count-avis'), |
|
39 |
url(r'^ajax/count/avis-requis/$', count_mandatory_avis, name='alfortville-count-avis-requis'), |
|
40 |
] |
welco/contrib/alfortville/views.py | ||
---|---|---|
1 |
# welco - multichannel request processing |
|
2 |
# Copyright (C) 2015 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
import json |
|
18 |
import logging |
|
19 | ||
20 |
from django.conf import settings |
|
21 |
from django.contrib.auth.decorators import login_required |
|
22 |
from django.contrib.contenttypes.models import ContentType |
|
23 |
from django.core.urlresolvers import reverse |
|
24 |
from django.db.models import Q |
|
25 |
from django.http import HttpResponse, HttpResponseRedirect |
|
26 |
from django.utils.translation import ugettext_lazy as _ |
|
27 |
from django.views.generic import TemplateView, DetailView |
|
28 | ||
29 |
from hobo.agent.common.models import Role |
|
30 | ||
31 |
from .models import Inbox |
|
32 | ||
33 |
from welco.sources.mail.models import Mail |
|
34 |
from welco.sources.mail.views import Home as MailOriginalHome |
|
35 |
from welco.qualif.models import Association |
|
36 |
from welco.utils import get_wcs_data, get_wcs_options, response_for_json |
|
37 |
from welco.views import HomeMail as HomeScreen |
|
38 | ||
39 | ||
40 |
class MailHome(MailOriginalHome): |
|
41 |
def get_template(self): |
|
42 |
return super(MailHome, self).get_template() |
|
43 | ||
44 | ||
45 |
class DgsMailHome(MailHome): |
|
46 |
display_filter = True |
|
47 |
allow_reject = False |
|
48 | ||
49 |
def get_queryset(self): |
|
50 |
return Mail.objects.filter(status='done-qualif') |
|
51 | ||
52 | ||
53 |
class DgHomeScreen(HomeScreen): |
|
54 |
def get_template_names(self): |
|
55 |
if self.kwargs.get('table') == 'table/': |
|
56 |
return ['alfortville/dg-table.html'] |
|
57 |
return [self.template_name] |
|
58 | ||
59 |
def get_context_data(self, **kwargs): |
|
60 |
context = super(DgHomeScreen, self).get_context_data(**kwargs) |
|
61 |
context['home_screen_title'] = self.home_screen_title |
|
62 |
context['mails'] = context['source'].get_queryset() |
|
63 |
return context |
|
64 | ||
65 | ||
66 |
class Dgs(DgHomeScreen): |
|
67 |
source_klass = DgsMailHome |
|
68 |
home_screen_title = _('DGS Validation') |
|
69 | ||
70 |
def check_user_ok(self): |
|
71 |
return 'DGS' in [x.name for x in self.request.user.groups.all()] |
|
72 | ||
73 |
dgs = login_required(Dgs.as_view()) |
|
74 | ||
75 | ||
76 |
def is_dga_role(role_name): |
|
77 |
return bool(role_name.startswith('DGA') or role_name.startswith('DGD') or |
|
78 |
role_name.startswith('Cabinet')) |
|
79 | ||
80 | ||
81 |
class DgaMailHome(MailHome): |
|
82 |
display_filter = True |
|
83 |
allow_reject = False |
|
84 | ||
85 |
def filter_formdef_condition(self, formdef): |
|
86 |
roles = set() |
|
87 |
for function_role in formdef.get('functions', {}).values(): |
|
88 |
if is_dga_role(function_role.get('role', {}).get('name', '')): |
|
89 |
roles.add(function_role.get('role').get('slug')) |
|
90 |
return self.user_roles.intersection(roles) |
|
91 | ||
92 |
def get_queryset(self): |
|
93 |
mellon = self.request.session['mellon_session'] |
|
94 |
params = {'NameID': mellon['name_id_content']} |
|
95 |
self.user_roles = set([x['slug'] for x in get_wcs_data('api/user/', params).get('user_roles')]) |
|
96 |
formdef_references = [] |
|
97 |
for category in get_wcs_options('api/formdefs/', self.filter_formdef_condition): |
|
98 |
formdef_references.extend([x[0] for x in category[1]]) |
|
99 |
return Mail.objects.filter(status='done-dgs', |
|
100 |
associations__formdef_reference__in=formdef_references) |
|
101 | ||
102 |
class Dga(DgHomeScreen): |
|
103 |
source_klass = DgaMailHome |
|
104 |
home_screen_title = _('DGA Validation') |
|
105 | ||
106 |
def check_user_ok(self): |
|
107 |
user_roles = [x.name for x in self.request.user.groups.all()] |
|
108 |
return any([x for x in user_roles if is_dga_role(x)]) |
|
109 | ||
110 |
dga = login_required(Dga.as_view()) |
|
111 | ||
112 | ||
113 |
class QualifMixin(object): |
|
114 |
def get(self, request, *args, **kwargs): |
|
115 |
return HttpResponseRedirect(self.get_redirect_url()) |
|
116 | ||
117 |
def post(self, request, *args, **kwargs): |
|
118 |
source_pks = request.POST.getlist('pks') |
|
119 |
logger = logging.getLogger(__name__) |
|
120 |
validation_steps = settings.VALIDATION_STEPS.get('mail') |
|
121 |
mail_content_type = ContentType.objects.get_for_model(Mail) |
|
122 |
allowed_mails = [str(x.id) for x in self.source_klass(request).get_queryset()] |
|
123 |
mail_ids = set(source_pks).intersection(set(allowed_mails)) |
|
124 |
for mail in Mail.objects.filter(id__in=mail_ids): |
|
125 |
if mail.status: |
|
126 |
mail.status = validation_steps[ |
|
127 |
validation_steps.index(mail.status)+1] |
|
128 |
else: |
|
129 |
mail.status = validation_steps[0] |
|
130 |
if mail.status == validation_steps[-1]: |
|
131 |
for association in Association.objects.filter( |
|
132 |
source_type=mail_content_type, |
|
133 |
source_pk=mail.id): |
|
134 |
try: |
|
135 |
association.push(request) |
|
136 |
except Exception, e: |
|
137 |
logger.exception('error pushing mail') |
|
138 |
continue |
|
139 |
mail.save() |
|
140 |
return HttpResponseRedirect(self.get_redirect_url()) |
|
141 | ||
142 | ||
143 |
class DgaQualifMany(QualifMixin, Dga): |
|
144 |
def get_redirect_url(self): |
|
145 |
return reverse('alfortville-dga', kwargs={'table': 'table/'}) |
|
146 | ||
147 |
dga_qualif_many = login_required(DgaQualifMany.as_view()) |
|
148 | ||
149 | ||
150 |
class DgsQualifMany(QualifMixin, Dgs): |
|
151 |
def get_redirect_url(self): |
|
152 |
return reverse('alfortville-dgs', kwargs={'table': 'table/'}) |
|
153 | ||
154 |
dgs_qualif_many = login_required(DgsQualifMany.as_view()) |
|
155 | ||
156 | ||
157 |
class Copies(DetailView): |
|
158 |
model = Mail |
|
159 |
template_name = 'alfortville/copies.html' |
|
160 | ||
161 |
def get_context_data(self, **kwargs): |
|
162 |
context = super(Copies, self).get_context_data(**kwargs) |
|
163 |
checked_dicts = { |
|
164 |
Inbox.MANDATORY_AVIS: {}, |
|
165 |
Inbox.AVIS: {}, |
|
166 |
Inbox.INFO: {}, |
|
167 |
} |
|
168 |
for inbox in Inbox.objects.filter(source_pk=self.object.id): |
|
169 |
checked_dicts[inbox.subtype][inbox.role_slug] = True |
|
170 |
context['checked_info'] = checked_dicts[Inbox.INFO] |
|
171 |
context['checked_avis'] = checked_dicts[Inbox.AVIS] |
|
172 |
context['checked_mandatory_avis'] = checked_dicts[Inbox.MANDATORY_AVIS] |
|
173 |
context['roles'] = [] |
|
174 |
all_roles = get_wcs_data('api/roles').get('data') |
|
175 |
for start in ('Maire', 'Cabinet', 'Adjoint', 'Conseiller', 'Elu', 'DGS', 'DGD', |
|
176 |
'DGA', 'Direction'): |
|
177 |
roles = [x for x in all_roles if x['text'].startswith(start)] |
|
178 |
roles.sort(lambda x, y: cmp(x['text'], y['text'])) |
|
179 |
context['roles'].extend(roles) |
|
180 |
return context |
|
181 | ||
182 |
def post(self, request, *args, **kwargs): |
|
183 |
lists = { |
|
184 |
Inbox.MANDATORY_AVIS: request.POST.getlist('mandatory_avis'), |
|
185 |
Inbox.AVIS: request.POST.getlist('avis'), |
|
186 |
Inbox.INFO: request.POST.getlist('info'), |
|
187 |
} |
|
188 |
for subtype in lists.keys(): |
|
189 |
Inbox.objects.filter(subtype=subtype).filter( |
|
190 |
source_pk=kwargs['pk']).exclude( |
|
191 |
role_slug__in=lists[subtype]).delete() |
|
192 |
mail_content_type = ContentType.objects.get_for_model(Mail) |
|
193 |
for subtype in lists.keys(): |
|
194 |
for role_slug in lists[subtype]: |
|
195 |
Inbox.objects.get_or_create(source_type=mail_content_type, |
|
196 |
source_pk=kwargs['pk'], role_slug=role_slug, subtype=subtype) |
|
197 |
return HttpResponseRedirect('.') |
|
198 | ||
199 |
copies = login_required(Copies.as_view()) |
|
200 | ||
201 | ||
202 |
def copies_ajax(request, *args, **kwargs): |
|
203 |
roles_by_slug = {} |
|
204 |
for role in get_wcs_data('api/roles').get('data'): |
|
205 |
roles_by_slug[role['slug']] = role |
|
206 |
lists = { |
|
207 |
Inbox.MANDATORY_AVIS: [], |
|
208 |
Inbox.AVIS: [], |
|
209 |
Inbox.INFO: [] |
|
210 |
} |
|
211 |
for inbox in Inbox.objects.filter(source_pk=kwargs.get('pk')): |
|
212 |
lists[inbox.subtype].append(roles_by_slug[inbox.role_slug]['text']) |
|
213 |
response = HttpResponse(content_type='application/json') |
|
214 |
json.dump({'info': ', '.join(lists[Inbox.INFO]) or '-', |
|
215 |
'avis': ', '.join(lists[Inbox.AVIS]) or '-', |
|
216 |
'mandatory_avis': ', '.join(lists[Inbox.MANDATORY_AVIS]) or '-'}, |
|
217 |
response, indent=2) |
|
218 |
return response |
|
219 | ||
220 | ||
221 |
class MailTable(TemplateView): |
|
222 |
template_name = 'alfortville/mail-table.html' |
|
223 |
button_text = _('Next') |
|
224 | ||
225 |
def get_context_data(self, *args, **kwargs): |
|
226 |
context = super(MailTable, self).get_context_data(**kwargs) |
|
227 |
params = {} |
|
228 |
if self.request.session.get('mellon_session'): |
|
229 |
mellon = self.request.session['mellon_session'] |
|
230 |
params['NameID'] = mellon['name_id_content'] |
|
231 | ||
232 |
user_roles = [x.uuid for x in Role.objects.filter(user=self.request.user)] |
|
233 | ||
234 |
mails = Mail.objects.filter(status='done-dga') |
|
235 |
content_type = ContentType.objects.get_for_model(Mail) |
|
236 |
context['objects'] = Inbox.objects.filter( |
|
237 |
role_slug__in=user_roles, |
|
238 |
source_type=content_type, |
|
239 |
source_pk__in=[x.id for x in mails], |
|
240 |
subtype=self.subtype, done=False).order_by('source_pk').distinct('source_pk') |
|
241 | ||
242 |
context['subtype'] = self.subtype |
|
243 |
context['button_text'] = self.button_text |
|
244 |
context['title'] = self.title |
|
245 |
context['display_comments_field'] = True |
|
246 | ||
247 |
return context |
|
248 | ||
249 |
def post(self, request, *args, **kwargs): |
|
250 |
user_roles = [x.uuid for x in Role.objects.filter(user=self.request.user)] |
|
251 |
content_type = ContentType.objects.get_for_model(Mail) |
|
252 |
Inbox.objects.filter( |
|
253 |
source_type=content_type, |
|
254 |
source_pk__in=request.POST.getlist('object-pk'), |
|
255 |
role_slug__in=user_roles, |
|
256 |
subtype=self.subtype).update( |
|
257 |
done=True, |
|
258 |
comments=request.POST.get('comments')) |
|
259 |
return HttpResponseRedirect('.') |
|
260 | ||
261 | ||
262 |
class TableMandatoryAvis(MailTable): |
|
263 |
subtype = Inbox.MANDATORY_AVIS |
|
264 |
title = _('Mandatory Avis Copies') |
|
265 | ||
266 |
table_mandatory_avis = login_required(TableMandatoryAvis.as_view()) |
|
267 | ||
268 | ||
269 |
class TableAvis(MailTable): |
|
270 |
subtype = Inbox.AVIS |
|
271 |
title = _('Avis Copies') |
|
272 | ||
273 |
table_avis = login_required(TableAvis.as_view()) |
|
274 | ||
275 | ||
276 |
class TableInfo(MailTable): |
|
277 |
subtype = Inbox.INFO |
|
278 |
title = _('Info Copies') |
|
279 | ||
280 |
table_info = login_required(TableInfo.as_view()) |
|
281 | ||
282 | ||
283 |
class TableWaiting(TemplateView): |
|
284 |
template_name = 'alfortville/mail-table-waiting.html' |
|
285 | ||
286 |
def get_context_data(self, *args, **kwargs): |
|
287 |
context = super(TableWaiting, self).get_context_data(**kwargs) |
|
288 | ||
289 |
content_type = ContentType.objects.get_for_model(Mail) |
|
290 |
context['objects'] = Mail.objects.filter( |
|
291 |
status__in=('', 'done-qualif', 'done-dgs')).order_by('creation_timestamp') |
|
292 |
return context |
|
293 | ||
294 |
table_waiting = login_required(TableWaiting.as_view()) |
|
295 | ||
296 | ||
297 |
@login_required |
|
298 |
def count_dgs(request, *args, **kwargs): |
|
299 |
count = DgsMailHome(request).get_queryset().count() |
|
300 |
return response_for_json(request, {'count': count}) |
|
301 | ||
302 |
@login_required |
|
303 |
def count_dga(request, *args, **kwargs): |
|
304 |
count = DgaMailHome(request).get_queryset().count() |
|
305 |
return response_for_json(request, {'count': count}) |
|
306 | ||
307 |
@login_required |
|
308 |
def count_info(request, *args, **kwargs): |
|
309 |
table = TableInfo() |
|
310 |
table.request = request |
|
311 |
count = table.get_context_data().get('objects').count() |
|
312 |
return response_for_json(request, {'count': count}) |
|
313 | ||
314 |
@login_required |
|
315 |
def count_avis(request, *args, **kwargs): |
|
316 |
table = TableAvis() |
|
317 |
table.request = request |
|
318 |
count = table.get_context_data().get('objects').count() |
|
319 |
return response_for_json(request, {'count': count}) |
|
320 | ||
321 |
@login_required |
|
322 |
def count_mandatory_avis(request, *args, **kwargs): |
|
323 |
table = TableMandatoryAvis() |
|
324 |
table.request = request |
|
325 |
count = table.get_context_data().get('objects').count() |
|
326 |
return response_for_json(request, {'count': count}) |
welco/settings.py | ||
---|---|---|
224 | 224 |
# as a phone line number and take it automatically. |
225 | 225 |
PHONE_AUTOTAKE_MELLON_USERNAME = False |
226 | 226 | |
227 |
# enable/disable specific features |
|
228 |
# ex: FLAVOURS = ['alfortville'] |
|
229 |
FLAVOURS = [] |
|
230 | ||
231 | 227 |
REST_FRAMEWORK = {} |
232 | 228 |
REST_FRAMEWORK['DEFAULT_AUTHENTICATION_CLASSES'] = ['rest_framework.authentication.BasicAuthentication'] |
233 | 229 |
welco/sources/mail/forms.py | ||
---|---|---|
23 | 23 |
registered_mail_number = forms.CharField(label=_('Registered Mail Number'), required=False) |
24 | 24 |
reference = forms.CharField(label=_('Reference'), required=False, widget=forms.HiddenInput) |
25 | 25 |
subject = forms.CharField(label=_('Subject'), required=False, widget=forms.HiddenInput) |
26 | ||
27 |
def __init__(self, *args, **kwargs): |
|
28 |
super(MailQualificationForm, self).__init__(*args, **kwargs) |
|
29 |
if 'alfortville' in getattr(settings, 'FLAVOURS', []): |
|
30 |
self.fields['reference'].widget = forms.TextInput() |
|
31 |
self.fields['subject'].widget = forms.TextInput() |
welco/sources/mail/models.py | ||
---|---|---|
44 | 44 |
note = models.TextField(_('Note'), null=True) |
45 | 45 |
external_id = models.CharField(_('External Id'), null=True, max_length=32) |
46 | 46 | |
47 |
# used only if settings.FLAVOURS contains 'alfortville' |
|
48 | 47 |
reference = models.CharField(_('Reference'), null=True, max_length=30) |
49 | 48 |
subject = models.CharField(_('Subject'), null=True, max_length=200) |
50 | 49 | |
... | ... | |
69 | 68 |
'post_date': self.post_date, |
70 | 69 |
'registered_mail_number': self.registered_mail_number, |
71 | 70 |
} |
72 |
if 'alfortville' in getattr(settings, 'FLAVOURS', []): |
|
73 |
data['reference'] = self.reference |
|
74 |
data['subject'] = self.subject |
|
75 | 71 |
return self.get_qualification_form_class()(data) |
76 | 72 | |
77 | 73 |
@classmethod |
78 | 74 |
def get_qualification_form_submit_url(cls): |
79 | 75 |
return reverse('qualif-mail-save') |
80 | 76 | |
81 |
# TODO: get_info_roles(), get_avis() and get_mandatory_avis() are custom to |
|
82 |
# alfortville, they shouldn't appear in this file. |
|
83 |
def get_info_roles(self): |
|
84 |
from welco.contrib.alfortville.models import Inbox |
|
85 |
return Inbox.objects.filter(subtype=Inbox.INFO, |
|
86 |
source_type=ContentType.objects.get_for_model(Mail), |
|
87 |
source_pk=self.id) |
|
88 | ||
89 |
def get_avis(self): |
|
90 |
from welco.contrib.alfortville.models import Inbox |
|
91 |
return Inbox.objects.filter(subtype=Inbox.AVIS, |
|
92 |
source_type=ContentType.objects.get_for_model(Mail), |
|
93 |
source_pk=self.id) |
|
94 | ||
95 |
def get_mandatory_avis(self): |
|
96 |
from welco.contrib.alfortville.models import Inbox |
|
97 |
return Inbox.objects.filter(subtype=Inbox.MANDATORY_AVIS, |
|
98 |
source_type=ContentType.objects.get_for_model(Mail), |
|
99 |
source_pk=self.id) |
|
100 | ||
101 | 77 |
def contact_name(self): |
102 | 78 |
if not self.contact_id: |
103 | 79 |
return '' |
... | ... | |
128 | 104 |
'registered_mail_number': self.registered_mail_number, |
129 | 105 |
'external_id': self.external_id, |
130 | 106 |
} |
131 |
if 'alfortville' in getattr(settings, 'FLAVOURS', []): |
|
132 |
context['reference'] = self.reference |
|
133 |
context['subject'] = self.subject |
|
134 | 107 |
return context |
135 | 108 | |
136 | 109 |
welco/sources/mail/templates/welco/mail_summary.html | ||
---|---|---|
36 | 36 |
</ul> |
37 | 37 |
{% endif %} |
38 | 38 | |
39 |
{% if object.get_mandatory_avis|length %} |
|
40 |
<div class="bo-block |
|
41 |
{% for avis in object.get_mandatory_avis %}{% if not avis.comments %} important {% endif %}{% endfor %}"> |
|
42 |
<strong>{% trans "Mandatory Avis" %}</strong> |
|
43 |
<ul> |
|
44 |
{% for avis in object.get_mandatory_avis %} |
|
45 |
<li>{{avis.role_name}}: |
|
46 |
{% if avis.comments %}{{ avis.comments }}{% else %}{% trans 'Waiting for avis.' %}{% endif %}</li> |
|
47 |
{% endfor %} |
|
48 |
</ul> |
|
49 |
</div> |
|
50 |
{% endif %} |
|
51 | ||
52 |
{% if object.get_avis|length %} |
|
53 |
<div class="bo-block"> |
|
54 |
<strong>{% trans "Avis" %}</strong> |
|
55 |
<ul> |
|
56 |
{% for avis in object.get_avis %} |
|
57 |
<li>{{avis.role_name}}: |
|
58 |
{% if avis.comments %}{{ avis.comments }}{% else %}{% trans 'Waiting for avis.' %}{% endif %}</li> |
|
59 |
{% endfor %} |
|
60 |
</ul> |
|
61 |
</div> |
|
62 |
{% endif %} |
|
63 | ||
64 |
{% if object.get_info_roles|length %} |
|
65 |
<div class="bo-block visas"> |
|
66 |
<strong>{% trans "Visas" %}</strong> |
|
67 |
<ul> |
|
68 |
{% for info in object.get_info_roles %} |
|
69 |
<li>{{info.role_name}} {% if not info.done %}{% trans "(not yet seen)" %}{% endif %}</li> |
|
70 |
{% endfor %} |
|
71 |
</ul> |
|
72 |
</div> |
|
73 |
{% endif %} |
|
74 | ||
75 | 39 |
<style> |
76 | 40 |
p.postit { |
77 | 41 |
background: rgba(241,231,103,1); |
... | ... | |
80 | 44 |
padding: 1ex; |
81 | 45 |
width: 90%; |
82 | 46 |
} |
83 |
div.visas ul { |
|
84 |
margin-bottom: 0; |
|
85 |
} |
|
86 | 47 |
</style> |
welco/templates/welco/qualification.html | ||
---|---|---|
21 | 21 |
<button class="add">{% trans 'Add' %}</button> |
22 | 22 |
</div> |
23 | 23 | |
24 |
{% if source_type_name == 'mail' %} |
|
25 |
<!-- special alfortville stuff, this should be factored out --> |
|
26 |
<div id="copies"> |
|
27 |
<p><strong>Copies</strong> (<a rel="popup" data-inplace-submit="true" href="{% url 'alfortville-copies' pk=source_pk %}">{% trans 'Edit' %}</a>)</p> |
|
28 |
<p class="info">Pour information : <span>...</span></p> |
|
29 |
<p class="avis">Pour avis facultatif : <span>...</span></p> |
|
30 |
<p class="mandatory_avis">Pour avis requis : <span>...</span></p> |
|
31 |
<script>$('#copies').trigger('welco:load-copies');</script> |
|
32 |
</div> |
|
33 |
{% endif %} |
|
34 | ||
35 |
{% if associations|length %} |
|
36 |
<div class="done"> |
|
37 |
{% if validation_steps %} |
|
38 |
{% for validation_step in validation_steps %} |
|
39 |
<button class="done" data-action-url="{% url 'qualif-done' %}?step={{validation_step.id}}">{{validation_step.label}}</button> |
|
40 |
{% endfor %} |
|
41 |
{% else %} |
|
42 |
<button class="done" data-action-url="{% url 'qualif-done' %}">{% trans 'Submit' %}</button> |
|
43 |
{% endif %} |
|
44 |
</div> |
|
45 |
{% endif %} |
|
46 | 24 |
</form> |
47 | 25 |
{% endif %} |
48 | 26 |
</div> |
49 |
- |