Browse Source

Warn user view. #37

Rafał Pitoń 11 years ago
parent
commit
a9448c4c23

+ 4 - 4
misago/acl/permissions/warnings.py

@@ -40,7 +40,7 @@ class WarningsACL(BaseACL):
 
 
     def can_warn_members(self):
     def can_warn_members(self):
         try:
         try:
-            self.allow_member_warn()
+            self.allow_warning_members()
             return True
             return True
         except ACLError403:
         except ACLError403:
             return False
             return False
@@ -66,9 +66,9 @@ def build(acl, roles):
     acl.warnings = WarningsACL()
     acl.warnings = WarningsACL()
     acl.warnings.acl['can_warn_members'] = False
     acl.warnings.acl['can_warn_members'] = False
     acl.warnings.acl['can_see_other_members_warns'] = False
     acl.warnings.acl['can_see_other_members_warns'] = False
-    acl.warnings.acl['can_be_warned'] = False
-    acl.warnings.acl['can_cancel_warnings'] = 0
-    acl.warnings.acl['can_cancel_warnings_newer_than'] = 0
+    acl.warnings.acl['can_be_warned'] = True
+    acl.warnings.acl['can_cancel_warnings'] = 1
+    acl.warnings.acl['can_cancel_warnings_newer_than'] = 5
     acl.warnings.acl['can_delete_warnings'] = False
     acl.warnings.acl['can_delete_warnings'] = False
 
 
     for role in roles:
     for role in roles:

+ 3 - 1
misago/apps/warnuser/forms.py

@@ -3,4 +3,6 @@ import floppyforms as forms
 from misago.forms import Form
 from misago.forms import Form
 
 
 class WarnMemberForm(Form):
 class WarnMemberForm(Form):
-    reason = forms.CharField(label=_("Warning reason"), max_length=2048)
+    reason = forms.CharField(label=_("Warning Reason"), max_length=2048,
+                             widget=forms.Textarea,
+                             error_messages={'max_length': _("Warn reason is too long.")})

+ 68 - 6
misago/apps/warnuser/views.py

@@ -1,11 +1,15 @@
+from datetime import timedelta
+from django.core.urlresolvers import reverse
 from django.shortcuts import redirect
 from django.shortcuts import redirect
 from django.template import RequestContext
 from django.template import RequestContext
+from django.utils import timezone
 from django.utils.translation import ugettext as _
 from django.utils.translation import ugettext as _
 from misago import messages
 from misago import messages
 from misago.acl.exceptions import ACLError403
 from misago.acl.exceptions import ACLError403
 from misago.apps.errors import error403, error404
 from misago.apps.errors import error403, error404
+from misago.apps.warnuser.forms import WarnMemberForm
 from misago.decorators import block_guest, check_csrf
 from misago.decorators import block_guest, check_csrf
-from misago.models import User
+from misago.models import User, Warn, WarnLevel
 from misago.shortcuts import render_to_response
 from misago.shortcuts import render_to_response
 
 
 @block_guest
 @block_guest
@@ -14,15 +18,73 @@ def warn_user(request, user, slug):
     try:
     try:
         user = User.objects.get(pk=user)
         user = User.objects.get(pk=user)
     except User.DoesNotExist:
     except User.DoesNotExist:
-        return error404(request, _("Requested user could not be found"))
+        return error404(request, _("Requested user could not be found."))
 
 
     try:
     try:
-        request.acl.warnings.allow_warning_members():
+        request.acl.warnings.allow_warning_members()
         user.acl().warnings.allow_warning()
         user.acl().warnings.allow_warning()
     except ACLError403 as e:
     except ACLError403 as e:
         return error403(request, e)
         return error403(request, e)
 
 
