diff --git a/issue/migrations/0015_auto_20140817_0239.py b/issue/migrations/0015_auto_20140817_0239.py
new file mode 100644
index 0000000..4d6b183
--- /dev/null
+++ b/issue/migrations/0015_auto_20140817_0239.py
@@ -0,0 +1,26 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import models, migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('issue', '0014_auto_20140815_0517'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='issue',
+ name='subscribers',
+ field=models.ManyToManyField(to='issue.User', blank=True, null=True),
+ preserve_default=True,
+ ),
+ migrations.AddField(
+ model_name='project',
+ name='subscribers',
+ field=models.ManyToManyField(to='issue.User', blank=True, null=True),
+ preserve_default=True,
+ ),
+ ]
diff --git a/issue/models.py b/issue/models.py
index 81d37e4..bdfc688 100644
--- a/issue/models.py
+++ b/issue/models.py
@@ -51,6 +51,9 @@ class Project(models.Model):
verbose_name="Do unregistered users have read access "
"to this project?")
+ subscribers = models.ManyToManyField(User, blank=True, null=True,
+ related_name='subscribed_projects')
+
def grant_user(self, user):
perm = ProjectPermission(project=self,
manage_project_permission=True,
@@ -157,6 +160,9 @@ class Issue(models.Model):
assignee = models.ForeignKey(User, blank=True, null=True, related_name='+')
+ subscribers = models.ManyToManyField(User, blank=True, null=True,
+ related_name='subscribed_issues')
+
@staticmethod
def next_id(project):
diff --git a/issue/templates/issue/issue.html b/issue/templates/issue/issue.html
index 4308412..3f42b05 100644
--- a/issue/templates/issue/issue.html
+++ b/issue/templates/issue/issue.html
@@ -179,6 +179,20 @@
No one assigned
{% endif %}
{% endcomment %}
+ {% if request.user.is_authenticated %}
+
+
+ Notifications
+
+
+
+ {% if request.user in issue.subscribers.all %}
+
Unsubscribe
+ {% else %}
+
Subscribe
+ {% endif %}
+
+ {% endif %}
diff --git a/issue/templates/issue/project.html b/issue/templates/issue/project.html
index 538be9b..8c9dd04 100644
--- a/issue/templates/issue/project.html
+++ b/issue/templates/issue/project.html
@@ -4,8 +4,13 @@
{% block page_title %}{{ project }}{% endblock %}
{% block projectmenu %}
-{% if perm.manage_project_permission or perm.modify_project or perm.delete_project or perm.create_project %}
+{% if request.user.is_authenticated or perm.manage_project_permission or perm.modify_project or perm.delete_project or perm.create_project %}
+{% if request.user in project.subscribers.all %}
+Unsubscribe
+{% else %}
+Subscribe
+{% endif %}
{% if perm.manage_project_permission %}
Manage permissions
{% endif %}
diff --git a/issue/urls.py b/issue/urls.py
index 1ba30c9..89bb0ca 100644
--- a/issue/urls.py
+++ b/issue/urls.py
@@ -5,6 +5,8 @@ urlpatterns = [
url(r'^add$', 'issue.views.project_add', name='add-project'),
url(r'^(?P[a-z0-9_-]+)/edit$', 'issue.views.project_edit', name='edit-project'),
url(r'^(?P[a-z0-9_-]+)/delete$', 'issue.views.project_delete', name='delete-project'),
+ url(r'^(?P[a-z0-9_-]+)/subscribe$', 'issue.views.project_subscribe', name='subscribe-project'),
+ url(r'^(?P[a-z0-9_-]+)/unsubscribe$', 'issue.views.project_unsubscribe', name='unsubscribe-project'),
url(r'^(?P[a-z0-9_-]+)/issues$', 'issue.views.issue_list', name='list-issue'),
url(r'^(?P[a-z0-9_-]+)/issues/add$', 'issue.views.issue_edit', name='add-issue'),
url(r'^(?P[a-z0-9_-]+)/issues/(?P[0-9]+)$', 'issue.views.issue', name='show-issue'),
@@ -15,6 +17,8 @@ urlpatterns = [
url(r'^(?P[a-z0-9_-]+)/issues/(?P[0-9]+)/comments/(?P[0-9]+)/edit$', 'issue.views.issue_edit_comment', name='edit-comment'),
url(r'^(?P[a-z0-9_-]+)/issues/(?P[0-9]+)/comments/(?P[0-9]+)/delete$', 'issue.views.issue_delete_comment', name='delete-comment'),
url(r'^(?P[a-z0-9_-]+)/issues/(?P[0-9]+)/delete$', 'issue.views.issue_delete', name='delete-issue'),
+ url(r'^(?P[a-z0-9_-]+)/issues/(?P[0-9]+)/subscribe$', 'issue.views.issue_subscribe', name='subscribe-issue'),
+ url(r'^(?P[a-z0-9_-]+)/issues/(?P[0-9]+)/unsubscribe$', 'issue.views.issue_unsubscribe', name='unsubscribe-issue'),
url(r'^(?P[a-z0-9_-]+)/issues/(?P[0-9]+)/add-label/(?P[0-9]+)$', 'issue.views.issue_add_label', name='add-label-to-issue'),
url(r'^(?P[a-z0-9_-]+)/issues/(?P[0-9]+)/remove-label/(?P[0-9]+)$', 'issue.views.issue_remove_label', name='remove-label-from-issue'),
url(r'^(?P[a-z0-9_-]+)/issues/(?P[0-9]+)/add-milestone/(?P[a-z0-9_.-]+)$', 'issue.views.issue_add_milestone', name='add-milestone-to-issue'),
diff --git a/issue/views.py b/issue/views.py
index f6ca898..f715e0a 100644
--- a/issue/views.py
+++ b/issue/views.py
@@ -914,3 +914,73 @@ def team_delete(request, team):
messages.success(request, 'Team deleted successfully.')
return redirect('list-team')
+
+
+@login_required
+def project_subscribe(request, project):
+
+ user = User.objects.get(username=request.user.username)
+
+ if user in project.subscribers.all():
+ messages.warning(request, 'You are already subscribed to this project.')
+ else:
+ project.subscribers.add(user)
+ project.save()
+ messages.success(request, 'You have been subscribed to this project successfully.')
+
+ next = request.GET.get('next')
+ if next:
+ return redirect(next)
+ else:
+ return redirect('list-issue', project.name)
+
+
+@login_required
+def project_unsubscribe(request, project):
+
+ user = User.objects.get(username=request.user.username)
+
+ if user in project.subscribers.all():
+ project.subscribers.remove(user)
+ project.save()
+ messages.success(request, 'You will not receive any notifications for this project anymore.')
+ else:
+ messages.warning(request, 'You are not subscribed to this project.')
+
+ next = request.GET.get('next')
+ if next:
+ return redirect(next)
+ else:
+ return redirect('list-issue', project.name)
+
+
+@login_required
+def issue_subscribe(request, project, issue):
+
+ issue = get_object_or_404(Issue, project=project.name, id=issue)
+ user = User.objects.get(username=request.user.username)
+
+ if user in issue.subscribers.all():
+ messages.warning(request, 'You are already subscribed to this issue.')
+ else:
+ issue.subscribers.add(user)
+ issue.save()
+ messages.success(request, 'You have been subscribed to this issue successfully.')
+
+ return redirect('show-issue', project.name, issue.id)
+
+
+@login_required
+def issue_unsubscribe(request, project, issue):
+
+ issue = get_object_or_404(Issue, project=project.name, id=issue)
+ user = User.objects.get(username=request.user.username)
+
+ if user in issue.subscribers.all():
+ issue.subscribers.remove(user)
+ issue.save()
+ messages.success(request, 'You will not receive any notifications for this issue anymore.')
+ else:
+ messages.warning(request, 'You are not subscribed to this issue.')
+
+ return redirect('show-issue', project.name, issue.id)