    def send_html_mail(subject, text_msg, html_msg, sender, recipient, 
            fail_silently=False, auth_user=None, auth_password=None, connection=None):
def send_mass_html_mail(emails, *args, **kwargs):
def get_image_field_class():
def get_image_field_full_name():
def get_user_model():
def get_user_model_path():
def get_username_field():
def get_atomic_func():
def get_paginator_class():
            def querystring(self):
def is_installed(app_name):
def get_related_model_class(parent_model, field_name):
def slugify(text):
def is_authenticated(user):
def is_anonymous(user):
        class PageRepr(int): PYBB_TOPIC_PAGE_SIZE = getattr(settings, 'PYBB_TOPIC_PAGE_SIZE', 10)
PYBB_FORUM_PAGE_SIZE = getattr(settings, 'PYBB_FORUM_PAGE_SIZE', 20)
PYBB_AVATAR_WIDTH = getattr(settings, 'PYBB_AVATAR_WIDTH', 80)
PYBB_AVATAR_HEIGHT = getattr(settings, 'PYBB_AVATAR_HEIGHT', 80)
PYBB_MAX_AVATAR_SIZE = getattr(settings, 'PYBB_MAX_AVATAR_SIZE', 1024 * 50)
PYBB_DEFAULT_TIME_ZONE = getattr(settings, 'PYBB_DEFAULT_TIME_ZONE', 3)
PYBB_SIGNATURE_MAX_LENGTH = getattr(settings, 'PYBB_SIGNATURE_MAX_LENGTH', 1024)
PYBB_SIGNATURE_MAX_LINES = getattr(settings, 'PYBB_SIGNATURE_MAX_LINES', 3)
PYBB_DEFAULT_MARKUP = getattr(settings, 'PYBB_DEFAULT_MARKUP', 'bbcode')
PYBB_FREEZE_FIRST_POST = getattr(settings, 'PYBB_FREEZE_FIRST_POST', False)
PYBB_ATTACHMENT_SIZE_LIMIT = getattr(settings, 'PYBB_ATTACHMENT_SIZE_LIMIT', 1024 * 1024)
PYBB_ATTACHMENT_ENABLE = getattr(settings, 'PYBB_ATTACHMENT_ENABLE', False)
PYBB_ATTACHMENT_UPLOAD_TO = getattr(settings, 'PYBB_ATTACHMENT_UPLOAD_TO', os.path.join('pybb_upload', 'attachments'))
PYBB_DEFAULT_AVATAR_URL = getattr(settings, 'PYBB_DEFAULT_AVATAR_URL',
                                  getattr(settings, 'STATIC_URL', '') + 'pybb/img/default_avatar.jpg')
PYBB_DEFAULT_TITLE = getattr(settings, 'PYBB_DEFAULT_TITLE', 'PYBB Powered Forum')
PYBB_SMILES_PREFIX = getattr(settings, 'PYBB_SMILES_PREFIX', 'pybb/emoticons/')
PYBB_SMILES = getattr(settings, 'PYBB_SMILES', {
    '&gt;_&lt;': 'angry.png',
    ':.(': 'cry.png',
    'o_O': 'eyes.png',
    '[]_[]': 'geek.png',
    '8)': 'glasses.png',
    ':D': 'lol.png',
    ':(': 'sad.png',
    ':O': 'shok.png',
    '-_-': 'shy.png',
    ':)': 'smile.png',
    ':P': 'tongue.png',
    ';)': 'wink.png'
})
PYBB_NICE_URL = getattr(settings, 'PYBB_NICE_URL', False)
PYBB_NICE_URL_PERMANENT_REDIRECT = getattr(settings, 'PYBB_NICE_URL_PERMANENT_REDIRECT', True)
PYBB_NICE_URL_SLUG_DUPLICATE_LIMIT = getattr(settings, 'PYBB_NICE_URL_SLUG_DUPLICATE_LIMIT', 100)
PYBB_MARKUP_ENGINES_PATHS = getattr(settings, 'PYBB_MARKUP_ENGINES_PATHS', {
    'bbcode': 'pybb.markup.bbcode.BBCodeParser',
    'markdown': 'pybb.markup.markdown.MarkdownParser'})
    PYBB_MARKUP_ENGINES = PYBB_MARKUP_ENGINES_PATHS
    PYBB_MARKUP_ENGINES = getsetting_with_deprecation_check(settings, 'PYBB_MARKUP_ENGINES')
    PYBB_QUOTE_ENGINES = PYBB_MARKUP_ENGINES_PATHS
    PYBB_QUOTE_ENGINES = getsetting_with_deprecation_check(settings, 'PYBB_QUOTE_ENGINES')
