From 7890cd2622fd8c5da5ec1dac777c1dadade22a65 Mon Sep 17 00:00:00 2001 From: Francesco Siddi Date: Wed, 30 Aug 2017 23:06:43 +0200 Subject: [PATCH] Introducing settings blueprint Now settings live in a dedicated space, the settings blueprint can be used by Pillar applications, and the templates can be extended or overridden. Moved subscription and email settings to the blender-cloud repository. --- pillar/web/__init__.py | 4 +- pillar/web/settings/__init__.py | 5 ++ pillar/web/settings/routes.py | 40 +++++++++ pillar/web/users/__init__.py | 1 - pillar/web/users/routes.py | 102 ++-------------------- src/templates/_macros/_menu.pug | 8 +- src/templates/users/settings/_sidebar.pug | 12 +-- src/templates/users/settings/billing.pug | 68 --------------- src/templates/users/settings/emails.pug | 27 ------ src/templates/users/settings/profile.pug | 4 +- 10 files changed, 63 insertions(+), 208 deletions(-) create mode 100644 pillar/web/settings/__init__.py create mode 100644 pillar/web/settings/routes.py delete mode 100644 src/templates/users/settings/billing.pug delete mode 100644 src/templates/users/settings/emails.pug diff --git a/pillar/web/__init__.py b/pillar/web/__init__.py index bb609537..b499792d 100644 --- a/pillar/web/__init__.py +++ b/pillar/web/__init__.py @@ -1,5 +1,6 @@ def setup_app(app): - from . import main, users, projects, nodes, notifications, organizations, redirects, subquery + from . import main, users, projects, nodes, notifications, organizations, redirects, subquery, \ + settings main.setup_app(app, url_prefix=None) users.setup_app(app, url_prefix=None) redirects.setup_app(app, url_prefix='/r') @@ -8,3 +9,4 @@ def setup_app(app): notifications.setup_app(app, url_prefix='/notifications') subquery.setup_app(app) organizations.setup_app(app, url_prefix='/orgs') + settings.setup_app(app, url_prefix='/settings') diff --git a/pillar/web/settings/__init__.py b/pillar/web/settings/__init__.py new file mode 100644 index 00000000..9a13f987 --- /dev/null +++ b/pillar/web/settings/__init__.py @@ -0,0 +1,5 @@ +from .routes import blueprint + + +def setup_app(app, url_prefix): + app.register_blueprint(blueprint, url_prefix=url_prefix) diff --git a/pillar/web/settings/routes.py b/pillar/web/settings/routes.py new file mode 100644 index 00000000..5ed1d9fd --- /dev/null +++ b/pillar/web/settings/routes.py @@ -0,0 +1,40 @@ +import json +import logging + +from flask import Blueprint, flash, render_template +from flask_login import login_required, current_user +from werkzeug.exceptions import abort + +from pillar.web import system_util +from pillar.web.users import forms +from pillarsdk import User, exceptions as sdk_exceptions + +log = logging.getLogger(__name__) +blueprint = Blueprint('settings', __name__) + + +@blueprint.route('/profile', methods=['GET', 'POST']) +@login_required +def profile(): + """Profile view and edit page. This is a temporary implementation. + """ + if current_user.has_role('protected'): + return abort(404) # TODO: make this 403, handle template properly + api = system_util.pillar_api() + user = User.find(current_user.objectid, api=api) + + form = forms.UserProfileForm( + full_name=user.full_name, + username=user.username) + + if form.validate_on_submit(): + try: + user.full_name = form.full_name.data + user.username = form.username.data + user.update(api=api) + flash("Profile updated", 'success') + except sdk_exceptions.ResourceInvalid as e: + message = json.loads(e.content) + flash(message) + + return render_template('users/settings/profile.html', form=form, title='profile') diff --git a/pillar/web/users/__init__.py b/pillar/web/users/__init__.py index 7d499918..9a13f987 100644 --- a/pillar/web/users/__init__.py +++ b/pillar/web/users/__init__.py @@ -3,4 +3,3 @@ from .routes import blueprint def setup_app(app, url_prefix): app.register_blueprint(blueprint, url_prefix=url_prefix) - diff --git a/pillar/web/users/routes.py b/pillar/web/users/routes.py index e14fa0de..3579d62d 100644 --- a/pillar/web/users/routes.py +++ b/pillar/web/users/routes.py @@ -1,23 +1,21 @@ -import json import logging -from werkzeug import exceptions as wz_exceptions -from flask import abort, Blueprint, flash, redirect, render_template, request, session,\ +from flask import abort, Blueprint, redirect, render_template, request, session, \ url_for from flask_login import login_required, logout_user +from werkzeug import exceptions as wz_exceptions -from pillarsdk import exceptions as sdk_exceptions -from pillarsdk.users import User -from pillarsdk.groups import Group import pillar.api.blender_cloud.subscription import pillar.auth -from pillar.web import system_util +from pillar.api.blender_cloud.subscription import update_subscription from pillar.api.local_auth import generate_and_store_token, get_local_user from pillar.api.utils.authentication import find_user_in_db, upsert_user -from pillar.api.blender_cloud.subscription import update_subscription from pillar.auth import current_user from pillar.auth.oauth import OAuthSignIn, ProviderConfigurationMissing, ProviderNotImplemented, \ OAuthCodeNotProvided +from pillar.web import system_util +from pillarsdk import exceptions as sdk_exceptions +from pillarsdk.users import User from . import forms log = logging.getLogger(__name__) @@ -127,92 +125,6 @@ def switch(): return redirect(blender_id.switch_user_url(next_url=next_url_after_bid_login)) -@blueprint.route('/settings/profile', methods=['GET', 'POST']) -@login_required -def settings_profile(): - """Profile view and edit page. This is a temporary implementation. - """ - if current_user.has_role('protected'): - return abort(404) # TODO: make this 403, handle template properly - api = system_util.pillar_api() - user = User.find(current_user.objectid, api=api) - - form = forms.UserProfileForm( - full_name=user.full_name, - username=user.username) - - if form.validate_on_submit(): - try: - user.full_name = form.full_name.data - user.username = form.username.data - user.update(api=api) - flash("Profile updated", 'success') - except sdk_exceptions.ResourceInvalid as e: - message = json.loads(e.content) - flash(message) - - return render_template('users/settings/profile.html', form=form, title='profile') - - -@blueprint.route('/settings/emails', methods=['GET', 'POST']) -@login_required -def settings_emails(): - """Main email settings. - """ - if current_user.has_role('protected'): - return abort(404) # TODO: make this 403, handle template properly - api = system_util.pillar_api() - user = User.find(current_user.objectid, api=api) - - # Force creation of settings for the user (safely remove this code once - # implemented on account creation level, and after adding settings to all - # existing users) - if not user.settings: - user.settings = dict(email_communications=1) - user.update(api=api) - - if user.settings.email_communications is None: - user.settings.email_communications = 1 - user.update(api=api) - - # Generate form - form = forms.UserSettingsEmailsForm( - email_communications=user.settings.email_communications) - - if form.validate_on_submit(): - try: - user.settings.email_communications = form.email_communications.data - user.update(api=api) - flash("Profile updated", 'success') - except sdk_exceptions.ResourceInvalid as e: - message = json.loads(e.content) - flash(message) - - return render_template('users/settings/emails.html', form=form, title='emails') - - -@blueprint.route('/settings/billing') -@login_required -def settings_billing(): - """View the subscription status of a user - """ - if current_user.has_role('protected'): - return abort(404) # TODO: make this 403, handle template properly - api = system_util.pillar_api() - user = User.find(current_user.objectid, api=api) - groups = [] - if user.groups: - for group_id in user.groups: - group = Group.find(group_id, api=api) - groups.append(group.name) - - store_user = pillar.api.blender_cloud.subscription.fetch_subscription_info(user.email) - - return render_template( - 'users/settings/billing.html', - store_user=store_user, groups=groups, title='billing') - - @blueprint.route('/u//edit', methods=['GET', 'POST']) @login_required def users_edit(user_id): @@ -273,3 +185,5 @@ def users_index(): if not current_user.has_role('admin'): return abort(403) return render_template('users/index.html') + + diff --git a/src/templates/_macros/_menu.pug b/src/templates/_macros/_menu.pug index 6f3d5d24..8ad0a4d2 100644 --- a/src/templates/_macros/_menu.pug +++ b/src/templates/_macros/_menu.pug @@ -30,13 +30,13 @@ li(class="dropdown") li.subscription-status(class="{{ subscription }}") | {% if subscription == 'subscriber' %} a.navbar-item( - href="{{url_for('users.settings_billing')}}" + href="{{url_for('settings.billing')}}" title="View subscription info") i.pi-grin span Your subscription is active! | {% elif subscription == 'demo' %} a.navbar-item( - href="{{url_for('users.settings_billing')}}" + href="{{url_for('settings.billing')}}" title="View subscription info") i.pi-heart-filled span You have a free account. @@ -72,13 +72,13 @@ li(class="dropdown") | {% endif %} li a.navbar-item( - href="{{ url_for('users.settings_profile') }}" + href="{{ url_for('settings.profile') }}" title="Settings") i.pi-cog | Settings li a.navbar-item( - href="{{ url_for('users.settings_billing') }}" + href="{{ url_for('settings.billing') }}" title="Billing") i.pi-credit-card | Subscription diff --git a/src/templates/users/settings/_sidebar.pug b/src/templates/users/settings/_sidebar.pug index 0caf2b1b..d9519be0 100644 --- a/src/templates/users/settings/_sidebar.pug +++ b/src/templates/users/settings/_sidebar.pug @@ -4,17 +4,7 @@ .settings-content ul a(class="{% if title == 'profile' %}active{% endif %}", - href="{{ url_for('users.settings_profile') }}") + href="{{ url_for('settings.profile') }}") li i.pi-vcard | Profile - a(class="{% if title == 'emails' %}active{% endif %}", - href="{{ url_for('users.settings_emails') }}") - li - i.pi-email - | Emails - a(class="{% if title == 'billing' %}active{% endif %}", - href="{{ url_for('users.settings_billing') }}") - li - i.pi-credit-card - | Subscription diff --git a/src/templates/users/settings/billing.pug b/src/templates/users/settings/billing.pug deleted file mode 100644 index 47cb521c..00000000 --- a/src/templates/users/settings/billing.pug +++ /dev/null @@ -1,68 +0,0 @@ -| {% extends 'layout.html' %} -| {% block head %} -style(type='text/css'). - button#recheck_subscription { - margin-top: 1em; - } -| {% endblock %} -| {% block body %} -.container - #settings - include _sidebar - #settings-container - .settings-header - .settings-title Subscription - - .settings-content - - | {% if store_user['cloud_access'] %} - h3.subscription-active - i.pi-check - | Your subscription is active - h4 Thank you for supporting us! - - hr - - p Subscription expires on: {{ store_user['expiration_date'][:10] }} - a(href="https://store.blender.org/my-account/subscriptions/") Manage your subscription on Blender Store - | {% else %} - - | {% if 'demo' in groups %} - h3.subscription-demo - i.pi-heart-filled - | You have a free account - - hr - - p You have full access to the Blender Cloud, provided by the Blender Institute. This account is meant for free evaluation of the service. Get in touch with #[a(href="mailto:cloudsupport@blender.org") cloudsupport@blender.org] if you have any questions. - - | {% else %} - h3.subscription-missing - i.pi-info - | You do not have an active subscription. - h3 - a(href="https://store.blender.org/product/membership/") Get full access to Blender Cloud now! - | {% endif %} - - | {% endif %} - - p - button#recheck_subscription.btn.btn-default(onclick="javascript:recheck_subscription(this)") Re-check my subscription - hr - - -script. - function recheck_subscription(button) { - $(button).text('Checking'); - - $.get('/api/bcloud/update-subscription') - .done(function() { - window.location.reload(); - }) - .fail(function() { - alert('Unable to update subscription, please check your internet connection.'); - }) - ; - } - -| {% endblock %} diff --git a/src/templates/users/settings/emails.pug b/src/templates/users/settings/emails.pug deleted file mode 100644 index 9cfd6315..00000000 --- a/src/templates/users/settings/emails.pug +++ /dev/null @@ -1,27 +0,0 @@ -| {% extends 'layout.html' %} -| {% block body %} -.container - #settings - include _sidebar - #settings-container - .settings-header - .settings-title Emails - - .settings-content - - .settings-form - form#settings-form(method='POST', action="{{url_for('users.settings_emails')}}") - | {{ form.csrf_token }} - | {% for subfield in form.email_communications %} - .form-group. - {{ subfield }} - {{ subfield.label }} - | {% endfor %} - - .buttons - button.btn.btn-default.button-submit(type='submit') - i.pi-check - | Save Changes - - -| {% endblock %} diff --git a/src/templates/users/settings/profile.pug b/src/templates/users/settings/profile.pug index c0d4b248..a0c16f5b 100644 --- a/src/templates/users/settings/profile.pug +++ b/src/templates/users/settings/profile.pug @@ -2,14 +2,14 @@ | {% block body %} .container #settings - include _sidebar + | {% include 'users/settings/_sidebar.html'%} #settings-container .settings-header .settings-title Profile .settings-content .settings-form - form#settings-form(method='POST', action="{{url_for('users.settings_profile')}}") + form#settings-form(method='POST', action="{{url_for('settings.profile')}}") .left .form-group | {{ form.full_name.label }}