Purge things for legacy (jquery) mobile, and unused template themes

gosh it feels good to get rid of this stuff...  fingers crossed that nothing
was broken, but am thinking it's safe
This commit is contained in:
Lance Edgar 2021-01-30 15:52:47 -06:00
parent fac00e6ecd
commit 708641a8f1
70 changed files with 196 additions and 4886 deletions

View file

@ -115,14 +115,6 @@ class MasterView(View):
# set to True to declare model as "contact"
is_contact = False
supports_mobile = False
mobile_creatable = False
mobile_editable = False
mobile_pageable = True
mobile_filterable = False
mobile_executable = False
mobile = False
listing = False
creating = False
creates_multiple = False
@ -170,14 +162,6 @@ class MasterView(View):
rows_downloadable_csv = False
rows_downloadable_xlsx = False
mobile_rows_creatable = False
mobile_rows_creatable_via_browse = False
mobile_rows_quickable = False
mobile_rows_filterable = False
mobile_rows_viewable = False
mobile_rows_editable = False
mobile_rows_deletable = False
row_labels = {}
@property
@ -236,24 +220,6 @@ class MasterView(View):
"""
return getattr(cls, 'version_grid_factory', grids.Grid)
@classmethod
def get_mobile_grid_factory(cls):
"""
Must return a callable to be used when creating new mobile grid
instances. Instead of overriding this, you can set
:attr:`mobile_grid_factory`. Default factory is :class:`MobileGrid`.
"""
return getattr(cls, 'mobile_grid_factory', grids.MobileGrid)
@classmethod
def get_mobile_row_grid_factory(cls):
"""
Must return a callable to be used when creating new mobile row grid
instances. Instead of overriding this, you can set
:attr:`mobile_row_grid_factory`. Default factory is :class:`MobileGrid`.
"""
return getattr(cls, 'mobile_row_grid_factory', grids.MobileGrid)
def set_labels(self, obj):
labels = self.collect_labels()
for key, label in six.iteritems(labels):
@ -624,163 +590,6 @@ class MasterView(View):
def render_version_comment(self, transaction, column):
return transaction.meta.get('comment', "")
def mobile_index(self):
"""
Mobile "home" page for the data model
"""
self.mobile = True
self.listing = True
grid = self.make_mobile_grid()
return self.render_to_response('index', {'grid': grid}, mobile=True)
@classmethod
def get_mobile_grid_key(cls):
"""
Must return a unique "config key" for the mobile grid, for sort/filter
purposes etc. (It need only be unique among *mobile* grids.) Instead
of overriding this, you can set :attr:`mobile_grid_key`. Default is
the value returned by :meth:`get_route_prefix()`.
"""
if hasattr(cls, 'mobile_grid_key'):
return cls.mobile_grid_key
return 'mobile.{}'.format(cls.get_route_prefix())
def make_mobile_grid(self, factory=None, key=None, data=None, columns=None, **kwargs):
"""
Creates a new mobile grid instance
"""
if factory is None:
factory = self.get_mobile_grid_factory()
if key is None:
key = self.get_mobile_grid_key()
if data is None:
data = self.get_mobile_data(session=kwargs.get('session'))
if columns is None:
columns = self.get_mobile_grid_columns()
kwargs.setdefault('request', self.request)
kwargs.setdefault('mobile', True)
kwargs = self.make_mobile_grid_kwargs(**kwargs)
grid = factory(key, data, columns, **kwargs)
self.configure_mobile_grid(grid)
grid.load_settings()
return grid
def get_mobile_grid_columns(self):
if hasattr(self, 'mobile_grid_columns'):
return self.mobile_grid_columns
# TODO
return ['listitem']
def get_mobile_data(self, session=None):
"""
Must return the "raw" / full data set for the mobile grid. This data
should *not* yet be sorted or filtered in any way; that happens later.
Default is the value returned by :meth:`get_data()`, in which case all
records visible in the traditional view, are visible in mobile too.
"""
return self.get_data(session=session)
def make_mobile_grid_kwargs(self, **kwargs):
"""
Must return a dictionary of kwargs to be passed to the factory when
creating new mobile grid instances.
"""
defaults = {
'model_class': getattr(self, 'model_class', None),
'pageable': self.mobile_pageable,
'sortable': False,
'filterable': self.mobile_filterable,
'renderers': self.make_mobile_grid_renderers(),
'url': lambda obj: self.get_action_url('view', obj, mobile=True),
}
# TODO: this seems wrong..
if self.mobile_filterable:
defaults['filters'] = self.make_mobile_filters()
defaults.update(kwargs)
return defaults
def make_mobile_grid_renderers(self):
return {
'listitem': self.render_mobile_listitem,
}
def render_mobile_listitem(self, obj, i):
return obj
def configure_mobile_grid(self, grid):
pass
def make_mobile_row_grid(self, factory=None, key=None, data=None, columns=None, **kwargs):
"""
Make a new (configured) rows grid instance for mobile.
"""
instance = kwargs.pop('instance', self.get_instance())
if factory is None:
factory = self.get_mobile_row_grid_factory()
if key is None:
key = 'mobile.{}.{}'.format(self.get_grid_key(), self.request.matchdict[self.get_model_key()])
if data is None:
data = self.get_mobile_row_data(instance)
if columns is None:
columns = self.get_mobile_row_grid_columns()
kwargs.setdefault('request', self.request)
kwargs.setdefault('mobile', True)
kwargs = self.make_mobile_row_grid_kwargs(**kwargs)
grid = factory(key, data, columns, **kwargs)
self.configure_mobile_row_grid(grid)
grid.load_settings()
return grid
def get_mobile_row_grid_columns(self):
if hasattr(self, 'mobile_row_grid_columns'):
return self.mobile_row_grid_columns
# TODO
return ['listitem']
def make_mobile_row_grid_kwargs(self, **kwargs):
"""
Must return a dictionary of kwargs to be passed to the factory when
creating new mobile *row* grid instances.
"""
defaults = {
'model_class': self.model_row_class,
# TODO
'pageable': self.pageable,
'sortable': False,
'filterable': self.mobile_rows_filterable,
'renderers': self.make_mobile_row_grid_renderers(),
'url': lambda obj: self.get_row_action_url('view', obj, mobile=True),
}
# TODO: this seems wrong..
if self.mobile_rows_filterable:
defaults['filters'] = self.make_mobile_row_filters()
defaults.update(kwargs)
return defaults
def make_mobile_row_grid_renderers(self):
return {
'listitem': self.render_mobile_row_listitem,
}
def configure_mobile_row_grid(self, grid):
pass
def make_mobile_filters(self):
"""
Returns a set of filters for the mobile grid, if applicable.
"""
def make_mobile_row_filters(self):
"""
Returns a set of filters for the mobile row grid, if applicable.
"""
def render_mobile_row_listitem(self, obj, i):
return obj
def create(self, form=None, template='create'):
"""
View for creating a new model record.
@ -800,22 +609,6 @@ class MasterView(View):
context['dform'] = form.make_deform_form()
return self.render_to_response(template, context)
def mobile_create(self):
"""
Mobile view for creating a new primary object
"""
self.mobile = True
self.creating = True
form = self.make_mobile_form(self.get_model_class())
if self.request.method == 'POST':
if self.validate_mobile_form(form):
# let save_create_form() return alternate object if necessary
obj = self.save_mobile_create_form(form)
self.after_create(obj)
self.flash_after_create(obj)
return self.redirect_after_create(obj, mobile=True)
return self.render_to_response('create', {'form': form}, mobile=True)
def save_create_form(self, form):
uploads = self.normalize_uploads(form)
self.before_create(form)
@ -1044,19 +837,10 @@ class MasterView(View):
self.request.session.flash("{} has been created: {}".format(
self.get_model_title(), self.get_instance_title(obj)))
def save_mobile_create_form(self, form):
self.before_create(form)
with self.Session.no_autoflush:
obj = self.objectify(form, self.form_deserialized)
self.before_create_flush(obj, form)
self.Session.add(obj)
self.Session.flush()
return obj
def redirect_after_create(self, instance, mobile=False):
def redirect_after_create(self, instance, **kwargs):
if self.populatable and self.should_populate(instance):
return self.redirect(self.get_action_url('populate', instance, mobile=mobile))
return self.redirect(self.get_action_url('view', instance, mobile=mobile))
return self.redirect(self.get_action_url('populate', instance))
return self.redirect(self.get_action_url('view', instance))
def should_populate(self, obj):
return True
@ -1249,8 +1033,8 @@ class MasterView(View):
self.Session.flush()
return cloned
def redirect_after_clone(self, instance, mobile=False):
return self.redirect(self.get_action_url('view', instance, mobile=mobile))
def redirect_after_clone(self, instance, **kwargs):
return self.redirect(self.get_action_url('view', instance))
def touch(self):
"""
@ -1414,75 +1198,6 @@ class MasterView(View):
versions.extend(query.all())
return versions
def mobile_view(self):
"""
Mobile view for displaying a single object's details
"""
self.mobile = True
self.viewing = True
instance = self.get_instance()
form = self.make_mobile_form(instance)
context = {
'instance': instance,
'instance_title': self.get_instance_title(instance),
'instance_editable': self.editable_instance(instance),
# 'instance_deletable': self.deletable_instance(instance),
'form': form,
}
if self.has_rows:
context['model_row_class'] = self.model_row_class
context['grid'] = self.make_mobile_row_grid(instance=instance)
return self.render_to_response('view', context, mobile=True)
def make_mobile_form(self, instance=None, factory=None, fields=None, schema=None, **kwargs):
"""
Creates a new mobile form for the given model class/instance.
"""
if factory is None:
factory = self.get_mobile_form_factory()
if fields is None:
fields = self.get_mobile_form_fields()
if schema is None:
schema = self.make_mobile_form_schema()
if not self.creating:
kwargs['model_instance'] = instance
kwargs = self.make_mobile_form_kwargs(**kwargs)
form = factory(fields, schema, **kwargs)
self.configure_mobile_form(form)
return form
def get_mobile_form_fields(self):
if hasattr(self, 'mobile_form_fields'):
return self.mobile_form_fields
# TODO
# raise NotImplementedError
def make_mobile_form_schema(self):
if not self.model_class:
# TODO
raise NotImplementedError
def make_mobile_form_kwargs(self, **kwargs):
"""
Return a dictionary of kwargs to be passed to the factory when creating
new mobile forms.
"""
defaults = {
'request': self.request,
'readonly': self.viewing,
'model_class': getattr(self, 'model_class', None),
'action_url': self.request.current_route_url(_query=None),
}
if self.creating:
defaults['cancel_url'] = self.get_index_url(mobile=True)
else:
instance = kwargs['model_instance']
defaults['cancel_url'] = self.get_action_url('view', instance, mobile=True)
defaults.update(kwargs)
return defaults
def configure_common_form(self, form):
"""
Configure the form in whatever way is deemed "common" - i.e. where
@ -1491,6 +1206,8 @@ class MasterView(View):
By default this removes the 'uuid' field (if present), sets any primary
key fields to be readonly (if we have a :attr:`model_class` and are in
edit mode), and sets labels as defined by the master class hierarchy.
TODO: this logic should be moved back into configure_form()
"""
form.remove_field('uuid')
@ -1516,62 +1233,29 @@ class MasterView(View):
# is the safer option and would help prevent unwanted mistakes
form.set_default('local_only', True)
def configure_mobile_form(self, form):
"""
Configure the main "mobile" form for the view's data model.
"""
self.configure_common_form(form)
def validate_mobile_form(self, form):
if form.validate(newstyle=True):
# TODO: deprecate / remove self.form_deserialized
self.form_deserialized = form.validated
return True
else:
return False
def make_mobile_row_form(self, instance=None, factory=None, fields=None, schema=None, **kwargs):
"""
Creates a new mobile form for the given model class/instance.
"""
if factory is None:
factory = self.get_mobile_row_form_factory()
if fields is None:
fields = self.get_mobile_row_form_fields()
if schema is None:
schema = self.make_mobile_row_form_schema()
if not self.creating:
kwargs['model_instance'] = instance
kwargs = self.make_mobile_row_form_kwargs(**kwargs)
form = factory(fields, schema, **kwargs)
self.configure_mobile_row_form(form)
return form
def make_quick_row_form(self, instance=None, factory=None, fields=None, schema=None, mobile=False, **kwargs):
def make_quick_row_form(self, instance=None, factory=None, fields=None, schema=None, **kwargs):
"""
Creates a "quick" form for adding a new row to the given instance.
"""
if factory is None:
factory = self.get_quick_row_form_factory(mobile=mobile)
factory = self.get_quick_row_form_factory()
if fields is None:
fields = self.get_quick_row_form_fields(mobile=mobile)
fields = self.get_quick_row_form_fields()
if schema is None:
schema = self.make_quick_row_form_schema(mobile=mobile)
schema = self.make_quick_row_form_schema()
kwargs['mobile'] = mobile
kwargs = self.make_quick_row_form_kwargs(**kwargs)
form = factory(fields, schema, **kwargs)
self.configure_quick_row_form(form, mobile=mobile)
self.configure_quick_row_form(form)
return form
def get_quick_row_form_factory(self, mobile=False):
def get_quick_row_form_factory(self, **kwargs):
return forms.Form
def get_quick_row_form_fields(self, mobile=False):
def get_quick_row_form_fields(self, **kwargs):
pass
def make_quick_row_form_schema(self, mobile=False):
def make_quick_row_form_schema(self, **kwargs):
schema = colander.MappingSchema()
schema.add(colander.SchemaNode(colander.String(), name='quick_entry'))
return schema
@ -1585,102 +1269,12 @@ class MasterView(View):
defaults.update(kwargs)
return defaults
def configure_quick_row_form(self, form, mobile=False):
def configure_quick_row_form(self, form, **kwargs):
pass
def get_mobile_row_form_fields(self):
if hasattr(self, 'mobile_row_form_fields'):
return self.mobile_row_form_fields
# TODO
# raise NotImplementedError
def make_mobile_row_form_schema(self):
if not self.model_row_class:
# TODO
raise NotImplementedError
def make_mobile_row_form_kwargs(self, **kwargs):
"""
Return a dictionary of kwargs to be passed to the factory when creating
new mobile row forms.
"""
defaults = {
'request': self.request,
'mobile': True,
'readonly': self.viewing,
'model_class': getattr(self, 'model_row_class', None),
'action_url': self.request.current_route_url(_query=None),
}
if self.creating:
defaults['cancel_url'] = self.request.get_referrer()
else:
instance = kwargs['model_instance']
defaults['cancel_url'] = self.get_row_action_url('view', instance, mobile=True)
defaults.update(kwargs)
return defaults
def configure_mobile_row_form(self, form):
"""
Configure the mobile row form.
"""
# TODO: is any of this stuff from configure_form() needed?
# if self.editing:
# model_class = self.get_model_class(error=False)
# if model_class:
# mapper = orm.class_mapper(model_class)
# for key in mapper.primary_key:
# for field in form.fields:
# if field == key.name:
# form.set_readonly(field)
# break
# form.remove_field('uuid')
self.set_row_labels(form)
def validate_mobile_row_form(self, form):
controls = self.request.POST.items()
try:
self.form_deserialized = form.validate(controls)
except deform.ValidationFailure:
return False
return True
def validate_quick_row_form(self, form):
return form.validate(newstyle=True)
def get_mobile_row_data(self, parent):
query = self.get_row_data(parent)
return self.sort_mobile_row_data(query)
def sort_mobile_row_data(self, query):
return query
def mobile_row_route_url(self, route_name, **kwargs):
route_name = 'mobile.{}.{}_row'.format(self.get_route_prefix(), route_name)
return self.request.route_url(route_name, **kwargs)
def mobile_view_row(self):
"""
Mobile view for row items
"""
self.mobile = True
self.viewing = True
row = self.get_row_instance()
parent = self.get_parent(row)
form = self.make_mobile_row_form(row)
context = {
'row': row,
'parent_instance': parent,
'parent_title': self.get_instance_title(parent),
'parent_url': self.get_action_url('view', parent, mobile=True),
'instance': row,
'instance_title': self.get_row_instance_title(row),
'instance_editable': self.row_editable(row),
'parent_model_title': self.get_model_title(),
'form': form,
}
return self.render_to_response('view_row', context, mobile=True)
def make_default_row_grid_tools(self, obj):
if self.rows_creatable:
link = tags.link_to("Create a new {}".format(self.get_row_model_title()),
@ -1851,61 +1445,16 @@ class MasterView(View):
context['dform'] = form.make_deform_form()
return self.render_to_response('edit', context)
def mobile_edit(self):
"""
Mobile view for editing an existing model record.
"""
self.mobile = True
self.editing = True
obj = self.get_instance()
if not self.editable_instance(obj):
msg = "Edit is not permitted for {}: {}".format(
self.get_model_title(),
self.get_instance_title(obj))
self.request.session.flash(msg, 'error')
return self.redirect(self.get_action_url('view', obj))
form = self.make_mobile_form(obj)
if self.request.method == 'POST':
if self.validate_mobile_form(form):
# note that save_form() may return alternate object
obj = self.save_mobile_edit_form(form)
msg = "{} has been updated: {}".format(
self.get_model_title(),
self.get_instance_title(obj))
self.request.session.flash(msg)
return self.redirect_after_edit(obj, mobile=True)
context = {
'instance': obj,
'instance_title': self.get_instance_title(obj),
'instance_deletable': self.deletable_instance(obj),
'instance_url': self.get_action_url('view', obj, mobile=True),
'form': form,
}
if hasattr(form, 'make_deform_form'):
context['dform'] = form.make_deform_form()
return self.render_to_response('edit', context, mobile=True)
def save_edit_form(self, form):
if not self.mobile:
uploads = self.normalize_uploads(form)
uploads = self.normalize_uploads(form)
obj = self.objectify(form)
if not self.mobile:
self.process_uploads(obj, form, uploads)
self.process_uploads(obj, form, uploads)
self.after_edit(obj)
self.Session.flush()
return obj
def save_mobile_edit_form(self, form):
return self.save_edit_form(form)
def redirect_after_edit(self, instance, mobile=False):
return self.redirect(self.get_action_url('view', instance, mobile=mobile))
def redirect_after_edit(self, instance, **kwargs):
return self.redirect(self.get_action_url('view', instance))
def delete(self):
"""
@ -2350,13 +1899,11 @@ class MasterView(View):
"""
return getattr(cls, 'permission_prefix', cls.get_route_prefix())
def get_index_url(self, mobile=False, **kwargs):
def get_index_url(self, **kwargs):
"""
Returns the master view's index URL.
"""
route = self.get_route_prefix()
if mobile:
route = 'mobile.{}'.format(route)
return self.request.route_url(route, **kwargs)
@classmethod
@ -2366,15 +1913,13 @@ class MasterView(View):
"""
return getattr(cls, 'index_title', cls.get_model_title_plural())
def get_action_url(self, action, instance, mobile=False, **kwargs):
def get_action_url(self, action, instance, **kwargs):
"""
Generate a URL for the given action on the given instance
"""
kw = self.get_action_route_kwargs(instance)
kw.update(kwargs)
route_prefix = self.get_route_prefix()
if mobile:
route_prefix = 'mobile.{}'.format(route_prefix)
return self.request.route_url('{}.{}'.format(route_prefix, action), **kw)
def get_help_url(self):
@ -2394,7 +1939,7 @@ class MasterView(View):
return global_help_url(self.rattail_config)
def render_to_response(self, template, data, mobile=False):
def render_to_response(self, template, data, **kwargs):
"""
Return a response with the given template rendered with the given data.
Note that ``template`` must only be a "key" (e.g. 'index' or 'view').
@ -2405,13 +1950,12 @@ class MasterView(View):
context = {
'master': self,
'use_buefy': self.get_use_buefy(),
'mobile': mobile,
'model_title': self.get_model_title(),
'model_title_plural': self.get_model_title_plural(),
'route_prefix': self.get_route_prefix(),
'permission_prefix': self.get_permission_prefix(),
'index_title': self.get_index_title(),
'index_url': self.get_index_url(mobile=mobile),
'index_url': self.get_index_url(),
'action_url': self.get_action_url,
'grid_index': self.grid_index,
'help_url': self.get_help_url(),
@ -2430,34 +1974,20 @@ class MasterView(View):
context['row_model_title_plural'] = self.get_row_model_title_plural()
context['row_action_url'] = self.get_row_action_url
if mobile and self.viewing and self.mobile_rows_quickable:
# quick row does *not* mimic keyboard wedge by default, but can
context['quick_row_keyboard_wedge'] = False
# quick row does *not* use autocomplete by default, but can
context['quick_row_autocomplete'] = False
context['quick_row_autocomplete_url'] = '#'
context.update(data)
context.update(self.template_kwargs(**context))
if hasattr(self, 'template_kwargs_{}'.format(template)):
context.update(getattr(self, 'template_kwargs_{}'.format(template))(**context))
if mobile and hasattr(self, 'mobile_template_kwargs_{}'.format(template)):
context.update(getattr(self, 'mobile_template_kwargs_{}'.format(template))(**context))
# First try the template path most specific to the view.
if mobile:
mako_path = '/mobile{}/{}.mako'.format(self.get_template_prefix(), template)
else:
mako_path = '{}/{}.mako'.format(self.get_template_prefix(), template)
mako_path = '{}/{}.mako'.format(self.get_template_prefix(), template)
try:
return render_to_response(mako_path, context, request=self.request)
except IOError:
# Failing that, try one or more fallback templates.
for fallback in self.get_fallback_templates(template, mobile=mobile):
for fallback in self.get_fallback_templates(template):
try:
return render_to_response(fallback, context, request=self.request)
except IOError:
@ -2504,9 +2034,7 @@ class MasterView(View):
return render('{}/{}.mako'.format(self.get_template_prefix(), template),
context, request=self.request)
def get_fallback_templates(self, template, mobile=False):
if mobile:
return ['/mobile/master/{}.mako'.format(template)]
def get_fallback_templates(self, template, **kwargs):
return ['/master/{}.mako'.format(template)]
def get_default_engine_dbkey(self):
@ -3736,14 +3264,6 @@ class MasterView(View):
"""
return getattr(cls, 'form_factory', forms.Form)
@classmethod
def get_mobile_form_factory(cls):
"""
Returns the factory or class which is to be used when creating new
mobile forms.
"""
return getattr(cls, 'mobile_form_factory', forms.Form)
@classmethod
def get_row_form_factory(cls):
"""
@ -3752,14 +3272,6 @@ class MasterView(View):
"""
return getattr(cls, 'row_form_factory', forms.Form)
@classmethod
def get_mobile_row_form_factory(cls):
"""
Returns the factory or class which is to be used when creating new
mobile row forms.
"""
return getattr(cls, 'mobile_row_form_factory', forms.Form)
def download_path(self, obj, filename):
"""
Should return absolute path on disk, for the given object and filename.
@ -4055,49 +3567,8 @@ class MasterView(View):
def after_create_row(self, row_object):
pass
def redirect_after_create_row(self, row, mobile=False):
return self.redirect(self.get_row_action_url('view', row, mobile=mobile))
def mobile_create_row(self):
"""
Mobile view for creating a new row object
"""
self.mobile = True
self.creating = True
parent = self.get_instance()
instance_url = self.get_action_url('view', parent, mobile=True)
form = self.make_mobile_row_form(self.model_row_class, cancel_url=instance_url)
if self.request.method == 'POST':
if self.validate_mobile_row_form(form):
self.before_create_row(form)
# let save() return alternate object if necessary
obj = self.save_create_row_form(form)
self.after_create_row(obj)
return self.redirect_after_create_row(obj, mobile=True)
return self.render_to_response('create_row', {
'instance_title': self.get_instance_title(parent),
'instance_url': instance_url,
'parent_object': parent,
'form': form,
}, mobile=True)
def mobile_quick_row(self):
"""
Mobile view for "quick" location or creation of a row object
"""
parent = self.get_instance()
parent_url = self.get_action_url('view', parent, mobile=True)
form = self.make_quick_row_form(self.model_row_class, mobile=True, cancel_url=parent_url)
if self.request.method == 'POST':
if self.validate_quick_row_form(form):
row = self.save_quick_row_form(form)
if not row:
self.request.session.flash("Could not locate/create row for entry: "
"{}".format(form.validated['quick_entry']),
'error')
return self.redirect(parent_url)
return self.redirect_after_quick_row(row, mobile=True)
return self.redirect(parent_url)
def redirect_after_create_row(self, row, **kwargs):
return self.redirect(self.get_row_action_url('view', row))
def save_quick_row_form(self, form):
raise NotImplementedError("You must define `{}:{}.save_quick_row_form()` "
@ -4105,8 +3576,8 @@ class MasterView(View):
self.__class__.__module__,
self.__class__.__name__))
def redirect_after_quick_row(self, row, mobile=False):
return self.redirect(self.get_row_action_url('edit', row, mobile=mobile))
def redirect_after_quick_row(self, row, **kwargs):
return self.redirect(self.get_row_action_url('edit', row))
def view_row(self):
"""
@ -4182,34 +3653,6 @@ class MasterView(View):
'dform': form.make_deform_form(),
})
def mobile_edit_row(self):
"""
Mobile view for editing a row object
"""
self.mobile = True
self.editing = True
row = self.get_row_instance()
instance_url = self.get_row_action_url('view', row, mobile=True)
form = self.make_mobile_row_form(row)
if self.request.method == 'POST':
if self.validate_mobile_row_form(form):
self.save_edit_row_form(form)
return self.redirect_after_edit_row(row, mobile=True)
parent = self.get_parent(row)
return self.render_to_response('edit_row', {
'row': row,
'instance': row,
'parent_instance': parent,
'instance_title': self.get_row_instance_title(row),
'instance_url': instance_url,
'instance_deletable': self.row_deletable(row),
'parent_title': self.get_instance_title(parent),
'parent_url': self.get_action_url('view', parent, mobile=True),
'form': form},
mobile=True)
def save_edit_row_form(self, form):
obj = self.objectify(form, self.form_deserialized)
self.after_edit_row(obj)
@ -4224,8 +3667,8 @@ class MasterView(View):
Event hook, called just after an existing row object is saved.
"""
def redirect_after_edit_row(self, row, mobile=False):
return self.redirect(self.get_row_action_url('view', row, mobile=mobile))
def redirect_after_edit_row(self, row, **kwargs):
return self.redirect(self.get_row_action_url('view', row))
def row_deletable(self, row):
"""
@ -4252,22 +3695,6 @@ class MasterView(View):
self.delete_row_object(row)
return self.redirect(self.get_action_url('view', self.get_parent(row)))
def mobile_delete_row(self):
"""
Mobile view which can "delete" a sub-row from the parent.
"""
if self.request.method == 'POST':
parent = self.get_instance()
row = self.get_row_instance()
if self.get_parent(row) is not parent:
raise RuntimeError("Can only delete rows which belong to current object")
self.delete_row_object(row)
return self.redirect(self.get_action_url('view', parent, mobile=True))
self.session.flash("Must POST to delete a row", 'error')
return self.redirect(self.request.get_referrer(mobile=True))
def get_parent(self, row):
raise NotImplementedError
@ -4357,13 +3784,11 @@ class MasterView(View):
return True
return False
def get_row_action_url(self, action, row, mobile=False):
def get_row_action_url(self, action, row, **kwargs):
"""
Generate a URL for the given action on the given row.
"""
route_name = '{}.{}_row'.format(self.get_route_prefix(), action)
if mobile:
route_name = 'mobile.{}'.format(route_name)
return self.request.route_url(route_name, **self.get_row_action_route_kwargs(row))
def get_row_action_route_kwargs(self, row):
@ -4426,7 +3851,6 @@ class MasterView(View):
model_title_plural = cls.get_model_title_plural()
if cls.has_rows:
row_model_title = cls.get_row_model_title()
legacy_mobile = cls.legacy_mobile_enabled(rattail_config)
config.add_tailbone_permission_group(permission_prefix, model_title_plural, overwrite=False)
@ -4437,10 +3861,6 @@ class MasterView(View):
config.add_route(route_prefix, '{}/'.format(url_prefix))
config.add_view(cls, attr='index', route_name=route_prefix,
permission='{}.list'.format(permission_prefix))
if legacy_mobile and cls.supports_mobile:
config.add_route('mobile.{}'.format(route_prefix), '/mobile{}/'.format(url_prefix))
config.add_view(cls, attr='mobile_index', route_name='mobile.{}'.format(route_prefix),
permission='{}.list'.format(permission_prefix))
# download results
# this is the "new" more flexible approach, but we only want to
@ -4495,17 +3915,12 @@ class MasterView(View):
permission='{}.quickie'.format(permission_prefix))
# create
if cls.creatable or (legacy_mobile and cls.mobile_creatable):
if cls.creatable:
config.add_tailbone_permission(permission_prefix, '{}.create'.format(permission_prefix),
"Create new {}".format(model_title))
if cls.creatable:
config.add_route('{}.create'.format(route_prefix), '{}/new'.format(url_prefix))
config.add_view(cls, attr='create', route_name='{}.create'.format(route_prefix),
permission='{}.create'.format(permission_prefix))
if legacy_mobile and cls.mobile_creatable:
config.add_route('mobile.{}.create'.format(route_prefix), '/mobile{}/new'.format(url_prefix))
config.add_view(cls, attr='mobile_create', route_name='mobile.{}.create'.format(route_prefix),
permission='{}.create'.format(permission_prefix))
# populate new object
if cls.populatable:
@ -4572,10 +3987,6 @@ class MasterView(View):
config.add_route('{}.view'.format(route_prefix), instance_url_prefix)
config.add_view(cls, attr='view', route_name='{}.view'.format(route_prefix),
permission='{}.view'.format(permission_prefix))
if legacy_mobile and cls.supports_mobile:
config.add_route('mobile.{}.view'.format(route_prefix), '/mobile{}'.format(instance_url_prefix))
config.add_view(cls, attr='mobile_view', route_name='mobile.{}.view'.format(route_prefix),
permission='{}.view'.format(permission_prefix))
# version history
if cls.has_versions and rattail_config and rattail_config.versioning_enabled():
@ -4625,30 +4036,20 @@ class MasterView(View):
"Download associated data for {}".format(model_title))
# edit
if cls.editable or (legacy_mobile and cls.mobile_editable):
if cls.editable:
config.add_tailbone_permission(permission_prefix, '{}.edit'.format(permission_prefix),
"Edit {}".format(model_title))
if cls.editable:
config.add_route('{}.edit'.format(route_prefix), '{}/edit'.format(instance_url_prefix))
config.add_view(cls, attr='edit', route_name='{}.edit'.format(route_prefix),
permission='{}.edit'.format(permission_prefix))
if legacy_mobile and cls.mobile_editable:
config.add_route('mobile.{}.edit'.format(route_prefix), '/mobile{}/edit'.format(instance_url_prefix))
config.add_view(cls, attr='mobile_edit', route_name='mobile.{}.edit'.format(route_prefix),
permission='{}.edit'.format(permission_prefix))
# execute
if cls.executable or (legacy_mobile and cls.mobile_executable):
if cls.executable:
config.add_tailbone_permission(permission_prefix, '{}.execute'.format(permission_prefix),
"Execute {}".format(model_title))
if cls.executable:
config.add_route('{}.execute'.format(route_prefix), '{}/execute'.format(instance_url_prefix))
config.add_view(cls, attr='execute', route_name='{}.execute'.format(route_prefix),
permission='{}.execute'.format(permission_prefix))
if legacy_mobile and cls.mobile_executable:
config.add_route('mobile.{}.execute'.format(route_prefix), '/mobile{}/execute'.format(instance_url_prefix))
config.add_view(cls, attr='mobile_execute', route_name='mobile.{}.execute'.format(route_prefix),
permission='{}.execute'.format(permission_prefix))
# delete
if cls.deletable:
@ -4683,21 +4084,12 @@ class MasterView(View):
# create row
if cls.has_rows:
if cls.rows_creatable or (legacy_mobile and cls.mobile_rows_creatable):
if cls.rows_creatable:
config.add_tailbone_permission(permission_prefix, '{}.create_row'.format(permission_prefix),
"Create new {} rows".format(model_title))
if cls.rows_creatable:
config.add_route('{}.create_row'.format(route_prefix), '{}/new-row'.format(instance_url_prefix))
config.add_view(cls, attr='create_row', route_name='{}.create_row'.format(route_prefix),
permission='{}.create_row'.format(permission_prefix))
if legacy_mobile and cls.mobile_rows_creatable:
config.add_route('mobile.{}.create_row'.format(route_prefix), '/mobile{}/new-row'.format(instance_url_prefix))
config.add_view(cls, attr='mobile_create_row', route_name='mobile.{}.create_row'.format(route_prefix),
permission='{}.create_row'.format(permission_prefix))
if cls.mobile_rows_quickable:
config.add_route('mobile.{}.quick_row'.format(route_prefix), '/mobile{}/quick-row'.format(instance_url_prefix))
config.add_view(cls, attr='mobile_quick_row', route_name='mobile.{}.quick_row'.format(route_prefix),
permission='{}.create_row'.format(permission_prefix))
# view row
if cls.has_rows:
@ -4705,35 +4097,21 @@ class MasterView(View):
config.add_route('{}.view_row'.format(route_prefix), '{}/{{uuid}}/rows/{{row_uuid}}'.format(url_prefix))
config.add_view(cls, attr='view_row', route_name='{}.view_row'.format(route_prefix),
permission='{}.view'.format(permission_prefix))
if legacy_mobile and cls.mobile_rows_viewable:
config.add_route('mobile.{}.view_row'.format(route_prefix), '/mobile{}/{{uuid}}/rows/{{row_uuid}}'.format(url_prefix))
config.add_view(cls, attr='mobile_view_row', route_name='mobile.{}.view_row'.format(route_prefix),
permission='{}.view'.format(permission_prefix))
# edit row
if cls.has_rows:
if cls.rows_editable or (legacy_mobile and cls.mobile_rows_editable):
if cls.rows_editable:
config.add_tailbone_permission(permission_prefix, '{}.edit_row'.format(permission_prefix),
"Edit individual {} rows".format(model_title))
if cls.rows_editable:
config.add_route('{}.edit_row'.format(route_prefix), '{}/{{uuid}}/rows/{{row_uuid}}/edit'.format(url_prefix))
config.add_view(cls, attr='edit_row', route_name='{}.edit_row'.format(route_prefix),
permission='{}.edit_row'.format(permission_prefix))
if legacy_mobile and cls.mobile_rows_editable:
config.add_route('mobile.{}.edit_row'.format(route_prefix), '/mobile{}/{{uuid}}/rows/{{row_uuid}}/edit'.format(url_prefix))
config.add_view(cls, attr='mobile_edit_row', route_name='mobile.{}.edit_row'.format(route_prefix),
permission='{}.edit_row'.format(permission_prefix))
# delete row
if cls.has_rows:
if cls.rows_deletable or (legacy_mobile and cls.mobile_rows_deletable):
if cls.rows_deletable:
config.add_tailbone_permission(permission_prefix, '{}.delete_row'.format(permission_prefix),
"Delete individual {} rows".format(model_title))
if cls.rows_deletable:
config.add_route('{}.delete_row'.format(route_prefix), '{}/{{uuid}}/rows/{{row_uuid}}/delete'.format(url_prefix))
config.add_view(cls, attr='delete_row', route_name='{}.delete_row'.format(route_prefix),
permission='{}.delete_row'.format(permission_prefix))
if legacy_mobile and cls.mobile_rows_deletable:
config.add_route('mobile.{}.delete_row'.format(route_prefix), '/mobile{}/{{uuid}}/rows/{{row_uuid}}/delete'.format(url_prefix))
config.add_view(cls, attr='mobile_delete_row', route_name='mobile.{}.delete_row'.format(route_prefix),
permission='{}.delete_row'.format(permission_prefix))