Skip to content

Commit

Permalink
Merge pull request #1683 from Scifabric/issue-1619
Browse files Browse the repository at this point in the history
Issue 1619
  • Loading branch information
teleyinex committed Oct 3, 2017
2 parents b59fad1 + 5a069c7 commit 5846c9a
Show file tree
Hide file tree
Showing 8 changed files with 214 additions and 5 deletions.
4 changes: 2 additions & 2 deletions doc/conf.py
Expand Up @@ -73,9 +73,9 @@
#
# The short X.Y version.

version = 'v2.7.1'
version = 'v2.7.2'
# The full version, including alpha/beta/rc tags.
release = 'v2.7.1'
release = 'v2.7.2'

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
Expand Down
15 changes: 15 additions & 0 deletions doc/user/project_settings.rst
Expand Up @@ -314,3 +314,18 @@ and task runs, use this section to delete the project.

.. note::
Only projects without results can be deleted.

Transfer project ownership
==========================

You can transfer the project ownership to another user in the PYBOSSA server.

For changing the ownership, just add the email of the user that you want to make
the new owner.

.. note::
If your are not an admin of PYBOSSA you cannot undo this action, and you will
not be able to modify/change settings of the project as you will not be the
owner anymore. Admins however can change the ownership always.


3 changes: 3 additions & 0 deletions pybossa/forms/forms.py
Expand Up @@ -475,3 +475,6 @@ class AvatarUploadForm(Form):
y1 = IntegerField(label=None, widget=HiddenInput(), default=0)
x2 = IntegerField(label=None, widget=HiddenInput(), default=0)
y2 = IntegerField(label=None, widget=HiddenInput(), default=0)

class TransferOwnershipForm(Form):
email_addr = EmailField(lazy_gettext('Email of the new owner'))
3 changes: 2 additions & 1 deletion pybossa/forms/projects_view_forms.py
Expand Up @@ -27,4 +27,5 @@
BlogpostForm,
PasswordForm,
GenericBulkTaskImportForm,
AvatarUploadForm)
AvatarUploadForm,
TransferOwnershipForm)
2 changes: 1 addition & 1 deletion pybossa/themes/default
47 changes: 47 additions & 0 deletions pybossa/view/projects.py
Expand Up @@ -1822,3 +1822,50 @@ def reset_secret_key(short_name):
msg = gettext('New secret key generated')
flash(msg, 'success')
return redirect_content_type(url_for('.update', short_name=short_name))

@blueprint.route('/<short_name>/transferownership', methods=['GET', 'POST'])
@login_required
def transfer_ownership(short_name):
"""Transfer project ownership."""

project, owner, ps = project_by_shortname(short_name)

pro = pro_features()

title = project_title(project, "Results")

ensure_authorized_to('update', project)

form = TransferOwnershipForm(request.body)

if request.method == 'POST' and form.validate():
new_owner = user_repo.filter_by(email_addr=form.email_addr.data)
if len(new_owner) == 1:
new_owner = new_owner[0]
project.owner_id = new_owner.id
project_repo.update(project)
msg = gettext("Project owner updated")
return redirect_content_type(url_for('.details',
short_name=short_name))
else:
msg = gettext("New project owner not found by email")
flash(msg, 'info')
return redirect_content_type(url_for('.transfer_ownership',
short_name=short_name))
else:
owner_serialized = cached_users.get_user_summary(owner.name)
project = add_custom_contrib_button_to(project, get_user_id_or_ip(), ps=ps)
response = dict(template='/projects/transferownership.html',
project=project,
owner=owner_serialized,
n_tasks=ps.n_tasks,
overall_progress=ps.overall_progress,
n_task_runs=ps.n_task_runs,
last_activity=ps.last_activity,
n_completed_tasks=ps.n_completed_tasks,
n_volunteers=ps.n_volunteers,
title=title,
pro_features=pro,
form=form,
target='.transfer_ownership')
return handle_content_type(response)
2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -66,7 +66,7 @@