-    form = 123
+    if not WarnLevel.objects.get_level(1):
+        messages.error(request, _("No warning levels have been defined."))
+        return redirect(request.POST.get('retreat',
+            reverse('user', kwargs={
+                'user': user.pk,
+                'username': user.username_slug,
+                })))
+
+    current_warning_level = user.get_current_warning_level()
+    next_warning_level = WarnLevel.objects.get_level(user.warning_level + 1)
+
+    if not next_warning_level:
+        return render_to_response('warn_user/max_level.html',
+                                  {
+                                   'warned_user': user,
+                                   'retreat': request.POST.get('retreat'),
+                                  },
+                                  context_instance=RequestContext(request))
+
+    form = WarnMemberForm(initial={'reason': request.POST.get('reason')})
     if ('origin' in request.POST
     if ('origin' in request.POST
-            and request.POST.get('origin') == 'warning_form'):
-        pass
+            and request.POST.get('origin') == 'warning-form'):
+        form = WarnMemberForm(request.POST, request=request)
+        if form.is_valid():
+            user.warning_level += 1
+            if next_warning_level.expires_after_minutes:
+                user.warning_level_update_on = timezone.now()
+                user.warning_level_update_on += timedelta(
+                    minutes=next_warning_level.expires_after_minutes)
+            else:
+                user.warning_level_update_on = None
+            user.save(force_update=True)
+
+            Warn.objects.create(
+                user=user,
+                giver=request.user,
+                giver_name=request.user.username,
+                giver_slug=request.user.username_slug,
+                date=timezone.now(),
+                ip=request.session.get_ip(request),
+                agent=request.META.get('HTTP_USER_AGENT'),
+                reason=form.cleaned_data['reason'],
+                )
+
+            messages.success(request,
+                _("%(user)s warning level has been increased.") % {
+                    'user': user.username})
+            return redirect(request.POST.get('retreat',
+                reverse('user', kwargs={
+                    'user': user.pk,
+                    'username': user.username_slug,
+                    })))
+
+    return render_to_response('warn_user/form.html',
+                              {
+                               'warned_user': user,
+                               'current_warning_level': current_warning_level,
+                               'next_warning_level': next_warning_level,
+                               'form': form,
+                               'retreat': request.POST.get('retreat'),
+                              },
+                              context_instance=RequestContext(request))

+ 2 - 2
misago/models/forummodel.py

@@ -35,8 +35,8 @@ class ForumManager(TreeManager):
 
 
     def populate_tree(self, force=False):
     def populate_tree(self, force=False):
         if not self.forums_tree:
         if not self.forums_tree:
-            self.forums_tree = cache.get('forums_tree')
-        if not self.forums_tree or force:
+            self.forums_tree = cache.get('forums_tree', 'nada')
+        if self.forums_tree == 'nada' or force:
             self.forums_tree = {}
             self.forums_tree = {}
             for forum in Forum.objects.order_by('lft'):
             for forum in Forum.objects.order_by('lft'):
                 self.forums_tree[forum.pk] = forum
                 self.forums_tree[forum.pk] = forum

+ 35 - 0
misago/models/usermodel.py

@@ -539,6 +539,41 @@ class User(models.Model):
             return True
             return True
         return False
         return False
 
 
+    def is_warning_level_expired(self):
+        if self.warning_level and self.warning_level_update_on:
+            return timezone.now() > self.warning_level_update_on
+        return False
+
+    def update_warning_level(self):
+        if self.is_warning_level_expired():
+            self.warning_level -= 1
+            self.warning_level_update_on = None
+
+        warning_level_model = None
+        if self.warning_level:
+            from misago.models import WarnLevel
+            warning_level_model = WarnLevel.objects.get_level(
+                self.warning_level)
+            if (warning_level_model
+                    and warning_level_model.expires_after_minutes):
+                self.warning_level_update_on = timezone.now()
+                self.warning_level_update_on += timedelta(
+                    minutes=warning_level_model.expires_after_minutes)
+        self.save(force_update=True)
+
+    def get_warning_level(self):
+        if self.warning_level:
+            from misago.models import WarnLevel
+            return WarnLevel.objects.get_level(
+                self.warning_level)
+        else:
+            return None
+
+    def get_current_warning_level(self):
+        if self.is_warning_level_expired():
+            self.update_warning_level()
+        return self.get_warning_level()
+
     def timeline(self, qs, length=100):
     def timeline(self, qs, length=100):
         posts = {}
         posts = {}
         now = tz_util.now()
         now = tz_util.now()

+ 42 - 0
misago/models/warnlevelmodel.py

@@ -1,4 +1,36 @@
+from django.core.cache import cache
 from django.db import models
 from django.db import models
