Просмотр исходного кода

Renamed "core" package to "apps" package

Ralfp 12 лет назад
Родитель
Сommit
684880d96a
47 измененных файлов с 593 добавлено и 68 удалено
  1. 2 2
      misago/admin.py
  2. 0 0
      misago/apps/__init__.py
  3. 0 0
      misago/apps/admin/__init__.py
  4. 0 0
      misago/apps/admin/clients/__init__.py
  5. 0 0
      misago/apps/admin/clients/forms.py
  6. 2 2
      misago/apps/admin/clients/views.py
  7. 0 0
      misago/apps/admin/forumroles/__init__.py
  8. 0 0
      misago/apps/admin/forumroles/forms.py
  9. 2 2
      misago/apps/admin/forumroles/views.py
  10. 0 0
      misago/apps/admin/forums/__init__.py
  11. 180 0
      misago/apps/admin/forums/forms.py
  12. 348 0
      misago/apps/admin/forums/views.py
  13. 0 0
      misago/apps/admin/home.py
  14. 0 0
      misago/apps/admin/index.py
  15. 0 0
      misago/apps/admin/online/__init__.py
  16. 0 0
      misago/apps/admin/online/forms.py
  17. 2 2
      misago/apps/admin/online/views.py
  18. 0 0
      misago/apps/admin/roles/__init__.py
  19. 0 0
      misago/apps/admin/roles/forms.py
  20. 2 2
      misago/apps/admin/roles/views.py
  21. 10 13
      misago/apps/admin/sections/__init__.py
  22. 5 5
      misago/apps/admin/sections/forums.py
  23. 4 4
      misago/apps/admin/sections/overview.py
  24. 2 2
      misago/apps/admin/sections/perms.py
  25. 2 2
      misago/apps/admin/sections/system.py
  26. 5 5
      misago/apps/admin/sections/users.py
  27. 0 0
      misago/apps/admin/settings/__init__.py
  28. 0 0
      misago/apps/admin/settings/forms.py
  29. 2 2
      misago/apps/admin/settings/views.py
  30. 0 0
      misago/apps/admin/stats/__init__.py
  31. 0 0
      misago/apps/admin/stats/forms.py
  32. 2 2
      misago/apps/admin/stats/views.py
  33. 1 1
      misago/apps/admin/team.py
  34. 0 0
      misago/apps/admin/widgets.py
  35. 0 0
      misago/apps/front/__init__.py
  36. 0 0
      misago/apps/front/index.py
  37. 0 0
      misago/apps/front/readall.py
  38. 0 0
      misago/apps/signin/__init__.py
  39. 0 0
      misago/apps/signin/forms.py
  40. 2 2
      misago/apps/signin/urls.py
  41. 1 1
      misago/apps/signin/views.py
  42. 0 0
      misago/apps/views.py
  43. 1 1
      misago/decorators.py
  44. 2 2
      misago/firewalls.py
  45. 1 1
      misago/models/forummodel.py
  46. 10 10
      misago/settings_base.py
  47. 5 5
      misago/urls.py

+ 2 - 2
misago/admin.py

@@ -147,13 +147,13 @@ class AdminSite(object):
         late_actions = []
 
         # Load default admin site
-        from misago.core.admin.sections import ADMIN_SECTIONS
+        from misago.apps.admin.sections import ADMIN_SECTIONS
         for section in ADMIN_SECTIONS:
             self.sections.append(section)
             self.sections_index[section.id] = section
 
             # Loop section actions
-            section_actions = import_module('misago.core.admin.sections.%s' % section.id)
+            section_actions = import_module('misago.apps.admin.sections.%s' % section.id)
             for action in section_actions.ADMIN_ACTIONS:
                 self.actions_index[action.id] = action
                 if not action.after:

+ 0 - 0
misago/core/__init__.py → misago/apps/__init__.py


+ 0 - 0
misago/core/admin/__init__.py → misago/apps/admin/__init__.py


+ 0 - 0
misago/core/admin/clients/__init__.py → misago/apps/admin/clients/__init__.py


+ 0 - 0
misago/core/admin/clients/forms.py → misago/apps/admin/clients/forms.py


+ 2 - 2
misago/core/admin/clients/views.py → misago/apps/admin/clients/views.py

@@ -2,11 +2,11 @@ from django.core.urlresolvers import reverse as django_reverse
 from django import forms
 from django.utils.translation import ugettext as _
 from misago.admin import site
-from misago.core.admin.widgets import *
+from misago.apps.admin.widgets import *
 from misago.forms import Form
 from misago.models import ThemeAdjustment
 from misago.utils.strings import slugify
-from misago.core.admin.clients.forms import ThemeAdjustmentForm
+from misago.apps.admin.clients.forms import ThemeAdjustmentForm
 
 def reverse(route, target=None):
     if target:

+ 0 - 0
misago/core/admin/forumroles/__init__.py → misago/apps/admin/forumroles/__init__.py


+ 0 - 0
misago/core/admin/forumroles/forms.py → misago/apps/admin/forumroles/forms.py


+ 2 - 2
misago/core/admin/forumroles/views.py → misago/apps/admin/forumroles/views.py

@@ -3,11 +3,11 @@ from django.core.urlresolvers import reverse as django_reverse
 from django.utils.translation import ugettext as _
 from misago.acl.builder import build_forum_form
 from misago.admin import site
-from misago.core.admin.widgets import *
+from misago.apps.admin.widgets import *
 from misago.forms import Form, YesNoSwitch
 from misago.models import ForumRole
 from misago.utils.strings import slugify
-from misago.core.admin.forumroles.forms import ForumRoleForm
+from misago.apps.admin.forumroles.forms import ForumRoleForm
 
 def reverse(route, target=None):
     if target:

+ 0 - 0
misago/core/admin/online/__init__.py → misago/apps/admin/forums/__init__.py


+ 180 - 0
misago/apps/admin/forums/forms.py

