Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
9209ae0
Add placeholder 404.html to app/templates
tekd Feb 22, 2016
7ddbf2b
Add 404 error handler to app/views.py
tekd Feb 22, 2016
b2de4b4
Add temporary styling to 404 page
tekd Feb 22, 2016
d591540
Fix title in head to 'Page Not Found...'
tekd Feb 22, 2016
f307e2c
Show tracebacks for 500 errors in tests.
toolness Feb 22, 2016
d9f8633
Add placeholder 404.html to app/templates
tekd Feb 22, 2016
1ca2a02
Add 404 error handler to app/views.py
tekd Feb 22, 2016
88627c3
Add temporary styling to 404 page
tekd Feb 22, 2016
c3c3855
Fix title in head to 'Page Not Found...'
tekd Feb 22, 2016
bce58b9
Merge branch 'issue-281' of https://github.com/tekd/noi2 into issue-281
tekd Feb 24, 2016
b57e92c
Add styling and icon to 404 page
tekd Feb 26, 2016
dbd34c5
Change text to error page and adjust styling
tekd Feb 26, 2016
81b730f
Remove error handler in views
tekd Feb 26, 2016
8e6c559
Add error_handlers module
tekd Feb 26, 2016
876109a
Add register_blueprint for error handling
tekd Feb 26, 2016
30d93cf
Change to 4 spaces
tekd Feb 26, 2016
c66065a
Add support for optional email confirmation.
toolness Feb 26, 2016
9ecbabd
better 500 tracebacks for *all* tests, not just test_views.
toolness Feb 26, 2016
02cf759
Add placeholder 404.html to app/templates
tekd Feb 22, 2016
1d0f587
Add 404 error handler to app/views.py
tekd Feb 22, 2016
c6db4b2
Add temporary styling to 404 page
tekd Feb 22, 2016
b64a488
Fix title in head to 'Page Not Found...'
tekd Feb 22, 2016
553c8e4
Add placeholder 404.html to app/templates
tekd Feb 22, 2016
34af48d
Add temporary styling to 404 page
tekd Feb 22, 2016
e20be7e
Fix title in head to 'Page Not Found...'
tekd Feb 22, 2016
ebd81b3
Add styling and icon to 404 page
tekd Feb 26, 2016
5f0bd23
Change text to error page and adjust styling
tekd Feb 26, 2016
c382ab2
Remove error handler in views
tekd Feb 26, 2016
3167074
Add error_handlers module
tekd Feb 26, 2016
7da40ac
Add register_blueprint for error handling
tekd Feb 26, 2016
664f839
Change to 4 spaces
tekd Feb 26, 2016
982e129
Fix merge conflict
tekd Feb 27, 2016
94c12dd
Add more descriptive name for method
tekd Feb 27, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion app/config/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,16 @@ SECURITY_CHANGEABLE: True
SECURITY_PASSWORD_HASH: bcrypt
SECURITY_PASSWORD_SALT: 'bb83d6fa8d0a4adc'
SECURITY_SEND_REGISTER_EMAIL: False
SECURITY_CONFIRMABLE: True
SECURITY_LOGIN_WITHOUT_CONFIRMATION: True

# This is a workaround for
# https://github.com/mattupstate/flask-security/pull/489.
SECURITY_MSG_CONFIRM_REGISTRATION: ['DO_NOT_DISPLAY_ME', 'success']

SECURITY_EMAIL_SENDER: noreply@networkofinnovators.org
SECURITY_POST_REGISTER_VIEW: 'views.register_step_2'
SECURITY_POST_CONFIRM_VIEW: 'views.register_step_2'
SECURITY_POST_CONFIRM_VIEW: 'views.confirmation_success'
SECURITY_POST_LOGIN_VIEW: 'views.activity'
SECURITY_EMAIL_SUBJECT_REGISTER: 'Welcome to the Network of Innovators!'
SECURITY_EMAIL_SUBJECT_PASSWORD_NOTICE: 'Your Network of Innovators password has been reset'
Expand Down
8 changes: 8 additions & 0 deletions app/error_handlers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import flask
from flask import Blueprint, render_template

