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

new FieldDescription for implementing placeholder and title properties #20

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 11 additions & 5 deletions fbone/admin/forms.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,24 @@
# -*- coding: utf-8 -*-

from flask.ext.wtf import Form
from wtforms import HiddenField, SubmitField, RadioField, DateField
from wtforms.validators import AnyOf
from wtforms import HiddenField, SubmitField, RadioField, DateField, SelectMultipleField, TextField, BooleanField
from wtforms.validators import AnyOf, Required

from ..user import USER_ROLE, USER_STATUS
from ..user import USER_STATUS


class UserForm(Form):
next = HiddenField()
role_code = RadioField(u"Role", [AnyOf([str(val) for val in USER_ROLE.keys()])],
choices=[(str(val), label) for val, label in USER_ROLE.items()])
role_code_select = SelectMultipleField(u"Role", choices=[])
status_code = RadioField(u"Status", [AnyOf([str(val) for val in USER_STATUS.keys()])],
choices=[(str(val), label) for val, label in USER_STATUS.items()])
# A demo of datepicker.
created_time = DateField(u'Created time')
confirmed = BooleanField(u'Confirmed')
submit = SubmitField(u'Save')

class RoleForm(Form):
next = HiddenField()
name = TextField(u"Role Code", [Required()])
description = TextField(u"Description", default=u"")
submit = SubmitField(u'Save')
50 changes: 44 additions & 6 deletions fbone/admin/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
from ..extensions import db
from ..decorators import admin_required

from ..user import User
from .forms import UserForm

from ..user import User, Role
from .forms import UserForm, RoleForm
from datetime import datetime

admin = Blueprint('admin', __name__, url_prefix='/admin')

Expand All @@ -18,7 +18,9 @@
@admin_required
def index():
users = User.query.all()
return render_template('admin/index.html', users=users, active='index')
roles = Role.query.all()
return render_template('admin/index.html', users=users, roles=roles,
active='index')


@admin.route('/users')
Expand All @@ -28,20 +30,56 @@ def users():
users = User.query.all()
return render_template('admin/users.html', users=users, active='users')


@admin.route('/user/<int:user_id>', methods=['GET', 'POST'])
@login_required
@admin_required
def user(user_id):
user = User.query.filter_by(id=user_id).first_or_404()
form = UserForm(obj=user, next=request.args.get('next'))

form.role_code_select.choices = [(r.name, r.description) for r in Role.query.order_by('name')]
if form.validate_on_submit():
form.populate_obj(user)

user.empty_roles()
for rolename in form.role_code_select.data:
user.add_role(rolename)

if form.confirmed.data == True:
user.confirmed_at = datetime.utcnow()
else:
user.confirmed_at = None

db.session.add(user)
db.session.commit()

flash('User updated.', 'success')
else:
form.role_code_select.data = [r.name for r in user.roles]
if user.confirmed_at:
form.confirmed.data = True

return render_template('admin/user.html', user=user, form=form)

@admin.route('/roles')
@login_required
@admin_required
def roles():
roles = Role.query.all()
return render_template('admin/roles.html', roles=roles, active='roles')

@admin.route('/role/<int:role_id>', methods=['GET', 'POST'])
@login_required
@admin_required
def role(role_id):
role = Role.query.filter_by(id=role_id).first_or_404()
form = RoleForm(obj=role, next=request.args.get('next'))
if form.validate_on_submit():
form.populate_obj(role)

db.session.add(role)
db.session.commit()

flash('Role updated.', 'success')

return render_template('admin/role.html', role=role, form=form)

29 changes: 27 additions & 2 deletions fbone/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,19 @@
from flask.ext.babel import Babel

from .config import DefaultConfig
from .user import User, user
from .user import User, Role, SocialConnection, user
from .settings import settings
from .frontend import frontend
from .frontend import frontend, forms
from .api import api
from .admin import admin
from .extensions import db, mail, cache, login_manager, oid
from .utils import INSTANCE_FOLDER_PATH

