Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Develop to master - handle 'delete all' without JavaScript #96

Merged
merged 9 commits into from
Oct 26, 2023
5 changes: 5 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ jobs:
run: bin/test.sh
timeout-minutes: 20

- name: Retrieve logs
if: always()
run: ahoy logs
timeout-minutes: 5

- name: Retrieve screenshots
if: failure()
run: bin/process-artifacts.sh
Expand Down
64 changes: 47 additions & 17 deletions ckanext/datarequests/actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
from ckan.plugins import toolkit as tk
from ckan.plugins.toolkit import h, config

from . import constants, db, validator
from . import common, constants, db, validator


log = logging.getLogger(__name__)
Expand All @@ -53,23 +53,23 @@ def _get_user(user_id):
USERS_CACHE[user_id] = user
return user
except Exception as e:
log.warn(e)
log.warning(e)


def _get_organization(organization_id):
try:
organization_show = tk.get_action('organization_show')
return organization_show({'ignore_auth': True}, {'id': organization_id, 'include_users': True})
except Exception as e:
log.warn(e)
log.warning(e)


def _get_package(package_id):
try:
package_show = tk.get_action('package_show')
return package_show({'ignore_auth': True}, {'id': package_id})
except Exception as e:
log.warn(e)
log.warning(e)


def _dictize_datarequest(datarequest):
Expand Down Expand Up @@ -155,17 +155,17 @@ def _get_datarequest_involved_users(context, datarequest_dict):
users.update([follower.user_id for follower in db.DataRequestFollower.get(datarequest_id=datarequest_id)])
users.update([comment['user_id'] for comment in list_datarequest_comments(new_context, {'datarequest_id': datarequest_id})])

if datarequest_dict['organization']:
users.update([user['id'] for user in datarequest_dict['organization']['users']])
org = datarequest_dict.get('organization')
if org:
users.update(_get_admin_users_from_organisation(org))

# Notifications are not sent to the user that performs the action
users.discard(context['auth_user_obj'].id)

return users


def _send_mail(user_ids, action_type, datarequest):

def _send_mail(user_ids, action_type, datarequest, job_title=None):
for user_id in user_ids:
try:
user_data = model.User.get(user_id)
Expand All @@ -179,12 +179,19 @@ def _send_mail(user_ids, action_type, datarequest):
subject = tk.render('emails/subjects/{0}.txt'.format(action_type), extra_vars)
body = tk.render('emails/bodies/{0}.txt'.format(action_type), extra_vars)

mailer.mail_user(user_data, subject, body)

tk.enqueue_job(mailer.mail_user, [user_data, subject, body], title=job_title)
except Exception:
log.exception("Error sending notification to {0}".format(user_id))


def _get_admin_users_from_organisation(org_dict):
all_users = org_dict.get('users', [])
if common.get_config_bool_value('ckanext.datarequests.notify_all_members', True):
return {user['id'] for user in all_users}
else:
return {user['id'] for user in all_users if user.get('capacity') == 'admin'}