blueprint = flask.Blueprint('error_handlers', __name__)

@blueprint.app_errorhandler(404)
def page_not_found(e):
return render_template('404.html'), 404
16 changes: 10 additions & 6 deletions app/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
QUESTIONS_BY_ID, LEVELS_BY_SCORE, QUESTIONNAIRES_BY_ID)
from app.forms import (NOIForgotPasswordForm, NOILoginForm,
NOIResetPasswordForm, NOIChangePasswordForm,
NOIRegisterForm)
NOIConfirmRegisterForm, NOISendConfirmationForm)
from app.models import db, User, Role
from app.views import views
from app.utils import get_nopic_avatar
Expand All @@ -29,6 +29,7 @@
import yaml
import json
import os
import error_handlers


# App config vars that are exposed to client-side JavaScript code.
Expand Down Expand Up @@ -68,6 +69,11 @@ def create_app(config=None): #pylint: disable=too-many-statements
'''
app = Flask(__name__)

'''
add error page handling
'''
app.register_blueprint(error_handlers.blueprint)

with open('/noi/app/config/config.yml', 'r') as config_file:
app.config.update(yaml.load(config_file))

Expand All @@ -81,9 +87,6 @@ def create_app(config=None): #pylint: disable=too-many-statements
else:
app.config.update(config)

# Confirming email is currently unsupported.
app.config['SECURITY_CONFIRMABLE'] = False

with open('/noi/app/data/deployments.yaml') as deployments_yaml:
deployments = yaml.load(deployments_yaml)

Expand Down Expand Up @@ -118,10 +121,11 @@ def create_app(config=None): #pylint: disable=too-many-statements
user_datastore = DeploySQLAlchemyUserDatastore(db, User, Role)
security.init_app(app, datastore=user_datastore,
login_form=NOILoginForm,
register_form=NOIRegisterForm,
confirm_register_form=NOIConfirmRegisterForm,
forgot_password_form=NOIForgotPasswordForm,
reset_password_form=NOIResetPasswordForm,
change_password_form=NOIChangePasswordForm)
change_password_form=NOIChangePasswordForm,
send_confirmation_form=NOISendConfirmationForm)

db.init_app(app)
alchemydumps.init_app(app, db)
Expand Down
15 changes: 11 additions & 4 deletions app/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@
from flask.ext.babel import get_locale
from flask_wtf import Form
from flask_wtf.file import FileField, FileAllowed
from flask_security.forms import (LoginForm, RegisterForm,
from flask_security.forms import (LoginForm, ConfirmRegisterForm,
ForgotPasswordForm, ChangePasswordForm,
ResetPasswordForm,
SendConfirmationForm,
email_required,
email_validator, unique_user_email,
valid_user_email,
Expand Down Expand Up @@ -213,6 +214,13 @@ class ChangeLocaleForm(Form):
)


class NOISendConfirmationForm(SendConfirmationForm):
'''
Localizeable version of Flask-Security's SendConfirmationForm
'''
submit = SubmitField(lazy_gettext('Resend Confirmation Instructions'))


class NOIForgotPasswordForm(ForgotPasswordForm):
'''
Localizeable version of Flask-Security's ForgotPasswordForm
Expand All @@ -233,9 +241,9 @@ class NOILoginForm(LoginForm):
submit = SubmitField(lazy_gettext('Log in'))


class NOIRegisterForm(RegisterForm):
class NOIConfirmRegisterForm(ConfirmRegisterForm):
'''
Localizeable version of Flask-Security's RegisterForm
Localizeable version of Flask-Security's ConfirmRegisterForm
'''

# Note that extra fields in this registration form are passed
Expand All @@ -254,7 +262,6 @@ class NOIRegisterForm(RegisterForm):
password = PasswordField(
lazy_gettext('Password'), validators=[password_required,
password_length])
password_confirm = None
submit = SubmitField(lazy_gettext('Sign up'))


Expand Down
14 changes: 14 additions & 0 deletions app/l10n.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ def gettext_no_interpolate(string):