@@ -0,0 +1,180 @@
+from django.utils.translation import ugettext_lazy as _
+from django import forms
+from mptt.forms import TreeNodeChoiceField
+from misago.forms import Form, YesNoSwitch
+from misago.models import Forum
+from misago.validators import validate_sluggable
+
+class CategoryForm(Form):
+    parent = False
+    perms = False
+    name = forms.CharField(max_length=255, validators=[validate_sluggable(
+                                                                          _("Category name must be sluggable."),
+                                                                          _("Category name is too long.")
+                                                                          )])
+    description = forms.CharField(widget=forms.Textarea, required=False)
+    closed = forms.BooleanField(widget=YesNoSwitch, required=False)
+    style = forms.CharField(max_length=255, required=False)
+    attrs = forms.CharField(max_length=255, required=False)
+    show_details = forms.BooleanField(widget=YesNoSwitch, required=False, initial=True)
+
+    layout = (
+              (
+               _("Basic Options"),
+               (
+                ('parent', {'label': _("Category Parent")}),
+                ('perms', {'label': _("Copy Permissions from")}),
+                ('name', {'label': _("Category Name")}),
+                ('description', {'label': _("Category Description")}),
+                ('closed', {'label': _("Closed Category")}),
+                ),
+              ),
+              (
+               _("Display Options"),
+               (
+                ('attrs', {'label': _("Category Attributes"), 'help_text': _('Custom templates can check categories for predefined attributes that will change way they are rendered.')}),
+                ('show_details', {'label': _("Show Subforums Details"), 'help_text': _('Allows you to prevent this category subforums from displaying statistics, last post data, etc. ect. on forums lists.')}),
+                ('style', {'label': _("Category Style"), 'help_text': _('You can add custom CSS classess to this category, to change way it looks on board index.')}),
+                ),
+              ),
+             )
+
+    def finalize_form(self):
+        self.fields['parent'] = TreeNodeChoiceField(queryset=Forum.tree.get(token='root').get_descendants(include_self=True), level_indicator=u'- - ')
+        self.fields['perms'] = TreeNodeChoiceField(queryset=Forum.tree.get(token='root').get_descendants(), level_indicator=u'- - ', required=False, empty_label=_("Don't copy permissions"))
+
+    def clean_attrs(self):
+        clean = []
+        data = self.cleaned_data['attrs'].strip().split()
+        for i in data:
+            i = i.strip()
+            if not i in clean:
+                clean.append(i)
+        return ' '.join(clean)
+
+
+class ForumForm(Form):
+    parent = False
+    perms = False
+    name = forms.CharField(max_length=255, validators=[validate_sluggable(
+                                                                          _("Forum name must be sluggable."),
+                                                                          _("Forum name is too long.")
+                                                                          )])
+    description = forms.CharField(widget=forms.Textarea, required=False)
+    closed = forms.BooleanField(widget=YesNoSwitch, required=False)
+    style = forms.CharField(max_length=255, required=False)
+    prune_start = forms.IntegerField(min_value=0, initial=0)
+    prune_last = forms.IntegerField(min_value=0, initial=0)
+    attrs = forms.CharField(max_length=255, required=False)
+    show_details = forms.BooleanField(widget=YesNoSwitch, required=False, initial=True)
+
+    layout = (
+              (
+               _("Basic Options"),
+               (
+                ('parent', {'label': _("Forum Parent")}),
+                ('perms', {'label': _("Copy Permissions from")}),
+                ('name', {'label': _("Forum Name")}),
+                ('description', {'label': _("Forum Description")}),
+                ('closed', {'label': _("Closed Forum")}),
+                ),
+               ),
+              (
+               _("Prune Forum"),
+               (
+                ('prune_start', {'label': _("Delete threads with first post older than"), 'help_text': _('Enter number of days since thread start after which thread will be deleted or zero to don\'t delete threads.')}),
+                ('prune_last', {'label': _("Delete threads with last post older than"), 'help_text': _('Enter number of days since since last reply in thread after which thread will be deleted or zero to don\'t delete threads.')}),
+                ),
+               ),
+              (
+               _("Display Options"),
+               (
+                ('attrs', {'label': _("Subforums List Attributes"), 'help_text': _('Custom templates can check forums for predefined attributes that will change way subforums lists are rendered.')}),
+                ('show_details', {'label': _("Show Subforums Details"), 'help_text': _("Allows you to prevent this forum's subforums from displaying statistics, last post data, etc. ect. on subforums list.")}),
+                ('style', {'label': _("Forum Style"), 'help_text': _('You can add custom CSS classess to this forum to change way it looks on forums lists.')}),
+                ),
+               ),
+              )
+
+    def finalize_form(self):
+        self.fields['parent'] = TreeNodeChoiceField(queryset=Forum.tree.get(token='root').get_descendants(), level_indicator=u'- - ')
+        self.fields['perms'] = TreeNodeChoiceField(queryset=Forum.tree.get(token='root').get_descendants(), level_indicator=u'- - ', required=False, empty_label=_("Don't copy permissions"))
+
+    def clean_attrs(self):
+        clean = []
+        data = self.cleaned_data['attrs'].strip().split()
+        for i in data:
+            i = i.strip()
+            if not i in clean:
+                clean.append(i)
+        return ' '.join(clean)
+
+
+class RedirectForm(Form):
+    parent = False
+    perms = False
+    name = forms.CharField(max_length=255, validators=[validate_sluggable(
+                                                                          _("Redirect name must be sluggable."),
+                                                                          _("Redirect name is too long.")
+                                                                          )])
+    description = forms.CharField(widget=forms.Textarea, required=False)
+    redirect = forms.URLField(max_length=255)
+    style = forms.CharField(max_length=255, required=False)
+
+    layout = (
+              (
+               _("Basic Options"),
+               (
+                ('parent', {'label': _("Redirect Parent")}),
+                ('perms', {'label': _("Copy Permissions from")}),
+                ('name', {'label': _("Redirect Name")}),
+                ('redirect', {'label': _("Redirect URL")}),
+                ('description', {'label': _("Redirect Description")}),
+                ),
+               ),
+              (
+               _("Display Options"),
+               (
+                ('style', {'label': _("Redirect Style"), 'help_text': _('You can add custom CSS classess to this redirect to change way it looks on forums lists.')}),
+                ),
+               ),
+              )
+
+    def finalize_form(self):
+        self.fields['parent'] = TreeNodeChoiceField(queryset=Forum.tree.get(token='root').get_descendants(), level_indicator=u'- - ')
+        self.fields['perms'] = TreeNodeChoiceField(queryset=Forum.tree.get(token='root').get_descendants(), level_indicator=u'- - ', required=False, empty_label=_("Don't copy permissions"))
+
+
+class DeleteForm(Form):
+    layout = (
+              (
+               _("Delete Options"),
+               (
+                ('contents', {'label': _("Move threads to")}),
+                ('subforums', {'label': _("Move subforums to")}),
+                ),
+               ),
+              )
+
+    def __init__(self, *args, **kwargs):
+        self.forum = kwargs.pop('forum')
+        super(DeleteForm, self).__init__(*args, **kwargs)
+
+    def finalize_form(self):
+        self.fields['contents'] = TreeNodeChoiceField(queryset=Forum.tree.get(token='root').get_descendants(), required=False, empty_label=_("Remove with forum"), level_indicator=u'- - ')
+        self.fields['subforums'] = TreeNodeChoiceField(queryset=Forum.tree.get(token='root').get_descendants(), required=False, empty_label=_("Remove with forum"), level_indicator=u'- - ')
+
+    def clean_contents(self):
+        data = self.cleaned_data['contents']
+        if data:
+            if data.type == 'category':
+                raise forms.ValidationError(_("Categories cannot contain threads."))
+            if data.type == 'redirect':
+                raise forms.ValidationError(_("Redirects cannot contain threads."))
+        return data
+
+    def clean(self):
+        cleaned_data = super(DeleteForm, self).clean()
+        if self.forum.type == 'forum' and cleaned_data['contents'] and cleaned_data['contents'].lft > self.forum.lft and cleaned_data['contents'].rght < self.forum.rght and not cleaned_data['subforums']:
+            raise forms.ValidationError(_("Destination you want to move this forum's threads to will be deleted with this forum."))
+        return cleaned_data

