From b0b551af82c4a7ab6b79df134268111059ff2dcc Mon Sep 17 00:00:00 2001 From: Lance Edgar Date: Tue, 19 Feb 2019 17:10:02 -0600 Subject: [PATCH] Add basic support for "mobile edit" of records specifically need to allow this for Customer records, for one app --- tailbone/templates/mobile/master/view.mako | 4 ++ tailbone/views/customers.py | 4 +- tailbone/views/employees.py | 4 +- tailbone/views/master.py | 84 +++++++++++++++++----- tailbone/views/messages.py | 4 +- tailbone/views/roles.py | 4 +- tailbone/views/users.py | 2 +- tailbone/views/vendors/core.py | 4 +- 8 files changed, 88 insertions(+), 22 deletions(-) diff --git a/tailbone/templates/mobile/master/view.mako b/tailbone/templates/mobile/master/view.mako index fc503c76..9f00d8af 100644 --- a/tailbone/templates/mobile/master/view.mako +++ b/tailbone/templates/mobile/master/view.mako @@ -42,3 +42,7 @@ ${form.render()|n}
${grid.render_complete()|n} % endif + +% if master.mobile_editable and instance_editable and request.has_perm('{}.edit'.format(permission_prefix)): + ${h.link_to("Edit This", url('mobile.{}.edit'.format(route_prefix), uuid=instance.uuid), class_='ui-btn ui-corner-all')} +% endif diff --git a/tailbone/views/customers.py b/tailbone/views/customers.py index 4e243f6c..3651b298 100644 --- a/tailbone/views/customers.py +++ b/tailbone/views/customers.py @@ -223,7 +223,9 @@ class CustomersView(MasterView): f.set_renderer('groups', self.render_groups) f.set_readonly('groups') - def objectify(self, form, data): + def objectify(self, form, data=None): + if data is None: + data = form.validated customer = super(CustomersView, self).objectify(form, data) customer = self.objectify_contact(customer, data) return customer diff --git a/tailbone/views/employees.py b/tailbone/views/employees.py index 9ea2cb6a..b8ef0962 100644 --- a/tailbone/views/employees.py +++ b/tailbone/views/employees.py @@ -195,7 +195,9 @@ class EmployeesView(MasterView): if not self.viewing: f.remove_fields('first_name', 'last_name') - def objectify(self, form, data): + def objectify(self, form, data=None): + if data is None: + data = form.validated employee = super(EmployeesView, self).objectify(form, data) self.update_stores(employee, data) self.update_departments(employee, data) diff --git a/tailbone/views/master.py b/tailbone/views/master.py index b8ae34e4..971e344b 100644 --- a/tailbone/views/master.py +++ b/tailbone/views/master.py @@ -100,6 +100,7 @@ class MasterView(View): supports_mobile = False mobile_creatable = False + mobile_editable = False mobile_pageable = True mobile_filterable = False mobile_executable = False @@ -1111,7 +1112,7 @@ class MasterView(View): context = { 'instance': instance, 'instance_title': self.get_instance_title(instance), - # 'instance_editable': self.editable_instance(instance), + 'instance_editable': self.editable_instance(instance), # 'instance_deletable': self.deletable_instance(instance), 'form': form, } @@ -1199,12 +1200,12 @@ class MasterView(View): self.configure_common_form(form) def validate_mobile_form(self, form): - controls = self.request.POST.items() - try: - self.form_deserialized = form.validate(controls) - except deform.ValidationFailure: + if form.validate(newstyle=True): + # TODO: deprecate / remove self.form_deserialized + self.form_deserialized = form.validated + return True + else: return False - return True def make_mobile_row_form(self, instance=None, factory=None, fields=None, schema=None, **kwargs): """ @@ -1535,15 +1536,61 @@ 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): - uploads = self.normalize_uploads(form) - obj = self.objectify(form, self.form_deserialized) - self.process_uploads(obj, form, uploads) + if not self.mobile: + uploads = self.normalize_uploads(form) + obj = self.objectify(form) + if not self.mobile: + self.process_uploads(obj, form, uploads) self.after_edit(obj) self.Session.flush() + return obj - def redirect_after_edit(self, instance): - return self.redirect(self.get_action_url('view', instance)) + 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 delete(self): """ @@ -3286,12 +3333,17 @@ class MasterView(View): "Download associated data for {}".format(model_title)) # edit + if cls.editable or cls.mobile_editable: + config.add_tailbone_permission(permission_prefix, '{}.edit'.format(permission_prefix), + "Edit {}".format(model_title)) if cls.editable: - config.add_route('{0}.edit'.format(route_prefix), '{0}/{{{1}}}/edit'.format(url_prefix, model_key)) - config.add_view(cls, attr='edit', route_name='{0}.edit'.format(route_prefix), - permission='{0}.edit'.format(permission_prefix)) - config.add_tailbone_permission(permission_prefix, '{0}.edit'.format(permission_prefix), - "Edit {0}".format(model_title)) + config.add_route('{}.edit'.format(route_prefix), '{}/{{{}}}/edit'.format(url_prefix, model_key)) + config.add_view(cls, attr='edit', route_name='{}.edit'.format(route_prefix), + permission='{}.edit'.format(permission_prefix)) + if cls.mobile_editable: + config.add_route('mobile.{}.edit'.format(route_prefix), '/mobile{}/{{{}}}/edit'.format(url_prefix, model_key)) + config.add_view(cls, attr='mobile_edit', route_name='mobile.{}.edit'.format(route_prefix), + permission='{}.edit'.format(permission_prefix)) # execute if cls.executable or cls.mobile_executable: diff --git a/tailbone/views/messages.py b/tailbone/views/messages.py index 7d72aecb..1ebc5fd7 100644 --- a/tailbone/views/messages.py +++ b/tailbone/views/messages.py @@ -238,7 +238,9 @@ class MessagesView(MasterView): elif self.viewing: f.remove('body') - def objectify(self, form, data): + def objectify(self, form, data=None): + if data is None: + data = form.validated message = super(MessagesView, self).objectify(form, data) if self.creating: diff --git a/tailbone/views/roles.py b/tailbone/views/roles.py index 19c91276..b5e665e7 100644 --- a/tailbone/views/roles.py +++ b/tailbone/views/roles.py @@ -106,7 +106,9 @@ class RolesView(PrincipalMasterView): return "" return six.text_type(role.session_timeout) - def objectify(self, form, data): + def objectify(self, form, data=None): + if data is None: + data = form.validated role = super(RolesView, self).objectify(form, data) role.permissions = data['permissions'] return role diff --git a/tailbone/views/users.py b/tailbone/views/users.py index be979dd7..79a118f0 100644 --- a/tailbone/views/users.py +++ b/tailbone/views/users.py @@ -229,7 +229,7 @@ class UsersView(PrincipalMasterView): .filter(~model.Role.uuid.in_(excluded))\ .order_by(model.Role.name) - def objectify(self, form, data): + def objectify(self, form, data=None): # create/update user as per normal if data is None: diff --git a/tailbone/views/vendors/core.py b/tailbone/views/vendors/core.py index fe38dcc7..f555ba5e 100644 --- a/tailbone/views/vendors/core.py +++ b/tailbone/views/vendors/core.py @@ -114,7 +114,9 @@ class VendorsView(MasterView): f.set_readonly('contact') f.set_renderer('contact', self.render_contact) - def objectify(self, form, data): + def objectify(self, form, data=None): + if data is None: + data = form.validated vendor = super(VendorsView, self).objectify(form, data) vendor = self.objectify_contact(vendor, data)