Browse Source

#702: comments cleanup

Rafał Pitoń 8 years ago
parent
commit
0d3b72cccf
64 changed files with 168 additions and 582 deletions
  1. 4 12
      misago/acl/api.py
  2. 1 3
      misago/acl/builder.py
  3. 1 3
      misago/acl/forms.py
  4. 1 3
      misago/acl/panels.py
  5. 3 7
      misago/acl/providers.py
  6. 1 3
      misago/admin/views/generic/base.py
  7. 4 19
      misago/admin/views/generic/list.py
  8. 1 3
      misago/admin/views/generic/mixin.py
  9. 0 10
      misago/categories/forms.py
  10. 0 3
      misago/categories/migrations/0003_categories_roles.py
  11. 0 20
      misago/categories/permissions.py
  12. 6 3
      misago/categories/tests/test_categories_admin_views.py
  13. 7 5
      misago/categories/tests/test_utils.py
  14. 2 6
      misago/categories/views/permsadmin.py
  15. 1 3
      misago/conf/forms.py
  16. 2 6
      misago/conf/tests/test_admin_views.py
  17. 3 3
      misago/core/exceptions.py
  18. 2 6
      misago/core/pgutils.py
  19. 8 12
      misago/core/templatetags/misago_forms.py
  20. 2 7
      misago/core/tests/test_exceptionhandlers.py
  21. 2 7
      misago/core/tests/test_migrationutils.py
  22. 1 3
      misago/core/testutils.py
  23. 13 26
      misago/core/utils.py
  24. 1 3
      misago/datamover/db.py
  25. 0 5
      misago/datamover/markup/__init__.py
  26. 0 3
      misago/markup/bbcode/inline.py
  27. 1 3
      misago/markup/parser.py
  28. 1 3
      misago/markup/pipeline.py
  29. 0 10
      misago/search/permissions.py
  30. 1 3
      misago/search/tests/test_searchproviders.py
  31. 1 3
      misago/threads/api/postingendpoint/__init__.py
  32. 1 3
      misago/threads/api/postingendpoint/category.py
  33. 1 3
      misago/threads/api/postingendpoint/privatethread.py
  34. 1 3
      misago/threads/api/postingendpoint/syncprivatethreads.py
  35. 2 7
      misago/threads/paginator.py
  36. 3 13
      misago/threads/participants.py
  37. 1 15
      misago/threads/permissions/attachments.py
  38. 0 18
      misago/threads/permissions/polls.py
  39. 0 13
      misago/threads/permissions/privatethreads.py
  40. 0 26
      misago/threads/permissions/threads.py
  41. 1 3
      misago/threads/tests/test_threads_moderation.py
  42. 10 15
      misago/threads/tests/test_threadslists.py
  43. 5 3
      misago/threads/tests/test_utils.py
  44. 0 5
      misago/threads/viewmodels/threads.py
  45. 18 33
      misago/users/api/auth.py
  46. 0 5
      misago/users/api/userendpoints/avatar.py
  47. 10 29
      misago/users/avatars/dynamic.py
  48. 0 3
      misago/users/bans.py
  49. 2 8
      misago/users/djangoadmin.py
  50. 7 21
      misago/users/forms/admin.py
  51. 14 20
      misago/users/models/user.py
  52. 0 10
      misago/users/permissions/account.py
  53. 0 18
      misago/users/permissions/delete.py
  54. 0 18
      misago/users/permissions/moderation.py
  55. 2 18
      misago/users/permissions/profiles.py
  56. 2 6
      misago/users/tests/test_user_avatar_api.py
  57. 1 3
      misago/users/tests/test_user_changeemail_api.py
  58. 1 3
      misago/users/tests/test_user_changepassword_api.py
  59. 1 3
      misago/users/tests/test_user_create_api.py
  60. 1 3
      misago/users/tests/test_user_signature_api.py
  61. 2 6
      misago/users/tests/test_user_username_api.py
  62. 9 27
      misago/users/tests/test_users_api.py
  63. 0 6
      misago/users/tokens.py
  64. 3 9
      misago/users/validators.py

+ 4 - 12
misago/acl/api.py

@@ -19,9 +19,7 @@ from .providers import providers
 
 
 def get_user_acl(user):
-    """
-    Get ACL for User
-    """
+    """get ACL for User"""
     acl_key = 'acl_%s' % user.acl_key
 
     acl_cache = threadstore.get(acl_key)
@@ -41,9 +39,7 @@ def get_user_acl(user):
 
 
 def add_acl(user, target):
-    """
-    Add valid ACL to target (iterable of objects or single object)
-    """
+    """add valid ACL to target (iterable of objects or single object)"""
     if hasattr(target, '__iter__'):
         for item in target:
             _add_acl_to_target(user, item)
@@ -52,9 +48,7 @@ def add_acl(user, target):
 
 
 def _add_acl_to_target(user, target):
-    """
-    Add valid ACL to single target, helper for add_acl function
-    """
+    """add valid ACL to single target, helper for add_acl function"""
     target.acl = {}
 
     for annotator in providers.get_type_annotators(target):
@@ -62,9 +56,7 @@ def _add_acl_to_target(user, target):
 
 
 def serialize_acl(target):
-    """
-    Serialize authenticated user's ACL
-    """
+    """serialize authenticated user's ACL"""
     serialized_acl = copy.deepcopy(target.acl_cache)
 
     for serializer in providers.get_type_serializers(target):

+ 1 - 3
misago/acl/builder.py

@@ -2,9 +2,7 @@ from .providers import providers
 
 
 def build_acl(roles):
-    """
-    Build ACL for given roles
-    """
+    """build ACL for given roles"""
     acl = {}
 
     for extension, module in providers.list():

+ 1 - 3
misago/acl/forms.py

@@ -14,9 +14,7 @@ class RoleForm(forms.ModelForm):
 
 
 def get_permissions_forms(role, data=None):
-    """
-    Utility function for building forms in admin
-    """
+    """utility function for building forms in admin"""
     role_permissions = role.permissions
 
     perms_forms = []

+ 1 - 3
misago/acl/panels.py

@@ -4,9 +4,7 @@ from django.utils.translation import ugettext_lazy as _
 
 
 class MisagoACLPanel(Panel):
-    """
-    Panel that displays current user's ACL
-    """
+    """panel that displays current user's ACL"""
     title = _("Misago User ACL")
     template = 'misago/acl_debug.html'
 

+ 3 - 7
misago/acl/providers.py

@@ -3,8 +3,8 @@ from importlib import import_module
 from misago.conf import settings
 
 
-# Manager for permission providers
 class PermissionProviders(object):
+    """manager for permission providers"""
     def __init__(self):
         self._initialized = False
         self._providers = []
@@ -33,15 +33,11 @@ class PermissionProviders(object):
             types_dict[hashType] = tuple(types_dict[hashType])
 
     def acl_annotator(self, hashable_type, func):
-        """
-        registers ACL annotator for specified types
-        """
+        """registers ACL annotator for specified types"""
         self._annotators.setdefault(hashable_type, []).append(func)
 
     def acl_serializer(self, hashable_type, func):
-        """
-        registers ACL serializer for specified types
-        """
+        """registers ACL serializer for specified types"""
         self._serializers.setdefault(hashable_type, []).append(func)
 
     def get_type_annotators(self, obj):

+ 1 - 3
misago/admin/views/generic/base.py

@@ -12,9 +12,7 @@ class AdminView(View):
         return '%s:%s' % (request.resolver_match.namespace, matched_url)
 
     def process_context(self, request, context):
-        """
-        Simple hook for extending and manipulating template context.
-        """
+        """simple hook for extending and manipulating template context."""
         return context
 
     def render(self, request, context=None, template=None):

+ 4 - 19
misago/admin/views/generic/list.py

@@ -65,10 +65,6 @@ class ListView(AdminView):
     def get_queryset(self):
         return self.get_model().objects.all()
 
-    """
-    Dispatch response
-    """
-
     def dispatch(self, request, *args, **kwargs):
         mass_actions_list = self.mass_actions or []
         extra_actions_list = self.extra_actions or []
@@ -174,9 +170,7 @@ class ListView(AdminView):
         context['page'] = context['paginator'].page(page)
         context['items'] = context['page'].object_list
 