from flask.ext.security import ( Security, SQLAlchemyUserDatastore )
from flask.ext.social import ( Social )
from flask.ext.social.datastore import ( SQLAlchemyConnectionDatastore )
from flask.ext.principal import ( Principal )


# For import *
__all__ = ['create_app']
Expand Down Expand Up @@ -93,6 +98,21 @@ def load_user(id):
# flask-openid
oid.init_app(app)

security_ds = SQLAlchemyUserDatastore(db, User, Role)
social_ds = SQLAlchemyConnectionDatastore(db, SocialConnection )
app.security = Security(app, security_ds,
login_form=forms.LoginForm,
register_form=forms.SignupForm,
confirm_register_form=forms.SignupForm,
# reset_password_form=ResetPasswordForm,
# send_confirmation_form=SendConfirmationForm,
forgot_password_form=forms.RecoverPasswordForm,
change_password_form=forms.ChangePasswordForm,
)
app.social = Social(app, social_ds)

app.principal = Principal(app)


def configure_blueprints(app, blueprints):
"""Configure blueprints in views."""
Expand All @@ -102,6 +122,8 @@ def configure_blueprints(app, blueprints):


def configure_template_filters(app):
# add jinja extensions
app.jinja_env.add_extension("jinja2.ext.do")

@app.template_filter()
def pretty_date(value):
Expand All @@ -111,6 +133,9 @@ def pretty_date(value):
def format_date(value, format='%Y-%m-%d'):
return value.strftime(format)

@app.template_filter()
def is_classname(value, clsstr):
return (value.__class__.__name__ == clsstr)

def configure_logging(app):
"""Configure file(info) and email(error) logging."""
Expand Down
33 changes: 33 additions & 0 deletions fbone/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,39 @@ class DefaultConfig(BaseConfig):
OPENID_FS_STORE_PATH = os.path.join(INSTANCE_FOLDER_PATH, 'openid')
make_dir(OPENID_FS_STORE_PATH)

# Flask-Security options
SECURITY_CONFIRMABLE = True
SECURITY_REGISTERABLE = True
SECURITY_RECOVERABLE = True
SECURITY_TRACKABLE = True
SECURITY_CHANGEABLE = True
# SECURITY_PASSWORD_HASH = 'bcrypt'
# SECURITY_PASSWORD_SALT = 'add your salt here'


SOCIAL_TWITTER = {
'consumer_key': 'twitter consumer key',
'consumer_secret': 'twitter consumer secret'
}

SOCIAL_FACEBOOK = {
#'consumer_key': 'facebook app id',
'consumer_key': '212817878843088',
#'consumer_secret': 'faceboo app secret',
'consumer_secret': '43f451ab755274ac8f6efb3be82be7ba',
}

SOCIAL_FOURSQUARE = {
'consumer_key': 'client id',
'consumer_secret': 'client secret'
}

SOCIAL_GOOGLE = {
'consumer_key': 'xxxx',
'consumer_secret': 'xxxx'
}



class TestConfig(BaseConfig):
TESTING = True
Expand Down
32 changes: 32 additions & 0 deletions fbone/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
"""
Misc stuff for working with forms
"""

class FieldDescription(object):
"""
Holds additional field information for form fields
"""
def __init__(self, *args, **kwargs):
self.description = None
self.placeholder = None
self.title = None

if args and len(args) > 0:
self.description = args[0]

if kwargs:
for key,val in kwargs.iteritems():
if not hasattr(self, key):
msg = "'%s' object has no attribute '%s'" % ( self.__class__.__name__, key )
raise AttributeError(msg)
setattr(self, key, val)

def __str__(self):
return self.description

def __repr__(self):
return self.description

def __unicode__(self):
return self.description
36 changes: 10 additions & 26 deletions fbone/frontend/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,23 @@
from ..utils import (PASSWORD_LEN_MIN, PASSWORD_LEN_MAX,
USERNAME_LEN_MIN, USERNAME_LEN_MAX)

from flask_security import forms

class LoginForm(Form):
next = HiddenField()
login = TextField(u'Username or email', [Required()])
password = PasswordField('Password', [Required(), Length(PASSWORD_LEN_MIN, PASSWORD_LEN_MAX)])
remember = BooleanField('Remember me')
submit = SubmitField('Sign in')
class LoginForm(forms.LoginForm):
pass


