Projet

Général

Profil

0001-remove-alfortville-custom-extension-31454.patch

Frédéric Péters, 16 mars 2019 10:36

Télécharger (47,7 ko)

Voir les différences:

Subject: [PATCH 1/2] remove alfortville custom extension (#31454)

 MANIFEST.in                                   |   1 -
 welco/contrib/alfortville/__init__.py         |  26 --
 .../alfortville/management/__init__.py        |   0
 .../management/commands/__init__.py           |   0
 .../management/commands/send-avis-email.py    |  55 ---
 .../alfortville/migrations/0001_initial.py    |  27 --
 .../migrations/0002_auto_20151009_1331.py     |  31 --
 .../migrations/0003_inbox_comments.py         |  20 --
 .../migrations/0004_auto_20151016_1523.py     |  20 --
 .../migrations/0005_auto_20151016_1523.py     |  20 --
 .../0006_inbox_last_update_timestamp.py       |  19 -
 .../alfortville/migrations/__init__.py        |   0
 welco/contrib/alfortville/models.py           |  44 ---
 .../templates/alfortville/avis-email_body.txt |   9 -
 .../alfortville/avis-email_subject.txt        |   1 -
 .../templates/alfortville/copies.html         |  37 --
 .../templates/alfortville/dg-table.html       |  63 ----
 .../alfortville/mail-table-waiting.html       |  45 ---
 .../templates/alfortville/mail-table.html     |  77 -----
 welco/contrib/alfortville/urls.py             |  40 ---
 welco/contrib/alfortville/views.py            | 326 ------------------
 welco/settings.py                             |   4 -
 welco/sources/mail/forms.py                   |   6 -
 welco/sources/mail/models.py                  |  27 --
 .../mail/templates/welco/mail_summary.html    |  39 ---
 welco/static/css/style.css                    |  93 -----
 welco/static/js/welco.js                      |  13 -
 welco/templates/welco/qualification.html      |  22 --
 28 files changed, 1065 deletions(-)
 delete mode 100644 welco/contrib/alfortville/__init__.py
 delete mode 100644 welco/contrib/alfortville/management/__init__.py
 delete mode 100644 welco/contrib/alfortville/management/commands/__init__.py
 delete mode 100644 welco/contrib/alfortville/management/commands/send-avis-email.py
 delete mode 100644 welco/contrib/alfortville/migrations/0001_initial.py
 delete mode 100644 welco/contrib/alfortville/migrations/0002_auto_20151009_1331.py
 delete mode 100644 welco/contrib/alfortville/migrations/0003_inbox_comments.py
 delete mode 100644 welco/contrib/alfortville/migrations/0004_auto_20151016_1523.py
 delete mode 100644 welco/contrib/alfortville/migrations/0005_auto_20151016_1523.py
 delete mode 100644 welco/contrib/alfortville/migrations/0006_inbox_last_update_timestamp.py
 delete mode 100644 welco/contrib/alfortville/migrations/__init__.py
 delete mode 100644 welco/contrib/alfortville/models.py
 delete mode 100644 welco/contrib/alfortville/templates/alfortville/avis-email_body.txt
 delete mode 100644 welco/contrib/alfortville/templates/alfortville/avis-email_subject.txt
 delete mode 100644 welco/contrib/alfortville/templates/alfortville/copies.html
 delete mode 100644 welco/contrib/alfortville/templates/alfortville/dg-table.html
 delete mode 100644 welco/contrib/alfortville/templates/alfortville/mail-table-waiting.html
 delete mode 100644 welco/contrib/alfortville/templates/alfortville/mail-table.html
 delete mode 100644 welco/contrib/alfortville/urls.py
 delete mode 100644 welco/contrib/alfortville/views.py
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/static/css/style.css
2 2
	display: none;
3 3
}
4 4

  
5
body.mail-dg-table div#main-content,
6
body.mail-table div#main-content,
7
body.dgs-home div#main-content,
8 5
body.welco-home div#main-content {
9 6
	width: 100%;
10 7
	border: 0;
......
12 9
	height: calc(100vh - 41px); /* #top 40px + #top border 1px */
13 10
}
14 11

  
15
body.mail-table div#more-user-links,
16
body.dgs-home div#more-user-links,
17 12
body.welco-home div#more-user-links {
18 13
	display: none;
