Merge pull request #867 from taigaio/us/2119/get-in-touch-with-the-project-admins
US 2219: Get in touch with the project adminsremotes/origin/issue/4795/notification_even_they_are_disabled
commit
bebee6c8f3
|
@ -1,5 +1,9 @@
|
||||||
# Changelog #
|
# Changelog #
|
||||||
|
|
||||||
|
## 3.1.0 ¿¿?? (¿¿??)
|
||||||
|
|
||||||
|
### Features
|
||||||
|
- Contact with the project: if the projects have this module enabled Taiga users can contact them.
|
||||||
|
|
||||||
## 3.0.0 Stellaria Borealis (2016-10-02)
|
## 3.0.0 Stellaria Borealis (2016-10-02)
|
||||||
|
|
||||||
|
|
|
@ -306,6 +306,7 @@ INSTALLED_APPS = [
|
||||||
"taiga.projects.tasks",
|
"taiga.projects.tasks",
|
||||||
"taiga.projects.issues",
|
"taiga.projects.issues",
|
||||||
"taiga.projects.wiki",
|
"taiga.projects.wiki",
|
||||||
|
"taiga.projects.contact",
|
||||||
"taiga.searches",
|
"taiga.searches",
|
||||||
"taiga.timeline",
|
"taiga.timeline",
|
||||||
"taiga.mdrender",
|
"taiga.mdrender",
|
||||||
|
|
|
@ -157,7 +157,6 @@
|
||||||
padding-bottom:20px;
|
padding-bottom:20px;
|
||||||
padding-left:20px;
|
padding-left:20px;
|
||||||
padding-top:20px;
|
padding-top:20px;
|
||||||
text-align:center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -188,6 +187,13 @@
|
||||||
background: #aad400;
|
background: #aad400;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
width: 90%;
|
||||||
|
margin: 0 auto;
|
||||||
|
border-bottom: 1px solid #e1e1e1;
|
||||||
|
border-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.bodyContent img{
|
.bodyContent img{
|
||||||
display:inline;
|
display:inline;
|
||||||
height:auto;
|
height:auto;
|
||||||
|
@ -392,20 +398,29 @@
|
||||||
<table border="0" cellpadding="0" cellspacing="0" width="100%" id="templateHeader">
|
<table border="0" cellpadding="0" cellspacing="0" width="100%" id="templateHeader">
|
||||||
<tr>
|
<tr>
|
||||||
<td valign="top" class="headerContent">
|
<td valign="top" class="headerContent">
|
||||||
<img src="{{ static("emails/top-bg-update.png") }}" />
|
<img src="{{ static('emails/top-bg-update.png') }}" >
|
||||||
<a href="{{ resolve_front_url("home") }}" title="Taiga">
|
<a href="{{ resolve_front_url('home') }}" title="Taiga">
|
||||||
<img src="{{ static("emails/logo-color.png") }}" id="headerImage" alt="Taiga logo" />
|
<img src="{{ static('emails/logo-color.png') }}" id="headerImage" alt="Taiga logo" />
|
||||||
</a>
|
</a>
|
||||||
{% block body %}
|
</td>
|
||||||
{% endblock %}
|
</tr>
|
||||||
|
</table>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center" valign="top">
|
||||||
|
<table border="0" cellpadding="0" cellspacing="0" width="100%" id="templateBody">
|
||||||
|
<tr>
|
||||||
|
<td valign="top" class="bodyContent">
|
||||||
|
{% block body %}{% endblock %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% block social %}
|
{% block social %}
|
||||||
<tr>
|
<tr>
|
||||||
<td valign="top" class="social-links">
|
<td valign="top" class="social-links">
|
||||||
<a href="{{ sr("social.twitter_url") }}" title="{{ _("Follow us on Twitter") }}" style="color: #9dce0a">{{ _("Twitter") }}</a>
|
<a href="{{ sr('social.twitter_url') }}" title="{{ _('Follow us on Twitter') }}" style="color: #9dce0a">{{ _("Twitter") }}</a>
|
||||||
<a href="{{ sr("social.github_url") }}" title="{{ _("Get the code on GitHub") }}" style="color: #9dce0a">{{ _("GitHub") }}</a>
|
<a href="{{ sr('social.github_url') }}" title="{{ _('Get the code on GitHub') }}" style="color: #9dce0a">{{ _("GitHub") }}</a>
|
||||||
<a href="{{ sr("taigaio_url") }}" title="{{ _("Visit our website") }}" style="color: #9dce0a">{{ _("Taiga.io") }}</a>
|
<a href="{{ sr('taigaio_url') }}" title="{{ _('Visit our website') }}" style="color: #9dce0a">{{ _("Taiga.io") }}</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2014-2016 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
# Copyright (C) 2014-2016 Jesús Espino <jespinog@gmail.com>
|
||||||
|
# Copyright (C) 2014-2016 David Barragán <bameda@dbarragan.com>
|
||||||
|
# Copyright (C) 2014-2016 Alejandro Alonso <alejandro.alonso@kaleidos.net>
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
# published by the Free Software Foundation, either version 3 of the
|
||||||
|
# License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# 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 import admin
|
||||||
|
|
||||||
|
from . import models
|
||||||
|
|
||||||
|
|
||||||
|
class ContactEntryAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ["created_date", "project", "user"]
|
||||||
|
list_display_links = list_display
|
||||||
|
list_filter = ["created_date", ]
|
||||||
|
date_hierarchy = "created_date"
|
||||||
|
ordering = ("-created_date", "id")
|
||||||
|
search_fields = ("project__name", "project__slug", "user__username", "user__email", "user__full_name")
|
||||||
|
|
||||||
|
admin.site.register(models.ContactEntry, ContactEntryAdmin)
|
|
@ -0,0 +1,49 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2014-2016 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
# Copyright (C) 2014-2016 Jesús Espino <jespinog@gmail.com>
|
||||||
|
# Copyright (C) 2014-2016 David Barragán <bameda@dbarragan.com>
|
||||||
|
# Copyright (C) 2014-2016 Alejandro Alonso <alejandro.alonso@kaleidos.net>
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
# published by the Free Software Foundation, either version 3 of the
|
||||||
|
# License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# 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 taiga.base import status
|
||||||
|
from taiga.base.api.mixins import CreateModelMixin, BlockedByProjectMixin
|
||||||
|
from taiga.base.api.viewsets import GenericViewSet
|
||||||
|
|
||||||
|
from . import models
|
||||||
|
from . import permissions
|
||||||
|
from . import services
|
||||||
|
from . import validators
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
||||||
|
class ContactViewSet(BlockedByProjectMixin, CreateModelMixin, GenericViewSet):
|
||||||
|
permission_classes = (permissions.ContactPermission,)
|
||||||
|
validator_class = validators.ContactEntryValidator
|
||||||
|
model = models.ContactEntry
|
||||||
|
|
||||||
|
def create(self, *args, **kwargs):
|
||||||
|
response = super().create(*args, **kwargs)
|
||||||
|
|
||||||
|
if response.status_code == status.HTTP_201_CREATED:
|
||||||
|
if settings.CELERY_ENABLED:
|
||||||
|
services.send_contact_email.delay(self.object.id)
|
||||||
|
else:
|
||||||
|
services.send_contact_email(self.object.id)
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
def pre_save(self, obj):
|
||||||
|
obj.user = self.request.user
|
||||||
|
super().pre_save(obj)
|
|
@ -0,0 +1,35 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.2 on 2016-11-10 15:18
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('projects', '0056_auto_20161110_1518'),
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ContactEntry',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('comment', models.TextField(verbose_name='comment')),
|
||||||
|
('created_date', models.DateTimeField(auto_now_add=True, verbose_name='created date')),
|
||||||
|
('project', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='contact_entries', to='projects.Project', verbose_name='project')),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='contact_entries', to=settings.AUTH_USER_MODEL, verbose_name='user')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'contact entry',
|
||||||
|
'ordering': ['-created_date', 'id'],
|
||||||
|
'verbose_name_plural': 'contact entries',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
|
@ -0,0 +1,39 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2014-2016 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
# Copyright (C) 2014-2016 Jesús Espino <jespinog@gmail.com>
|
||||||
|
# Copyright (C) 2014-2016 David Barragán <bameda@dbarragan.com>
|
||||||
|
# Copyright (C) 2014-2016 Alejandro Alonso <alejandro.alonso@kaleidos.net>
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
# published by the Free Software Foundation, either version 3 of the
|
||||||
|
# License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# 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.conf import settings
|
||||||
|
from django.db import models
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
|
||||||
|
class ContactEntry(models.Model):
|
||||||
|
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="contact_entries",
|
||||||
|
verbose_name=_("user"))
|
||||||
|
|
||||||
|
project = models.ForeignKey("projects.Project", null=False, blank=False,
|
||||||
|
related_name="contact_entries", verbose_name=_("project"))
|
||||||
|
|
||||||
|
comment = models.TextField(null=False, blank=False, verbose_name=_("comment"))
|
||||||
|
|
||||||
|
created_date = models.DateTimeField(null=False, blank=False, auto_now_add=True,
|
||||||
|
verbose_name=_("created date"))
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = "contact entry"
|
||||||
|
verbose_name_plural = "contact entries"
|
||||||
|
ordering = ["-created_date", "id"]
|
|
@ -0,0 +1,29 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2014-2016 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
# Copyright (C) 2014-2016 Jesús Espino <jespinog@gmail.com>
|
||||||
|
# Copyright (C) 2014-2016 David Barragán <bameda@dbarragan.com>
|
||||||
|
# Copyright (C) 2014-2016 Alejandro Alonso <alejandro.alonso@kaleidos.net>
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
# published by the Free Software Foundation, either version 3 of the
|
||||||
|
# License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# 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 taiga.base.api.permissions import PermissionComponent
|
||||||
|
from taiga.base.api.permissions import TaigaResourcePermission
|
||||||
|
|
||||||
|
|
||||||
|
class IsContactActivated(PermissionComponent):
|
||||||
|
def check_permissions(self, request, view, obj=None):
|
||||||
|
return request.user.is_authenticated() and obj.project.is_contact_activated
|
||||||
|
|
||||||
|
|
||||||
|
class ContactPermission(TaigaResourcePermission):
|
||||||
|
create_perms = IsContactActivated()
|
|
@ -0,0 +1,45 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2014-2016 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
# Copyright (C) 2014-2016 Jesús Espino <jespinog@gmail.com>
|
||||||
|
# Copyright (C) 2014-2016 David Barragán <bameda@dbarragan.com>
|
||||||
|
# Copyright (C) 2014-2016 Alejandro Alonso <alejandro.alonso@kaleidos.net>
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
# published by the Free Software Foundation, either version 3 of the
|
||||||
|
# License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# 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 taiga.base.mails import mail_builder
|
||||||
|
from taiga.celery import app
|
||||||
|
from taiga.front.templatetags.functions import resolve as resolve_front_url
|
||||||
|
from taiga.users.services import get_user_photo_url
|
||||||
|
|
||||||
|
from . import models
|
||||||
|
|
||||||
|
|
||||||
|
@app.task
|
||||||
|
def send_contact_email(contact_entry_id):
|
||||||
|
contact_entry = models.ContactEntry.objects.filter(id=contact_entry_id).first()
|
||||||
|
if contact_entry is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
ctx = {
|
||||||
|
"comment": contact_entry.comment,
|
||||||
|
"full_name": contact_entry.user.get_full_name(),
|
||||||
|
"project_name": contact_entry.project.name,
|
||||||
|
"photo_url": get_user_photo_url(contact_entry.user),
|
||||||
|
"user_profile_url": resolve_front_url("user", contact_entry.user.username),
|
||||||
|
"project_settings_url": resolve_front_url("project-admin", contact_entry.project.slug),
|
||||||
|
}
|
||||||
|
users = contact_entry.project.get_users().exclude(id=contact_entry.user_id)
|
||||||
|
addresses = ", ".join([u.email for u in users])
|
||||||
|
email = mail_builder.contact_notification(addresses, ctx)
|
||||||
|
email.extra_headers["Reply-To"] = ", ".join([contact_entry.user.email])
|
||||||
|
email.send()
|
|
@ -0,0 +1,24 @@
|
||||||
|
{% extends "emails/base-body-html.jinja" %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
<p>
|
||||||
|
{% if photo_url %}
|
||||||
|
<a href="{{ user_profile_url }}" title="{{ full_name }}" >
|
||||||
|
<img style="vertical-align:middle;" src="{{ photo_url }}" alt="{{ full_name }}" width="60"/>
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
|
{% trans full_name=full_name, comment=comment, project_name=project_name %}
|
||||||
|
<a style="font-weight: bold;text-decoration:none;color:#222;" href="{{ user_profile_url }}" title="{{ full_name }}">{{ full_name }}</a> has written to {{ project_name }}
|
||||||
|
{% endtrans %}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>{{ comment }}</p>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
{% trans project_name=project_name %}
|
||||||
|
You are receiving this message because you are listed as administrator of the project titled {{ project_name }}. If you don't want members of the Taiga community contacting your project, please <a href="{{ project_settings_url }}">update your project settings</a> to prevent such contacts. Regular communications amongst members of the project will not be affected.
|
||||||
|
{% endtrans %}
|
||||||
|
</p>
|
||||||
|
{% endblock %}
|
|
@ -0,0 +1,9 @@
|
||||||
|
{% trans full_name=full_name, comment=comment, project_name=project_name %}
|
||||||
|
{{ full_name }} has written to {{ project_name }}
|
||||||
|
{% endtrans %}
|
||||||
|
---------
|
||||||
|
{{ comment }}
|
||||||
|
---------
|
||||||
|
{% trans project_name=project_name %}
|
||||||
|
You are receiving this message because you are listed as administrator of the project titled {{ project_name }}. If you don't want members of the Taiga community contacting your project, please <a href="{{ project_settings_url }}">update your project settings</a< to prevent such contacts. Regular communications amongst members of the project will not be affected.
|
||||||
|
{% endtrans %}
|
|
@ -0,0 +1,3 @@
|
||||||
|
{% trans full_name=full_name|safe, project_name=project_name|safe %}
|
||||||
|
[Taiga] {{ full_name }} has sent a message to the project {{ project_name }}
|
||||||
|
{% endtrans %}
|
|
@ -0,0 +1,28 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2014-2016 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
# Copyright (C) 2014-2016 Jesús Espino <jespinog@gmail.com>
|
||||||
|
# Copyright (C) 2014-2016 David Barragán <bameda@dbarragan.com>
|
||||||
|
# Copyright (C) 2014-2016 Alejandro Alonso <alejandro.alonso@kaleidos.net>
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
# published by the Free Software Foundation, either version 3 of the
|
||||||
|
# License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# 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 taiga.base.api import validators
|
||||||
|
|
||||||
|
from . import models
|
||||||
|
|
||||||
|
|
||||||
|
class ContactEntryValidator(validators.ModelValidator):
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = models.ContactEntry
|
||||||
|
read_only_fields = ("user", "created_date", )
|
|
@ -0,0 +1,25 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Generated by Django 1.9.2 on 2016-11-10 15:18
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('projects', '0055_json_to_jsonb'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='project',
|
||||||
|
name='is_contact_activated',
|
||||||
|
field=models.BooleanField(default=True, verbose_name='active contact'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='projecttemplate',
|
||||||
|
name='is_contact_activated',
|
||||||
|
field=models.BooleanField(default=True, verbose_name='active contact'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -164,7 +164,8 @@ class Project(ProjectDefaults, TaggedMixin, TagsColorsdMixin, models.Model):
|
||||||
total_milestones = models.IntegerField(null=True, blank=True,
|
total_milestones = models.IntegerField(null=True, blank=True,
|
||||||
verbose_name=_("total of milestones"))
|
verbose_name=_("total of milestones"))
|
||||||
total_story_points = models.FloatField(null=True, blank=True, verbose_name=_("total story points"))
|
total_story_points = models.FloatField(null=True, blank=True, verbose_name=_("total story points"))
|
||||||
|
is_contact_activated = models.BooleanField(default=True, null=False, blank=True,
|
||||||
|
verbose_name=_("active contact"))
|
||||||
is_epics_activated = models.BooleanField(default=False, null=False, blank=True,
|
is_epics_activated = models.BooleanField(default=False, null=False, blank=True,
|
||||||
verbose_name=_("active epics panel"))
|
verbose_name=_("active epics panel"))
|
||||||
is_backlog_activated = models.BooleanField(default=True, null=False, blank=True,
|
is_backlog_activated = models.BooleanField(default=True, null=False, blank=True,
|
||||||
|
@ -734,7 +735,8 @@ class ProjectTemplate(models.Model):
|
||||||
default_owner_role = models.CharField(max_length=50, null=False,
|
default_owner_role = models.CharField(max_length=50, null=False,
|
||||||
blank=False,
|
blank=False,
|
||||||
verbose_name=_("default owner's role"))
|
verbose_name=_("default owner's role"))
|
||||||
|
is_contact_activated = models.BooleanField(default=True, null=False, blank=True,
|
||||||
|
verbose_name=_("active contact"))
|
||||||
is_epics_activated = models.BooleanField(default=False, null=False, blank=True,
|
is_epics_activated = models.BooleanField(default=False, null=False, blank=True,
|
||||||
verbose_name=_("active epics panel"))
|
verbose_name=_("active epics panel"))
|
||||||
is_backlog_activated = models.BooleanField(default=True, null=False, blank=True,
|
is_backlog_activated = models.BooleanField(default=True, null=False, blank=True,
|
||||||
|
@ -780,6 +782,7 @@ class ProjectTemplate(models.Model):
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
def load_data_from_project(self, project):
|
def load_data_from_project(self, project):
|
||||||
|
self.is_contact_activated = project.is_contact_activated
|
||||||
self.is_epics_activated = project.is_epics_activated
|
self.is_epics_activated = project.is_epics_activated
|
||||||
self.is_backlog_activated = project.is_backlog_activated
|
self.is_backlog_activated = project.is_backlog_activated
|
||||||
self.is_kanban_activated = project.is_kanban_activated
|
self.is_kanban_activated = project.is_kanban_activated
|
||||||
|
@ -896,6 +899,7 @@ class ProjectTemplate(models.Model):
|
||||||
raise Exception("Project need an id (must be a saved project)")
|
raise Exception("Project need an id (must be a saved project)")
|
||||||
|
|
||||||
project.creation_template = self
|
project.creation_template = self
|
||||||
|
project.is_contact_activated = self.is_contact_activated
|
||||||
project.is_epics_activated = self.is_epics_activated
|
project.is_epics_activated = self.is_epics_activated
|
||||||
project.is_backlog_activated = self.is_backlog_activated
|
project.is_backlog_activated = self.is_backlog_activated
|
||||||
project.is_kanban_activated = self.is_kanban_activated
|
project.is_kanban_activated = self.is_kanban_activated
|
||||||
|
|
|
@ -213,6 +213,7 @@ class ProjectSerializer(serializers.LightSerializer):
|
||||||
members = MethodField()
|
members = MethodField()
|
||||||
total_milestones = Field()
|
total_milestones = Field()
|
||||||
total_story_points = Field()
|
total_story_points = Field()
|
||||||
|
is_contact_activated = Field()
|
||||||
is_epics_activated = Field()
|
is_epics_activated = Field()
|
||||||
is_backlog_activated = Field()
|
is_backlog_activated = Field()
|
||||||
is_kanban_activated = Field()
|
is_kanban_activated = Field()
|
||||||
|
@ -474,6 +475,7 @@ class ProjectTemplateSerializer(serializers.LightSerializer):
|
||||||
created_date = Field()
|
created_date = Field()
|
||||||
modified_date = Field()
|
modified_date = Field()
|
||||||
default_owner_role = Field()
|
default_owner_role = Field()
|
||||||
|
is_contact_activated = Field()
|
||||||
is_epics_activated = Field()
|
is_epics_activated = Field()
|
||||||
is_backlog_activated = Field()
|
is_backlog_activated = Field()
|
||||||
is_kanban_activated = Field()
|
is_kanban_activated = Field()
|
||||||
|
|
|
@ -223,6 +223,10 @@ router.register(r"history/task", TaskHistory, base_name="task-history")
|
||||||
router.register(r"history/issue", IssueHistory, base_name="issue-history")
|
router.register(r"history/issue", IssueHistory, base_name="issue-history")
|
||||||
router.register(r"history/wiki", WikiHistory, base_name="wiki-history")
|
router.register(r"history/wiki", WikiHistory, base_name="wiki-history")
|
||||||
|
|
||||||
|
# Contact
|
||||||
|
from taiga.projects.contact.api import ContactViewSet
|
||||||
|
router.register(r"contact", ContactViewSet, base_name="contact")
|
||||||
|
|
||||||
|
|
||||||
# Timelines
|
# Timelines
|
||||||
from taiga.timeline.api import ProfileTimeline
|
from taiga.timeline.api import ProfileTimeline
|
||||||
|
|
|
@ -0,0 +1,50 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2014-2016 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
# Copyright (C) 2014-2016 Jesús Espino <jespinog@gmail.com>
|
||||||
|
# Copyright (C) 2014-2016 David Barragán <bameda@dbarragan.com>
|
||||||
|
# Copyright (C) 2014-2016 Alejandro Alonso <alejandro.alonso@kaleidos.net>
|
||||||
|
# Copyright (C) 2014-2016 Anler Hernández <hello@anler.me>
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
# published by the Free Software Foundation, either version 3 of the
|
||||||
|
# License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# 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.core.urlresolvers import reverse
|
||||||
|
|
||||||
|
from tests import factories as f
|
||||||
|
from tests.utils import helper_test_http_method
|
||||||
|
|
||||||
|
from taiga.base.utils import json
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
pytestmark = pytest.mark.django_db
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def data():
|
||||||
|
m = type("Models", (object,), {})
|
||||||
|
m.user = f.UserFactory.create()
|
||||||
|
m.project = f.ProjectFactory.create()
|
||||||
|
f.MembershipFactory(user=m.project.owner, project=m.project, is_admin=True)
|
||||||
|
|
||||||
|
return m
|
||||||
|
|
||||||
|
|
||||||
|
def test_contact_create(client, data):
|
||||||
|
url = reverse("contact-list")
|
||||||
|
users = [None, data.user]
|
||||||
|
|
||||||
|
contact_data = json.dumps({
|
||||||
|
"project": data.project.id,
|
||||||
|
"comment": "Testing comment"
|
||||||
|
})
|
||||||
|
results = helper_test_http_method(client, 'post', url, contact_data, users)
|
||||||
|
assert results == [401, 201]
|
|
@ -0,0 +1,70 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Copyright (C) 2014-2016 Andrey Antukh <niwi@niwi.nz>
|
||||||
|
# Copyright (C) 2014-2016 Jesús Espino <jespinog@gmail.com>
|
||||||
|
# Copyright (C) 2014-2016 David Barragán <bameda@dbarragan.com>
|
||||||
|
# Copyright (C) 2014-2016 Alejandro Alonso <alejandro.alonso@kaleidos.net>
|
||||||
|
# Copyright (C) 2014-2016 Anler Hernández <hello@anler.me>
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
# published by the Free Software Foundation, either version 3 of the
|
||||||
|
# License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# 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.core import mail
|
||||||
|
from django.core.urlresolvers import reverse
|
||||||
|
|
||||||
|
from tests import factories as f
|
||||||
|
|
||||||
|
from taiga.base.utils import json
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
pytestmark = pytest.mark.django_db
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_comment(client):
|
||||||
|
user = f.UserFactory.create()
|
||||||
|
project = f.ProjectFactory.create()
|
||||||
|
f.MembershipFactory(user=project.owner, project=project, is_admin=True)
|
||||||
|
|
||||||
|
url = reverse("contact-list")
|
||||||
|
|
||||||
|
contact_data = json.dumps({
|
||||||
|
"project": project.id,
|
||||||
|
"comment": "Testing comment"
|
||||||
|
})
|
||||||
|
|
||||||
|
client.login(user)
|
||||||
|
|
||||||
|
assert len(mail.outbox) == 0
|
||||||
|
response = client.post(url, contact_data, content_type="application/json")
|
||||||
|
assert response.status_code == 201
|
||||||
|
assert len(mail.outbox) == 1
|
||||||
|
assert mail.outbox[0].to == [project.owner.email]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_comment_disabled(client):
|
||||||
|
user = f.UserFactory.create()
|
||||||
|
project = f.ProjectFactory.create()
|
||||||
|
project.is_contact_activated = False
|
||||||
|
project.save()
|
||||||
|
f.MembershipFactory(user=project.owner, project=project, is_admin=True)
|
||||||
|
|
||||||
|
url = reverse("contact-list")
|
||||||
|
|
||||||
|
contact_data = json.dumps({
|
||||||
|
"project": project.id,
|
||||||
|
"comment": "Testing comment"
|
||||||
|
})
|
||||||
|
|
||||||
|
client.login(user)
|
||||||
|
|
||||||
|
response = client.post(url, contact_data, content_type="application/json")
|
||||||
|
assert response.status_code == 403
|
Loading…
Reference in New Issue