Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add admin panel #243

Merged
merged 10 commits into from
Dec 11, 2015
41 changes: 41 additions & 0 deletions app/admin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from flask import redirect, request
from flask_login import current_user
from flask_security.utils import url_for_security
from flask_admin import Admin
from flask_admin.contrib.sqla import ModelView
import flask_wtf
from wtforms import TextField

from .models import User, db

class NoiModelView(ModelView):
# The latest docs for flask-admin document a SecureForm class, but
# this hasn't yet been added to the latest release, so we'll use
# the "old" way of enabling CSRF support, documented here:
#
# http://flask-admin.readthedocs.org/en/v1.3.0/introduction/
form_base_class = flask_wtf.Form

can_delete = False
can_create = False

def is_accessible(self):
return current_user.is_authenticated() and current_user.is_admin()

def inaccessible_callback(self, name, **kwargs):
return redirect(url_for_security('login', next=request.url))


class UserModelView(NoiModelView):
column_list = ('first_name', 'last_name', 'email')
form_columns = column_list
column_searchable_list = ('first_name', 'last_name', 'email')

def scaffold_form(self):
form_class = super(UserModelView, self).scaffold_form()
form_class.email = TextField('Email')
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More context on this at pallets-eco/flask-admin#1134.

return form_class

def init_app(app):
admin = Admin(app, template_mode='bootstrap3')
admin.add_view(UserModelView(User, db.session))
8 changes: 7 additions & 1 deletion app/csp.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from app import csrf
from flask import current_app, make_response, request, Response
from flask import current_app, make_response, request, Response, url_for

from hashlib import sha256
from base64 import b64encode
Expand All @@ -10,6 +10,12 @@ def add_header(response):
Add a Content Security Policy (CSP) header to the given response.
'''

if request.path.startswith(url_for('admin.index')):
# Ugh, flask-admin has inline scripts. Since only a handful of
# users will have access to this view anyways, just disable CSP
# for it.
return response
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Filed pallets-eco/flask-admin#1135 so we hopefully can remove this someday.


script_src = [
"'self'",
"use.typekit.net"
Expand Down
3 changes: 2 additions & 1 deletion app/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from flask_security import SQLAlchemyUserDatastore, user_registered
from flask_security.utils import get_identity_attributes

from app import (csrf, cache, mail, bcrypt, s3, assets, security,
from app import (csrf, cache, mail, bcrypt, s3, assets, security, admin,
babel, celery, alchemydumps, sass, email_errors, csp,
QUESTIONNAIRES, NOI_COLORS, LEVELS, ORG_TYPES,
QUESTIONS_BY_ID, LEVELS_BY_SCORE, QUESTIONNAIRES_BY_ID)
Expand Down Expand Up @@ -134,6 +134,7 @@ def create_app(config=None): #pylint: disable=too-many-statements
app.config['SEARCH_DEPLOYMENTS'].append(noi_deploy)
babel.init_app(app)
l10n.init_app(app)
admin.init_app(app)

app.config['DOMAINS'] = this_deployment.get('domains',
default_deployment['domains'])
Expand Down
3 changes: 3 additions & 0 deletions app/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,9 @@ class User(db.Model, UserMixin, DeploymentMixin): #pylint: disable=no-init,too-f
updated_at = Column(types.DateTime(), default=datetime.datetime.now,
onupdate=datetime.datetime.now)

def is_admin(self):
return self.email in current_app.config.get('ADMIN_UI_USERS', [])

@property
def full_name(self):
return u"%s %s" % (self.first_name, self.last_name)
Expand Down
3 changes: 3 additions & 0 deletions app/templates/__base_ui__.html
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ <h3 class="e-name">{{ current_user.full_name }}</h3>
<a href="{{ url_for('views.faq') }}">{{ gettext('FAQ') }}</a>
<a href="{{ url_for('views.about_page') }}">{{ gettext('About') }}</a>
<a href="{{ url_for('views.terms_and_conditions') }}">{{ gettext("Terms of Service") }}</a>
{% if current_user.is_admin() -%}
<a href="{{ url_for('admin.index') }}">{{ gettext('Admin Panel') }}</a>
{%- endif %}
<a href="{{ url_for_security('logout') }}">{{ gettext('Sign Out') }}</a>
</section>

Expand Down
9 changes: 9 additions & 0 deletions app/templates/admin/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{% extends 'admin/master.html' %}

{% block body %}
{% if current_user.is_authenticated() and current_user.is_admin() %}
<p>Welcome to the NoI admin panel.</p>
{% else %}
<p>You probably shouldn't be here.</p>
{% endif %}
{% endblock %}
5 changes: 5 additions & 0 deletions app/templates/admin/master.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{% extends admin_base_template %}

{% block brand %}
<a class="navbar-brand" href="{{ url_for('views.activity') }}">{{ gettext("Network of Innovators") }}</a>
{% endblock %}