diff --git a/requirements.txt b/requirements.txt index 0965438f..c89644da 100644 --- a/requirements.txt +++ b/requirements.txt @@ -22,3 +22,4 @@ Celery django-celery django-kombu defusedxml==0.4.1 +django-countries diff --git a/scielomanager/accounts/forms.py b/scielomanager/accounts/forms.py index 67be7c03..398d9c94 100644 --- a/scielomanager/accounts/forms.py +++ b/scielomanager/accounts/forms.py @@ -1,5 +1,11 @@ # coding: utf-8 from django import forms +from django.contrib.auth.forms import PasswordResetForm +from django.contrib.auth.tokens import default_token_generator +from django.contrib.sites.models import get_current_site +from django.utils.http import int_to_base36 +from django.template import loader +from scielomanager.tasks import send_mail class PasswordChangeForm(forms.Form): @@ -9,3 +15,41 @@ class PasswordChangeForm(forms.Form): widget=forms.PasswordInput(attrs={'class': 'span3'})) new_password_again = forms.CharField( widget=forms.PasswordInput(attrs={'class': 'span3'})) + + +class PasswordResetForm(PasswordResetForm): + """ + Customization of django.contrib.auth.forms:PasswordResetForm + to send emails via celery task + """ + + def save(self, domain_override=None, + subject_template_name='registration/password_reset_subject.txt', + email_template_name='registration/password_reset_email.html', + use_https=False, token_generator=default_token_generator, + from_email=None, request=None): + """ + Generates a one-use only link for resetting password and sends to the + user. + """ + for user in self.users_cache: + if not domain_override: + current_site = get_current_site(request) + site_name = current_site.name + domain = current_site.domain + else: + site_name = domain = domain_override + c = { + 'email': user.email, + 'domain': domain, + 'site_name': site_name, + 'uid': int_to_base36(user.id), + 'user': user, + 'token': token_generator.make_token(user), + 'protocol': use_https and 'https' or 'http', + } + subject = loader.render_to_string(subject_template_name, c) + # Email subject *must not* contain newlines + subject = ''.join(subject.splitlines()) + email = loader.render_to_string(email_template_name, c) + send_mail.delay(subject, email, [user.email]) diff --git a/scielomanager/accounts/management/__init__.py b/scielomanager/accounts/management/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/scielomanager/accounts/management/commands/__init__.py b/scielomanager/accounts/management/commands/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/scielomanager/accounts/management/commands/anonymize_data.py b/scielomanager/accounts/management/commands/anonymize_data.py new file mode 100644 index 00000000..78729aa9 --- /dev/null +++ b/scielomanager/accounts/management/commands/anonymize_data.py @@ -0,0 +1,87 @@ +# coding: utf-8 +from django.core.management.base import BaseCommand +from django.contrib.auth.models import User +from django.db.models import Q + +def display_users(users_list): + print "-" * 80 + print "| pk | username | first_name | last_name | email |" + for user in users_list: + print "| %s | %s | %s | %s | %s |" % (user.pk, user.username, user.first_name, user.last_name, user.email) + print "-" * 80 + print "total: %s users" % users_list.count() + print "-" * 80 + + +class Command(BaseCommand): + help = 'replace sensible data to be anonymized, such as first_name, last_name, email, api tokens, etc' + + def handle(self, *args, **options): + print "#" * 80 + print "# THIS COMMAND WILL MODIFY USER DATA, AND SHOULD AFFECT THE LOGIN CREDENTIALS! #" + print "# ----------- PLEASE BE SURE YOU HAVE A BACKUP TO AVOID DATA LOSS ------------ #" + print "#" * 80 + prompt_backup = raw_input('the database has a back up? [y/N]: ') + if prompt_backup.lower() == 'y': + users = User.objects.all() + print "Found %s users!" % users.count() + prompt_show_all_users = raw_input('want to list all these users? [y/N]: ') + if prompt_show_all_users.lower() == 'y': + display_users(users) + + # exclude non-scielo users + non_scielo_users = users.exclude(email__endswith="@scielo.org") + print "Found %s NON-scielo users!" % non_scielo_users.count() + prompt_show_non_scielo_users = raw_input('want to list all these users? [y/N]: ') + if prompt_show_non_scielo_users.lower() == 'y': + display_users(non_scielo_users) + + # lookup to know if exists particular users to be excludes, such as: QAL1, QAL2, Produtor, etc + has_special_users = non_scielo_users.filter( + Q(first_name__iexact="Produtor") | Q(first_name__iexact="QAL1") | Q(first_name__iexact="QAL2") + ).exists() + + if has_special_users: + prompt_to_exclude_special_users = raw_input( + 'Found at least one special user (QAL1 or QAL2 or Produtor). Do you want to ignore this users from modifications? [y/N]: ' + ) + if prompt_to_exclude_special_users.lower() == 'y': + non_scielo_users = non_scielo_users.exclude( + Q(first_name__iexact="Produtor") | Q(first_name__iexact="QAL1") | Q(first_name__iexact="QAL2") + ) + print "Now the list of NON-scielo users to be modified has %s users" % non_scielo_users.count() + prompt_show_non_scielo_users = raw_input('want to list all these users? [y/N]: ') + if prompt_show_non_scielo_users.lower() == 'y': + display_users(non_scielo_users) + + print "#" * 80 + print "# NOW WILL MODIFY USER DATA! #" + print "# user.username will be set to user_ #" + print "# user.first_name will be set to user_fn_ #" + print "# user.last_name will be set to user_ln_ #" + print "# user.email will be set to user_@example.com #" + print "# user.password will be set to 'test.scielo' [hashed] #" + print "# user.api_key will be regenerated with a random uuid using tastypie.models > ApiKey > generate_key #" + print "# ----------- BE SURE YOU HAVE A BACKUP TO AVOID DATA LOSS ------------ #" + print "#" * 80 + prompt_confirm_modify = raw_input('Are you sure? the process CAN NOT BE UNDONE [y/N]: ') + if prompt_confirm_modify.lower() == 'y': + print "Updating users ... (hold on, may take a while) ..." + for user in non_scielo_users: + user.username = "user_%s" % user.pk + user.first_name = "user_fn_%s" % user.pk + user.last_name = "user_ln_%s" % user.pk + user.email = "user_%s@example.com" % user.pk + user.set_password('test.scielo') + # save new user field + user.save() + # generate a new api_key + user.api_key.key = None + user.api_key.save() + # show updated user info + display_users(non_scielo_users) + print "done!" + else: + print "Nothing to do here! NON-scielo users were NOT changed!" + else: + print "Nothing to do here! Go and make a backup." diff --git a/scielomanager/accounts/templates/accounts/my_account.html b/scielomanager/accounts/templates/accounts/my_account.html index f9a433dc..afaab931 100644 --- a/scielomanager/accounts/templates/accounts/my_account.html +++ b/scielomanager/accounts/templates/accounts/my_account.html @@ -1,28 +1,148 @@ {% extends "base_lv1.html" %} {% load i18n %} +{% load user_avatar %} +{% block content %} + -{% block breadcrumb %} - +
+
+
+
+