app.config['SECURITY_MSG_UNAUTHORIZED'] = (
lazy_gettext('You do not have permission to view this resource.'), 'error')
app.config['SECURITY_MSG_EMAIL_CONFIRMED'] = (
lazy_gettext('Thank you. Your email has been confirmed.'), 'success')
app.config['SECURITY_MSG_ALREADY_CONFIRMED'] = (
lazy_gettext('Your email has already been confirmed.'), 'info')
app.config['SECURITY_MSG_INVALID_CONFIRMATION_TOKEN'] = (
lazy_gettext('Invalid confirmation token.'), 'error')
app.config['SECURITY_MSG_EMAIL_ALREADY_ASSOCIATED'] = (
lazy_gettext('%(email)s is already associated with an account.'), 'error')
app.config['SECURITY_MSG_PASSWORD_MISMATCH'] = (
Expand All @@ -45,6 +51,14 @@ def gettext_no_interpolate(string):
'instructions have been sent to %(email)s.'), 'error')
app.config['SECURITY_MSG_INVALID_RESET_PASSWORD_TOKEN'] = (
lazy_gettext('Invalid reset password token.'), 'error')
app.config['SECURITY_MSG_CONFIRMATION_REQUIRED'] = (
lazy_gettext('Email requires confirmation.'), 'error')
app.config['SECURITY_MSG_CONFIRMATION_REQUEST'] = (
lazy_gettext('Confirmation instructions have been sent to %(email)s.'), 'info')
app.config['SECURITY_MSG_CONFIRMATION_EXPIRED'] = (
lazy_gettext('You did not confirm your email within %(within)s. New '
'instructions to confirm your email have been sent to '
'%(email)s.'), 'error')
app.config['SECURITY_MSG_LOGIN_EXPIRED'] = (
lazy_gettext('You did not login within %(within)s. New instructions to '
'login have been sent to %(email)s.'), 'error')
Expand Down
20 changes: 20 additions & 0 deletions app/static/sass/_error-page.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.e-error-display {
background-color: $blue;
text-align: center;
text-transform: uppercase;
padding-top: 4em;
h2,h3,h4 {
color: $white-80;;
padding: .5em;
}
}

.error-page-icon {
color: $white-80;;
font-size: 4em;
}

