diff --git a/experimenter/experimenter/nimbus_ui_new/forms.py b/experimenter/experimenter/nimbus_ui_new/forms.py
index 6911a7b6ae..c04dcdf7fe 100644
--- a/experimenter/experimenter/nimbus_ui_new/forms.py
+++ b/experimenter/experimenter/nimbus_ui_new/forms.py
@@ -191,3 +191,35 @@ def __init__(self, *args, **kwargs):
def get_changelog_message(self):
return f"{self.request.user} updated metrics"
+
+
+class SubscribeForm(NimbusChangeLogFormMixin, forms.ModelForm):
+ class Meta:
+ model = NimbusExperiment
+ fields = []
+
+ def __init__(self, *args, **kwargs):
+ self.user = kwargs.pop("user", None)
+ super().__init__(*args, **kwargs)
+
+ def save(self, commit=True):
+ self.instance.subscribers.add(self.user)
+ if commit:
+ self.instance.save()
+ return self.instance
+
+
+class UnsubscribeForm(NimbusChangeLogFormMixin, forms.ModelForm):
+ class Meta:
+ model = NimbusExperiment
+ fields = []
+
+ def __init__(self, *args, **kwargs):
+ self.user = kwargs.pop("user", None)
+ super().__init__(*args, **kwargs)
+
+ def save(self, commit=True):
+ self.instance.subscribers.remove(self.user)
+ if commit:
+ self.instance.save()
+ return self.instance
diff --git a/experimenter/experimenter/nimbus_ui_new/templates/nimbus_experiments/detail.html b/experimenter/experimenter/nimbus_ui_new/templates/nimbus_experiments/detail.html
index 4377c43ccc..5e87761cd8 100644
--- a/experimenter/experimenter/nimbus_ui_new/templates/nimbus_experiments/detail.html
+++ b/experimenter/experimenter/nimbus_ui_new/templates/nimbus_experiments/detail.html
@@ -90,22 +90,16 @@
Overview
Team projects |
-
+ |
{% for project in experiment.projects.all %}
{{ project }}
{% empty %}
Not set
{% endfor %}
|
- Subscribers |
-
- {% for subscriber in experiment.subscribers.all %}
- {{ subscriber }}
- {% empty %}
- Not set
- {% endfor %}
- |
+ {% include 'nimbus_experiments/subscribers_list.html' %}
+
diff --git a/experimenter/experimenter/nimbus_ui_new/templates/nimbus_experiments/subscribers_list.html b/experimenter/experimenter/nimbus_ui_new/templates/nimbus_experiments/subscribers_list.html
new file mode 100644
index 0000000000..d277f764b4
--- /dev/null
+++ b/experimenter/experimenter/nimbus_ui_new/templates/nimbus_experiments/subscribers_list.html
@@ -0,0 +1,35 @@
+
+ Subscribers |
+
+ {% for subscriber in experiment.subscribers.all %}
+ {{ subscriber.email }}
+ {% empty %}
+ Not Set
+ {% endfor %}
+ |
+
+ {% if request.user in experiment.subscribers.all %}
+
+ {% else %}
+
+ {% endif %}
+ |
+
diff --git a/experimenter/experimenter/nimbus_ui_new/tests/test_forms.py b/experimenter/experimenter/nimbus_ui_new/tests/test_forms.py
index 6ccbd1614f..0140fdc5de 100644
--- a/experimenter/experimenter/nimbus_ui_new/tests/test_forms.py
+++ b/experimenter/experimenter/nimbus_ui_new/tests/test_forms.py
@@ -9,7 +9,9 @@
NimbusExperimentCreateForm,
QAStatusForm,
SignoffForm,
+ SubscribeForm,
TakeawaysForm,
+ UnsubscribeForm,
)
from experimenter.openidc.tests.factories import UserFactory
from experimenter.outcomes import Outcomes
@@ -274,3 +276,28 @@ def test_invalid_form_with_wrong_application_outcomes_and_segments(self):
self.assertIn("primary_outcomes", form.errors)
self.assertIn("secondary_outcomes", form.errors)
self.assertIn("segments", form.errors)
+
+
+class SubscriptionFormTests(RequestFormTestCase):
+ def setUp(self):
+ super().setUp()
+ self.experiment = NimbusExperimentFactory.create(
+ name="Test Experiment",
+ owner=self.user,
+ qa_signoff=False,
+ vp_signoff=False,
+ legal_signoff=False,
+ )
+
+ def test_subscribe_form_adds_subscriber(self):
+ form = SubscribeForm(instance=self.experiment, data={}, user=self.user)
+ self.assertTrue(form.is_valid())
+ form.save()
+ self.assertIn(self.user, self.experiment.subscribers.all())
+
+ def test_unsubscribe_form_removes_subscriber(self):
+ self.experiment.subscribers.add(self.user)
+ form = UnsubscribeForm(instance=self.experiment, data={}, user=self.user)
+ self.assertTrue(form.is_valid())
+ form.save()
+ self.assertNotIn(self.user, self.experiment.subscribers.all())
diff --git a/experimenter/experimenter/nimbus_ui_new/tests/test_views.py b/experimenter/experimenter/nimbus_ui_new/tests/test_views.py
index 0b43db2971..71f9881bac 100644
--- a/experimenter/experimenter/nimbus_ui_new/tests/test_views.py
+++ b/experimenter/experimenter/nimbus_ui_new/tests/test_views.py
@@ -1090,6 +1090,33 @@ def test_signoff_edit_mode_post_valid_form(self):
self.assertTrue(self.experiment.vp_signoff)
self.assertTrue(self.experiment.legal_signoff)
+ def test_subscribe_to_experiment(self):
+ self.assertNotIn(self.user, self.experiment.subscribers.all())
+
+ response = self.client.post(
+ reverse("subscribe", kwargs={"slug": self.experiment.slug})
+ )
+
+ self.experiment.refresh_from_db()
+
+ self.assertIn(self.user, self.experiment.subscribers.all())
+ self.assertEqual(response.status_code, 200)
+
+ def test_unsubscribe_from_experiment(self):
+ self.experiment.subscribers.add(self.user)
+ self.experiment.save()
+
+ self.assertIn(self.user, self.experiment.subscribers.all())
+
+ response = self.client.post(
+ reverse("unsubscribe", kwargs={"slug": self.experiment.slug})
+ )
+
+ self.experiment.refresh_from_db()
+
+ self.assertNotIn(self.user, self.experiment.subscribers.all())
+ self.assertEqual(response.status_code, 200)
+
class TestNimbusExperimentsCreateView(AuthTestCase):
def test_post_creates_experiment(self):
diff --git a/experimenter/experimenter/nimbus_ui_new/urls.py b/experimenter/experimenter/nimbus_ui_new/urls.py
index ac0b0581fe..2c1e766180 100644
--- a/experimenter/experimenter/nimbus_ui_new/urls.py
+++ b/experimenter/experimenter/nimbus_ui_new/urls.py
@@ -8,7 +8,9 @@
NimbusExperimentsListTableView,
QAStatusUpdateView,
SignoffUpdateView,
+ SubscribeView,
TakeawaysUpdateView,
+ UnsubscribeView,
)
urlpatterns = [
@@ -52,4 +54,10 @@
NimbusExperimentsCreateView.as_view(),
name="nimbus-new-create",
),
+ re_path(r"^(?P[\w-]+)/subscribe/", SubscribeView.as_view(), name="subscribe"),
+ re_path(
+ r"^(?P[\w-]+)/unsubscribe/",
+ UnsubscribeView.as_view(),
+ name="unsubscribe",
+ ),
]
diff --git a/experimenter/experimenter/nimbus_ui_new/views.py b/experimenter/experimenter/nimbus_ui_new/views.py
index 081675176b..900dfdbd3a 100644
--- a/experimenter/experimenter/nimbus_ui_new/views.py
+++ b/experimenter/experimenter/nimbus_ui_new/views.py
@@ -1,5 +1,6 @@
from django.conf import settings
from django.http import HttpResponse
+from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.views.generic import CreateView, DetailView
from django.views.generic.edit import UpdateView
@@ -18,7 +19,9 @@
NimbusExperimentCreateForm,
QAStatusForm,
SignoffForm,
+ SubscribeForm,
TakeawaysForm,
+ UnsubscribeForm,
)
@@ -215,3 +218,27 @@ class MetricsUpdateView(NimbusExperimentViewMixin, RequestFormMixin, UpdateView)
def form_valid(self, form):
super().form_valid(form)
return self.render_to_response(self.get_context_data(form=form))
+
+
+class SubscribeView(NimbusExperimentViewMixin, RequestFormMixin, UpdateView):
+ form_class = SubscribeForm
+ template_name = "nimbus_experiments/subscribers_list.html"
+
+ def form_valid(self, form):
+ experiment = get_object_or_404(NimbusExperiment, slug=self.kwargs["slug"])
+ form.instance = experiment
+ form.user = self.request.user
+ form.save()
+ return render(self.request, self.template_name, {"experiment": experiment})
+
+
+class UnsubscribeView(NimbusExperimentViewMixin, RequestFormMixin, UpdateView):
+ form_class = UnsubscribeForm
+ template_name = "nimbus_experiments/subscribers_list.html"
+
+ def form_valid(self, form):
+ experiment = get_object_or_404(NimbusExperiment, slug=self.kwargs["slug"])
+ form.instance = experiment
+ form.user = self.request.user
+ form.save()
+ return render(self.request, self.template_name, {"experiment": experiment})