+ 348 - 0
misago/apps/admin/forums/views.py

@@ -0,0 +1,348 @@
+import copy
+from django.core.urlresolvers import reverse as django_reverse
+from django.db.models import Q
+from django.utils.translation import ugettext as _
+from mptt.forms import TreeNodeChoiceField
+from misago.admin import site
+from misago.apps.admin.widgets import *
+from misago.models import Forum
+from misago.utils.strings import slugify
+from misago.apps.admin.forums.forms import CategoryForm, ForumForm, RedirectForm, DeleteForm
+
+def reverse(route, target=None):
+    if target:
+        return django_reverse(route, kwargs={'target': target.pk, 'slug': target.slug})
+    return django_reverse(route)
+
+
+"""
+Views
+"""
+class List(ListWidget):
+    admin = site.get_action('forums')
+    id = 'list'
+    columns = (
+               ('forum', _("Forum")),
+               )
+    nothing_checked_message = _('You have to select at least one forum.')
+    actions = (
+               ('resync', _("Resynchronise forums")),
+               ('prune', _("Prune forums"), _("Are you sure you want to delete all content from selected forums?")),
+               )
+    empty_message = _('No forums are currently defined.')
+
+    def get_items(self):
+        return self.admin.model.objects.get(token='root').get_descendants()
+
+    def sort_items(self, page_items, sorting_method):
+        return page_items.order_by('lft')
+
+    def get_item_actions(self, item):
+        if item.type == 'category':
+            return (
+                    self.action('chevron-up', _("Move Category Up"), reverse('admin_forums_up', item), post=True),
+                    self.action('chevron-down', _("Move Category Down"), reverse('admin_forums_down', item), post=True),
+                    self.action('pencil', _("Edit Category"), reverse('admin_forums_edit', item)),
+                    self.action('remove', _("Delete Category"), reverse('admin_forums_delete', item)),
+                    )
+
+        if item.type == 'forum':
+            return (
+                    self.action('chevron-up', _("Move Forum Up"), reverse('admin_forums_up', item), post=True),
+                    self.action('chevron-down', _("Move Forum Down"), reverse('admin_forums_down', item), post=True),
+                    self.action('pencil', _("Edit Forum"), reverse('admin_forums_edit', item)),
+                    self.action('remove', _("Delete Forum"), reverse('admin_forums_delete', item)),
+                    )
+
+        return (
+                self.action('chevron-up', _("Move Redirect Up"), reverse('admin_forums_up', item), post=True),
+                self.action('chevron-down', _("Move Redirect Down"), reverse('admin_forums_down', item), post=True),
+                self.action('pencil', _("Edit Redirect"), reverse('admin_forums_edit', item)),
+                self.action('remove', _("Delete Redirect"), reverse('admin_forums_delete', item)),
+                )
+
+    def action_resync(self, items, checked):
+        return Message(_('Selected forums have been resynchronised successfully.'), 'success'), reverse('admin_forums')
+
+    def action_prune(self, items, checked):
+        return Message(_('Selected forums have been pruned successfully.'), 'success'), reverse('admin_forums')
+
+
+class NewCategory(FormWidget):
+    admin = site.get_action('forums')
+    id = 'new_category'
+    fallback = 'admin_forums'
+    form = CategoryForm
+    submit_button = _("Save Category")
+
+    def get_new_url(self, model):
+        return reverse('admin_forums_new_category')
+
+    def get_edit_url(self, model):
+        return reverse('admin_forums_edit', model)
+
+    def submit_form(self, form, target):
+        new_forum = Forum(
+                          name=form.cleaned_data['name'],
+                          slug=slugify(form.cleaned_data['name']),
+                          type='category',
+                          attrs=form.cleaned_data['attrs'],
+                          show_details=form.cleaned_data['show_details'],
+                          style=form.cleaned_data['style'],
+                          closed=form.cleaned_data['closed'],
+                          )
+        new_forum.set_description(form.cleaned_data['description'])
+        new_forum.insert_at(form.cleaned_data['parent'], position='last-child', save=True)
+        Forum.objects.populate_tree(True)
+
+        if form.cleaned_data['perms']:
+            new_forum.copy_permissions(form.cleaned_data['perms'])
+            self.request.monitor['acl_version'] = int(self.request.monitor['acl_version']) + 1
+
+        return new_forum, Message(_('New Category has been created.'), 'success')
+
+
+class NewForum(FormWidget):
+    admin = site.get_action('forums')
+    id = 'new_forum'
+    fallback = 'admin_forums'
+    form = ForumForm
+    submit_button = _("Save Forum")
+
+    def get_new_url(self, model):
+        return reverse('admin_forums_new_forum')
+
+    def get_edit_url(self, model):
+        return reverse('admin_forums_edit', model)
+
+    def submit_form(self, form, target):
+        new_forum = Forum(
+                          name=form.cleaned_data['name'],
+                          slug=slugify(form.cleaned_data['name']),
+                          type='forum',
+                          attrs=form.cleaned_data['attrs'],
+                          show_details=form.cleaned_data['show_details'],
+                          style=form.cleaned_data['style'],
+                          closed=form.cleaned_data['closed'],
+                          prune_start=form.cleaned_data['prune_start'],
+                          prune_last=form.cleaned_data['prune_last'],
+                          )
+        new_forum.set_description(form.cleaned_data['description'])
+        new_forum.insert_at(form.cleaned_data['parent'], position='last-child', save=True)
+        Forum.objects.populate_tree(True)
+
+        if form.cleaned_data['perms']:
+            new_forum.copy_permissions(form.cleaned_data['perms'])
+            self.request.monitor['acl_version'] = int(self.request.monitor['acl_version']) + 1
+
+        return new_forum, Message(_('New Forum has been created.'), 'success')
+
+    def __call__(self, request):
+        if self.admin.model.objects.get(token='root').get_descendants().count() == 0:
+            request.messages.set_flash(Message(_("You have to create at least one category before you will be able to create forums.")), 'error', self.admin.id)
+            return redirect(self.get_fallback_url())
+        return super(NewForum, self).__call__(request)
+
+
+class NewRedirect(FormWidget):
+    admin = site.get_action('forums')
+    id = 'new_redirect'
+    fallback = 'admin_forums'
+    form = RedirectForm
+    submit_button = _("Save Forum")
+
+    def get_new_url(self, model):
+        return reverse('admin_forums_new_redirect')
+
+    def get_edit_url(self, model):
+        return reverse('admin_forums_edit', model)
+
+    def submit_form(self, form, target):
+        new_forum = Forum(
+                          name=form.cleaned_data['name'],
+                          slug=slugify(form.cleaned_data['name']),
+                          redirect=form.cleaned_data['redirect'],
+                          style=form.cleaned_data['style'],
+                          type='redirect',
+                          )
+        new_forum.set_description(form.cleaned_data['description'])
+        new_forum.insert_at(form.cleaned_data['parent'], position='last-child', save=True)
+        Forum.objects.populate_tree(True)
+
+        if form.cleaned_data['perms']:
+            new_forum.copy_permissions(form.cleaned_data['perms'])
+            self.request.monitor['acl_version'] = int(self.request.monitor['acl_version']) + 1
+
+        return new_forum, Message(_('New Redirect has been created.'), 'success')
+
+    def __call__(self, request):
+        if self.admin.model.objects.get(token='root').get_descendants().count() == 0:
+            request.messages.set_flash(Message(_("You have to create at least one category before you will be able to create redirects.")), 'error', self.admin.id)
+            return redirect(self.get_fallback_url())
+        return super(NewRedirect, self).__call__(request)
+
+
+class Up(ButtonWidget):
+    admin = site.get_action('forums')
+    id = 'up'
+    fallback = 'admin_forums'
+    notfound_message = _('Requested Forum could not be found.')
+
+    def action(self, target):
+        previous_sibling = target.get_previous_sibling()
+        if previous_sibling:
+            target.move_to(previous_sibling, 'left')
+            return Message(_('Forum "%(name)s" has been moved up.') % {'name': target.name}, 'success'), False
+        return Message(_('Forum "%(name)s" is first child of its parent node and cannot be moved up.') % {'name': target.name}, 'info'), False
+
+
+class Down(ButtonWidget):
+    admin = site.get_action('forums')
+    id = 'down'
+    fallback = 'admin_forums'
+    notfound_message = _('Requested Forum could not be found.')
+
+    def action(self, target):
+        next_sibling = target.get_next_sibling()
+        if next_sibling:
+            target.move_to(next_sibling, 'right')
+            return Message(_('Forum "%(name)s" has been moved down.') % {'name': target.name}, 'success'), False
+        return Message(_('Forum "%(name)s" is last child of its parent node and cannot be moved down.') % {'name': target.name}, 'info'), False
+
+
+class Edit(FormWidget):
+    admin = site.get_action('forums')
+    id = 'edit'
+    name = _("Edit Forum")
+    fallback = 'admin_forums'
+    form = ForumForm
+    target_name = 'name'
+    notfound_message = _('Requested Forum could not be found.')
+    submit_fallback = True
+
+    def get_url(self, model):
+        return reverse('admin_forums_edit', model)
+
+    def get_edit_url(self, model):
+        return self.get_url(model)
+
+    def get_form(self, target):
+        if target.type == 'category':
+            self.name = _("Edit Category")
+            self.form = CategoryForm
+        if target.type == 'redirect':
+            self.name = _("Edit Redirect")
+            self.form = RedirectForm
+        return self.form
+
+    def get_form_instance(self, form, target, initial, post=False):
+        form_inst = super(Edit, self).get_form_instance(form, target, initial, post)
+        valid_targets = Forum.tree.get(token='root').get_descendants(include_self=target.type == 'category').exclude(Q(lft__gte=target.lft) & Q(rght__lte=target.rght))
+        form_inst.fields['parent'] = TreeNodeChoiceField(queryset=valid_targets, level_indicator=u'- - ')
+        return form_inst
+
+    def get_initial_data(self, model):
+        initial = {
+                   'parent': model.parent,
+                   'name': model.name,
+                   'description': model.description,
+                   }
+
+        if model.type == 'redirect':
+            initial['redirect'] = model.redirect
+        else:
+            initial['attrs'] = model.attrs
+            initial['show_details'] = model.show_details
+            initial['style'] = model.style
+            initial['closed'] = model.closed
+
+        if model.type == 'forum':
+            initial['prune_start'] = model.prune_start
+            initial['prune_last'] = model.prune_last
+
+        return initial
+
+    def submit_form(self, form, target):
+        target.name = form.cleaned_data['name']
+        target.slug = slugify(form.cleaned_data['name'])
+        target.set_description(form.cleaned_data['description'])
+        if target.type == 'redirect':
+            target.redirect = form.cleaned_data['redirect']
+        else:
+            target.attrs = form.cleaned_data['attrs']
+            target.show_details = form.cleaned_data['show_details']
+            target.style = form.cleaned_data['style']
+            target.closed = form.cleaned_data['closed']
+
+        if target.type == 'forum':
+            target.prune_start = form.cleaned_data['prune_start']
+            target.prune_last = form.cleaned_data['prune_last']
+
+        if form.cleaned_data['parent'].pk != target.parent.pk:
+            target.move_to(form.cleaned_data['parent'], 'last-child')
+            self.request.monitor['acl_version'] = int(self.request.monitor['acl_version']) + 1
+
+        target.save(force_update=True)
+        Forum.objects.populate_tree(True)
+
+        if form.cleaned_data['perms']:
+            target.copy_permissions(form.cleaned_data['perms'])
+
+        if form.cleaned_data['parent'].pk != target.parent.pk or form.cleaned_data['perms']:
+            self.request.monitor['acl_version'] = int(self.request.monitor['acl_version']) + 1
+
+        return target, Message(_('Changes in forum "%(name)s" have been saved.') % {'name': self.original_name}, 'success')
+
+
+class Delete(FormWidget):
+    admin = site.get_action('forums')
+    id = 'delete'
+    name = _("Delete Forum")
+    fallback = 'admin_forums'
+    template = 'delete'
+    form = DeleteForm
+    target_name = 'name'
+    notfound_message = _('Requested Forum could not be found.')
+    submit_fallback = True
+
+    def get_url(self, model):
+        return reverse('admin_forums_delete', model)
+
+    def get_form(self, target):
+        if target.type == 'category':
+            self.name = _("Delete Category")
+        if target.type == 'redirect':
+            self.name = _("Delete Redirect")
+        return self.form
+
+    def get_form_instance(self, form, target, initial, post=False):
+        if post:
+            form_inst = form(self.request.POST, forum=target, request=self.request, initial=self.get_initial_data(target))
+        else:
+            form_inst = form(forum=target, request=self.request, initial=self.get_initial_data(target))
+        if target.type != 'forum':
+            del form_inst.fields['contents']
+        valid_targets = Forum.tree.get(token='root').get_descendants().exclude(Q(lft__gte=target.lft) & Q(rght__lte=target.rght))
+        form_inst.fields['subforums'] = TreeNodeChoiceField(queryset=valid_targets, required=False, empty_label=_("Remove with forum"), level_indicator=u'- - ')
+        return form_inst
+
+    def submit_form(self, form, target):
+        if target.type == 'forum':
+            new_forum = form.cleaned_data['contents']
+            if new_forum:
+                target.move_content(new_forum)
+                new_forum.sync()
+                new_forum.save(force_update=True)
+        new_parent = form.cleaned_data['subforums']
+        if new_parent:
+            for child in target.get_descendants():
+                if child.parent_id == target.pk:
+                    child.move_to(new_parent, 'last-child')
+                    child.save(force_update=True)
+        else:
+            for child in target.get_descendants().order_by('-lft'):
+                Forum.objects.get(id=child.pk).delete()
+        Forum.objects.get(id=target.pk).delete()
+        Forum.objects.populate_tree(True)
+        self.request.monitor['acl_version'] = int(self.request.monitor['acl_version']) + 1
+        return target, Message(_('Forum "%(name)s" has been deleted.') % {'name': self.original_name}, 'success')