.e-error-display a {
color: $white-90;
font-weight: bold;
}
2 changes: 2 additions & 0 deletions app/static/sass/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ body {

@import 'desktop-hack';

@import 'error-page';

/* Useful for making blocks of content that we don't have time to style
* not look utterly unstyled and horrible. */
.b-temporary-styling {
Expand Down
1 change: 1 addition & 0 deletions app/style_guide.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,4 @@ def send_static_asset(path):

return send_from_directory('/noi/app/templates/style-guide/static',
path)

15 changes: 15 additions & 0 deletions app/templates/404.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{% extends '__base_ui__.html' %}

{% block title %}Page Not Found | Network of Innovators{% endblock %}
{% block body_class %}b-landing-page{% endblock %}

{% block content %}
<section class="b-homepage-panel e-error-display">
<div class='row'>
<i class="material-icons error-page-icon">error_outline</i>
<h2>Error 404</h2>
<h3>We couldn't find what you were looking for</h3>
<h4>Try using the navigation above or click <a href="/">here</a> to go back to the homepage</h4>
</div>
</section>
{% endblock %}
6 changes: 6 additions & 0 deletions app/templates/__base_new__.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,13 @@

{% from "_macros.html" import render_alert %}
{% for category, message in get_flashed_messages(with_categories=true) %}
{# Some flashed messages come from third-party code and we don't want
them to be displayed. The only way we can disable them from being
displayed, unfortunately, is to set their message text to something
we detect for here. #}
{%- if message != 'DO_NOT_DISPLAY_ME' -%}
{{ render_alert(message=message, category=category) }}
{%- endif -%}
{% endfor %}

{% block header %}{% endblock %}
Expand Down
3 changes: 0 additions & 3 deletions app/templates/security/_menu_new.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,5 @@ <h2 class="sr-only">{{ gettext('Menu') }}</h2>
{% if security.recoverable %}
<li><a href="{{ url_for_security('forgot_password') }}">{{ gettext('Forgot password') }}</a><br/></li>
{% endif %}
{% if security.confirmable %}
<li><a href="{{ url_for_security('send_confirmation') }}">{{ gettext('Confirm account') }}</a></li>
{% endif %}
</ul>
{% endif %}
24 changes: 24 additions & 0 deletions app/templates/security/send_confirmation.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{% extends '__base_new__.html' %}

{% block title %}{{ gettext('Send confirmation instructions') }}{% endblock %}

{% from "security/_macros_new.html" import render_field_with_errors, render_field %}

{% block content %}
<div class="b-onboarding">
<header>
<div class="b-logo"></div>
</header>

<section>
<p class="e-onboarding-message">{{ gettext('Send confirmation instructions') }}</p>
<form action="{{ url_for_security('send_confirmation') }}" method="POST" name="send_confirmation_form" class="e-onboarding-form">
{{ send_confirmation_form.hidden_tag() }}
{{ render_field_with_errors(send_confirmation_form.email) }}
{{ send_confirmation_form.submit(class="b-button") }}
</form>
</section>
{% include "security/_menu_new.html" %}
</div>

{% endblock %}
4 changes: 2 additions & 2 deletions app/tests/test_admin.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from base64 import b64encode

from flask import Flask
from flask_testing import TestCase

from .util import create_empty_flask_app
from .test_views import ViewTestCase
from ..admin import _init_basic_auth as init_basic_auth

Expand Down Expand Up @@ -47,7 +47,7 @@ def test_admin_users_are_not_redirected_to_login(self):

class BasicAuthTests(TestCase):
def create_app(self):
app = Flask('test')
app = create_empty_flask_app()
app.config.update({'ADMIN_UI_BASIC_AUTH': 'foo:bar'})

@app.route('/foo/bar')
Expand Down
5 changes: 2 additions & 3 deletions app/tests/test_l10n.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,13 @@
from babel.messages.pofile import read_po
from babel.messages.catalog import TranslationError, Message
from babel.messages import checkers
from flask import Flask

from .test_views import ViewTestCase
from .util import eq_
from .util import eq_, create_empty_flask_app
from app import l10n

def test_flask_security_strings_do_not_interpolate():
app = Flask('test')
app = create_empty_flask_app()
l10n.configure_app(app)
eq_(unicode(app.config['SECURITY_MSG_EMAIL_ALREADY_ASSOCIATED'][0]),
'%(email)s is already associated with an account.')
Expand Down
7 changes: 3 additions & 4 deletions app/tests/test_models.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import time
import StringIO
import contextlib
from flask import Flask
from flask_testing import TestCase
from sqlalchemy import create_engine
from sqlalchemy.exc import OperationalError, IntegrityError
from sqlalchemy_utils import Country
from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT
import psycopg2

from .util import eq_
from .util import eq_, create_empty_flask_app
from .factories import UserFactory, UserSkillFactory
from .. import models, LEVELS, babel

Expand Down Expand Up @@ -60,7 +59,7 @@ def create_postgres_database():
con.close()

def db_test_request_context():
app = Flask('minimal_db_app')
app = create_empty_flask_app()
app.config['SQLALCHEMY_DATABASE_URI'] = TEST_DB_URL
db.init_app(app)
return app.test_request_context()
Expand Down Expand Up @@ -96,7 +95,7 @@ class DbTestCase(TestCase):
)

def create_app(self):
app = Flask('test')
app = create_empty_flask_app()
app.config.update(self.BASE_APP_CONFIG)
db.init_app(app)
return app
Expand Down
5 changes: 3 additions & 2 deletions app/tests/test_style_guide.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@
from app import assets, csrf
from app.views import views

from flask import Flask
from flask.ext.testing import TestCase

from .util import create_empty_flask_app

class StyleGuideTests(TestCase):
def create_app(self):
app = Flask('app')
app = create_empty_flask_app()
app.config['SECRET_KEY'] = 'test'
app.register_blueprint(views)
app.register_blueprint(style_guide.views)
Expand Down
Loading