+from django.utils.datastructures import SortedDict
+from misago.thread import local
+
+_thread_local = local()
+
+class WarnLevelManager(models.Manager):
+    def get_levels(self):
+        try:
+            return _thread_local._misago_warning_levels
+        except AttributeError:
+            _thread_local._misago_warning_levels = self.fetch_levels()
+            return _thread_local._misago_warning_levels
+
+    def get_level(self, level):
+        return self.get_levels().get(level)
+
+    def fetch_levels(self):
+        from_cache = cache.get('warning_levels', 'nada')
+        if from_cache != 'nada':
+            return from_cache
+
+        from_db = self.fetch_levels_from_db()
+        cache.set('warning_levels', from_db)
+        return from_db
+
+    def fetch_levels_from_db(self):
+        fetched_levels = SortedDict()
+        for level in self.order_by('warning_level').iterator():
+            fetched_levels[level.warning_level] = level
+        return fetched_levels
+
 
 
 class WarnLevel(models.Model):
 class WarnLevel(models.Model):
     name = models.CharField(max_length=255)
     name = models.CharField(max_length=255)
@@ -9,5 +41,15 @@ class WarnLevel(models.Model):
     inhibit_posting_replies = models.PositiveIntegerField(default=0)
     inhibit_posting_replies = models.PositiveIntegerField(default=0)
     inhibit_posting_threads = models.PositiveIntegerField(default=0)
     inhibit_posting_threads = models.PositiveIntegerField(default=0)
 
 
+    objects = WarnLevelManager()
+
     class Meta:
     class Meta:
         app_label = 'misago'
         app_label = 'misago'
+
+    def save(self, *args, **kwargs):
+        super(WarnLevel, self).save(*args, **kwargs)
+        cache.delete('warning_levels')
+
+    def delete(self, *args, **kwargs):
+        super(WarnLevel, self).delete(*args, **kwargs)
+        cache.delete('warning_levels')

+ 1 - 1
misago/urls.py