{% trans "User information" %}:

+
+
+
{% trans "First name" %}:
+
{{ user.first_name }}
+
{% trans "Last name" %}:
+
{{ user.last_name }}
+
{% trans "Username" %}:
+
{{ user.username }}
+
{% trans "Email" %}:
+
{{ user.email }}
+
+
+
+
+ +
+ + + Change your avatar at: + + https://secure.gravatar.com + + + +
+
+
+
+
+ +
+
+
+

{% trans "Change Password" %}:

+
+
+ {% with password_form as form %} + {% include "articletrack/includes/form_snippet.html" %} + {% endwith %} +
+
+
+
+
+

{% trans "Notifications & Other preferences" %}:

+
+
+ {% with profile_form as form %} + {% include "articletrack/includes/form_snippet.html" %} + {% endwith %} +
+
+
+
+
+
+
+

{% trans "My collections" %}:

+
+ + + + + + + + + + {% for collection in my_collecttions %} + + + + + + {% empty %} + + + + {% endfor %} + +
#{% trans "Collection" %}:{% trans "Am I manager?" %}
{{ forloop.counter }}{{ collection.name }} + {% if collection.is_manager %} + + {% else %} + + {% endif %} +
{% trans "No collections related yet!" %}
+
+
+
+
+
+

{% trans "API Token" %}:

+
+

{% trans "This is your token" %}: {{user.api_key.key}}

+

+ + {% trans 'Read more about the API usage' %} + +

+
+
+
+
+
+
+
{% endblock %} -{% block content %} -
- -
-
-