def throttle_datarequest(creator):
""" Check that the account is not creating requests too quickly.
This should happen after validation, so a request that fails
Expand Down Expand Up @@ -234,7 +241,7 @@ def create_datarequest(context, data_dict):
:param description: A brief description for your data request
:type description: string

:param organiztion_id: The ID of the organization you want to asign the
:param organization_id: The ID of the organization you want to asign the
data request (optional).
:type organization_id: string

Expand Down Expand Up @@ -267,10 +274,11 @@ def create_datarequest(context, data_dict):

datarequest_dict = _dictize_datarequest(data_req)

if datarequest_dict['organization']:
users = {user['id'] for user in datarequest_dict['organization']['users']}
org = datarequest_dict.get('organization')
if org:
users = _get_admin_users_from_organisation(org)
users.discard(creator.id)
_send_mail(users, 'new_datarequest', datarequest_dict)
_send_mail(users, 'new_datarequest', datarequest_dict, 'Data Request Created Email')

return datarequest_dict

Expand Down Expand Up @@ -331,7 +339,7 @@ def update_datarequest(context, data_dict):
:param description: A brief description for your data request
:type description: string

:param organiztion_id: The ID of the organization you want to asign the
:param organization_id: The ID of the organization you want to asign the
data request.
:type organization_id: string

Expand Down Expand Up @@ -363,13 +371,34 @@ def update_datarequest(context, data_dict):
# Validate data
validator.validate_datarequest(context, data_dict)

# Determine whether organisation has changed
organisation_updated = data_req.organization_id != data_dict['organization_id']
if organisation_updated:
unassigned_organisation_id = data_req.organization_id

# Set the data provided by the user in the data_red
_undictize_datarequest_basic(data_req, data_dict)

session.add(data_req)
session.commit()

return _dictize_datarequest(data_req)
datarequest_dict = _dictize_datarequest(data_req)

if organisation_updated and common.get_config_bool_value('ckanext.datarequests.notify_on_update'):
org = datarequest_dict['organization']
# Email Admin users of the assigned organisation
if org:
users = _get_admin_users_from_organisation(org)
users.discard(context['auth_user_obj'].id)
_send_mail(users, 'new_datarequest_organisation',
datarequest_dict, 'Data Request Assigned Email')
# Email Admin users of unassigned organisation
users = _get_admin_users_from_organisation(_get_organization(unassigned_organisation_id))
users.discard(context['auth_user_obj'].id)
_send_mail(users, 'unassigned_datarequest_organisation',
datarequest_dict, 'Data Request Unassigned Email')

return datarequest_dict


def list_datarequests(context, data_dict):
Expand Down Expand Up @@ -602,7 +631,8 @@ def close_datarequest(context, data_dict):

# Mailing
users = _get_datarequest_involved_users(context, datarequest_dict)
_send_mail(users, 'close_datarequest', datarequest_dict)
_send_mail(users, 'close_datarequest',
datarequest_dict, 'Data Request Closed Send Email')

return datarequest_dict

Expand Down
58 changes: 32 additions & 26 deletions ckanext/datarequests/controllers/controller_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,10 @@ def pager_url(state=None, sort=None, q=None, page=None):
return tk.render(file_to_render, extra_vars=extra_vars)
except ValueError as e:
# This exception should only occur if the page value is not valid
log.warn(e)
log.warning(e)
return tk.abort(400, tk._('"page" parameter must be an integer'))
except tk.NotAuthorized as e:
log.warn(e)
log.warning(e)
return tk.abort(403, tk._('Unauthorized to list Data Requests'))


Expand All @@ -174,7 +174,7 @@ def _process_post(action, context):
return tk.redirect_to(tk.url_for('datarequest.show', id=result['id']))

except tk.ValidationError as e:
log.warn(e)
log.warning(e)
# Fill the fields that will display some information in the page
c.datarequest = {
'id': data_dict.get('id', ''),
Expand Down Expand Up @@ -210,7 +210,7 @@ def new():
post_result = _process_post(constants.CREATE_DATAREQUEST, context)
return post_result or tk.render('datarequests/new.html')
except tk.NotAuthorized as e:
log.warn(e)
log.warning(e)
return tk.abort(403, tk._('Unauthorized to create a Data Request'))


Expand All @@ -229,7 +229,7 @@ def show(id):
except tk.ObjectNotFound:
return tk.abort(404, tk._('Data Request %s not found') % id)
except tk.NotAuthorized as e:
log.warn(e)
log.warning(e)
return tk.abort(403, tk._('You are not authorized to view the Data Request %s' % id))


Expand All @@ -249,10 +249,10 @@ def update(id):
post_result = _process_post(constants.UPDATE_DATAREQUEST, context)
return post_result or tk.render('datarequests/edit.html')
except tk.ObjectNotFound as e:
log.warn(e)
log.warning(e)
return tk.abort(404, tk._('Data Request %s not found') % id)
except tk.NotAuthorized as e:
log.warn(e)
log.warning(e)
return tk.abort(403, tk._('You are not authorized to update the Data Request %s' % id))


Expand All @@ -266,10 +266,10 @@ def delete(id):
h.flash_notice(tk._('Data Request %s has been deleted') % datarequest.get('title', ''))
return tk.redirect_to(tk.url_for('datarequest.index'))
except tk.ObjectNotFound as e:
log.warn(e)
log.warning(e)
return tk.abort(404, tk._('Data Request %s not found') % id)
except tk.NotAuthorized as e:
log.warn(e)
log.warning(e)
return tk.abort(403, tk._('You are not authorized to delete the Data Request %s' % id))


Expand Down Expand Up @@ -353,14 +353,14 @@ def _return_page(errors=None, errors_summary=None):
return _return_page()

except tk.ValidationError as e: # Accepted Dataset is not valid
log.warn(e)
log.warning(e)
errors_summary = _get_errors_summary(e.error_dict)
return _return_page(e.error_dict, errors_summary)
except tk.ObjectNotFound as e:
log.warn(e)
log.warning(e)
return tk.abort(404, tk._('Data Request %s not found') % id)
except tk.NotAuthorized as e:
log.warn(e)
log.warning(e)
return tk.abort(403, tk._('You are not authorized to close the Data Request %s' % id))


Expand Down Expand Up @@ -398,14 +398,14 @@ def comment(id):
h.flash_notice(flash_message)

except tk.NotAuthorized as e:
log.warn(e)
log.warning(e)
return tk.abort(403, tk._('You are not authorized to %s' % action_text))
except tk.ValidationError as e:
log.warn(e)
log.warning(e)
c.errors = e.error_dict
c.errors_summary = _get_errors_summary(c.errors)
except tk.ObjectNotFound as e:
log.warn(e)
log.warning(e)
return tk.abort(404, tk._(str(e)))
# Other exceptions are not expected. Otherwise, the request will fail.

Expand All @@ -426,11 +426,11 @@ def comment(id):
return tk.render('datarequests/comment.html')

except tk.ObjectNotFound as e:
log.warn(e)
log.warning(e)
return tk.abort(404, tk._('Data Request %s not found' % id))

except tk.NotAuthorized as e:
log.warn(e)
log.warning(e)
return tk.abort(403, tk._('You are not authorized to list the comments of the Data Request %s' % id))


Expand All @@ -443,10 +443,10 @@ def delete_comment(datarequest_id, comment_id):
h.flash_notice(tk._('Comment has been deleted'))
return tk.redirect_to(tk.url_for('datarequest.comment', id=datarequest_id))
except tk.ObjectNotFound as e:
log.warn(e)
log.warning(e)
return tk.abort(404, tk._('Comment %s not found') % comment_id)
except tk.NotAuthorized as e:
log.warn(e)
log.warning(e)
return tk.abort(403, tk._('You are not authorized to delete this comment'))


Expand All @@ -467,11 +467,17 @@ def purge(user_id):
data_dict = {'user_id': user_id}
context = _get_context()

try:
tk.get_action(constants.PURGE_DATAREQUESTS)(context, data_dict)
except tk.ObjectNotFound as e:
log.warn(e)
return tk.abort(404, tk._('User %s not found') % user_id)
post_params = request_helpers.get_post_params()
if post_params:
if 'cancel' in post_params:
return tk.redirect_to('datarequest.index')

h.flash_notice(tk._('Deleted data request(s) for user'))
return tk.redirect_to('datarequest.index')
try:
tk.get_action(constants.PURGE_DATAREQUESTS)(context, data_dict)
h.flash_notice(tk._('Deleted data request(s) for user'))
return tk.redirect_to('datarequest.index')
except tk.ObjectNotFound as e:
log.warning(e)
return tk.abort(404, tk._('User %s not found') % user_id)
else:
return tk.render('datarequests/confirm_delete_all.html', extra_vars={'user_id': user_id})
2 changes: 1 addition & 1 deletion ckanext/datarequests/plugin_mixins/flask_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ def get_blueprint(self):
"/{}/purge/<user_id>".format(constants.DATAREQUESTS_MAIN_PATH),
"purge",
controller_functions.purge,
('POST',),
('GET', 'POST',),
),
]

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{% extends "page.html" %}

{% block subtitle %}{{ _("Confirm Delete") }}{% endblock %}

{% block maintag %}<div class="row" role="main">{% endblock %}

{% block main_content %}
<section class="module col-md-6 col-md-offset-3">
<div class="module-content">
{% block form %}
<p>{{ _('Are you sure you want to delete ALL data requests for user {user_id}?').format(user_id=user_id) }}</p>
<p class="form-actions">
<form id="confirm-datarequest-delete-all-form" action="{% url_for 'datarequest.purge', user_id=user_id %}" method="POST">
{% if 'csrf_input' in h %}
{{ h.csrf_input() }}
{% endif %}
<button class="btn btn-danger" type="submit" name="cancel" >{{ _('Cancel') }}</button>
<button class="btn btn-primary" type="submit" name="delete" >{{ _('Confirm Delete') }}</button>
</form>
</p>
{% endblock %}
</div>
</section>
{% endblock %}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ <h3 class="dataset-heading">
</h3>
{% if g.userobj.sysadmin %}
<div class="btn-group pull-right">
<a class="btn btn-danger btn-danger-serious" href="{% url_for 'datarequest.purge', user_id=datarequest.user_id %}" data-module="confirm-action" data-module-content="Are you sure you want to delete ALL data requests for this user?" title="Delete all for this user">
<a class="btn btn-danger btn-danger-serious" href="{% url_for 'datarequest.purge', user_id=datarequest.user_id %}" title="Delete all for this user">
<i class="fa fa-skull"></i>
</a>
</div>
Expand Down
Loading