More mobile view improvements, various
This commit is contained in:
parent
14ac7aa198
commit
7f14f50ee0
|
@ -3,21 +3,29 @@
|
||||||
* Global styles for mobile templates
|
* Global styles for mobile templates
|
||||||
****************************************/
|
****************************************/
|
||||||
|
|
||||||
|
/* main user menu button when root */
|
||||||
[data-role="header"] a.root-user,
|
[data-role="header"] a.root-user,
|
||||||
[data-role="header"] a.root-user:hover {
|
[data-role="header"] a.root-user:hover {
|
||||||
background-color: red;
|
background-color: red;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* become/stop root menu links */
|
||||||
#usermenu .root-user a {
|
#usermenu .root-user a {
|
||||||
background-color: red;
|
background-color: red;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* normal flash messages */
|
||||||
|
.flash {
|
||||||
|
color: green;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* error flash messages */
|
||||||
|
.error {
|
||||||
|
color: red;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
.replacement-header {
|
.replacement-header {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* used by login page */
|
|
||||||
.error {
|
|
||||||
color: red;
|
|
||||||
margin-bottom: 1em;
|
|
||||||
}
|
|
||||||
|
|
|
@ -36,6 +36,11 @@ $(document).on('pageshow', function() {
|
||||||
if (el.is(':visible')) {
|
if (el.is(':visible')) {
|
||||||
el.focus();
|
el.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: seems like this should be better somehow...
|
||||||
|
// remove all flash messages after 2.5 seconds
|
||||||
|
window.setTimeout(function() { $('.flash, .error').remove(); }, 2500);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -18,11 +18,6 @@
|
||||||
${form.begin(**{'data-ajax': 'false'})}
|
${form.begin(**{'data-ajax': 'false'})}
|
||||||
${form.hidden('referrer', value=referrer)}
|
${form.hidden('referrer', value=referrer)}
|
||||||
|
|
||||||
## this is used by mobile view
|
|
||||||
% if error:
|
|
||||||
<div class="error">${error}</div>
|
|
||||||
% endif
|
|
||||||
|
|
||||||
${form.field_div('username', form.text('username'))}
|
${form.field_div('username', form.text('username'))}
|
||||||
${form.field_div('password', form.password('password'))}
|
${form.field_div('password', form.password('password'))}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
## -*- coding: utf-8 -*-
|
## -*- coding: utf-8 -*-
|
||||||
<%inherit file="/mobile/base.mako" />
|
<%inherit file="/mobile/base.mako" />
|
||||||
|
|
||||||
<%def name="title()">About ${project_title}</%def>
|
<%def name="title()">About ${self.app_title()}</%def>
|
||||||
|
|
||||||
<h2>${project_title} ${project_version}</h2>
|
<h2>${project_title} ${project_version}</h2>
|
||||||
|
|
||||||
|
|
|
@ -22,18 +22,20 @@
|
||||||
<%def name="mobile_body()">
|
<%def name="mobile_body()">
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
|
## note that our toolbars are *external* (in jqm-speak) by default
|
||||||
|
|
||||||
|
${self.mobile_header()}
|
||||||
|
|
||||||
<div data-role="page" data-url="${self.page_url()}"${' data-rel="dialog"' if dialog else ''|n}>
|
<div data-role="page" data-url="${self.page_url()}"${' data-rel="dialog"' if dialog else ''|n}>
|
||||||
|
|
||||||
${self.mobile_usermenu()}
|
${self.mobile_usermenu()}
|
||||||
|
|
||||||
${self.mobile_header()}
|
|
||||||
|
|
||||||
${self.mobile_page_body()}
|
${self.mobile_page_body()}
|
||||||
|
|
||||||
${self.mobile_footer()}
|
|
||||||
|
|
||||||
</div><!-- page -->
|
</div><!-- page -->
|
||||||
|
|
||||||
|
${self.mobile_footer()}
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</%def>
|
</%def>
|
||||||
|
|
||||||
|
@ -67,14 +69,14 @@
|
||||||
<div id="usermenu" data-role="panel" data-display="overlay">
|
<div id="usermenu" data-role="panel" data-display="overlay">
|
||||||
<ul data-role="listview">
|
<ul data-role="listview">
|
||||||
<li data-icon="home">${h.link_to("Home", url('mobile.home'))}</li>
|
<li data-icon="home">${h.link_to("Home", url('mobile.home'))}</li>
|
||||||
|
% if request.has_perm('datasync.restart'):
|
||||||
|
<li>${h.link_to("DataSync", url('datasync.mobile'))}</li>
|
||||||
|
% endif
|
||||||
% if request.is_root:
|
% if request.is_root:
|
||||||
<li class="root-user" data-icon="forbidden">${h.link_to("Stop being root", url('stop_root'), **{'data-ajax': 'false'})}</li>
|
<li class="root-user" data-icon="forbidden">${h.link_to("Stop being root", url('stop_root'), **{'data-ajax': 'false'})}</li>
|
||||||
% elif request.is_admin:
|
% elif request.is_admin:
|
||||||
<li class="root-user" data-icon="forbidden">${h.link_to("Become root", url('become_root'), **{'data-ajax': 'false'})}</li>
|
<li class="root-user" data-icon="forbidden">${h.link_to("Become root", url('become_root'), **{'data-ajax': 'false'})}</li>
|
||||||
% endif
|
% endif
|
||||||
% if request.has_perm('datasync.restart'):
|
|
||||||
<li>${h.link_to("DataSync", url('datasync.mobile'))}</li>
|
|
||||||
% endif
|
|
||||||
<li data-icon="lock">${h.link_to("Logout", url('logout'), **{'data-ajax': 'false'})}</li>
|
<li data-icon="lock">${h.link_to("Logout", url('logout'), **{'data-ajax': 'false'})}</li>
|
||||||
<li data-icon="info">${h.link_to("About {}".format(capture(self.app_title)), url('mobile.about'))}</li>
|
<li data-icon="info">${h.link_to("About {}".format(capture(self.app_title)), url('mobile.about'))}</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
@ -83,6 +85,19 @@
|
||||||
|
|
||||||
<%def name="mobile_page_body()">
|
<%def name="mobile_page_body()">
|
||||||
<div role="main" class="ui-content">
|
<div role="main" class="ui-content">
|
||||||
|
|
||||||
|
% if request.session.peek_flash('error'):
|
||||||
|
% for error in request.session.pop_flash('error'):
|
||||||
|
<div class="error">${error}</div>
|
||||||
|
% endfor
|
||||||
|
% endif
|
||||||
|
|
||||||
|
% if request.session.peek_flash():
|
||||||
|
% for msg in request.session.pop_flash():
|
||||||
|
<div class="flash">${msg|n}</div>
|
||||||
|
% endfor
|
||||||
|
% endif
|
||||||
|
|
||||||
% if capture(self.page_title):
|
% if capture(self.page_title):
|
||||||
<h2>${self.page_title()}</h2>
|
<h2>${self.page_title()}</h2>
|
||||||
% endif
|
% endif
|
||||||
|
|
|
@ -4,17 +4,17 @@
|
||||||
<%def name="mobile_body()">
|
<%def name="mobile_body()">
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
${self.mobile_header()}
|
|
||||||
|
|
||||||
<div data-role="page" data-url="${self.page_url()}"${' data-rel="dialog"' if dialog else ''|n}>
|
<div data-role="page" data-url="${self.page_url()}"${' data-rel="dialog"' if dialog else ''|n}>
|
||||||
|
|
||||||
${self.mobile_usermenu()}
|
${self.mobile_usermenu()}
|
||||||
|
|
||||||
|
${self.mobile_header()}
|
||||||
|
|
||||||
${self.mobile_page_body()}
|
${self.mobile_page_body()}
|
||||||
|
|
||||||
|
${self.mobile_footer()}
|
||||||
|
|
||||||
</div><!-- page -->
|
</div><!-- page -->
|
||||||
|
|
||||||
${self.mobile_footer()}
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</%def>
|
</%def>
|
|
@ -4,5 +4,5 @@
|
||||||
<%def name="title()">DataSync</%def>
|
<%def name="title()">DataSync</%def>
|
||||||
|
|
||||||
${h.form(url('datasync.restart'))}
|
${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()}
|
${h.end_form()}
|
||||||
|
|
|
@ -27,36 +27,24 @@ Pyramid Views
|
||||||
from __future__ import unicode_literals, absolute_import
|
from __future__ import unicode_literals, absolute_import
|
||||||
|
|
||||||
from .core import View
|
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,
|
GridView, AlchemyGridView, SortableAlchemyGridView,
|
||||||
PagedAlchemyGridView, SearchableAlchemyGridView)
|
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):
|
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.core')
|
||||||
config.include('tailbone.views.common')
|
config.include('tailbone.views.common')
|
||||||
|
|
||||||
config.include('tailbone.views.auth')
|
config.include('tailbone.views.auth')
|
||||||
|
|
||||||
|
# main table views
|
||||||
config.include('tailbone.views.bouncer')
|
config.include('tailbone.views.bouncer')
|
||||||
config.include('tailbone.views.brands')
|
config.include('tailbone.views.brands')
|
||||||
config.include('tailbone.views.categories')
|
config.include('tailbone.views.categories')
|
||||||
|
@ -87,5 +75,6 @@ def includeme(config):
|
||||||
config.include('tailbone.views.users')
|
config.include('tailbone.views.users')
|
||||||
config.include('tailbone.views.vendors')
|
config.include('tailbone.views.vendors')
|
||||||
|
|
||||||
|
# batch views
|
||||||
config.include('tailbone.views.batches')
|
config.include('tailbone.views.batches')
|
||||||
config.include('tailbone.views.batch.pricing')
|
config.include('tailbone.views.batch.pricing')
|
||||||
|
|
|
@ -98,8 +98,7 @@ class AuthenticationView(View):
|
||||||
|
|
||||||
# redirect if already logged in
|
# redirect if already logged in
|
||||||
if self.request.user:
|
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)
|
return self.redirect(referrer)
|
||||||
|
|
||||||
form = Form(self.request, schema=UserLogin)
|
form = Form(self.request, schema=UserLogin)
|
||||||
|
@ -111,14 +110,11 @@ class AuthenticationView(View):
|
||||||
if user:
|
if user:
|
||||||
# okay now they're truly logged in
|
# okay now they're truly logged in
|
||||||
headers = remember(self.request, user.uuid)
|
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)
|
referrer = self.request.session.pop('next_url', referrer)
|
||||||
return self.redirect(referrer, headers=headers)
|
return self.redirect(referrer, headers=headers)
|
||||||
else:
|
else:
|
||||||
if mobile:
|
self.request.session.flash("Invalid username or password", 'error')
|
||||||
context['error'] = "Invalid username or password"
|
|
||||||
else:
|
|
||||||
self.request.session.flash("Invalid username or password")
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def mobile_login(self):
|
def mobile_login(self):
|
||||||
|
|
|
@ -56,6 +56,18 @@ class CommonView(View):
|
||||||
project_title = "Tailbone"
|
project_title = "Tailbone"
|
||||||
project_version = tailbone.__version__
|
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):
|
def about(self):
|
||||||
"""
|
"""
|
||||||
Generic view to show "about project" info page.
|
Generic view to show "about project" info page.
|
||||||
|
@ -89,19 +101,36 @@ class CommonView(View):
|
||||||
self.request.session.flash("Thank you for your feedback.")
|
self.request.session.flash("Thank you for your feedback.")
|
||||||
return httpexceptions.HTTPFound(location=form.data['referrer'])
|
return httpexceptions.HTTPFound(location=form.data['referrer'])
|
||||||
return {'form': forms.FormRenderer(form)}
|
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
|
@classmethod
|
||||||
def defaults(cls, config):
|
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
|
# about
|
||||||
config.add_route('about', '/about')
|
config.add_route('about', '/about')
|
||||||
config.add_view(cls, attr='about', route_name='about', renderer='/about.mako')
|
config.add_view(cls, attr='about', route_name='about', renderer='/about.mako')
|
||||||
config.add_route('mobile.about', '/mobile/about')
|
config.add_route('mobile.about', '/mobile/about')
|
||||||
config.add_view(cls, attr='about', route_name='mobile.about', renderer='/mobile/about.mako')
|
config.add_view(cls, attr='about', route_name='mobile.about', renderer='/mobile/about.mako')
|
||||||
|
|
||||||
|
# feedback
|
||||||
config.add_route('feedback', '/feedback')
|
config.add_route('feedback', '/feedback')
|
||||||
config.add_view(cls, attr='feedback', route_name='feedback',
|
config.add_view(cls, attr='feedback', route_name='feedback', renderer='/feedback.mako')
|
||||||
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):
|
def includeme(config):
|
||||||
|
|
|
@ -30,7 +30,6 @@ import subprocess
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from rattail.db import model
|
from rattail.db import model
|
||||||
from rattail.config import parse_list
|
|
||||||
|
|
||||||
from tailbone.views import MasterView
|
from tailbone.views import MasterView
|
||||||
|
|
||||||
|
@ -66,7 +65,7 @@ class DataSyncChangesView(MasterView):
|
||||||
def restart(self):
|
def restart(self):
|
||||||
# TODO: Add better validation (e.g. CSRF) here?
|
# TODO: Add better validation (e.g. CSRF) here?
|
||||||
if self.request.method == 'POST':
|
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))
|
log.debug("attempting datasync restart with command: {}".format(cmd))
|
||||||
result = subprocess.call(cmd)
|
result = subprocess.call(cmd)
|
||||||
if result == 0:
|
if result == 0:
|
||||||
|
|
Loading…
Reference in a new issue