setup(
name = 'pybossa',
version = '2.7.1',
version = '2.7.2',
packages = find_packages(),
install_requires = requirements,
# only needed when installing directly from setup.py (PyPi, eggs?) and pointing to e.g. a git repo.
Expand Down
143 changes: 143 additions & 0 deletions test/test_view/test_project_transferownership.py
@@ -0,0 +1,143 @@
# -*- coding: utf8 -*-
# This file is part of PYBOSSA.
#
# Copyright (C) 2017 Scifabric
#
# PYBOSSA 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.
#
# PYBOSSA 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 PYBOSSA. If not, see <http://www.gnu.org/licenses/>.
from default import db, with_context
from factories import ProjectFactory, UserFactory
from helper import web
from pybossa.repositories import UserRepository, ProjectRepository
import json

project_repo = ProjectRepository(db)
user_repo = UserRepository(db)

class TestProjectTransferOwnership(web.Helper):

@with_context
def test_transfer_anon_get(self):
"""Test transfer ownership page is not shown to anon."""
project = ProjectFactory.create()
url = '/project/%s/transferownership' % project.short_name
res = self.app_get_json(url, follow_redirects=True)
assert 'signin' in res.data, res.data

@with_context
def test_transfer_auth_not_owner_get(self):
"""Test transfer ownership page is forbidden for not owner."""
admin, owner, user = UserFactory.create_batch(3)
project = ProjectFactory.create(owner=owner)
url = '/project/%s/transferownership?api_key=%s' % (project.short_name,
user.api_key)
res = self.app_get_json(url, follow_redirects=True)
data = json.loads(res.data)
assert data['code'] == 403, data

@with_context
def test_transfer_auth_owner_get(self):
"""Test transfer ownership page is ok for owner."""
admin, owner, user = UserFactory.create_batch(3)
project = ProjectFactory.create(owner=owner)
url = '/project/%s/transferownership?api_key=%s' % (project.short_name,
owner.api_key)
res = self.app_get_json(url, follow_redirects=True)
data = json.loads(res.data)
assert data['form'], data
assert data['form']['errors'] == {}, data
assert data['form']['email_addr'] is None, data
assert data['form']['csrf'] is not None, data

@with_context
def test_transfer_auth_admin_get(self):
"""Test transfer ownership page is ok for admin."""
admin, owner, user = UserFactory.create_batch(3)
project = ProjectFactory.create(owner=owner)
url = '/project/%s/transferownership?api_key=%s' % (project.short_name,
admin.api_key)
res = self.app_get_json(url, follow_redirects=True)
data = json.loads(res.data)
assert data['form'], data
assert data['form']['errors'] == {}, data
assert data['form']['email_addr'] is None, data
assert data['form']['csrf'] is not None, data

@with_context
def test_transfer_auth_owner_post(self):
"""Test transfer ownership page post is ok for owner."""
admin, owner, user = UserFactory.create_batch(3)
project = ProjectFactory.create(owner=owner)
url = '/project/%s/transferownership?api_key=%s' % (project.short_name,
owner.api_key)

assert project.owner_id == owner.id
payload = dict(email_addr=user.email_addr)
res = self.app_post_json(url, data=payload,
follow_redirects=True)
data = json.loads(res.data)
assert data['next'] is not None, data

err_msg = "The project owner id should be different"
assert project.owner_id == user.id, err_msg

@with_context
def test_transfer_auth_owner_post_wrong_email(self):
"""Test transfer ownership page post is ok for wrong email."""
admin, owner, user = UserFactory.create_batch(3)
project = ProjectFactory.create(owner=owner)
url = '/project/%s/transferownership?api_key=%s' % (project.short_name,
owner.api_key)

assert project.owner_id == owner.id
payload = dict(email_addr="wrong@email.com")
res = self.app_post_json(url, data=payload,
follow_redirects=True)
data = json.loads(res.data)
assert data['next'] is not None, data
assert "project owner not found" in data['flash'], data
err_msg = "The project owner id should be the same"
assert project.owner_id == owner.id, err_msg

@with_context
def test_transfer_auth_admin_post(self):
"""Test transfer ownership page post is ok for admin."""
admin, owner, user = UserFactory.create_batch(3)
project = ProjectFactory.create(owner=owner)
url = '/project/%s/transferownership?api_key=%s' % (project.short_name,
admin.api_key)

assert project.owner_id == owner.id
payload = dict(email_addr=user.email_addr)
res = self.app_post_json(url, data=payload,
follow_redirects=True)
data = json.loads(res.data)
assert data['next'] is not None, data

err_msg = "The project owner id should be different"
assert project.owner_id == user.id, err_msg

@with_context
def test_transfer_auth_user_post(self):
"""Test transfer ownership page post is forbidden for not owner."""
admin, owner, user = UserFactory.create_batch(3)
project = ProjectFactory.create(owner=owner)
url = '/project/%s/transferownership?api_key=%s' % (project.short_name,
user.api_key)

assert project.owner_id == owner.id
payload = dict(email_addr=user.email_addr)
res = self.app_post_json(url, data=payload,
follow_redirects=True)
data = json.loads(res.data)
assert data['code'] == 403, data

0 comments on commit 5846c9a

Please sign in to comment.