API Token

-

{{user.api_key.key}}

-

- - {% trans 'Read more about the API usage' %} - -

-
+{% block extrafooter %} +{{ block.super }} + {% endblock %} diff --git a/scielomanager/accounts/urls.py b/scielomanager/accounts/urls.py index 8a3c5d55..9b96650f 100644 --- a/scielomanager/accounts/urls.py +++ b/scielomanager/accounts/urls.py @@ -5,6 +5,7 @@ from django.views.generic.base import TemplateView from . import views +from . import forms urlpatterns = patterns('', @@ -23,6 +24,7 @@ 'template_name': 'registration/password_reset_form.html', 'email_template_name': 'registration/password_reset_email.html', 'post_reset_redirect': '/accounts/password/reset/done/', + 'password_reset_form': forms.PasswordResetForm, }, name='registration.password_reset'), diff --git a/scielomanager/accounts/views.py b/scielomanager/accounts/views.py index c7d01eb0..f201c531 100644 --- a/scielomanager/accounts/views.py +++ b/scielomanager/accounts/views.py @@ -8,13 +8,32 @@ from django.utils.translation import ugettext as _ from django.contrib.auth import authenticate from django.conf import settings +from journalmanager.forms import UserProfileForm from . import forms @login_required def my_account(request): - return render_to_response('accounts/my_account.html', {}, + profile_form = UserProfileForm(instance=request.user.get_profile()) + password_form = forms.PasswordChangeForm() + # password_form faz post na view: password_change, então não deve ser tratado aqui + if request.method == "POST": + profile_form = UserProfileForm(request.POST, instance=request.user.get_profile()) + if profile_form.is_valid(): + profile_form.save() + messages.success(request, _('Saved successfully')) + else: + messages.error(request, _('There are some errors or missing data.')) + + my_collecttions = [{'name': c.name, 'is_manager': c.is_managed_by_user(request.user)} for c in request.user.user_collection.all()] + + context = { + 'profile_form': profile_form, + 'password_form': password_form, + 'my_collecttions': my_collecttions, + } + return render_to_response('accounts/my_account.html', context, context_instance=RequestContext(request)) diff --git a/scielomanager/articletrack/notifications.py b/scielomanager/articletrack/notifications.py index 299af863..4792170a 100644 --- a/scielomanager/articletrack/notifications.py +++ b/scielomanager/articletrack/notifications.py @@ -3,6 +3,7 @@ import logging from scielomanager import notifications +from scielomanager.tools import user_receive_emails logger = logging.getLogger(__name__) @@ -48,7 +49,7 @@ def set_recipients(self, checkin): and the submitter (checkin.submitted_by). """ if checkin.team_members: - send_to = set([member.email for member in checkin.team_members]) + send_to = set([member.email for member in checkin.team_members if user_receive_emails(member)]) # the submitter already belong to a related team self.recipients = list(send_to) else: @@ -88,11 +89,14 @@ def set_recipients(self, ticket): and the submitter (checkin.submitted_by, already belong to a team) of each checkin, and the author to the ticket related. """ - send_to = set([ticket.author.email, ]) + send_to = set() + if user_receive_emails(ticket.author): + send_to.update([unicode(ticket.author.email), ]) + for checkin in ticket.article.checkins.all(): # the submitter already belong to a related team if checkin.team_members: - send_to.update([member.email for member in checkin.team_members]) + send_to.update([member.email for member in checkin.team_members if user_receive_emails(member)]) else: logger.info("[TicketMessage.set_recipients] Can't prepare a message, checkin.team_members is empty. Checkin pk == %s" % checkin.pk) self.recipients = list(send_to) diff --git a/scielomanager/articletrack/templates/articletrack/includes/article_and_checkin_information.html b/scielomanager/articletrack/templates/articletrack/includes/article_and_checkin_information.html index 3f74ce4f..e63e3e2b 100644 --- a/scielomanager/articletrack/templates/articletrack/includes/article_and_checkin_information.html +++ b/scielomanager/articletrack/templates/articletrack/includes/article_and_checkin_information.html @@ -1,4 +1,5 @@ {% load i18n %} +{% load tz %}
@@ -51,7 +52,7 @@

{% trans "Check-in Information" %}:

{% trans "Package name" %}:
{{ checkin.package_name }}
{% trans "Updated at" %}:
-
{{ checkin.created_at|date:"d/m/Y - H:i" }}
+
{{ checkin.created_at|timezone:user.get_profile.tz|date:"d/m/Y - H:i" }}
{% trans "Submitted by" %}:
{% if checkin.submitted_by %}
@@ -75,7 +76,7 @@

{% trans "Check-in Information" %}:

{% endwith %}
{% trans "Reviewed at" %}:
-
{{ checkin.reviewed_at|date:"d/m/Y - H:i" }}
+
{{ checkin.reviewed_at|timezone:user.get_profile.tz|date:"d/m/Y - H:i" }}
{% endif %} {%if checkin.scielo_reviewed_by %} @@ -88,7 +89,7 @@

{% trans "Check-in Information" %}:

{% endwith %}
{% trans "Reviewed at" %}:
-
{{ checkin.scielo_reviewed_at|date:"d/m/Y - H:i" }}
+
{{ checkin.scielo_reviewed_at|timezone:user.get_profile.tz|date:"d/m/Y - H:i" }}
{% endif %} {% endif %} @@ -103,7 +104,7 @@

{% trans "Check-in Information" %}:

{% endwith %}
{% trans "Accepted at" %}:
-
{{ checkin.accepted_at|date:"d/m/Y - H:i" }}
+
{{ checkin.accepted_at|timezone:user.get_profile.tz|date:"d/m/Y - H:i" }}
{% elif checkin.is_rejected %}
{% trans "Rejection info" %}:
@@ -115,7 +116,7 @@

{% trans "Check-in Information" %}:

{% endwith %}
{% trans "Rejected at" %}:
-
{{ checkin.rejected_at|date:"d/m/Y - H:i" }}
+
{{ checkin.rejected_at|timezone:user.get_profile.tz|date:"d/m/Y - H:i" }}
{% trans "Rejected reason" %}:
{{ checkin.rejected_cause }}
@@ -125,7 +126,7 @@

{% trans "Check-in Information" %}:

{% trans "Will Expire at" %}:
- {{ checkin.expiration_at|date:"d/m/Y - H:i" }} + {{ checkin.expiration_at|timezone:user.get_profile.tz|date:"d/m/Y - H:i" }}
{% endif %} diff --git a/scielomanager/articletrack/templates/articletrack/includes/checkin_list_and_filterform.html b/scielomanager/articletrack/templates/articletrack/includes/checkin_list_and_filterform.html index 9fdf752a..1ea25736 100644 --- a/scielomanager/articletrack/templates/articletrack/includes/checkin_list_and_filterform.html +++ b/scielomanager/articletrack/templates/articletrack/includes/checkin_list_and_filterform.html @@ -1,4 +1,5 @@ {% load i18n %} +{% load tz %} {% load pagination_tags %} {% load trans_status %} @@ -43,7 +44,7 @@ {{ checkin.article.article_title }} {{ checkin.article.journal_title }} {{ checkin.article.issue_label }} - {{ checkin.created_at|date:"d/m/Y - H:i" }} + {{ checkin.created_at|timezone:user.get_profile.tz|date:"d/m/Y - H:i" }} {% if checkin.get_error_level == 'in progress' %} @@ -55,13 +56,13 @@ {% if status == "review" %} {{ checkin.reviewed_by.get_full_name|default:"--" }} - {{ checkin.reviewed_at|date:"d/m/Y - H:i"|default:"--" }} + {{ checkin.reviewed_at|timezone:user.get_profile.tz|date:"d/m/Y - H:i"|default:"--" }} {% elif status == "accepted" %} {{ checkin.accepted_by.get_full_name|default:"--" }} - {{ checkin.accepted_at|date:"d/m/Y - H:i"|default:"--" }} + {{ checkin.accepted_at|timezone:user.get_profile.tz|date:"d/m/Y - H:i"|default:"--" }} {% elif status == "rejected" %} {{ checkin.rejected_by.get_full_name|default:"--" }} - {{ checkin.rejected_at|date:"d/m/Y - H:i"|default:"--" }} + {{ checkin.rejected_at|timezone:user.get_profile.tz|date:"d/m/Y - H:i"|default:"--" }} {% endif %}
diff --git a/scielomanager/articletrack/templates/articletrack/includes/checkin_status_notice_block.html b/scielomanager/articletrack/templates/articletrack/includes/checkin_status_notice_block.html index 89acf812..898345b6 100644 --- a/scielomanager/articletrack/templates/articletrack/includes/checkin_status_notice_block.html +++ b/scielomanager/articletrack/templates/articletrack/includes/checkin_status_notice_block.html @@ -1,4 +1,5 @@ {% load i18n %} +{% load tz %}
@@ -11,7 +12,7 @@

{% trans "REJECTED" %}:

{% with checkin.rejected_by as user %} {% include "articletrack/includes/gravatar_tooltip.html" %} {% endwith %} - {% trans "at" %} {{ checkin.rejected_at|date:"d/m/Y - H:i" }} + {% trans "at" %} {{ checkin.rejected_at|timezone:user.get_profile.tz|date:"d/m/Y - H:i" }}

@@ -24,7 +25,7 @@

{% trans "ACCEPTED" %}:

{% with checkin.accepted_by as user %} {% include "articletrack/includes/gravatar_tooltip.html" %} {% endwith %} - {% trans "at" %} {{ checkin.accepted_at|date:"d/m/Y - H:i" }} + {% trans "at" %} {{ checkin.accepted_at|timezone:user.get_profile.tz|date:"d/m/Y - H:i" }}

@@ -53,7 +54,7 @@

{% trans "CHECKED-OUT" %}:

{% trans "THIS CHECKIN WILL EXPIRE TODAY" %}:

{% trans "This checkin will expire at: " %} - {% trans "at" %} {{ checkin.expiration_at|date:"d/m/Y - H:i" }} + {% trans "at" %} {{ checkin.expiration_at|timezone:user.get_profile.tz|date:"d/m/Y - H:i" }}

{% trans "After that date, the checkin will be unreachable." %} diff --git a/scielomanager/articletrack/templates/articletrack/notice_detail.html b/scielomanager/articletrack/templates/articletrack/notice_detail.html index 0537ef3b..3ecd4d56 100644 --- a/scielomanager/articletrack/templates/articletrack/notice_detail.html +++ b/scielomanager/articletrack/templates/articletrack/notice_detail.html @@ -1,5 +1,6 @@ {% extends "base_list_lv0.html" %} {% load i18n %} +{% load tz %} {% load static %} {% load trans_status %} {% load user_avatar %} @@ -192,7 +193,7 @@

{% trans 'Notices' %}:

{{ notice.message }} - {{ notice.created_at|date:"d/m/Y - H:i" }} + {{ notice.created_at|timezone:user.get_profile.tz|date:"d/m/Y - H:i" }} {% empty %} @@ -356,7 +357,7 @@

{% trans 'Previous attempts' %}:

{{ forloop.revcounter }} - {{ check.created_at|date:"d/m/Y - H:i" }} + {{ check.created_at|timezone:user.get_profile.tz|date:"d/m/Y - H:i" }} {% if check.pk == checkin.pk %} {% trans "current" %} {% endif %} diff --git a/scielomanager/articletrack/templates/articletrack/ticket_add.html b/scielomanager/articletrack/templates/articletrack/ticket_add.html index 973c0264..e36f18e4 100644 --- a/scielomanager/articletrack/templates/articletrack/ticket_add.html +++ b/scielomanager/articletrack/templates/articletrack/ticket_add.html @@ -1,5 +1,6 @@ {% extends "base_list_lv0.html" %} {% load i18n %} +{% load tz %} {% block page_title %}{% trans "Add a Ticket" %}{% endblock %} @@ -19,7 +20,7 @@

{% trans "Checking" %}:

{{ checkin.package_name }} {{ checkin.attempt_ref }} - {{ checkin.created_at|date:"d/m/Y - H:i" }} + {{ checkin.created_at|timezone:user.get_profile.tz|date:"d/m/Y - H:i" }}
@@ -45,4 +46,4 @@

{% trans "New Ticket" %}:

-{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/scielomanager/articletrack/templates/articletrack/ticket_detail.html b/scielomanager/articletrack/templates/articletrack/ticket_detail.html index bd1cd056..705a8779 100644 --- a/scielomanager/articletrack/templates/articletrack/ticket_detail.html +++ b/scielomanager/articletrack/templates/articletrack/ticket_detail.html @@ -1,5 +1,6 @@ {% extends "base_list_lv0.html" %} {% load i18n %} +{% load tz %} {% load urlize_ticket %} {% load user_avatar %} @@ -29,9 +30,9 @@