Compare commits

..

1 Commits

Author SHA1 Message Date
Nicolas Roche 150841377a kb: use html.unescape (#55535) 2021-07-13 11:15:45 +02:00
84 changed files with 393 additions and 342 deletions

View File

@ -1,6 +1,2 @@
# trivial: apply black
ce7f2dd5000cf1eb462ae18aeeb5ab66913b452f
# trivial: apply isort & pyupgrade
250ef2e74ae674f7039318787ba85b63f2825c7d
# misc: apply double-quote-string-fixer (#79788)
103dff03cb7862a6dde22b1a358a7af7f86424fc

View File

@ -1,22 +1,8 @@
# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: double-quote-string-fixer
- repo: https://github.com/psf/black
rev: 22.3.0
rev: 20.8b1
hooks:
- id: black
args: ['--target-version', 'py37', '--skip-string-normalization', '--line-length', '110']
- repo: https://github.com/PyCQA/isort
rev: 5.12.0
hooks:
- id: isort
args: ['--profile', 'black', '--line-length', '110']
- repo: https://github.com/asottile/pyupgrade
rev: v3.1.0
hooks:
- id: pyupgrade
args: ['--keep-percent-format', '--py37-plus']

14
Jenkinsfile vendored
View File

@ -23,18 +23,10 @@ pipeline {
stage('Packaging') {
steps {
script {
env.SHORT_JOB_NAME=sh(
returnStdout: true,
// given JOB_NAME=gitea/project/PR-46, returns project
// given JOB_NAME=project/main, returns project
script: '''
echo "${JOB_NAME}" | sed "s/gitea\\///" | awk -F/ '{print $1}'
'''
).trim()
if (env.GIT_BRANCH == 'main' || env.GIT_BRANCH == 'origin/main') {
sh "sudo -H -u eobuilder /usr/local/bin/eobuilder -d bullseye,bookworm ${SHORT_JOB_NAME}"
if (env.JOB_NAME == 'welco' && env.GIT_BRANCH == 'origin/main') {
sh 'sudo -H -u eobuilder /usr/local/bin/eobuilder welco'
} else if (env.GIT_BRANCH.startsWith('hotfix/')) {
sh "sudo -H -u eobuilder /usr/local/bin/eobuilder -d bullseye,bookworm --branch ${env.GIT_BRANCH} --hotfix ${SHORT_JOB_NAME}"
sh "sudo -H -u eobuilder /usr/local/bin/eobuilder --branch ${env.GIT_BRANCH} --hotfix welco"
}
}
}

13
README
View File

@ -44,17 +44,8 @@ black is used to format the code, using thoses parameters:
black --target-version py37 --skip-string-normalization --line-length 110
isort is used to format the imports, using those parameters:
isort --profile black --line-length 110
pyupgrade is used to automatically upgrade syntax, using those parameters:
pyupgrade --keep-percent-format --py37-plus
There is .pre-commit-config.yaml to use pre-commit to automatically run black,
isort and pyupgrade before commits. (execute `pre-commit install` to install
the git hook.)
There is .pre-commit-config.yaml to use pre-commit to automatically run black
before commits. (execute `pre-commit install` to install the git hook.)
License

1
debian/compat vendored Normal file
View File

@ -0,0 +1 @@
9

5
debian/control vendored
View File

@ -2,13 +2,13 @@ Source: welco
Maintainer: Frederic Peters <fpters@entrouvert.com>
Section: python
Priority: optional
Build-Depends: python3-setuptools, python3-all, debhelper-compat (= 12), python3-django, dh-python
Build-Depends: python3-setuptools, python3-all, debhelper (>= 9), python3-django, dh-python, dh-systemd
Standards-Version: 3.9.6
Package: python3-welco
Architecture: all
Depends: ${misc:Depends}, ${python3:Depends},
python3-django (>= 2:2.2),
python3-django (>= 1:1.11),
python3-gadjo,
python3-requests (>= 2.11),
python3-django-haystack (>= 2.4.0),
@ -25,7 +25,6 @@ Depends: ${misc:Depends},
python3-django-tenant-schemas,
python3-psycopg2,
python3-django-mellon,
python3-uwsgidecorators,
python3-xstatic-select2,
uwsgi,
uwsgi-plugin-python3,

2
debian/rules vendored
View File

@ -5,7 +5,7 @@ export PYBUILD_NAME=welco
export PYBUILD_DISABLE=test
%:
dh $@ --with python3 --buildsystem=pybuild
dh $@ --with python3,systemd --buildsystem=pybuild
override_dh_install:
dh_install

4
debian/uwsgi.ini vendored
View File

@ -9,10 +9,6 @@ http-socket = /run/welco/welco.sock
chmod-socket = 666
vacuum = true
spooler-processes = 3
spooler-python-import = hobo.provisionning.spooler
spooler-max-tasks = 20
master = true
processes = 5
harakiri = 120

2
debian/welco.cron.d vendored
View File

@ -1,4 +1,4 @@
SHELL=/bin/sh
PATH=/usr/sbin:/usr/sbin:/usr/bin:/sbin:/bin
*/10 6-22 * * * welco /usr/bin/welco-manage tenant_command feed_mail_maarch --all-tenants -v0
*/10 6-22 * * * welco /usr/bin/welco-manage tenant_command feed_mail_maarch --all-tenants

View File

@ -1,3 +1,3 @@
#!/bin/sh
/sbin/runuser -u welco /usr/bin/welco-manage -- tenant_command clearsessions --all-tenants -v0
/sbin/runuser -u welco /usr/bin/welco-manage -- tenant_command clearsessions --all-tenants

1
debian/welco.dirs vendored
View File

@ -1,6 +1,5 @@
/etc/welco
/usr/lib/welco
/var/lib/welco/collectstatic
/var/lib/welco/spooler
/var/lib/welco/tenants
/var/log/welco

1
debian/welco.init vendored
View File

@ -38,7 +38,6 @@ GROUP=$NAME
DAEMON_ARGS=${DAEMON_ARGS:-"--pidfile=$PIDFILE
--uid $USER --gid $GROUP
--ini /etc/$NAME/uwsgi.ini
--spooler /var/lib/$NAME/spooler/
--daemonize /var/log/uwsgi.$NAME.log"}
# Load the VERBOSE setting and other rcS variables

View File

@ -20,7 +20,6 @@ case "$1" in
# ensure dirs ownership
chown $USER:$GROUP /var/log/$NAME
chown $USER:$GROUP /var/lib/$NAME/collectstatic
chown $USER:$GROUP /var/lib/$NAME/spooler
chown $USER:$GROUP /var/lib/$NAME/tenants
# create a secret file
SECRET_FILE=$CONFIG_DIR/secret

View File

@ -1,18 +1,16 @@
[Unit]
Description=Welco
After=network.target postgresql.service
After=network.target syslog.target postgresql.service
Wants=postgresql.service
[Service]
SyslogIdentifier=uwsgi/%p
Environment=WELCO_SETTINGS_FILE=/usr/lib/%p/debian_config.py
Environment=LANG=C.UTF-8
User=%p
Group=%p
ExecStartPre=/usr/bin/welco-manage migrate_schemas --noinput --verbosity 1
ExecStartPre=/usr/bin/welco-manage collectstatic --noinput
ExecStartPre=/bin/mkdir -p /var/lib/welco/spooler/%m/
ExecStart=/usr/bin/uwsgi --ini /etc/%p/uwsgi.ini --spooler /var/lib/welco/spooler/%m/
ExecStart=/usr/bin/uwsgi --ini /etc/%p/uwsgi.ini
ExecReload=/bin/kill -HUP $MAINPID
KillSignal=SIGQUIT
TimeoutStartSec=0
@ -20,6 +18,7 @@ PrivateTmp=true
Restart=on-failure
RuntimeDirectory=welco
Type=notify
StandardError=syslog
NotifyAccess=all
[Install]

View File

@ -2,8 +2,8 @@
import os
import sys
if __name__ == '__main__':
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'welco.settings')
if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "welco.settings")
from django.core.management import execute_from_command_line

View File

@ -1,14 +1,15 @@
#! /usr/bin/env python
# -*- coding: utf-8 -*-
import os
import subprocess
import sys
from distutils.cmd import Command
from setuptools.command.install_lib import install_lib as _install_lib
from distutils.command.build import build as _build
from distutils.command.sdist import sdist
from setuptools import find_packages, setup
from setuptools.command.install_lib import install_lib as _install_lib
from distutils.cmd import Command
from setuptools import setup, find_packages
class eo_sdist(sdist):
@ -28,7 +29,7 @@ def get_version():
tag exists, take 0.0- and add the length of the commit log.
"""
if os.path.exists('VERSION'):
with open('VERSION') as v:
with open('VERSION', 'r') as v:
return v.read()
if os.path.exists('.git'):
p = subprocess.Popen(
@ -106,14 +107,15 @@ setup(
'License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
],
install_requires=[
'django>=2.2,<3.3',
'django>=1.11,<2.3',
'gadjo',
'django-ckeditor<4.5.4',
'django-haystack<3.2.2',
'django-taggit<3.1.1',
'djangorestframework>=3.3,<3.15',
'django-haystack<2.8',
'django-taggit',
'djangorestframework>=3.3,<3.10',
'requests',
'whoosh',
'XStatic-Select2',

View File

@ -14,8 +14,8 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import django_webtest
import pytest
import django_webtest
@pytest.fixture

View File

@ -14,11 +14,11 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from unittest import mock
import httmock
import mock
import pytest
import requests
from django.contrib.contenttypes.models import ContentType
from django.core.files.base import ContentFile

View File

@ -15,6 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import pytest
from django.core.management import call_command
from django.core.management.base import CommandError

View File

@ -14,14 +14,14 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import json
import httmock
import json
import requests
from webtest import Upload
from django.contrib.contenttypes.models import ContentType
from django.core.files.base import ContentFile
from django.utils.encoding import force_str
from webtest import Upload
from django.utils.encoding import force_text
from welco.sources.mail.models import Mail
@ -74,11 +74,11 @@ def test_qualification_save_view(settings, app, db):
def response_get(url, request):
headers = {'content-type': 'application/json'}
content = {
'err': 0,
'data': [
"err": 0,
"data": [
{
'title': 'Foo',
'slug': 'foo',
"title": "Foo",
"slug": "foo",
}
],
}
@ -134,9 +134,9 @@ def test_reject_view(settings, app, user):
@httmock.urlmatch(netloc='maarch.example.net', path='/rest/res/resource/status', method='PUT')
def response_ok(url, request):
assert json.loads(force_str(request.body)) == {'status': 'FOO', 'resId': ['42']}
assert json.loads(force_text(request.body)) == {'status': 'FOO', 'resId': ['42']}
headers = {'content-type': 'application/json'}
content = {'maarch_say': 'ok'}
content = {"maarch_say": "ok"}
return httmock.response(200, content, headers)
with httmock.HTTMock(response_ok):

View File

@ -14,10 +14,10 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from unittest import mock
import httmock
import mock
import pytest
from django.contrib.contenttypes.models import ContentType
from django.core.files.base import ContentFile
@ -201,7 +201,7 @@ def test_menu_json_view(app, user, mail_group, phone_group, counter_group, kb_gr
app.set_user(user.username)
resp = app.get('/menu.json', status=200)
assert resp.content_type == 'application/json'
assert sorted(x['label'] for x in resp.json) == ['Call Center', 'Counter', 'Knowledge Base', 'Mails']
assert sorted([x['label'] for x in resp.json]) == ['Call Center', 'Counter', 'Knowledge Base', 'Mails']
resp = app.get('/menu.json?callback=foo', status=200)
assert resp.content_type == 'application/javascript'

View File

@ -14,9 +14,9 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from unittest import mock
import mock
import pytest
from django.test import override_settings
from welco.forms import QualificationForm

View File

@ -17,12 +17,14 @@
import json
import pytest
from django.contrib.auth.models import User
from django.utils.encoding import force_str
from httmock import HTTMock, urlmatch
from django.utils.encoding import force_text
from httmock import urlmatch, HTTMock
class BaseMock:
class BaseMock(object):
def __init__(self, netloc):
self.netloc = netloc
self.clear()
@ -49,7 +51,7 @@ class BaseMock:
class MaarchMock(BaseMock):
def list_endpoint(self, url, request):
self.requests.append(('list_endpoint', url, request, json.loads(force_str(request.body))))
self.requests.append(('list_endpoint', url, request, json.loads(force_text(request.body))))
return {
'content': json.dumps(self.next_response()),
'headers': {
@ -61,13 +63,13 @@ class MaarchMock(BaseMock):
list_endpoint.path = '^/rest/res/list$'
def update_external_infos(self, url, request):
self.requests.append(('update_external_infos', url, request, json.loads(force_str(request.body))))
self.requests.append(('update_external_infos', url, request, json.loads(force_text(request.body))))
return json.dumps({})
update_external_infos.path = '^/rest/res/externalInfos$'
def update_status(self, url, request):
self.requests.append(('update_status', url, request, json.loads(force_str(request.body))))
self.requests.append(('update_status', url, request, json.loads(force_text(request.body))))
return {
'content': json.dumps(self.next_response()),
'headers': {
@ -79,7 +81,7 @@ class MaarchMock(BaseMock):
update_status.path = '^/rest/res/resource/status$'
def post_courrier(self, url, request):
self.requests.append(('post_courrier', url, request, json.loads(force_str(request.body))))
self.requests.append(('post_courrier', url, request, json.loads(force_text(request.body))))
post_courrier.path = '^/rest/res$'
@ -175,10 +177,8 @@ PDF_MOCK = b'%PDF-1.4 ...'
def test_feed(settings, app, maarch, wcs, user):
import base64
from django.contrib.contenttypes.models import ContentType
from django.core.management import call_command
from django.contrib.contenttypes.models import ContentType
from welco.sources.mail.models import Mail
app.set_user(user.username)
@ -194,7 +194,7 @@ def test_feed(settings, app, maarch, wcs, user):
'resources': [
{
'res_id': 1,
'fileBase64Content': force_str(base64.b64encode(PDF_MOCK)),
'fileBase64Content': force_text(base64.b64encode(PDF_MOCK)),
}
],
}

View File

@ -18,9 +18,11 @@ import json
import re
import pytest
from django.test import override_settings
from django.urls import reverse
from django.utils.encoding import force_str
from django.test import override_settings
from django.utils import six
from django.utils.encoding import force_text
from django.utils.timezone import now, timedelta
from welco.sources.phone import models
@ -164,10 +166,10 @@ def test_current_calls(user, client):
assert response['content-type'] == 'application/json'
payload = response.json()
assert isinstance(payload, dict)
assert set(payload.keys()) == {'err', 'data'}
assert set(payload.keys()) == set(['err', 'data'])
assert payload['err'] == 0
data = payload['data']
assert set(data.keys()) == {'calls', 'lines', 'all-lines'}
assert set(data.keys()) == set(['calls', 'lines', 'all-lines'])
assert isinstance(data['calls'], list)
assert isinstance(data['lines'], list)
assert isinstance(data['all-lines'], list)
@ -175,14 +177,14 @@ def test_current_calls(user, client):
assert len(data['lines']) == 5
assert len(data['all-lines']) == 10
for call in data['calls']:
assert set(call.keys()) <= {'caller', 'callee', 'start', 'data'}
assert isinstance(call['caller'], str)
assert isinstance(call['callee'], str)
assert isinstance(call['start'], str)
assert set(call.keys()) <= set(['caller', 'callee', 'start', 'data'])
assert isinstance(call['caller'], six.string_types)
assert isinstance(call['callee'], six.string_types)
assert isinstance(call['start'], six.string_types)
if 'data' in call:
assert isinstance(call['data'], dict)
assert len([call for call in data['lines'] if isinstance(call, str)]) == 5
assert len([call for call in data['all-lines'] if isinstance(call, str)]) == 10
assert len([call for call in data['lines'] if isinstance(call, six.string_types)]) == 5
assert len([call for call in data['all-lines'] if isinstance(call, six.string_types)]) == 10
# unregister user to all remaining lines
for number in range(0, 5):
@ -192,9 +194,9 @@ def test_current_calls(user, client):
assert response['content-type'] == 'application/json'
payload = response.json()
assert isinstance(payload, dict)
assert set(payload.keys()) == {'err', 'data'}
assert set(payload.keys()) == set(['err', 'data'])
assert payload['err'] == 0
assert set(payload['data'].keys()) == {'calls', 'lines', 'all-lines'}
assert set(payload['data'].keys()) == set(['calls', 'lines', 'all-lines'])
assert len(payload['data']['calls']) == 0
assert len(payload['data']['lines']) == 0
assert len(payload['data']['all-lines']) == 10
@ -227,18 +229,18 @@ def test_phone_zone(user, client):
client.login(username='toto', password='toto')
response = client.get(reverse('phone-zone'))
assert response.status_code == 200
assert 'You do not have a phoneline configured' in force_str(response.content)
assert 'You do not have a phoneline configured' in force_text(response.content)
models.PhoneLine.take(callee='102', user=user)
response = client.get(reverse('phone-zone'))
assert response.status_code == 200
assert 'You do not have a phoneline configured' not in force_str(response.content)
assert '<li>102' in force_str(response.content)
assert 'data-callee="102"' in force_str(response.content)
assert 'You do not have a phoneline configured' not in force_text(response.content)
assert '<li>102' in force_text(response.content)
assert 'data-callee="102"' in force_text(response.content)
currents = re.search(
'<div id="source-mainarea" ' 'data-current-calls="/api/phone/current-calls/">' '(.*?)</div>',
force_str(response.content),
force_text(response.content),
flags=re.DOTALL,
)
assert currents.group(1).strip() == ''
@ -249,7 +251,7 @@ def test_phone_zone(user, client):
assert response.status_code == 200
response = client.get(reverse('phone-zone'))
assert response.status_code == 200
assert '<h1>Current Call: <strong>003369999999</strong></h1>' in force_str(response.content)
assert '<h1>Current Call: <strong>003369999999</strong></h1>' in force_text(response.content)
# simulate a mellon user
session = client.session
@ -257,19 +259,19 @@ def test_phone_zone(user, client):
session.save()
response = client.get(reverse('phone-zone'))
assert response.status_code == 200
assert 'agent007' not in force_str(response.content)
assert 'data-callee="agent007"' not in force_str(response.content)
assert '<li>102' in force_str(response.content)
assert 'data-callee="102"' in force_str(response.content)
assert 'agent007' not in force_text(response.content)
assert 'data-callee="agent007"' not in force_text(response.content)
assert '<li>102' in force_text(response.content)
assert 'data-callee="102"' in force_text(response.content)
with override_settings(PHONE_AUTOTAKE_MELLON_USERNAME=True):
response = client.get(reverse('phone-zone'))
assert response.status_code == 200
assert '<h1>Current Call: <strong>003369999999</strong></h1>' in force_str(response.content)
assert 'agent007' in force_str(response.content)
assert 'data-callee="agent007"' in force_str(response.content)
assert '<li>102' in force_str(response.content)
assert 'data-callee="102"' in force_str(response.content)
assert '<h1>Current Call: <strong>003369999999</strong></h1>' in force_text(response.content)
assert 'agent007' in force_text(response.content)
assert 'data-callee="agent007"' in force_text(response.content)
assert '<li>102' in force_text(response.content)
assert 'data-callee="102"' in force_text(response.content)
def test_call_expiration(user, client):

11
tox.ini
View File

@ -1,7 +1,5 @@
[tox]
envlist =
py3-django32-drf314
py3-django32-black-coverage-pylint-drf312
envlist = py3-django22-black-coverage-pylint
toxworkdir = {env:TMPDIR:/tmp}/tox-{env:USER}/welco/{env:BRANCH_NAME:}
[testenv]
@ -14,7 +12,7 @@ setenv =
fast: FAST=--nomigrations
coverage: COVERAGE=--junitxml=junit-{envname}.xml --cov-report xml --cov-report html --cov=welco/
deps =
django32: django>=3.2,<3.3
django22: django>=2.2,<2.3
pytest-cov
pytest-django
pytest!=5.3.3
@ -22,16 +20,13 @@ deps =
mock<4
httmock
python-dateutil
pylint<3
astroid<3
pylint
pylint-django
django-webtest
pyquery
lxml
git+https://git.entrouvert.org/debian/django-ckeditor.git
black: pre-commit
drf312: djangorestframework>=3.12,<3.13
drf314: djangorestframework>=3.14,<3.15
commands =
pylint: ./pylint.sh welco/
py.test {env:COVERAGE:} {posargs:tests/}

View File

@ -15,7 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.apps import apps
from django.urls import include, re_path
from django.conf.urls import include, url
def register_urls(urlpatterns):
@ -25,9 +25,9 @@ def register_urls(urlpatterns):
if hasattr(app, 'get_before_urls'):
urls = app.get_before_urls()
if urls:
pre_urls.append(re_path('^', include(urls)))
pre_urls.append(url('^', include(urls)))
if hasattr(app, 'get_after_urls'):
urls = app.get_after_urls()
if urls:
post_urls.append(re_path('^', include(urls)))
post_urls.append(url('^', include(urls)))
return pre_urls + urlpatterns + post_urls

View File

@ -15,8 +15,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django import forms
from django.utils.translation import gettext_lazy as _
from django.utils.translation import pgettext_lazy
from django.utils.translation import ugettext_lazy as _, pgettext_lazy
DEFAULT_TITLE_CHOICES = (
('', ''),

View File

@ -17,9 +17,9 @@
import json
import logging
import random
import requests
import time
import requests
from django import template
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
@ -27,14 +27,14 @@ from django.core.exceptions import PermissionDenied
from django.http import HttpResponse
from django.template import RequestContext
from django.views.decorators.csrf import csrf_exempt
from django.views.generic import FormView, TemplateView
from django.views.generic import TemplateView, FormView
from welco.utils import get_wcs_data, sign_url
from .forms import ContactAddForm
class HomeZone:
class HomeZone(object):
def __init__(self, request):
self.request = request
@ -47,7 +47,7 @@ class ContactsZone(TemplateView):
template_name = 'contacts/zone.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context = super(ContactsZone, self).get_context_data(**kwargs)
context['source_pk'] = self.request.GET.get('source_pk')
if 'source_pk' in self.request.GET:
source_class = ContentType.objects.get(id=self.request.GET['source_type']).model_class()
@ -68,7 +68,7 @@ zone = csrf_exempt(ContactsZone.as_view())
def search_json(request):
user_groups = {x.name for x in request.user.groups.all()}
user_groups = set([x.name for x in request.user.groups.all()])
for channel in settings.CHANNEL_ROLES:
channel_groups = set(settings.CHANNEL_ROLES[channel])
if user_groups.intersection(channel_groups):
@ -104,7 +104,7 @@ class ContactDetailFragmentView(TemplateView):
template_name = 'contacts/contact_detail_fragment.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context = super(ContactDetailFragmentView, self).get_context_data(**kwargs)
user_id = self.kwargs.get('slug').split('-')[-1]
user_details = get_wcs_data('api/users/%s/' % user_id)

View File

@ -15,7 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django import forms
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ugettext_lazy as _
from .utils import get_wcs_options
@ -24,7 +24,7 @@ class QualificationForm(forms.Form):
formdef_reference = forms.CharField(label=_('Associated Form'))
def __init__(self, user, *args, **kwargs):
super().__init__(*args, **kwargs)
super(QualificationForm, self).__init__(*args, **kwargs)
params = {'backoffice-submission': 'on'}
if hasattr(user, 'saml_identifiers') and user.saml_identifiers.exists():
params['NameID'] = user.saml_identifiers.first().name_id

View File

@ -38,4 +38,4 @@ class PageForm(forms.ModelForm):
i += 1
slug = '%s-%s' % (base_slug, i)
self.instance.slug = slug
return super().save(commit=commit)
return super(PageForm, self).save(commit=commit)

View File

@ -1,5 +1,8 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
import ckeditor.fields
from django.db import migrations, models
class Migration(migrations.Migration):

View File

@ -1,4 +1,7 @@
from django.db import migrations, models
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):

View File

@ -1,5 +1,8 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
import taggit.managers
from django.db import migrations, models
class Migration(migrations.Migration):

View File

@ -1,4 +1,7 @@
from django.db import migrations, models
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):

View File

@ -14,10 +14,11 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from ckeditor.fields import RichTextField
from django.db import models
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from django.db import models
from django.utils.translation import ugettext_lazy as _
from ckeditor.fields import RichTextField
from taggit.managers import TaggableManager

View File

@ -17,6 +17,7 @@
import html
from django.utils.html import strip_tags
from haystack import indexes
from .models import Page

View File

@ -20,19 +20,20 @@ from django import template
from django.conf import settings
from django.contrib.auth.decorators import login_required, user_passes_test
from django.core.exceptions import PermissionDenied
from django.urls import reverse_lazy
from django.db.models import Count
from django.http import HttpResponse, HttpResponseRedirect
from django.template import RequestContext
from django.urls import reverse_lazy
from django.views.decorators.csrf import csrf_exempt
from django.views.generic import CreateView, DeleteView, DetailView, ListView, TemplateView, UpdateView
from django.views.generic import DetailView, CreateView, UpdateView, ListView, DeleteView, TemplateView
from haystack.forms import SearchForm
from haystack.generic_views import SearchView
from haystack.query import SearchQuerySet
from taggit.models import Tag
from .forms import PageForm
from .models import Page
from .forms import PageForm
def check_user_perms(user, access=False):
@ -41,7 +42,7 @@ def check_user_perms(user, access=False):
allowed_roles.extend(settings.KB_ACCESS_ROLES)
if settings.KB_ROLE:
allowed_roles.append(settings.KB_ROLE) # legacy
user_groups = {x.name for x in user.groups.all()}
user_groups = set([x.name for x in user.groups.all()])
return user_groups.intersection(allowed_roles)
@ -55,10 +56,10 @@ class PageListView(ListView):
def dispatch(self, request, *args, **kwargs):
check_request_perms(request, access=True)
return super().dispatch(request, *args, **kwargs)
return super(PageListView, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context = super(PageListView, self).get_context_data(**kwargs)
context['form'] = SearchForm()
context['can_manage'] = check_user_perms(self.request.user)
return context
@ -73,7 +74,7 @@ class PageAddView(CreateView):
def dispatch(self, request, *args, **kwargs):
check_request_perms(request)
return super().dispatch(request, *args, **kwargs)
return super(PageAddView, self).dispatch(request, *args, **kwargs)
page_add = login_required(PageAddView.as_view())
@ -85,7 +86,7 @@ class PageEditView(UpdateView):
def dispatch(self, request, *args, **kwargs):
check_request_perms(request)
return super().dispatch(request, *args, **kwargs)
return super(PageEditView, self).dispatch(request, *args, **kwargs)
page_edit = login_required(PageEditView.as_view())
@ -96,10 +97,10 @@ class PageDetailView(DetailView):
def dispatch(self, request, *args, **kwargs):
check_request_perms(request, access=True)
return super().dispatch(request, *args, **kwargs)
return super(PageDetailView, self).dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context = super(PageDetailView, self).get_context_data(**kwargs)
context['can_manage'] = check_user_perms(self.request.user)
return context
@ -121,7 +122,7 @@ class PageDeleteView(DeleteView):
def dispatch(self, request, *args, **kwargs):
check_request_perms(request)
return super().dispatch(request, *args, **kwargs)
return super(PageDeleteView, self).dispatch(request, *args, **kwargs)
page_delete = login_required(PageDeleteView.as_view())
@ -133,7 +134,7 @@ class PageSearchView(SearchView):
def dispatch(self, request, *args, **kwargs):
check_request_perms(request, access=True)
return super().dispatch(request, *args, **kwargs)
return super(PageSearchView, self).dispatch(request, *args, **kwargs)
page_search = login_required(PageSearchView.as_view())
@ -143,7 +144,7 @@ class KbZone(TemplateView):
template_name = 'kb/zone.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context = super(KbZone, self).get_context_data(**kwargs)
context['source_pk'] = self.request.GET.get('source_pk')
context['form'] = SearchForm()
context['tags'] = (
@ -189,7 +190,7 @@ def page_search_json(request):
return response
class HomeZone:
class HomeZone(object):
def __init__(self, request):
self.request = request

View File

@ -14,15 +14,16 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import ckeditor.widgets
from django.urls import reverse
from django.forms.utils import flatatt
from django.template.loader import render_to_string
from django.urls import reverse
from django.utils.encoding import force_str
from django.utils.encoding import force_text
from django.utils.html import conditional_escape
from django.utils.safestring import mark_safe
from django.utils.translation import get_language
import ckeditor.widgets
def ckeditor_render(self, name, value, attrs=None, renderer=None):
if value is None:
@ -41,7 +42,7 @@ def ckeditor_render(self, name, value, attrs=None, renderer=None):
# Force to text to evaluate possible lazy objects
external_plugin_resources = [
[force_str(a), force_str(b), force_str(c)] for a, b, c in self.external_plugin_resources
[force_text(a), force_text(b), force_text(c)] for a, b, c in self.external_plugin_resources
]
return mark_safe(
@ -49,7 +50,7 @@ def ckeditor_render(self, name, value, attrs=None, renderer=None):
'ckeditor/widget.html',
{
'final_attrs': flatatt(final_attrs),
'value': conditional_escape(force_str(value)),
'value': conditional_escape(force_text(value)),
'id': final_attrs['id'],
'config': ckeditor.widgets.json_encode(self.config),
'external_plugin_resources': ckeditor.widgets.json_encode(external_plugin_resources),

View File

@ -1,4 +1,7 @@
from django.db import migrations, models
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):

View File

@ -1,4 +1,7 @@
from django.db import migrations, models
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):

View File

@ -1,4 +1,7 @@
from django.db import migrations, models
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):

View File

@ -1,4 +1,7 @@
from django.db import migrations, models
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):

View File

@ -1,4 +1,7 @@
from django.db import migrations, models
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):

View File

@ -1,4 +1,7 @@
from django.db import migrations, models
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):

View File

@ -1,4 +1,7 @@
from django.db import migrations, models
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):

View File

@ -1,3 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models

View File

@ -14,13 +14,13 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.contrib.contenttypes.fields import GenericForeignKey
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from django.db import models
from django.utils.translation import ugettext_lazy as _
from welco.utils import get_wcs_formdef_details, get_wcs_services, push_wcs_formdata
from welco.utils import get_wcs_formdef_details, push_wcs_formdata, get_wcs_services
class Association(models.Model):

View File

@ -1,3 +1,5 @@
# -*- coding: utf-8 -*-
"""
Django settings for welco project.
@ -9,9 +11,8 @@ https://docs.djangoproject.com/en/1.7/ref/settings/
"""
import os
from django.conf import global_settings
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ugettext_lazy as _
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(__file__))

View File

@ -1,4 +1,7 @@
from django.db import migrations, models
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):

View File

@ -16,7 +16,7 @@
from django.contrib.contenttypes.fields import GenericRelation
from django.db import models
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ugettext_lazy as _
from welco.qualif.models import Association

View File

@ -14,10 +14,10 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.urls import path
from django.conf.urls import url
from . import views
urlpatterns = [
path('ajax/counter/zone/', views.zone, name='counter-zone'),
url(r'^ajax/counter/zone/$', views.zone, name='counter-zone'),
]

View File

@ -18,17 +18,17 @@ import json
from django import template
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.contrib.contenttypes.models import ContentType
from django.http import HttpResponse
from django.template import RequestContext
from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse
from django.views.generic import TemplateView
from .models import CounterPresence
class Home:
class Home(object):
source_key = 'counter'
def __init__(self, request, **kwargs):
@ -46,7 +46,7 @@ class CounterZone(TemplateView):
template_name = 'welco/counter_home.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context = super(CounterZone, self).get_context_data(**kwargs)
context['source_type'] = ContentType.objects.get_for_model(CounterPresence)
new_source = CounterPresence()
new_source.save()

View File

@ -26,9 +26,8 @@ class AppConfig(django.apps.AppConfig):
return urls.urlpatterns
def ready(self):
from django.db.models import signals
from welco.qualif.models import Association
from django.db.models import signals
signals.post_save.connect(self.association_post_save, sender=Association)

View File

@ -15,8 +15,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django import forms
from django.utils.translation import ugettext_lazy as _
from django.conf import settings
from django.utils.translation import gettext_lazy as _
class MailQualificationForm(forms.Form):

View File

@ -15,10 +15,12 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import base64
import urllib.parse
from dateutil.parser import parse as parse_datetime
from django.utils import six
from django.utils.six.moves.urllib import parse as urlparse
import requests
from dateutil.parser import parse as parse_datetime
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry
@ -27,7 +29,7 @@ class MaarchError(Exception):
pass
class MaarchCourrier:
class MaarchCourrier(object):
url = None
username = None
password = None
@ -42,7 +44,7 @@ class MaarchCourrier:
def __repr__(self):
return '<MaarchCourrier url:%s>' % self.url
class Courrier:
class Courrier(object):
content = None
format = None
status = None
@ -87,7 +89,7 @@ class MaarchCourrier:
data = {key: self.__dict__[key] for key in self.__dict__ if key not in excluded_keys}
if data:
for key, value in data.items():
if isinstance(value, str):
if isinstance(value, six.string_types):
d.append({'column': key, 'value': value, 'type': 'string'})
elif isinstance(value, int):
d.append({'column': key, 'value': str(value), 'type': 'int'})
@ -151,19 +153,19 @@ class MaarchCourrier:
@property
def list_url(self):
return urllib.parse.urljoin(self.url, 'rest/res/list')
return urlparse.urljoin(self.url, 'rest/res/list')
@property
def update_external_infos_url(self):
return urllib.parse.urljoin(self.url, 'rest/res/externalInfos')
return urlparse.urljoin(self.url, 'rest/res/externalInfos')
@property
def update_status_url(self):
return urllib.parse.urljoin(self.url, 'rest/res/resource/status')
return urlparse.urljoin(self.url, 'rest/res/resource/status')
@property
def post_courrier_url(self):
return urllib.parse.urljoin(self.url, 'rest/res')
return urlparse.urljoin(self.url, 'rest/res')
def get_courriers(self, clause, fields=None, limit=None, include_file=False, order_by=None):
if fields:

View File

@ -14,10 +14,10 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
from optparse import make_option
import os
from django.core.files.base import ContentFile
from django.core.files import File
from django.core.management.base import BaseCommand, CommandError
from ...models import Mail
@ -35,7 +35,7 @@ class Command(BaseCommand):
continue
if not open(filepath).read(5) == '%PDF-':
continue
mail = Mail(content=ContentFile(open(filepath).read(), name=os.path.basename(filepath)))
mail = Mail(content=File(open(filepath)))
mail.scanner_category = kwargs.get('category')
mail.save()
count += 1

View File

@ -14,12 +14,12 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
from optparse import make_option
import os
from django.conf import settings
from django.core.files.base import ContentFile
from django.core.management.base import BaseCommand, CommandError
from django.conf import settings
from django.db import transaction
from ...models import Mail

View File

@ -1,4 +1,7 @@
from django.db import migrations, models
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):

View File

@ -1,4 +1,7 @@
from django.db import migrations, models
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):

View File

@ -1,4 +1,7 @@
from django.db import migrations, models
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):

View File

@ -1,4 +1,7 @@
from django.db import migrations, models
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):

View File

@ -1,4 +1,7 @@
from django.db import migrations, models
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):

View File

@ -1,4 +1,7 @@
from django.db import migrations, models
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):

View File

@ -1,4 +1,7 @@
from django.db import migrations, models
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):

View File

@ -1,4 +1,7 @@
from django.db import migrations, models
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):

View File

@ -1,3 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models

View File

@ -1,3 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models

View File

@ -1,3 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models

View File

@ -1,3 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models

View File

@ -15,17 +15,17 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import re
import requests
import subprocess
import requests
from django.conf import settings
from django.contrib.contenttypes.fields import GenericRelation
from django.contrib.contenttypes.models import ContentType
from django.urls import reverse
from django.db import models
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ugettext_lazy as _
from welco.qualif.models import Association
from welco.utils import get_wcs_data

View File

@ -14,17 +14,17 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.urls import path, re_path
from django.conf.urls import url
from .views import edit_note, feeder, mail_count, mail_response, note, qualification_save, reject, viewer
from .views import viewer, feeder, qualification_save, edit_note, note, reject, mail_count, mail_response
urlpatterns = [
path('mail/viewer/', viewer, name='mail-viewer'),
path('mail/feeder/', feeder, name='mail-feeder'),
path('ajax/mail/reject', reject, name='mail-reject'),
path('ajax/qualification-mail-save', qualification_save, name='qualif-mail-save'),
path('ajax/mail/edit-note/', edit_note, name='mail-edit-note'),
re_path(r'^ajax/mail/note/(?P<pk>\w+)$', note, name='mail-note'),
path('ajax/count/mail/', mail_count, name='mail-count'),
path('api/mail/response/', mail_response, name='mail-api-response'),
url('viewer/$', viewer, name='mail-viewer'),
url('mail/feeder/$', feeder, name='mail-feeder'),
url(r'^ajax/mail/reject$', reject, name='mail-reject'),
url(r'^ajax/qualification-mail-save$', qualification_save, name='qualif-mail-save'),
url(r'^ajax/mail/edit-note/$', edit_note, name='mail-edit-note'),
url(r'^ajax/mail/note/(?P<pk>\w+)$', note, name='mail-note'),
url(r'^ajax/count/mail/$', mail_count, name='mail-count'),
url(r'^api/mail/response/$', mail_response, name='mail-api-response'),
]

View File

@ -32,7 +32,7 @@ class WelcoMaarchCourrier(MaarchCourrier):
grc_response_status,
batch_size=10,
):
super().__init__(url, username, password)
super(WelcoMaarchCourrier, self).__init__(url, username, password)
self.grc_status = grc_status
self.grc_received_status = grc_received_status
self.grc_send_status = grc_send_status
@ -49,7 +49,7 @@ class WelcoMaarchCourrier(MaarchCourrier):
)
def get_mail(self, mail_id):
return self.get_courriers(clause='res_id=%s' % mail_id)[0]
return self.get_courriers(clause="res_id=%s" % mail_id)[0]
def set_grc_received_status(self, mails):
self.update_status(mails, self.grc_received_status)

View File

@ -18,25 +18,27 @@ import json
import logging
from django import template
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.contrib.contenttypes.models import ContentType
from django.db.transaction import atomic
from django.http import HttpResponse, HttpResponseRedirect
from django.template import RequestContext
from django.contrib import messages
from django.urls import reverse
from django.utils.translation import gettext_lazy as _
from django.template import RequestContext
from django.http import HttpResponse, HttpResponseRedirect
from django.utils.translation import ugettext_lazy as _
from django.views.decorators.csrf import csrf_exempt
from django.views.generic import TemplateView
from rest_framework import authentication, permissions, serializers, status
from django.db.transaction import atomic
from rest_framework import authentication, serializers, permissions, status
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
from welco.utils import response_for_json
from .forms import MailQualificationForm
from .models import Mail
from .utils import MaarchError, get_maarch
from .forms import MailQualificationForm
from .utils import get_maarch, MaarchError
logger = logging.getLogger(__name__)
@ -62,7 +64,7 @@ class Feeder(TemplateView):
feeder = login_required(csrf_exempt(Feeder.as_view()))
class Home:
class Home(object):
source_key = 'mail'
display_filter = True
allow_reject = True
@ -110,7 +112,7 @@ class EditNote(TemplateView):
template_name = 'welco/mail_edit_note.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context = super(EditNote, self).get_context_data(**kwargs)
context['mail'] = Mail.objects.get(id=self.request.GET['mail'])
return context

View File

@ -1,3 +1,6 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models

View File

@ -1,8 +1,11 @@
import datetime
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
from django.utils.timezone import now, utc
from django.utils.timezone import utc
from django.utils.timezone import now
import datetime
from django.conf import settings
class Migration(migrations.Migration):

View File

@ -1,4 +1,7 @@
from django.db import migrations, models
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):

View File

@ -1,4 +1,7 @@
from django.db import migrations, models
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):

View File

@ -18,10 +18,10 @@ import logging
from django.conf import settings
from django.contrib.contenttypes.fields import GenericRelation
from django.db import models
from django.urls import reverse
from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.utils.timezone import now, timedelta
from django.utils.translation import gettext_lazy as _
from welco.qualif.models import Association

View File

@ -14,15 +14,15 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.urls import path, re_path
from django.conf.urls import url
from . import views
urlpatterns = [
path('ajax/phone/zone/', views.zone, name='phone-zone'),
path('api/phone/call-event/', views.call_event, name='phone-call-event'),
re_path(r'^api/phone/active-call/(?P<pk>\w+)/$', views.active_call, name='phone-active-call'),
path('api/phone/current-calls/', views.current_calls, name='phone-current-calls'),
path('api/phone/take-line/', views.take_line, name='phone-take-line'),
path('api/phone/release-line/', views.release_line, name='phone-release-line'),
url(r'^ajax/phone/zone/$', views.zone, name='phone-zone'),
url(r'^api/phone/call-event/$', views.call_event, name='phone-call-event'),
url(r'^api/phone/active-call/(?P<pk>\w+)/$', views.active_call, name='phone-active-call'),
url(r'^api/phone/current-calls/$', views.current_calls, name='phone-current-calls'),
url(r'^api/phone/take-line/$', views.take_line, name='phone-take-line'),
url(r'^api/phone/release-line/$', views.release_line, name='phone-release-line'),
]

View File

@ -19,19 +19,20 @@ import logging
from django import template
from django.conf import settings
from django.contrib.auth.decorators import login_required
from django.contrib.contenttypes.models import ContentType
from django.http import HttpResponse, HttpResponseBadRequest
from django.template import RequestContext
from django.utils.encoding import force_str
from django.utils.timezone import now
from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth.decorators import login_required
from django.http import HttpResponseBadRequest, HttpResponse
from django.utils import six
from django.utils.encoding import force_text
from django.utils.timezone import now
from django.views.generic import TemplateView
from .models import PhoneCall, PhoneLine
class Home:
class Home(object):
source_key = 'phone'
def __init__(self, request, **kwargs):
@ -59,7 +60,7 @@ class PhoneZone(TemplateView):
username = username[0].split('@', 1)[0][:80] # remove realm
if username:
PhoneLine.take(callee=username, user=self.request.user)
context = super().get_context_data(**kwargs)
context = super(PhoneZone, self).get_context_data(**kwargs)
context['source_type'] = ContentType.objects.get_for_model(PhoneCall)
context['phonelines'] = PhoneLine.objects.filter(users__id=self.request.user.id)
context['phonecalls'] = PhoneCall.get_current_calls(self.request.user)
@ -84,25 +85,22 @@ def call_event(request):
"""
logger = logging.getLogger(__name__)
try:
payload = json.loads(force_str(request.body))
payload = json.loads(force_text(request.body))
assert isinstance(payload, dict), 'payload is not a JSON object'
assert set(payload.keys()) <= {
'event',
'caller',
'callee',
'data',
}, 'payload keys must be "event", "caller", "callee" and optionnaly "data"'
assert {'event', 'caller', 'callee'} <= set(
assert set(payload.keys()) <= set(
['event', 'caller', 'callee', 'data']
), 'payload keys must be "event", "caller", "callee" and optionnaly "data"'
assert set(['event', 'caller', 'callee']) <= set(
payload.keys()
), 'payload keys must be "event", "caller", "callee" and optionnaly "data"'
assert payload['event'] in ('start', 'stop'), 'event must be "start" or "stop"'
assert isinstance(payload['caller'], str), 'caller must be a string'
assert isinstance(payload['callee'], str), 'callee must be a string'
assert isinstance(payload['caller'], six.string_types), 'caller must be a string'
assert isinstance(payload['callee'], six.string_types), 'callee must be a string'
if 'data' in payload:
assert isinstance(payload['data'], dict), 'data must be a JSON object'
except (TypeError, ValueError, AssertionError) as e:
return HttpResponseBadRequest(
json.dumps({'err': 1, 'msg': force_str(e)}), content_type='application/json'
json.dumps({'err': 1, 'msg': force_text(e)}), content_type='application/json'
)
# janitoring: stop active calls to the callee
if settings.PHONE_ONE_CALL_PER_CALLEE:
@ -202,15 +200,15 @@ def take_line(request):
"""
logger = logging.getLogger(__name__)
try:
payload = json.loads(force_str(request.body))
payload = json.loads(force_text(request.body))
assert isinstance(payload, dict), 'payload is not a JSON object'
assert list(payload.keys()) == ['callee'], 'payload must have only one key: callee'
except (TypeError, ValueError, AssertionError) as e:
return HttpResponseBadRequest(
json.dumps({'err': 1, 'msg': force_str(e)}), content_type='application/json'
json.dumps({'err': 1, 'msg': force_text(e)}), content_type='application/json'
)
PhoneLine.take(payload['callee'], request.user)
logger.info('user %s took line %s', request.user, payload['callee'])
logger.info(u'user %s took line %s', request.user, payload['callee'])
return HttpResponse(json.dumps({'err': 0}), content_type='application/json')
@ -223,13 +221,13 @@ def release_line(request):
"""
logger = logging.getLogger(__name__)
try:
payload = json.loads(force_str(request.body))
payload = json.loads(force_text(request.body))
assert isinstance(payload, dict), 'payload is not a JSON object'
assert list(payload.keys()) == ['callee'], 'payload must have only one key: callee'
except (TypeError, ValueError, AssertionError) as e:
return HttpResponseBadRequest(
json.dumps({'err': 1, 'msg': force_str(e)}), content_type='application/json'
json.dumps({'err': 1, 'msg': force_text(e)}), content_type='application/json'
)
PhoneLine.release(payload['callee'], request.user)
logger.info('user %s released line %s', request.user, payload['callee'])
logger.info(u'user %s released line %s', request.user, payload['callee'])
return HttpResponse(json.dumps({'err': 0}), content_type='application/json')

View File

@ -14,69 +14,71 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from ckeditor import views as ckeditor_views
from django.conf import settings
from django.conf.urls import include, url
from django.conf.urls.static import static
from django.contrib import admin
from django.contrib.staticfiles.urls import staticfiles_urlpatterns
from django.urls import include, path, re_path
from django.views.decorators.cache import never_cache
import welco.contacts.views
import welco.kb.views
import welco.views
from ckeditor import views as ckeditor_views
from . import apps
from .kb.views import kb_manager_required
import welco.views
import welco.contacts.views
import welco.kb.views
urlpatterns = [
path('', welco.views.home, name='home'),
path('mail/', welco.views.home_mail, name='home-mail'),
path('phone/', welco.views.home_phone, name='home-phone'),
path('counter/', welco.views.home_counter, name='home-counter'),
re_path(r'^', include('welco.sources.phone.urls')),
re_path(r'^', include('welco.sources.counter.urls')),
path('ajax/qualification', welco.views.qualification, name='qualif-zone'),
re_path(
url(r'^$', welco.views.home, name='home'),
url(r'^mail/$', welco.views.home_mail, name='home-mail'),
url(r'^phone/$', welco.views.home_phone, name='home-phone'),
url(r'^counter/$', welco.views.home_counter, name='home-counter'),
url(r'^', include('welco.sources.phone.urls')),
url(r'^', include('welco.sources.counter.urls')),
url(r'^ajax/qualification$', welco.views.qualification, name='qualif-zone'),
url(
r'^ajax/remove-association/(?P<pk>\w+)$',
welco.views.remove_association,
name='ajax-remove-association',
),
re_path(r'^ajax/create-formdata/(?P<pk>\w+)$', welco.views.create_formdata, name='ajax-create-formdata'),
path('ajax/kb', welco.kb.views.zone, name='kb-zone'),
path('kb/', welco.kb.views.page_list, name='kb-home'),
path('kb/add/', welco.kb.views.page_add, name='kb-page-add'),
path('kb/search/', welco.kb.views.page_search, name='kb-page-search'),
path('kb/search/json/', welco.kb.views.page_search_json, name='kb-page-search-json'),
re_path(r'^kb/(?P<slug>[\w-]+)/$', welco.kb.views.page_detail, name='kb-page-view'),
re_path(r'^ajax/kb/(?P<slug>[\w-]+)/$', welco.kb.views.page_detail_fragment, name='kb-page-fragment'),
re_path(r'^kb/(?P<slug>[\w-]+)/edit$', welco.kb.views.page_edit, name='kb-page-edit'),
re_path(r'^kb/(?P<slug>[\w-]+)/delete$', welco.kb.views.page_delete, name='kb-page-delete'),
path('ajax/contacts', welco.contacts.views.zone, name='contacts-zone'),
path('contacts/search/json/', welco.contacts.views.search_json, name='contacts-search-json'),
re_path(
url(r'^ajax/create-formdata/(?P<pk>\w+)$', welco.views.create_formdata, name='ajax-create-formdata'),
url(r'^ajax/kb$', welco.kb.views.zone, name='kb-zone'),
url(r'^kb/$', welco.kb.views.page_list, name='kb-home'),
url(r'^kb/add/$', welco.kb.views.page_add, name='kb-page-add'),
url(r'^kb/search/$', welco.kb.views.page_search, name='kb-page-search'),
url(r'^kb/search/json/$', welco.kb.views.page_search_json, name='kb-page-search-json'),
url(r'^kb/(?P<slug>[\w-]+)/$', welco.kb.views.page_detail, name='kb-page-view'),
url(r'^ajax/kb/(?P<slug>[\w-]+)/$', welco.kb.views.page_detail_fragment, name='kb-page-fragment'),
url(r'^kb/(?P<slug>[\w-]+)/edit$', welco.kb.views.page_edit, name='kb-page-edit'),
url(r'^kb/(?P<slug>[\w-]+)/delete$', welco.kb.views.page_delete, name='kb-page-delete'),
url(r'^ajax/contacts$', welco.contacts.views.zone, name='contacts-zone'),
url(r'^contacts/search/json/$', welco.contacts.views.search_json, name='contacts-search-json'),
url(
r'^ajax/contacts/(?P<slug>[\w-]+)/$',
welco.contacts.views.contact_detail_fragment,
name='contact-page-fragment',
),
path('contacts/add/', welco.contacts.views.contact_add, name='contacts-add'),
re_path(
url(r'^contacts/add/$', welco.contacts.views.contact_add, name='contacts-add'),
url(
r'^ajax/summary/(?P<source_type>\w+)/(?P<source_pk>\w+)/$',
welco.views.wcs_summary,
name='wcs-summary',
),
re_path(r'^admin/', admin.site.urls),
path('logout/', welco.views.logout, name='auth_logout'),
path('login/', welco.views.login, name='auth_login'),
re_path(r'^menu.json$', welco.views.menu_json, name='menu_json'),
re_path(r'^ckeditor/upload/', kb_manager_required(ckeditor_views.upload), name='ckeditor_upload'),
re_path(
url(r'^admin/', admin.site.urls),
url(r'^logout/$', welco.views.logout, name='auth_logout'),
url(r'^login/$', welco.views.login, name='auth_login'),
url(r'^menu.json$', welco.views.menu_json, name='menu_json'),
url(r'^ckeditor/upload/', kb_manager_required(ckeditor_views.upload), name='ckeditor_upload'),
url(
r'^ckeditor/browse/', never_cache(kb_manager_required(ckeditor_views.browse)), name='ckeditor_browse'
),
]
if 'mellon' in settings.INSTALLED_APPS:
urlpatterns.append(re_path(r'^accounts/mellon/', include('mellon.urls')))
urlpatterns.append(url(r'^accounts/mellon/', include('mellon.urls')))
# static and media files
urlpatterns += staticfiles_urlpatterns()

View File

@ -16,25 +16,25 @@
import base64
import datetime
import hashlib
import hmac
import hashlib
import json
import random
import re
import urllib.parse
import requests
from django.conf import settings
from django.core.cache import cache
from django.http import HttpResponse, HttpResponseBadRequest
from django.utils.encoding import smart_bytes
from django.utils.http import quote, urlencode
from django.utils.http import urlencode, quote
from django.utils.six.moves.urllib import parse as urlparse
def sign_url(url, key, algo='sha256', timestamp=None, nonce=None):
parsed = urllib.parse.urlparse(url)
parsed = urlparse.urlparse(url)
new_query = sign_query(parsed.query, key, algo, timestamp, nonce)
return urllib.parse.urlunparse(parsed[:4] + (new_query,) + parsed[5:])
return urlparse.urlunparse(parsed[:4] + (new_query,) + parsed[5:])
def sign_query(query, key, algo='sha256', timestamp=None, nonce=None):

View File

@ -16,20 +16,20 @@
import json
from django import template
from django.conf import settings
from django.contrib.auth import logout as auth_logout
from django.contrib.auth import views as auth_views
from django.contrib.auth.decorators import login_required
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import PermissionDenied
from django.urls import reverse
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import resolve_url
from django import template
from django.template import RequestContext
from django.urls import reverse
from django.utils.encoding import force_str
from django.utils.encoding import force_text
from django.utils.http import quote
from django.utils.translation import gettext_lazy as _
from django.utils.translation import ugettext_lazy as _
from django.views.decorators.csrf import csrf_exempt
from django.views.generic import TemplateView
@ -38,14 +38,13 @@ if 'mellon' in settings.INSTALLED_APPS:
else:
get_idps = lambda: []
from .contacts.views import HomeZone as ContactsHomeZone
from .forms import QualificationForm
from .kb.views import HomeZone as KbHomeZone
from .kb.views import check_user_perms as check_kb_user_perms
from .qualif.models import Association
from .sources.counter.views import Home as CounterHome
from .sources.mail.views import Home as MailHome
from .sources.phone.views import Home as PhoneHome
from .sources.counter.views import Home as CounterHome
from .qualif.models import Association
from .kb.views import HomeZone as KbHomeZone, check_user_perms as check_kb_user_perms
from .contacts.views import HomeZone as ContactsHomeZone
from .forms import QualificationForm
class LoginView(auth_views.LoginView):
@ -60,7 +59,7 @@ class LoginView(auth_views.LoginView):
except KeyError:
return HttpResponseBadRequest('invalid value for "next" parameter')
return HttpResponseRedirect(resolve_url('mellon_login') + '?next=' + quoted_next_url)
return super().dispatch(request, *args, **kwargs)
return super(LoginView, self).dispatch(request, *args, **kwargs)
login = LoginView.as_view()
@ -81,7 +80,7 @@ class Qualification(TemplateView):
template_name = 'welco/qualification_no_validation.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context = super(Qualification, self).get_context_data(**kwargs)
context['form'] = QualificationForm(self.request.user)
context['source_type'] = self.request.GET['source_type']
source_type = ContentType.objects.get(id=self.request.GET['source_type'])
@ -113,21 +112,21 @@ class ChannelHome(TemplateView):
source_klass = MailHome
def check_user_ok(self):
user_groups = {x.name for x in self.request.user.groups.all()}
user_groups = set([x.name for x in self.request.user.groups.all()])
channel_groups = set(settings.CHANNEL_ROLES[self.source_klass.source_key])
return user_groups.intersection(channel_groups)
def get_context_data(self, **kwargs):
if not self.check_user_ok():
raise PermissionDenied()
context = super().get_context_data(**kwargs)
context = super(ChannelHome, self).get_context_data(**kwargs)
context['panels'] = [{'key': x, 'zone_url': x + '-zone'} for x in settings.SCREEN_PANELS]
context['source'] = self.source_klass(self.request, **kwargs)
context['kb'] = KbHomeZone(self.request)
context['contacts'] = ContactsHomeZone(self.request)
context['channels'] = []
user_groups = {x.name for x in self.request.user.groups.all()}
user_groups = set([x.name for x in self.request.user.groups.all()])
for channel in settings.CHANNEL_ROLES:
channel_groups = set(settings.CHANNEL_ROLES[channel])
if user_groups.intersection(channel_groups):
@ -137,7 +136,7 @@ class ChannelHome(TemplateView):
@login_required
def home(request):
user_groups = {x.name for x in request.user.groups.all()}
user_groups = set([x.name for x in request.user.groups.all()])
for channel in settings.CHANNEL_ROLES:
channel_groups = set(settings.CHANNEL_ROLES[channel])
if user_groups.intersection(channel_groups):
@ -210,7 +209,7 @@ def create_formdata(request, *args, **kwargs):
@login_required
def menu_json(request):
response = HttpResponse(content_type='application/json')
user_groups = {x.name for x in request.user.groups.all()}
user_groups = set([x.name for x in request.user.groups.all()])
menu = []
labels = {
'mail': _('Mails'),
@ -222,7 +221,7 @@ def menu_json(request):
if user_groups.intersection(channel_groups):
menu.append(
{
'label': force_str(labels.get(channel)),
'label': force_text(labels.get(channel)),
'slug': channel,
'url': request.build_absolute_uri(reverse('home-%s' % channel)),
}
@ -230,7 +229,7 @@ def menu_json(request):
if check_kb_user_perms(request.user, access=True):
menu.append(
{
'label': force_str(_('Knowledge Base')),
'label': force_text(_('Knowledge Base')),
'slug': 'book',
'url': request.build_absolute_uri(reverse('kb-home')),
}

View File

@ -9,7 +9,7 @@ https://docs.djangoproject.com/en/1.7/howto/deployment/wsgi/
import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'welco.settings')
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "welco.settings")
from django.core.wsgi import get_wsgi_application