+ 0 - 0
misago/core/admin/home.py → misago/apps/admin/home.py


+ 0 - 0
misago/core/admin/index.py → misago/apps/admin/index.py


+ 0 - 0
misago/core/admin/roles/__init__.py → misago/apps/admin/online/__init__.py


+ 0 - 0
misago/core/admin/online/forms.py → misago/apps/admin/online/forms.py


+ 2 - 2
misago/core/admin/online/views.py → misago/apps/admin/online/views.py

@@ -1,7 +1,7 @@
 from django.utils.translation import ugettext as _
 from misago.admin import site
-from misago.core.admin.widgets import ListWidget
-from misago.core.admin.online.forms import SearchSessionsForm
+from misago.apps.admin.widgets import ListWidget
+from misago.apps.admin.online.forms import SearchSessionsForm
 
 class List(ListWidget):
     admin = site.get_action('online')

+ 0 - 0
misago/core/admin/settings/__init__.py → misago/apps/admin/roles/__init__.py


+ 0 - 0
misago/core/admin/roles/forms.py → misago/apps/admin/roles/forms.py


+ 2 - 2
misago/core/admin/roles/views.py → misago/apps/admin/roles/views.py

@@ -4,11 +4,11 @@ from django.shortcuts import redirect
 from django.utils.translation import ugettext as _
 from misago.acl.builder import build_form 
 from misago.admin import site