PYBB_MARKUP = getattr(settings, 'PYBB_MARKUP', None)
        PYBB_MARKUP = None
        PYBB_MARKUP = 'bbcode'
PYBB_TEMPLATE = getattr(settings, 'PYBB_TEMPLATE', "base.html")
PYBB_TEMPLATE_MAIL_TXT = getattr(settings, 
                                 'PYBB_TEMPLATE_MAIL_TXT', 
                                 'pybb/mail_templates/base.html')
PYBB_TEMPLATE_MAIL_HTML = getattr(settings, 
                                  'PYBB_TEMPLATE_MAIL_HTML', 
                                  'pybb/mail_templates/base-html.html')
PYBB_DEFAULT_AUTOSUBSCRIBE = getattr(settings, 'PYBB_DEFAULT_AUTOSUBSCRIBE', True)
PYBB_ENABLE_ANONYMOUS_POST = getattr(settings, 'PYBB_ENABLE_ANONYMOUS_POST', False)
PYBB_ANONYMOUS_USERNAME = getattr(settings, 'PYBB_ANONYMOUS_USERNAME', 'Anonymous')
PYBB_ANONYMOUS_VIEWS_CACHE_BUFFER = getattr(settings, 'PYBB_ANONYMOUS_VIEWS_CACHE_BUFFER', 100)
PYBB_DISABLE_SUBSCRIPTIONS = getattr(settings, 'PYBB_DISABLE_SUBSCRIPTIONS', False)
PYBB_DISABLE_NOTIFICATIONS = getattr(settings, 'PYBB_DISABLE_NOTIFICATIONS', False)
PYBB_PREMODERATION = getattr(settings, 'PYBB_PREMODERATION', False)
    PYBB_BODY_CLEANERS = ['pybb.markup.base.rstrip_str', 'pybb.markup.base.filter_blanks']
    PYBB_BODY_CLEANERS = getsetting_with_deprecation_check(settings, 'PYBB_BODY_CLEANERS')
PYBB_BODY_VALIDATOR = getattr(settings, 'PYBB_BODY_VALIDATOR', None)
PYBB_POLL_MAX_ANSWERS = getattr(settings, 'PYBB_POLL_MAX_ANSWERS', 10)
PYBB_AUTO_USER_PERMISSIONS = getattr(settings, 'PYBB_AUTO_USER_PERMISSIONS', True)
PYBB_USE_DJANGO_MAILER = getattr(settings, 'PYBB_USE_DJANGO_MAILER', False)
PYBB_PERMISSION_HANDLER = getattr(settings, 'PYBB_PERMISSION_HANDLER', 'pybb.permissions.DefaultPermissionHandler')
PYBB_PROFILE_RELATED_NAME = getattr(settings, 'PYBB_PROFILE_RELATED_NAME', 'pybb_profile')
PYBB_ENABLE_ADMIN_POST_FORM = getattr(settings, 'PYBB_ENABLE_ADMIN_POST_FORM', True)
PYBB_ALLOW_DELETE_OWN_POST = getattr(settings, 'PYBB_ALLOW_DELETE_OWN_POST', True)
def getsetting_with_deprecation_check(all_settings, setting_name):  # pragma: no cover
def bbcode(s):  # pragma: no cover
def markdown(s):  # pragma: no cover
def _render_quote(name, value, options, parent, context):  # pragma: no cover
def smile_it(s):  # pragma: no cover _MARKUP_ENGINES = {}
_MARKUP_ENGINES_FORMATTERS = {}
_MARKUP_ENGINES_QUOTERS = {}
def resolve_class(name):
def resolve_function(path):
def get_markup_engine(name=None):
def _get_markup_formatter(name=None):
def _get_markup_quoter(name=None):
def get_body_cleaner(name):
def unescape(text):
def get_pybb_profile(user):
def get_pybb_profile_model():
def build_cache_key(key_name, **kwargs):
    def __init__(self, to, *args, **kwargs):
    def deconstruct(self, *args, **kwargs):
    def __call__(self, instance, filename):
class FilePathGenerator(object): class DefaultPermissionHandler(object):
    def filter_categories(self, user, qs):
    def may_view_category(self, user, category):
    def filter_forums(self, user, qs):
    def may_view_forum(self, user, forum):
    def may_create_topic(self, user, forum):
    def filter_topics(self, user, qs):
    def may_view_topic(self, user, topic):
    def may_moderate_topic(self, user, topic):
    def may_close_topic(self, user, topic):
    def may_open_topic(self, user, topic):
    def may_stick_topic(self, user, topic):
    def may_unstick_topic(self, user, topic):
    def may_vote_in_topic(self, user, topic):
    def may_create_post(self, user, topic):
    def may_post_as_admin(self, user):
    def may_subscribe_topic(self, user, topic):
    def filter_posts(self, user, qs):
    def may_view_post(self, user, post):
    def may_moderate_post(self, user, post):
    def may_edit_post(self, user, post):
    def may_delete_post(self, user, post):
    def may_admin_post(self, user, post):
    def may_block_user(self, user, user_to_block):
    def may_attach_files(self, user):
    def may_create_poll(self, user):
    def may_edit_topic_slug(self, user):
    def may_change_forum(self, user, forum):
    def may_manage_moderators(self, user): class Topic(models.Model):
    POLL_TYPE_NONE = 0
    POLL_TYPE_SINGLE = 1
    POLL_TYPE_MULTIPLE = 2

    POLL_TYPE_CHOICES = (
        (POLL_TYPE_NONE, _('None')),
        (POLL_TYPE_SINGLE, _('Single answer')),
        (POLL_TYPE_MULTIPLE, _('Multiple answers')),
    )

    forum = models.ForeignKey(Forum, on_delete=models.CASCADE, related_name='topics', verbose_name=_('Forum'))
    name = models.CharField(_('Subject'), max_length=255)
    created = models.DateTimeField(_('Created'), null=True, db_index=True)
    updated = models.DateTimeField(_('Updated'), null=True, db_index=True)
    user = models.ForeignKey(get_user_model_path(), on_delete=models.CASCADE, verbose_name=_('User'))
    views = models.IntegerField(_('Views count'), blank=True, default=0)
    sticky = models.BooleanField(_('Sticky'), default=False)
    closed = models.BooleanField(_('Closed'), default=False)
    subscribers = models.ManyToManyField(get_user_model_path(), related_name='subscriptions',
                                         verbose_name=_('Subscribers'), blank=True)
    post_count = models.IntegerField(_('Post count'), blank=True, default=0)
    readed_by = models.ManyToManyField(get_user_model_path(), through='TopicReadTracker', related_name='readed_topics')
    on_moderation = models.BooleanField(_('On moderation'), default=False)
    poll_type = models.IntegerField(_('Poll type'), choices=POLL_TYPE_CHOICES, default=POLL_TYPE_NONE)
    poll_question = models.TextField(_('Poll question'), blank=True, null=True)
    slug = models.SlugField(verbose_name=_("Slug"), max_length=255)

    class Meta(object):
        ordering = ['-created']
        verbose_name = _('Topic')
        verbose_name_plural = _('Topics')
        unique_together = ('forum', 'slug')

    def __str__(self):
        return self.name

    @cached_property
    def head(self):
        try:
            return self.posts.all().order_by('created', 'id')[0]
        except IndexError:
            return None

    @cached_property
    def last_post(self):
        try:
            return self.posts.order_by('-created', '-id').select_related('user')[0]
        except IndexError:
            return None

    def get_absolute_url(self):
        if defaults.PYBB_NICE_URL:
            return reverse('pybb:topic', kwargs={'slug': self.slug, 'forum_slug': self.forum.slug, 'category_slug': self.forum.category.slug})
        return reverse('pybb:topic', kwargs={'pk': self.id})

    def save(self, *args, **kwargs):
        if self.id is None:
            self.created = self.updated = tznow()

        forum_changed = False
        old_topic = None
        if self.id is not None:
            old_topic = Topic.objects.get(id=self.id)
            if self.forum != old_topic.forum:
                forum_changed = True

        super(Topic, self).save(*args, **kwargs)

        if forum_changed:
            old_topic.forum.update_counters()
            self.forum.update_counters()

    def delete(self, using=None):
        super(Topic, self).delete(using)
        self.forum.update_counters()

    def update_counters(self):
        self.post_count = self.posts.count()
        # force cache overwrite to get the real latest updated post
        if hasattr(self, 'last_post'):
            del self.last_post
        if self.last_post:
            self.updated = self.last_post.updated or self.last_post.created
        self.save()

    def get_parents(self):
        """
        Used in templates for breadcrumb building
        """
        parents = self.forum.get_parents()
        parents.append(self.forum)
        return parents

    def poll_votes(self):
        if self.poll_type != self.POLL_TYPE_NONE:
            return PollAnswerUser.objects.filter(poll_answer__topic=self).count()
        else:
            return None class Post(RenderableItem):
    topic = models.ForeignKey(Topic, on_delete=models.CASCADE, related_name='posts', verbose_name=_('Topic'))
    user = models.ForeignKey(get_user_model_path(), on_delete=models.CASCADE, related_name='posts', verbose_name=_('User'))
    created = models.DateTimeField(_('Created'), blank=True, db_index=True)
    updated = models.DateTimeField(_('Updated'), blank=True, null=True, db_index=True)
    user_ip = models.GenericIPAddressField(_('User IP'), blank=True, null=True, default='0.0.0.0')
    on_moderation = models.BooleanField(_('On moderation'), default=False)

    class Meta(object):
        ordering = ['created']
        verbose_name = _('Post')
        verbose_name_plural = _('Posts')

    def summary(self):
        limit = 50
        tail = len(self.body) > limit and '...' or ''
        return self.body[:limit] + tail

    def __str__(self):
        return self.summary()

    @cached_property
    def is_topic_head(self):
        return self.pk and self.topic.head.pk == self.pk

    def save(self, *args, **kwargs):
        created_at = tznow()
        if self.created is None:
            self.created = created_at
        self.render()

        new = self.pk is None

        topic_changed = False
        old_post = None
        if not new:
            old_post = Post.objects.get(pk=self.pk)
            if old_post.topic != self.topic:
                topic_changed = True

        super(Post, self).save(*args, **kwargs)

        # If post is topic head and moderated, moderate topic too
        if self.topic.head == self and not self.on_moderation and self.topic.on_moderation:
            self.topic.on_moderation = False

        self.topic.update_counters()
        self.topic.forum.update_counters()

        if topic_changed:
            old_post.topic.update_counters()
            old_post.topic.forum.update_counters()

    def get_absolute_url(self):
        return reverse('pybb:post', kwargs={'pk': self.id})

    def delete(self, *args, **kwargs):
        self_id = self.id
        head_post_id = self.topic.posts.order_by('created', 'id')[0].id

        if self_id == head_post_id:
            self.topic.delete()
        else:
            super(Post, self).delete(*args, **kwargs)
            self.topic.update_counters()
            self.topic.forum.update_counters()

    def get_parents(self):
        """
        Used in templates for breadcrumb building
        """
        return self.topic.forum.category, self.topic.forum, self.topic, class Attachment(models.Model):
    class Meta(object):
        verbose_name = _('Attachment')
        verbose_name_plural = _('Attachments')

    post = models.ForeignKey(Post, on_delete=models.CASCADE, verbose_name=_('Post'), related_name='attachments')
    size = models.IntegerField(_('Size'))
    file = models.FileField(_('File'),
                            upload_to=FilePathGenerator(to=defaults.PYBB_ATTACHMENT_UPLOAD_TO))

    def save(self, *args, **kwargs):
        self.size = self.file.size
        super(Attachment, self).save(*args, **kwargs)

    def size_display(self):
        size = self.size
        if size < 1024:
            return '%db' % size
        elif size < 1024 * 1024:
            return '%dKb' % int(size / 1024)
        else:
            return '%.2fMb' % (size / float(1024 * 1024)) class PollAnswer(models.Model):
    topic = models.ForeignKey(Topic, on_delete=models.CASCADE, related_name='poll_answers', verbose_name=_('Topic'))
    text = models.CharField(max_length=255, verbose_name=_('Text'))

    class Meta:
        verbose_name = _('Poll answer')
        verbose_name_plural = _('Polls answers')

    def __str__(self):
        return self.text

    def votes(self):
        return self.users.count()

    def votes_percent(self):
        topic_votes = self.topic.poll_votes()
        if topic_votes > 0:
            return 1.0 * self.votes() / topic_votes * 100
        else:
            return 0 class ForumSubscription(models.Model):

    TYPE_NOTIFY = 1
    TYPE_SUBSCRIBE = 2
    TYPE_CHOICES = (
        (TYPE_NOTIFY, _('be notified only when a new topic is added')),
        (TYPE_SUBSCRIBE, _('be auto-subscribed to topics')),
    )

    user = models.ForeignKey(get_user_model_path(), on_delete=models.CASCADE,
        related_name='forum_subscriptions+', verbose_name=_('Subscriber'))
    forum = models.ForeignKey(Forum, on_delete=models.CASCADE,
        related_name='subscriptions+', verbose_name=_('Forum'))
    type = models.PositiveSmallIntegerField(
        _('Subscription type'), choices=TYPE_CHOICES,
        help_text=_((
            'The auto-subscription works like you manually subscribed to watch each topic :\n'
            'you will be notified when a topic will receive an answer. \n'
            'If you choose to be notified only when a new topic is added. It means'
            'you will be notified only once when the topic is created : '
            'you won\'t be notified for the answers.'
        )), )

    class Meta(object):
        verbose_name = _('Subscription to forum')
        verbose_name_plural = _('Subscriptions to forums')
        unique_together = ('user', 'forum',)

    def __str__(self):
        return '%(user)s\'s subscription to "%(forum)s"' % {'user': self.user,
                                                            'forum': self.forum}

    def save(self, all_topics=False, **kwargs):
        if all_topics and self.type == self.TYPE_SUBSCRIBE:
            old = None if not self.pk else ForumSubscription.objects.get(pk=self.pk)
            if not old or old.type != self.type :
                topics = Topic.objects.filter(forum=self.forum).exclude(subscribers=self.user)
                self.user.subscriptions.add(*topics)
        super(ForumSubscription, self).save(**kwargs)

    def delete(self, all_topics=False, **kwargs):
        if all_topics:
            topics = Topic.objects.filter(forum=self.forum, subscribers=self.user)
            self.user.subscriptions.remove(*topics)
        super(ForumSubscription, self).delete(**kwargs) class Category(models.Model):
    name = models.CharField(_('Name'), max_length=80)
    position = models.IntegerField(_('Position'), blank=True, default=0)
    hidden = models.BooleanField(_('Hidden'), default=False,
                                 help_text=_('If checked, this category will be visible only for staff'))
    slug = models.SlugField(_("Slug"), max_length=255, unique=True)

    class Meta(object):
        ordering = ['position']
        verbose_name = _('Category')
        verbose_name_plural = _('Categories')

    def __str__(self):
        return self.name

    def forum_count(self):
        return self.forums.all().count()

    def get_absolute_url(self):
        if defaults.PYBB_NICE_URL:
            return reverse('pybb:category', kwargs={'slug': self.slug, })
        return reverse('pybb:category', kwargs={'pk': self.id})

    @property
    def topics(self):
        return Topic.objects.filter(forum__category=self).select_related()

    @property
    def posts(self):
        return Post.objects.filter(topic__forum__category=self).select_related() class Forum(models.Model):
    category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name='forums', verbose_name=_('Category'))
    parent = models.ForeignKey('self', on_delete=models.CASCADE, related_name='child_forums', verbose_name=_('Parent forum'),
                               blank=True, null=True)
    name = models.CharField(_('Name'), max_length=80)
    position = models.IntegerField(_('Position'), blank=True, default=0)
    description = models.TextField(_('Description'), blank=True)
    moderators = models.ManyToManyField(get_user_model_path(), blank=True, verbose_name=_('Moderators'))
    updated = models.DateTimeField(_('Updated'), blank=True, null=True)
    post_count = models.IntegerField(_('Post count'), blank=True, default=0)
    topic_count = models.IntegerField(_('Topic count'), blank=True, default=0)
    hidden = models.BooleanField(_('Hidden'), default=False)
    readed_by = models.ManyToManyField(get_user_model_path(), through='ForumReadTracker', related_name='readed_forums')
    headline = models.TextField(_('Headline'), blank=True, null=True)
    slug = models.SlugField(verbose_name=_("Slug"), max_length=255)

    class Meta(object):
        ordering = ['position']
        verbose_name = _('Forum')
        verbose_name_plural = _('Forums')
        unique_together = ('category', 'slug')

    def __str__(self):
        return self.name

    def update_counters(self):
        self.topic_count = Topic.objects.filter(forum=self).count()
        if self.topic_count:
            posts = Post.objects.filter(topic__forum_id=self.id)
            self.post_count = posts.count()
            if self.post_count:
                try:
                    last_post = posts.order_by('-created', '-id')[0]
                    self.updated = last_post.updated or last_post.created
                except IndexError:
                    pass
        else:
            self.post_count = 0
        self.save()

    def get_absolute_url(self):
        if defaults.PYBB_NICE_URL:
            return reverse('pybb:forum', kwargs={'slug': self.slug, 'category_slug': self.category.slug})
        return reverse('pybb:forum', kwargs={'pk': self.id})

    @property
    def posts(self):
        return Post.objects.filter(topic__forum=self).select_related()

    @cached_property
    def last_post(self):
        try:
            return self.posts.order_by('-created', '-id')[0]
        except IndexError:
            return None

    def get_parents(self):
        """
        Used in templates for breadcrumb building
        """
        parents = [self.category]
        parent = self.parent
        while parent is not None:
            parents.insert(1, parent)
            parent = parent.parent
        return parents def create_or_check_slug(instance, model, **extra_filters):
    """
    returns a unique slug

    :param instance : target instance
    :param model: needed as instance._meta.model is available since django 1.6
    :param extra_filters: filters needed for Forum and Topic for their unique_together field
    """
    initial_slug = instance.slug or slugify(instance.name)
    count = -1
    last_count_len = 0
    slug_is_not_unique = True
    while slug_is_not_unique:
        count += 1

        if count >= defaults.PYBB_NICE_URL_SLUG_DUPLICATE_LIMIT:
            msg = _('After %(limit)s attemps, there is not any unique slug value for "%(slug)s"')
            raise ValidationError(msg % {'limit': defaults.PYBB_NICE_URL_SLUG_DUPLICATE_LIMIT,
                                         'slug': initial_slug})

        count_len = len(str(count))

        if last_count_len != count_len:
            last_count_len = count_len
            filters = {'slug__startswith': initial_slug[:(254-count_len)], }
            if extra_filters:
                filters.update(extra_filters)
            objs = model.objects.filter(**filters).exclude(pk=instance.pk)
            slug_list = [obj.slug for obj in objs]

        if count == 0:
            slug = initial_slug
        else:
            slug = '%s-%d' % (initial_slug[:(254-count_len)], count)
        slug_is_not_unique = slug in slug_list

    return slug
import re
import inspect
import django
from django import forms
from django.core.exceptions import FieldError, PermissionDenied
from django.forms.models import inlineformset_factory, BaseInlineFormSet
from django.utils.decorators import method_decorator
from django.utils.text import Truncator
from django.utils.translation import ugettext, ugettext_lazy
from django.utils.timezone import now as tznow
from django.utils.translation import ugettext as _
from pybb import compat, defaults, util, permissions
from pybb.models import Topic, Post, Attachment, PollAnswer, \
    ForumSubscription, Category, Forum, create_or_check_slug
        if args:
            kwargs.update(dict(zip(inspect.getargspec(forms.ModelForm.__init__)[0][1:], args)))
        if 'instance' in kwargs and kwargs['instance']:
            kwargs.setdefault('initial', {}).update({'login': getattr(kwargs['instance'].user, username_field)})
        super(AdminPostForm, self).__init__(**kwargs)

    def save(self, *args, **kwargs):
        try:
            self.user = User.objects.filter(**{username_field: self.cleaned_data['login']}).get()
        except User.DoesNotExist:
            if username_field != 'email':
                create_data = {username_field: self.cleaned_data['login'],
                               'email': '%s@example.com' % self.cleaned_data['login'],
                               'is_staff': False}
            else:
                create_data = {'email': '%s@example.com' % self.cleaned_data['login'],
                               'is_staff': False}
            self.user = User.objects.create(**create_data)
        return super(AdminPostForm, self).save(*args, **kwargs)


try:
    class EditProfileForm(forms.ModelForm):
        class Meta(object):
            model = util.get_pybb_profile_model()
            fields = ['signature', 'time_zone', 'language', 'show_signatures', 'avatar']

        def __init__(self, *args, **kwargs):
            super(EditProfileForm, self).__init__(*args, **kwargs)
            self.fields['signature'].widget = forms.Textarea(attrs={'rows': 2, 'cols:': 60})

        def clean_avatar(self):
            if self.cleaned_data['avatar'] and (self.cleaned_data['avatar'].size > defaults.PYBB_MAX_AVATAR_SIZE):
                forms.ValidationError(ugettext('Avatar is too large, max size: %s bytes' %
                                               defaults.PYBB_MAX_AVATAR_SIZE))
            return self.cleaned_data['avatar']

        def clean_signature(self):
            value = self.cleaned_data['signature'].strip()
            if len(re.findall(r'\n', value)) > defaults.PYBB_SIGNATURE_MAX_LINES:
                raise forms.ValidationError('Number of lines is limited to %d' % defaults.PYBB_SIGNATURE_MAX_LINES)
            if len(value) > defaults.PYBB_SIGNATURE_MAX_LENGTH:
                raise forms.ValidationError('Length of signature is limited to %d' % defaults.PYBB_SIGNATURE_MAX_LENGTH)
            return value
except FieldError:
    pass


class UserSearchForm(forms.Form):
    query = forms.CharField(required=False, label='')

    def filter(self, qs):
        if self.is_valid():
            query = self.cleaned_data['query']
            return qs.filter(**{'%s__contains' % username_field: query})
        else:
            return qs


class PollForm(forms.Form):
    def __init__(self, topic, *args, **kwargs):
        self.topic = topic

        super(PollForm, self).__init__(*args, **kwargs)

        qs = PollAnswer.objects.filter(topic=topic)
        if topic.poll_type == Topic.POLL_TYPE_SINGLE:
            self.fields['answers'] = forms.ModelChoiceField(
                label='', queryset=qs, empty_label=None,
                widget=forms.RadioSelect())
        elif topic.poll_type == Topic.POLL_TYPE_MULTIPLE:
            self.fields['answers'] = forms.ModelMultipleChoiceField(
                label='', queryset=qs,
                widget=forms.CheckboxSelectMultiple())

    def clean_answers(self):
        answers = self.cleaned_data['answers']
        if self.topic.poll_type == Topic.POLL_TYPE_SINGLE:
            return [answers]
        else:
            return answers


class ForumSubscriptionForm(forms.Form):
    def __init__(self, user, forum, instance=None, *args, **kwargs):
        super(ForumSubscriptionForm, self).__init__(*args, **kwargs)
        self.user = user
        self.forum = forum
        self.instance = instance

        type_choices = list(ForumSubscription.TYPE_CHOICES)
        if instance :
            type_choices.append(
                ('unsubscribe', _('be unsubscribe from this forum')))
            type_initial = instance.type
        else:
            type_initial = ForumSubscription.TYPE_NOTIFY
        self.fields['type'] = forms.ChoiceField(
            label=_('You want to'), choices=type_choices, initial=type_initial,
            widget=forms.RadioSelect())

        topic_choices = (
            ('new', _('only new topics')),
            ('all', _('all topics of the forum')),
        )
        self.fields['topics'] = forms.ChoiceField(
            label=_('Concerned topics'), choices=topic_choices,
            initial=topic_choices[0][0], widget=forms.RadioSelect())

    def process(self):
        """
        saves or deletes the ForumSubscription's instance
        """
        action = self.cleaned_data.get('type')
        all_topics = self.cleaned_data.get('topics') == 'all'
        if action == 'unsubscribe':
            self.instance.delete(all_topics=all_topics)
            return 'delete-all' if all_topics else 'delete'
        else:
            if not self.instance:

                self.instance = ForumSubscription()