diff --git a/tailbone/forms/__init__.py b/tailbone/forms/__init__.py
deleted file mode 100644
index 9982a978..00000000
--- a/tailbone/forms/__init__.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# -*- coding: utf-8 -*-
-################################################################################
-#
-# Rattail -- Retail Software Framework
-# Copyright © 2010-2017 Lance Edgar
-#
-# This file is part of Rattail.
-#
-# Rattail is free software: you can redistribute it and/or modify it under the
-# terms of the GNU General Public License as published by the Free Software
-# Foundation, either version 3 of the License, or (at your option) any later
-# version.
-#
-# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
-# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# Rattail. If not, see .
-#
-################################################################################
-"""
-Forms
-"""
-
-from __future__ import unicode_literals, absolute_import
-
-from formencode import Schema
-
-from .core import Form, Field, FieldSet, GenericFieldSet
-from .simpleform import SimpleForm, FormRenderer
-from .alchemy import AlchemyForm
-from .fields import AssociationProxyField
-from .renderers import *
-
-from . import fields
-from . import renderers
-from . import validators
diff --git a/tailbone/forms/alchemy.py b/tailbone/forms/alchemy.py
deleted file mode 100644
index 161d549f..00000000
--- a/tailbone/forms/alchemy.py
+++ /dev/null
@@ -1,117 +0,0 @@
-# -*- coding: utf-8; -*-
-################################################################################
-#
-# Rattail -- Retail Software Framework
-# Copyright © 2010-2017 Lance Edgar
-#
-# This file is part of Rattail.
-#
-# Rattail is free software: you can redistribute it and/or modify it under the
-# terms of the GNU General Public License as published by the Free Software
-# Foundation, either version 3 of the License, or (at your option) any later
-# version.
-#
-# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
-# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# Rattail. If not, see .
-#
-################################################################################
-"""
-FormAlchemy Forms
-"""
-
-from __future__ import unicode_literals, absolute_import
-
-from rattail.core import Object
-
-import formalchemy as fa
-from pyramid.renderers import render
-from webhelpers2.html import HTML, tags
-
-from tailbone.db import Session
-
-
-class TemplateEngine(fa.templates.TemplateEngine):
- """
- Mako template engine for FormAlchemy.
- """
-
- def render(self, template, prefix='/forms/', suffix='.mako', **kwargs):
- template = ''.join((prefix, template, suffix))
- return render(template, kwargs)
-
-
-class AlchemyForm(Object):
- """
- Form to contain a :class:`formalchemy.FieldSet` instance.
- """
- id = None
- create_label = "Create"
- update_label = "Save"
-
- allow_successive_creates = False
-
- def __init__(self, request, fieldset, session=None, csrf_field='_csrf', **kwargs):
- super(AlchemyForm, self).__init__(**kwargs)
- self.request = request
- self.fieldset = fieldset
- self.session = session
- self.csrf_field = csrf_field
-
- def _get_readonly(self):
- return self.fieldset.readonly
- def _set_readonly(self, val):
- self.fieldset.readonly = val
- readonly = property(_get_readonly, _set_readonly)
-
- @property
- def successive_create_label(self):
- return "%s and continue" % self.create_label
-
- def csrf(self, name=None):
- """
- NOTE: this method was copied from `pyramid_simpleform.FormRenderer`
-
- Returns the CSRF hidden input. Creates new CSRF token
- if none has been assigned yet.
-
- The name of the hidden field is **_csrf** by default.
- """
- name = name or self.csrf_field
-
- token = self.request.session.get_csrf_token()
- if token is None:
- token = self.request.session.new_csrf_token()
-
- return tags.hidden(name, value=token)
-
- def csrf_token(self, name=None):
- """
- NOTE: this method was copied from `pyramid_simpleform.FormRenderer`
-
- Convenience function. Returns CSRF hidden tag inside hidden DIV.
- """
- return HTML.tag("div", self.csrf(name), style="display:none;")
-
- def render(self, **kwargs):
- kwargs['form'] = self
- if self.readonly:
- template = '/forms/form_readonly.mako'
- else:
- template = '/forms/form.mako'
- return render(template, kwargs)
-
- def render_fields(self):
- return self.fieldset.render()
-
- def save(self):
- self.fieldset.sync()
- self.session.flush()
-
- def validate(self):
- self.fieldset.rebind(data=self.request.params)
- return self.fieldset.validate()
diff --git a/tailbone/forms/core.py b/tailbone/forms/core.py
deleted file mode 100644
index c2702fb7..00000000
--- a/tailbone/forms/core.py
+++ /dev/null
@@ -1,119 +0,0 @@
-# -*- coding: utf-8 -*-
-################################################################################
-#
-# Rattail -- Retail Software Framework
-# Copyright © 2010-2017 Lance Edgar
-#
-# This file is part of Rattail.
-#
-# Rattail is free software: you can redistribute it and/or modify it under the
-# terms of the GNU General Public License as published by the Free Software
-# Foundation, either version 3 of the License, or (at your option) any later
-# version.
-#
-# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
-# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# Rattail. If not, see .
-#
-################################################################################
-"""
-Forms Core
-"""
-
-from __future__ import unicode_literals, absolute_import
-
-from rattail.util import OrderedDict, prettify
-
-import formalchemy
-from formalchemy.helpers import content_tag
-from pyramid.renderers import render
-
-
-class Form(object):
- """
- Base class for all forms.
- """
- create_label = "Create"
- update_label = "Save"
-
- def __init__(self, request, readonly=False, action_url=None):
- self.request = request
- self.readonly = readonly
- self.action_url = action_url
-
- def render(self, **kwargs):
- kwargs.setdefault('form', self)
- if self.readonly:
- template = '/forms/form_readonly.mako'
- else:
- template = '/forms/form.mako'
- return render(template, kwargs)
-
- def render_fields(self, **kwargs):
- kwargs.setdefault('fieldset', self.fieldset)
- if self.readonly:
- template = '/forms/fieldset_readonly.mako'
- else:
- template = '/forms/fieldset.mako'
- return render(template, kwargs)
-
-
-class Field(object):
- """
- Manually create instances of this class to populate a simple form.
- """
-
- def __init__(self, name, value=None, label=None, requires_label=True):
- self.name = name
- self.value = value
- self._label = label or prettify(self.name)
- self.requires_label = requires_label
-
- def is_required(self):
- return True
-
- def label(self):
- return self._label
-
- def label_tag(self, **html_options):
- """
- Logic stolen from FormAlchemy so all fields can render their own label.
- Original docstring follows.
-
- return the tag for the field.
- """
- html_options.update(for_=self.name)
- if 'class_' in html_options:
- html_options['class_'] += self.is_required() and ' field_req' or ' field_opt'
- else:
- html_options['class_'] = self.is_required() and 'field_req' or 'field_opt'
- return content_tag('label', self.label(), **html_options)
-
- def render_readonly(self):
- if self.value is None:
- return ''
- return unicode(self.value)
-
-
-class FieldSet(object):
- """
- Generic fieldset for use with manually-created simple forms.
- """
-
- def __init__(self):
- self.fields = OrderedDict()
- self.render_fields = self.fields
-
-
-class GenericFieldSet(formalchemy.FieldSet):
- """
- FieldSet class based on FormAlchemy, but without the SQLAlchemy magic.
- """
- __sa__ = False
- _bound_pk = None
- data = None
- prettify = staticmethod(prettify)
diff --git a/tailbone/forms/fields.py b/tailbone/forms/fields.py
deleted file mode 100644
index 8f401bbd..00000000
--- a/tailbone/forms/fields.py
+++ /dev/null
@@ -1,106 +0,0 @@
-# -*- coding: utf-8 -*-
-################################################################################
-#
-# Rattail -- Retail Software Framework
-# Copyright © 2010-2017 Lance Edgar
-#
-# This file is part of Rattail.
-#
-# Rattail is free software: you can redistribute it and/or modify it under the
-# terms of the GNU General Public License as published by the Free Software
-# Foundation, either version 3 of the License, or (at your option) any later
-# version.
-#
-# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
-# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# Rattail. If not, see .
-#
-################################################################################
-"""
-FormAlchemy Fields
-"""
-
-from __future__ import unicode_literals, absolute_import
-
-import formalchemy as fa
-
-
-def AssociationProxyField(name, **kwargs):
- """
- Returns a FormAlchemy ``Field`` class which is aware of association
- proxies.
- """
-
- class ProxyField(fa.Field):
-
- def sync(self):
- if not self.is_readonly():
- setattr(self.parent.model, self.name,
- self.renderer.deserialize())
-
- def value(obj):
- return getattr(obj, name, None)
-
- kwargs.setdefault('value', value)
- return ProxyField(name, **kwargs)
-
-
-class DefaultEmailField(fa.Field):
- """
- Generic field for view/edit of default email address for a contact
- """
-
- def __init__(self, name=None, **kwargs):
- kwargs.setdefault('value', self.value)
- if 'renderer' not in kwargs:
- from tailbone.forms.renderers import EmailFieldRenderer
- kwargs['renderer'] = EmailFieldRenderer
- super(DefaultEmailField, self).__init__(name, **kwargs)
-
- def value(self, contact):
- if contact.emails:
- return contact.emails[0].address
-
- def sync(self):
- if not self.is_readonly():
- address = self._deserialize()
- contact = self.parent.model
- if contact.emails:
- if address:
- email = contact.emails[0]
- email.address = address
- else:
- contact.emails.pop(0)
- elif address:
- email = contact.add_email_address(address)
-
-
-class DefaultPhoneField(fa.Field):
- """
- Generic field for view/edit of default phone number for a contact
- """
-
- def __init__(self, name=None, **kwargs):
- kwargs.setdefault('value', self.value)
- super(DefaultPhoneField, self).__init__(name, **kwargs)
-
- def value(self, contact):
- if contact.phones:
- return contact.phones[0].number
-
- def sync(self):
- if not self.is_readonly():
- number = self._deserialize()
- contact = self.parent.model
- if contact.phones:
- if number:
- phone = contact.phones[0]
- phone.number = number
- else:
- contact.phones.pop(0)
- elif number:
- phone = contact.add_phone_number(number)
diff --git a/tailbone/forms/renderers/__init__.py b/tailbone/forms/renderers/__init__.py
deleted file mode 100644
index b0f30233..00000000
--- a/tailbone/forms/renderers/__init__.py
+++ /dev/null
@@ -1,51 +0,0 @@
-# -*- coding: utf-8; -*-
-################################################################################
-#
-# Rattail -- Retail Software Framework
-# Copyright © 2010-2017 Lance Edgar
-#
-# This file is part of Rattail.
-#
-# Rattail is free software: you can redistribute it and/or modify it under the
-# terms of the GNU General Public License as published by the Free Software
-# Foundation, either version 3 of the License, or (at your option) any later
-# version.
-#
-# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
-# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# Rattail. If not, see .
-#
-################################################################################
-"""
-FormAlchemy Field Renderers
-"""
-
-from __future__ import unicode_literals, absolute_import
-
-from .core import CustomFieldRenderer, DateFieldRenderer
-
-from .common import (StrippedTextFieldRenderer, CodeTextAreaFieldRenderer, AutocompleteFieldRenderer,
- DecimalFieldRenderer, CurrencyFieldRenderer, QuantityFieldRenderer,
- DateTimeFieldRenderer, DateTimePrettyFieldRenderer, LocalDateTimeFieldRenderer, TimeFieldRenderer,
- EmailFieldRenderer, EnumFieldRenderer, YesNoFieldRenderer)
-
-from .files import FileFieldRenderer
-
-from .people import PersonFieldRenderer, PeopleFieldRenderer, CustomerFieldRenderer
-from .users import UserFieldRenderer, PermissionsFieldRenderer
-from .employees import EmployeeFieldRenderer
-
-from .stores import StoreFieldRenderer
-from .vendors import VendorFieldRenderer, PurchaseFieldRenderer
-from .products import (GPCFieldRenderer, ScancodeFieldRenderer,
- DepartmentFieldRenderer, SubdepartmentFieldRenderer, CategoryFieldRenderer,
- BrandFieldRenderer, ProductFieldRenderer,
- CostFieldRenderer, PriceFieldRenderer, PriceWithExpirationFieldRenderer)
-
-from .custorders import CustomerOrderFieldRenderer
-
-from .batch import BatchIDFieldRenderer, HandheldBatchFieldRenderer, HandheldBatchesFieldRenderer
diff --git a/tailbone/forms/renderers/batch.py b/tailbone/forms/renderers/batch.py
deleted file mode 100644
index d45a17ac..00000000
--- a/tailbone/forms/renderers/batch.py
+++ /dev/null
@@ -1,103 +0,0 @@
-# -*- coding: utf-8; -*-
-################################################################################
-#
-# Rattail -- Retail Software Framework
-# Copyright © 2010-2017 Lance Edgar
-#
-# This file is part of Rattail.
-#
-# Rattail is free software: you can redistribute it and/or modify it under the
-# terms of the GNU General Public License as published by the Free Software
-# Foundation, either version 3 of the License, or (at your option) any later
-# version.
-#
-# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
-# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# Rattail. If not, see .
-#
-################################################################################
-"""
-Batch Field Renderers
-"""
-
-from __future__ import unicode_literals, absolute_import
-
-import os
-import stat
-import random
-
-import formalchemy as fa
-from webhelpers2.html import tags, HTML
-
-from tailbone.forms.renderers import FileFieldRenderer as BaseFileFieldRenderer
-
-
-class BatchIDFieldRenderer(fa.FieldRenderer):
- """
- Renderer for batch ID fields.
- """
- def render_readonly(self, **kwargs):
- try:
- batch_id = self.raw_value
- except AttributeError:
- # this can happen when creating a new batch, b/c the default value
- # comes from a sequence
- pass
- else:
- if batch_id:
- return '{:08d}'.format(batch_id)
- return ''
-
-
-class FileFieldRenderer(BaseFileFieldRenderer):
- """
- Custom file field renderer for batches based on a single source data file.
- In edit mode, shows a file upload field. In readonly mode, shows the
- filename and its size.
- """
-
- def get_size(self):
- size = super(FileFieldRenderer, self).get_size()
- if size:
- return size
- batch = self.field.parent.model
- path = batch.filepath(self.request.rattail_config, filename=self.field.value)
- if os.path.isfile(path):
- return os.stat(path)[stat.ST_SIZE]
- return 0
-
- def render(self, **kwargs):
- return BaseFileFieldRenderer.render(self, **kwargs)
-
-
-class HandheldBatchFieldRenderer(fa.FieldRenderer):
- """
- Renderer for inventory batch's "handheld batch" field.
- """
-
- def render_readonly(self, **kwargs):
- batch = self.raw_value
- if batch:
- return tags.link_to(
- batch.id_str,
- self.request.route_url('batch.handheld.view', uuid=batch.uuid))
- return ''
-
-
-class HandheldBatchesFieldRenderer(fa.FieldRenderer):
- """
- Renders a list of associated handheld batches, for a given (presumably
- inventory or labels) batch.
- """
-
- def render_readonly(self, **kwargs):
- items = ''
- for handheld in self.raw_value:
- text = tags.link_to(handheld.handheld.id_str,
- self.request.route_url('batch.handheld.view', uuid=handheld.handheld_uuid))
- items += HTML.tag('li', c=text)
- return HTML.tag('ul', c=items)
diff --git a/tailbone/forms/renderers/bouncer.py b/tailbone/forms/renderers/bouncer.py
deleted file mode 100644
index 019abab7..00000000
--- a/tailbone/forms/renderers/bouncer.py
+++ /dev/null
@@ -1,63 +0,0 @@
-# -*- coding: utf-8 -*-
-################################################################################
-#
-# Rattail -- Retail Software Framework
-# Copyright © 2010-2017 Lance Edgar
-#
-# This file is part of Rattail.
-#
-# Rattail is free software: you can redistribute it and/or modify it under the
-# terms of the GNU General Public License as published by the Free Software
-# Foundation, either version 3 of the License, or (at your option) any later
-# version.
-#
-# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
-# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# Rattail. If not, see .
-#
-################################################################################
-"""
-Batch Field Renderers
-"""
-
-from __future__ import unicode_literals
-
-import os
-import stat
-import random
-
-from formalchemy.ext import fsblob
-
-
-class BounceMessageFieldRenderer(fsblob.FileFieldRenderer):
- """
- Custom file field renderer for email bounce messages. In readonly mode,
- shows the filename and size.
- """
-
- @classmethod
- def new(cls, request, handler):
- name = 'Configured%s_%s' % (cls.__name__, unicode(random.random())[2:])
- return type(str(name), (cls,), dict(request=request, handler=handler))
-
- @property
- def storage_path(self):
- return self.handler.root_msgdir
-
- def get_size(self):
- size = super(BounceMessageFieldRenderer, self).get_size()
- if size:
- return size
- bounce = self.field.parent.model
- path = os.path.join(self.handler.msgpath(bounce))
- if os.path.isfile(path):
- return os.stat(path)[stat.ST_SIZE]
- return 0
-
- def get_url(self, filename):
- bounce = self.field.parent.model
- return self.request.route_url('emailbounces.download', uuid=bounce.uuid)
diff --git a/tailbone/forms/renderers/common.py b/tailbone/forms/renderers/common.py
deleted file mode 100644
index e1468e08..00000000
--- a/tailbone/forms/renderers/common.py
+++ /dev/null
@@ -1,315 +0,0 @@
-# -*- coding: utf-8; -*-
-################################################################################
-#
-# Rattail -- Retail Software Framework
-# Copyright © 2010-2017 Lance Edgar
-#
-# This file is part of Rattail.
-#
-# Rattail is free software: you can redistribute it and/or modify it under the
-# terms of the GNU General Public License as published by the Free Software
-# Foundation, either version 3 of the License, or (at your option) any later
-# version.
-#
-# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
-# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# Rattail. If not, see .
-#
-################################################################################
-"""
-Common Field Renderers
-"""
-
-from __future__ import unicode_literals, absolute_import
-
-import datetime
-
-from rattail.time import localtime, make_utc
-from rattail.util import pretty_quantity
-
-import formalchemy as fa
-from formalchemy import fields as fa_fields, helpers as fa_helpers
-from pyramid.renderers import render
-from webhelpers2.html import HTML, tags
-
-from tailbone.util import pretty_datetime, raw_datetime
-
-
-class StrippedTextFieldRenderer(fa.TextFieldRenderer):
- """
- Standard text field renderer, which strips whitespace from either end of
- the input value on deserialization.
- """
-
- def deserialize(self):
- value = super(StrippedTextFieldRenderer, self).deserialize()
- if value is not None:
- return value.strip()
-
-
-class CodeTextAreaFieldRenderer(fa.TextAreaFieldRenderer):
-
- def render_readonly(self, **kwargs):
- value = self.raw_value
- if not value:
- return ''
- return HTML.tag('pre', c=value)
-
- def render(self, **kwargs):
- kwargs.setdefault('size', (80, 8))
- return super(CodeTextAreaFieldRenderer, self).render(**kwargs)
-
-
-class AutocompleteFieldRenderer(fa.FieldRenderer):
- """
- Custom renderer for an autocomplete field.
- """
-
- service_route = None
- width = '300px'
-
- @property
- def focus_name(self):
- return self.name + '-textbox'
-
- @property
- def needs_focus(self):
- return not bool(self.value or self.field_value)
-
- @property
- def field_display(self):
- return self.raw_value
-
- @property
- def field_value(self):
- return self.value
-
- @property
- def service_url(self):
- return self.request.route_url(self.service_route)
-
- def render(self, options=None, **kwargs):
- if kwargs.pop('autocomplete', True):
- return self.render_autocomplete(**kwargs)
- # 'selected' is a kwarg for autocomplete template *and* select tag
- kwargs.pop('selected', None)
- return self.render_dropdown(options, **kwargs)
-
- def render_autocomplete(self, **kwargs):
- kwargs.setdefault('field_name', self.name)
- kwargs.setdefault('field_value', self.field_value)
- kwargs.setdefault('field_display', self.field_display)
- kwargs.setdefault('service_url', self.service_url)
- kwargs.setdefault('width', self.width)
- return render('/forms/field_autocomplete.mako', kwargs)
-
- def render_dropdown(self, options, **kwargs):
- # NOTE: this logic copied from formalchemy.fields.SelectFieldRenderer.render()
- kwargs.setdefault('auto-enhance', 'true')
- if callable(options):
- L = fa_fields._normalized_options(options(self.field.parent))
- if not self.field.is_required() and not self.field.is_collection:
- L.insert(0, self.field._null_option)
- else:
- L = list(options)
- if len(L) > 0:
- if len(L[0]) == 2:
- L = [(k, self.stringify_value(v)) for k, v in L]
- else:
- L = [fa_fields._stringify(k) for k in L]
- return fa_fields.h.select(self.name, self.value, L, **kwargs)
-
- def render_readonly(self, **kwargs):
- value = self.field_display
- if value is None:
- return u''
- return unicode(value)
-
-
-class DateTimeFieldRenderer(fa.DateTimeFieldRenderer):
- """
- This renderer assumes the datetime field value is in UTC, and will convert
- it to the local time zone before rendering it in the standard "raw" format.
- """
-
- def render_readonly(self, **kwargs):
- value = self.raw_value
- if not value:
- return ''
- return raw_datetime(self.request.rattail_config, value)
-
-
-class DateTimePrettyFieldRenderer(fa.DateTimeFieldRenderer):
- """
- Custom date/time field renderer, which displays a "pretty" value in
- read-only mode, leveraging config to show the correct timezone.
- """
-
- def render_readonly(self, **kwargs):
- value = self.raw_value
- if not value:
- return ''
- return pretty_datetime(self.request.rattail_config, value)
-
-
-class LocalDateTimeFieldRenderer(fa.DateTimeFieldRenderer):
- """
- This renderer assumes the datetime field value is "naive" in local time zone.
- """
-
- def render_readonly(self, **kwargs):
- value = self.raw_value
- if not value:
- return ''
- value = localtime(self.request.rattail_config, value)
- return raw_datetime(self.request.rattail_config, value)
-
-
-class TimeFieldRenderer(fa.TimeFieldRenderer):
- """
- Custom renderer for time fields. In edit mode, renders a simple text
- input, which is expected to become a 'timepicker' widget in the UI.
- However the particular magic required for that lives in 'tailbone.js'.
- """
- format = '%I:%M %p'
-
- def render(self, **kwargs):
- kwargs.setdefault('class_', 'timepicker')
- return fa_helpers.text_field(self.name, value=self.value, **kwargs)
-
- def render_readonly(self, **kwargs):
- return self.render_value(self.raw_value)
-
- def render_value(self, value):
- value = self.convert_value(value)
- if isinstance(value, datetime.time):
- return value.strftime(self.format)
- return ''
-
- def convert_value(self, value):
- if isinstance(value, datetime.datetime):
- if not value.tzinfo:
- value = make_utc(value, tzinfo=True)
- return localtime(self.request.rattail_config, value).time()
- return value
-
- def stringify_value(self, value, as_html=False):
- if not as_html:
- return self.render_value(value)
- return super(TimeFieldRenderer, self).stringify_value(value, as_html=as_html)
-
- def _serialized_value(self):
- return self.params.getone(self.name)
-
- def deserialize(self):
- value = self._serialized_value()
- if value:
- try:
- return datetime.datetime.strptime(value, self.format).time()
- except ValueError:
- pass
-
-
-class EmailFieldRenderer(fa.TextFieldRenderer):
- """
- Renderer for email address fields
- """
-
- def render_readonly(self, **kwargs):
- address = self.raw_value
- if not address:
- return ''
- return tags.link_to(address, 'mailto:{}'.format(address))
-
-
-class EnumFieldRenderer(fa_fields.SelectFieldRenderer):
- """
- Renderer for simple enumeration fields.
- """
- enumeration = {}
- render_key = False
-
- def __init__(self, arg, render_key=False):
- if isinstance(arg, dict):
- self.enumeration = arg
- self.render_key = render_key
- else:
- self(arg)
-
- def __call__(self, field):
- super(EnumFieldRenderer, self).__init__(field)
- return self
-
- def render_readonly(self, **kwargs):
- value = self.raw_value
- if value is None:
- return ''
- rendered = self.enumeration.get(value, unicode(value))
- if self.render_key:
- rendered = '{} - {}'.format(value, rendered)
- return rendered
-
- def render(self, **kwargs):
- opts = [(self.enumeration[x], x) for x in self.enumeration]
- if not self.field.is_required():
- opts.insert(0, self.field._null_option)
- return fa_fields.SelectFieldRenderer.render(self, opts, **kwargs)
-
-
-class DecimalFieldRenderer(fa.FieldRenderer):
- """
- Sort of generic field renderer for decimal values. You must provide the
- number of places after the decimal (scale). Note that this in turn relies
- on simple string formatting; the renderer does not attempt any mathematics
- of its own.
- """
-
- def __init__(self, scale):
- self.scale = scale
-
- def __call__(self, field):
- super(DecimalFieldRenderer, self).__init__(field)
- return self
-
- def render_readonly(self, **kwargs):
- value = self.raw_value
- if value is None:
- return ''
- fmt = '{{0:0.{0}f}}'.format(self.scale)
- return fmt.format(value)
-
-
-class CurrencyFieldRenderer(fa_fields.FloatFieldRenderer):
- """
- Sort of generic field renderer for currency values.
- """
-
- def render_readonly(self, **kwargs):
- value = self.raw_value
- if value is None:
- return ''
- if value < 0:
- return "(${:0,.2f})".format(0 - value)
- return "${:0,.2f}".format(value)
-
-
-class QuantityFieldRenderer(fa_fields.FloatFieldRenderer):
- """
- Sort of generic field renderer for quantity values.
- """
-
- def render_readonly(self, **kwargs):
- return pretty_quantity(self.raw_value)
-
-
-class YesNoFieldRenderer(fa.CheckBoxFieldRenderer):
-
- def render_readonly(self, **kwargs):
- value = self.raw_value
- if value is None:
- return u''
- return u'Yes' if value else u'No'
diff --git a/tailbone/forms/renderers/core.py b/tailbone/forms/renderers/core.py
deleted file mode 100644
index 37b2454d..00000000
--- a/tailbone/forms/renderers/core.py
+++ /dev/null
@@ -1,100 +0,0 @@
-# -*- coding: utf-8 -*-
-################################################################################
-#
-# Rattail -- Retail Software Framework
-# Copyright © 2010-2017 Lance Edgar
-#
-# This file is part of Rattail.
-#
-# Rattail is free software: you can redistribute it and/or modify it under the
-# terms of the GNU General Public License as published by the Free Software
-# Foundation, either version 3 of the License, or (at your option) any later
-# version.
-#
-# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
-# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# Rattail. If not, see .
-#
-################################################################################
-"""
-Core Field Renderers
-"""
-
-from __future__ import unicode_literals, absolute_import
-
-import datetime
-
-import formalchemy as fa
-from formalchemy.fields import AbstractField
-from pyramid.renderers import render
-
-
-class CustomFieldRenderer(fa.FieldRenderer):
- """
- Base class for renderers which accept customization args, and "fake out"
- FormAlchemy by pretending to still be a renderer factory when in fact it's
- already dealing with a renderer instance.
- """
-
- def __init__(self, *args, **kwargs):
- if len(args) == 1 and isinstance(args[0], AbstractField):
- super(CustomFieldRenderer, self).__init__(args[0])
- self.init(**kwargs)
- else:
- assert len(args) == 0
- self.init(**kwargs)
-
- def __call__(self, field):
- super(CustomFieldRenderer, self).__init__(field)
- return self
-
- def init(self, **kwargs):
- pass
-
- @property
- def rattail_config(self):
- return self.request.rattail_config
-
-
-class DateFieldRenderer(CustomFieldRenderer, fa.DateFieldRenderer):
- """
- Date field renderer which uses jQuery UI datepicker widget when rendering
- in edit mode.
- """
- date_format = '%Y-%m-%d'
- change_year = False
-
- def init(self, date_format=None, change_year=False):
- if date_format:
- self.date_format = date_format
- self.change_year = change_year
-
- def render_readonly(self, **kwargs):
- value = self.raw_value
- if value is None:
- return ''
- return value.strftime(self.date_format)
-
- def render(self, **kwargs):
- kwargs['name'] = self.name
- kwargs['value'] = self.value
- kwargs['change_year'] = self.change_year
- return render('/forms/fields/date.mako', kwargs)
-
- def deserialize(self):
- value = self._serialized_value()
- if not value:
- return None
- try:
- return datetime.datetime.strptime(value, '%Y-%m-%d')
- except ValueError:
- raise fa.ValidationError("Date value must be in YYYY-MM-DD format")
- except Exception as error:
- raise fa.ValidationError(unicode(error))
-
- def _serialized_value(self):
- return self.params.getone(self.name)
diff --git a/tailbone/forms/renderers/custorders.py b/tailbone/forms/renderers/custorders.py
deleted file mode 100644
index 26385582..00000000
--- a/tailbone/forms/renderers/custorders.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# -*- coding: utf-8; -*-
-################################################################################
-#
-# Rattail -- Retail Software Framework
-# Copyright © 2010-2017 Lance Edgar
-#
-# This file is part of Rattail.
-#
-# Rattail is free software: you can redistribute it and/or modify it under the
-# terms of the GNU General Public License as published by the Free Software
-# Foundation, either version 3 of the License, or (at your option) any later
-# version.
-#
-# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
-# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# Rattail. If not, see .
-#
-################################################################################
-"""
-Customer order field renderers
-"""
-
-from __future__ import unicode_literals, absolute_import
-
-import formalchemy as fa
-from webhelpers2.html import tags
-
-
-class CustomerOrderFieldRenderer(fa.fields.SelectFieldRenderer):
- """
- Renders a link to the customer order
- """
-
- def render_readonly(self, **kwargs):
- order = self.raw_value
- if not order:
- return ''
- return tags.link_to(order, self.request.route_url('custorders.view', uuid=order.uuid))
diff --git a/tailbone/forms/renderers/employees.py b/tailbone/forms/renderers/employees.py
deleted file mode 100644
index af5ce631..00000000
--- a/tailbone/forms/renderers/employees.py
+++ /dev/null
@@ -1,49 +0,0 @@
-# -*- coding: utf-8; -*-
-################################################################################
-#
-# Rattail -- Retail Software Framework
-# Copyright © 2010-2017 Lance Edgar
-#
-# This file is part of Rattail.
-#
-# Rattail is free software: you can redistribute it and/or modify it under the
-# terms of the GNU General Public License as published by the Free Software
-# Foundation, either version 3 of the License, or (at your option) any later
-# version.
-#
-# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
-# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# Rattail. If not, see .
-#
-################################################################################
-"""
-Employee Field Renderers
-"""
-
-from __future__ import unicode_literals, absolute_import
-
-import six
-from webhelpers2.html import tags
-
-from tailbone.forms.renderers import AutocompleteFieldRenderer
-
-
-class EmployeeFieldRenderer(AutocompleteFieldRenderer):
- """
- Renderer for :class:`rattail.db.model.Employee` instance fields.
- """
- service_route = 'employees.autocomplete'
-
- def render_readonly(self, **kwargs):
- employee = self.raw_value
- if not employee:
- return ''
- render_name = kwargs.get('render_name', six.text_type)
- title = render_name(employee)
- if kwargs.get('hyperlink') and self.request.has_perm('employees.view'):
- return tags.link_to(title, self.request.route_url('employees.view', uuid=employee.uuid))
- return title
diff --git a/tailbone/forms/renderers/files.py b/tailbone/forms/renderers/files.py
deleted file mode 100644
index 08f12382..00000000
--- a/tailbone/forms/renderers/files.py
+++ /dev/null
@@ -1,102 +0,0 @@
-# -*- coding: utf-8 -*-
-################################################################################
-#
-# Rattail -- Retail Software Framework
-# Copyright © 2010-2017 Lance Edgar
-#
-# This file is part of Rattail.
-#
-# Rattail is free software: you can redistribute it and/or modify it under the
-# terms of the GNU General Public License as published by the Free Software
-# Foundation, either version 3 of the License, or (at your option) any later
-# version.
-#
-# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
-# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# Rattail. If not, see .
-#
-################################################################################
-"""
-Batch Field Renderers
-"""
-
-from __future__ import unicode_literals, absolute_import
-
-import os
-import stat
-import random
-
-from formalchemy.ext import fsblob
-from formalchemy.fields import FileFieldRenderer as Base
-
-
-class FileFieldRenderer(fsblob.FileFieldRenderer):
- """
- Custom file field renderer. In readonly mode, shows a filename and its
- size; in edit mode, supports a single file upload.
- """
-
- @classmethod
- def new(cls, view, **kwargs):
- name = b'Configured{}_{}'.format(cls.__name__, str(random.random())[2:])
- return type(name, (cls,), dict(view=view, **kwargs))
-
- @property
- def request(self):
- return self.view.request
-
- @property
- def storage_path(self):
- return self.view.upload_dir
-
- def get_file_path(self):
- """
- Returns the absolute path to the data file.
- """
- if hasattr(self, 'file_path'):
- return self.file_path
- return self.field.value
-
- def get_size(self):
- """
- Returns the size of the data file, in bytes.
- """
- path = self.get_file_path()
- if path and os.path.isfile(path):
- return os.stat(path)[stat.ST_SIZE]
- return 0
-
- def get_url(self, filename):
- """
- Must return a URL suitable for downloading the file
- """
- url = self.get_download_url()
- if url:
- if callable(url):
- return url(filename)
- return url
- return self.view.get_action_url('download', self.field.parent.model)
-
- def get_download_url(self):
- if hasattr(self, 'download_url'):
- return self.download_url
-
- def render(self, **kwargs):
- return Base.render(self, **kwargs)
-
- def render_readonly(self, **kwargs):
- """
- Render the filename and the binary size in a human readable with a link
- to the file itself.
- """
- value = self.get_file_path()
- if value:
- content = '{} ({})'.format(fsblob.normalized_basename(value),
- self.readable_size())
- return fsblob.h.content_tag('a', content,
- href=self.get_url(value), **kwargs)
- return ''
diff --git a/tailbone/forms/renderers/people.py b/tailbone/forms/renderers/people.py
deleted file mode 100644
index 54b4aefa..00000000
--- a/tailbone/forms/renderers/people.py
+++ /dev/null
@@ -1,81 +0,0 @@
-# -*- coding: utf-8; -*-
-################################################################################
-#
-# Rattail -- Retail Software Framework
-# Copyright © 2010-2017 Lance Edgar
-#
-# This file is part of Rattail.
-#
-# Rattail is free software: you can redistribute it and/or modify it under the
-# terms of the GNU General Public License as published by the Free Software
-# Foundation, either version 3 of the License, or (at your option) any later
-# version.
-#
-# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
-# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# Rattail. If not, see .
-#
-################################################################################
-"""
-People Field Renderers
-"""
-
-from __future__ import unicode_literals, absolute_import
-
-import six
-import formalchemy as fa
-from webhelpers2.html import tags, HTML
-
-from tailbone.forms.renderers.common import AutocompleteFieldRenderer
-
-
-class PersonFieldRenderer(AutocompleteFieldRenderer):
- """
- Renderer for :class:`rattail.db.model.Person` instance fields.
- """
- service_route = 'people.autocomplete'
-
- def render_readonly(self, **kwargs):
- person = self.raw_value
- if not person:
- return ''
- return tags.link_to(person, self.request.route_url('people.view', uuid=person.uuid))
-
-
-class PeopleFieldRenderer(fa.FieldRenderer):
- """
- Renderer for "people" list relationship
- """
-
- def render_readonly(self, **kwargs):
- html = ''
- people = self.raw_value
- if not people:
- return html
- for person in people:
- link = tags.link_to(person, self.request.route_url('people.view', uuid=person.uuid))
- html += HTML.tag('li', c=link)
- html = HTML.tag('ul', c=html)
- return html
-
-
-class CustomerFieldRenderer(AutocompleteFieldRenderer):
- """
- Renderer for :class:`rattail.db.model.Customer` instance fields.
- """
-
- service_route = 'customers.autocomplete'
-
- def render_readonly(self, **kwargs):
- customer = self.raw_value
- if not customer:
- return ''
- text = self.render_value(customer)
- return tags.link_to(text, self.request.route_url('customers.view', uuid=customer.uuid))
-
- def render_value(self, customer):
- return six.text_type(customer)
diff --git a/tailbone/forms/renderers/products.py b/tailbone/forms/renderers/products.py
deleted file mode 100644
index daca5dc5..00000000
--- a/tailbone/forms/renderers/products.py
+++ /dev/null
@@ -1,224 +0,0 @@
-# -*- coding: utf-8; -*-
-################################################################################
-#
-# Rattail -- Retail Software Framework
-# Copyright © 2010-2017 Lance Edgar
-#
-# This file is part of Rattail.
-#
-# Rattail is free software: you can redistribute it and/or modify it under the
-# terms of the GNU General Public License as published by the Free Software
-# Foundation, either version 3 of the License, or (at your option) any later
-# version.
-#
-# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
-# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# Rattail. If not, see .
-#
-################################################################################
-"""
-Product Field Renderers
-"""
-
-from __future__ import unicode_literals, absolute_import
-
-import six
-
-from rattail.gpc import GPC
-from rattail.db import model
-from rattail.db.util import maxlen
-
-import formalchemy as fa
-from formalchemy import TextFieldRenderer
-from formalchemy.fields import SelectFieldRenderer
-from webhelpers2.html import tags, literal
-
-from tailbone.forms.renderers.common import AutocompleteFieldRenderer
-from tailbone.util import pretty_datetime
-
-
-class ProductFieldRenderer(AutocompleteFieldRenderer):
- """
- Renderer for :class:`rattail.db.model.Product` instance fields.
- """
-
- service_route = 'products.autocomplete'
-
- @property
- def field_display(self):
- product = self.raw_value
- if product:
- return product.full_description
- return ''
-
- def render_readonly(self, **kwargs):
- product = self.raw_value
- if not product:
- return ""
- render = kwargs.get('render_product', self.render_product)
- text = render(product)
- if kwargs.get('hyperlink', True):
- return tags.link_to(text, self.request.route_url('products.view', uuid=product.uuid))
- return text
-
- def render_product(self, product):
- return six.text_type(product)
-
-
-class ProductKeyFieldRenderer(TextFieldRenderer):
- """
- Base class for product key field renderers.
- """
-
- def render_readonly(self, **kwargs):
- value = self.raw_value
- if value is None:
- return ''
- value = self.render_value(value)
- if kwargs.get('link'):
- product = self.field.parent.model
- value = tags.link_to(value, kwargs['link'](product))
- return value
-
- def render_value(self, value):
- return unicode(value)
-
-
-class GPCFieldRenderer(ProductKeyFieldRenderer):
- """
- Renderer for :class:`rattail.gpc.GPC` fields.
- """
-
- @property
- def length(self):
- # Hm, should maybe consider hard-coding this...?
- return len(unicode(GPC(0)))
-
- def render_value(self, gpc):
- return gpc.pretty()
-
-
-class ScancodeFieldRenderer(ProductKeyFieldRenderer):
- """
- Renderer for :class:`rattail.db.model.Product.scancode` field
- """
-
- @property
- def length(self):
- return maxlen(model.Product.scancode)
-
-
-class DepartmentFieldRenderer(SelectFieldRenderer):
- """
- Shows the department number as well as the name.
- """
-
- def render(self, **kwargs):
- kwargs.setdefault('auto-enhance', 'true')
- return super(DepartmentFieldRenderer, self).render(**kwargs)
-
- def render_readonly(self, **kwargs):
- department = self.raw_value
- if not department:
- return ''
- if department.number:
- text = '({}) {}'.format(department.number, department.name)
- else:
- text = department.name
- return tags.link_to(text, self.request.route_url('departments.view', uuid=department.uuid))
-
-
-class SubdepartmentFieldRenderer(SelectFieldRenderer):
- """
- Shows a link to the subdepartment.
- """
-
- def render_readonly(self, **kwargs):
- subdept = self.raw_value
- if not subdept:
- return ""
- if subdept.number:
- text = "({}) {}".format(subdept.number, subdept.name)
- else:
- text = subdept.name
- return tags.link_to(text, self.request.route_url('subdepartments.view', uuid=subdept.uuid))
-
-
-class CategoryFieldRenderer(SelectFieldRenderer):
- """
- Shows a link to the category.
- """
-
- def render_readonly(self, **kwargs):
- category = self.raw_value
- if not category:
- return ""
- if category.code:
- text = "({}) {}".format(category.code, category.name)
- else:
- text = category.name
- return tags.link_to(text, self.request.route_url('categories.view', uuid=category.uuid))
-
-
-class BrandFieldRenderer(AutocompleteFieldRenderer):
- """
- Renderer for :class:`rattail.db.model.Brand` instance fields.
- """
-
- service_route = 'brands.autocomplete'
-
-
-class CostFieldRenderer(fa.FieldRenderer):
- """
- Renders fields which reference a ProductCost object
- """
-
- def render_readonly(self, **kwargs):
- cost = self.raw_value
- if not cost:
- return ''
- return '${:0.2f}'.format(cost.unit_cost)
-
-
-class PriceFieldRenderer(TextFieldRenderer):
- """
- Renderer for fields which reference a :class:`ProductPrice` instance.
- """
-
- def render_readonly(self, **kwargs):
- price = self.field.raw_value
- if price:
- if not price.product.not_for_sale:
- if price.price is not None and price.pack_price is not None:
- if price.multiple > 1:
- return literal('$ %0.2f / %u ($ %0.2f / %u)' % (
- price.price, price.multiple,
- price.pack_price, price.pack_multiple))
- return literal('$ %0.2f ($ %0.2f / %u)' % (
- price.price, price.pack_price, price.pack_multiple))
- if price.price is not None:
- if price.multiple > 1:
- return '$ %0.2f / %u' % (price.price, price.multiple)
- return '$ %0.2f' % price.price
- if price.pack_price is not None:
- return '$ %0.2f / %u' % (price.pack_price, price.pack_multiple)
- return ''
-
-
-class PriceWithExpirationFieldRenderer(PriceFieldRenderer):
- """
- Price field renderer which also displays the expiration date, if present.
- """
-
- def render_readonly(self, **kwargs):
- result = super(PriceWithExpirationFieldRenderer, self).render_readonly(**kwargs)
- if result:
- price = self.field.raw_value
- if price.ends:
- result = '{0} ({1})'.format(
- result, pretty_datetime(self.request.rattail_config, price.ends))
- return result
diff --git a/tailbone/forms/renderers/stores.py b/tailbone/forms/renderers/stores.py
deleted file mode 100644
index 332a91a6..00000000
--- a/tailbone/forms/renderers/stores.py
+++ /dev/null
@@ -1,49 +0,0 @@
-# -*- coding: utf-8; -*-
-################################################################################
-#
-# Rattail -- Retail Software Framework
-# Copyright © 2010-2017 Lance Edgar
-#
-# This file is part of Rattail.
-#
-# Rattail is free software: you can redistribute it and/or modify it under the
-# terms of the GNU General Public License as published by the Free Software
-# Foundation, either version 3 of the License, or (at your option) any later
-# version.
-#
-# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
-# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# Rattail. If not, see .
-#
-################################################################################
-"""
-Store Field Renderers
-"""
-
-from __future__ import unicode_literals, absolute_import
-
-from formalchemy.fields import SelectFieldRenderer
-from webhelpers2.html import tags
-
-
-class StoreFieldRenderer(SelectFieldRenderer):
- """
- Renderer for :class:`rattail.db.model.Store` instance fields.
- """
-
- def render(self, **kwargs):
- kwargs.setdefault('auto-enhance', 'true')
- return super(StoreFieldRenderer, self).render(**kwargs)
-
- def render_readonly(self, **kwargs):
- store = self.raw_value
- if not store:
- return ""
- text = "({}) {}".format(store.id, store.name)
- if kwargs.get('hyperlink', True):
- return tags.link_to(text, self.request.route_url('stores.view', uuid=store.uuid))
- return text
diff --git a/tailbone/forms/renderers/users.py b/tailbone/forms/renderers/users.py
deleted file mode 100644
index c6343492..00000000
--- a/tailbone/forms/renderers/users.py
+++ /dev/null
@@ -1,97 +0,0 @@
-# -*- coding: utf-8; -*-
-################################################################################
-#
-# Rattail -- Retail Software Framework
-# Copyright © 2010-2017 Lance Edgar
-#
-# This file is part of Rattail.
-#
-# Rattail is free software: you can redistribute it and/or modify it under the
-# terms of the GNU General Public License as published by the Free Software
-# Foundation, either version 3 of the License, or (at your option) any later
-# version.
-#
-# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
-# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# Rattail. If not, see .
-#
-################################################################################
-"""
-User Field Renderers
-"""
-
-from __future__ import unicode_literals, absolute_import
-
-import six
-
-from rattail.db import model
-from rattail.db.auth import has_permission, administrator_role
-
-import formalchemy
-from webhelpers2.html import HTML, tags
-
-from tailbone.db import Session
-
-
-class UserFieldRenderer(formalchemy.TextFieldRenderer):
- """
- Renderer for :class:`rattail:rattail.db.model.User` instance fields.
- """
-
- def render_readonly(self, **kwargs):
- user = self.raw_value
- if not user:
- return ''
- title = six.text_type(user)
- if kwargs.get('hyperlink') and self.request.has_perm('users.view'):
- return tags.link_to(title, self.request.route_url('users.view', uuid=user.uuid))
- return title
-
-
-def PermissionsFieldRenderer(permissions, include_guest=False, include_authenticated=False):
-
- class PermissionsFieldRenderer(formalchemy.FieldRenderer):
-
- def deserialize(self):
- perms = []
- i = len(self.name) + 1
- for key in self.params:
- if key.startswith(self.name):
- perms.append(key[i:])
- return perms
-
- def _render(self, readonly=False, **kwargs):
- principal = self.field.model
- html = ''
- for groupkey in sorted(permissions, key=lambda k: permissions[k]['label'].lower()):
- inner = HTML.tag('p', c=permissions[groupkey]['label'])
- perms = permissions[groupkey]['perms']
- rendered = False
- for key in sorted(perms, key=lambda p: perms[p]['label'].lower()):
- checked = has_permission(Session(), principal, key,
- include_guest=include_guest,
- include_authenticated=include_authenticated)
- if checked or not readonly:
- label = perms[key]['label']
- if readonly:
- span = HTML.tag('span', c="[X]" if checked else "[ ]")
- inner += HTML.tag('p', class_='perm', c=span + ' ' + label)
- else:
- inner += tags.checkbox(self.name + '-' + key,
- checked=checked, label=label)
- rendered = True
- if rendered:
- html += HTML.tag('div', class_='group', c=inner)
- return html or "(none granted)"
-
- def render(self, **kwargs):
- return self._render(**kwargs)
-
- def render_readonly(self, **kwargs):
- return self._render(readonly=True, **kwargs)
-
- return PermissionsFieldRenderer
diff --git a/tailbone/forms/renderers/vendors.py b/tailbone/forms/renderers/vendors.py
deleted file mode 100644
index 3b7cf28d..00000000
--- a/tailbone/forms/renderers/vendors.py
+++ /dev/null
@@ -1,60 +0,0 @@
-# -*- coding: utf-8; -*-
-################################################################################
-#
-# Rattail -- Retail Software Framework
-# Copyright © 2010-2017 Lance Edgar
-#
-# This file is part of Rattail.
-#
-# Rattail is free software: you can redistribute it and/or modify it under the
-# terms of the GNU General Public License as published by the Free Software
-# Foundation, either version 3 of the License, or (at your option) any later
-# version.
-#
-# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
-# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# Rattail. If not, see .
-#
-################################################################################
-"""
-Vendor Field Renderers
-"""
-
-from __future__ import unicode_literals, absolute_import
-
-from formalchemy.fields import SelectFieldRenderer
-from webhelpers2.html import tags
-
-from tailbone.forms.renderers.common import AutocompleteFieldRenderer
-
-
-class VendorFieldRenderer(AutocompleteFieldRenderer):
- """
- Renderer for :class:`rattail.db.model.Vendor` instance fields.
- """
- service_route = 'vendors.autocomplete'
-
- def render_readonly(self, **kwargs):
- vendor = self.raw_value
- if not vendor:
- return ""
- text = "({}) {}".format(vendor.id, vendor.name)
- if kwargs.get('hyperlink', True):
- return tags.link_to(text, self.request.route_url('vendors.view', uuid=vendor.uuid))
- return text
-
-
-class PurchaseFieldRenderer(SelectFieldRenderer):
- """
- Renderer for :class:`rattail.db.model.Purchase` relation fields.
- """
-
- def render_readonly(self, **kwargs):
- purchase = self.raw_value
- if not purchase:
- return ''
- return tags.link_to(purchase, self.request.route_url('purchases.view', uuid=purchase.uuid))
diff --git a/tailbone/forms/simpleform.py b/tailbone/forms/simpleform.py
deleted file mode 100644
index afe1f289..00000000
--- a/tailbone/forms/simpleform.py
+++ /dev/null
@@ -1,82 +0,0 @@
-# -*- coding: utf-8; -*-
-################################################################################
-#
-# Rattail -- Retail Software Framework
-# Copyright © 2010-2017 Lance Edgar
-#
-# This file is part of Rattail.
-#
-# Rattail is free software: you can redistribute it and/or modify it under the
-# terms of the GNU General Public License as published by the Free Software
-# Foundation, either version 3 of the License, or (at your option) any later
-# version.
-#
-# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
-# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# Rattail. If not, see .
-#
-################################################################################
-"""
-Simple Forms
-"""
-
-from __future__ import unicode_literals, absolute_import
-
-from rattail.util import prettify
-
-import pyramid_simpleform
-from pyramid_simpleform import renderers
-from webhelpers2.html import tags, HTML
-
-from tailbone.forms import Form
-
-
-class SimpleForm(Form):
- """
- Customized simple form.
- """
-
- def __init__(self, request, schema, obj=None, **kwargs):
- super(SimpleForm, self).__init__(request, **kwargs)
- self._form = pyramid_simpleform.Form(request, schema=schema, obj=obj)
-
- def __getattr__(self, attr):
- return getattr(self._form, attr)
-
- def render(self, **kwargs):
- kwargs['form'] = FormRenderer(self)
- return super(SimpleForm, self).render(**kwargs)
-
- def validate(self):
- return self._form.validate()
-
-
-class FormRenderer(renderers.FormRenderer):
- """
- Customized form renderer. Provides some extra methods for convenience.
- """
-
- def __getattr__(self, attr):
- return getattr(self.form, attr)
-
- def field_div(self, name, field, label=None):
- errors = self.errors_for(name)
- if errors:
- errors = [HTML.tag('div', class_='field-error', c=x) for x in errors]
- errors = tags.literal('').join(errors)
-
- label = HTML.tag('label', for_=name, c=label or prettify(name))
- inner = HTML.tag('div', class_='field', c=field)
-
- outer_class = 'field-wrapper {}'.format(name)
- if errors:
- outer_class += ' error'
- outer = HTML.tag('div', class_=outer_class, c=(errors or '') + label + inner)
- return outer
-
- def referrer_field(self):
- return self.hidden('referrer', value=self.form.request.get_referrer())
diff --git a/tailbone/forms/validators.py b/tailbone/forms/validators.py
deleted file mode 100644
index fa5ab4ea..00000000
--- a/tailbone/forms/validators.py
+++ /dev/null
@@ -1,153 +0,0 @@
-# -*- coding: utf-8 -*-
-################################################################################
-#
-# Rattail -- Retail Software Framework
-# Copyright © 2010-2017 Lance Edgar
-#
-# This file is part of Rattail.
-#
-# Rattail is free software: you can redistribute it and/or modify it under the
-# terms of the GNU General Public License as published by the Free Software
-# Foundation, either version 3 of the License, or (at your option) any later
-# version.
-#
-# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
-# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# Rattail. If not, see .
-#
-################################################################################
-"""
-Custom Form Validators
-"""
-
-from __future__ import unicode_literals, absolute_import
-
-import re
-
-from rattail.db import model
-from rattail.db.util import validate_email_address, validate_phone_number
-from rattail.gpc import GPC
-
-import formencode as fe
-import formalchemy as fa
-
-from tailbone.db import Session
-
-
-class ValidGPC(fe.validators.FancyValidator):
- """
- Validator for fields which should contain GPC value.
- """
-
- def _to_python(self, value, state):
- if value is not None:
- digits = re.sub(r'\D', '', value)
- if digits:
- try:
- return GPC(digits)
- except ValueError as error:
- raise fe.Invalid("Invalid UPC: {}".format(error), value, state)
-
- def _from_python(self, upc, state):
- if upc is None:
- return ''
- return upc.pretty()
-
-
-class ModelValidator(fe.validators.FancyValidator):
- """
- Generic validator for data model reference fields.
- """
- model_class = None
-
- @property
- def model_name(self):
- self.model_class.__name__
-
- def _to_python(self, value, state):
- if value:
- obj = Session.query(self.model_class).get(value)
- if obj:
- return obj
- raise fe.Invalid("{} not found".format(self.model_name), value, state)
-
- def _from_python(self, value, state):
- obj = value
- if not obj:
- return ''
- return obj.uuid
-
- def validate_python(self, value, state):
- obj = value
- if obj is not None and not isinstance(obj, self.model_class):
- raise fe.Invalid("Value must be a valid {} object".format(self.model_name), value, state)
-
-
-class ValidStore(ModelValidator):
- """
- Validator for store field.
- """
- model_class = model.Store
-
-
-class ValidCustomer(ModelValidator):
- """
- Validator for customer field.
- """
- model_class = model.Customer
-
-
-class ValidDepartment(ModelValidator):
- """
- Validator for department field.
- """
- model_class = model.Department
-
-
-class ValidEmployee(ModelValidator):
- """
- Validator for employee field.
- """
- model_class = model.Employee
-
-
-class ValidProduct(ModelValidator):
- """
- Validator for product field.
- """
- model_class = model.Product
-
-
-class ValidUser(ModelValidator):
- """
- Validator for user field.
- """
- model_class = model.User
-
-
-def valid_email_address(value, field=None):
- """
- FormAlchemy-compatible validation function, which leverages FormEncode
- under the hood.
- """
- if value:
- try:
- return validate_email_address(value, error=True)
- except Exception as error:
- raise fa.ValidationError(unicode(error))
-
-
-def valid_phone_number(value, field=None):
- """
- FormAlchemy-compatible validation function, which leverages FormEncode
- under the hood.
- """
- if value:
- try:
- return validate_phone_number(value, error=True)
- except Exception as error:
- raise fa.ValidationError(unicode(error))
diff --git a/tailbone/templates/forms/field_autocomplete.mako b/tailbone/templates/forms/field_autocomplete.mako
deleted file mode 100644
index 17720c68..00000000
--- a/tailbone/templates/forms/field_autocomplete.mako
+++ /dev/null
@@ -1,4 +0,0 @@
-## -*- coding: utf-8 -*-
-<%namespace file="/autocomplete.mako" import="autocomplete" />
-
-${autocomplete(field_name, service_url, field_value, field_display, width=width, selected=selected, cleared=cleared)}
diff --git a/tailbone/templates/forms/fields/date.mako b/tailbone/templates/forms/fields/date.mako
deleted file mode 100644
index e74e90a8..00000000
--- a/tailbone/templates/forms/fields/date.mako
+++ /dev/null
@@ -1,10 +0,0 @@
-## -*- coding: utf-8 -*-
-${h.text(name, value=value)}
-
diff --git a/tailbone/templates/forms/fieldset.mako b/tailbone/templates/forms/fieldset.mako
deleted file mode 100644
index 98fc47d8..00000000
--- a/tailbone/templates/forms/fieldset.mako
+++ /dev/null
@@ -1,40 +0,0 @@
-## -*- coding: utf-8 -*-
-<% _focus_rendered = False %>
-
-% for error in fieldset.errors.get(None, []):
-
${error}
-% endfor
-
-% for field in fieldset.render_fields.itervalues():
-
- % if field.requires_label:
-
- % for error in field.errors:
-
${error}
- % endfor
- ${field.label_tag()|n}
-
${field.render()|n}
- % if 'instructions' in field.metadata:
-
${field.metadata['instructions']}
- % endif
-
-
- % if not _focus_rendered and (fieldset.focus is True or fieldset.focus is field):
- % if not field.is_readonly() and getattr(field.renderer, 'needs_focus', True):
-
- <% _focus_rendered = True %>
- % endif
- % endif
- % else:
- ${field.render()|n}
- % endif
-
-% endfor
diff --git a/tailbone/templates/forms/fieldset_readonly.mako b/tailbone/templates/forms/fieldset_readonly.mako
deleted file mode 100644
index 58aef14c..00000000
--- a/tailbone/templates/forms/fieldset_readonly.mako
+++ /dev/null
@@ -1,7 +0,0 @@
-## -*- coding: utf-8 -*-
-<%namespace file="/forms/lib.mako" import="render_field_readonly" />
-
- % for field in fieldset.render_fields.itervalues():
- ${render_field_readonly(field)}
- % endfor
-
diff --git a/tailbone/templates/forms/form.mako b/tailbone/templates/forms/form.mako
deleted file mode 100644
index 3baf812a..00000000
--- a/tailbone/templates/forms/form.mako
+++ /dev/null
@@ -1,21 +0,0 @@
-## -*- coding: utf-8 -*-
-
diff --git a/tailbone/templates/forms/form_readonly.mako b/tailbone/templates/forms/form_readonly.mako
deleted file mode 100644
index c8aa0543..00000000
--- a/tailbone/templates/forms/form_readonly.mako
+++ /dev/null
@@ -1,7 +0,0 @@
-## -*- coding: utf-8 -*-
-
- ${form.render_fields()|n}
- % if buttons:
- ${buttons|n}
- % endif
-
diff --git a/tailbone/templates/forms/lib.mako b/tailbone/templates/forms/lib.mako
deleted file mode 100644
index 602d35c4..00000000
--- a/tailbone/templates/forms/lib.mako
+++ /dev/null
@@ -1,10 +0,0 @@
-## -*- coding: utf-8 -*-
-
-<%def name="render_field_readonly(field)">
- % if field.requires_label:
-
- ${field.label_tag()|n}
-
${field.render_readonly()}
-
- % endif
-%def>