-from misago.core.admin.widgets import *
+from misago.apps.admin.widgets import *
 from misago.forms import Form, YesNoSwitch
 from misago.models import Forum, ForumRole, Role
 from misago.utils.strings import slugify
-from misago.core.admin.roles.forms import RoleForm
+from misago.apps.admin.roles.forms import RoleForm
 
 def reverse(route, target=None):
     if target:

+ 10 - 13
misago/core/admin/sections/__init__.py → misago/apps/admin/sections/__init__.py

@@ -8,6 +8,16 @@ ADMIN_SECTIONS = (
                  icon='signal',
                  ),
     AdminSection(
+                 id='users',
+                 name=_("Users"),
+                 icon='user',
+                 ),
+    AdminSection(
+                 id='forums',
+                 name=_("Forums"),
+                 icon='comment',
+                 ),
+    AdminSection(
                  id='perms',
                  name=_("Permissions"),
                  icon='adjust',
@@ -18,16 +28,3 @@ ADMIN_SECTIONS = (
                  icon='cog',
                  ),
 )
-
-"""
-    AdminSection(
-                 id='users',
-                 name=_("Users"),
-                 icon='user',
-                 ),
-    AdminSection(
-                 id='forums',
-                 name=_("Forums"),
-                 icon='comment',
-                 ),
-"""