@@ -22,7 +22,7 @@ urlpatterns = patterns('misago.apps',
     url(r'^popular/(?P<page>[1-9]([0-9]+)?)/$', 'popularthreads.popular_threads', name="popular_threads"),
     url(r'^popular/(?P<page>[1-9]([0-9]+)?)/$', 'popularthreads.popular_threads', name="popular_threads"),
     url(r'^new/$', 'newthreads.new_threads', name="new_threads"),
     url(r'^new/$', 'newthreads.new_threads', name="new_threads"),
     url(r'^new/(?P<page>[1-9]([0-9]+)?)/$', 'newthreads.new_threads', name="new_threads"),
     url(r'^new/(?P<page>[1-9]([0-9]+)?)/$', 'newthreads.new_threads', name="new_threads"),
-    url(r'^warn-user/(?P<slug>\w+)-(?P<user>\d+)/', 'warnuser.warn_user', name="warn_user"),
+    url(r'^warn-user/(?P<slug>\w+)-(?P<user>\d+)/', 'warnuser.views.warn_user', name="warn_user"),
 )
 )
 
 
 urlpatterns += patterns('',
 urlpatterns += patterns('',

+ 2 - 2
templates/cranefly/private_threads/thread.html

@@ -278,10 +278,10 @@
               <div class="post-footer">{% filter trim %}
               <div class="post-footer">{% filter trim %}
                 <div class="post-actions">
                 <div class="post-actions">
                   {% if user.pk != post.user_id and post.user_id and acl.warnings.can_warn_members() %}
                   {% if user.pk != post.user_id and post.user_id and acl.warnings.can_warn_members() %}
-                  <form action="{{ url('post_report', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="form-inline form-report" method="post" autocomplete="off">
+                  <form action="{{ url('warn_user', user=post.user.pk, slug=post.user.username_slug) }}" class="form-inline" method="post" autocomplete="off">
                     <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
                     <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
                     <input type="hidden" name="retreat" value="{{ request_path }}">
                     <input type="hidden" name="retreat" value="{{ request_path }}">
-                    <input type="hidden" name="reason" value="{% trans message=url('private_thread_find', thread=thread.pk, slug=thread.slug, post=post.pk), thread=thread.name %}Your message {{ message }} in thread "{{ thread }}" was found to violate community guidelines.{% endtrans %}">
+                    <input type="hidden" name="reason" value="{% trans message=url('private_thread_find', thread=thread.pk, slug=thread.slug, post=post.pk), thread=thread.name %}Your message {{ message }} in thread &quot;{{ thread }}&quot; was found to violate community guidelines.{% endtrans %}">
                     <button type="submit" class="btn btn-link tooltip-top" title="{% trans %}Warn user for this post.{% endtrans %}">{% trans %}Warn{% endtrans %}</button>
                     <button type="submit" class="btn btn-link tooltip-top" title="{% trans %}Warn user for this post.{% endtrans %}">{% trans %}Warn{% endtrans %}</button>
                   </form>
                   </form>
                   {% endif %}
                   {% endif %}

+ 1 - 1
templates/cranefly/profiles/profile.html

@@ -78,7 +78,7 @@
             {% endif %}
             {% endif %}
             {% if acl.warnings.can_warn_members() %}
             {% if acl.warnings.can_warn_members() %}
             <li class="pull-right">
             <li class="pull-right">
-              <form class="form-inline form-destroy-user" action="{{ url('destroy_user', username=profile.username_slug, user=profile.pk) }}" method="post">
+              <form class="form-inline" action="{{ url('warn_user', user=profile.pk, slug=profile.username_slug) }}" method="post">
                 <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
                 <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
                 <input type="hidden" name="retreat" value="{{ request_path }}">
                 <input type="hidden" name="retreat" value="{{ request_path }}">
                 <input type="hidden" name="reason" value="{% trans %}Your profile contents were found to violate community guidelines.{% endtrans %}">
                 <input type="hidden" name="reason" value="{% trans %}Your profile contents were found to violate community guidelines.{% endtrans %}">

+ 2 - 2
templates/cranefly/threads/thread.html

@@ -387,10 +387,10 @@
             {% if user.is_authenticated() %}
             {% if user.is_authenticated() %}
             <div class="post-actions">
             <div class="post-actions">
               {% if user.pk != post.user_id and post.user_id and acl.warnings.can_warn_members() %}
               {% if user.pk != post.user_id and post.user_id and acl.warnings.can_warn_members() %}
-              <form action="{{ url('post_report', thread=thread.pk, slug=thread.slug, post=post.pk) }}" class="form-inline form-report" method="post" autocomplete="off">
+              <form action="{{ url('warn_user', user=post.user.pk, slug=post.user.username_slug) }}" class="form-inline" method="post" autocomplete="off">
                 <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
                 <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
                 <input type="hidden" name="retreat" value="{{ request_path }}">
                 <input type="hidden" name="retreat" value="{{ request_path }}">
-                <input type="hidden" name="reason" value="{% trans message=url('thread_find', thread=thread.pk, slug=thread.slug, post=post.pk), thread=thread.name %}Your message {{ message }} in thread "{{ thread }}" was found to violate community guidelines.{% endtrans %}">
+                <input type="hidden" name="reason" value="{% trans message=url('thread_find', thread=thread.pk, slug=thread.slug, post=post.pk), thread=thread.name %}Your message {{ message }} in thread &quot;{{ thread }}&quot; was found to violate community guidelines.{% endtrans %}">
                 <button type="submit" class="btn btn-link tooltip-top" title="{% trans %}Warn user for this post.{% endtrans %}">{% trans %}Warn{% endtrans %}</button>
                 <button type="submit" class="btn btn-link tooltip-top" title="{% trans %}Warn user for this post.{% endtrans %}">{% trans %}Warn{% endtrans %}</button>
               </form>
               </form>
               {% endif %}
               {% endif %}

+ 85 - 0
templates/cranefly/warn_user/form.html

@@ -0,0 +1,85 @@
+{% extends "cranefly/layout.html" %}
+{% import "forms.html" as form_theme with context %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=warned_user.username, parent=_('Warn User')) }}{% endblock %}
+
+{% block content %}
+<div class="row">
+  <div class="span6 offset3">
+    <div class="form-container container-horizontal">
+
+      <div class="form-header">
+        <h1>{% trans user=warned_user.username %}Warn User: {{ user }}{% endtrans %}</h1>
+      </div>
+
+      <form action="{{ url('warn_user', user=warned_user.pk, slug=warned_user.username_slug) }}" method="post">
+        <input type="hidden" name="{{ csrf_id }}" value="{{ csrf_token }}">
+        <input type="hidden" name="origin" value="warning-form">
+        {% if retreat %}
+        <input type="hidden" name="retreat" value="{{ retreat }}">
+        {% endif %}
+        <div class="form-fields">
+
+          <fieldset>
+            {{ form_theme.row(form.reason, attrs={'class': 'span6', 'rows': 4}) }}
+          </fieldset>
+
+          <hr>
+
+          <h4>{% trans next_level_name=wrap_level_name(next_warning_level) %}Next Warning Level: {{ next_level_name }}{% endtrans %}</h4>
+          {% if next_warning_level.description %}
+          <p>{{ next_warning_level.description }}</p>
+          {% endif %}
+
+          <table class="table">
+            <tr>
+              <th class="text-right">{% trans %}Expires After:{% endtrans %}</th>
+              <td>{{ level_restriction_expires(next_warning_level.expires_after_minutes) }}</td>
+            </tr>
+            <tr>
+              <th class="text-right">{% trans %}Posting Replies Restriction:{% endtrans %}</th>
+              <td>{{ level_restriction_legend(next_warning_level.inhibit_posting_replies) }}</td>
+            </tr>
+            <tr>
+              <th class="text-right">{% trans %}Posting Threads Restriction:{% endtrans %}</th>
+              <td>{{ level_restriction_legend(next_warning_level.inhibit_posting_threads) }}</td>
+            </tr>
+          </table>
+
+        </div>
+        <div class="form-actions">
+          <button type="submit" class="btn btn-primary">{% trans %}Warn User{% endtrans %}</button>
+          {% if retreat %}
+          <a href="{{ retreat }}" class="btn">{% trans %}Cancel{% endtrans %}</a>
+          {% endif %}
+        </div>
+      </form>
+
+    </div>
+  </div>
+</div>
+{% endblock %}
+
+{# Macros #}
+{% macro wrap_level_name(warning_level) -%}
+<strong>{{ warning_level.name }}</strong>
+{%- endmacro %}
+
+{% macro level_restriction_expires(level_expires) -%}
+<span class="icon-clock"></span> {% if level_expires > 0 %}
+{{ level_expires|timeamount|capfirst }}
+{% else %}
+{% trans %}Never{% endtrans %}
+{% endif %}
+{%- endmacro %}
+
+{% macro level_restriction_legend(restriction) -%}
+{% if restriction == 0 %}
+<div class="text-success"><span class="icon-ok"></span> {% trans %}No{% endtrans %}</div>
+{% elif restriction == 1 %}
+<div class="text-info"><span class="icon-eye-open"></span> {% trans %}Moderator Review{% endtrans %}</div>
+{% elif restriction == 2 %}
+<div class="text-error"><span class="icon-remove"></span> {% trans %}Forbidden{% endtrans %}</div>
+{% endif %}
+{%- endmacro %}

+ 26 - 0
templates/cranefly/warn_user/max_level.html

@@ -0,0 +1,26 @@
+{% extends "cranefly/layout.html" %}
+{% import "forms.html" as form_theme with context %}
+{% import "cranefly/macros.html" as macros with context %}
+
+{% block title %}{{ macros.page_title(title=warned_user.username, parent=_('Warn User')) }}{% endblock %}
+
+{% block content %}
+<div class="row">
+  <div class="span6 offset3">
+    <div class="form-container container-horizontal">
+
+      <div class="form-header">
+        <h1>{% trans user=warned_user.username %}Warn User: {{ user }}{% endtrans %}</h1>
+      </div>
+
+      <div class="form-fields">
+        <p>{% trans %}This user warning level is already at maximum and can not be increased any further.{% endtrans %}</p>
+        {% if retreat %}
+        <p><a href="{{ retreat }}">{% trans %}Go Back{% endtrans %}</a></p>
+        {% endif %}
+      </div>
+
+    </div>
+  </div>
+</div>
+{% endblock %}

+ 1 - 1
templates/forms.html

@@ -9,7 +9,7 @@
 
 
 {% macro row(_field, label=None, help_text=None, attrs=None) -%}
 {% macro row(_field, label=None, help_text=None, attrs=None) -%}
   <div id="{{ _field.html_name }}-control-group" class="control-group{% if _field.errors %} error{% endif %}">
   <div id="{{ _field.html_name }}-control-group" class="control-group{% if _field.errors %} error{% endif %}">
-    <label class="control-label" for="id_{{ _field.html_name }}">{% if label %}{{ label }}{% elif _field.label %}{{ _field.label }}{% else %}{{ _field.html_name }}{% endif %}</label>
+    <label class="control-label" for="id_{{ _field.html_name }}">{% if label %}{{ label }}{% elif _field.label %}{{ _field.label }}{% else %}{{ _field.html_name }}{% endif %}:</label>
     <div class="controls">
     <div class="controls">
       {% if attrs == None %}{% set attrs = {} %}{% endif %}
       {% if attrs == None %}{% set attrs = {} %}{% endif %}
       {% if _field.field.widget.__class__.__name__ == 'ForumTOS' %}{% do attrs.update({'inline': make_tos_label()}) %}{% endif %}
       {% if _field.field.widget.__class__.__name__ == 'ForumTOS' %}{% do attrs.update({'inline': make_tos_label()}) %}{% endif %}