-    """
-    Filter list items
-    """
+    # Filter list items
     search_form = None
 
     def get_search_form(self, request):
@@ -231,10 +225,7 @@ class ListView(AdminView):
                 active_filters, context['items']
             )
 
-    """
-    Order list items
-    """
-
+    # Order list items
     @property
     def ordering_session_key(self):
         return 'misago_admin_%s_order_by' % self.root_link
@@ -290,10 +281,7 @@ class ListView(AdminView):
                     order_as_dict['order_by'] = order_as_dict['order_by'][1:]
                 context['order_by'].append(order_as_dict)
 
-    """
-    Mass actions
-    """
-
+    # Mass actions
     def handle_mass_action(self, request, context):
         limit = self.items_per_page or 64
         action = self.select_mass_action(request.POST.get('action'))
@@ -323,10 +311,7 @@ class ListView(AdminView):
         else:
             raise MassActionError(_("Action is not allowed."))
 
-    """
-    Querystring builder
-    """
-
+    # Querystring builder
     def make_querystring(self, context):
         values = {}
         filter_values = {}

+ 1 - 3
misago/admin/views/generic/mixin.py

@@ -15,7 +15,5 @@ class AdminBaseMixin(object):
     message_404 = None
 
     def get_model(self):
-        """
-        Basic method for retrieving Model, used in cases such as User model.
-        """
+        """basic method for retrieving Model, used in cases such as User model."""
         return self.model

+ 0 - 10
misago/categories/forms.py

@@ -13,11 +13,6 @@ from . import THREADS_ROOT_NAME
 from .models import Category, CategoryRole
 
 
-"""
-Fields
-"""
-
-
 class AdminCategoryFieldMixin(object):
     def __init__(self, *args, **kwargs):
         self.base_level = kwargs.pop('base_level', 1)
