diff --git a/tests/unit/manage/test_views.py b/tests/unit/manage/test_views.py index 2c862f5c2132..384520cdd0be 100644 --- a/tests/unit/manage/test_views.py +++ b/tests/unit/manage/test_views.py @@ -1239,7 +1239,15 @@ def test_post_new_role_validation_fails(self, db_request): def test_post_new_role(self, monkeypatch, db_request): project = ProjectFactory.create(name="foobar") user = UserFactory.create(username="testuser") + EmailFactory.create(user=user, email="useremail") + otherowner = UserFactory.create(username="otherowner") + otherowner_role = RoleFactory.create( + user=otherowner, + project=project, + role_name="Owner", + ) + EmailFactory.create(user=otherowner, email="otherowneremail") user_service = pretend.stub( find_userid=lambda username: user.id, get_user=lambda userid: user, @@ -1250,7 +1258,7 @@ def test_post_new_role(self, monkeypatch, db_request): db_request.method = "POST" db_request.POST = pretend.stub() db_request.remote_addr = "10.10.10.10" - db_request.user = UserFactory.create() + db_request.user = otherowner form_obj = pretend.stub( validate=pretend.call_recorder(lambda: True), username=pretend.stub(data=user.username), @@ -1299,8 +1307,8 @@ def test_post_new_role(self, monkeypatch, db_request): db_request.user, project.name, form_obj.role_name.data, - [] - ) + [otherowner.email], + ), ] assert send_added_as_collaborator_email.calls == [ @@ -1309,15 +1317,19 @@ def test_post_new_role(self, monkeypatch, db_request): db_request.user, project.name, form_obj.role_name.data, - user.email) + user.email, + ), ] # Only one role is created - role = db_request.db.query(Role).one() + role = db_request.db.query(Role).filter(Role.user == user).one() assert result == { "project": project, - "roles_by_user": {user.username: [role]}, + "roles_by_user": { + user.username: [role], + otherowner.username: [otherowner_role], + }, "form": form_obj, } @@ -1328,6 +1340,90 @@ def test_post_new_role(self, monkeypatch, db_request): assert entry.submitted_by == db_request.user assert entry.submitted_from == db_request.remote_addr + def test_post_new_role_notify_all_owners(self, monkeypatch, db_request): + project = ProjectFactory.create(name="foobar") + user = UserFactory.create(username="testuser") + EmailFactory.create(user=user, email="useremail") + + owner = UserFactory.create(username="owneruser") + EmailFactory.create(user=owner, email="owneremail") + other_owner = UserFactory.create(username="otherowner") + EmailFactory.create( + user=other_owner, + email="otherowneremail", + ) + + RoleFactory.create( + user=owner, project=project, role_name="Owner" + ) + RoleFactory.create( + user=other_owner, project=project, role_name="Owner" + ) + + user_service = pretend.stub( + find_userid=lambda username: user.id, + get_user=lambda userid: user, + ) + db_request.find_service = pretend.call_recorder( + lambda iface, context: user_service + ) + db_request.method = "POST" + db_request.POST = pretend.stub() + db_request.remote_addr = "10.10.10.10" + db_request.user = owner + form_obj = pretend.stub( + validate=pretend.call_recorder(lambda: True), + username=pretend.stub(data=user.username), + role_name=pretend.stub(data="Owner"), + ) + form_class = pretend.call_recorder(lambda *a, **kw: form_obj) + db_request.session = pretend.stub( + flash=pretend.call_recorder(lambda *a, **kw: None), + ) + + send_collaborator_added_email = pretend.call_recorder(lambda *a: None) + monkeypatch.setattr( + views, + 'send_collaborator_added_email', + send_collaborator_added_email, + ) + + send_added_as_collaborator_email = pretend.call_recorder( + lambda *a: None) + monkeypatch.setattr( + views, + 'send_added_as_collaborator_email', + send_added_as_collaborator_email, + ) + + views.manage_project_roles( + project, db_request, _form_class=form_class + ) + + assert db_request.session.flash.calls == [ + pretend.call("Added collaborator 'testuser'", queue="success"), + ] + + assert send_collaborator_added_email.calls == [ + pretend.call( + db_request, + user, + db_request.user, + project.name, + form_obj.role_name.data, + [other_owner.email, owner.email] + ) + ] + + assert send_added_as_collaborator_email.calls == [ + pretend.call( + db_request, + db_request.user, + project.name, + form_obj.role_name.data, + user.email) + ] + def test_post_duplicate_role(self, db_request): project = ProjectFactory.create(name="foobar") user = UserFactory.create(username="testuser") @@ -1385,16 +1481,23 @@ def test_post_duplicate_role(self, db_request): class TestChangeProjectRoles: - def test_change_role(self, db_request): + def test_change_role(self, db_request, monkeypatch): project = ProjectFactory.create(name="foobar") user = UserFactory.create(username="testuser") role = RoleFactory.create( user=user, project=project, role_name="Owner" ) + EmailFactory.create(user=user, email="useremail") + + otherowner = UserFactory.create(username="otherowner") + RoleFactory.create( + user=otherowner, project=project, role_name="Owner" + ) + EmailFactory.create(user=otherowner, email="otherowneremail") new_role_name = "Maintainer" db_request.method = "POST" - db_request.user = UserFactory.create() + db_request.user = otherowner db_request.remote_addr = "10.10.10.10" db_request.POST = MultiDict({ "role_id": role.id, @@ -1407,6 +1510,22 @@ def test_change_role(self, db_request): lambda *a, **kw: "/the-redirect" ) + send_user_role_changed_email = pretend.call_recorder( + lambda *a: None) + monkeypatch.setattr( + views, + 'send_user_role_changed_email', + send_user_role_changed_email + ) + + send_role_changed_for_user_email = pretend.call_recorder( + lambda *a: None) + monkeypatch.setattr( + views, + 'send_role_changed_for_user_email', + send_role_changed_for_user_email + ) + result = views.change_project_role(project, db_request) assert role.role_name == new_role_name @@ -1419,6 +1538,21 @@ def test_change_role(self, db_request): assert isinstance(result, HTTPSeeOther) assert result.headers["Location"] == "/the-redirect" + assert send_user_role_changed_email.calls == [ + pretend.call( + db_request, + role, + ), + ] + + assert send_role_changed_for_user_email.calls == [ + pretend.call( + db_request, + role, + [otherowner.email], + ), + ] + entry = db_request.db.query(JournalEntry).one() assert entry.name == project.name @@ -1426,6 +1560,77 @@ def test_change_role(self, db_request): assert entry.submitted_by == db_request.user assert entry.submitted_from == db_request.remote_addr + def test_change_role_email_notify_all_owners(self, db_request, + monkeypatch): + project = ProjectFactory.create(name="foobar") + user = UserFactory.create(username="testuser") + role = RoleFactory.create( + user=user, project=project, role_name="Owner" + ) + EmailFactory.create(user=user, email="useremail") + owner = UserFactory.create(username="owneruser") + EmailFactory.create(user=owner, email="owneremail") + other_owner = UserFactory.create(username="otherowner") + EmailFactory.create( + user=other_owner, + email="otherowneremail", + ) + + RoleFactory.create( + user=owner, project=project, role_name="Owner" + ) + RoleFactory.create( + user=other_owner, project=project, role_name="Owner" + ) + new_role_name = "Maintainer" + + db_request.method = "POST" + db_request.user = owner + db_request.remote_addr = "10.10.10.10" + db_request.POST = MultiDict({ + "role_id": role.id, + "role_name": new_role_name, + }) + db_request.session = pretend.stub( + flash=pretend.call_recorder(lambda *a, **kw: None), + ) + db_request.route_path = pretend.call_recorder( + lambda *a, **kw: "/the-redirect" + ) + + send_user_role_changed_email = pretend.call_recorder( + lambda *a: None) + monkeypatch.setattr( + views, + 'send_user_role_changed_email', + send_user_role_changed_email + ) + + send_role_changed_for_user_email = pretend.call_recorder( + lambda *a: None) + monkeypatch.setattr( + views, + 'send_role_changed_for_user_email', + send_role_changed_for_user_email + ) + + views.change_project_role(project, db_request) + + assert send_user_role_changed_email.calls == [ + pretend.call( + db_request, + role, + ) + ] + + assert send_role_changed_for_user_email.calls == [ + pretend.call( + db_request, + role, + [other_owner.email, owner.email], + ) + ] + def test_change_role_invalid_role_name(self, pyramid_request): project = pretend.stub(name="foobar") @@ -1579,15 +1784,21 @@ def test_change_own_owner_role_when_multiple(self, db_request): class TestDeleteProjectRoles: - def test_delete_role(self, db_request): + def test_delete_role(self, db_request, monkeypatch): project = ProjectFactory.create(name="foobar") user = UserFactory.create(username="testuser") role = RoleFactory.create( user=user, project=project, role_name="Owner" ) + EmailFactory.create(user=user, email="useremail") + otherowner = UserFactory.create(username="otherowner") + otherowner_role = RoleFactory.create( + user=otherowner, project=project, role_name="Owner" + ) + EmailFactory.create(user=otherowner, email="otherowneremail") db_request.method = "POST" - db_request.user = UserFactory.create() + db_request.user = otherowner db_request.remote_addr = "10.10.10.10" db_request.POST = MultiDict({"role_id": role.id}) db_request.session = pretend.stub( @@ -1597,18 +1808,48 @@ def test_delete_role(self, db_request): lambda *a, **kw: "/the-redirect" ) - result = views.delete_project_role(project, db_request) + send_removed_from_role_email = pretend.call_recorder( + lambda *a: None) + monkeypatch.setattr( + views, + 'send_removed_from_role_email', + send_removed_from_role_email + ) + + send_role_removed_from_user_email = pretend.call_recorder( + lambda *a: None) + monkeypatch.setattr( + views, + 'send_role_removed_from_user_email', + send_role_removed_from_user_email + ) + result = views.delete_project_role(project, db_request) assert db_request.route_path.calls == [ pretend.call('manage.project.roles', project_name=project.name), ] - assert db_request.db.query(Role).all() == [] + assert db_request.db.query(Role).all() == [otherowner_role] assert db_request.session.flash.calls == [ pretend.call("Successfully removed role", queue="success"), ] assert isinstance(result, HTTPSeeOther) assert result.headers["Location"] == "/the-redirect" + assert send_removed_from_role_email.calls == [ + pretend.call( + db_request, + role, + ) + ] + + assert send_role_removed_from_user_email.calls == [ + pretend.call( + db_request, + role, + [otherowner.email], + ), + ] + entry = db_request.db.query(JournalEntry).one() assert entry.name == project.name @@ -1616,6 +1857,71 @@ def test_delete_role(self, db_request): assert entry.submitted_by == db_request.user assert entry.submitted_from == db_request.remote_addr + def test_delete_role_notify_all_owners(self, db_request, monkeypatch): + project = ProjectFactory.create(name="foobar") + user = UserFactory.create(username="testuser") + role = RoleFactory.create( + user=user, project=project, role_name="Owner" + ) + owner = UserFactory.create(username="owneruser") + EmailFactory.create(user=owner, email="owneremail") + other_owner = UserFactory.create(username="otherowner") + EmailFactory.create( + user=other_owner, + email="otherowneremail", + ) + + RoleFactory.create( + user=owner, project=project, role_name="Owner" + ) + RoleFactory.create( + user=other_owner, project=project, role_name="Owner" + ) + + db_request.method = "POST" + db_request.user = owner + db_request.remote_addr = "10.10.10.10" + db_request.POST = MultiDict({"role_id": role.id}) + db_request.session = pretend.stub( + flash=pretend.call_recorder(lambda *a, **kw: None), + ) + db_request.route_path = pretend.call_recorder( + lambda *a, **kw: "/the-redirect" + ) + + send_removed_from_role_email = pretend.call_recorder( + lambda *a: None) + monkeypatch.setattr( + views, + 'send_removed_from_role_email', + send_removed_from_role_email + ) + + send_role_removed_from_user_email = pretend.call_recorder( + lambda *a: None) + monkeypatch.setattr( + views, + 'send_role_removed_from_user_email', + send_role_removed_from_user_email + ) + + views.delete_project_role(project, db_request) + + assert send_removed_from_role_email.calls == [ + pretend.call( + db_request, + role, + ) + ] + + assert send_role_removed_from_user_email.calls == [ + pretend.call( + db_request, + role, + [other_owner.email, owner.email], + ) + ] + def test_delete_missing_role(self, db_request): project = ProjectFactory.create(name="foobar") missing_role_id = str(uuid.uuid4()) diff --git a/tests/unit/test_email.py b/tests/unit/test_email.py index bf0cb502ef24..e133610fec36 100644 --- a/tests/unit/test_email.py +++ b/tests/unit/test_email.py @@ -507,3 +507,271 @@ def test_added_as_collaborator_email( recipients=[stub_user.email], ), ] + + +class TestRemovedAsCollaboratorEmail: + + def test_removed_as_collaborator_email( + self, pyramid_request, pyramid_config, monkeypatch): + + stub_user = pretend.stub( + email='email', + username='username', + ) + stub_submitter_user = pretend.stub( + email='submiteremail', + username='submitterusername' + ) + stub_role = pretend.stub( + role_name="Owner", + user=stub_user, + project=pretend.stub( + name="test_project" + ), + ) + pyramid_request.user = stub_submitter_user + + subject_renderer = pyramid_config.testing_add_renderer( + 'email/removed-as-collaborator.subject.txt' + ) + subject_renderer.string_response = 'Email Subject' + body_renderer = pyramid_config.testing_add_renderer( + 'email/removed-as-collaborator.body.txt' + ) + body_renderer.string_response = 'Email Body' + + send_email = pretend.stub( + delay=pretend.call_recorder(lambda *args, **kwargs: None) + ) + pyramid_request.task = pretend.call_recorder( + lambda *args, **kwargs: send_email + ) + monkeypatch.setattr(email, 'send_email', send_email) + + result = email.send_removed_from_role_email( + pyramid_request, + role=stub_role, + ) + + assert result == { + 'project': 'test_project', + 'role': 'Owner', + 'submitter': stub_submitter_user.username + } + subject_renderer.assert_() + body_renderer.assert_(submitter=stub_submitter_user.username) + body_renderer.assert_(project='test_project') + body_renderer.assert_(role='Owner') + + assert pyramid_request.task.calls == [ + pretend.call(send_email), + ] + assert send_email.delay.calls == [ + pretend.call( + 'Email Body', + 'Email Subject', + recipients=[stub_user.email], + ), + ] + + +class TestUserRoleChangedEmail: + + def test_user_role_changed_email( + self, pyramid_request, pyramid_config, monkeypatch): + + stub_user = pretend.stub( + email='email', + username='username', + ) + stub_submitter_user = pretend.stub( + email='submiteremail', + username='submitterusername' + ) + stub_role = pretend.stub( + role_name="Owner", + user=stub_user, + project=pretend.stub( + name="test_project" + ), + ) + pyramid_request.user = stub_submitter_user + + subject_renderer = pyramid_config.testing_add_renderer( + 'email/role-removed-from-user.subject.txt' + ) + subject_renderer.string_response = 'Email Subject' + body_renderer = pyramid_config.testing_add_renderer( + 'email/role-removed-from-user.body.txt' + ) + body_renderer.string_response = 'Email Body' + + send_email = pretend.stub( + delay=pretend.call_recorder(lambda *args, **kwargs: None) + ) + pyramid_request.task = pretend.call_recorder( + lambda *args, **kwargs: send_email + ) + monkeypatch.setattr(email, 'send_email', send_email) + + result = email.send_role_removed_from_user_email( + pyramid_request, + role=stub_role, + email_recipients=[stub_user.email, stub_submitter_user.email] + ) + + assert result == { + 'project': 'test_project', + 'role': 'Owner', + 'submitter': stub_submitter_user.username, + 'username': stub_user.username, + } + subject_renderer.assert_() + body_renderer.assert_(submitter=stub_submitter_user.username) + body_renderer.assert_(project='test_project') + body_renderer.assert_(role='Owner') + + assert pyramid_request.task.calls == [ + pretend.call(send_email), + ] + assert send_email.delay.calls == [ + pretend.call( + 'Email Body', + 'Email Subject', + bcc=[stub_user.email, stub_submitter_user.email], + ), + ] + + +class TestUserRoleChangedEmailEmail: + + def test_user_role_changed_email( + self, pyramid_request, pyramid_config, monkeypatch): + + stub_user = pretend.stub( + email='email', + username='username', + ) + stub_submitter_user = pretend.stub( + email='submiteremail', + username='submitterusername' + ) + stub_role = pretend.stub( + role_name="Owner", + user=stub_user, + project=pretend.stub( + name="test_project" + ), + ) + pyramid_request.user = stub_submitter_user + + subject_renderer = pyramid_config.testing_add_renderer( + 'email/user-role-changed.subject.txt' + ) + subject_renderer.string_response = 'Email Subject' + body_renderer = pyramid_config.testing_add_renderer( + 'email/user-role-changed.body.txt' + ) + body_renderer.string_response = 'Email Body' + + send_email = pretend.stub( + delay=pretend.call_recorder(lambda *args, **kwargs: None) + ) + pyramid_request.task = pretend.call_recorder( + lambda *args, **kwargs: send_email + ) + monkeypatch.setattr(email, 'send_email', send_email) + + result = email.send_user_role_changed_email( + pyramid_request, + role=stub_role, + ) + + assert result == { + 'project': 'test_project', + 'role': 'Owner', + 'submitter': stub_submitter_user.username + } + subject_renderer.assert_() + body_renderer.assert_(submitter=stub_submitter_user.username) + body_renderer.assert_(project='test_project') + body_renderer.assert_(role='Owner') + + assert pyramid_request.task.calls == [ + pretend.call(send_email), + ] + assert send_email.delay.calls == [ + pretend.call( + 'Email Body', + 'Email Subject', + recipients=[stub_user.email], + ), + ] + + +class TestRoleChangedForUserEmail: + + def test_role_changed_for_user_email( + self, pyramid_request, pyramid_config, monkeypatch): + + stub_user = pretend.stub( + email='email', + username='username', + ) + stub_submitter_user = pretend.stub( + email='submiteremail', + username='submitterusername' + ) + stub_role = pretend.stub( + role_name="Owner", + user=stub_user, + project=pretend.stub( + name="test_project" + ), + ) + pyramid_request.user = stub_submitter_user + + subject_renderer = pyramid_config.testing_add_renderer( + 'email/role-changed-for-user.subject.txt' + ) + subject_renderer.string_response = 'Email Subject' + body_renderer = pyramid_config.testing_add_renderer( + 'email/role-changed-for-user.body.txt' + ) + body_renderer.string_response = 'Email Body' + + send_email = pretend.stub( + delay=pretend.call_recorder(lambda *args, **kwargs: None) + ) + pyramid_request.task = pretend.call_recorder( + lambda *args, **kwargs: send_email + ) + monkeypatch.setattr(email, 'send_email', send_email) + + result = email.send_role_changed_for_user_email( + pyramid_request, + role=stub_role, + email_recipients=[stub_user.email, stub_submitter_user.email] + ) + + assert result == { + 'project': 'test_project', + 'role': 'Owner', + 'submitter': stub_submitter_user.username, + 'username': stub_user.username, + } + subject_renderer.assert_() + body_renderer.assert_(submitter=stub_submitter_user.username) + body_renderer.assert_(project='test_project') + body_renderer.assert_(role='Owner') + + assert pyramid_request.task.calls == [ + pretend.call(send_email), + ] + assert send_email.delay.calls == [ + pretend.call( + 'Email Body', + 'Email Subject', + bcc=[stub_user.email, stub_submitter_user.email], + ), + ] diff --git a/warehouse/email.py b/warehouse/email.py index 9a45c47d3cb7..f52d404102fc 100644 --- a/warehouse/email.py +++ b/warehouse/email.py @@ -189,3 +189,84 @@ def send_added_as_collaborator_email(request, submitter, project_name, role, request.task(send_email).delay(body, subject, recipients=[user_email]) return fields + + +def send_removed_from_role_email(request, role): + fields = { + 'project': role.project.name, + 'submitter': request.user.username, + 'role': role.role_name + } + + subject = render( + 'email/removed-as-collaborator.subject.txt', fields, request=request + ) + + body = render( + 'email/removed-as-collaborator.body.txt', fields, request=request + ) + + request.task(send_email).delay(body, subject, recipients=[role.user.email]) + + return fields + + +def send_role_removed_from_user_email(request, role, email_recipients): + fields = { + 'project': role.project.name, + 'submitter': request.user.username, + 'role': role.role_name, + 'username': role.user.username, + } + + subject = render( + 'email/role-removed-from-user.subject.txt', fields, request=request + ) + + body = render( + 'email/role-removed-from-user.body.txt', fields, request=request + ) + + request.task(send_email).delay(body, subject, bcc=email_recipients) + return fields + + +def send_user_role_changed_email(request, role): + fields = { + 'project': role.project.name, + 'submitter': request.user.username, + 'role': role.role_name + } + + subject = render( + 'email/user-role-changed.subject.txt', fields, request=request + ) + + body = render( + 'email/user-role-changed.body.txt', fields, request=request + ) + + request.task(send_email).delay(body, subject, recipients=[role.user.email]) + + return fields + + +def send_role_changed_for_user_email(request, role, email_recipients): + fields = { + 'project': role.project.name, + 'submitter': request.user.username, + 'role': role.role_name, + 'username': role.user.username, + } + + subject = render( + 'email/role-changed-for-user.subject.txt', fields, request=request + ) + + body = render( + 'email/role-changed-for-user.body.txt', fields, request=request + ) + + request.task(send_email).delay(body, subject, bcc=email_recipients) + + return fields diff --git a/warehouse/manage/views.py b/warehouse/manage/views.py index 4306d31a7fe0..112012e77e36 100644 --- a/warehouse/manage/views.py +++ b/warehouse/manage/views.py @@ -24,7 +24,9 @@ from warehouse.email import ( send_account_deletion_email, send_added_as_collaborator_email, send_collaborator_added_email, send_email_verification_email, - send_password_change_email, send_primary_email_change_email + send_password_change_email, send_primary_email_change_email, + send_removed_from_role_email, send_role_changed_for_user_email, + send_role_removed_from_user_email, send_user_role_changed_email, ) from warehouse.manage.forms import ( AddEmailForm, ChangePasswordForm, CreateRoleForm, ChangeRoleForm, @@ -558,8 +560,8 @@ def manage_project_roles(project, request, _form_class=CreateRoleForm): .join(Role.user) .filter(Role.role_name == 'Owner', Role.project == project) ) - owner_emails = [owner.user.email for owner in owners] - owner_emails.remove(request.user.email) + owner_emails = [owner.user.email for owner in owners + if owner.user.email != user.email] send_collaborator_added_email( request, @@ -567,9 +569,8 @@ def manage_project_roles(project, request, _form_class=CreateRoleForm): request.user, project.name, form.role_name.data, - owner_emails + owner_emails, ) - send_added_as_collaborator_email( request, request.user, @@ -683,7 +684,24 @@ def change_project_role(project, request, _form_class=ChangeRoleForm): submitted_from=request.remote_addr, ), ) + owners = ( + request.db.query(Role) + .join(Role.user) + .filter( + Role.role_name == 'Owner', + Role.project == role.project, + ) + ) + owner_emails = [owner.user.email for owner in owners + if owner.user.email != role.user.email] + role.role_name = form.role_name.data + send_user_role_changed_email(request, role) + send_role_changed_for_user_email( + request, + role, + owner_emails, + ) request.session.flash( 'Successfully changed role', queue="success" ) @@ -733,6 +751,18 @@ def delete_project_role(project, request): submitted_from=request.remote_addr, ), ) + owners = ( + request.db.query(Role) + .join(Role.user) + .filter( + Role.role_name == 'Owner', + Role.project == role.project, + ) + ) + owner_emails = [owner.user.email for owner in owners] + + send_removed_from_role_email(request, role) + send_role_removed_from_user_email(request, role, owner_emails) request.session.flash("Successfully removed role", queue="success") return HTTPSeeOther( diff --git a/warehouse/templates/email/added-as-collaborator.body.txt b/warehouse/templates/email/added-as-collaborator.body.txt index 2f18248512f6..edac65a29ff1 100644 --- a/warehouse/templates/email/added-as-collaborator.body.txt +++ b/warehouse/templates/email/added-as-collaborator.body.txt @@ -1,4 +1,17 @@ -You have been added as {{ role }} to the PyPI project {{ project }} by {{ submitter }}. +You have been added as a collaborator to the PyPI project {{ project }} +by {{ submitter }}. + +{% if role == 'Owner' %} +You are now a project owner. This means that you may add other collaborators, +upload releases for the project, delete files and releases, or delete the entire +project. +{% endif %} + +{% if role == 'Maintainer' %} +You are now a project maintainer. This means that you may upload releases for +the project. You cannot add collaborators, delete files, delete releases, or +delete the project. +{% endif %} Congratulations! diff --git a/warehouse/templates/email/removed-as-collaborator.body.txt b/warehouse/templates/email/removed-as-collaborator.body.txt new file mode 100644 index 000000000000..5cd0326ab23f --- /dev/null +++ b/warehouse/templates/email/removed-as-collaborator.body.txt @@ -0,0 +1,16 @@ +Your role in the {{ project }} project has been removed by {{ submitter }}. + +{% if role == 'Owner' %} +You are no longer a project owner. This means that you can no longer add other +collaborators, upload releases for the project, delete files and releases, or +delete the entire project. +{% endif %} + +{% if role == 'Maintainer' %} +You are no longer a project maintainer. This means that you can no longer add +other collaborators, upload releases for the project, delete files and releases, +or delete the entire project. +{% endif %} + +If this was a mistake, you can reply to this email directly to communicate with +the PyPI administrators. diff --git a/warehouse/templates/email/removed-as-collaborator.subject.txt b/warehouse/templates/email/removed-as-collaborator.subject.txt new file mode 100644 index 000000000000..5f78440df7a1 --- /dev/null +++ b/warehouse/templates/email/removed-as-collaborator.subject.txt @@ -0,0 +1 @@ +Role removed on PyPI for project {{ project }} diff --git a/warehouse/templates/email/role-changed-for-user.body.txt b/warehouse/templates/email/role-changed-for-user.body.txt new file mode 100644 index 000000000000..031ec2e32819 --- /dev/null +++ b/warehouse/templates/email/role-changed-for-user.body.txt @@ -0,0 +1,9 @@ +A collaborator's role has been changed in a project you own on PyPI: + + Username: {{ username }} + New role: {{ role }} + Collaborator for: {{ project }} + Changed by: {{ submitter }} + +If this was a mistake, you can reply to this email directly to communicate with +the PyPI administrators. diff --git a/warehouse/templates/email/role-changed-for-user.subject.txt b/warehouse/templates/email/role-changed-for-user.subject.txt new file mode 100644 index 000000000000..2201101c0d24 --- /dev/null +++ b/warehouse/templates/email/role-changed-for-user.subject.txt @@ -0,0 +1 @@ +Role changed on PyPI for project {{ project }} \ No newline at end of file diff --git a/warehouse/templates/email/role-removed-from-user.body.txt b/warehouse/templates/email/role-removed-from-user.body.txt new file mode 100644 index 000000000000..aa1cea8330e5 --- /dev/null +++ b/warehouse/templates/email/role-removed-from-user.body.txt @@ -0,0 +1,9 @@ +A collaborator's role has been removed from a project you own on PyPI: + + Username: {{ username }} + Role was previously: {{ role }} + Collaborator for: {{ project }} + Changed by: {{ submitter }} + +If this was a mistake, you can reply to this email directly to communicate with +the PyPI administrators. diff --git a/warehouse/templates/email/role-removed-from-user.subject.txt b/warehouse/templates/email/role-removed-from-user.subject.txt new file mode 100644 index 000000000000..6d1c406878d4 --- /dev/null +++ b/warehouse/templates/email/role-removed-from-user.subject.txt @@ -0,0 +1 @@ +Role removed on PyPI for project {{ project }} \ No newline at end of file diff --git a/warehouse/templates/email/user-role-changed.body.txt b/warehouse/templates/email/user-role-changed.body.txt new file mode 100644 index 000000000000..5cf017551f84 --- /dev/null +++ b/warehouse/templates/email/user-role-changed.body.txt @@ -0,0 +1,16 @@ +Your role in the {{ project }} project has been changed by {{ submitter }}. + +{% if role == 'Owner' %} +You are now a project owner. This means that you may add other collaborators, +upload releases for the project, delete files and releases, or delete the entire +project. +{% endif %} + +{% if role == 'Maintainer' %} +You are now a project maintainer. This means that you may upload releases for +the project. You cannot add collaborators, delete files, delete releases, or +delete the project. +{% endif %} + +If this was a mistake, you can reply to this email directly to communicate with +the PyPI administrators. diff --git a/warehouse/templates/email/user-role-changed.subject.txt b/warehouse/templates/email/user-role-changed.subject.txt new file mode 100644 index 000000000000..2201101c0d24 --- /dev/null +++ b/warehouse/templates/email/user-role-changed.subject.txt @@ -0,0 +1 @@ +Role changed on PyPI for project {{ project }} \ No newline at end of file