+ + % if request.session.peek_flash('error'): + % for error in request.session.pop_flash('error'): +
${error}
+ % endfor + % endif + + % if request.session.peek_flash(): + % for msg in request.session.pop_flash(): +
${msg|n}
+ % endfor + % endif + % if capture(self.page_title):

${self.page_title()}

% endif diff --git a/tailbone/templates/mobile/base_external_toolbars.mako b/tailbone/templates/mobile/base_internal_toolbars.mako similarity index 84% rename from tailbone/templates/mobile/base_external_toolbars.mako rename to tailbone/templates/mobile/base_internal_toolbars.mako index 058a627a..107ca928 100644 --- a/tailbone/templates/mobile/base_external_toolbars.mako +++ b/tailbone/templates/mobile/base_internal_toolbars.mako @@ -4,17 +4,17 @@ <%def name="mobile_body()"> - ${self.mobile_header()} -
${self.mobile_usermenu()} + ${self.mobile_header()} + ${self.mobile_page_body()} + ${self.mobile_footer()} +
- ${self.mobile_footer()} - diff --git a/tailbone/templates/mobile/datasync.mako b/tailbone/templates/mobile/datasync.mako index 58d42977..1f72702a 100644 --- a/tailbone/templates/mobile/datasync.mako +++ b/tailbone/templates/mobile/datasync.mako @@ -4,5 +4,5 @@ <%def name="title()">DataSync ${h.form(url('datasync.restart'))} -${h.submit('restart', "Restart DataSync", id='datasync-restart')} +${h.submit('restart', "Restart DataSync Daemon", id='datasync-restart')} ${h.end_form()} diff --git a/tailbone/views/__init__.py b/tailbone/views/__init__.py index 3e04f114..b39739dc 100644 --- a/tailbone/views/__init__.py +++ b/tailbone/views/__init__.py @@ -27,36 +27,24 @@ Pyramid Views from __future__ import unicode_literals, absolute_import from .core import View -from tailbone.views.grids import ( +from .master import MasterView + +# TODO: deprecate / remove some of this +from .autocomplete import AutocompleteView +from .crud import CrudView +from .grids import ( GridView, AlchemyGridView, SortableAlchemyGridView, PagedAlchemyGridView, SearchableAlchemyGridView) -from .crud import CrudView -from .master import MasterView -from tailbone.views.autocomplete import AutocompleteView - - -def home(request): - """ - Default home view. - """ - - return {} - - -def add_routes(config): - config.add_route('home', '/') def includeme(config): - add_routes(config) - - config.add_view(home, route_name='home', - renderer='/home.mako') + # core views config.include('tailbone.views.core') config.include('tailbone.views.common') - config.include('tailbone.views.auth') + + # main table views config.include('tailbone.views.bouncer') config.include('tailbone.views.brands') config.include('tailbone.views.categories') @@ -87,5 +75,6 @@ def includeme(config): config.include('tailbone.views.users') config.include('tailbone.views.vendors') + # batch views config.include('tailbone.views.batches') config.include('tailbone.views.batch.pricing') diff --git a/tailbone/views/auth.py b/tailbone/views/auth.py index 0cb2f60f..ed5fbf47 100644 --- a/tailbone/views/auth.py +++ b/tailbone/views/auth.py @@ -98,8 +98,7 @@ class AuthenticationView(View): # redirect if already logged in if self.request.user: - if not mobile: - self.request.session.flash("{} is already logged in".format(self.request.user), 'error') + self.request.session.flash("{} is already logged in".format(self.request.user), 'error') return self.redirect(referrer) form = Form(self.request, schema=UserLogin) @@ -111,14 +110,11 @@ class AuthenticationView(View): if user: # okay now they're truly logged in headers = remember(self.request, user.uuid) - # Treat URL from session as referrer, if available. + # treat URL from session as referrer, if available referrer = self.request.session.pop('next_url', referrer) return self.redirect(referrer, headers=headers) else: - if mobile: - context['error'] = "Invalid username or password" - else: - self.request.session.flash("Invalid username or password") + self.request.session.flash("Invalid username or password", 'error') return context def mobile_login(self): diff --git a/tailbone/views/common.py b/tailbone/views/common.py index e4be854a..f0b874fd 100644 --- a/tailbone/views/common.py +++ b/tailbone/views/common.py @@ -56,6 +56,18 @@ class CommonView(View): project_title = "Tailbone" project_version = tailbone.__version__ + def home(self, mobile=False): + """ + Home page view. + """ + return {} + + def mobile_home(self): + """ + Home page view for mobile. + """ + return self.home(mobile=True) + def about(self): """ Generic view to show "about project" info page. @@ -89,19 +101,36 @@ class CommonView(View): self.request.session.flash("Thank you for your feedback.") return httpexceptions.HTTPFound(location=form.data['referrer']) return {'form': forms.FormRenderer(form)} + + def bogus_error(self): + """ + A special view which simply raises an error, for the sake of testing + uncaught exception handling. + """ + raise Exception("Congratulations, you have triggered a bogus error.") @classmethod def defaults(cls, config): + # home + config.add_route('home', '/') + config.add_view(cls, attr='home', route_name='home', renderer='/home.mako') + config.add_route('mobile.home', '/mobile/') + config.add_view(cls, attr='mobile_home', route_name='mobile.home', renderer='/mobile/home.mako') + # about config.add_route('about', '/about') config.add_view(cls, attr='about', route_name='about', renderer='/about.mako') config.add_route('mobile.about', '/mobile/about') config.add_view(cls, attr='about', route_name='mobile.about', renderer='/mobile/about.mako') + # feedback config.add_route('feedback', '/feedback') - config.add_view(cls, attr='feedback', route_name='feedback', - renderer='/feedback.mako') + config.add_view(cls, attr='feedback', route_name='feedback', renderer='/feedback.mako') + + # bogus error + config.add_route('bogus_error', '/bogus-error') + config.add_view(cls, attr='bogus_error', route_name='bogus_error', permission='errors.bogus') def includeme(config): diff --git a/tailbone/views/datasync.py b/tailbone/views/datasync.py index 6d31599d..4002e373 100644 --- a/tailbone/views/datasync.py +++ b/tailbone/views/datasync.py @@ -30,7 +30,6 @@ import subprocess import logging from rattail.db import model -from rattail.config import parse_list from tailbone.views import MasterView @@ -66,7 +65,7 @@ class DataSyncChangesView(MasterView): def restart(self): # TODO: Add better validation (e.g. CSRF) here? if self.request.method == 'POST': - cmd = parse_list(self.rattail_config.require('tailbone', 'datasync.restart')) + cmd = self.rattail_config.getlist('tailbone', 'datasync.restart', default='/bin/sleep 3') # simulate by default log.debug("attempting datasync restart with command: {}".format(cmd)) result = subprocess.call(cmd) if result == 0: