{{ content_title }}
{{ _('Please override the "content" block of your template!') }}
- {% endif %} - {% endblock %} +{{ content_title }}
{{ _('Please override the "content" block of your template!') }}
+ {% endif %} + {% endblock %} + {% endif %} +diff --git a/lib/assets/js/comment-form-animation.js b/lib/assets/js/comment-form-animation.js
index 83c1ef2f1..5ee42e74f 100644
--- a/lib/assets/js/comment-form-animation.js
+++ b/lib/assets/js/comment-form-animation.js
@@ -16,7 +16,7 @@ function showForm(button) {
$(document).click(function(event) {
if(!$(event.target).closest('#commentform').length && !$(event.target).closest('#button-create').length) {
var commentform = document.getElementById("commentform");
- if(commentform.className == "show") {
+ if(commentform && commentform.className == "show") {
button = document.getElementsByClassName('fas fa-times')[0];
makeFormDisappear(commentform, button);
}
diff --git a/lib/scss/6_components/_conversations.scss b/lib/scss/6_components/_conversations.scss
index ee3fbe523..308a270d6 100644
--- a/lib/scss/6_components/_conversations.scss
+++ b/lib/scss/6_components/_conversations.scss
@@ -11,6 +11,7 @@ $profile-label-clear-color: #30BFD3 !default;
.ConversationList {
@extend .Container;
+ max-width: 100%;
p {
padding: 0 $spacing-unit / 3;
diff --git a/lib/scss/6_components/generic/_page-header.scss b/lib/scss/6_components/generic/_page-header.scss
index 4b39e8e84..eacdc8e32 100644
--- a/lib/scss/6_components/generic/_page-header.scss
+++ b/lib/scss/6_components/generic/_page-header.scss
@@ -41,6 +41,13 @@ $header-box-shadow-lower: 0 -2px 4px 0 rgba(0, 0, 0, 0.2) !default;
&-topIcon {
cursor: pointer;
+ display: none;
+ }
+
+ @media only screen and (max-width: 960px) {
+ &-topIcon {
+ display: block;
+ }
}
// Elements
diff --git a/lib/scss/6_components/generic/_page.scss b/lib/scss/6_components/generic/_page.scss
index 3e8b9fbb3..eca2e0526 100644
--- a/lib/scss/6_components/generic/_page.scss
+++ b/lib/scss/6_components/generic/_page.scss
@@ -23,6 +23,26 @@ $page-title-font-size: 1.1 * $font-size-h1 !default;
}
}
+ &-content-container {
+ display: flex;
+
+ .NavMenu {
+ position: fixed;
+ }
+ }
+
+ @media only screen and (min-width: 960px) {
+ &-content.logged-in, #docs {
+ padding-left: 400px !important;
+ }
+ }
+
+ @media only screen and (max-width: 960px) {
+ .NavMenu-fixed {
+ display: none;
+ }
+ }
+
&-mainContainer {
background: $page-background-image;
background-size: contain;
diff --git a/locale/pt_BR/LC_MESSAGES/django.mo b/locale/pt_BR/LC_MESSAGES/django.mo
index e71eb3d4b..9687aa12e 100644
Binary files a/locale/pt_BR/LC_MESSAGES/django.mo and b/locale/pt_BR/LC_MESSAGES/django.mo differ
diff --git a/locale/pt_BR/LC_MESSAGES/django.po b/locale/pt_BR/LC_MESSAGES/django.po
index fef498d11..4b37a5ed1 100644
--- a/locale/pt_BR/LC_MESSAGES/django.po
+++ b/locale/pt_BR/LC_MESSAGES/django.po
@@ -7,8 +7,8 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2018-11-06 16:51+0000\n"
-"PO-Revision-Date: 2018-11-06 15:03-0200\n"
+"POT-Creation-Date: 2018-11-14 03:05+0000\n"
+"PO-Revision-Date: 2018-11-14 01:09-0200\n"
"Last-Translator: Fábio Mendes {{ _('Please override the "content" block of your template!') }} {{ _('Please override the "content" block of your template!') }} {{ action_button(_('Create a conversation'), '/conversations/add')}} {{ action_button(_('Create a conversation'), '/profile/boards/add')}} {{ _('You still have {n} available comments').format(n=comments_left) }} {% trans %}You reached the limit of comments in this conversation.{% endtrans %}{{ content_title }}
{{ content_title }}
{% trans %}Leave your comment{% endtrans %}
{% trans %}Ooops!{% endtrans %}
{{ _("{}/{} comment{}.").format(comments_made, max_comments, "" if max_comments == 1 else "s")}}
+ +{{ _("{}/{} comment{}.").format(comments_made, max_comments if can_comment else 0, "" if max_comments == 1 else "s")}}
{{ _("{} waiting moderation.").format(comments_under_moderation)}}
diff --git a/src/ej_conversations/migrations/0004_update_fields.py b/src/ej_conversations/migrations/0004_update_fields.py new file mode 100644 index 000000000..2afaef2f2 --- /dev/null +++ b/src/ej_conversations/migrations/0004_update_fields.py @@ -0,0 +1,40 @@ +# Generated by Django 2.1.2 on 2018-11-07 14:03 + +import django.core.validators +from django.db import migrations, models +import ej_conversations.validators + + +class Migration(migrations.Migration): + + dependencies = [ + ('ej_conversations', '0003_conversation_hidden'), + ] + + operations = [ + migrations.RemoveField( + model_name='taggedconversation', + name='content_object', + ), + migrations.RemoveField( + model_name='taggedconversation', + name='tag', + ), + migrations.RemoveField( + model_name='conversation', + name='hidden', + ), + migrations.AddField( + model_name='conversation', + name='is_hidden', + field=models.BooleanField(default=False, help_text='Hidden conversations does not appears in boards or in the main /conversations/ endpoint.', verbose_name='hidden?'), + ), + migrations.AlterField( + model_name='comment', + name='content', + field=models.TextField(help_text='Body of text for the comment', max_length=252, validators=[django.core.validators.MinLengthValidator(2), ej_conversations.validators.is_not_empty], verbose_name='Content'), + ), + migrations.DeleteModel( + name='TaggedConversation', + ), + ] diff --git a/src/ej_conversations/models/conversation.py b/src/ej_conversations/models/conversation.py index a2e067c1c..0deed4d3a 100644 --- a/src/ej_conversations/models/conversation.py +++ b/src/ej_conversations/models/conversation.py @@ -67,6 +67,14 @@ class Conversation(TimeStampedModel): 'endpoint.' ), ) + is_hidden = models.BooleanField( + _('hidden?'), + default=False, + help_text=_( + 'Hidden conversations does not appears in boards or in the main /conversations/ ' + 'endpoint.' + ), + ) objects = ConversationManager() tags = TaggableManager(through='ConversationTag') @@ -123,7 +131,7 @@ def user_votes(self, user): """ if user.id is None: return Vote.objects.none() - return Vote.objects.filter(comment__conversation=self, author=user) + return Vote.objects.filter(comment__conversation=self, author=user, comment__status=Comment.STATUS.approved) def create_comment(self, author, content, commit=True, *, status=None, check_limits=True, **kwargs): diff --git a/src/ej_conversations/roles.py b/src/ej_conversations/roles.py index 52019e3e4..8d59afab9 100644 --- a/src/ej_conversations/roles.py +++ b/src/ej_conversations/roles.py @@ -18,7 +18,7 @@ def conversation_card(conversation, request=None, url=None, **kwargs): """ user = getattr(request, 'user', None) - is_author = conversation.author == user + can_moderate = user.has_perm('ej.can_moderate_conversation', conversation) return { 'conversation': conversation, 'url': url or conversation.get_absolute_url(), @@ -26,7 +26,7 @@ def conversation_card(conversation, request=None, url=None, **kwargs): 'n_comments': conversation.approved_comments.count(), 'n_votes': conversation.vote_count(), 'n_followers': conversation.followers.count(), - 'is_author': is_author, + 'user_can_moderate': can_moderate, **kwargs, } @@ -135,10 +135,14 @@ def comment_form(conversation, request=None, comment_content=None, **kwargs): """ user = getattr(request, 'user', None) n_comments = rules.compute('ej_conversations.remaining_comments', conversation, user) + conversation_url = conversation.get_absolute_url() + login = reverse('auth:login') + login_anchor = a(_('login'), href=f'{login}?next={conversation_url}') return { - 'can_comment': user.has_perm('ej.can_comment', conversation), + 'can_comment': user.is_authenticated, 'comments_left': n_comments, 'user_is_owner': conversation.author == user, 'csrf_input': csrf_input(request), 'comment_content': comment_content, + 'login_anchor': login_anchor, } diff --git a/src/ej_conversations/routes/admin.py b/src/ej_conversations/routes/admin.py index 826143c59..b899b3d72 100644 --- a/src/ej_conversations/routes/admin.py +++ b/src/ej_conversations/routes/admin.py @@ -10,8 +10,12 @@ log = getLogger('ej') -@urlpatterns.route('add/', login=True, perms=['ej.can_add_promoted_conversation']) +@urlpatterns.route('add/', login=True) def create(request): + # TODO: Fix this case of permission in django-boogie + if not request.user.has_perm('ej.can_add_promoted_conversation'): + raise Http404 + form = forms.ConversationForm(request.POST or None) if request.method == 'POST' and form.is_valid(): with transaction.atomic(): diff --git a/src/ej_conversations/routes/conversations.py b/src/ej_conversations/routes/conversations.py index 58025a9f0..2fc39f664 100644 --- a/src/ej_conversations/routes/conversations.py +++ b/src/ej_conversations/routes/conversations.py @@ -17,7 +17,7 @@ @urlpatterns.route('', name='list') def conversation_list(request): return { - 'conversations': Conversation.objects.filter(is_promoted=True), + 'conversations': Conversation.objects.filter(is_promoted=True, is_hidden=False), 'can_add_conversation': request.user.has_perm('ej.can_add_promoted_conversation'), 'create_url': reverse('conversation:create'), 'topic': _('A space for adolescents to discuss actions that promote, guarantee and defend their rights'), @@ -87,7 +87,7 @@ def get_conversation_detail_context(request, conversation): # Permissions and predicates 'is_favorite': is_favorite, - 'can_view_comment': user.is_authenticated, + 'can_comment': user.is_authenticated, 'can_edit': user.has_perm('ej.can_edit_conversation', conversation), 'cannot_comment_reason': '', 'comments_under_moderation': n_comments_under_moderation, diff --git a/src/ej_conversations/rules.py b/src/ej_conversations/rules.py index 2b5878748..4c0fefa79 100644 --- a/src/ej_conversations/rules.py +++ b/src/ej_conversations/rules.py @@ -146,14 +146,14 @@ def can_comment(user, conversation): @rules.register_perm('ej.can_add_promoted_conversation') -def can_add_promoted_conversation(user, conversation): +def can_add_promoted_conversation(user): """ - Check if user can comment in conversation. + Check if user can add a promoted conversation * Has explicit 'ej_conversations.can_publish_promoted' permission stored in the db. """ - return user.has_perm('ej_conversations.can_publish_promoted', conversation) + return user.has_perm('ej_conversations.can_publish_promoted') @rules.register_perm('ej.can_edit_conversation') diff --git a/src/ej_conversations/tests/conftest.py b/src/ej_conversations/tests/conftest.py index b543bd832..f8cceecbc 100644 --- a/src/ej_conversations/tests/conftest.py +++ b/src/ej_conversations/tests/conftest.py @@ -12,6 +12,10 @@ def user(db): user = User.objects.create_user('email@server.com', 'password') user.board_name = 'testboard' + + # TODO: Fix this dirty way to set user permissions + user.has_perm = lambda x, y=None: True + user.save() return user diff --git a/src/ej_conversations/tests/test_routes.py b/src/ej_conversations/tests/test_routes.py index caa3a809f..9e585c09a 100644 --- a/src/ej_conversations/tests/test_routes.py +++ b/src/ej_conversations/tests/test_routes.py @@ -9,7 +9,7 @@ class TestRoutes(UrlTester, ConversationRecipes): '/conversations/', '/conversations/conversation/', ] - login_urls = [ + admin_urls = [ '/conversations/add/', ] owner_urls = [ diff --git a/src/ej_profiles/migrations/0003_update_image_field.py b/src/ej_profiles/migrations/0003_update_image_field.py new file mode 100644 index 000000000..111afd7ca --- /dev/null +++ b/src/ej_profiles/migrations/0003_update_image_field.py @@ -0,0 +1,22 @@ +# Generated by Django 2.1.2 on 2018-11-08 14:24 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('ej_profiles', '0002_add_state_field'), + ] + + operations = [ + migrations.RemoveField( + model_name='profile', + name='image', + ), + migrations.AddField( + model_name='profile', + name='profile_photo', + field=models.ImageField(blank=True, null=True, upload_to='profile_images', verbose_name='Profile Photo'), + ), + ] diff --git a/src/ej_users/jinja2/ej_users/recover-password-message.jinja2 b/src/ej_users/jinja2/ej_users/recover-password-message.jinja2 index d7b31642b..a0f4e1b59 100644 --- a/src/ej_users/jinja2/ej_users/recover-password-message.jinja2 +++ b/src/ej_users/jinja2/ej_users/recover-password-message.jinja2 @@ -1,7 +1,9 @@ {% block content %} - Hello ! - You can use the following link to reset your password: - {{ link }} - Thanks, - Your friends at Empurrando Juntos. + {{_( + "Hello ! + You can use the following link to reset your password: + {{ link }} + Thanks, + Your friends at Empurrando Juntos." + )}} {% endblock %} \ No newline at end of file diff --git a/src/ej_users/routes.py b/src/ej_users/routes.py index 474a56f15..e2b09c214 100644 --- a/src/ej_users/routes.py +++ b/src/ej_users/routes.py @@ -1,5 +1,4 @@ import logging -import os from django.conf import settings from django.contrib import auth @@ -11,7 +10,6 @@ from django.shortcuts import redirect from django.template.loader import get_template from django.utils.translation import ugettext_lazy as _ -from jinja2 import FileSystemLoader, Environment from rest_framework import status from rest_framework.authtoken.models import Token @@ -134,14 +132,6 @@ def reset_password(request, token): @urlpatterns.route('recover-password/') def recover_password(request): form = forms.RecoverPasswordForm(request.POST or None) - - dirname = os.path.dirname(__file__) - template_dir = os.path.join(dirname, 'jinja2/ej_users') - - loader = FileSystemLoader(searchpath=template_dir) - environment = Environment(loader=loader) - template_file = "recover-password-message.jinja2" - template = environment.get_template(template_file) user = None success = False @@ -152,13 +142,14 @@ def recover_password(request): host = 'http://localhost:8000' else: - host = 'http://' + settings.HOSTNAME + host = settings.HOSTNAME user = User.objects.get_by_email(request.POST['email']) token = generate_token(user) - template_message = template.render({'link': host + '/reset-password/' + token.url}) + from_email = settings.DEFAULT_FROM_EMAIL + template_message = _recover_password_message(host + '/reset-password/' + token.url) send_mail(_("Please reset your password"), template_message, - 'empurrandojuntos@gmail.com', [request.POST['email']], + from_email, [request.POST['email']], fail_silently=False) return { @@ -200,3 +191,12 @@ def api_key(request): # Auxiliary functions and templates # login_extra_template = get_template('socialaccount/snippets/login_extra.html') + + +# E-MAIL MESSAGE +def _recover_password_message(link): + return _(f""" + Hello! You can use the following link to reset your password: + {link} + Thanks, + Your friends at Empurrando Juntos.""")