@@ -48,11 +43,6 @@ class AdminCategoryMultipleChoiceField(AdminCategoryFieldMixin, TreeNodeMultiple
     pass
 
 
-"""
-Forms
-"""
-
-
 class CategoryFormBase(forms.ModelForm):
     name = forms.CharField(label=_("Name"), validators=[validate_sluggable()])
     description = forms.CharField(

+ 0 - 3
misago/categories/migrations/0003_categories_roles.py

@@ -6,9 +6,6 @@ from django.utils.translation import ugettext as _
 
 
 def create_default_categories_roles(apps, schema_editor):
-    """
-    Crete roles
-    """
     CategoryRole = apps.get_model('misago_categories', 'CategoryRole')
 
     CategoryRole.objects.create(

+ 0 - 20
misago/categories/permissions.py

@@ -12,11 +12,6 @@ from misago.users.models import AnonymousUser
 from .models import Category, CategoryRole, RoleCategoryACL
 
 
-"""
-Admin Permissions Form
-"""
-
-
 class PermissionsForm(forms.Form):
     legend = _("Category access")
 
@@ -31,11 +26,6 @@ def change_permissions_form(role):
         return None
 
 
-"""
-ACL Builder
-"""
-
-
 def build_acl(acl, roles, key_name):
     new_acl = {
         'visible_categories': [],
@@ -95,11 +85,6 @@ def build_category_acl(acl, category, categories_roles, key_name):
             acl['browseable_categories'].append(category.pk)
 
 
-"""
-ACL's for targets
-"""
-
-
 def add_acl_to_category(user, target):
     target.acl['can_see'] = can_see_category(user, target)
     target.acl['can_browse'] = can_browse_category(user, target)
@@ -127,11 +112,6 @@ def register_with(registry):
     registry.acl_serializer(AnonymousUser, serialize_categories_alcs)
 
 
-"""
-ACL tests
-"""
-
-
 def allow_see_category(user, target):
     try:
         category_id = target.pk

+ 6 - 3
misago/categories/tests/test_categories_admin_views.py

@@ -328,9 +328,6 @@ class CategoryAdminViewsTests(CategoryAdminTestCate):
 
 class CategoryAdminDeleteViewTests(CategoryAdminTestCate):
     def setUp(self):
-        super(CategoryAdminDeleteViewTests, self).setUp()
-        self.root = Category.objects.root_category()
-        self.first_category = Category.objects.get(slug='first-category')
         """
         Create categories tree for test cases:
 
@@ -344,6 +341,12 @@ class CategoryAdminDeleteViewTests(CategoryAdminTestCate):
         Category E
           + Category F
         """
+
+        super(CategoryAdminDeleteViewTests, self).setUp()
+
+        self.root = Category.objects.root_category()
+        self.first_category = Category.objects.get(slug='first-category')
+
         self.client.post(
             reverse('misago:admin:categories:nodes:new'),
             data={

+ 7 - 5
misago/categories/tests/test_utils.py

@@ -7,11 +7,6 @@ from misago.users.testutils import AuthenticatedUserTestCase
 
 class CategoriesUtilsTests(AuthenticatedUserTestCase):
     def setUp(self):
-        super(CategoriesUtilsTests, self).setUp()
-        threadstore.clear()
-
-        self.root = Category.objects.root_category()
-        self.first_category = Category.objects.get(slug='first-category')
         """
         Create categories tree for test cases:
 
@@ -25,6 +20,13 @@ class CategoriesUtilsTests(AuthenticatedUserTestCase):
         Category E
           + Subcategory F
         """
+
+        super(CategoriesUtilsTests, self).setUp()
+        threadstore.clear()
+
+        self.root = Category.objects.root_category()
+        self.first_category = Category.objects.get(slug='first-category')
+
         Category(
             name='Category A',
             slug='category-a',

+ 2 - 6
misago/categories/views/permsadmin.py

@@ -88,9 +88,7 @@ class DeleteCategoryRole(CategoryRoleAdmin, generic.ButtonView):
 
 
 class CategoryPermissions(CategoryAdmin, generic.ModelFormView):
-    """
-    Category roles view for assinging roles to category, add link to it in categories list
-    """
+    """category roles view for assinging roles to category, add link to it in categories list"""
     templates_dir = 'misago/admin/categoryroles'
     template = 'categoryroles.html'
 
@@ -154,9 +152,7 @@ CategoriesList.add_item_action(
 
 
 class RoleCategoriesACL(RoleAdmin, generic.ModelFormView):
-    """
-    Role categories view for assinging categories to role, add link to it in user roles list
-    """
+    """role categories view for assinging categories to role, add link to it in user roles list"""
     templates_dir = 'misago/admin/categoryroles'
     template = 'rolecategories.html'
 

+ 1 - 3
misago/conf/forms.py

@@ -136,9 +136,7 @@ def setting_field(FormType, setting):
 
 
 def ChangeSettingsForm(data=None, group=None):
-    """
-    Factory method that builds valid form for settings group
-    """
+    """factory method that builds valid form for settings group"""
 
     class FormType(forms.Form):
         pass

+ 2 - 6
misago/conf/tests/test_admin_views.py

@@ -26,9 +26,7 @@ class AdminSettingsViewsTests(AdminTestCase):
             self.assertContains(response, group_link)
 
     def test_invalid_group_handling(self):
-        """
-        invalid group results in redirect to settings list
-        """
+        """invalid group results in redirect to settings list"""
         group_link = reverse(
             'misago:admin:system:settings:group', kwargs={
                 'key': 'invalid-group',
@@ -39,9 +37,7 @@ class AdminSettingsViewsTests(AdminTestCase):
         self.assertTrue(reverse('misago:admin:system:settings:index') in response['location'])
 
     def test_groups_views(self):
-        """
-        each settings group view returns 200 and contains all settings in group
-        """
+        """each settings group view returns 200 and contains all settings in group"""
         for group in SettingsGroup.objects.all():
             group_link = reverse(
                 'misago:admin:system:settings:group', kwargs={

+ 3 - 3
misago/core/exceptions.py

@@ -2,7 +2,7 @@ from django.core.exceptions import PermissionDenied
 
 
 class AjaxError(Exception):
-    """You've tried to do something over AJAX but misago blurped"""
+    """you've tried to do something over AJAX but misago blurped"""
 
     def __init__(self, message=None, code=406):
         self.message = message
@@ -16,10 +16,10 @@ class Banned(PermissionDenied):
 
 
 class ExplicitFirstPage(Exception):
-    """The url that was used to reach view contained explicit first page"""
+    """the url that was used to reach view contained explicit first page"""
     pass
 
 
 class OutdatedSlug(Exception):
-    """The url that was used to reach view contained outdated slug"""
+    """the url that was used to reach view contained outdated slug"""
     pass

+ 2 - 6
misago/core/pgutils.py

@@ -46,9 +46,7 @@ DROP INDEX %(index_name)s
 
 
 def batch_update(queryset, step=50):
-    """
-    Util because psycopg2 iterators aren't really memory effective
-    """
+    """util because psycopg2 iterators aren't memory effective in Dj<1.11"""
     paginator = Paginator(queryset.order_by('pk'), step)
     for page_number in paginator.page_range:
         for obj in paginator.page(page_number).object_list:
@@ -56,9 +54,7 @@ def batch_update(queryset, step=50):
 
 
 def batch_delete(queryset, step=50):
-    """
-    Another util cos paginator goes bobbins when you are deleting
-    """
+    """another util cos paginator goes bobbins when you are deleting"""
     queryset_exists = True
     while queryset_exists:
         for obj in queryset[:step]:

+ 8 - 12
misago/core/templatetags/misago_forms.py

@@ -5,17 +5,17 @@ from django.template.loader import render_to_string
 
 
 register = template.Library()
-"""
-Form row: renders single row in form
-
-Syntax:
-{% form_row form.field %} # renders vertical field
-{% form_row form.field "col-md-3" "col-md-9" %} # renders horizontal field
-"""
 
 
 @register.tag
 def form_row(parser, token):
+    """
+    Form row: renders single row in form
+
+    Syntax:
+    {% form_row form.field %} # renders vertical field
+    {% form_row form.field "col-md-3" "col-md-9" %} # renders horizontal field
+    """
     args = token.split_contents()
 
     if len(args) < 2:
@@ -72,11 +72,7 @@ class FormRowNode(template.Node):
         )
 
 
-"""
-Form input: renders given field input
-"""
-
-
 @register.tag
 def form_input(parser, token):
+    """form input: renders given field input"""
     return crispy_forms_field.crispy_field(parser, token)

+ 2 - 7
misago/core/tests/test_exceptionhandlers.py

@@ -19,17 +19,12 @@ INVALID_EXCEPTIONS = [
 
 class IsMisagoExceptionTests(TestCase):
     def test_is_misago_exception_true_for_handled_exceptions(self):
-        """
-        exceptionhandler.is_misago_exception recognizes handled exceptions
-        """
+        """exceptionhandler.is_misago_exception recognizes handled exceptions"""
         for exception in exceptionhandler.HANDLED_EXCEPTIONS:
             self.assertTrue(exceptionhandler.is_misago_exception(exception()))
 
     def test_is_misago_exception_false_for_not_handled_exceptions(self):
-        """
-        exceptionhandler.is_misago_exception fails to recognize other
-        exceptions
-        """
+        """exceptionhandler.is_misago_exception fails to recognize other exceptions"""
         for exception in INVALID_EXCEPTIONS:
             self.assertFalse(exceptionhandler.is_misago_exception(exception()))
 

+ 2 - 7
misago/core/tests/test_migrationutils.py

@@ -7,18 +7,13 @@ from misago.core.models import CacheVersion
 
 class CacheBusterUtilsTests(TestCase):
     def test_cachebuster_register_cache(self):
-        """
-        cachebuster_register_cache registers cache on migration successfully
-        """
+        """cachebuster_register_cache registers cache on migration successfully"""
         cache_name = 'eric_licenses'
         migrationutils.cachebuster_register_cache(apps, cache_name)
         CacheVersion.objects.get(cache=cache_name)
 
     def test_cachebuster_unregister_cache(self):
-        """
-        cachebuster_unregister_cache removes cache on migration successfully
-        """
-
+        """cachebuster_unregister_cache removes cache on migration successfully"""
         cache_name = 'eric_licenses'
         migrationutils.cachebuster_register_cache(apps, cache_name)
         CacheVersion.objects.get(cache=cache_name)

+ 1 - 3
misago/core/testutils.py

@@ -5,9 +5,7 @@ from .cache import cache
 
 
 class MisagoTestCase(TestCase):
-    """
-    TestCase class that empties global state before and after each test
-    """
+    """TestCase class that empties global state before and after each test"""
 
     def clear_state(self):
         cache.clear()

+ 13 - 26
misago/core/utils.py

@@ -34,13 +34,11 @@ def encode_json_html(string):
     return string.replace('<', r'\u003C')
 
 
-"""
-Turn ISO 8601 string into datetime object
-"""
 ISO8601_FORMATS = ("%Y-%m-%dT%H:%M:%S", "%Y-%m-%dT%H:%M:%S.%f", )
 
 
 def parse_iso8601_string(value):
+    """turns ISO 8601 string into datetime object"""
     value = force_text(value, strings_only=True).rstrip('Z')
 
     for format in ISO8601_FORMATS:
@@ -69,23 +67,17 @@ def parse_iso8601_string(value):
     return timezone.make_aware(parsed_value, tz_correction)
 
 
-"""
-Mark request as having sensitive parameters
-We can't use decorator because of DRF uses custom HttpRequest
-that is incompatibile with Django's decorator
-"""
-
-
 def hide_post_parameters(request):
+    """
+    Mark request as having sensitive parameters
+    We can't use decorator because of DRF uses custom HttpRequest
+    that is incompatibile with Django's decorator
+    """
     request.sensitive_post_parameters = '__ALL__'
 
 
-"""
-Return path utility
-"""
-
-
 def clean_return_path(request):
+    """return path utility that returns return path from referer or POST"""
     if request.method == 'POST' and 'return_path' in request.POST:
         return _get_return_path_from_post(request)
     else:
@@ -124,9 +116,12 @@ def _get_return_path_from_referer(request):
         return None
 
 
-"""
-Utils for resolving requests destination
-"""
+def is_request_to_misago(request):
+    try:
+        return request._request_to_misago
+    except AttributeError:
+        request._request_to_misago = _is_request_path_under_misago(request)
+        return request._request_to_misago
 
 
 def _is_request_path_under_misago(request):
@@ -139,14 +134,6 @@ def _is_request_path_under_misago(request):
     return path_info[:len(forum_index)] == forum_index
 
 
-def is_request_to_misago(request):
-    try:
-        return request._request_to_misago
-    except AttributeError:
-        request._request_to_misago = _is_request_path_under_misago(request)
-        return request._request_to_misago
-
-
 def is_referer_local(request):
     referer = request.META.get('HTTP_REFERER')
 

+ 1 - 3
misago/datamover/db.py

@@ -2,9 +2,7 @@ from django.db import connections
 
 
 def fetch_assoc(query, *args):
-    """
-    Return all rows from a cursor as a dict
-    """
+    """return all rows from a cursor as a dict"""
     with connections['misago05'].cursor() as cursor:
         cursor.execute(query, *args)
 

+ 0 - 5
misago/datamover/markup/__init__.py

@@ -29,11 +29,6 @@ def clean_original(post):
     return post
 
 
-"""
-Fake request and user for parser
-"""
-
-
 class FakeUser(object):
     slug = get_random_string(40)
 

+ 0 - 3
misago/markup/bbcode/inline.py

@@ -24,9 +24,6 @@ class SimpleBBCodePattern(SimpleTagPattern):
         self.tag = tag or bbcode.lower()
 
 
-"""
-Register basic BBCodes
-"""
 bold = SimpleBBCodePattern('b')
 italics = SimpleBBCodePattern('i')
 underline = SimpleBBCodePattern('u')

+ 1 - 3
misago/markup/parser.py

@@ -82,9 +82,7 @@ def parse(
 
 
 def md_factory(allow_links=True, allow_images=True, allow_blocks=True):
-    """
-    Create and configure markdown object
-    """
+    """creates and configures markdown object"""
     md = markdown.Markdown(safe_mode='escape', extensions=['nl2br'])
 
     # Remove references

+ 1 - 3
misago/markup/pipeline.py

@@ -8,9 +8,7 @@ from misago.conf import settings
 
 
 class MarkupPipeline(object):
-    """
-    Small framework for extending parser
-    """
+    """small framework for extending parser"""
 
     def extend_markdown(self, md):
         for extension in settings.MISAGO_MARKUP_EXTENSIONS:

+ 0 - 10
misago/search/permissions.py

@@ -6,11 +6,6 @@ from misago.acl.models import Role
 from misago.core.forms import YesNoSwitch
 
 
-"""
-Admin Permissions Form
-"""
-
-
 class PermissionsForm(forms.Form):
     legend = _("Search")
 
@@ -24,11 +19,6 @@ def change_permissions_form(role):
         return None
 
 
-"""
-ACL Builder
-"""
-
-
 def build_acl(acl, roles, key_name):
     new_acl = {'can_search': 0}
     new_acl.update(acl)

+ 1 - 3
misago/search/tests/test_searchproviders.py

@@ -49,9 +49,7 @@ class SearchProvidersTests(TestCase):
         self.assertEqual(searchproviders.get_providers('REQUEST')[0].request, 'REQUEST')
 
     def test_get_allowed_providers(self):
-        """
-        get_allowed_providers returns only providers that didn't raise in allow_search
-        """
+        """get_allowed_providers returns only providers that didn't raise in allow_search"""
         searchproviders = SearchProviders([])
 
         searchproviders._initialized = True

+ 1 - 3
misago/threads/api/postingendpoint/__init__.py

@@ -128,9 +128,7 @@ class PostingEndpoint(object):
 
 
 class PostingMiddleware(object):
-    """
-    Abstract middleware class
-    """
+    """abstract middleware class"""
 
     def __init__(self, **kwargs):
         self.kwargs = kwargs

+ 1 - 3
misago/threads/api/postingendpoint/category.py

@@ -15,9 +15,7 @@ from . import PostingEndpoint, PostingMiddleware
 
 
 class CategoryMiddleware(PostingMiddleware):
-    """
-    Middleware that validates category id and sets category on thread and post instances
-    """
+    """middleware that validates category id and sets category on thread and post instances"""
 
     def use_this_middleware(self):
         if self.mode == PostingEndpoint.START:

+ 1 - 3
misago/threads/api/postingendpoint/privatethread.py

@@ -6,9 +6,7 @@ from . import PostingEndpoint, PostingMiddleware
 
 
 class PrivateThreadMiddleware(PostingMiddleware):
-    """
-    Middleware that sets private threads category for thread and post
-    """
+    """middleware that sets private threads category for thread and post"""
 
     def use_this_middleware(self):
         if self.mode == PostingEndpoint.START:

+ 1 - 3
misago/threads/api/postingendpoint/syncprivatethreads.py

@@ -5,9 +5,7 @@ from . import PostingEndpoint, PostingMiddleware
 
 
 class SyncPrivateThreadsMiddleware(PostingMiddleware):
-    """
-    Middleware that sets private thread participants to sync unread threads
-    """
+    """middleware that sets private thread participants to sync unread threads"""
 
     def use_this_middleware(self):
         if self.mode == PostingEndpoint.REPLY:

+ 2 - 7
misago/threads/paginator.py

@@ -2,10 +2,7 @@ from django.core.paginator import Paginator
 
 
 class PostsPaginator(Paginator):
-    """
-    Paginator that returns that makes last item on page
-    repeat as first item on next page.
-    """
+    """paginator that returns that makes last item on page repeat as first item on next page."""
 
     def __init__(self, object_list, per_page, orphans=0, allow_empty_first_page=True):
         per_page = int(per_page) - 1
@@ -15,9 +12,7 @@ class PostsPaginator(Paginator):
               self).__init__(object_list, per_page, orphans, allow_empty_first_page)
 
     def page(self, number):
-        """
-        Returns a Page object for the given 1-based page number.
-        """
+        """returns a Page object for the given 1-based page number."""
         number = self.validate_number(number)
         bottom = (number - 1) * self.per_page
         top = bottom + self.per_page

+ 3 - 13
misago/threads/participants.py

@@ -67,16 +67,10 @@ def set_users_unread_private_threads_sync(users=None, participants=None, exclude
 
 
 def set_owner(thread, user):
-    """
-    Set user as thread's owner
-    """
     ThreadParticipant.objects.set_owner(thread, user)
 
 
 def change_owner(request, thread, user):
-    """
-    Replace thread's owner with other
-    """
     ThreadParticipant.objects.set_owner(thread, user)
     set_users_unread_private_threads_sync(
         participants=thread.participants_list,
@@ -100,9 +94,7 @@ def change_owner(request, thread, user):
 
 
 def add_participant(request, thread, user):
-    """
-    Adds single participant to thread, registers this on the event
-    """
+    """adds single participant to thread, registers this on the event"""
     add_participants(request, thread, [user])
 
     if request.user == user:
@@ -124,7 +116,7 @@ def add_participant(request, thread, user):
 def add_participants(request, thread, users):
     """
     Add multiple participants to thread, set "recound private threads" flag on them
-    notify them about being added to thread
+    notify them about being added to thread.
     """
     ThreadParticipant.objects.add_participants(thread, users)
 
@@ -162,9 +154,7 @@ def build_noticiation_email(request, thread, user):
 
 
 def remove_participant(request, thread, user):
-    """
-    Remove thread participant, set "recound private threads" flag on user
-    """
+    """remove thread participant, set "recound private threads" flag on user"""
     removed_owner = False
     remaining_participants = []
 

+ 1 - 15
misago/threads/permissions/attachments.py

@@ -7,11 +7,7 @@ from misago.core.forms import YesNoSwitch
 from misago.threads.models import Attachment
 
 
-"""
-Admin Permissions Form
-"""
-
-
+# Admin Permissions Forms
 class PermissionsForm(forms.Form):
     legend = _("Attachments")
 
@@ -44,11 +40,6 @@ def change_permissions_form(role):
         return None
 
 
-"""
-ACL Builder
-"""
-
-
 def build_acl(acl, roles, key_name):
     new_acl = {
         'max_attachment_size': 0,
@@ -67,11 +58,6 @@ def build_acl(acl, roles, key_name):
     )
 
 
-"""
-ACL's for targets
-"""
-
-
 def add_acl_to_attachment(user, attachment):
     if user.is_authenticated and user.id == attachment.uploader_id:
         attachment.acl.update({

+ 0 - 18
misago/threads/permissions/polls.py

@@ -23,9 +23,6 @@ __all__ = [
     'allow_see_poll_votes',
     'can_see_poll_votes',
 ]
-"""
-Admin Permissions Forms
-"""
 
 
 class RolePermissionsForm(forms.Form):
@@ -80,11 +77,6 @@ def change_permissions_form(role):
         return None
 
 
-"""
-ACL Builder
-"""
-
-
 def build_acl(acl, roles, key_name):
     acl.update({
         'can_start_polls': 0,
@@ -106,11 +98,6 @@ def build_acl(acl, roles, key_name):
     )
 
 
-"""
-ACL's for targets
-"""
-
-
 def add_acl_to_poll(user, poll):
     poll.acl.update({
         'can_vote': can_vote_poll(user, poll),
@@ -131,11 +118,6 @@ def register_with(registry):
     registry.acl_annotator(Thread, add_acl_to_thread)
 
 
-"""
-ACL tests
-"""
-
-
 def allow_start_poll(user, target):
     if user.is_anonymous:
         raise PermissionDenied(_("You have to sign in to start polls."))

+ 0 - 13
misago/threads/permissions/privatethreads.py

@@ -28,9 +28,6 @@ __all__ = [
     'allow_message_user',
     'can_message_user',
 ]
-"""
-Admin Permissions Form
-"""
 
 
 class PermissionsForm(forms.Form):
@@ -71,11 +68,6 @@ def change_permissions_form(role):
         return None
 
 
-"""
-ACL Builder
-"""
-
-
 def build_acl(acl, roles, key_name):
     new_acl = {
         'can_use_private_threads': 0,
@@ -178,11 +170,6 @@ def register_with(registry):
     registry.acl_annotator(Thread, add_acl_to_thread)
 
 
-"""
-ACL tests
-"""
-
-
 def allow_use_private_threads(user):
     if user.is_anonymous:
         raise PermissionDenied(_("You have to sign in to use private threads."))

+ 0 - 26
misago/threads/permissions/threads.py

@@ -45,9 +45,6 @@ __all__ = [
     'exclude_invisible_threads',
     'exclude_invisible_posts',
 ]
-"""
-Admin Permissions Forms
-"""
 
 
 class RolePermissionsForm(forms.Form):
@@ -241,11 +238,6 @@ def change_permissions_form(role):
         return None
 
 
-"""
-ACL Builder
-"""
-
-
 def build_acl(acl, roles, key_name):
     acl.update({
         'can_see_unapproved_content_lists': False,
@@ -346,11 +338,6 @@ def build_category_acl(acl, category, categories_roles, key_name):
     return final_acl
 
 
-"""
-ACL's for targets
-"""
-
-
 def add_acl_to_category(user, category):
     category_acl = user.acl_cache['categories'].get(category.pk, {})
 
@@ -508,11 +495,6 @@ def register_with(registry):
     registry.acl_annotator(Post, add_acl_to_post)
 
 
-"""
-ACL tests
-"""
-
-
 def allow_see_thread(user, target):
     category_acl = user.acl_cache['categories'].get(
         target.category_id, {
@@ -872,9 +854,6 @@ def allow_delete_event(user, target):
 
 
 can_delete_event = return_boolean(allow_delete_event)
-"""
-Permission check helpers
-"""
 
 
 def can_change_owned_thread(user, target):
@@ -909,11 +888,6 @@ def has_time_to_edit_post(user, target):
         return True
 
 
-"""
-Queryset helpers
-"""
-
-
 def exclude_invisible_threads(user, categories, queryset):
     show_all = []
     show_accepted_visible = []

+ 1 - 3
misago/threads/tests/test_threads_moderation.py

@@ -75,9 +75,7 @@ class ThreadsModerationTests(AuthenticatedUserTestCase):
         self.assertEqual(event.event_type, 'pinned_locally')
 
     def test_pin_invalid_thread(self):
-        """
-        pin_thread_locally returns false for already locally pinned thread
-        """
+        """pin_thread_locally returns false for already locally pinned thread"""
         self.thread.weight = 1
 
         self.assertFalse(moderation.pin_thread_locally(self.request, self.thread))

+ 10 - 15
misago/threads/tests/test_threadslists.py

@@ -18,12 +18,6 @@ LISTS_URLS = ('', 'my/', 'new/', 'unread/', 'subscribed/', )
 
 class ThreadsListTestCase(AuthenticatedUserTestCase):
     def setUp(self):
-        super(ThreadsListTestCase, self).setUp()
-
-        self.api_link = reverse('misago:api:thread-list')
-
-        self.root = Category.objects.root_category()
-        self.first_category = Category.objects.get(slug='first-category')
         """
         Create categories tree for test cases:
 
@@ -37,6 +31,13 @@ class ThreadsListTestCase(AuthenticatedUserTestCase):
         Category E
           + Subcategory F
         """
+        super(ThreadsListTestCase, self).setUp()
+
+        self.api_link = reverse('misago:api:thread-list')
+
+        self.root = Category.objects.root_category()
+        self.first_category = Category.objects.get(slug='first-category')
+
         Category(
             name='Category A',
             slug='category-a',
@@ -725,9 +726,7 @@ class ThreadsVisibilityTests(ThreadsListTestCase):
         self.assertEqual(response_json['results'][0]['id'], test_thread.pk)
 
     def test_list_user_can_see_hidden_thread(self):
-        """
-        list shows hidden thread that belongs to other user due to permission
-        """
+        """list shows hidden thread that belongs to other user due to permission"""
         test_thread = testutils.post_thread(
             category=self.category_a,
             is_hidden=True,
@@ -749,9 +748,7 @@ class ThreadsVisibilityTests(ThreadsListTestCase):
         self.assertEqual(response_json['results'][0]['id'], test_thread.pk)
 
     def test_list_user_can_see_unapproved_thread(self):
-        """
-        list shows hidden thread that belongs to other user due to permission
-        """
+        """list shows hidden thread that belongs to other user due to permission"""
         test_thread = testutils.post_thread(
             category=self.category_a,
             is_unapproved=True,
@@ -1546,9 +1543,7 @@ class UnapprovedListTests(ThreadsListTestCase):
         self.assertNotContains(response, hidden_thread.get_absolute_url())
 
     def test_list_shows_owned_threads_for_unapproving_user(self):
-        """
-        list shows owned threads with unapproved posts for user without perm
-        """
+        """list shows owned threads with unapproved posts for user without perm"""
         visible_thread = testutils.post_thread(
             poster=self.user,
             category=self.category_b,

+ 5 - 3
misago/threads/tests/test_utils.py

@@ -6,9 +6,6 @@ from misago.threads.utils import add_categories_to_items, get_thread_id_from_url
 
 class AddCategoriesToItemsTests(MisagoTestCase):
     def setUp(self):
-        super(AddCategoriesToItemsTests, self).setUp()
-
-        self.root = Category.objects.root_category()
         """
         Create categories tree for test cases:
 
@@ -22,6 +19,11 @@ class AddCategoriesToItemsTests(MisagoTestCase):
         Category E
           + Subcategory F
         """
+
+        super(AddCategoriesToItemsTests, self).setUp()
+
+        self.root = Category.objects.root_category()
+
         Category(
             name='Category A',
             slug='category-a',

+ 0 - 5
misago/threads/viewmodels/threads.py

@@ -182,11 +182,6 @@ class PrivateThreads(ViewModel):
         make_participants_aware(request.user, threads)
 
 
-"""
-Thread queryset utils
-"""
-
-
 def get_threads_queryset(user, categories, list_type):
     queryset = exclude_invisible_threads(user, categories, Thread.objects)
 

+ 18 - 33
misago/users/api/auth.py

@@ -29,16 +29,14 @@ def gateway(request):
         return session_user(request)
 
 
-"""
-POST /auth/ with CSRF, username and password
-will attempt to authenticate new user
-"""
-
-
 @api_view(['POST'])
 @permission_classes((UnbannedAnonOnly, ))
 @csrf_protect
 def login(request):
+    """
+    POST /auth/ with CSRF, username and password
+    will attempt to authenticate new user
+    """
     form = AuthenticationForm(request, data=request.data)
     if form.is_valid():
         auth.login(request, form.user_cache)
@@ -52,13 +50,10 @@ def login(request):
         )
 
 
-"""
-GET /auth/ will return current auth user, either User or AnonymousUser
-"""
-
 
 @api_view()
 def session_user(request):
+    """GET /auth/ will return current auth user, either User or AnonymousUser"""
     if request.user.is_authenticated:
         UserSerializer = AuthenticatedUserSerializer
     else:
@@ -67,13 +62,9 @@ def session_user(request):
     return Response(UserSerializer(request.user).data)
 
 
-"""
-GET /auth/criteria/ will return password and username criteria for accounts
-"""
-
-
 @api_view(['GET'])
 def get_criteria(request):
+    """GET /auth/criteria/ will return password and username criteria for accounts"""
     criteria = {
         'username': {
             'min_length': settings.username_length_min,
@@ -92,16 +83,14 @@ def get_criteria(request):
     return Response(criteria)
 
 
-"""
-POST /auth/send-activation/ with CSRF token and email
-will mail account activation link to requester
-"""
-
-
 @api_view(['POST'])
 @permission_classes((UnbannedAnonOnly, ))
 @csrf_protect
 def send_activation(request):
+    """
+    POST /auth/send-activation/ with CSRF token and email
+    will mail account activation link to requester
+    """
     form = ResendActivationForm(request.data)
     if form.is_valid():
         requesting_user = form.user_cache
@@ -132,16 +121,14 @@ def send_activation(request):
         )
 
 
-"""
-POST /auth/send-password-form/ with CSRF token and email
-will mail change password form link to requester
-"""
-
-
 @api_view(['POST'])
 @permission_classes((UnbannedOnly, ))
 @csrf_protect
 def send_password_form(request):
+    """
+    POST /auth/send-password-form/ with CSRF token and email
+    will mail change password form link to requester
+    """
     form = ResetPasswordForm(request.data)
     if form.is_valid():
         requesting_user = form.user_cache
@@ -174,12 +161,6 @@ def send_password_form(request):
         )
 
 
-"""
-POST /auth/change-password/user/token/ with CSRF and new password
-will change forgotten password
-"""
-
-
 class PasswordChangeFailed(Exception):
     pass
 
@@ -188,6 +169,10 @@ class PasswordChangeFailed(Exception):
 @permission_classes((UnbannedOnly, ))
 @csrf_protect
 def change_forgotten_password(request, pk, token):
+    """
+    POST /auth/change-password/user/token/ with CSRF and new password
+    will change forgotten password
+    """
     invalid_message = _("Form link is invalid. Please try again.")
     expired_message = _("Your link has expired. Please request new one.")
 

+ 0 - 5
misago/users/api/userendpoints/avatar.py

@@ -136,11 +136,6 @@ def avatar_post(options, user, data):
     return Response(response_dict)
 
 
-"""
-Avatar rpc handlers
-"""
-
-
 def avatar_generate(user, data):
     avatars.dynamic.set_avatar(user)
     return _("New avatar based on your account was set.")

+ 10 - 29
misago/users/avatars/dynamic.py

@@ -8,6 +8,15 @@ from misago.conf import settings
 from . import store
 
 
+COLOR_WHEEL = (
+    '#d32f2f', '#c2185b', '#7b1fa2', '#512da8', '#303f9f', '#1976d2', '#0288D1', '#0288d1',
+    '#0097a7', '#00796b', '#388e3c', '#689f38', '#afb42b', '#fbc02d', '#ffa000', '#f57c00',
+    '#e64a19',
+)
+COLOR_WHEEL_LEN = len(COLOR_WHEEL)
+FONT_FILE = os.path.join(os.path.dirname(__file__), 'font.ttf')
+
+
 def set_avatar(user):
     name_bits = settings.MISAGO_DYNAMIC_AVATAR_DRAWER.split('.')
 
@@ -19,12 +28,8 @@ def set_avatar(user):
     store.store_new_avatar(user, image)
 
 
-"""
-Default drawer
-"""
-
-
 def draw_default(user):
+    """default avatar drawer that draws username's first letter on color"""
     image_size = max(settings.MISAGO_AVATARS_SIZES)
 
     image = Image.new("RGBA", (image_size, image_size), 0)
@@ -34,14 +39,6 @@ def draw_default(user):
     return image
 
 
-COLOR_WHEEL = (
-    '#d32f2f', '#c2185b', '#7b1fa2', '#512da8', '#303f9f', '#1976d2', '#0288D1', '#0288d1',
-    '#0097a7', '#00796b', '#388e3c', '#689f38', '#afb42b', '#fbc02d', '#ffa000', '#f57c00',
-    '#e64a19',
-)
-COLOR_WHEEL_LEN = len(COLOR_WHEEL)
-
-
 def draw_avatar_bg(user, image):
     image_size = image.size
 
@@ -56,9 +53,6 @@ def draw_avatar_bg(user, image):
     return image
 
 
-FONT_FILE = os.path.join(os.path.dirname(__file__), 'font.ttf')
-
-
 def draw_avatar_flavour(user, image):
     string = user.username[0]
 
@@ -74,16 +68,3 @@ def draw_avatar_flavour(user, image):
     writer.text(text_pos, string, font=font)
 
     return image
-
-
-"""
-Some utils for drawring avatar programmatically
-"""
-CHARS = 'qwertyuiopasdfghjklzxcvbnm1234567890'
-
-
-def string_to_int(string):
-    value = 0
-    for p, c in enumerate(string.lower()):
-        value += p * (CHARS.find(c))
-    return value

+ 0 - 3
misago/users/bans.py

@@ -127,9 +127,6 @@ def _get_session_bancache(request):
         if not cachebuster.is_valid(VERSION_KEY, ban_cache['version']):
             return None
         if ban_cache.get('expires_on'):
-            """
-            Hydrate ban date
-            """
             if ban_cache['expires_on'] < timezone.today():
                 return None
         return ban_cache

+ 2 - 8
misago/users/djangoadmin.py

@@ -30,27 +30,21 @@ class UserAdminForm(forms.ModelForm):
     it is kind of overkill - overwrite the whole template just to add one
     button - isn't it?
     """
-    #: pseudo-field
     edit_from_misago_link = forms.Field()
 
     def __init__(self, *args, **kwargs):
-        # noinspection PyArgumentList
         super(UserAdminForm, self).__init__(*args, **kwargs)
         self.init_edit_from_misago_link_field()
 
     def init_edit_from_misago_link_field(self):
-        """
-        Init for the pseudo-field, and replace it's widget `render`.
-        """
+        """init for the pseudo-field, and replace it's widget `render`"""
         field = self.fields['edit_from_misago_link']
         field.required = False
         field.label = ''
         field.widget.render = self.render_edit_from_misago_link
 
     def render_edit_from_misago_link(self, *args, **kwargs):
-        """
-        Composes an html hyperlink for the pseudo-field render.
-        """
+        """composes an html hyperlink for the pseudo-field render"""
         text = _('Edit this user in Misago admin panel')
         link_html_template = ('<a href="{}" target="blank">' + text + '</a>')
         link_url = reverse(

+ 7 - 21
misago/users/forms/admin.py

@@ -14,9 +14,6 @@ from misago.users.validators import validate_email, validate_username
 
 
 UserModel = get_user_model()
-"""
-Users
-"""
 
 
 class UserBaseForm(forms.ModelForm):
@@ -330,11 +327,11 @@ def SearchUsersForm(*args, **kwargs):
     extra_fields = {
         'rank':
             forms.TypedChoiceField(
-                label=_("Has rank"), coerce=int, required=False, choices=ranks_choices
+                label=_("Has rank"), coerce=int, required=False, choices=ranks_choices,
             ),
         'role':
             forms.TypedChoiceField(
-                label=_("Has role"), coerce=int, required=False, choices=roles_choices
+                label=_("Has role"), coerce=int, required=False, choices=roles_choices,
             )
     }
 
@@ -342,11 +339,6 @@ def SearchUsersForm(*args, **kwargs):
     return FinalForm(*args, **kwargs)
 
 
-"""
-Ranks
-"""
-
-
 class RankForm(forms.ModelForm):
     name = forms.CharField(
         label=_("Name"),
@@ -421,11 +413,6 @@ class RankForm(forms.ModelForm):
         return data
 
 
-"""
-Bans
-"""
-
-
 class BanUsersForm(forms.Form):
     ban_type = forms.MultipleChoiceField(
         label=_("Values to ban"),
@@ -445,7 +432,7 @@ class BanUsersForm(forms.Form):
         max_length=1000,
         help_text=_("Optional message displayed to users instead of default one."),
         widget=forms.Textarea(attrs={'rows': 3}),
-        error_messages={'max_length': _("Message can't be longer than 1000 characters.")}
+        error_messages={'max_length': _("Message can't be longer than 1000 characters."),}
     )
     staff_message = forms.CharField(
         label=_("Team message"),
@@ -453,7 +440,7 @@ class BanUsersForm(forms.Form):
         max_length=1000,
         help_text=_("Optional ban message for moderators and administrators."),
         widget=forms.Textarea(attrs={'rows': 3}),
-        error_messages={'max_length': _("Message can't be longer than 1000 characters.")}
+        error_messages={'max_length': _("Message can't be longer than 1000 characters."),}
     )
     expires_on = IsoDateTimeField(
         label=_("Expires on"),
@@ -472,8 +459,7 @@ class BanForm(forms.ModelForm):
             'for rought matches. For example, making IP ban for value '
             '"83.*" will ban all IP addresses beginning with "83.".'
         ),
-        error_messages={'max_length': _("Banned value can't be longer "
-                                        "than 250 characters.")}
+        error_messages={'max_length': _("Banned value can't be longer than 250 characters."),}
     )
     user_message = forms.CharField(
         label=_("User message"),
@@ -481,7 +467,7 @@ class BanForm(forms.ModelForm):
         max_length=1000,
         help_text=_("Optional message displayed to user instead of default one."),
         widget=forms.Textarea(attrs={'rows': 3}),
-        error_messages={'max_length': _("Message can't be longer than 1000 characters.")}
+        error_messages={'max_length': _("Message can't be longer than 1000 characters."),}
     )
     staff_message = forms.CharField(
         label=_("Team message"),
@@ -489,7 +475,7 @@ class BanForm(forms.ModelForm):
         max_length=1000,
         help_text=_("Optional ban message for moderators and administrators."),
         widget=forms.Textarea(attrs={'rows': 3}),
-        error_messages={'max_length': _("Message can't be longer than 1000 characters.")}
+        error_messages={'max_length': _("Message can't be longer than 1000 characters."),}
     )
     expires_on = IsoDateTimeField(
         label=_("Expires on"),

+ 14 - 20
misago/users/models/user.py

@@ -154,23 +154,21 @@ class User(AbstractBaseUser, PermissionsMixin):
         (LIMIT_INVITES_TO_FOLLOWED, _("Users I follow")),
         (LIMIT_INVITES_TO_NOBODY, _("Nobody")),
     ]
-    """
-    Note that "username" field is purely for shows.
-    When searching users by their names, always use lowercased string
-    and slug field instead that is normalized around DB engines
-    differences in case handling.
-    """
+    # Note that "username" field is purely for shows.
+    # When searching users by their names, always use lowercased string
+    # and slug field instead that is normalized around DB engines
+    # differences in case handling.
     username = models.CharField(max_length=30)
     slug = models.CharField(max_length=30, unique=True)
-    """
-    Misago stores user email in two fields:
-    "email" holds normalized email address
-    "email_hash" is lowercase hash of email address used to identify account
-    as well as enforcing on database level that no more than one user can be
-    using one email address
-    """
+
+    # Misago stores user email in two fields:
+    # "email" holds normalized email address
+    # "email_hash" is lowercase hash of email address used to identify account
+    # as well as enforcing on database level that no more than one user can be
+    # using one email address
     email = models.EmailField(max_length=255, db_index=True)
     email_hash = models.CharField(max_length=32, unique=True)
+
     joined_on = models.DateTimeField(_('joined on'), default=timezone.now)
     joined_from_ip = models.GenericIPAddressField()
     last_ip = models.GenericIPAddressField(null=True, blank=True)
@@ -270,7 +268,7 @@ class User(AbstractBaseUser, PermissionsMixin):
         self.email = self.__class__.objects.normalize_email(self.email)
 
     def lock(self):
-        """Locks user in DB"""
+        """locks user in DB, shortcut for locking user model in views"""
         return User.objects.select_for_update().get(pk=self.pk)
 
     def delete(self, *args, **kwargs):
@@ -337,9 +335,7 @@ class User(AbstractBaseUser, PermissionsMixin):
         )
 
     def get_username(self):
-        """
-        Dirty hack: return real username instead of normalized slug
-        """
+        """dirty hack: return real username instead of normalized slug"""
         return self.username
 
     def get_full_name(self):
@@ -406,9 +402,7 @@ class User(AbstractBaseUser, PermissionsMixin):
         self.acl_key = md5(','.join(roles_pks).encode()).hexdigest()[:12]
 
     def email_user(self, subject, message, from_email=None, **kwargs):
-        """
-        Sends an email to this User.
-        """
+        """sends an email to this user (for compat with Django)"""
         send_mail(subject, message, from_email, [self.email], **kwargs)
 
     def is_following(self, user):

+ 0 - 10
misago/users/permissions/account.py

@@ -6,11 +6,6 @@ from misago.acl.models import Role
 from misago.core.forms import YesNoSwitch
 
 
-"""
-Admin Permissions Form
-"""
-
-
 class PermissionsForm(forms.Form):
     legend = _("Account settings")
 
@@ -46,11 +41,6 @@ def change_permissions_form(role):
         return None
 
 
-"""
-ACL Builder
-"""
-
-
 def build_acl(acl, roles, key_name):
     new_acl = {
         'name_changes_allowed': 0,

+ 0 - 18
misago/users/permissions/delete.py

@@ -16,9 +16,6 @@ __all__ = [
     'allow_delete_user',
     'can_delete_user',
 ]
-"""
-Admin Permissions Form
-"""
 
 
 class PermissionsForm(forms.Form):
@@ -45,11 +42,6 @@ def change_permissions_form(role):
         return None
 
 
-"""
-ACL Builder
-"""
-
-
 def build_acl(acl, roles, key_name):
     new_acl = {
         'can_delete_users_newer_than': 0,
@@ -66,11 +58,6 @@ def build_acl(acl, roles, key_name):
     )
 
 
-"""
-ACL's for targets
-"""
-
-
 def add_acl_to_user(user, target):
     target.acl['can_delete'] = can_delete_user(user, target)
     if target.acl['can_delete']:
@@ -81,11 +68,6 @@ def register_with(registry):
     registry.acl_annotator(get_user_model(), add_acl_to_user)
 
 
-"""
-ACL tests
-"""
-
-
 def allow_delete_user(user, target):
     newer_than = user.acl_cache['can_delete_users_newer_than']
     less_posts_than = user.acl_cache['can_delete_users_with_less_posts_than']

+ 0 - 18
misago/users/permissions/moderation.py

@@ -26,9 +26,6 @@ __all__ = [
     'allow_lift_ban',
     'can_lift_ban',
 ]
-"""
-Admin Permissions Form
-"""
 
 
 class PermissionsForm(forms.Form):
@@ -60,11 +57,6 @@ def change_permissions_form(role):
         return None
 
 
-"""
-ACL Builder
-"""
-
-
 def build_acl(acl, roles, key_name):
     new_acl = {
         'can_rename_users': 0,
@@ -91,11 +83,6 @@ def build_acl(acl, roles, key_name):
     )
 
 
-"""
-ACL's for targets
-"""
-
-
 def add_acl_to_user(user, target):
     target.acl['can_rename'] = can_rename_user(user, target)
     target.acl['can_moderate_avatar'] = can_moderate_avatar(user, target)
@@ -121,11 +108,6 @@ def register_with(registry):
     registry.acl_annotator(get_user_model(), add_acl_to_user)
 
 
-"""
-ACL tests
-"""
-
-
 def allow_rename_user(user, target):
     if not user.acl_cache['can_rename_users']:
         raise PermissionDenied(_("You can't rename users."))

+ 2 - 18
misago/users/permissions/profiles.py

@@ -21,9 +21,8 @@ __all__ = [
     'allow_see_ban_details',
     'can_see_ban_details',
 ]
-"""
-Admin Permissions Form
-"""
+
+
 CAN_BROWSE_USERS_LIST = YesNoSwitch(label=_("Can browse users list"), initial=1)
 CAN_SEARCH_USERS = YesNoSwitch(label=_("Can search user profiles"), initial=1)
 CAN_SEE_USER_NAME_HISTORY = YesNoSwitch(label=_("Can see other members name history"))
@@ -64,11 +63,6 @@ def change_permissions_form(role):
         return None
 
 
-"""
-ACL Builder
-"""
-
-
 def build_acl(acl, roles, key_name):
     new_acl = {
         'can_browse_users_list': 0,
@@ -99,11 +93,6 @@ def build_acl(acl, roles, key_name):
     )
 
 
-"""
-ACL's for targets
-"""
-
-
 def add_acl_to_user(user, target):
     target.acl['can_have_attitude'] = False
     target.acl['can_follow'] = can_follow_user(user, target)
@@ -121,11 +110,6 @@ def register_with(registry):
     registry.acl_annotator(get_user_model(), add_acl_to_user)
 
 
-"""
-ACL tests
-"""
-
-
 def allow_browse_users_list(user):
     if not user.acl_cache['can_browse_users_list']:
         raise PermissionDenied(_("You can't browse users list."))

+ 2 - 6
misago/users/tests/test_user_avatar_api.py

@@ -18,9 +18,7 @@ UserModel = get_user_model()
 
 
 class UserAvatarTests(AuthenticatedUserTestCase):
-    """
-    tests for user avatar RPC (/api/users/1/avatar/)
-    """
+    """tests for user avatar RPC (/api/users/1/avatar/)"""
 
     def setUp(self):
         super(UserAvatarTests, self).setUp()
@@ -285,9 +283,7 @@ class UserAvatarTests(AuthenticatedUserTestCase):
 
 
 class UserAvatarModerationTests(AuthenticatedUserTestCase):
-    """
-    tests for moderate user avatar RPC (/api/users/1/moderate-avatar/)
-    """
+    """tests for moderate user avatar RPC (/api/users/1/moderate-avatar/)"""
 
     def setUp(self):
         super(UserAvatarModerationTests, self).setUp()

+ 1 - 3
misago/users/tests/test_user_changeemail_api.py

@@ -9,9 +9,7 @@ UserModel = get_user_model()
 
 
 class UserChangeEmailTests(AuthenticatedUserTestCase):
-    """
-    tests for user change email RPC (/api/users/1/change-email/)
-    """
+    """tests for user change email RPC (/api/users/1/change-email/)"""
 
     def setUp(self):
         super(UserChangeEmailTests, self).setUp()

+ 1 - 3
misago/users/tests/test_user_changepassword_api.py

@@ -5,9 +5,7 @@ from misago.users.testutils import AuthenticatedUserTestCase
 
 
 class UserChangePasswordTests(AuthenticatedUserTestCase):
-    """
-    tests for user change password RPC (/api/users/1/change-password/)
-    """
+    """tests for user change password RPC (/api/users/1/change-password/)"""
 
     def setUp(self):
         super(UserChangePasswordTests, self).setUp()

+ 1 - 3
misago/users/tests/test_user_create_api.py

@@ -11,9 +11,7 @@ UserModel = get_user_model()
 
 
 class UserCreateTests(UserTestCase):
-    """
-    tests for new user registration (POST to /api/users/)
-    """
+    """tests for new user registration (POST to /api/users/)"""
 
     def setUp(self):
         super(UserCreateTests, self).setUp()

+ 1 - 3
misago/users/tests/test_user_signature_api.py

@@ -3,9 +3,7 @@ from misago.users.testutils import AuthenticatedUserTestCase
 
 
 class UserSignatureTests(AuthenticatedUserTestCase):
-    """
-    tests for user signature RPC (POST to /api/users/1/signature/)
-    """
+    """tests for user signature RPC (POST to /api/users/1/signature/)"""
 
     def setUp(self):
         super(UserSignatureTests, self).setUp()

+ 2 - 6
misago/users/tests/test_user_username_api.py

@@ -11,9 +11,7 @@ UserModel = get_user_model()
 
 
 class UserUsernameTests(AuthenticatedUserTestCase):
-    """
-    tests for user change name RPC (POST to /api/users/1/username/)
-    """
+    """tests for user change name RPC (POST to /api/users/1/username/)"""
 
     def setUp(self):
         super(UserUsernameTests, self).setUp()
@@ -108,9 +106,7 @@ class UserUsernameTests(AuthenticatedUserTestCase):
 
 
 class UserUsernameModerationTests(AuthenticatedUserTestCase):
-    """
-    tests for moderate username RPC (/api/users/1/moderate-username/)
-    """
+    """tests for moderate username RPC (/api/users/1/moderate-username/)"""
 
     def setUp(self):
         super(UserUsernameModerationTests, self).setUp()

+ 9 - 27
misago/users/tests/test_users_api.py

@@ -20,9 +20,7 @@ UserModel = get_user_model()
 
 
 class ActivePostersListTests(AuthenticatedUserTestCase):
-    """
-    tests for active posters list (GET /users/?list=active)
-    """
+    """tests for active posters list (GET /users/?list=active)"""
 
     def setUp(self):
         super(ActivePostersListTests, self).setUp()
@@ -69,9 +67,7 @@ class ActivePostersListTests(AuthenticatedUserTestCase):
 
 
 class FollowersListTests(AuthenticatedUserTestCase):
-    """
-    tests for generic list (GET /users/) filtered by followers
-    """
+    """tests for generic list (GET /users/) filtered by followers"""
 
     def setUp(self):
         super(FollowersListTests, self).setUp()
@@ -117,9 +113,7 @@ class FollowersListTests(AuthenticatedUserTestCase):
 
 
 class FollowsListTests(AuthenticatedUserTestCase):
-    """
-    tests for generic list (GET /users/) filtered by follows
-    """
+    """tests for generic list (GET /users/) filtered by follows"""
 
     def setUp(self):
         super(FollowsListTests, self).setUp()
@@ -165,9 +159,7 @@ class FollowsListTests(AuthenticatedUserTestCase):
 
 
 class RankListTests(AuthenticatedUserTestCase):
-    """
-    tests for generic list (GET /users/) filtered by rank
-    """
+    """tests for generic list (GET /users/) filtered by rank"""
 
     def setUp(self):
         super(RankListTests, self).setUp()
@@ -241,9 +233,7 @@ class RankListTests(AuthenticatedUserTestCase):
 
 
 class SearchNamesListTests(AuthenticatedUserTestCase):
-    """
-    tests for generic list (GET /users/) filtered by username disallowing searches
-    """
+    """tests for generic list (GET /users/) filtered by username disallowing searches"""
 
     def setUp(self):
         super(SearchNamesListTests, self).setUp()
@@ -295,9 +285,7 @@ class UserRetrieveTests(AuthenticatedUserTestCase):
 
 
 class UserForumOptionsTests(AuthenticatedUserTestCase):
-    """
-    tests for user forum options RPC (POST to /api/users/1/forum-options/)
-    """
+    """tests for user forum options RPC (POST to /api/users/1/forum-options/)"""
 
     def setUp(self):
         super(UserForumOptionsTests, self).setUp()
@@ -405,9 +393,7 @@ class UserForumOptionsTests(AuthenticatedUserTestCase):
 
 
 class UserFollowTests(AuthenticatedUserTestCase):
-    """
-    tests for user follow RPC (POST to /api/users/1/follow/)
-    """
+    """tests for user follow RPC (POST to /api/users/1/follow/)"""
 
     def setUp(self):
         super(UserFollowTests, self).setUp()
@@ -471,9 +457,7 @@ class UserFollowTests(AuthenticatedUserTestCase):
 
 
 class UserBanTests(AuthenticatedUserTestCase):
-    """
-    tests for ban endpoint (GET to /api/users/1/ban/)
-    """
+    """tests for ban endpoint (GET to /api/users/1/ban/)"""
 
     def setUp(self):
         super(UserBanTests, self).setUp()
@@ -516,9 +500,7 @@ class UserBanTests(AuthenticatedUserTestCase):
 
 
 class UserDeleteTests(AuthenticatedUserTestCase):
-    """
-    tests for user delete RPC (POST to /api/users/1/delete/)
-    """
+    """tests for user delete RPC (POST to /api/users/1/delete/)"""
 
     def setUp(self):
         super(UserDeleteTests, self).setUp()

+ 0 - 6
misago/users/tokens.py

@@ -65,9 +65,6 @@ def _make_checksum(obfuscated):
     return sha256(force_bytes('%s:%s' % (settings.SECRET_KEY, obfuscated))).hexdigest()[:8]
 
 
-"""
-Convenience functions for activation token
-"""
 ACTIVATION_TOKEN = 'activation'
 
 
@@ -79,9 +76,6 @@ def is_activation_token_valid(user, token):
     return is_valid(user, ACTIVATION_TOKEN, token)
 
 
-"""
-Convenience functions for password change token
-"""
 PASSWORD_CHANGE_TOKEN = 'change_password'
 
 

+ 3 - 9
misago/users/validators.py

@@ -48,9 +48,7 @@ def validate_email(value, exclude=None):
     validate_email_banned(value)
 
 
-"""
-Username validators
-"""
+# Username validators
 def validate_username_available(value, exclude=None):
     try:
         user = UserModel.objects.get_by_username(value)
@@ -101,9 +99,7 @@ def validate_username(value, exclude=None):
     validate_username_banned(value)
 
 
-"""
-New account validators
-"""
+# New account validators
 SFS_API_URL = u'http://api.stopforumspam.org/api?email=%(email)s&ip=%(ip)s&f=json&confidence'  # noqa
 
 
@@ -140,9 +136,7 @@ def validate_gmail_email(request, form, cleaned_data):
         form.add_error('email', ValidationError(_("This email is not allowed.")))
 
 
-"""
-Registration validation
-"""
+# Registration validation
 def load_registration_validators(validators):
     loaded_validators = []
     for path in validators: