Browse Source

Merge branch 'master' into new-cli

sh4nks 8 years ago
parent
commit
7726bef567

+ 5 - 0
.gitignore

@@ -54,3 +54,8 @@ flaskbb/static/emoji/*
 bower_components
 node_modules
 .directory
+
+# Ignore plugins
+flaskbb/plugins/*
+!flaskbb/plugins/__init__.py
+!flaskbb/plugins/portal/

+ 1 - 1
docs/installation.rst

@@ -3,7 +3,7 @@ Installation
 
 -  `Basic Setup <#basic-setup>`_
 -  `Configuration <#configuration>`_
--  `Deplyoing <#deploying>`_
+-  `Deploying <#deploying>`_
 
 
 

+ 0 - 2
flaskbb/_compat.py

@@ -16,7 +16,6 @@ if not PY2:     # pragma: no cover
     iterkeys = lambda d: iter(d.keys())
     itervalues = lambda d: iter(d.values())
     iteritems = lambda d: iter(d.items())
-    max_integer = sys.maxsize
 else:           # pragma: no cover
     text_type = unicode
     string_types = (str, unicode)
@@ -26,7 +25,6 @@ else:           # pragma: no cover
     iterkeys = lambda d: d.iterkeys()
     itervalues = lambda d: d.itervalues()
     iteritems = lambda d: d.iteritems()
-    max_integer = sys.maxint
 
 
 def to_bytes(text):

+ 7 - 4
flaskbb/auth/forms.py

@@ -17,7 +17,7 @@ from flask_babelplus import lazy_gettext as _
 
 from flaskbb.user.models import User
 from flaskbb.utils.helpers import time_utcnow
-from flaskbb.utils.recaptcha import RecaptchaField
+from flaskbb.utils.fields import RecaptchaField
 
 USERNAME_RE = r'^[\w.+-]+$'
 is_username = regexp(USERNAME_RE,
@@ -32,13 +32,15 @@ class LoginForm(FlaskForm):
     password = PasswordField(_("Password"), validators=[
         DataRequired(message=_("Please enter your password."))])
 
-    recaptcha = RecaptchaField(_("Captcha"))
-
     remember_me = BooleanField(_("Remember me"), default=False)
 
     submit = SubmitField(_("Login"))
 
 
+class LoginRecaptchaForm(LoginForm):
+    recaptcha = RecaptchaField(_("Captcha"))
+
+
 class RegisterForm(FlaskForm):
     username = StringField(_("Username"), validators=[
         DataRequired(message=_("A valid username is required.")),
@@ -58,7 +60,8 @@ class RegisterForm(FlaskForm):
 
     language = SelectField(_('Language'))
 
-    accept_tos = BooleanField(_("I accept the Terms of Service"), default=True)
+    accept_tos = BooleanField(_("I accept the Terms of Service"), validators=[
+        DataRequired(message=_("Please accept the TOS."))], default=True)
 
     submit = SubmitField(_("Register"))
 

+ 8 - 4
flaskbb/auth/views.py

@@ -21,9 +21,10 @@ from flaskbb.utils.helpers import (render_template, redirect_or_next,
                                    format_timedelta)
 from flaskbb.email import send_reset_token, send_activation_token
 from flaskbb.exceptions import AuthenticationError
-from flaskbb.auth.forms import (LoginForm, ReauthForm, ForgotPasswordForm,
-                                ResetPasswordForm, RegisterForm,
-                                AccountActivationForm, RequestActivationForm)
+from flaskbb.auth.forms import (LoginForm, LoginRecaptchaForm, ReauthForm,
+                                ForgotPasswordForm, ResetPasswordForm,
+                                RegisterForm, AccountActivationForm,
+                                RequestActivationForm)
 from flaskbb.user.models import User
 from flaskbb.fixtures.settings import available_languages
 from flaskbb.utils.settings import flaskbb_config
@@ -85,7 +86,10 @@ def login():
         stats_diff = flaskbb_config["AUTH_REQUESTS"] - window_stats[1]
         login_recaptcha = stats_diff >= flaskbb_config["LOGIN_RECAPTCHA"]
 
-    form = LoginForm(request.form)
+    form = LoginForm()
+    if login_recaptcha and flaskbb_config["RECAPTCHA_ENABLED"]:
+        form = LoginRecaptchaForm()
+
     if form.validate_on_submit():
         try:
             user = User.authenticate(form.login.data, form.password.data)

+ 1 - 0
flaskbb/configs/default.py

@@ -172,6 +172,7 @@ class DefaultConfig(object):
     # Celery
     CELERY_BROKER_URL = 'redis://localhost:6379'
     CELERY_RESULT_BACKEND = 'redis://localhost:6379'
+    if not REDIS_ENABLED: CELERY_ALWAYS_EAGER=True
 
     # FlaskBB Settings
     # ------------------------------ #

+ 28 - 1
flaskbb/forum/models.py

@@ -15,7 +15,7 @@ from sqlalchemy.orm import aliased
 
 from flaskbb.extensions import db
 from flaskbb.utils.helpers import (slugify, get_categories_and_forums,
-                                   get_forums, time_utcnow)
+                                   get_forums, time_utcnow, topic_is_unread)
 from flaskbb.utils.database import CRUDMixin, UTCDateTime
 from flaskbb.utils.settings import flaskbb_config
 
@@ -322,6 +322,33 @@ class Topic(db.Model, CRUDMixin):
         """Returns the slugified url for the topic."""
         return url_for("forum.view_topic", topic_id=self.id, slug=self.slug)
 
+    def first_unread(self, topicsread, user, forumsread=None):
+        """Returns the url to the first unread post if any else to the topic
+        itself.
+
+        :param topicsread: The topicsread object for the topic
+
+        :param user: The user who should be checked if he has read the last post
+                    in the topic
+
+        :param forumsread: The forumsread object in which the topic is. If you
+                        also want to check if the user has marked the forum as
+                        read, than you will also need to pass an forumsread
+                        object.
+        """
+
+        # If the topic is unread try to get the first unread post
+        if topic_is_unread(self, topicsread, user, forumsread):
+            # 
+            query = Post.query.filter(Post.topic_id == self.id)
+            if topicsread is not None:
+                query = query.filter(Post.date_created > topicsread.last_read)
+            post = query.order_by(Post.id.asc()).first()
+            if post is not None:
+                return post.url
+        
+        return self.url
+
     # Methods
     def __init__(self, title=None, user=None):
         """Creates a topic object with some initial values.

+ 19 - 3
flaskbb/forum/views.py

@@ -438,23 +438,39 @@ def edit_post(post_id):
               "danger")
         return redirect(post.topic.url)
 
-    form = ReplyForm()
+    if post.first_post:
+        form = NewTopicForm()
+    else:
+        form = ReplyForm()
+
     if form.validate_on_submit():
         if "preview" in request.form:
             return render_template(
                 "forum/new_post.html", topic=post.topic,
-                form=form, preview=form.content.data
+                form=form, preview=form.content.data, edit_mode=True
             )
         else:
             form.populate_obj(post)
             post.date_modified = time_utcnow()
             post.modified_by = current_user.username
             post.save()
+
+            if post.first_post:
+                post.topic.title = form.title.data
+                post.topic.save()
             return redirect(post.topic.url)
     else:
+        if post.first_post:
+            form.title.data = post.topic.title
+
         form.content.data = post.content
 
-    return render_template("forum/new_post.html", topic=post.topic, form=form)
+    return render_template(
+        "forum/new_post.html",
+        topic=post.topic,
+        form=form,
+        edit_mode=True
+    )
 
 
 @forum.route("/post/<int:post_id>/delete", methods=["POST"])

+ 2 - 4
flaskbb/management/forms.py

@@ -19,7 +19,6 @@ from sqlalchemy.orm.session import make_transient, make_transient_to_detached
 from flask_babelplus import lazy_gettext as _
 
 from flaskbb.utils.fields import BirthdayField
-from flaskbb.utils.widgets import SelectBirthdayWidget
 from flaskbb.extensions import db
 from flaskbb.forum.models import Forum, Category
 from flaskbb.user.models import User, Group
@@ -60,9 +59,8 @@ class UserForm(FlaskForm):
     password = PasswordField("Password", validators=[
         Optional()])
 
-    birthday = BirthdayField(_("Birthday"), format="%d %m %Y",
-                             widget=SelectBirthdayWidget(),
-                             validators=[Optional()])
+    birthday = BirthdayField(_("Birthday"), format="%d %m %Y", validators=[
+        Optional()])
 
     gender = SelectField(_("Gender"), default="None", choices=[
         ("None", ""),

+ 2 - 2
flaskbb/management/models.py

@@ -12,7 +12,7 @@ from wtforms import (TextField, IntegerField, FloatField, BooleanField,
                      SelectField, SelectMultipleField, validators)
 from flask_wtf import FlaskForm
 
-from flaskbb._compat import max_integer, text_type, iteritems
+from flaskbb._compat import text_type, iteritems
 from flaskbb.extensions import db, cache
 from flaskbb.utils.database import CRUDMixin
 
@@ -204,7 +204,7 @@ class Setting(db.Model, CRUDMixin):
         return settings
 
     @classmethod
-    @cache.memoize(timeout=max_integer)
+    @cache.cached(key_prefix="settings")
     def as_dict(cls, from_group=None, upper=True):
         """Returns all settings as a dict. This method is cached. If you want
         to invalidate the cache, simply execute ``self.invalidate_cache()``.

+ 1 - 1
flaskbb/templates/forum/forum.html

@@ -76,7 +76,7 @@
                         </div>
                         <div class="col-md-11 col-sm-10 col-xs-10">
                             <div class="topic-name">
-                                <a href="{{ topic.url }}">{{ topic.title }}</a>
+                                <a href="{{ topic.first_unread(topicread, current_user, forumsread) }}">{{ topic.title }}</a>
                                 <!-- Topic Pagination -->
                                 <span class="topic-pages">{{ topic_pages(topic, flaskbb_config["POSTS_PER_PAGE"]) }}</span>
                             </div>

+ 17 - 3
flaskbb/templates/forum/new_post.html

@@ -4,26 +4,40 @@
 {% extends theme("layout.html") %}
 
 {% block content %}
-{% from theme("macros.html") import render_quickreply, render_submit_field %}
+{% from theme("macros.html") import render_quickreply, render_submit_field, render_field %}
 
 <div class="page-view">
     <ol class="breadcrumb flaskbb-breadcrumb">
         <li><a href="{{ url_for('forum.index') }}">{% trans %}Forum{% endtrans %}</a></li>
         <li><a href="{{ topic.forum.url }}">{{ topic.forum.title }}</a></li>
         <li><a href="{{ topic.url }}">{{ topic.title }}</a></li>
-        <li class="active">{% trans %}New Post{% endtrans %}</li>
+        <li class="active">
+            {% if edit_mode %}
+                {% trans %}Edit Post{% endtrans %}
+            {% else %}
+                {% trans %}New Post{% endtrans %}
+            {% endif %}
+        </li>
     </ol>
 
     <form class="form-horizontal" role="form" method="post">
         {{ form.hidden_tag() }}
         <div class="panel page-panel">
             <div class="panel-heading page-head">
-                {% trans %}New Post{% endtrans %}
+                {% if edit_mode %}
+                    {% trans %}Edit Post{% endtrans %}
+                {% else %}
+                    {% trans %}New Post{% endtrans %}
+                {% endif %}
             </div>
 
             <div class="panel-body page-body">
                 <div class="col-md-12 col-sm-12 col-xs-12">
 
+                    {% if form.title %}
+                        {{ render_field(form.title, div_class="col-md-12 col-sm-12 col-xs-12") }}
+                    {% endif %}
+
                     <div class="form-group">
                         <div class="col-md-12 col-sm-12 col-xs-12">
                             <div class="editor-box">

+ 1 - 1
flaskbb/templates/forum/topic.html

@@ -65,7 +65,7 @@
                     <div class="post-meta clearfix">
                         <div class="pull-left">
                             <!-- Creation date / Date modified -->
-                            <a href="{{ generate_post_url(topic, post, posts.page) }}">
+                            <a href="{{ post.url }}">
                                 {{ post.date_created|format_date('%d %B %Y - %H:%M') }}
                             </a>
                             {% if post.user_id and post.date_modified %}

+ 1 - 1
flaskbb/templates/forum/topic_horizontal.html

@@ -66,7 +66,7 @@
                     <div class="post-meta clearfix">
                         <div class="pull-left">
                             <!-- Creation date / Date modified -->
-                            <a href="{{ generate_post_url(topic, post, posts.page) }}">
+                            <a href="{{ post.url }}">
                                 {{ post.date_created|format_date('%d %B %Y - %H:%M') }}
                             </a>
                             {% if post.user_id and post.date_modified %}

+ 1892 - 0
flaskbb/translations/fr/LC_MESSAGES/messages.po

@@ -0,0 +1,1892 @@
+# French translations for PROJECT.
+# Copyright (C) 2016 ORGANIZATION
+# This file is distributed under the same license as the PROJECT project.
+# Mickael QUETELARD <mickael.quetelard@gmail.com>, 2016.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: FlaskBB\n"
+"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n"
+"POT-Creation-Date: 2016-06-09 11:35+0200\n"
+"PO-Revision-Date: 2016-11-07 18:34+0100\n"
+"Last-Translator: Mickael QUETELARD <mickael.quetelard@gmail.com>\n"
+"Language: fr\n"
+"Language-Team: fr <LL@li.org>\n"
+"Plural-Forms: nplurals=2; plural=(n > 1)\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=utf-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: Babel 2.3.4\n"
+
+#: flaskbb/email.py:26
+msgid "Password Recovery Confirmation"
+msgstr "Confirmation de récupération de mot de passe"
+
+#: flaskbb/email.py:48 flaskbb/templates/auth/account_activation.html:1
+#: flaskbb/templates/auth/account_activation.html:10
+msgid "Account Activation"
+msgstr "Activation du Compte"
+
+#: flaskbb/auth/forms.py:24 flaskbb/management/forms.py:32
+msgid "You can only use letters, numbers or dashes."
+msgstr "Vous ne pouvez utiliser que des lettres, des chiffres ou des tirets."
+
+#: flaskbb/auth/forms.py:28
+msgid "Username or Email address"
+msgstr "Nom d'utilisateur ou adresse Email"
+
+#: flaskbb/auth/forms.py:29
+msgid "Please enter your username or email address."
+msgstr "Veuillez entrer votre nom d'utilisateur ou votre adresse email."
+
+#: flaskbb/auth/forms.py:32 flaskbb/auth/forms.py:51 flaskbb/auth/forms.py:86
+#: flaskbb/auth/forms.py:109 flaskbb/user/forms.py:64
+msgid "Password"
+msgstr "Mot de passe"
+
+#: flaskbb/auth/forms.py:33 flaskbb/auth/forms.py:87 flaskbb/user/forms.py:65
+msgid "Please enter your password."
+msgstr "Veuillez entrer votre mot de passe."
+
+#: flaskbb/auth/forms.py:35 flaskbb/auth/forms.py:57 flaskbb/auth/forms.py:97
+msgid "Captcha"
+msgstr ""
+
+#: flaskbb/auth/forms.py:37
+msgid "Remember me"
+msgstr "Se souvenir de moi"
+
+#: flaskbb/auth/forms.py:39 flaskbb/templates/auth/login.html:1
+#: flaskbb/templates/auth/login.html:10 flaskbb/templates/layout.html:134
+#: flaskbb/templates/navigation.html:60
+msgid "Login"
+msgstr "Connexion"
+
+#: flaskbb/auth/forms.py:43 flaskbb/auth/forms.py:124
+#: flaskbb/management/forms.py:52 flaskbb/templates/forum/memberlist.html:43
+#: flaskbb/templates/forum/search_result.html:104
+#: flaskbb/templates/management/banned_users.html:62
+#: flaskbb/templates/management/users.html:62
+msgid "Username"
+msgstr "Nom d'utilisateur"
+
+#: flaskbb/auth/forms.py:44 flaskbb/auth/forms.py:125
+#: flaskbb/message/forms.py:23
+msgid "A valid username is required."
+msgstr "Un nom d'utilisateur valide est requis."
+
+#: flaskbb/auth/forms.py:47 flaskbb/auth/forms.py:93 flaskbb/auth/forms.py:105
+#: flaskbb/auth/forms.py:128 flaskbb/management/forms.py:56
+msgid "Email address"
+msgstr "Adresse email"
+
+#: flaskbb/auth/forms.py:48 flaskbb/auth/forms.py:94 flaskbb/auth/forms.py:106
+#: flaskbb/auth/forms.py:129 flaskbb/management/forms.py:57
+#: flaskbb/user/forms.py:37
+msgid "A valid email address is required."
+msgstr "Une adresse email valide est requise."
+
+#: flaskbb/auth/forms.py:49 flaskbb/auth/forms.py:130
+#: flaskbb/management/forms.py:58 flaskbb/user/forms.py:38
+#: flaskbb/user/forms.py:43 flaskbb/user/forms.py:46
+msgid "Invalid email address."
+msgstr "Adresse email invalide."
+
+#: flaskbb/auth/forms.py:53 flaskbb/auth/forms.py:111
+msgid "Passwords must match."
+msgstr "Les mots de passe doivent correspondre."
+
+#: flaskbb/auth/forms.py:55 flaskbb/auth/forms.py:113
+msgid "Confirm password"
+msgstr "Confirmation de mot de passe."
+
+#: flaskbb/auth/forms.py:59 flaskbb/user/forms.py:29
+msgid "Language"
+msgstr "Langue"
+
+#: flaskbb/auth/forms.py:61
+msgid "I accept the Terms of Service"
+msgstr "J'accepte les conditions d'utilisation"
+
+#: flaskbb/auth/forms.py:63 flaskbb/templates/auth/register.html:1
+#: flaskbb/templates/auth/register.html:10 flaskbb/templates/layout.html:139
+#: flaskbb/templates/navigation.html:66
+msgid "Register"
+msgstr "S'inscrire"
+
+#: flaskbb/auth/forms.py:68 flaskbb/management/forms.py:116
+msgid "This username is already taken."
+msgstr "Ce nom d'utilisateur est déjà utilisé."
+
+#: flaskbb/auth/forms.py:73 flaskbb/management/forms.py:130
+#: flaskbb/user/forms.py:60
+msgid "This email address is already taken."
+msgstr "Cette adresse email est déjà utilisé."
+
+#: flaskbb/auth/forms.py:89 flaskbb/templates/auth/reauth.html:1
+#: flaskbb/templates/auth/reauth.html:10
+msgid "Refresh Login"
+msgstr "Rafraichir la connexion"
+
+#: flaskbb/auth/forms.py:99
+msgid "Request Password"
+msgstr "Demande du mot de passe"
+
+#: flaskbb/auth/forms.py:115
+msgid "Reset password"
+msgstr "Réinitialiser le mot de passe"
+
+#: flaskbb/auth/forms.py:120
+msgid "Wrong email address."
+msgstr "Mauvaise adresse email."
+
+#: flaskbb/auth/forms.py:132
+msgid "Send Confirmation Mail"
+msgstr "Envois d'un mail de confirmation"
+
+#: flaskbb/auth/forms.py:138
+msgid "User does not exist."
+msgstr "L'utilisateur n'existe pas."
+
+#: flaskbb/auth/forms.py:141
+msgid "User is already active."
+msgstr "L'utilisateur est déjà actif."
+
+#: flaskbb/auth/forms.py:145
+msgid "Email confirmation token"
+msgstr "Token de confirmation d'email"
+
+#: flaskbb/auth/forms.py:146
+msgid "Please enter the token that we have sent to you."
+msgstr "Veuillez entrer le token que nous vous avons envoyé."
+
+#: flaskbb/auth/forms.py:150
+msgid "Confirm Email"
+msgstr "Confirmation d'email"
+
+#: flaskbb/auth/views.py:95
+msgid "Wrong username or password."
+msgstr "Nom d'utilisateur ou mot de passe erroné."
+
+#: flaskbb/auth/views.py:111
+msgid "Reauthenticated."
+msgstr "Ré-authentification."
+
+#: flaskbb/auth/views.py:114
+msgid "Wrong password."
+msgstr "Mot de passe erroné."
+
+#: flaskbb/auth/views.py:125
+msgid "Logged out"
+msgstr "Déconnecter"
+
+#: flaskbb/auth/views.py:136
+msgid "The registration has been disabled."
+msgstr "L'enregistrement a été désactivé."
+
+#: flaskbb/auth/views.py:150
+#, python-format
+msgid "An account activation email has been sent to %(email)s"
+msgstr "Un email d'activation de compte a été envoyé à %(email)s"
+
+#: flaskbb/auth/views.py:154
+msgid "Thanks for registering."
+msgstr "Merci pour votre inscription."
+
+#: flaskbb/auth/views.py:173
+msgid "Email sent! Please check your inbox."
+msgstr "Email envoyé! Veuillez vérifier votre boite mail."
+
+#: flaskbb/auth/views.py:176
+msgid ""
+"You have entered an username or email address that is not linked with "
+"your account."
+msgstr "Vous avez entré un nom d'utilisateur ou une adresse e-mail qui n'est pas liée "
+"à votre compte."
+
+#: flaskbb/auth/views.py:193
+msgid "Your password token is invalid."
+msgstr "Le token de votre mot de passe est invalide."
+
+#: flaskbb/auth/views.py:197
+msgid "Your password token is expired."
+msgstr "Le token de votre mot de passe est expiré."
+
+#: flaskbb/auth/views.py:203
+msgid "Your password has been updated."
+msgstr "Votre mot de passe a été mis à jour."
+
+#: flaskbb/auth/views.py:214 flaskbb/auth/views.py:232
+msgid "This account is already activated."
+msgstr "Ce compte est déjà activé."
+
+#: flaskbb/auth/views.py:221
+msgid "A new account activation token has been sent to your email address."
+msgstr "Un nouveau token d'activation a été envoyé à votre adresse email."
+
+#: flaskbb/auth/views.py:245
+msgid "Your account activation token is invalid."
+msgstr "Votre token d'activation est invalid."
+
+#: flaskbb/auth/views.py:249
+msgid "Your account activation token is expired."
+msgstr "Votre token d'activation est expiré."
+
+#: flaskbb/auth/views.py:260
+msgid "Your account has been activated."
+msgstr "Votre compte a été activé."
+
+#: flaskbb/forum/forms.py:22
+msgid "Quick reply"
+msgstr "Réponse rapide"
+
+#: flaskbb/forum/forms.py:23 flaskbb/forum/forms.py:34
+#: flaskbb/forum/forms.py:55
+msgid "You cannot post a reply without content."
+msgstr "Vous ne pouvez pas poster une réponse sans contenu."
+
+#: flaskbb/forum/forms.py:25 flaskbb/forum/forms.py:39
+#: flaskbb/templates/forum/topic_controls.html:94
+msgid "Reply"
+msgstr "Répondre"
+
+#: flaskbb/forum/forms.py:33 flaskbb/forum/forms.py:54
+#: flaskbb/forum/forms.py:100
+msgid "Content"
+msgstr "Contenu"
+
+#: flaskbb/forum/forms.py:36 flaskbb/forum/forms.py:57
+msgid "Track this topic"
+msgstr "Suivre ce topic"
+
+#: flaskbb/forum/forms.py:40 flaskbb/forum/forms.py:61
+msgid "Preview"
+msgstr "Prévisualisation"
+
+#: flaskbb/forum/forms.py:51
+msgid "Topic title"
+msgstr "Titre du topic"
+
+#: flaskbb/forum/forms.py:52
+msgid "Please choose a title for your topic."
+msgstr "Veuillez choisir un titre pour votre topic."
+
+#: flaskbb/forum/forms.py:60
+msgid "Post Topic"
+msgstr "Poster un Topic"
+
+#: flaskbb/forum/forms.py:73 flaskbb/templates/management/reports.html:39
+#: flaskbb/templates/management/unread_reports.html:39
+msgid "Reason"
+msgstr "Raison"
+
+#: flaskbb/forum/forms.py:74
+msgid "What is the reason for reporting this post?"
+msgstr "Quelle est la raison de ce post"
+
+#: flaskbb/forum/forms.py:77
+msgid "Report post"
+msgstr "Post de rapport"
+
+#: flaskbb/forum/forms.py:85 flaskbb/forum/forms.py:89
+#: flaskbb/forum/forms.py:104 flaskbb/templates/forum/memberlist.html:26
+#: flaskbb/templates/forum/search_form.html:1
+#: flaskbb/templates/forum/search_form.html:10
+#: flaskbb/templates/forum/search_form.html:15
+#: flaskbb/templates/forum/search_result.html:1
+#: flaskbb/templates/forum/search_result.html:10
+#: flaskbb/templates/layout.html:75
+#: flaskbb/templates/management/banned_users.html:39
+#: flaskbb/templates/management/users.html:39
+#: flaskbb/templates/navigation.html:21
+msgid "Search"
+msgstr "Rechercher"
+
+#: flaskbb/forum/forms.py:97
+msgid "Criteria"
+msgstr "Critère"
+
+#: flaskbb/forum/forms.py:101
+msgid "Post"
+msgstr "Poster"
+
+#: flaskbb/forum/forms.py:101 flaskbb/templates/forum/edit_forum.html:31
+#: flaskbb/templates/forum/forum.html:49
+#: flaskbb/templates/forum/search_result.html:134
+#: flaskbb/templates/forum/topictracker.html:31
+#: flaskbb/templates/management/reports.html:38
+#: flaskbb/templates/management/unread_reports.html:38
+msgid "Topic"
+msgstr ""
+
+#: flaskbb/forum/forms.py:102 flaskbb/templates/forum/category.html:9
+#: flaskbb/templates/forum/category_layout.html:8
+#: flaskbb/templates/forum/edit_forum.html:13
+#: flaskbb/templates/forum/forum.html:10
+#: flaskbb/templates/forum/memberlist.html:9
+#: flaskbb/templates/forum/new_post.html:11
+#: flaskbb/templates/forum/new_topic.html:11
+#: flaskbb/templates/forum/search_form.html:9
+#: flaskbb/templates/forum/search_result.html:9
+#: flaskbb/templates/forum/search_result.html:213
+#: flaskbb/templates/forum/topic.html:10
+#: flaskbb/templates/forum/topic_horizontal.html:14
+#: flaskbb/templates/forum/topictracker.html:14
+#: flaskbb/templates/layout.html:73
+#: flaskbb/templates/management/banned_users.html:8
+#: flaskbb/templates/management/category_form.html:8
+#: flaskbb/templates/management/forum_form.html:8
+#: flaskbb/templates/management/forums.html:7
+#: flaskbb/templates/management/forums.html:62
+#: flaskbb/templates/management/group_form.html:8
+#: flaskbb/templates/management/groups.html:7
+#: flaskbb/templates/management/overview.html:7
+#: flaskbb/templates/management/plugins.html:7
+#: flaskbb/templates/management/reports.html:8
+#: flaskbb/templates/management/settings.html:7
+#: flaskbb/templates/management/unread_reports.html:8
+#: flaskbb/templates/management/user_form.html:8
+#: flaskbb/templates/management/users.html:8
+#: flaskbb/templates/message/message_layout.html:6
+#: flaskbb/templates/navigation.html:19 flaskbb/templates/user/all_posts.html:6
+#: flaskbb/templates/user/all_topics.html:6
+#: flaskbb/templates/user/profile.html:5
+#: flaskbb/templates/user/settings_layout.html:6
+msgid "Forum"
+msgstr ""
+
+#: flaskbb/forum/forms.py:102 flaskbb/templates/forum/search_result.html:99
+#: flaskbb/templates/management/management_layout.html:13
+#: flaskbb/templates/management/users.html:1
+#: flaskbb/templates/management/users.html:34
+msgid "Users"
+msgstr "Utilisateurs"
+
+#: flaskbb/forum/views.py:167
+msgid "You do not have the permissions to create a new topic."
+msgstr "Vous n'avez pas la permission de créer un nouveau topic."
+
+#: flaskbb/forum/views.py:195 flaskbb/utils/helpers.py:109
+msgid "You do not have the permissions to delete this topic."
+msgstr "Vous n'avez pas la permission de supprimer ce topic."
+
+#: flaskbb/forum/views.py:212
+msgid "You do not have the permissions to lock this topic."
+msgstr "Vous n'avez pas la permission de fermer ce topic."
+
+#: flaskbb/forum/views.py:228
+msgid "You do not have the permissions to unlock this topic."
+msgstr "Vous n'avez pas la permission de débloquer ce topic."
+
+#: flaskbb/forum/views.py:244
+msgid "You do not have the permissions to highlight this topic."
+msgstr "Vous n'avez pas la permission de mettre en évidence ce sujet."
+
+#: flaskbb/forum/views.py:261
+msgid "You do not have the permissions to trivialize this topic."
+msgstr "Vous n'avez pas la permission de banaliser ce sujet.
+
+#: flaskbb/forum/views.py:284
+msgid "You do not have the permissions to moderate this forum."
+msgstr "Vous n'avez pas la permission de modérer ce forum."
+
+#: flaskbb/forum/views.py:306
+msgid "In order to perform this action you have to select at least one topic."
+msgstr "Pour exécuter cette action, vous devez sélectionner au moins un sujet."
+
+#: flaskbb/forum/views.py:315
+#, python-format
+msgid "%(count)s topics locked."
+msgstr "%(count)s sujets fermés."
+
+#: flaskbb/forum/views.py:321
+#, python-format
+msgid "%(count)s topics unlocked."
+msgstr "%(count)s sujets débloqués."
+
+#: flaskbb/forum/views.py:328
+#, python-format
+msgid "%(count)s topics highlighted."
+msgstr "%(count)s sujets mis en avant."
+
+#: flaskbb/forum/views.py:334
+#, python-format
+msgid "%(count)s topics trivialized."
+msgstr "%(count)s sujets banalisés."
+
+#: flaskbb/forum/views.py:341
+#, python-format
+msgid "%(count)s topics deleted."
+msgstr "%(count)s sujets supprimés."
+
+#: flaskbb/forum/views.py:349
+msgid "Please choose a new forum for the topics."
+msgstr "Veuillez choisir un nouveau forum pour les sujets."
+
+#: flaskbb/forum/views.py:361
+msgid "You do not have the permissions to move this topic."
+msgstr "Vous n'avez pas la permission de déplacer ce sujet."
+
+#: flaskbb/forum/views.py:381 flaskbb/forum/views.py:408
+msgid "You do not have the permissions to post in this topic."
+msgstr "Vous n'avez pas la permission de poster dans ce sujet."
+
+#: flaskbb/forum/views.py:434
+msgid "You do not have the permissions to edit this post."
+msgstr "Vous n'avez pas la permission d'éditer ce sujet."
+
+#: flaskbb/forum/views.py:465
+msgid "You do not have the permissions to delete this post."
+msgstr "Vous n'avez pas la permissions de supprimer ce message."
+
+#: flaskbb/forum/views.py:489
+msgid "Thanks for reporting."
+msgstr "Merci pour le rapport."
+
+#: flaskbb/forum/views.py:525
+#, python-format
+msgid "Forum %(forum)s marked as read."
+msgstr "Le forum %(forum)s marqué comme lu."
+
+#: flaskbb/forum/views.py:547
+msgid "All forums marked as read."
+msgstr "Tous les forum sont marqué comme lus."
+
+#: flaskbb/management/forms.py:53
+msgid "Please enter a username."
+msgstr "Veuillez entrer un nom d'utilisateur."
+
+#: flaskbb/management/forms.py:63 flaskbb/user/forms.py:82
+msgid "Birthday"
+msgstr "Date d'anniversaire"
+
+#: flaskbb/management/forms.py:67 flaskbb/user/forms.py:86
+msgid "Gender"
+msgstr "Genre"
+
+#: flaskbb/management/forms.py:69 flaskbb/user/forms.py:88
+msgid "Male"
+msgstr "Homme"
+
+#: flaskbb/management/forms.py:70 flaskbb/user/forms.py:89
+msgid "Female"
+msgstr "Femme"
+
+#: flaskbb/management/forms.py:72 flaskbb/user/forms.py:91
+msgid "Location"
+msgstr "Localité"
+
+#: flaskbb/management/forms.py:75 flaskbb/templates/forum/search_result.html:48
+#: flaskbb/templates/forum/topic.html:52
+#: flaskbb/templates/forum/topic_horizontal.html:63
+#: flaskbb/templates/message/conversation.html:47
+#: flaskbb/templates/message/conversation.html:101 flaskbb/user/forms.py:94
+msgid "Website"
+msgstr "Site internet"
+
+#: flaskbb/management/forms.py:78 flaskbb/user/forms.py:97
+msgid "Avatar"
+msgstr ""
+
+#: flaskbb/management/forms.py:81
+msgid "Forum signature"
+msgstr "Signature"
+
+#: flaskbb/management/forms.py:84 flaskbb/user/forms.py:103
+msgid "Notes"
+msgstr ""
+
+#: flaskbb/management/forms.py:87
+msgid "Is active?"
+msgstr "Est actif ?"
+
+#: flaskbb/management/forms.py:91
+msgid "Primary group"
+msgstr "Groupe principale"
+
+#: flaskbb/management/forms.py:96
+msgid "Secondary groups"
+msgstr "Groupe secondaire"
+
+#: flaskbb/management/forms.py:102 flaskbb/management/forms.py:216
+#: flaskbb/management/forms.py:336 flaskbb/management/forms.py:416
+#: flaskbb/templates/management/settings.html:59 flaskbb/user/forms.py:32
+#: flaskbb/user/forms.py:48 flaskbb/user/forms.py:74 flaskbb/user/forms.py:106
+msgid "Save"
+msgstr "Sauvegarder"
+
+#: flaskbb/management/forms.py:151
+msgid "Group name"
+msgstr "Nom du groupe"
+
+#: flaskbb/management/forms.py:152
+msgid "Please enter a name for the group."
+msgstr "Veuillez entrer un nom pour le groupe."
+
+#: flaskbb/management/forms.py:154 flaskbb/management/forms.py:287
+#: flaskbb/management/forms.py:404 flaskbb/templates/management/groups.html:35
+msgid "Description"
+msgstr ""
+
+#: flaskbb/management/forms.py:158
+msgid "Is 'Admin' group?"
+msgstr "Est un groupe Admin ?"
+
+#: flaskbb/management/forms.py:159
+msgid "With this option the group has access to the admin panel."
+msgstr "Avec cette option le groupe a accès au panneau d'administraton."
+
+#: flaskbb/management/forms.py:163
+msgid "Is 'Super Moderator' group?"
+msgstr "Est un groupe de Super Modérateur ?"
+
+#: flaskbb/management/forms.py:164
+msgid ""
+"Check this, if the users in this group are allowed to moderate every "
+"forum."
+msgstr ""
+"Vérifier ceci, si les utilisateurs de ce groupe ont la'autorisation"
+"de modérer chaque forum."
+
+#: flaskbb/management/forms.py:168
+msgid "Is 'Moderator' group?"
+msgstr "Est un groupe Modérateur ?"
+
+#: flaskbb/management/forms.py:169
+msgid ""
+"Check this, if the users in this group are allowed to moderate specified "
+"forums."
+msgstr ""
+"Vérifier ceci, si les utilisateurs de ce groupe ont l'autorisation de modérer des"
+"forums spécifiques."
+
+#: flaskbb/management/forms.py:173
+msgid "Is 'Banned' group?"
+msgstr "Est un groupe bannis ?"
+
+#: flaskbb/management/forms.py:174
+msgid "Only one group of type 'Banned' is allowed."
+msgstr "Un seul groupe de type 'Bannis' est autorisé."
+
+#: flaskbb/management/forms.py:177
+msgid "Is 'Guest' group?"
+msgstr "Est un groupe Visiteur ?"
+
+#: flaskbb/management/forms.py:178
+msgid "Only one group of type 'Guest' is allowed."
+msgstr "Un seul groupe de type Visiteur est autorisé."
+
+#: flaskbb/management/forms.py:181
+msgid "Can edit posts"
+msgstr "Peut modifier les messaes"
+
+#: flaskbb/management/forms.py:182
+msgid "Check this, if the users in this group can edit posts."
+msgstr "Vérifier ceci, si les utilisateurs de ce groupe peuvent modifier les messages."
+
+#: flaskbb/management/forms.py:185
+msgid "Can delete posts"
+msgstr "Peut supprimer les sujets"
+
+#: flaskbb/management/forms.py:186
+msgid "Check this, if the users in this group can delete posts."
+msgstr "Vérifier ceci, si les utilisateurs de ce groupe peuvent supprimer les messages."
+
+#: flaskbb/management/forms.py:190
+msgid "Can delete topics"
+msgstr "Peut supprimer les sujets"
+
+#: flaskbb/management/forms.py:191
+msgid "Check this, if the users in this group can delete topics."
+msgstr "Vérifier ceci, si les utilisateurs de ce groupe peuvent supprimer les sujets."
+
+#: flaskbb/management/forms.py:195
+msgid "Can create topics"
+msgstr "Peut créer des sujets"
+
+#: flaskbb/management/forms.py:196
+msgid "Check this, if the users in this group can create topics."
+msgstr "Vérifier ceci, si les utilisateurs de ce groupe peuvent créer des sujets."
+
+#: flaskbb/management/forms.py:200
+msgid "Can post replies"
+msgstr "Peut envoyer des réponses"
+
+#: flaskbb/management/forms.py:201
+msgid "Check this, if the users in this group can post replies."
+msgstr "Véririfer ceci, si les utilisateurs de ce groupe peuvent envoyer des réponses."
+
+#: flaskbb/management/forms.py:206
+msgid "Moderators can edit user profiles"
+msgstr "Les modérateurs peuvent modifier les profils utilisateur"
+
+#: flaskbb/management/forms.py:207
+msgid ""
+"Allow moderators to edit another user's profile including password and "
+"email changes."
+msgstr "Permettre aux modérateurs de modifier d'autre profil utilisateur incluant "
+"les changement de mot de passe et d'adresse email."
+
+#: flaskbb/management/forms.py:212
+msgid "Moderators can ban users"
+msgstr "Les modérateurs peuvent bannir les utilisateurs"
+
+#: flaskbb/management/forms.py:213
+msgid "Allow moderators to ban other users."
+msgstr "Permettre aux modérateurs de bannir d'autres utilisateurs."
+
+#: flaskbb/management/forms.py:230
+msgid "This group name is already taken."
+msgstr "Ce nom de groupe est déjà pris."
+
+#: flaskbb/management/forms.py:244
+msgid "There is already a group of type 'Banned'."
+msgstr "Il y a déjà un groupe de type 'Bannis'."
+
+#: flaskbb/management/forms.py:259
+msgid "There is already a group of type 'Guest'."
+msgstr "Il y a déjà un groupe de type 'Visiteur'."
+
+#: flaskbb/management/forms.py:282
+msgid "Forum title"
+msgstr "Titre du forum"
+
+#: flaskbb/management/forms.py:283
+msgid "Please enter a forum title."
+msgstr "Veuillez entrer un titre de forum."
+
+#: flaskbb/management/forms.py:289 flaskbb/management/forms.py:406
+msgid "You can format your description with Markdown."
+msgstr "Vous pouvez écrire votre description au format Markdown."
+
+#: flaskbb/management/forms.py:293 flaskbb/management/forms.py:410
+msgid "Position"
+msgstr ""
+
+#: flaskbb/management/forms.py:295
+msgid "Please enter a position for theforum."
+msgstr "Veuillez entrer une position pour le forum."
+
+#: flaskbb/management/forms.py:300
+msgid "Category"
+msgstr "Catégorie"
+
+#: flaskbb/management/forms.py:304
+msgid "The category that contains this forum."
+msgstr "La catégorie qui contient ce forum."
+
+#: flaskbb/management/forms.py:308
+msgid "External link"
+msgstr "Lien externe"
+
+#: flaskbb/management/forms.py:310
+msgid "A link to a website i.e. 'http://flaskbb.org'."
+msgstr "Un lien vers le site i.e. 'http://flaskbb.org'."
+
+#: flaskbb/management/forms.py:314
+#: flaskbb/templates/forum/category_layout.html:80
+#: flaskbb/templates/forum/search_result.html:283
+#: flaskbb/templates/management/forums.html:135
+msgid "Moderators"
+msgstr "Modérateurs"
+
+#: flaskbb/management/forms.py:315
+msgid ""
+"Comma separated usernames. Leave it blank if you do not want to set any "
+"moderators."
+msgstr "Nom d'utilisateur séparé par des virgules. Laissez-le vide si vous ne voulez pas définir de "
+"modérateurs."
+
+#: flaskbb/management/forms.py:320
+msgid "Show moderators"
+msgstr "Voir les modérateurs"
+
+#: flaskbb/management/forms.py:321
+msgid "Do you want to show the moderators on the index page?"
+msgstr "Voulez-vous voir les modérateurs sur la page d'index ?"
+
+#: flaskbb/management/forms.py:325
+msgid "Locked?"
+msgstr "Fermer ?"
+
+#: flaskbb/management/forms.py:326
+msgid "Disable new posts and topics in this forum."
+msgstr "Désactiver les nouveaux sujets dans ce forum."
+
+#: flaskbb/management/forms.py:330
+msgid "Group access"
+msgstr "Accès au groupe"
+
+#: flaskbb/management/forms.py:333
+msgid "Select the groups that can access this forum."
+msgstr "Sélectionner les groupes qui peuvent accéder à ce forum."
+
+#: flaskbb/management/forms.py:341
+msgid "You cannot convert a forum that contains topics into an external link."
+msgstr "Vous ne pouvez pas convertir un forum qui contient des sujets dans un lien externe."
+
+#: flaskbb/management/forms.py:347
+msgid "You also need to specify some moderators."
+msgstr "Vous avez aussi besoin de spécifier des modérateurs."
+
+#: flaskbb/management/forms.py:359
+#, python-format
+msgid "%(user)s is not in a moderators group."
+msgstr "%'(user)s n'est pas dans un groupe de modérateurs."
+
+#: flaskbb/management/forms.py:400
+msgid "Category title"
+msgstr "Titre de catégorie"
+
+#: flaskbb/management/forms.py:401
+msgid "Please enter a category title."
+msgstr "Veuillez entrer un titre de catégorie."
+
+#: flaskbb/management/forms.py:412
+msgid "Please enter a position for the category."
+msgstr "Veuillez entrer une position pour la catégorie."
+
+#: flaskbb/management/views.py:111
+msgid "Settings saved."
+msgstr "Paramètres sauvegardés."
+
+#: flaskbb/management/views.py:150
+msgid "You are not allowed to edit this user."
+msgstr "Vous n'avez pas l'autorisation de modifier cet utilisateur."
+
+#: flaskbb/management/views.py:184
+msgid "User updated."
+msgstr "Utilisateur mis à jour."
+
+#: flaskbb/management/views.py:188
+msgid "Edit User"
+msgstr "Modifier l'utilisateur"
+
+#: flaskbb/management/views.py:223
+msgid "You cannot delete yourself."
+msgstr "Vous ne pouvez pas vous supprimer vous-même."
+
+#: flaskbb/management/views.py:227
+msgid "User deleted."
+msgstr "Utilisateur supprimé."
+
+#: flaskbb/management/views.py:237
+msgid "User added."
+msgstr "Utilisateur ajouté."
+
+#: flaskbb/management/views.py:241
+#: flaskbb/templates/management/banned_users.html:24
+#: flaskbb/templates/management/user_form.html:24
+#: flaskbb/templates/management/users.html:24
+msgid "Add User"
+msgstr "Ajouter un utilisateur"
+
+#: flaskbb/management/views.py:271
+msgid "You do not have the permissions to ban this user."
+msgstr "Vous n'avez pas la permission de bannir cette utilisateur."
+
+#: flaskbb/management/views.py:295
+#: flaskbb/templates/management/banned_users.html:96
+#: flaskbb/templates/management/users.html:122
+msgid "Unban"
+msgstr "Ré-habiliter"
+
+#: flaskbb/management/views.py:313
+msgid "A moderator cannot ban an admin user."
+msgstr "Un modérateur ne peut pas bannir un admin."
+
+#: flaskbb/management/views.py:317
+msgid "User is now banned."
+msgstr "L'utilisateur est maintenant bannis."
+
+#: flaskbb/management/views.py:319
+msgid "Could not ban user."
+msgstr "Vous ne pouvez pas bannir l'utilisateur."
+
+#: flaskbb/management/views.py:329
+msgid "You do not have the permissions to unban this user."
+msgstr "Vous n'avez pas la permission de ré-habiliter cet utilisateur."
+
+#: flaskbb/management/views.py:344 flaskbb/templates/management/users.html:112
+msgid "Ban"
+msgstr "Bannir"
+
+#: flaskbb/management/views.py:359
+msgid "User is now unbanned."
+msgstr "L'utilisateur est maintenant ré-habilité."
+
+#: flaskbb/management/views.py:361
+msgid "Could not unban user."
+msgstr "Vous nz pouvez pas ré-habiliter l'utilisateur."
+
+#: flaskbb/management/views.py:422
+#, python-format
+msgid "Report %(id)s is already marked as read."
+msgstr "Le rapport %(id)s est déjà marqué comme lu."
+
+#: flaskbb/management/views.py:429
+#, python-format
+msgid "Report %(id)s marked as read."
+msgstr "LE rapport %(id)s est marqué comme lu."
+
+#: flaskbb/management/views.py:443
+msgid "All reports were marked as read."
+msgstr "Tous les rapports ont été marqué comme lus."
+
+#: flaskbb/management/views.py:474
+msgid "Group updated."
+msgstr "Groupe mis à jour."
+
+#: flaskbb/management/views.py:478
+msgid "Edit Group"
+msgstr "Modifier le groupe"
+
+#: flaskbb/management/views.py:506
+msgid "You cannot delete one of the standard groups."
+msgstr "Vous ne pouvez pas supprimer un des groupes standard."
+
+#: flaskbb/management/views.py:514
+msgid "You cannot delete the standard groups. Try renaming it instead."
+msgstr "Vous ne pouvez pas supprimer les groupes standard. Essayez de le renommer à la place."
+
+#: flaskbb/management/views.py:520
+msgid "Group deleted."
+msgstr "Groupe supprimé."
+
+#: flaskbb/management/views.py:523
+msgid "No group chosen."
+msgstr "Aucun groupe choisi."
+
+#: flaskbb/management/views.py:533
+msgid "Group added."
+msgstr "Groupe ajouté."
+
+#: flaskbb/management/views.py:537
+#: flaskbb/templates/management/group_form.html:21
+#: flaskbb/templates/management/groups.html:20
+msgid "Add Group"
+msgstr "Ajouter un groupe"
+
+#: flaskbb/management/views.py:556
+msgid "Forum updated."
+msgstr "Forum mis à jour."
+
+#: flaskbb/management/views.py:567 flaskbb/templates/management/forums.html:154
+msgid "Edit Forum"
+msgstr "Modifier le forum"
+
+#: flaskbb/management/views.py:580
+msgid "Forum deleted."
+msgstr "Forum supprimé."
+
+#: flaskbb/management/views.py:592
+msgid "Forum added."
+msgstr "Forum ajouté."
+
+#: flaskbb/management/views.py:601
+#: flaskbb/templates/management/category_form.html:21
+#: flaskbb/templates/management/forum_form.html:21
+#: flaskbb/templates/management/forums.html:20
+#: flaskbb/templates/management/forums.html:44
+msgid "Add Forum"
+msgstr "Ajouter un forum"
+
+#: flaskbb/management/views.py:611
+msgid "Category added."
+msgstr "Catégorie ajoutée."
+
+#: flaskbb/management/views.py:615
+#: flaskbb/templates/management/category_form.html:22
+#: flaskbb/templates/management/forum_form.html:22
+#: flaskbb/templates/management/forums.html:21
+msgid "Add Category"
+msgstr "Ajouter un catégorie"
+
+#: flaskbb/management/views.py:627
+msgid "Category updated."
+msgstr "Catégorie mise à jour."
+
+#: flaskbb/management/views.py:631 flaskbb/templates/management/forums.html:47
+msgid "Edit Category"
+msgstr "Modifier la catégorie"
+
+#: flaskbb/management/views.py:644
+msgid "Category with all associated forums deleted."
+msgstr "Catégorie avec tous les forums associés supprimés."
+
+#: flaskbb/management/views.py:662
+#, python-format
+msgid "Plugin %(plugin)s is already enabled."
+msgstr "Le plugin %(plugin)s est déjà activé."
+
+#: flaskbb/management/views.py:668
+#, python-format
+msgid "Plugin %(plugin)s enabled. Please restart FlaskBB now."
+msgstr "Le plugin %(plugin)s est activé. Veuillez redémarrer FlaskBB maintenant."
+
+#: flaskbb/management/views.py:671
+msgid ""
+"It seems that FlaskBB does not have enough filesystem permissions. Try "
+"removing the 'DISABLED' file by yourself instead."
+msgstr "Il semble que FlaskBB ne dispose pas de permissions suffisantes sur le système de fichiers."
+"Essayez en supprimant le fichier 'DISABLED' par vous-même."
+
+#: flaskbb/management/views.py:684
+#, python-format
+msgid "Plugin %(plugin)s not found."
+msgstr "Le plugin %(plugin)s n'a pas été trouvé."
+
+#: flaskbb/management/views.py:689
+#, python-format
+msgid "Plugin %(plugin)s disabled. Please restart FlaskBB now."
+msgstr "Le plugin %(plugin)s est désactivé. Veuillez redémarrer FlaskBB maintenant."
+
+#: flaskbb/management/views.py:692
+msgid ""
+"It seems that FlaskBB does not have enough filesystem permissions. Try "
+"creating the 'DISABLED' file by yourself instead."
+msgstr "Il semble que FlaskBB ne dispose pas de permissions suffisantes sur le système de fichiers."
+"Essayez en créant le fichier 'DISABLED' par vous-même."
+
+#: flaskbb/management/views.py:707
+msgid "Plugin has been uninstalled."
+msgstr "Le plugin a été désinstallé."
+
+#: flaskbb/management/views.py:709
+msgid "Cannot uninstall plugin."
+msgstr "Le plugin ne peut pas être désinstallé."
+
+#: flaskbb/management/views.py:722
+msgid "Plugin has been installed."
+msgstr "Le plugin a été désinstallé."
+
+#: flaskbb/management/views.py:724
+msgid "Cannot install plugin."
+msgstr "Le plugin ne peut pas être installé."
+
+#: flaskbb/message/forms.py:22
+msgid "Recipient"
+msgstr "Destinataire"
+
+#: flaskbb/message/forms.py:25
+msgid "Subject"
+msgstr "Sujet"
+
+#: flaskbb/message/forms.py:26
+msgid "A Subject is required."
+msgstr "Un sujet est requis."
+
+#: flaskbb/message/forms.py:28 flaskbb/message/forms.py:59
+#: flaskbb/templates/forum/search_result.html:43
+#: flaskbb/templates/forum/topic.html:47
+#: flaskbb/templates/forum/topic_horizontal.html:58
+#: flaskbb/templates/message/conversation.html:97
+#: flaskbb/templates/user/profile_layout.html:47
+msgid "Message"
+msgstr ""
+
+#: flaskbb/message/forms.py:29 flaskbb/message/forms.py:60
+msgid "A message is required."
+msgstr "Un message est requis."
+
+#: flaskbb/message/forms.py:31
+msgid "Start Conversation"
+msgstr "Débuter la conversation"
+
+#: flaskbb/message/forms.py:32
+msgid "Save Conversation"
+msgstr "Sauvegarder la conversation"
+
+#: flaskbb/message/forms.py:37
+msgid "The username you entered does not exist."
+msgstr "Le nom d'utilisateur que vous avez entré n'existe pas.
+
+#: flaskbb/message/forms.py:40
+msgid "You cannot send a PM to yourself."
+msgstr "Vous ne pouvez pas vous envoyer de message."
+
+#: flaskbb/message/forms.py:61
+msgid "Send Message"
+msgstr "Envois du message"
+
+#: flaskbb/message/views.py:71 flaskbb/message/views.py:127
+msgid ""
+"You cannot send any messages anymore because you have reached your "
+"message limit."
+msgstr "Vous ne pouvez plus envoyer de message parce que vous avez atteint "
+"votre limite de message."
+
+#: flaskbb/message/views.py:144 flaskbb/message/views.py:216
+msgid "Message saved."
+msgstr "Message sauvegardé."
+
+#: flaskbb/message/views.py:169 flaskbb/message/views.py:234
+msgid "Message sent."
+msgstr "Message envoyé."
+
+#: flaskbb/message/views.py:175
+msgid "Compose Message"
+msgstr "Rédiger un message"
+
+#: flaskbb/message/views.py:202
+msgid "You cannot edit a sent message."
+msgstr "Vous ne pouvez pas modifier un message envoyé."
+
+#: flaskbb/message/views.py:242
+msgid "Edit Message"
+msgstr "Modifier un message"
+
+#: flaskbb/templates/forum/memberlist.html:1
+#: flaskbb/templates/forum/memberlist.html:10
+#: flaskbb/templates/forum/memberlist.html:36 flaskbb/templates/layout.html:74
+#: flaskbb/templates/navigation.html:20
+msgid "Memberlist"
+msgstr "Liste de membre"
+
+#: flaskbb/templates/layout.html:87 flaskbb/templates/layout.html:103
+#: flaskbb/templates/message/inbox.html:1
+#: flaskbb/templates/message/message_layout.html:18
+msgid "Inbox"
+msgstr "Boîte de réception"
+
+#: flaskbb/templates/layout.html:104
+#: flaskbb/templates/message/message_layout.html:16
+#: flaskbb/templates/navigation.html:54
+msgid "New Message"
+msgstr "Nouveau message"
+
+#: flaskbb/templates/forum/topictracker.html:1
+#: flaskbb/templates/forum/topictracker.html:15
+#: flaskbb/templates/forum/topictracker.html:26
+#: flaskbb/templates/layout.html:116 flaskbb/templates/navigation.html:35
+msgid "Topic Tracker"
+msgstr "Suivre le sujet"
+
+#: flaskbb/templates/layout.html:119
+#: flaskbb/templates/management/management_layout.html:17
+#: flaskbb/templates/navigation.html:38
+#: flaskbb/templates/user/settings_layout.html:8
+msgid "Settings"
+msgstr "Paramètres"
+
+#: flaskbb/templates/layout.html:121
+#: flaskbb/templates/management/banned_users.html:9
+#: flaskbb/templates/management/category_form.html:9
+#: flaskbb/templates/management/forum_form.html:9
+#: flaskbb/templates/management/forums.html:8
+#: flaskbb/templates/management/group_form.html:9
+#: flaskbb/templates/management/groups.html:8
+#: flaskbb/templates/management/overview.html:8
+#: flaskbb/templates/management/plugins.html:8
+#: flaskbb/templates/management/reports.html:9
+#: flaskbb/templates/management/settings.html:8
+#: flaskbb/templates/management/unread_reports.html:9
+#: flaskbb/templates/management/user_form.html:9
+#: flaskbb/templates/management/users.html:9
+#: flaskbb/templates/navigation.html:40
+msgid "Management"
+msgstr "Gestion"
+
+#: flaskbb/templates/layout.html:125 flaskbb/templates/navigation.html:44
+msgid "Logout"
+msgstr "Déconnexion"
+
+#: flaskbb/templates/auth/reset_password.html:1
+#: flaskbb/templates/auth/reset_password.html:10
+#: flaskbb/templates/layout.html:141 flaskbb/templates/navigation.html:67
+msgid "Reset Password"
+msgstr "Réinitialiser le mot de passe"
+
+#: flaskbb/templates/macros.html:325
+msgid "Pages"
+msgstr ""
+
+#: flaskbb/templates/navigation.html:53
+msgid "Private Messages"
+msgstr "Messages privés."
+
+#: flaskbb/templates/auth/forgot_password.html:1
+#: flaskbb/templates/auth/forgot_password.html:10
+msgid "Forgot Password"
+msgstr "Mot de passe oublié"
+
+#: flaskbb/templates/auth/login.html:27
+msgid "Not a member yet?"
+msgstr "Pas encore membre ?"
+
+#: flaskbb/templates/auth/login.html:28
+msgid "Forgot your Password?"
+msgstr "Mot de passe oublié ?"
+
+#: flaskbb/templates/auth/request_account_activation.html:1
+#: flaskbb/templates/auth/request_account_activation.html:10
+msgid "Request Account Activation"
+msgstr "Demander l'activation du compte"
+
+#: flaskbb/templates/email/activate_account.html:3
+#: flaskbb/templates/email/reset_password.html:2
+#, python-format
+msgid "Dear %(user)s,"
+msgstr "Che(è)r(e) %(user)s, "
+
+#: flaskbb/templates/email/activate_account.html:5
+msgid "Click the link below to activate your account:"
+msgstr "Cliquez sur le lien au-dessus pour activer votre compte:"
+
+#: flaskbb/templates/email/activate_account.html:9
+#: flaskbb/templates/email/reset_password.html:8
+msgid "Sincerely,"
+msgstr "Cordialement, "
+
+#: flaskbb/templates/email/activate_account.html:10
+#: flaskbb/templates/email/reset_password.html:9
+msgid "The Administration"
+msgstr "L'administration"
+
+#: flaskbb/templates/email/reset_password.html:4
+msgid "Click the link below to reset your password:"
+msgstr "Cliquez sur le lien au-dessus pour réinitialiser votre mot de passe:"
+
+#: flaskbb/templates/errors/forbidden_page.html:1
+msgid "Forbidden"
+msgstr "Interdit"
+
+#: flaskbb/templates/errors/forbidden_page.html:9
+msgid "403 - Forbidden Page"
+msgstr "403 - Page Interdite"
+
+#: flaskbb/templates/errors/forbidden_page.html:10
+msgid "You do not have the permission to view this page."
+msgstr "Vous n'avez pas la permission de voir cette page."
+
+#: flaskbb/templates/errors/forbidden_page.html:11
+#: flaskbb/templates/errors/page_not_found.html:11
+msgid "Back to the Forums"
+msgstr "Retour aux Forums"
+
+#: flaskbb/templates/errors/page_not_found.html:1
+msgid "Page not found"
+msgstr "Page non trouvée"
+
+#: flaskbb/templates/errors/page_not_found.html:9
+msgid "404 - Page not found!"
+msgstr "404 - Page non trouvée"
+
+#: flaskbb/templates/errors/page_not_found.html:10
+msgid "The page you were looking for does not exist."
+msgstr "La paege que vous recherchez n'existe pas."
+
+#: flaskbb/templates/errors/server_error.html:1
+msgid "Server Error"
+msgstr "Erreur serveur"
+
+#: flaskbb/templates/errors/server_error.html:9
+msgid "500 - Server Error"
+msgstr "500 - Erreur serveur"
+
+#: flaskbb/templates/errors/server_error.html:10
+msgid "Something went wrong!"
+msgstr "Quelque chose à mal tourné !"
+
+#: flaskbb/templates/errors/too_many_logins.html:1
+msgid "Too Many Requests"
+msgstr "Trop de requêtes"
+
+#: flaskbb/templates/errors/too_many_logins.html:9
+msgid "429 - Too Many Requests"
+msgstr "429 - Trop de requêtes"
+
+#: flaskbb/templates/errors/too_many_logins.html:10
+msgid ""
+"In order to prevent brute force attacks on accounts we have limited the "
+"amount of requests on this route."
+msgstr "Afin d'empêcher les attaques de force brute sur les comptes, nous avons limité le "
+"nombre de demandes sur cette url."
+
+#: flaskbb/templates/forum/category_layout.html:9
+#: flaskbb/templates/forum/search_result.html:129
+#: flaskbb/templates/forum/search_result.html:214
+#: flaskbb/templates/management/overview.html:85
+#: flaskbb/templates/user/all_posts.html:28
+#: flaskbb/templates/user/all_topics.html:8
+#: flaskbb/templates/user/all_topics.html:28
+#: flaskbb/templates/user/profile_layout.html:69
+msgid "Topics"
+msgstr "Sujets"
+
+#: flaskbb/templates/forum/category_layout.html:10
+#: flaskbb/templates/forum/edit_forum.html:32
+#: flaskbb/templates/forum/forum.html:50
+#: flaskbb/templates/forum/memberlist.html:50
+#: flaskbb/templates/forum/search_result.html:16
+#: flaskbb/templates/forum/search_result.html:40
+#: flaskbb/templates/forum/search_result.html:105
+#: flaskbb/templates/forum/search_result.html:135
+#: flaskbb/templates/forum/search_result.html:215
+#: flaskbb/templates/forum/topic.html:44
+#: flaskbb/templates/forum/topic_horizontal.html:55
+#: flaskbb/templates/forum/topictracker.html:32
+#: flaskbb/templates/management/banned_users.html:63
+#: flaskbb/templates/management/overview.html:88
+#: flaskbb/templates/management/users.html:63
+#: flaskbb/templates/message/conversation.html:44
+#: flaskbb/templates/message/conversation.html:95
+#: flaskbb/templates/user/all_posts.html:8
+#: flaskbb/templates/user/all_posts.html:34
+#: flaskbb/templates/user/all_topics.html:34
+#: flaskbb/templates/user/profile_layout.html:75
+msgid "Posts"
+msgstr "Messages"
+
+#: flaskbb/templates/forum/category_layout.html:11
+#: flaskbb/templates/forum/edit_forum.html:34
+#: flaskbb/templates/forum/forum.html:52
+#: flaskbb/templates/forum/search_result.html:137
+#: flaskbb/templates/forum/search_result.html:216
+#: flaskbb/templates/forum/topictracker.html:34
+msgid "Last Post"
+msgstr "Dernier message"
+
+#: flaskbb/templates/forum/category_layout.html:27
+#: flaskbb/templates/forum/search_result.html:232
+#: flaskbb/templates/management/forums.html:80
+msgid "Link to"
+msgstr "Liens vers"
+
+#: flaskbb/templates/forum/category_layout.html:114
+#: flaskbb/templates/forum/edit_forum.html:68
+#: flaskbb/templates/forum/edit_forum.html:91
+#: flaskbb/templates/forum/forum.html:85 flaskbb/templates/forum/forum.html:107
+#: flaskbb/templates/forum/search_result.html:162
+#: flaskbb/templates/forum/search_result.html:184
+#: flaskbb/templates/forum/search_result.html:317
+#: flaskbb/templates/forum/topictracker.html:68
+#: flaskbb/templates/forum/topictracker.html:91
+#: flaskbb/templates/management/plugins.html:42
+msgid "by"
+msgstr "par"
+
+#: flaskbb/templates/forum/category_layout.html:123
+#: flaskbb/templates/forum/search_result.html:326
+msgid "No posts."
+msgstr "Pas de messages."
+
+#: flaskbb/templates/forum/edit_forum.html:33
+#: flaskbb/templates/forum/forum.html:51
+#: flaskbb/templates/forum/search_result.html:136
+#: flaskbb/templates/forum/topictracker.html:33
+msgid "Views"
+msgstr "Vues"
+
+#: flaskbb/templates/forum/edit_forum.html:107
+#: flaskbb/templates/forum/forum.html:120
+#: flaskbb/templates/forum/topictracker.html:107
+msgid "No Topics."
+msgstr "Pas de sujets."
+
+#: flaskbb/templates/forum/edit_forum.html:117
+msgid "Back"
+msgstr "Retour"
+
+#: flaskbb/templates/forum/edit_forum.html:128
+msgid "Lock"
+msgstr "Bloquer"
+
+#: flaskbb/templates/forum/edit_forum.html:131
+msgid "Unlock"
+msgstr "Débloquer"
+
+#: flaskbb/templates/forum/edit_forum.html:137
+msgid "Highlight"
+msgstr "Mise en avant"
+
+#: flaskbb/templates/forum/edit_forum.html:140
+msgid "Trivialize"
+msgstr "Banaliser"
+
+#: flaskbb/templates/forum/edit_forum.html:145
+#: flaskbb/templates/management/groups.html:64
+#: flaskbb/templates/management/users.html:132
+msgid "Delete"
+msgstr "Supprimer"
+
+#: flaskbb/templates/forum/edit_forum.html:158
+msgid "Move to..."
+msgstr "Déplacer vers"
+
+#: flaskbb/templates/forum/edit_forum.html:165
+msgid "Move"
+msgstr "Déplacer"
+
+#: flaskbb/templates/forum/forum.html:25
+#: flaskbb/templates/management/unread_reports.html:50
+#: flaskbb/templates/management/unread_reports.html:69
+msgid "Mark as Read"
+msgstr "Marquer comme lu"
+
+#: flaskbb/templates/forum/forum.html:31
+#: flaskbb/templates/forum/topic_controls.html:97
+msgid "Locked"
+msgstr "Bloqué"
+
+#: flaskbb/templates/forum/forum.html:35
+#: flaskbb/templates/forum/new_topic.html:1
+#: flaskbb/templates/forum/new_topic.html:13
+#: flaskbb/templates/forum/new_topic.html:21
+msgid "New Topic"
+msgstr "Nouveau sujet"
+
+#: flaskbb/templates/forum/forum.html:130
+msgid "Moderation Mode"
+msgstr "Mode modération"
+
+#: flaskbb/templates/forum/index.html:11
+msgid "Board Statistics"
+msgstr "Statistiques du forum"
+
+#: flaskbb/templates/forum/index.html:12
+msgid "Who is online?"
+msgstr "Qui est en ligne ?"
+
+#: flaskbb/templates/forum/index.html:17
+msgid "Total number of registered users"
+msgstr "Nombre total d'utilisateur enregistré"
+
+#: flaskbb/templates/forum/index.html:18
+msgid "Total number of topics"
+msgstr "Nombre total de sujets"
+
+#: flaskbb/templates/forum/index.html:19
+msgid "Total number of posts"
+msgstr "Nombre total de messages"
+
+#: flaskbb/templates/forum/index.html:22
+msgid "Newest registered user"
+msgstr "Nouvel utilisateur enregistré"
+
+#: flaskbb/templates/forum/index.html:22
+msgid "No users"
+msgstr "Aucun utilisateurs"
+
+#: flaskbb/templates/forum/index.html:23
+msgid "Registered users online"
+msgstr "Utilisateurs enregistrés en ligne "
+
+#: flaskbb/templates/forum/index.html:25
+msgid "Guests online"
+msgstr "Visiteurs en ligne"
+
+#: flaskbb/templates/forum/memberlist.html:46
+#: flaskbb/templates/forum/search_result.html:106
+#: flaskbb/templates/management/banned_users.html:64
+#: flaskbb/templates/management/users.html:64
+msgid "Date registered"
+msgstr "Date d'enregistrement"
+
+#: flaskbb/templates/forum/memberlist.html:48
+#: flaskbb/templates/forum/search_result.html:107
+#: flaskbb/templates/management/banned_users.html:65
+#: flaskbb/templates/management/users.html:65
+msgid "Group"
+msgstr "Groupe"
+
+#: flaskbb/templates/forum/new_post.html:1
+#: flaskbb/templates/forum/new_post.html:14
+#: flaskbb/templates/forum/new_post.html:21
+msgid "New Post"
+msgstr "Nouveau message"
+
+#: flaskbb/templates/forum/online_users.html:1
+#: flaskbb/templates/forum/online_users.html:15
+msgid "Online Users"
+msgstr "Utilisateurs en ligne"
+
+#: flaskbb/templates/forum/report_post.html:1
+#: flaskbb/templates/forum/report_post.html:16
+msgid "Report Post"
+msgstr "Rapport de message"
+
+#: flaskbb/templates/forum/report_post.html:20
+msgid "Report"
+msgstr "Rapport"
+
+#: flaskbb/templates/forum/report_post.html:21
+msgid "Close"
+msgstr "Fermer"
+
+#: flaskbb/templates/forum/search_result.html:39
+#: flaskbb/templates/forum/topic.html:43
+#: flaskbb/templates/forum/topic_horizontal.html:54
+#: flaskbb/templates/message/conversation.html:43
+#: flaskbb/templates/message/conversation.html:94
+msgid "Joined"
+msgstr "A rejoint"
+
+#: flaskbb/templates/forum/search_result.html:54
+#: flaskbb/templates/forum/topic.html:58
+#: flaskbb/templates/message/conversation.html:106
+msgid "Guest"
+msgstr "Visiteur"
+
+#: flaskbb/templates/forum/search_result.html:89
+msgid "No posts found matching your search criteria."
+msgstr "Aucun messages correspondant à vos critères de recherche."
+
+#: flaskbb/templates/forum/search_result.html:119
+#: flaskbb/templates/management/banned_users.html:104
+#: flaskbb/templates/management/users.html:140
+msgid "No users found matching your search criteria."
+msgstr "Aucun utilisateurs correspondant à vos critères de recherche."
+
+#: flaskbb/templates/forum/search_result.html:197
+msgid "No topics found matching your search criteria."
+msgstr "Aucun sujets correspondant à vos critères de recherche. "
+
+#: flaskbb/templates/forum/search_result.html:335
+msgid "No forums found matching your search criteria."
+msgstr "Aucun forums correspondant à vos critères de recherche"
+
+#: flaskbb/templates/forum/topic.html:2
+#: flaskbb/templates/forum/topic_horizontal.html:2
+#, python-format
+msgid "%(title)s - Topic"
+msgstr "%(title)s - Sujet"
+
+#: flaskbb/templates/forum/topic_controls.html:15
+msgid "Moderate"
+msgstr "Modérer"
+
+#: flaskbb/templates/forum/topic_controls.html:24
+msgid "Delete Topic"
+msgstr "Supprimer le topic"
+
+#: flaskbb/templates/forum/topic_controls.html:36
+msgid "Lock Topic"
+msgstr "Fermer le sujet"
+
+#: flaskbb/templates/forum/topic_controls.html:45
+msgid "Unlock Topic"
+msgstr "Débloquer le sujet"
+
+#: flaskbb/templates/forum/topic_controls.html:56
+msgid "Highlight Topic"
+msgstr "Mise en avant du sujet"
+
+#: flaskbb/templates/forum/topic_controls.html:65
+msgid "Trivialize Topic"
+msgstr "Banaliser le sujet"
+
+#: flaskbb/templates/forum/topic_controls.html:80
+msgid "Untrack Topic"
+msgstr "Ne plus suivre ce sujet"
+
+#: flaskbb/templates/forum/topic_controls.html:87
+msgid "Track Topic"
+msgstr "Suivre ce sujet"
+
+#: flaskbb/templates/forum/topictracker.html:117
+msgid "Untrack Topics"
+msgstr "Ne plus suivre ces sujets"
+
+#: flaskbb/templates/management/banned_users.html:1
+#: flaskbb/templates/management/banned_users.html:21
+#: flaskbb/templates/management/banned_users.html:34
+#: flaskbb/templates/management/user_form.html:21
+#: flaskbb/templates/management/users.html:21
+msgid "Banned Users"
+msgstr "Utilisateurs bannis"
+
+#: flaskbb/templates/management/banned_users.html:10
+#: flaskbb/templates/management/banned_users.html:20
+#: flaskbb/templates/management/user_form.html:10
+#: flaskbb/templates/management/user_form.html:20
+#: flaskbb/templates/management/users.html:10
+#: flaskbb/templates/management/users.html:20
+msgid "Manage Users"
+msgstr "Gérer les utilisateurs"
+
+#: flaskbb/templates/management/banned_users.html:70
+#: flaskbb/templates/management/groups.html:39
+#: flaskbb/templates/management/unread_reports.html:45
+#: flaskbb/templates/management/users.html:69
+msgid "Actions"
+msgstr ""
+
+#: flaskbb/templates/management/banned_users.html:74
+#: flaskbb/templates/management/users.html:79
+msgid "Are you sure you want to unban these Users?"
+msgstr "Etes-vous sûr de vouloir ré-habiliter ces utilisateurs ?"
+
+#: flaskbb/templates/management/banned_users.html:75
+#: flaskbb/templates/management/users.html:80
+msgid "Unban selected Users"
+msgstr "Utilisateurs sélectionnés ré-habilités"
+
+#: flaskbb/templates/management/category_form.html:20
+#: flaskbb/templates/management/forum_form.html:20
+#: flaskbb/templates/management/forums.html:19
+#: flaskbb/templates/management/forums.html:30
+msgid "Manage Forums"
+msgstr "Gérer les forums"
+
+#: flaskbb/templates/management/forums.html:1
+#: flaskbb/templates/management/forums.html:9
+#: flaskbb/templates/management/management_layout.html:19
+msgid "Forums"
+msgstr ""
+
+#: flaskbb/templates/management/forums.html:52
+msgid "Delete Category"
+msgstr "Supprimer la catégorie"
+
+#: flaskbb/templates/management/forums.html:63
+msgid "Topics / Posts"
+msgstr "Sujets / Messages"
+
+#: flaskbb/templates/management/forums.html:100
+msgid "Edit Link"
+msgstr "Modifier le lien"
+
+#: flaskbb/templates/management/forums.html:105
+msgid "Delete Link"
+msgstr "Supprimer le lien"
+
+#: flaskbb/templates/management/forums.html:159
+msgid "Delete Forum"
+msgstr "Supprimer le forum"
+
+#: flaskbb/templates/management/group_form.html:10
+#: flaskbb/templates/management/group_form.html:20
+#: flaskbb/templates/management/groups.html:9
+#: flaskbb/templates/management/groups.html:19
+msgid "Manage Groups"
+msgstr "Gérer les groupes"
+
+#: flaskbb/templates/management/groups.html:1
+#: flaskbb/templates/management/groups.html:28
+#: flaskbb/templates/management/management_layout.html:18
+#: flaskbb/templates/management/overview.html:82
+msgid "Groups"
+msgstr "Groupes"
+
+#: flaskbb/templates/management/groups.html:34
+msgid "Group Name"
+msgstr "Nom de groupe"
+
+#: flaskbb/templates/management/groups.html:43
+msgid "Are you sure you want to delete these Groups?"
+msgstr "Etes-vous sûr de vouloir supprier ces groupes ?"
+
+#: flaskbb/templates/management/groups.html:44
+msgid "Delete selected Groups"
+msgstr "Supprimer les groupes sélectionnés"
+
+#: flaskbb/templates/management/groups.html:59
+#: flaskbb/templates/management/users.html:103
+msgid "Edit"
+msgstr "Modifier"
+
+#: flaskbb/templates/management/groups.html:71
+msgid "No groups found."
+msgstr "Aucun groupes trouvés."
+
+#: flaskbb/templates/management/management_layout.html:12
+#: flaskbb/templates/management/overview.html:1
+#: flaskbb/templates/management/overview.html:16
+#: flaskbb/templates/user/all_posts.html:16
+#: flaskbb/templates/user/all_topics.html:16
+#: flaskbb/templates/user/profile_layout.html:57
+msgid "Overview"
+msgstr "Aperçu"
+
+#: flaskbb/templates/management/management_layout.html:14
+#: flaskbb/templates/management/overview.html:91
+#: flaskbb/templates/management/reports.html:1
+#: flaskbb/templates/management/reports.html:10
+msgid "Reports"
+msgstr "Rapports"
+
+#: flaskbb/templates/management/management_layout.html:20
+#: flaskbb/templates/management/overview.html:115
+#: flaskbb/templates/management/plugins.html:1
+#: flaskbb/templates/management/plugins.html:9
+msgid "Plugins"
+msgstr ""
+
+#: flaskbb/templates/management/overview.html:25
+msgid "Everything seems alright."
+msgstr "Tout semble bien"
+
+#: flaskbb/templates/management/overview.html:26
+msgid "No new notifications."
+msgstr "Aucunes nouvelles notification."
+
+#: flaskbb/templates/management/overview.html:38
+msgid "users"
+msgstr "utilisateurs"
+
+#: flaskbb/templates/management/overview.html:50
+msgid "posts"
+msgstr "messages"
+
+#: flaskbb/templates/management/overview.html:62
+msgid "topics"
+msgstr "sujets"
+
+#: flaskbb/templates/management/overview.html:70
+msgid "Statistics"
+msgstr "Statistiques"
+
+#: flaskbb/templates/management/overview.html:73
+msgid "Registered users"
+msgstr "Utilisateur enregistrés"
+
+#: flaskbb/templates/management/overview.html:76
+msgid "Online users"
+msgstr "Utilisateurs en ligne"
+
+#: flaskbb/templates/management/overview.html:79
+msgid "Banned users"
+msgstr "Utilisateurs bannis"
+
+#: flaskbb/templates/management/overview.html:96
+msgid "Components"
+msgstr "Composants"
+
+#: flaskbb/templates/management/plugins.html:19
+msgid "Manage Plugins"
+msgstr "Gérer les plugins"
+
+#: flaskbb/templates/management/plugins.html:25
+msgid "Plugin"
+msgstr ""
+
+#: flaskbb/templates/management/plugins.html:26
+msgid "Information"
+msgstr ""
+
+#: flaskbb/templates/management/plugins.html:27
+msgid "Manage"
+msgstr "Gérer"
+
+#: flaskbb/templates/management/plugins.html:40
+msgid "Version"
+msgstr ""
+
+#: flaskbb/templates/management/plugins.html:48
+msgid "Enable"
+msgstr "Activer"
+
+#: flaskbb/templates/management/plugins.html:53
+msgid "Disable"
+msgstr "Désactiver"
+
+#: flaskbb/templates/management/plugins.html:60
+msgid "Install"
+msgstr "Installer"
+
+#: flaskbb/templates/management/plugins.html:66
+msgid "Uninstall"
+msgstr "Désinstaller"
+
+#: flaskbb/templates/management/reports.html:21
+#: flaskbb/templates/management/unread_reports.html:21
+msgid "Show unread reports"
+msgstr "Voir les rapports non-lus"
+
+#: flaskbb/templates/management/reports.html:22
+#: flaskbb/templates/management/unread_reports.html:22
+msgid "Show all reports"
+msgstr "Voir tous les rapports"
+
+#: flaskbb/templates/management/reports.html:31
+msgid "All Reports"
+msgstr "Tout les rapports"
+
+#: flaskbb/templates/management/reports.html:37
+#: flaskbb/templates/management/unread_reports.html:37
+msgid "Poster"
+msgstr "Afficher"
+
+#: flaskbb/templates/management/reports.html:40
+#: flaskbb/templates/management/unread_reports.html:40
+msgid "Reporter"
+msgstr "Rapporter"
+
+#: flaskbb/templates/management/reports.html:41
+#: flaskbb/templates/management/unread_reports.html:41
+msgid "Reported"
+msgstr "Rapporté"
+
+#: flaskbb/templates/management/reports.html:54
+#: flaskbb/templates/management/unread_reports.html:76
+msgid "No unread reports."
+msgstr "Aucun rapports non-lus."
+
+#: flaskbb/templates/management/unread_reports.html:1
+#: flaskbb/templates/management/unread_reports.html:10
+#: flaskbb/templates/management/unread_reports.html:31
+msgid "Unread Reports"
+msgstr "Rapports non-lus"
+
+#: flaskbb/templates/management/unread_reports.html:49
+msgid "Are you sure you want to mark these Reports as read?"
+msgstr "Ete-vous sûr de vouloir marquer ces rapports comme lus ?"
+
+#: flaskbb/templates/management/users.html:73
+msgid "Are you sure you want to ban these Users?"
+msgstr "Etes-vous sûr de vouloir bannir ces utilisateurs ?"
+
+#: flaskbb/templates/management/users.html:74
+msgid "Ban selected Users"
+msgstr "Utilisateurs sélectionnés bannis"
+
+#: flaskbb/templates/management/users.html:85
+msgid "Are you sure you want to delete these Users?"
+msgstr "Etes-vous sûr de vouloir supprimer ces utilisateurs ?"
+
+#: flaskbb/templates/management/users.html:86
+msgid "Delete selected Users"
+msgstr "Utilisateurs sélectionnés supprimés"
+
+#: flaskbb/templates/message/conversation.html:105
+msgid "Deleted"
+msgstr "Supprimé"
+
+#: flaskbb/templates/message/conversation_list.html:6
+msgid "Conversations"
+msgstr ""
+
+#: flaskbb/templates/message/conversation_list.html:88
+msgid "No conversations found."
+msgstr "Aucune conversation trouvée"
+
+#: flaskbb/templates/message/drafts.html:1
+#: flaskbb/templates/message/message_layout.html:20
+msgid "Drafts"
+msgstr "Brouillons"
+
+#: flaskbb/templates/message/message_layout.html:8
+msgid "Private Message"
+msgstr "Message privé"
+
+#: flaskbb/templates/message/message_layout.html:19
+msgid "Sent"
+msgstr "Envoyer"
+
+#: flaskbb/templates/message/message_layout.html:21
+#: flaskbb/templates/message/trash.html:1
+msgid "Trash"
+msgstr "Corbeille"
+
+#: flaskbb/templates/message/sent.html:1
+msgid "Sent Messages"
+msgstr "Envoyer les messages"
+
+#: flaskbb/templates/user/all_posts.html:65
+msgid "The user has not written any posts yet."
+msgstr "L'utilisateur n'a pas encore écrit de messages."
+
+#: flaskbb/templates/user/all_topics.html:67
+msgid "The user has not opened any topics yet."
+msgstr "L'utilisateur n'a pas encore ouvert de sujets."
+
+#: flaskbb/templates/user/change_email.html:7
+#: flaskbb/templates/user/settings_layout.html:18
+msgid "Change E-Mail Address"
+msgstr "Changer l'adresse E-Mail"
+
+#: flaskbb/templates/user/change_password.html:7
+#: flaskbb/templates/user/settings_layout.html:19
+msgid "Change Password"
+msgstr "Changer le mot de passe"
+
+#: flaskbb/templates/user/change_user_details.html:8
+#: flaskbb/templates/user/settings_layout.html:17
+msgid "Change User Details"
+msgstr "Changer les détails utilisateur"
+
+#: flaskbb/templates/user/general_settings.html:7
+#: flaskbb/templates/user/settings_layout.html:16
+msgid "General Settings"
+msgstr "Paramètres général"
+
+#: flaskbb/templates/user/profile.html:19
+msgid "Info"
+msgstr ""
+
+#: flaskbb/templates/user/profile.html:25
+msgid "User has not added any notes about him."
+msgstr "L'utilisateur n'a pas encore ajouté de notes à son sujet."
+
+#: flaskbb/templates/user/profile.html:34
+msgid "Signature"
+msgstr ""
+
+#: flaskbb/templates/user/profile_layout.html:27
+msgid "Never seen"
+msgstr "Jamais vu"
+
+#: flaskbb/templates/user/settings_layout.html:15
+msgid "Account Settings"
+msgstr "Paramètres de compte"
+
+#: flaskbb/user/forms.py:30
+msgid "Theme"
+msgstr "Thème"
+
+#: flaskbb/user/forms.py:36
+msgid "Old email address"
+msgstr "Ancienne adresse email"
+
+#: flaskbb/user/forms.py:40
+msgid "New email address"
+msgstr "Nouvelle adresse email"
+
+#: flaskbb/user/forms.py:42
+msgid "Email addresses must match."
+msgstr "Les adresses email doivent correspondre."
+
+#: flaskbb/user/forms.py:45
+msgid "Confirm email address"
+msgstr "Confirmer l'adresse email"
+
+#: flaskbb/user/forms.py:67
+msgid "New password"
+msgstr "Nouveau mot de passe"
+
+#: flaskbb/user/forms.py:69
+msgid "New passwords must match."
+msgstr "Les mots de passe doivent correspondre."
+
+#: flaskbb/user/forms.py:72
+msgid "Confirm new password"
+msgstr "Confirmer le nouveau mot de passe"
+
+#: flaskbb/user/forms.py:78
+msgid "Old password is wrong."
+msgstr "L'ancien mot de passe est incorrect"
+
+#: flaskbb/user/forms.py:100
+msgid "Forum Signature"
+msgstr "Signature du forum"
+
+#: flaskbb/user/views.py:66
+msgid "Settings updated."
+msgstr "Paramètre mis à jour."
+
+#: flaskbb/user/views.py:82
+msgid "Password updated."
+msgstr "Mot de passe mis à jour."
+
+#: flaskbb/user/views.py:94
+msgid "Email address updated."
+msgstr "Adresse email mise à jour."
+
+#: flaskbb/user/views.py:107
+msgid "User details updated."
+msgstr "Détails utilisateurs mis à jour."
+
+#: flaskbb/utils/helpers.py:92
+msgid "You do not have the permissions to execute this action."
+msgstr "Vous n'avez pas la permission d'exécuter cette action."

+ 2 - 4
flaskbb/user/forms.py

@@ -18,7 +18,6 @@ from flask_babelplus import lazy_gettext as _
 
 from flaskbb.user.models import User
 from flaskbb.extensions import db
-from flaskbb.utils.widgets import SelectBirthdayWidget
 from flaskbb.utils.fields import BirthdayField
 from flaskbb.utils.helpers import check_image
 
@@ -79,9 +78,8 @@ class ChangePasswordForm(FlaskForm):
 
 
 class ChangeUserDetailsForm(FlaskForm):
-    birthday = BirthdayField(_("Birthday"), format="%d %m %Y",
-                             validators=[Optional()],
-                             widget=SelectBirthdayWidget())
+    birthday = BirthdayField(_("Birthday"), format="%d %m %Y", validators=[
+        Optional()])
 
     gender = SelectField(_("Gender"), default="None", choices=[
         ("None", ""),

+ 6 - 7
flaskbb/user/models.py

@@ -12,7 +12,6 @@ from werkzeug.security import generate_password_hash, check_password_hash
 from flask import url_for
 from flask_login import UserMixin, AnonymousUserMixin
 
-from flaskbb._compat import max_integer
 from flaskbb.extensions import db, cache
 from flaskbb.exceptions import AuthenticationError
 from flaskbb.utils.helpers import time_utcnow
@@ -89,7 +88,7 @@ class User(db.Model, UserMixin, CRUDMixin):
     _password = db.Column('password', db.String(120), nullable=False)
     date_joined = db.Column(UTCDateTime(timezone=True), default=time_utcnow)
     lastseen = db.Column(UTCDateTime(timezone=True), default=time_utcnow)
-    birthday = db.Column(UTCDateTime(timezone=True))
+    birthday = db.Column(db.DateTime)
     gender = db.Column(db.String(10))
     website = db.Column(db.String(200))
     location = db.Column(db.String(100))
@@ -338,12 +337,12 @@ class User(db.Model, UserMixin, CRUDMixin):
         return self.secondary_groups.filter(
             groups_users.c.group_id == group.id).count() > 0
 
-    @cache.memoize(timeout=max_integer)
+    @cache.memoize()
     def get_groups(self):
         """Returns all the groups the user is in."""
         return [self.primary_group] + list(self.secondary_groups)
 
-    @cache.memoize(timeout=max_integer)
+    @cache.memoize()
     def get_permissions(self, exclude=None):
         """Returns a dictionary with all permissions the user has"""
         if exclude:
@@ -360,7 +359,7 @@ class User(db.Model, UserMixin, CRUDMixin):
                 perms[c] = getattr(group, c) or perms.get(c, False)
         return perms
 
-    @cache.memoize(timeout=max_integer)
+    @cache.memoize()
     def get_unread_messages(self):
         """Returns all unread messages for the user."""
         unread_messages = Conversation.query.\
@@ -471,11 +470,11 @@ class Guest(AnonymousUserMixin):
     def groups(self):
         return self.get_groups()
 
-    @cache.memoize(timeout=max_integer)
+    @cache.memoize()
     def get_groups(self):
         return Group.query.filter(Group.guest == True).all()
 
-    @cache.memoize(timeout=max_integer)
+    @cache.memoize()
     def get_permissions(self, exclude=None):
         """Returns a dictionary with all permissions the user has"""
         if exclude:

+ 215 - 2
flaskbb/utils/fields.py

@@ -3,19 +3,232 @@
     flaskbb.utils.fields
     ~~~~~~~~~~~~~~~~~~~~
 
-    Additional fields for wtforms
+    Additional fields and widgets for wtforms.
+    The reCAPTCHA Field was taken from Flask-WTF and modified
+    to use our own settings system.
 
     :copyright: (c) 2014 by the FlaskBB Team.
     :license: BSD, see LICENSE for more details.
 """
 from datetime import datetime
-from wtforms.fields import DateField
+try:
+    import urllib2 as http
+except ImportError:
+    # Python 3
+    from urllib import request as http
+
+from flask import request, current_app, Markup, json
+from werkzeug import url_encode
+from wtforms import ValidationError
+from wtforms.fields import DateField, Field
+from wtforms.widgets.core import Select, HTMLString, html_params
+
+from flaskbb._compat import to_bytes, to_unicode
+from flaskbb.utils.settings import flaskbb_config
+
+JSONEncoder = json.JSONEncoder
+
+RECAPTCHA_SCRIPT = u'https://www.google.com/recaptcha/api.js'
+RECAPTCHA_TEMPLATE = u'''
+<script src='%s' async defer></script>
+<div class="g-recaptcha" %s></div>
+'''
+
+RECAPTCHA_VERIFY_SERVER = 'https://www.google.com/recaptcha/api/siteverify'
+RECAPTCHA_ERROR_CODES = {
+    'missing-input-secret': 'The secret parameter is missing.',
+    'invalid-input-secret': 'The secret parameter is invalid or malformed.',
+    'missing-input-response': 'The response parameter is missing.',
+    'invalid-input-response': 'The response parameter is invalid or malformed.'
+}
+
+
+class RecaptchaValidator(object):
+    """Validates a ReCaptcha."""
+
+    def __init__(self, message=None):
+        if message is None:
+            message = RECAPTCHA_ERROR_CODES['missing-input-response']
+        self.message = message
+
+    def __call__(self, form, field):
+        if current_app.testing or not flaskbb_config["RECAPTCHA_ENABLED"]:
+            return True
+
+        if request.json:
+            response = request.json.get('g-recaptcha-response', '')
+        else:
+            response = request.form.get('g-recaptcha-response', '')
+        remote_ip = request.remote_addr
+
+        if not response:
+            raise ValidationError(field.gettext(self.message))
+
+        if not self._validate_recaptcha(response, remote_ip):
+            field.recaptcha_error = 'incorrect-captcha-sol'
+            raise ValidationError(field.gettext(self.message))
+
+    def _validate_recaptcha(self, response, remote_addr):
+        """Performs the actual validation."""
+        try:
+            private_key = flaskbb_config['RECAPTCHA_PRIVATE_KEY']
+        except KeyError:
+            raise RuntimeError("No RECAPTCHA_PRIVATE_KEY config set")
+
+        data = url_encode({
+            'secret': private_key,
+            'remoteip': remote_addr,
+            'response': response
+        })
+
+        http_response = http.urlopen(RECAPTCHA_VERIFY_SERVER, to_bytes(data))
+
+        if http_response.code != 200:
+            return False
+
+        json_resp = json.loads(to_unicode(http_response.read()))
+
+        if json_resp["success"]:
+            return True
+
+        for error in json_resp.get("error-codes", []):
+            if error in RECAPTCHA_ERROR_CODES:
+                raise ValidationError(RECAPTCHA_ERROR_CODES[error])
+
+        return False
+
+
+class RecaptchaWidget(object):
+
+    def recaptcha_html(self, public_key):
+        html = current_app.config.get('RECAPTCHA_HTML')
+        if html:
+            return Markup(html)
+        params = current_app.config.get('RECAPTCHA_PARAMETERS')
+        script = RECAPTCHA_SCRIPT
+        if params:
+            script += u'?' + url_encode(params)
+
+        attrs = current_app.config.get('RECAPTCHA_DATA_ATTRS', {})
+        attrs['sitekey'] = public_key
+        snippet = u' '.join([u'data-%s="%s"' % (k, attrs[k]) for k in attrs])
+        return Markup(RECAPTCHA_TEMPLATE % (script, snippet))
+
+    def __call__(self, field, error=None, **kwargs):
+        """Returns the recaptcha input HTML."""
+
+        if not flaskbb_config["RECAPTCHA_ENABLED"]:
+            return
+
+        try:
+            public_key = flaskbb_config['RECAPTCHA_PUBLIC_KEY']
+        except KeyError:
+            raise RuntimeError("RECAPTCHA_PUBLIC_KEY config not set")
+
+        return self.recaptcha_html(public_key)
+
+
+class RecaptchaField(Field):
+    widget = RecaptchaWidget()
+
+    # error message if recaptcha validation fails
+    recaptcha_error = None
+
+    def __init__(self, label='', validators=None, **kwargs):
+        validators = validators or [RecaptchaValidator()]
+        super(RecaptchaField, self).__init__(label, validators, **kwargs)
+
+
+class SelectBirthdayWidget(object):
+    """Renders a DateTime field with 3 selects.
+    For more information see: http://stackoverflow.com/a/14664504
+    """
+    FORMAT_CHOICES = {
+        '%d': [(x, str(x)) for x in range(1, 32)],
+        '%m': [(x, str(x)) for x in range(1, 13)]
+    }
+
+    FORMAT_CLASSES = {
+        '%d': 'select_date_day',
+        '%m': 'select_date_month',
+        '%Y': 'select_date_year'
+    }
+
+    def __init__(self, years=range(1930, datetime.utcnow().year + 1)):
+        """Initialzes the widget.
+
+        :param years: The min year which should be chooseable.
+                      Defatuls to ``1930``.
+        """
+        super(SelectBirthdayWidget, self).__init__()
+        self.FORMAT_CHOICES['%Y'] = [(x, str(x)) for x in years]
+
+    def __call__(self, field, **kwargs):
+        field_id = kwargs.pop('id', field.id)
+        html = []
+        allowed_format = ['%d', '%m', '%Y']
+        surrounded_div = kwargs.pop('surrounded_div', None)
+        css_class = kwargs.get('class', None)
+
+        for date_format in field.format.split():
+            if date_format in allowed_format:
+                choices = self.FORMAT_CHOICES[date_format]
+                id_suffix = date_format.replace('%', '-')
+                id_current = field_id + id_suffix
+
+                if css_class is not None:  # pragma: no cover
+                    select_class = "{} {}".format(
+                        css_class, self.FORMAT_CLASSES[date_format]
+                    )
+                else:
+                    select_class = self.FORMAT_CLASSES[date_format]
+
+                kwargs['class'] = select_class
+
+                try:
+                    del kwargs['placeholder']
+                except KeyError:
+                    pass
+
+                if surrounded_div is not None:
+                    html.append('<div class="%s">' % surrounded_div)
+
+                html.append('<select %s>' % html_params(name=field.name,
+                                                        id=id_current,
+                                                        **kwargs))
+
+                if field.data:
+                    current_value = int(field.data.strftime(date_format))
+                else:
+                    current_value = None
+
+                for value, label in choices:
+                    selected = (value == current_value)
+
+                    # Defaults to blank
+                    if value == 1 or value == 1930:
+                        html.append(
+                            Select.render_option("None", " ", selected)
+                        )
+
+                    html.append(Select.render_option(value, label, selected))
+
+                html.append('</select>')
+
+                if surrounded_div is not None:
+                    html.append("</div>")
+
+            html.append(' ')
+
+        return HTMLString(''.join(html))
 
 
 class BirthdayField(DateField):
     """Same as DateField, except it allows ``None`` values in case a user
     wants to delete his birthday.
     """
+    widget = SelectBirthdayWidget()
+
     def __init__(self, label=None, validators=None, format='%Y-%m-%d',
                  **kwargs):
         DateField.__init__(self, label, validators, format, **kwargs)

+ 1 - 0
flaskbb/utils/populate.py

@@ -8,6 +8,7 @@
     :copyright: (c) 2014 by the FlaskBB Team.
     :license: BSD, see LICENSE for more details.
 """
+from __future__ import unicode_literals
 from flaskbb.management.models import Setting, SettingsGroup
 from flaskbb.user.models import User, Group
 from flaskbb.forum.models import Post, Topic, Forum, Category

+ 0 - 137
flaskbb/utils/recaptcha.py

@@ -1,137 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-    flaskbb.utils.recaptcha
-    ~~~~~~~~~~~~~~~~~~~~~~~
-
-    The reCAPTCHA Field. Taken from Flask-WTF and modified
-    to use our own settings system.
-
-    :copyright: (c) 2014 by the FlaskBB Team.
-    :license: BSD, see LICENSE for more details.
-"""
-from wtforms.fields import Field
-
-try:
-    import urllib2 as http
-except ImportError:
-    # Python 3
-    from urllib import request as http
-
-from flask import request, current_app, Markup, json
-from werkzeug import url_encode
-from wtforms import ValidationError
-
-from flaskbb._compat import to_bytes, to_unicode
-from flaskbb.utils.settings import flaskbb_config
-
-JSONEncoder = json.JSONEncoder
-
-RECAPTCHA_SCRIPT = u'https://www.google.com/recaptcha/api.js'
-RECAPTCHA_TEMPLATE = u'''
-<script src='%s' async defer></script>
-<div class="g-recaptcha" %s></div>
-'''
-
-RECAPTCHA_VERIFY_SERVER = 'https://www.google.com/recaptcha/api/siteverify'
-RECAPTCHA_ERROR_CODES = {
-    'missing-input-secret': 'The secret parameter is missing.',
-    'invalid-input-secret': 'The secret parameter is invalid or malformed.',
-    'missing-input-response': 'The response parameter is missing.',
-    'invalid-input-response': 'The response parameter is invalid or malformed.'
-}
-
-
-class RecaptchaValidator(object):
-    """Validates a ReCaptcha."""
-
-    def __init__(self, message=None):
-        if message is None:
-            message = RECAPTCHA_ERROR_CODES['missing-input-response']
-        self.message = message
-
-    def __call__(self, form, field):
-        if current_app.testing or not flaskbb_config["RECAPTCHA_ENABLED"]:
-            return True
-
-        if request.json:
-            response = request.json.get('g-recaptcha-response', '')
-        else:
-            response = request.form.get('g-recaptcha-response', '')
-        remote_ip = request.remote_addr
-
-        if not response:
-            raise ValidationError(field.gettext(self.message))
-
-        if not self._validate_recaptcha(response, remote_ip):
-            field.recaptcha_error = 'incorrect-captcha-sol'
-            raise ValidationError(field.gettext(self.message))
-
-    def _validate_recaptcha(self, response, remote_addr):
-        """Performs the actual validation."""
-        try:
-            private_key = flaskbb_config['RECAPTCHA_PRIVATE_KEY']
-        except KeyError:
-            raise RuntimeError("No RECAPTCHA_PRIVATE_KEY config set")
-
-        data = url_encode({
-            'secret': private_key,
-            'remoteip': remote_addr,
-            'response': response
-        })
-
-        http_response = http.urlopen(RECAPTCHA_VERIFY_SERVER, to_bytes(data))
-
-        if http_response.code != 200:
-            return False
-
-        json_resp = json.loads(to_unicode(http_response.read()))
-
-        if json_resp["success"]:
-            return True
-
-        for error in json_resp.get("error-codes", []):
-            if error in RECAPTCHA_ERROR_CODES:
-                raise ValidationError(RECAPTCHA_ERROR_CODES[error])
-
-        return False
-
-
-class RecaptchaWidget(object):
-
-    def recaptcha_html(self, public_key):
-        html = current_app.config.get('RECAPTCHA_HTML')
-        if html:
-            return Markup(html)
-        params = current_app.config.get('RECAPTCHA_PARAMETERS')
-        script = RECAPTCHA_SCRIPT
-        if params:
-            script += u'?' + url_encode(params)
-
-        attrs = current_app.config.get('RECAPTCHA_DATA_ATTRS', {})
-        attrs['sitekey'] = public_key
-        snippet = u' '.join([u'data-%s="%s"' % (k, attrs[k]) for k in attrs])
-        return Markup(RECAPTCHA_TEMPLATE % (script, snippet))
-
-    def __call__(self, field, error=None, **kwargs):
-        """Returns the recaptcha input HTML."""
-
-        if not flaskbb_config["RECAPTCHA_ENABLED"]:
-            return
-
-        try:
-            public_key = flaskbb_config['RECAPTCHA_PUBLIC_KEY']
-        except KeyError:
-            raise RuntimeError("RECAPTCHA_PUBLIC_KEY config not set")
-
-        return self.recaptcha_html(public_key)
-
-
-class RecaptchaField(Field):
-    widget = RecaptchaWidget()
-
-    # error message if recaptcha validation fails
-    recaptcha_error = None
-
-    def __init__(self, label='', validators=None, **kwargs):
-        validators = validators or [RecaptchaValidator()]
-        super(RecaptchaField, self).__init__(label, validators, **kwargs)

+ 2 - 2
flaskbb/utils/search.py

@@ -65,7 +65,7 @@ class TopicWhoosheer(AbstractWhoosheer):
             topic_id=topic.id,
             title=topic.title,
             username=topic.username,
-            content=topic.first_post.content
+            content=getattr(topic.first_post,'content',None)
         )
 
     @classmethod
@@ -74,7 +74,7 @@ class TopicWhoosheer(AbstractWhoosheer):
             topic_id=topic.id,
             title=topic.title,
             username=topic.username,
-            content=topic.first_post.content
+            content=getattr(topic.first_post,'content',None)
         )
 
     @classmethod

+ 0 - 96
flaskbb/utils/widgets.py

@@ -1,96 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-    flaskbb.utils.widgets
-    ~~~~~~~~~~~~~~~~~~~~~
-
-    Additional widgets for wtforms.
-
-    :copyright: (c) 2014 by the FlaskBB Team.
-    :license: BSD, see LICENSE for more details.
-"""
-from datetime import datetime
-from wtforms.widgets.core import Select, HTMLString, html_params
-
-
-class SelectBirthdayWidget(object):
-    """Renders a DateTime field with 3 selects.
-    For more information see: http://stackoverflow.com/a/14664504
-    """
-    FORMAT_CHOICES = {
-        '%d': [(x, str(x)) for x in range(1, 32)],
-        '%m': [(x, str(x)) for x in range(1, 13)]
-    }
-
-    FORMAT_CLASSES = {
-        '%d': 'select_date_day',
-        '%m': 'select_date_month',
-        '%Y': 'select_date_year'
-    }
-
-    def __init__(self, years=range(1930, datetime.utcnow().year + 1)):
-        """Initialzes the widget.
-
-        :param years: The min year which should be chooseable.
-                      Defatuls to ``1930``.
-        """
-        super(SelectBirthdayWidget, self).__init__()
-        self.FORMAT_CHOICES['%Y'] = [(x, str(x)) for x in years]
-
-    def __call__(self, field, **kwargs):
-        field_id = kwargs.pop('id', field.id)
-        html = []
-        allowed_format = ['%d', '%m', '%Y']
-        surrounded_div = kwargs.pop('surrounded_div', None)
-        css_class = kwargs.get('class', None)
-
-        for date_format in field.format.split():
-            if date_format in allowed_format:
-                choices = self.FORMAT_CHOICES[date_format]
-                id_suffix = date_format.replace('%', '-')
-                id_current = field_id + id_suffix
-
-                if css_class is not None:  # pragma: no cover
-                    select_class = "{} {}".format(
-                        css_class, self.FORMAT_CLASSES[date_format]
-                    )
-                else:
-                    select_class = self.FORMAT_CLASSES[date_format]
-
-                kwargs['class'] = select_class
-
-                try:
-                    del kwargs['placeholder']
-                except KeyError:
-                    pass
-
-                if surrounded_div is not None:
-                    html.append('<div class="%s">' % surrounded_div)
-
-                html.append('<select %s>' % html_params(name=field.name,
-                                                        id=id_current,
-                                                        **kwargs))
-
-                if field.data:
-                    current_value = int(field.data.strftime(date_format))
-                else:
-                    current_value = None
-
-                for value, label in choices:
-                    selected = (value == current_value)
-
-                    # Defaults to blank
-                    if value == 1 or value == 1930:
-                        html.append(
-                            Select.render_option("None", " ", selected)
-                        )
-
-                    html.append(Select.render_option(value, label, selected))
-
-                html.append('</select>')
-
-                if surrounded_div is not None:
-                    html.append("</div>")
-
-            html.append(' ')
-
-        return HTMLString(''.join(html))

+ 31 - 0
migrations/versions/d87cea4e995d_remove_timezone_info_from_birthday_field.py

@@ -0,0 +1,31 @@
+"""remove timezone info from birthday field
+
+Revision ID: d87cea4e995d
+Revises: d9530a529b3f
+Create Date: 2016-11-19 09:19:28.000276
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = 'd87cea4e995d'
+down_revision = 'd9530a529b3f'
+
+from alembic import op
+import sqlalchemy as sa
+import flaskbb
+
+
+def upgrade():
+    connection = op.get_bind()
+
+    if connection.engine.dialect.name != "sqlite":
+        # user/models.py
+        op.alter_column('users', 'birthday', type_=sa.DateTime(), existing_type=flaskbb.utils.database.UTCDateTime(timezone=True), existing_nullable=True)
+
+
+def downgrade():
+    connection = op.get_bind()
+
+    if connection.engine.dialect.name != "sqlite":
+        # user/models.py
+        op.alter_column('users', 'birthday', existing_type=sa.DateTime(), type_=flaskbb.utils.database.UTCDateTime(timezone=True), existing_nullable=True)

+ 36 - 1
tests/unit/utils/test_fields.py

@@ -1,7 +1,7 @@
 """Tests for the utils/fields.py file."""
 import pytest
 from wtforms.form import Form
-from flaskbb.utils.fields import BirthdayField
+from flaskbb.utils.fields import SelectBirthdayWidget, BirthdayField
 
 
 def test_birthday_field():
@@ -19,3 +19,38 @@ def test_birthday_field():
 
     with pytest.raises(ValueError):
         form.birthday.process_formdata(b)
+
+
+def test_select_birthday_widget():
+    """Test the SelectDateWidget."""
+
+    assert SelectBirthdayWidget.FORMAT_CHOICES['%d'] == [
+        (x, str(x)) for x in range(1, 32)
+    ]
+    assert SelectBirthdayWidget.FORMAT_CHOICES['%m'] == [
+        (x, str(x)) for x in range(1, 13)
+    ]
+
+    assert SelectBirthdayWidget.FORMAT_CLASSES == {
+        '%d': 'select_date_day',
+        '%m': 'select_date_month',
+        '%Y': 'select_date_year'
+    }
+
+    select_birthday_widget = SelectBirthdayWidget(years=[0, 1])
+
+    assert select_birthday_widget.FORMAT_CHOICES['%Y'] == [(0, '0'), (1, '1')]
+
+    class Field(object):
+        id = 'world'
+        name = 'helloWorld'
+        format = '%d %m %Y'
+        data = None
+
+    html = select_birthday_widget(field=Field(), surrounded_div="test-div")
+    assert 'world' in html
+    assert 'helloWorld' in html
+    assert 'class="select_date_day"' in html
+    assert 'class="select_date_month"' in html
+    assert 'class="select_date_year"' in html
+    assert '<div class="test-div">' in html

+ 0 - 37
tests/unit/utils/test_widgets.py

@@ -1,37 +0,0 @@
-"""Tests for the utils/widgets.py file."""
-from flaskbb.utils.widgets import SelectBirthdayWidget
-
-
-def test_select_birthday_widget():
-    """Test the SelectDateWidget."""
-
-    assert SelectBirthdayWidget.FORMAT_CHOICES['%d'] == [
-        (x, str(x)) for x in range(1, 32)
-    ]
-    assert SelectBirthdayWidget.FORMAT_CHOICES['%m'] == [
-        (x, str(x)) for x in range(1, 13)
-    ]
-
-    assert SelectBirthdayWidget.FORMAT_CLASSES == {
-        '%d': 'select_date_day',
-        '%m': 'select_date_month',
-        '%Y': 'select_date_year'
-    }
-
-    select_birthday_widget = SelectBirthdayWidget(years=[0, 1])
-
-    assert select_birthday_widget.FORMAT_CHOICES['%Y'] == [(0, '0'), (1, '1')]
-
-    class Field(object):
-        id = 'world'
-        name = 'helloWorld'
-        format = '%d %m %Y'
-        data = None
-
-    html = select_birthday_widget(field=Field(), surrounded_div="test-div")
-    assert 'world' in html
-    assert 'helloWorld' in html
-    assert 'class="select_date_day"' in html
-    assert 'class="select_date_month"' in html
-    assert 'class="select_date_year"' in html
-    assert '<div class="test-div">' in html