+ 5 - 5
misago/core/admin/sections/forums.py → misago/apps/admin/sections/forums.py

@@ -38,7 +38,7 @@ ADMIN_ACTIONS = (
                          },
                         ],
                route='admin_forums',
-               urlpatterns=patterns('misago.forums.views',
+               urlpatterns=patterns('misago.apps.admin.forums.views',
                         url(r'^$', 'List', name='admin_forums'),
                         url(r'^new/category/$', 'NewCategory', name='admin_forums_new_category'),
                         url(r'^new/forum/$', 'NewForum', name='admin_forums_new_forum'),
@@ -56,7 +56,7 @@ ADMIN_ACTIONS = (
                help=_("Thread Labels allow you to group threads together within forums."),
                icon='tags',
                route='admin_forums_labels',
-               urlpatterns=patterns('misago.admin.views',
+               urlpatterns=patterns('misago.apps.admin.index',
                         url(r'^$', 'todo', name='admin_forums_labels'),
                     ),
                ),
@@ -67,7 +67,7 @@ ADMIN_ACTIONS = (
                help=_("Forbid usage of words in messages"),
                icon='volume-off',
                route='admin_forums_badwords',
-               urlpatterns=patterns('misago.admin.views',
+               urlpatterns=patterns('misago.apps.admin.index',
                         url(r'^$', 'todo', name='admin_forums_badwords'),
                     ),
                ),
@@ -78,7 +78,7 @@ ADMIN_ACTIONS = (
                help=_("Tests that new messages have to pass"),
                icon='filter',
                route='admin_forums_tests',
-               urlpatterns=patterns('misago.admin.views',
+               urlpatterns=patterns('misago.apps.admin.index',
                         url(r'^$', 'todo', name='admin_forums_tests'),
                     ),
                ),
@@ -89,7 +89,7 @@ ADMIN_ACTIONS = (
                help=_("Manage allowed attachment types."),
                icon='download-alt',
                route='admin_forums_attachments',
-               urlpatterns=patterns('misago.admin.views',
+               urlpatterns=patterns('misago.apps.admin.index',
                         url(r'^$', 'todo', name='admin_forums_attachments'),
                     ),
                ),

+ 4 - 4
misago/core/admin/sections/overview.py → misago/apps/admin/sections/overview.py

@@ -11,7 +11,7 @@ ADMIN_ACTIONS = (
                help=_("Your forums right now"),
                icon='home',
                route='admin_home',
-               urlpatterns=patterns('misago.core.admin.index',
+               urlpatterns=patterns('misago.apps.admin.index',
                         url(r'^$', 'index', name='admin_home'),
                     ),
                ),
@@ -22,7 +22,7 @@ ADMIN_ACTIONS = (
                help=_("Create Statistics Reports"),
                icon='signal',
                route='admin_stats',
-               urlpatterns=patterns('misago.core.admin.stats.views',
+               urlpatterns=patterns('misago.apps.admin.stats.views',
                         url(r'^$', 'form', name='admin_stats'),
                         url(r'^(?P<model>[a-z0-9]+)/(?P<date_start>[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9])/(?P<date_end>[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9])/(?P<precision>\w+)$', 'graph', name='admin_stats_graph'),
                     ),
@@ -43,7 +43,7 @@ ADMIN_ACTIONS = (
                          },
                         ],
                route='admin_online',
-               urlpatterns=patterns('misago.core.admin.online.views',
+               urlpatterns=patterns('misago.apps.admin.online.views',
                         url(r'^$', 'List', name='admin_online'),
                         url(r'^(?P<page>\d+)/$', 'List', name='admin_online'),
                     ),
@@ -64,7 +64,7 @@ ADMIN_ACTIONS = (
                          },
                         ],
                route='admin_team',
-               urlpatterns=patterns('misago.core.admin.team',
+               urlpatterns=patterns('misago.apps.admin.team',
                         url(r'^$', 'List', name='admin_team'),
                     ),
                ),

+ 2 - 2
misago/core/admin/sections/perms.py → misago/apps/admin/sections/perms.py

@@ -26,7 +26,7 @@ ADMIN_ACTIONS = (
                          },
                         ],
                route='admin_roles',
-               urlpatterns=patterns('misago.core.admin.roles.views',
+               urlpatterns=patterns('misago.apps.admin.roles.views',
                         url(r'^$', 'List', name='admin_roles'),
                         url(r'^new/$', 'New', name='admin_roles_new'),
                         url(r'^forums/(?P<slug>([a-z0-9]|-)+)-(?P<target>\d+)/$', 'Forums', name='admin_roles_masks'),
@@ -57,7 +57,7 @@ ADMIN_ACTIONS = (
                          },
                         ],
                route='admin_roles_forums',
-               urlpatterns=patterns('misago.core.admin.forumroles.views',
+               urlpatterns=patterns('misago.apps.admin.forumroles.views',
                         url(r'^$', 'List', name='admin_roles_forums'),
                         url(r'^new/$', 'New', name='admin_roles_forums_new'),
                         url(r'^acl/(?P<slug>([a-z0-9]|-)+)-(?P<target>\d+)/$', 'ACL', name='admin_roles_forums_acl'),

+ 2 - 2
misago/core/admin/sections/system.py → misago/apps/admin/sections/system.py

@@ -11,7 +11,7 @@ ADMIN_ACTIONS = (
                help=_("Change your forum configuration"),
                icon='wrench',
                route='admin_settings',
-               urlpatterns=patterns('misago.core.admin.settings.views',
+               urlpatterns=patterns('misago.apps.admin.settings.views',
                         url(r'^$', 'settings', name='admin_settings'),
                         url(r'^search/$', 'settings_search', name='admin_settings_search'),
                         url(r'^(?P<group_slug>([a-z0-9]|-)+)-(?P<group_id>\d+)/$', 'settings', name='admin_settings')
@@ -39,7 +39,7 @@ ADMIN_ACTIONS = (
                          },
                         ],
                route='admin_clients',
-               urlpatterns=patterns('misago.core.admin.clients.views',
+               urlpatterns=patterns('misago.apps.admin.clients.views',
                         url(r'^$', 'List', name='admin_clients'),
                         url(r'^new/$', 'New', name='admin_clients_new'),
                         url(r'^edit/(?P<slug>([a-z0-9]|-)+)-(?P<target>\d+)/$', 'Edit', name='admin_clients_edit'),

+ 5 - 5
misago/core/admin/sections/users.py → misago/apps/admin/sections/users.py

@@ -26,7 +26,7 @@ ADMIN_ACTIONS = (
                          },
                         ],
                route='admin_users',
-               urlpatterns=patterns('misago.users.views',
+               urlpatterns=patterns('misago.apps.admin.users.views',
                         url(r'^$', 'List', name='admin_users'),
                         url(r'^(?P<page>\d+)/$', 'List', name='admin_users'),
                         url(r'^inactive/$', 'inactive', name='admin_users_inactive'),
@@ -57,7 +57,7 @@ ADMIN_ACTIONS = (
                          },
                         ],
                route='admin_ranks',
-               urlpatterns=patterns('misago.ranks.views',
+               urlpatterns=patterns('misago.apps.admin.ranks.views',
                         url(r'^$', 'List', name='admin_ranks'),
                         url(r'^new/$', 'New', name='admin_ranks_new'),
                         url(r'^edit/(?P<slug>([a-z0-9]|-)+)-(?P<target>\d+)/$', 'Edit', name='admin_ranks_edit'),
@@ -86,7 +86,7 @@ ADMIN_ACTIONS = (
                          },
                         ],
                route='admin_bans',
-               urlpatterns=patterns('misago.banning.views',
+               urlpatterns=patterns('misago.apps.admin.banning.views',
                         url(r'^$', 'List', name='admin_bans'),
                         url(r'^(?P<page>\d+)/$', 'List', name='admin_bans'),
                         url(r'^new/$', 'New', name='admin_bans_new'),
@@ -116,7 +116,7 @@ ADMIN_ACTIONS = (
                          },
                         ],
                route='admin_prune_users',
-               urlpatterns=patterns('misago.prune.views',
+               urlpatterns=patterns('misago.apps.admin.prune.views',
                         url(r'^$', 'List', name='admin_prune_users'),
                         url(r'^new/$', 'New', name='admin_prune_users_new'),
                         url(r'^edit/(?P<target>\d+)/$', 'Edit', name='admin_prune_users_edit'),
@@ -146,7 +146,7 @@ ADMIN_ACTIONS = (
                          },
                         ],
                route='admin_newsletters',
-               urlpatterns=patterns('misago.newsletters.views',
+               urlpatterns=patterns('misago.apps.admin.newsletters.views',
                         url(r'^$', 'List', name='admin_newsletters'),
                         url(r'^(?P<page>\d+)/$', 'List', name='admin_newsletters'),
                         url(r'^new/$', 'New', name='admin_newsletters_new'),

+ 0 - 0
misago/core/admin/stats/__init__.py → misago/apps/admin/settings/__init__.py


+ 0 - 0
misago/core/admin/settings/forms.py → misago/apps/admin/settings/forms.py


+ 2 - 2
misago/core/admin/settings/views.py → misago/apps/admin/settings/views.py

@@ -6,8 +6,8 @@ from misago.forms import Form, FormLayout, FormFields
 from misago.messages import Message
 from misago.search import SearchQuery, SearchException
 from misago.models import SettingsGroup, Setting
-from misago.core.views import error404
-from misago.core.admin.settings.forms import SearchForm
+from misago.apps.views import error404
+from misago.apps.admin.settings.forms import SearchForm
 
 def settings(request, group_id=None, group_slug=None):
     # Load groups and find selected group

+ 0 - 0
misago/core/front/__init__.py → misago/apps/admin/stats/__init__.py


+ 0 - 0
misago/core/admin/stats/forms.py → misago/apps/admin/stats/forms.py


+ 2 - 2
misago/core/admin/stats/views.py → misago/apps/admin/stats/views.py

@@ -8,8 +8,8 @@ from django.utils import timezone
 from django.utils.translation import ugettext as _
 from misago.forms import FormLayout
 from misago.messages import Message
-from misago.core.admin.stats.forms import GenerateStatisticsForm
-from misago.core.views import error404
+from misago.apps.admin.stats.forms import GenerateStatisticsForm
+from misago.apps.views import error404
 
 def form(request):
     """

+ 1 - 1
misago/core/admin/team.py → misago/apps/admin/team.py

@@ -1,6 +1,6 @@
 from django.utils.translation import ugettext as _
 from misago.admin import site
-from misago.core.admin.widgets import ListWidget
+from misago.apps.admin.widgets import ListWidget
 
 class List(ListWidget):
     admin = site.get_action('team')

+ 0 - 0
misago/core/admin/widgets.py → misago/apps/admin/widgets.py


+ 0 - 0
misago/core/signin/__init__.py → misago/apps/front/__init__.py


+ 0 - 0
misago/core/front/index.py → misago/apps/front/index.py


+ 0 - 0
misago/core/front/readall.py → misago/apps/front/readall.py


+ 0 - 0
misago/apps/signin/__init__.py


+ 0 - 0
misago/core/signin/forms.py → misago/apps/signin/forms.py


+ 2 - 2
misago/core/signin/urls.py → misago/apps/signin/urls.py

@@ -1,13 +1,13 @@
 from django.conf.urls import patterns, url
 from misago.admin import ADMIN_PATH
 
-urlpatterns = patterns('misago.core.signin.views',
+urlpatterns = patterns('misago.apps.signin.views',
     url(r'^signin/$', 'signin', name="sign_in"),
     url(r'^signout/$', 'signout', name="sign_out"),
 )
 
 # Include admin patterns
 if ADMIN_PATH:
-    urlpatterns += patterns('misago.core.signin.views',
+    urlpatterns += patterns('misago.apps.signin.views',
         url(r'^' + ADMIN_PATH + 'signout/$', 'signout', name="admin_sign_out"),
     )

+ 1 - 1
misago/core/signin/views.py → misago/apps/signin/views.py

@@ -11,7 +11,7 @@ from misago.auth import AuthException, auth_admin, auth_forum, sign_user_in
 from misago.decorators import (block_authenticated, block_banned, block_crawlers,
                             block_guest, block_jammed, check_csrf)
 from misago.models import SignInAttempt, Token
-from misago.core.signin.forms import SignInForm
+from misago.apps.signin.forms import SignInForm
 from misago.utils.strings import random_string
 
 @block_crawlers

+ 0 - 0
misago/core/views.py → misago/apps/views.py


+ 1 - 1
misago/decorators.py

@@ -1,6 +1,6 @@
 from django.utils.translation import ugettext as _
 from misago.acl.exceptions import ACLError403, ACLError404
-from misago.core.views import error403, error404, error_banned
+from misago.apps.views import error403, error404, error_banned
 
 def acl_errors(f):
     def decorator(*args, **kwargs):

+ 2 - 2
misago/firewalls.py

@@ -2,8 +2,8 @@ from django.conf import settings
 from django.utils.translation import ugettext_lazy as _
 from misago.admin import ADMIN_PATH
 from misago.messages import Message
-from misago.core.views import error403, error404
-from misago.core.signin.views import signin
+from misago.apps.views import error403, error404
+from misago.apps.signin.views import signin
 
 class FirewallForum(object):
     admin = False

+ 1 - 1
misago/models/forummodel.py

@@ -169,7 +169,7 @@ class Forum(MPTTModel):
                 perms = role.permissions
                 try:
                     perms['forums'][self.pk] = perms['forums'][target.pk]
-                    role.set_permissions(perms)
+                    role.permissions = perms
                     role.save(force_update=True)
                 except KeyError:
                     pass

+ 10 - 10
misago/settings_base.py

@@ -116,20 +116,20 @@ PERMISSION_PROVIDERS = (
 
 # List of UserCP extensions
 USERCP_EXTENSIONS = (
-    'misago.core.front.usercp.options',
-    'misago.core.front.usercp.avatar',
-    'misago.core.front.usercp.signature',
-    'misago.core.front.usercp.credentials',
-    'misago.core.front.usercp.username',
+    'misago.apps.front.usercp.options',
+    'misago.apps.front.usercp.avatar',
+    'misago.apps.front.usercp.signature',
+    'misago.apps.front.usercp.credentials',
+    'misago.apps.front.usercp.username',
 )
 
 # List of User Profile extensions
 PROFILE_EXTENSIONS = (
-    'misago.core.front.profiles.posts',
-    'misago.core.front.profiles.threads',
-    'misago.core.front.profiles.follows',
-    'misago.core.front.profiles.followers',
-    'misago.core.front.profiles.details',
+    'misago.apps.front.profiles.posts',
+    'misago.apps.front.profiles.threads',
+    'misago.apps.front.profiles.follows',
+    'misago.apps.front.profiles.followers',
+    'misago.apps.front.profiles.details',
 )
 
 # List of Markdown Extensions

+ 5 - 5
misago/urls.py

@@ -4,16 +4,16 @@ from django.contrib.staticfiles.urls import staticfiles_urlpatterns
 from misago.admin import ADMIN_PATH, site
 
 # Include frontend patterns
-urlpatterns = patterns('misago.core.front',
+urlpatterns = patterns('misago.apps.front',
     url(r'^$', 'index.index', name="index"),
     url(r'^read-all/$', 'readall.read_all', name="read_all"),
 )
 
 # Include shared Sign-In action
 urlpatterns += patterns('',
-    (r'^', include('misago.core.signin.urls')),
+    (r'^', include('misago.apps.signin.urls')),
     # Remove after ACP was refactored
-    url(r'^users/(?P<username>\w+)-(?P<user>\d+)/$', 'misago.core.admin.adminindex.todo', name="user"),    
+    url(r'^users/(?P<username>\w+)-(?P<user>\d+)/$', 'misago.apps.admin.adminindex.todo', name="user"),    
 )
 
 """
@@ -49,5 +49,5 @@ if settings.DEBUG:
     )
 
 # Set error handlers
-handler403 = 'misago.core.views.error403'
-handler404 = 'misago.core.views.error404'
+handler403 = 'misago.apps.views.error403'
+handler404 = 'misago.apps.views.error404'