class SignupForm(Form):
next = HiddenField()
email = EmailField(u'Email', [Required(), Email()],
description=u"What's your email address?")
password = PasswordField(u'Password', [Required(), Length(PASSWORD_LEN_MIN, PASSWORD_LEN_MAX)],
description=u'%s characters or more! Be tricky.' % PASSWORD_LEN_MIN)
class RecoverPasswordForm(forms.ForgotPasswordForm):
pass

class ChangePasswordForm(forms.ChangePasswordForm):
pass

class SignupForm(forms.RegisterForm):
name = TextField(u'Choose your username', [Required(), Length(USERNAME_LEN_MIN, USERNAME_LEN_MAX)],
description=u"Don't worry. you can change it later.")
agree = BooleanField(u'Agree to the ' +
Markup('<a target="blank" href="/terms">Terms of Service</a>'), [Required()])
submit = SubmitField('Sign up')

def validate_name(self, field):
if User.query.filter_by(name=field.data).first() is not None:
Expand All @@ -41,19 +38,6 @@ def validate_email(self, field):
if User.query.filter_by(email=field.data).first() is not None:
raise ValidationError(u'This email is taken')


class RecoverPasswordForm(Form):
email = EmailField(u'Your email', [Email()])
submit = SubmitField('Send instructions')


class ChangePasswordForm(Form):
activation_key = HiddenField()
password = PasswordField(u'Password', [Required()])
password_again = PasswordField(u'Password again', [EqualTo('password', message="Passwords don't match")])
submit = SubmitField('Save')


class ReauthForm(Form):
next = HiddenField()
password = PasswordField(u'Password', [Required(), Length(PASSWORD_LEN_MIN, PASSWORD_LEN_MAX)])
Expand Down
14 changes: 7 additions & 7 deletions fbone/frontend/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
frontend = Blueprint('frontend', __name__)


@frontend.route('/login/openid', methods=['GET', 'POST'])
#@frontend.route('/login/openid', methods=['GET', 'POST'])
@oid.loginhandler
def login_openid():
if current_user.is_authenticated():
Expand Down Expand Up @@ -86,7 +86,7 @@ def search():
return render_template('frontend/search.html', pagination=pagination, keywords=keywords)


@frontend.route('/login', methods=['GET', 'POST'])
#@frontend.route('/login', methods=['GET', 'POST'])
def login():
if current_user.is_authenticated():
return redirect(url_for('user.index'))
Expand All @@ -109,7 +109,7 @@ def login():
return render_template('frontend/login.html', form=form)


@frontend.route('/reauth', methods=['GET', 'POST'])
#@frontend.route('/reauth', methods=['GET', 'POST'])
@login_required
def reauth():
form = ReauthForm(next=request.args.get('next'))
Expand All @@ -127,15 +127,15 @@ def reauth():
return render_template('frontend/reauth.html', form=form)


@frontend.route('/logout')
#@frontend.route('/logout')
@login_required
def logout():
logout_user()
flash(_('Logged out'), 'success')
return redirect(url_for('frontend.index'))


@frontend.route('/signup', methods=['GET', 'POST'])
#@frontend.route('/signup', methods=['GET', 'POST'])
def signup():
if current_user.is_authenticated():
return redirect(url_for('user.index'))
Expand All @@ -156,7 +156,7 @@ def signup():
return render_template('frontend/signup.html', form=form)


@frontend.route('/change_password', methods=['GET', 'POST'])
#@frontend.route('/change_password', methods=['GET', 'POST'])
def change_password():
user = None
if current_user.is_authenticated():
Expand Down Expand Up @@ -187,7 +187,7 @@ def change_password():
return render_template("frontend/change_password.html", form=form)


@frontend.route('/reset_password', methods=['GET', 'POST'])
#@frontend.route('/reset_password', methods=['GET', 'POST'])
def reset_password():
form = RecoverPasswordForm()

Expand Down
Loading