19 14
}
20 15

  
21
body.mail-table div#content,
22
body.dgs-home div#content,
23 16
body.welco-home div#content {
24 17
	margin: 0;
25 18
	padding: 0;
26 19
	height: 100%;
27 20
}
28 21

  
29
.dgs-page,
30 22
.all {
31 23
	display: flex;
32 24
	flex-wrap: wrap;
......
59 51
	height: 95%;
60 52
}
61 53

  
62
.mail-table .cell {
63
	height: 20%;
64
}
65

  
66
.mail-table .cell.top {
67
	height: 80%;
68
}
69

  
70
.mail-table-waiting .cell,
71
.mail-table-subtype1 .cell {
72
	height: 20%;
73
}
74

  
75
.mail-table-waiting .cell.top,
76
.mail-table-subtype1 .cell.top {
77
	height: 80%;
78
}
79

  
80 54
div#content .cell h2 {
81 55
	font-size: 100%;
82 56
	padding-left: 1ex;
......
334 308
	padding: 1ex;
335 309
}
336 310

  
337
table.dgs-summary {
338
	border-collapse: collapse;
339
	width: calc(100% + 1px);
340
}
341

  
342 311
div.objects-list > div:hover table {
343 312
	background: white;
344 313
}
345 314

  
346
table.dgs-summary td {
347
	border: 1px solid #ccc;
348
	border-width: 1px 1px 0 0;
349
	padding: 1ex 1ex 0.4ex 1ex;
350
}
351

  
352
table.dgs-summary td:last-child {
353
	text-align: center;
354
}
355

  
356
div.objects-list > div table.dgs-summary a {
357
	padding: 0;
358
}
359

  
360
table.dgs-summary button {
361
	padding-left: 1ex;
362
	padding-right: 1ex;
363
}
364

  
365
.dgs-page > div {
366
	width: calc(50% - 1ex);
367
	height: 100%;
368
}
369

  
370
.dgs-view {
371
	overflow-y: auto;
372
	padding: 1ex;
373
}
374

  
375
div.qualif-source p,
376
div.qualif-source p label {
377
	display: inline-block;
378
}
379

  
380
div.qualif-source input#id_post_date {
381
	width: 12ex;
382
}
383

  
384 315
div.buttons {
385 316
	margin-top: 1em;
386 317
}
......
439 370
	width: 70%;
440 371
}
441 372

  
442
div#copies p {
443
	margin: 0;
444
	margin-left: 1ex;
445
}
446

  
447
div#copies p:first-child {
448
	margin-left: 0;
449
}
450

  
451 373
div.add-formdef-reference {
452 374
	background: white;
453 375
	position: relative;
......
527 449
	margin-right: 1ex;
528 450
}
529 451

  
530
p.waiting-for,
531
p.no-mail-table {
532
	padding: 10px;
533
}
534

  
535
button.save.pinned {
536
	background: transparent;
537
	box-shadow: none;
538
	border: none;
539
}
540

  
541 452
input#id_keywords {
542 453
	width: 90%;
543 454
}
......
748 659
#content button#create-new-contact {
749 660
	height: auto;
750 661
}
751

  
752
table.main td {
753
	border-left: 1px solid #f0f0f0;
754
}
welco/static/js/welco.js
373 373
    $('#postit > div.content').load('/ajax/mail/note/' + source_pk);
374 374
  });
375 375

  
376
  $(document).on('gadjo:dialog-done welco:load-copies', function(ev) {
377
    if (ev.target && ev.target.id == 'create-new-contact') return;
378
    var source_pk = $('div.source .active[data-source-pk]').data('source-pk');
379
    $.getJSON(
380
       '/ajax/copies/' + source_pk + '/',
381
       function(data) {
382
         $('.info span').text(data.info);
383
         $('.avis span').text(data.avis);
384
         $('.mandatory_avis span').text(data.mandatory_avis);
385
       }
386
    );
387
  });
388

  
389 376
  $(document).on('gadjo:dialog-done', function(ev, data) {
390 377
    if (ev.target && ev.target.id != 'create-new-contact') return;
391 378
    if (data.err == 1) {
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
-