aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/runserver.py3
-rw-r--r--src/web/controllers/abstract.py2
-rw-r--r--src/web/controllers/user.py25
-rw-r--r--src/web/models/user.py10
-rw-r--r--src/web/templates/admin/dashboard.html18
-rw-r--r--src/web/templates/home.html2
-rw-r--r--src/web/templates/layout.html6
-rwxr-xr-xsrc/web/utils.py2
-rw-r--r--src/web/views/__init__.py2
-rw-r--r--src/web/views/views.py274
10 files changed, 50 insertions, 294 deletions
diff --git a/src/runserver.py b/src/runserver.py
index b355d7d1..d0bfa533 100755
--- a/src/runserver.py
+++ b/src/runserver.py
@@ -54,6 +54,9 @@ with application.app_context():
application.register_blueprint(views.categories_bp)
application.register_blueprint(views.category_bp)
application.register_blueprint(views.icon_bp)
+ application.register_blueprint(views.admin_bp)
+ application.register_blueprint(views.users_bp)
+ application.register_blueprint(views.user_bp)
if __name__ == '__main__':
diff --git a/src/web/controllers/abstract.py b/src/web/controllers/abstract.py
index f33d241e..99d92ff3 100644
--- a/src/web/controllers/abstract.py
+++ b/src/web/controllers/abstract.py
@@ -84,7 +84,7 @@ class AbstractController(object):
def create(self, **attrs):
assert self._user_id_key is None or self._user_id_key in attrs \
- or self.user_id is not None, \
+ or self.user_id is None, \
"You must provide user_id one way or another"
if self._user_id_key is not None and self._user_id_key not in attrs:
diff --git a/src/web/controllers/user.py b/src/web/controllers/user.py
index 3f96b185..d8bf1fa1 100644
--- a/src/web/controllers/user.py
+++ b/src/web/controllers/user.py
@@ -1,3 +1,6 @@
+import random
+import hashlib
+from werkzeug import generate_password_hash
from .abstract import AbstractController
from web.models import User
@@ -5,3 +8,25 @@ from web.models import User
class UserController(AbstractController):
_db_cls = User
_user_id_key = 'id'
+
+ def unset_activation_key(self, obj_id):
+ self.update({'id': obj_id}, {'activation_key': ""})
+
+ def set_activation_key(self, obj_id):
+ key = str(random.getrandbits(256)).encode("utf-8")
+ key = hashlib.sha512(key).hexdigest()[:86]
+ self.update({'id': obj_id}, {'activation_key': key})
+
+ def _handle_password(self, attrs):
+ if attrs.get('password'):
+ attrs['pwdhash'] = generate_password_hash(attrs.pop('password'))
+ elif 'password' in attrs:
+ del attrs['password']
+
+ def create(self, **attrs):
+ self._handle_password(attrs)
+ return super().create(**attrs)
+
+ def update(self, filters, attrs):
+ self._handle_password(attrs)
+ return super().update(filters, attrs)
diff --git a/src/web/models/user.py b/src/web/models/user.py
index c5e70036..d1b9c568 100644
--- a/src/web/models/user.py
+++ b/src/web/models/user.py
@@ -30,7 +30,7 @@ import re
import random
import hashlib
from datetime import datetime
-from werkzeug import generate_password_hash, check_password_hash
+from werkzeug import check_password_hash
from flask.ext.login import UserMixin
from bootstrap import db
@@ -63,12 +63,6 @@ class User(db.Model, UserMixin):
"""
return self.id
- def set_password(self, password):
- """
- Hash the password of the user.
- """
- self.pwdhash = generate_password_hash(password)
-
def check_password(self, password):
"""
Check the password of the user.
@@ -79,7 +73,7 @@ class User(db.Model, UserMixin):
"""
Return True if the user has administrator rights.
"""
- return len([role for role in self.roles if role.name == "admin"]) != 0
+ return "admin" in [role.name for role in self.roles]
def __eq__(self, other):
return self.id == other.id
diff --git a/src/web/templates/admin/dashboard.html b/src/web/templates/admin/dashboard.html
index 25bd3883..2436c955 100644
--- a/src/web/templates/admin/dashboard.html
+++ b/src/web/templates/admin/dashboard.html
@@ -20,29 +20,31 @@
<tr {% if user.activation_key != "" %}class="warning"{% endif %}>
<td>{{ loop.index }}</td>
{% if user.id == current_user.id %}
- <td><a href="/management">{{ user.nickname }}</a> (It's you!)</td>
+ <td><a href="{{ url_for("user.management") }}">{{ user.nickname }}</a> (It's you!)</td>
{% else %}
- <td><a href="/admin/user/{{ user.id }}">{{ user.nickname }}</a></td>
+ <td><a href="{{ url_for("admin.user", user_id=user.id) }}">{{ user.nickname }}</a></td>
{% endif %}
<td><a href="mailto:{{ user.email }}">{{ user.email }}</a></td>
<td class="date">{{ user.last_seen }}</td>
<td>
- <a href="/admin/user/{{ user.id }}"><i class="glyphicon glyphicon-user" title="{{ _('View this user') }}"></i></a>
- <a href="/admin/edit_user/{{ user.id }}"><i class="glyphicon glyphicon-edit" title="{{ _('Edit this user') }}"></i></a>
+ <a href="{{ url_for("admin.user", user_id=user.id) }}"><i class="glyphicon glyphicon-user" title="{{ _('View this user') }}"></i></a>
+ <a href="{{ url_for("admin.user_form", user_id=user.id) }}"><i class="glyphicon glyphicon-edit" title="{{ _('Edit this user') }}"></i></a>
{% if user.id != current_user.id %}
+ <a href="{{ url_for("admin.toggle_user", user_id=user.id) }}">
{% if user.activation_key == "" %}
- <a href="/admin/disable_user/{{ user.id }}"><i class="glyphicon glyphicon-ban-circle" title="Disable this account"></i></a>
+ <i class="glyphicon glyphicon-ban-circle" title="{{ _("Disable this account") }}"></i>
{% else %}
- <a href="/admin/enable_user/{{ user.id }}"><i class="glyphicon glyphicon-ok-circle" title="Enable this account"></i></a>
+ <i class="glyphicon glyphicon-ok-circle" title="{{ _("Enable this account") }}"></i>
{% endif %}
- <a href="/admin/delete_user/{{ user.id }}"><i class="glyphicon glyphicon-remove" title="{{ _('Delete this user') }}" onclick="return confirm('{{ _('You are going to delete this account.') }}');"></i></a>
+ </a>
+ <a href="{{ url_for("admin.delete_user", user_id=user.id) }}"><i class="glyphicon glyphicon-remove" title="{{ _('Delete this user') }}" onclick="return confirm('{{ _('You are going to delete this account.') }}');"></i></a>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
-<a href="/admin/create_user" class="btn btn-default">{{ _('Add a new user') }}</a>
+<a href="{{ url_for("admin.user_form") }}" class="btn btn-default">{{ _('Add a new user') }}</a>
<h1>{{ _('Send notification messages') }}</h1>
<form action="" method="post">
{{ form.hidden_tag() }}
diff --git a/src/web/templates/home.html b/src/web/templates/home.html
index 86d96e94..6b136870 100644
--- a/src/web/templates/home.html
+++ b/src/web/templates/home.html
@@ -3,7 +3,7 @@
{% if feeds|count == 0 %}
<div class="col-md-4 col-md-offset-4">
<h1>{{ _("You don't have any feeds.") }}</h1>
- <h1><a href="{{ url_for("feed.form") }}">{{ _('Add some') }}</a>, {{ _('or') }} <a href="/management">{{ _('upload an OPML file.') }}</a></h1>
+ <h1><a href="{{ url_for("feed.form") }}">{{ _('Add some') }}</a>, {{ _('or') }} <a href="{{ url_for("user.management") }}">{{ _('upload an OPML file.') }}</a></h1>
</div>
{% else %}
<div class="container-fluid">
diff --git a/src/web/templates/layout.html b/src/web/templates/layout.html
index cf2498e2..eb213ca5 100644
--- a/src/web/templates/layout.html
+++ b/src/web/templates/layout.html
@@ -81,12 +81,12 @@
<div><span class="glyphicon glyphicon-user"></span>&nbsp;<b class="caret"></b></div>
</a>
<ul class="dropdown-menu">
- <li><a href="{{ url_for("profile") }}"><span class="glyphicon glyphicon-user"></span> {{ _('Profile') }}</a></li>
- <li><a href="{{ url_for("management") }}"><span class="glyphicon glyphicon-cog"></span> {{ _('Your data') }}</a></li>
+ <li><a href="{{ url_for("user.profile") }}"><span class="glyphicon glyphicon-user"></span> {{ _('Profile') }}</a></li>
+ <li><a href="{{ url_for("user.management") }}"><span class="glyphicon glyphicon-cog"></span> {{ _('Your data') }}</a></li>
<li><a href="{{ url_for("about") }}"><span class="glyphicon glyphicon-question-sign"></span> {{ _('About') }}</a></li>
{% if g.user.is_admin() %}
<li role="presentation" class="divider"></li>
- <li><a href="{{ url_for("dashboard") }}"><span class="glyphicon glyphicon-dashboard"></span> {{ _('Dashboard') }}</a></li>
+ <li><a href="{{ url_for("admin.dashboard") }}"><span class="glyphicon glyphicon-dashboard"></span> {{ _('Dashboard') }}</a></li>
<li role="presentation" class="divider"></li>
{% endif %}
<li><a href="{{ url_for("logout") }}"><span class="glyphicon glyphicon-log-out"></span> {{ _('Logout') }}</a></li>
diff --git a/src/web/utils.py b/src/web/utils.py
index 38d15f25..6182eb0e 100755
--- a/src/web/utils.py
+++ b/src/web/utils.py
@@ -112,7 +112,7 @@ def fetch(id, feed_id=None):
"""
cmd = [sys.executable, conf.BASE_DIR+'/manager.py', 'fetch_asyncio', str(id),
str(feed_id)]
- p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
+ return subprocess.Popen(cmd, stdout=subprocess.PIPE)
def history(user_id, year=None, month=None):
"""
diff --git a/src/web/views/__init__.py b/src/web/views/__init__.py
index 1da9205b..f02e86e6 100644
--- a/src/web/views/__init__.py
+++ b/src/web/views/__init__.py
@@ -5,3 +5,5 @@ from .article import article_bp, articles_bp
from .feed import feed_bp, feeds_bp
from .category import category_bp, categories_bp
from .icon import icon_bp
+from .admin import admin_bp
+from .user import user_bp, users_bp
diff --git a/src/web/views/views.py b/src/web/views/views.py
index 1e7b7539..f543c6fa 100644
--- a/src/web/views/views.py
+++ b/src/web/views/views.py
@@ -53,8 +53,7 @@ from web import utils, notifications, export
from web.lib.view_utils import etag_match
from web.models import User, Feed, Article, Role
from web.decorators import feed_access_required
-from web.forms import SignupForm, SigninForm, InformationMessageForm,\
- ProfileForm, UserForm, RecoverPasswordForm \
+from web.forms import SignupForm, SigninForm
from web.controllers import UserController, FeedController, \
ArticleController
@@ -130,11 +129,6 @@ def internal_server_error(e):
return render_template('errors/500.html'), 500
-def redirect_url(default='home'):
- return request.args.get('next') or \
- request.referrer or \
- url_for(default)
-
@g.babel.localeselector
def get_locale():
"""
@@ -196,6 +190,7 @@ def logout():
flash(gettext("Logged out successfully."), 'success')
return redirect(url_for('login'))
+
@app.route('/signup', methods=['GET', 'POST'])
def signup():
"""
@@ -423,94 +418,6 @@ def export_opml():
return response
-@app.route('/management', methods=['GET', 'POST'])
-@login_required
-def management():
- """
- Display the management page.
- """
- if request.method == 'POST':
- if None != request.files.get('opmlfile', None):
- # Import an OPML file
- data = request.files.get('opmlfile', None)
- if not utils.allowed_file(data.filename):
- flash(gettext('File not allowed.'), 'danger')
- else:
- try:
- nb = utils.import_opml(g.user.email, data.read())
- if conf.CRAWLING_METHOD == "classic":
- utils.fetch(g.user.email, None)
- flash(str(nb) + ' ' + gettext('feeds imported.'),
- "success")
- flash(gettext("Downloading articles..."), 'info')
- except:
- flash(gettext("Impossible to import the new feeds."),
- "danger")
- elif None != request.files.get('jsonfile', None):
- # Import an account
- data = request.files.get('jsonfile', None)
- if not utils.allowed_file(data.filename):
- flash(gettext('File not allowed.'), 'danger')
- else:
- try:
- nb = utils.import_json(g.user.email, data.read())
- flash(gettext('Account imported.'), "success")
- except:
- flash(gettext("Impossible to import the account."),
- "danger")
- else:
- flash(gettext('File not allowed.'), 'danger')
-
- nb_feeds = len(g.user.feeds.all())
- articles = Article.query.filter(Article.user_id == g.user.id)
- nb_articles = articles.count()
- nb_unread_articles = articles.filter(Article.readed == False).count()
- return render_template('management.html', user=g.user,
- nb_feeds=nb_feeds, nb_articles=nb_articles,
- nb_unread_articles=nb_unread_articles)
-
-
-@app.route('/profile', methods=['GET', 'POST'])
-@login_required
-def profile():
- """
- Edit the profile of the currently logged user.
- """
- user = UserController(g.user.id).get(id=g.user.id)
- form = ProfileForm()
-
- if request.method == 'POST':
- if form.validate():
- form.populate_obj(user)
- if form.password.data != "":
- user.set_password(form.password.data)
- db.session.commit()
- flash("%s %s %s" % (gettext('User'), user.nickname,
- gettext('successfully updated.')),
- 'success')
- return redirect(url_for('profile'))
- else:
- return render_template('profile.html', user=user, form=form)
-
- if request.method == 'GET':
- form = ProfileForm(obj=user)
- return render_template('profile.html', user=user, form=form)
-
-@app.route('/delete_account', methods=['GET'])
-@login_required
-def delete_account():
- """
- Delete the account of the user (with all its data).
- """
- user = UserController(g.user.id).get(id=g.user.id)
- if user is not None:
- db.session.delete(user)
- db.session.commit()
- flash(gettext('Your account has been deleted.'), 'success')
- else:
- flash(gettext('This user does not exist.'), 'danger')
- return redirect(url_for('login'))
-
@app.route('/expire_articles', methods=['GET'])
@login_required
def expire_articles():
@@ -526,180 +433,3 @@ def expire_articles():
flash(gettext('Articles deleted.'), 'info')
db.session.commit()
return redirect(redirect_url())
-
-@app.route('/confirm_account/<string:activation_key>', methods=['GET'])
-def confirm_account(activation_key=None):
- """
- Confirm the account of a user.
- """
- if activation_key != "":
- user = User.query.filter(User.activation_key == activation_key).first()
- if user is not None:
- user.activation_key = ""
- db.session.commit()
- flash(gettext('Your account has been confirmed.'), 'success')
- else:
- flash(gettext('Impossible to confirm this account.'), 'danger')
- return redirect(url_for('login'))
-
-@app.route('/recover', methods=['GET', 'POST'])
-def recover():
- """
- Enables the user to recover its account when he has forgotten
- its password.
- """
- form = RecoverPasswordForm()
-
- if request.method == 'POST':
- if form.validate():
- user = User.query.filter(User.email == form.email.data).first()
- characters = string.ascii_letters + string.digits
- password = "".join(random.choice(characters) for x in range(random.randint(8, 16)))
- user.set_password(password)
- db.session.commit()
-
- # Send the confirmation email
- try:
- notifications.new_password_notification(user, password)
- flash(gettext('New password sent to your address.'), 'success')
- except Exception as e:
- flash(gettext('Problem while sending your new password.') + ': ' + str(e), 'danger')
-
- return redirect(url_for('login'))
- return render_template('recover.html', form=form)
-
- if request.method == 'GET':
- return render_template('recover.html', form=form)
-
-#
-# Views dedicated to administration tasks.
-#
-@app.route('/admin/dashboard', methods=['GET', 'POST'])
-@login_required
-@admin_permission.require(http_exception=403)
-def dashboard():
- """
- Adminstrator's dashboard.
- """
- form = InformationMessageForm()
-
- if request.method == 'POST':
- if form.validate():
- try:
- notifications.information_message(form.subject.data, form.message.data)
- except Exception as e:
- flash(gettext('Problem while sending email') + ': ' + str(e), 'danger')
-
- users = User.query.all()
- return render_template('admin/dashboard.html', users=users, current_user=g.user, form=form)
-
-@app.route('/admin/create_user', methods=['GET', 'POST'])
-@app.route('/admin/edit_user/<int:user_id>', methods=['GET', 'POST'])
-@login_required
-@admin_permission.require(http_exception=403)
-def create_user(user_id=None):
- """
- Create or edit a user.
- """
- form = UserForm()
-
- if request.method == 'POST':
- if form.validate():
- role_user = Role.query.filter(Role.name == "user").first()
- if user_id is not None:
- # Edit a user
- user = User.query.filter(User.id == user_id).first()
- form.populate_obj(user)
- if form.password.data != "":
- user.set_password(form.password.data)
- db.session.commit()
- flash(gettext('User') + ' ' + user.nickname + ' ' + gettext('successfully updated.'), 'success')
- else:
- # Create a new user
- user = User(nickname=form.nickname.data,
- email=form.email.data,
- pwdhash=generate_password_hash(form.password.data))
- user.roles.extend([role_user])
- user.activation_key = ""
- db.session.add(user)
- db.session.commit()
- flash("%s %s %s" % (gettext('User'), user.nickname,
- gettext('successfully created.')),
- 'success')
- return redirect(url_for('create_user', user_id=user.id))
- else:
- return redirect(url_for('create_user'))
-
- if request.method == 'GET':
- if user_id is not None:
- user = User.query.filter(User.id == user_id).first()
- form = UserForm(obj=user)
- message = "%s <i>%s</i>" % (gettext('Edit the user'),
- user.nickname)
- else:
- form = UserForm()
- message = gettext('Add a new user')
- return render_template('/admin/create_user.html',
- form=form, message=message)
-
-@app.route('/admin/user/<int:user_id>', methods=['GET'])
-@login_required
-@admin_permission.require(http_exception=403)
-def user(user_id=None):
- """
- See information about a user (stations, etc.).
- """
- user = UserController().get(id=user_id)
- if user is not None:
- article_contr = ArticleController(user_id)
- return render_template('/admin/user.html', user=user, feeds=user.feeds,
- article_count=article_contr.count_by_feed(),
- unread_article_count=article_contr.count_by_feed(readed=False))
-
- else:
- flash(gettext('This user does not exist.'), 'danger')
- return redirect(redirect_url())
-
-@app.route('/admin/delete_user/<int:user_id>', methods=['GET'])
-@login_required
-@admin_permission.require(http_exception=403)
-def delete_user(user_id=None):
- """
- Delete a user (with all its data).
- """
- user = User.query.filter(User.id == user_id).first()
- if user is not None:
- db.session.delete(user)
- db.session.commit()
- flash(gettext('User') + ' ' + user.nickname + ' ' + gettext('successfully deleted.'), 'success')
- else:
- flash(gettext('This user does not exist.'), 'danger')
- return redirect(redirect_url())
-
-@app.route('/admin/enable_user/<int:user_id>', methods=['GET'])
-@app.route('/admin/disable_user/<int:user_id>', methods=['GET'])
-@login_required
-@admin_permission.require()
-def disable_user(user_id=None):
- """
- Enable or disable the account of a user.
- """
- user = User.query.filter(User.id == user_id).first()
- if user is not None:
- if user.activation_key != "":
-
- # Send the confirmation email
- try:
- notifications.new_account_activation(user)
- user.activation_key = ""
- flash(gettext('Account of the user') + ' ' + user.nickname + ' ' + gettext('successfully activated.'), 'success')
- except Exception as e:
- flash(gettext('Problem while sending activation email') + ': ' + str(e), 'danger')
-
- else:
- user.activation_key = hashlib.sha512(str(random.getrandbits(256)).encode("utf-8")).hexdigest()[:86]
- flash(gettext('Account of the user') + ' ' + user.nickname + ' ' + gettext('successfully disabled.'), 'success')
- db.session.commit()
- else:
- flash(gettext('This user does not exist.'), 'danger')
- return redirect(redirect_url())
bgstack15